Simple SOAP client with wsdl2php using WSDL

Nusoap used to be my favourite php toolkit for SOAP communication. But I was struggling a lot with it to make a simple client from the wsdl file. I really liked the way .NET SOAP modules parse the wsdl in a second and give you classes that you can use in your code. So why not having the same thing in php?

Luckily for me somebody thought of it first. Enter wsdl2php. It is simple, lightweight and easy to use. It parses the wsdl file and creates ready to use php classes. It is really handy for tackling those complex data types that you might have on your web services. It can be used both on Linux and Windows from the php command line.

So go ahead and download the latest version from sourceforge. If you are on a Linux distribution you can use the pear installer to install it from the .tgz file:

sudo pear install wsdl2php-0.2.1-pear.tgz

and then use it as a command line app with:

wsdl2php http://yoursoapserver/Service.wsdl

If you are on Windows, no worries, you can also extract the tgz and call the wsdl2php.php file from the command line with the URL to the WSDL as an argument. Something like:

C:\PHP5\php.exe -f "C:\wsdl2php\wsdl2php.php" --  "http://yoursoapserver/Service.wsdl"

There are also more info how to use php cli under windows on the php manual pages.

The script should generate a php file in the working directory with all the classes and data types that are found on the WSDL. The generated client class extends php's SoapClient class, which shold come with the php core. You just have to include it in your php app, and you are ready to go:

require_once 'YourWebService.php';
$client = new yourWebService();
$input = "Your Input"; # check the generated classes to find out
                       # what the required input should be. 

try{
   echo $client->someFunction($input);
}
catch (Exception $e){
   echo $e->getMessage();
}


If you are dealing with complex data types, be sure to check the generated classes to determine what your input variables should look like.

If you learn to use this tool, it should make your work with SOAP and WSDL a lot easier.

However if you are struggling to hard and still can not make a soap client work with a wsdl file and generate the desired XML output read my blog post about posting raw XML with nusoap to a SOAP Web Service

Multiple table delete: Cascade delete vs Join Delete

There is this really old php/mysql project that I maintain and every know and than there are some problems with data integrity, orphaned records and constraint violations.

After some debugging I found out that the main problem is that related data is deleted in separate delete statements, and some of them throw exceptions and don't get executed.

I immediately thought of making a CASCADE DELETE on the constraints, but as I started reading on google about cascades I found out that many people are against it. Personally I have had at least 2 or 3 bad experiences with accidentally deleting some data and then reflecting that on the cascaded data. Probably that is why I try to avoid cascading.

The other option is to make an inner join on the delete statement.

Let's say that we have such a model: User 1:N Service 1:N Service_Parameter

The query would look something like this:

delete user.*, service.*, service_parameters.* 
from user 
inner join service on service.user_id = user.id 
inner join service_parameter on service_parameter.service_id = service.id 
where user.id = $id


People seem to be against this idea as you would have to remember the data relation and execute such a query whenever you are deleting data. However if your application code is written properly you would usually have a delete method where this query would be written, and then execute this method (or stored procedure) whenever you want to delete something.

If you delete something manually from the DB, accidentally or intentionally, you would not delete the cascaded data. This might be a pro or a con whichever way you want to look at it.

Perssonally I would like to have to make more separate deletes or a stored procedure with the above query rather than using the dreaded CASCADE DELETE and not having an explicit control of what am I deleting.

Validating Cyrillic (UTF8) alphanumeric input with PHP preg_match and regular expresions

Recently I needed to make a validation rule that a Cyrillic alphanumeric string was entered in an input field. I saw answers like matching one by one all the characters in the alphabet. Luckily I found a more clever solution.

It can be done with preg_match() with the following pattern:


$str="ABC abc 1234 АБВ абв";

$pattern  = "/^[a-zA-Z\p{Cyrillic}0-9\s\-]+$/u";

$result = (bool) preg_match($pattern, $str);
if($result)
   echo "$str is composed of Cyrillic and alphanumeric characters\n";



Here is a decomposition of the pattern:

a-zA-Z is for the Latin characters. You can omit this if you want only Cyrillic input.

\p{Cyrillic} is for the cyrillic characters. You can also use \p{Arabic}, \p{Greek} or other alphabeths. See this site for a full list of utf8 scripts.

0-9 is for the numbers.

\s is for the space

\- is for the dash

Sending raw XML with php nusoap client

In my last blog post I described how to use wsdl2php to parse a wsdl file and generate php classes. This should make your life a lot simpler when dealing with SOAP Web Services.

But there are times that all the frameworks and toolkits in the world wont be able to produce the desired plain XML structure of the SOAP call. Especially when you are working with Chinese low-end equipment.

I usually work in php nusoap and I had trouble finding the right way to do this. I was so desperate, that I have been posting raw XML with CURL to mimic the SOAP Request. Luckily I found a way to do i with our favourite php soap toolkit.

Thow following is an example that calls a function insertUser with raw XML:


require_once('nusoap/lib/nusoap.php');

$endpoint = "http://yoursoapserver.com/SomeSoapService";

$client = new nusoap_client($endpoint, false);

$msg = $client->serializeEnvelope("
<ns1:insertuser xmlns:1="http://yoursoapserver.com/">
  <arg0 xsi:type="xsd:string">name</arg0>
  <arg1>
    <username xsi:type="xsd:string">username</username>
    <password xsi:type="xsd:string">0ac495f743a36cef9b0eaafa92ae08e21</password>
  </arg1>
  <arg2 xsi:type="xsd:string">email</arg2>
  <arg3 xsi:type="xsd:string">domain</arg3>
</ns1:insertuser>
");

$result=$client->send($msg, $endpoint);

print_r($result);

Mysql database migration and character set conversion

How often have you stumbled on a mysql dump file with strange hieroglyphs. You have an old Mysql 4 database in latin1 character set and you need to migrate it on a new server with utf8 support.

The procedure is simple and easy as long as you know the steps.

The first thing you need to do is make a mysql dump with the flag --default-characer-set set as the charset of the source db:

mysqldump --default-character-set=latin1 -u user -p old_db > dump.file

Now you need to replace all inctances of the old charset (latin1) in the dumpfile with the newcharset (utf8). You can do this with sed from the command line

sed "s/latin1/utf8/" dump.file > new_dump.file

Create the new database with the desired charset on the new server with the SQL command:

CREATE DATABASE new_db CHARSET utf8 COLLATE utf8_general_ci

And finaly restore the database with the command

cat new_dump.file | mysql --default-character-set=utf8 -u user -p newdb

Afterwards be sure too make all sql connections with utf8 encoding. You can execute this sql command imminently after you make a the connection to the db from the application:

SET NAMES utf8

And that is all there is to it.

Automate web clicks in perl with WWW::Mechanize

I have been struggling a lot lately with incomplete API's to systems that I depend on. Badly documented XML's, incomplete SOAP services, complex databases. The idea to automate the click-able processes is not a genuine one, but when you don't have a proper interface it becomes a painstaking effort. As I am a php native, I was struggling a lot with CURL, until I found a comment on a webforum to try perl.

Enter WWW::Mechanize.

In order to use this module you need to install it first, the best way being from perl's cpan:

cpan> install WWW::Mechanize

After the installation is complete u can use it in your perl script by adding:

use WWW::Mechanize;

The first thing you need to do is get the web page where the desired form is located. You write

my $mech = WWW::Mechanize->new();
$mech->get("http://somesite.com/index.php");

After you get the web page you would usualy like to submit a form. You can chose the form you want to submit in two ways by name or by number. You need to specify the fields that you want to submit, and the submit button. These two options are optional and can be omitted.

Lets say you need to login somewhere:

$result = $mech->submit_form(
form_name => 'frmLogin', #name of the form
#instead of form name you can specify
#form_number => 1
fields      => 
{
 txtUser    => 'admin', # name of the input field and value 
 txtPass    => 'adminpass',
}
button    => 'btnSubmit' #name of the submit button
);

Retrieving server reply

After you log in, $mech will keep the session data and you can continue to the next step. In case you need to see what did the web site reply after you submitted the form, you can print out the the returned html with:

print $result->content();

You might also need this if you need to parse the returned html.

Multi-step automation


In case you have to complete several steps, don't worry just continue on as with the first step. Mech keeps the last returned html web page, so you can go on and submit a second form from with the same procedure:

#STEP 1: Login
$mech->submit_form(
form_name => 'frmLogin', 
fields      => 
{
 txtUser    => 'admin', 
 txtPass    => 'adminpass',
},
button    => 'btnSubmit' 
);

#STEP 2: Input form
$mech->submit_form(
form_name => 'frmInput', 
fields      => 
{
 txtField    => 'some value', 
 selectbox    => 'another value',
},
button    => 'btnSubmit' 
);

Beware of JavaScript


The only drawback with WWW::Mechanize is that it does not support javascript. To bad as most modern web pages have at least some client side scripting. In case you cant go pass the javascript, try to figure out what it is doing, and try to simulate it. Ussually a simple javascript will populate some select boxes or set some hidden fields. Always try to find what are the exact values that you need to submit, and go with it.

Examples

In addition you can find a complete example of simple form submission. The script connects to a network device's web interface and reboots it. It takes a single argument, the ip of the device. It is executed from the command line with

perl reboot_device.pl 192.168.1.1

#!/usr/bin/perl -w
use WWW::Mechanize;

my $ip = $ARGV[0];
 
my $mech = WWW::Mechanize->new();

$url_logon = "http://$ip:8080";
$url_reboot= "http://$ip:8080/reboot.htm";

$mech->get($url_logon);
$mech->submit_form(
form_number => 1,
fields      => 
{
 user    => 'admin',
 pass    => 'adminpass',
}
);

##### STEP 1: DATES #####
$mech->get( $url_reboot );
$mech->submit_form(
form_number => 1,
button    => 'submit'
);

Working around

Working around is one of the main job descriptions of an ambitious developer. Especially when you are a third-world ambitious developer in a company with low-budget high expectations.

Actually I am not here to complain. I love working around. I love developing interesting and clever solutions that solve hopeless and expensive problems. My whole carer is based on workarounds, and I would like to share it. Because sharing is caring.

My favorite part about workarounds is the political aspect. When a simple hack saves your company X thousand of dollars that you can distribute as a bonus to your employees, it goes way beyond technology and economics. It becomes political, as you take the goodies away from the big players, and give it to the community. Kinda like Robin Hood with geeky glasses.

This is my opening blog post. What I plan to do in the future is share my experience and keep it simple. Until then, I will be working around.