I have this Library to control errors; I would like if you can evaluate it and make a review of the code, not about the goals of the library:
Everything works for me correctly, but I am also open to hear about any type of improvement that can be implemented in the code as long as it does not affect the objective of the library.
Introduction:
What does [BEH] do? [BEH] is a very simple PHP error handler implementation that throws ErrorException exceptions instead of using the default PHP error handling behaviour. This means that all runtime errors including Fatal are presented to the developer in the form of an exception. It also means that any unhandled errors are delivered to a single point: the global exception handler. also includes support for login errors with error document
Why use [BEH]? Developers need the ability to decide how their code behaves when an error occurs. Exceptions offer the only truly consistent way to report and recover from errors in PHP.
This method of handling errors has proven to be extremely effective. Similar strategies are used in major PHP frameworks such as [Laravel]. [BEH] is a standalone implementation that can be used for any project, and not required third party library or software.
Usage: [BEH] Configuration:
- .htaccess: for Apache Version > 2.4.
- .user.ini: for CGI/FPM/FastCGI.
- error_conf.php: common configuration. Note: not tested in NGINX
Custom [BEH] for PHP And Server Error It must be configured manually in: .htaccess, .user.ini and conf.json depending on the scenario or desired host configuration; let's leave some examples:
- Single host server.
- Virtual Host and name of the github project.
- Development with Xampp in Locally or other stack
.htaccess
# php_value auto_prepend_file "/var/www/html/error/init_error.php"
# php_value auto_prepend_file "/var/www/virtual-hostc/git-hub-project-name/error/init_error.php"
php_value auto_prepend_file "C:/xampp/htdocs/error/error_init.php"
Note: because of dependencies you must set the configuration inside <IfModule mod_php7.c> to work correctly; .htaccess file have an example
.user.ini
# auto_prepend_file = "/var/www/html/error/init_error.php"
# auto_prepend_file = "/var/www/virtual-host/git-hub-project-name/error/init_error.php"
auto_prepend_file = "C:/xampp/htdocs/error/error_init.php"
note: .user.ini files not use php_value statement.
Error Server must be setting up with directive in .htaccess file:
ErrorDocument 400 /error/error_server.php
Error_conf.php
skin of [BEH] must be: basic, bs4, custom.
Custom skin [BEH] you must implement two files skin like.
sk_custom_server_error.php
sk_custom_handler_error.php
Content of skin for [BEH] you must add the same variable print like basic skin, but you can use your own html design.
Skin are located in Skin
Project Structure
.htaccess File
<Files .user.ini>
deny from all
</Files>
<Files *error.php>
deny from all
</Files>
<Files server_error.php>
Order Deny,Allow
Allow from all
</Files>
<IfModule mod_php7.c>
#php_value auto_prepend_file "/var/www/html/test1/error/init_error.php"
php_value auto_prepend_file "C:/xampp/htdocs/dev/t2/BEH-Basic-Error-Handler/error/init_error.php"
</IfModule>
ErrorDocument 400 /error/server_error.php
ErrorDocument 401 /error/server_error.php
ErrorDocument 402 /error/server_error.php
ErrorDocument 403 /error/server_error.php
ErrorDocument 404 /error/server_error.php
ErrorDocument 405 /error/server_error.php
ErrorDocument 406 /error/server_error.php
ErrorDocument 407 /error/server_error.php
ErrorDocument 408 /error/server_error.php
ErrorDocument 409 /error/server_error.php
ErrorDocument 410 /error/server_error.php
ErrorDocument 411 /error/server_error.php
ErrorDocument 412 /error/server_error.php
ErrorDocument 413 /error/server_error.php
ErrorDocument 414 /error/server_error.php
ErrorDocument 415 /error/server_error.php
ErrorDocument 500 /error/server_error.php
ErrorDocument 501 /error/server_error.php
ErrorDocument 502 /error/server_error.php
ErrorDocument 503 /error/server_error.php
ErrorDocument 504 /error/server_error.php
ErrorDocument 505 /error/server_error.php
.user.ini File
auto_prepend_file = "/var/www/html/test1/error/init_error.php"
index.php
<?php
ob_start();
# error Example:
require_once 'test.php';
$test = new Test();
$test->testMethod();
echo '<br>No problem Detected.<br>Go to test.php and uncomment examples in test Method';
test.php
<?php
class Test
{
/** @noinspection PhpUnhandledExceptionInspection */
public function testMethod()
{
# TODO Test 1: Uncomment to get an Error Output
echo $foo;
# TODO Test 2: Uncomment to get an throw exception Output
#throw new Exception('This is an Example of throw Exception');
# TODO Test 3: Uncomment to get an shutdown Output
#set_time_limit(1);
#while (true) {
#}
}
}
conf_error.php
<?php
$conf = [
'skin' => 'bs4',
'error' => 'auto',
];
init_error.php
<?php
try {
if (ini_set('session.gc_maxlifetime', '0') === false) {
throw new Exception('Unable to set session.gc_maxlifetime.');
}
if (ini_set('session.use_only_cookies', '1') === false) {
throw new Exception('Unable to set session.use_only_cookies.');
}
if (ini_set('session.cookie_httponly', '1') === false) {
throw new Exception('Unable to set session.cookie_httponly.');
}
if (ini_set('error_reporting', '1') === false) {
throw new Exception('Unable to set error_reporting.');
}
if (ini_set('display_errors', '0') === false) {
throw new Exception('Unable to set display_errors.');
}
} catch (Exception $e) {
echo 'Exception catch: ', $e->getMessage(), "\n";
}
error_reporting(E_ALL);
require_once $_SERVER['DOCUMENT_ROOT'] . '/error/handler_error.php';
server_error.php
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/error/conf_error.php';
$error_text = 'Unknown Error';
$code = $_SERVER['REDIRECT_STATUS'];
$codes = [
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Payload Too Large',
414 => 'URI Too Long',
415 => 'Unsupported Media Type',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
];
$source_url = 'http' . ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') ? 's' : '') . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
if (array_key_exists($code, $codes) && is_numeric($code)) {
$error_text = $codes[$code];
}
/** @var array $conf */
$error_skin = $_SERVER['DOCUMENT_ROOT'] . '/error/skin/sk_' . $conf['skin'] . '_server_error.php';
if (file_exists($error_skin)) {
/** @noinspection PhpIncludeInspection */
require_once $error_skin;
} else {
require_once $_SERVER['DOCUMENT_ROOT'] . '/error/skin/sk_basic_server_error.php';
}
die;
handler_error.php
<?php
declare(strict_types=1);
namespace IcarosNet\BEHBasicErrorHandler;
class HandlerError
{
private static ?HandlerError $instance = null;
public function __construct()
{
register_shutdown_function([$this, "shutdownHandler"]);
set_exception_handler([$this, "exceptionHandler"]);
set_error_handler([$this, "errorHandler"]);
}
public function shutdownHandler()
{
$error = error_get_last();
if (null != $error) {
$this->CleanOutput();
$error_type = 'ShutdownHandler';
$error_level = $error['type'];
$error_desc = $error['message'];
$error_file = $error['file'];
$error_line = $error['line'];
$error_trace = array_reverse(debug_backtrace());
array_pop($error_trace);
$error_trace_smg = $this->getBacktrace($error_trace, $error_type);
$this->output($error_type, $error_level, $error_desc, $error_file, $error_line, $error_trace_smg);
}
}
public function exceptionHandler($e)
{
$this->CleanOutput();
$error_type = 'ExceptionHandler';
$error_level = ($e->getCode() == 0 ? 'Not Set' : $e->getCode());
$error_desc = $e->getMessage();
$error_file = $e->getFile();
$error_line = $e->getLine();
$error_trace = $e->getTrace();
$error_trace_smg = $this->getBacktrace($error_trace, $error_type);
$this->output($error_type, $error_level, $error_desc, $error_file, $error_line, $error_trace_smg);
}
public function errorHandler($error_level = null, $error_desc = null, $error_file = null, $error_line = null)
{
$this->CleanOutput();
$error_type = 'ErrorHandler';
$error_trace = array_reverse(debug_backtrace());
array_pop($error_trace);
$error_trace_smg = $this->getBacktrace($error_trace, $error_type);
$this->output($error_type, $error_level, $error_desc, $error_file, $error_line, $error_trace_smg);
}
private function getBacktrace($error_trace, $error_type)
{
$error_trace_smg = '';
if (!empty($error_trace)) {
foreach ($error_trace as $track) {
$error_trace_smg .= ' ' . (isset($track['file']) ? $track['file'] : '<unknown file>') . ' ' . (isset($track['line']) ? $track['line'] : '<unknown line>') . ' calling Method: ' . $track['function'] . '()' . PHP_EOL;
}
} else {
$error_trace_smg = 'No backtrace data in the ' . $error_type . '.';
}
return $error_trace_smg;
}
/**
* @param $error_type
* @param $error_level
* @param $error_desc
* @param $error_file
* @param $error_line
* @param $error_trace_smg
*/
private function output($error_type, $error_level, $error_desc, $error_file, $error_line, $error_trace_smg)
{
$conf = [];
require_once $_SERVER['DOCUMENT_ROOT'] . '/error/conf_error.php';
$out_type = $this->getRQType($conf['error']);
if ($out_type == 'plain') {
/** @var array $conf */
$error_skin = $_SERVER['DOCUMENT_ROOT'] . '/error/skin/sk_' . $conf['skin'] . '_handler_error.php';
//header('content-type: text/plain');
if (file_exists($error_skin)) {
/** @noinspection PhpIncludeInspection */
require_once $error_skin;
} else {
require_once $_SERVER['DOCUMENT_ROOT'] . '/error/skin/sk_basic_handler_error.php';
}
} else {
header('Content-Type: application/json');
echo json_encode(
[
'error_type' => $error_type,
'error_level' => $error_level,
'error_desc' => $error_desc,
'error_file' => $error_file,
'error_line' => $error_line,
'error_trace_smg' => $error_trace_smg,
]
);
}
$this->clearLastError();
}
private function cleanOutput()
{
ob_end_clean();
flush();
}
private function clearLastError()
{
error_clear_last();
exit();
}
private function getRQType($error)
{
/** @var string $error */
if ($error == 'auto') {
if (isset($_POST) && !empty($_POST)) {
return (count($_POST) > 1 ? 'plain' : ($this->isJson($_GET) ? 'json' : 'plain'));
}
if (isset($_GET) && !empty($_GET)) {
return (count($_GET) > 1 ? 'plain' : ($this->isJson($_GET) ? 'json' : 'plain'));
}
return 'plain';
} else {
return $error;
}
}
private function isJson($value)
{
json_decode(current($value));
return (json_last_error() == JSON_ERROR_NONE);
}
public static function getInstance(): HandlerError
{
if (!self::$instance instanceof self) self::$instance = new self;
return self::$instance;
}
}
HandlerError::getInstance();