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

src/Extender.php (1 issue)

Labels
Severity

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);
0 ignored issues
show
It seems like $check_constants defined by \Comodojo\Extender\Checks::constants() on line 221 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...
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);
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);
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