Completed
Push — 0.4 ( 90769c...612214 )
by jean
10s
created

StepRunner::stop()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Darkilliant\ProcessBundle\Runner;
4
5
use Darkilliant\ProcessBundle\ProcessNotifier\ChainProcessNotifier;
6
use Psr\Log\LoggerInterface;
7
use Symfony\Component\OptionsResolver\OptionsResolver;
8
use Darkilliant\ProcessBundle\Configuration\ConfigurationProcess;
9
use Darkilliant\ProcessBundle\Configuration\ConfigurationStep;
10
use Darkilliant\ProcessBundle\Registry\LoggerRegistry;
11
use Darkilliant\ProcessBundle\Registry\StepRegistry;
12
use Darkilliant\ProcessBundle\Resolver\OptionDynamicValueResolver;
13
use Darkilliant\ProcessBundle\State\ProcessState;
14
use Darkilliant\ProcessBundle\Step\IterableStepInterface;
15
use Darkilliant\ProcessBundle\Step\StepInterface;
16
17
class StepRunner
18
{
19
    /** @var LoggerInterface */
20
    protected $logger;
21
22
    /** @var StepRegistry */
23
    protected $registry;
24
25
    /** @var LoggerRegistry */
26
    protected $loggerRegistry;
27
28
    /** @var OptionDynamicValueResolver */
29
    protected $dynamicValueResolver;
30
31
    /** @var array */
32
    protected $configuration;
33
34
    /** @var ChainProcessNotifier */
35
    protected $notifier;
36
37
    private $shouldStop = false;
38
39
    /**
40
     * @internal
41
     */
42 14
    public function __construct(LoggerRegistry $loggerRegistry, StepRegistry $registry, OptionDynamicValueResolver $dynamicValueResolver, array $configuration, LoggerInterface $logger, ChainProcessNotifier $notifier)
43
    {
44 14
        $this->configuration = $configuration;
45 14
        $this->logger = $logger;
46 14
        $this->dynamicValueResolver = $dynamicValueResolver;
47 14
        $this->registry = $registry;
48 14
        $this->loggerRegistry = $loggerRegistry;
49 14
        $this->notifier = $notifier;
50
51 14
        if ('cli' === PHP_SAPI) {
52 14
            pcntl_signal(SIGINT, [$this, 'stop']);
53
        }
54 14
    }
55
56 1
    public function stop()
57
    {
58 1
        $this->logger->error('step runner stop scheduled');
59 1
        $this->shouldStop = true;
60 1
    }
61
62 1
    public function setNotifier($notifier)
63
    {
64 1
        $this->notifier = $notifier;
65 1
    }
66
67
    /**
68
     * @param string $processName
69
     *
70
     * @throws \Exception
71
     *
72
     * @return ConfigurationProcess
73
     */
74 12
    public function buildConfigurationProcess(string $processName, string $logger = null): ConfigurationProcess
75
    {
76 12
        if (empty($this->configuration['process'][$processName])) {
77 2
            throw new \Exception(sprintf(
78 2
                'process %s not found, available (%s)',
79 2
                $processName,
80 2
                implode(', ', array_keys($this->configuration['process']))
81
            ));
82
        }
83
84 10
        if (null !== $logger) {
85 2
            $this->configuration['process'][$processName]['logger'] = $logger;
86
        }
87
88 10
        return ConfigurationProcess::create($this->configuration['process'][$processName]);
89
    }
90
91 8
    public function run(ConfigurationProcess $process, array $context = [], $data = [], $dryRun = false): bool
92
    {
93 8
        $processState = new ProcessState(
94 8
            $context,
95 8
            $this->loggerRegistry->resolveService($process->getLogger()),
96 8
            $this
97
        );
98 8
        $processState->setData($data);
99 8
        $processState->setDryRun($dryRun);
100
101 8
        if ($process->getDeprecated()) {
102 1
            $processState->warning('DEPRECATED STEPS USED', ['deprecated' => $process->getDeprecated()]);
103
        }
104
105 8
        return $this->runSteps($processState, $process->getSteps());
106
    }
107
108 4
    public function finalizeStep(ProcessState $processState, $step)
109
    {
110 4
        $processState->markSuccess();
111
112 4
        $this->registry->resolveService($step->getService())->finalize($processState);
113
114 3
        if (ProcessState::RESULT_OK !== $processState->getResult()) {
115 1
            return false;
116
        }
117
118 2
        return true;
119
    }
120
121 8
    public function runSteps(ProcessState $processState, array $steps): bool
122
    {
123 8
        foreach ($steps as $step) {
124
            try {
125 8
                if (!$this->runStep($processState, $step)) {
126 7
                    return false;
127
                }
128 1
            } catch (\Throwable $exception) {
129 1
                $processState->getLogger()->error('fail step', array_merge([
130 1
                    'message' => $exception->getMessage(),
131 1
                    'step' => $step->getService(),
132 1
                ], $processState->getRawContext()));
133
134 7
                return false;
135
            }
136
        }
137
138 6
        return true;
139
    }
140
141 7
    protected function configureOptions(StepInterface $service, ConfigurationStep $step, ProcessState $processState): ProcessState
142
    {
143 7
        return $processState->setOptions(
144 7
            $this->dynamicValueResolver->resolve(
145 7
                $service->configureOptionResolver(new OptionsResolver())->resolve($step->getOptions()),
146
                [
147 7
                    'data' => $processState->getData(),
148 7
                    'context' => $processState->getRawContext(),
149
                ]
150
            )
151
        );
152
    }
153
154 8
    protected function runStep(ProcessState $processState, ConfigurationStep $step): int
155
    {
156 8
        $processState->markSuccess();
157
158 8
        if (!$step->isEnabled()) {
159 1
            return true;
160
        }
161
162
        /**
163
         * @var ConfigurationStep
164
         */
165 8
        $service = $this->registry->resolveService($step->getService());
166
167 8
        $processState = $this->configureOptions($service, $step, $processState);
168 8
        $options = $processState->getOptions();
169
170 8
        $this->notifier->onStartProcess($processState, $service);
171
172 8
        $service->execute($processState);
173
174 7
        $this->notifier->onExecutedProcess($processState, $service);
175
176 7
        if (ProcessState::RESULT_OK !== $processState->getResult()) {
177 2
            return false;
178
        }
179 6
        if ($service instanceof IterableStepInterface) {
180 4
            $this->notifier->onStartIterableProcess($processState, $service);
181
182 4
            $iterator = $processState->getIterator();
183
184 4
            $count = $service->count($processState);
185
186 4
            while ($service->valid($processState)) {
187 4
                if ('cli' === PHP_SAPI) {
188
                    // check ctrl+c (SIGINT)
189 4
                    pcntl_signal_dispatch();
190
                }
191
192 4
                $currentIndex = $service->getProgress($processState);
193
194 4
                $service->next($processState);
195 4
                $this->notifier->onUpdateIterableProcess($processState, $service);
196
197 4
                if ($this->shouldStop || ProcessState::RESULT_BREAK === $processState->getResult()) {
198 1
                    $processState->noLoop();
199 1
                    $this->finalizeSteps($processState, $step->getChildren());
200 1
                    $this->notifier->onEndProcess($processState, $service);
201
202 1
                    return true;
203
                }
204
205
                // Add metadata information of the current iteration of loop
206 4
                $processState->loop($currentIndex, $count, !$service->valid($processState));
207
208 4
                $isSuccessful = $this->runSteps($processState, $step->getChildren());
209 4
                $processState->setIterator($iterator);
210 4
                $processState->setOptions($options);
211
212 4
                if ($isSuccessful) {
213 3
                    $service->onSuccessLoop($processState);
214 3
                    $processState->getLogger()->info('successful', $processState->getRawContext());
215
                } else {
216 1
                    $service->onFailedLoop($processState);
217
                }
218
            }
219 3
            $processState->noLoop();
220
221 3
            $this->finalizeSteps($processState, $step->getChildren());
222
        }
223
224 6
        $this->notifier->onEndProcess($processState, $service);
225
226 6
        return true;
227
    }
228
229 4
    private function finalizeSteps(ProcessState $processState, array $steps)
230
    {
231 4
        foreach ($steps as $step) {
232
            try {
233 4
                if (!$this->finalizeStep($processState, $step)) {
234 3
                    return false;
235
                }
236 1
            } catch (\Exception $exception) {
237 1
                $processState->getLogger()->error('fail step', array_merge([
238 1
                    'message' => $exception->getMessage(),
239 1
                    'step' => $step->getService(),
240 1
                ], $processState->getRawContext()));
241
242 3
                return false;
243
            }
244
        }
245
246 2
        return true;
247
    }
248
}
249