[StopWatch] Provide a cleaner API
This commit is contained in:
parent
acd1287d02
commit
acdb325067
@ -51,7 +51,7 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
|
||||
{
|
||||
switch ($eventName) {
|
||||
case 'kernel.request':
|
||||
$this->stopwatch->startSection();
|
||||
$this->stopwatch->openSection();
|
||||
break;
|
||||
case 'kernel.view':
|
||||
case 'kernel.response':
|
||||
@ -62,7 +62,8 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
|
||||
}
|
||||
break;
|
||||
case 'kernel.terminate':
|
||||
$this->stopwatch->startSection();
|
||||
$token = $event->getResponse()->headers->get('X-Debug-Token');
|
||||
$this->stopwatch->openSection($token);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -82,8 +83,7 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
|
||||
$this->updateProfile($token);
|
||||
break;
|
||||
case 'kernel.terminate':
|
||||
$token = $event->getResponse()->headers->get('X-Debug-Token');
|
||||
$this->stopwatch->stopSection($token.'.terminate');
|
||||
$this->stopwatch->stopSection($token);
|
||||
$this->updateProfile($token);
|
||||
break;
|
||||
}
|
||||
@ -265,18 +265,7 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
|
||||
return;
|
||||
}
|
||||
|
||||
$events = $this->stopwatch->getSectionEvents($token);
|
||||
$origin = $events['__section__']->getOrigin();
|
||||
|
||||
foreach ($this->stopwatch->getSectionEvents($token.'.terminate') as $name => $event) {
|
||||
if (isset($events[$name])) {
|
||||
$events[$name]->merge($event);
|
||||
} else {
|
||||
$events[$name] = $event->setOrigin($origin);
|
||||
}
|
||||
}
|
||||
|
||||
$profile->getCollector('time')->setEvents($events);
|
||||
$profile->getCollector('time')->setEvents($this->stopwatch->getSectionEvents($token));
|
||||
$profiler->saveProfile($profile);
|
||||
|
||||
// children
|
||||
|
@ -18,24 +18,33 @@ namespace Symfony\Component\HttpKernel\Debug;
|
||||
*/
|
||||
class Stopwatch
|
||||
{
|
||||
private $waiting;
|
||||
private $sections;
|
||||
private $events;
|
||||
private $origin;
|
||||
private $activeSections;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->sections = $this->activeSections = array('__root__' => new Section('__root__'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new section.
|
||||
* Creates a new section or re-opens an existing section.
|
||||
*
|
||||
* @param string|null $id The id of the session to re-open, null to create a new one
|
||||
*
|
||||
* @throws \LogicException When the section to re-open is not reachable
|
||||
*/
|
||||
public function startSection()
|
||||
public function openSection($id = null)
|
||||
{
|
||||
if ($this->events) {
|
||||
$this->start('__section__.child', 'section');
|
||||
$this->waiting[] = array($this->events, $this->origin);
|
||||
$this->events = array();
|
||||
$current = end($this->activeSections);
|
||||
|
||||
if (null !== $id) {
|
||||
if (false === $current->get($id)) {
|
||||
throw new \LogicException(sprintf('The section "%s" has been started at an other level and can not be opened.', $id));
|
||||
}
|
||||
}
|
||||
|
||||
$this->origin = microtime(true) * 1000;
|
||||
|
||||
$this->start('__section__.child', 'section');
|
||||
$this->activeSections[] = $current->open($id);
|
||||
$this->start('__section__');
|
||||
}
|
||||
|
||||
@ -52,17 +61,12 @@ class Stopwatch
|
||||
{
|
||||
$this->stop('__section__');
|
||||
|
||||
if (null !== $id) {
|
||||
$this->sections[$id] = $this->events;
|
||||
if (1 == count($this->activeSections)) {
|
||||
throw new \LogicException('There is no started section to stop.');
|
||||
}
|
||||
|
||||
if ($this->waiting) {
|
||||
list($this->events, $this->origin) = array_pop($this->waiting);
|
||||
$this->stop('__section__.child');
|
||||
} else {
|
||||
$this->origin = null;
|
||||
$this->events = array();
|
||||
}
|
||||
$this->sections[$id] = array_pop($this->activeSections)->setId($id);
|
||||
$this->stop('__section__.child');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,6 +78,129 @@ class Stopwatch
|
||||
* @return StopwatchEvent A StopwatchEvent instance
|
||||
*/
|
||||
public function start($name, $category = null)
|
||||
{
|
||||
return end($this->activeSections)->startEvent($name, $category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return StopwatchEvent A StopwatchEvent instance
|
||||
*/
|
||||
public function stop($name)
|
||||
{
|
||||
return end($this->activeSections)->stopEvent($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops then restarts an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return Symfony\Component\HttpKernel\Debug\StopwatchEvent A StopwatchEvent instance
|
||||
*/
|
||||
public function lap($name)
|
||||
{
|
||||
return end($this->activeSections)->stopEvent($name)->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all events for a given section.
|
||||
*
|
||||
* @param string $id A section identifier
|
||||
*
|
||||
* @return Symfony\Component\HttpKernel\Debug\StopwatchEvent[] An array of StopwatchEvent instances
|
||||
*/
|
||||
public function getSectionEvents($id)
|
||||
{
|
||||
return isset($this->sections[$id]) ? $this->sections[$id]->getEvents() : array();
|
||||
}
|
||||
}
|
||||
|
||||
class Section
|
||||
{
|
||||
private $events = array();
|
||||
private $origin;
|
||||
private $id;
|
||||
private $children = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param float|null $origin Set the origin of the events in this section, use null to set their origin to their start time
|
||||
*/
|
||||
public function __construct($origin = null)
|
||||
{
|
||||
$this->origin = is_numeric($origin) ? $origin : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the child section.
|
||||
*
|
||||
* @param string $id The child section identifier
|
||||
*
|
||||
* @return Section|false The child section or false when none found
|
||||
*/
|
||||
public function get($id)
|
||||
{
|
||||
foreach ($this->children as $child) {
|
||||
if ($id === $child->getId()) {
|
||||
return $child;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or re-opens a child section.
|
||||
*
|
||||
* @param string|null $id null to create a new section, the identifier to re-open an existing one.
|
||||
*
|
||||
* @return Section A child section
|
||||
*/
|
||||
public function open($id)
|
||||
{
|
||||
if (false === $session = $this->get($id)) {
|
||||
$session = $this->children[] = new self(microtime(true) * 1000);
|
||||
}
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string The identifier of the section
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session identifier.
|
||||
*
|
||||
* @param string $id The session identifier
|
||||
*
|
||||
* @return Section The current section
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
* @param string $category The event category
|
||||
*
|
||||
* @return Symfony\Component\HttpKernel\Debug\StopwatchEvent The event
|
||||
*/
|
||||
public function startEvent($name, $category)
|
||||
{
|
||||
if (!isset($this->events[$name])) {
|
||||
$this->events[$name] = new StopwatchEvent($this->origin ?: microtime(true) * 1000, $category);
|
||||
@ -87,9 +214,11 @@ class Stopwatch
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return StopwatchEvent A StopwatchEvent instance
|
||||
* @return Symfony\Component\HttpKernel\Debug\StopwatchEvent The event
|
||||
*
|
||||
* @throws \LogicException When the event has not been started
|
||||
*/
|
||||
public function stop($name)
|
||||
public function stopEvent($name)
|
||||
{
|
||||
if (!isset($this->events[$name])) {
|
||||
throw new \LogicException(sprintf('Event "%s" is not started.', $name));
|
||||
@ -99,11 +228,13 @@ class Stopwatch
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops then restart an event.
|
||||
* Stops then restarts an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return StopwatchEvent A StopwatchEvent instance
|
||||
* @return Symfony\Component\HttpKernel\Debug\StopwatchEvent The event
|
||||
*
|
||||
* @throws \LogicException When the event has not been started
|
||||
*/
|
||||
public function lap($name)
|
||||
{
|
||||
@ -111,14 +242,12 @@ class Stopwatch
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all events for a given section.
|
||||
* Returns the events from this section.
|
||||
*
|
||||
* @param string $id A section identifier
|
||||
*
|
||||
* @return StopwatchEvent[] An array of StopwatchEvent instances
|
||||
* @return Symfony\Component\HttpKernel\Debug\StopwatchEvent[] An array of StopwatchEvent instances
|
||||
*/
|
||||
public function getSectionEvents($id)
|
||||
public function getEvents()
|
||||
{
|
||||
return isset($this->sections[$id]) ? $this->sections[$id] : array();
|
||||
return $this->events;
|
||||
}
|
||||
}
|
@ -59,47 +59,6 @@ class StopwatchEvent
|
||||
return $this->origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the origin.
|
||||
*
|
||||
* @param float $origin The origin time in milliseconds
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*
|
||||
* @throws \InvalidArgumentException When the raw time is not valid
|
||||
*/
|
||||
public function setOrigin($origin)
|
||||
{
|
||||
$origin = $this->formatTime($origin);
|
||||
$delta = $this->origin - $origin;
|
||||
$this->origin = $origin;
|
||||
foreach ($this->started as $i => $time) {
|
||||
$this->started[$i] = $this->formatTime($time + $delta);
|
||||
}
|
||||
foreach ($this->periods as $i => $period) {
|
||||
$this->periods[$i] = array(
|
||||
$this->formatTime($period[0] + $delta),
|
||||
$this->formatTime($period[1] + $delta)
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two events.
|
||||
*
|
||||
* @param StopWatchEvent $event The event to merge
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*/
|
||||
public function merge(StopWatchEvent $event)
|
||||
{
|
||||
$this->periods = array_merge($this->periods, $event->setOrigin($this->origin)->getPeriods());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new event period.
|
||||
*
|
||||
|
@ -149,59 +149,4 @@ class StopwatchEventTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
new StopwatchEvent("abc");
|
||||
}
|
||||
|
||||
public function testSetOrigin()
|
||||
{
|
||||
$event = $this
|
||||
->getMockBuilder('Symfony\\Component\\HttpKernel\\Debug\\StopwatchEvent')
|
||||
->setMethods(array('getNow'))
|
||||
->setConstructorArgs(array(0))
|
||||
->getMock()
|
||||
;
|
||||
|
||||
$event
|
||||
->expects($this->exactly(4))
|
||||
->method('getNow')
|
||||
->will($this->onConsecutiveCalls(10, 20, 30, 40))
|
||||
;
|
||||
|
||||
$this->assertEquals(
|
||||
array(array(0, 10), array(20, 40)),
|
||||
$event->start()->stop()->start()->setOrigin(10)->stop()->getPeriods()
|
||||
);
|
||||
}
|
||||
|
||||
public function testMerge()
|
||||
{
|
||||
$e1 = $this
|
||||
->getMockBuilder('Symfony\\Component\\HttpKernel\\Debug\\StopwatchEvent')
|
||||
->setMethods(array('getNow'))
|
||||
->setConstructorArgs(array(0))
|
||||
->getMock()
|
||||
;
|
||||
|
||||
$e1
|
||||
->expects($this->exactly(2))
|
||||
->method('getNow')
|
||||
->will($this->onConsecutiveCalls(0, 10))
|
||||
;
|
||||
|
||||
$e2 = $this
|
||||
->getMockBuilder('Symfony\\Component\\HttpKernel\\Debug\\StopwatchEvent')
|
||||
->setMethods(array('getNow'))
|
||||
->setConstructorArgs(array(10))
|
||||
->getMock()
|
||||
;
|
||||
|
||||
$e2
|
||||
->expects($this->exactly(2))
|
||||
->method('getNow')
|
||||
->will($this->onConsecutiveCalls(50, 60))
|
||||
;
|
||||
|
||||
$this->assertEquals(
|
||||
array(array(0, 10), array(60, 70)),
|
||||
$e1->start()->stop()->merge($e2->start()->stop())->getPeriods()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -68,19 +68,19 @@ class StopwatchTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$stopwatch = new Stopwatch();
|
||||
|
||||
$stopwatch->startSection();
|
||||
$stopwatch->openSection();
|
||||
$stopwatch->start('foo', 'cat');
|
||||
$stopwatch->stop('foo');
|
||||
$stopwatch->start('bar', 'cat');
|
||||
$stopwatch->stop('bar');
|
||||
$stopwatch->stopSection('1');
|
||||
|
||||
$stopwatch->startSection();
|
||||
$stopwatch->openSection();
|
||||
$stopwatch->start('foobar', 'cat');
|
||||
$stopwatch->stop('foobar');
|
||||
$stopwatch->stopSection('2');
|
||||
|
||||
$stopwatch->startSection();
|
||||
$stopwatch->openSection();
|
||||
$stopwatch->start('foobar', 'cat');
|
||||
$stopwatch->stop('foobar');
|
||||
$stopwatch->stopSection('0');
|
||||
@ -91,4 +91,31 @@ class StopwatchTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertCount(2, $stopwatch->getSectionEvents('2'));
|
||||
$this->assertCount(2, $stopwatch->getSectionEvents('0'));
|
||||
}
|
||||
|
||||
public function testReopenASection()
|
||||
{
|
||||
$stopwatch = new Stopwatch();
|
||||
|
||||
$stopwatch->openSection();
|
||||
$stopwatch->start('foo', 'cat');
|
||||
$stopwatch->stopSection('section');
|
||||
|
||||
$stopwatch->openSection('section');
|
||||
$stopwatch->start('bar', 'cat');
|
||||
$stopwatch->stopSection('section');
|
||||
|
||||
$events = $stopwatch->getSectionEvents('section');
|
||||
|
||||
$this->assertCount(3, $events);
|
||||
$this->assertCount(2, $events['__section__']->getPeriods());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testReopenANewSectionShouldThrowAnException()
|
||||
{
|
||||
$stopwatch = new Stopwatch();
|
||||
$stopwatch->openSection('section');
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user