15use Wikimedia\ScopedCallback;
89 public const PRESEND = 1;
91 public const POSTSEND = 2;
94 public const STAGES = [ self::PRESEND, self::POSTSEND ];
97 private static $scopeStack;
102 private static $preventOpportunisticUpdates = 0;
106 return self::$scopeStack;
114 if ( !defined(
'MW_PHPUNIT_TEST' ) ) {
115 throw new LogicException(
'Cannot reconfigure DeferredUpdates outside tests' );
117 self::$scopeStack = $scopeStack;
141 self::getScopeStack()->current()->addUpdate( $update, $stage );
142 self::tryOpportunisticExecute();
162 $stage = self::POSTSEND,
178 $type = get_class( $update )
180 $updateId = spl_object_id( $update );
181 $logger->debug(
"DeferredUpdates::run: started $type #{updateId}", [
'updateId' => $updateId ] );
183 $updateException =
null;
185 $startTime = microtime(
true );
187 self::attemptUpdate( $update );
188 }
catch ( Throwable $updateException ) {
189 MWExceptionHandler::logException( $updateException );
191 "Deferred update '{deferred_type}' failed to run.",
193 'deferred_type' => $type,
194 'exception' => $updateException,
197 self::getScopeStack()->onRunUpdateFailed( $update );
199 $walltime = microtime(
true ) - $startTime;
200 $logger->debug(
"DeferredUpdates::run: ended $type #{updateId}, processing time: {walltime}", [
201 'updateId' => $updateId,
202 'walltime' => $walltime,
207 if ( $updateException && $update instanceof EnqueueableDataUpdate ) {
209 self::getScopeStack()->queueDataUpdate( $update );
210 }
catch ( Throwable $jobException ) {
211 MWExceptionHandler::logException( $jobException );
213 "Deferred update '{deferred_type}' failed to enqueue as a job.",
215 'deferred_type' => $type,
216 'exception' => $jobException,
219 self::getScopeStack()->onRunUpdateFailed( $update );
223 return $updateException;
245 public static function doUpdates( $stage = self::ALL ) {
248 '@phan-var ErrorPageError $guiError';
251 '@phan-var Throwable $exception';
253 $scope = self::getScopeStack()->current();
256 $activeUpdate = $scope->getActiveUpdate();
257 if ( $activeUpdate ) {
258 $class = get_class( $activeUpdate );
260 throw new LogicException(
261 __METHOD__ .
": reached from $class, which is not TransactionRoundAwareUpdate"
264 if ( $activeUpdate->getTransactionRoundRequirement() !== $activeUpdate::TRX_ROUND_ABSENT ) {
265 throw new LogicException(
266 __METHOD__ .
": reached from $class, which does not specify TRX_ROUND_ABSENT"
271 $scope->processUpdates(
273 static function (
DeferrableUpdate $update, $activeStage ) use ( &$guiError, &$exception ) {
274 $scopeStack = self::getScopeStack();
275 $childScope = $scopeStack->descend( $activeStage, $update );
277 $e = self::run( $update );
278 $guiError = $guiError ?: ( $e instanceof
ErrorPageError ? $e : null );
279 $exception = $exception ?: $e;
285 $childScope->processUpdates(
288 $e = self::run( $sub );
289 $guiError = $guiError ?: ( $e instanceof
ErrorPageError ? $e : null );
290 $exception = $exception ?: $e;
294 $scopeStack->ascend();
301 if ( $exception && defined(
'MW_PHPUNIT_TEST' ) ) {
310 if ( $guiError && $stage === self::PRESEND && !headers_sent() ) {
356 if ( self::getRecursiveExecutionStackDepth()
357 || self::$preventOpportunisticUpdates
362 if ( self::getScopeStack()->allowOpportunisticUpdates() ) {
363 self::doUpdates( self::ALL );
376 self::$preventOpportunisticUpdates++;
377 return new ScopedCallback(
static function () {
378 self::$preventOpportunisticUpdates--;
392 return self::getScopeStack()->current()->pendingUpdatesCount();
408 return self::getScopeStack()->current()->getPendingUpdates( $stage );
420 self::getScopeStack()->current()->clearPendingUpdates();
430 return self::getScopeStack()->getRecursiveDepth();
446 self::getScopeStack()->onRunUpdateStart( $update );
450 self::getScopeStack()->onRunUpdateEnd( $update );
455class_alias( DeferredUpdates::class,
'DeferredUpdates' );
wfGetCaller( $level=2)
Get the name of the function which called this function wfGetCaller( 1 ) is the function with the wfG...
if(!defined('MW_SETUP_CALLBACK'))
An error page which can definitely be safely rendered using the OutputPage.