file_get_contents(url... when the url server is down - php

I run a bunch of servers that I monitor on a minutely basis from another server. The relevant bit of code goes like this
$ctx = stream_context_create(array('http'=>array('timeout'=>10)));
try
{
$mei = file_get_contents("https://url/status.php?key=shhh",false,$ctx);
} catch(Exception $e)
{
trigger_error($e->getMessage());
$mei = null;
}
I started providing a stream context when I realized that if one of the monitored servers is down the whole setup stops working and returns a 504, Bad Gateway, error.
Everything good so far. What puzzles me is this - for some reason the 10s timeout is triggering an error message in my Nginx log file but I am unable to catch the exception in my code above. I should mention that this is NOT and error_reporting issue. I checked my error_reporting settings and, for good measure, tried with error_reporting(E_ALL) right at the top.
I could always just stick in an # prior to file_get_contents and everything would be fine but this puzzles me - either I need to stop working and spot my mistake here or else there is another issue at work.

In PHP (>=7) it would be better to catch \Throwable type, because it's the base class for both Error and Exception.
$ctx = stream_context_create(array('http'=>array('timeout'=>10)));
try
{
$mei = file_get_contents("https://url/status.php?key=shhh",false,$ctx);
}
catch(\Throwable $e)
{
trigger_error($e->getMessage());
$mei = null;
}

Use Guzzle or curl to verify the availability of the server prior your request the it's file_get_contents.
can be prettier but you get the point:
$server = 'https://url/status.php?key=shhh';
$client = new GuzzleHttp\Client();
$res = $client->request('GET', $server);
if ($res->getStatusCode() == 200) {
$ctx = stream_context_create(array('http' => array('timeout' => 10)));
try {
$mei = file_get_contents($server, false, $ctx);
} catch (Exception $e) {
trigger_error($e->getMessage());
$mei = null;
}
}

Related

dns_get_record(): A temporary server error occurred.

I'm querying a whole bunch of addresses, some are online and some are not. I can't seem to get around this error however, even catching the exception fails :(
dns_get_record(): A temporary server error occurred.
try {
$result = dns_get_record('_minecraft._tcp.' . $addr, DNS_SRV);
}
catch (Exception $e) {
return [$addr,$port];
}
If this error occurs, I want to continue the script, skipping the record, however currently the script just halts.
Any help appreciated!!
I can't catch this exception too. And how I understood it's a bug of php:
https://bugs.php.net/bug.php?id=73149
But I found another solution. You can use # when you call this function. This symbol kill all errors when you call this one. And it will looks like that:
$dns = #dns_get_record($domain, DNS_A);
if(!$dns){
return false;
}
I was able to get the IP (A record) for a host using the below PHP function
gethostbynamel(string $hostname): array|false
Reference: gethostbynamel — Get a list of IPv4 addresses corresponding to a given Internet host name
try this:
try {
$dns = dns_get_record($domain, DNS_A);
}
catch (Exception $e) {
if ($e->getMessage() !== 'dns_get_record(): A temporary server error occurred.') {
throw $e;
}
$dns = false;
}

php try catch not working properly

I have code like this:
try {
$providerError = false;
$providerErrorMessage = null;
$nbg_xml_url = "http://www.somesite.com/rss.php";
$xml_content = file_get_contents($nbg_xml_url);
// ... some code stuff
} catch (Exception $e) {
$providerError = true;
$providerErrorMessage = $e -> getMessage();
$usd = 1;
$rate = null;
$gel = null;
} finally {
// .. Write in db
}`
and problem is that, when file_get_contents can not read url (may be site not responding or something like this..) my code writes error: failed to open stream: HTTP request failed! and execution goes direct to finally block bypass catch block without entering in it..
any ideas?
You can set an empty error handler to prevent the warning and afterward throw a custom exception in case of failure. In this case I would write a custom file_get_content like so:
function get_file_contents($url) {
$xml_content = file_get_contents($url);
if(!$xml_content) {
throw new Exception('file_get_contents failed');
}
return $xml_content;
}
and would use it in your block:
set_error_handler(function() { /* ignore errors */ });
try {
$providerError = false;
$providerErrorMessage = null;
$nbg_xml_url = "http://www.somesite.com/rss.php";
$xml_content = get_file_contents($nbg_xml_url); //<----------
// ... some code stuff
} catch (Exception $e) {
$providerError = true;
$providerErrorMessage = $e -> getMessage();
$usd = 1;
$rate = null;
$gel = null;
} finally {
// .. Write in db
}
Then remember to restore the error handler calling:
restore_error_handler();
Note that when using your own error handler it will bypass the
error_reporting
setting and all errors included notices, warnings, etc., will be passed to it.
$xml_content = file_get_contents($nbg_xml_url);
The function file_get_contents does not throw an exception. Thus an exception will not be thrown if as you say the file is not found.
From the docs:
An E_WARNING level error is generated if filename cannot be found...
This function returns the read data or FALSE on failure. So you could check if $xml_content is FALSE ($xml_content === false) and proceed accordingly.
This is a php code for catching any error or exception.
Throwable is the base interface for any object that can be thrown via a throw statement, including Error and Exception.
This will catch fatal errors too. Without throwable it will not catch fatal errors.
try {
// Code that may throw an Exception or Error.
} catch (Throwable $t) {
// Executed only in PHP 7, will not match in PHP 5.x
} catch (Exception $e) {
// Executed only in PHP 5.x, will not be reached in PHP 7
}

Guzzle Curl error not catche by try catch statement (Laravel)

In a Laravel project I need to call API REST to delete remote data.
My problem is that my catch statement dont catch Guzzle exception when I got an error. My code is the following :
try {
$client = new \GuzzleHttp\Client();
$request = $client->delete(Config::get('REST_API').'/order-product/'.$id);
$status = $request->getStatusCode();
} catch (Exception $e) {
var_dump($e);exit();
}
The exception is catched by Laravel but not in my catch statement. The exception throwed by Guzzle is :
GuzzleHttp\Ring\Exception\ConnectException
It raised in my line 3 of script and it doesn't catched in my script. Could you give me a way to catch the Guzzle Exception ?
I should indicate that I already seen these posts but I not get a good response :
How to resolve cURL Error (7): couldn't connect to host?
and
Catching cURL errors from Guzzle
It worked with me when I used
\GuzzleHttp\Exception\ConnectException
instead of
\Guzzle\Http\Exception\ConnectException
I had a similar problem and solved it using the following thing.I have used your example and given the inline comments for you to understand.
try {
$client = new \GuzzleHttp\Client();
$request = $client->delete(Config::get('REST_API').'/order-product/'.$id);
$status = $request->getStatusCode();
if($status == 200){
$response = $response->json();
}else{
// The server responded with some error. You can throw back your exception
// to the calling function or decide to handle it here
throw new \Exception('Failed');
}
} catch (\Guzzle\Http\Exception\ConnectException $e) {
//Catch the guzzle connection errors over here.These errors are something
// like the connection failed or some other network error
$response = json_encode((string)$e->getResponse()->getBody());
}
Hope this helps!
Maybe that exception is not extending the Exception class. You may try to catch it like:
try {
$client = new \GuzzleHttp\Client();
$request = $client->delete(Config::get('REST_API').'/order-product/'.$id);
$status = $request->getStatusCode();
} catch (\GuzzleHttp\Ring\Exception\ConnectException $e) {
var_dump($e);exit();
} catch (Exception $e) {
// ...
}
You probably mean to catch \Exception (in the root namespace), either by adding the backslash in the catch statement or a use Exception statement.
Just Updating the answer to the new Guzzle exception namespace
try {
$client = new \GuzzleHttp\Client();
$request = $client->delete(Config::get('REST_API').'/order-product/'.$id);
$status = $request->getStatusCode();
} catch (\GuzzleHttp\Exception\ConnectException $e) {
var_dump($e);exit();
}

Soap is not working in PHP

I am using SoapClient but I am not able to get the result. I get this error:
The server was unable to process the request due to an internal error.
For more information about the error, either turn on
IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute
or from the configuration behavior) on the server in order to send the
exception information back to the client, or turn on tracing as per
the Microsoft .NET Framework 3.0 SDK documentation and inspect the
server trace logs.
<?php
$silverpop = new SoapClient($my_url, array('trace' => 1));
/*$client = new stdClass();
$client->LoginID = 'mylogin-id';
$client->LicenceKey = 'mylicense-key';*/
$clientobj = (object) array("LoginID" => "mylogin-id", "LicenceKey" => "mylicense-key");
try {
//$var = $silverpop->__soapCall("GetServicesforPincode",array('P_Pincode'=>'110014','P_ClientObject'=>$clientobj));
//$var = $silverpop->GetServicesforPincode('110014',$clientobj);
$var = $silverpop -> __soapCall("GetServicesforPincode", array('110014', $clientobj));
} catch (SoapFault $exception) {
echo $exception -> getMessage();
}
echo '<pre>';
print_r($var);
?>
What am I doing wrong?
Either you have send your data not according to the specifications or your SoapServer is not working. I think the first one as Soap isn't always as clear as it should. As it looks like the error message is actually generated by the SoapServer, I recommend checking the schema for allowed parameters/calls and their format. If that's all correct, check if you are missing headers etc.
If all of the above is correct, fix your SoapServer. If you haven't gotten access to it, poke the owner.
try{
$client = new SoapClient($my_url,array('trace' => 1));
$object = new stdClass();
$object->LoginID = 'mylogin-id';
$object->LicenceKey = 'mylicense-key';
$xml = simplexml_load_string($client->GetServicesforPincode($object));
$json = json_encode($xml);
print_r($json);
}
catch (SoapFault $exception) { echo $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';
}

Categories