Completed
Push — master ( ee0121...2fa707 )
by Sinnarasa
06:26
created

src/Routing/Matcher/ArrayMatcher.php (1 issue)

Severity

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 JetFire\Routing\Matcher;
4
5
6
use JetFire\Routing\Router;
7
8
/**
9
 * Class RoutesMatch
10
 * @package JetFire\Routing\Match
11
 */
12
class ArrayMatcher implements MatcherInterface
13
{
14
15
    /**
16
     * @var
17
     */
18
    private $router;
19
20
    /**
21
     * @var array
22
     */
23
    private $request = [];
24
25
    /**
26
     * @var array
27
     */
28
    private $resolver = ['isClosureAndTemplate','isControllerAndTemplate','isTemplate'];
29
30
    /**
31
     * @var array
32
     */
33
    private $dispatcher = [
34
        'isClosure' => 'JetFire\Routing\Dispatcher\ClosureDispatcher',
35
        'isController' => 'JetFire\Routing\Dispatcher\ControllerDispatcher',
36
        'isTemplate' => 'JetFire\Routing\Dispatcher\TemplateDispatcher',
37
        'isControllerAndTemplate' => ['JetFire\Routing\Dispatcher\ControllerDispatcher','JetFire\Routing\Dispatcher\TemplateDispatcher'],
38
        'isClosureAndTemplate' => ['JetFire\Routing\Dispatcher\ClosureDispatcher','JetFire\Routing\Dispatcher\TemplateDispatcher'],
39
    ];
40
41
    /**
42
     * @param Router $router
43
     */
44
    public function __construct(Router $router)
45
    {
46
        $this->router = $router;
47
    }
48
49
    /**
50
     * @param array $resolver
51
     */
52
    public function setResolver($resolver = []){
53
        $this->resolver = $resolver;
54
    }
55
56
    /**
57
     * @param string $resolver
58
     */
59
    public function addResolver($resolver){
60
        $this->resolver[] = $resolver;
61
    }
62
63
    /**
64
     * @return array
65
     */
66
    public function getResolver()
67
    {
68
        return $this->resolver;
69
    }
70
71
    /**
72
     * @param array $dispatcher
73
     */
74
    public function setDispatcher($dispatcher = []){
75
        $this->dispatcher = $dispatcher;
76
    }
77
78
    /**
79
     * @param $method
80
     * @param $class
81
     * @return mixed|void
82
     * @internal param array $dispatcher
83
     */
84
    public function addDispatcher($method,$class){
85
        $this->dispatcher[$method] = $class;
86
    }
87
88
    /**
89
     * @return bool
90
     */
91
    public function match()
92
    {
93
        $this->request = [];
94
        for ($i = 0; $i < $this->router->collection->countRoutes; ++$i) {
95
            $this->request['prefix'] = ($this->router->collection->getRoutes('prefix_' . $i) != '') ? $this->router->collection->getRoutes('prefix_' . $i) : '';
96
            $this->request['subdomain'] = ($this->router->collection->getRoutes('subdomain_' . $i) != '') ? $this->router->collection->getRoutes('subdomain_' . $i) : '';
97
            foreach ($this->router->collection->getRoutes('routes_' . $i) as $route => $params) {
98
                $this->request['params'] = $params;
99
                $this->request['collection_index'] = $i;
100
                if($this->checkSubdomain($route)) {
101
                    $route = strstr($route, '/');
102
                    $this->request['route'] = preg_replace_callback('#:([\w]+)#', [$this, 'paramMatch'], '/' . trim(trim($this->request['prefix'], '/') . '/' . trim($route, '/'), '/'));
103
                    if ($this->routeMatch('#^' . $this->request['route'] . '$#')) {
104
                        $this->setCallback();
105
                        return $this->generateTarget();
106
                    }
107
                }
108
            }
109
        }
110
        return false;
111
    }
112
113
    /**
114
     * @param $route
115
     * @return bool
116
     */
117
    private function checkSubdomain($route){
0 ignored issues
show
checkSubdomain uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
118
        $url = (isset($_SERVER['REQUEST_SCHEME']) ? $_SERVER['REQUEST_SCHEME'] : 'http') . '://' . ($host = (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME']));
119
        $domain = $this->router->collection->getDomain($url);
120
        if(!empty($this->request['subdomain']) && $route[0] == '/') $route = trim($this->request['subdomain'],'.').'.'.$domain.$route;
121
        if($route[0] == '/'){
122
            return ($host != $domain) ? false : true;
123
        }elseif($route[0] != '/' && $host != $domain){
124
            $route = substr($route, 0, strpos($route, "/"));
125
            $route = str_replace('{host}', $domain, $route);
126
            $route = preg_replace_callback('#{subdomain}#', [$this, 'subdomainMatch'], $route);
127
            if (preg_match('#^' . $route . '$#', $host, $this->request['called_subdomain'])) {
128
                $this->request['called_subdomain'] = array_shift($this->request['called_subdomain']);
129
                return true;
130
            }
131
        }
132
        return false;
133
    }
134
135
    /**
136
     * @return string
137
     */
138
    private function subdomainMatch()
139
    {
140
        if (is_array($this->request['params']) && isset($this->request['params']['subdomain'])) {
141
            $this->request['params']['subdomain'] = str_replace('(', '(?:', $this->request['params']['subdomain']);
142
            return '(' . $this->request['params']['subdomain'] . ')';
143
        }
144
        return '([^/]+)';
145
    }
146
147
    /**
148
     * @param $match
149
     * @return string
150
     */
151
    private function paramMatch($match)
152
    {
153
        if (is_array($this->request['params']) && isset($this->request['params']['arguments'][$match[1]])) {
154
            $this->request['params']['arguments'][$match[1]] = str_replace('(', '(?:', $this->request['params']['arguments'][$match[1]]);
155
            return '(' . $this->request['params']['arguments'][$match[1]] . ')';
156
        }
157
        return '([^/]+)';
158
    }
159
160
    /**
161
     * @param $regex
162
     * @return bool
163
     */
164
    private function routeMatch($regex)
165
    {
166
        if (substr($this->request['route'], -1) == '*') {
167
            $pos = strpos($this->request['route'], '*');
168 View Code Duplication
            if (substr($this->router->route->getUrl(), 0, $pos) == substr($this->request['route'], 0, $pos) && isset($this->request['params']))
169
                return true;
170
        }
171 View Code Duplication
        if (preg_match($regex, $this->router->route->getUrl(), $this->request['parameters'])) {
172
            array_shift($this->request['parameters']);
173
            return true;
174
        }
175
        return false;
176
    }
177
178
    /**
179
     * @return bool
180
     */
181
    private function generateTarget()
182
    {
183
        if($this->validMethod())
184 View Code Duplication
            foreach($this->resolver as $resolver)
185
                if (is_array($target = call_user_func_array([$this,$resolver],[$this->router->route->getCallback()]))) {
186
                    $this->setTarget($target);
187
                    $this->router->response->setStatusCode(202);
188
                    return true;
189
                }
190
        $this->router->response->setStatusCode(405);
191
        return false;
192
    }
193
194
    /**
195
     * @param array $target
196
     */
197 View Code Duplication
    public function setTarget($target = []){
198
        $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
199
        $this->router->route->setDetail($this->request);
200
        $this->router->route->setTarget($target);
201
        $this->router->route->addTarget('block', $this->router->collection->getRoutes('block_'.$index));
202
        $this->router->route->addTarget('view_dir', $this->router->collection->getRoutes('view_dir_'.$index));
203
    }
204
205
    /**
206
     *
207
     */
208
    private function setCallback(){
209
        if (isset($this->request['params'])) {
210
            if(is_callable($this->request['params']))
211
                $this->router->route->setCallback($this->request['params']);
212
            else {
213
                (is_array($this->request['params']) && isset($this->request['params']['use']))
214
                    ? $this->router->route->setCallback($this->request['params']['use'])
215
                    : $this->router->route->setCallback($this->request['params']);
216
                if (isset($this->request['params']['name'])) $this->router->route->setName($this->request['params']['name']);
217
                if (isset($this->request['params']['method'])) $this->request['params']['method'] = is_array($this->request['params']['method']) ? $this->request['params']['method'] : [$this->request['params']['method']];
218
            }
219
        }
220
    }
221
222
    /**
223
     * @return bool
224
     */
225
    public function validMethod()
226
    {
227
        if(is_callable($this->request['params']))return true;
228
        if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')
229
            return (isset($this->request['params']['ajax']) && $this->request['params']['ajax'] === true) ? true : false;
230
        $method = (isset($this->request['params']['method'])) ? $this->request['params']['method'] : ['GET'];
231
        return (in_array($this->router->route->getMethod(), $method)) ? true : false;
232
    }
233
234
    /**
235
     * @param $callback
236
     * @return array|bool
237
     * @throws \Exception
238
     */
239 View Code Duplication
    public function isClosureAndTemplate($callback){
240
        if(is_array($cls = $this->isClosure($callback))) {
241
            if (is_array($this->request['params']) && isset($this->request['params']['template']) && is_array($tpl = $this->isTemplate($this->request['params']['template']))) {
242
                return array_merge(array_merge($cls, $tpl),[
243
                    'dispatcher' => $this->dispatcher['isClosureAndTemplate']
244
                ]);
245
            }
246
            return $cls;
247
        }
248
        return false;
249
    }
250
251
    /**
252
     * @param $callback
253
     * @return array|bool
254
     * @throws \Exception
255
     */
256 View Code Duplication
    public function isControllerAndTemplate($callback){
257
        if(is_array($ctrl = $this->isController($callback))) {
258
            if (is_array($this->request['params']) && isset($this->request['params']['template']) && is_array($tpl = $this->isTemplate($this->request['params']['template']))) {
259
                return array_merge(array_merge($ctrl, $tpl),[
260
                    'dispatcher' => $this->dispatcher['isControllerAndTemplate']
261
                ]);
262
            }
263
            return $ctrl;
264
        }
265
        return false;
266
    }
267
268
269
    /**
270
     * @param $callback
271
     * @return bool|array
272
     */
273
    public function isClosure($callback)
274
    {
275
        if (is_callable($callback)) {
276
            return [
277
                'dispatcher' => $this->dispatcher['isClosure'],
278
                'closure' => $callback
279
            ];
280
        }
281
        return false;
282
    }
283
284
    /**
285
     * @param $callback
286
     * @throws \Exception
287
     * @return bool|array
288
     */
289
    public function isController($callback)
290
    {
291
        if (is_string($callback) && strpos($callback, '@') !== false) {
292
            $routes = explode('@', $callback);
293
            if (!isset($routes[1])) $routes[1] = 'index';
294
            $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
295
            $class = (class_exists($routes[0]))
296
                ? $routes[0]
297
                : $this->router->collection->getRoutes()['ctrl_namespace_'.$index].$routes[0];
298
            if (!class_exists($class))
299
                throw new \Exception('Class "' . $class . '." is not found');
300
            if (method_exists($class, $routes[1])) {
301
                return [
302
                    'dispatcher' => $this->dispatcher['isController'],
303
                    'di' => $this->router->getConfig()['di'],
304
                    'controller' => $class,
305
                    'action' => $routes[1]
306
                ];
307
            }
308
            throw new \Exception('The required method "' . $routes[1] . '" is not found in "' . $class . '"');
309
        }
310
        return false;
311
    }
312
313
    /**
314
     * @param $callback
315
     * @throws \Exception
316
     * @return bool|array
317
     */
318
    public function isTemplate($callback)
319
    {
320
        if(is_string($callback)) {
321
            $path = trim($callback, '/');
322
            $extension = substr(strrchr($path, "."), 1);
323
            $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
324
            $viewDir = $this->router->collection->getRoutes('view_dir_' . $index);
325
            $target = null;
326
            if (in_array('.' . $extension, $this->router->getConfig()['templateExtension']) && (is_file($fullPath = $viewDir . $path) || is_file($fullPath = $path)))
327
                $target = $fullPath;
328
            else {
329
                foreach ($this->router->getConfig()['templateExtension'] as $ext) {
330
                    if (is_file($fullPath = $viewDir . $path . $ext) || is_file($fullPath = $path . $ext)) {
331
                        $target = $fullPath;
332
                        $extension = substr(strrchr($ext, "."), 1);
333
                        break;
334
                    }
335
                }
336
            }
337
            if(is_null($target))
338
                throw new \Exception('Template file "' . $path . '" is not found in "' . $viewDir . '"');
339
            return [
340
                'dispatcher' => $this->dispatcher['isTemplate'],
341
                'template'   => $target,
342
                'extension'  => $extension,
343
                'callback'   => $this->router->getConfig()['templateCallback']
344
            ];
345
        }
346
        return false;
347
    }
348
349
}
350