Test Failed
Pull Request — master (#19)
by Flo
02:19
created

Dispatcher::dispatch()   C

Complexity

Conditions 7
Paths 25

Size

Total Lines 39
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 39
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 20
nc 25
nop 0
1
<?php
2
/**
3
 * Class Dispatcher | Dispatcher.php
4
 * @package Faulancer\AbstractController
5
 * @author Florian Knapp <[email protected]>
6
 */
7
namespace Faulancer\Controller;
8
9
use Faulancer\Exception\ClassNotFoundException;
10
use Faulancer\Exception\DispatchFailureException;
11
use Faulancer\Exception\IncompatibleResponseException;
12
use Faulancer\Form\AbstractFormHandler;
13
use Faulancer\Http\JsonResponse;
14
use Faulancer\Http\Request;
15
use Faulancer\Http\Response;
16
use Faulancer\Exception\MethodNotFoundException;
17
use Faulancer\Service\Config;
18
19
/**
20
 * Class Dispatcher
21
 */
22
class Dispatcher
23
{
24
25
    /**
26
     * The current request object
27
     *
28
     * @var Request
29
     */
30
    protected $request;
31
32
    /**
33
     * The configuration object
34
     *
35
     * @var Config
36
     */
37
    protected $config;
38
39
    /**
40
     * user, api
41
     *
42
     * @var string
43
     */
44
    protected $requestType = 'default';
45
46
    /**
47
     * Dispatcher constructor.
48
     *
49
     * @param Request $request
50
     * @param Config  $config
51
     */
52
    public function __construct(Request $request, Config $config)
53
    {
54
        $this->request = $request;
55
        $this->config  = $config;
56
    }
57
58
    /**
59
     * Bootstrap for every route call
60
     *
61
     * @return Response|JsonResponse|mixed
62
     * @throws MethodNotFoundException
63
     * @throws ClassNotFoundException
64
     * @throws DispatchFailureException
65
     * @throws IncompatibleResponseException
66
     */
67
    public function dispatch()
68
    {
69
        // Check for core assets path
70
        if ($assets = $this->resolveAssetsPath()) {
71
            return $assets;
72
        }
73
74
        /** @var Response $response */
75
        $response = null;
0 ignored issues
show
Unused Code introduced by
$response is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
76
77
        if (strpos($this->request->getUri(), '/api') === 0) {
78
            $this->requestType = 'api';
79
        }
80
81
        $target  = $this->getRoute($this->request->getUri());
82
        $class   = $target['class'];
83
        $action  = $this->requestType === 'api' ? $this->getRestfulAction() : $target['action'];
84
        $payload = !empty($target['var']) ? $target['var'] : [];
85
86
        $payload = array_map('stripslashes', $payload);
87
        $payload = array_map('htmlentities', $payload);
88
        $payload = array_map('strip_tags', $payload);
89
90
        /** @var Response $class */
91
        $class = new $class($this->request);
92
93
        if (!method_exists($class, $action)) {
94
            throw new MethodNotFoundException('Class "' . get_class($class) . '" doesn\'t have the method ' . $action);
95
        }
96
97
        $response = call_user_func_array([$class, $action], $payload);
98
99
        if (!$response instanceof Response) {
100
            throw new IncompatibleResponseException('No valid response returned.');
101
        }
102
103
        return $response;
104
105
    }
106
107
    /**
108
     * @return bool|string
109
     */
110
    private function resolveAssetsPath()
111
    {
112
        $matches = [];
113
114
        if (preg_match('/(?<style>css)|(?<script>js)/', $this->request->getUri(), $matches)) {
115
116
            $file = $this->request->getUri();
117
118
            if (strpos($file, 'core') !== false) {
119
120
                $path = str_replace('/core', '', $file);
121
122
                if ($matches['style'] === 'css') {
123
                    return $this->sendCssFileHeader($path);
124
                } else if ($matches['script'] === 'js') {
125
                    return $this->sendJsFileHeader($path);
126
                }
127
128
            }
129
130
        }
131
132
        return false;
133
    }
134
135
    /**
136
     * @param $file
137
     * @return string
138
     * @codeCoverageIgnore
139
     */
140
    public function sendCssFileHeader($file)
141
    {
142
        header('Content-Type: text/css');
143
        echo file_get_contents(__DIR__ . '/../../public/assets' . $file);
144
        exit(0);
145
    }
146
147
    /**
148
     * @param $file
149
     * @return string
150
     * @codeCoverageIgnore
151
     */
152
    public function sendJsFileHeader($file)
153
    {
154
        header('Content-Type: text/javascript');
155
        echo file_get_contents(__DIR__ . '/../../public/assets' . $file);
156
        exit(0);
157
    }
158
159
    /**
160
     * Get data for specific route path
161
     *
162
     * @param string $path
163
     *
164
     * @return array
165
     * @throws MethodNotFoundException
166
     */
167
    private function getRoute($path)
168
    {
169
        if (strpos($this->request->getUri(), '/api') === 0) {
170
            $routes = $this->config->get('routes:rest');
171
        } else {
172
            $routes = $this->config->get('routes');
173
        }
174
175
        foreach ($routes as $name => $data) {
176
177
            if ($target = $this->getDirectMatch($path, $data)) {
178
                return $target;
179
            } else if ($target = $this->getVariableMatch($path, $data)) {
180
                return $target;
181
            }
182
183
        }
184
185
        throw new MethodNotFoundException('No matching route for path "' . $path . '" found');
186
    }
187
188
    /**
189
     * Determines if we have a direct/static route match
190
     *
191
     * @param string $uri  The request uri
192
     * @param array  $data The result from ClassParser
193
     *
194
     * @return array
195
     * @throws MethodNotFoundException
196
     */
197
    private function getDirectMatch($uri, array $data) :array
198
    {
199
        if (!empty($data['path']) && $uri === $data['path']) {
200
201
            if ($this->requestType === 'default' && in_array($this->request->getMethod(), $data['method'])) {
202
203
                return [
204
                    'class'  => $data['controller'],
205
                    'action' => $data['action'] . 'Action'
206
                ];
207
208 View Code Duplication
            } else if ($this->requestType === 'api') {
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...
209
210
                return [
211
                    'class'  => $data['controller'],
212
                    'action' => $this->getRestfulAction()
213
                ];
214
215
            }
216
217
            throw new MethodNotFoundException('Non valid request method available.');
218
219
        }
220
221
        return [];
222
    }
223
224
    /**
225
     * Determines if we have a variable route match
226
     *
227
     * @param string $uri
228
     * @param array  $data
229
     *
230
     * @return array
231
     * @throws MethodNotFoundException
232
     */
233
    private function getVariableMatch($uri, array $data) :array
234
    {
235
        if (empty($data['path']) || $data['path'] === '/') {
236
            return [];
237
        }
238
239
        $var   = [];
240
        $regex = str_replace(['/', '___'], ['\/', '+'], $data['path']);
241
242
        if (preg_match('|^' . $regex . '$|', $uri, $var)) {
243
244
            array_splice($var, 0, 1);
245
246
            if ($this->requestType === 'default'  && in_array($this->request->getMethod(), $data['method'])) {
247
248
                return [
249
                    'class'  => $data['controller'],
250
                    'action' => $data['action'] . 'Action',
251
                    'var'    => $var
252
                ];
253
254 View Code Duplication
            } else if ($this->requestType === 'api') {
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...
255
256
                return [
257
                    'class'  => $data['controller'],
258
                    'action' => $this->getRestfulAction(),
259
                    'var'    => $var
260
                ];
261
262
            }
263
264
        }
265
266
        return [];
267
    }
268
269
    /**
270
     * @return string
271
     */
272
    private function getRestfulAction()
273
    {
274
        $method = strtoupper($this->request->getMethod());
275
276
        switch ($method) {
277
278
            case 'GET':
279
                return 'get';
280
281
            case 'POST':
282
                return 'create';
283
284
            case 'PUT':
285
                return 'update';
286
287
            case 'DELETE':
288
                return 'delete';
289
290
            case 'PATCH':
291
                return 'update';
292
293
            default:
294
                return 'get';
295
296
        }
297
    }
298
299
}