How to add attributes to soapVars - php

I'd like to create soapVars with attributes like this:
<tag attr="xxx">yyy</tag>
Is this possible with the SoapVar constructor, but without using XSD_ANYXML and raw xml strings?

The best way to do it is:
<?php
$tag['_'] = 'yyy';
$tag['attr'] = 'xxx';
$tagVar = new SoapVar($tag, SOAP_ENC_OBJECT);
?>
the result would be:
<tag attr="xxx">yyy</tag>

After spending many hours searching for a solution, I only found this workaround. Works in my case.
/**
* A SoapClient derived class that sets the namespace correctly in the input parameters
*/
class SoapClientNS extends SoapClient {
// return xml request
function __doRequest($request, $location, $action, $version, $one_way = NULL) {
//Replace each <Typename> with <ns1:Typename> or
$request = str_replace('RequestBase', 'ns1:RequestBase', $request);
return parent::__doRequest($request, $location, $action, $version, $one_way);
}
}
$client = new SoapClientNS($wsdlURL);
$client->getAllBooks(array('RequestBase' => array('code' => 'AAAA', 'password' => '234234fdf')));
The request XML was something like this:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://www.travco.co.uk/trlink/xsd/country/request">
<env:Body>
<ns1:getAllBooks>
<RequestBase code="AAAA" password="234234fdf"/>
</ns1:getAllBooks>
</env:Body>
</env:Envelope>

pcmind's answer did not work for me, and also not if you try it in PhpFiddle.
I stumbled on this article, which basically creates xml with XMLWriter:
http://eosrei.net/articles/2012/01/php-soap-xml-attributes-namespaces-xmlwriter
This totally works for my case.

Related

SOAP - PHP is possible to change 'ns1' tag name?

I need send a XML with this format:
<soapenv:Body>
<loc:amount>
<loc:user>user_name</loc:user>
<loc:charge>
<description>description_text</description>
</loc:charge>
</loc:amount>
I have generate the XML body with soapVar, but I cant change "ns1" tag.
Source:
$var = '<loc:amount> <loc:user>user_name</loc:user> <loc:charge> <description>description_text</description> </loc:charge> </loc:amount>';
$params[0] = new SoapVar($var,XSD_ANYXML,'amount','http://mydomain.cm');
And the header result is this:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://mydomain.cm" xmlns:ns2="http://header_domain.cm">
I need this result:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:loc="http://mydomain.cm" xmlns:ns2="http://header_domain.cm">
Is possible to change 'ns1' tag to 'loc'??
Thanks
Namespace prefix names are insignificant; it is only through their binding to a namespace value that they derive meaning. No conformant XML processor will care about the specific namespace prefix names; you should not either.
I've such an issue with UPnP Devices, which use "u" as default namespace-prefix and I need to change it from ns1. As I also found no better solution, I'll post my solution here.
class UpnpSoapClient extends SoapClient {
public function __doRequest($request, $location, $action, $version, $oneWay = 0)
{
// Some fucking UPnP Devices need the fucking u as namespace-prefix ... WTF?
if (preg_match('/xmlns:ns1=/', $request)) {
$request = preg_replace('/xmlns:ns1=/', 'xmlns:u=', $request, 1);
$request = preg_replace('/ns1:/', 'u:', $request);
}
return parent::__doRequest($request, $location, $action, $version);
}
}

How can I add urn to SoapClient on PHP?

I have this xml generated by SoapUI from this wsdl:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:safetypay:mws:api:messages">
<soapenv:Header/>
<soapenv:Body>
<urn:CommunicationTestRequest>
<urn:ApiKey>?</urn:ApiKey>
<urn:RequestDateTime>?</urn:RequestDateTime>
<urn:Signature>?</urn:Signature>
</urn:CommunicationTestRequest>
</soapenv:Body>
</soapenv:Envelope>
It Works ok, but when I create it with the client for PHP, SoapClient, it fails... I have found that the error its because it is slightly different, it is only missing the urn (which I have no idea what it is). The xml from SoapClient looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<CommunicationTestRequest>
<RequestDateTime>2012-04-17T23:21:54</RequestDateTime>
<ApiKey>XXXXX</ApiKey>
<Signature>XXXXX</Signature>
</CommunicationTestRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The three main diferences are:
1) In the definition the code generated by SoapClient is lacking of this:
xmlns:urn="urn:safetypay:mws:api:messages"
2) In the xml generated by SoapUI I have soapenv, while in the one generated by SoapClient it is SOAP-ENV
3) In the SoapUI's I have urn before each child, the SoapClient's not.
I've spend almost the whole day changing configurations and moving backward from objects to array (since in the past it works with other wsdl's), and my research on the internet didn't gave me nothing... so... can anyone have any idea of what's my mistake?
The request is this:
$std = new RequestDateTime();
$std->RequestDateTime = date ( 'Y-m-d\TH:i:s', time () );
$std->ApiKey = SafetyPaySoap::API_KEY_SAND;
$std->Signature = SafetyPaySoap::SIG_KEY_SAND;
$this->_wsdl->CommunicationTest($std);
where the configuration for the SoapClient is this one
$config = array ();
$config ['exceptions'] = true;
$config ['trace'] = true;
$config ['cache_wsdl'] = WSDL_CACHE_NONE;
$config ['soap_version'] = SOAP_1_1;
parent::SoapClient ( $url . '?wsdl', $config );
Edit:
I was able to fix this error. I had to rewrite the __doRequest method. And it now looks like this:
public function __doRequest($request, $location, $action, $version, $one_way = null) {
$request = str_ireplace("<{$this->_method}Request>", "<{$this->_method}Request xmlns='urn:safetypay:mws:api:messages'>", $request);
return parent::__doRequest ( $request, $location, $action, $version, $one_way );
}
So, what I'm doing is add the namespace at the request, and with that it generates this xml:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<CommunicationTestRequest xmlns='urn:safetypay:mws:api:messages'>
<ApiKey>XXXX</ApiKey>
<RequestDateTime>2012-04-18T20:22:51</RequestDateTime>
<Signature>XXXXX</Signature>
</CommunicationTestRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The urn: is actually a XML namespace. Looking at the documentation for SoapClient, it looks like it passes in the target namespace as a 'uri' entry in the configuration array. It might be worth trying the below and seeing if that makes a difference.
$config = array();
$config['uri'] = "urn:safetypay:mws:api:messages";
// your other configurations
parent::SoapClient('https://mws2.safetypay.com/sandbox/express/ws/v.2.4/MerchantWS.asmx?wsdl', $config);

Sending Raw XML via PHP SoapClient request

I am trying to simply send RAW xml to a webservice via PHP and SoapClient. The problem is when I encode my XML it changes the order of elements in the XML that is converted to an associative array.
// Initialize the Soap Client:
$this->_transactionServicesClient = new SoapClient($soapWSDLUrl);
How or what would be the best way to send the following XML as a string to my SoapClient?
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.micros.com/pos/les/TransactionServices">
<SOAP-ENV:Body>
<ns1:PostTransaction>
<ns1:REQ>
<ns1:RequestHeader>
<ns1:InterfaceVersion>3.0.7</ns1:InterfaceVersion>
<ns1:ClientName>TRANS_SERVICES</ns1:ClientName>
</ns1:RequestHeader>
<ns1:CheckDetailEntries>
<ns1:MenuItem>
<ns1:ReferenceEntry>Pizza4</ns1:ReferenceEntry>
<ns1:Count>1</ns1:Count>
<ns1:Price>10.00</ns1:Price>
<ns1:ItemNumber>112001</ns1:ItemNumber>
<ns1:PriceLevel>1</ns1:PriceLevel>
<ns1:Seat xsi:nil="true"/>
</ns1:MenuItem>
</ns1:CheckDetailEntries>
<ns1:CheckHeaderRequest>
<ns1:CheckId>03:21:05.050505</ns1:CheckId>
<ns1:GuestCount>1</ns1:GuestCount>
<ns1:GuestInformation>
<ns1:ID>001</ns1:ID>
<ns1:FirstName>xxx</ns1:FirstName>
<ns1:LastName>xxx</ns1:LastName>
<ns1:Address1>xxx Rd</ns1:Address1>
<ns1:Address2>xx</ns1:Address2>
<ns1:Address3>xx</ns1:Address3>
<ns1:PhoneNum>xx</ns1:PhoneNum>
<ns1:UserText1>None</ns1:UserText1>
<ns1:UserText2>None</ns1:UserText2>
<ns1:UserText3>None</ns1:UserText3>
<ns1:GUID></ns1:GUID></ns1:GuestInformation>
</ns1:CheckHeaderRequest>
<ns1:OrderTypeNumber>1</ns1:OrderTypeNumber>
</ns1:REQ>
</ns1:PostTransaction>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Update/Resolution: Here is the code I used to extend the SOAP Client and send my raw Soap Envelope: My answer below
Update/Resolution: Here is the code I used to extend the SOAP Client and send my raw Soap Envelope
Here is how I extended SoapClient:
<?php
class MySoapClient extends SoapClient {
function __construct($wsdl, $options) {
parent::__construct($wsdl, $options);
$this->server = new SoapServer($wsdl, $options);
}
public function __doRequest($request, $location, $action, $version)
{
$result = parent::__doRequest($request, $location, $action, $version);
return $result;
}
function __myDoRequest($array,$op) {
$request = $array;
$location = 'http://xxxxx:xxxx/TransactionServices/TransactionServices6.asmx';
$action = 'http://www.micros.com/pos/les/TransactionServices/'.$op;
$version = '1';
$result =$this->__doRequest($request, $location, $action, $version);
return $result;
}
}
// To invoke my new custom method with my Soap Envelope already prepared.
$soapClient = new MySoapClient("http://xxxx:xxxx/TransactionServices/TransactionServices6.asmx?WSDL", array("trace" => 1));
$PostTransaction = $soapClient->__myDoRequest($orderRequest,$op);
?>
Also posted on pastie.org: http://pastie.org/3687935 before I turned this into the answer.
For testing purposes, you can subclass SoapClient and override the __doRequest method - it receives the generated XML and you can pre-process it.
But better find what's going wrong with the XML encoding. You can use SoapVar and SoapParam instances to specify the exact way given object has to be represented. Those saved my life when namespaces had to be given

PHP AND SOAP. Change envelope

It is many questions there about PHP and SOAP. But I am not found answer on my situation.
So. I use PHP SoapClient and WSDL for it. Object sends this:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.site.com"><SOAP-ENV:Body>
But I need this:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body>
Question. How I can do this with standard PHP class SoapClient?
Thank you.
I search answer in php.net
<?php
class MSSoapClient extends SoapClient {
function __doRequest($request, $location, $action, $version) {
$namespace = "http://tempuri.com";
$request = preg_replace('/<ns1:(\w+)/', '<$1 xmlns="'.$namespace.'"', $request, 1);
$request = preg_replace('/<ns1:(\w+)/', '<$1', $request);
$request = str_replace(array('/ns1:', 'xmlns:ns1="'.$namespace.'"'), array('/', ''), $request);
// parent call
return parent::__doRequest($request, $location, $action, $version);
}
}
$client = new MSSoapClient(...);
?>
This code change Envelope in request. And need for ASP SOAP server.
Use SoapVar::typeNamespace for set your xmlns:xsi
something like this :
new SoapVar($data, SOAP_ENC_OBJECT, "Type", "http://www.w3.org/2001/XMLSchema");
source : https://www.php.net/manual/fr/soapvar.construct

How can i create a soap header like this?

Doing some SOAP calls to a 3rd party application. They provide this soap header as an example of what the application expects. How can I create a SOAP header like this in PHP?
<SOAP-ENV:Header>
<NS1:Security xsi:type="NS2:Security" xmlns:NS1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:NS2="urn:dkWSValueObjects">
<NS2:UsernameToken xsi:type="NS2:TUsernameToken">
<Username xsi:type="xsd:string">XXXX</Username>
<Password xsi:type="xsd:string">XXX</Password>
</NS2:UsernameToken>
</NS1:Security>
</SOAP-ENV:Header>
I do what i think is a correct call and keep getting in return that no headers were sent.
Here is a sample from my code.
class SOAPStruct
{
function __construct($user, $pass)
{
$this->Username = $user;
$this->Password = $pass;
}
}
$client = new SoapClient("http://www.example.com/service");
$auth = new SOAPStruct("username", "password");
$header = new SoapHeader("http://example.com/service", "TUsernameToken", $auth);
$client->__setSoapHeaders(array($header));
$client->__soapCall("GetSubscriptionGroupTypes", array(), NULL, $header)
And this is the SOAP header i get back. (its more but i stripped info away that might be sensitive)
<SOAP-ENV:Header>
<ns2:TUsernameToken>
<Username>username</Username>
<Password>password</Password>
</ns2:TUsernameToken>
</SOAP-ENV:Header>
SOAP header handling in PHP is actually not very flexible and I'd go as far as saying that especially the use two namespaces within the header will make it impossible to inject the header simply by using a SoapHeader-construct of some type.
I think the best way to handle this one is to shape the XML request yourself by overriding SoapClient::__doRequest() in a custom class that extends SoapClient.
class My_SoapClient extends SoapClient
{
public function __doRequest($request, $location, $action, $version, $one_way = 0)
{
$xmlRequest = new DOMDocument('1.0');
$xmlRequest->loadXML($request);
/*
* Do your processing using DOM
* e.g. insert security header and so on
*/
$request = $xmlRequest->saveXML();
return parent::__doRequest($request, $location, $action, $version, $one_way);
}
}
Please see SoapClient::__doRequest for further information and some examples.

Categories