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

RouteHandler::handleAsCallable()   B

Complexity

Conditions 9
Paths 3

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 11
cts 11
cp 1
rs 8.0555
c 0
b 0
f 0
cc 9
nc 3
nop 2
crap 9
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
0 ignored issues
show
Unused Code introduced by
The parameter $di is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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) {
0 ignored issues
show
Bug introduced by
The class ArgumentCountError does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
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
0 ignored issues
show
Bug introduced by
There is no parameter named $di. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
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