Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
97.56% |
40 / 41 |
|
85.71% |
6 / 7 |
CRAP | |
0.00% |
0 / 1 |
Dispatcher | |
97.56% |
40 / 41 |
|
85.71% |
6 / 7 |
25 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
dispatch | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
8 | |||
dispatchString | |
93.75% |
15 / 16 |
|
0.00% |
0 / 1 |
8.02 | |||
dispatchArray | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
dispatchClosure | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
getController | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
set | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Dispatcher |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace phpOMS\Dispatcher; |
16 | |
17 | use phpOMS\Application\ApplicationAbstract; |
18 | use phpOMS\Autoloader; |
19 | use phpOMS\System\File\PathException; |
20 | |
21 | /** |
22 | * Dispatcher class. |
23 | * |
24 | * @package phpOMS\Dispatcher |
25 | * @license OMS License 2.0 |
26 | * @link https://jingga.app |
27 | * @since 1.0.0 |
28 | */ |
29 | final class Dispatcher implements DispatcherInterface |
30 | { |
31 | /** |
32 | * Application. |
33 | * |
34 | * @var null|ApplicationAbstract |
35 | * @since 1.0.0 |
36 | */ |
37 | private ?ApplicationAbstract $app; |
38 | |
39 | /** |
40 | * Controller. |
41 | * |
42 | * Set in the module manager on module initialization. |
43 | * |
44 | * @var array |
45 | * @since 1.0.0 |
46 | */ |
47 | private array $controllers = []; |
48 | |
49 | /** |
50 | * Constructor. |
51 | * |
52 | * @param ApplicationAbstract $app Appliaction |
53 | * |
54 | * @since 1.0.0 |
55 | */ |
56 | public function __construct(ApplicationAbstract $app = null) |
57 | { |
58 | $this->app = $app; |
59 | } |
60 | |
61 | /** |
62 | * {@inheritdoc} |
63 | */ |
64 | public function dispatch(array | string | callable $controller, mixed ...$data) : array |
65 | { |
66 | $views = []; |
67 | $data = \array_values($data); |
68 | |
69 | if (\is_array($controller) && isset($controller['dest'])) { |
70 | if (!empty($controller['data'])) { |
71 | $data = \array_merge( |
72 | empty($data) ? [] : $data, |
73 | \is_array($controller['data']) ? $controller['data'] : [$controller['data']] |
74 | ); |
75 | } |
76 | |
77 | $controller = $controller['dest']; |
78 | } |
79 | |
80 | // Php void functions always return null. |
81 | // In a different language the Api functions would reguire a return type |
82 | // If null is returned (i.e. void functions) these get ignored later in the response renderer as null is not "rendered" |
83 | if (\is_string($controller)) { |
84 | $views += $this->dispatchString($controller, $data); |
85 | } elseif (\is_array($controller)) { |
86 | $views += $this->dispatchArray($controller, $data); |
87 | } else { |
88 | $views[] = $this->dispatchClosure($controller, $data); |
89 | } |
90 | |
91 | return $views; |
92 | } |
93 | |
94 | /** |
95 | * Dispatch string. |
96 | * |
97 | * The dispatcher can dispatch static functions. |
98 | * String: `some/namespace/path::myStaticFunction` |
99 | * |
100 | * Additionally it's also possible to dispatch functions of modules. |
101 | * Modules are classes which can get instantiated with `new Class(ApplicationAbstract $app)` |
102 | * String: `some/namespace/path:myMethod` |
103 | * |
104 | * @param string $controller Controller string |
105 | * @param null|array $data Data |
106 | * |
107 | * @return array |
108 | * |
109 | * @throws PathException this exception is thrown if the function cannot be autoloaded |
110 | * @throws \Exception this exception is thrown if the function is not callable |
111 | * @throws \UnexpectedValueException this exception is thrown if the controller string is malformed |
112 | * |
113 | * @since 1.0.0 |
114 | */ |
115 | private function dispatchString(string $controller, array $data = null) : array |
116 | { |
117 | $views = []; |
118 | $dispatch = \explode(':', $controller); |
119 | |
120 | if (!Autoloader::exists($dispatch[0]) && !isset($this->controllers[$dispatch[0]])) { |
121 | throw new PathException($dispatch[0]); |
122 | } |
123 | |
124 | if (($c = \count($dispatch)) === 3) { |
125 | /* Handling static functions */ |
126 | $function = $dispatch[0] . '::' . $dispatch[2]; |
127 | |
128 | if (!\is_callable($function)) { |
129 | throw new \Exception('Endpoint "'. $function .'" is not callable!'); |
130 | } |
131 | |
132 | $views[$controller] = $data === null ? $function() : $function(...$data); |
133 | } elseif ($c === 2) { |
134 | $obj = $this->getController($dispatch[0]); |
135 | $views[$controller] = $data === null |
136 | ? $obj->{$dispatch[1]}() |
137 | : $obj->{$dispatch[1]}(...$data); |
138 | } else { |
139 | throw new \UnexpectedValueException('Unexpected function.'); |
140 | } |
141 | |
142 | return $views; |
143 | } |
144 | |
145 | /** |
146 | * Dispatch array. |
147 | * |
148 | * @param array $controller Controller string |
149 | * @param null|array $data Data |
150 | * |
151 | * @return array |
152 | * |
153 | * @since 1.0.0 |
154 | */ |
155 | private function dispatchArray(array $controller, array $data = null) : array |
156 | { |
157 | $views = []; |
158 | foreach ($controller as $controllerSingle) { |
159 | $views += $data === null ? $this->dispatch($controllerSingle) : $this->dispatch($controllerSingle, ...$data); |
160 | } |
161 | |
162 | return $views; |
163 | } |
164 | |
165 | /** |
166 | * Dispatch closure. |
167 | * |
168 | * @param Callable $controller Controller string |
169 | * @param null|array $data Data |
170 | * |
171 | * @return mixed |
172 | * |
173 | * @since 1.0.0 |
174 | */ |
175 | private function dispatchClosure(callable $controller, array $data = null) : mixed |
176 | { |
177 | return $data === null ? $controller($this->app) : $controller($this->app, ...$data); |
178 | } |
179 | |
180 | /** |
181 | * Dispatch controller. |
182 | * |
183 | * @param string $controller Controller |
184 | * |
185 | * @return object |
186 | * |
187 | * @throws PathException this exception is thrown in case the controller couldn't be found |
188 | * |
189 | * @since 1.0.0 |
190 | */ |
191 | private function getController(string $controller) : object |
192 | { |
193 | if (!isset($this->controllers[$controller])) { |
194 | $this->controllers[$controller] = new $controller($this->app); |
195 | } |
196 | |
197 | return $this->controllers[$controller]; |
198 | } |
199 | |
200 | /** |
201 | * Set controller by alias. |
202 | * |
203 | * @param object $controller Controller |
204 | * @param string $name Controller string |
205 | * |
206 | * @return void |
207 | * |
208 | * @since 1.0.0 |
209 | */ |
210 | public function set(object $controller, string $name) : void |
211 | { |
212 | $this->controllers[$name] = $controller; |
213 | } |
214 | } |