MediaWiki  master
Job.php
Go to the documentation of this file.
1 <?php
32 abstract class Job implements RunnableJob {
34  public $command;
35 
37  public $params;
38 
40  public $metadata = [];
41 
43  protected $title;
44 
46  protected $removeDuplicates = false;
47 
49  protected $error;
50 
52  protected $teardownCallbacks = [];
53 
55  protected $executionFlags = 0;
56 
65  public static function factory( $command, $params = [] ) {
66  global $wgJobClasses;
67 
68  if ( $params instanceof Title ) {
69  // Backwards compatibility for old signature ($command, $title, $params)
70  $title = $params;
71  $params = func_num_args() >= 3 ? func_get_arg( 2 ) : [];
72  } elseif ( isset( $params['namespace'] ) && isset( $params['title'] ) ) {
73  // Handle job classes that take title as constructor parameter.
74  // If a newer classes like GenericParameterJob uses these parameters,
75  // then this happens in Job::__construct instead.
76  $title = Title::makeTitle( $params['namespace'], $params['title'] );
77  } else {
78  // Default title for job classes not implementing GenericParameterJob.
79  // This must be a valid title because it not directly passed to
80  // our Job constructor, but rather it's subclasses which may expect
81  // to be able to use it.
82  $title = Title::makeTitle( NS_SPECIAL, 'Blankpage' );
83  }
84 
85  if ( isset( $wgJobClasses[$command] ) ) {
86  $handler = $wgJobClasses[$command];
87 
88  if ( is_callable( $handler ) ) {
89  $job = call_user_func( $handler, $title, $params );
90  } elseif ( class_exists( $handler ) ) {
91  if ( is_subclass_of( $handler, GenericParameterJob::class ) ) {
92  $job = new $handler( $params );
93  } else {
94  $job = new $handler( $title, $params );
95  }
96  } else {
97  $job = null;
98  }
99 
100  if ( $job instanceof Job ) {
101  $job->command = $command;
102 
103  return $job;
104  } else {
105  throw new InvalidArgumentException(
106  "Could not instantiate job '$command': bad spec!"
107  );
108  }
109  }
110 
111  throw new InvalidArgumentException( "Invalid job command '{$command}'" );
112  }
113 
120  public function __construct( $command, $params = null ) {
121  if ( $params instanceof Title ) {
122  // Backwards compatibility for old signature ($command, $title, $params)
123  $title = $params;
124  $params = func_num_args() >= 3 ? func_get_arg( 2 ) : [];
125  } else {
126  // Newer jobs may choose to not have a top-level title (e.g. GenericParameterJob)
127  $title = null;
128  }
129 
130  if ( !is_array( $params ) ) {
131  throw new InvalidArgumentException( '$params must be an array' );
132  }
133 
134  if (
135  $title &&
136  !isset( $params['namespace'] ) &&
137  !isset( $params['title'] )
138  ) {
139  // When constructing this class for submitting to the queue,
140  // normalise the $title arg of old job classes as part of $params.
141  $params['namespace'] = $title->getNamespace();
142  $params['title'] = $title->getDBkey();
143  }
144 
145  $this->command = $command;
146  $this->params = $params + [ 'requestId' => WebRequest::getRequestId() ];
147 
148  if ( $this->title === null ) {
149  // Set this field for access via getTitle().
150  $this->title = ( isset( $params['namespace'] ) && isset( $params['title'] ) )
151  ? Title::makeTitle( $params['namespace'], $params['title'] )
152  // GenericParameterJob classes without namespace/title params
153  // should not use getTitle(). Set an invalid title as placeholder.
154  : Title::makeTitle( NS_SPECIAL, '' );
155  }
156  }
157 
162  public function hasExecutionFlag( $flag ) {
163  return ( $this->executionFlags & $flag ) === $flag;
164  }
165 
170  public function getType() {
171  return $this->command;
172  }
173 
177  final public function getTitle() {
178  return $this->title;
179  }
180 
185  public function getParams() {
186  return $this->params;
187  }
188 
195  public function getMetadata( $field = null ) {
196  if ( $field === null ) {
197  return $this->metadata;
198  }
199 
200  return $this->metadata[$field] ?? null;
201  }
202 
210  public function setMetadata( $field, $value ) {
211  $old = $this->getMetadata( $field );
212  if ( $value === null ) {
213  unset( $this->metadata[$field] );
214  } else {
215  $this->metadata[$field] = $value;
216  }
217 
218  return $old;
219  }
220 
226  public function getReleaseTimestamp() {
227  return isset( $this->params['jobReleaseTimestamp'] )
228  ? wfTimestampOrNull( TS_UNIX, $this->params['jobReleaseTimestamp'] )
229  : null;
230  }
231 
236  public function getQueuedTimestamp() {
237  return isset( $this->metadata['timestamp'] )
238  ? wfTimestampOrNull( TS_UNIX, $this->metadata['timestamp'] )
239  : null;
240  }
241 
246  public function getRequestId() {
247  return $this->params['requestId'] ?? null;
248  }
249 
254  public function getReadyTimestamp() {
255  return $this->getReleaseTimestamp() ?: $this->getQueuedTimestamp();
256  }
257 
271  public function ignoreDuplicates() {
273  }
274 
279  public function allowRetries() {
280  return true;
281  }
282 
287  public function workItemCount() {
288  return 1;
289  }
290 
301  public function getDeduplicationInfo() {
302  $info = [
303  'type' => $this->getType(),
304  'params' => $this->getParams()
305  ];
306  if ( is_array( $info['params'] ) ) {
307  // Identical jobs with different "root" jobs should count as duplicates
308  unset( $info['params']['rootJobSignature'] );
309  unset( $info['params']['rootJobTimestamp'] );
310  // Likewise for jobs with different delay times
311  unset( $info['params']['jobReleaseTimestamp'] );
312  // Identical jobs from different requests should count as duplicates
313  unset( $info['params']['requestId'] );
314  // Queues pack and hash this array, so normalize the order
315  ksort( $info['params'] );
316  }
317 
318  return $info;
319  }
320 
340  public static function newRootJobParams( $key ) {
341  return [
342  'rootJobIsSelf' => true,
343  'rootJobSignature' => sha1( $key ),
344  'rootJobTimestamp' => wfTimestampNow()
345  ];
346  }
347 
354  public function getRootJobParams() {
355  return [
356  'rootJobSignature' => $this->params['rootJobSignature'] ?? null,
357  'rootJobTimestamp' => $this->params['rootJobTimestamp'] ?? null
358  ];
359  }
360 
367  public function hasRootJobParams() {
368  return isset( $this->params['rootJobSignature'] )
369  && isset( $this->params['rootJobTimestamp'] );
370  }
371 
377  public function isRootJob() {
378  return $this->hasRootJobParams() && !empty( $this->params['rootJobIsSelf'] );
379  }
380 
387  protected function addTeardownCallback( $callback ) {
388  $this->teardownCallbacks[] = $callback;
389  }
390 
395  public function teardown( $status ) {
396  foreach ( $this->teardownCallbacks as $callback ) {
397  call_user_func( $callback, $status );
398  }
399  }
400 
405  public function toString() {
406  $paramString = '';
407  if ( $this->params ) {
408  foreach ( $this->params as $key => $value ) {
409  if ( $paramString != '' ) {
410  $paramString .= ' ';
411  }
412  if ( is_array( $value ) ) {
413  $filteredValue = [];
414  foreach ( $value as $k => $v ) {
415  $json = FormatJson::encode( $v );
416  if ( $json === false || mb_strlen( $json ) > 512 ) {
417  $filteredValue[$k] = gettype( $v ) . '(...)';
418  } else {
419  $filteredValue[$k] = $v;
420  }
421  }
422  if ( count( $filteredValue ) <= 10 ) {
423  $value = FormatJson::encode( $filteredValue );
424  } else {
425  $value = "array(" . count( $value ) . ")";
426  }
427  } elseif ( is_object( $value ) && !method_exists( $value, '__toString' ) ) {
428  $value = "object(" . get_class( $value ) . ")";
429  }
430 
431  $flatValue = (string)$value;
432  if ( mb_strlen( $value ) > 1024 ) {
433  $flatValue = "string(" . mb_strlen( $value ) . ")";
434  }
435 
436  $paramString .= "$key={$flatValue}";
437  }
438  }
439 
440  $metaString = '';
441  foreach ( $this->metadata as $key => $value ) {
442  if ( is_scalar( $value ) && mb_strlen( $value ) < 1024 ) {
443  $metaString .= ( $metaString ? ",$key=$value" : "$key=$value" );
444  }
445  }
446 
447  $s = $this->command;
448  if ( is_object( $this->title ) ) {
449  $s .= " {$this->title->getPrefixedDBkey()}";
450  }
451  if ( $paramString != '' ) {
452  $s .= " $paramString";
453  }
454  if ( $metaString != '' ) {
455  $s .= " ($metaString)";
456  }
457 
458  return $s;
459  }
460 
461  protected function setLastError( $error ) {
462  $this->error = $error;
463  }
464 
469  public function getLastError() {
470  return $this->error;
471  }
472 }
Job\getMetadata
getMetadata( $field=null)
Stable to override.
Definition: Job.php:195
Job\getRootJobParams
getRootJobParams()
Stable to override.
Definition: Job.php:354
Job\getReleaseTimestamp
getReleaseTimestamp()
Stable to override.
Definition: Job.php:226
Job\teardown
teardown( $status)
Stable to override
Definition: Job.php:395
Job\getType
getType()
string Job type that defines what sort of changes this job makes Stable to override
Definition: Job.php:170
Job\getParams
getParams()
array Parameters that specify sources, targets, and options for execution Stable to override
Definition: Job.php:185
Job\toString
toString()
string Debugging string describing the job Stable to override
Definition: Job.php:405
Job\workItemCount
workItemCount()
Stable to override.
Definition: Job.php:287
Job\getRequestId
getRequestId()
string|null Id of the request that created this job. Follows jobs recursively, allowing to track the ...
Definition: Job.php:246
Job\$title
Title $title
Definition: Job.php:43
Job\getQueuedTimestamp
getQueuedTimestamp()
Definition: Job.php:236
Job\getReadyTimestamp
getReadyTimestamp()
int|null UNIX timestamp of when the job was runnable, or null 1.26 Stable to override
Definition: Job.php:254
RunnableJob
Job that has a run() method and metadata accessors for JobQueue::pop() and JobQueue::ack()
Definition: RunnableJob.php:37
$s
$s
Definition: mergeMessageFileList.php:184
Job\addTeardownCallback
addTeardownCallback( $callback)
Definition: Job.php:387
Job\$teardownCallbacks
callable[] $teardownCallbacks
Definition: Job.php:52
Job\$params
array $params
Array of job parameters.
Definition: Job.php:37
Job\setLastError
setLastError( $error)
Definition: Job.php:461
Job\ignoreDuplicates
ignoreDuplicates()
Whether the queue should reject insertion of this job if a duplicate exists.
Definition: Job.php:271
Job\getTitle
getTitle()
Definition: Job.php:177
Job\hasExecutionFlag
hasExecutionFlag( $flag)
JOB_* class constant bool 1.31 Stable to override
Definition: Job.php:162
Job
Class to both describe a background job and handle jobs.
Definition: Job.php:32
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:58
FormatJson\encode
static encode( $value, $pretty=false, $escaping=0)
Returns the JSON representation of a value.
Definition: FormatJson.php:115
Job\$error
string $error
Text for error that occurred last.
Definition: Job.php:49
Title\getDBkey
getDBkey()
Get the main part with underscores.
Definition: Title.php:1025
Title\getNamespace
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:1034
Job\$command
string $command
Definition: Job.php:34
wfTimestampOrNull
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
Definition: GlobalFunctions.php:1831
Job\$metadata
array $metadata
Additional queue metadata.
Definition: Job.php:40
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:591
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:1844
Job\newRootJobParams
static newRootJobParams( $key)
Get "root job" parameters for a task.
Definition: Job.php:340
Job\factory
static factory( $command, $params=[])
Create the appropriate object to handle a specific job.
Definition: Job.php:65
Job\allowRetries
allowRetries()
bool Whether this job can be retried on failure by job runners 1.21 Stable to override
Definition: Job.php:279
Job\$removeDuplicates
bool $removeDuplicates
Expensive jobs may set this to true.
Definition: Job.php:46
Job\getLastError
getLastError()
string Stable to override
Definition: Job.php:469
Job\getDeduplicationInfo
getDeduplicationInfo()
Subclasses may need to override this to make duplication detection work.
Definition: Job.php:301
Job\$executionFlags
int $executionFlags
Bitfield of JOB_* class constants.
Definition: Job.php:55
$wgJobClasses
$wgJobClasses
Maps jobs to their handlers; extensions can add to this to provide custom jobs.
Definition: DefaultSettings.php:7976
Title
Represents a title within MediaWiki.
Definition: Title.php:41
$job
if(count( $args)< 1) $job
Definition: recompressTracked.php:50
WebRequest\getRequestId
static getRequestId()
Get the unique request ID.
Definition: WebRequest.php:327
Job\isRootJob
isRootJob()
Stable to override.
Definition: Job.php:377
Job\hasRootJobParams
hasRootJobParams()
Stable to override.
Definition: Job.php:367
Job\setMetadata
setMetadata( $field, $value)
Stable to override.
Definition: Job.php:210
Job\__construct
__construct( $command, $params=null)
Stable to call.
Definition: Job.php:120