Completed
Pull Request — master (#125)
by
unknown
07:31
created

ArrayLoader::findCallbacksByTriggerAndPosition()   B

Complexity

Conditions 9
Paths 6

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 9

Importance

Changes 3
Bugs 2 Features 1
Metric Value
c 3
b 2
f 1
dl 0
loc 21
ccs 15
cts 15
cp 1
rs 7.041
cc 9
eloc 11
nc 6
nop 2
crap 9
1
<?php
2
3
namespace Finite\Loader;
4
5
use Finite\Event\Callback\Callback;
6
use Finite\Event\Callback\CallbackBuilderFactory;
7
use Finite\Event\Callback\CallbackBuilderFactoryInterface;
8
use Finite\Event\CallbackHandler;
9
use Finite\State\Accessor\PropertyPathStateAccessor;
10
use Finite\StateMachine\StateMachineInterface;
11
use Finite\State\State;
12
use Finite\State\StateInterface;
13
use Finite\Transition\Transition;
14
use Symfony\Component\OptionsResolver\Options;
15
use Symfony\Component\OptionsResolver\OptionsResolver;
16
17
/**
18
 * Loads a StateMachine from an array.
19
 *
20
 * @author Yohan Giarelli <[email protected]>
21
 */
22
class ArrayLoader implements LoaderInterface
0 ignored issues
show
Complexity introduced by
The class ArrayLoader has a coupling between objects value of 14. Consider to reduce the number of dependencies under 13.
Loading history...
23
{
24
    /**
25
     * @var array
26
     */
27
    private $config;
28
29
    /**
30
     * @var CallbackHandler
31
     */
32
    private $callbackHandler;
33
34
    /**
35
     * @var CallbackBuilderFactoryInterface
36
     */
37
    private $callbackBuilderFactory;
38
39
    /**
40
     * @param array                           $config
41
     * @param CallbackHandler                 $handler
42
     * @param CallbackBuilderFactoryInterface $callbackBuilderFactory
43 35
     */
44
    public function __construct(array $config, CallbackHandler $handler = null, CallbackBuilderFactoryInterface $callbackBuilderFactory = null)
45 35
    {
46 35
        $this->callbackHandler = $handler;
47 35
        $this->callbackBuilderFactory = $callbackBuilderFactory;
48
        $this->config = array_merge(
49 35
            array(
50 28
                'class' => '',
51 28
                'graph' => 'default',
52 28
                'property_path' => 'finiteState',
53 28
                'states' => array(),
54 28
                'transitions' => array(),
55
            ),
56 28
            $config
57 35
        );
58
    }
59
60
    /**
61
     * {@inheritdoc}
62 30
     */
63
    public function load(StateMachineInterface $stateMachine)
64 30
    {
65 5
        if (null === $this->callbackHandler) {
66 4
            $this->callbackHandler = new CallbackHandler($stateMachine->getDispatcher());
67
        }
68 30
69 30
        if (null === $this->callbackBuilderFactory) {
70 24
            $this->callbackBuilderFactory = new CallbackBuilderFactory();
71
        }
72 30
73 30
        if (!$stateMachine->hasStateAccessor()) {
74
            $stateMachine->setStateAccessor(new PropertyPathStateAccessor($this->config['property_path']));
75 30
        }
76 30
77 30
        $stateMachine->setGraph($this->config['graph']);
78 30
79
        $this->loadCallbacks($stateMachine);
80
        $this->loadStates($stateMachine);
81
        $this->loadTransitions($stateMachine);
82
    }
83 9
84
    /**
85 9
     * {@inheritdoc}
86
     */
87 5
    public function supports($object, $graph = 'default')
88
    {
89
        $reflection = new \ReflectionClass($this->config['class']);
90
91
        return $reflection->isInstance($object) && $graph === $this->config['graph'];
92
    }
93 30
94
    /**
95 30
     * @param $triggerName
96 30
     *
97 30
     * @return array
98 30
     */
99 30
    protected function findCallbacksByTrigger($triggerName)
100 30
    {
101 24
        $callbacks = [];
102
103 30
        foreach ([Callback::CLAUSE_BEFORE, Callback::CLAUSE_AFTER] as $position) {
104 25
            $callbacks[$position] = [];
105 25
106 24
            $callbacks[$position] = array_merge(
107 30
                $callbacks[$position],
108
                $this->findCallbacksByTriggerAndPosition($triggerName, $position)
109
            );
110
        }
111
112 30
        return $callbacks;
113
    }
114 30
115 30
    /**
116 30
     * @param $triggerName
117
     * @param $position
118 30
     *
119
     * @return array
120
     */
121
    protected function findCallbacksByTriggerAndPosition($triggerName, $position)
122
    {
123 25
        $callbacks = [];
124
125 25
        if (empty($this->config['callbacks'][$position])) {
126
            return $callbacks;
127 25
        }
128 5
129 4
        foreach ($this->config['callbacks'][$position] as $callbackName => $callback) {
130
            foreach ([Callback::CLAUSE_FROM, Callback::CLAUSE_TO, Callback::CLAUSE_ON] as $clause) {
131 25
                if (!empty($callback[$clause])) {
132 30
                    if ((is_string($callback[$clause]) && $callback[$clause] === $triggerName)
133
                        || (is_array($callback[$clause]) && in_array($triggerName, $callback[$clause]))) {
134 30
                        $callbacks[] = [$callbackName => $callback];
135 25
                    }
136 25
                }
137 25
            }
138 20
        }
139 25
140 25
        return $callbacks;
141 25
    }
142 25
143 20
    /**
144 20
     * @param StateMachineInterface $stateMachine
145 24
     */
146 30
    private function loadStates(StateMachineInterface $stateMachine)
147
    {
148
        $resolver = new OptionsResolver();
149
        $resolver->setDefaults(array('type' => StateInterface::TYPE_NORMAL, 'properties' => array()));
150
        $resolver->setAllowedValues('type', array(
151 30
            StateInterface::TYPE_INITIAL,
152
            StateInterface::TYPE_NORMAL,
153 30
            StateInterface::TYPE_FINAL,
154 25
        ));
155
156
        foreach ($this->config['states'] as $state => $config) {
157 10
            $config = $resolver->resolve($config);
158 10
            $stateMachine->addState(new State(
159 8
                $state,
160 10
                $config['type'],
161
                [],
162 10
                $config['properties'],
163
                $this->findCallbacksByTrigger($state)
164 10
            ));
165 5
        }
166
    }
167
168 10
    /**
169 10
     * @param StateMachineInterface $stateMachine
170 10
     */
171 10
    private function loadTransitions(StateMachineInterface $stateMachine)
172
    {
173 10
        $resolver = new OptionsResolver();
174 10
        $resolver->setRequired(array(Callback::CLAUSE_FROM, Callback::CLAUSE_TO));
175 10
        $resolver->setDefaults(array('guard' => null, 'configure_properties' => null, 'properties' => array()));
176 10
177 10
        $resolver->setAllowedTypes('configure_properties', array('null', 'callable'));
178 10
179
        $resolver->setNormalizer(Callback::CLAUSE_FROM, function (Options $options, $v) { return (array) $v; });
180 10
        $resolver->setNormalizer('guard', function (Options $options, $v) { return !isset($v) ? null : $v; });
181 8
        $resolver->setNormalizer('configure_properties', function (Options $options, $v) {
182 10
            $resolver = new OptionsResolver();
183
184 10
            $resolver->setDefaults($options['properties']);
185
186 10
            if (is_callable($v)) {
187
                $v($resolver);
188 10
            }
189
190 10
            return $resolver;
191 8
        });
192 8
193
        foreach ($this->config['transitions'] as $transition => $config) {
194 8
            $config = $resolver->resolve($config);
195
            $stateMachine->addTransition(
196 10
                new Transition(
197
                    $transition,
198 10
                    $config[Callback::CLAUSE_FROM],
199 10
                    $config['to'],
200 10
                    $config['guard'],
201
                    $config['configure_properties'],
202 10
                    $this->findCallbacksByTrigger($transition)
203 10
                )
204 10
            );
205 10
        }
206 10
    }
207 10
208
    /**
209 10
     * @param StateMachineInterface $stateMachine
210
     */
211
    private function loadCallbacks(StateMachineInterface $stateMachine)
212
    {
213
        if (!isset($this->config['callbacks'])) {
214
            return;
215
        }
216
217
        $stateMachine->setCallbacks($this->config['callbacks']);
218
219
        foreach (array(Callback::CLAUSE_BEFORE, Callback::CLAUSE_AFTER) as $position) {
220
            $this->loadCallbacksFor($position, $stateMachine);
221
        }
222
    }
223
224
    private function loadCallbacksFor($position, $stateMachine)
225
    {
226
        if (!isset($this->config['callbacks'][$position])) {
227
            return;
228
        }
229
230
        $method = 'add'.ucfirst($position);
231
        $resolver = $this->getCallbacksResolver();
232
        foreach ($this->config['callbacks'][$position] as $specs) {
233
            $specs = $resolver->resolve($specs);
234
235
            $callback = $this->callbackBuilderFactory->createBuilder($stateMachine)
236
                ->setFrom($specs[Callback::CLAUSE_FROM])
237
                ->setTo($specs[Callback::CLAUSE_TO])
238
                ->setOn($specs[Callback::CLAUSE_ON])
239
                ->setCallable($specs[Callback::CLAUSE_DO])
240
                ->getCallback();
241
242
            $this->callbackHandler->$method($callback);
243
        }
244
    }
245
246
    private function getCallbacksResolver()
247
    {
248
        $resolver = new OptionsResolver();
249
250
        $resolver->setDefaults(
251
            array(
252
                Callback::CLAUSE_ON => array(),
253
                Callback::CLAUSE_FROM => array(),
254
                Callback::CLAUSE_TO => array(),
255
            )
256
        );
257
258
        $resolver->setRequired(array(Callback::CLAUSE_DO));
259
260
        $resolver->setAllowedTypes(Callback::CLAUSE_ON,   array('string', 'array'));
261
        $resolver->setAllowedTypes(Callback::CLAUSE_FROM, array('string', 'array'));
262
        $resolver->setAllowedTypes(Callback::CLAUSE_TO,   array('string', 'array'));
263
264
        $toArrayNormalizer = function (Options $options, $value) {
265
            return (array) $value;
266
        };
267
        $resolver->setNormalizer(Callback::CLAUSE_ON,  $toArrayNormalizer);
268
        $resolver->setNormalizer(Callback::CLAUSE_FROM, $toArrayNormalizer);
269
        $resolver->setNormalizer(Callback::CLAUSE_TO,   $toArrayNormalizer);
270
271
        return $resolver;
272
    }
273
}
274