Assistant   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 51
dl 0
loc 148
rs 10
c 1
b 0
f 0
wmc 21

6 Methods

Rating   Name   Duplication   Size   Complexity  
A extractRouteAttributes() 0 7 2
A passDataToMethod() 0 23 6
A registerRoute() 0 12 2
A hasRouteAttribute() 0 3 1
A passDataToCallback() 0 20 3
B generatePassingData() 0 25 7
1
<?php
2
declare(strict_types=1);
3
4
namespace Utilities\Router\Utils;
5
6
use Utilities\Router\AnonymousController;
7
use Utilities\Router\Attributes\Route;
8
use Utilities\Router\Controller;
9
use Utilities\Router\Request;
10
use Utilities\Router\Router;
11
12
/**
13
 * Assistant class
14
 *
15
 * @link    https://github.com/utilities-php/router
16
 * @author  Shahrad Elahi (https://github.com/shahradelahi)
17
 * @license https://github.com/utilities-php/router/blob/master/LICENSE (MIT License)
18
 */
19
class Assistant
20
{
21
22
    /**
23
     * Pass data to anonymous function
24
     *
25
     * @param callable $callback
26
     * @param array $mergeWith (optional)
27
     * @return bool
28
     */
29
    public static function passDataToCallback(callable $callback, array $mergeWith = []): bool
30
    {
31
        if (!is_callable($callback)) {
32
            return false;
33
        }
34
35
        try {
36
37
            $reflection = new \ReflectionFunction($callback);
38
            $arguments = $reflection->getParameters();
39
40
            call_user_func_array($callback, self::generatePassingData($arguments, $mergeWith));
41
42
            return true;
43
44
        } catch (\Exception $e) {
45
            throw new \RuntimeException(
46
                $e->getMessage(),
47
                $e->getCode(),
48
                $e->getPrevious()
49
            );
50
        }
51
    }
52
53
    /**
54
     * Generate passing data
55
     *
56
     * @param \ReflectionParameter[] $require ["headers", "queries", "params", "input"]
57
     * @param array $mergeWith
58
     * @return array
59
     */
60
    private static function generatePassingData(array $require, array $mergeWith = []): array
61
    {
62
        $methodParams = [];
63
        $fetchRequirements = [];
64
65
        foreach ($require as $key => $value) {
66
            $fetchRequirements[$key] = $value->getName();
67
        }
68
69
        foreach ($fetchRequirements as $key) {
70
            if ($key == 'headers') {
71
                $methodParams['headers'] = getallheaders();
72
            }
73
            if ($key == 'queries') {
74
                $methodParams['queries'] = array_merge($_GET, $_POST);
75
            }
76
            if ($key == 'params') {
77
                $methodParams['params'] = Request::getParams();
78
            }
79
            if ($key == 'input') {
80
                $methodParams['input'] = file_get_contents('php://input');
81
            }
82
        }
83
84
        return array_merge($methodParams, $mergeWith);
85
    }
86
87
    /**
88
     * Register route from a controller
89
     *
90
     * @param Controller $class controller class
91
     * @param string $method method name
92
     * @param Route $route
93
     * @return void
94
     */
95
    public static function registerRoute(Controller $class, string $method, Route $route): void
96
    {
97
        if (!method_exists($class, $method)) {
98
            throw new \RuntimeException(sprintf(
99
                'Method `%s` not found in class `%s`',
100
                $method,
101
                get_class($class)
102
            ));
103
        }
104
105
        Router::match($route->getMethod(), $route->getUri(), function () use ($class, $method) {
106
            Assistant::passDataToMethod($class, $method);
107
        });
108
    }
109
110
    /**
111
     * Pass the request to the controller for self-processing
112
     *
113
     * @param Controller $class the controller class
114
     * @param string $method the method name
115
     * @param array $mergeWith (optional)
116
     * @return bool
117
     */
118
    public static function passDataToMethod(AnonymousController $class, string $method, array $mergeWith = []): bool
119
    {
120
        if (!method_exists($class, $method)) {
121
            return false;
122
        }
123
124
        $reflection = new \ReflectionMethod($class, $method);
125
        $arguments = $reflection->getParameters();
126
127
        if (Assistant::hasRouteAttribute($reflection)) {
128
            foreach (self::extractRouteAttributes($reflection) as $route) {
129
                if ($route->isSecure()) {
130
                    $authenticated = call_user_func([$class, '__isAuthenticated']);
131
                    if (!$authenticated) {
132
                        $class->__unauthorized();
133
                    }
134
                }
135
            }
136
        }
137
138
        call_user_func_array([$class, $method], self::generatePassingData($arguments, $mergeWith));
139
140
        return true;
141
    }
142
143
    /**
144
     * Check method has Route attribute
145
     *
146
     * @param \ReflectionMethod $reflection
147
     * @return bool
148
     */
149
    public static function hasRouteAttribute(\ReflectionMethod $reflection): bool
150
    {
151
        return count($reflection->getAttributes(Route::class)) > 0;
152
    }
153
154
    /**
155
     * Extract route attributes from method
156
     *
157
     * @param \ReflectionMethod $reflection
158
     * @return array|Route[]
159
     */
160
    public static function extractRouteAttributes(\ReflectionMethod $reflection): array
161
    {
162
        $attributes = [];
163
        foreach ($reflection->getAttributes(Route::class) as $attribute) {
164
            $attributes[] = $attribute->newInstance();
165
        }
166
        return $attributes;
167
    }
168
169
}