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;
244 public static function doUpdates( $stage = self::ALL ) {
250 $scope = self::getScopeStack()->current();
253 $activeUpdate = $scope->getActiveUpdate();
254 if ( $activeUpdate ) {
255 $class = get_class( $activeUpdate );
257 throw new LogicException(
258 __METHOD__ .
": reached from $class, which is not TransactionRoundAwareUpdate"
261 if ( $activeUpdate->getTransactionRoundRequirement() !== $activeUpdate::TRX_ROUND_ABSENT ) {
262 throw new LogicException(
263 __METHOD__ .
": reached from $class, which does not specify TRX_ROUND_ABSENT"
268 $scope->processUpdates(
270 static function (
DeferrableUpdate $update, $activeStage ) use ( &$guiError, &$exception ) {
271 $scopeStack = self::getScopeStack();
272 $childScope = $scopeStack->descend( $activeStage, $update );
274 $e = self::run( $update );
275 $guiError = $guiError ?: ( $e instanceof
ErrorPageError ? $e : null );
276 $exception = $exception ?: $e;
282 $childScope->processUpdates(
285 $e = self::run( $sub );
286 $guiError = $guiError ?: ( $e instanceof
ErrorPageError ? $e : null );
287 $exception = $exception ?: $e;
291 $scopeStack->ascend();
298 if ( $exception && defined(
'MW_PHPUNIT_TEST' ) ) {
306 if ( $guiError && $stage === self::PRESEND && !headers_sent() ) {
352 if ( self::getRecursiveExecutionStackDepth()
353 || self::$preventOpportunisticUpdates
358 if ( self::getScopeStack()->allowOpportunisticUpdates() ) {
359 self::doUpdates( self::ALL );
372 self::$preventOpportunisticUpdates++;
373 return new ScopedCallback(
static function () {
374 self::$preventOpportunisticUpdates--;
388 return self::getScopeStack()->current()->pendingUpdatesCount();
404 return self::getScopeStack()->current()->getPendingUpdates( $stage );
416 self::getScopeStack()->current()->clearPendingUpdates();
426 return self::getScopeStack()->getRecursiveDepth();
442 self::getScopeStack()->onRunUpdateStart( $update );
446 self::getScopeStack()->onRunUpdateEnd( $update );
451class_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.