HttpRequestHandler::executeRequest()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4.5923

Importance

Changes 0
Metric Value
cc 4
eloc 14
nc 4
nop 3
dl 0
loc 24
ccs 10
cts 15
cp 0.6667
crap 4.5923
rs 9.7998
c 0
b 0
f 0
1
<?php
2
3
namespace ByJG\RestServer;
4
5
use ByJG\RestServer\Exception\ClassNotFoundException;
6
use ByJG\RestServer\Exception\Error404Exception;
7
use ByJG\RestServer\Exception\Error405Exception;
8
use ByJG\RestServer\Exception\Error520Exception;
9
use ByJG\RestServer\Exception\InvalidClassException;
10
use ByJG\RestServer\OutputProcessor\BaseOutputProcessor;
11
use ByJG\RestServer\OutputProcessor\OutputProcessorInterface;
12
use ByJG\RestServer\Route\RouteDefinition;
13
use ByJG\RestServer\Route\RouteDefinitionInterface;
14
use Closure;
15
use FastRoute\Dispatcher;
16
17
class HttpRequestHandler implements RequestHandler
18
{
19
    const OK = "OK";
20
    const METHOD_NOT_ALLOWED = "NOT_ALLOWED";
21
    const NOT_FOUND = "NOT FOUND";
22
23
    protected $useErrorHandler = true;
24
25
    /**
26
     * @param RouteDefinitionInterface $routeDefinition
27
     * @return bool
28
     * @throws ClassNotFoundException
29
     * @throws Error404Exception
30
     * @throws Error405Exception
31
     * @throws Error520Exception
32
     * @throws InvalidClassException
33
     */
34 7
    protected function process(RouteDefinitionInterface $routeDefinition)
35
    {
36
        // Initialize ErrorHandler with default error handler
37 7
        if ($this->useErrorHandler) {
38 7
            ErrorHandler::getInstance()->register();
39
        }
40
41
        // Get HttpRequest
42 7
        $request = $this->getHttpRequest();
43
44
        // Get the URL parameters
45 7
        $httpMethod = $request->server('REQUEST_METHOD');
46 7
        $uri = parse_url($request->server('REQUEST_URI'), PHP_URL_PATH);
47 7
        parse_str(parse_url($request->server('REQUEST_URI'), PHP_URL_QUERY), $queryStr);
48
49
        // Generic Dispatcher for RestServer
50 7
        $dispatcher = $routeDefinition->getDispatcher();
51
52 7
        $routeInfo = $dispatcher->dispatch($httpMethod, $uri);
53
54
        // Processing
55 7
        switch ($routeInfo[0]) {
56 7
            case Dispatcher::NOT_FOUND:
57 3
                if ($this->tryDeliveryPhysicalFile() === false) {
58 2
                    $this->prepareToOutput();
59 2
                    throw new Error404Exception("Route '$uri' not found");
60
                }
61 1
                return true;
62
63 4
            case Dispatcher::METHOD_NOT_ALLOWED:
64 1
                $this->prepareToOutput();
65 1
                throw new Error405Exception('Method not allowed');
66
67 3
            case Dispatcher::FOUND:
68
                // ... 200 Process:
69 3
                $vars = array_merge($routeInfo[2], $queryStr);
70
71
                // Get the Selected Route
72 3
                $selectedRoute = $routeInfo[1];
73
74
                // Default Handler for errors and
75 3
                $outputProcessor = $this->prepareToOutput($selectedRoute["output_processor"]);
76
77
                // Class
78 3
                $class = $selectedRoute["class"];
79 3
                $request->appendVars($vars);
80
81
                // Execute the request
82 3
                $this->executeRequest($outputProcessor, $class, $request);
83
84 2
                break;
85
86
            default:
87
                throw new Error520Exception('Unknown');
88
        }
89 2
    }
90
91 6
    protected function prepareToOutput($class = null)
92
    {
93 6
        if (empty($class)) {
94 3
            $outputProcessor = BaseOutputProcessor::getFromHttpAccept();
95
        } else {
96 3
            $outputProcessor = BaseOutputProcessor::getFromClassName($class);
97
        }
98 6
        $outputProcessor->writeContentType();
99 6
        ErrorHandler::getInstance()->setHandler($outputProcessor->getErrorHandler());
100
101 6
        return $outputProcessor;
102
    }
103
104 7
    protected function getHttpRequest()
105
    {
106 7
        return new HttpRequest($_GET, $_POST, $_SERVER, isset($_SESSION) ? $_SESSION : [], $_COOKIE);
107
    }
108
109
    /**
110
     * @param OutputProcessorInterface $outputProcessor
111
     * @param $class
112
     * @param HttpRequest $request
113
     * @throws ClassNotFoundException
114
     * @throws InvalidClassException
115
     */
116 3
    protected function executeRequest(OutputProcessorInterface $outputProcessor, $class, HttpRequest $request)
117
    {
118
        // Create the Request and Response methods
119 3
        $response = new HttpResponse();
120
121
        // Process Closure
122 3
        if ($class instanceof Closure) {
123 2
            $class($response, $request);
124 2
            $outputProcessor->processResponse($response);
125 2
            return;
126
        }
127
128
        // Process Class::Method()
129 1
        $function = $class[1];
130 1
        $class =  $class[0];
131 1
        if (!class_exists($class)) {
132 1
            throw new ClassNotFoundException("Class '$class' defined in the route is not found");
133
        }
134
        $instance = new $class();
135
        if (!method_exists($instance, $function)) {
136
            throw new InvalidClassException("There is no method '$class::$function''");
137
        }
138
        $instance->$function($response, $request);
139
        $outputProcessor->processResponse($response);
140
    }
141
142
    /**
143
     * Handle the ROUTE (see web/app-dist.php)
144
     *
145
     * @param RouteDefinitionInterface $routeDefinition
146
     * @param bool $outputBuffer
147
     * @param bool $session
148
     * @return bool|void
149
     * @throws ClassNotFoundException
150
     * @throws Error404Exception
151
     * @throws Error405Exception
152
     * @throws Error520Exception
153
     * @throws InvalidClassException
154
     */
155 7
    public function handle(RouteDefinitionInterface $routeDefinition, $outputBuffer = true, $session = false)
156
    {
157 7
        if ($outputBuffer) {
158
            ob_start();
159
        }
160 7
        if ($session) {
161
            session_start();
162
        }
163
164
        // --------------------------------------------------------------------------
165
        // Check if script exists or if is itself
166
        // --------------------------------------------------------------------------
167 7
        return $this->process($routeDefinition);
168
    }
169
170
    /**
171
     * @return bool
172
     * @throws Error404Exception
173
     */
174 3
    protected function tryDeliveryPhysicalFile()
175
    {
176 3
        $file = $_SERVER['SCRIPT_FILENAME'];
177 3
        if (!empty($file) && file_exists($file)) {
178 3
            $mime = $this->mimeContentType($file);
179
180 3
            if ($mime === false) {
0 ignored issues
show
introduced by Joao Gilberto Magalhaes
The condition $mime === false is always false.
Loading history...
181 2
                return false;
182
            }
183
184 1
            if (!defined("RESTSERVER_TEST")) {
185
                header("Content-Type: $mime");
186
            }
187 1
            echo file_get_contents($file);
188 1
            return true;
189
        }
190
191
        return false;
192
    }
193
194
    /**
195
     * Get the Mime Type based on the filename
196
     *
197
     * @param string $filename
198
     * @return string
199
     * @throws Error404Exception
200
     */
201 7
    protected function mimeContentType($filename)
202
    {
203
        $prohibitedTypes = [
204 7
            "php",
205
            "vb",
206
            "cs",
207
            "rb",
208
            "py",
209
            "py3",
210
            "lua"
211
        ];
212
213
        $mimeTypes = [
214 7
            'txt' => 'text/plain',
215
            'htm' => 'text/html',
216
            'html' => 'text/html',
217
            'css' => 'text/css',
218
            'js' => 'application/javascript',
219
            'json' => 'application/json',
220
            'xml' => 'application/xml',
221
            'swf' => 'application/x-shockwave-flash',
222
            'flv' => 'video/x-flv',
223
            // images
224
            'png' => 'image/png',
225
            'jpe' => 'image/jpeg',
226
            'jpeg' => 'image/jpeg',
227
            'jpg' => 'image/jpeg',
228
            'gif' => 'image/gif',
229
            'bmp' => 'image/bmp',
230
            'ico' => 'image/vnd.microsoft.icon',
231
            'tiff' => 'image/tiff',
232
            'tif' => 'image/tiff',
233
            'svg' => 'image/svg+xml',
234
            'svgz' => 'image/svg+xml',
235
            // archives
236
            'zip' => 'application/zip',
237
            'rar' => 'application/x-rar-compressed',
238
            'exe' => 'application/x-msdownload',
239
            'msi' => 'application/x-msdownload',
240
            'cab' => 'application/vnd.ms-cab-compressed',
241
            // audio/video
242
            'mp3' => 'audio/mpeg',
243
            'qt' => 'video/quicktime',
244
            'mov' => 'video/quicktime',
245
            // adobe
246
            'pdf' => 'application/pdf',
247
            'psd' => 'image/vnd.adobe.photoshop',
248
            'ai' => 'application/postscript',
249
            'eps' => 'application/postscript',
250
            'ps' => 'application/postscript',
251
            // ms office
252
            'doc' => 'application/msword',
253
            'rtf' => 'application/rtf',
254
            'xls' => 'application/vnd.ms-excel',
255
            'ppt' => 'application/vnd.ms-powerpoint',
256
            // open office
257
            'odt' => 'application/vnd.oasis.opendocument.text',
258
            'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
259
        ];
260
261 7
        if (!file_exists($filename)) {
262 1
            throw new Error404Exception();
263
        }
264
265 6
        $ext = substr(strrchr($filename, "."), 1);
266 6
        if (!in_array($ext, $prohibitedTypes)) {
267 4
            if (array_key_exists($ext, $mimeTypes)) {
268 4
                return $mimeTypes[$ext];
269
            } elseif (function_exists('finfo_open')) {
270
                $finfo = finfo_open(FILEINFO_MIME);
271
                $mimetype = finfo_file($finfo, $filename);
272
                finfo_close($finfo);
273
                return $mimetype;
274
            }
275
        }
276
277 2
        return false;
0 ignored issues
show
Bug Best Practice introduced by Joao Gilberto Magalhaes
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
278
    }
279
}
280