MediaWiki  master
Job.php
Go to the documentation of this file.
1 <?php
35 abstract class Job implements RunnableJob {
37  public $command;
38 
40  public $params;
41 
43  public $metadata = [];
44 
46  protected $title;
47 
49  protected $removeDuplicates = false;
50 
52  protected $error;
53 
55  protected $teardownCallbacks = [];
56 
58  protected $executionFlags = 0;
59 
68  public static function factory( $command, $params = [] ) {
69  global $wgJobClasses;
70 
71  if ( $params instanceof Title ) {
72  // Backwards compatibility for old signature ($command, $title, $params)
73  $title = $params;
74  $params = func_num_args() >= 3 ? func_get_arg( 2 ) : [];
75  } elseif ( isset( $params['namespace'] ) && isset( $params['title'] ) ) {
76  // Handle job classes that take title as constructor parameter.
77  // If a newer classes like GenericParameterJob uses these parameters,
78  // then this happens in Job::__construct instead.
79  $title = Title::makeTitle( $params['namespace'], $params['title'] );
80  } else {
81  // Default title for job classes not implementing GenericParameterJob.
82  // This must be a valid title because it not directly passed to
83  // our Job constructor, but rather it's subclasses which may expect
84  // to be able to use it.
85  $title = Title::makeTitle( NS_SPECIAL, 'Blankpage' );
86  }
87 
88  if ( isset( $wgJobClasses[$command] ) ) {
89  $handler = $wgJobClasses[$command];
90 
91  if ( is_callable( $handler ) ) {
92  $job = call_user_func( $handler, $title, $params );
93  } elseif ( class_exists( $handler ) ) {
94  if ( is_subclass_of( $handler, GenericParameterJob::class ) ) {
95  $job = new $handler( $params );
96  } else {
97  $job = new $handler( $title, $params );
98  }
99  } else {
100  $job = null;
101  }
102 
103  if ( $job instanceof Job ) {
104  $job->command = $command;
105 
106  return $job;
107  } else {
108  throw new InvalidArgumentException(
109  "Could not instantiate job '$command': bad spec!"
110  );
111  }
112  }
113 
114  throw new InvalidArgumentException( "Invalid job command '{$command}'" );
115  }
116 
123  public function __construct( $command, $params = null ) {
124  if ( $params instanceof Title ) {
125  // Backwards compatibility for old signature ($command, $title, $params)
126  $title = $params;
127  $params = func_num_args() >= 3 ? func_get_arg( 2 ) : [];
128  } else {
129  // Newer jobs may choose to not have a top-level title (e.g. GenericParameterJob)
130  $title = null;
131  }
132 
133  if ( !is_array( $params ) ) {
134  throw new InvalidArgumentException( '$params must be an array' );
135  }
136 
137  if (
138  $title &&
139  !isset( $params['namespace'] ) &&
140  !isset( $params['title'] )
141  ) {
142  // When constructing this class for submitting to the queue,
143  // normalise the $title arg of old job classes as part of $params.
144  $params['namespace'] = $title->getNamespace();
145  $params['title'] = $title->getDBkey();
146  }
147 
148  $this->command = $command;
149  $this->params = $params + [ 'requestId' => WebRequest::getRequestId() ];
150 
151  if ( $this->title === null ) {
152  // Set this field for access via getTitle().
153  $this->title = ( isset( $params['namespace'] ) && isset( $params['title'] ) )
154  ? Title::makeTitle( $params['namespace'], $params['title'] )
155  // GenericParameterJob classes without namespace/title params
156  // should not use getTitle(). Set an invalid title as placeholder.
157  : Title::makeTitle( NS_SPECIAL, '' );
158  }
159  }
160 
165  public function hasExecutionFlag( $flag ) {
166  return ( $this->executionFlags & $flag ) === $flag;
167  }
168 
173  public function getType() {
174  return $this->command;
175  }
176 
180  final public function getTitle() {
181  return $this->title;
182  }
183 
188  public function getParams() {
189  return $this->params;
190  }
191 
198  public function getMetadata( $field = null ) {
199  if ( $field === null ) {
200  return $this->metadata;
201  }
202 
203  return $this->metadata[$field] ?? null;
204  }
205 
213  public function setMetadata( $field, $value ) {
214  $old = $this->getMetadata( $field );
215  if ( $value === null ) {
216  unset( $this->metadata[$field] );
217  } else {
218  $this->metadata[$field] = $value;
219  }
220 
221  return $old;
222  }
223 
229  public function getReleaseTimestamp() {
230  return isset( $this->params['jobReleaseTimestamp'] )
231  ? wfTimestampOrNull( TS_UNIX, $this->params['jobReleaseTimestamp'] )
232  : null;
233  }
234 
239  public function getQueuedTimestamp() {
240  return isset( $this->metadata['timestamp'] )
241  ? wfTimestampOrNull( TS_UNIX, $this->metadata['timestamp'] )
242  : null;
243  }
244 
249  public function getRequestId() {
250  return $this->params['requestId'] ?? null;
251  }
252 
257  public function getReadyTimestamp() {
258  return $this->getReleaseTimestamp() ?: $this->getQueuedTimestamp();
259  }
260 
274  public function ignoreDuplicates() {
276  }
277 
282  public function allowRetries() {
283  return true;
284  }
285 
290  public function workItemCount() {
291  return 1;
292  }
293 
304  public function getDeduplicationInfo() {
305  $info = [
306  'type' => $this->getType(),
307  'params' => $this->getParams()
308  ];
309  if ( is_array( $info['params'] ) ) {
310  // Identical jobs with different "root" jobs should count as duplicates
311  unset( $info['params']['rootJobSignature'] );
312  unset( $info['params']['rootJobTimestamp'] );
313  // Likewise for jobs with different delay times
314  unset( $info['params']['jobReleaseTimestamp'] );
315  // Identical jobs from different requests should count as duplicates
316  unset( $info['params']['requestId'] );
317  // Queues pack and hash this array, so normalize the order
318  ksort( $info['params'] );
319  }
320 
321  return $info;
322  }
323 
343  public static function newRootJobParams( $key ) {
344  return [
345  'rootJobIsSelf' => true,
346  'rootJobSignature' => sha1( $key ),
347  'rootJobTimestamp' => wfTimestampNow()
348  ];
349  }
350 
357  public function getRootJobParams() {
358  return [
359  'rootJobSignature' => $this->params['rootJobSignature'] ?? null,
360  'rootJobTimestamp' => $this->params['rootJobTimestamp'] ?? null
361  ];
362  }
363 
370  public function hasRootJobParams() {
371  return isset( $this->params['rootJobSignature'] )
372  && isset( $this->params['rootJobTimestamp'] );
373  }
374 
380  public function isRootJob() {
381  return $this->hasRootJobParams() && !empty( $this->params['rootJobIsSelf'] );
382  }
383 
390  protected function addTeardownCallback( $callback ) {
391  $this->teardownCallbacks[] = $callback;
392  }
393 
398  public function teardown( $status ) {
399  foreach ( $this->teardownCallbacks as $callback ) {
400  call_user_func( $callback, $status );
401  }
402  }
403 
408  public function toString() {
409  $paramString = '';
410  if ( $this->params ) {
411  foreach ( $this->params as $key => $value ) {
412  if ( $paramString != '' ) {
413  $paramString .= ' ';
414  }
415  if ( is_array( $value ) ) {
416  $filteredValue = [];
417  foreach ( $value as $k => $v ) {
418  $json = FormatJson::encode( $v );
419  if ( $json === false || mb_strlen( $json ) > 512 ) {
420  $filteredValue[$k] = gettype( $v ) . '(...)';
421  } else {
422  $filteredValue[$k] = $v;
423  }
424  }
425  if ( count( $filteredValue ) <= 10 ) {
426  $value = FormatJson::encode( $filteredValue );
427  } else {
428  $value = "array(" . count( $value ) . ")";
429  }
430  } elseif ( is_object( $value ) && !method_exists( $value, '__toString' ) ) {
431  $value = "object(" . get_class( $value ) . ")";
432  }
433 
434  $flatValue = (string)$value;
435  if ( mb_strlen( $value ) > 1024 ) {
436  $flatValue = "string(" . mb_strlen( $value ) . ")";
437  }
438 
439  $paramString .= "$key={$flatValue}";
440  }
441  }
442 
443  $metaString = '';
444  foreach ( $this->metadata as $key => $value ) {
445  if ( is_scalar( $value ) && mb_strlen( $value ) < 1024 ) {
446  $metaString .= ( $metaString ? ",$key=$value" : "$key=$value" );
447  }
448  }
449 
450  $s = $this->command;
451  if ( is_object( $this->title ) ) {
452  $s .= " {$this->title->getPrefixedDBkey()}";
453  }
454  if ( $paramString != '' ) {
455  $s .= " $paramString";
456  }
457  if ( $metaString != '' ) {
458  $s .= " ($metaString)";
459  }
460 
461  return $s;
462  }
463 
464  protected function setLastError( $error ) {
465  $this->error = $error;
466  }
467 
472  public function getLastError() {
473  return $this->error;
474  }
475 }
Job\getMetadata
getMetadata( $field=null)
Definition: Job.php:198
Job\getRootJobParams
getRootJobParams()
Definition: Job.php:357
Job\getReleaseTimestamp
getReleaseTimestamp()
Definition: Job.php:229
Job\teardown
teardown( $status)
Definition: Job.php:398
Job\getType
getType()
string Job type that defines what sort of changes this job makes
Definition: Job.php:173
Job\getParams
getParams()
array Parameters that specify sources, targets, and options for execution
Definition: Job.php:188
Job\toString
toString()
string Debugging string describing the job
Definition: Job.php:408
Job\workItemCount
workItemCount()
Definition: Job.php:290
Job\getRequestId
getRequestId()
string|null Id of the request that created this job. Follows jobs recursively, allowing to track the ...
Definition: Job.php:249
Job\$title
Title $title
Definition: Job.php:46
Job\getQueuedTimestamp
getQueuedTimestamp()
Definition: Job.php:239
Job\getReadyTimestamp
getReadyTimestamp()
int|null UNIX timestamp of when the job was runnable, or null 1.26
Definition: Job.php:257
RunnableJob
Job that has a run() method and metadata accessors for JobQueue::pop() and JobQueue::ack()
Definition: RunnableJob.php:37
Job\addTeardownCallback
addTeardownCallback( $callback)
Definition: Job.php:390
Job\$teardownCallbacks
callable[] $teardownCallbacks
Definition: Job.php:55
Job\$params
array $params
Array of job parameters.
Definition: Job.php:40
Job\setLastError
setLastError( $error)
Definition: Job.php:464
Job\ignoreDuplicates
ignoreDuplicates()
Whether the queue should reject insertion of this job if a duplicate exists.
Definition: Job.php:274
Job\getTitle
getTitle()
Definition: Job.php:180
Job\hasExecutionFlag
hasExecutionFlag( $flag)
JOB_* class constant bool 1.31
Definition: Job.php:165
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:53
Job
Class to both describe a background job and handle jobs.
Definition: Job.php:35
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:52
Title\getDBkey
getDBkey()
Get the main part with underscores.
Definition: Title.php:1059
Title\getNamespace
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:1068
Job\$command
string $command
Definition: Job.php:37
wfTimestampOrNull
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
Definition: GlobalFunctions.php:1848
Job\$metadata
array $metadata
Additional queue metadata.
Definition: Job.php:43
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:626
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:1861
Job\newRootJobParams
static newRootJobParams( $key)
Get "root job" parameters for a task.
Definition: Job.php:343
Job\factory
static factory( $command, $params=[])
Create the appropriate object to handle a specific job.
Definition: Job.php:68
$s
foreach( $mmfl['setupFiles'] as $fileName) if( $queue) if(empty( $mmfl['quiet'])) $s
Definition: mergeMessageFileList.php:206
Job\allowRetries
allowRetries()
bool Whether this job can be retried on failure by job runners 1.21
Definition: Job.php:282
Job\$removeDuplicates
bool $removeDuplicates
Expensive jobs may set this to true.
Definition: Job.php:49
Job\getLastError
getLastError()
string
Definition: Job.php:472
Job\getDeduplicationInfo
getDeduplicationInfo()
Subclasses may need to override this to make duplication detection work.
Definition: Job.php:304
Job\$executionFlags
int $executionFlags
Bitfield of JOB_* class constants.
Definition: Job.php:58
$wgJobClasses
$wgJobClasses
Maps jobs to their handlers; extensions can add to this to provide custom jobs.
Definition: DefaultSettings.php:8045
Title
Represents a title within MediaWiki.
Definition: Title.php:46
$job
if(count( $args)< 1) $job
Definition: recompressTracked.php:50
WebRequest\getRequestId
static getRequestId()
Get the current request ID.
Definition: WebRequest.php:330
Job\isRootJob
isRootJob()
Definition: Job.php:380
Job\hasRootJobParams
hasRootJobParams()
Definition: Job.php:370
Job\setMetadata
setMetadata( $field, $value)
Definition: Job.php:213
Job\__construct
__construct( $command, $params=null)
Definition: Job.php:123