Issues (13)

src/HttpRequestHandler.php (1 issue)

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
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;
278
    }
279
}
280