MediaWiki  master
Profiler.php
Go to the documentation of this file.
1 <?php
22 use Psr\Log\LoggerInterface;
24 use Wikimedia\ScopedCallback;
25 
36 abstract class Profiler {
38  protected $profileID = false;
40  protected $params = [];
42  protected $context = null;
44  protected $trxProfiler;
46  protected $logger;
48  private $allowOutput = false;
49 
51  private static $instance = null;
52 
56  public function __construct( array $params ) {
57  if ( isset( $params['profileID'] ) ) {
58  $this->profileID = $params['profileID'];
59  }
60  $this->params = $params;
61  $this->trxProfiler = new TransactionProfiler();
62  $this->logger = LoggerFactory::getInstance( 'profiler' );
63  }
64 
69  final public static function instance() {
70  if ( self::$instance === null ) {
71  global $wgProfiler;
72 
73  $params = ( $wgProfiler ?? [] ) + [
74  'class' => ProfilerStub::class,
75  'sampling' => 1,
76  'threshold' => 0.0,
77  'output' => [],
78  ];
79 
80  $inSample = mt_rand( 0, $params['sampling'] - 1 ) === 0;
81  // wfIsCLI() is not available yet
82  if ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' || !$inSample ) {
83  $params['class'] = ProfilerStub::class;
84  }
85 
86  // "Redundant attempt to cast $params['output'] of type array{} to array"
87  // Not correct, this could be a non-array if $wgProfiler sets it to a non-array.
88  // @phan-suppress-next-line PhanRedundantCondition
89  if ( !is_array( $params['output'] ) ) {
90  $params['output'] = [ $params['output'] ];
91  }
92 
93  self::$instance = new $params['class']( $params );
94  }
95  return self::$instance;
96  }
97 
105  final public static function replaceStubInstance( Profiler $profiler ) {
106  if ( self::$instance && !( self::$instance instanceof ProfilerStub ) ) {
107  throw new MWException( 'Could not replace non-stub profiler instance.' );
108  } else {
109  self::$instance = $profiler;
110  }
111  }
112 
116  public function setProfileID( $id ) {
117  $this->profileID = $id;
118  }
119 
123  public function getProfileID() {
124  if ( $this->profileID === false ) {
125  return WikiMap::getCurrentWikiDbDomain()->getId();
126  } else {
127  return $this->profileID;
128  }
129  }
130 
136  public function setContext( $context ) {
137  wfDeprecated( __METHOD__, '1.38' );
138  $this->context = $context;
139  }
140 
146  public function getContext() {
147  wfDeprecated( __METHOD__, '1.38' );
148  return $this->context ?? RequestContext::getMain();
149  }
150 
159  abstract public function scopedProfileIn( $section );
160 
164  public function scopedProfileOut( SectionProfileCallback &$section = null ) {
165  $section = null;
166  }
167 
172  public function getTransactionProfiler() {
173  return $this->trxProfiler;
174  }
175 
179  abstract public function close();
180 
188  private function getOutputs() {
189  $outputs = [];
190  foreach ( $this->params['output'] as $outputType ) {
191  // The class may be specified as either the full class name (for
192  // example, 'ProfilerOutputStats') or (for backward compatibility)
193  // the trailing portion of the class name (for example, 'stats').
194  $outputClass = strpos( $outputType, 'ProfilerOutput' ) === false
195  ? 'ProfilerOutput' . ucfirst( $outputType )
196  : $outputType;
197  if ( !class_exists( $outputClass ) ) {
198  throw new MWException( "'$outputType' is an invalid output type" );
199  }
200  $outputInstance = new $outputClass( $this, $this->params );
201  if ( $outputInstance->canUse() ) {
202  $outputs[] = $outputInstance;
203  }
204  }
205  return $outputs;
206  }
207 
213  public function logData() {
214  if ( $this->params['threshold'] > 0.0 ) {
215  // Note, this is also valid for CLI processes.
216  $timeElapsed = microtime( true ) - $_SERVER['REQUEST_TIME_FLOAT'];
217  if ( $timeElapsed <= $this->params['threshold'] ) {
218  return;
219  }
220  }
221 
222  $outputs = [];
223  foreach ( $this->getOutputs() as $output ) {
224  if ( !$output->logsToOutput() ) {
225  $outputs[] = $output;
226  }
227  }
228 
229  if ( $outputs ) {
230  $stats = $this->getFunctionStats();
231  foreach ( $outputs as $output ) {
232  $output->log( $stats );
233  }
234  }
235  }
236 
243  public function logDataPageOutputOnly() {
244  if ( !$this->allowOutput ) {
245  return;
246  }
247 
248  $outputs = [];
249  foreach ( $this->getOutputs() as $output ) {
250  if ( $output->logsToOutput() ) {
251  $outputs[] = $output;
252  }
253  }
254 
255  if ( $outputs ) {
256  $stats = $this->getFunctionStats();
257  foreach ( $outputs as $output ) {
258  $output->log( $stats );
259  }
260  }
261  }
262 
272  public function getContentType() {
273  if ( $this->allowOutput ) {
274  foreach ( headers_list() as $header ) {
275  if ( preg_match( '#^content-type: (\w+/\w+);?#i', $header, $m ) ) {
276  return $m[1];
277  }
278  }
279  }
280  return null;
281  }
282 
288  public function setAllowOutput() {
289  $this->allowOutput = true;
290  }
291 
298  public function getAllowOutput() {
299  return $this->allowOutput;
300  }
301 
328  abstract public function getFunctionStats();
329 
335  abstract public function getOutput();
336 }
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
MediaWiki exception.
Definition: MWException.php:29
PSR-3 logger instance factory.
Stub profiler that does nothing.
Profiler base class that defines the interface and some shared functionality.
Definition: Profiler.php:36
setAllowOutput()
Enable appending profiles to standard output.
Definition: Profiler.php:288
static replaceStubInstance(Profiler $profiler)
Replace the current profiler with $profiler if no non-stub profiler is set.
Definition: Profiler.php:105
setProfileID( $id)
Definition: Profiler.php:116
string bool $profileID
Profiler ID for bucketing data.
Definition: Profiler.php:38
static Profiler $instance
Definition: Profiler.php:51
getTransactionProfiler()
Definition: Profiler.php:172
setContext( $context)
Definition: Profiler.php:136
IContextSource $context
Current request context.
Definition: Profiler.php:42
getOutputs()
Get all usable outputs.
Definition: Profiler.php:188
close()
Close opened profiling sections.
logData()
Log the data to the backing store for all ProfilerOutput instances that have one.
Definition: Profiler.php:213
TransactionProfiler $trxProfiler
Definition: Profiler.php:44
__construct(array $params)
Definition: Profiler.php:56
logDataPageOutputOnly()
Log the data to the script/request output for all ProfilerOutput instances that do so.
Definition: Profiler.php:243
getOutput()
Returns a profiling output to be stored in debug file.
static instance()
Singleton.
Definition: Profiler.php:69
getContentType()
Get the Content-Type for deciding how to format appended profile output.
Definition: Profiler.php:272
getFunctionStats()
Get the aggregated inclusive profiling data for each method.
getProfileID()
Definition: Profiler.php:123
scopedProfileIn( $section)
Mark the start of a custom profiling frame (e.g.
getAllowOutput()
Whether appending profiles is allowed.
Definition: Profiler.php:298
scopedProfileOut(SectionProfileCallback &$section=null)
Definition: Profiler.php:164
bool $allowOutput
Definition: Profiler.php:48
LoggerInterface $logger
Definition: Profiler.php:46
array $params
All of the params passed from $wgProfiler.
Definition: Profiler.php:40
getContext()
Definition: Profiler.php:146
static getMain()
Get the RequestContext object associated with the main request.
Subclass ScopedCallback to avoid call_user_func_array(), which is slow.
static getCurrentWikiDbDomain()
Definition: WikiMap.php:293
Detect high-contention DB queries via profiling calls.
$wgProfiler
Config variable stub for the Profiler setting, for use by phpdoc and IDEs.
$header