Completed
Push — master ( f9a3f2...2eb481 )
by Sinnarasa
02:45
created

ArrayMatcher::isController()   C

Complexity

Conditions 8
Paths 25

Size

Total Lines 23
Code Lines 18

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 23
rs 6.1403
cc 8
eloc 18
nc 25
nop 1
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
        'matchClosure' => 'JetFire\Routing\Dispatcher\ClosureDispatcher',
35
        'matchController' => 'JetFire\Routing\Dispatcher\ControllerDispatcher',
36
        'matchTemplate' => 'JetFire\Routing\Dispatcher\TemplateDispatcher',
37
    ];
38
39
    /**
40
     * @param Router $router
41
     */
42
    public function __construct(Router $router)
43
    {
44
        $this->router = $router;
45
    }
46
47
    /**
48
     * @param array $resolver
49
     */
50
    public function setResolver($resolver = []){
51
        $this->resolver = $resolver;
52
    }
53
54
    /**
55
     * @param string $resolver
56
     */
57
    public function addResolver($resolver){
58
        $this->resolver[] = $resolver;
59
    }
60
61
    /**
62
     * @return array
63
     */
64
    public function getResolver()
65
    {
66
        return $this->resolver;
67
    }
68
69
    /**
70
     * @param array $dispatcher
71
     */
72
    public function setDispatcher($dispatcher = []){
73
        $this->dispatcher = $dispatcher;
74
    }
75
76
    /**
77
     * @param $method
78
     * @param $class
79
     * @return mixed|void
80
     * @internal param array $dispatcher
81
     */
82
    public function addDispatcher($method,$class){
83
        $this->dispatcher[$method] = $class;
84
    }
85
86
    /**
87
     * @return bool
88
     */
89
    public function match()
90
    {
91
        $this->request = [];
92
        for ($i = 0; $i < $this->router->collection->countRoutes; ++$i) {
93
            $this->request['prefix'] = ($this->router->collection->getRoutes('prefix_' . $i) != '') ? $this->router->collection->getRoutes('prefix_' . $i) : '';
94
            foreach ($this->router->collection->getRoutes('routes_' . $i) as $route => $params) {
95
                $this->request['params'] = $params;
96
                $this->request['collection_index'] = $i;
97
                $this->request['route'] = preg_replace_callback('#:([\w]+)#', [$this, 'paramMatch'], '/' . trim(trim($this->request['prefix'], '/') . '/' . trim($route, '/'), '/'));
98
                if ($this->routeMatch('#^' . $this->request['route'] . '$#')) {
99
                    $this->setCallback();
100
                    return $this->generateTarget();
101
                }
102
            }
103
        }
104
        return false;
105
    }
106
107
    /**
108
     * @param $match
109
     * @return string
110
     */
111
    private function paramMatch($match)
112
    {
113
        if (is_array($this->request['params']) && isset($this->request['params']['arguments'][$match[1]])) {
114
            $this->request['params']['arguments'][$match[1]] = str_replace('(', '(?:', $this->request['params']['arguments'][$match[1]]);
115
            return '(' . $this->request['params']['arguments'][$match[1]] . ')';
116
        }
117
        return '([^/]+)';
118
    }
119
120
    /**
121
     * @param $regex
122
     * @return bool
123
     */
124
    private function routeMatch($regex)
125
    {
126
        if (substr($this->request['route'], -1) == '*') {
127
            $pos = strpos($this->request['route'], '*');
128 View Code Duplication
            if (substr($this->router->route->getUrl(), 0, $pos) == substr($this->request['route'], 0, $pos) && isset($this->request['params']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
129
                return true;
130
        }
131 View Code Duplication
        if (preg_match($regex, $this->router->route->getUrl(), $this->request['parameters'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
132
            array_shift($this->request['parameters']);
133
            return true;
134
        }
135
        return false;
136
    }
137
138
    /**
139
     * @return bool
140
     */
141
    private function generateTarget()
142
    {
143
        if($this->validMethod())
144 View Code Duplication
            foreach($this->resolver as $resolver)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
145
                if (is_array($target = call_user_func_array([$this,$resolver],[$this->router->route->getCallback()]))) {
146
                    $this->setTarget($target);
147
                    $this->router->response->setStatusCode(202);
148
                    return true;
149
                }
150
        $this->router->response->setStatusCode(405);
151
        return false;
152
    }
153
154
    /**
155
     * @param array $target
156
     */
157 View Code Duplication
    public function setTarget($target = []){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
158
        $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
159
        $this->router->route->setDetail($this->request);
160
        $this->router->route->setTarget($target);
161
        $this->router->route->addTarget('block', $this->router->collection->getRoutes('block_'.$index));
162
        $this->router->route->addTarget('view_dir', $this->router->collection->getRoutes('view_dir_'.$index));
163
    }
164
165
    /**
166
     *
167
     */
168
    private function setCallback(){
169
        if (isset($this->request['params'])) {
170
            if(is_callable($this->request['params']))
171
                $this->router->route->setCallback($this->request['params']);
172
            else {
173
                (is_array($this->request['params']) && isset($this->request['params']['use']))
174
                    ? $this->router->route->setCallback($this->request['params']['use'])
175
                    : $this->router->route->setCallback($this->request['params']);
176
                if (isset($this->request['params']['name'])) $this->router->route->setName($this->request['params']['name']);
177
                if (isset($this->request['params']['method'])) $this->request['params']['method'] = is_array($this->request['params']['method']) ? $this->request['params']['method'] : [$this->request['params']['method']];
178
            }
179
        }
180
    }
181
182
    /**
183
     * @return bool
184
     */
185
    public function validMethod()
0 ignored issues
show
Coding Style introduced by
validMethod 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...
186
    {
187
        if(is_callable($this->request['params']))return true;
188
        if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')
189
            return (isset($this->request['params']['ajax']) && $this->request['params']['ajax'] === true) ? true : false;
190
        $method = (isset($this->request['params']['method'])) ? $this->request['params']['method'] : ['GET'];
191
        return (in_array($this->router->route->getMethod(), $method)) ? true : false;
192
    }
193
194
    /**
195
     * @param $callback
196
     * @return array|bool
197
     * @throws \Exception
198
     */
199 View Code Duplication
    public function isClosureAndTemplate($callback){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
200
        if(is_array($cls = $this->isClosure($callback))) {
201
            if (is_array($this->request['params']) && isset($this->request['params']['template']) && is_array($tpl = $this->isTemplate($this->request['params']['template']))) {
202
                return array_merge(array_merge($cls, $tpl),[
203
                    'dispatcher' => [$this->dispatcher['matchClosure'], $this->dispatcher['matchTemplate']]
204
                ]);
205
            }
206
            return $cls;
207
        }
208
        return false;
209
    }
210
211
    /**
212
     * @param $callback
213
     * @return array|bool
214
     * @throws \Exception
215
     */
216 View Code Duplication
    public function isControllerAndTemplate($callback){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
217
        if(is_array($ctrl = $this->isController($callback))) {
218
            if (is_array($this->request['params']) && isset($this->request['params']['template']) && is_array($tpl = $this->isTemplate($this->request['params']['template']))) {
219
                return array_merge(array_merge($ctrl, $tpl),[
220
                    'dispatcher' => [$this->dispatcher['matchController'], $this->dispatcher['matchTemplate']]
221
                ]);
222
            }
223
            return $ctrl;
224
        }
225
        return false;
226
    }
227
228
229
    /**
230
     * @param $callback
231
     * @return bool|array
232
     */
233
    public function isClosure($callback)
234
    {
235
        if (is_callable($callback)) {
236
            return [
237
                'dispatcher' => $this->dispatcher['matchClosure'],
238
                'closure' => $callback
239
            ];
240
        }
241
        return false;
242
    }
243
244
    /**
245
     * @param $callback
246
     * @throws \Exception
247
     * @return bool|array
248
     */
249
    public function isController($callback)
250
    {
251
        if (is_string($callback) && strpos($callback, '@') !== false) {
252
            $routes = explode('@', $callback);
253
            if (!isset($routes[1])) $routes[1] = 'index';
254
            $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
255
            $class = (class_exists($routes[0]))
256
                ? $routes[0]
257
                : $this->router->collection->getRoutes()['ctrl_namespace_'.$index].$routes[0];
258
            if (!class_exists($class))
259
                throw new \Exception('Class "' . $class . '." is not found');
260
            if (method_exists($class, $routes[1])) {
261
                return [
262
                    'dispatcher' => $this->dispatcher['matchController'],
263
                    'di' => $this->router->getConfig()['di'],
264
                    'controller' => $class,
265
                    'action' => $routes[1]
266
                ];
267
            }
268
            throw new \Exception('The required method "' . $routes[1] . '" is not found in "' . $class . '"');
269
        }
270
        return false;
271
    }
272
273
    /**
274
     * @param $callback
275
     * @throws \Exception
276
     * @return bool|array
277
     */
278
    public function isTemplate($callback)
279
    {
280
        if(is_string($callback)) {
281
            $path = trim($callback, '/');
282
            $extension = substr(strrchr($path, "."), 1);
283
            $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
284
            $viewDir = $this->router->collection->getRoutes('view_dir_' . $index);
285
            $target = null;
286
            if (in_array('.' . $extension, $this->router->getConfig()['templateExtension']) && (is_file($fullPath = $viewDir . $path) || is_file($fullPath = $path)))
287
                $target = $fullPath;
288
            else {
289
                foreach ($this->router->getConfig()['templateExtension'] as $ext) {
290
                    if (is_file($fullPath = $viewDir . $path . $ext) || is_file($fullPath = $path . $ext)) {
291
                        $target = $fullPath;
292
                        $extension = substr(strrchr($ext, "."), 1);
293
                        break;
294
                    }
295
                }
296
            }
297
            if(is_null($target))
298
                throw new \Exception('Template file "' . $path . '" is not found in "' . $viewDir . '"');
299
            return [
300
                'dispatcher' => $this->dispatcher['matchTemplate'],
301
                'template'   => $target,
302
                'extension'  => $extension,
303
                'callback'   => $this->router->getConfig()['templateCallback']
304
            ];
305
        }
306
        return false;
307
    }
308
309
}
310