Completed
Push — master ( fc63d4...58d04b )
by Yohan
06:35 queued 04:20
created

ArrayLoader::getCallbacksResolver()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 1

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 27
ccs 18
cts 18
cp 1
rs 8.8571
cc 1
eloc 17
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Finite\Loader;
4
5
use Finite\Event\Callback\CallbackBuilder;
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 13. 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
     */
44 35
    public function __construct(array $config, CallbackHandler $handler = null, CallbackBuilderFactoryInterface $callbackBuilderFactory = null)
45
    {
46 35
        $this->callbackHandler        = $handler;
47 35
        $this->callbackBuilderFactory = $callbackBuilderFactory;
48 35
        $this->config = array_merge(
49
            array(
50 35
                'class'         => '',
51 28
                'graph'         => 'default',
52 28
                'property_path' => 'finiteState',
53 28
                'states'        => array(),
54 28
                'transitions'   => array(),
55 28
            ),
56
            $config
57 28
        );
58 35
    }
59
60
    /**
61
     * @{inheritDoc}
62
     */
63 30
    public function load(StateMachineInterface $stateMachine)
64
    {
65 30
        if (null === $this->callbackHandler) {
66 5
            $this->callbackHandler = new CallbackHandler($stateMachine->getDispatcher());
67 4
        }
68
69 30
        if (null === $this->callbackBuilderFactory) {
70 30
            $this->callbackBuilderFactory = new CallbackBuilderFactory;
71 24
        }
72
73 30
        $stateMachine->setStateAccessor(new PropertyPathStateAccessor($this->config['property_path']));
74 30
        $stateMachine->setGraph($this->config['graph']);
75
76 30
        $this->loadStates($stateMachine);
77 30
        $this->loadTransitions($stateMachine);
78 30
        $this->loadCallbacks($stateMachine);
79 30
    }
80
81
    /**
82
     * @{inheritDoc}
83
     */
84 5
    public function supports($object, $graph = 'default')
85 4
    {
86 5
        $reflection = new \ReflectionClass($this->config['class']);
87
88 5
        return $reflection->isInstance($object) && $graph === $this->config['graph'];
89
    }
90
91
    /**
92
     * @param StateMachineInterface $stateMachine
93
     */
94 30
    private function loadStates(StateMachineInterface $stateMachine)
95
    {
96 30
        $resolver = new OptionsResolver;
97 30
        $resolver->setDefaults(array('type' => StateInterface::TYPE_NORMAL, 'properties' => array()));
98 30
        $resolver->setAllowedValues('type', array(
99 30
            StateInterface::TYPE_INITIAL,
100 30
            StateInterface::TYPE_NORMAL,
101 6
            StateInterface::TYPE_FINAL
102 24
        ));
103
104 30
        foreach ($this->config['states'] as $state => $config) {
105 25
            $config = $resolver->resolve($config);
106 25
            $stateMachine->addState(new State($state, $config['type'], array(), $config['properties']));
107 24
        }
108 30
    }
109
110
    /**
111
     * @param StateMachineInterface $stateMachine
112
     */
113 30
    private function loadTransitions(StateMachineInterface $stateMachine)
114
    {
115 30
        $resolver = new OptionsResolver();
116 30
        $resolver->setRequired(array('from', 'to'));
117 30
        $resolver->setDefaults(array('guard' => null, 'configure_properties' => null, 'properties' => array()));
118
119 30
        $resolver->setAllowedTypes('configure_properties', array('null', 'callable'));
120
121
        $resolver->setNormalizer('from', function (Options $options, $v) { return (array) $v; });
122
        $resolver->setNormalizer('guard', function (Options $options, $v) { return !isset($v) ? null : $v; });
123
        $resolver->setNormalizer('configure_properties', function (Options $options, $v) {
124 25
            $resolver = new OptionsResolver;
125
126 25
            $resolver->setDefaults($options['properties']);
127
128 25
            if (is_callable($v)) {
129 5
                $v($resolver);
130 4
            }
131
132 25
            return $resolver;
133 30
        });
134
135 30
        foreach ($this->config['transitions'] as $transition => $config) {
136 25
            $config = $resolver->resolve($config);
137 25
            $stateMachine->addTransition(
138 25
                new Transition(
139 20
                    $transition,
140 25
                    $config['from'],
141 25
                    $config['to'],
142 25
                    $config['guard'],
143 25
                    $config['configure_properties']
144 20
                )
145 20
            );
146 24
        }
147 30
    }
148
149
    /**
150
     * @param StateMachineInterface $stateMachine
151
     */
152 30
    private function loadCallbacks(StateMachineInterface $stateMachine)
153
    {
154 30
        if (!isset($this->config['callbacks'])) {
155 25
            return;
156
        }
157
158 10
        foreach (array('before', 'after') as $position) {
159 10
            $this->loadCallbacksFor($position, $stateMachine);
160 8
        }
161 10
    }
162
163 10
    private function loadCallbacksFor($position, $stateMachine)
164
    {
165 10
        if (!isset($this->config['callbacks'][$position])) {
166 5
            return;
167
        }
168
169 10
        $method   = 'add'.ucfirst($position);
170 10
        $resolver = $this->getCallbacksResolver();
171 10
        foreach ($this->config['callbacks'][$position] as $specs) {
172 10
            $specs = $resolver->resolve($specs);
173
174 10
            $callback = $this->callbackBuilderFactory->createBuilder($stateMachine)
175 10
                ->setFrom($specs['from'])
176 10
                ->setTo($specs['to'])
177 10
                ->setOn($specs['on'])
178 10
                ->setCallable($specs['do'])
179 10
                ->getCallback();
180
181 10
            $this->callbackHandler->$method($callback);
182 8
        }
183 10
    }
184
185 10
    private function getCallbacksResolver()
186
    {
187 10
        $resolver = new OptionsResolver;
188
189 10
        $resolver->setDefaults(
190
            array(
191 10
                'on'   => array(),
192 8
                'from' => array(),
193 8
                'to'   => array(),
194
            )
195 8
        );
196
197 10
        $resolver->setRequired(array('do'));
198
199 10
        $resolver->setAllowedTypes('on',   array('string', 'array'));
200 10
        $resolver->setAllowedTypes('from', array('string', 'array'));
201 10
        $resolver->setAllowedTypes('to',   array('string', 'array'));
202
203 10
        $toArrayNormalizer = function (Options $options, $value) {
204 10
            return (array) $value;
205 10
        };
206 10
        $resolver->setNormalizer('on',  $toArrayNormalizer);
207 10
        $resolver->setNormalizer('from', $toArrayNormalizer);
208 10
        $resolver->setNormalizer('to',   $toArrayNormalizer);
209
210 10
        return $resolver;
211
    }
212
}
213