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
Related
I got this error every time run the application. I got everything ok. I have checked every steps and I got nothing. is it for php update ? I am using php 8. Here is my code.
class ViewProducts extends AddNewProduct
{
private $to_array2, $to_array1;
public function viewAllProducts() {
// get image name
$text_path = $this->getTextFilePath();
//extract file contents
$file_contents = file_get_contents($text_path);
// convert string to array
$this->to_array1 = explode('$', $file_contents);
foreach ($this->to_array1 as $item => $value) {
$this->to_array2 = explode(',', $value);
if(isset($this->to_array2[0])){
echo '<pre>';
var_dump($this->to_array2[$item][$value]);
echo '</pre>';
//$to_array[$item]['id'] = $this->to_array2[0];
//$to_array[$item]['name'] = $this->to_array2[1];
} else {
echo 'Nothing found';
}
}
return $this->to_array2;
}
}
Error details:
Please fill with data.
Warning: Uninitialized string offset 11 in D:\PROJECTS\IDE\xampp\htdocs\tenth-project\app\classes\ViewProducts.php on line 24
string(0) ""
Fatal error: Uncaught TypeError: Cannot access offset of type string on string in D:\PROJECTS\IDE\xampp\htdocs\tenth-project\app\classes\ViewProducts.php:24
Stack trace:
#0 D:\PROJECTS\IDE\xampp\htdocs\tenth-project\pages\action.php(24): App\classes\ViewProducts->viewAllProducts()
#1 {main}
thrown in D:\PROJECTS\IDE\xampp\htdocs\tenth-project\app\classes\ViewProducts.php on line 24
Look carefully at these two lines:
this->to_array2 = explode(',', $value);
And:
var_dump($this->to_array2[$item][$value]);
You have a string, which you split into a list of parts; you then try to access that list as though it was a two-dimensional array, using the original string as one of the keys. This doesn't even begin to make sense.
Unless you've changed them to hide details of the real program, one of your problems is probably that your variable names are poorly chosen. See how much clearer this is:
$item_list = explode('$', $file_contents);
foreach ($item_list as $item_number => $item_details) {
$field_list = explode(',', $item_details);
if(isset($field_list[0])){
echo '<pre>';
var_dump($field_list[$item_number][$item_details]); // clearly nonsense
echo '</pre>'!
} else {
echo 'Nothing found';
}
}
With knowledge of what the program is actually doing, you can probably do better than "item" and "field".
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.
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.
Disclaimer; I'm fully aware of the pitfalls and "evils" of eval, including but not limited to: performance issues, security, portability etc.
The problem
Reading the PHP manual on eval...
eval() returns NULL unless return is
called in the evaluated code, in which
case the value passed to return is
returned. If there is a parse error in
the evaluated code, eval() returns
FALSE and execution of the following
code continues normally. It is not
possible to catch a parse error in
eval() using set_error_handler().
In short, no error capture except returning false which is very helpful, but I'm sur eI could do way better!
The reason
A part of the site's functionality I'm working on relies on executing expressions. I'd like not to pass through the path of sandbox or execution modules, so I've ended using eval. Before you shout "what if the client turned bad?!" know that the client is pretty much trusted; he wouldn't want to break his own site, and anyone getting access to this functionality pretty much owns the server, regardless of eval.
The client knows about expressions like in Excel, and it isn't a problem explaining the little differences, however, having some form of warning is pretty much standard functionality.
This is what I have so far:
define('CR',chr(13));
define('LF',chr(10));
function test($cond=''){
$cond=trim($cond);
if($cond=='')return 'Success (condition was empty).'; $result=false;
$cond='$result = '.str_replace(array(CR,LF),' ',$cond).';';
try {
$success=eval($cond);
if($success===false)return 'Error: could not run expression.';
return 'Success (condition return '.($result?'true':'false').').';
}catch(Exception $e){
return 'Error: exception '.get_class($e).', '.$e->getMessage().'.';
}
}
Notes
The function returns a message string in any event
The code expression should be a single-line piece of PHP, without PHP tags and without an ending semicolon
New lines are converted to spaces
A variable is added to contain the result (expression should return either true or false, and in order not to conflict with eval's return, a temp variable is used.)
So, what would you add to further aide the user? Is there any further parsing functions which might better pinpoint possible errors/issues?
Chris.
Since PHP 7 eval() will generate a ParseError exception for syntax errors:
try {
$result = eval($code);
} catch (ParseError $e) {
// Report error somehow
}
In PHP 5 eval() will generate a parse error, which is special-cased to not abort execution (as parse errors would usually do). However, it also cannot be caught through an error handler. A possibility is to catch the printed error message, assuming that display_errors=1:
ob_start();
$result = eval($code);
if ('' !== $error = ob_get_clean()) {
// Report error somehow
}
I've found a good alternative/answer to my question.
First of, let me start by saying that nikic's suggestion works when I set error_reporting(E_ALL); notices are shown in PHP output, and thanks to OB, they can be captured.
Next, I've found this very useful code:
/**
* Check the syntax of some PHP code.
* #param string $code PHP code to check.
* #return boolean|array If false, then check was successful, otherwise an array(message,line) of errors is returned.
*/
function php_syntax_error($code){
if(!defined("CR"))
define("CR","\r");
if(!defined("LF"))
define("LF","\n") ;
if(!defined("CRLF"))
define("CRLF","\r\n") ;
$braces=0;
$inString=0;
foreach (token_get_all('<?php ' . $code) as $token) {
if (is_array($token)) {
switch ($token[0]) {
case T_CURLY_OPEN:
case T_DOLLAR_OPEN_CURLY_BRACES:
case T_START_HEREDOC: ++$inString; break;
case T_END_HEREDOC: --$inString; break;
}
} else if ($inString & 1) {
switch ($token) {
case '`': case '\'':
case '"': --$inString; break;
}
} else {
switch ($token) {
case '`': case '\'':
case '"': ++$inString; break;
case '{': ++$braces; break;
case '}':
if ($inString) {
--$inString;
} else {
--$braces;
if ($braces < 0) break 2;
}
break;
}
}
}
$inString = #ini_set('log_errors', false);
$token = #ini_set('display_errors', true);
ob_start();
$code = substr($code, strlen('<?php '));
$braces || $code = "if(0){{$code}\n}";
if (eval($code) === false) {
if ($braces) {
$braces = PHP_INT_MAX;
} else {
false !== strpos($code,CR) && $code = strtr(str_replace(CRLF,LF,$code),CR,LF);
$braces = substr_count($code,LF);
}
$code = ob_get_clean();
$code = strip_tags($code);
if (preg_match("'syntax error, (.+) in .+ on line (\d+)$'s", $code, $code)) {
$code[2] = (int) $code[2];
$code = $code[2] <= $braces
? array($code[1], $code[2])
: array('unexpected $end' . substr($code[1], 14), $braces);
} else $code = array('syntax error', 0);
} else {
ob_end_clean();
$code = false;
}
#ini_set('display_errors', $token);
#ini_set('log_errors', $inString);
return $code;
}
Seems it easily does exactly what I need (yay)!
How to test for parse errors inside eval():
$result = #eval($evalcode . "; return true;");
If $result == false, $evalcode has a parse error and does not execute the 'return true' part. Obviously $evalcode must not return itself something, but with this trick you can test for parse errors in expressions effectively...
Good news: As of PHP 7, eval() now* throws a ParseError exception if the evaluated code is invalid:
try
{
eval("Oops :-o");
}
catch (ParseError $err)
{
echo "YAY! ERROR CAPTURED: $err";
}
* Well, for quite a while then... ;)
I think that best solution is
try {
eval(/* ... */);
} catch (Throwable $t) {
//...
}
It catches every error and exception, including Call to undefined function etc
You can also try something like this:
$filePath = '/tmp/tmp_eval'.mt_rand();
file_put_contents($filePath, $evalCode);
register_shutdown_function('unlink', $filePath);
require($filePath);
So any errors in $evalCode will be handled by errors handler.
when I'm trying to getimagesize($img) and the image doesn't exist, I get an error. I don't want to first check whether the file exists, just handle the error.
I'm not sure how try catch works, but I want to do something like:
try: getimagesize($img) $works = true
catch: $works = flase
Like you said, if used on a non-existing file, getimagesize generates a warning :
This code :
if ($data = getimagesize('not-existing.png')) {
echo "OK";
} else {
echo "NOT OK";
}
will get you a
Warning: getimagesize(not-existing.png) [function.getimagesize]:
failed to open stream: No such file or directory
A solution would be to use the # operator, to mask that error :
if ($data = #getimagesize('not-existing.png')) {
echo "OK";
} else {
echo "NOT OK";
}
As the file doesn't exist, $data will still be false ; but no warning will be displayed.
Another solution would be to check if the file exists, before using getimagesize ; something like this would do :
if (file_exists('not-existing.png') &&
($data = getimagesize('not-existing.png'))
) {
echo "OK";
} else {
echo "NOT OK";
}
If the file doesn't exist, getimagesize is not called -- which means no warning
Still, this solution is not the one you should use for images that are on another server, and accessed via HTTP (if you are in this case), as it'll mean two requests to the remote server.
For local images, that would be quite OK, I suppose ; only problem I see is the notice generated when there is a read error not being masked.
Finally :
I would allow errors to be displayed on your developpement server,
And would not display those on your production server -- see display_errors, about that ;-)
Call me a dirty hacker zombie who will be going to hell, but I usually get around this problem by catching the warning output into an output buffer, and then checking the buffer. Try this:
ob_start();
$data = getimagesize('not-existing.png');
$resize_warning = ob_get_clean();
if(!empty($resize_warning)) {
print "NOT OK";
# We could even print out the warning here, just as PHP would do
print "$resize_warning";
} else {
print "OK"
}
Like I said, not the way to get a cozy place in programmer's heaven, but when it comes to dysfunctional error handling, a man has to do what a man has to do.
I'm sorry that raise such old topic. Recently encountered a similar problem and found this topic instead a solution. For religious reasons I think that '#' is bad decision. And then I found another solution, it looks something like this:
function exception_error_handler( $errno, $errstr, $errfile, $errline ) {
throw new Exception($errstr);
}
set_error_handler("exception_error_handler");
try {
$imageinfo = getimagesize($image_url);
} catch (Exception $e) {
$imageinfo = false;
}
This solution has worked for me.
try {
if (url_exists ($photoUrl) && is_array (getimagesize ($photoUrl)))
{
return $photoUrl;
}
} catch (\Exception $e) { return ''; }
Simple and working solution based on other answers:
$img_url = "not-existing.jpg";
if ( is_file($img_url) && is_array($img_size = getimagesize($img_url)) ) {
print_r($img_size);
echo "OK";
} else {
echo "NOT OK";
}