1 <?php
  2 
  3 /**
  4  * Class to stop the script time.
  5  *
  6  * @author gharlan
  7  *
  8  * @package redaxo\core
  9  */
 10 class rex_timer
 11 {
 12     const SEC = 1;
 13     const MILLISEC = 1000;
 14     const MICROSEC = 1000000;
 15 
 16     public static $serverTimings = [];
 17 
 18     private $start;
 19     private $duration;
 20 
 21     /**
 22      * Constructor.
 23      *
 24      * @param float $start Start time
 25      */
 26     public function __construct($start = null)
 27     {
 28         if ($start) {
 29             $this->start = $start;
 30         } else {
 31             $this->reset();
 32         }
 33     }
 34 
 35     /**
 36      * Measures the runtime of the given callable.
 37      *
 38      * On sufficient user permissions - or in debug mode - this timings will be sent over the wire to the browser via server timing api http headers.
 39      *
 40      * @param string   $label
 41      * @param callable $callable
 42      *
 43      * @return mixed result of callable
 44      */
 45     public static function measure($label, callable $callable)
 46     {
 47         static $enabled = false;
 48 
 49         // we might get called very early in the process, in which case we can't determine yet whether the user is logged in.
 50         // this also means, in debug-mode we get more timings in comparison to admin-only timings.
 51         if (!$enabled) {
 52             // dont create the user (can cause session locks), to prevent influencing the things we try to measure.
 53             $enabled = rex::isDebugMode() || ($user = rex::getUser()) && $user->isAdmin();
 54         }
 55 
 56         if (!$enabled) {
 57             return $callable();
 58         }
 59 
 60         $timer = new self();
 61         $result = $callable();
 62         $timer->stop();
 63 
 64         $duration = isset(self::$serverTimings[$label]) ? self::$serverTimings[$label] : 0;
 65         $duration += $timer->getDelta(self::MILLISEC);
 66 
 67         self::$serverTimings[$label] = $duration;
 68 
 69         return $result;
 70     }
 71 
 72     /**
 73      * Resets the timer.
 74      */
 75     public function reset()
 76     {
 77         $this->start = microtime(true);
 78     }
 79 
 80     /**
 81      * Stops the timer.
 82      */
 83     public function stop()
 84     {
 85         $this->duration = microtime(true) - $this->start;
 86     }
 87 
 88     /**
 89      * Returns the time difference.
 90      *
 91      * @param int $precision Factor which will be multiplied, for convertion into different units (e.g. 1000 for milli,...)
 92      *
 93      * @return float Time difference
 94      */
 95     public function getDelta($precision = self::MILLISEC)
 96     {
 97         $duration = null === $this->duration ? microtime(true) - $this->start : $this->duration;
 98 
 99         return $duration * $precision;
100     }
101 
102     /**
103      * Returns the formatted time difference.
104      *
105      * @param int $precision Factor which will be multiplied, for convertion into different units (e.g. 1000 for milli,...)
106      * @param int $decimals  Number of decimals points
107      *
108      * @return string Formatted time difference
109      */
110     public function getFormattedDelta($precision = self::MILLISEC, $decimals = 3)
111     {
112         $time = $this->getDelta($precision);
113         return rex_formatter::number($time, [$decimals]);
114     }
115 }
116