How do I handle Warning: SimpleXMLElement::__construct()? - php

I'm getting this error when I run in local host, if internet is disconnected (if internet is connect its ok) I want to handle this error, "error can show " but want to handle not fatal error break on PHP page.
Warning: SimpleXMLElement::__construct() [simplexmlelement.--construct]:
php_network_getaddresses: getaddrinfo failed: No such host is known.
in F:\xampp\htdocs\shoptpoint\sections\docType_head_index.php on line 30
but I'm trying to handle using try-catch. Below is my code
$apiurl="http://publisher.usb.api.shopping.com/publisher/3.0/rest/GeneralSearch?apiKey=78b0db8a-0ee1-4939-a2f9-d3cd95ec0fcc&trackingId=7000610&categoryId='5855855'";
try{
new SimpleXMLElement($apiurl,null, true);
}catch(Exception $e){
echo $e->getMessage();
}
How do I handle the error and my page can execute end of the project?

Using set_error_handler, you can do the following to convert any notices/warnings raised by SimpleXMLElement into a catchable Exception.
Take the following:-
<?php
function getData() {
return new SimpleXMLElement('http://10.0.1.1', null, true);
}
$xml = getData();
/*
PHP Warning: SimpleXMLElement::__construct(http://10.0.1.1): failed to open stream: Operation timed out
PHP Warning: SimpleXMLElement::__construct(): I/O warning : failed to load external entity "http://10.0.1.1"
PHP Fatal error: Uncaught exception 'Exception' with message 'String could not be parsed as XML'
*/
See how we get 2 Warnings before the the Exception from SimpleXMLElement is thrown? Well, we can convert those to an Exception like this:-
<?php
function getData() {
set_error_handler(function($errno, $errstr, $errfile, $errline) {
throw new Exception($errstr, $errno);
});
try {
$xml = new SimpleXMLElement('http://10.0.1.1', null, true);
}catch(Exception $e) {
restore_error_handler();
throw $e;
}
return $xml;
}
$xml = getData();
/*
PHP Fatal error: Uncaught exception 'Exception' with message 'SimpleXMLElement::__construct(http://10.0.1.1): failed to open stream: Operation timed out'
*/
Good luck,
Anthony.

If for any reason you don't want to set up the error handler you can also use some libxml functions to suppress E_WARNING from raising:
// remembers the old setting and enables usage of libxml internal error handler
$previousSetting = libxml_use_internal_errors(true);
// still need to try/catch because invalid XML raises an Exception
try {
// XML is missing root node
new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>',null, true);
} catch(Exception $e) {
echo $e->getMessage(); // this won't help much: String could not be parsed as XML
$xmlError = libxml_get_last_error(); // returns object of class LibXMLError or FALSE
if ($xmlError) {
echo $xmlError->message; // this is more helpful: Start tag expected, '<' not found
}
}
// sets libxml usage of internal error handler to previous setting
libxml_use_internal_errors($previousSetting);
Alternatively you can use libxml_get_errors() instead of libxml_get_last_error() to get all errors. With it you can get all errors regarding parsing the XML as an array of LibXMLError objects.
Some helpful links:
https://www.php.net/manual/en/function.libxml-use-internal-errors.php
https://www.php.net/manual/en/function.libxml-get-last-error.php

Related

Handle SOAP fatal error when there is a connection issue

I'm trying to connect to a soap service using a WSDL file in php 5.6
The snippet below works fine if I'm on the network, but if I'm disconnected I get a fatal error.
try {
$soap_client = new SoapClient($wsdl_file, ['exceptions' => true]);
}
catch (SoapFault $fault) {
echo 'poop';
}
catch (Exception $exception) {
echo 'pee';
}
edit: it does seem to do something with the SoapFault, because I can see my 'poop' debug message, but it still results in a fatal error
These are the errors I get
Warning (2): SoapClient(): php_network_get_addresses: getaddrinfo failed: No such host is known.
Warning (2): SoapClient(http://soap.service.com/serivce.svc) [soapclient.soapclient]: failed to open stream: php_network_getaddresses: getaddrinfo failed: No such host is known.
Error: SOAP-ERROR: Parsing Schema: can't import schema from 'http://soap.service.com/serivce.svc'
How can I gracefully handle the error so that php continues to run, so I can set a variable and render an HTML page indicating that there was a problem connecting
This was a cakephp issue
https://github.com/cakephp/cakephp/issues/8501
$restore = error_reporting(0);
try {
$soap_client = new SoapClient($wsdl_file, ['exceptions' => true]);
}
catch (SoapFault $e) {
trigger_error($e->getMessage()); // Overwrites E_ERROR with E_USER_NOTICE
}
finally {
error_reporting($restore);
}

How to log database errors in zend framework

I am using zend framework 1.12 for my project. I want to catch all types of fatal errors and send them to an email address for quick fix. I have written the below mentioned code in Bootstrap.php file for this purpose.
protected function _initFatalErrorCatcher()
{
register_shutdown_function(array($this, 'errorlogHandler'));
}
public function errorlogHandler()
{
$e = error_get_last();
if (!is_null($e)) { //fatal error
$msg = 'Fatal error: ' . $e['message'];
$msg .= ' in' . $e['file'];
$msg .= ' on line: ' . $e['line'];
$mail = new Zend_Mail('utf-8');
$mail->setBodyHtml($msg);
$mail->setFrom('zzz#z.com');
$mail->addTo('yyy#y.com');
$mail->setSubject('check this error');
$mail->send();
}
}
Using the above code, i am able to send fatal errors other than database connection related errors and query related errors to email. I followed the instructions from Catch Zend PDO Exception as well, but i believe i am missing something as its not working.
Any help on this will be appreciated.
EDIT:
I am also using Zend_Log to write the error logs in a log-file. But, using this i could not find a way to write the fatal errors. Code for this is given below.
$writer = new Zend_Log_Writer_Stream(APPLICATION_PATH . "/../data/log-file.log");
$errors = $this->_getParam('error_handler');
$exception = $errors->exception;
$log = new Zend_Log($writer);
$log->debug($exception->getMessage() . "\n" . $exception->getTraceAsString());
Scenario for database connection related issue:
If there is any error in host name, database name or in user name, it shows a Fatal error in browser like below. But its not detected by register_shutdown_function() or Zend_Log().
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42000] [1044] Access denied for user 'AAAA'#'%' to database 'BBBB'' in /var/www/project_name/library/Zend/Db/Adapter/Pdo/Abstract.php on line 144 PDOException: SQLSTATE[42000] [1044] Access denied for user 'AAAA'#'%' to database 'BBBB' in /var/www/project_name/library/Zend/Db/Adapter/Pdo/Abstract.php on line 129
The post here shows an example. Basically use set_error_handler to tickle php into throwing exceptions when an error is encountered. This example from link:
<?php
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
}
set_error_handler("exception_error_handler");
/* Trigger exception */
strpos();
?>
Hope this helps
//$array contains the values for insert
try {
$this->db->insert('Users', $array );
} catch (Exception $e){
echo $e->getMessage();
}
I have solved it by writing the below mentioned code in Bootstrap.php file.
protected function _initDbConfig()
{
$config = new Zend_Config($this->getOptions());
$params = $config->database->toArray();
try {
$db = Zend_Db::factory('Pdo_Mysql', $params);
$db->getConnection();
} catch (Zend_Db_Adapter_Exception $e) {
// perhaps the RDBMS is not running
// code to send email goes here
} catch (Zend_Exception $e) {
// perhaps factory() failed to load the specified Adapter class
// code to send email goes here
}
}
In application.ini, i have the following code.
database.host = "localhost"
database.username = "AAAA"
database.password = "*****"
database.dbname = "BBBBB"

Mysqli php i want a custom error message when unknown server host

I am writing a script to install the database of an application in php. It working fine but when im trying to install a database that doesnt exist i want only my own error message but i keep getting the default Warning : Warning: mysqli::mysqli() [mysqli.mysqli]: (HY000/2005): Unknown MySQL server host 'kasdasd'.
So I know that the host is wrong and I want it to be so, with only my own errormessage. How do I get rid of this message?
My connectclass with parameter DBConfig $config:
$this->mysqli = new mysqli($config->m_host,
$config->m_user,
$config->m_passw,
$config->m_db);
if ($this->mysqli->connect_error) {
return false;
}
$this->mysqli->set_charset("utf8");
return true;
an easy solution would be to see if you can open the hostname using fsockopen and suppressing the errors:
$port = 80;
if($fp = #fsockopen($config->m_host,$port)){
$db = new mysqli($config->m_host,$config->m_user,$config->m_passw,$config->m_db);
}else{
echo 'hostname not recognized';
}
#fclose($fp);
Edited:
You can use
if ($mysqli->connect_error) {
/** handle your error here **/
// Throw a custom exception if you like! (see below)
// or just echo "There was an error";
}
You can further use mysqli_connect_errno() to find out wht happened and handle it accordingly.
Edit: mysqli doesn't throw erros so below is incorrect.
Wrap your code in a try-catch, then you can throw whatever kind of error you like:
try {
/** your code here **/
} catch (Exception $e) {
/** your handling here **/
// i.e.
throw new BadHostNameException($config->m_host);
// or
echo "Could not connect!":
}
Note: you should replace Exception $e with the specific kind of exception a bad host throws so catch(MysqlBadHostnameException $e) (the type of error will be in your error log from your previous attempts or you can do get_class($e) in my example above.
// Isusing a custom exception: Add this outside of your class..
class BadHostNameException extends Exception {}

PHP SOAP error catching

I'm getting desperate, all I want is simple error handling when the PHP SOAP Web Service is down to echo an error message login service down. Please help me!
At the moment it's still displaying the error (along with warnings...):
Fatal error: SOAP-ERROR: Parsing WSDL
Here is the script:
<?php
session_start();
$login="0000000000000nhfidsj"; //It is like this for testing, It will be changed to a GET
$username = substr($login,0,13); //as password is always 13 char long
//(the validation is done int he javascript)
$password = substr($login,13);
try
{
ini_set('default_socket_timeout', 5); //So time out is 5 seconds
$client = new SoapClient("http://192.168.0.142:8080/services/Logon?wsdl"); //locally hosted
$array = $client->login(array('username'=>$username,
'password'=>$password));
$result = $array->return;
}catch(SoapFault $client){
$result = "0";
}
if($result == "true")//as this would be what the ws returns if login success
{
$_SESSION['user'] = $login;
echo "00";
}
else
{
echo "01 error: login failed";
}
?>
UPDATE July 2018
If you don't care about getting the SoapFault details and just want to catch any errors coming from the SoapClient you can catch "Throwable" in PHP 7+. The original problem was that SoapClient can "Fatal Error" before it throws a SoapFault so by catching both errors and exceptions with Throwable you will have very simple error handling e.g.
try{
soap connection...
}catch(Throwable $e){
echo 'sorry... our service is down';
}
If you need to catch the SoapFault specifically, try the original answer which should allow you to suppress the fatal error that prevents the SoapFault being thrown
Original answer relevant for older PHP versions
SOAP can fatal error calling the native php functions internally which prevents the SoapFaults being thrown so we need to log and suppress those native errors.
First you need to turn on exceptions handling:
try {
$client = new SoapClient("http://192.168.0.142:8080/services/Logon?wsdl",array(
'exceptions' => true,
));
} catch ( SoapFault $e ) { // Do NOT try and catch "Exception" here
echo 'sorry... our service is down';
}
AND THEN you also need to silently suppress any "PHP errors" that originate from SOAP using a custom error handler:
set_error_handler('handlePhpErrors');
function handlePhpErrors($errno, $errmsg, $filename, $linenum, $vars) {
if (stristr($errmsg, "SoapClient::SoapClient")) {
error_log($errmsg); // silently log error
return; // skip error handling
}
}
You will then find it now instead trips a SoapFault exception with the correct message "Soap error: SOAP-ERROR: Parsing WSDL: Couldn't load from '...'" and so you end up back in your catch statement able to handle the error more effectively.
Fatal error: SOAP-ERROR: Parsing WSDL Means the WSDL is wrong and maybe missing? so it's not related to soap. And you cannot handle FATAL ERROR with a try catch. See this link : http://ru2.php.net/set_error_handler#35622
What do you get when you try to access http://192.168.0.142:8080/services/Logon?wsdl in your browser?
You can check if the WSDL is present like this
$handle = curl_init($url);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, TRUE);
$response = curl_exec($handle);
$httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
/* You don't have a WSDL Service is down. exit the function */
}
curl_close($handle);
/* Do your stuff with SOAP here. */
Unfortunately SOAP throws a fatal error when the service is down / unreachable rather than returning a SoapFault object.
That being said, you can set it to throw an exception. You probably omitted the part where you're setting the exceptions soap_client option to false
$client = new SoapClient("http://192.168.0.142:8080/services/Logon?wsdl",array(
'exceptions' => false, // change to true so it will throw an exception
));
Catch the exception when service is down:
try {
$client = new SoapClient("http://192.168.0.142:8080/services/Logon?wsdl",array(
'exceptions' => true,
));
}
catch ( Exception $e )
{
echo 'sorry... our service is down';
}
Perhaps a better alternative:
set_error_handler('my_error_handler');
set_exception_handler('my_exception_handler');
function my_exception_handler($e) {
exit('Error, something went terribly wrong: '.$e);
}
function my_error_handler($no,$str,$file,$line) {
$e = new ErrorException($str,$no,0,$file,$line);
my_exception_handler($e);
}
Where you can adjust error messages in the mentioned functions.
I use it to return a message in the same situation you do, as it can occur at any time.
Say you send a soap message after the initial login, and that response never arrives or arrives only partially, this way you can return a message without any script paths, names and linenumbers.
In such cases I do not return $e at all, instead I just output something like: 'Something went wrong, please try it again (later).'
I ended up handling it this way:
libxml_use_internal_errors(true);
$sxe = simplexml_load_string(file_get_contents($url));
if (!$sxe) {
return [
'error' => true,
'info' => 'WSDL does not return valid xml',
];
}
libxml_use_internal_errors(false);
Do your soap call after this check.
Everything turned out to be much more trivial - when using namespaces, be sure to specify the root ns!
Those catch (SoapFailt $fault) - is wrong, right way catch (\SoapFault $fault)
SoapFault doesn't extends Exception, catch the especific type works:
try {
$client = new SoapClient("http://192.168.0.142:8080/services/Logon?wsdl",array(
'exceptions' => true,
));
}
catch ( SoapFault $e )
{
echo 'sorry... our service is down';
}

PHP DOMDocument error handling

In my application I am loading xml from url in order to parse it.
But sometimes this url may not be valid. In this case I need to handle errors.
I have the following code:
$xdoc = new DOMDocument();
try{
$xdoc->load($url); // This line causes Warning: DOMDocument::load(...)
// [domdocument.load]: failed to open stream:
// HTTP request failed! HTTP/1.1 404 Not Found in ...
} catch (Exception $e) {
$xdoc = null;
}
if($xdoc == null){
// Handle
} else {
// Proceed
}
I know I probably doing it wrong, but what's a correct way to handle this kind of exceptions? I don't want to see error messages on my page.
The manual for DOMDocument::load() says:
If an empty string is passed as the
filename or an empty file is named, a
warning will be generated. This
warning is not generated by libxml and
cannot be handled using libxml's error
handling functions.
But there is no information on how to handle it.
Thanks.
From what I can gather from the documentation, handling warnings issued by this method is tricky because they are not generated by the libxml extension and thus cannot be handled by libxml_get_last_error(). You could either use the error suppression operator and check the return value for false...
if (#$xdoc->load($url) === false)
// ...handle it
...or register an error handler which throws an exception on error:
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
and then catch it.
set_error_handler(function($number, $error){
if (preg_match('/^DOMDocument::loadXML\(\): (.+)$/', $error, $m) === 1) {
throw new Exception($m[1]);
}
});
$xml = new DOMDocument();
$xml->loadXML($xmlData);
restore_error_handler();
That works for me in PHP 5.3. But if you're not using loadXML, you might need to do some modifications.
To disable throwing errors:
$internal_errors = libxml_use_internal_errors(true);
$dom = new DOMDocument();
// etc...
libxml_use_internal_errors($internal_errors);
From php.net
If an empty string is passed as the
filename or an empty file is named, a
warning will be generated. This
warning is not generated by libxml and
cannot be handled using libxml's error
handling functions.
In your production environment you shouldn't have errors displayed to the user. They don't need to see them so taking this into account you can use...
$xdoc = new DOMDocument();
if ( $xdoc->load($url) ) {
// valid
}
else {
// invalid
}
For me , following did the trick
$feed = new DOMDocument();
$res= #$feed->load('http://www.astrology.com/horoscopes/daily-extended.rss');
if($res==1){
//do sth
}

Categories