PageRenderTime 57ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Profiler.php

https://gitlab.com/php.bjoernbartels.earth/profiler
PHP | 569 lines | 309 code | 39 blank | 221 comment | 11 complexity | 59d11199e18dd96bcd8be391bc1377c8 MD5 | raw file
  1. <?php
  2. /**
  3. * profiler main object
  4. */
  5. namespace Profiler;
  6. use Profiler\Exception;
  7. use Profiler\Checkpoint;
  8. use Profiler\Checkpoint\CheckpointInterface;
  9. use Profiler\Checkpoint\CheckpointAbstract;
  10. use Profiler\Checkpoint\Dummy;
  11. use Profiler\Writer\WriterInterface;
  12. /**
  13. * profiler main object
  14. *
  15. * @category php
  16. * @package Profiler
  17. * @author Björn Bartels <coding@bjoernbartels.earth>
  18. * @link https://gitlab.bjoernbartels.earth/groups/php
  19. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
  20. * @copyright copyright (c) 2007 Björn Bartels <coding@bjoernbartels.earth>
  21. */
  22. class Profiler
  23. {
  24. /**
  25. * Show memory usage in byte
  26. */
  27. const BYTE = 'byte';
  28. /**
  29. * Show memory usage in kilobyte
  30. */
  31. const KILOBYTE = 'kB';
  32. /**
  33. * Show memory usage in megabyte
  34. */
  35. const MEGABYTE = 'MB';
  36. /**
  37. * Show memory usage in gigabyte
  38. */
  39. const GIGABYTE = 'GB';
  40. /**
  41. * initial profiling starting checkpoint
  42. *
  43. * @access protected
  44. * @var CheckpointAbstract
  45. */
  46. protected $application = null;
  47. /**
  48. * internal dummy checkpoint
  49. *
  50. * @access protected
  51. * @var CheckpointAbstract
  52. */
  53. protected $dummy = null;
  54. /**
  55. * list of checkpoints to profile
  56. *
  57. * @access protected
  58. * @var array
  59. */
  60. protected $checkpoints = array();
  61. /**
  62. * time floating-point number precision
  63. *
  64. * @access protected
  65. * @var integer
  66. */
  67. protected $timeFloating = 6;
  68. /**
  69. * memory floating-point number precision
  70. *
  71. * @access protected
  72. * @var integer
  73. */
  74. protected $memoryFloating = 6;
  75. /**
  76. * internal depth count
  77. *
  78. * @access protected
  79. * @var integer
  80. */
  81. protected $depth = 0;
  82. /**
  83. * (internal) memory unit sign divisor
  84. *
  85. * @access protected
  86. * @var integer
  87. */
  88. protected $divisor = 1024;
  89. /**
  90. * memory unit sign
  91. *
  92. * @access protected
  93. * @var string
  94. */
  95. protected $divisorSign = self::MEGABYTE;
  96. /**
  97. * get php's 'real' memory usage
  98. *
  99. * @access protected
  100. * @var boolean
  101. */
  102. protected $useRealMemoryUsage = false;
  103. /**
  104. * is this profiler active?
  105. *
  106. * @access protected
  107. * @var boolean
  108. */
  109. protected $active = true;
  110. /**
  111. * writer instance
  112. *
  113. * @access protected
  114. * @var WriterInterface
  115. */
  116. protected $writer = null;
  117. /**
  118. * Profiler singleton object instance
  119. *
  120. * @static
  121. * @access protected
  122. * @var Profiler
  123. */
  124. protected static $instance = null;
  125. /**
  126. * create/get profiler instance
  127. *
  128. * @static
  129. * @access public
  130. * @param array|Zend_Config $options
  131. * @return Profiler
  132. */
  133. public static function getInstance($options = array())
  134. {
  135. if (self::$instance === null) {
  136. self::$instance = new self($options);
  137. }
  138. return self::$instance;
  139. }
  140. /**
  141. * class constructor
  142. *
  143. * @access protected
  144. * @param array $options
  145. * @throws Profiler\Exception
  146. */
  147. protected function __construct($options = array())
  148. {
  149. if (!is_array($options)) {
  150. throw new Exception('Invalid options format given.');
  151. }
  152. foreach ($options as $name => $value)
  153. {
  154. $methodName = 'set' . ucfirst($name);
  155. if (!method_exists($this, $methodName)) {
  156. throw new Exception(
  157. sprintf(
  158. 'Invalid or unknown option "%s".', $name
  159. )
  160. );
  161. }
  162. $this->$methodName($value);
  163. }
  164. if (!isset($options['active'])) {
  165. $this->setActive(true);
  166. }
  167. }
  168. /**
  169. * retrieve memory usage from system/php
  170. *
  171. * @access public
  172. * @return integer
  173. * @see http://php.net/manual/de/function.memory-get-usage.php
  174. */
  175. public static function getMemoryUsage()
  176. {
  177. return memory_get_usage(
  178. Profiler::getInstance()->getRealMemoryUsage()
  179. );
  180. }
  181. /**
  182. * return current Unix timestamp with microseconds
  183. *
  184. * @access public
  185. * @return double
  186. */
  187. public static function getMicrotime()
  188. {
  189. return microtime(true);
  190. }
  191. /**
  192. * set to use php's 'real' memory usage
  193. *
  194. * @access public
  195. * @param boolean $use
  196. * @return Profiler
  197. * @see http://php.net/manual/de/function.memory-get-usage.php
  198. */
  199. public function setRealMemoryUsage($use = true)
  200. {
  201. $this->useRealMemoryUsage = (bool)$use;
  202. return $this;
  203. }
  204. /**
  205. * use php's 'real' memory usage?
  206. *
  207. * @access public
  208. * @return boolean
  209. * @see http://php.net/manual/de/function.memory-get-usage.php
  210. */
  211. public function getRealMemoryUsage()
  212. {
  213. return $this->useRealMemoryUsage;
  214. }
  215. /**
  216. * set time and memory floating-point numbers precision
  217. *
  218. * @access public
  219. * @param integer $floating
  220. * @return Profiler
  221. */
  222. public function setFloating($floating = 6)
  223. {
  224. $this->timeFloating = $floating;
  225. $this->memoryFloating = $floating;
  226. return $this;
  227. }
  228. /**
  229. * set time floating-point numbers precision
  230. *
  231. * @access public
  232. * @param integer $floating
  233. * @return Profiler
  234. */
  235. public function setTimeFloating($floating = 6)
  236. {
  237. $this->timeFloating = $floating;
  238. return $this;
  239. }
  240. /**
  241. * get time floating-point numbers precision
  242. *
  243. * @access public
  244. * @return integer
  245. */
  246. public function getTimeFloating()
  247. {
  248. return $this->timeFloating;
  249. }
  250. /**
  251. * set memory floating-point numbers precision
  252. *
  253. * @access public
  254. * @param integer $floating
  255. * @return Profiler
  256. */
  257. public function setMemoryFloating($floating = 6)
  258. {
  259. $this->memoryFloating = $floating;
  260. return $this;
  261. }
  262. /**
  263. * get memory floating-point numbers precision
  264. *
  265. * @access public
  266. * @return integer
  267. */
  268. public function getMemoryFloating()
  269. {
  270. return $this->memoryFloating;
  271. }
  272. /**
  273. * set memory unit sign and internal divisor
  274. *
  275. * @access public
  276. * @param string $divisorSign
  277. * @throws Profiler\Exception
  278. * @return Profiler
  279. */
  280. public function setDivisorSign($divisorSign = self::MEGABYTE)
  281. {
  282. if ($divisorSign == self::BYTE) {
  283. $this->divisor = 1;
  284. } elseif ($divisorSign == self::KILOBYTE) {
  285. $this->divisor = 1024;
  286. } elseif ($divisorSign == self::MEGABYTE) {
  287. $this->divisor = 1048576;
  288. } elseif ($divisorSign == self::GIGABYTE) {
  289. $this->divisor = 1073741824;
  290. } else
  291. {
  292. throw new Exception(
  293. sprintf(
  294. 'Unknown divisor sign "%s".', $divisorSign
  295. )
  296. );
  297. }
  298. $this->divisorSign = $divisorSign;
  299. return $this;
  300. }
  301. /**
  302. * get internal divisor
  303. *
  304. * @access public
  305. * @return integer
  306. */
  307. public function getDivisor()
  308. {
  309. return $this->divisor;
  310. }
  311. /**
  312. * get memory unit sign
  313. *
  314. * @access public
  315. * @return string
  316. */
  317. public function getDivisorSign()
  318. {
  319. return $this->divisorSign;
  320. }
  321. /**
  322. * set if profiler is active
  323. *
  324. * @access public
  325. * @param boolean $active
  326. * @return Profiler
  327. */
  328. public function setActive($active = true)
  329. {
  330. $this->active = (bool)$active;
  331. return $this;
  332. }
  333. /**
  334. * get if profiler is active
  335. *
  336. * @access public
  337. * @return boolean
  338. */
  339. public function getActive()
  340. {
  341. return $this->active;
  342. }
  343. /**
  344. * is profiler active?
  345. *
  346. * @access public
  347. * @uses Profiler::getActive()
  348. * @return boolean
  349. */
  350. public function isActive()
  351. {
  352. return $this->getActive();
  353. }
  354. /**
  355. * set current profiling writer instance
  356. *
  357. * @access public
  358. * @param string|WriterInterface $writer
  359. * @return Profiler
  360. */
  361. public function setWriter($writer)
  362. {
  363. if (is_string($writer) && file_exists(__DIR__ . '/Profiler/Writer/' . $writer . '.php')) {
  364. $className = 'Profiler\\Writer\\' . $writer;
  365. $writer = new $className($this);
  366. }
  367. if (!$writer instanceof WriterInterface) {
  368. throw new Exception(
  369. 'Given writer must be an instance of ' .
  370. 'Profiler\\Writer\\WriterInterface.'
  371. );
  372. }
  373. $this->writer = $writer;
  374. return $this;
  375. }
  376. /**
  377. * start profiling new checkpoint, increase internal depth count
  378. *
  379. * @access public
  380. * @param string $title
  381. * @throws Profiler\Exception
  382. * @return CheckpointAbstract
  383. */
  384. public function start($title)
  385. {
  386. if (!$this->isActive()) {
  387. if ($this->dummy === null) {
  388. $this->dummy = new Dummy('');
  389. }
  390. return $this->dummy;
  391. }
  392. if (!count($this->checkpoints)) {
  393. $this->attach(
  394. $this->application = new Checkpoint(
  395. 'Application', $this->depth = 0
  396. )
  397. );
  398. }
  399. $this->attach(
  400. $checkpoint = new Checkpoint(
  401. $title, ++$this->depth
  402. )
  403. );
  404. return $checkpoint;
  405. }
  406. /**
  407. * stop profiling a given checkpoint
  408. *
  409. * @access public
  410. * @param CheckpointAbstract $checkpoint
  411. * @return Profiler
  412. */
  413. public function stop(CheckpointAbstract $checkpoint)
  414. {
  415. if ($this->isActive()) {
  416. $this->detach($checkpoint);
  417. }
  418. return $this;
  419. }
  420. /**
  421. * retrieve list of curretn checkpoints
  422. *
  423. * @access public
  424. * @return array
  425. */
  426. public function getCheckpoints()
  427. {
  428. return $this->checkpoints;
  429. }
  430. /**
  431. * empty the list of curretn checkpoints
  432. *
  433. * @access public
  434. * @return Profiler
  435. */
  436. public function clearCheckpoints()
  437. {
  438. $this->checkpoints = [];
  439. return $this;
  440. }
  441. /**
  442. * invoke writer and create output
  443. *
  444. * @access public
  445. * @throws Exception
  446. * @return mixed
  447. */
  448. public function write()
  449. {
  450. if (!$this->isActive()) {
  451. throw new Exception(
  452. sprintf(
  453. 'Cannot write profiling because profiler is not active.'
  454. )
  455. );
  456. }
  457. if (!$this->writer instanceof WriterInterface) {
  458. throw new Exception(
  459. sprintf(
  460. 'Cannot write profiling because no valid writer was set.'
  461. )
  462. );
  463. }
  464. $this->application->stop();
  465. foreach ($this->checkpoints as $checkpoint)
  466. {
  467. if (!$checkpoint->isActive()) {
  468. continue;
  469. }
  470. throw new Exception(
  471. sprintf(
  472. 'Found active checkpoint: "%s".', $checkpoint->title
  473. )
  474. );
  475. }
  476. return $this->writer->get();
  477. }
  478. /**
  479. * add new checkpoint to checkpoint-list
  480. *
  481. * @access protected
  482. * @param CheckpointInterface $checkpoint
  483. * @return Profiler
  484. */
  485. protected function attach(CheckpointInterface $checkpoint)
  486. {
  487. $this->checkpoints[] = $checkpoint;
  488. return $this;
  489. }
  490. /**
  491. * stops checkpoint, decrease internal depth count
  492. *
  493. * @access protected
  494. * @param CheckpointAbstract $checkpoint
  495. * @return Profiler
  496. */
  497. protected function detach(CheckpointAbstract $checkpoint)
  498. {
  499. --$this->depth;
  500. $checkpoint->stop(false);
  501. return $this;
  502. }
  503. /**
  504. * destroys current profiler instance
  505. *
  506. * @access public
  507. */
  508. public static function destroy()
  509. {
  510. self::$instance = null;
  511. }
  512. }