Issues (2)

src/Router.php (1 issue)

1
<?php
2
/**
3
 * Class for the router system
4
 * @author Vermeulen Maxime <[email protected]>
5
 * @version 2.0
6
 */
7
8
namespace BfwFastRoute;
9
10
use \Exception;
11
use \FastRoute;
12
13
/**
14
 * Permet de gérer la vue et de savoir vers quel page envoyer
15
 * @package bfw-fastroute
16
 */
17
class Router implements \SplObserver
18
{
19
    /**
20
     * @const ERR_TARGET_NOT_DECLARED : Error code if the target has not
21
     * been declared
22
     */
23
    const ERR_TARGET_NOT_DECLARED = 2001001;
24
    
25
    /**
26
     * @var \BFW\Module $module The bfw module instance for this module
27
     */
28
    protected $module;
29
    
30
    /**
31
     * @var \BFW\Config $config The bfw config instance for this module
32
     */
33
    protected $config;
34
    
35
    /**
36
     * @var object|null $ctrlRouterInfos The context object passed to
37
     * subject for the action "searchRoute".
38
     */
39
    protected $ctrlRouterInfos;
40
    
41
    /**
42
     * @var \FastRoute\Dispatcher $dispatcher FastRoute dispatcher
43
     */
44
    protected $dispatcher;
45
    
46
    /**
47
     * Constructor
48
     * Get config and linker instance
49
     * Call fastRoute dispatcher
50
     * 
51
     * @param \BFW\Module $module
52
     */
53
    public function __construct(\BFW\Module $module)
54
    {
55
        $this->module = $module;
56
        $this->config = $module->getConfig();
57
        
58
        $this->dispatcher = FastRoute\simpleDispatcher([
59
            $this,
60
            'addRoutesToCollector'
61
        ]);
62
    }
63
    
64
    /**
65
     * Getter accessor for module property
66
     * 
67
     * @return \BFW\Module
68
     */
69
    public function getModule(): \BFW\Module
70
    {
71
        return $this->module;
72
    }
73
74
    /**
75
     * Getter accessor for config property
76
     * 
77
     * @return \BFW\Config
78
     */
79
    public function getConfig(): \BFW\Config
80
    {
81
        return $this->config;
82
    }
83
84
    /**
85
     * Getter accessor for ctrlRouterInfos property
86
     * 
87
     * @return object
88
     */
89
    public function getCtrlRouterInfos()
90
    {
91
        return $this->ctrlRouterInfos;
92
    }
93
94
    /**
95
     * Getter accessor for dispatcher property
96
     * 
97
     * @return \FastRoute\Dispatcher
98
     */
99
    public function getDispatcher(): \FastRoute\Dispatcher
100
    {
101
        return $this->dispatcher;
102
    }
103
    
104
    /**
105
     * Observer update method
106
     * Call obtainCurrentRoute method on action "apprun_loadAllAppModules".
107
     * 
108
     * @param \SplSubject $subject
109
     * 
110
     * @return void
111
     */
112
    public function update(\SplSubject $subject)
113
    {
114
        if ($subject->getAction() === 'ctrlRouterLink_exec_searchRoute') {
115
            $this->obtainCtrlRouterInfos($subject);
116
            
117
            if ($this->ctrlRouterInfos->isFound === false) {
118
                $this->searchRoute();
119
            }
120
        }
121
    }
122
    
123
    /**
124
     * Set the property ctrlRouterInfos with the context object obtain linked
125
     * to the subject.
126
     * Allow override to get only some part. And used for unit test.
127
     * 
128
     * @param \BFW\Subject $subject
129
     * 
130
     * @return void
131
     */
132
    protected function obtainCtrlRouterInfos(\BFW\Subject $subject)
133
    {
134
        $this->ctrlRouterInfos = $subject->getContext();
135
    }
136
    
137
    /**
138
     * Call by dispatcher; Add route in config to fastRoute router
139
     * 
140
     * @param FastRoute\RouteCollector $router FastRoute router
141
     * 
142
     * @return void
143
     */
144
    public function addRoutesToCollector(FastRoute\RouteCollector $router)
145
    {
146
        $this->module->monolog->getLogger()->debug('Add all routes.');
147
        
148
        $routes = $this->config->getValue('routes');
149
        
150
        foreach ($routes as $slug => $infos) {
151
            $slug = trim($slug);
152
153
            //Défault method
154
            $method = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'];
155
            
156
            //If method is declared for the route
157
            if (isset($infos['httpMethod'])) {
158
                //Get the method ans remove it from httpMethod array
159
                $method = $infos['httpMethod'];
160
                unset($infos['httpMethod']);
161
            }
162
163
            $router->addRoute($method, $slug, $infos);
164
        }
165
    }
166
    
167
    /**
168
     * Obtain informations about the current route from fastRoute dispatcher
169
     * 
170
     * @return void
171
     */
172
    protected function searchRoute()
173
    {
174
        //Get current request informations
175
        $bfwRequest = \BFW\Request::getInstance();
176
        $request    = $bfwRequest->getRequest()->path;
177
        $method     = $bfwRequest->getMethod();
178
        
179
        //If request is index
180
        if ($request === '') {
181
            $request = '/';
182
        }
183
184
        //Get route information from dispatcher
185
        $routeInfo   = $this->dispatcher->dispatch($method, $request);
186
        $routeStatus = $routeInfo[0];
187
        
188
        $this->module
189
            ->monolog
190
            ->getLogger()
191
            ->debug(
192
                'Search the current route into declared routes.',
193
                [
194
                    'request' => $request,
195
                    'method' => $method,
196
                    'status' => $routeStatus
197
                ]
198
            );
199
        
200
        //Get and send request http status to the controller/router linker
201
        $httpStatus = $this->checkStatus($routeStatus);
202
        
203
        if ($httpStatus === 404) {
204
            //404 will be declared by \BFW\Application::runCtrlRouterLink()
205
            return;
206
        }
207
        
208
        http_response_code($httpStatus);
209
        $this->addInfosToCtrlRouter();
210
        
211
        if ($httpStatus !== 200) {
212
            return;
213
        }
214
        
215
        //Obtains datas for route from config file and send to linker
216
        $this->addTargetToCtrlRouter($routeInfo[1]);
217
218
        //Add gets datas in route to $_GET var
219
        $this->addDatasToGetVar($routeInfo[1]);
220
        $this->addToSuperglobalVar('GET', $routeInfo[2]);
221
    }
222
    
223
    /**
224
     * Get http status for response from dispatcher
225
     * 
226
     * @param int $routeStatus : Route status send by dispatcher for request
227
     * 
228
     * @return int
229
     */
230
    protected function checkStatus(int $routeStatus): int
231
    {
232
        $httpStatus = 200;
233
        
234
        if ($routeStatus === FastRoute\Dispatcher::METHOD_NOT_ALLOWED) {
235
            $httpStatus = 405;
236
        } elseif ($routeStatus === FastRoute\Dispatcher::NOT_FOUND) {
237
            $httpStatus = 404;
238
        }
239
        
240
        return $httpStatus;
241
    }
242
    
243
    /**
244
     * Update ctrlRouterInfos properties isFound and forWho
245
     * 
246
     * @return void
247
     */
248
    protected function addInfosToCtrlRouter()
249
    {
250
        $modulesConfig = \BFW\Application::getInstance()
251
            ->getConfig()
252
            ->getValue('modules', 'modules.php')
253
        ;
254
        $forWho        = $modulesConfig['controller']['name'];
255
        
256
        $this->ctrlRouterInfos->isFound = true;
257
        $this->ctrlRouterInfos->forWho  = $forWho;
258
    }
259
    
260
    /**
261
     * Obtains route informations from config file and send this informations
262
     * to the controller/router linker
263
     * 
264
     * @param array $routeInfos : Route information from config file
265
     * 
266
     * @return void
267
     * 
268
     * @throws \Exception If target not define in config file
269
     */
270
    protected function addTargetToCtrlRouter(array $routeInfos)
271
    {
272
        if (array_key_exists('target', $routeInfos) === false) {
273
            throw new Exception(
274
                'Router : target not defined',
275
                self::ERR_TARGET_NOT_DECLARED
276
            );
277
        }
278
        
279
        $this->ctrlRouterInfos->target = $routeInfos['target'];
280
    }
281
    
282
    /**
283
     * Add datas into a superglobal var
284
     * 
285
     * @param string $globalVarName : Name of the superglobal var
286
     * @param array $datasToAdd : Datas to add to $_GET var
287
     * 
288
     * @return void
289
     */
290
    protected function addToSuperglobalVar(
291
        string $globalVarName,
292
        array $datasToAdd
293
    ) {
294
        global ${'_'.$globalVarName};
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
295
        ${'_'.$globalVarName} = array_merge(${'_'.$globalVarName}, $datasToAdd);
296
    }
297
    
298
    /**
299
     * If property 'get' has been declared into current route config, add
300
     * them into superglobal $_GET .
301
     * 
302
     * @param array $routeInfos route informations declared in config
303
     * 
304
     * @return void
305
     */
306
    protected function addDatasToGetVar(array $routeInfos)
307
    {
308
        if (isset($routeInfos['get'])) {
309
            $this->addToSuperglobalVar('GET', $routeInfos['get']);
310
        }
311
    }
312
}
313