Hello,
From some of my posts you’d figure I had a big project, where many problems occurred between SOAP PHP and other services..yeah it happened.
I had to implement connections with various security protocols, the other day at StackOverflow I noticed someone having problem with this, and most of the posts have no responses at all.
This specific post will show you to establish a connection to a SOAP WebService that is secured with WSSE, the specific doc is Web Services Security Username Token Profile: http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd
In order to structure it clearly, and make sure PHP doens’t f*ck while generating the XML, we create various classes.
First we need a Class to store our username and password.
1 2 3 4 5 6 7 8 |
class WSSEAuth { private $Username; private $Password; function __construct($username, $password) { $this->Username= $username; $this->Password= $password; } } |
Then we need a class to store our Username Token related to this service.
1 2 3 4 5 6 7 8 9 |
class WSSEToken { private $UsernameToken; function __construct($innerVal) { $this->UsernameToken = $innerVal; } } |
Now instead of extending the SoapClient, which made errors happened in other places of communication with various other SOAP APIs.
I just make client inside another class, with the security options prepared and then return it to be used elsewhere in the code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
class MySoapClient { private $username; private $password; private $options; private $header; private $wsdl; private $wsse; private $gw; public $start_time; public function getClient($wsse, $user, $pw, $wsdl, $gw) { $this->wsse = $wsse; $this->username = $user; $this->password = $pw; $this->wsdl = $wsdl; $this->gw = $gw; $this->start_time = microtime(true); $this->options = array( 'trace'=>1, 'username'=>$this->username, 'password'=>$this->password, 'exceptions'=>true, 'connection_timeout'=>5, ); $objSoapVarUser = new SoapVar($this->username, XSD_STRING, NULL, $this->wsse, NULL, $this->wsse); $objSoapVarPass = new SoapVar($this->password, XSD_STRING, NULL, $this->wsse, NULL, $this->wsse); $objWSSEAuth = new WSSEAuth($objSoapVarUser, $objSoapVarPass); $objSoapVarWSSEAuth = new SoapVar($objWSSEAuth, SOAP_ENC_OBJECT, NULL, $this->wsse, 'UsernameToken', $this->wsse); $objWSSEToken = new WSSEToken($objSoapVarWSSEAuth); $objSoapVarWSSEToken = new SoapVar($objWSSEToken, SOAP_ENC_OBJECT, NULL, $this->wsse, 'UsernameToken', $this->wsse); $this->header = new SoapVar($objSoapVarWSSEToken, SOAP_ENC_OBJECT, NULL, $this->wsse, 'Security', $this->wsse); $objSoapVarWSSEHeader[] = new SoapHeader($this->wsse, 'Security', $this->header, true, $this->gw); $objClient = new SoapClient($this->wsdl, $this->options); $objClient->__setSoapHeaders($objSoapVarWSSEHeader); return $objClient; } } |
I have trace, exceptions and connection_timeout activated which are all optional.
After all those 3 classes are created, I can just access my prepared client like this:
1 2 |
$var = new MySoapClient; $var_client = $var->getClient('http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd', 'your_user', 'your_password', 'your_wsdl', 'your_gate'); |
Obvious as it seems, you need to indicate your user, pw, wsdl and gate (optional) to connect.
Afterwards you use it just as normal, example:
1 |
$var_client->__soapCall('Function', $soap_payload); |
Hope this comes in handy for some of you..
Happy coding!
Hello guys,
Do you use PHP? Do you use SOAP (you better :p) default extension?
This post is to be applied in that case and in that case only.
I don’t like NuSOAP and I think it has no use anymore.
This is a pretty neat ‘hack’. After sending some data to a webservice, say you need to save the XML that is easy.. PHP gives you your request in XML.
But say you need to save the payload as it is, just to re-send it later for some reason.
The payload is the final PHP Soap Object you constructed. (Works in arrays too as the PHP SOAP Object needs to be converted to array to be sent anyway).
You may say, well I will use serialize, and store the serialization of the object, however PHP serialize does NOT work with PHP SOAP Objects.
It doesn’t recognize the classes and the objects get malformed.
This is a trouble you only experience if you are really trying to rebuild a SOAP Object and not a simple array, but trust me there are cases in which you need SOAP Objects and not just arrays to enforce complicated hierarchy of namespaces.
You move on to json_encode/decode? Then it just won’t work as all the objects will be re-constructed into Array’s..
So what you actually need is a re-constructive function that applies the correct decode transformation to a previous json_encode of the entire Payload SOAP Object.
So after you apply json_decode and you have a extensive Array. You apply this “””beautiful””” (read neat hack) function I created to reconstruct it accordingly.
You may want to add some switch’s in case you used any different type besides XSD_STRING or just SOAP_ENC_OBJECT when constructing the parameters.
I don’t use any other type seeing as it doesn’t matter when it gets transformed to XML.
Here’s the neat function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
function soapifyAO(array $data, $type_cast) { foreach ($data as $k=> &$value) { if (is_array($value)) { if ($k == '0') $value = soapifyAO($value, 2); elseif ($k == 'enc_value') $value = soapifyAO($value, 3); elseif (isset($value[0])) $value = soapifyAO($value, 1); else $value = soapifyAO($value, 2); } } switch ($type_cast) { case 1: return new ArrayObject($data); break; case 2: switch ($data['enc_type']) { case 101: return new SoapVar($data['enc_value'], XSD_STRING, NULL, $data['enc_ns'], $data['enc_name'], $data['enc_namens']); break; case 301: return new SoapVar($data['enc_value'], SOAP_ENC_OBJECT, NULL, $data['enc_ns'], $data['enc_name'], $data['enc_namens']); break; } break; case 3: return (object) $data; break; } } |
Enjoy. Share!
Today again I’m here because there’s a special Modulus Check Digit of the norm ISO/IEC 7064, the Mod 11,2, that I wasn’t able to find the code for in PHP.
To help you all I decided to build a version of it. Mod 11 2 isn’t a very known modulus because it isn’t used very often and perhaps that’s why there isn’t much information around.
For example the Mod 97,10 is used to calculate the check digit of an IBAN (International Bank Account Number) and so you can find it in many places.
The Mod 11-2 is used in medical related services, that’s where you will find it most of the times.
Code can also be found in the gist here and in the box below.
Share, enjoy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
class Iso7064mod112 { public $code; public function encode($code) { $this->code = $code; $c = $this->computeCheck($this->code); if ($c == 10) { return $this->code . "X"; } else { return $this->code . $c; } } public function verify($code) { $this->code = $code; return (computeCheck(substr($this->code, 0, -1)) == getCheckDigit($this->code)); } public function computeCheck($str) { $p = 0; for ($i = 0; $i < strlen($str); $i++) { $c = $str[$i]; $p = 2 * ($p + $c); } $p %= 11; return (12 - $p) % 11; } public function getCheckDigit($str) { $c = substr($str, -1); if ($c == 'X' || $c == 'x') { return 10; } else { return $c; } } } |
So you found yourself looking for one of the following keywords ‘PHP SOAP Repeated Element Name‘ or ‘SOAP Structure Repeated Name‘.
Then look no further, I’m going to show you how to properly address this problem.
PHP SoapClient is not the most complete library out there, it’s actually rather poor at times, if you have the time to test and implement, a better, faster, more complete library to replace PHP SoapClient, then google this: WSO2 Web Services Framework for PHP (WSO2 WSF/PHP), it’s a paid-turned-open source project.
In reference to our problem, you may have a structure like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
<Competition> <Dogs> <Dog> <Gender>Male</Gender> <Breed>Aidi</Breed> </Dog> <Dog> <Gender>Female</Gender> <Breed>Labrador</Breed> </Dog> </Dogs> </Competition> |
And whether you are building the response using Objects or Arrays, the answer is pretty much the same.
In case you are working with a complex WSDL Specification you are probably using Objects, for perfect control over the namespaces.
The Solution
The simplest solution relies on a pretty little thing called, ArrayObject, implemented in PHP 5, which can be used whether you are building an Array or an Object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// Note, this is a method which can be used in simple and complex responses $dogParams = new ArrayObject(); // Dog Parameters $dogs = new ArrayObject(); // Dog Element $Competition = new stdClass(); // Competition Element // * Note the hard-coded 0, because this is an example $dogParams[0]->Gender = 'Male'; $dogParams[0]->Breed = 'Aidi'; // If you are building a complex response, you could have gone for this // example using a SoapVar $dogParams[1]->Gender = new SoapVar("Female", XSD_STRING, NULL, $namespace, 'Gender', $namespace); $dogParams[1]->Breed= new SoapVar("Labrador", XSD_STRING, NULL, $namespace, 'Breed', $namespace); // Now that we have two (2) dogs information, create Dog object $dog=[]; $dog[0] = new SoapVar($dogParams[0],SOAP_ENC_OBJECT,NULL,$namespace,'Dog',$namespace); $dog[1] = new SoapVar($dogParams[1],SOAP_ENC_OBJECT,NULL,$namespace,'Dog',$namespace); // and append it to Dogs $dogs->append($dog[0]); $dogs->append($dog[1]); // And now our outside structure, Competition element $competition->Dogs = new SoapVar($dogs, SOAP_ENC_OBJECT, NULL, $namespace, 'Dogs ', $namespace); |
If you are a building simple response without need for namespace declarations, you can just put the value instead of using SoapVar’s.
The example above will output the XML repeated element example above when sent via SOAP.
PHP snippet to Watermark Images Automatically.
This snippet brings a one line .htaccess, if you choose to use it, any call to a image on your website, gets automatically watermarked, stopping anyone from downloading the image without the watermark.
You can also center the watermark in the following ‘variable name‘ positions:
You can either change the default settings in the .php file, or if you invoke the script direct directly you can pass parameters either in .htaccess or in a html page, as you can see in the demo.
The scripts uses two merging type functions (imagecopy and imagecopymerge).
In case you already have a .PNG or .GIF with the opacity set, you can set the script to just overlay it, otherwise if you use a JPEG or any other file without transparency set, you can choose the opacity and the script will merge it accordingly.
You can find the code over at github. You can find a Demo here.
Enjoy.