diff --git a/CHANGELOG.md b/CHANGELOG.md
index bfba1034..e83ab5a2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,10 +6,12 @@
- The real request method and target url are now displayed in the profiler.
- Support the cache plugin configuration for `respect_response_cache_directives`.
+- Extended WebProfilerToolbar item to list request with details.
### Changed
- The profiler design has been updated.
+- Removed stopwatch-plugin in favor of `ProfileClient`.
### Deprecated
diff --git a/Collector/Collector.php b/Collector/Collector.php
index 15d61024..ae1f3b8d 100644
--- a/Collector/Collector.php
+++ b/Collector/Collector.php
@@ -108,4 +108,14 @@ public function getClientStacks($client)
return $stack->getClient() == $client;
});
}
+
+ /**
+ * @return int
+ */
+ public function getTotalDuration()
+ {
+ return array_reduce($this->data['stacks'], function ($carry, Stack $stack) {
+ return $carry + $stack->getDuration();
+ }, 0);
+ }
}
diff --git a/Collector/ProfileClient.php b/Collector/ProfileClient.php
index aaf0c6b2..45a03d19 100644
--- a/Collector/ProfileClient.php
+++ b/Collector/ProfileClient.php
@@ -8,6 +8,8 @@
use Http\Client\HttpClient;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
+use Symfony\Component\Stopwatch\Stopwatch;
+use Symfony\Component\Stopwatch\StopwatchEvent;
/**
* The ProfileClient decorates any client that implement both HttpClient and HttpAsyncClient interfaces to gather target
@@ -34,13 +36,24 @@ class ProfileClient implements HttpClient, HttpAsyncClient
*/
private $formatter;
+ /**
+ * @var Stopwatch
+ */
+ private $stopwatch;
+
+ /**
+ * @var array
+ */
+ private $eventNames = [];
+
/**
* @param HttpClient|HttpAsyncClient $client The client to profile. Client must implement both HttpClient and
* HttpAsyncClient interfaces.
* @param Collector $collector
* @param Formatter $formatter
+ * @param Stopwatch $stopwatch
*/
- public function __construct($client, Collector $collector, Formatter $formatter)
+ public function __construct($client, Collector $collector, Formatter $formatter, Stopwatch $stopwatch)
{
if (!($client instanceof HttpClient && $client instanceof HttpAsyncClient)) {
throw new \RuntimeException(sprintf(
@@ -54,6 +67,7 @@ public function __construct($client, Collector $collector, Formatter $formatter)
$this->client = $client;
$this->collector = $collector;
$this->formatter = $formatter;
+ $this->stopwatch = $stopwatch;
}
/**
@@ -63,16 +77,21 @@ public function sendAsyncRequest(RequestInterface $request)
{
$stack = $this->collector->getCurrentStack();
$this->collectRequestInformations($request, $stack);
+ $event = $this->stopwatch->start($this->getStopwatchEventName($request));
- return $this->client->sendAsyncRequest($request)->then(function (ResponseInterface $response) use ($stack) {
- $this->collectResponseInformations($response, $stack);
+ return $this->client->sendAsyncRequest($request)->then(
+ function (ResponseInterface $response) use ($event, $stack) {
+ $event->stop();
+ $this->collectResponseInformations($response, $event, $stack);
- return $response;
- }, function (\Exception $exception) use ($stack) {
- $this->collectExceptionInformations($exception, $stack);
+ return $response;
+ }, function (\Exception $exception) use ($event, $stack) {
+ $event->stop();
+ $this->collectExceptionInformations($exception, $event, $stack);
- throw $exception;
- });
+ throw $exception;
+ }
+ );
}
/**
@@ -82,15 +101,18 @@ public function sendRequest(RequestInterface $request)
{
$stack = $this->collector->getCurrentStack();
$this->collectRequestInformations($request, $stack);
+ $event = $this->stopwatch->start($this->getStopwatchEventName($request));
try {
$response = $this->client->sendRequest($request);
+ $event->stop();
- $this->collectResponseInformations($response, $stack);
+ $this->collectResponseInformations($response, $event, $stack);
return $response;
} catch (\Exception $e) {
- $this->collectExceptionInformations($e, $stack);
+ $event->stop();
+ $this->collectExceptionInformations($e, $event, $stack);
throw $e;
}
@@ -115,32 +137,56 @@ private function collectRequestInformations(RequestInterface $request, Stack $st
/**
* @param ResponseInterface $response
+ * @param StopwatchEvent $event
* @param Stack|null $stack
*/
- private function collectResponseInformations(ResponseInterface $response, Stack $stack = null)
+ private function collectResponseInformations(ResponseInterface $response, StopwatchEvent $event, Stack $stack = null)
{
if (!$stack) {
return;
}
+ $stack->setDuration($event->getDuration());
$stack->setResponseCode($response->getStatusCode());
$stack->setClientResponse($this->formatter->formatResponse($response));
}
/**
- * @param \Exception $exception
- * @param Stack|null $stack
+ * @param \Exception $exception
+ * @param StopwatchEvent $event
+ * @param Stack|null $stack
*/
- private function collectExceptionInformations(\Exception $exception, Stack $stack = null)
+ private function collectExceptionInformations(\Exception $exception, StopwatchEvent $event, Stack $stack = null)
{
if ($exception instanceof HttpException) {
- $this->collectResponseInformations($exception->getResponse(), $stack);
+ $this->collectResponseInformations($exception->getResponse(), $event, $stack);
}
if (!$stack) {
return;
}
+ $stack->setDuration($event->getDuration());
$stack->setClientException($this->formatter->formatException($exception));
}
+
+ /**
+ * Generates the event name.
+ *
+ * @param RequestInterface $request
+ *
+ * @return string
+ */
+ private function getStopwatchEventName(RequestInterface $request)
+ {
+ $name = sprintf('%s %s', $request->getMethod(), $request->getUri());
+
+ if (isset($this->eventNames[$name])) {
+ $name .= sprintf(' [#%d]', ++$this->eventNames[$name]);
+ } else {
+ $this->eventNames[$name] = 1;
+ }
+
+ return $name;
+ }
}
diff --git a/Collector/ProfileClientFactory.php b/Collector/ProfileClientFactory.php
index 2fe4b2a3..613243ea 100644
--- a/Collector/ProfileClientFactory.php
+++ b/Collector/ProfileClientFactory.php
@@ -6,6 +6,7 @@
use Http\Client\HttpAsyncClient;
use Http\Client\HttpClient;
use Http\HttplugBundle\ClientFactory\ClientFactory;
+use Symfony\Component\Stopwatch\Stopwatch;
/**
* The ProfileClientFactory decorates any ClientFactory and returns the created client decorated by a ProfileClient.
@@ -31,12 +32,18 @@ class ProfileClientFactory implements ClientFactory
*/
private $formatter;
+ /**
+ * @var Stopwatch
+ */
+ private $stopwatch;
+
/**
* @param ClientFactory|callable $factory
* @param Collector $collector
* @param Formatter $formatter
+ * @param Stopwatch $stopwatch
*/
- public function __construct($factory, Collector $collector, Formatter $formatter)
+ public function __construct($factory, Collector $collector, Formatter $formatter, Stopwatch $stopwatch)
{
if (!$factory instanceof ClientFactory && !is_callable($factory)) {
throw new \RuntimeException(sprintf('First argument to ProfileClientFactory::__construct must be a "%s" or a callable.', ClientFactory::class));
@@ -44,6 +51,7 @@ public function __construct($factory, Collector $collector, Formatter $formatter
$this->factory = $factory;
$this->collector = $collector;
$this->formatter = $formatter;
+ $this->stopwatch = $stopwatch;
}
/**
@@ -57,6 +65,6 @@ public function createClient(array $config = [])
$client = new FlexibleHttpClient($client);
}
- return new ProfileClient($client, $this->collector, $this->formatter);
+ return new ProfileClient($client, $this->collector, $this->formatter, $this->stopwatch);
}
}
diff --git a/Collector/Stack.php b/Collector/Stack.php
index 5431525d..a73a165b 100644
--- a/Collector/Stack.php
+++ b/Collector/Stack.php
@@ -76,6 +76,11 @@ final class Stack
*/
private $responseCode;
+ /**
+ * @var int
+ */
+ private $duration = 0;
+
/**
* @param string $client
* @param string $request
@@ -277,4 +282,20 @@ public function setRequestScheme($requestScheme)
{
$this->requestScheme = $requestScheme;
}
+
+ /**
+ * @return int
+ */
+ public function getDuration()
+ {
+ return $this->duration;
+ }
+
+ /**
+ * @param int $duration
+ */
+ public function setDuration($duration)
+ {
+ $this->duration = $duration;
+ }
}
diff --git a/DependencyInjection/HttplugExtension.php b/DependencyInjection/HttplugExtension.php
index 76389ae9..3b56ea28 100644
--- a/DependencyInjection/HttplugExtension.php
+++ b/DependencyInjection/HttplugExtension.php
@@ -279,11 +279,6 @@ private function configureClient(ContainerBuilder $container, $clientName, array
$pluginClientOptions = [];
if ($profiling) {
- // Add the stopwatch plugin
- if (!in_array('httplug.plugin.stopwatch', $arguments['plugins'])) {
- array_unshift($plugins, 'httplug.plugin.stopwatch');
- }
-
//Decorate each plugin with a ProfilePlugin instance.
foreach ($plugins as $pluginServiceId) {
$this->decoratePluginWithProfilePlugin($container, $pluginServiceId);
@@ -561,6 +556,7 @@ private function configureAutoDiscoveryFactory(ContainerBuilder $container, $dis
$factory,
new Reference('httplug.collector.collector'),
new Reference('httplug.collector.formatter'),
+ new Reference('debug.stopwatch'),
]);
$factory = new Reference($factoryServiceId);
}
diff --git a/Resources/config/data-collector.xml b/Resources/config/data-collector.xml
index 348dce41..ddd6d146 100644
--- a/Resources/config/data-collector.xml
+++ b/Resources/config/data-collector.xml
@@ -30,31 +30,37 @@