I am building an app with Symfony 2, and I am wondering, how could I handle errors when I try to read a index in an array that doesn't exist? Sadly this kind of errors don't throw exceptions, so I can't really use a try-catch block.
Example:
$test = array();
$test["323"]; // Undefined index error!
Please, ideas how to handle this errors?
Update: I have seen many solutions with isset. The problem with this is that I would have to do it with every single access to an array index. Can anyone offer me a more DRY solution?
Both:
if(isset($test["323"])){
//Good
}
and
if(array_key_exists('123', $test)){
//Good
}
Will allow you to check if an array index is defined before attempting to use it. This is not a Symfony-specific error. Its a common PHP warning that occurs whenever you attempt to access an array element that doesn't exist.
$val = isset($test["323"]) ? $test["323"] : null;
An option would be to use set_error_handler() in order to, somehow, simulate exceptions. An example usage would be the following, although I'm sure you can adjust this to your specific use case:
function my_error_handler($errno,$errstr)
{
/* handle the issue */
return true; // if you want to bypass php's default handler
}
$test = array();
set_error_handler('my_error_handler');
$use_it=$test["323"]; // Undefined index error!
restore_error_handler();
You can see that we "wrap" our "critical" piece of code around set_error_handler() and restore_error_handler(). The code in question can be as little as a line, to as large as your whole script. Of course, the larger the critical section, the more "intelligent" the error handler has to be.
use array_key_exists(), like
if (array_key_exists('123', $test)) {
echo "it exists";
}
You can catch this bx surrounding it with isset:
if(isset($test["323"])){
//The value fo this key is set.
}
May be you wanted to achieve this ?
$test = array();
$test[]="323";
echo $test[0];//323
Simple way to try/catch undefined or many php errors.
try{
set_error_handler(function($errno, $errstr, $errfile, $errline, array $errcontext){
if (0 === error_reporting()) { return false; }
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});
$data = $json['name']; ---> Undefined Index name
}catch(Exception $e){
//Perform Furthur action
}
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().
Is it possible to log a stacktrace on warnings?
Here is the way I log all warning and notice errors
function boot_error_handler($errno, $errstr, $errfile, $errline){
switch($errno){
case E_WARNING:
case E_PARSE:
case E_NOTICE:
$message = "$errstr $errfile:$errline";
if(class_exists('Log')){
Log::write($message, 'warning', true);
}
if(ENV != ENV_PROD){
echo $message;
}
break;
}
}
set_error_handler('boot_error_handler');
You can use debug_backtrace() function in you error handler to get current stacktrace. The thing is that it returns array of associative arrays wichi is uncomfortable to use if you just need to log it, because you would need to generate a human-readable string form it.
Another solution would be to create an Exception instance and use its method getTraceAsString().
$exception = new \Exception();
$trace = $exception->getTraceAsString();
So basically it depends if the default exsceptions stacktrace format is enough for you or you want to have soem custom format.
(new \Exception)->getTraceAsString();
is an easy way of generating a nicely formatted trace (PHP 5.4+)
It'll just return a string, so you can either replace or extend your existing log messages with it as appropriate.
Use debug_backtrace() or fire an exception, catch it and get its trace with getTraceAsString():
try {
throw new Exception();
} catch (Exception $e) {
$e->getTraceAsString();
}
So I have a nested php array which I am using using for fetching data:
$this->synthArray[$synthId]['synth_map'][$mapId]['map_sequence'][$gnome];
This line gives me error undefined index error sometimes. I am trying to figure out which index gives that error - I can do isset() on each level to achieve this, but I was wondering if there is an easier way to find the culprit...
Edit
Your array has 3 points of failure...I mean there are 3 variables which can be absent in the array.
$this->synthArray[$synthId]['synth_map'][$mapId]['map_sequence'][$gnome];
You can check this as:
if(isset($this->synthArray[$synthId])) {
if(isset($this->synthArray[$synthId]['synth_map'][$mapId])) {
if(isset($this->synthArray[$synthId]['synth_map'][$mapId]['map_sequence'][$gnome])) {
// it's correct
} else {
//$gnome is an invalid key
}
} else {
// $mapId is an invalid key
}
} else {
// $synthId is an invalid key
}
You can use set_error_handler to register your own error handler.
function errorHandler( $errno, $errstr, $errfile, $errline ) {
// catch the error here and take action
echo "{$errstr} in file {$errfile} on line {$errline}"; // example
/* Don't execute PHP internal error handler */
return true;
}
set_error_handler('errorHandler');
Hope this helps.
PHPs token_get_all function (which allows converting a PHP source code into tokens) can throw two errors: One if an unterminated multiline comment is encountered, the other if an unexpected char is found.
I would like to catch those errors and throw them as Exceptions.
Problem is: As those errors are parse errors they cannot be handled with an error handling function you would normally specify using set_error_handler.
What I have currently implemented is the following:
// Reset the error message in error_get_last()
#$errorGetLastResetUndefinedVariable;
$this->tokens = #token_get_all($code);
$error = error_get_last();
if (preg_match(
'~^(Unterminated comment) starting line ([0-9]+)$~',
$error['message'],
$matches
)
) {
throw new ParseErrorException($matches[1], $matches[2]);
}
if (preg_match(
'~^(Unexpected character in input:\s+\'(.)\' \(ASCII=[0-9]+\))~s',
$error['message'],
$matches
)
) {
throw new ParseErrorException($matches[1]);
}
It should be obvious that I'm not really excited to use that solution. Especially the fact that I reset the error message in error_get_last by accessing an undefined variable seems pretty unsatisfactory.
So: Is there a better solution to this problem?
Set a custom errorhandler using set_error_handler.
Call token_get_all.
Then unset the error handler by calling restore_error_handler.
This will allow you to catch warnings. Make sure you remove the # suppressor.
You can for instance register an error handler that is in a class that will just record any warnings for inspection later on.
Untested example code:
class CatchWarnings {
private $warnings = array();
public function handler($errno, $errstr, $errfile, $errline) {
switch ($errno) {
case E_USER_WARNING:
$this->warnings[] = $errstr;
return true; // cancel error handling bubble
}
return false; // error handling as usual
}
public function has_warnings() {
return count($this->warnings) > 0;
}
}
$cw = new CatchWarnings();
set_error_handler(array($cw, "handler"));
token_get_all();
restore_error_handler();
Usually validation and execution are two separate things, but it seems like there is no way to validate/lint a piece of PHP code (not since 5.x anyway).
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
}