Completed
Push — master ( 69686b...b3f98f )
by Arman
21s queued 16s
created

RouteDispatcher::resolveAction()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 9
rs 10
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.9.7
13
 */
14
15
namespace Quantum\Router;
16
17
use Quantum\Libraries\Csrf\Exceptions\CsrfException;
18
use Quantum\Exceptions\ControllerException;
19
use Quantum\Di\Exceptions\DiException;
20
use Quantum\Libraries\Csrf\Csrf;
21
use Quantum\Http\Request;
22
use ReflectionException;
23
use Quantum\Di\Di;
24
25
class RouteDispatcher
26
{
27
28
    /**
29
     * Handles the incoming HTTP request.
30
     * @param Request $request
31
     * @return void
32
     * @throws ControllerException
33
     * @throws CsrfException
34
     * @throws DiException
35
     * @throws ReflectionException
36
     */
37
    public static function handle(Request $request): void
38
    {
39
        $callback = route_callback();
40
41
        if ($callback) {
42
            self::callControllerMethod($callback);
43
            return;
44
        }
45
46
        $controller = self::resolveController();
47
        $action = self::resolveAction($controller);
48
49
        self::verifyCsrf($controller, $request);
50
51
        self::callControllerHook($controller, '__before');
52
        self::callControllerMethod([$controller, $action]);
53
        self::callControllerHook($controller, '__after');
54
    }
55
56
    /**
57
     * Loads and gets the current route's controller instance.
58
     * @return RouteController
59
     * @throws DiException
60
     * @throws ReflectionException
61
     */
62
    private static function resolveController(): RouteController
63
    {
64
        $controllerName = current_controller();
65
        $moduleName = current_module();
66
67
        $controllerPath = modules_dir() . DS . $moduleName . DS . 'Controllers' . DS . $controllerName . '.php';
68
        $controllerClass = module_base_namespace() . '\\' . $moduleName . '\\Controllers\\' . $controllerName;
69
70
        require_once $controllerPath;
71
72
        return new $controllerClass();
73
    }
74
75
    /**
76
     * Retrieves the current route's action for the controller.
77
     * @param RouteController $controller
78
     * @return string|null
79
     * @throws ControllerException
80
     */
81
    private static function resolveAction(RouteController $controller): ?string
82
    {
83
        $action = current_action();
84
85
        if (!$action || !method_exists($controller, $action)) {
86
            throw ControllerException::actionNotDefined($action);
0 ignored issues
show
Bug introduced by
It seems like $action can also be of type null; however, parameter $name of Quantum\Exceptions\Contr...ion::actionNotDefined() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

86
            throw ControllerException::actionNotDefined(/** @scrutinizer ignore-type */ $action);
Loading history...
87
        }
88
89
        return $action;
90
    }
91
92
    /**
93
     * Calls controller method
94
     * @param callable $callable
95
     * @return void
96
     * @throws DiException
97
     * @throws ReflectionException
98
     */
99
    private static function callControllerMethod(callable $callable)
100
    {
101
        call_user_func_array($callable, Di::autowire($callable, self::getRouteParams()));
102
    }
103
104
    /**
105
     * Calls controller lifecycle method if it exists.
106
     * @param object $controller
107
     * @param string $method
108
     * @return void
109
     * @throws DiException
110
     * @throws ReflectionException
111
     */
112
    private static function callControllerHook(object $controller, string $method): void
113
    {
114
        if (method_exists($controller, $method)) {
115
            self::callControllerMethod([$controller, $method]);
116
        }
117
    }
118
119
    /**
120
     * Retrieves the route parameters from the current route.
121
     * @return array
122
     */
123
    private static function getRouteParams(): array
124
    {
125
        return array_column(route_params(), 'value');
126
    }
127
128
    /**
129
     * Verifies CSRF token if required
130
     * @param RouteController $controller
131
     * @param Request $request
132
     * @return void
133
     * @throws CsrfException
134
     */
135
    private static function verifyCsrf(RouteController $controller, Request $request): void
136
    {
137
        if ($controller->csrfVerification && in_array($request->getMethod(), Csrf::METHODS, true)) {
138
            csrf()->checkToken($request);
139
        }
140
    }
141
}
142