From 3bcdf1d164fef87ce66f5d72f234a2a44228a333 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Sat, 2 Jul 2016 19:54:05 +0200 Subject: [PATCH] Added improved data collector and web profiler page --- ClientFactory/PluginClientFactory.php | 7 +- Collector/DebugPlugin.php | 63 ++++++ Collector/DebugPluginCollector.php | 181 ++++++++++++++++++ Collector/MessageJournal.php | 109 ----------- Collector/PluginJournal.php | 50 +++++ Collector/RequestStackProvider.php | 141 ++++++++++++++ Collector/Twig/HttpMessageMarkupExtension.php | 49 +++++ DependencyInjection/Configuration.php | 5 + DependencyInjection/HttplugExtension.php | 53 +++-- Resources/config/data-collector.xml | 15 +- Resources/public/script/httplug.js | 130 +++++++++++++ Resources/public/style/httplug.css | 24 +++ Resources/views/webprofiler.html.twig | 101 +++++++--- .../DependencyInjection/ConfigurationTest.php | 2 + composer.json | 7 +- 15 files changed, 781 insertions(+), 156 deletions(-) create mode 100644 Collector/DebugPlugin.php create mode 100644 Collector/DebugPluginCollector.php delete mode 100644 Collector/MessageJournal.php create mode 100644 Collector/PluginJournal.php create mode 100644 Collector/RequestStackProvider.php create mode 100644 Collector/Twig/HttpMessageMarkupExtension.php create mode 100644 Resources/public/script/httplug.js create mode 100644 Resources/public/style/httplug.css diff --git a/ClientFactory/PluginClientFactory.php b/ClientFactory/PluginClientFactory.php index 120066e3..f0ff28dc 100644 --- a/ClientFactory/PluginClientFactory.php +++ b/ClientFactory/PluginClientFactory.php @@ -15,12 +15,13 @@ final class PluginClientFactory /** * @param Plugin[] $plugins * @param ClientFactory $factory - * @param array $config + * @param array $config config to the client factory + * @param array $pluginClientOptions config forwarded to the PluginClient * * @return PluginClient */ - public static function createPluginClient(array $plugins, ClientFactory $factory, array $config) + public static function createPluginClient(array $plugins, ClientFactory $factory, array $config, array $pluginClientOptions = []) { - return new PluginClient($factory->createClient($config), $plugins); + return new PluginClient($factory->createClient($config), $plugins, $pluginClientOptions); } } diff --git a/Collector/DebugPlugin.php b/Collector/DebugPlugin.php new file mode 100644 index 00000000..224b3bef --- /dev/null +++ b/Collector/DebugPlugin.php @@ -0,0 +1,63 @@ + + */ +final class DebugPlugin implements Plugin +{ + /** + * @var DebugPluginCollector + */ + private $collector; + + /** + * @var string + */ + private $clientName; + + /** + * @var int + */ + private $depth = -1; + + /** + * @param DebugPluginCollector $collector + * @param string $clientName + */ + public function __construct(DebugPluginCollector $collector, $clientName) + { + $this->collector = $collector; + $this->clientName = $clientName; + } + + /** + * {@inheritdoc} + */ + public function handleRequest(RequestInterface $request, callable $next, callable $first) + { + $collector = $this->collector; + $clientName = $this->clientName; + $depth = &$this->depth; + + $collector->addRequest($request, $clientName, ++$depth); + + return $next($request)->then(function (ResponseInterface $response) use ($collector, $clientName, &$depth) { + $collector->addResponse($response, $clientName, $depth--); + + return $response; + }, function (Exception $exception) use ($collector, $clientName, &$depth) { + $collector->addFailure($exception, $clientName, $depth--); + + throw $exception; + }); + } +} diff --git a/Collector/DebugPluginCollector.php b/Collector/DebugPluginCollector.php new file mode 100644 index 00000000..220557b5 --- /dev/null +++ b/Collector/DebugPluginCollector.php @@ -0,0 +1,181 @@ + + */ +final class DebugPluginCollector extends DataCollector +{ + /** + * @var Formatter + */ + private $formatter; + + /** + * @var PluginJournal + */ + private $journal; + + /** + * @param Formatter $formatter + * @param PluginJournal $journal + */ + public function __construct(Formatter $formatter, PluginJournal $journal) + { + $this->formatter = $formatter; + $this->journal = $journal; + } + + /** + * @param RequestInterface $request + * @param string $clientName + * @param int $depth + */ + public function addRequest(RequestInterface $request, $clientName, $depth) + { + $this->data[$clientName]['request'][$depth][] = $this->formatter->formatRequest($request); + } + + /** + * @param ResponseInterface $response + * @param string $clientName + * @param int $depth + */ + public function addResponse(ResponseInterface $response, $clientName, $depth) + { + $this->data[$clientName]['response'][$depth][] = $this->formatter->formatResponse($response); + $this->data[$clientName]['failure'][$depth][] = false; + } + + /** + * @param Exception $exception + * @param string $clientName + * @param int $depth + */ + public function addFailure(Exception $exception, $clientName, $depth) + { + if ($exception instanceof Exception\HttpException) { + $formattedResponse = $this->formatter->formatResponse($exception->getResponse()); + } elseif ($exception instanceof Exception\TransferException) { + $formattedResponse = $exception->getMessage(); + } else { + $formattedResponse = sprintf('Unexpected exception of type "%s"', get_class($exception)); + } + + $this->data[$clientName]['response'][$depth][] = $formattedResponse; + $this->data[$clientName]['failure'][$depth][] = true; + } + + /** + * Returns the successful request-resonse pairs. + * + * @return array + */ + public function getSucessfulRequests() + { + $count = 0; + foreach ($this->data as $client) { + if (isset($client['failure'])) { + foreach ($client['failure'][0] as $failure) { + if (!$failure) { + ++$count; + } + } + } + } + + return $count; + } + + /** + * Returns the failed request-resonse pairs. + * + * @return array + */ + public function getFailedRequests() + { + $count = 0; + foreach ($this->data as $client) { + if (isset($client['failure'])) { + foreach ($client['failure'][0] as $failure) { + if ($failure) { + ++$count; + } + } + } + } + + return $count; + } + + /** + * Returns the total number of request made. + * + * @return int + */ + public function getTotalRequests() + { + return $this->getSucessfulRequests() + $this->getFailedRequests(); + } + + /** + * Return a RequestStackProvider for each client. + * + * @return RequestStackProvider[] + */ + public function getClients() + { + return RequestStackProvider::createFromCollectedData($this->data); + } + + /** + * @return PluginJournal + */ + public function getJournal() + { + return $this->journal; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + // We do not need to collect any data from the Symfony Request and Response + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'httplug'; + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + return serialize([$this->data, $this->journal]); + } + + /** + * {@inheritdoc} + */ + public function unserialize($data) + { + list($this->data, $this->journal) = unserialize($data); + } +} diff --git a/Collector/MessageJournal.php b/Collector/MessageJournal.php deleted file mode 100644 index 8930d085..00000000 --- a/Collector/MessageJournal.php +++ /dev/null @@ -1,109 +0,0 @@ - - */ -class MessageJournal extends DataCollector implements Journal -{ - /** - * @var Formatter - */ - private $formatter; - - /** - * @param Formatter $formatter - */ - public function __construct(Formatter $formatter = null) - { - $this->formatter = $formatter ?: new SimpleFormatter(); - $this->data = ['success' => [], 'failure' => []]; - } - - /** - * {@inheritdoc} - */ - public function addSuccess(RequestInterface $request, ResponseInterface $response) - { - $this->data['success'][] = [ - 'request' => $this->formatter->formatRequest($request), - 'response' => $this->formatter->formatResponse($response), - ]; - } - - /** - * {@inheritdoc} - */ - public function addFailure(RequestInterface $request, Exception $exception) - { - if ($exception instanceof Exception\HttpException) { - $formattedResponse = $this->formatter->formatResponse($exception->getResponse()); - } elseif ($exception instanceof Exception\TransferException) { - $formattedResponse = $exception->getMessage(); - } else { - $formattedResponse = sprintf('Unexpected exception of type "%s"', get_class($exception)); - } - - $this->data['failure'][] = [ - 'request' => $this->formatter->formatRequest($request), - 'response' => $formattedResponse, - ]; - } - - /** - * Returns the successful request-resonse pairs. - * - * @return array - */ - public function getSucessfulRequests() - { - return $this->data['success']; - } - - /** - * Returns the failed request-resonse pairs. - * - * @return array - */ - public function getFailedRequests() - { - return $this->data['failure']; - } - - /** - * Returns the total number of request made. - * - * @return int - */ - public function getTotalRequests() - { - return count($this->data['success']) + count($this->data['failure']); - } - - /** - * {@inheritdoc} - */ - public function collect(Request $request, Response $response, \Exception $exception = null) - { - // We do not need to collect any data from the Symfony Request and Response - } - - /** - * {@inheritdoc} - */ - public function getName() - { - return 'httplug'; - } -} diff --git a/Collector/PluginJournal.php b/Collector/PluginJournal.php new file mode 100644 index 00000000..cb53d37d --- /dev/null +++ b/Collector/PluginJournal.php @@ -0,0 +1,50 @@ + + */ +final class PluginJournal +{ + /** + * @var array ['clientName'=>['index' => 'PluginName'] + */ + private $data; + + /** + * @param string $clientName + * + * @return array + */ + public function getPlugins($clientName) + { + return $this->data[$clientName]; + } + + /** + * @param string $clientName + * @param int $idx + * + * @return string|null + */ + public function getPluginName($clientName, $idx) + { + if (isset($this->data[$clientName][$idx])) { + return $this->data[$clientName][$idx]; + } + + return; + } + + /** + * @param string $clientName + * @param array $plugins + */ + public function setPlugins($clientName, array $plugins) + { + $this->data[$clientName] = $plugins; + } +} diff --git a/Collector/RequestStackProvider.php b/Collector/RequestStackProvider.php new file mode 100644 index 00000000..c5a52e5d --- /dev/null +++ b/Collector/RequestStackProvider.php @@ -0,0 +1,141 @@ + + */ +final class RequestStackProvider +{ + /** + * Array that tell if a request errored or not. true = success, false = failure. + * + * @var array + */ + private $failures; + + /** + * A multidimensional array with requests. + * $requests[0][0] is the first request before all plugins. + * $requests[0][1] is the first request after the first plugin. + * + * @var array + */ + private $requests; + + /** + * A multidimensional array with responses. + * $responses[0][0] is the first responses before all plugins. + * $responses[0][1] is the first responses after the first plugin. + * + * @var array + */ + private $responses; + + /** + * @param array $failures if the response was successful or not + * @param array $requests + * @param array $responses + */ + public function __construct(array $failures, array $requests, array $responses) + { + $this->failures = $failures; + $this->requests = $requests; + $this->responses = $responses; + } + + /** + * Create an array of ClientDataCollector from collected data. + * + * @param array $data + * + * @return RequestStackProvider[] + */ + public static function createFromCollectedData(array $data) + { + $clientData = []; + foreach ($data as $clientName => $messages) { + $clientData[$clientName] = static::createOne($messages); + } + + return $clientData; + } + + /** + * @param array $messages is an array with keys 'failure', 'request' and 'response' which hold requests for each call to + * sendRequest and for each depth + * + * @return RequestStackProvider + */ + private static function createOne($messages) + { + $orderedFaulure = []; + $orderedRequests = []; + $orderedResponses = []; + + foreach ($messages['failure'] as $depth => $failures) { + foreach ($failures as $idx => $failure) { + $orderedFaulure[$idx][$depth] = $failure; + } + } + + foreach ($messages['request'] as $depth => $requests) { + foreach ($requests as $idx => $request) { + $orderedRequests[$idx][$depth] = $request; + } + } + + foreach ($messages['response'] as $depth => $responses) { + foreach ($responses as $idx => $response) { + $orderedResponses[$idx][$depth] = $response; + } + } + + return new self($orderedFaulure, $orderedRequests, $orderedResponses); + } + + /** + * Get the index keys for the request and response stacks. + * + * @return array + */ + public function getStackIndexKeys() + { + return array_keys($this->requests); + } + + /** + * @param int $idx + * + * @return array + */ + public function getRequstStack($idx) + { + return $this->requests[$idx]; + } + + /** + * @param int $idx + * + * @return array + */ + public function getResponseStack($idx) + { + return $this->responses[$idx]; + } + + /** + * @param int $idx + * + * @return array + */ + public function getFailureStack($idx) + { + return $this->failures[$idx]; + } +} diff --git a/Collector/Twig/HttpMessageMarkupExtension.php b/Collector/Twig/HttpMessageMarkupExtension.php new file mode 100644 index 00000000..b6c5c904 --- /dev/null +++ b/Collector/Twig/HttpMessageMarkupExtension.php @@ -0,0 +1,49 @@ + + */ +class HttpMessageMarkupExtension extends \Twig_Extension +{ + /** + * {@inheritdoc} + * + * @return array + */ + public function getFilters() + { + return [ + new \Twig_SimpleFilter('httplug_markup', [$this, 'markup'], ['is_safe' => ['html']]), + ]; + } + + /** + * @param string $message http message + */ + public function markup($message) + { + $safeMessage = htmlentities($message); + $parts = preg_split('|\\r?\\n\\r?\\n|', $safeMessage, 2); + + if (!isset($parts[1])) { + // This is not a HTTP message + return $safeMessage; + } + + if (empty($parts[1])) { + $parts[1] = '(This message has no captured body)'; + } + + // make header names bold + $headers = preg_replace("|\n(.*?): |si", "\n$1: ", $parts[0]); + + return sprintf("%s\n\n
%s
", $headers, $parts[1]); + } + + public function getName() + { + return 'httplug.message_markup'; + } +} diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 856fa26d..cf0ba826 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -83,6 +83,11 @@ public function getConfigTreeBuilder() ->defaultValue('auto') ->end() ->scalarNode('formatter')->defaultNull()->end() + ->scalarNode('captured_body_length') + ->defaultValue(0) + ->canNotBeEmpty() + ->info('Limit long HTTP message bodies to x characters. If set to 0 we do not read the message body. Only available with the default formatter (FullHttpMessageFormatter).') + ->end() ->end() ->end() ->end(); diff --git a/DependencyInjection/HttplugExtension.php b/DependencyInjection/HttplugExtension.php index 9d964103..09b6c6e4 100644 --- a/DependencyInjection/HttplugExtension.php +++ b/DependencyInjection/HttplugExtension.php @@ -7,6 +7,7 @@ use Http\Client\Common\Plugin\AuthenticationPlugin; use Http\Client\Common\PluginClient; use Http\HttplugBundle\ClientFactory\DummyClient; +use Http\HttplugBundle\Collector\DebugPlugin; use Http\Message\Authentication\BasicAuth; use Http\Message\Authentication\Bearer; use Http\Message\Authentication\Wsse; @@ -42,9 +43,13 @@ public function load(array $configs, ContainerBuilder $container) $config['_inject_collector_plugin'] = true; if (!empty($config['toolbar']['formatter'])) { - $container->getDefinition('httplug.collector.message_journal') + // Add custom formatter + $container->getDefinition('httplug.collector.debug_collector') ->replaceArgument(0, new Reference($config['toolbar']['formatter'])); } + + $container->getDefinition('httplug.formatter.full_http_message') + ->addArgument($config['toolbar']['captured_body_length']); } foreach ($config['classes'] as $service => $class) { @@ -79,11 +84,7 @@ private function configureClients(ContainerBuilder $container, array $config) $first = $name; } - if (isset($config['_inject_collector_plugin'])) { - array_unshift($arguments['plugins'], 'httplug.collector.history_plugin'); - } - - $this->configureClient($container, $name, $arguments); + $this->configureClient($container, $name, $arguments, $config['_inject_collector_plugin']); } // If we have clients configured @@ -93,10 +94,12 @@ private function configureClients(ContainerBuilder $container, array $config) $container->setAlias('httplug.client.default', 'httplug.client.'.$first); } } elseif (isset($config['_inject_collector_plugin'])) { - // No client was configured. Make sure to inject history plugin to the auto discovery client. + $serviceIdDebugPlugin = $this->registerDebugPlugin($container, 'default'); + // No client was configured. Make sure to configure the auto discovery client with the PluginClient. $container->register('httplug.client', PluginClient::class) ->addArgument(new Reference('httplug.client.default')) - ->addArgument([new Reference('httplug.collector.history_plugin')]); + ->addArgument([]) + ->addArgument(['debug_plugins' => [new Reference($serviceIdDebugPlugin)]]); } } @@ -205,16 +208,20 @@ private function configureAuthentication(ContainerBuilder $container, array $con * @param ContainerBuilder $container * @param string $name * @param array $arguments + * @param bool $enableCollector */ - private function configureClient(ContainerBuilder $container, $name, array $arguments) + private function configureClient(ContainerBuilder $container, $name, array $arguments, $enableCollector) { $serviceId = 'httplug.client.'.$name; $def = $container->register($serviceId, DummyClient::class); - if (empty($arguments['plugins'])) { + // If there is no plugins nor should we use the data collector + if (empty($arguments['plugins']) && !$enableCollector) { $def->setFactory([new Reference($arguments['factory']), 'createClient']) ->addArgument($arguments['config']); } else { + $serviceIdDebugPlugin = $this->registerDebugPlugin($container, $name); + $def->setFactory('Http\HttplugBundle\ClientFactory\PluginClientFactory::createPluginClient') ->addArgument( array_map( @@ -225,7 +232,12 @@ function ($id) { ) ) ->addArgument(new Reference($arguments['factory'])) - ->addArgument($arguments['config']); + ->addArgument($arguments['config']) + ->addArgument(['debug_plugins' => [new Reference($serviceIdDebugPlugin)]]); + + // tell the plugin journal what plugins we used + $container->getDefinition('httplug.collector.plugin_journal') + ->addMethodCall('setPlugins', [$name, $arguments['plugins']]); } @@ -247,4 +259,23 @@ function ($id) { ->setDecoratedService($serviceId); } } + + /** + * Create a new plugin service for this client. + * + * @param ContainerBuilder $container + * @param string $name + * + * @return string + */ + private function registerDebugPlugin(ContainerBuilder $container, $name) + { + $serviceIdDebugPlugin = 'httplug.client.'.$name.'.debug_plugin'; + $container->register($serviceIdDebugPlugin, DebugPlugin::class) + ->addArgument(new Reference('httplug.collector.debug_collector')) + ->addArgument($name) + ->setPublic(false); + + return $serviceIdDebugPlugin; + } } diff --git a/Resources/config/data-collector.xml b/Resources/config/data-collector.xml index 6f18a30e..d1815dfd 100644 --- a/Resources/config/data-collector.xml +++ b/Resources/config/data-collector.xml @@ -4,14 +4,17 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - null + + + + + + + - - + + diff --git a/Resources/public/script/httplug.js b/Resources/public/script/httplug.js new file mode 100644 index 00000000..fbd84e8f --- /dev/null +++ b/Resources/public/script/httplug.js @@ -0,0 +1,130 @@ +/** + * Toggle hide/show on the message body + */ +function httplug_toggleBody(el) { + var bodies = document.querySelectorAll(".httplug-http-body"); + + httplug_toggleVisibility(bodies); + + var newLabel = el.getAttribute("data-label"); + var oldLabel = el.innerHTML; + el.innerHTML = newLabel; + el.setAttribute("data-label", oldLabel); +} + +function httplug_togglePluginStack(el) { + var requestTable = httplug_getClosest(el, '.httplug-request-table'); + var stacks = requestTable.querySelectorAll('.httplug-request-stack'); + + httplug_toggleVisibility(stacks, "table-row"); + + var newLabel = el.getAttribute("data-label"); + var oldLabel = el.innerHTML; + el.innerHTML = newLabel; + el.setAttribute("data-label", oldLabel); +} + + + +/** + * Get the closest matching element up the DOM tree. + * + * {@link https://gomakethings.com/climbing-up-and-down-the-dom-tree-with-vanilla-javascript/} + * + * @param {Element} elem Starting element + * @param {String} selector Selector to match against (class, ID, data attribute, or tag) + * @return {Boolean|Element} Returns null if not match found + */ +var httplug_getClosest = function ( elem, selector ) { + + // Variables + var firstChar = selector.charAt(0); + var supports = 'classList' in document.documentElement; + var attribute, value; + + // If selector is a data attribute, split attribute from value + if ( firstChar === '[' ) { + selector = selector.substr( 1, selector.length - 2 ); + attribute = selector.split( '=' ); + + if ( attribute.length > 1 ) { + value = true; + attribute[1] = attribute[1].replace( /"/g, '' ).replace( /'/g, '' ); + } + } + + // Get closest match + for ( ; elem && elem !== document && elem.nodeType === 1; elem = elem.parentNode ) { + + // If selector is a class + if ( firstChar === '.' ) { + if ( supports ) { + if ( elem.classList.contains( selector.substr(1) ) ) { + return elem; + } + } else { + if ( new RegExp('(^|\\s)' + selector.substr(1) + '(\\s|$)').test( elem.className ) ) { + return elem; + } + } + } + + // If selector is an ID + if ( firstChar === '#' ) { + if ( elem.id === selector.substr(1) ) { + return elem; + } + } + + // If selector is a data attribute + if ( firstChar === '[' ) { + if ( elem.hasAttribute( attribute[0] ) ) { + if ( value ) { + if ( elem.getAttribute( attribute[0] ) === attribute[1] ) { + return elem; + } + } else { + return elem; + } + } + } + + // If selector is a tag + if ( elem.tagName.toLowerCase() === selector ) { + return elem; + } + + } + + return null; + +}; + +/** + * Check if element is hidden. + * @param el + * @returns {boolean} + */ +var httplug_isHidden = function (el) { + var style = window.getComputedStyle(el); + return (style.display === 'none') +} + +/** + * Toggle visibility on elemets + * @param els + * @param display defaults to "block" + */ +var httplug_toggleVisibility = function (els, display) { + if (typeof display === 'undefined') { + display = "block"; + } + + for (var i = 0; i < els.length; i++) { + if (httplug_isHidden(els[i])) { + els[i].style.display = display; + } else { + els[i].style.display = "none"; + } + } +}; diff --git a/Resources/public/style/httplug.css b/Resources/public/style/httplug.css new file mode 100644 index 00000000..907350ca --- /dev/null +++ b/Resources/public/style/httplug.css @@ -0,0 +1,24 @@ + +.push-right { + float: right; +} + +.httplug-http-body { + display: none; +} + +.httplug-request-table { + +} + +.httplug-plugin-name { + font-size: 130%; + font-weight: bold; +} +.httplug-request-stack { + + display: none; +} +.httplug-error { + color: red; +} diff --git a/Resources/views/webprofiler.html.twig b/Resources/views/webprofiler.html.twig index d667fc87..08e4d60d 100644 --- a/Resources/views/webprofiler.html.twig +++ b/Resources/views/webprofiler.html.twig @@ -17,11 +17,11 @@ {% set text %}
Successful requests - {{ collector.sucessfulRequests|length }} + {{ collector.sucessfulRequests }}
- Faild requests - {{ collector.failedRequests|length }} + Failed requests + {{ collector.failedRequests }}
{% endset %} @@ -30,7 +30,8 @@ {% endblock %} {% block head %} - {# Optional. Here you can link to or define your own CSS and JS contents. #} + + {{ parent() }} {% endblock %} @@ -50,37 +51,89 @@ {% block panel %}

HTTPlug

- {% if (collector.failedRequests|length > 0) %} -

Failed requests

- {{ macro.printMessages(collector.failedRequests) }} - {% endif %} + - {% if (collector.sucessfulRequests|length > 0) %} -

Successful requests

- {{ macro.printMessages(collector.sucessfulRequests) }} - {% endif %} +
+ {% for name, client in collector.clients %} +
+

{{ name }} {{ client.stackIndexKeys|length }}

- {% if collector.totalRequests == 0 %} +
+

+ These messages are sent by client named "{{ name }}". +

-
-

No request were sent.

+ {% for stackIndex in client.stackIndexKeys %} + {% set failureStack = client.failureStack(stackIndex) %} +

+ Request #{{ stackIndex }} + {% if failureStack[0] %} + - Errored + {% endif %} +

+ {{ macro.printMessages(client.requstStack(stackIndex), client.responseStack(stackIndex), failureStack, collector.journal.plugins(name)) }} + {% endfor %} +
- {% endif %} + {% else %} +
+

No request were sent.

+
+ {% endfor %} + +
{% endblock %} -{% macro printMessages(messages) %} - +{% macro printMessages(requestStack, responseStack, failureStack, pluginNames) %} +
- - + + - {% for message in messages %} + + + + + {% if requestStack|length > 1 %} - - + - {% endfor %} + {% for idx in 0..requestStack|length-1 %} + {% if loop.first %} + {# We do not have a plugin at the first entry in the stack #} + + + + + {% else %} + + + + + {% endif %} + + + + + {% if loop.last %} + + + + + {% endif %} + {% endfor %} + {% endif %}
RequestResponseRequestResponse
{{ requestStack[responseStack|length-1]|httplug_markup|nl2br }}{{ responseStack[0]|httplug_markup|nl2br }}
{{ message['request'] }}{{ message['response'] }} + +
↓ Start - End + {% if failureStack[idx] %} + + {% endif %} +
↓ {{ pluginNames[idx-1] }} ↑ + {% if failureStack[idx] %} + + {% endif %} +
{{ requestStack[idx]|httplug_markup|nl2br }}{{ responseStack[idx]|httplug_markup|nl2br }}
HTTP client
{% endmacro %} diff --git a/Tests/Unit/DependencyInjection/ConfigurationTest.php b/Tests/Unit/DependencyInjection/ConfigurationTest.php index bb709196..1a571c73 100644 --- a/Tests/Unit/DependencyInjection/ConfigurationTest.php +++ b/Tests/Unit/DependencyInjection/ConfigurationTest.php @@ -40,6 +40,7 @@ public function testEmptyConfiguration() 'toolbar' => [ 'enabled' => 'auto', 'formatter' => null, + 'captured_body_length' => 0, ], 'plugins' => [ 'authentication' => [], @@ -114,6 +115,7 @@ public function testSupportsAllConfigFormats() 'toolbar' => [ 'enabled' => true, 'formatter' => 'my_toolbar_formatter', + 'captured_body_length' => 0, ], 'plugins' => [ 'authentication' => [ diff --git a/composer.json b/composer.json index ef088300..d18e26ab 100644 --- a/composer.json +++ b/composer.json @@ -19,14 +19,16 @@ "php": ">=5.5", "php-http/client-implementation": "^1.0", "php-http/message-factory": "^1.0.2", - "php-http/client-common": "^1.1", + "php-http/client-common": "^1.2", "php-http/cache-plugin": "^1.0", "php-http/logger-plugin": "^1.0", "php-http/stopwatch-plugin": "^1.0", "symfony/options-resolver": "^2.7 || ^3.0", "symfony/event-dispatcher": "^2.7 || ^3.0", "symfony/framework-bundle": "^2.7 || ^3.0", - "php-http/discovery": "^0.9" + "php-http/message": "^1.3", + "php-http/discovery": "^0.9", + "twig/twig": "^1.18||^2.0" }, "require-dev": { "phpunit/phpunit": "^4.5", @@ -35,7 +37,6 @@ "php-http/guzzle6-adapter": "^1.1.1", "php-http/react-adapter": "^0.2", "php-http/buzz-adapter": "^0.2", - "php-http/message": "^1.0", "symfony/symfony": "^2.7 || ^3.0", "polishsymfonycommunity/symfony-mocker-container": "^1.0", "matthiasnoback/symfony-dependency-injection-test": "^0.7"