Checking a URL is valid (from php soap client) - php

I am writing a web app which will allow the user to specify a URL for a SoapClient. I wanted to validate that php can connect to the client when the user submits a form. I thouhgt I could do this via try catch or set_error_handler (or some combination of the two). However it looks like this is not possible for fatal errors. Is there a way to get SoapClent to test a URL which won't throw an unrecoverable error?
Fatal error: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://example.com/wibble'
I want it to flag an error as the URL doesn’t exist, but I would like to be able to catch it.
Otherwise I suppose I could try to download and validate the URL myself, but I would have thought that it would be possible to do it from the SoapClient.
Should this be a fatal error?
Edit
After reading rogeriopvl's answer I reaslise that I should have said that I had tried the 'exceptions' option to the soapclient constructor and (in desperation) the use-soap-error-handler function.

Are you using xdebug? According to this PHP bug report and discussion, the issue has been fixed at least since PHP 5.1, but this xdebug bug messes with 'fatal error to exception conversions' in a way that the exception is not generated and the fatal error 'leaks through'.
I can reproduce this locally, with xdebug enabled:
try {
$soapClient = new SoapClient('http://www.example.com');
}
catch(Exception $e) {
$exceptionMessage = t($e->getMessage());
print_r($exceptionMessage);
}
This gives me the fatal error you described, without even entering the catch clause:
Fatal error: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://www.example.com'
It works if I disable xdebug right before the call:
xdebug_disable();
try {
$soapClient = new SoapClient('http://www.example.com');
}
catch(Exception $e) {
$exceptionMessage = t($e->getMessage());
print_r($exceptionMessage);
}
This triggers the exception as expected, and I get a proper SoapFault Object in the catch clause with a message of:
SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://www.example.com'
So basically exceptions work as advertised. If they don't work in your case, you might encounter the xdebug bug, or maybe a similar issue with another 3rd party component.

Quoting SoapClient documentation:
The exceptions option is a boolean value defining whether soap errors throw exceptions of type SoapFault.
So you should try something like:
$client = new SoapClient("some.wsdl", array('exceptions' => TRUE));
This way will throw SoapFault exceptions allowing you to catch them.

See: http://bugs.xdebug.org/view.php?id=249
Possible solution:
Index: trunk/www/sites/all/libraries/classes/defaqtoSoapClient.class.php
===================================================================
--- classes/defaqtoSoapClient.class.php
+++ classes/defaqtoSoapClient.class.php
## -31,10 +31,23 ##
try {
+ // xdebug and soap exception handling interfere with each other here
+ // so disable xdebug if it is on - just for this call
+ if (function_exists('xdebug_disable')) {
+ xdebug_disable();
+ }
//Create the SoapClient instance
parent::__construct($wsdl, $options);
}
catch(Exception $parent_class_construct_exception) {
+ if (function_exists('xdebug_enable')) {
+ xdebug_enable();
+ }
// Throw an exception an say that the SOAP client initialisation is failed
throw $parent_class_construct_exception;
+ }
+ if (function_exists('xdebug_enable')) {
+ xdebug_enable();
}
}

you could try and do a curl or fsockopen request to check the URL is valid.

For your information, i'm using SoapClient with PHPUnit to test remote WebServices and got the same problem!
when using an old PHPUnit version (3.3.x) as third party, phpunit crash
when using current version of PHPUnit (3.4.6) as third party, phpunit display "RuntimeException".
Here is my first test method :
public function testUnavailableURL() {
$client = new SoapClient("http://wrong.URI");
}
Here is PHPUnit first result :
There was 1 error:
1) MyTestCase::testUnavailableURL
RuntimeException:
FAILURES!
Here is my second test method :
public function testUnavailableURL() {
try {
$client = #new SoapClient("http://wrong.URI");
} catch (SoapFault $fault) {
print "SOAP Fault: (faultcode: {$fault->faultcode}, faultstring: {$fault->faultstring})";
}
}
Here is PHPUnit second test result :
PHPUnit 3.4.6 by Sebastian Bergmann.
.SOAP Fault: (faultcode: WSDL, faultstring: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://wrong.URI' : failed to load external entity "http://wrong.URI"
)...
Time: 3 seconds, Memory: 4.25Mb
OK
NB: i found a phpunit ticket on this subject : ticket 417

Related

SoapClient manage error / exception

I'm using a soap service that is unavailable today. It's returning a 403 Forbidden code, and then I got this message :
Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing WSDL: Couldn't load from...[MY URL]
How can I catch this SoapFault ?
Here is my code :
$client = new SoapClient($myurl);
I also tried to use the 2nd cosntructor argument with trace and exception(s) (with and without s, saw the two versions on the net. Can't find the doc).
I tried to catch the thrown error using
catch (SoapFault $exception) {
wtf($exception->getMessage());
}
or
catch (Exception $e){
wtf($e->getMessage());
}
(As seen in official doc comments)
Nothing works. Still this Fatal Error and SoapFault uncaught.
I also tried # before new SoapClient,
and catch with and without backslash (because i'm in a namespace).
At this point, I don't know what to do to handle this error properly.
Maybe a chicken sacrifice.
Resolved, the right way is in fact
['exceptions' => true]
And the exception must be caught with
catch (\SoapFault $exception) {
wtf($exception);
}

How to handle Fatal error: cURL error 7: Failed to connect to xxxx port 443

I have a script that connects to a third party API. It is and should be running on a non-stop loop 24/7 (I use a sleep at the end before restarting the loop).
The problem is that sometimes the third party API gets ddosed or the connection simply drops with this error:
Fatal error: Uncaught exception
'GuzzleHttp\Ring\Exception\ConnectException' with message 'cURL error
7: Failed to connect to xxx.com port 443
Is there any way to "break" on this fatal error to ensure the code is restarted and proceed if the action can be made or must I manually restart each time I get this error?
From Michael's comment
it looks like you can just catch the GuzzleHttp\Ring\Exception\ConnectException exception
like this:
use GuzzleHttp\Ring\Exception\ConnectException;
try {
// the code which throws the error
} catch( ConnectException $ex ) {
switch ( $ex->getMessage() ) {
case '7': // to be verified
// handle your exception in the way you want,
// maybe with a graceful fallback
break;
}
}
it appears guzzle's ConnectException extends some classes and ultimately extends php's Exception so you can safely use the getCode() method, allowing you to catch an identifier on which you can react accordingly to your needs.
catch ConnectionException as sample code below:
use Illuminate\Http\Client\ConnectionException;
...
try {
Http::get('<your url>');
}
catch (ConnectionException $e) {
// Do something with $e for example Log::debug($e->getMessage());
}

PHP AMQP consumer: Server channel error: 404, message: NOT_FOUND

I'm using the amqp extension in pecl 1.0.3, compiled with 2.7.1 rabbitmq.
I'm trying to get a basic consumer/producer example working, but I keep getting errors. There's very little php documentation on this extension and a lot of it seemed to be outdated or wrong.
I used the code a user posted, but can't seem to get the consumer part working
Connection:
function amqp_connection() {
$amqpConnection = new AMQPConnection();
$amqpConnection->setLogin("guest");
$amqpConnection->setPassword("guest");
$amqpConnection->connect();
if(!$amqpConnection->isConnected()) {
die("Cannot connect to the broker, exiting !\n");
}
return $amqpConnection;
}
Sender:
function amqp_send($text, $routingKey, $exchangeName){
$amqpConnection = amqp_connection();
$channel = new AMQPChannel($amqpConnection);
$exchange = new AMQPExchange($channel);
$exchange->setName($exchangeName);
$exchange->setType("fanout");
if($message = $exchange->publish($text, $routingKey)){
echo "sent";
}
if (!$amqpConnection->disconnect()) {
throw new Exception("Could not disconnect !");
}
}
Receiver:
function amqp_receive($exchangeName, $routingKey, $queueName) {
$amqpConnection = amqp_connection();
$channel = new AMQPChannel($amqpConnection);
$queue = new AMQPQueue($channel);
$queue->setName($queueName);
$queue->bind($exchangeName, $routingKey);
//Grab the info
//...
}
Then sending it:
amqp_send("Abcdefg", "action", "amq.fanout");
And Receiving it:
amqp_receive("amq.fanout","action","action");
I keep getting a problem running the script and points to the amqp receive:
PHP Fatal error: Uncaught exception 'AMQPQueueException' with message 'Server channel error: 404, message: NOT_FOUND - no queue 'action' in vhost '/'' in /home/jamescowhen/test.php:21
Can anyone point me to the right direction? The whole sample is from a user note here:
http://www.php.net/manual/en/amqp.examples.php#109024
The exception seems to be caused by your queue not being declared (as the error message describes 404 - the queue 'action' was not found). The reason why the example works for the original poster is probably because he already has declared the queue earlier, without realizing that it's missing in his example.
You can declare the queue by calling ->declare() on the queue object. You'll also have to do this with the exchange object, unless you're certain that it already exists when you attempt to hook the queue onto it.

Phpunit - mocking SoapFault->getMessage()

I'm connecting to a third party service using SOAP. This service will return SoapFaults some times. Because this is expected, I want to test this, by mocking the SoapFaults. There are five standard SoapFaults it will return.
Here is the start of a real SoapFault:
object(SoapFault)#7 (11) {
["message":protected]=>
string(19) "{ 'INVALID_INPUT' }"
I am interested in this message. In my code I use $e->getMessage().
catch (SoapFault $e)
{
// Catch any Soap faults and convert to an error message
switch ($e->getMessage())
{
case "{ 'INVALID_INPUT' }":
$this->addError(self::INVALID_INPUT);
return FALSE;
but I cannot figure out how to mock the SoapClient response. The SoapClient object doesn't appear to take in any input to set the message, and the method getMessage() is final, so I can't mock it.
Any ideas would be appreciated. Thanks.
There should be no need to mock SoapFault. You can just use the real exception (usually exceptions are not mocked for testing as they are Value objects) and mock the SoapClient to throw your prepared exception.
By doing so you get around not being able to mock getMessage because you build the SoapFault in your test case like shown below.
As I've had troube figuring out what exactly your problem was i build a little test case to make sure it works out. As it was there already i guess the code explains it better than i could.
Test example:
<?php
class SoapFaultTest extends PHPUnit_Framework_TestCase {
public function testSoapFault() {
$soapFault = new SoapFault("test", "myMessage");
$soapClient = $this->getMockBuilder('SoapClient')
->setMethods(array('methodOnService'))
->disableOriginalConstructor()
->getMock();
$soapClient->expects($this->once())
->method('methodOnService')
->will($this->throwException($soapFault));
$x = new Consumer();
$this->assertSame(
"InvalidInput",
$x->doSoapStuff($soapClient)
);
}
}
class Consumer {
public function doSoapStuff(SoapClient $soapClient) {
try {
$soapClient->methodOnService();
} catch(Exception $e) {
if($e->getMessage() == "myMessage") {
return "InvalidInput";
}
}
}
}
Outputs:
phpunit SoapFaultTest.php
PHPUnit 3.6.3 by Sebastian Bergmann.
.
Time: 0 seconds, Memory: 3.75Mb
OK (1 test, 2 assertions)
The assertion is kept simple but you should be easily able to adapt this for your way of handling errors
Next to what edorian answered, there should be no need to test that a third-party component works as announced. Don't write tests for components you don't have written the code (or for which you can not write code, if they are broken, they're broken and you're lost).
Just test that your code works as expected. You should be able to mock your own code.
Example: Your code will always break if code or data of it is missing, e.g. files of your application get partially deleted under circumstances. Do you write tests for such scenarios? Naturally not.
You can however write yourself an integration suite that automatically tests if certain remote dependencies provide the expected interface and responses. But I would put that suite apart from your testsuites that cover your own code-base.
Additionally you can create your own assertion functions that can be used in each testcase, for example one for SoapClient Exception Message.
I want to thank hakre for his comment. This is exactly what I wanted.
The SoapFault extends Exception, which expects an input of ($message, $code, $previous).
But the SoapFault expects ($faultcode, $faultstring, $faultactor, $detail, $faultname, $headerfault).
This is what threw me.
The $faultstring maps to the exception message.
So to mock it, I did:
$mock_soap->expects($this->once())
->method('checkVat')
->will($this->throwException(new SoapFault('a', "Error Message")));
And in my real code
$e->getMessage() now returns 'Error Message'

Handle Selenium RC open method failure (404)

I have a loop that checks for the existence of urls. If one does not exist Selenium exits:
XHR ERROR: URL = http://localhost/pages/156.php Response_Code = 404 Error_Message = Not Found.
If i catch the exception:
try {
$this->selenium->open($url);
}
catch(PHPUnit_Framework_Exception $e) { echo "caught\n"; }
Anything i do afterwards gives me this error:
ERROR Server Exception: sessionId should not be null; has this session been started yet?
I even tried to set the exception as expected:
$this->selenium->setExpectedException('PHPUnit_Framework_Exception');
But still the session is stopped and the test is completed. How can i make Selenium keep testing the urls? Thanks.
Create new selenium instance for every url!
$this->selenium = new Testing_Selenium("*firefox", "http://your-app-under-test.org/");
$result = $this->selenium->start();
$this->selenium->open("/the-page-to-test.php");
or see http://pear.php.net/package/Testing_Selenium/docs/0.4.3/Selenium/Testing_Selenium.html#method__construct for reference, there are more arguments to the constructor.

Categories