I am writing a form validation class and wish to include regular expressions in the validation. Therefore, the regex provided isn't guaranteed to be valid.
How can I (efficiently) check that the regex is valid?
Use the pattern in your preg_* calls. If the function returns false there is likely a problem with your pattern. As far as I know this is the easiest way to check if a regex pattern is valid in PHP.
Here's an example specifying the right kind of boolean check:
$invalidPattern = 'i am not valid regex';
$subject = 'This is some text I am searching in';
if (#preg_match($invalidPattern, $subject) === false) {
// the regex failed and is likely invalid
}
When you have error reporting on, you can't get away with simply testing the boolean result. If the regex fails warnings are thrown (i.e. 'Warning: No ending delimiter xxx found'.)
What I find odd, is that the PHP documentation tells nothing about these thrown warnings.
Below is my solution for this problem, using try, catch.
//Enable all errors to be reported. E_WARNING is what we must catch, but I like to have all errors reported, always.
error_reporting(E_ALL);
ini_set('display_errors', 1);
//My error handler for handling exceptions.
set_error_handler(function($severity, $message, $file, $line)
{
if(!(error_reporting() & $severity))
{
return;
}
throw new ErrorException($message, $severity, $severity, $file, $line);
});
//Very long function name for example purpose.
function checkRegexOkWithoutNoticesOrExceptions($test)
{
try
{
preg_match($test, '');
return true;
}
catch(Exception $e)
{
return false;
}
}
You shouldn't be using # to silence all errors because it also silences fatal errors.
function isRegularExpression($string) {
set_error_handler(function() {}, E_WARNING);
$isRegularExpression = preg_match($string, "") !== FALSE;
restore_error_handler();
return isRegularExpression;
}
This only silences warnings for the preg_match call.
Anyone still looking at this question anno 2018, and is using php 7, should be using try/catch.
try {
preg_match($mypattern, '');
} catch (\Throwable $exception) {
// regex was invalid and more info is in $exception->getMessage()
}
PHP has progressed quite a bit since this question was first asked (and answered). You can now (PHP 5.2+) simply write the following to, not only test if the regular expression is valid, but to get the detailed error message if it's not:
if(#preg_match($pattern, '') === false){
echo error_get_last()["message"];
}
Placed in a function
/**
* Return an error message if the given pattern argument or its underlying regular expression
* are not syntactically valid. Otherwise (if they are valid), NULL is returned.
*
* #param $pattern
*
* #return string|null
*/
function regexHasErrors($pattern): ?string
{
if(#preg_match($pattern, '') === false){
//Silence the error by using a #
return str_replace("preg_match(): ", "", error_get_last()["message"]);
//Make it prettier by removing the function name prefix
}
return NULL;
}
Demo
This is my solution using the upcoming warning if something is wrong with the expression:
function isRegEx($test)
{
$notThisLine = error_get_last();
$notThisLine = isset($notThisLine['line']) ? $notThisLine['line'] + 0 : 0;
while (($lines = rand(1, 100)) == $notThisLine);
eval(
str_repeat("\n", $lines) .
'#preg_match(\'' . addslashes($test) . '\', \'\');'
);
$check = error_get_last();
$check = isset($check['line']) ? $check['line'] + 0 : 0;
return $check == $notThisLine;
}
Related
Came across this today, not sure how to solve it.
I want to catch an E_WARNING level error issued by hex2bin().
I thought I might be able to use a try()catch() exception method but it did not work.
try {
$foo = hex2bin($bar);
}
catch (Exception $e) {
die('Custom message');
}
The standard E_WARNING that is issued is, of course:
Warning: hex2bin(): Input string must be hexadecimal string
In this particular situation I do not want to use the standard methods to disable errors, such as:
error_reporting(-1);
error_reporting(E_ALL);
ini_set("display_errors", 1);
Nor do I want to suppress the error using a #hex2bin method.
I actually want to have a custom error displayed, but I just do not seem to be able to find a way to catch when hex2bin throws an error.
You can check the input value to verify that it is hex before passing it to the hex2bin method.
if (ctype_xdigit($bar) && strlen($bar) % 2 == 0) {
$foo = hex2bin($bar);
} else {
//display error here
}
Per the documentation, hex2bin returns:
the binary representation of the given data or FALSE on failure.
That means you can do something like this if you want to use it in a try/catch block:
try {
$foo = #hex2bin($bar);
if(false === $foo) {
throw new Exception("Invalid hexedecimal value.");
}
} catch(Exception $e) {
echo $e;
}
Yes, hex2bin is suppressed. You can remove the # if you set error_reporting to suppress warnings.
Alternatively, you can check the type of $foo with ctype_xdigit() prior to using hex2bin().
I need to catch some warnings being thrown from some php native functions and then handle them.
Specifically:
array dns_get_record ( string $hostname [, int $type= DNS_ANY [, array &$authns [, array &$addtl ]]] )
It throws a warning when the DNS query fails.
try/catch doesn't work because a warning is not an exception.
I now have 2 options:
set_error_handler seems like overkill because I have to use it to filter every warning in the page (is this true?);
Adjust error reporting/display so these warnings don't get echoed to screen, then check the return value; if it's false, no records is found for hostname.
What's the best practice here?
Set and restore error handler
One possibility is to set your own error handler before the call and restore the previous error handler later with restore_error_handler().
set_error_handler(function() { /* ignore errors */ });
dns_get_record();
restore_error_handler();
You could build on this idea and write a re-usable error handler that logs the errors for you.
set_error_handler([$logger, 'onSilencedError']);
dns_get_record();
restore_error_handler();
Turning errors into exceptions
You can use set_error_handler() and the ErrorException class to turn all php errors into exceptions.
set_error_handler(function($errno, $errstr, $errfile, $errline) {
// error was suppressed with the #-operator
if (0 === error_reporting()) {
return false;
}
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});
try {
dns_get_record();
} catch (ErrorException $e) {
// ...
}
The important thing to note when using your own error handler is that it will bypass the error_reporting setting and pass all errors (notices, warnings, etc.) to your error handler. You can set a second argument on set_error_handler() to define which error types you want to receive, or access the current setting using ... = error_reporting() inside the error handler.
Suppressing the warning
Another possibility is to suppress the call with the # operator and check the return value of dns_get_record() afterwards. But I'd advise against this as errors/warnings are triggered to be handled, not to be suppressed.
The solution that really works turned out to be setting simple error handler with E_WARNING parameter, like so:
set_error_handler("warning_handler", E_WARNING);
dns_get_record(...)
restore_error_handler();
function warning_handler($errno, $errstr) {
// do something
}
Be careful with the # operator - while it suppresses warnings it also suppresses fatal errors. I spent a lot of time debugging a problem in a system where someone had written #mysql_query( '...' ) and the problem was that mysql support was not loaded into PHP and it threw a silent fatal error. It will be safe for those things that are part of the PHP core but please use it with care.
bob#mypc:~$ php -a
Interactive shell
php > echo #something(); // this will just silently die...
No further output - good luck debugging this!
bob#mypc:~$ php -a
Interactive shell
php > echo something(); // lets try it again but don't suppress the error
PHP Fatal error: Call to undefined function something() in php shell code on line 1
PHP Stack trace:
PHP 1. {main}() php shell code:0
bob#mypc:~$
This time we can see why it failed.
I wanted to try/catch a warning, but at the same time keep the usual warning/error logging (e.g. in /var/log/apache2/error.log); for which the handler has to return false. However, since the "throw new..." statement basically interrupts the execution, one then has to do the "wrap in function" trick, also discussed in:
Is there a static way to throw exception in php
Or, in brief:
function throwErrorException($errstr = null,$code = null, $errno = null, $errfile = null, $errline = null) {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
function warning_handler($errno, $errstr, $errfile, $errline, array $errcontext) {
return false && throwErrorException($errstr, 0, $errno, $errfile, $errline);
# error_log("AAA"); # will never run after throw
/* Do execute PHP internal error handler */
# return false; # will never run after throw
}
...
set_error_handler('warning_handler', E_WARNING);
...
try {
mkdir($path, 0777, true);
} catch (Exception $e) {
echo $e->getMessage();
// ...
}
EDIT: after closer inspection, it turns out it doesn't work: the "return false && throwErrorException ..." will, basically, not throw the exception, and just log in the error log; removing the "false &&" part, as in "return throwErrorException ...", will make the exception throwing work, but will then not log in the error_log... I'd still keep this posted, though, as I haven't seen this behavior documented elsewhere.
Combining these lines of code around a file_get_contents() call to an external url helped me handle warnings like "failed to open stream: Connection timed out" much better:
set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line, array $err_context)
{
throw new ErrorException( $err_msg, 0, $err_severity, $err_file, $err_line );
}, E_WARNING);
try {
$iResult = file_get_contents($sUrl);
} catch (Exception $e) {
$this->sErrorMsg = $e->getMessage();
}
restore_error_handler();
This solution works within object context, too. You could use it in a function:
public function myContentGetter($sUrl)
{
... code above ...
return $iResult;
}
Normaly you should never use # unless this is the only solution. In that specific case the function dns_check_record should be use first to know if the record exists.
If dns_get_record() fails, it should return FALSE, so you can suppress the warning with # and then check the return value.
You should probably try to get rid of the warning completely, but if that's not possible, you can prepend the call with # (i.e. #dns_get_record(...)) and then use any information you can get to figure out if the warning happened or not.
try checking whether it returns some boolean value then you can simply put it as a condition. I encountered this with the oci_execute(...) which was returning some violation with my unique keys.
ex.
oci_parse($res, "[oracle pl/sql]");
if(oci_execute){
...do something
}
As of PHP8, you can do the following instead of setting error handlers to catch Errors and Warnings. I Believe in PHP 7.something you could catch some Errors.
try {
call_user_func('sprintf', array_merge([$string], $args));
} catch (Throwable $e) {
$logger->info('mesage...');
}
You should generally be somewhere that you can pass or access a logger if you bulkhead in this way, as it can obfuscate coder errors, such as passing incorrectly typed parameters to a method, and mask a variety of other problems.
https://php.watch/versions/8.0/internal-function-exceptions
Not sure if notices are caught (likely not), but you can likely solve around examples like this one, by thinking a little more about what you are looking to do.
Both builder pattern, and options patterns provide solutions for this where prior to the site of call, which can be a private function or just after validity checks, you can throw a real custom exception that is attributable only to your code. That will make even built-in functions very safe to use.
One other nice practice is to use either debug_backtrace, with DEBUG_BACKTRACE_IGNORE_ARGS or use the getTrace or getTraceAsString methods on the Throwable so that some of the context is preseved.
FolderStructure
index.php //Script File
logs //Folder for log Every warning and Errors
CustomException.php //Custom exception File
CustomException.php
/**
* Custom error handler
*/
function handleError($code, $description, $file = null, $line = null, $context = null) {
$displayErrors = ini_get("display_errors");;
$displayErrors = strtolower($displayErrors);
if (error_reporting() === 0 || $displayErrors === "on") {
return false;
}
list($error, $log) = mapErrorCode($code);
$data = array(
'timestamp' => date("Y-m-d H:i:s:u", time()),
'level' => $log,
'code' => $code,
'type' => $error,
'description' => $description,
'file' => $file,
'line' => $line,
'context' => $context,
'path' => $file,
'message' => $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']'
);
$data = array_map('htmlentities',$data);
return fileLog(json_encode($data));
}
/**
* This method is used to write data in file
* #param mixed $logData
* #param string $fileName
* #return boolean
*/
function fileLog($logData, $fileName = ERROR_LOG_FILE) {
$fh = fopen($fileName, 'a+');
if (is_array($logData)) {
$logData = print_r($logData, 1);
}
$status = fwrite($fh, $logData . "\n");
fclose($fh);
// $file = file_get_contents($filename);
// $content = '[' . $file .']';
// file_put_contents($content);
return ($status) ? true : false;
}
/**
* Map an error code into an Error word, and log location.
*
* #param int $code Error code to map
* #return array Array of error word, and log location.
*/
function mapErrorCode($code) {
$error = $log = null;
switch ($code) {
case E_PARSE:
case E_ERROR:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
$error = 'Fatal Error';
$log = LOG_ERR;
break;
case E_WARNING:
case E_USER_WARNING:
case E_COMPILE_WARNING:
case E_RECOVERABLE_ERROR:
$error = 'Warning';
$log = LOG_WARNING;
break;
case E_NOTICE:
case E_USER_NOTICE:
$error = 'Notice';
$log = LOG_NOTICE;
break;
case E_STRICT:
$error = 'Strict';
$log = LOG_NOTICE;
break;
case E_DEPRECATED:
case E_USER_DEPRECATED:
$error = 'Deprecated';
$log = LOG_NOTICE;
break;
default :
break;
}
return array($error, $log);
}
//calling custom error handler
set_error_handler("handleError");
just include above file into your script file like this
index.php
error_reporting(E_ALL);
ini_set('display_errors', 'off');
define('ERROR_LOG_FILE', 'logs/app_errors.log');
include_once 'CustomException.php';
echo $a; // here undefined variable warning will be logged into logs/app_errors.log
Since PHP7 you can catch most errors and warnings like so:
try {
whatever();
} catch (Throwable $e) {
}
More: https://www.php.net/manual/en/language.errors.php7.php
I would only recommend using # to suppress warnings when it's a straight forward operation (e.g. $prop = #($high/($width - $depth)); to skip division by zero warnings). However in most cases it's better to handle.
I'm creating functions where I want to be able to output true or false pertaining to if statements within the function are met. An example of this is like this:
function pokeme($number){
if($number > 10)
{
return true;
}
else
{
return false;
}
}
However, one problem with this approach is that if the function returns false, you wont be able to decipher which false means what, especially if there is more than one if/else statements.
My question is, how do I output a false plus a way to later identify what that false was?
Do I do an array?
return array("false", "message pertaining to whatever");
However, if that is done, you can't really do this, plus...:
if(pokeme()){ /*success*/ } else { /*there may be multiple falses for different situations... how do I distinguish what it is? */}
Note that the way the idea is demonstrated here might not be the best, but once you get te hang of it, it gets easier. Also, read end note, please.
If you want to use like this (true is expected and false is problem):
if(pokeme()){ /*success*/ } else { /* not needed */}
You can do something like this:
function pokeme($number){
//let's say you want to return true if it's >10 and -9 to -1
if($number > 10){
// do something
return true;
}
if($number < 0 && $number > -10){
return true;
}
// handling multiple problems (just 2 ^^)
if($number < -9){
throw new Exception("Invalid input. Can't use negative smaller than -9.");
}
throw new Exception('Invalid input. Expected bigger than 10.');
}
Two tests:
try{
echo "RESULT1 :".pokeme(-42).":"; // not shown (error in this line)
echo "RESULT2 :".pokeme(11).":"; // not shown
}catch(Exception $e){
echo "Error: '".$e->getMessage()."'"; // Just the message
}
echo "<br><br>";
try{
echo "RESULT3 :".pokeme(11).":<br>"; // shown
echo "RESULT4 :".pokeme(10).":"; // not shown (error in this line)
}catch(Exception $e){
echo $e; // Full error
}
You can use it like this:
try{
if(pokeme(11)){
echo "VALID INPUT<br>";
}
if(pokeme(5)){
echo "I'm not seen :\\";
}
}catch(Exception $e){
echo "Error: '".$e->getMessage()."'";
}
End note: Think of this like you are using a built-in php function that might cause an error. This way you have to handle it with a try..catch.
More about Exceptions.
For fully automated input validations you can make use of the Symfony form component, and its Validation.
You can also add very simple constraints like LessThan(10) exactly like in your example, and the component automatically writes the appropriate error message back to your page (and the invalid form e.g. dont performs DB inserting).
It exists a lot of prepared constraints to use, you can also create own ones.
Or if you want to write all by your self, i suggest you to read OOP and Exception handling before.
EDIT
If you want to "collect" errors with its messages, solving this procedural for your code example (not recommended) you can store this messages in a temporary array in the superglobal variable $_SESSION. I explicitly say superglobal variable. Dont use a global variable and inject it with the global key, this will become very complex code in long therm.
Anyway my idea using $_SESSION. This code works for me:
<?php
session_start();
// Resetting the tmp session array on every page reload. That previous error messages are resetted.
$_SESSION['errors'] = array();
function printSessionErrors()
{
// Print errors stored in the session if some exists
if (array_key_exists('errors', $_SESSION)) {
foreach ($_SESSION['errors'] as $i => $error) {
$success = true === $error['success'] ? 'true' : 'false'; // Only for print
$message = $error['message'];
echo "Success: $success. Message: $message <br>";
}
}
}
function pokeme($number)
{
$expected = 10;
$success = null;
if ($number > $expected) {
$success = true;
} else {
$_SESSION['errors'][] = array(
'success' => $success,
'message' => "Number $number is less than $expected"
);
$success = false;
}
return $success;
}
pokeme(1);
pokeme(7);
pokeme(99);
printSessionErrors();
Now depending on if it was a form POST or procedural validation you add printCleanSessionErrors() on top (after session_start()) or on bottom on the code.
I get this output:
Success: false. Message: Number 1 is less than 10
Success: false. Message: Number 7 is less than 10
You only have to add the $_SESSION['errors'][] = array .... into your other error situations.
That's one of those questions, which, being very poorly phrased, lead to completely wrong answers.
Before asking such a question, one has to realize that there are two kinds of functions:
functions that perform some action
functions that do nothing but just verify the parameter, telling whether it's right or wrong
And the approach for these two kinds must be completely different.
In case pokeme() function has to use the $number parameter somehow, but for some reason cannot do that, then an exception must be thrown. Because it's the very purpose for exceptions: to handle the exceptional behavior. In this case, the function will never returns false, so it makes no sense to return true either. So it will be either the correct result or exception will be thrown. But it is important to understand that as a rule, exceptions shouldn't be caught on the spot. Least an exception has to be used to convey some message from a function.
An exception should be used only if a function is unable to do its job:
function divide_by($number, $divisor)
{
if (!is_numeric($number) || !is_numeric($divisor)) {
throw new InvalidArgumentException("The arguments must be nubmers");
}
if ($divisor === 0) {
throw new InvalidArgumentException("Cannot divide by zero");
}
return $number / $divisor;
}
In case pokeme() function, as it shown in the example provided, has to test the $number somehow, and tell if it's all right or not, no exceptions have to be used. Because wrong value is not an exceptional case.
Here, the simplest approach to convey the message would be to reverse the function's "polarity" and make it return false in case the parameter is all right, and a true-ish value in case it's wrong. Which will allow us to return the very message we need.
function check_number($number)
{
if(!is_int($number))
{
return "The value provided is not numeric";
}
if($number < 10)
{
return "The number is less than 10";
}
return false;
}
and then it can be used like this
$error = check_number($number);
if(!$error) {
/*success*/
} else {
$errors[] = $error;
}
But this approach is rather silly. A more robust solution would be to write a class, utilizing such a handy feature as class variables
class NumberValidator
{
protected $error;
public function validate()
{
if(!is_numeric($number))
{
$this->error = "The value provided is not numeric";
return false;
}
if($number < 10)
{
$this->error = "The number is less than 10";
return false;
}
return true;
}
public function getError()
{
return $this->error;
}
}
and then it can be used like this
$validator = new NumberValidator();
if($validator->validate($number)) {
/*success*/
} else {
$errors[] = $validator->getError();
}
Which would you recommend?
Return an error code, such as E_USER_ERROR from a function, and determine proper message higher up:
function currentScriptFilename()
{
if(!isset($_SERVER['SCRIPT_FILENAME']))
{
//This?
return E_USER_ERROR;
}
else
{
$url = $_SERVER['SCRIPT_FILENAME'];
$exploded = explode('/', $url);
return end($exploded);
}
}
Execute trigger_error() from the function, with a specific error message:
function currentScriptFilename()
{
if(!isset($_SERVER['SCRIPT_FILENAME']))
{
//Or this?
trigger_error('$_SERVER[\'SCRIPT_FILENAME\'] is not set.', E_USER_ERROR);
}
else
{
$url = $_SERVER['SCRIPT_FILENAME'];
$exploded = explode('/', $url);
return end($exploded);
}
}
I am not sure if I will regret having put a bunch of error messages in my functions further down the line, since I would like to use them for other projects.
Or, would you recommend something totally different?
Do not mix the matters.
Error notification and error handling are different tasks.
You have to use both methods simultaneously.
If you think that $_SERVER['SCRIPT_FILENAME'] availability is worth an error message, you can use trigger error. However PHP itself will throw a notice if you won't check it.
If you want to handle this error, just check this function's return value.
But I would not create a special function for this task.
So,
if (!$filename = basename($_SERVER['SCRIPT_FILENAME']) {
// do whatever you want to handle this error.
}
would be enough
Exceptions could be useful to handle errors, to know if we had any errors occurred.
A simple example:
try {
$filename = basename($_SERVER['SCRIPT_FILENAME'])
if (!$filename) throw new Exception("no filename");
$data = get_some_data_from_db() or throw new Exception("no data");
$template = new Template();
//Exception could be thrown inside of Template class as well.
}
catch (Exception $e) {
//if we had any errors
show_error_page();
}
$template->show();
3.Use exceptions.
If this is the route you are going, I'd rather recommend throwing Exceptions rather then returing an E_ERROR (E_USER_ERROR should be used), as this is just an integer, and possibly a totally valid return for your function.
Advantages:
- Throwing of an Exception cannot be interpreted as anything else then an error by mistake.
- You keep the possibility to add a descriptive error message, even though you don't handle the error at that point/
- You keep a backtrace in your Exception.
- You can catch specific exceptions at specific points, making the decision where in your project a specific type of error should be handled a lot easier.
If not using exceptions which you should be, use trigger_error().
If it is an error you'd like to deal with, try returning FALSE like a lot of the in built functions do.
If you do use exceptions, catch them like this
try {
whatever()
} catch (Exception $e) {
// handle however
}
I need to catch some warnings being thrown from some php native functions and then handle them.
Specifically:
array dns_get_record ( string $hostname [, int $type= DNS_ANY [, array &$authns [, array &$addtl ]]] )
It throws a warning when the DNS query fails.
try/catch doesn't work because a warning is not an exception.
I now have 2 options:
set_error_handler seems like overkill because I have to use it to filter every warning in the page (is this true?);
Adjust error reporting/display so these warnings don't get echoed to screen, then check the return value; if it's false, no records is found for hostname.
What's the best practice here?
Set and restore error handler
One possibility is to set your own error handler before the call and restore the previous error handler later with restore_error_handler().
set_error_handler(function() { /* ignore errors */ });
dns_get_record();
restore_error_handler();
You could build on this idea and write a re-usable error handler that logs the errors for you.
set_error_handler([$logger, 'onSilencedError']);
dns_get_record();
restore_error_handler();
Turning errors into exceptions
You can use set_error_handler() and the ErrorException class to turn all php errors into exceptions.
set_error_handler(function($errno, $errstr, $errfile, $errline) {
// error was suppressed with the #-operator
if (0 === error_reporting()) {
return false;
}
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});
try {
dns_get_record();
} catch (ErrorException $e) {
// ...
}
The important thing to note when using your own error handler is that it will bypass the error_reporting setting and pass all errors (notices, warnings, etc.) to your error handler. You can set a second argument on set_error_handler() to define which error types you want to receive, or access the current setting using ... = error_reporting() inside the error handler.
Suppressing the warning
Another possibility is to suppress the call with the # operator and check the return value of dns_get_record() afterwards. But I'd advise against this as errors/warnings are triggered to be handled, not to be suppressed.
The solution that really works turned out to be setting simple error handler with E_WARNING parameter, like so:
set_error_handler("warning_handler", E_WARNING);
dns_get_record(...)
restore_error_handler();
function warning_handler($errno, $errstr) {
// do something
}
Be careful with the # operator - while it suppresses warnings it also suppresses fatal errors. I spent a lot of time debugging a problem in a system where someone had written #mysql_query( '...' ) and the problem was that mysql support was not loaded into PHP and it threw a silent fatal error. It will be safe for those things that are part of the PHP core but please use it with care.
bob#mypc:~$ php -a
Interactive shell
php > echo #something(); // this will just silently die...
No further output - good luck debugging this!
bob#mypc:~$ php -a
Interactive shell
php > echo something(); // lets try it again but don't suppress the error
PHP Fatal error: Call to undefined function something() in php shell code on line 1
PHP Stack trace:
PHP 1. {main}() php shell code:0
bob#mypc:~$
This time we can see why it failed.
I wanted to try/catch a warning, but at the same time keep the usual warning/error logging (e.g. in /var/log/apache2/error.log); for which the handler has to return false. However, since the "throw new..." statement basically interrupts the execution, one then has to do the "wrap in function" trick, also discussed in:
Is there a static way to throw exception in php
Or, in brief:
function throwErrorException($errstr = null,$code = null, $errno = null, $errfile = null, $errline = null) {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
function warning_handler($errno, $errstr, $errfile, $errline, array $errcontext) {
return false && throwErrorException($errstr, 0, $errno, $errfile, $errline);
# error_log("AAA"); # will never run after throw
/* Do execute PHP internal error handler */
# return false; # will never run after throw
}
...
set_error_handler('warning_handler', E_WARNING);
...
try {
mkdir($path, 0777, true);
} catch (Exception $e) {
echo $e->getMessage();
// ...
}
EDIT: after closer inspection, it turns out it doesn't work: the "return false && throwErrorException ..." will, basically, not throw the exception, and just log in the error log; removing the "false &&" part, as in "return throwErrorException ...", will make the exception throwing work, but will then not log in the error_log... I'd still keep this posted, though, as I haven't seen this behavior documented elsewhere.
Combining these lines of code around a file_get_contents() call to an external url helped me handle warnings like "failed to open stream: Connection timed out" much better:
set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line, array $err_context)
{
throw new ErrorException( $err_msg, 0, $err_severity, $err_file, $err_line );
}, E_WARNING);
try {
$iResult = file_get_contents($sUrl);
} catch (Exception $e) {
$this->sErrorMsg = $e->getMessage();
}
restore_error_handler();
This solution works within object context, too. You could use it in a function:
public function myContentGetter($sUrl)
{
... code above ...
return $iResult;
}
Normaly you should never use # unless this is the only solution. In that specific case the function dns_check_record should be use first to know if the record exists.
If dns_get_record() fails, it should return FALSE, so you can suppress the warning with # and then check the return value.
You should probably try to get rid of the warning completely, but if that's not possible, you can prepend the call with # (i.e. #dns_get_record(...)) and then use any information you can get to figure out if the warning happened or not.
try checking whether it returns some boolean value then you can simply put it as a condition. I encountered this with the oci_execute(...) which was returning some violation with my unique keys.
ex.
oci_parse($res, "[oracle pl/sql]");
if(oci_execute){
...do something
}
As of PHP8, you can do the following instead of setting error handlers to catch Errors and Warnings. I Believe in PHP 7.something you could catch some Errors.
try {
call_user_func('sprintf', array_merge([$string], $args));
} catch (Throwable $e) {
$logger->info('mesage...');
}
You should generally be somewhere that you can pass or access a logger if you bulkhead in this way, as it can obfuscate coder errors, such as passing incorrectly typed parameters to a method, and mask a variety of other problems.
https://php.watch/versions/8.0/internal-function-exceptions
Not sure if notices are caught (likely not), but you can likely solve around examples like this one, by thinking a little more about what you are looking to do.
Both builder pattern, and options patterns provide solutions for this where prior to the site of call, which can be a private function or just after validity checks, you can throw a real custom exception that is attributable only to your code. That will make even built-in functions very safe to use.
One other nice practice is to use either debug_backtrace, with DEBUG_BACKTRACE_IGNORE_ARGS or use the getTrace or getTraceAsString methods on the Throwable so that some of the context is preseved.
FolderStructure
index.php //Script File
logs //Folder for log Every warning and Errors
CustomException.php //Custom exception File
CustomException.php
/**
* Custom error handler
*/
function handleError($code, $description, $file = null, $line = null, $context = null) {
$displayErrors = ini_get("display_errors");;
$displayErrors = strtolower($displayErrors);
if (error_reporting() === 0 || $displayErrors === "on") {
return false;
}
list($error, $log) = mapErrorCode($code);
$data = array(
'timestamp' => date("Y-m-d H:i:s:u", time()),
'level' => $log,
'code' => $code,
'type' => $error,
'description' => $description,
'file' => $file,
'line' => $line,
'context' => $context,
'path' => $file,
'message' => $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']'
);
$data = array_map('htmlentities',$data);
return fileLog(json_encode($data));
}
/**
* This method is used to write data in file
* #param mixed $logData
* #param string $fileName
* #return boolean
*/
function fileLog($logData, $fileName = ERROR_LOG_FILE) {
$fh = fopen($fileName, 'a+');
if (is_array($logData)) {
$logData = print_r($logData, 1);
}
$status = fwrite($fh, $logData . "\n");
fclose($fh);
// $file = file_get_contents($filename);
// $content = '[' . $file .']';
// file_put_contents($content);
return ($status) ? true : false;
}
/**
* Map an error code into an Error word, and log location.
*
* #param int $code Error code to map
* #return array Array of error word, and log location.
*/
function mapErrorCode($code) {
$error = $log = null;
switch ($code) {
case E_PARSE:
case E_ERROR:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
$error = 'Fatal Error';
$log = LOG_ERR;
break;
case E_WARNING:
case E_USER_WARNING:
case E_COMPILE_WARNING:
case E_RECOVERABLE_ERROR:
$error = 'Warning';
$log = LOG_WARNING;
break;
case E_NOTICE:
case E_USER_NOTICE:
$error = 'Notice';
$log = LOG_NOTICE;
break;
case E_STRICT:
$error = 'Strict';
$log = LOG_NOTICE;
break;
case E_DEPRECATED:
case E_USER_DEPRECATED:
$error = 'Deprecated';
$log = LOG_NOTICE;
break;
default :
break;
}
return array($error, $log);
}
//calling custom error handler
set_error_handler("handleError");
just include above file into your script file like this
index.php
error_reporting(E_ALL);
ini_set('display_errors', 'off');
define('ERROR_LOG_FILE', 'logs/app_errors.log');
include_once 'CustomException.php';
echo $a; // here undefined variable warning will be logged into logs/app_errors.log
Since PHP7 you can catch most errors and warnings like so:
try {
whatever();
} catch (Throwable $e) {
}
More: https://www.php.net/manual/en/language.errors.php7.php
I would only recommend using # to suppress warnings when it's a straight forward operation (e.g. $prop = #($high/($width - $depth)); to skip division by zero warnings). However in most cases it's better to handle.