Passed
Push — master ( 71b580...625333 )
by Harry
01:57
created

Run::update()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
ccs 8
cts 8
cp 1
cc 2
eloc 7
nc 2
nop 1
crap 2
1
<?php
2
/**
3
 * This file is part of graze/parallel-process.
4
 *
5
 * Copyright © 2018 Nature Delivered Ltd. <https://www.graze.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license https://github.com/graze/parallel-process/blob/master/LICENSE.md
11
 * @link    https://github.com/graze/parallel-process
12
 */
13
14
namespace Graze\ParallelProcess;
15
16
use Exception;
17
use Graze\ParallelProcess\Event\EventDispatcherTrait;
18
use Graze\ParallelProcess\Event\RunEvent;
19
use Symfony\Component\Process\Exception\ProcessFailedException;
20
use Symfony\Component\Process\Process;
21
use Throwable;
22
23
class Run implements RunInterface
24
{
25
    use EventDispatcherTrait;
26
27
    const ON_SUCCESS  = 1;
28
    const ON_FAILURE  = 2;
29
    const ON_PROGRESS = 3;
30
31
    /** @var Process */
32
    private $process;
33
    /** @var float */
34
    private $started = 0;
35
    /** @var bool */
36
    private $successful = false;
37
    /** @var bool */
38
    private $completed = false;
39
    /** @var string */
40
    private $last = '';
41
    /** @var string */
42
    private $lastType = 'std';
43
    /** @var bool */
44
    private $updateOnPoll = true;
45
    /** @var bool */
46
    private $updateOnProcessOutput = true;
47
    /** @var array */
48
    private $tags;
49
50
    /**
51
     * Run constructor.
52
     *
53
     * @param Process $process
54
     * @param array   $tags List of key value tags associated with this run
55
     */
56 43
    public function __construct(Process $process, array $tags = [])
57
    {
58 43
        $this->process = $process;
59 43
        $this->tags = $tags;
60 43
    }
61
62
    /**
63
     * @return string[]
64
     */
65 38
    protected function getEventNames()
66
    {
67
        return [
68 38
            RunEvent::STARTED,
69 38
            RunEvent::COMPLETED,
70 38
            RunEvent::FAILED,
71 38
            RunEvent::UPDATED,
72
        ];
73
    }
74
75
    /**
76
     * Start the process
77
     *
78
     * @return $this
79
     */
80 35
    public function start()
81
    {
82 35
        if (!$this->process->isRunning()) {
83 34
            $this->started = microtime(true);
84 34
            $this->dispatch(RunEvent::STARTED, new RunEvent($this));
85 34
            $this->process->start(
86 34
                function ($type, $data) {
87 21
                    $this->lastType = $type;
88 21
                    foreach (explode("\n", $data) as $line) {
89 21
                        $line = rtrim($line);
90 21
                        if (mb_strlen($line) > 0) {
91 21
                            $this->last = $line;
92 21
                            if ($this->updateOnProcessOutput) {
93 21
                                $this->dispatch(RunEvent::UPDATED, new RunEvent($this));
94
                            }
95
                        }
96
                    }
97 34
                }
98
            );
99 34
            $this->completed = false;
100
        }
101
102 35
        return $this;
103
    }
104
105
    /**
106
     * Poll the process to see if it is still running, and trigger events
107
     *
108
     * @return bool true if the process is currently running (started and not terminated)
109
     */
110 32
    public function poll()
111
    {
112 32
        if ($this->completed || !$this->hasStarted()) {
113 1
            return false;
114
        }
115
116 31
        if ($this->process->isRunning()) {
117 25
            if ($this->updateOnPoll) {
118 16
                $this->dispatch(RunEvent::UPDATED, new RunEvent($this));
119
            }
120 25
            return true;
121
        }
122
123 31
        $this->completed = true;
124
125 31
        if ($this->process->isSuccessful()) {
126 27
            $this->successful = true;
127 27
            $this->dispatch(RunEvent::COMPLETED, new RunEvent($this));
128
        } else {
129 5
            $this->dispatch(RunEvent::FAILED, new RunEvent($this));
130
        }
131 31
        return false;
132
    }
133
134
    /**
135
     * Return if the underlying process is running
136
     *
137
     * @return bool
138
     */
139 30
    public function isRunning()
140
    {
141 30
        return $this->process->isRunning();
142
    }
143
144
    /**
145
     * @return bool
146
     */
147 25
    public function isSuccessful()
148
    {
149 25
        return $this->successful;
150
    }
151
152
    /**
153
     * @return bool
154
     */
155 35
    public function hasStarted()
156
    {
157 35
        return $this->process->isStarted();
158
    }
159
160
    /**
161
     * @return Process
162
     */
163 4
    public function getProcess()
164
    {
165 4
        return $this->process;
166
    }
167
168
    /**
169
     * @param bool $updateOnPoll
170
     *
171
     * @return $this
172
     */
173 10
    public function setUpdateOnPoll($updateOnPoll)
174
    {
175 10
        $this->updateOnPoll = $updateOnPoll;
176 10
        return $this;
177
    }
178
179
    /**
180
     * @return bool
181
     */
182 2
    public function isUpdateOnPoll()
183
    {
184 2
        return $this->updateOnPoll;
185
    }
186
187
    /**
188
     * @param bool $update
189
     *
190
     * @return $this
191
     */
192 15
    public function setUpdateOnProcessOutput($update)
193
    {
194 15
        $this->updateOnProcessOutput = $update;
195 15
        return $this;
196
    }
197
198
    /**
199
     * @return bool
200
     */
201 2
    public function isUpdateOnProcessOutput()
202
    {
203 2
        return $this->updateOnProcessOutput;
204
    }
205
206
    /**
207
     * @return array
208
     */
209 21
    public function getTags()
210
    {
211 21
        return $this->tags;
212
    }
213
214
    /**
215
     * @return float number of seconds this run has been running for (0 for not started)
216
     */
217 22
    public function getDuration()
218
    {
219 22
        return $this->started > 0 ? microtime(true) - $this->started : 0;
220
    }
221
222
    /**
223
     * @return float[]|null the process between 0 and 1 if the run supports it, otherwise null
224
     */
225 15
    public function getProgress()
226
    {
227 15
        return null;
228
    }
229
230
    /**
231
     * @return string
232
     */
233 15
    public function getLastMessage()
234
    {
235 15
        return $this->last;
236
    }
237
238
    /**
239
     * @return string
240
     */
241 9
    public function getLastMessageType()
242
    {
243 9
        return $this->lastType;
244
    }
245
246
    /**
247
     * If the run was unsuccessful, get the error if applicable
248
     *
249
     * @return Exception[]|Throwable[]
250
     */
251 5
    public function getExceptions()
252
    {
253 5
        if ($this->hasStarted() && !$this->isSuccessful()) {
254 4
            return [new ProcessFailedException($this->process)];
255
        }
256 1
        return [];
257
    }
258
}
259