Completed
Push — master ( cba7ca...11b479 )
by Mikael
04:30
created

src/Route/RouteHandler.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Anax\Route;
4
5
use Anax\Commons\ContainerInjectableInterface;
6
use Anax\Route\Exception\ConfigurationException;
7
use Anax\Route\Exception\NotFoundException;
8
9
/**
10
 * Call a routes handler and return the results.
11
 */
12
class RouteHandler
13
{
14
    /**
15
     * Handle the action for a route and return the results.
16
     *
17
     * @param string                       $method    the request method.
18
     * @param string                       $path      that was matched.
19
     * @param string|array                 $action    base for the callable.
20
     * @param array                        $arguments optional arguments.
21
     * @param ContainerInjectableInterface $di        container with services.
22
     *
23
     * @return mixed as the result from the route handler.
24
     */
25 110
    public function handle(
26
        string $method = null,
27
        string $path = null,
28
        $action,
29
        array $arguments = [],
30
        ContainerInjectableInterface $di = null
31
    ) {
32 110
        if (is_null($action)) {
33 1
            return;
34
        }
35
36 109
        if (is_callable($action)) {
37 92
            return $this->handleAsCallable($action, $arguments);
38
        }
39
40 17
        if (is_string($action) && class_exists($action)) {
41 16
            $callable = $this->isControllerAction($method, $path, $action);
42 16
            if ($callable) {
43 16
                return $this->handleAsControllerAction($callable);
44
            }
45
        }
46
47
        // if ($di
48
        //     && is_array($action)
49
        //     && isset($action[0])
50
        //     && isset($action[1])
51
        //     && is_string($action[0])
52
        // ) {
53
        //     // Try to load service from app/di injected container
54
        //     return $this->handleUsingDi($action, $arguments, $di);
55
        // }
56
        //
57 1
        throw new ConfigurationException("Handler for route does not seem to be a callable action.");
58
    }
59
60
61
62
    /**
63
     * Check if items can be used to call a controller action, verify
64
     * that the controller exists, the action has a class-method to call.
65
     *
66
     * @param string $method the request method.
67
     * @param string $path   the matched path, base for the controller action
68
     *                       and the arguments.
69
     * @param string $class  the controller class
70
     *
71
     * @return array with callable details.
72
     */
73 16
    protected function isControllerAction(
74
        string $method = null,
75
        string $path = null,
76
        string $class
77
    ) {
78 16
        $args = explode("/", $path);
79 16
        $action = array_shift($args);
80 16
        $action = empty($action) ? "index" : $action;
81 16
        $action1 = "${action}Action${method}";
82 16
        $action2 = "${action}Action";
83
84 16
        $refl = null;
85 16
        foreach ([$action1, $action2] as $action) {
86
            try {
87 16
                $refl = new \ReflectionMethod($class, $action);
88 16
                if (!$refl->isPublic()) {
89
                    throw new NotFoundException("Controller method '$class::$action' is not a public method.");
90
                }
91
92 16
                return [$class, $action, $args];
93 13
            } catch (\ReflectionException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
94
                ;
95
            }
96
        }
97
98
        return false;
99
    }
100
101
102
103
    /**
104
     * Call the controller action with optional arguments and call
105
     * initialisation methods if available.
106
     *
107
     * @param string $callable with details on what controller action to call.
108
     *
109
     * @return mixed result from the handler.
110
     */
111
    protected function handleAsControllerAction(array $callable)
112
    {
113 16
        $class = $callable[0];
114 16
        $action = $callable[1];
115 16
        $args = $callable[2];
116
117 16
        $obj = new $class();
118 16
        $refl = new \ReflectionMethod($class, "initialize");
119 16
        if ($refl->isPublic()) {
120 16
            $obj->initialize();
121
        }
122
123
        try {
124 16
            $res = $obj->$action(...$args);
125 2
        } catch (\ArgumentCountError $e) {
126 1
            throw new NotFoundException($e->getMessage());
127 1
        } catch (\TypeError $e) {
128 1
            throw new NotFoundException($e->getMessage());
129
        }
130
131 14
        return $res;
132
    }
133
134
135
136
    /**
137
     * Handle as callable support callables where the method is not static.
138
     *
139
     * @param string|array                 $action    base for the callable
140
     * @param array                        $arguments optional arguments
141
     * @param ContainerInjectableInterface $di        container with services
142
     *
143
     * @return mixed as the result from the route handler.
144
     */
145
    protected function handleAsCallable(
146
        $action,
147
        array $arguments
148
    ) {
149 92
        if (is_array($action)
150 92
            && isset($action[0])
151 92
            && isset($action[1])
152 92
            && is_string($action[0])
153 92
            && is_string($action[1])
154 92
            && class_exists($action[0])
155
        ) {
156
            // ["SomeClass", "someMethod"] but not static
157 2
            $refl = new \ReflectionMethod($action[0], $action[1]);
158 2
            if ($refl->isPublic() && !$refl->isStatic()) {
159 1
                $obj = new $action[0]();
160 1
                return $obj->{$action[1]}();
161
            }
162
        }
163
164 91
        return call_user_func($action, ...$arguments);
165
    }
166
167
168
169
    // /**
170
    //  * Load callable as a service from the $di container.
171
    //  *
172
    //  * @param string|array                 $action    base for the callable
173
    //  * @param array                        $arguments optional arguments
174
    //  * @param ContainerInjectableInterface $di        container with services
175
    //  *
176
    //  * @return mixed as the result from the route handler.
177
    //  */
178
    // protected function handleUsingDi(
179
    //     $action,
180
    //     array $arguments,
181
    //     ContainerInjectableInterface $di
182
    // ) {
183
    //     if (!$di->has($action[0])) {
184
    //         throw new ConfigurationException("Routehandler '{$action[0]}' not loaded in di.");
185
    //     }
186
    //
187
    //     $service = $di->get($action[0]);
188
    //     if (!is_callable([$service, $action[1]])) {
189
    //         throw new ConfigurationException(
190
    //             "Routehandler '{$action[0]}' does not have a callable method '{$action[1]}'."
191
    //         );
192
    //     }
193
    //
194
    //     return call_user_func(
195
    //         [$service, $action[1]],
196
    //         ...$arguments
197
    //     );
198
    // }
199
}
200