/Lib/Timer.php

https://github.com/QACMS-Modules/QACMS-SeoTools · PHP · 230 lines · 119 code · 37 blank · 74 comment · 21 complexity · a22b73073312d36bbd1f73d822e7f2f7 MD5 · raw file

  1. <?php
  2. class Timer
  3. {
  4. // command constants
  5. const CMD_START = 'start';
  6. const CMD_STOP = 'end';
  7. // return format constants
  8. const SECONDS = 0;
  9. const MILLISECONDS = 1;
  10. const MICROSECONDS = 2;
  11. // number of microseconds in a second
  12. const USECDIV = 1000000;
  13. /**
  14. * Stores current state of the timer
  15. *
  16. * @var boolean
  17. */
  18. private $_running = false;
  19. /**
  20. * Contains the queue of times
  21. *
  22. * @var array
  23. */
  24. private $_queue = array();
  25. /**
  26. * Start the timer
  27. *
  28. * @return void
  29. */
  30. public function start()
  31. {
  32. // push current time
  33. $this->_pushTime(self::CMD_START);
  34. }
  35. /**
  36. * Stop the timer
  37. *
  38. * @return void
  39. */
  40. public function stop()
  41. {
  42. // push current time
  43. $this->_pushTime(self::CMD_STOP);
  44. }
  45. /**
  46. * Reset contents of the queue
  47. *
  48. * @return void
  49. */
  50. public function reset()
  51. {
  52. // reset the queue
  53. $this->_queue = array();
  54. }
  55. /**
  56. * Add a time entry to the queue
  57. *
  58. * @param string $cmd Command to push
  59. * @return void
  60. */
  61. private function _pushTime($cmd)
  62. {
  63. // capture the time as early in the function as possible
  64. $mt = microtime();
  65. // set current running state depending on the command
  66. if ($cmd == self::CMD_START) {
  67. // check if the timer has already been started
  68. if ($this->_running === true) {
  69. trigger_error('Timer has already been started', E_USER_NOTICE);
  70. return;
  71. }
  72. // set current state
  73. $this->_running = true;
  74. } else if ($cmd == self::CMD_STOP) {
  75. // check if the timer is already stopped
  76. if ($this->_running === false) {
  77. trigger_error('Timer has already been stopped/paused or has not yet been started', E_USER_NOTICE);
  78. return;
  79. }
  80. // set current state
  81. $this->_running = false;
  82. } else {
  83. // fail execution of the script
  84. trigger_error('Invalid command specified', E_USER_ERROR);
  85. return;
  86. }
  87. // recapture the time as close to the end of the function as possible
  88. if ($cmd === self::CMD_START) {
  89. $mt = microtime();
  90. }
  91. // split the time into components
  92. list($usec, $sec) = explode(' ', $mt);
  93. // typecast them to the required types
  94. $sec = (int) $sec;
  95. $usec = (float) $usec;
  96. $usec = (int) ($usec * self::USECDIV);
  97. // create the array
  98. $time = array(
  99. $cmd => array(
  100. 'sec' => $sec,
  101. 'usec' => $usec,
  102. ),
  103. );
  104. // add a time entry depending on the command
  105. if ($cmd == self::CMD_START) {
  106. array_push($this->_queue, $time);
  107. } else if ($cmd == self::CMD_STOP) {
  108. $count = count($this->_queue);
  109. $array =& $this->_queue[$count - 1];
  110. $array = array_merge($array, $time);
  111. }
  112. }
  113. /**
  114. * Get time of execution from all queue entries
  115. *
  116. * @param int $format Format of the returned data
  117. * @return int|float
  118. */
  119. public function get($format = self::SECONDS)
  120. {
  121. // stop timer if it is still running
  122. if ($this->_running === true) {
  123. trigger_error('Forcing timer to stop', E_USER_NOTICE);
  124. $this->stop();
  125. }
  126. // reset all values
  127. $sec = 0;
  128. $usec = 0;
  129. // loop through each time entry
  130. foreach ($this->_queue as $time) {
  131. // start and end times
  132. $start = $time[self::CMD_START];
  133. $end = $time[self::CMD_STOP];
  134. // calculate difference between start and end seconds
  135. $sec_diff = $end['sec'] - $start['sec'];
  136. // if starting and finishing seconds are the same
  137. if ($sec_diff === 0) {
  138. // only add the microseconds difference
  139. $usec += ($end['usec'] - $start['usec']);
  140. } else {
  141. // add the difference in seconds (compensate for microseconds)
  142. $sec += $sec_diff - 1;
  143. // add the difference time between start and end microseconds
  144. $usec += (self::USECDIV - $start['usec']) + $end['usec'];
  145. }
  146. }
  147. if ($usec > self::USECDIV) {
  148. // move the full second microseconds to the seconds' part
  149. $sec += (int) floor($usec / self::USECDIV);
  150. // keep only the microseconds that are over the self::USECDIV
  151. $usec = $usec % self::USECDIV;
  152. }
  153. switch ($format) {
  154. case self::MICROSECONDS:
  155. return ($sec * self::USECDIV) + $usec;
  156. case self::MILLISECONDS:
  157. return ($sec * 1000) + (int) round($usec / 1000, 0);
  158. case self::SECONDS:
  159. default:
  160. return (float) $sec + (float) ($usec / self::USECDIV);
  161. }
  162. }
  163. /**
  164. * Get the average time of execution from all queue entries
  165. *
  166. * @param int $format Format of the returned data
  167. * @return float
  168. */
  169. public static function getAverage($format = self::SECONDS)
  170. {
  171. $count = count($this->_queue);
  172. $sec = 0;
  173. $usec = $this->get(self::MICROSECONDS);
  174. if ($usec > self::USECDIV) {
  175. // move the full second microseconds to the seconds' part
  176. $sec += (int) floor($usec / self::USECDIV);
  177. // keep only the microseconds that are over the self::USECDIV
  178. $usec = $usec % self::USECDIV;
  179. }
  180. switch ($format) {
  181. case self::MICROSECONDS:
  182. $value = ($sec * self::USECDIV) + $usec;
  183. return round($value / $count, 2);
  184. case self::MILLISECONDS:
  185. $value = ($sec * 1000) + (int) round($usec / 1000, 0);
  186. return round($value / $count, 2);
  187. case self::SECONDS:
  188. default:
  189. $value = (float) $sec + (float) ($usec / self::USECDIV);
  190. return round($value / $count, 2);
  191. }
  192. }
  193. }