php_analyzer.type_inference.infeasible_condition.generic
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
![]() |
|||
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 |