Completed
Push — master ( 8a4ca9...acd23c )
by Marco
02:46
created

src/Extender.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
        $default_timezone = ini_get('date.timezone');
194
195 12
        if ( empty($default_timezone) ) {
196
197
            date_default_timezone_set(defined('EXTENDER_TIMEZONE') ? EXTENDER_TIMEZONE : 'Europe/Rome');
198
199
        }
200
201 12
        $this->timestamp_absolute = microtime(true);
202
203 12
        $this->color = new Console_Color2();
204
205
        // get command line options (vsdh)
206
207 12
        list($this->verbose_mode, $this->debug_mode, $this->summary_mode, $this->daemon_mode, $help_mode) = self::getCommandlineOptions();
208
209 12
        if ( $help_mode ) {
210
211
            self::showHelp($this->color);
212
213
            self::end(0);
214
215
        }
216
217 12
        $this->logger = ExtenderLogger::create($this->verbose_mode, $this->debug_mode);
218
219
        // do checks
220
221 12
        $check_constants = Checks::constants();
222
223 12
        if ( $check_constants !== true ) {
224
225
            $this->logger->critical($check_constants);
226
227
            self::end(1);
228
229
        }
230
231 12
        if ( Checks::signals() === false AND $this->daemon_mode ) {
232
233
            $this->logger->critical("Extender cannot run in daemon mode without PHP Process Control Extensions");
234
235
            self::end(1);
236
237
        }
238
239 12
        if ( Checks::database() === false ) {
240
241
            $this->logger->critical("Extender database not available, exiting");
242
243
            self::end(1);
244
245
        }
246
247 12
        $this->tasks = TasksTable::load($this->logger);
248
249 12
        $this->events = Events::load($this->logger);
250
251
        // setup extender parameters
252
253 12
        $this->max_result_bytes_in_multithread = defined('EXTENDER_MAX_RESULT_BYTES') ? filter_var(EXTENDER_MAX_RESULT_BYTES, FILTER_VALIDATE_INT) : 2048;
254
255 12
        $this->max_childs_runtime = defined('EXTENDER_MAX_CHILDS_RUNTIME') ? filter_var(EXTENDER_MAX_CHILDS_RUNTIME, FILTER_VALIDATE_INT) : 600;
256
257 12
        $this->multithread_mode = defined('EXTENDER_MULTITHREAD_ENABLED') ? filter_var(EXTENDER_MULTITHREAD_ENABLED, FILTER_VALIDATE_BOOLEAN) : false;
258
259
        // if in daemon mode, remember parent pid, setup lock and register signal handlers
260
261 12
        if ( $this->daemon_mode ) {
262
263
            $this->parent_pid = posix_getpid();
264
265
            Lock::register($this->parent_pid);
266
267
            $this->adjustNiceness();
268
269
            if ( Checks::signals() ) $this->registerSignals();
270
271
        }
272
273
        // init the runner
274
275 12
        $this->runner = new JobsRunner($this->logger, $this->getMultithreadMode(), $this->max_result_bytes_in_multithread, $this->max_childs_runtime);
276
277 12
        $this->logger->notice("Extender ready");
278
279
        // store initial status and queue information
280
281 12
        Status::dump($this->timestamp_absolute, $this->parent_pid, $this->completed_processes, $this->failed_processes, $this->paused);
282
283 12
        Queue::dump(0, 0);
284
285
        // we are ready to go!
286
287 12
    }
288
289
    /**
290
     * Set max result length (in bytes) that should be read from child tasks
291
     *
292
     * @param   int     $bytes  Maximum length (bytes)
293
     *
294
     * @return  Extender          $this
295
     */
296 3
    final public function setMaxResultLength($bytes) {
297
298 3
        $this->max_result_bytes_in_multithread = filter_var($bytes, FILTER_VALIDATE_INT, array("default" => 2048));
299
300 3
        return $this;
301
302
    }
303
304
    /**
305
     * Get max result length (in bytes)
306
     *
307
     * @return  int     Bytes parent should read (max)
308
     */
309 3
    final public function getMaxResultLength() {
310
311 3
        return $this->max_result_bytes_in_multithread;
312
313
    }
314
315
    /**
316
     * Set maximum time (in seconds) the parent will wait for child tasks to be completed (in miltithread mode)
317
     *
318
     * After $time seconds, parent will start killing tasks
319
     *
320
     * @param   int     $time   Maximum time (seconds)
321
     *
322
     * @return  Extender          $this
323
     */
324 3
    final public function setMaxChildsRuntime($time) {
325
326 3
        $this->max_childs_runtime = filter_var($time, FILTER_VALIDATE_INT, array("min_range" => 1, "default" => 300));
327
328 3
        return $this;
329
330
    }
331
332
    /**
333
     * Get maximum time (in seconds) the parent will wait for child tasks to be completed (in miltithread mode)
334
     *
335
     * @return  int     Time parent will wait for childs to be completed
336
     */
337 3
    final public function getMaxChildsRuntime() {
338
339 3
        return $this->max_childs_runtime;
340
341
    }
342
343
    /**
344
     * Set working mode (single or multithread)
345
     *
346
     * If multithread enabled, extender will use pcntl to fork child tasks
347
     *
348
     * @param   bool    $mode   Enable/disable multithread
349
     *
350
     * @return  Extender          $this
351
     */
352 3
    final public function setMultithreadMode($mode) {
353
354 3
        $this->multithread_mode = filter_var($mode, FILTER_VALIDATE_BOOLEAN);
355
356 3
        return $this;
357
358
    }
359
360
    /**
361
     * Get multithread mode status
362
     *
363
     * @return  bool    True if enabled, false if disabled
364
     */
365 12
    final public function getMultithreadMode() {
366
367 12
        return ($this->multithread_mode AND Checks::multithread()) ? true : false;
368
369
    }
370
371
    /**
372
     * Get daemon mode status
373
     *
374
     * @return  bool    True if enabled, false if disabled
375
     */
376 6
    final public function getDaemonMode() {
377
378 6
        return $this->daemon_mode;
379
380
    }
381
382
    /**
383
     * Get the number of completed processes
384
     *
385
     * @return  int
386
     */
387 3
    final public function getCompletedProcesses() {
388
389 3
        return $this->completed_processes;
390
391
    }
392
393
    /**
394
     * Get the number of failed processes
395
     *
396
     * @return int
397
     */
398 3
    final public function getFailedProcesses() {
399
400 3
        return $this->failed_processes;
401
402
    }
403
404
    /**
405
     * Get current version
406
     *
407
     * @return  string
408
     */
409 3
    final public function getVersion() {
410
411 3
        return Version::getVersion();
412
413
    }
414
415
    /**
416
     * Get events manager
417
     *
418
     * @return  \Comodojo\Extender\Events
419
     */
420 6
    final public function events() {
421
422 6
        return $this->events;
423
424
    }
425
426
    /**
427
     * Get console color instance
428
     *
429
     * @return  \Console_Color2
430
     */
431 3
    final public function color() {
432
433 3
        return $this->color;
434
435
    }
436
437
    /**
438
     * Get internal logger
439
     *
440
     * @return  \Monolog\Logger
441
     */
442 3
    final public function logger() {
443
444 3
        return $this->logger;
445
446
    }
447
448
    /**
449
     * Get jobs' runner
450
     *
451
     * @return  JobsRunner
452
     */
453 3
    final public function runner() {
454
455 3
        return $this->runner;
456
457
    }
458
459
    /**
460
     * Get the tasks' table
461
     *
462
     * @return  TasksTable
463
     */
464 6
    final public function tasks() {
465
466 6
        return $this->tasks;
467
468
    }
469
470
    /**
471
     * Do extend!
472
     *
473
     */
474 3
    public function extend() {
475
476 3
        if ( $this->getDaemonMode() ) {
477
478
            $this->logger->notice("Executing extender in daemon mode");
479
480
            $idle_time = defined("EXTENDER_IDLE_TIME") ? filter_var(EXTENDER_IDLE_TIME, FILTER_VALIDATE_INT, array(
481
                'options' => array(
482
                    'default' => 1,
483
                    'min_range' => 1
484
                )
485
            )) : 1;
486
487
            while (true) {
488
489
                $this->cycle();
490
491
                sleep($idle_time);
492
493
            }
494
495
        } else {
496
497 3
            $this->logger->notice("Executing extender in single mode");
498
499 3
            $this->cycle();
500
501
        }
502
503 3
    }
504
505
    /**
506
     * Change parent process priority according to EXTENDER_NICENESS
507
     *
508
     */
509
    final public function adjustNiceness() {
510
511
        if ( defined("EXTENDER_PARENT_NICENESS") ) {
512
513
            $niceness = proc_nice(EXTENDER_PARENT_NICENESS);
514
515
            if ( $niceness == false ) $this->logger->warning("Unable to set parent process niceness to ".EXTENDER_PARENT_NICENESS);
516
517
        }
518
519
    }
520
521
    /**
522
     * Register signals
523
     *
524
     */
525
    final public function registerSignals() {
526
527
        $pluggable_signals = array(
528
            SIGHUP, SIGCHLD, SIGUSR2, SIGILL, SIGTRAP, SIGABRT, SIGIOT, SIGBUS, SIGFPE,
529
            SIGSEGV, SIGPIPE, SIGALRM, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ,
530
            SIGVTALRM, SIGPROF, SIGWINCH, SIGIO, SIGSYS, SIGBABY
531
        );
532
533
        if ( defined('SIGPOLL') )   $pluggable_signals[] = SIGPOLL;
534
        if ( defined('SIGPWR') )    $pluggable_signals[] = SIGPWR;
535
        if ( defined('SIGSTKFLT') ) $pluggable_signals[] = SIGSTKFLT;
536
537
        // register supported signals
538
539
        pcntl_signal(SIGTERM, array($this, 'sigTermHandler'));
540
541
        pcntl_signal(SIGINT, array($this, 'sigTermHandler'));
542
543
        pcntl_signal(SIGTSTP, array($this, 'sigStopHandler'));
544
545
        pcntl_signal(SIGCONT, array($this, 'sigContHandler'));
546
547
        //pcntl_signal(SIGUSR1, array($this,'sigUsr1Handler'));
548
549
        // register pluggable signals
550
551
        foreach ( $pluggable_signals as $signal ) {
552
553
            pcntl_signal($signal, array($this, 'genericSignalHandler'));
554
555
        }
556
557
        // register shutdown function
558
559
        register_shutdown_function(array($this, 'shutdown'));
560
561
    }
562
563
    /**
564
     * Delete all status file after exit() called
565
     *
566
     */
567 3
    final public function shutdown($force = false) {
568
569 3
        if ( $this->parent_pid == posix_getpid() ) {
570
571
            $this->logger->info("Shutdown in progress, cleaning environment");
572
573
            Lock::release();
574
575
            Status::release();
576
577
            Queue::release();
578
579
            Planner::release();
580
581
        }
582
583 3
        if ( $force === true ) {
584
585 3
            $this->logger->info("Shutdown in progress, cleaning environment");
586
587 3
            Status::release();
588
589 3
            Queue::release();
590
591 3
            Planner::release();
592
593 3
        }
594
595 3
    }
596
597
    /**
598
     * The sigTerm handler.
599
     *
600
     * It kills everything and then exit with status 1
601
     */
602
    final public function sigTermHandler() {
603
604
        if ( $this->parent_pid == posix_getpid() ) {
605
606
            $this->logger->info("Received TERM signal, shutting down extender gracefully");
607
608
            $this->runner->killAll($this->parent_pid);
609
610
            self::end(1);
611
612
        }
613
614
    }
615
616
    /**
617
     * The sigStop handler.
618
     *
619
     * It just pauses extender execution
620
     */
621
    final public function sigStopHandler() {
622
623
        if ( $this->parent_pid == posix_getpid() ) {
624
625
            $this->logger->info("Received STOP signal, pausing extender");
626
627
            $this->paused = true;
628
629
        }
630
631
    }
632
633
    /**
634
     * The sigCont handler.
635
     *
636
     * It just resume extender execution
637
     */
638
    final public function sigContHandler() {
639
640
        if ( $this->parent_pid == posix_getpid() ) {
641
642
            $this->logger->info("Received CONT signal, resuming extender");
643
644
            $this->paused = false;
645
646
        }
647
648
    }
649
650
    /**
651
     * The generig signal handler.
652
     *
653
     * It can be used to handle custom signals
654
     */
655
    final public function genericSignalHandler($signal) {
656
657
        if ( $this->parent_pid == posix_getpid() ) {
658
659
            $this->logger->info("Received ".$signal." signal, firing associated event(s)");
660
661
            $this->events->fire("extender.signal.".$signal, "VOID", $this);
662
663
        }
664
665
    }
666
667 3
    private function cycle() {
668
669
      // fire extender ready event
670
671 3
      $this->events->fire("extender", "VOID", $this);
672
673
      // dispatch signals (if multithread active)
674
675 3
      if ( $this->getMultithreadMode() ) pcntl_signal_dispatch();
676
677
      // if extender is paused (SIGINT), skip to extend
678
679 3
      if ( $this->paused ) return;
680
681
      // fix relative timestamp
682
683 3
      $this->timestamp = microtime(true);
684
685
      // fire tasktable event
686
687 3
      $this->tasks = $this->events->fire("extender.tasks", "TASKSTABLE", $this->tasks);
688
689
      // get the next planned activity interval
690
691 3
      $plans = Planner::get();
692
693 3
      if ( !is_null($plans) AND $this->timestamp < $plans ) {
694
695
          // nothing to do right now, still waiting if in daemon mode
696
697
          $this->logger->info("Next planned job: ".date('c', $plans));
698
699
          $this->logger->notice("Extender completed\n");
700
701
          if ( $this->getDaemonMode() === false ) {
702
703
              $this->shutdown(true);
704
705
              self::end(0);
706
707
          }
708
709
          return;
710
711
      }
712
713
      // if no plan is retrieved, try to retrieve it from scheduler
714
715
      try {
716
717
          // get schedules and dispatch schedule event
718
719 3
          list($schedules, $planned) = Scheduler::getSchedules($this->logger, $this->timestamp);
720
721
          // write next planned activity interval
722
723 3
          if ( !is_null($planned) AND $planned != 0 ) Planner::set($planned);
724
725 3
          $scheduled = new Schedule();
726
727 3
          $scheduled->setSchedules($schedules);
728
729
          // expose the current shcedule via events
730
731 3
          $scheduled = $this->events->fire("extender.schedule", "SCHEDULE", $scheduled);
732
733
          // if no jobs in queue, exit gracefully
734
735 3
          if ( $scheduled->howMany() == 0 ) {
736
737 3
              $this->logger->info("No jobs to process right now, exiting");
738
739 3
              $this->logger->notice("Extender completed\n");
740
741 3
              if ( $this->getDaemonMode() === false ) {
742
743 3
                  $this->shutdown(true);
744
745 3
                  self::end(0);
746
747 3
              }
748
749 3
              return;
750
751
          }
752
753
          // compose jobs
754
755
          foreach ( $scheduled->getSchedules() as $schedule ) {
756
757
              if ( $this->tasks->isRegistered($schedule['task']) ) {
758
759
                  $job = new Job();
760
761
                  $job->setName($schedule['name'])
762
                      ->setId($schedule['id'])
763
                      ->setParameters(unserialize($schedule['params']))
764
                      ->setTask($schedule['task'])
765
                      ->setClass($this->tasks->getClass($schedule['task']));
766
767
                  $this->runner->addJob($job);
768
769
              } else {
770
771
                  $this->logger->warning("Skipping job due to unknown task", array(
772
                      "ID"     => $schedule['id'],
773
                      "NAME"   => $schedule['name'],
774
                      "TASK"   => $schedule['task']
775
                  ));
776
777
              }
778
779
          }
780
781
          // lauch runner
782
783
          $result = $this->runner->run();
784
785
          // free runner for next cycle
786
787
          $this->runner->free();
788
789
          // compose results
790
791
          $results = new JobsResult($result);
792
793
          // update schedules
794
795
          Scheduler::updateSchedules($this->logger, $result);
796
797
          // increment counters
798
799
          foreach ( $result as $r ) {
800
801
              if ( $r[2] ) $this->completed_processes++;
802
803
              else $this->failed_processes++;
804
805
          }
806
807
      } catch (Exception $e) {
808
809
          $this->logger->error($e->getMessage());
810
811
          if ( $this->getDaemonMode() === false ) {
812
813
              self::end(1);
814
815
          }
816
817
      }
818
819
      // fire result event
820
821
      $this->events->fire("extender.result", "VOID", $results);
0 ignored issues
show
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...
822
823
      $this->logger->notice("Extender completed\n");
824
825
      // show summary (if -s)
826
827
      if ( $this->summary_mode ) self::showSummary($this->timestamp, $result, $this->color);
0 ignored issues
show
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...
828
829
      Status::dump($this->timestamp_absolute, $this->parent_pid, $this->completed_processes, $this->failed_processes, $this->paused);
830
831
      if ( $this->getDaemonMode() === false ) {
832
833
          $this->shutdown(true);
834
835
          self::end(0);
836
837
      }
838
839
    }
840
841
    private static function showHelp($color) {
842
843
        echo Version::getDescription();
844
845
        echo "\nVersion: ".$color->convert("%g".Version::getVersion()."%n");
846
847
        echo "\n\nAvailable options:";
848
849
        echo "\n------------------";
850
851
        echo "\n".$color->convert("%g -v %n").": verbose mode";
852
853
        echo "\n".$color->convert("%g -V %n").": debug mode";
854
855
        echo "\n".$color->convert("%g -s %n").": show summary of executed jobs (if any)";
856
857
        echo "\n".$color->convert("%g -d %n").": run extender in daemon mode";
858
859
        echo "\n".$color->convert("%g -h %n").": show this help";
860
861
        echo "\n\n";
862
863
    }
864
865 12
    private static function getCommandlineOptions() {
866
867 12
        $options = getopt("svdVh");
868
869
        return array(
870 12
            array_key_exists('v', $options) ? true : false,
871 12
            array_key_exists('V', $options) ? true : false,
872 12
            array_key_exists('s', $options) ? true : false,
873 12
            array_key_exists('d', $options) ? true : false,
874 12
            array_key_exists('h', $options) ? true : false
875 12
        );
876
877
    }
878
879
    /**
880
     * @param double $timestamp
881
     */
882
    private static function showSummary($timestamp, $completed_processes, $color) {
883
884
        $header_string = "\n\n --- Comodojo Extender Summary --- ".date('c', $timestamp)."\n\n";
885
886
        $tbl = new Console_Table(CONSOLE_TABLE_ALIGN_LEFT, CONSOLE_TABLE_BORDER_ASCII, 1, null, true);
887
888
        $tbl->setHeaders(array(
889
            'Pid',
890
            'Name',
891
            'Log Id',
892
            'Success',
893
            'Result (truncated)',
894
            'Time elapsed'
895
        ));
896
897
        foreach ( $completed_processes as $key => $completed_process ) {
898
899
            $pid = $completed_process[0];
900
901
            $name = $completed_process[1];
902
903
            $success = $color->convert($completed_process[2] ? "%gYES%n" : "%rNO%n");
904
905
            $result = str_replace(array("\r", "\n"), " ", $completed_process[5]);
906
907
            $result = strlen($result) >= 80 ? substr($result, 0, 80)."..." : $result;
908
909
            $elapsed = empty($completed_process[4]) ? "--" : ($completed_process[4] - $completed_process[3]);
910
911
            $worklog_id = $completed_process[7];
912
913
            $tbl->addRow(array(
914
                $pid,
915
                $name,
916
                $worklog_id,
917
                $success,
918
                $result,
919
                $elapsed
920
            ));
921
922
        }
923
924
        $footer_string = "\n\nTotal script runtime: ".(microtime(true) - $timestamp)." seconds\r\n\n";
925
926
        print $header_string.$tbl->getTable().$footer_string;
927
928
    }
929
930
    /**
931
     * @param integer $returnCode
932
     */
933 3 View Code Duplication
    private static function end($returnCode) {
934
935 3
        if ( defined('COMODOJO_PHPUNIT_TEST') && @constant('COMODOJO_PHPUNIT_TEST') === true ) {
936
937 3
            if ( $returnCode === 1 ) throw new Exception("PHPUnit Test Exception");
938
939 3
            else return $returnCode;
940
941
        } else {
942
943
            exit($returnCode);
944
945
        }
946
947
    }
948
949
}
950