Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[HttpKernel] added a Stopwatch

  • Loading branch information...
commit 106ebdbe184b0cd9ff8f3a12232cf242b0696f58 1 parent fbc422b
@fabpot fabpot authored
View
125 src/Symfony/Component/HttpKernel/Debug/Stopwatch.php
@@ -0,0 +1,125 @@
+<?php
+
+/*
+ * This file is part of the Symfony framework.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Symfony\Component\HttpKernel\Debug;
+
+/**
+ * Stopwatch provides a way to profile code.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Stopwatch
+{
+ private $waiting;
+ private $sections;
+ private $events;
+ private $origin;
+
+ /**
+ * Starts a new section.
+ */
+ public function startSection()
+ {
+ if ($this->events) {
+ $this->start('section.child', 'section');
+ $this->waiting[] = array($this->events, $this->origin);
+ $this->events = array();
+ }
+
+ $this->origin = microtime(true) * 1000;
+
+ $this->start('section');
+ }
+
+ /**
+ * Stops the last started section.
+ *
+ * @param string $id The identifier of the section
+ */
+ public function stopSection($id)
+ {
+ $event = $this->stop('section');
+
+ if ($id) {
+ $this->sections[$id] = $this->events;
+ }
+
+ if ($this->waiting) {
+ list($this->events, $this->origin) = array_pop($this->waiting);
+ $this->stop('section.child');
+ } else {
+ $this->origin = null;
+ $this->events = array();
+ }
+ }
+
+ /**
+ * Starts an event.
+ *
+ * @param string $name The event name
+ * @param string $category The event category
+ *
+ * @return StopwatchEvent A StopwatchEvent instance
+ */
+ public function start($name, $category = null)
+ {
+ if (!isset($this->events[$name])) {
+ $this->events[$name] = new StopwatchEvent($this->origin, $category);
+ }
+ $this->events[$name]->start();
+
+ return $this->events[$name];
+ }
+
+ /**
+ * Stops an event.
+ *
+ * @param string $name The event name
+ *
+ * @return StopwatchEvent A StopwatchEvent instance
+ */
+ public function stop($name)
+ {
+ if (!isset($this->events[$name])) {
+ throw new \LogicException(sprintf('Event "%s" is not started.', $name));
+ }
+
+ $this->events[$name]->stop();
+
+ return $this->events[$name];
+ }
+
+ /**
+ * Stops then restart an event.
+ *
+ * @param string $name The event name
+ *
+ * @return StopwatchEvent A StopwatchEvent instance
+ */
+ public function lap($name)
+ {
+ $this->stop($name);
+
+ return $this->start($name);
+ }
+
+ /**
+ * Gets all events for a given section.
+ *
+ * @param string $id A section identifier
+ *
+ * @return StopwatchEvent[] An array of StopwatchEvent instances
+ */
+ public function getSectionEvents($id)
+ {
+ return isset($this->sections[$id]) ? $this->sections[$id] : array();
+ }
+}
View
149 src/Symfony/Component/HttpKernel/Debug/StopwatchEvent.php
@@ -0,0 +1,149 @@
+<?php
+
+/*
+ * This file is part of the Symfony framework.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Symfony\Component\HttpKernel\Debug;
+
+/**
+ * Represents an Event managed by Stopwatch.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class StopwatchEvent
+{
+ private $periods;
+ private $start;
+ private $origin;
+ private $category;
+ private $started;
+
+ /**
+ * Constructor.
+ *
+ * @param integer $origin The origin time in milliseconds
+ * @param string $category The event category
+ */
+ public function __construct($origin, $category = null)
+ {
+ $this->origin = $origin;
+ $this->category = $category ?: 'default';
+ $this->started = array();
+ $this->periods = array();
+ }
+
+ /**
+ * Gets the category.
+ *
+ * @return string The category
+ */
+ public function getCategory()
+ {
+ return $this->category;
+ }
+
+ /**
+ * Gets the origin.
+ *
+ * @return integer The origin in milliseconds
+ */
+ public function getOrigin()
+ {
+ return $this->origin;
+ }
+
+ /**
+ * Starts a new event period.
+ */
+ public function start()
+ {
+ $this->started[] = $this->getNow();
+ }
+
+ /**
+ * Stops the last started event period.
+ */
+ public function stop()
+ {
+ if (!count($this->started)) {
+ throw new \LogicException('stop() called but start() has not been called before.');
+ }
+
+ $this->periods[] = array(array_pop($this->started), $this->getNow());
+ }
+
+ /**
+ * Stops the current period and then starts a new one.
+ */
+ public function lap()
+ {
+ $this->stop();
+ $this->start();
+ }
+
+ /**
+ * Stops all non already stopped periods.
+ */
+ public function ensureStopped()
+ {
+ while (count($this->started)) {
+ $this->stop();
+ }
+ }
+
+ /**
+ * Gets all event periods.
+ *
+ * @return array An array of periods
+ */
+ public function getPeriods()
+ {
+ return $this->periods;
+ }
+
+ /**
+ * Gets the relative time of the start of the first period.
+ *
+ * @return integer The time (in milliseconds)
+ */
+ public function getStartTime()
+ {
+ return isset($this->periods[0]) ? $this->periods[0][0] : 0;
+ }
+
+ /**
+ * Gets the relative time of the end of the last period.
+ *
+ * @return integer The time (in milliseconds)
+ */
+ public function getEndTime()
+ {
+ return count($this->periods) ? $this->periods[count($this->periods) - 1][1] : 0;
+ }
+
+ /**
+ * Gets the total time of all periods.
+ *
+ * @return integer The time (in milliseconds)
+ */
+ public function getTotalTime()
+ {
+ $total = 0;
+ foreach ($this->periods as $period) {
+ $total += $period[1] - $period[0];
+ }
+
+ return (int) sprintf('%d', $total);
+ }
+
+ private function getNow()
+ {
+ return (int) sprintf('%d', microtime(true) * 1000 - $this->origin);
+ }
+}
View
144 tests/Symfony/Tests/Component/HttpKernel/Debug/StopwatchEventTest.php
@@ -0,0 +1,144 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Tests\Component\HttpKernel\Debug;
+
+use Symfony\Component\HttpKernel\Debug\StopwatchEvent;
+
+/**
+ * StopwatchEventTest
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class StopwatchEventTest extends \PHPUnit_Framework_TestCase
+{
+ public function testGetOrigin()
+ {
+ $event = new StopwatchEvent(12);
+ $this->assertEquals(12, $event->getOrigin());
+ }
+
+ public function testGetCategory()
+ {
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $this->assertEquals('default', $event->getCategory());
+
+ $event = new StopwatchEvent(time(), 'cat');
+ $this->assertEquals('cat', $event->getCategory());
+ }
+
+ public function testGetPeriods()
+ {
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $this->assertEquals(array(), $event->getPeriods());
+
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $event->start();
+ $event->stop();
+ $this->assertCount(1, $event->getPeriods());
+
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $event->start();
+ $event->stop();
+ $event->start();
+ $event->stop();
+ $this->assertCount(2, $event->getPeriods());
+ }
+
+ public function testLap()
+ {
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $event->start();
+ $event->lap();
+ $event->stop();
+ $this->assertCount(2, $event->getPeriods());
+ }
+
+ public function testTotalTime()
+ {
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $event->start();
+ usleep(10000);
+ $event->stop();
+ $total = $event->getTotalTime();
+ $this->assertTrue($total >= 10 && $total <= 20);
+
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $event->start();
+ usleep(10000);
+ $event->stop();
+ $event->start();
+ usleep(10000);
+ $event->stop();
+ $total = $event->getTotalTime();
+ $this->assertTrue($total >= 20 && $total <= 30);
+ }
+
+ /**
+ * @expectedException \LogicException
+ */
+ public function testStopWithoutStart()
+ {
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $event->stop();
+ }
+
+ public function testEnsureStopped()
+ {
+ // this also test overlap between two periods
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $event->start();
+ usleep(10000);
+ $event->start();
+ usleep(10000);
+ $event->ensureStopped();
+ $total = $event->getTotalTime();
+ $this->assertTrue($total >= 30 && $total <= 40);
+ }
+
+ public function testStartTime()
+ {
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $this->assertEquals(0, $event->getStartTime());
+
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $event->start();
+ $event->stop();
+ $this->assertEquals(0, $event->getStartTime());
+
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $event->start();
+ usleep(10000);
+ $event->stop();
+ $start = $event->getStartTime();
+ $this->assertTrue($start >= 0 && $start <= 20);
+ }
+
+ public function testEndTime()
+ {
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $this->assertEquals(0, $event->getEndTime());
+
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $event->start();
+ $this->assertEquals(0, $event->getEndTime());
+
+ $event = new StopwatchEvent(microtime(true) * 1000);
+ $event->start();
+ usleep(10000);
+ $event->stop();
+ $event->start();
+ usleep(10000);
+ $event->stop();
+ $end = $event->getEndTime();
+ $this->assertTrue($end >= 20 && $end <= 30);
+ }
+}
View
87 tests/Symfony/Tests/Component/HttpKernel/Debug/StopwatchTest.php
@@ -0,0 +1,87 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Tests\Component\HttpKernel\Debug;
+
+use Symfony\Component\HttpKernel\Debug\Stopwatch;
+
+/**
+ * StopwatchTest
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class StopwatchTest extends \PHPUnit_Framework_TestCase
+{
+ public function testStart()
+ {
+ $stopwatch = new Stopwatch();
+ $event = $stopwatch->start('foo', 'cat');
+
+ $this->assertInstanceof('Symfony\Component\HttpKernel\Debug\StopwatchEvent', $event);
+ $this->assertEquals('cat', $event->getCategory());
+ }
+
+ public function testStop()
+ {
+ $stopwatch = new Stopwatch();
+ $stopwatch->start('foo', 'cat');
+ usleep(10000);
+ $event = $stopwatch->stop('foo');
+
+ $this->assertInstanceof('Symfony\Component\HttpKernel\Debug\StopwatchEvent', $event);
+ $total = $event->getTotalTime();
+ $this->assertTrue($total >= 10 && $total <= 20);
+ }
+
+ public function testLap()
+ {
+ $stopwatch = new Stopwatch();
+ $stopwatch->start('foo', 'cat');
+ usleep(10000);
+ $event = $stopwatch->lap('foo');
+ usleep(10000);
+ $stopwatch->stop('foo');
+
+ $this->assertInstanceof('Symfony\Component\HttpKernel\Debug\StopwatchEvent', $event);
+ $total = $event->getTotalTime();
+ $this->assertTrue($total >= 20 && $total <= 30);
+ }
+
+ /**
+ * @expectedException \LogicException
+ */
+ public function testStopWithoutStart()
+ {
+ $stopwatch = new Stopwatch();
+ $stopwatch->stop('foo');
+ }
+
+ public function testSection()
+ {
+ $stopwatch = new Stopwatch();
+
+ $stopwatch->startSection();
+ $stopwatch->start('foo', 'cat');
+ $stopwatch->stop('foo');
+ $stopwatch->start('bar', 'cat');
+ $stopwatch->stop('bar');
+ $stopwatch->stopSection(1);
+
+ $stopwatch->startSection();
+ $stopwatch->start('foobar', 'cat');
+ $stopwatch->stop('foobar');
+ $stopwatch->stopSection(2);
+
+ // the section is an event by itself
+ $this->assertCount(3, $stopwatch->getSectionEvents(1));
+ $this->assertCount(2, $stopwatch->getSectionEvents(2));
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.