DaemonCommand   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 181
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 26
c 2
b 1
f 0
lcom 1
cbo 6
dl 0
loc 181
ccs 0
cts 101
cp 0
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 2
A configure() 0 21 1
B execute() 0 22 4
B startDaemon() 0 36 6
C stopDaemon() 0 33 8
A statusDaemon() 0 13 2
A getRunningPid() 0 15 3
1
<?php
2
3
/**
4
 * PHPCI - Continuous Integration for PHP.
5
 *
6
 * @copyright    Copyright 2014, Block 8 Limited.
7
 * @license      https://github.com/Block8/PHPCI/blob/master/LICENSE.md
8
 *
9
 * @link         https://www.phptesting.org/
10
 */
11
12
namespace PHPCI\Command;
13
14
use Monolog\Logger;
15
use PHPCI\ProcessControl\Factory;
16
use PHPCI\ProcessControl\ProcessControlInterface;
17
use Symfony\Component\Console\Command\Command;
18
use Symfony\Component\Console\Input\InputArgument;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Input\InputOption;
21
use Symfony\Component\Console\Output\OutputInterface;
22
23
/**
24
 * Daemon that loops and call the run-command.
25
 *
26
 * @author       Gabriel Baker <[email protected]>
27
 */
28
class DaemonCommand extends Command
29
{
30
    /**
31
     * @var Logger
32
     */
33
    protected $logger;
34
35
    /**
36
     * @var string
37
     */
38
    protected $pidFilePath;
39
40
    /**
41
     * @var string
42
     */
43
    protected $logFilePath;
44
45
    /**
46
     * @var ProcessControlInterface
47
     */
48
    protected $processControl;
49
50
    public function __construct(Logger $logger, ProcessControlInterface $processControl = null, $name = null)
51
    {
52
        parent::__construct($name);
53
        $this->logger = $logger;
54
        $this->processControl = $processControl ?: Factory::getInstance();
55
    }
56
57
    protected function configure()
58
    {
59
        $this
60
            ->setName('phpci:daemon')
61
            ->setDescription('Initiates the daemon to run commands.')
62
            ->addArgument(
63
                'state', InputArgument::REQUIRED, 'start|stop|status'
64
            )
65
            ->addOption(
66
                'pid-file', 'p', InputOption::VALUE_REQUIRED,
67
                'Path of the PID file',
68
                implode(DIRECTORY_SEPARATOR,
69
                    array(PHPCI_DIR, 'daemon', 'daemon.pid'))
70
            )
71
            ->addOption(
72
                'log-file', 'l', InputOption::VALUE_REQUIRED,
73
                'Path of the log file',
74
                implode(DIRECTORY_SEPARATOR,
75
                    array(PHPCI_DIR, 'daemon', 'daemon.log'))
76
        );
77
    }
78
79
    /**
80
     * Loops through running.
81
     */
82
    protected function execute(InputInterface $input, OutputInterface $output)
83
    {
84
        $this->pidFilePath = $input->getOption('pid-file');
85
        $this->logFilePath = $input->getOption('log-file');
86
87
        $state = $input->getArgument('state');
88
89
        switch ($state) {
90
            case 'start':
91
                $this->startDaemon();
92
                break;
93
            case 'stop':
94
                $this->stopDaemon();
95
                break;
96
            case 'status':
97
                $this->statusDaemon($output);
98
                break;
99
            default:
100
                $this->output->writeln('<error>Not a valid choice, please use start, stop or status</error>');
0 ignored issues
show
Bug introduced by
The property output does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
101
                break;
102
        }
103
    }
104
105
    protected function startDaemon()
106
    {
107
        $pid = $this->getRunningPid();
108
        if ($pid) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pid of type null|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
109
            $this->logger->notice('Daemon already started', array('pid' => $pid));
110
111
            return 'alreadystarted';
112
        }
113
114
        $this->logger->info('Trying to start the daemon');
115
116
        $cmd = 'nohup %s/daemonise phpci:daemonise > %s 2>&1 &';
117
        $command = sprintf($cmd, PHPCI_DIR, $this->logFilePath);
118
        $output = $exitCode = null;
119
        exec($command, $output, $exitCode);
120
121
        if ($exitCode !== 0) {
122
            $this->logger->error(sprintf('daemonise exited with status %d', $exitCode));
123
124
            return 'notstarted';
125
        }
126
127
        for ($i = 0; !($pid = $this->getRunningPid()) && $i < 5; ++$i) {
128
            sleep(1);
129
        }
130
131
        if (!$pid) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pid of type null|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
132
            $this->logger->error('Could not start the daemon');
133
134
            return 'notstarted';
135
        }
136
137
        $this->logger->notice('Daemon started', array('pid' => $pid));
138
139
        return 'started';
140
    }
141
142
    protected function stopDaemon()
143
    {
144
        $pid = $this->getRunningPid();
145
        if (!$pid) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pid of type null|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
146
            $this->logger->notice('Cannot stop the daemon as it is not started');
147
148
            return 'notstarted';
149
        }
150
151
        $this->logger->info('Trying to terminate the daemon', array('pid' => $pid));
152
        $this->processControl->kill($pid);
153
154
        for ($i = 0; ($pid = $this->getRunningPid()) && $i < 5; ++$i) {
155
            sleep(1);
156
        }
157
158
        if ($pid) {
159
            $this->logger->warning('The daemon is resiting, trying to kill it', array('pid' => $pid));
160
            $this->processControl->kill($pid, true);
161
162
            for ($i = 0; ($pid = $this->getRunningPid()) && $i < 5; ++$i) {
163
                sleep(1);
164
            }
165
        }
166
167
        if (!$pid) {
168
            $this->logger->notice('Daemon stopped');
169
170
            return 'stopped';
171
        }
172
173
        $this->logger->error('Could not stop the daemon');
174
    }
175
176
    protected function statusDaemon(OutputInterface $output)
177
    {
178
        $pid = $this->getRunningPid();
179
        if ($pid) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pid of type null|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
180
            $output->writeln(sprintf('The daemon is running, PID: %d', $pid));
181
182
            return 'running';
183
        }
184
185
        $output->writeln('The daemon is not running');
186
187
        return 'notrunning';
188
    }
189
190
    /** Check if there is a running daemon
191
     * @return int|null
192
     */
193
    protected function getRunningPid()
194
    {
195
        if (!file_exists($this->pidFilePath)) {
196
            return;
197
        }
198
199
        $pid = intval(trim(file_get_contents($this->pidFilePath)));
200
201
        if ($this->processControl->isRunning($pid, true)) {
0 ignored issues
show
Unused Code introduced by
The call to ProcessControlInterface::isRunning() has too many arguments starting with true.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
202
            return $pid;
203
        }
204
205
        // Not found, remove the stale PID file
206
        unlink($this->pidFilePath);
207
    }
208
}
209