Completed
Push — 2.0 ( f4f3c4...648144 )
by Vermeulen
01:58
created

BfwApi::runRest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
1
<?php
2
/**
3
 * @author Vermeulen Maxime <[email protected]>
4
 * @version 2.0
5
 */
6
7
namespace BfwApi;
8
9
use \Exception;
10
11
/**
12
 * Class for API system
13
 * @package bfw-api
14
 */
15
class BfwApi implements \SplObserver
16
{
17
    /**
18
     * @var \BFW\Module $module The bfw module instance for this module
19
     */
20
    protected $module;
21
    
22
    /**
23
     * @var \BFW\Config $config The bfw config instance for this module
24
     */
25
    protected $config;
26
    
27
    /**
28
     * @var \FastRoute\Dispatcher $dispatcher FastRoute dispatcher
29
     */
30
    protected $dispatcher;
31
    
32
    /**
33
     * @var boolean $routeFindByOther If the route for current request has been
34
     *  found by an other module. So we not need to search.
35
     */
36
    protected $routeFindByOther;
37
    
38
    /**
39
     * Constructor
40
     * 
41
     * @param \BFW\Module $module
42
     */
43
    public function __construct(\BFW\Module $module)
44
    {
45
        $this->routeFindByOther = false;
46
        
47
        $this->module = $module;
48
        $this->config = $module->getConfig();
49
        
50
        $this->dispatcher = \FastRoute\simpleDispatcher([$this, 'addRoutesToCollector']);
51
    }
52
    
53
    /**
54
     * Call by dispatcher; Add route in config to fastRoute router
55
     * 
56
     * @param \FastRoute\RouteCollector $router FastRoute router
57
     * 
58
     * @return void
59
     */
60
    public function addRoutesToCollector(\FastRoute\RouteCollector $router)
61
    {
62
        $urlPrefix = $this->config->getConfig('urlPrefix', 'config.php');
63
        $routes    = $this->config->getConfig('routes', 'routes.php');
64
        
65
        foreach ($routes as $slug => $infos) {
66
            $slug = trim($urlPrefix.$slug);
67
68
            //Défault method
69
            $method = ['GET', 'POST', 'PUT', 'DELETE'];
70
            
71
            //If method is declared for the route
72
            if (isset($infos['httpMethod'])) {
73
                //Get the method ans remove it from httpMethod array
74
                $method = $infos['httpMethod'];
75
                unset($infos['httpMethod']);
76
            }
77
78
            $router->addRoute($method, $slug, $infos);
79
        }
80
    }
81
    
82
    /**
83
     * Observer update method
84
     * Call run method on action "bfw_run_finish" and route has
85
     * not been already found.
86
     * Update attribute routeFindByOther on action "request_route_find".
87
     * 
88
     * @param \SplSubject $subject
89
     * 
90
     * @return void
91
     */
92
    public function update(\SplSubject $subject)
93
    {
94
        if (
95
            $subject->getAction() === 'bfw_run_finish'
96
            && $this->routeFindByOther === false
97
        ) {
98
            $this->run();
99
        }
100
        
101
        if ($subject->getAction() === 'request_route_find') {
102
            $this->routeFindByOther = true;
103
        }
104
    }
105
    
106
    /**
107
     * Run when the notify "bfw_run_finish" is emit
108
     * Check if we are in an API route
109
     * If it's an API route,
110
     * * Get the class name to use for this route
111
     * * Call the method corresponding to request in the class declared
112
     * 
113
     * @return void
114
     */
115
    public function run()
116
    {
117
        $className = $this->obtainClassNameForCurrentRoute();
118
        
119
        //Get current request informations
120
        $bfwRequest = \BFW\Request::getInstance();
121
        $method     = strtolower($bfwRequest->getMethod());
122
        
123
        if (!class_exists($className)) {
124
            throw new \Exception('Class '.$className.' not found.');
125
        }
126
        if (!method_exists($className, $method.'Request')) {
127
            throw new \Exception(
128
                'Method '.$method.'Request not found in class '.$className.'.'
129
            );
130
        }
131
        
132
        $useRest    = $this->config->getConfig('useRest', 'config.php');
133
        $useGraphQL = $this->config->getConfig('useGraphQL', 'config.php');
134
        
135
        if ($useRest === true) {
136
            return $this->runRest($className, $method);
137
        } elseif ($useGraphQL === true) {
138
            return $this->runGraphQL();
139
        }
140
        
141
        throw new Exception(
142
            'Please choose between REST and GraphQL in config file.'
143
        );
144
    }
145
    
146
    protected function runRest($className, $method)
147
    {
148
        $api = new $className;
149
        $api->{$method.'Request'}();
150
    }
151
    
152
    protected function runGraphQL()
153
    {
154
        //Not implement yet
155
        http_response_code(501);
156
    }
157
    
158
    /**
159
     * Obtain the classname to use for current route from fastRoute dispatcher
160
     * 
161
     * @return string|void The classname or nothing if error
162
     * 
163
     * @throw \Exception If no "className" is define in config for the route.
164
     */
165
    protected function obtainClassNameForCurrentRoute()
166
    {
167
        //Get current request informations
168
        $bfwRequest = \BFW\Request::getInstance();
169
        $request    = $bfwRequest->getRequest()->path;
170
        $method     = $bfwRequest->getMethod();
171
172
        //Get route information from dispatcher
173
        $routeInfo   = $this->dispatcher->dispatch($method, $request);
174
        $routeStatus = $routeInfo[0];
175
        
176
        //Get and send request http status to the controller/router linker
177
        $httpStatus = $this->checkStatus($routeStatus);
178
        
179
        if ($httpStatus !== 200) {
180
            http_response_code($httpStatus);
181
            return;
182
        }
183
184
        global $_GET;
185
        $_GET = array_merge($_GET, $routeInfo[2]);
186
        
187
        if (!isset($routeInfo[1]['className'])) {
188
            throw new Exception('className not define for uri '.$request);
189
        }
190
        
191
        http_response_code(200);
192
        $this->sendNotifyRouteFindToOthers();
193
        
194
        return $routeInfo[1]['className'];
195
    }
196
    
197
    /**
198
     * Get http status for response from dispatcher
199
     * 
200
     * @param int $routeStatus : Route status send by dispatcher for request
201
     * 
202
     * @return int
203
     */
204
    protected function checkStatus($routeStatus)
205
    {
206
        $httpStatus = 200;
207
        
208
        if ($routeStatus === \FastRoute\Dispatcher::METHOD_NOT_ALLOWED) {
209
            $httpStatus = 405;
210
        } elseif ($routeStatus === \FastRoute\Dispatcher::NOT_FOUND) {
211
            $httpStatus = 404;
212
        }
213
        
214
        return $httpStatus;
215
    }
216
    
217
    /**
218
     * Send to all observer of Application a notify who contains the message
219
     * "request_route_find" to say the route for the current request has been
220
     * found by us.
221
     * 
222
     * @return void
223
     */
224
    protected function sendNotifyRouteFindToOthers()
225
    {
226
        $app = \BFW\Application::getInstance();
227
        $app->notifyAction('request_route_find');
228
    }
229
}
230