Passed
Pull Request — master (#10)
by Thalles
02:26
created

Dispatch::reorderRoutes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace CoffeeCode\Router;
4
5
/**
6
 * Class CoffeeCode Dispatch
7
 *
8
 * @author  Robson V. Leite <https://github.com/robsonvleite>
9
 * @package CoffeeCode\Router
10
 */
11
abstract class Dispatch
12
{
13
    use RouterTrait;
14
    
15
    /** @var null|array */
16
    protected $route;
17
    
18
    /** @var bool|string */
19
    protected $projectUrl;
20
    
21
    /** @var string */
22
    protected $separator;
23
    
24
    /** @var null|string */
25
    protected $namespace;
26
    
27
    /** @var null|string */
28
    protected $group;
29
    
30
    /** @var null|array */
31
    protected $data;
32
    
33
    /** @var int */
34
    protected $error;
35
    
36
    /** @const int Bad Request */
37
    public const BAD_REQUEST = 400;
38
    
39
    /** @const int Not Found */
40
    public const NOT_FOUND = 404;
41
    
42
    /** @const int Method Not Allowed */
43
    public const METHOD_NOT_ALLOWED = 405;
44
    
45
    /** @const int Not Implemented */
46
    public const NOT_IMPLEMENTED = 501;
47
    
48
    /**
49
     * Dispatch constructor.
50
     *
51
     * @param string      $projectUrl
52
     * @param null|string $separator
53
     */
54
    public function __construct(string $projectUrl, ?string $separator = ":")
55
    {
56
        $this->projectUrl = (substr($projectUrl, "-1") == "/" ? substr($projectUrl, 0, -1) : $projectUrl);
57
        $this->patch = (filter_input(INPUT_GET, "route", FILTER_DEFAULT) ?? "/");
58
        $this->separator = ($separator ?? ":");
59
        $this->httpMethod = $_SERVER['REQUEST_METHOD'];
60
    }
61
    
62
    /**
63
     * @return array
64
     */
65
    public function __debugInfo()
66
    {
67
        return $this->routes;
68
    }
69
    
70
    /**
71
     * @param null|string $namespace
72
     *
73
     * @return Dispatch
74
     */
75
    public function namespace(?string $namespace): Dispatch
76
    {
77
        $this->namespace = ($namespace ? ucwords($namespace) : null);
78
        return $this;
79
    }
80
    
81
    /**
82
     * @param null|string $group
83
     *
84
     * @return Dispatch
85
     */
86
    public function group(?string $group): Dispatch
87
    {
88
        $this->group = ($group ? str_replace("/", "", $group) : null);
89
        return $this;
90
    }
91
    
92
    /**
93
     * @return null|array
94
     */
95
    public function data(): ?array
96
    {
97
        return $this->data;
98
    }
99
    
100
    /**
101
     * @return null|int
102
     */
103
    public function error(): ?int
104
    {
105
        return $this->error;
106
    }
107
    
108
    /**
109
     * @return bool
110
     */
111
    public function dispatch(): bool
112
    {
113
        if (empty($this->routes) || empty($this->routes[$this->httpMethod])) {
114
            $this->error = self::NOT_IMPLEMENTED;
115
            return false;
116
        }
117
    
118
        $this->reorderRoutes();
119
        
120
        $this->route = null;
121
        foreach ($this->routes[$this->httpMethod] as $key => $route) {
122
            if (preg_match("~^" . $key . "$~", $this->patch, $found)) {
123
                $this->route = $route;
124
            }
125
        }
126
    
127
        return $this->execute();
128
    }
129
    
130
    /**
131
     * @return bool
132
     */
133
    private function execute()
134
    {
135
        if ($this->route) {
136
            if (is_callable($this->route['handler'])) {
137
                call_user_func($this->route['handler'], ($this->route['data'] ?? []));
138
                return true;
139
            }
140
    
141
            $controller = $this->route['handler'];
142
            $method = $this->route['action'];
143
    
144
            if (class_exists($controller)) {
145
                $newController = new $controller($this);
146
                if (method_exists($controller, $method)) {
147
                    $newController->$method(($this->route['data'] ?? []));
148
                    return true;
149
                }
150
        
151
                $this->error = self::METHOD_NOT_ALLOWED;
152
                return false;
153
            }
154
    
155
            $this->error = self::BAD_REQUEST;
156
            return false;
157
        }
158
    
159
        $this->error = self::NOT_FOUND;
160
        return false;
161
    }
162
    
163
    /**
164
     * httpMethod form spoofing
165
     */
166
    protected function formSpoofing(): void
167
    {
168
        $post = filter_input_array(INPUT_POST, FILTER_DEFAULT);
169
    
170
        if (!empty($post['_method']) && in_array($post['_method'], ["PUT", "PATCH", "DELETE"])) {
171
            $this->httpMethod = $post['_method'];
172
            $this->data = $post;
173
        
174
            unset($this->data["_method"]);
175
            return;
176
        }
177
    
178
        if ($this->httpMethod == "POST") {
179
            $this->data = filter_input_array(INPUT_POST, FILTER_DEFAULT);
180
        
181
            unset($this->data["_method"]);
182
            return;
183
        }
184
    
185
        if (in_array($this->httpMethod, ["PUT", "PATCH", "DELETE"]) && !empty($_SERVER['CONTENT_LENGTH'])) {
186
            parse_str(file_get_contents('php://input', false, null, 0, $_SERVER['CONTENT_LENGTH']), $putPatch);
187
            $this->data = $putPatch;
188
        
189
            unset($this->data["_method"]);
190
            return;
191
        }
192
    
193
        $this->data = [];
194
        return;
195
    }
196
    
197
    /**
198
     * Rearrange routess
199
     */
200
    private function reorderRoutes(): void
201
    {
202
        foreach ($this->routes as $key => $value) {
203
            uksort($this->routes[$key], [$this, 'sort']);
204
        }
205
    }
206
    
207
    /**
208
     * @param string $a
209
     * @param string $b
210
     *
211
     * @return int
212
     */
213
    private function sort(string $a, string $b): int
214
    {
215
        return $a <=> $b;
216
    }
217
}