ProcessBase::setSimulated()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Consolidation\SiteProcess;
4
5
use Psr\Log\LoggerInterface;
6
use Symfony\Component\Console\Style\OutputStyle;
7
use Symfony\Component\Process\Process;
8
use Consolidation\SiteProcess\Util\RealtimeOutputHandler;
9
use Consolidation\SiteProcess\Util\Escape;
10
use Symfony\Component\Console\Output\OutputInterface;
11
use Symfony\Component\Console\Output\ConsoleOutputInterface;
12
13
/**
14
 * A wrapper around Symfony Process.
15
 *
16
 * - Supports simulated mode. Typically enabled via a --simulate option.
17
 * - Supports verbose mode - logs all runs.
18
 * - Can convert output json data into php array (convenience method)
19
 * - Provides a "realtime output" helper
20
 */
21
class ProcessBase extends Process
22
{
23
    /**
24
     * @var OutputStyle
25
     */
26
    protected $output;
27
28
    /**
29
     * @var OutputInterface
30
     */
31
    protected $stderr;
32
33
    private $simulated = false;
34
35
    private $verbose = false;
36
37
    /**
38
     * @var LoggerInterface
39
     */
40
    private $logger;
41
42
    /**
43
     * realtimeStdout returns the output stream that realtime output
44
     * should be sent to (if applicable)
45
     *
46
     * @return OutputStyle $output
47
     */
48
    public function realtimeStdout()
49
    {
50
        return $this->output;
51
    }
52
53
    protected function realtimeStderr()
54
    {
55
        if ($this->stderr) {
56
            return $this->stderr;
57
        }
58
        if (method_exists($this->output, 'getErrorStyle')) {
59
            return $this->output->getErrorStyle();
60
        }
61
62
        return $this->realtimeStdout();
63
    }
64
65
    /**
66
     * setRealtimeOutput allows the caller to inject an OutputStyle object
67
     * that will be used to stream realtime output if applicable.
68
     *
69
     * @param OutputStyle $output
70
     */
71
    public function setRealtimeOutput(OutputInterface $output, $stderr = null)
72
    {
73
        $this->output = $output;
0 ignored issues
show
Documentation Bug introduced by
$output is of type object<Symfony\Component...Output\OutputInterface>, but the property $output was declared to be of type object<Symfony\Component...sole\Style\OutputStyle>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
74
        $this->stderr = $stderr instanceof ConsoleOutputInterface ? $stderr->getErrorOutput() : $stderr;
75
    }
76
77
    /**
78
     * @return bool
79
     */
80
    public function isVerbose()
81
    {
82
        return $this->verbose;
83
    }
84
85
    /**
86
     * @param bool $verbose
87
     */
88
    public function setVerbose($verbose)
89
    {
90
        $this->verbose = $verbose;
91
    }
92
93
    /**
94
     * @return bool
95
     */
96
    public function isSimulated()
97
    {
98
        return $this->simulated;
99
    }
100
101
    /**
102
     * @param bool $simulated
103
     */
104
    public function setSimulated($simulated)
105
    {
106
        $this->simulated = $simulated;
107
    }
108
109
    /**
110
     * @return LoggerInterface
111
     */
112
    public function getLogger()
113
    {
114
        return $this->logger;
115
    }
116
117
    /**
118
     * @param LoggerInterface $logger
119
     */
120
    public function setLogger($logger)
121
    {
122
        $this->logger = $logger;
123
    }
124
125
    /**
126
     * @inheritDoc
127
     */
128
    public function start(callable $callback = null, array $env = [])
129
    {
130
        $cmd = $this->getCommandLine();
131
        if ($this->isSimulated()) {
132
            $this->getLogger()->notice('Simulating: ' . $cmd);
133
            // Run a command that always succeeds (on Linux and Windows).
134
            $this->setCommandLine('true');
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Proces...ocess::setCommandLine() has been deprecated with message: since Symfony 4.2.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
135
        } elseif ($this->isVerbose()) {
136
            $this->getLogger()->info('Executing: ' . $cmd);
137
        }
138
        parent::start($callback, $env);
139
        // Set command back to original value in case anyone asks.
140
        if ($this->isSimulated()) {
141
            $this->setCommandLine($cmd);
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Proces...ocess::setCommandLine() has been deprecated with message: since Symfony 4.2.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
142
        }
143
    }
144
145
    /**
146
     * Get Process output and decode its JSON.
147
     *
148
     * @return array
149
     *   An associative array.
150
     */
151
    public function getOutputAsJson()
152
    {
153
        $output = trim($this->getOutput());
154
        if (empty($output)) {
155
            throw new \InvalidArgumentException('Output is empty.');
156
        }
157
        if (Escape::isWindows()) {
158
            // Doubled double quotes were converted to \\".
159
            // Revert to double quote.
160
            $output = str_replace('\\"', '"', $output);
161
            // Revert of doubled backslashes.
162
            $output = preg_replace('#\\\\{2}#', '\\', $output);
163
        }
164
        $sanitizedOutput = $this->removeNonJsonJunk($output);
165
        $json = json_decode($sanitizedOutput, true);
166
        if (!isset($json)) {
167
            $msg = 'Unable to decode output into JSON: ' . json_last_error_msg();
168
            if (json_last_error() == JSON_ERROR_SYNTAX) {
169
                $msg .= "\n\n$output";
170
            }
171
            throw new \InvalidArgumentException($msg);
172
        }
173
        return $json;
174
    }
175
176
    /**
177
     * Allow for a certain amount of resiliancy in the output received when
178
     * json is expected.
179
     *
180
     * @param string $data
181
     * @return string
182
     */
183
    protected function removeNonJsonJunk($data)
184
    {
185
        // Exit early if we have no output.
186
        $data = trim($data);
187
        if (empty($data)) {
188
            return $data;
189
        }
190
        // If the data is a simple quoted string, or an array, then exit.
191
        if ((($data[0] == '"') && ($data[strlen($data) - 1] == '"')) ||
192
            (($data[0] == "[") && ($data[strlen($data) - 1] == "]"))
193
        ) {
194
            return $data;
195
        }
196
        // If the json is not a simple string or a simple array, then is must
197
        // be an associative array. We will remove non-json garbage characters
198
        // before and after the enclosing curley-braces.
199
        $start = strpos($data, '{');
200
        $end = strrpos($data, '}') + 1;
201
        $data = substr($data, $start, $end - $start);
202
        return $data;
203
    }
204
205
    /**
206
     * Return a realTime output object.
207
     *
208
     * @return callable
209
     */
210
    public function showRealtime()
211
    {
212
        $realTimeOutput = new RealtimeOutputHandler($this->realtimeStdout(), $this->realtimeStderr());
213
        $realTimeOutput->configure($this);
0 ignored issues
show
Unused Code introduced by
The call to the method Consolidation\SiteProces...putHandler::configure() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
214
        return $realTimeOutput;
215
    }
216
}
217