Completed
Push — master ( 6202fd...f9a3f2 )
by Sinnarasa
02:36
created

ArrayMatcher::matchControllerTemplate()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 11
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 11
loc 11
rs 8.8571
cc 5
eloc 7
nc 3
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 $matcher = ['matchClosureTemplate','matchControllerTemplate','matchTemplate'];
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 string $matcher
49
     */
50
    public function addMatcher($matcher){
51
        $this->matcher[] = $matcher;
52
    }
53
54
    /**
55
     * @return array
56
     */
57
    public function getMatcher()
58
    {
59
        return $this->matcher;
60
    }
61
62
    /**
63
     * @param array $dispatcher
64
     */
65
    public function setDispatcher($dispatcher = []){
66
        $this->dispatcher = $dispatcher;
67
    }
68
69
    /**
70
     * @param $method
71
     * @param $class
72
     * @return mixed|void
73
     * @internal param array $dispatcher
74
     */
75
    public function addDispatcher($method,$class){
76
        $this->dispatcher[$method] = $class;
77
    }
78
79
    /**
80
     * @return bool
81
     */
82
    public function match()
83
    {
84
        $this->request = [];
85
        for ($i = 0; $i < $this->router->collection->countRoutes; ++$i) {
86
            $this->request['prefix'] = ($this->router->collection->getRoutes('prefix_' . $i) != '') ? $this->router->collection->getRoutes('prefix_' . $i) : '';
87
            foreach ($this->router->collection->getRoutes('routes_' . $i) as $route => $params) {
88
                $this->request['params'] = $params;
89
                $this->request['collection_index'] = $i;
90
                $this->request['route'] = preg_replace_callback('#:([\w]+)#', [$this, 'paramMatch'], '/' . trim(trim($this->request['prefix'], '/') . '/' . trim($route, '/'), '/'));
91
                if ($this->routeMatch('#^' . $this->request['route'] . '$#')) {
92
                    $this->setCallback();
93
                    return $this->generateTarget();
94
                }
95
            }
96
        }
97
        return false;
98
    }
99
100
    /**
101
     * @param $match
102
     * @return string
103
     */
104
    private function paramMatch($match)
105
    {
106
        if (is_array($this->request['params']) && isset($this->request['params']['arguments'][$match[1]])) {
107
            $this->request['params']['arguments'][$match[1]] = str_replace('(', '(?:', $this->request['params']['arguments'][$match[1]]);
108
            return '(' . $this->request['params']['arguments'][$match[1]] . ')';
109
        }
110
        return '([^/]+)';
111
    }
112
113
    /**
114
     * @param $regex
115
     * @return bool
116
     */
117
    private function routeMatch($regex)
118
    {
119
        if (substr($this->request['route'], -1) == '*') {
120
            $pos = strpos($this->request['route'], '*');
121 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...
122
                return true;
123
        }
124 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...
125
            array_shift($this->request['parameters']);
126
            return true;
127
        }
128
        return false;
129
    }
130
131
    /**
132
     * @return bool
133
     */
134
    private function generateTarget()
135
    {
136
        if($this->validMethod())
137
            foreach($this->matcher as $match)
138
                if (is_array($target = call_user_func_array([$this,$match],[$this->router->route->getCallback()]))) {
139
                    $this->setTarget($target);
140
                    $this->router->response->setStatusCode(202);
141
                    return true;
142
                }
143
        $this->router->response->setStatusCode(405);
144
        return false;
145
    }
146
147
    /**
148
     * @param array $target
149
     */
150 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...
151
        $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
152
        $this->router->route->setDetail($this->request);
153
        $this->router->route->setTarget($target);
154
        $this->router->route->addTarget('block', $this->router->collection->getRoutes('block_'.$index));
155
        $this->router->route->addTarget('view_dir', $this->router->collection->getRoutes('view_dir_'.$index));
156
    }
157
158
    /**
159
     *
160
     */
161
    private function setCallback(){
162
        if (isset($this->request['params'])) {
163
            if(is_callable($this->request['params']))
164
                $this->router->route->setCallback($this->request['params']);
165
            else {
166
                (is_array($this->request['params']) && isset($this->request['params']['use']))
167
                    ? $this->router->route->setCallback($this->request['params']['use'])
168
                    : $this->router->route->setCallback($this->request['params']);
169
                if (isset($this->request['params']['name'])) $this->router->route->setName($this->request['params']['name']);
170
                if (isset($this->request['params']['method'])) $this->request['params']['method'] = is_array($this->request['params']['method']) ? $this->request['params']['method'] : [$this->request['params']['method']];
171
            }
172
        }
173
    }
174
175
    /**
176
     * @return bool
177
     */
178
    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...
179
    {
180
        if(is_callable($this->request['params']))return true;
181
        if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')
182
            return (isset($this->request['params']['ajax']) && $this->request['params']['ajax'] === true) ? true : false;
183
        $method = (isset($this->request['params']['method'])) ? $this->request['params']['method'] : ['GET'];
184
        return (in_array($this->router->route->getMethod(), $method)) ? true : false;
185
    }
186
187
    /**
188
     * @param $callback
189
     * @return array|bool
190
     * @throws \Exception
191
     */
192 View Code Duplication
    public function matchClosureTemplate($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...
193
        if(is_array($cls = $this->matchClosure($callback))) {
194
            if (is_array($this->request['params']) && isset($this->request['params']['template']) && is_array($tpl = $this->matchTemplate($this->request['params']['template']))) {
195
                return array_merge(array_merge($cls, $tpl),[
196
                    'dispatcher' => [$this->dispatcher['matchClosure'], $this->dispatcher['matchTemplate']]
197
                ]);
198
            }
199
            return $cls;
200
        }
201
        return false;
202
    }
203
204
    /**
205
     * @param $callback
206
     * @return array|bool
207
     * @throws \Exception
208
     */
209 View Code Duplication
    public function matchControllerTemplate($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...
210
        if(is_array($ctrl = $this->matchController($callback))) {
211
            if (is_array($this->request['params']) && isset($this->request['params']['template']) && is_array($tpl = $this->matchTemplate($this->request['params']['template']))) {
212
                return array_merge(array_merge($ctrl, $tpl),[
213
                    'dispatcher' => [$this->dispatcher['matchController'], $this->dispatcher['matchTemplate']]
214
                ]);
215
            }
216
            return $ctrl;
217
        }
218
        return false;
219
    }
220
221
222
    /**
223
     * @param $callback
224
     * @return bool|array
225
     */
226
    public function matchClosure($callback)
227
    {
228
        if (is_callable($callback)) {
229
            return [
230
                'dispatcher' => $this->dispatcher['matchClosure'],
231
                'closure' => $callback
232
            ];
233
        }
234
        return false;
235
    }
236
237
    /**
238
     * @param $callback
239
     * @throws \Exception
240
     * @return bool|array
241
     */
242
    public function matchController($callback)
243
    {
244
        if (is_string($callback) && strpos($callback, '@') !== false) {
245
            $routes = explode('@', $callback);
246
            if (!isset($routes[1])) $routes[1] = 'index';
247
            $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
248
            $class = (class_exists($routes[0]))
249
                ? $routes[0]
250
                : $this->router->collection->getRoutes()['ctrl_namespace_'.$index].$routes[0];
251
            if (!class_exists($class))
252
                throw new \Exception('Class "' . $class . '." is not found');
253
            if (method_exists($class, $routes[1])) {
254
                return [
255
                    'dispatcher' => $this->dispatcher['matchController'],
256
                    'di' => $this->router->getConfig()['di'],
257
                    'controller' => $class,
258
                    'action' => $routes[1]
259
                ];
260
            }
261
            throw new \Exception('The required method "' . $routes[1] . '" is not found in "' . $class . '"');
262
        }
263
        return false;
264
    }
265
266
    /**
267
     * @param $callback
268
     * @throws \Exception
269
     * @return bool|array
270
     */
271
    public function matchTemplate($callback)
272
    {
273
        if(is_string($callback)) {
274
            $path = trim($callback, '/');
275
            $extension = substr(strrchr($path, "."), 1);
276
            $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
277
            $viewDir = $this->router->collection->getRoutes('view_dir_' . $index);
278
            $target = null;
279
            if (in_array('.' . $extension, $this->router->getConfig()['templateExtension']) && (is_file($fullPath = $viewDir . $path) || is_file($fullPath = $path)))
280
                $target = $fullPath;
281
            else {
282
                foreach ($this->router->getConfig()['templateExtension'] as $ext) {
283
                    if (is_file($fullPath = $viewDir . $path . $ext) || is_file($fullPath = $path . $ext)) {
284
                        $target = $fullPath;
285
                        $extension = substr(strrchr($ext, "."), 1);
286
                        break;
287
                    }
288
                }
289
            }
290
            if(is_null($target))
291
                throw new \Exception('Template file "' . $path . '" is not found in "' . $viewDir . '"');
292
            return [
293
                'dispatcher' => $this->dispatcher['matchTemplate'],
294
                'template'   => $target,
295
                'extension'  => $extension,
296
                'callback'   => $this->router->getConfig()['templateCallback']
297
            ];
298
        }
299
        return false;
300
    }
301
302
}
303