wikimedia /
mediawiki
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * Arbitrary section name based PHP profiling. |
||
| 4 | * |
||
| 5 | * This program is free software; you can redistribute it and/or modify |
||
| 6 | * it under the terms of the GNU General Public License as published by |
||
| 7 | * the Free Software Foundation; either version 2 of the License, or |
||
| 8 | * (at your option) any later version. |
||
| 9 | * |
||
| 10 | * This program is distributed in the hope that it will be useful, |
||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 13 | * GNU General Public License for more details. |
||
| 14 | * |
||
| 15 | * You should have received a copy of the GNU General Public License along |
||
| 16 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
| 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
| 18 | * http://www.gnu.org/copyleft/gpl.html |
||
| 19 | * |
||
| 20 | * @file |
||
| 21 | * @ingroup Profiler |
||
| 22 | * @author Aaron Schulz |
||
| 23 | */ |
||
| 24 | use Wikimedia\ScopedCallback; |
||
|
0 ignored issues
–
show
|
|||
| 25 | |||
| 26 | /** |
||
| 27 | * Custom PHP profiler for parser/DB type section names that xhprof/xdebug can't handle |
||
| 28 | * |
||
| 29 | * @since 1.25 |
||
| 30 | */ |
||
| 31 | class SectionProfiler { |
||
| 32 | /** @var array Map of (mem,real,cpu) */ |
||
| 33 | protected $start; |
||
| 34 | /** @var array Map of (mem,real,cpu) */ |
||
| 35 | protected $end; |
||
| 36 | /** @var array List of resolved profile calls with start/end data */ |
||
| 37 | protected $stack = []; |
||
| 38 | /** @var array Queue of open profile calls with start data */ |
||
| 39 | protected $workStack = []; |
||
| 40 | |||
| 41 | /** @var array Map of (function name => aggregate data array) */ |
||
| 42 | protected $collated = []; |
||
| 43 | /** @var bool */ |
||
| 44 | protected $collateDone = false; |
||
| 45 | |||
| 46 | /** @var bool Whether to collect the full stack trace or just aggregates */ |
||
| 47 | protected $collateOnly = true; |
||
| 48 | /** @var array Cache of a standard broken collation entry */ |
||
| 49 | protected $errorEntry; |
||
| 50 | |||
| 51 | /** |
||
| 52 | * @param array $params |
||
| 53 | */ |
||
| 54 | public function __construct( array $params = [] ) { |
||
| 55 | $this->errorEntry = $this->getErrorEntry(); |
||
| 56 | $this->collateOnly = empty( $params['trace'] ); |
||
| 57 | } |
||
| 58 | |||
| 59 | /** |
||
| 60 | * @param string $section |
||
| 61 | * @return SectionProfileCallback |
||
| 62 | */ |
||
| 63 | public function scopedProfileIn( $section ) { |
||
| 64 | $this->profileInInternal( $section ); |
||
| 65 | |||
| 66 | return new SectionProfileCallback( $this, $section ); |
||
| 67 | } |
||
| 68 | |||
| 69 | /** |
||
| 70 | * @param ScopedCallback $section |
||
| 71 | */ |
||
| 72 | public function scopedProfileOut( ScopedCallback &$section ) { |
||
| 73 | $section = null; |
||
| 74 | } |
||
| 75 | |||
| 76 | /** |
||
| 77 | * Get the aggregated inclusive profiling data for each method |
||
| 78 | * |
||
| 79 | * The percent time for each time is based on the current "total" time |
||
| 80 | * used is based on all methods so far. This method can therefore be |
||
| 81 | * called several times in between several profiling calls without the |
||
| 82 | * delays in usage of the profiler skewing the results. A "-total" entry |
||
| 83 | * is always included in the results. |
||
| 84 | * |
||
| 85 | * @return array List of method entries arrays, each having: |
||
| 86 | * - name : method name |
||
| 87 | * - calls : the number of invoking calls |
||
| 88 | * - real : real time elapsed (ms) |
||
| 89 | * - %real : percent real time |
||
| 90 | * - cpu : real time elapsed (ms) |
||
| 91 | * - %cpu : percent real time |
||
| 92 | * - memory : memory used (bytes) |
||
| 93 | * - %memory : percent memory used |
||
| 94 | * - min_real : min real time in a call (ms) |
||
| 95 | * - max_real : max real time in a call (ms) |
||
| 96 | */ |
||
| 97 | public function getFunctionStats() { |
||
| 98 | $this->collateData(); |
||
| 99 | |||
| 100 | $totalCpu = max( $this->end['cpu'] - $this->start['cpu'], 0 ); |
||
| 101 | $totalReal = max( $this->end['real'] - $this->start['real'], 0 ); |
||
| 102 | $totalMem = max( $this->end['memory'] - $this->start['memory'], 0 ); |
||
| 103 | |||
| 104 | $profile = []; |
||
| 105 | foreach ( $this->collated as $fname => $data ) { |
||
| 106 | $profile[] = [ |
||
| 107 | 'name' => $fname, |
||
| 108 | 'calls' => $data['count'], |
||
| 109 | 'real' => $data['real'] * 1000, |
||
| 110 | '%real' => $totalReal ? 100 * $data['real'] / $totalReal : 0, |
||
| 111 | 'cpu' => $data['cpu'] * 1000, |
||
| 112 | '%cpu' => $totalCpu ? 100 * $data['cpu'] / $totalCpu : 0, |
||
| 113 | 'memory' => $data['memory'], |
||
| 114 | '%memory' => $totalMem ? 100 * $data['memory'] / $totalMem : 0, |
||
| 115 | 'min_real' => 1000 * $data['min_real'], |
||
| 116 | 'max_real' => 1000 * $data['max_real'] |
||
| 117 | ]; |
||
| 118 | } |
||
| 119 | |||
| 120 | $profile[] = [ |
||
| 121 | 'name' => '-total', |
||
| 122 | 'calls' => 1, |
||
| 123 | 'real' => 1000 * $totalReal, |
||
| 124 | '%real' => 100, |
||
| 125 | 'cpu' => 1000 * $totalCpu, |
||
| 126 | '%cpu' => 100, |
||
| 127 | 'memory' => $totalMem, |
||
| 128 | '%memory' => 100, |
||
| 129 | 'min_real' => 1000 * $totalReal, |
||
| 130 | 'max_real' => 1000 * $totalReal |
||
| 131 | ]; |
||
| 132 | |||
| 133 | return $profile; |
||
| 134 | } |
||
| 135 | |||
| 136 | /** |
||
| 137 | * Clear all of the profiling data for another run |
||
| 138 | */ |
||
| 139 | public function reset() { |
||
| 140 | $this->start = null; |
||
|
0 ignored issues
–
show
It seems like
null of type null is incompatible with the declared type array of property $start.
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. Loading history...
|
|||
| 141 | $this->end = null; |
||
|
0 ignored issues
–
show
It seems like
null of type null is incompatible with the declared type array of property $end.
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. Loading history...
|
|||
| 142 | $this->stack = []; |
||
| 143 | $this->workStack = []; |
||
| 144 | $this->collated = []; |
||
| 145 | $this->collateDone = false; |
||
| 146 | } |
||
| 147 | |||
| 148 | /** |
||
| 149 | * @return array Initial collation entry |
||
| 150 | */ |
||
| 151 | protected function getZeroEntry() { |
||
| 152 | return [ |
||
| 153 | 'cpu' => 0.0, |
||
| 154 | 'real' => 0.0, |
||
| 155 | 'memory' => 0, |
||
| 156 | 'count' => 0, |
||
| 157 | 'min_real' => 0.0, |
||
| 158 | 'max_real' => 0.0 |
||
| 159 | ]; |
||
| 160 | } |
||
| 161 | |||
| 162 | /** |
||
| 163 | * @return array Initial collation entry for errors |
||
| 164 | */ |
||
| 165 | protected function getErrorEntry() { |
||
| 166 | $entry = $this->getZeroEntry(); |
||
| 167 | $entry['count'] = 1; |
||
| 168 | return $entry; |
||
| 169 | } |
||
| 170 | |||
| 171 | /** |
||
| 172 | * Update the collation entry for a given method name |
||
| 173 | * |
||
| 174 | * @param string $name |
||
| 175 | * @param float $elapsedCpu |
||
| 176 | * @param float $elapsedReal |
||
| 177 | * @param int $memChange |
||
| 178 | */ |
||
| 179 | protected function updateEntry( $name, $elapsedCpu, $elapsedReal, $memChange ) { |
||
| 180 | $entry =& $this->collated[$name]; |
||
| 181 | if ( !is_array( $entry ) ) { |
||
| 182 | $entry = $this->getZeroEntry(); |
||
| 183 | $this->collated[$name] =& $entry; |
||
| 184 | } |
||
| 185 | $entry['cpu'] += $elapsedCpu; |
||
| 186 | $entry['real'] += $elapsedReal; |
||
| 187 | $entry['memory'] += $memChange > 0 ? $memChange : 0; |
||
| 188 | $entry['count']++; |
||
| 189 | $entry['min_real'] = min( $entry['min_real'], $elapsedReal ); |
||
| 190 | $entry['max_real'] = max( $entry['max_real'], $elapsedReal ); |
||
| 191 | } |
||
| 192 | |||
| 193 | /** |
||
| 194 | * This method should not be called outside SectionProfiler |
||
| 195 | * |
||
| 196 | * @param string $functionname |
||
| 197 | */ |
||
| 198 | public function profileInInternal( $functionname ) { |
||
| 199 | // Once the data is collated for reports, any future calls |
||
| 200 | // should clear the collation cache so the next report will |
||
| 201 | // reflect them. This matters when trace mode is used. |
||
| 202 | $this->collateDone = false; |
||
| 203 | |||
| 204 | $cpu = $this->getTime( 'cpu' ); |
||
| 205 | $real = $this->getTime( 'wall' ); |
||
| 206 | $memory = memory_get_usage(); |
||
| 207 | |||
| 208 | if ( $this->start === null ) { |
||
| 209 | $this->start = [ 'cpu' => $cpu, 'real' => $real, 'memory' => $memory ]; |
||
| 210 | } |
||
| 211 | |||
| 212 | $this->workStack[] = [ |
||
| 213 | $functionname, |
||
| 214 | count( $this->workStack ), |
||
| 215 | $real, |
||
| 216 | $cpu, |
||
| 217 | $memory |
||
| 218 | ]; |
||
| 219 | } |
||
| 220 | |||
| 221 | /** |
||
| 222 | * This method should not be called outside SectionProfiler |
||
| 223 | * |
||
| 224 | * @param string $functionname |
||
| 225 | */ |
||
| 226 | public function profileOutInternal( $functionname ) { |
||
| 227 | $item = array_pop( $this->workStack ); |
||
| 228 | if ( $item === null ) { |
||
| 229 | $this->debugGroup( 'profileerror', "Profiling error: $functionname" ); |
||
| 230 | return; |
||
| 231 | } |
||
| 232 | list( $ofname, /* $ocount */, $ortime, $octime, $omem ) = $item; |
||
| 233 | |||
| 234 | if ( $functionname === 'close' ) { |
||
| 235 | $message = "Profile section ended by close(): {$ofname}"; |
||
| 236 | $this->debugGroup( 'profileerror', $message ); |
||
| 237 | View Code Duplication | if ( $this->collateOnly ) { |
|
| 238 | $this->collated[$message] = $this->errorEntry; |
||
| 239 | } else { |
||
| 240 | $this->stack[] = [ $message, 0, 0.0, 0.0, 0, 0.0, 0.0, 0 ]; |
||
| 241 | } |
||
| 242 | $functionname = $ofname; |
||
| 243 | } elseif ( $ofname !== $functionname ) { |
||
| 244 | $message = "Profiling error: in({$ofname}), out($functionname)"; |
||
| 245 | $this->debugGroup( 'profileerror', $message ); |
||
| 246 | View Code Duplication | if ( $this->collateOnly ) { |
|
| 247 | $this->collated[$message] = $this->errorEntry; |
||
| 248 | } else { |
||
| 249 | $this->stack[] = [ $message, 0, 0.0, 0.0, 0, 0.0, 0.0, 0 ]; |
||
| 250 | } |
||
| 251 | } |
||
| 252 | |||
| 253 | $realTime = $this->getTime( 'wall' ); |
||
| 254 | $cpuTime = $this->getTime( 'cpu' ); |
||
| 255 | $memUsage = memory_get_usage(); |
||
| 256 | |||
| 257 | if ( $this->collateOnly ) { |
||
| 258 | $elapsedcpu = $cpuTime - $octime; |
||
| 259 | $elapsedreal = $realTime - $ortime; |
||
| 260 | $memchange = $memUsage - $omem; |
||
| 261 | $this->updateEntry( $functionname, $elapsedcpu, $elapsedreal, $memchange ); |
||
| 262 | } else { |
||
| 263 | $this->stack[] = array_merge( $item, [ $realTime, $cpuTime, $memUsage ] ); |
||
| 264 | } |
||
| 265 | |||
| 266 | $this->end = [ |
||
| 267 | 'cpu' => $cpuTime, |
||
| 268 | 'real' => $realTime, |
||
| 269 | 'memory' => $memUsage |
||
| 270 | ]; |
||
| 271 | } |
||
| 272 | |||
| 273 | /** |
||
| 274 | * Returns a tree of function calls with their real times |
||
| 275 | * @return string |
||
| 276 | * @throws Exception |
||
| 277 | */ |
||
| 278 | public function getCallTreeReport() { |
||
| 279 | if ( $this->collateOnly ) { |
||
| 280 | throw new Exception( "Tree is only available for trace profiling." ); |
||
| 281 | } |
||
| 282 | return implode( '', array_map( |
||
| 283 | [ $this, 'getCallTreeLine' ], $this->remapCallTree( $this->stack ) |
||
| 284 | ) ); |
||
| 285 | } |
||
| 286 | |||
| 287 | /** |
||
| 288 | * Recursive function the format the current profiling array into a tree |
||
| 289 | * |
||
| 290 | * @param array $stack Profiling array |
||
| 291 | * @return array |
||
| 292 | */ |
||
| 293 | protected function remapCallTree( array $stack ) { |
||
| 294 | if ( count( $stack ) < 2 ) { |
||
| 295 | return $stack; |
||
| 296 | } |
||
| 297 | $outputs = []; |
||
| 298 | for ( $max = count( $stack ) - 1; $max > 0; ) { |
||
| 299 | /* Find all items under this entry */ |
||
| 300 | $level = $stack[$max][1]; |
||
| 301 | $working = []; |
||
| 302 | for ( $i = $max -1; $i >= 0; $i-- ) { |
||
| 303 | if ( $stack[$i][1] > $level ) { |
||
| 304 | $working[] = $stack[$i]; |
||
| 305 | } else { |
||
| 306 | break; |
||
| 307 | } |
||
| 308 | } |
||
| 309 | $working = $this->remapCallTree( array_reverse( $working ) ); |
||
| 310 | $output = []; |
||
| 311 | foreach ( $working as $item ) { |
||
| 312 | array_push( $output, $item ); |
||
| 313 | } |
||
| 314 | array_unshift( $output, $stack[$max] ); |
||
| 315 | $max = $i; |
||
| 316 | |||
| 317 | array_unshift( $outputs, $output ); |
||
| 318 | } |
||
| 319 | $final = []; |
||
| 320 | foreach ( $outputs as $output ) { |
||
| 321 | foreach ( $output as $item ) { |
||
| 322 | $final[] = $item; |
||
| 323 | } |
||
| 324 | } |
||
| 325 | return $final; |
||
| 326 | } |
||
| 327 | |||
| 328 | /** |
||
| 329 | * Callback to get a formatted line for the call tree |
||
| 330 | * @param array $entry |
||
| 331 | * @return string |
||
| 332 | */ |
||
| 333 | protected function getCallTreeLine( $entry ) { |
||
| 334 | // $entry has (name, level, stime, scpu, smem, etime, ecpu, emem) |
||
| 335 | list( $fname, $level, $startreal, , , $endreal ) = $entry; |
||
| 336 | $delta = $endreal - $startreal; |
||
| 337 | $space = str_repeat( ' ', $level ); |
||
| 338 | # The ugly double sprintf is to work around a PHP bug, |
||
| 339 | # which has been fixed in recent releases. |
||
| 340 | return sprintf( "%10s %s %s\n", |
||
| 341 | trim( sprintf( "%7.3f", $delta * 1000.0 ) ), $space, $fname ); |
||
| 342 | } |
||
| 343 | |||
| 344 | /** |
||
| 345 | * Populate collated data |
||
| 346 | */ |
||
| 347 | protected function collateData() { |
||
| 348 | if ( $this->collateDone ) { |
||
| 349 | return; |
||
| 350 | } |
||
| 351 | $this->collateDone = true; |
||
| 352 | // Close opened profiling sections |
||
| 353 | while ( count( $this->workStack ) ) { |
||
| 354 | $this->profileOutInternal( 'close' ); |
||
| 355 | } |
||
| 356 | |||
| 357 | if ( $this->collateOnly ) { |
||
| 358 | return; // already collated as methods exited |
||
| 359 | } |
||
| 360 | |||
| 361 | $this->collated = []; |
||
| 362 | |||
| 363 | # Estimate profiling overhead |
||
| 364 | $oldEnd = $this->end; |
||
| 365 | $profileCount = count( $this->stack ); |
||
| 366 | $this->calculateOverhead( $profileCount ); |
||
| 367 | |||
| 368 | # First, subtract the overhead! |
||
| 369 | $overheadTotal = $overheadMemory = $overheadInternal = []; |
||
| 370 | foreach ( $this->stack as $entry ) { |
||
| 371 | // $entry is (name,pos,rtime0,cputime0,mem0,rtime1,cputime1,mem1) |
||
| 372 | $fname = $entry[0]; |
||
| 373 | $elapsed = $entry[5] - $entry[2]; |
||
| 374 | $memchange = $entry[7] - $entry[4]; |
||
| 375 | |||
| 376 | if ( $fname === '-overhead-total' ) { |
||
| 377 | $overheadTotal[] = $elapsed; |
||
| 378 | $overheadMemory[] = max( 0, $memchange ); |
||
| 379 | } elseif ( $fname === '-overhead-internal' ) { |
||
| 380 | $overheadInternal[] = $elapsed; |
||
| 381 | } |
||
| 382 | } |
||
| 383 | $overheadTotal = $overheadTotal ? |
||
| 384 | array_sum( $overheadTotal ) / count( $overheadInternal ) : 0; |
||
| 385 | $overheadMemory = $overheadMemory ? |
||
| 386 | array_sum( $overheadMemory ) / count( $overheadInternal ) : 0; |
||
| 387 | $overheadInternal = $overheadInternal ? |
||
| 388 | array_sum( $overheadInternal ) / count( $overheadInternal ) : 0; |
||
| 389 | |||
| 390 | # Collate |
||
| 391 | foreach ( $this->stack as $index => $entry ) { |
||
| 392 | // $entry is (name,pos,rtime0,cputime0,mem0,rtime1,cputime1,mem1) |
||
| 393 | $fname = $entry[0]; |
||
| 394 | $elapsedCpu = $entry[6] - $entry[3]; |
||
| 395 | $elapsedReal = $entry[5] - $entry[2]; |
||
| 396 | $memchange = $entry[7] - $entry[4]; |
||
| 397 | $subcalls = $this->calltreeCount( $this->stack, $index ); |
||
| 398 | |||
| 399 | if ( substr( $fname, 0, 9 ) !== '-overhead' ) { |
||
| 400 | # Adjust for profiling overhead (except special values with elapsed=0) |
||
| 401 | if ( $elapsed ) { |
||
| 402 | $elapsed -= $overheadInternal; |
||
|
0 ignored issues
–
show
The variable
$elapsed does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
Loading history...
|
|||
| 403 | $elapsed -= ( $subcalls * $overheadTotal ); |
||
| 404 | $memchange -= ( $subcalls * $overheadMemory ); |
||
| 405 | } |
||
| 406 | } |
||
| 407 | |||
| 408 | $this->updateEntry( $fname, $elapsedCpu, $elapsedReal, $memchange ); |
||
| 409 | } |
||
| 410 | |||
| 411 | $this->collated['-overhead-total']['count'] = $profileCount; |
||
| 412 | arsort( $this->collated, SORT_NUMERIC ); |
||
| 413 | |||
| 414 | // Unclobber the end info map (the overhead checking alters it) |
||
| 415 | $this->end = $oldEnd; |
||
| 416 | } |
||
| 417 | |||
| 418 | /** |
||
| 419 | * Dummy calls to calculate profiling overhead |
||
| 420 | * |
||
| 421 | * @param int $profileCount |
||
| 422 | */ |
||
| 423 | protected function calculateOverhead( $profileCount ) { |
||
| 424 | $this->profileInInternal( '-overhead-total' ); |
||
| 425 | for ( $i = 0; $i < $profileCount; $i++ ) { |
||
| 426 | $this->profileInInternal( '-overhead-internal' ); |
||
| 427 | $this->profileOutInternal( '-overhead-internal' ); |
||
| 428 | } |
||
| 429 | $this->profileOutInternal( '-overhead-total' ); |
||
| 430 | } |
||
| 431 | |||
| 432 | /** |
||
| 433 | * Counts the number of profiled function calls sitting under |
||
| 434 | * the given point in the call graph. Not the most efficient algo. |
||
| 435 | * |
||
| 436 | * @param array $stack |
||
| 437 | * @param int $start |
||
| 438 | * @return int |
||
| 439 | */ |
||
| 440 | protected function calltreeCount( $stack, $start ) { |
||
| 441 | $level = $stack[$start][1]; |
||
| 442 | $count = 0; |
||
| 443 | for ( $i = $start -1; $i >= 0 && $stack[$i][1] > $level; $i-- ) { |
||
| 444 | $count ++; |
||
| 445 | } |
||
| 446 | return $count; |
||
| 447 | } |
||
| 448 | |||
| 449 | /** |
||
| 450 | * Get the initial time of the request, based on getrusage() |
||
| 451 | * |
||
| 452 | * @param string|bool $metric Metric to use, with the following possibilities: |
||
| 453 | * - user: User CPU time (without system calls) |
||
| 454 | * - cpu: Total CPU time (user and system calls) |
||
| 455 | * - wall (or any other string): elapsed time |
||
| 456 | * - false (default): will fall back to default metric |
||
| 457 | * @return float |
||
| 458 | */ |
||
| 459 | protected function getTime( $metric = 'wall' ) { |
||
| 460 | if ( $metric === 'cpu' || $metric === 'user' ) { |
||
| 461 | $ru = wfGetRusage(); |
||
| 462 | if ( !$ru ) { |
||
| 463 | return 0; |
||
| 464 | } |
||
| 465 | $time = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6; |
||
| 466 | if ( $metric === 'cpu' ) { |
||
| 467 | # This is the time of system calls, added to the user time |
||
| 468 | # it gives the total CPU time |
||
| 469 | $time += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6; |
||
| 470 | } |
||
| 471 | return $time; |
||
| 472 | } else { |
||
| 473 | return microtime( true ); |
||
| 474 | } |
||
| 475 | } |
||
| 476 | |||
| 477 | /** |
||
| 478 | * Add an entry in the debug log file |
||
| 479 | * |
||
| 480 | * @param string $s String to output |
||
| 481 | */ |
||
| 482 | protected function debug( $s ) { |
||
| 483 | if ( function_exists( 'wfDebug' ) ) { |
||
| 484 | wfDebug( $s ); |
||
| 485 | } |
||
| 486 | } |
||
| 487 | |||
| 488 | /** |
||
| 489 | * Add an entry in the debug log group |
||
| 490 | * |
||
| 491 | * @param string $group Group to send the message to |
||
| 492 | * @param string $s String to output |
||
| 493 | */ |
||
| 494 | protected function debugGroup( $group, $s ) { |
||
| 495 | if ( function_exists( 'wfDebugLog' ) ) { |
||
| 496 | wfDebugLog( $group, $s ); |
||
| 497 | } |
||
| 498 | } |
||
| 499 | } |
||
| 500 | |||
| 501 | /** |
||
| 502 | * Subclass ScopedCallback to avoid call_user_func_array(), which is slow |
||
| 503 | * |
||
| 504 | * This class should not be used outside of SectionProfiler |
||
| 505 | */ |
||
| 506 | class SectionProfileCallback extends ScopedCallback { |
||
| 507 | /** @var SectionProfiler */ |
||
| 508 | protected $profiler; |
||
| 509 | /** @var string */ |
||
| 510 | protected $section; |
||
| 511 | |||
| 512 | /** |
||
| 513 | * @param SectionProfiler $profiler |
||
| 514 | * @param string $section |
||
| 515 | */ |
||
| 516 | public function __construct( SectionProfiler $profiler, $section ) { |
||
| 517 | parent::__construct( null ); |
||
| 518 | $this->profiler = $profiler; |
||
| 519 | $this->section = $section; |
||
| 520 | } |
||
| 521 | |||
| 522 | function __destruct() { |
||
| 523 | $this->profiler->profileOutInternal( $this->section ); |
||
| 524 | } |
||
| 525 | } |
||
| 526 |
Let’s assume that you have a directory layout like this:
. |-- OtherDir | |-- Bar.php | `-- Foo.php `-- SomeDir `-- Foo.phpand let’s assume the following content of
Bar.php:If both files
OtherDir/Foo.phpandSomeDir/Foo.phpare loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.phpHowever, as
OtherDir/Foo.phpdoes not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: