Passed
Pull Request — master (#38)
by Melech
05:50 queued 01:43
created

Dispatcher   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 237
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 22
eloc 63
dl 0
loc 237
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getArgumentValue() 0 8 2
A dispatch() 0 10 1
A dispatchCallable() 0 7 1
A dispatchClass() 0 8 1
A __construct() 0 3 1
A getArguments() 0 21 3
A dispatchVariable() 0 7 1
A dispatchConstant() 0 8 2
A getDependencies() 0 38 6
A dispatchClassProperty() 0 11 2
A dispatchClassMethod() 0 17 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Valkyrja\Dispatcher;
15
16
use Valkyrja\Container\Contract\Container;
17
use Valkyrja\Dispatcher\Contract\Dispatcher as Contract;
18
use Valkyrja\Dispatcher\Data\CallableDispatch;
19
use Valkyrja\Dispatcher\Data\ClassDispatch;
20
use Valkyrja\Dispatcher\Data\ConstantDispatch;
21
use Valkyrja\Dispatcher\Data\Contract\Dispatch;
22
use Valkyrja\Dispatcher\Data\GlobalVariableDispatch;
23
use Valkyrja\Dispatcher\Data\MethodDispatch;
24
use Valkyrja\Dispatcher\Data\PropertyDispatch;
25
use Valkyrja\Dispatcher\Exception\InvalidArgumentException;
26
27
use function array_map;
28
use function constant;
29
use function is_array;
30
use function is_string;
31
32
/**
33
 * Class Dispatcher.
34
 *
35
 * @author Melech Mizrachi
36
 */
37
class Dispatcher implements Contract
38
{
39
    /**
40
     * Dispatcher constructor.
41
     *
42
     * @param Container $container The container
43
     */
44
    public function __construct(
45
        protected Container $container = new \Valkyrja\Container\Container()
46
    ) {
47
    }
48
49
    /**
50
     * @inheritDoc
51
     */
52
    public function dispatch(Dispatch $dispatch, array|null $arguments = null): mixed
53
    {
54
        return match (true) {
55
            $dispatch instanceof MethodDispatch         => $this->dispatchClassMethod($dispatch, $arguments),
56
            $dispatch instanceof PropertyDispatch       => $this->dispatchClassProperty($dispatch),
57
            $dispatch instanceof ConstantDispatch       => $this->dispatchConstant($dispatch),
58
            $dispatch instanceof ClassDispatch          => $this->dispatchClass($dispatch, $arguments),
59
            $dispatch instanceof CallableDispatch       => $this->dispatchCallable($dispatch, $arguments),
60
            $dispatch instanceof GlobalVariableDispatch => $this->dispatchVariable($dispatch),
61
            default                                     => throw new InvalidArgumentException('Invalid dispatch'),
62
        };
63
    }
64
65
    /**
66
     * Dispatch a class method.
67
     *
68
     * @param MethodDispatch               $dispatch  The dispatch
69
     * @param array<array-key, mixed>|null $arguments The arguments
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, mixed>|null at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, mixed>|null.
Loading history...
70
     *
71
     * @return mixed
72
     */
73
    protected function dispatchClassMethod(MethodDispatch $dispatch, array|null $arguments = null): mixed
74
    {
75
        $method = $dispatch->getMethod();
76
        // Get the arguments with dependencies
77
        $arguments = $this->getArguments($dispatch, $arguments) ?? [];
78
        // Get the class
79
        $class = $dispatch->getClass();
80
        /**
81
         * @psalm-suppress MixedMethodCall The developer should have passed the proper arguments
82
         *
83
         * @var mixed $response
84
         */
85
        $response = $dispatch->isStatic()
86
            ? $class::$method(...$arguments)
87
            : $this->container->get($class)->$method(...$arguments);
88
89
        return $response;
90
    }
91
92
    /**
93
     * Dispatch a class property.
94
     *
95
     * @param PropertyDispatch $dispatch The dispatch
96
     *
97
     * @return mixed
98
     */
99
    protected function dispatchClassProperty(PropertyDispatch $dispatch): mixed
100
    {
101
        $property = $dispatch->getProperty();
102
        // Get the class
103
        $class = $dispatch->getClass();
104
        /** @var mixed $response */
105
        $response = $dispatch->isStatic()
106
            ? $class::$$property
107
            : $this->container->get($class)->{$property};
108
109
        return $response;
110
    }
111
112
    /**
113
     * Dispatch a constant.
114
     *
115
     * @param ConstantDispatch $dispatch The dispatch
116
     *
117
     * @return mixed
118
     */
119
    protected function dispatchConstant(ConstantDispatch $dispatch): mixed
120
    {
121
        $constant = $dispatch->getConstant();
122
        $constant = ($class = $dispatch->getClass())
123
            ? $class . '::' . $constant
124
            : $constant;
125
126
        return constant($constant);
127
    }
128
129
    /**
130
     * Dispatch a class.
131
     *
132
     * @param ClassDispatch                $dispatch  The dispatch
133
     * @param array<array-key, mixed>|null $arguments The arguments
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, mixed>|null at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, mixed>|null.
Loading history...
134
     *
135
     * @return mixed
136
     */
137
    protected function dispatchClass(ClassDispatch $dispatch, array|null $arguments = null): mixed
138
    {
139
        $className = $dispatch->getClass();
140
        // Get the arguments with dependencies
141
        $arguments = $this->getArguments($dispatch, $arguments) ?? [];
142
143
        // Get the class through the container
144
        return $this->container->get($className, $arguments);
145
    }
146
147
    /**
148
     * Dispatch a function.
149
     *
150
     * @param CallableDispatch             $dispatch  The dispatch
151
     * @param array<array-key, mixed>|null $arguments The arguments
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, mixed>|null at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, mixed>|null.
Loading history...
152
     *
153
     * @return mixed
154
     */
155
    protected function dispatchCallable(CallableDispatch $dispatch, array|null $arguments = null): mixed
156
    {
157
        $callable = $dispatch->getCallable();
158
        // Get the arguments with dependencies
159
        $arguments = $this->getArguments($dispatch, $arguments) ?? [];
160
161
        return $callable(...$arguments);
162
    }
163
164
    /**
165
     * Dispatch a variable.
166
     *
167
     * @param GlobalVariableDispatch $dispatch The dispatch
168
     *
169
     * @return mixed
170
     */
171
    protected function dispatchVariable(GlobalVariableDispatch $dispatch): mixed
172
    {
173
        $variable = $dispatch->getVariable();
174
175
        global $$variable;
176
177
        return $$variable;
178
    }
179
180
    /**
181
     * Get a dispatch's arguments.
182
     *
183
     * @param CallableDispatch|ClassDispatch|MethodDispatch $dispatch  The dispatch
184
     * @param array<array-key, mixed>|null                  $arguments [optional] The arguments
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, mixed>|null at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, mixed>|null.
Loading history...
185
     *
186
     * @return array<array-key, mixed>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, mixed>|null at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, mixed>|null.
Loading history...
187
     */
188
    protected function getArguments(CallableDispatch|ClassDispatch|MethodDispatch $dispatch, array|null $arguments = null): array|null
189
    {
190
        // Get either the arguments passed or from the dispatch model
191
        $arguments ??= $dispatch->getArguments();
192
193
        // Set the listener arguments to a new blank array
194
        $dependencies = $this->getDependencies($dispatch);
195
196
        // If there are no arguments
197
        if ($arguments === null) {
198
            // Return the dependencies only
199
            return $dependencies;
200
        }
201
202
        // Iterate through the arguments
203
        foreach ($arguments as $argument) {
204
            // Append the argument to the arguments list
205
            $dependencies[] = $this->getArgumentValue($argument);
206
        }
207
208
        return $dependencies;
209
    }
210
211
    /**
212
     * Get a dispatch's dependencies.
213
     *
214
     * @param CallableDispatch|ClassDispatch|MethodDispatch $dispatch The dispatch
215
     *
216
     * @return array<array-key, mixed>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, mixed>|null at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, mixed>|null.
Loading history...
217
     */
218
    protected function getDependencies(CallableDispatch|ClassDispatch|MethodDispatch $dispatch): array|null
219
    {
220
        // If there are dependencies
221
        if (($dependencies = $dispatch->getDependencies()) === null) {
222
            return [];
223
        }
224
225
        $context = match (true) {
0 ignored issues
show
Unused Code introduced by
The assignment to $context is dead and can be removed.
Loading history...
226
            $dispatch instanceof ClassDispatch    => $dispatch->getClass(),
0 ignored issues
show
Bug introduced by
The method getClass() does not exist on Valkyrja\Dispatcher\Data\CallableDispatch. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

226
            $dispatch instanceof ClassDispatch    => $dispatch->/** @scrutinizer ignore-call */ getClass(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
227
            $dispatch instanceof CallableDispatch => is_array($callable = $dispatch->getCallable()) && is_string($callable[0])
0 ignored issues
show
Bug introduced by
The method getCallable() does not exist on Valkyrja\Dispatcher\Data\MethodDispatch. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

227
            $dispatch instanceof CallableDispatch => is_array($callable = $dispatch->/** @scrutinizer ignore-call */ getCallable()) && is_string($callable[0])

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getCallable() does not exist on Valkyrja\Dispatcher\Data\ClassDispatch. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

227
            $dispatch instanceof CallableDispatch => is_array($callable = $dispatch->/** @scrutinizer ignore-call */ getCallable()) && is_string($callable[0])

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
228
                ? $callable[0]
229
                : null,
230
        };
231
        $member  = match (true) {
0 ignored issues
show
Unused Code introduced by
The assignment to $member is dead and can be removed.
Loading history...
232
            $dispatch instanceof MethodDispatch   => $dispatch->getMethod(),
0 ignored issues
show
Bug introduced by
The method getMethod() does not exist on Valkyrja\Dispatcher\Data\CallableDispatch. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

232
            $dispatch instanceof MethodDispatch   => $dispatch->/** @scrutinizer ignore-call */ getMethod(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getMethod() does not exist on Valkyrja\Dispatcher\Data\ClassDispatch. It seems like you code against a sub-type of Valkyrja\Dispatcher\Data\ClassDispatch such as Valkyrja\Dispatcher\Data\MethodDispatch. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

232
            $dispatch instanceof MethodDispatch   => $dispatch->/** @scrutinizer ignore-call */ getMethod(),
Loading history...
233
            $dispatch instanceof CallableDispatch => is_array($callable = $dispatch->getCallable()) && is_string($callable[1])
234
                ? $callable[1]
235
                : null,
236
            default                               => null
237
        };
238
239
        $containerContext = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $containerContext is dead and can be removed.
Loading history...
240
241
        $container = $this->container;
242
        // $hasContext = $context !== null && $container instanceof ContextAwareContainer;
243
        //
244
        // if ($hasContext) {
245
        //     /** @var ContextAwareContainer $container */
246
        //     $containerContext = $container->withContext($context, $member);
247
        // }
248
249
        return array_map(
250
            /** @param class-string $dependency */
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
251
            // static fn (string $dependency): mixed => $containerContext !== null && $containerContext->has($dependency)
252
            //     ? $containerContext->get($dependency)
253
            //     : $container->get($dependency),
254
            static fn (string $dependency): mixed => $container->get($dependency),
255
            $dependencies
256
        );
257
    }
258
259
    /**
260
     * Get argument value.
261
     *
262
     * @param mixed $argument The argument
263
     *
264
     * @return mixed
265
     */
266
    protected function getArgumentValue(mixed $argument): mixed
267
    {
268
        if ($argument instanceof Dispatch) {
269
            // Dispatch the argument and set the results to the argument
270
            $argument = $this->dispatch($argument);
271
        }
272
273
        return $argument;
274
    }
275
}
276