Fix PHP Warnings / Errors - php

Sometimes my scripts produce an error, mostly a Warning.
I have sometimes an idea why this happens, but I have also no clue sometimes why it happens.
Now my question: Is it possible that if a warning gets showed, I can see for what was in the variable?
"Warning: Invalid argument supplied for foreach() in ....";
I get this message but no clue, what was in the variable.
The problem is, it's a script running few hours, with different data, so it's hard to reproduce it. Because, I don't know what was in the variable.
I need this for all kind of Warnings / Errors / Notice / Fatal Error etc.
Thanks for the help.
P.S.

you have a full chapter in php dedicated to errors: http://www.php.net/manual/en/book.errorfunc.php
From the php manual: http://www.php.net/manual/en/function.set-error-handler.php
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
if (!(error_reporting() & $errno)) {
// This error code is not included in error_reporting
return;
}
switch ($errno) {
case E_USER_ERROR:
echo "<b>My ERROR</b> [$errno] $errstr<br />\n";
echo " Fatal error on line $errline in file $errfile";
echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
echo "Aborting...<br />\n";
exit(1);
break;
case E_USER_WARNING:
echo "<b>My WARNING</b> [$errno] $errstr<br />\n";
break;
case E_USER_NOTICE:
echo "<b>My NOTICE</b> [$errno] $errstr<br />\n";
break;
default:
echo "Unknown error type: [$errno] $errstr<br />\n";
break;
}
/* Don't execute PHP internal error handler */
return true;
}

There are a lot of warnings/errors, usually you should just apply logic.
Warning: Invalid argument supplied for foreach() in ....
This means that some variable in the foreach is invalid:
foreach ($array as $value)
or
foreach ($array as $key => $value)
If your script does not match that exact syntax, or if $array is not an actual array, an error would be triggered.

It's not easy to design a debbuging workaround for long-time usage with logging. Basically in this case you would need to adapt the specific foreach with:
foreach (must_be_array($var) as $whatever) ...
Then define that assertion function:
function must_be_array($var) {
if (is_array($var)) return $var;
print_r(array_slice(debug_backtrace(), 1));
// return array(); // to remove now redundant warning
}
The debug_backtrace gives additional context information (called functions and parameters might be helpful). But it won't tell you what should have been in the array variable. It's still going to be an empty variable.

Take a look at set_error_handler() and debug_backtrace().
Also, if there is a specific line/foreach loop that regularly produces this error, add some code something like this on the line before it...
if (!is_array($shouldBeAnArry)) {
// log something here, including data about the variable
// from var_dump(), debug_backtrace() etc etc
} else {
// do the loop
}
This is true of all errors/warnings etc - if an error pops up, add some validation to counter the cause of the error on the line(s) before.

Related

XAMPP is able to modify headers after printing HTTP

I have XAMPP 5.6.3 on my development environment. I have a problem that my development environment is too tolerant. The problem is that if I run this on my development environment
echo "1";
header("Location: /homepage.php");
it will redirect to homepage without problem.
But if I do the same on my production environment, it would give the common error of (Cannot modify header information).
What I want is to receive the same fatal error on my development environment.
Thanks in advance!
You need to turn on error reporting to receive the error. You can do it from code or by modifying php.ini
From code
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
Or find php.ini file in xampp/php directory and modify these line
display_errors=On
error_reporting=E_ALL & ~E_DEPRECATED & ~E_STRICT
You can choose any other Error Level Constant
You can also check this answer How do I get PHP errors to display?
Edit: You can use set_error_handler function to modify default error behavior. Here is the documentation for this function https://www.php.net/manual/en/function.set-error-handler.php
Here is a example code to make warning fatal
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
if (!(error_reporting() & $errno)) {
// This error code is not included in error_reporting, so let it fall
// through to the standard PHP error handler
return false;
}
switch ($errno) {
case E_WARNING:
case E_USER_ERROR:
echo "<b>My ERROR</b> [$errno] $errstr<br />\n";
echo " Fatal error on line $errline in file $errfile";
echo ", PHP ".PHP_VERSION." (".PHP_OS.")<br />\n";
echo "Aborting...<br />\n";
exit(1);
break;
case E_USER_WARNING:
echo "<b>My WARNING</b> [$errno] $errstr<br />\n";
break;
case E_USER_NOTICE:
echo "<b>My NOTICE</b> [$errno] $errstr<br />\n";
break;
default:
echo "Unknown error type: [$errno] $errstr<br />\n";
break;
}
/* Don't execute PHP internal error handler */
return true;
}
error_reporting(E_ALL);
set_error_handler("myErrorHandler");

Error log and display settings without ini_set()?

I'm trying to get error logging working so that errors don't show on my live website to people other than me (identified by my IP address), but it's not working because apparently ini_set() is disabled for my server due to "security reasons." Here's the code I was trying to use:
error_reporting(E_ALL);
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/path/to/php-error.log');
I don't want to hard code the option to disable showing errors, because I do a check in my PHP pages to see if it's my IP address visiting the website and if so still show the errors.
Is there any way to log errors, dynamically changing whether they are displayed on the page or not, without using ini_set()?
You should take a look at PHP's in-built custom error handling functions, which you could build your verification into. This replaces PHP's error handler with a custom function.
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
$ip = $_SERVER['REMOTE_ADDR'];
if ( $ip != '127.0.0.1' ) return true; // Leave if not your IP
switch ($errno) {
case E_USER_ERROR:
echo "<b>My ERROR</b> [$errno] $errstr<br />\n";
echo " Fatal error on line $errline in file $errfile";
echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
echo "Aborting...<br />\n";
exit(1);
break;
case E_USER_WARNING:
echo "<b>My WARNING</b> [$errno] $errstr<br />\n";
break;
case E_USER_NOTICE:
echo "<b>My NOTICE</b> [$errno] $errstr<br />\n";
break;
default:
echo "Unknown error type: [$errno] $errstr<br />\n";
break;
}
/* Don't execute PHP internal error handler */
return true;
}
set_error_handler("myErrorHandler");
More info from the manual at: http://www.php.net/manual/en/function.set-error-handler.php

PHP Notice with page URL

I have received many PHP Notice in log, but i want to know what page URL where happened Notice, how can i log this info?
[29-Nov-2012 13:58:29] PHP Notice: Array to string conversion in /usr/home/sdf/data/www/sdfsdf.com/core/test.php on line 156
I want to log any info, when get NOTICE, how to log it?
Try to correlate the timestamps in the php error log with your web server access log.
Or, you could tail -f the log and trigger random pages.
Or, set a custom error handler to log all sorts of data.
http://php.net/manual/en/function.set-error-handler.php
function myErrorHandler( $errno, $errstr, $errfile, $errline ){
if (!(error_reporting() & $errno)) {
// This error code is not included in error_reporting
return;
}
switch ($errno) {
case E_USER_ERROR:
echo "<b>My ERROR</b> [$errno] $errstr<br />\n";
echo " Fatal error on line $errline in file $errfile";
echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
echo "Aborting...<br />\n";
exit(1);
break;
case E_USER_WARNING:
echo "<b>My WARNING</b> [$errno] $errstr<br />\n";
break;
case E_USER_NOTICE:
echo "<b>My NOTICE</b> [$errno] $errstr<br />\n";
break;
default:
echo "Unknown error type: [$errno] $errstr<br />\n";
break;
}
/* Don't execute PHP internal error handler */
return true;
}
set_error_handler( "myErrorHandler" );
What you could do is to compare you php notice times to your webservers access log in order to find out the URL which caused that notice.
The notice actually does not look like URL dependent.
It indicates, that you're trying to cast a "type" array to a string which happens when you do something like this:
$array = array( 'foo', 'bar' );
echo $array;
$string = 'test' . $array;
printf ( 'foo %s', $array );
To find this error in particular, you could do this to figure out what's wrong (including printing a backtrace using debug_backtrace):
if (is_string($var)) {
//Then it's OK - do whatever you were doing on line 156
} else {
//Something's wrong! Let's log it to some file!
ob_start();
var_dump($var);
print_r(debug_backtrace());
echo "\n----------------------------------------------\n";
$debugContent = ob_get_clean();
$logHandle = fopen("wrong_var_type.log", "a");
if ($logHandle !== false) {
fwrite($logHandle, $debugContent . "\n");
}
#fclose($logHandle);
}
Alternatively, use a logger:
https://stackoverflow.com/a/10800296/247893
You need to set up something to capture error/debug info as it happens, but not show it to the entire world. Not sure if you have session management built in to this application, but you may want to add some basic controls for this exercise.
Sometimes errors are harder to track down if you use functions that are included on multiple pages. The error may appear to happen on the parent page, but is actually triggered in the function, which is included on another page. The error line numbers can be misleading in this case.
If you have intermittent errors that you aren't able to immediately isolate, it may help to get some feedback on what's happening in your script(s). Here's a rough example of how to do some basic debugging in functions:
function get_func_argNames($funcName)
{
$f = new ReflectionFunction($funcName);
$result = array();
foreach ($f->getParameters() as $param)
{
$result[] = $param->name;
}
return $result;
}
function myCoolFunction($arg1, $arg2, $arg3)
{
$debug = false;
$php_function_args = implode(', ',get_func_argNames(__FUNCTION__));
$_debug_txt = "<b><span style='color:blue;'>function</span> <span style='color:darkblue;'>" .__FUNCTION__. "</span></b>($php_function_args)";
if ($debug)
{
EmailAppDev($_debug_txt);
}
// myCoolFunction
$x = $arg1 + $arg2 + $arg3;
return $x
}
Ideally you'll have a session account that can control who $debug is enabled for.
If you aren't using functions, you'll need to set up something similar in strategic areas of your scripts, to find out when and where things are going sour.
Without having your entire app to look at it's pretty hard to give specifics.

php - How to catch an unexpected error?

I'm writing a script, where a lot of things could go wrong. I'm making if/else statements for the obvious things, that could heppen, but is there a way to catch something, that could possible heppen, but I don't know what it is yet?
For example something causes an error of some kind, in the middle of the script. I want to inform the user, that something has gone wrong, but without dozens of php warning scripts.
I would need something like
-- start listening && stop error reporting --
the script
-- end listening --
if(something went wrong)
$alert = 'Oops, something went wrong.';
else
$confirm = 'Everything is fine.'
Thanks.
Why not try...catch?
$has_errors = false;
try {
// code here
} catch (exception $e) {
// handle exception, or save it for later
$has_errors = true;
}
if ($has_errors!==false)
print 'This did not work';
Edit:
Here is a sample for set_error_handler, which will take care of any error that happens outside the context of a try...catch block. This will also handle notices, if PHP is configured to show notices.
based on code from: http://php.net/manual/en/function.set-error-handler.php
set_error_handler('genericErrorHandler');
function genericErrorHandler($errno, $errstr, $errfile, $errline) {
if (!(error_reporting() & $errno)) {
// This error code is not included in error_reporting
return;
}
switch ($errno) {
case E_USER_ERROR:
echo "<b>My ERROR</b> [$errno] $errstr<br />\n";
echo " Fatal error on line $errline in file $errfile";
echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
echo "Aborting...<br />\n";
exit(1);
break;
case E_USER_WARNING:
echo "<b>My WARNING</b> [$errno] $errstr<br />\n";
break;
case E_USER_NOTICE:
echo "<b>My NOTICE</b> [$errno] $errstr<br />\n";
break;
default:
echo "Unknown error type: [$errno] $errstr<br />\n";
break;
}
/* Don't execute PHP internal error handler */
return true;
}
$v = 10 / 0 ;
die('here');
Read up on Exceptions:
try {
// a bunch of stuff
// more stuff
// some more stuff
} catch (Exception $e) {
// something went wrong
}
throw new Exception('Division by zero.');
try {
echo inverse(5) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
http://php.net/manual/en/language.exceptions.php
You should definitely use the try-catch syntax to catch any exception thrown by your script.
Additionally you can extend exceptions and implement new ones that fulfill your needs.This way, you can throw your own exceptions when you find any other kind of unexpected error (error for your script's logic).
A very short example explaining the use of extending exceptions :
//your own exception class
class limitExceededException extends Exception { ... }
try{
// your script here
if($limit > 10)
throw new limitExceededException();
}catch(limitExceededException $e){//catching only your limit exceeded exception
echo "limit exceeded! cause : ".$e->getMessage();
}catch(Exception $e){//catching all other exceptions
echo "unidentified exception : ".$e->getMessage();
}
Besides using try/catch, I think it's important to consider if you should catch an unexpected error. If it's unexpected then your code has no idea how to handle it and allowing the application to continue may produce bad data or other incorrect results. It may be better to just let it crash to an error page. I just recently had a problem where someone had added generic exception handlers to everything, and it hid the original location of the exception making the bug difficult to find.

How to display variables on error?

I am getting occasional Undefined offset issues when parsing a large file inside PHP.
How can I display the variables ONLY when the error occurs, to see what the issue is?
The error is occuring at this point of my php
list($export_date, $application_id, $language_code, $title, $description, $release_notes, $company_url, $suppport_url, $screenshot_url_1, $screenshot_url_2, $screenshot_url_3, $screenshot_url_4, $screenshot_width_height_1, $screenshot_width_height_2, $screenshot_width_height_3, $screenshot_width_height_4,$ipadscreenshot_url_1, $ipadscreenshot_url_2, $ipadscreenshot_url_3, $ipadscreenshot_url_4, $ipadscreenshot_width_height_1, $ipadscreenshot_width_height_2, $ipadscreenshot_width_height_3, $ipadscreenshot_width_height_4 ) = explode($delimiter, $line);
So I would like to echo all the variables on the offset error, if no error, just move onto the next record without doing anything.
the error will occur if explode($delimiter, $line) didn't return as much parameters as the list statement requires, you can check if that's the case like this:
$parts = explode($delimiter, $line);
if(count($parts)!=20) { //say your list needs 20 elements
echo '20 parts expected, got '. count($parts). '<br />';//consider using error_log than echoing data
var_dump($line, $parts);
} else {
list($export_date, $application_id, $language_code /*fill others*/) = $parts;
}
Did you try set_error_handler? It allows you to write a function that will be executed when an error or warning occurs. The errcontext parameter contains all the variables. You could for example log $line when it happens and then continue to the next line. See the link for examples.
#aularon's solution is a great quick solution but if you are looking for a long-term fix, try setting the error handler. I'm guessing you aren't really getting an error, but rather a warning. You could do something like this:
(see: http://us2.php.net/manual/en/function.set-error-handler.php)
function myErrorHandler($errno, $errstr, $errfile, $errline, $symbols)
{
if (!(error_reporting() & $errno)) {
// This error code is not included in error_reporting
return;
}
switch ($errno) {
case E_USER_WARNING:
echo "<b>My WARNING</b> [$errno] $errstr<br />\n";
vardump($symbols['line']); // untested but this should give you the line as set when the error was raised
break; // maybe die or exit here instead
}
/* Don't execute PHP internal error handler */
return true;
}
You may want to set_error_handler() right before you start your loop, then restore_error_handler() right after it so you don't end up with a fuzzy error handler for the entire app or script.
I have two approaches for you:
First:
You could store these values in a temporary array and count the items, if there are less than 24, something went wrong!
$tmp_arr = explode($delimiter, $line);
if(count($tmp_arr) < 24) {
print_r($tmp_arr); // gives you a nice output
}
else
{
list($export_date, $application_id, $language_code, $title, $description, $release_notes, $company_url, $suppport_url, $screenshot_url_1, $screenshot_url_2, $screenshot_url_3, $screenshot_url_4, $screenshot_width_height_1, $screenshot_width_height_2, $screenshot_width_height_3, $screenshot_width_height_4,$ipadscreenshot_url_1, $ipadscreenshot_url_2, $ipadscreenshot_url_3, $ipadscreenshot_url_4, $ipadscreenshot_width_height_1, $ipadscreenshot_width_height_2, $ipadscreenshot_width_height_3, $ipadscreenshot_width_height_4 ) = explode($delimiter, $tmp_arr);
}
If you don't like the temporary array, you could count the delimiters (not as good in my opinion)
if(substr_count($line, $delimiter) < 23) {
// less than 24 fields!
print_r(explode($delimiter, $tmp_arr));
}
else
{
// everything alright!
list($export_date, $application_id, $language_code, $title, $description, $release_notes, $company_url, $suppport_url, $screenshot_url_1, $screenshot_url_2, $screenshot_url_3, $screenshot_url_4, $screenshot_width_height_1, $screenshot_width_height_2, $screenshot_width_height_3, $screenshot_width_height_4,$ipadscreenshot_url_1, $ipadscreenshot_url_2, $ipadscreenshot_url_3, $ipadscreenshot_url_4, $ipadscreenshot_width_height_1, $ipadscreenshot_width_height_2, $ipadscreenshot_width_height_3, $ipadscreenshot_width_height_4 ) = explode($delimiter, $line);
}
!Attention! you only have 23 delimiters for 24 fields! ;)
Second Approach:
Since the Undefined Offset issue is just a "Notice" from PHP you could write an error handler which catches the notice.
See:
http://www.codeunit.co.za/2009/09/09/php-how-to-catch-a-script-warning-or-notice/
But this one maybe a little overkill ;)
Best Regards
Simon

Categories