Completed
Push — master ( 4c62ba...bdcabc )
by Marco
06:43
created

Extender::runner()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 5
ccs 2
cts 2
cp 1
rs 9.4286
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php namespace Comodojo\Extender;
2
3
use \Console_Color2;
4
use \Console_Table;
5
use \Comodojo\Extender\Scheduler\Scheduler;
6
use \Comodojo\Extender\Scheduler\Schedule;
7
use \Comodojo\Extender\Runner\JobsRunner;
8
use \Comodojo\Extender\Runner\JobsResult;
9
use \Comodojo\Extender\Job\Job;
10
use \Comodojo\Extender\Log\ExtenderLogger;
11
use \Comodojo\Extender\Events;
12
use \Comodojo\Extender\TasksTable;
13
use \Exception;
14
15
/**
16
 * Extender main class
17
 *
18
 * @package     Comodojo extender
19
 * @author      Marco Giovinazzi <[email protected]>
20
 * @license     GPL-3.0+
21
 *
22
 * LICENSE:
23
 *
24
 * This program is free software: you can redistribute it and/or modify
25
 * it under the terms of the GNU Affero General Public License as
26
 * published by the Free Software Foundation, either version 3 of the
27
 * License, or (at your option) any later version.
28
 *
29
 * This program is distributed in the hope that it will be useful,
30
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32
 * GNU Affero General Public License for more details.
33
 *
34
 * You should have received a copy of the GNU Affero General Public License
35
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
36
 */
37
38
class Extender {
39
40
    // configurable things
41
42
    /**
43
     * Max result lenght (in bytes) retrieved from parent in miltithread mode
44
     *
45
     * @var int
46
     */
47
    private $max_result_bytes_in_multithread = null;
48
49
    /**
50
     * Maximum time (in seconds) the parent will wait for child tasks to be completed (in miltithread mode)
51
     *
52
     * @var int
53
     */
54
    private $max_childs_runtime = null;
55
56
    /**
57
     * Multithread mode
58
     *
59
     * @var bool
60
     */
61
    private $multithread_mode = false;
62
63
    /**
64
     * Verbose mode, if requested via command line arg -v
65
     *
66
     * @var bool
67
     */
68
    private $verbose_mode = false;
69
70
    /**
71
     * Debug mode, if requested via command line arg -V
72
     *
73
     * @var bool
74
     */
75
    private $debug_mode = false;
76
77
    /**
78
     * Summary mode, if requested via command line arg -s
79
     *
80
     * @var bool
81
     */
82
    private $summary_mode = false;
83
84
    /**
85
     * Daemon mode, if requested via command line arg -d
86
     *
87
     * @var bool
88
     */
89
    private $daemon_mode = false;
90
91
    /**
92
     * Timestamp, relative, of current extend() cycle
93
     *
94
     * @var float
95
     */
96
    private $timestamp = null;
97
98
    /**
99
     * Timestamp, absolute, sinnce extender was initiated
100
     *
101
     * @var float
102
     */
103
    private $timestamp_absolute = null;
104
105
    /**
106
     * PID of the parent extender process
107
     *
108
     * @var int
109
     */
110
    private $parent_pid = null;
111
112
    /**
113
     * Set exender in paused mode (no job will be processed)
114
     *
115
     * @var bool
116
     */
117
    private $paused = false;
118
119
    // Helper classes
120
121
    /**
122
     * Events manager instance
123
     *
124
     * @var \Comodojo\Extender\Events
125
     */
126
    private $events = null;
127
128
    /**
129
     * Console_Color2 instance
130
     *
131
     * @var \Console_Color2
132
     */
133
    private $color = null;
134
135
    /**
136
     * Logger instance
137
     *
138
     * @var \Monolog\Logger
139
     */
140
    private $logger = null;
141
142
    /**
143
     * JobsRunner instance
144
     *
145
     * @var \Comodojo\Extender\Runner\JobsRunner
146
     */
147
    private $runner = null;
148
149
    /**
150
     * TasksTable instance
151
     *
152
     * @var \Comodojo\Extender\TasksTable
153
     */
154
    private $tasks = null;
155
156
    // checks and locks are static!
157
158
    // local archives
159
160
    /**
161
     * Failed processes, refreshed each cycle (in daemon mode)
162
     *
163
     * @var int
164
     */
165
    private $failed_processes = 0;
166
167
    /**
168
     * Completed processes
169
     *
170
     * @var int
171
     */
172
    private $completed_processes = 0;
173
174
    /**
175
     * Constructor method
176
     *
177
     * Prepare extender environment, do checks and fire extender.ready event
178
     */
179 12
    final public function __construct() {
180
181
        // check if extender is running from cli
182
183 12
        if ( Checks::cli() === false ) {
184
185
            echo "Extender runs only in php-cli, exiting";
186
187
            self::end(1);
188
189
        }
190
191
        // setup default timezone (in daemon mode, timezone warning may break extender)
192
193 12
        date_default_timezone_set(defined('EXTENDER_TIMEZONE') ? EXTENDER_TIMEZONE : 'Europe/Rome');
194
195 12
        $this->timestamp_absolute = microtime(true);
196
197 12
        $this->color = new Console_Color2();
198
199
        // get command line options (vsdh)
200
201 12
        list($this->verbose_mode, $this->debug_mode, $this->summary_mode, $this->daemon_mode, $help_mode) = self::getCommandlineOptions();
202
203 12
        if ( $help_mode ) {
204
205
            self::showHelp($this->color);
206
207
            self::end(0);
208
209
        }
210
211 12
        $this->logger = ExtenderLogger::create($this->verbose_mode, $this->debug_mode);
212
213
        // do checks
214
215 12
        $check_constants = Checks::constants();
216
217 12
        if ( $check_constants !== true ) {
218
219
            $this->logger->critical($check_constants);
0 ignored issues
show
Bug introduced by
It seems like $check_constants defined by \Comodojo\Extender\Checks::constants() on line 215 can also be of type boolean; however, Monolog\Logger::critical() does only seem to accept string, 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.

Loading history...
220
221
            self::end(1);
222
223
        }
224
225 12
        if ( Checks::signals() === false AND $this->daemon_mode ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
226
227
            $this->logger->critical("Extender cannot run in daemon mode without PHP Process Control Extensions");
228
229
            self::end(1);
230
231
        }
232
233 12
        if ( Checks::database() === false ) {
234
235
            $this->logger->critical("Extender database not available, exiting");
236
237
            self::end(1);
238
239
        }
240
241 12
        $this->tasks = TasksTable::load($this->logger);
242
243 12
        $this->events = Events::load($this->logger);
244
245
        // setup extender parameters
246
247 12
        $this->max_result_bytes_in_multithread = defined('EXTENDER_MAX_RESULT_BYTES') ? filter_var(EXTENDER_MAX_RESULT_BYTES, FILTER_VALIDATE_INT) : 2048;
248
249 12
        $this->max_childs_runtime = defined('EXTENDER_MAX_CHILDS_RUNTIME') ? filter_var(EXTENDER_MAX_CHILDS_RUNTIME, FILTER_VALIDATE_INT) : 300;
250
251 12
        $this->multithread_mode = defined('EXTENDER_MULTITHREAD_ENABLED') ? filter_var(EXTENDER_MULTITHREAD_ENABLED, FILTER_VALIDATE_BOOLEAN) : false;
252
253
        // if in daemon mode, remember parent pid, setup lock and register signal handlers
254
255 12
        if ( $this->daemon_mode ) {
256
257
            $this->parent_pid = posix_getpid();
258
259
            Lock::register($this->parent_pid);
260
261
            $this->adjustNiceness();
262
263
            if ( Checks::signals() ) $this->registerSignals();
264
265
        }
266
267
        // init the runner
268
269 12
        $this->runner = new JobsRunner($this->logger, $this->getMultithreadMode(), $this->max_result_bytes_in_multithread, $this->max_childs_runtime);
270
271 12
        $this->logger->notice("Extender ready");
272
273
        // store initial status and queue information
274
275 12
        Status::dump($this->timestamp_absolute, $this->parent_pid, $this->completed_processes, $this->failed_processes, $this->paused);
0 ignored issues
show
Documentation introduced by
$this->parent_pid is of type integer, but the function expects a object<Comodojo\Extender\itn>.

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);
Loading history...
Documentation introduced by
$this->failed_processes is of type integer, but the function expects a object<Comodojo\Extender\itn>.

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);
Loading history...
276
277 12
        Queue::dump(0, 0);
278
279
        // we are ready to go!
280
281 12
    }
282
283
    /**
284
     * Set max result length (in bytes) that should be read from child tasks
285
     *
286
     * @param   int     $bytes  Maximum length (bytes)
287
     *
288
     * @return  Extender          $this
289
     */
290 3
    final public function setMaxResultLength($bytes) {
291
292 3
        $this->max_result_bytes_in_multithread = filter_var($bytes, FILTER_VALIDATE_INT, array("default" => 2048));
293
294 3
        return $this;
295
296
    }
297
298
    /**
299
     * Get max result length (in bytes)
300
     *
301
     * @return  int     Bytes parent should read (max)
302
     */
303 3
    final public function getMaxResultLength() {
304
305 3
        return $this->max_result_bytes_in_multithread;
306
307
    }
308
309
    /**
310
     * Set maximum time (in seconds) the parent will wait for child tasks to be completed (in miltithread mode)
311
     *
312
     * After $time seconds, parent will start killing tasks
313
     *
314
     * @param   int     $time   Maximum time (seconds)
315
     *
316
     * @return  Extender          $this
317
     */
318 3
    final public function setMaxChildsRuntime($time) {
319
320 3
        $this->max_childs_runtime = filter_var($time, FILTER_VALIDATE_INT, array("min_range" => 1, "default" => 300));
321
322 3
        return $this;
323
324
    }
325
326
    /**
327
     * Get maximum time (in seconds) the parent will wait for child tasks to be completed (in miltithread mode)
328
     *
329
     * @return  int     Time parent will wait for childs to be completed
330
     */
331 3
    final public function getMaxChildsRuntime() {
332
333 3
        return $this->max_childs_runtime;
334
335
    }
336
337
    /**
338
     * Set working mode (single or multithread)
339
     *
340
     * If multithread enabled, extender will use pcntl to fork child tasks
341
     *
342
     * @param   bool    $mode   Enable/disable multithread
343
     *
344
     * @return  Extender          $this
345
     */
346 3
    final public function setMultithreadMode($mode) {
347
348 3
        $this->multithread_mode = filter_var($mode, FILTER_VALIDATE_BOOLEAN);
349
350 3
        return $this;
351
352
    }
353
354
    /**
355
     * Get multithread mode status
356
     *
357
     * @return  bool    True if enabled, false if disabled
358
     */
359 12
    final public function getMultithreadMode() {
360
361 12
        return ($this->multithread_mode AND Checks::multithread()) ? true : false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
362
363
    }
364
365
    /**
366
     * Get daemon mode status
367
     *
368
     * @return  bool    True if enabled, false if disabled
369
     */
370 6
    final public function getDaemonMode() {
371
372 6
        return $this->daemon_mode;
373
374
    }
375
376
    /**
377
     * Get the number of completed processes
378
     *
379
     * @return  int
380
     */
381 3
    final public function getCompletedProcesses() {
382
383 3
        return $this->completed_processes;
384
385
    }
386
387
    /**
388
     * Get the number of failed processes
389
     *
390
     * @return int
391
     */
392 3
    final public function getFailedProcesses() {
393
394 3
        return $this->failed_processes;
395
396
    }
397
398
    /**
399
     * Get current version
400
     *
401
     * @return  string
402
     */
403 3
    final public function getVersion() {
404
405 3
        return Version::getVersion();
406
407
    }
408
409
    /**
410
     * Get events manager
411
     *
412
     * @return  \Comodojo\Extender\Events
413
     */
414 6
    final public function events() {
415
416 6
        return $this->events;
417
418
    }
419
420
    /**
421
     * Get console color instance
422
     *
423
     * @return  \Console_Color2
424
     */
425 3
    final public function color() {
426
427 3
        return $this->color;
428
429
    }
430
431
    /**
432
     * Get internal logger
433
     *
434
     * @return  \Monolog\Logger
435
     */
436 3
    final public function logger() {
437
438 3
        return $this->logger;
439
440
    }
441
442
    /**
443
     * Get jobs' runner
444
     *
445
     * @return  JobsRunner
446
     */
447 3
    final public function runner() {
448
449 3
        return $this->runner;
450
451
    }
452
453
    /**
454
     * Get the tasks' table
455
     *
456
     * @return  TasksTable
457
     */
458 6
    final public function tasks() {
459
460 6
        return $this->tasks;
461
462
    }
463
464
    /**
465
     * Do extend!
466
     *
467
     */
468 3
    public function extend() {
469
470 3
        if ( $this->getDaemonMode() ) {
471
472
        $this->logger->notice("Executing extender in daemon mode");
473
474
        while (true) {
475
476
            $this->cycle();
477
478
            sleep(EXTENDER_IDLE_TIME);
479
480
        }
481
482
        } else {
483
484 3
            $this->logger->notice("Executing extender in single mode");
485
486 3
            $this->cycle();
487
488
        }
489
490 3
    }
491
492
    /**
493
     * Change parent process priority according to EXTENDER_NICENESS
494
     *
495
     */
496
    final public function adjustNiceness() {
497
498
        if ( defined("EXTENDER_PARENT_NICENESS") ) {
499
500
            $niceness = proc_nice(EXTENDER_PARENT_NICENESS);
501
502
            if ( $niceness == false ) $this->logger->warning("Unable to set parent process niceness to ".EXTENDER_PARENT_NICENESS);
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
503
504
        }
505
506
    }
507
508
    /**
509
     * Register signals
510
     *
511
     */
512
    final public function registerSignals() {
513
514
        $pluggable_signals = array(
515
            SIGHUP, SIGCHLD, SIGUSR2, SIGILL, SIGTRAP, SIGABRT, SIGIOT, SIGBUS, SIGFPE,
516
            SIGSEGV, SIGPIPE, SIGALRM, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ,
517
            SIGVTALRM, SIGPROF, SIGWINCH, SIGIO, SIGSYS, SIGBABY
518
        );
519
520
        if ( defined('SIGPOLL') )   $pluggable_signals[] = SIGPOLL;
521
        if ( defined('SIGPWR') )    $pluggable_signals[] = SIGPWR;
522
        if ( defined('SIGSTKFLT') ) $pluggable_signals[] = SIGSTKFLT;
523
524
        // register supported signals
525
526
        pcntl_signal(SIGTERM, array($this, 'sigTermHandler'));
527
528
        pcntl_signal(SIGINT, array($this, 'sigTermHandler'));
529
530
        pcntl_signal(SIGTSTP, array($this, 'sigStopHandler'));
531
532
        pcntl_signal(SIGCONT, array($this, 'sigContHandler'));
533
534
        //pcntl_signal(SIGUSR1, array($this,'sigUsr1Handler'));
0 ignored issues
show
Unused Code Comprehensibility introduced by
77% 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.

Loading history...
535
536
        // register pluggable signals
537
538
        foreach ( $pluggable_signals as $signal ) {
539
540
            pcntl_signal($signal, array($this, 'genericSignalHandler'));
541
542
        }
543
544
        // register shutdown function
545
546
        register_shutdown_function(array($this, 'shutdown'));
547
548
    }
549
550
    /**
551
     * Delete all status file after exit() called
552
     *
553
     */
554 3
    final public function shutdown($force = false) {
555
556 3
        if ( $this->parent_pid == posix_getpid() ) {
557
558
            $this->logger->info("Shutdown in progress, cleaning environment");
559
560
            Lock::release();
561
562
            Status::release();
563
564
            Queue::release();
565
566
            Planner::release();
567
568
        }
569
570 3
        if ( $force === true ) {
571
572 3
            $this->logger->info("Shutdown in progress, cleaning environment");
573
574 3
            Status::release();
575
576 3
            Queue::release();
577
578 3
            Planner::release();
579
580 3
        }
581
582 3
    }
583
584
    /**
585
     * The sigTerm handler.
586
     *
587
     * It kills everything and then exit with status 1
588
     */
589
    final public function sigTermHandler() {
590
591
        if ( $this->parent_pid == posix_getpid() ) {
592
593
            $this->logger->info("Received TERM signal, shutting down extender gracefully");
594
595
            $this->runner->killAll($this->parent_pid);
596
597
            self::end(1);
598
599
        }
600
601
    }
602
603
    /**
604
     * The sigStop handler.
605
     *
606
     * It just pauses extender execution
607
     */
608
    final public function sigStopHandler() {
609
610
        if ( $this->parent_pid == posix_getpid() ) {
611
612
            $this->logger->info("Received STOP signal, pausing extender");
613
614
            $this->paused = true;
615
616
        }
617
618
    }
619
620
    /**
621
     * The sigCont handler.
622
     *
623
     * It just resume extender execution
624
     */
625
    final public function sigContHandler() {
626
627
        if ( $this->parent_pid == posix_getpid() ) {
628
629
            $this->logger->info("Received CONT signal, resuming extender");
630
631
            $this->paused = false;
632
633
        }
634
635
    }
636
637
    /**
638
     * The generig signal handler.
639
     *
640
     * It can be used to handle custom signals
641
     */
642
    final public function genericSignalHandler($signal) {
643
644
        if ( $this->parent_pid == posix_getpid() ) {
645
646
            $this->logger->info("Received ".$signal." signal, firing associated event(s)");
647
648
            $this->events->fire("extender.signal.".$signal, "VOID", $this);
649
650
        }
651
652
    }
653
654 3
    private function cycle() {
655
656
      // fire extender ready event
657
658 3
      $this->events->fire("extender", "VOID", $this);
659
660
      // dispatch signals (if multithread active)
661
662 3
      if ( $this->getMultithreadMode() ) pcntl_signal_dispatch();
663
664
      // if extender is paused (SIGINT), skip to extend
665
666 3
      if ( $this->paused ) return;
667
668
      // fix relative timestamp
669
670 3
      $this->timestamp = microtime(true);
671
672
      // fire tasktable event
673
674 3
      $this->tasks = $this->events->fire("extender.tasks", "TASKSTABLE", $this->tasks);
675
676
      // get the next planned activity interval
677
678 3
      $plans = Planner::get();
679
680 3
      if ( !is_null($plans) AND $this->timestamp < $plans ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
681
682
          // nothing to do right now, still waiting if in daemon mode
683
684
          $this->logger->info("Next planned job: ".date('c', $plans));
685
686
          $this->logger->notice("Extender completed\n");
687
688
          if ( $this->getDaemonMode() === false ) {
689
690
              $this->shutdown(true);
691
692
              self::end(0);
693
694
          }
695
696
          return;
697
698
      }
699
700
      // if no plan is retrieved, try to retrieve it from scheduler
701
702
      try {
703
704
          // get schedules and dispatch schedule event
705
706 3
          list($schedules, $planned) = Scheduler::getSchedules($this->logger, $this->timestamp);
707
708
          // write next planned activity interval
709
710 3
          if ( !is_null($planned) AND $planned != 0 ) Planner::set($planned);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
711
712 3
          $scheduled = new Schedule();
713
714 3
          $scheduled->setSchedules($schedules);
715
716
          // expose the current shcedule via events
717
718 3
          $scheduled = $this->events->fire("extender.schedule", "SCHEDULE", $scheduled);
719
720
          // if no jobs in queue, exit gracefully
721
722 3
          if ( $scheduled->howMany() == 0 ) {
723
724 3
              $this->logger->info("No jobs to process right now, exiting");
725
726 3
              $this->logger->notice("Extender completed\n");
727
728 3
              if ( $this->getDaemonMode() === false ) {
729
730 3
                  $this->shutdown(true);
731
732 3
                  self::end(0);
733
734 3
              }
735
736 3
              return;
737
738
          }
739
740
          // compose jobs
741
742
          foreach ( $scheduled->getSchedules() as $schedule ) {
743
744
              if ( $this->tasks->isRegistered($schedule['task']) ) {
745
746
                  $job = new Job();
747
748
                  $job->setName($schedule['name'])
749
                      ->setId($schedule['id'])
750
                      ->setParameters(unserialize($schedule['params']))
751
                      ->setTask($schedule['task'])
752
                      ->setClass($this->tasks->getClass($schedule['task']));
753
754
                  $this->runner->addJob($job);
755
756
              } else {
757
758
                  $this->logger->warning("Skipping job due to unknown task", array(
759
                      "ID"     => $schedule['id'],
760
                      "NAME"   => $schedule['name'],
761
                      "TASK"   => $schedule['task']
762
                  ));
763
764
              }
765
766
          }
767
768
          // lauch runner
769
770
          $result = $this->runner->run();
771
772
          // free runner for next cycle
773
774
          $this->runner->free();
775
776
          // compose results
777
778
          $results = new JobsResult($result);
779
780
          // update schedules
781
782
          Scheduler::updateSchedules($this->logger, $result);
783
784
          // increment counters
785
786
          foreach ( $result as $r ) {
787
788
              if ( $r[2] ) $this->completed_processes++;
789
790
              else $this->failed_processes++;
791
792
          }
793
794
      } catch (Exception $e) {
795
796
          $this->logger->error($e->getMessage());
797
798
          if ( $this->getDaemonMode() === false ) {
799
800
              self::end(1);
801
802
          }
803
804
      }
805
806
      // fire result event
807
808
      $this->events->fire("extender.result", "VOID", $results);
0 ignored issues
show
Bug introduced by
The variable $results does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
809
810
      $this->logger->notice("Extender completed\n");
811
812
      // show summary (if -s)
813
814
      if ( $this->summary_mode ) self::showSummary($this->timestamp, $result, $this->color);
0 ignored issues
show
Bug introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
815
816
      Status::dump($this->timestamp_absolute, $this->parent_pid, $this->completed_processes, $this->failed_processes, $this->paused);
0 ignored issues
show
Documentation introduced by
$this->parent_pid is of type integer, but the function expects a object<Comodojo\Extender\itn>.

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);
Loading history...
Documentation introduced by
$this->failed_processes is of type integer, but the function expects a object<Comodojo\Extender\itn>.

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);
Loading history...
817
818
      if ( $this->getDaemonMode() === false ) {
819
820
          $this->shutdown(true);
821
822
          self::end(0);
823
824
      }
825
826
    }
827
828
    private static function showHelp($color) {
829
830
        echo Version::getDescription();
831
832
        echo "\nVersion: ".$color->convert("%g".Version::getVersion()."%n");
833
834
        echo "\n\nAvailable options:";
835
836
        echo "\n------------------";
837
838
        echo "\n".$color->convert("%g -v %n").": verbose mode";
839
840
        echo "\n".$color->convert("%g -V %n").": debug mode";
841
842
        echo "\n".$color->convert("%g -s %n").": show summary of executed jobs (if any)";
843
844
        echo "\n".$color->convert("%g -d %n").": run extender in daemon mode";
845
846
        echo "\n".$color->convert("%g -h %n").": show this help";
847
848
        echo "\n\n";
849
850
    }
851
852 12
    private static function getCommandlineOptions() {
853
854 12
        $options = getopt("svdVh");
855
856
        return array(
857 12
            array_key_exists('v', $options) ? true : false,
858 12
            array_key_exists('V', $options) ? true : false,
859 12
            array_key_exists('s', $options) ? true : false,
860 12
            array_key_exists('d', $options) ? true : false,
861 12
            array_key_exists('h', $options) ? true : false
862 12
        );
863
864
    }
865
866
    /**
867
     * @param double $timestamp
868
     */
869
    private static function showSummary($timestamp, $completed_processes, $color) {
870
871
        $header_string = "\n\n --- Comodojo Extender Summary --- ".date('c', $timestamp)."\n\n";
872
873
        $tbl = new Console_Table(CONSOLE_TABLE_ALIGN_LEFT, CONSOLE_TABLE_BORDER_ASCII, 1, null, true);
874
875
        $tbl->setHeaders(array(
876
            'Pid',
877
            'Name',
878
            'Log Id',
879
            'Success',
880
            'Result (truncated)',
881
            'Time elapsed'
882
        ));
883
884
        foreach ( $completed_processes as $key => $completed_process ) {
885
886
            $pid = $completed_process[0];
887
888
            $name = $completed_process[1];
889
890
            $success = $color->convert($completed_process[2] ? "%gYES%n" : "%rNO%n");
891
892
            $result = str_replace(array("\r", "\n"), " ", $completed_process[5]);
893
894
            $result = strlen($result) >= 80 ? substr($result, 0, 80)."..." : $result;
895
896
            $elapsed = empty($completed_process[4]) ? "--" : ($completed_process[4] - $completed_process[3]);
897
898
            $worklog_id = $completed_process[7];
899
900
            $tbl->addRow(array(
901
                $pid,
902
                $name,
903
                $worklog_id,
904
                $success,
905
                $result,
906
                $elapsed
907
            ));
908
909
        }
910
911
        $footer_string = "\n\nTotal script runtime: ".(microtime(true) - $timestamp)." seconds\r\n\n";
912
913
        print $header_string.$tbl->getTable().$footer_string;
914
915
    }
916
917
    /**
918
     * @param integer $returnCode
919
     */
920 3 View Code Duplication
    private static function end($returnCode) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
921
922 3
        if ( defined('COMODOJO_PHPUNIT_TEST') && @constant('COMODOJO_PHPUNIT_TEST') === true ) {
923
924 3
            if ( $returnCode === 1 ) throw new Exception("PHPUnit Test Exception");
925
926 3
            else return $returnCode;
927
928
        } else {
929
930
            exit($returnCode);
931
932
        }
933
934
    }
935
936
}
937