Completed
Push — master ( bb1ab9...4cd17e )
by Sinnarasa
02:25
created

RoutesMatch::setDispatcher()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace JetFire\Routing\Match;
4
5
6
use JetFire\Routing\Router;
7
8
/**
9
 * Class RoutesMatch
10
 * @package JetFire\Routing\Match
11
 */
12
class RoutesMatch 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 = array_merge($dispatcher,$this->dispatcher);
66
    }
67
68
    /**
69
     * @return bool
70
     */
71
    public function match()
72
    {
73
        $this->request = [];
74
        for ($i = 0; $i < $this->router->collection->countRoutes; ++$i) {
75
            $this->request['block'] = $this->router->collection->getRoutes('path_' . $i);
76
            $this->request['prefix'] = ($this->router->collection->getRoutes('prefix_' . $i) != '') ? $this->router->collection->getRoutes('prefix_' . $i) : '';
77
            foreach ($this->router->collection->getRoutes('routes_' . $i) as $route => $dependencies) {
78
                $this->request['path'] = $dependencies;
79
                $this->request['index'] = $i;
80
                $this->request['route'] = preg_replace_callback('#:([\w]+)#', [$this, 'paramMatch'], '/' . trim(trim($this->request['prefix'], '/') . '/' . trim($route, '/'), '/'));
81
                if ($this->routeMatch('#^' . $this->request['route'] . '$#'))
82
                    return $this->generateTarget();
83
            }
84
        }
85
        unset($this->request);
86
        return false;
87
    }
88
89
    /**
90
     * @param $match
91
     * @return string
92
     */
93
    private function paramMatch($match)
94
    {
95
        if (is_array($this->request['path']) && isset($this->request['path']['arguments'][$match[1]])) {
96
            $this->request['path']['arguments'][$match[1]] = str_replace('(', '(?:', $this->request['path']['arguments'][$match[1]]);
97
            return '(' . $this->request['path']['arguments'][$match[1]] . ')';
98
        }
99
        return '([^/]+)';
100
    }
101
102
    /**
103
     * @param $regex
104
     * @return bool
105
     */
106
    private function routeMatch($regex)
107
    {
108
        if (substr($this->request['route'], -1) == '*') {
109
            $pos = strpos($this->request['route'], '*');
110 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...
111
                if (isset($this->request)) return true;
112
        }
113 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...
114
            array_shift($this->request['parameters']);
115
            return true;
116
        }
117
        return false;
118
    }
119
120
    /**
121
     * @return bool
122
     */
123
    private function generateTarget()
124
    {
125
        if(is_callable($this->request['path'])){
126
            $this->router->route->setCallback($this->request['path']);
127
            $this->router->route->setDetail($this->request);
128
            $this->matchClosure();
129
            $this->router->response->setStatusCode(202);
130
        } else {
131
            if (isset($this->request['path']['name'])) $this->router->route->setName($this->request['path']['name']);
132
            if (isset($this->request['path']['method'])) $this->request['path']['method'] = is_array($this->request['path']['method']) ? $this->request['path']['method'] : [$this->request['path']['method']];
133
            if (isset($this->request['path']))
134
                (is_array($this->request['path']) && isset($this->request['path']['use']))
135
                    ? $this->router->route->setCallback($this->request['path']['use'])
136
                    : $this->router->route->setCallback($this->request['path']);
137
            $this->router->route->setDetail($this->request);
138
            if($this->validMethod()) {
139
                foreach($this->matcher as $matcher)
140
                    call_user_func([$this,$matcher]);
141
                $this->router->response->setStatusCode(202);
142
            }else
143
                $this->router->response->setStatusCode(405);
144
        }
145
        return $this->router->route->hasTarget();
146
    }
147
148
    /**
149
     * @return bool
150
     */
151
    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...
152
    {
153
        if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')
154
            return (isset($this->request['path']['ajax']) && $this->request['path']['ajax'] === true) ? true : false;
155
        $method = (isset($this->router->route->getDetail()['path']['method'])) ? $this->router->route->getDetail()['path']['method'] : ['GET'];
156
        return (in_array($this->router->route->getMethod(), $method)) ? true : false;
157
    }
158
159
160
    /**
161
     * @return bool
162
     */
163
    public function matchClosure()
164
    {
165
        if (is_callable($this->router->route->getCallback())) {
166
            $this->router->route->setTarget(['dispatcher' => $this->dispatcher['matchClosure'], 'closure' => $this->router->route->getCallback()]);
167
            return true;
168
        }
169
        return false;
170
    }
171
172
    /**
173
     * @return bool
174
     * @throws \Exception
175
     */
176
    public function matchController()
177
    {
178
        if (!$this->router->route->hasTarget() && strpos($this->router->route->getCallback(), '@') !== false) {
179
            $routes = explode('@', $this->router->route->getCallback());
180
            if (!isset($routes[1])) $routes[1] = 'index';
181
            $index = isset($this->request['index']) ? $this->request['index'] : 0;
182
            $class = (class_exists($routes[0]))
183
                ? $routes[0]
184
                : $this->router->collection->getRoutes()['namespace_'.$index].$routes[0];
185
            if (!class_exists($class))
186
                throw new \Exception('Class "' . $class . '." is not found');
187
            if (method_exists($class, $routes[1])) {
188
                $this->router->route->setTarget([
189
                    'dispatcher' => $this->dispatcher['matchController'],
190
                    'di' => $this->router->getConfig()['di'],
191
                    'controller' => $class,
192
                    'action' => $routes[1]
193
                ]);
194
                return true;
195
            }
196
            throw new \Exception('The required method "' . $routes[1] . '" is not found in "' . $class . '"');
197
        }
198
        return false;
199
    }
200
201
    /**
202
     * @return bool
203
     * @throws \Exception
204
     */
205
    public function matchTemplate()
206
    {
207
        if (!$this->router->route->hasTarget()) {
208
            $path = trim($this->router->route->getCallback(), '/');
209
            $extension = explode('.', $path);
210
            $extension = end($extension);
211
            $index = isset($this->request['index']) ? $this->request['index'] : 0;
212
            $block = $this->router->collection->getRoutes('path_'.$index);
213
            if (in_array('.' . $extension, $this->router->getConfig()['viewExtension'])) {
214
                if (is_file($block . $path)) {
215
                    $target = $block . $path;
216
                    $this->router->route->setTarget([
217
                        'dispatcher' => $this->dispatcher['matchTemplate'],
218
                        'template' => $target,
219
                        'block' => $block,
220
                        'extension' => $extension,
221
                        'callback' => $this->router->getConfig()['viewCallback']
222
                    ]);
223
                    return true;
224
                }
225
                throw new \Exception('Template file "' . $path . '" is not found in "' . $block . '"');
226
            } else {
227
                foreach ($this->router->getConfig()['viewExtension'] as $ext) {
228
                    if (is_file($block . $path . $ext)){
229
                        $target = $block . $path . $ext;
230
                        $this->router->route->setTarget(['dispatcher' => $this->dispatcher['matchTemplate'], 'template' => $target,'block' => $block,  'extension' => str_replace('.', '', $ext),'callback' => $this->router->getConfig()['viewCallback']]);
231
                        return true;
232
                    }
233
                }
234
                throw new \Exception('Template file "' . $path . '" is not found in "' .$block . '"');
235
            }
236
        }
237
        return false;
238
    }
239
}
240