Total Complexity | 79 |
Total Lines | 562 |
Duplicated Lines | 0 % |
Changes | 8 | ||
Bugs | 2 | Features | 1 |
Complex classes like Output often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Output, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
33 | class Output extends Message\Response |
||
34 | { |
||
35 | use FilePathCollectorTrait; |
||
36 | |||
37 | /** |
||
38 | * Output::$mimeType |
||
39 | * |
||
40 | * @var string |
||
41 | */ |
||
42 | protected $mimeType = 'text/html'; |
||
43 | |||
44 | /** |
||
45 | * Output::$charset |
||
46 | * |
||
47 | * @var string |
||
48 | */ |
||
49 | protected $charset = 'utf8'; |
||
50 | |||
51 | // ------------------------------------------------------------------------ |
||
52 | |||
53 | /** |
||
54 | * Output::__construct |
||
55 | */ |
||
56 | public function __construct() |
||
57 | { |
||
58 | parent::__construct(); |
||
59 | |||
60 | // Set Browser Views Directory |
||
61 | $this->setFileDirName('Views'); |
||
62 | $this->addFilePath(PATH_KERNEL); |
||
63 | |||
64 | // Autoload exception and error language file |
||
65 | language()->loadFile(['exception', 'error']); |
||
66 | |||
67 | // Register Kernel defined handler |
||
68 | $this->register(); |
||
69 | } |
||
70 | |||
71 | // ------------------------------------------------------------------------ |
||
72 | |||
73 | /** |
||
74 | * Output::register |
||
75 | * |
||
76 | * Register Kernel defined error, exception and shutdown handler. |
||
77 | * |
||
78 | * @return void |
||
79 | */ |
||
80 | final private function register() |
||
81 | { |
||
82 | $whoops = new \Whoops\Run(); |
||
83 | |||
84 | if (is_ajax() or $this->mimeType === 'application/json' or $this->mimeType === 'application/xml') { |
||
85 | $whoops->pushHandler(new CallbackHandler(function ($error) { |
||
|
|||
86 | $this->send([ |
||
87 | 'status' => 500, |
||
88 | 'success' => false, |
||
89 | 'message' => $error->getMessage(), |
||
90 | 'metadata' => [ |
||
91 | 'file' => $error->getFile(), |
||
92 | 'line' => $error->getLine(), |
||
93 | 'trace' => $error->getTrace(), |
||
94 | ], |
||
95 | ]); |
||
96 | })); |
||
97 | } elseif (is_cli() or $this->mimeType === 'text/plain') { |
||
98 | $whoops->pushHandler(new PlainTextHandler()); |
||
99 | } elseif ($this->mimeType === 'text/html') { |
||
100 | $whoops->pushHandler(new PrettyPageHandler()); |
||
101 | } |
||
102 | |||
103 | $whoops->register(); |
||
104 | |||
105 | set_error_handler([&$this, 'errorHandler']); |
||
106 | set_exception_handler([&$whoops, 'handleException']); |
||
107 | register_shutdown_function([&$this, 'shutdownHandler']); |
||
108 | } |
||
109 | |||
110 | // ------------------------------------------------------------------------ |
||
111 | |||
112 | /** |
||
113 | * Output::shutdownHandler |
||
114 | * |
||
115 | * Kernel defined shutdown handler function. |
||
116 | * |
||
117 | * @return void |
||
118 | * @throws \O2System\Spl\Exceptions\ErrorException |
||
119 | */ |
||
120 | public function shutdownHandler() |
||
121 | { |
||
122 | $lastError = error_get_last(); |
||
123 | |||
124 | if (is_array($lastError)) { |
||
125 | $this->errorHandler( |
||
126 | $lastError[ 'type' ], |
||
127 | $lastError[ 'message' ], |
||
128 | $lastError[ 'file' ], |
||
129 | $lastError[ 'line' ] |
||
130 | ); |
||
131 | } |
||
132 | } |
||
133 | // -------------------------------------------------------------------- |
||
134 | |||
135 | /** |
||
136 | * Output::errorHandler |
||
137 | * |
||
138 | * Kernel defined error handler function. |
||
139 | * |
||
140 | * @param int $errorSeverity The first parameter, errno, contains the level of the error raised, as an integer. |
||
141 | * @param string $errorMessage The second parameter, errstr, contains the error message, as a string. |
||
142 | * @param string $errorFile The third parameter is optional, errfile, which contains the filename that the error |
||
143 | * was raised in, as a string. |
||
144 | * @param string $errorLine The fourth parameter is optional, errline, which contains the line number the error |
||
145 | * was raised at, as an integer. |
||
146 | * @param array $errorContext The fifth parameter is optional, errcontext, which is an array that points to the |
||
147 | * active symbol table at the point the error occurred. In other words, errcontext will |
||
148 | * contain an array of every variable that existed in the scope the error was triggered |
||
149 | * in. User error handler must not modify error context. |
||
150 | * |
||
151 | * @return bool If the function returns FALSE then the normal error handler continues. |
||
152 | * @throws ErrorException |
||
153 | */ |
||
154 | public function errorHandler($errorSeverity, $errorMessage, $errorFile, $errorLine, $errorContext = []) |
||
155 | { |
||
156 | $isFatalError = (((E_ERROR | E_COMPILE_ERROR | E_CORE_ERROR | E_USER_ERROR) & $errorSeverity) === $errorSeverity); |
||
157 | |||
158 | // When the error is fatal the Kernel will throw it as an exception. |
||
159 | if ($isFatalError) { |
||
160 | throw new ErrorException($errorMessage, $errorSeverity, $errorLine, $errorLine, $errorContext); |
||
161 | } |
||
162 | |||
163 | // Should we ignore the error? We'll get the current error_reporting |
||
164 | // level and add its bits with the severity bits to find out. |
||
165 | if (($errorSeverity & error_reporting()) !== $errorSeverity) { |
||
166 | return false; |
||
167 | } |
||
168 | |||
169 | $error = new ErrorException($errorMessage, $errorSeverity, $errorFile, $errorLine, $errorContext); |
||
170 | |||
171 | // Logged the error |
||
172 | if (services()->has('logger')) { |
||
173 | logger()->error( |
||
174 | implode( |
||
175 | ' ', |
||
176 | [ |
||
177 | '[ ' . $error->getStringSeverity() . ' ] ', |
||
178 | $error->getMessage(), |
||
179 | $error->getFile() . ':' . $error->getLine(), |
||
180 | ] |
||
181 | ) |
||
182 | ); |
||
183 | } |
||
184 | |||
185 | // Should we display the error? |
||
186 | if (str_ireplace(['off', 'none', 'no', 'false', 'null'], 0, ini_get('display_errors')) == 1) { |
||
187 | if (is_ajax()) { |
||
188 | $this->setContentType('application/json'); |
||
189 | $this->statusCode = 500; |
||
190 | $this->reasonPhrase = 'Internal Server Error'; |
||
191 | |||
192 | $this->send(implode( |
||
193 | ' ', |
||
194 | [ |
||
195 | '[ ' . $error->getStringSeverity() . ' ] ', |
||
196 | $error->getMessage(), |
||
197 | $error->getFile() . ':' . $error->getLine(), |
||
198 | ] |
||
199 | )); |
||
200 | exit(EXIT_ERROR); |
||
201 | } |
||
202 | |||
203 | $filePath = $this->getFilePath('error'); |
||
204 | |||
205 | ob_start(); |
||
206 | include $filePath; |
||
207 | $htmlOutput = ob_get_contents(); |
||
208 | ob_end_clean(); |
||
209 | |||
210 | echo $htmlOutput; |
||
211 | exit(EXIT_ERROR); |
||
212 | } |
||
213 | } |
||
214 | |||
215 | // ------------------------------------------------------------------------ |
||
216 | |||
217 | /** |
||
218 | * Output::getFilePath |
||
219 | * |
||
220 | * @param string $filename |
||
221 | * |
||
222 | * @return string |
||
223 | */ |
||
224 | public function getFilePath($filename) |
||
235 | } |
||
236 | } |
||
237 | } |
||
238 | |||
239 | // ------------------------------------------------------------------------ |
||
240 | |||
241 | /** |
||
242 | * Output::setContentType |
||
243 | * |
||
244 | * @param string $mimeType |
||
245 | * @param string $charset |
||
246 | * |
||
247 | * @return $this |
||
248 | */ |
||
249 | public function setContentType($mimeType, $charset = null) |
||
250 | { |
||
251 | static $mimes = []; |
||
252 | |||
253 | if (empty($mimes)) { |
||
254 | $mimes = require(str_replace('Http', 'Config', __DIR__) . DIRECTORY_SEPARATOR . 'Mimes.php'); |
||
255 | } |
||
256 | |||
257 | if (strpos($mimeType, '/') === false) { |
||
258 | $extension = ltrim($mimeType, '.'); |
||
259 | // Is this extension supported? |
||
260 | if (isset($mimes[ $extension ])) { |
||
261 | $mimeType =& $mimes[ $extension ]; |
||
262 | if (is_array($mimeType)) { |
||
263 | $mimeType = current($mimeType); |
||
264 | } |
||
265 | } |
||
266 | } |
||
267 | |||
268 | $this->mimeType = $mimeType; |
||
269 | |||
270 | $this->addHeader( |
||
271 | 'Content-Type', |
||
272 | $mimeType |
||
273 | . (empty($charset) ? '' : '; charset=' . $charset) |
||
274 | ); |
||
275 | |||
276 | return $this; |
||
277 | } |
||
278 | |||
279 | // ------------------------------------------------------------------------ |
||
280 | |||
281 | /** |
||
282 | * Output::addHeader |
||
283 | * |
||
284 | * @param string $name |
||
285 | * @param string $value |
||
286 | * |
||
287 | * @return static |
||
288 | */ |
||
289 | public function addHeader($name, $value) |
||
294 | } |
||
295 | |||
296 | // ------------------------------------------------------------------------ |
||
297 | |||
298 | /** |
||
299 | * Output::send |
||
300 | * |
||
301 | * @param $data |
||
302 | * @param array $headers |
||
303 | */ |
||
304 | public function send($data = null, array $headers = []) |
||
305 | { |
||
306 | $response = [ |
||
307 | 'status' => $statusCode = $this->statusCode, |
||
308 | 'reason' => $reasonPhrase = readable($this->reasonPhrase), |
||
309 | 'success' => true, |
||
310 | 'message' => null, |
||
311 | 'result' => [], |
||
312 | ]; |
||
313 | |||
314 | if (is_array($data)) { |
||
315 | if (isset($data[ 'status' ])) { |
||
316 | $response[ 'status' ] = $statusCode = $data[ 'status' ]; |
||
317 | unset($data[ 'status' ]); |
||
318 | } |
||
319 | |||
320 | if (isset($data[ 'reason' ])) { |
||
321 | $response[ 'reason' ] = $reasonPhrase = $data[ 'reason' ]; |
||
322 | unset($data[ 'reason' ]); |
||
323 | } |
||
324 | |||
325 | if (isset($data[ 'success' ])) { |
||
326 | $response[ 'success' ] = $data[ 'success' ]; |
||
327 | unset($data[ 'success' ]); |
||
328 | } |
||
329 | |||
330 | if (isset($data[ 'message' ])) { |
||
331 | $response[ 'message' ] = $data[ 'message' ]; |
||
332 | unset($data[ 'message' ]); |
||
333 | } |
||
334 | |||
335 | if (isset($data[ 'metadata' ])) { |
||
336 | $response[ 'metadata' ] = $data[ 'metadata' ]; |
||
337 | unset($data[ 'metadata' ]); |
||
338 | } |
||
339 | |||
340 | if (isset($data[ 'result' ])) { |
||
341 | $data = $data[ 'result' ]; |
||
342 | } |
||
343 | |||
344 | if (isset($data[ 'data' ])) { |
||
345 | $data = $data[ 'data' ]; |
||
346 | } |
||
347 | } elseif (is_object($data)) { |
||
348 | if (isset($data->status)) { |
||
349 | $response[ 'status' ] = $statusCode = $data->status; |
||
350 | unset($data->status); |
||
351 | } |
||
352 | |||
353 | if (isset($data->reason)) { |
||
354 | $response[ 'reason' ] = $reasonPhrase = $data->reason; |
||
355 | unset($data->reason); |
||
356 | } |
||
357 | |||
358 | if (isset($data->success)) { |
||
359 | $response[ 'success' ] = $data->success; |
||
360 | unset($data->success); |
||
361 | } |
||
362 | |||
363 | if (isset($data->message)) { |
||
364 | $response[ 'message' ] = $data->message; |
||
365 | unset($data->message); |
||
366 | } |
||
367 | |||
368 | if (isset($data->result)) { |
||
369 | $data = $data->result; |
||
370 | } |
||
371 | |||
372 | if (isset($data->data)) { |
||
373 | $data = $data->data; |
||
374 | } |
||
375 | } |
||
376 | |||
377 | if (is_object($data) and method_exists($data, 'getArrayCopy')) { |
||
378 | $data = $data->getArrayCopy(); |
||
379 | } |
||
380 | |||
381 | $this->sendHeaderStatus($statusCode, $reasonPhrase); |
||
382 | |||
383 | $this->sendHeaders($headers); |
||
384 | |||
385 | if (is_object($data) and method_exists($data, 'getArrayCopy')) { |
||
386 | $data = $data->getArrayCopy(); |
||
387 | } |
||
388 | |||
389 | if (is_array($data)) { |
||
390 | if (is_string(key($data))) { |
||
391 | $response[ 'result' ] = [$data]; |
||
392 | } elseif (is_numeric(key($data))) { |
||
393 | $response[ 'result' ] = $data; |
||
394 | } |
||
395 | } else { |
||
396 | $response[ 'result' ] = $data; |
||
397 | } |
||
398 | |||
399 | if (is_ajax()) { |
||
400 | $contentType = isset($_SERVER[ 'HTTP_X_REQUESTED_CONTENT_TYPE' ]) ? $_SERVER[ 'HTTP_X_REQUESTED_CONTENT_TYPE' ] : 'application/json'; |
||
401 | $this->setContentType($contentType); |
||
402 | } |
||
403 | |||
404 | if ($this->mimeType === 'application/json') { |
||
405 | echo json_encode($response, JSON_PRETTY_PRINT); |
||
406 | } elseif ($this->mimeType === 'application/xml') { |
||
407 | $xml = new \SimpleXMLElement('<?xml version="1.0"?><response></response>'); |
||
408 | $xml->addAttribute('status', $statusCode); |
||
409 | $xml->addAttribute('reason', $reasonPhrase); |
||
410 | $this->arrayToXml($response, $xml); |
||
411 | |||
412 | echo $xml->asXML(); |
||
413 | } elseif(is_cli()) { |
||
414 | print_cli($response, true); |
||
415 | } elseif(is_array($response['result'])) { |
||
416 | print_r($response['result']); |
||
417 | } else { |
||
418 | echo $response[ 'result' ]; |
||
419 | } |
||
420 | } |
||
421 | |||
422 | // ------------------------------------------------------------------------ |
||
423 | |||
424 | /** |
||
425 | * Output::sendHeaders |
||
426 | * |
||
427 | * @param array $headers |
||
428 | */ |
||
429 | protected function sendHeaders(array $headers = []) |
||
430 | { |
||
431 | ini_set('expose_php', 0); |
||
432 | |||
433 | // collect headers that already sent |
||
434 | foreach (headers_list() as $header) { |
||
435 | $headerParts = explode(':', $header); |
||
436 | $headerParts = array_map('trim', $headerParts); |
||
437 | $headers[ $headerParts[ 0 ] ] = $headerParts[ 1 ]; |
||
438 | header_remove($header[ 0 ]); |
||
439 | } |
||
440 | |||
441 | if (count($headers)) { |
||
442 | $this->headers = array_merge($this->headers, $headers); |
||
443 | } |
||
444 | |||
445 | if ($this->statusCode === 204) { |
||
446 | $this->statusCode = 200; |
||
447 | $this->reasonPhrase = 'OK'; |
||
448 | } |
||
449 | |||
450 | $this->sendHeaderStatus($this->statusCode, $this->reasonPhrase, $this->protocol); |
||
451 | |||
452 | foreach ($this->headers as $name => $value) { |
||
453 | $this->sendHeader($name, $value); |
||
454 | } |
||
455 | } |
||
456 | |||
457 | // ------------------------------------------------------------------------ |
||
458 | |||
459 | /** |
||
460 | * Output::sendHeaderStatus |
||
461 | * |
||
462 | * @param int $statusCode |
||
463 | * @param string $reasonPhrase |
||
464 | * @param string $protocol |
||
465 | * |
||
466 | * @return $this |
||
467 | */ |
||
468 | public function sendHeaderStatus($statusCode, $reasonPhrase, $protocol = '1.1') |
||
476 | } |
||
477 | |||
478 | // ------------------------------------------------------------------------ |
||
479 | |||
480 | /** |
||
481 | * Output::sendHeader |
||
482 | * |
||
483 | * @param string $name |
||
484 | * @param string $value |
||
485 | * @param bool $replace |
||
486 | * |
||
487 | * @return static |
||
488 | */ |
||
489 | public function sendHeader($name, $value, $replace = true) |
||
490 | { |
||
491 | @header($name . ': ' . trim($value), $replace); |
||
492 | |||
493 | return $this; |
||
494 | } |
||
495 | |||
496 | // ------------------------------------------------------------------------ |
||
497 | |||
498 | /** |
||
499 | * Output::arrayToXml |
||
500 | * |
||
501 | * @param array $data |
||
502 | * @param \SimpleXMLElement $xml |
||
503 | */ |
||
504 | protected function arrayToXml(array $data, \SimpleXMLElement &$xml) |
||
505 | { |
||
506 | foreach ($data as $key => $value) { |
||
507 | if (is_numeric($key)) { |
||
508 | $key = 'item' . $key; //dealing with <0/>..<n/> issues |
||
509 | } |
||
510 | if (is_array($value)) { |
||
511 | $subnode = $xml->addChild($key); |
||
512 | $this->arrayToXml($value, $subnode); |
||
513 | } else { |
||
514 | $xml->addChild("$key", htmlspecialchars("$value")); |
||
515 | } |
||
516 | } |
||
517 | } |
||
518 | |||
519 | // ------------------------------------------------------------------------ |
||
520 | |||
521 | /** |
||
522 | * Output::sendPayload |
||
523 | * |
||
524 | * @param array $data |
||
525 | * @param string|null $mimeType |
||
526 | */ |
||
527 | public function sendPayload(array $data, $mimeType = null) |
||
542 | } |
||
543 | |||
544 | // ------------------------------------------------------------------------ |
||
545 | |||
546 | /** |
||
547 | * Output::sendError |
||
548 | * |
||
549 | * @param int $code |
||
550 | * @param null|array|string $vars |
||
551 | * @param array $headers |
||
552 | */ |
||
553 | public function sendError($code = 204, $vars = null, $headers = []) |
||
595 | } |
||
596 | } |
||
597 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.