15use Wikimedia\ScopedCallback;
16use Wikimedia\Timestamp\ConvertibleTimestamp;
90 public const PRESEND = 1;
92 public const POSTSEND = 2;
95 public const STAGES = [ self::PRESEND, self::POSTSEND ];
98 private static $scopeStack;
103 private static $preventOpportunisticUpdates = 0;
107 return self::$scopeStack;
115 if ( !defined(
'MW_PHPUNIT_TEST' ) ) {
116 throw new LogicException(
'Cannot reconfigure DeferredUpdates outside tests' );
118 self::$scopeStack = $scopeStack;
142 self::getScopeStack()->current()->addUpdate( $update, $stage );
143 self::tryOpportunisticExecute();
163 $stage = self::POSTSEND,
179 $type = get_class( $update )
181 $updateId = spl_object_id( $update );
182 $logger->debug(
"DeferredUpdates::run: started $type #{updateId}", [
'updateId' => $updateId ] );
184 $updateException =
null;
186 $startTime = ConvertibleTimestamp::hrtime();
188 self::attemptUpdate( $update );
189 }
catch ( Throwable $updateException ) {
190 MWExceptionHandler::logException( $updateException );
192 "Deferred update '{deferred_type}' failed to run.",
194 'deferred_type' => $type,
195 'exception' => $updateException,
198 self::getScopeStack()->onRunUpdateFailed( $update );
200 $walltime = ( ConvertibleTimestamp::hrtime() - $startTime ) / 1e9;
201 $logger->debug(
"DeferredUpdates::run: ended $type #{updateId}, processing time: {walltime}", [
202 'updateId' => $updateId,
203 'walltime' => $walltime,
208 if ( $updateException && $update instanceof EnqueueableDataUpdate ) {
210 self::getScopeStack()->queueDataUpdate( $update );
211 }
catch ( Throwable $jobException ) {
212 MWExceptionHandler::logException( $jobException );
214 "Deferred update '{deferred_type}' failed to enqueue as a job.",
216 'deferred_type' => $type,
217 'exception' => $jobException,
220 self::getScopeStack()->onRunUpdateFailed( $update );
224 return $updateException;
246 public static function doUpdates( $stage = self::ALL ) {
249 '@phan-var ErrorPageError $guiError';
252 '@phan-var Throwable $exception';
254 $scope = self::getScopeStack()->current();
257 $activeUpdate = $scope->getActiveUpdate();
258 if ( $activeUpdate ) {
259 $class = get_class( $activeUpdate );
261 throw new LogicException(
262 __METHOD__ .
": reached from $class, which is not TransactionRoundAwareUpdate"
265 if ( $activeUpdate->getTransactionRoundRequirement() !== $activeUpdate::TRX_ROUND_ABSENT ) {
266 throw new LogicException(
267 __METHOD__ .
": reached from $class, which does not specify TRX_ROUND_ABSENT"
272 $scope->processUpdates(
274 static function (
DeferrableUpdate $update, $activeStage ) use ( &$guiError, &$exception ) {
275 $scopeStack = self::getScopeStack();
276 $childScope = $scopeStack->descend( $activeStage, $update );
278 $e = self::run( $update );
279 $guiError = $guiError ?: ( $e instanceof
ErrorPageError ? $e : null );
280 $exception = $exception ?: $e;
286 $childScope->processUpdates(
289 $e = self::run( $sub );
290 $guiError = $guiError ?: ( $e instanceof
ErrorPageError ? $e : null );
291 $exception = $exception ?: $e;
295 $scopeStack->ascend();
302 if ( $exception && defined(
'MW_PHPUNIT_TEST' ) ) {
311 if ( $guiError && $stage === self::PRESEND && !headers_sent() ) {
357 if ( self::getRecursiveExecutionStackDepth()
358 || self::$preventOpportunisticUpdates
363 if ( self::getScopeStack()->allowOpportunisticUpdates() ) {
364 self::doUpdates( self::ALL );
377 self::$preventOpportunisticUpdates++;
378 return new ScopedCallback(
static function () {
379 self::$preventOpportunisticUpdates--;
393 return self::getScopeStack()->current()->pendingUpdatesCount();
409 return self::getScopeStack()->current()->getPendingUpdates( $stage );
421 self::getScopeStack()->current()->clearPendingUpdates();
431 return self::getScopeStack()->getRecursiveDepth();
447 self::getScopeStack()->onRunUpdateStart( $update );
451 self::getScopeStack()->onRunUpdateEnd( $update );
456class_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.