Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
49.21% covered (danger)
49.21%
31 / 63
33.33% covered (danger)
33.33%
4 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
JobSpecification
49.21% covered (danger)
49.21%
31 / 63
33.33% covered (danger)
33.33%
4 / 12
99.48
0.00% covered (danger)
0.00%
0 / 1
 __construct
68.75% covered (warning)
68.75%
11 / 16
0.00% covered (danger)
0.00%
0 / 1
3.27
 validateParams
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
5.20
 getType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getParams
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getReleaseTimestamp
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 ignoreDuplicates
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDeduplicationInfo
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
4
 getRootJobParams
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 hasRootJobParams
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 isRootJob
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 toSerializableArray
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 newFromArray
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21use MediaWiki\Http\Telemetry;
22use MediaWiki\Page\PageReference;
23use MediaWiki\Page\PageReferenceValue;
24
25/**
26 * Job queue task description base code.
27 *
28 * Example usage:
29 * @code
30 * $job = new JobSpecification(
31 *        'null',
32 *        [ 'lives' => 1, 'usleep' => 100, 'pi' => 3.141569 ],
33 *        [ 'removeDuplicates' => 1 ]
34 * );
35 * MediaWikiServices::getInstance()->getJobQueueGroup()->push( $job )
36 * @endcode
37 *
38 * @newable
39 * @since 1.23
40 * @ingroup JobQueue
41 */
42class JobSpecification implements IJobSpecification {
43    /** @var string */
44    protected $type;
45
46    /** @var array Array of job parameters or false if none */
47    protected $params;
48
49    /** @var PageReference */
50    protected $page;
51
52    /** @var array */
53    protected $opts;
54
55    /**
56     * @param string $type
57     * @param array $params Map of key/values
58     * @param array $opts Map of key/values
59     *   'removeDuplicates' key - whether to remove duplicate jobs
60     *   'removeDuplicatesIgnoreParams' key - array with parameters to ignore for deduplication
61     * @param PageReference|null $page
62     */
63    public function __construct(
64        $type, array $params, array $opts = [], ?PageReference $page = null
65    ) {
66        $params += [
67            'requestId' => Telemetry::getInstance()->getRequestId(),
68        ];
69        $this->validateParams( $params );
70        $this->validateParams( $opts );
71
72        $this->type = $type;
73        if ( $page ) {
74            // Make sure JobQueue classes can pull the title from parameters alone
75            if ( $page->getDBkey() !== '' ) {
76                $params += [
77                    'namespace' => $page->getNamespace(),
78                    'title' => $page->getDBkey()
79                ];
80            }
81        } else {
82            // We aim to remove the page from job specification and all we need
83            // is namespace/dbkey, so use LOCAL no matter what.
84            $page = PageReferenceValue::localReference( NS_SPECIAL, 'Badtitle/' . __CLASS__ );
85        }
86        $this->params = $params;
87        $this->page = $page;
88        $this->opts = $opts;
89    }
90
91    /**
92     * @param array $params
93     */
94    protected function validateParams( array $params ) {
95        foreach ( $params as $p => $v ) {
96            if ( is_array( $v ) ) {
97                $this->validateParams( $v );
98            } elseif ( !is_scalar( $v ) && $v !== null ) {
99                throw new UnexpectedValueException( "Job parameter $p is not JSON serializable." );
100            }
101        }
102    }
103
104    public function getType() {
105        return $this->type;
106    }
107
108    public function getParams() {
109        return $this->params;
110    }
111
112    public function getReleaseTimestamp() {
113        return isset( $this->params['jobReleaseTimestamp'] )
114            ? wfTimestampOrNull( TS_UNIX, $this->params['jobReleaseTimestamp'] )
115            : null;
116    }
117
118    public function ignoreDuplicates() {
119        return !empty( $this->opts['removeDuplicates'] );
120    }
121
122    public function getDeduplicationInfo() {
123        $info = [
124            'type' => $this->getType(),
125            'params' => $this->getParams()
126        ];
127        if ( is_array( $info['params'] ) ) {
128            // Identical jobs with different "root" jobs should count as duplicates
129            unset( $info['params']['rootJobSignature'] );
130            unset( $info['params']['rootJobTimestamp'] );
131            // Likewise for jobs with different delay times
132            unset( $info['params']['jobReleaseTimestamp'] );
133            // Identical jobs from different requests should count as duplicates
134            unset( $info['params']['requestId'] );
135            if ( isset( $this->opts['removeDuplicatesIgnoreParams'] ) ) {
136                foreach ( $this->opts['removeDuplicatesIgnoreParams'] as $field ) {
137                    unset( $info['params'][$field] );
138                }
139            }
140        }
141
142        return $info;
143    }
144
145    public function getRootJobParams() {
146        return [
147            'rootJobSignature' => $this->params['rootJobSignature'] ?? null,
148            'rootJobTimestamp' => $this->params['rootJobTimestamp'] ?? null
149        ];
150    }
151
152    public function hasRootJobParams() {
153        return isset( $this->params['rootJobSignature'] )
154            && isset( $this->params['rootJobTimestamp'] );
155    }
156
157    public function isRootJob() {
158        return $this->hasRootJobParams() && !empty( $this->params['rootJobIsSelf'] );
159    }
160
161    /**
162     * @deprecated since 1.41
163     * @return array Field/value map that can immediately be serialized
164     * @since 1.25
165     */
166    public function toSerializableArray() {
167        wfDeprecated( __METHOD__, '1.41' );
168        return [
169            'type'   => $this->type,
170            'params' => $this->params,
171            'opts'   => $this->opts,
172            'title'  => [
173                'ns'  => $this->page->getNamespace(),
174                'key' => $this->page->getDBkey()
175            ]
176        ];
177    }
178
179    /**
180     * @param array $map Field/value map
181     * @return JobSpecification
182     * @since 1.25
183     */
184    public static function newFromArray( array $map ) {
185        return new self(
186            $map['type'],
187            $map['params'],
188            $map['opts'],
189            PageReferenceValue::localReference( $map['title']['ns'], $map['title']['key'] )
190        );
191    }
192}