29use Wikimedia\ScopedCallback;
101 public const ALL = 0;
103 public const PRESEND = 1;
105 public const POSTSEND = 2;
108 public const STAGES = [ self::PRESEND, self::POSTSEND ];
111 private static $scopeStack;
116 private static $preventOpportunisticUpdates = 0;
120 return self::$scopeStack;
128 if ( !defined(
'MW_PHPUNIT_TEST' ) ) {
129 throw new LogicException(
'Cannot reconfigure DeferredUpdates outside tests' );
131 self::$scopeStack = $scopeStack;
155 self::getScopeStack()->current()->addUpdate( $update, $stage );
156 self::tryOpportunisticExecute();
176 $stage = self::POSTSEND,
192 $type = get_class( $update )
194 $updateId = spl_object_id( $update );
195 $logger->debug(
"DeferredUpdates::run: started $type #{updateId}", [
'updateId' => $updateId ] );
197 $updateException =
null;
199 $startTime = microtime(
true );
201 self::attemptUpdate( $update );
202 }
catch ( Throwable $updateException ) {
203 MWExceptionHandler::logException( $updateException );
205 "Deferred update '{deferred_type}' failed to run.",
207 'deferred_type' => $type,
208 'exception' => $updateException,
211 self::getScopeStack()->onRunUpdateFailed( $update );
213 $walltime = microtime(
true ) - $startTime;
214 $logger->debug(
"DeferredUpdates::run: ended $type #{updateId}, processing time: {walltime}", [
215 'updateId' => $updateId,
216 'walltime' => $walltime,
221 if ( $updateException && $update instanceof EnqueueableDataUpdate ) {
223 self::getScopeStack()->queueDataUpdate( $update );
224 }
catch ( Throwable $jobException ) {
227 "Deferred update '{deferred_type}' failed to enqueue as a job.",
229 'deferred_type' => $type,
230 'exception' => $jobException,
233 self::getScopeStack()->onRunUpdateFailed( $update );
237 return $updateException;
258 public static function doUpdates( $stage = self::ALL ) {
264 $scope = self::getScopeStack()->current();
267 $activeUpdate = $scope->getActiveUpdate();
268 if ( $activeUpdate ) {
269 $class = get_class( $activeUpdate );
271 throw new LogicException(
272 __METHOD__ .
": reached from $class, which is not TransactionRoundAwareUpdate"
275 if ( $activeUpdate->getTransactionRoundRequirement() !== $activeUpdate::TRX_ROUND_ABSENT ) {
276 throw new LogicException(
277 __METHOD__ .
": reached from $class, which does not specify TRX_ROUND_ABSENT"
282 $scope->processUpdates(
284 static function (
DeferrableUpdate $update, $activeStage ) use ( &$guiError, &$exception ) {
285 $scopeStack = self::getScopeStack();
286 $childScope = $scopeStack->descend( $activeStage, $update );
289 $guiError = $guiError ?: ( $e instanceof
ErrorPageError ? $e : null );
290 $exception = $exception ?: $e;
296 $childScope->processUpdates(
300 $guiError = $guiError ?: ( $e instanceof
ErrorPageError ? $e : null );
301 $exception = $exception ?: $e;
305 $scopeStack->ascend();
312 if ( $exception && defined(
'MW_PHPUNIT_TEST' ) ) {
320 if ( $guiError && $stage === self::PRESEND && !headers_sent() ) {
366 if ( self::getRecursiveExecutionStackDepth()
367 || self::$preventOpportunisticUpdates
372 if ( self::getScopeStack()->allowOpportunisticUpdates() ) {
373 self::doUpdates( self::ALL );
387 self::$preventOpportunisticUpdates++;
388 return new ScopedCallback(
static function () {
389 self::$preventOpportunisticUpdates--;
403 return self::getScopeStack()->current()->pendingUpdatesCount();
419 return self::getScopeStack()->current()->getPendingUpdates( $stage );
431 self::getScopeStack()->current()->clearPendingUpdates();
441 return self::getScopeStack()->getRecursiveDepth();
457 self::getScopeStack()->onRunUpdateStart( $update );
461 self::getScopeStack()->onRunUpdateEnd( $update );
466class_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.
Handler class for MWExceptions.
static logException(Throwable $e, $catcher=self::CAUGHT_BY_OTHER, $extraData=[])
Log a throwable to the exception log (if enabled).