Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
87.04% |
47 / 54 |
|
33.33% |
1 / 3 |
CRAP | |
0.00% |
0 / 1 |
JobFactory | |
87.04% |
47 / 54 |
|
33.33% |
1 / 3 |
15.49 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
newJob | |
96.67% |
29 / 30 |
|
0.00% |
0 / 1 |
7 | |||
needsTitle | |
72.73% |
16 / 22 |
|
0.00% |
0 / 1 |
7.99 |
1 | <?php |
2 | |
3 | namespace MediaWiki\JobQueue; |
4 | |
5 | use Closure; |
6 | use GenericParameterJob; |
7 | use InvalidArgumentException; |
8 | use Job; |
9 | use MediaWiki\Page\PageReference; |
10 | use MediaWiki\Title\Title; |
11 | use Wikimedia\ObjectFactory\ObjectFactory; |
12 | |
13 | /** |
14 | * @since 1.40 |
15 | */ |
16 | class JobFactory { |
17 | |
18 | private ObjectFactory $objectFactory; |
19 | |
20 | /** @var array<array|callable|string> Object specs, see ObjectFactory */ |
21 | private array $jobObjectSpecs; |
22 | |
23 | /** |
24 | * @param ObjectFactory $objectFactory |
25 | * @param array<array|callable|string> $jobObjectSpecs Object specs, see ObjectFactory |
26 | */ |
27 | public function __construct( ObjectFactory $objectFactory, array $jobObjectSpecs ) { |
28 | $this->objectFactory = $objectFactory; |
29 | $this->jobObjectSpecs = $jobObjectSpecs; |
30 | } |
31 | |
32 | /** |
33 | * Create the appropriate object to handle a specific job. |
34 | * |
35 | * @note For backwards compatibility with Job::factory, |
36 | * this method also supports an alternative signature: |
37 | * @code |
38 | * newJob( |
39 | * string $command, |
40 | * PageReference $page, |
41 | * array $params |
42 | * ) |
43 | * @endcode |
44 | * |
45 | * @param string $command Job command |
46 | * @param array $params Job parameters |
47 | * |
48 | * @return Job |
49 | * @throws InvalidArgumentException |
50 | */ |
51 | public function newJob( string $command, $params = [] ): Job { |
52 | if ( !isset( $this->jobObjectSpecs[ $command ] ) ) { |
53 | throw new InvalidArgumentException( "Invalid job command '{$command}'" ); |
54 | } |
55 | |
56 | $spec = $this->jobObjectSpecs[ $command ]; |
57 | $needsTitle = $this->needsTitle( $command, $spec ); |
58 | |
59 | // TODO: revisit support for old method signature |
60 | if ( $params instanceof PageReference ) { |
61 | // Backwards compatibility for old signature ($command, $title, $params) |
62 | $title = Title::newFromPageReference( $params ); |
63 | $params = func_num_args() >= 3 ? func_get_arg( 2 ) : []; |
64 | } elseif ( isset( $params['namespace'] ) && isset( $params['title'] ) ) { |
65 | // Handle job classes that take title as constructor parameter. |
66 | // If a newer classes like GenericParameterJob uses these parameters, |
67 | // then this happens in Job::__construct instead. |
68 | $title = Title::makeTitle( |
69 | $params['namespace'], |
70 | $params['title'] |
71 | ); |
72 | } else { |
73 | // Default title for job classes not implementing GenericParameterJob. |
74 | // This must be a valid title because it not directly passed to |
75 | // our Job constructor, but rather its subclasses which may expect |
76 | // to be able to use it. |
77 | $title = Title::makeTitle( |
78 | NS_SPECIAL, |
79 | 'Blankpage' |
80 | ); |
81 | } |
82 | |
83 | if ( $needsTitle ) { |
84 | $args = [ $title, $params ]; |
85 | } else { |
86 | $args = [ $params ]; |
87 | } |
88 | |
89 | /** @var Job $job */ |
90 | $job = $this->objectFactory->createObject( |
91 | $spec, |
92 | [ |
93 | 'allowClassName' => true, |
94 | 'allowCallable' => true, |
95 | 'extraArgs' => $args, |
96 | 'assertClass' => Job::class |
97 | ] |
98 | ); |
99 | |
100 | // TODO: create a setter, marked @internal |
101 | $job->command = $command; |
102 | return $job; |
103 | } |
104 | |
105 | /** |
106 | * Determines whether the job class needs a Title to be passed |
107 | * as the first parameter to the constructor. |
108 | * |
109 | * @param string $command |
110 | * @param string|array|Closure $spec |
111 | * |
112 | * @return bool |
113 | */ |
114 | private function needsTitle( string $command, $spec ): bool { |
115 | if ( is_callable( $spec ) ) { |
116 | $needsTitle = true; |
117 | } elseif ( is_array( $spec ) ) { |
118 | if ( isset( $spec['needsPage'] ) ) { |
119 | $needsTitle = $spec['needsPage']; |
120 | } elseif ( isset( $spec['class'] ) ) { |
121 | $needsTitle = !is_subclass_of( $spec['class'], |
122 | GenericParameterJob::class ); |
123 | } elseif ( isset( $spec['factory'] ) ) { |
124 | $needsTitle = true; |
125 | } else { |
126 | throw new InvalidArgumentException( |
127 | "Invalid job specification for '{$command}': " . |
128 | "must contain the 'class' or 'factory' key." |
129 | ); |
130 | } |
131 | } elseif ( is_string( $spec ) ) { |
132 | $needsTitle = !is_subclass_of( $spec, |
133 | GenericParameterJob::class ); |
134 | } else { |
135 | throw new InvalidArgumentException( |
136 | "Invalid job specification for '{$command}': " . |
137 | "must be a callable, an object spec array, or a class name" |
138 | ); |
139 | } |
140 | |
141 | return $needsTitle; |
142 | } |
143 | } |