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;
123 return self::$scopeStack;
131 if ( !defined(
'MW_PHPUNIT_TEST' ) ) {
132 throw new LogicException(
'Cannot reconfigure DeferredUpdates outside tests' );
134 self::$scopeStack = $scopeStack;
158 self::getScopeStack()->current()->addUpdate( $update, $stage );
159 self::tryOpportunisticExecute();
179 $stage = self::POSTSEND,
195 $type = get_class( $update )
197 $updateId = spl_object_id( $update );
198 $logger->debug(
"DeferredUpdates::run: started $type #{updateId}", [
'updateId' => $updateId ] );
200 $updateException =
null;
202 $startTime = microtime(
true );
204 self::attemptUpdate( $update );
205 }
catch ( Throwable $updateException ) {
206 MWExceptionHandler::logException( $updateException );
208 "Deferred update '{deferred_type}' failed to run.",
210 'deferred_type' => $type,
211 'exception' => $updateException,
214 self::getScopeStack()->onRunUpdateFailed( $update );
216 $walltime = microtime(
true ) - $startTime;
217 $logger->debug(
"DeferredUpdates::run: ended $type #{updateId}, processing time: {walltime}", [
218 'updateId' => $updateId,
219 'walltime' => $walltime,
224 if ( $updateException && $update instanceof EnqueueableDataUpdate ) {
226 self::getScopeStack()->queueDataUpdate( $update );
227 }
catch ( Throwable $jobException ) {
230 "Deferred update '{deferred_type}' failed to enqueue as a job.",
232 'deferred_type' => $type,
233 'exception' => $jobException,
236 self::getScopeStack()->onRunUpdateFailed( $update );
240 return $updateException;
261 public static function doUpdates( $stage = self::ALL ) {
267 $scope = self::getScopeStack()->current();
270 $activeUpdate = $scope->getActiveUpdate();
271 if ( $activeUpdate ) {
272 $class = get_class( $activeUpdate );
274 throw new LogicException(
275 __METHOD__ .
": reached from $class, which is not TransactionRoundAwareUpdate"
278 if ( $activeUpdate->getTransactionRoundRequirement() !== $activeUpdate::TRX_ROUND_ABSENT ) {
279 throw new LogicException(
280 __METHOD__ .
": reached from $class, which does not specify TRX_ROUND_ABSENT"
285 $scope->processUpdates(
287 static function (
DeferrableUpdate $update, $activeStage ) use ( &$guiError, &$exception ) {
288 $scopeStack = self::getScopeStack();
289 $childScope = $scopeStack->descend( $activeStage, $update );
292 $guiError = $guiError ?: ( $e instanceof
ErrorPageError ? $e : null );
293 $exception = $exception ?: $e;
299 $childScope->processUpdates(
303 $guiError = $guiError ?: ( $e instanceof
ErrorPageError ? $e : null );
304 $exception = $exception ?: $e;
308 $scopeStack->ascend();
315 if ( $exception && defined(
'MW_PHPUNIT_TEST' ) ) {
323 if ( $guiError && $stage === self::PRESEND && !headers_sent() ) {
367 if ( self::getRecursiveExecutionStackDepth()
368 || self::$preventOpportunisticUpdates
373 if ( self::getScopeStack()->allowOpportunisticUpdates() ) {
374 self::doUpdates( self::ALL );
388 self::$preventOpportunisticUpdates++;
389 return new ScopedCallback(
static function () {
390 self::$preventOpportunisticUpdates--;
404 return self::getScopeStack()->current()->pendingUpdatesCount();
420 return self::getScopeStack()->current()->getPendingUpdates( $stage );
432 self::getScopeStack()->current()->clearPendingUpdates();
442 return self::getScopeStack()->getRecursiveDepth();
458 self::getScopeStack()->onRunUpdateStart( $update );
462 self::getScopeStack()->onRunUpdateEnd( $update );
467class_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).