MediaWiki master
Job.php
Go to the documentation of this file.
1<?php
26
38abstract class Job implements RunnableJob {
40 public $command;
41
43 public $params;
44
46 public $metadata = [];
47
49 protected $title;
50
52 protected $removeDuplicates = false;
53
55 protected $error;
56
58 protected $teardownCallbacks = [];
59
61 protected $executionFlags = 0;
62
72 public static function factory( $command, $params = [] ) {
73 $factory = MediaWikiServices::getInstance()->getJobFactory();
74
75 // FIXME: fix handling for legacy signature!
76 // @phan-suppress-next-line PhanParamTooFewUnpack one argument is known to be present.
77 return $factory->newJob( ...func_get_args() );
78 }
79
86 public function __construct( $command, $params = null ) {
87 if ( $params instanceof PageReference ) {
88 // Backwards compatibility for old signature ($command, $title, $params)
89 $page = $params;
90 $params = func_num_args() >= 3 ? func_get_arg( 2 ) : [];
91 } else {
92 // Newer jobs may choose to not have a top-level title (e.g. GenericParameterJob)
93 $page = null;
94 }
95
96 if ( !is_array( $params ) ) {
97 throw new InvalidArgumentException( '$params must be an array' );
98 }
99
100 if (
101 $page &&
102 !isset( $params['namespace'] ) &&
103 !isset( $params['title'] )
104 ) {
105 // When constructing this class for submitting to the queue,
106 // normalise the $page arg of old job classes as part of $params.
107 $params['namespace'] = $page->getNamespace();
108 $params['title'] = $page->getDBkey();
109 }
110
111 $this->command = $command;
112 $this->params = $params + [
113 'requestId' => Telemetry::getInstance()->getRequestId(),
114 ];
115
116 if ( $this->title === null ) {
117 // Set this field for access via getTitle().
118 $this->title = ( isset( $params['namespace'] ) && isset( $params['title'] ) )
119 ? Title::makeTitle( $params['namespace'], $params['title'] )
120 // GenericParameterJob classes without namespace/title params
121 // should not use getTitle(). Set an invalid title as placeholder.
122 : Title::makeTitle( NS_SPECIAL, '' );
123 }
124 }
125
130 public function hasExecutionFlag( $flag ) {
131 return ( $this->executionFlags & $flag ) === $flag;
132 }
133
138 public function getType() {
139 return $this->command;
140 }
141
145 final public function getTitle() {
146 return $this->title;
147 }
148
153 public function getParams() {
154 return $this->params;
155 }
156
163 public function getMetadata( $field = null ) {
164 if ( $field === null ) {
165 return $this->metadata;
166 }
167
168 return $this->metadata[$field] ?? null;
169 }
170
178 public function setMetadata( $field, $value ) {
179 $old = $this->getMetadata( $field );
180 if ( $value === null ) {
181 unset( $this->metadata[$field] );
182 } else {
183 $this->metadata[$field] = $value;
184 }
185
186 return $old;
187 }
188
194 public function getReleaseTimestamp() {
195 $time = wfTimestampOrNull( TS_UNIX, $this->params['jobReleaseTimestamp'] ?? null );
196 return $time ? (int)$time : null;
197 }
198
203 public function getQueuedTimestamp() {
204 $time = wfTimestampOrNull( TS_UNIX, $this->metadata['timestamp'] ?? null );
205 return $time ? (int)$time : null;
206 }
207
212 public function getRequestId() {
213 return $this->params['requestId'] ?? null;
214 }
215
220 public function getReadyTimestamp() {
221 return $this->getReleaseTimestamp() ?: $this->getQueuedTimestamp();
222 }
223
237 public function ignoreDuplicates() {
238 return $this->removeDuplicates;
239 }
240
245 public function allowRetries() {
246 return true;
247 }
248
253 public function workItemCount() {
254 return 1;
255 }
256
267 public function getDeduplicationInfo() {
268 $info = [
269 'type' => $this->getType(),
270 'params' => $this->getParams()
271 ];
272 if ( is_array( $info['params'] ) ) {
273 // Identical jobs with different "root" jobs should count as duplicates
274 unset( $info['params']['rootJobSignature'] );
275 unset( $info['params']['rootJobTimestamp'] );
276 // Likewise for jobs with different delay times
277 unset( $info['params']['jobReleaseTimestamp'] );
278 // Identical jobs from different requests should count as duplicates
279 unset( $info['params']['requestId'] );
280 // Queues pack and hash this array, so normalize the order
281 ksort( $info['params'] );
282 }
283
284 return $info;
285 }
286
306 public static function newRootJobParams( $key ) {
307 return [
308 'rootJobIsSelf' => true,
309 'rootJobSignature' => sha1( $key ),
310 'rootJobTimestamp' => wfTimestampNow()
311 ];
312 }
313
320 public function getRootJobParams() {
321 return [
322 'rootJobSignature' => $this->params['rootJobSignature'] ?? null,
323 'rootJobTimestamp' => $this->params['rootJobTimestamp'] ?? null
324 ];
325 }
326
333 public function hasRootJobParams() {
334 return isset( $this->params['rootJobSignature'] )
335 && isset( $this->params['rootJobTimestamp'] );
336 }
337
343 public function isRootJob() {
344 return $this->hasRootJobParams() && !empty( $this->params['rootJobIsSelf'] );
345 }
346
353 protected function addTeardownCallback( $callback ) {
354 $this->teardownCallbacks[] = $callback;
355 }
356
361 public function teardown( $status ) {
362 foreach ( $this->teardownCallbacks as $callback ) {
363 call_user_func( $callback, $status );
364 }
365 }
366
371 public function toString() {
372 $paramString = '';
373 if ( $this->params ) {
374 foreach ( $this->params as $key => $value ) {
375 if ( $paramString != '' ) {
376 $paramString .= ' ';
377 }
378 if ( is_array( $value ) ) {
379 $filteredValue = [];
380 foreach ( $value as $k => $v ) {
381 $json = FormatJson::encode( $v );
382 if ( $json === false || mb_strlen( $json ) > 512 ) {
383 $filteredValue[$k] = get_debug_type( $v ) . '(...)';
384 } else {
385 $filteredValue[$k] = $v;
386 }
387 }
388 if ( count( $filteredValue ) <= 10 ) {
389 $value = FormatJson::encode( $filteredValue );
390 } else {
391 $value = "array(" . count( $value ) . ")";
392 }
393 } elseif ( is_object( $value ) && !method_exists( $value, '__toString' ) ) {
394 $value = get_debug_type( $value );
395 }
396
397 $flatValue = (string)$value;
398 if ( mb_strlen( $flatValue ) > 1024 ) {
399 $flatValue = "string(" . mb_strlen( $value ) . ")";
400 }
401
402 // Remove newline characters from the value, since
403 // newlines indicate new job lines in log files
404 $flatValue = preg_replace( '/\s+/', ' ', $flatValue );
405
406 $paramString .= "$key={$flatValue}";
407 }
408 }
409
410 $metaString = '';
411 foreach ( $this->metadata as $key => $value ) {
412 if ( is_scalar( $value ) && mb_strlen( $value ) < 1024 ) {
413 $metaString .= ( $metaString ? ",$key=$value" : "$key=$value" );
414 }
415 }
416
417 $s = $this->command;
418 if ( is_object( $this->title ) ) {
419 $s .= ' ' . $this->title->getPrefixedDBkey();
420 }
421 if ( $paramString != '' ) {
422 $s .= " $paramString";
423 }
424 if ( $metaString != '' ) {
425 $s .= " ($metaString)";
426 }
427
428 return $s;
429 }
430
431 protected function setLastError( $error ) {
432 $this->error = $error;
433 }
434
439 public function getLastError() {
440 return $this->error;
441 }
442}
const NS_SPECIAL
Definition Defines.php:54
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
array $params
The job parameters.
Describe and execute a background job.
Definition Job.php:38
hasRootJobParams()
Definition Job.php:333
string $command
Definition Job.php:40
setMetadata( $field, $value)
Definition Job.php:178
getParams()
array Parameters that specify sources, targets, and options for execution
Definition Job.php:153
getReadyTimestamp()
int|null UNIX timestamp of when the job was runnable, or null 1.26
Definition Job.php:220
toString()
string Debugging string describing the job
Definition Job.php:371
getType()
string Job type that defines what sort of changes this job makes
Definition Job.php:138
Title $title
Definition Job.php:49
getRootJobParams()
Definition Job.php:320
hasExecutionFlag( $flag)
bool 1.31
Definition Job.php:130
callable[] $teardownCallbacks
Definition Job.php:58
getQueuedTimestamp()
Definition Job.php:203
bool $removeDuplicates
Expensive jobs may set this to true.
Definition Job.php:52
setLastError( $error)
Definition Job.php:431
getMetadata( $field=null)
Definition Job.php:163
teardown( $status)
Definition Job.php:361
isRootJob()
Definition Job.php:343
addTeardownCallback( $callback)
Definition Job.php:353
array $params
Array of job parameters.
Definition Job.php:43
getTitle()
Definition Job.php:145
array $metadata
Additional queue metadata.
Definition Job.php:46
static factory( $command, $params=[])
Create the appropriate object to handle a specific job.
Definition Job.php:72
int $executionFlags
Bitfield of JOB_* class constants.
Definition Job.php:61
workItemCount()
Definition Job.php:253
getRequestId()
string|null Id of the request that created this job. Follows jobs recursively, allowing to track the ...
Definition Job.php:212
static newRootJobParams( $key)
Get "root job" parameters for a task.
Definition Job.php:306
getDeduplicationInfo()
Subclasses may need to override this to make duplication detection work.
Definition Job.php:267
getReleaseTimestamp()
Definition Job.php:194
ignoreDuplicates()
Whether the queue should reject insertion of this job if a duplicate exists.
Definition Job.php:237
getLastError()
string
Definition Job.php:439
string $error
Text for error that occurred last.
Definition Job.php:55
allowRetries()
Whether to retry execution of this job if run() returned false or threw an exception....
Definition Job.php:245
__construct( $command, $params=null)
Definition Job.php:86
Service for handling telemetry data.
Definition Telemetry.php:29
JSON formatter wrapper class.
Service locator for MediaWiki core services.
Represents a title within MediaWiki.
Definition Title.php:78
Interface for objects (potentially) representing a page that can be viewable and linked to on a wiki.
Job that has a run() method and metadata accessors for JobQueue::pop() and JobQueue::ack().