Completed
Push — master ( 7df873...57a8d7 )
by Greg
02:28
created

src/Task/Base/ParallelExec.php (1 issue)

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
2
namespace Robo\Task\Base;
3
4
use Robo\Contract\CommandInterface;
5
use Robo\Contract\PrintedInterface;
6
use Robo\Result;
7
use Robo\Task\BaseTask;
8
use Symfony\Component\Process\Exception\ProcessTimedOutException;
9
use Symfony\Component\Process\Process;
10
11
/**
12
 * Class ParallelExecTask
13
 *
14
 * ``` php
15
 * <?php
16
 * $this->taskParallelExec()
17
 *   ->process('php ~/demos/script.php hey')
18
 *   ->process('php ~/demos/script.php hoy')
19
 *   ->process('php ~/demos/script.php gou')
20
 *   ->run();
21
 * ?>
22
 * ```
23
 */
24
class ParallelExec extends BaseTask implements CommandInterface, PrintedInterface
25
{
26
    use \Robo\Common\CommandReceiver;
27
28
    /**
29
     * @var Process[]
30
     */
31
    protected $processes = [];
32
33
    /**
34
     * @var null|int
35
     */
36
    protected $timeout = null;
37
38
    /**
39
     * @var null|int
40
     */
41
    protected $idleTimeout = null;
42
43
    /**
44
     * @var null|int
45
     */
46
    protected $waitInterval = 0;
47
48
    /**
49
     * @var bool
50
     */
51
    protected $isPrinted = false;
52
53
    /**
54
     * {@inheritdoc}
55
     */
56
    public function getPrinted()
57
    {
58
        return $this->isPrinted;
59
    }
60
61
    /**
62
     * @param bool $isPrinted
63
     *
64
     * @return $this
65
     */
66
    public function printed($isPrinted = true)
67
    {
68
        $this->isPrinted = $isPrinted;
69
        return $this;
70
    }
71
72
    /**
73
     * @param string|\Robo\Contract\CommandInterface $command
74
     *
75
     * @return $this
76
     */
77
    public function process($command)
78
    {
79
        // TODO: Symfony 4 requires that we supply the working directory.
80
        $this->processes[] = new Process($this->receiveCommand($command), getcwd());
0 ignored issues
show
$this->receiveCommand($command) is of type string, but the function expects a array.

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...
81
        return $this;
82
    }
83
84
    /**
85
     * Stops process if it runs longer then `$timeout` (seconds).
86
     *
87
     * @param int $timeout
88
     *
89
     * @return $this
90
     */
91
    public function timeout($timeout)
92
    {
93
        $this->timeout = $timeout;
94
        return $this;
95
    }
96
97
    /**
98
     * Stops process if it does not output for time longer then `$timeout` (seconds).
99
     *
100
     * @param int $idleTimeout
101
     *
102
     * @return $this
103
     */
104
    public function idleTimeout($idleTimeout)
105
    {
106
        $this->idleTimeout = $idleTimeout;
107
        return $this;
108
    }
109
110
    /**
111
     * Parallel processing will wait `$waitInterval` seconds after launching each process and before
112
     * the next one.
113
     *
114
     * @param int $waitInterval
115
     *
116
     * @return $this
117
     */
118
    public function waitInterval($waitInterval)
119
    {
120
        $this->waitInterval = $waitInterval;
121
        return $this;
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127
    public function getCommand()
128
    {
129
        return implode(' && ', $this->processes);
130
    }
131
132
    /**
133
     * @return int
134
     */
135
    public function progressIndicatorSteps()
136
    {
137
        return count($this->processes);
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public function run()
144
    {
145
        $this->startProgressIndicator();
146
        $running = [];
147
        $queue = $this->processes;
148
        $nextTime = time();
149
        while (true) {
150
            if (($nextTime <= time()) && !empty($queue)) {
151
                $process = array_shift($queue);
152
                $process->setIdleTimeout($this->idleTimeout);
153
                $process->setTimeout($this->timeout);
154
                $process->start();
155
                $this->printTaskInfo($process->getCommandLine());
156
                $running[] = $process;
157
                $nextTime = time() + $this->waitInterval;
158
            }
159
            foreach ($running as $k => $process) {
160
                try {
161
                    $process->checkTimeout();
162
                } catch (ProcessTimedOutException $e) {
163
                    $this->printTaskWarning("Process timed out for {command}", ['command' => $process->getCommandLine(), '_style' => ['command' => 'fg=white;bg=magenta']]);
164
                }
165
                if (!$process->isRunning()) {
166
                    $this->advanceProgressIndicator();
167
                    if ($this->isPrinted) {
168
                        $this->printTaskInfo("Output for {command}:\n\n{output}", ['command' => $process->getCommandLine(), 'output' => $process->getOutput(), '_style' => ['command' => 'fg=white;bg=magenta']]);
169
                        $errorOutput = $process->getErrorOutput();
170
                        if ($errorOutput) {
171
                            $this->printTaskError(rtrim($errorOutput));
172
                        }
173
                    }
174
                    unset($running[$k]);
175
                }
176
            }
177
            if (empty($running) && empty($queue)) {
178
                break;
179
            }
180
            usleep(1000);
181
        }
182
        $this->stopProgressIndicator();
183
184
        $errorMessage = '';
185
        $exitCode = 0;
186
        foreach ($this->processes as $p) {
187
            if ($p->getExitCode() === 0) {
188
                continue;
189
            }
190
            $errorMessage .= "'" . $p->getCommandLine() . "' exited with code ". $p->getExitCode()." \n";
191
            $exitCode = max($exitCode, $p->getExitCode());
192
        }
193
        if (!$errorMessage) {
194
            $this->printTaskSuccess('{process-count} processes finished running', ['process-count' => count($this->processes)]);
195
        }
196
197
        return new Result($this, $exitCode, $errorMessage, ['time' => $this->getExecutionTime()]);
198
    }
199
}
200