1: <?php
2:
3: /*
4: * This file is part of the Symfony package.
5: *
6: * (c) Fabien Potencier <fabien@symfony.com>
7: *
8: * For the full copyright and license information, please view the LICENSE
9: * file that was distributed with this source code.
10: */
11:
12: namespace Symfony\Component\Stopwatch;
13:
14: /**
15: * Stopwatch provides a way to profile code.
16: *
17: * @author Fabien Potencier <fabien@symfony.com>
18: */
19: class Stopwatch
20: {
21: /**
22: * @var Section[]
23: */
24: private $sections;
25:
26: /**
27: * @var array
28: */
29: private $activeSections;
30:
31: public function __construct()
32: {
33: $this->sections = $this->activeSections = array('__root__' => new Section('__root__'));
34: }
35:
36: /**
37: * Creates a new section or re-opens an existing section.
38: *
39: * @param string|null $id The id of the session to re-open, null to create a new one
40: *
41: * @throws \LogicException When the section to re-open is not reachable
42: */
43: public function openSection($id = null)
44: {
45: $current = end($this->activeSections);
46:
47: if (null !== $id && null === $current->get($id)) {
48: throw new \LogicException(sprintf('The section "%s" has been started at an other level and can not be opened.', $id));
49: }
50:
51: $this->start('__section__.child', 'section');
52: $this->activeSections[] = $current->open($id);
53: $this->start('__section__');
54: }
55:
56: /**
57: * Stops the last started section.
58: *
59: * The id parameter is used to retrieve the events from this section.
60: *
61: * @see getSectionEvents
62: *
63: * @param string $id The identifier of the section
64: *
65: * @throws \LogicException When there's no started section to be stopped
66: */
67: public function stopSection($id)
68: {
69: $this->stop('__section__');
70:
71: if (1 == count($this->activeSections)) {
72: throw new \LogicException('There is no started section to stop.');
73: }
74:
75: $this->sections[$id] = array_pop($this->activeSections)->setId($id);
76: $this->stop('__section__.child');
77: }
78:
79: /**
80: * Starts an event.
81: *
82: * @param string $name The event name
83: * @param string $category The event category
84: *
85: * @return StopwatchEvent A StopwatchEvent instance
86: */
87: public function start($name, $category = null)
88: {
89: return end($this->activeSections)->startEvent($name, $category);
90: }
91:
92: /**
93: * Stops an event.
94: *
95: * @param string $name The event name
96: *
97: * @return StopwatchEvent A StopwatchEvent instance
98: */
99: public function stop($name)
100: {
101: return end($this->activeSections)->stopEvent($name);
102: }
103:
104: /**
105: * Stops then restarts an event.
106: *
107: * @param string $name The event name
108: *
109: * @return StopwatchEvent A StopwatchEvent instance
110: */
111: public function lap($name)
112: {
113: return end($this->activeSections)->stopEvent($name)->start();
114: }
115:
116: /**
117: * Gets all events for a given section.
118: *
119: * @param string $id A section identifier
120: *
121: * @return StopwatchEvent[] An array of StopwatchEvent instances
122: */
123: public function getSectionEvents($id)
124: {
125: return isset($this->sections[$id]) ? $this->sections[$id]->getEvents() : array();
126: }
127: }
128:
129:
130: /**
131: * @internal This class is for internal usage only
132: *
133: * @author Fabien Potencier <fabien@symfony.com>
134: */
135: class Section
136: {
137: /**
138: * @var StopwatchEvent[]
139: */
140: private $events = array();
141:
142: /**
143: * @var null|float
144: */
145: private $origin;
146:
147: /**
148: * @var string
149: */
150: private $id;
151:
152: /**
153: * @var Section[]
154: */
155: private $children = array();
156:
157: /**
158: * Constructor.
159: *
160: * @param float|null $origin Set the origin of the events in this section, use null to set their origin to their start time
161: */
162: public function __construct($origin = null)
163: {
164: $this->origin = is_numeric($origin) ? $origin : null;
165: }
166:
167: /**
168: * Returns the child section.
169: *
170: * @param string $id The child section identifier
171: *
172: * @return Section|null The child section or null when none found
173: */
174: public function get($id)
175: {
176: foreach ($this->children as $child) {
177: if ($id === $child->getId()) {
178: return $child;
179: }
180: }
181:
182: return null;
183: }
184:
185: /**
186: * Creates or re-opens a child section.
187: *
188: * @param string|null $id null to create a new section, the identifier to re-open an existing one.
189: *
190: * @return Section A child section
191: */
192: public function open($id)
193: {
194: if (null === $session = $this->get($id)) {
195: $session = $this->children[] = new self(microtime(true) * 1000);
196: }
197:
198: return $session;
199: }
200:
201: /**
202: * @return string The identifier of the section
203: */
204: public function getId()
205: {
206: return $this->id;
207: }
208:
209: /**
210: * Sets the session identifier.
211: *
212: * @param string $id The session identifier
213: *
214: * @return Section The current section
215: */
216: public function setId($id)
217: {
218: $this->id = $id;
219:
220: return $this;
221: }
222:
223: /**
224: * Starts an event.
225: *
226: * @param string $name The event name
227: * @param string $category The event category
228: *
229: * @return StopwatchEvent The event
230: */
231: public function startEvent($name, $category)
232: {
233: if (!isset($this->events[$name])) {
234: $this->events[$name] = new StopwatchEvent($this->origin ?: microtime(true) * 1000, $category);
235: }
236:
237: return $this->events[$name]->start();
238: }
239:
240: /**
241: * Stops an event.
242: *
243: * @param string $name The event name
244: *
245: * @return StopwatchEvent The event
246: *
247: * @throws \LogicException When the event has not been started
248: */
249: public function stopEvent($name)
250: {
251: if (!isset($this->events[$name])) {
252: throw new \LogicException(sprintf('Event "%s" is not started.', $name));
253: }
254:
255: return $this->events[$name]->stop();
256: }
257:
258: /**
259: * Stops then restarts an event.
260: *
261: * @param string $name The event name
262: *
263: * @return StopwatchEvent The event
264: *
265: * @throws \LogicException When the event has not been started
266: */
267: public function lap($name)
268: {
269: return $this->stopEvent($name)->start();
270: }
271:
272: /**
273: * Returns the events from this section.
274: *
275: * @return StopwatchEvent[] An array of StopwatchEvent instances
276: */
277: public function getEvents()
278: {
279: return $this->events;
280: }
281: }
282: