]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/libs/Timing.php
MediaWiki 1.30.2
[autoinstalls/mediawiki.git] / includes / libs / Timing.php
1 <?php
2 /**
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16  * http://www.gnu.org/copyleft/gpl.html
17  *
18  * @file
19  */
20
21 use Psr\Log\LoggerAwareInterface;
22 use Psr\Log\LoggerInterface;
23 use Psr\Log\NullLogger;
24
25 /**
26  * An interface to help developers measure the performance of their applications.
27  * This interface closely matches the W3C's User Timing specification.
28  * The key differences are:
29  *
30  * - The reference point for all measurements which do not explicitly specify
31  *   a start time is $_SERVER['REQUEST_TIME_FLOAT'], not navigationStart.
32  * - Successive calls to mark() and measure() with the same entry name cause
33  *   the previous entry to be overwritten. This ensures that there is a 1:1
34  *   mapping between names and entries.
35  * - Because there is a 1:1 mapping, instead of getEntriesByName(), we have
36  *   getEntryByName().
37  *
38  * The in-line documentation incorporates content from the User Timing Specification
39  * https://www.w3.org/TR/user-timing/
40  * Copyright © 2013 World Wide Web Consortium, (MIT, ERCIM, Keio, Beihang).
41  * https://www.w3.org/Consortium/Legal/2015/doc-license
42  *
43  * @since 1.27
44  */
45 class Timing implements LoggerAwareInterface {
46
47         /** @var array[] */
48         private $entries = [];
49
50         /** @var LoggerInterface */
51         protected $logger;
52
53         public function __construct( array $params = [] ) {
54                 $this->clearMarks();
55                 $this->setLogger( isset( $params['logger'] ) ? $params['logger'] : new NullLogger() );
56         }
57
58         /**
59          * Sets a logger instance on the object.
60          *
61          * @param LoggerInterface $logger
62          * @return null
63          */
64         public function setLogger( LoggerInterface $logger ) {
65                 $this->logger = $logger;
66         }
67
68         /**
69          * Store a timestamp with the associated name (a "mark")
70          *
71          * @param string $markName The name associated with the timestamp.
72          *  If there already exists an entry by that name, it is overwritten.
73          * @return array The mark that has been created.
74          */
75         public function mark( $markName ) {
76                 $this->entries[$markName] = [
77                         'name'      => $markName,
78                         'entryType' => 'mark',
79                         'startTime' => microtime( true ),
80                         'duration'  => 0,
81                 ];
82                 return $this->entries[$markName];
83         }
84
85         /**
86          * @param string $markName The name of the mark that should
87          *  be cleared. If not specified, all marks will be cleared.
88          */
89         public function clearMarks( $markName = null ) {
90                 if ( $markName !== null ) {
91                         unset( $this->entries[$markName] );
92                 } else {
93                         $this->entries = [
94                                 'requestStart' => [
95                                         'name'      => 'requestStart',
96                                         'entryType' => 'mark',
97                                         'startTime' => isset( $_SERVER['REQUEST_TIME_FLOAT'] )
98                                                 ? $_SERVER['REQUEST_TIME_FLOAT']
99                                                 : $_SERVER['REQUEST_TIME'],
100                                         'duration'  => 0,
101                                 ],
102                         ];
103                 }
104         }
105
106         /**
107          * This method stores the duration between two marks along with
108          * the associated name (a "measure").
109          *
110          * If neither the startMark nor the endMark argument is specified,
111          * measure() will store the duration from $_SERVER['REQUEST_TIME_FLOAT'] to
112          * the current time.
113          * If the startMark argument is specified, but the endMark argument is not
114          * specified, measure() will store the duration from the most recent
115          * occurrence of the start mark to the current time.
116          * If both the startMark and endMark arguments are specified, measure()
117          * will store the duration from the most recent occurrence of the start
118          * mark to the most recent occurrence of the end mark.
119          *
120          * @param string $measureName
121          * @param string $startMark
122          * @param string $endMark
123          * @return array|bool The measure that has been created, or false if either
124          *  the start mark or the end mark do not exist.
125          */
126         public function measure( $measureName, $startMark = 'requestStart', $endMark = null ) {
127                 $start = $this->getEntryByName( $startMark );
128                 if ( $start === null ) {
129                         $this->logger->error( __METHOD__ . ": The mark '$startMark' does not exist" );
130                         return false;
131                 }
132                 $startTime = $start['startTime'];
133
134                 if ( $endMark ) {
135                         $end = $this->getEntryByName( $endMark );
136                         if ( $end === null ) {
137                                 $this->logger->error( __METHOD__ . ": The mark '$endMark' does not exist" );
138                                 return false;
139                         }
140                         $endTime = $end['startTime'];
141                 } else {
142                         $endTime = microtime( true );
143                 }
144
145                 $this->entries[$measureName] = [
146                         'name'      => $measureName,
147                         'entryType' => 'measure',
148                         'startTime' => $startTime,
149                         'duration'  => $endTime - $startTime,
150                 ];
151
152                 return $this->entries[$measureName];
153         }
154
155         /**
156          * Sort entries in chronological order with respect to startTime.
157          */
158         private function sortEntries() {
159                 uasort( $this->entries, function ( $a, $b ) {
160                         return 10000 * ( $a['startTime'] - $b['startTime'] );
161                 } );
162         }
163
164         /**
165          * @return array[] All entries in chronological order.
166          */
167         public function getEntries() {
168                 $this->sortEntries();
169                 return $this->entries;
170         }
171
172         /**
173          * @param string $entryType
174          * @return array[] Entries (in chronological order) that have the same value
175          *  for the entryType attribute as the $entryType parameter.
176          */
177         public function getEntriesByType( $entryType ) {
178                 $this->sortEntries();
179                 $entries = [];
180                 foreach ( $this->entries as $entry ) {
181                         if ( $entry['entryType'] === $entryType ) {
182                                 $entries[] = $entry;
183                         }
184                 }
185                 return $entries;
186         }
187
188         /**
189          * @param string $name
190          * @return array|null Entry named $name or null if it does not exist.
191          */
192         public function getEntryByName( $name ) {
193                 return isset( $this->entries[$name] ) ? $this->entries[$name] : null;
194         }
195 }