Passed
Pull Request — master (#11)
by Joao
01:36
created

HttpRequestHandler::withDetailedErrorHandler()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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