why would does this error handling function cause domdocument() to hang? - php

I include this simple error handling function to format errors:
date_default_timezone_set('America/New_York');
// Create the error handler.
function my_error_handler ($e_number, $e_message, $e_file, $e_line, $e_vars) {
// Build the error message.
$message = "An error occurred in script '$e_file' on line $e_line: \n<br />$e_message\n<br />";
// Add the date and time.
$message .= "Date/Time: " . date('n-j-Y H:i:s') . "\n<br />";
// Append $e_vars to the $message.
$message .= "<pre>" . print_r ($e_vars, 1) . "</pre>\n<br />";
echo '<div id="Error">' . $message . '</div><br />';
} // End of my_error_handler() definition.
// Use my error handler.
set_error_handler ('my_error_handler');
When I include it in a script in with the following
$dom = new DOMDocument();
$dom->loadHTML($output);
$xpath = new DOMXPath($dom);
and parse a web page (in this case, http://www.ssense.com/women/designers/all/all/page_1, which I do have permission to parse) I get errors like
AN ERROR OCCURRED IN SCRIPT '/HSPHERE/LOCAL/HOME/SITE.COM/SCRIPT.PHP' ON LINE 59:
DOMDOCUMENT::LOADHTML(): HTMLPARSEENTITYREF: NO NAME IN ENTITY, LINE: 57
and
AN ERROR OCCURRED IN SCRIPT '/HSPHERE/LOCAL/HOME/SITE.COM/SCRIPT.PHP' ON LINE 59:
DOMDOCUMENT::LOADHTML(): TAG NAV INVALID IN ENTITY, LINE: 58
There are many errors and the page never finishes loading. However, if I do not include this error handler, the line
$dom->loadHTML($output);
does not throw any errors, and I get the results I expect in a few seconds. I assume the error handler is catching warnings related to loadHTML() that are not otherwise reported. (Even if I use
#$dom->loadHTML($output);
it still reports the errors.) How might I modify the error handler to accommodate calls to loadHTML(), or otherwise fix this problem?

The web page you're loading contains many errors. For instance, & instead of the & entity in the HTML.
PHP DOM uses libxml, so to disable all the errors insert the line:
libxml_use_internal_errors(true);
You can later get a list of the parsing errors with libxml_get_errors().

It's not the custom error handler that is causing the error.
I ran the following code without a custom error handler:
$output = file_get_contents("http://www.ssense.com/women/designers/all/all/page_1");
$dom = new DOMDocument();
$dom->loadHTML($output);
$xpath = new DOMXPath($dom);
When I ran it, I got a ton of warning messages similar to the ones in your error handler.
I think the problem you're seeing is just that your error handler is reporting errors that PHP isn't reporting by default.
By default, the level of error reporting is determined by your php.ini settings, but can be overridden by using the error_reporting() function. When you set your own error handler, you have to determine for yourself what level of reporting you want to deal with. Your error handler will be called on every error and notice, and so you will output error messages for everything unless you explicitly check the error being generated against the current error_reporting() level.
Remember that using the # error suppression operator is just shorthand for setting error_reporting(0) for that line. For example, this line:
#$dom->loadHTML($output);
Is simply shorthand for the following:
$errorLevel = error_reporting(0);
$dom->loadHTML($output);
error_reporting($errorLevel);
Since normal PHP error reporting is entirely bypassed when using a custom handler, using the # operator is meaningless since the current error_reporting() level is completely ignored. You would have to write custom code into your error handler to check the current error_reporting() level and handle it accordingly, for example:
function my_error_handler() {
if (error_reporting() == 0) {
return; // do nothing when error_reporting is disabled.
}
// normal error handling here
}
My assumption is that, when not using a custom error handler, PHP is simply defaulting to an error_reporting() level which is lower than the errors being produced.
If you add error_reporting(E_ALL | E_STRICT); to the top of your code, you will see those same errors even when you don't have your custom error handler enabled.

Related

How to display those errors which are disabled by #-operator - Error Control Operators?

I am using some external libraries which uses the # error silencing operator.
The library is generating some errors and it becomes difficult to point the exact location where the error occurs as the # operator hides it.
Is there any way to easily disable the #-operator in the code without making any actual changes to the code?
I have tried Scream Pecl extension but it does not seem to work. It's available for PHP 5.6 version while I am using PHP 7.
Scream extension is installed and is enabled in php.ini by using scream.enabled=1 (as per their documentation) but the error still doesn't show or log.
You cannot disable the behaviour of the # symbol, but you can log/handle these errors nonetheless by using your own error handler.
From the docs:
If you have set a custom error handler function with set_error_handler() then it will still get called,
What this operator does is basically set error_reporting to 0 for one statement. Which is also reflected on the same docs:
but this custom error handler can (and should) call error_reporting() which will return 0 when the call that triggered the error was preceded by an #.
So, assuming you could have something like this very simple implementation, you would need to fine tune the details:
function exceptional_error_handler($severity, $message, $file, $line)
{
if (error_reporting() === 0) {
// assuming your application always runs with `error_handling !== 0`,
// e.g. by setting this during application bootstrap
// that we got here means this is was a "silenced" error
throw new \ErrorException("Silenced error! '$message'", 0, $severity, $file, $line);
}
// regular (non-silenced) errors should respect your error_reporting levels
if (!(error_reporting() & $severity)) {
// This error code is not included in error_reporting
return;
}
// everything else is converted to an exception
throw new \ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler("exceptional_error_handler");
This converts all errors to exceptions, simply noting with a different message when it was a "silenced" error, but your error handler could do logging or have any kind of custom logic.
By doing this, you could leave the existing/third party code untouched, and just add the error handling logic to the bootstrapping part of your application.
Docs for set_error_handler().

How to show only json code not with php warning

in this case I accidentally made a wrong database username or password, my goal here is how to generate an error in the form of JSON data and open in the form of a PHP warning or error like my picture
// Here's my PHP Code
if (!empty($_POST)){
$test_conn = new mysqli('localhost' , $_POST['db_user'] , $_POST['db_pass']);
$conn = new mysqli('localhost' , $_POST['db_user'] , $_POST['db_pass'] , $_POST['db_name']);
if ($test_conn->connect_error){
$response = array('status' => 0 , 'message' => "Failed Connect to Databases");
print_r(json_encode($response));
}else{
// The Code When Username and Password is Correct
}
}
here I know that the username or password is wrong, but I am confused how to only display the json data in the image at the bottom and not display the php warning
If you are sure that this is what you want, just disable errors by adding the following at the top of your script:
error_reporting(0);
if you want to hide errors for a given code, use the following:
// Disable errors and get current error reporting level
$err = error_reporting(0);
/* here goes you buggy code */
// Set the default error reporting level
error_reporting($err);
You have to suppress the php error reporting in order to use your own check of connect_error. The php documentation on mysqli::$connect_error suggests to do this with the # error control operator like this:
$test_conn = #new mysqli( ... )
This will only suppress errors caused by a faulty instantiation of the mysqli object. It should be clear that you then have to handle these errors yourself (as you already do). All other errors still could cause your php script to stop and not to return any JSON string to the AJAX function that called the script.
As #Victor correctly pointed out in his answer you could disable all errors for the script (or parts of it) by setting error_reporting(0) at the beginning at the script. In that case you wouldn't have to check other possible errors, e.g. if the db credentials in the $_POST array are not set or empty.
As #Ben mentioned in his comments you could also make mysqli throw exceptions to avoid fatal errors by setting mysqli_report(MYSQLI_REPORT_STRICT). This would also be a feasible solution for your problem.
As a general consideration you should make these settings in your php.ini:
display_errors = Off
error_log = syslog
This prevents error message from being exposed to the www (security) while making them available for developers in /var/log/syslog (maintainability).

Handling PHP / mysql errors (including fatal) via custom error page

What is the best way to handle errors on front end websites? Similar to twitter, reddit, etc that have a nice 'something went wrong' page. I want to:
detect errors and be notified, even if its just to a log file
show a custom error page such as the fail whale or similar, so its
obvious something has gone wrong instead of showing nothing, or a horrible looking error.
Ideally anything that prevents a user from seeing the site, like fatal errors, custom exceptions, mysql being down, random 500 errors etc, should show the custom error page.
Fatal Errors
PHP : Custom error handler - handling parse & fatal errors - works ok, but if this happens at the end of a script, and it has already output something, this wont work
Non Fatal Errors
http://davidwalsh.name/custom-error-handling-php
MySQL Errors
Ideally need to cover everything from a simple query error, to the server not being there. Imagine its as simple as detecting the error in my database class and calling redirecting to / calling the error method
Server Errors
Is this something I should be doing in PHP OR Apache OR both?
Code at the moment
Currently I have the following from a mix of various other SO's, but fatal errors will just be reported after the rest of the page has loaded.
Anyone have any ideas for something that will work with all of the above?
<?
function xhandler($number,$string,$file,$line,$context)
{
//log to text file?
//log to xml file?
//store in database?
//whatever you want to do!
echo "<h1>here be dragons!</h1>";
echo "$number,$string,$file,$line,$context";
exit();
}
function fatal_handler() {
$errfile = "unknown file";
$errstr = "shutdown";
$errno = E_CORE_ERROR;
$errline = 0;
$error = error_get_last();
if( $error !== NULL) {
$errno = $error["type"];
$errfile = $error["file"];
$errline = $error["line"];
$errstr = $error["message"];
}
echo "<h1>here be dragons!</h1>";
print_r($error);
exit();
}
register_shutdown_function( "fatal_handler" );
set_error_handler('xhandler',E_ALL);
include('show_the_content');
function_that_doesnt_exist();
echo $unterminated_line_of_code
?>
You can't catch fatal errors. They are fatal, script execution stops immediately when they're encountered. These errors should never appear on production sites.
Notices and warnings should be suppressed from showing in HTML on production environment (live sites).
What these sites usually do is handle HTTP errors with PHP. You need to redirect these errors to a PHP script which will handle them. For example, in Apache you do it with the ErrorDocument directive. You can put these directives (one for each HTTP error code) in the server configuration or in the .htaccess file in the web site's document root.
In the PHP script, you can filter different HTTP errors with $_SERVER['REDIRECT_STATUS'], if you need to. When you receive a request with the desired HTTP error, you can handle it any way you wish.

Avoid DOMDocument XML warnings in php

I'm fetching xml files from a server and sometimes I'm getting a non-valid xml files, because of this I'm getting a warning:
Warning: DOMDocument::load() [domdocument.load]: Start tag expected, '<' not found in
How can I catch this warning and delete the file?
You have two choices. Either use the # error control operator in your load() call, e.g. #$dom->load(), which is somewhat slow because it globally changes the value of display_errors to off, executes the function and sets it back to on.
The other option, which I personally prefer (I hate the # operator, I can't stand to see it in my code) is to save the old value of libxml_use_internal_errors, enable it using libxml_use_internal_errors(TRUE), call the function, clear the errors buffer and restore the old value. Here's a snippet from my code that does that:
<?php
$previous_value = libxml_use_internal_errors(TRUE);
$doc->loadHTML((string)$e->response->getBody());
libxml_clear_errors();
libxml_use_internal_errors($previous_value);
I can't comment on answers yet, so I'll write it here:
Michael solution makes it less strict, but it'll still issue warnings for some of the errors:
nadav#shesek:~$ php -r '$dom=new DOMDocument; $dom->strictErrorChecking = FALSE ; $dom->loadHTML("<xy></zx>");'
PHP Warning: DOMDocument::loadHTML(): Tag xy invalid in Entity, line: 1 in Command line code on line 1
DON'T do what Fran Verona suggested - globally disabling error reporting is something you should never do. In production, set your own error handler and display a prettier message to the user, and make sure the error is logged somewhere - but never disable it completely. Setting error_reporting to 0 will cause PHP to disable error logging too.
Xeon06 solution is problematic because you're effecting the entire script error handler for a specific piece of code. Using your own error handler that simply ignores the error causes the same issues as Fran's solution.
Use set_error_handler.
set_error_handler("yourHandler", E_WARNING);
Turn off strict error checking:
$dom = new DOMDocument();
$dom->strictErrorChecking = FALSE ;
$dom->load('/path/to/file.xml');
if (!$dom->validate()) {
// Invalid XML!
}

PHP/Zend: How to force browsers to don't show warnings on webpage for a particular case?

I am trying to get twitter updates like this:
try {
$doc = new DOMDocument();
$doc->load('http://twitter.com/statuses/user_timeline/1234567890.rss');
$isOK = true;
} catch( Zend_Exception $e ) {
$isOK = false;
}
If there is not problem with internet connection then $isOK = true; is set. But if there is a problem in loading twitter page then it shows following warnings and does not set $isOK = false;
Warning: DOMDocument::load(http://twitter.com/statuses/user_timeline/1234567890.rss) [domdocument.load]: failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in /var/www/vcred/application/controllers/IndexController.php on line 120
I don't want to see above warning on my webpage in any case. Any idea?
Thanks
Several options:
Suppress all errors for just this function call
#$doc->load('http://twitter.com/statuses/user_timeline/1234567890.rss');
which is the same as
$oldLevel = error_reporting(0);
$doc->load('http://twitter.com/statuses/user_timeline/1234567890.rss');
error_reporting($oldLevel);
Suppressing errors this way is generally frowned upon, as it makes code harder to debug. Like Shrapnel pointed out, you want to disable public display of all error messages on a production system anyway. On Dev systems you are encouraged to use error_reporting(-1);, which would enable E_ALL and E_STRICT.
If you want to use try/catch, you can also change default error handling and convert all errors to exceptions by doing
function myErrorHandler($errno, $errstr, $errfile, $errline) {
throw new Exception($errstr, $errno);
}
set_error_handler("myErrorHandler");
This is a global change though and affects everything raised. You'd also have to use catch(Exception $e) instead of Zend_Exception then in your code, but it would work. Note that the above would convert everything, even Notices, so you will also get an Exception about $isOk being undefined if you are trying to access this later. Feel free to adapt the handler to your liking and check out the user comments for set_error_handler for more refined versions.
Another global change would be to change the application.ini in your application folder, e.g. letting Zend Framework control error handling:
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
Change these to your needs. They are the same as in PHP.ini, e.g.
display_errors:
This determines whether errors should be printed to the screen as part of the output or if they should be hidden from the user.
display_startup_errors:
Even when display_errors is on, errors that occur during PHP's startup sequence are not displayed. It's strongly recommended to keep display_startup_errors off, except for debugging.
ini_set('display_errors',0);
it should be set this way on any production site.
users shouldn't be allowed to see system error messages at all

Categories