Completed
Push — master ( 46d40e...8f6f9d )
by Sinnarasa
02:37
created

ArrayMatcher::addDispatcher()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 2
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
     * @var array
21
     */
22
    private $request = [];
23
24
    /**
25
     * @var array
26
     */
27
    private $matcher = ['matchClosure','matchController','matchTemplate'];
28
29
    /**
30
     * @var array
31
     */
32
    private $dispatcher = [
33
        'matchClosure' => 'JetFire\Routing\Dispatcher\ClosureDispatcher',
34
        'matchController' => 'JetFire\Routing\Dispatcher\ControllerDispatcher',
35
        'matchTemplate' => 'JetFire\Routing\Dispatcher\TemplateDispatcher',
36
    ];
37
38
    /**
39
     * @param Router $router
40
     */
41
    public function __construct(Router $router)
42
    {
43
        $this->router = $router;
44
    }
45
46
    /**
47
     * @param string $matcher
48
     */
49
    public function addMatcher($matcher){
50
        $this->matcher[] = $matcher;
51
    }
52
53
    /**
54
     * @return array
55
     */
56
    public function getMatcher()
57
    {
58
        return $this->matcher;
59
    }
60
61
    /**
62
     * @param array $dispatcher
63
     */
64
    public function setDispatcher($dispatcher = []){
65
        $this->dispatcher = $dispatcher;
66
    }
67
68
    /**
69
     * @param $method
70
     * @param $class
71
     * @return mixed|void
72
     * @internal param array $dispatcher
73
     */
74
    public function addDispatcher($method,$class){
75
        $this->dispatcher[$method] = $class;
76
    }
77
78
    /**
79
     * @return bool
80
     */
81
    public function match()
82
    {
83
        $this->request = [];
84
        for ($i = 0; $i < $this->router->collection->countRoutes; ++$i) {
85
            $this->request['prefix'] = ($this->router->collection->getRoutes('prefix_' . $i) != '') ? $this->router->collection->getRoutes('prefix_' . $i) : '';
86
            foreach ($this->router->collection->getRoutes('routes_' . $i) as $route => $params) {
87
                $this->request['params'] = $params;
88
                $this->request['collection_index'] = $i;
89
                $this->request['route'] = preg_replace_callback('#:([\w]+)#', [$this, 'paramMatch'], '/' . trim(trim($this->request['prefix'], '/') . '/' . trim($route, '/'), '/'));
90
                if ($this->routeMatch('#^' . $this->request['route'] . '$#')) {
91
                    $this->setRoute();
92
                    return $this->generateTarget();
93
                }
94
            }
95
        }
96
        unset($this->request);
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))
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
                if (isset($this->request)) 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) if(call_user_func([$this,$match])) break;
138
            $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
139
            $this->router->route->addTarget('block',$this->router->collection->getRoutes('block_'.$index));
140
            $this->router->route->addTarget('view_dir',$this->router->collection->getRoutes('view_dir_'.$index));
141
            $this->router->response->setStatusCode(202);
142
        }else
143
            $this->router->response->setStatusCode(405);
144
        return $this->router->route->hasTarget();
145
    }
146
147
    /**
148
     *
149
     */
150
    private function setRoute(){
151
        if (isset($this->request['params'])) {
152
            if(is_callable($this->request['params']))
153
                $this->router->route->setCallback($this->request['params']);
154
            else {
155
                (is_array($this->request['params']) && isset($this->request['params']['use']))
156
                    ? $this->router->route->setCallback($this->request['params']['use'])
157
                    : $this->router->route->setCallback($this->request['params']);
158
                if (isset($this->request['params']['name'])) $this->router->route->setName($this->request['params']['name']);
159
                if (isset($this->request['params']['method'])) $this->request['params']['method'] = is_array($this->request['params']['method']) ? $this->request['params']['method'] : [$this->request['params']['method']];
160
            }
161
        }
162
        $this->router->route->setDetail($this->request);
163
    }
164
165
    /**
166
     * @return bool
167
     */
168
    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...
169
    {
170
        if(is_callable($this->request['params']))return true;
171
        if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')
172
            return (isset($this->request['params']['ajax']) && $this->request['params']['ajax'] === true) ? true : false;
173
        $method = (isset($this->router->route->getDetail()['params']['method'])) ? $this->router->route->getDetail()['params']['method'] : ['GET'];
174
        return (in_array($this->router->route->getMethod(), $method)) ? true : false;
175
    }
176
177
178
    /**
179
     * @return bool
180
     */
181
    public function matchClosure()
182
    {
183
        if (is_callable($this->router->route->getCallback())) {
184
            $this->router->route->setTarget([
185
                'dispatcher' => $this->dispatcher['matchClosure'],
186
                'closure' => $this->router->route->getCallback()
187
            ]);
188
            return true;
189
        }
190
        return false;
191
    }
192
193
    /**
194
     * @return bool
195
     * @throws \Exception
196
     */
197
    public function matchController()
198
    {
199
        if (strpos($this->router->route->getCallback(), '@') !== false) {
200
            $routes = explode('@', $this->router->route->getCallback());
201
            if (!isset($routes[1])) $routes[1] = 'index';
202
            $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
203
            $class = (class_exists($routes[0]))
204
                ? $routes[0]
205
                : $this->router->collection->getRoutes()['ctrl_namespace_'.$index].$routes[0];
206
            if (!class_exists($class))
207
                throw new \Exception('Class "' . $class . '." is not found');
208
            if (method_exists($class, $routes[1])) {
209
                $this->router->route->setTarget([
210
                    'dispatcher' => $this->dispatcher['matchController'],
211
                    'di' => $this->router->getConfig()['di'],
212
                    'controller' => $class,
213
                    'action' => $routes[1]
214
                ]);
215
                return true;
216
            }
217
            throw new \Exception('The required method "' . $routes[1] . '" is not found in "' . $class . '"');
218
        }
219
        return false;
220
    }
221
222
    /**
223
     * @return bool
224
     * @throws \Exception
225
     */
226
    public function matchTemplate()
227
    {
228
        if(is_string($this->router->route->getCallback())) {
229
            $path = trim($this->router->route->getCallback(), '/');
230
            $extension = substr(strrchr($path, "."), 1);
231
            $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
232
            $viewDir = $this->router->collection->getRoutes('view_dir_' . $index);
233
            $target = null;
234
            if (in_array('.' . $extension, $this->router->getConfig()['templateExtension']) && is_file($viewDir . $path))
235
                $target = $viewDir . $path;
236
            else {
237
                foreach ($this->router->getConfig()['templateExtension'] as $ext) {
238
                    if (is_file($viewDir . $path . $ext)) {
239
                        $target = $viewDir . $path . $ext;
240
                        $extension = substr(strrchr($ext, "."), 1);
241
                        break;
242
                    }
243
                }
244
            }
245
            if(is_null($target))
246
                throw new \Exception('Template file "' . $path . '" is not found in "' . $viewDir . '"');
247
            $this->router->route->setTarget([
248
                'dispatcher' => $this->dispatcher['matchTemplate'],
249
                'template'   => $target,
250
                'extension'  => $extension,
251
                'callback'   => $this->router->getConfig()['templateCallback']
252
            ]);
253
            return true;
254
        }
255
        return false;
256
    }
257
}
258