Customizing and extending PHP’s SoapClient

The last time I had to work with Soap was when I was in college. Fortunately most webservices are RESTful these days, which are a lot easier to use in my opinion. I recently however, had to connect to two different webservices using SOAP which forced me to polish up my skills in this area. The first webservice was quite simple, but the second one was aweful. It required the XML to have all kinds of different attributes and namespaces, which I discovered to my surprise, is close to impossible to implement using PHP’s core SoapClient class. There simpy isn’t something like a method ‘addAttribute’.

I finally managed to crack it after days using PHP’s DOMDocument. I found a vague example of someone using it, but the problem was that they were using an override of __doRequest to return the result, which is XML. While SoapClient returns objects and handles exceptions. And that is exactly what I wanted it to do. This is how to do it:

This, of course, is a basic example. Let me explain what’s going on; some understanding of PHP’s SoapClient and Soap is required. Soap enables you to call webservices by sending data in XML format. WSDL describes the methods you can call on the webservice. Soap-xml should contain a reference to the WSDL file so that it can execute and process the calls. Let’s say you have a webservice with a method ‘doSomething’ you want to call. You initialize a SoapClient with the proper endpoint for the webservice and call ‘doSomething’ on that SoapClient instance. Of course SoapClient doesn’t have this method and will attempt to call this method on the webservice. It will validate the XML and deWDSL in the process and throw an exception if it contains errors. If it succeeds, it will return an object which it creates from the XML response it gets from the webservice.

In my example above, I gave my SoapClient a method ‘doSomething’. I did that because I want to call ‘doSomething’ on the webservice but I need to customize the XML first. Because SoapClient doesn’t offer me the methods to do this properly, I created the custom SoapClient and the method ‘doSomething’ in it. I now call this method with all the parameters I need to build up the XML. In this method I use DOMDocument to create the XML. DOMDocument is a powerful XML building tool which I can use to create exactly the XML I need. When I’m done I assign the XML to property $requestXml. Then I call SoapClient’s __soapCall() – method and pass the name of the remote method I want to call as string (‘doSomething’). Normally SoapClient does this for us when the method ‘doSomething’ doesn’t exist using PHP’s magic methods.

I created an override for method __doRequest which is also used by SoapClient to make the requests. __soapCall somewhere triggers a call to __doRequest, for which I created an override. As you can see, I replaced the first two parameters with my data and call __doRequest on the parent. These two parameters are built by SoapClient when a none-existing method is evoked on SoapClient : in this case I called parent::doSomething, which doesn’t exist on the parent. The $request param will just contain some empty XML because I called ‘doSomething’ on the parent without any parameters. I now replace it with my XML and call __doRequest on the parent. SoapClient now handles the rest of the work for me as it usually would do. My method ‘doSomething’ will now return an object or throw a SoapFault just as SoapClient would normally do.

I hope this made some sense, it’s kind of complicated to make this a simple story. Just let me know if you have any questions.

HTML2PDF : undefined offset

While this blog is mainly about code-examples, I’d like to post an occasional bugfix when it’s a nasty one. This is one of them.

The HTML2PDF library for PHP is a great library for converting HTML to PDF. I ran into a problem in an application where an exception was thrown by this library with the vague message ‘Undefined offset 5‘.

At first I thought it was my code, but as I wasn’t using any arrays I started inspecting the HTML closer and finally noticed a table-element was causing the problem. Because of a miscalculation with the rowspan-attribute of one of the table-cells, not all the table-rows contained an equal amount of columns. This explains the undefined offset issue; the library has to convert to table to pdf and uses arrays in the background to convert the table data. A mismatch in columns will break this code. The library does not account for invalid html in this case and continues on the assumption the table-markup is correct. This explains the vague error message.

I Hope this solves your issue. If this is not the case, start leaving out parts of your HTML untill the error dissapears to discover what element might be causing it.

Laravel 5 : Creating Console Commands

Anytime something causes me a great deal of time to figure out, I usually decide it’s worth posting. In this case it’s console commands in Laravel 5. Laravel 5 has just been released with some great improvements. Somewhat confusing is the usage of commands. Commands now appear to be some kind of event driven delegates. It is all well documented but not so well documented is wat happened to console commands, something we still need for our cronjobs and serious development work like running imports.

Here’s how to walk the walk in Laravel 5:
Generate the command
Console commands are now created with the following Artisan command :

In this case ‘GetMeABeerCommand’ is the class name of my command. You replace this with your own class name unless you need a beer too. The generated command will now be available in your App/Console/Commands folder.

Provide a console name and description
Now open this class and fill out a name for your command and a description :

The name your filled out will be the name you use to run the command from the console. Use alphanumeric characters, no spaces or funny stuff (I haven’t experimented with what characters are allowed but just stick to simple). This can be what ever you choose, but it’s logical to have it reflect what the command class does. So having a console command class ‘GetMeASodaCommand’ with a $name ‘command:get-beer’ doesn’t make much sense.

Register the console command
As with Laravel 4 you still have to register the command with a ServiceProvider. Laravel 5 installs with an AppServiceProvider (/App/Providers/) which you can use for this occassion, or you can create your own service provider. Just don’t forget to add it to the list/array of service providers in app.php if you decide to create your own.

You register the console command in the boot-method:

The ‘ABCD’ namespace here is my namespace I made up. In Laravel 5 you set your namespace with the Artisan command as you might now. The AppServiceProvider is generated at install and will already have the proper namespace. The only thing you have to to is add the lines in the boot-method. Notice how ‘get-beer’ is the name I provided above in the console command class.

Run the command

Go to your console (root of the website) and type :

You will now see a list of available options, your command will be listed under ‘commands’. You can run the command like this:

your-command-name in this case is the name you provided in the $name property of your console command class. In my case that’s ‘get-beer’. So I would run :

That’s all! I’m getting a beer. Happy coding!