2017-10-07

Integration of PHP Error Handling


Integration of PHP Error Handling

Last week, on how to integrate PHP 5 error handling, I was asked to explain at multiple development projects.

As used herein, "Integration of PHP Error Handling" means, for example, the following development requirements.
  1. # Normal Error
  2. # Exception
  3. # PHP Core Error
  4. We want to integrate and manage error handling such as logs and mails when these occur.
In order to realize the above, you need to understand the habit and mechanism of PHP error.
Here, we describe these realization methods.


Notice

  1. # In this article, we will focus on these implementation methods.
  2. # In this article, we do not mention general concepts such as "difference between error and exception".
  3. # In this article, we describe it only for suspend processing.
  4. # In this article, we do not mention restoration processing, trigger, processing level etc.
  5. # This article applies only to PHP 5. It does not apply to PHP 7.


Flow

One way to realize this "PHP error integration processing" is the flow of error processing as follows.
  1. Normal Error Processing Normal Error
  2. Exception Processing Exception
  3. Shutdown Processing PHP Core Error


Normal Error Handling

First, take handling authority of PHP Normal Error or User-Defined Error from PHP.
In PHP, the following functions are prepared.
mixed set_error_handler(callable $error_handler [, int $error_types = E_ALL | E_STRICT])
In order to take processing authority from PHP, create a callback function and register it in this function.
(Be sure to register it as early as possible of a series of request processing.)
Within the callback function, take the normal error and rethrow as an exception.
In short, the goal is to take the normal error handling authority from PHP, and pass it to the exception.
However, in this callback function, it is one point that PHP core error can not be taken.
  1. public function handleError()
  2. {
  3. //* Error Handler Definition
  4. function handleError($_number, $_message, $_file, $_line, $_contexts)
  5. {
  6. //* Not Includ Error Reporting
  7. if (! (error_reporting() & $_number)) {
  8. return;
  9. }
  10. //* to ErrorException
  11. throw new ErrorException($_message, 500, $_number, $_file, $_line);
  12. }
  13.  
  14. //* Error Handler Set
  15. set_error_handler('handleError');
  16. }


Exception Handling

Next, take exception processing authority which was not caught from PHP.
In PHP, the following functions are prepared.
callable set_exception_handler(callable $exception_handler)
In order to take processing authority from PHP, create a callback function and register it in this function.
(Be sure to register it as early as possible of a series of request processing.)
As a result, all normal errors and all uncaught exceptions are aggregated in one place.
But this is not enough.
We have not taken PHP Core Error yet.
Therefore, processing logic is not placed here.
  1. public function handleException()
  2. {
  3. //* Exception Handler Definition
  4. function handleException($_e)
  5. {
  6. //* Exception Context
  7. $_SERVER['X_EXCEPTION_HANDLER_CONTEXT'] = $_e;
  8.  
  9. //* Error Processing to Shutdown Logic
  10. exit;
  11. }
  12.  
  13. //* Exception Handler Set
  14. set_exception_handler('handleException');
  15. }


PHP Core Error Handling

In PHP 5, set_error_handler () can not take the processing authority of core error issued by PHP.
PHP 5 does not throw an exception of core error.
Therefore, in order to capture the PHP core error, the following function is used.
void register_shutdown_function(callable $callback [, mixed $parameter [, mixed $... ]])
This function makes it possible to register a callback function to be executed when script processing is completed or when exit () is called.
Utilizing this property, it is possible to integrate all processing such as error, exception, PHP core error, etc. as a result.
  1. public function handleShutdown($_error_mails = array())
  2. {
  3. //* Shutdown Function Definition
  4. function handleShutdown($_error_numbers = array(), $_error_mails = array(), $_http_status_codes = array())
  5. {
  6. //* Exception or Error
  7. if (! empty($_SERVER['X_EXCEPTION_HANDLER_CONTEXT'])) {
  8. $e = $_SERVER['X_EXCEPTION_HANDLER_CONTEXT'];
  9. unset($_SERVER['X_EXCEPTION_HANDLER_CONTEXT']);
  10. $message = $e->__toString();
  11. $code = $e->getCode();
  12. } else {
  13. $e = error_get_last();
  14. //* Normal Exit
  15. if (empty($e)) {
  16. return;
  17. }
  18.  
  19. //* Core Error
  20. $message = $_error_numbers[$e['type']] . ': ' . $e['message'] . ' in ' . $e['file'] . ' on line ' . $e['line'];
  21. $code = 500;
  22. }
  23.  
  24. //* Error Logging
  25. error_log($message, 4);
  26.  
  27. //* Error Mail
  28. $cmd = 'echo "' . $message . '" | mail -S "smtp=smtp://' . $_error_mails['host'] . '" -r "' . $_error_mails['from'] . '" -s "' . $_error_mails['subject'] . '" ' . $_error_mails['to'];
  29. $outputs = array();
  30. $status = null;
  31. $last_line = exec($cmd, $outputs, $status);
  32.  
  33. //* HTTP Status Code
  34. header('HTTP/1.1 ' . $code . ' ' . $_http_status_codes[$code]);
  35.  
  36. //* Shutdown
  37. exit($code . ' ' . $_http_status_codes[$code]);
  38. }
  39.  
  40. //* Shutdown Function Registration
  41. $error_numbers = self::$error_numbers;
  42. $http_status_codes = self::$http_status_codes;
  43. register_shutdown_function('handleShutdown', $error_numbers, $_error_mails, $http_status_codes);
  44. }


to Class Library

When these are made into a general purpose class library, it becomes as follows.
Logging, e-mail, exception context delivery, etc. should be changed according to circumstances.
  1. class AppE
  2. {
  3.  
  4. public static $error_numbers = array(
  5. 1 => 'Fatal',
  6. 2 => 'Warning',
  7. 4 => 'Parse Error',
  8. 8 => 'Notice',
  9. 16 => 'Core Fatal',
  10. 32 => 'Core Warning',
  11. 64 => 'Compile Error',
  12. 128 => 'Compile Warning',
  13. 256 => 'Ex Error',
  14. 512 => 'Ex Warning',
  15. 1024 => 'Ex Notice',
  16. 2048 => 'Strict Error',
  17. 4096 => 'Recoverable Error',
  18. 8192 => 'Deprecated',
  19. 16384 => 'Ex Deprecated',
  20. 32767 => 'All',
  21. );
  22.  
  23. //* HTTP Status Code
  24. public static $http_status_codes = array(
  25. 'default' => 200,
  26. 100 => 'Continue',
  27. 101 => 'Switching Protocols',
  28. 102 => 'Processing',
  29. 200 => 'OK',
  30. 201 => 'Created',
  31. 202 => 'Accepted',
  32. 203 => 'Non-Authoritative Information',
  33. 204 => 'No Content',
  34. 205 => 'Reset Content',
  35. 206 => 'Partial Content',
  36. 207 => 'Multi-Status',
  37. 226 => 'IM Used',
  38. 300 => 'Multiple Choices',
  39. 301 => 'Moved Permanently',
  40. 302 => 'Found',
  41. 303 => 'See Other',
  42. 304 => 'Not Modified',
  43. 305 => 'Use Proxy',
  44. 307 => 'Temporary Redirect',
  45. 400 => 'Bad Request',
  46. 401 => 'Unauthorized',
  47. 402 => 'Payment Required',
  48. 403 => 'Forbidden',
  49. 404 => 'Not Found',
  50. 405 => 'Method Not Allowed',
  51. 406 => 'Not Acceptable',
  52. 407 => 'Proxy Authentication Required',
  53. 408 => 'Request Timeout',
  54. 409 => 'Conflict',
  55. 410 => 'Gone',
  56. 411 => 'Length Required',
  57. 412 => 'Precondition Failed',
  58. 413 => 'Request Entity Too Large',
  59. 414 => 'Request-URI Too Long',
  60. 415 => 'Unsupported Media Type',
  61. 416 => 'Requested Range Not Satisfiable',
  62. 417 => 'Expectation Failed',
  63. 418 => "I'm a teapot",
  64. 422 => 'Unprocessable Entity',
  65. 423 => 'Locked',
  66. 424 => 'Failed Dependency',
  67. 426 => 'Upgrade Required',
  68. 500 => 'Internal Server Error',
  69. 501 => 'Not Implemented',
  70. 502 => 'Bad Gateway',
  71. 503 => 'Service Unavailable',
  72. 504 => 'Gateway Timeout',
  73. 505 => 'HTTP Version Not Supported',
  74. 506 => 'Variant Also Negotiates',
  75. 507 => 'Insufficient Storage',
  76. 509 => 'Bandwidth Limit Exceeded',
  77. 510 => 'Not Extended',
  78. );
  79.  
  80.  
  81. public function __construct()
  82. {}
  83.  
  84.  
  85. public function handleError()
  86. {
  87. //* Error Handler Definition
  88. function handleError($_number, $_message, $_file, $_line, $_contexts)
  89. {
  90. //* Not Includ Error Reporting
  91. if (! (error_reporting() & $_number)) {
  92. return;
  93. }
  94. //* to ErrorException
  95. throw new ErrorException($_message, 500, $_number, $_file, $_line);
  96. }
  97.  
  98. //* Error Handler Set
  99. set_error_handler('handleError');
  100. }
  101.  
  102.  
  103. public function handleException()
  104. {
  105. //* Exception Handler Definition
  106. function handleException($_e)
  107. {
  108. //* Exception Context
  109. $_SERVER['X_EXCEPTION_HANDLER_CONTEXT'] = $_e;
  110.  
  111. //* Error Processing to Shutdown Logic
  112. exit;
  113. }
  114.  
  115. //* Exception Handler Set
  116. set_exception_handler('handleException');
  117. }
  118.  
  119.  
  120. public function handleShutdown($_error_mails = array())
  121. {
  122. //* Shutdown Function Definition
  123. function handleShutdown($_error_numbers = array(), $_error_mails = array(), $_http_status_codes = array())
  124. {
  125. //* Exception or Error
  126. if (! empty($_SERVER['X_EXCEPTION_HANDLER_CONTEXT'])) {
  127. $e = $_SERVER['X_EXCEPTION_HANDLER_CONTEXT'];
  128. unset($_SERVER['X_EXCEPTION_HANDLER_CONTEXT']);
  129. $message = $e->__toString();
  130. $code = $e->getCode();
  131. } else {
  132. $e = error_get_last();
  133. //* Normal Exit
  134. if (empty($e)) {
  135. return;
  136. }
  137.  
  138. //* Core Error
  139. $message = $_error_numbers[$e['type']] . ': ' . $e['message'] . ' in ' . $e['file'] . ' on line ' . $e['line'];
  140. $code = 500;
  141. }
  142.  
  143. //* Error Logging
  144. error_log($message, 4);
  145.  
  146. //* Error Mail
  147. $cmd = 'echo "' . $message . '" | mail -S "smtp=smtp://' . $_error_mails['host'] . '" -r "' . $_error_mails['from'] . '" -s "' . $_error_mails['subject'] . '" ' . $_error_mails['to'];
  148. $outputs = array();
  149. $status = null;
  150. $last_line = exec($cmd, $outputs, $status);
  151.  
  152. //* HTTP Status Code
  153. header('HTTP/1.1 ' . $code . ' ' . $_http_status_codes[$code]);
  154.  
  155. //* Shutdown
  156. exit($code . ' ' . $_http_status_codes[$code]);
  157. }
  158.  
  159. //* Shutdown Function Registration
  160. $error_numbers = self::$error_numbers;
  161. $http_status_codes = self::$http_status_codes;
  162. register_shutdown_function('handleShutdown', $error_numbers, $_error_mails, $http_status_codes);
  163. }
  164.  
  165. }


Afterword

Many PHP frameworks provide interfaces for extended error handlers and extended exception handlers, but their contents merely implement similar things.
Recently, when the story of this layer came up, the number of engineers who understood these technologies has become really few.
This is what has been said from around the time the non-Java web application framework came out.
I realize that it has become a reality at many development sites.
A lot of recent engineers use the framework, that is not a means, it has become a purpose.
It can be seen from the fact that the number of engineers who do not know the basis of Web application development has increased.
In this way, the factor that the number of tool engineers has increased.
One of them is the increase in speed development method like Silicon Valley.
It is by no means negative.
In short, if we categorize the category of development engineers more subdivided, the misrecognition of mutual recognition will probably decrease.