This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Symbiote\QueuedJobs\Services; |
||
4 | |||
5 | use Exception; |
||
6 | use SilverStripe\Control\Controller; |
||
7 | use SilverStripe\Control\Director; |
||
8 | use SilverStripe\Control\Email\Email; |
||
9 | use SilverStripe\Control\Session; |
||
10 | use SilverStripe\Core\Config\Config; |
||
11 | use SilverStripe\Core\Convert; |
||
12 | use SilverStripe\Core\Injector\Injector; |
||
13 | use SilverStripe\Core\Object; |
||
14 | use SilverStripe\Dev\SapphireTest; |
||
15 | use SilverStripe\ORM\DataList; |
||
16 | use SilverStripe\ORM\DataObject; |
||
17 | use SilverStripe\ORM\DB; |
||
18 | use SilverStripe\ORM\FieldType\DBDatetime; |
||
19 | use Symbiote\QueuedJobs\DataObjects\QueuedJobDescriptor; |
||
20 | use SilverStripe\Security\Member; |
||
21 | use SilverStripe\Security\Permission; |
||
22 | use Psr\Log\LoggerInterface; |
||
23 | |||
24 | /** |
||
25 | * A service that can be used for starting, stopping and listing queued jobs. |
||
26 | * |
||
27 | * When a job is first added, it is initialised, its job type determined, then persisted to the database |
||
28 | * |
||
29 | * When the queues are scanned, a job is reloaded and processed. Ignoring the persistence and reloading, it looks |
||
30 | * something like |
||
31 | * |
||
32 | * job->getJobType(); |
||
33 | * job->getJobData(); |
||
34 | * data->write(); |
||
35 | * job->setup(); |
||
36 | * while !job->isComplete |
||
37 | * job->process(); |
||
38 | * job->getJobData(); |
||
39 | * data->write(); |
||
40 | * |
||
41 | * |
||
42 | * @author Marcus Nyeholt <[email protected]> |
||
43 | * @license BSD http://silverstripe.org/bsd-license/ |
||
44 | */ |
||
45 | class QueuedJobService |
||
46 | { |
||
47 | /** |
||
48 | * @var int |
||
49 | */ |
||
50 | private static $stall_threshold = 3; |
||
0 ignored issues
–
show
|
|||
51 | |||
52 | /** |
||
53 | * How much ram will we allow before pausing and releasing the memory? |
||
54 | * |
||
55 | * For instance, set to 268435456 (256MB) to pause this process if used memory exceeds |
||
56 | * this value. This needs to be set to a value lower than the php_ini max_memory as |
||
57 | * the system will otherwise crash before shutdown can be handled gracefully. |
||
58 | * |
||
59 | * This was increased to 256MB for SilverStripe 4.x as framework uses more memory than 3.x |
||
60 | * |
||
61 | * @var int |
||
62 | * @config |
||
63 | */ |
||
64 | private static $memory_limit = 268435456; |
||
0 ignored issues
–
show
|
|||
65 | |||
66 | /** |
||
67 | * Optional time limit (in seconds) to run the service before restarting to release resources. |
||
68 | * |
||
69 | * Defaults to no limit. |
||
70 | * |
||
71 | * @var int |
||
72 | * @config |
||
73 | */ |
||
74 | private static $time_limit = 0; |
||
0 ignored issues
–
show
|
|||
75 | |||
76 | /** |
||
77 | * Timestamp (in seconds) when the queue was started |
||
78 | * |
||
79 | * @var int |
||
80 | */ |
||
81 | protected $startedAt = 0; |
||
82 | |||
83 | /** |
||
84 | * Should "immediate" jobs be managed using the shutdown function? |
||
85 | * |
||
86 | * It is recommended you set up an inotify watch and use that for |
||
87 | * triggering immediate jobs. See the wiki for more information |
||
88 | * |
||
89 | * @var boolean |
||
90 | */ |
||
91 | private static $use_shutdown_function = true; |
||
0 ignored issues
–
show
|
|||
92 | |||
93 | /** |
||
94 | * The location for immediate jobs to be stored in |
||
95 | * |
||
96 | * @var string |
||
97 | */ |
||
98 | private static $cache_dir = 'queuedjobs'; |
||
0 ignored issues
–
show
|
|||
99 | |||
100 | /** |
||
101 | * @var DefaultQueueHandler |
||
102 | */ |
||
103 | public $queueHandler; |
||
104 | |||
105 | /** |
||
106 | * |
||
107 | * @var TaskRunnerEngine |
||
108 | */ |
||
109 | public $queueRunner; |
||
110 | |||
111 | /** |
||
112 | * Register our shutdown handler |
||
113 | */ |
||
114 | public function __construct() |
||
115 | { |
||
116 | // bind a shutdown function to process all 'immediate' queued jobs if needed, but only in CLI mode |
||
117 | if (Config::inst()->get(__CLASS__, 'use_shutdown_function') && Director::is_cli()) { |
||
118 | if (class_exists('PHPUnit_Framework_TestCase') && SapphireTest::is_running_test()) { |
||
0 ignored issues
–
show
This
if statement is empty and can be removed.
This check looks for the bodies of These if (rand(1, 6) > 3) {
//print "Check failed";
} else {
print "Check succeeded";
}
could be turned into if (rand(1, 6) <= 3) {
print "Check succeeded";
}
This is much more concise to read. ![]() The method
is_running_test() cannot be called from this context as it is declared protected in class SilverStripe\Dev\SapphireTest .
This check looks for access to methods that are not accessible from the current context. If you need to make a method accessible to another context you can raise its visibility level in the defining class. ![]() |
|||
119 | // do NOTHING |
||
120 | } else { |
||
121 | register_shutdown_function(array($this, 'onShutdown')); |
||
122 | } |
||
123 | } |
||
124 | if (Config::inst()->get('SilverStripe\\Control\\Email\\Email', 'queued_job_admin_email') == '') { |
||
125 | Config::modify()->set( |
||
126 | 'SilverStripe\\Control\\Email\\Email', 'queued_job_admin_email', |
||
127 | Config::inst()->get('SilverStripe\\Control\\Email\\Email', 'admin_email') |
||
128 | ); |
||
129 | } |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * Adds a job to the queue to be started |
||
134 | * |
||
135 | * Relevant data about the job will be persisted using a QueuedJobDescriptor |
||
136 | * |
||
137 | * @param QueuedJob $job |
||
138 | * The job to start. |
||
139 | * @param $startAfter |
||
140 | * The date (in Y-m-d H:i:s format) to start execution after |
||
141 | * @param int $userId |
||
142 | * The ID of a user to execute the job as. Defaults to the current user |
||
143 | * @return int |
||
144 | */ |
||
145 | public function queueJob(QueuedJob $job, $startAfter = null, $userId = null, $queueName = null) |
||
146 | { |
||
147 | $signature = $job->getSignature(); |
||
148 | |||
149 | // see if we already have this job in a queue |
||
150 | $filter = array( |
||
151 | 'Signature' => $signature, |
||
152 | 'JobStatus' => array( |
||
153 | QueuedJob::STATUS_NEW, |
||
154 | QueuedJob::STATUS_INIT |
||
155 | ) |
||
156 | ); |
||
157 | |||
158 | $existing = DataList::create('Symbiote\\QueuedJobs\\DataObjects\\QueuedJobDescriptor') |
||
159 | ->filter($filter) |
||
160 | ->first(); |
||
161 | |||
162 | if ($existing && $existing->ID) { |
||
163 | return $existing->ID; |
||
164 | } |
||
165 | |||
166 | $jobDescriptor = new QueuedJobDescriptor(); |
||
167 | $jobDescriptor->JobTitle = $job->getTitle(); |
||
168 | $jobDescriptor->JobType = $queueName ? $queueName : $job->getJobType(); |
||
169 | $jobDescriptor->Signature = $signature; |
||
170 | $jobDescriptor->Implementation = get_class($job); |
||
171 | $jobDescriptor->StartAfter = $startAfter; |
||
172 | |||
173 | $jobDescriptor->RunAsID = $userId ? $userId : Member::currentUserID(); |
||
0 ignored issues
–
show
The property
RunAsID does not exist on object<Symbiote\QueuedJo...ts\QueuedJobDescriptor> . Since you implemented __set , maybe consider adding a @property annotation.
Since your code implements the magic setter <?php
/**
* @property int $x
* @property int $y
* @property string $text
*/
class MyLabel
{
private $properties;
private $allowedProperties = array('x', 'y', 'text');
public function __get($name)
{
if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
return $properties[$name];
} else {
return null;
}
}
public function __set($name, $value)
{
if (in_array($name, $this->allowedProperties)) {
$properties[$name] = $value;
} else {
throw new \LogicException("Property $name is not defined.");
}
}
}
Since the property has write access only, you can use the @property-write annotation instead. Of course, you may also just have mistyped another name, in which case you should fix the error. See also the PhpDoc documentation for @property. ![]() The method
SilverStripe\Security\Member::currentUserID() has been deprecated with message: 5.0.0 use Security::getCurrentUser()
This method has been deprecated. The supplier of the class has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead. ![]() |
|||
174 | |||
175 | // copy data |
||
176 | $this->copyJobToDescriptor($job, $jobDescriptor); |
||
0 ignored issues
–
show
$jobDescriptor is of type object<Symbiote\QueuedJo...ts\QueuedJobDescriptor> , but the function expects a object<Symbiote\QueuedJo...Services\JobDescriptor> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
177 | |||
178 | $jobDescriptor->write(); |
||
179 | |||
180 | $this->startJob($jobDescriptor, $startAfter); |
||
0 ignored issues
–
show
$jobDescriptor is of type object<Symbiote\QueuedJo...ts\QueuedJobDescriptor> , but the function expects a object<Symbiote\QueuedJo...Services\JobDescriptor> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
181 | |||
182 | return $jobDescriptor->ID; |
||
183 | } |
||
184 | |||
185 | /** |
||
186 | * Start a job (or however the queue handler determines it should be started) |
||
187 | * |
||
188 | * @param JobDescriptor $jobDescriptor |
||
189 | * @param date $startAfter |
||
190 | */ |
||
191 | public function startJob($jobDescriptor, $startAfter = null) |
||
192 | { |
||
193 | if ($startAfter && strtotime($startAfter) > time()) { |
||
194 | $this->queueHandler->scheduleJob($jobDescriptor, $startAfter); |
||
195 | } else { |
||
196 | // immediately start it on the queue, however that works |
||
197 | $this->queueHandler->startJobOnQueue($jobDescriptor); |
||
198 | } |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * Copies data from a job into a descriptor for persisting |
||
203 | * |
||
204 | * @param QueuedJob $job |
||
205 | * @param JobDescriptor $jobDescriptor |
||
206 | */ |
||
207 | protected function copyJobToDescriptor($job, $jobDescriptor) |
||
208 | { |
||
209 | $data = $job->getJobData(); |
||
210 | |||
211 | $jobDescriptor->TotalSteps = $data->totalSteps; |
||
212 | $jobDescriptor->StepsProcessed = $data->currentStep; |
||
213 | if ($data->isComplete) { |
||
214 | $jobDescriptor->JobStatus = QueuedJob::STATUS_COMPLETE; |
||
215 | $jobDescriptor->JobFinished = date('Y-m-d H:i:s'); |
||
216 | } |
||
217 | |||
218 | $jobDescriptor->SavedJobData = serialize($data->jobData); |
||
219 | $jobDescriptor->SavedJobMessages = serialize($data->messages); |
||
220 | } |
||
221 | |||
222 | /** |
||
223 | * @param QueuedJobDescriptor $jobDescriptor |
||
224 | * @param QueuedJob $job |
||
225 | */ |
||
226 | protected function copyDescriptorToJob($jobDescriptor, $job) |
||
227 | { |
||
228 | $jobData = null; |
||
0 ignored issues
–
show
$jobData is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
229 | $messages = null; |
||
0 ignored issues
–
show
$messages is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
230 | |||
231 | // switching to php's serialize methods... not sure why this wasn't done from the start! |
||
232 | $jobData = @unserialize($jobDescriptor->SavedJobData); |
||
233 | $messages = @unserialize($jobDescriptor->SavedJobMessages); |
||
234 | |||
235 | if (!$jobData) { |
||
236 | // SS's convert:: function doesn't do this detection for us!! |
||
237 | if (function_exists('json_decode')) { |
||
238 | $jobData = json_decode($jobDescriptor->SavedJobData); |
||
239 | $messages = json_decode($jobDescriptor->SavedJobMessages); |
||
240 | } else { |
||
241 | $jobData = Convert::json2obj($jobDescriptor->SavedJobData); |
||
242 | $messages = Convert::json2obj($jobDescriptor->SavedJobMessages); |
||
243 | } |
||
244 | } |
||
245 | |||
246 | $job->setJobData( |
||
247 | $jobDescriptor->TotalSteps, |
||
248 | $jobDescriptor->StepsProcessed, |
||
249 | $jobDescriptor->JobStatus == QueuedJob::STATUS_COMPLETE, |
||
250 | $jobData, |
||
251 | $messages |
||
252 | ); |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * Check the current job queues and see if any of the jobs currently in there should be started. If so, |
||
257 | * return the next job that should be executed |
||
258 | * |
||
259 | * @param string $type Job type |
||
260 | * @return QueuedJobDescriptor |
||
261 | */ |
||
262 | public function getNextPendingJob($type = null) |
||
263 | { |
||
264 | // Filter jobs by type |
||
265 | $type = $type ?: QueuedJob::QUEUED; |
||
266 | $list = QueuedJobDescriptor::get() |
||
267 | ->filter('JobType', $type) |
||
268 | ->sort('ID', 'ASC'); |
||
269 | |||
270 | // see if there's any blocked jobs that need to be resumed |
||
271 | $waitingJob = $list |
||
272 | ->filter('JobStatus', QueuedJob::STATUS_WAIT) |
||
273 | ->first(); |
||
274 | if ($waitingJob) { |
||
275 | return $waitingJob; |
||
276 | } |
||
277 | |||
278 | // If there's an existing job either running or pending, the lets just return false to indicate |
||
279 | // that we're still executing |
||
280 | $runningJob = $list |
||
0 ignored issues
–
show
Are you sure the assignment to
$runningJob is correct as $list->filter('JobStatus...::STATUS_RUN))->first() (which targets SilverStripe\ORM\DataList::first() ) seems to always return null.
This check looks for function or method calls that always return null and whose return value is assigned to a variable. class A
{
function getObject()
{
return null;
}
}
$a = new A();
$object = $a->getObject();
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||
281 | ->filter('JobStatus', array(QueuedJob::STATUS_INIT, QueuedJob::STATUS_RUN)) |
||
282 | ->first(); |
||
283 | if ($runningJob) { |
||
284 | return false; |
||
0 ignored issues
–
show
The return type of
return false; (false ) is incompatible with the return type documented by Symbiote\QueuedJobs\Serv...vice::getNextPendingJob of type Symbiote\QueuedJobs\Data...ueuedJobDescriptor|null .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
285 | } |
||
286 | |||
287 | // Otherwise, lets find any 'new' jobs that are waiting to execute |
||
288 | $newJob = $list |
||
289 | ->filter('JobStatus', QueuedJob::STATUS_NEW) |
||
290 | ->where(sprintf( |
||
291 | '"StartAfter" < \'%s\' OR "StartAfter" IS NULL', |
||
292 | DBDatetime::now()->getValue() |
||
293 | )) |
||
294 | ->first(); |
||
295 | |||
296 | return $newJob; |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Runs an explicit check on all currently running jobs to make sure their "processed" count is incrementing |
||
301 | * between each run. If it's not, then we need to flag it as paused due to an error. |
||
302 | * |
||
303 | * This typically happens when a PHP fatal error is thrown, which can't be picked up by the error |
||
304 | * handler or exception checker; in this case, we detect these stalled jobs later and fix (try) to |
||
305 | * fix them |
||
306 | * |
||
307 | * @param int $queue The queue to check against |
||
308 | */ |
||
309 | public function checkJobHealth($queue = null) |
||
310 | { |
||
311 | $queue = $queue ?: QueuedJob::QUEUED; |
||
312 | // Select all jobs currently marked as running |
||
313 | $runningJobs = QueuedJobDescriptor::get() |
||
314 | ->filter(array( |
||
315 | 'JobStatus' => array( |
||
316 | QueuedJob::STATUS_RUN, |
||
317 | QueuedJob::STATUS_INIT, |
||
318 | ), |
||
319 | 'JobType' => $queue, |
||
320 | )); |
||
321 | |||
322 | // If no steps have been processed since the last run, consider it a broken job |
||
323 | // Only check jobs that have been viewed before. LastProcessedCount defaults to -1 on new jobs. |
||
324 | $stalledJobs = $runningJobs |
||
325 | ->filter('LastProcessedCount:GreaterThanOrEqual', 0) |
||
326 | ->where('"StepsProcessed" = "LastProcessedCount"'); |
||
327 | foreach ($stalledJobs as $stalledJob) { |
||
328 | $this->restartStalledJob($stalledJob); |
||
329 | } |
||
330 | |||
331 | // now, find those that need to be marked before the next check |
||
332 | // foreach job, mark it as having been incremented |
||
333 | foreach ($runningJobs as $job) { |
||
334 | $job->LastProcessedCount = $job->StepsProcessed; |
||
335 | $job->write(); |
||
336 | } |
||
337 | |||
338 | // finally, find the list of broken jobs and send an email if there's some found |
||
339 | $brokenJobs = QueuedJobDescriptor::get()->filter('JobStatus', QueuedJob::STATUS_BROKEN); |
||
340 | View Code Duplication | if ($brokenJobs && $brokenJobs->count()) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
341 | $this->getLogger()->error( |
||
342 | print_r( |
||
343 | array( |
||
344 | 'errno' => 0, |
||
345 | 'errstr' => 'Broken jobs were found in the job queue', |
||
346 | 'errfile' => __FILE__, |
||
347 | 'errline' => __LINE__, |
||
348 | 'errcontext' => array() |
||
349 | ), |
||
350 | true |
||
351 | ) |
||
352 | ); |
||
353 | } |
||
354 | } |
||
355 | |||
356 | /** |
||
357 | * Attempt to restart a stalled job |
||
358 | * |
||
359 | * @param QueuedJobDescriptor $stalledJob |
||
360 | * @return bool True if the job was successfully restarted |
||
361 | */ |
||
362 | protected function restartStalledJob($stalledJob) |
||
363 | { |
||
364 | if ($stalledJob->ResumeCounts < Config::inst()->get(__CLASS__, 'stall_threshold')) { |
||
365 | $stalledJob->restart(); |
||
366 | $message = sprintf( |
||
367 | _t( |
||
368 | 'QueuedJobs.STALLED_JOB_RESTART_MSG', |
||
369 | 'A job named %s appears to have stalled. It will be stopped and restarted, please login to make sure it has continued' |
||
370 | ), |
||
371 | $stalledJob->JobTitle |
||
372 | ); |
||
373 | } else { |
||
374 | $stalledJob->pause(); |
||
375 | $message = sprintf( |
||
376 | _t( |
||
377 | 'QueuedJobs.STALLED_JOB_MSG', |
||
378 | 'A job named %s appears to have stalled. It has been paused, please login to check it' |
||
379 | ), |
||
380 | $stalledJob->JobTitle |
||
381 | ); |
||
382 | } |
||
383 | |||
384 | $this->getLogger()->error($message); |
||
385 | $from = Config::inst()->get('SilverStripe\\Control\\Email\\Email', 'admin_email'); |
||
386 | $to = Config::inst()->get('SilverStripe\\Control\\Email\\Email', 'queued_job_admin_email'); |
||
387 | $subject = _t('QueuedJobs.STALLED_JOB', 'Stalled job'); |
||
388 | $mail = new Email($from, $to, $subject, $message); |
||
389 | $mail->send(); |
||
390 | } |
||
391 | |||
392 | /** |
||
393 | * Prepares the given jobDescriptor for execution. Returns the job that |
||
394 | * will actually be run in a state ready for executing. |
||
395 | * |
||
396 | * Note that this is called each time a job is picked up to be executed from the cron |
||
397 | * job - meaning that jobs that are paused and restarted will have 'setup()' called on them again, |
||
398 | * so your job MUST detect that and act accordingly. |
||
399 | * |
||
400 | * @param QueuedJobDescriptor $jobDescriptor |
||
401 | * The Job descriptor of a job to prepare for execution |
||
402 | * |
||
403 | * @return QueuedJob|boolean |
||
404 | */ |
||
405 | protected function initialiseJob(QueuedJobDescriptor $jobDescriptor) |
||
406 | { |
||
407 | // create the job class |
||
408 | $impl = $jobDescriptor->Implementation; |
||
409 | $job = Object::create($impl); |
||
410 | /* @var $job QueuedJob */ |
||
411 | if (!$job) { |
||
412 | throw new Exception("Implementation $impl no longer exists"); |
||
413 | } |
||
414 | |||
415 | $jobDescriptor->JobStatus = QueuedJob::STATUS_INIT; |
||
416 | $jobDescriptor->write(); |
||
417 | |||
418 | // make sure the data is there |
||
419 | $this->copyDescriptorToJob($jobDescriptor, $job); |
||
420 | |||
421 | // see if it needs 'setup' or 'restart' called |
||
422 | if ($jobDescriptor->StepsProcessed <= 0) { |
||
423 | $job->setup(); |
||
424 | } else { |
||
425 | $job->prepareForRestart(); |
||
426 | } |
||
427 | |||
428 | // make sure the descriptor is up to date with anything changed |
||
429 | $this->copyJobToDescriptor($job, $jobDescriptor); |
||
0 ignored issues
–
show
$jobDescriptor is of type object<Symbiote\QueuedJo...ts\QueuedJobDescriptor> , but the function expects a object<Symbiote\QueuedJo...Services\JobDescriptor> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
430 | $jobDescriptor->write(); |
||
431 | |||
432 | return $job; |
||
433 | } |
||
434 | |||
435 | /** |
||
436 | * Given a {@link QueuedJobDescriptor} mark the job as initialised. Works sort of like a mutex. |
||
437 | * Currently a database lock isn't entirely achievable, due to database adapters not supporting locks. |
||
438 | * This may still have a race condition, but this should minimise the possibility. |
||
439 | * Side effect is the job status will be changed to "Initialised". |
||
440 | * |
||
441 | * Assumption is the job has a status of "Queued" or "Wait". |
||
442 | * |
||
443 | * @param QueuedJobDescriptor $jobDescriptor |
||
444 | * @return boolean |
||
445 | */ |
||
446 | protected function grabMutex(QueuedJobDescriptor $jobDescriptor) |
||
447 | { |
||
448 | // write the status and determine if any rows were affected, for protection against a |
||
449 | // potential race condition where two or more processes init the same job at once. |
||
450 | // This deliberately does not use write() as that would always update LastEdited |
||
451 | // and thus the row would always be affected. |
||
452 | try { |
||
453 | DB::query(sprintf( |
||
454 | 'UPDATE "QueuedJobDescriptor" SET "JobStatus" = \'%s\' WHERE "ID" = %s', |
||
455 | QueuedJob::STATUS_INIT, |
||
456 | $jobDescriptor->ID |
||
457 | )); |
||
458 | } catch (Exception $e) { |
||
459 | return false; |
||
460 | } |
||
461 | |||
462 | if (DB::getConn()->affectedRows() === 0 && $jobDescriptor->JobStatus !== QueuedJob::STATUS_INIT) { |
||
0 ignored issues
–
show
The method
SilverStripe\ORM\DB::getConn() has been deprecated with message: since version 4.0 Use DB::get_conn instead
This method has been deprecated. The supplier of the class has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead. ![]() |
|||
463 | return false; |
||
464 | } |
||
465 | |||
466 | return true; |
||
467 | } |
||
468 | |||
469 | /** |
||
470 | * Start the actual execution of a job. |
||
471 | * The assumption is the jobID refers to a {@link QueuedJobDescriptor} that is status set as "Queued". |
||
472 | * |
||
473 | * This method will continue executing until the job says it's completed |
||
474 | * |
||
475 | * @param int $jobId |
||
476 | * The ID of the job to start executing |
||
477 | * @return boolean |
||
478 | */ |
||
479 | public function runJob($jobId) |
||
0 ignored issues
–
show
runJob uses the super-global variable $_SESSION which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
![]() |
|||
480 | { |
||
481 | // first retrieve the descriptor |
||
482 | $jobDescriptor = DataObject::get_by_id( |
||
483 | 'Symbiote\\QueuedJobs\\DataObjects\\QueuedJobDescriptor', |
||
484 | (int) $jobId |
||
485 | ); |
||
486 | if (!$jobDescriptor) { |
||
487 | throw new Exception("$jobId is invalid"); |
||
488 | } |
||
489 | |||
490 | // now lets see whether we have a current user to run as. Typically, if the job is executing via the CLI, |
||
491 | // we want it to actually execute as the RunAs user - however, if running via the web (which is rare...), we |
||
492 | // want to ensure that the current user has admin privileges before switching. Otherwise, we just run it |
||
493 | // as the currently logged in user and hope for the best |
||
494 | |||
495 | // We need to use $_SESSION directly because SS ties the session to a controller that no longer exists at |
||
496 | // this point of execution in some circumstances |
||
497 | $originalUserID = isset($_SESSION['loggedInAs']) ? $_SESSION['loggedInAs'] : 0; |
||
498 | $originalUser = $originalUserID |
||
499 | ? DataObject::get_by_id('SilverStripe\\Security\\Member', $originalUserID) |
||
500 | : null; |
||
501 | $runAsUser = null; |
||
502 | |||
503 | if (Director::is_cli() || !$originalUser || Permission::checkMember($originalUser, 'ADMIN')) { |
||
504 | $runAsUser = $jobDescriptor->RunAs(); |
||
505 | if ($runAsUser && $runAsUser->exists()) { |
||
506 | // the job runner outputs content way early in the piece, meaning there'll be cookie errors |
||
507 | // if we try and do a normal login, and we only want it temporarily... |
||
508 | if (Controller::has_curr()) { |
||
509 | Session::set('loggedInAs', $runAsUser->ID); |
||
510 | } else { |
||
511 | $_SESSION['loggedInAs'] = $runAsUser->ID; |
||
512 | } |
||
513 | |||
514 | // this is an explicit coupling brought about by SS not having |
||
515 | // a nice way of mocking a user, as it requires session |
||
516 | // nastiness |
||
517 | if (class_exists('SecurityContext')) { |
||
518 | singleton('SecurityContext')->setMember($runAsUser); |
||
519 | } |
||
520 | } |
||
521 | } |
||
522 | |||
523 | // set up a custom error handler for this processing |
||
524 | $errorHandler = new JobErrorHandler(); |
||
525 | |||
526 | $job = null; |
||
527 | |||
528 | $broken = false; |
||
529 | |||
530 | // Push a config context onto the stack for the duration of this job run. |
||
531 | Config::nest(); |
||
532 | |||
533 | if ($this->grabMutex($jobDescriptor)) { |
||
0 ignored issues
–
show
$jobDescriptor of type object<SilverStripe\ORM\DataObject> is not a sub-type of object<Symbiote\QueuedJo...ts\QueuedJobDescriptor> . It seems like you assume a child class of the class SilverStripe\ORM\DataObject to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
534 | try { |
||
535 | $job = $this->initialiseJob($jobDescriptor); |
||
0 ignored issues
–
show
$jobDescriptor of type object<SilverStripe\ORM\DataObject> is not a sub-type of object<Symbiote\QueuedJo...ts\QueuedJobDescriptor> . It seems like you assume a child class of the class SilverStripe\ORM\DataObject to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
536 | |||
537 | // get the job ready to begin. |
||
538 | if (!$jobDescriptor->JobStarted) { |
||
539 | $jobDescriptor->JobStarted = date('Y-m-d H:i:s'); |
||
540 | } else { |
||
541 | $jobDescriptor->JobRestarted = date('Y-m-d H:i:s'); |
||
542 | } |
||
543 | |||
544 | // Only write to job as "Running" if 'isComplete' was NOT set to true |
||
545 | // during setup() or prepareForRestart() |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
42% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
546 | if (!$job->jobFinished()) { |
||
547 | $jobDescriptor->JobStatus = QueuedJob::STATUS_RUN; |
||
548 | $jobDescriptor->write(); |
||
549 | } |
||
550 | |||
551 | $lastStepProcessed = 0; |
||
552 | // have we stalled at all? |
||
553 | $stallCount = 0; |
||
554 | |||
555 | if ($job->SubsiteID && class_exists('Subsite')) { |
||
556 | /** |
||
557 | * @todo Check for 4.x compatibility with Subsites once namespacing is implemented |
||
558 | */ |
||
559 | \Subsite::changeSubsite($job->SubsiteID); |
||
560 | |||
561 | // lets set the base URL as far as Director is concerned so that our URLs are correct |
||
562 | $subsite = DataObject::get_by_id('Subsite', $job->SubsiteID); |
||
563 | if ($subsite && $subsite->exists()) { |
||
564 | $domain = $subsite->domain(); |
||
565 | $base = rtrim(Director::protocol() . $domain, '/') . '/'; |
||
566 | |||
567 | Config::modify()->set('SilverStripe\\Control\\Director', 'alternate_base_url', $base); |
||
568 | } |
||
569 | } |
||
570 | |||
571 | // while not finished |
||
572 | while (!$job->jobFinished() && !$broken) { |
||
573 | // see that we haven't been set to 'paused' or otherwise by another process |
||
574 | $jobDescriptor = DataObject::get_by_id( |
||
575 | 'Symbiote\\QueuedJobs\\DataObjects\\QueuedJobDescriptor', |
||
576 | (int) $jobId |
||
577 | ); |
||
578 | if (!$jobDescriptor || !$jobDescriptor->exists()) { |
||
579 | $broken = true; |
||
580 | $this->getLogger()->error( |
||
581 | print_r( |
||
582 | array( |
||
583 | 'errno' => 0, |
||
584 | 'errstr' => 'Job descriptor ' . $jobId . ' could not be found', |
||
585 | 'errfile' => __FILE__, |
||
586 | 'errline' => __LINE__, |
||
587 | 'errcontext' => array() |
||
588 | ), |
||
589 | true |
||
590 | ) |
||
591 | ); |
||
592 | break; |
||
593 | } |
||
594 | if ($jobDescriptor->JobStatus != QueuedJob::STATUS_RUN) { |
||
595 | // we've been paused by something, so we'll just exit |
||
596 | $job->addMessage(sprintf(_t('QueuedJobs.JOB_PAUSED', "Job paused at %s"), date('Y-m-d H:i:s'))); |
||
597 | $broken = true; |
||
598 | } |
||
599 | |||
600 | if (!$broken) { |
||
601 | try { |
||
602 | $job->process(); |
||
603 | } catch (Exception $e) { |
||
604 | // okay, we'll just catch this exception for now |
||
605 | $job->addMessage( |
||
606 | sprintf( |
||
607 | _t('QueuedJobs.JOB_EXCEPT', 'Job caused exception %s in %s at line %s'), |
||
608 | $e->getMessage(), |
||
609 | $e->getFile(), |
||
610 | $e->getLine() |
||
611 | ), |
||
612 | 'ERROR' |
||
0 ignored issues
–
show
The call to
QueuedJob::addMessage() has too many arguments starting with 'ERROR' .
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the ![]() |
|||
613 | ); |
||
614 | $this->getLogger()->error($e->getMessage()); |
||
615 | $jobDescriptor->JobStatus = QueuedJob::STATUS_BROKEN; |
||
616 | } |
||
617 | |||
618 | // now check the job state |
||
619 | $data = $job->getJobData(); |
||
620 | if ($data->currentStep == $lastStepProcessed) { |
||
621 | $stallCount++; |
||
622 | } |
||
623 | |||
624 | if ($stallCount > Config::inst()->get(__CLASS__, 'stall_threshold')) { |
||
625 | $broken = true; |
||
626 | $job->addMessage( |
||
627 | sprintf( |
||
628 | _t('QueuedJobs.JOB_STALLED', "Job stalled after %s attempts - please check"), |
||
629 | $stallCount |
||
630 | ), |
||
631 | 'ERROR' |
||
0 ignored issues
–
show
The call to
QueuedJob::addMessage() has too many arguments starting with 'ERROR' .
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the ![]() |
|||
632 | ); |
||
633 | $jobDescriptor->JobStatus = QueuedJob::STATUS_BROKEN; |
||
634 | } |
||
635 | |||
636 | // now we'll be good and check our memory usage. If it is too high, we'll set the job to |
||
637 | // a 'Waiting' state, and let the next processing run pick up the job. |
||
638 | if ($this->isMemoryTooHigh()) { |
||
639 | $job->addMessage(sprintf( |
||
640 | _t('QueuedJobs.MEMORY_RELEASE', 'Job releasing memory and waiting (%s used)'), |
||
641 | $this->humanReadable($this->getMemoryUsage()) |
||
642 | )); |
||
643 | $jobDescriptor->JobStatus = QueuedJob::STATUS_WAIT; |
||
644 | $broken = true; |
||
645 | } |
||
646 | |||
647 | // Also check if we are running too long |
||
648 | if ($this->hasPassedTimeLimit()) { |
||
649 | $job->addMessage(_t( |
||
650 | 'QueuedJobs.TIME_LIMIT', |
||
651 | 'Queue has passed time limit and will restart before continuing' |
||
652 | )); |
||
653 | $jobDescriptor->JobStatus = QueuedJob::STATUS_WAIT; |
||
654 | $broken = true; |
||
655 | } |
||
656 | } |
||
657 | |||
658 | if ($jobDescriptor) { |
||
659 | $this->copyJobToDescriptor($job, $jobDescriptor); |
||
0 ignored issues
–
show
It seems like
$job defined by $this->initialiseJob($jobDescriptor) on line 535 can also be of type boolean ; however, Symbiote\QueuedJobs\Serv...::copyJobToDescriptor() does only seem to accept object<Symbiote\QueuedJobs\Services\QueuedJob> , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() $jobDescriptor is of type object<SilverStripe\ORM\DataObject> , but the function expects a object<Symbiote\QueuedJo...Services\JobDescriptor> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
660 | $jobDescriptor->write(); |
||
661 | View Code Duplication | } else { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
662 | $this->getLogger()->error( |
||
663 | print_r( |
||
664 | array( |
||
665 | 'errno' => 0, |
||
666 | 'errstr' => 'Job descriptor has been set to null', |
||
667 | 'errfile' => __FILE__, |
||
668 | 'errline' => __LINE__, |
||
669 | 'errcontext' => array() |
||
670 | ), |
||
671 | true |
||
672 | ) |
||
673 | ); |
||
674 | $broken = true; |
||
675 | } |
||
676 | } |
||
677 | |||
678 | // a last final save. The job is complete by now |
||
679 | if ($jobDescriptor) { |
||
680 | $jobDescriptor->write(); |
||
681 | } |
||
682 | |||
683 | if (!$broken) { |
||
684 | $job->afterComplete(); |
||
685 | $jobDescriptor->cleanupJob(); |
||
686 | } |
||
687 | } catch (Exception $e) { |
||
688 | // okay, we'll just catch this exception for now |
||
689 | $this->getLogger()->error($e->getMessage()); |
||
690 | $jobDescriptor->JobStatus = QueuedJob::STATUS_BROKEN; |
||
691 | $jobDescriptor->write(); |
||
692 | $broken = true; |
||
693 | } |
||
694 | } |
||
695 | |||
696 | $errorHandler->clear(); |
||
697 | |||
698 | Config::unnest(); |
||
699 | |||
700 | // okay let's reset our user if we've got an original |
||
701 | if ($runAsUser && $originalUser) { |
||
702 | Session::clear("loggedInAs"); |
||
703 | if ($originalUser) { |
||
704 | Session::set("loggedInAs", $originalUser->ID); |
||
705 | } |
||
706 | } |
||
707 | |||
708 | return !$broken; |
||
709 | } |
||
710 | |||
711 | /** |
||
712 | * Start timer |
||
713 | */ |
||
714 | protected function markStarted() |
||
715 | { |
||
716 | if ($this->startedAt) { |
||
717 | $this->startedAt = DBDatetime::now()->Format('U'); |
||
0 ignored issues
–
show
It seems like
\SilverStripe\ORM\FieldT...ime::now()->Format('U') can also be of type string . However, the property $startedAt is declared as type integer . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||
718 | } |
||
719 | } |
||
720 | |||
721 | /** |
||
722 | * Is execution time too long? |
||
723 | * |
||
724 | * @return bool True if the script has passed the configured time_limit |
||
725 | */ |
||
726 | protected function hasPassedTimeLimit() |
||
727 | { |
||
728 | // Ensure a limit exists |
||
729 | $limit = Config::inst()->get(__CLASS__, 'time_limit'); |
||
730 | if (!$limit) { |
||
731 | return false; |
||
732 | } |
||
733 | |||
734 | // Ensure started date is set |
||
735 | $this->markStarted(); |
||
736 | |||
737 | // Check duration |
||
738 | $now = DBDatetime::now()->Format('U'); |
||
739 | return $now > $this->startedAt + $limit; |
||
740 | } |
||
741 | |||
742 | /** |
||
743 | * Is memory usage too high? |
||
744 | * |
||
745 | * @return bool |
||
746 | */ |
||
747 | protected function isMemoryTooHigh() |
||
748 | { |
||
749 | $used = $this->getMemoryUsage(); |
||
750 | $limit = $this->getMemoryLimit(); |
||
751 | return $limit && ($used > $limit); |
||
752 | } |
||
753 | |||
754 | /** |
||
755 | * Get peak memory usage of this application |
||
756 | * |
||
757 | * @return float |
||
758 | */ |
||
759 | protected function getMemoryUsage() |
||
760 | { |
||
761 | // Note we use real_usage = false |
||
762 | // http://stackoverflow.com/questions/15745385/memory-get-peak-usage-with-real-usage |
||
763 | // Also we use the safer peak memory usage |
||
764 | return (float)memory_get_peak_usage(false); |
||
765 | } |
||
766 | |||
767 | /** |
||
768 | * Determines the memory limit (in bytes) for this application |
||
769 | * Limits to the smaller of memory_limit configured via php.ini or silverstripe config |
||
770 | * |
||
771 | * @return float Memory limit in bytes |
||
772 | */ |
||
773 | protected function getMemoryLimit() |
||
774 | { |
||
775 | // Limit to smaller of explicit limit or php memory limit |
||
776 | $limit = $this->parseMemory(Config::inst()->get(__CLASS__, 'memory_limit')); |
||
777 | if ($limit) { |
||
778 | return $limit; |
||
779 | } |
||
780 | |||
781 | // Fallback to php memory limit |
||
782 | $phpLimit = $this->getPHPMemoryLimit(); |
||
783 | if ($phpLimit) { |
||
784 | return $phpLimit; |
||
785 | } |
||
786 | } |
||
787 | |||
788 | /** |
||
789 | * Calculate the current memory limit of the server |
||
790 | * |
||
791 | * @return float |
||
792 | */ |
||
793 | protected function getPHPMemoryLimit() |
||
794 | { |
||
795 | return $this->parseMemory(trim(ini_get("memory_limit"))); |
||
796 | } |
||
797 | |||
798 | /** |
||
799 | * Convert memory limit string to bytes. |
||
800 | * Based on implementation in install.php5 |
||
801 | * |
||
802 | * @param string $memString |
||
803 | * @return float |
||
804 | */ |
||
805 | protected function parseMemory($memString) |
||
806 | { |
||
807 | switch (strtolower(substr($memString, -1))) { |
||
808 | case "b": |
||
809 | return round(substr($memString, 0, -1)); |
||
810 | case "k": |
||
811 | return round(substr($memString, 0, -1) * 1024); |
||
812 | case "m": |
||
813 | return round(substr($memString, 0, -1) * 1024 * 1024); |
||
814 | case "g": |
||
815 | return round(substr($memString, 0, -1) * 1024 * 1024 * 1024); |
||
816 | default: |
||
817 | return round($memString); |
||
818 | } |
||
819 | } |
||
820 | |||
821 | protected function humanReadable($size) |
||
822 | { |
||
823 | $filesizename = array(" Bytes", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"); |
||
824 | return $size ? round($size/pow(1024, ($i = floor(log($size, 1024)))), 2) . $filesizename[$i] : '0 Bytes'; |
||
825 | } |
||
826 | |||
827 | |||
828 | /** |
||
829 | * Gets a list of all the current jobs (or jobs that have recently finished) |
||
830 | * |
||
831 | * @param string $type |
||
832 | * if we're after a particular job list |
||
833 | * @param int $includeUpUntil |
||
834 | * The number of seconds to include jobs that have just finished, allowing a job list to be built that |
||
835 | * includes recently finished jobs |
||
836 | * @return QueuedJobDescriptor |
||
837 | */ |
||
838 | public function getJobList($type = null, $includeUpUntil = 0) |
||
839 | { |
||
840 | return DataObject::get( |
||
841 | 'Symbiote\\QueuedJobs\\DataObjects\\QueuedJobDescriptor', |
||
842 | $this->getJobListFilter($type, $includeUpUntil) |
||
843 | ); |
||
844 | } |
||
845 | |||
846 | /** |
||
847 | * Return the SQL filter used to get the job list - this is used by the UI for displaying the job list... |
||
848 | * |
||
849 | * @param string $type |
||
850 | * if we're after a particular job list |
||
851 | * @param int $includeUpUntil |
||
852 | * The number of seconds to include jobs that have just finished, allowing a job list to be built that |
||
853 | * includes recently finished jobs |
||
854 | * @return string |
||
855 | */ |
||
856 | public function getJobListFilter($type = null, $includeUpUntil = 0) |
||
857 | { |
||
858 | $util = singleton('Symbiote\\QueuedJobs\\QJUtils'); |
||
859 | |||
860 | $filter = array('JobStatus <>' => QueuedJob::STATUS_COMPLETE); |
||
861 | if ($includeUpUntil) { |
||
862 | $filter['JobFinished > '] = date('Y-m-d H:i:s', time() - $includeUpUntil); |
||
863 | } |
||
864 | |||
865 | $filter = $util->dbQuote($filter, ' OR '); |
||
866 | |||
867 | if ($type) { |
||
0 ignored issues
–
show
The expression
$type of type string|null is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
868 | $filter = $util->dbQuote(array('JobType =' => (string) $type)). ' AND ('.$filter.')'; |
||
869 | } |
||
870 | |||
871 | return $filter; |
||
872 | } |
||
873 | |||
874 | /** |
||
875 | * Process the job queue with the current queue runner |
||
876 | * |
||
877 | * @param string $queue |
||
878 | */ |
||
879 | public function runQueue($queue) |
||
880 | { |
||
881 | $this->checkJobHealth($queue); |
||
882 | $this->queueRunner->runQueue($queue); |
||
883 | } |
||
884 | |||
885 | /** |
||
886 | * Process all jobs from a given queue |
||
887 | * |
||
888 | * @param string $name The job queue to completely process |
||
889 | */ |
||
890 | public function processJobQueue($name) |
||
0 ignored issues
–
show
processJobQueue uses the super-global variable $_SESSION which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
![]() |
|||
891 | { |
||
892 | // Start timer to measure lifetime |
||
893 | $this->markStarted(); |
||
894 | |||
895 | // Begin main loop |
||
896 | do { |
||
897 | if (class_exists('Subsite')) { |
||
898 | // clear subsite back to default to prevent any subsite changes from leaking to |
||
899 | // subsequent actions |
||
900 | /** |
||
901 | * @todo Check for 4.x compatibility with Subsites once namespacing is implemented |
||
902 | */ |
||
903 | \Subsite::changeSubsite(0); |
||
904 | } |
||
905 | if (Controller::has_curr()) { |
||
906 | Session::clear('loggedInAs'); |
||
907 | } else { |
||
908 | unset($_SESSION['loggedInAs']); |
||
909 | } |
||
910 | |||
911 | if (class_exists('SecurityContext')) { |
||
912 | singleton('SecurityContext')->setMember(null); |
||
913 | } |
||
914 | |||
915 | $job = $this->getNextPendingJob($name); |
||
916 | if ($job) { |
||
917 | $success = $this->runJob($job->ID); |
||
918 | if (!$success) { |
||
919 | // make sure job is null so it doesn't continue the current |
||
920 | // processing loop. Next queue executor can pick up where |
||
921 | // things left off |
||
922 | $job = null; |
||
923 | } |
||
924 | } |
||
925 | } while ($job); |
||
926 | } |
||
927 | |||
928 | /** |
||
929 | * When PHP shuts down, we want to process all of the immediate queue items |
||
930 | * |
||
931 | * We use the 'getNextPendingJob' method, instead of just iterating the queue, to ensure |
||
932 | * we ignore paused or stalled jobs. |
||
933 | */ |
||
934 | public function onShutdown() |
||
935 | { |
||
936 | $this->processJobQueue(QueuedJob::IMMEDIATE); |
||
937 | } |
||
938 | |||
939 | /** |
||
940 | * Get a logger |
||
941 | * @return LoggerInterface |
||
942 | */ |
||
943 | public function getLogger() |
||
944 | { |
||
945 | return Injector::inst()->get(LoggerInterface::class); |
||
946 | } |
||
947 | } |
||
948 | |||
949 | /** |
||
950 | * Class used to handle errors for a single job |
||
951 | */ |
||
952 | class JobErrorHandler |
||
0 ignored issues
–
show
|
|||
953 | { |
||
954 | public function __construct() |
||
955 | { |
||
956 | set_error_handler(array($this, 'handleError')); |
||
957 | } |
||
958 | |||
959 | public function clear() |
||
960 | { |
||
961 | restore_error_handler(); |
||
962 | } |
||
963 | |||
964 | public function handleError($errno, $errstr, $errfile, $errline) |
||
965 | { |
||
966 | if (error_reporting()) { |
||
967 | // Don't throw E_DEPRECATED in PHP 5.3+ |
||
968 | if (defined('E_DEPRECATED')) { |
||
969 | if ($errno == E_DEPRECATED || $errno = E_USER_DEPRECATED) { |
||
970 | return; |
||
971 | } |
||
972 | } |
||
973 | |||
974 | switch ($errno) { |
||
975 | case E_NOTICE: |
||
976 | case E_USER_NOTICE: |
||
977 | case E_STRICT: |
||
978 | break; |
||
979 | default: |
||
980 | throw new Exception($errstr . " in $errfile at line $errline", $errno); |
||
981 | break; |
||
0 ignored issues
–
show
break; does not seem to be reachable.
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed. Unreachable code is most often the result of function fx() {
try {
doSomething();
return true;
}
catch (\Exception $e) {
return false;
}
return false;
}
In the above example, the last ![]() |
|||
982 | } |
||
983 | } |
||
984 | } |
||
985 | } |
||
986 |
This check marks private properties in classes that are never used. Those properties can be removed.