PHP SoapClient - Possible name spacing issues - non-wsdl mode - php

I previously managed to call a .NET service using PHP's SoapClient library without too many issues, but now I face a scenario where I have to access the same services in non-WSDL mode.
I seem to have created, more or less, the correct XML structure to send through to the service, but the server does not seem to be picking up the variables I am sending.
Based on output from SOAPUI, I should be attempting to generate the following SOAP call:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:int="http://someservices.com/">
<soapenv:Header/>
<soapenv:Body>
<int:getService>
<int:foo>String value for foo</int:foo>
<int:bar>String value for bar</int:bar>
</int:getService>
</soapenv:Body>
</soapenv:Envelope>
The output that I am generating with SOAPClient is:
<?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://someservices.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:enc="http://www.w3.org/2003/05/soap-encoding">
<env:Body>
<ns1:getService env:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<foo xsi:type="xsd:string">String value for foo</foo>
<bar xsi:type="xsd:string">String value for bar</bar>
</ns1:getService>
</env:Body>
</env:Envelope>
The error returned from the .NET service is "bar expected!" - indicating, at the very least, that the service is not picking up my second parameter.
I am constructing my soap call using the following:
$foo = 'String value for foo';
$bar = 'String value for bar';
$options = array(
'trace' => 1,
'exceptions' => 1,
'uri' => 'http://someservices.com',
'soapaction' => 'http://someservices.com/getService',
'soap_version' => SOAP_1_2,
'type_map' => array('type_ns' => 'int')
);
$response = $soapclient->__soapCall('getService', array( new SoapParam($foo, 'foo'), new SoapParam($bar, 'bar')), $options);
I am not sure what the issue is. Right now I am guessing that it might be an xmlns attribute issue. Note that the SOAPUI call specifies the int: in front of the function calls and parameters. I have tried to specify this xmlns but without luck. As you can see xmlns generated by Soap Client call is:
xmlns:ns1
Any help would be greatly appreciated.

As it turns out, having the namespace labelled as ns1 instead of int was not the issue.
The problem was that the getService parameter was being prefixed with the namespace ns1, whilst the variables foo and bar were not.
As such, this was resolved by appending "ns1:" to the Soap Params in the soapCall function, as follows:
$response = $soapclient->__soapCall('getService', array( new SoapParam($foo, 'ns1:foo'), new SoapParam($bar, 'ns1:bar')), $options);

Related

Troubles parsing XML object in PHP (laravel 5.8)

I'm having troubles parsing an XML object in PHP. I'm using Laravel 5.8
Here is one of my tries:
$xml = new \SimpleXMLElement($formatted, LIBXML_BIGLINES);
var_dump($xml->children('soapenv', true)->Envalop->Body->children('ns3', true)->getAddressBookResponse->addressBook[0]->businessUnit);
And I'm getting the following error:
Call to a member function children() on null
I have tried different variations of creating and accessing the SimpleXMLElement but always with the same result. I'm getting back an empty object of typeSimpleXMLElement
Here is a sample XML that I'm using as input (coming from SOAP API call):
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns3:getAddressBookResponse xmlns:ns2="http://oracle.e1.bssv.JPRCUST0/"
xmlns:ns3="http://oracle.e1.bssv.JPR01000/">
<e1MessageList>
<e1Messages>
<message>Description: All records for the query have not been returned.
</message>
</e1Messages>
</e1MessageList>
<addressBook>
<businessUnit>123456</businessUnit>
<categoryCodesAddressBook>
<categoryCode001>XXX</categoryCode001>
<categoryCode002>XXX</categoryCode002>
</categoryCodesAddressBook>
<description1>MOHAMEDHASSANALI</description1>
</addressBook>
<addressBook>
<businessUnit>789789</businessUnit>
<categoryCodesAddressBook>
<categoryCode001>YYY</categoryCode001>
<categoryCode002>YYY</categoryCode002>
</categoryCodesAddressBook>
<description1>ALIHASSANAHMED</description1>
</addressBook>
</ns3:getAddressBookResponse>
</soapenv:Body>
</soapenv:Envelope>
That is not just XML, but SOAP. I suggest using a SOAP library (like ext/soap).
It's Envelope not Envalop. And this is the element in you $xml variable.
If you want to treat it as just XML, do not rely on the namespaces prefixes but the actual namespace URIs (the values of the xmlns attributes). Prefixes can change. A prefix like ns3 is auto generated by a SOAP library, just adding/removing an element with another namespace could change it. So define constants or variables for the XMLNS values and use them.
const XMLNS_SOAP = 'http://schemas.xmlsoap.org/soap/envelope/';
const XMLNS_ADDRESSES = 'http://oracle.e1.bssv.JPR01000/';
$envelope = new \SimpleXMLElement($formatted, LIBXML_BIGLINES);
var_dump(
$envelope->children(XMLNS_SOAP)->Body->children(XMLNS_ADDRESSES)->getAddressBookResponse->children('')->addressBook[0]->businessUnit
);
Xpath expressions (SimpleXMLElement::xpath()) allow for conditional fetching. This avoids problems if an element is missing, but you will have to register your own prefixes for the namespaces.
const XMLNS_SOAP = 'http://schemas.xmlsoap.org/soap/envelope/';
const XMLNS_ADDRESSES = 'http://oracle.e1.bssv.JPR01000/';
$envelope = new \SimpleXMLElement($formatted, LIBXML_BIGLINES);
$envelope->registerXpathNamespace('s', XMLNS_SOAP);
$envelope->registerXpathNamespace('a', XMLNS_ADDRESSES);
var_dump(
$envelope->xpath('(s:Body/a:getAddressBookResponse/addressBook)[1]/businessUnit')
);

cannot generate proper soap xml request via php

I'm trying to create proper wsdl based soap request but with no success, here is example of what I need:
soap.xml:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<AuthMember xmlns="http://tempuri.org/">
<somefield>string</somefield>
</AuthMember>
</soap:Header>
<soap:Body>
<AuthenticateMember xmlns="http://tempuri.org/" />
</soap:Body>
</soap:Envelope>
My result is:
soap.xml:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/" xmlns:ns2="http://schemas.xmlsoap.org/ws/2002/07/utility">
<SOAP-ENV:Header>
<ns2:AuthMember>
<somefield>somefieldvalue</somefield>
</ns2:AuthMember>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:AuthenticateMember/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
this is my php code:
class SoapHeaderAuthMember
{
public $somefield;
public function __construct($somefield)
{
$this->somefield = $somefield;
}
}
$client = new SoapClient( 'https://www.somepage.com/service.asmx?WSDL',
array("exceptions"=>0, "trace" => 1 )
);
$authMember = new SoapHeaderAuthMember('somefieldvalue');
$soapHeaders[] = new SoapHeader('http://schemas.xmlsoap.org/ws/2002/07/utility', 'AuthMember', $authMember);
$client->__setSoapHeaders($soapHeaders);
$client->__soapCall('AuthenticateMember',array());
see,
1.it generates SOAP-ENV:Envelope instead of SOAP-ENV:Envelope
2.in header: I have ns2:AuthMember instead of AuthMember
3.in body I have ns1:AuthenticateMember instead of AuthenticateMember xmlns="http://tempuri.org/"
How can I get proper xml ? I've looked through php functions manuals and cannot find the answer, googling did not give me success results for my case.
Could you please help ?
The result that has been generated is what has been requested in the code, but an explanation of some attributes of namespaces is in order:
Each of the tags in an XML document has a fully-qualified form which is defined by the namespace and the tag name (even if it is in the 'default' namespace with no explicit namespace declaration). The fully-qualified version is typically written as {namespace}tag. By resolving the fully-qualified name it is possible to determine whether the representation of two elements is the same.
Namespaces are associated with elements in multiple ways including explicit namespace inclusion with the element
Example:
<AuthMember xmlns="http://tempuri.org/">
resolving to fully-qualified name {http://tempuri.org/}AuthMember
and via namespace-associated prefix
Example:
<... xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
resolving to {http://schemas.xmlsoap.org/soap/envelope/}Header
The prefix is in scope for the element in which the prefix was declared and any contained elements. The actual prefix used is not important (soap, SOAP-ENV or randomprefix could be used for the http://schemas.xmlsoap.org/soap/envelope/ namespace), although good practice is to use something meaningful.
Given that, the fully-qualified elements in the 'desired' and 'actual' documents are almost the same, the anomaly being the header element AuthMember. In your desired you indicate a namespace of http://tempuri.org/ for a fully-qualified {http://tempuri.org/}AuthMember. In the actual the prefix association results in a fully-qualified {http://schemas.xmlsoap.org/ws/2002/07/utility}AuthMember.
This is the result of the SoapHeader instantiation specifying the ...utility namespace for AuthMember. Changing that statement in your code to use the http://tempuri.org/ namespace should result in the actual document matching the desired.

PHP method input parameters

I don't know PHP, just the basics (if so...).
I have a method in my web service like this:
$server->register(
'registerDeviceOnServer', //method name
array('uniqueIdentifier' => 'xsd:string','deviceName' => 'xsd:string', 'systemVersion' => 'xsd:string', 'deviceModel' => 'xsd:string', 'userLocation_Locality' => 'xsd:string', 'userLocation_CountryCode' => 'xsd:string' ), //input data
array('xmlReturn' => 'xsd:string'), //output data
'urn:server.AAAA', //namespace
'urn:server.AAAA#registerDeviceOnServer', //soapaction
'rpc', //style
'encoded', //use
'regista o iDevice' //info for documentation
);
function registerDeviceOnServer($uniqueIdentifier,$deviceName,$systemVersion,$deviceModel,$userLocation_Locality,$userLocation_CountryCode)
{
//some code that talks to the database
//inserting the data into a table
//
//the SQL code works fine. I've tested.
}
When I call the web service, the data does not come in that exact order as it is declared in the php method. Does it have to come in that order, or does PHP assign the data to the vars?
SOAP request:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<registerDeviceOnServer xmlns="urn:server.AAAA">
<uniqueIdentifier>VVVVVVVVVVVVWWWWWWWW</uniqueIdentifier>
<userLocation_CountryCode>n/a</userLocation_CountryCode><deviceModel>iPhone Simulator</deviceModel><systemVersion>4.0</systemVersion><deviceName>iPhone OS</deviceName><userLocation_Locality>n/a</userLocation_Locality>
</registerDeviceOnServer>
</soap:Body>
</soap:Envelope>
So, how can I work this out?
Thanks,
RL
From your comments I can now see that you are actually using nusoap.php and not PHP's own SoapServer.
Check this forum post:
I had a similar issue recently which was caused because I didn't
provide a WSDL url to my SoapServer constructor.
Apparently the soap server can only map message arguments / parameters
when it has a valid WSDL for reference (which more or less made sense
to me afterwards).
Does this help you?
As #akirk says, maybe your problem is related to the WSDL. Make sure that the parameters are in the same order that's in the WSDL

Switching from NuSoap to PHP5 Soap - need a jumpstart

Here's the call I'm trying to make:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<urn:SessionHeader xmlns:urn="http://www.mywebservice.com/webservices/SoapService" xmlns="http://www.mywebservice.com/webservices/SoapService" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<urn:sessionId xmlns:urn="http://www.mywebservice.com/webservices/SoapService">LOGINTOKEN=your instance name</urn:sessionId>
</urn:SessionHeader>
</soap:Header>
<soap:Body xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<ns2:login xmlns:ns2="http://www.mywebservice.com/webservices/SoapService">
<wsUser>
<entityId>0</entityId>
<password>your password</password>
<username>your username</username>
</wsUser>
</ns2:login>
</soap:Body>
But I'm having trouble finding out how to set up the custom headers in PHP5's Soap. With nuSoap I could just put the whole thing into a variable and then use $client->setHeader($headerVar) but I can't find anything similar in PHP. If I could replicate this one call, I can figure the rest out. Any help would be appreciated!
Thanks in advance!
Update: I've gone through tutorial after tutorial, and read the PHP docs, but nothing seems to work. I can do what I want with curl (as well as nuSoap) but I thought the native PHP5 Soap would be easier and possibly more stable. I guess not...
Update 2
Here's the code I'm trying:
$soapurl = 'http://www.mywebservice.com/webservices/SoapService?wsdl';
$client = new SoapClient($soapurl,array('trace'=>true));
$token = "LOGINTOKEN=your instance name";
$header = new SoapHeader('http://www.mywebservice.com/webservices/SoapService', 'SessionHeader', array('sessionId' => $token));
$client->__setSoapHeaders($header);
$client->login(array("wsUser" => array('entityId'=>'0','username'=>'my username','password'=>'my password')));
And the error I get:
**Fatal error**: Uncaught SoapFault exception: [ns1:InvalidSecurity] An error was discovered processing the <wsse:Security> header in C:\www\soap\index.php:12 Stack trace: #0 C:\www\soap\index.php(12): SoapClient->__call('login', Array) #1 C:\www\soap\index.php(12): SoapClient->login(Array) #2 {main} thrown in C:\www\soap\index.php on line 12
Update 3
So it looks like the "sessionId" is being sent as "key" with the token sent as "value".
*REQUEST*:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.mywebservice.com/webservices/SoapService">
<SOAP-ENV:Header>
<ns1:SessionHeader><item><key>sessionId</key><value>LOGINTOKEN=my token</value></item>
</ns1:SessionHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body><ns1:login><wsUser><entityId>0</entityId><password>my password</password><username>my username</username></wsUser></ns1:login></SOAP-ENV:Body></SOAP-ENV:Envelope>
Have you tried the SoapHeader class to construct your header? Would something like this work?
//Assume $token holds your login token and $client holds the SoapClient object
$header = new SoapHeader('http://www.mywebservice.com/webservices/SoapService', 'SessionHeader', array('sessionId' => $token));
$client->__setSoapHeaders($header);
That should create the header and add it to the SoapClient. Every subsequent call using the SoapClient will then have that header set. As to the exact format, I wouldn't worry too much. You sample XML uses an alias for the namespace called urn. PHP probably won't arrive at exactly the same alias but it should still work. Also I don't think that declaring the xmlns in every child element is needed. I think that a child node automatically inherits the namespace of its parent in XML, but I'm not 100% certain on that. The bottom line is that as long as the right URL's are declared in the namespaces it should be fine even if the XML doesn't exactly match your example.
One other thing you could try-have you switched on tracing in the SoapClient? This is one of the parameters that can be passed to the constructor and it enables you to view the XML SOAP requests and responses. If it still doesn't work using the SoapHeader class try switching tracing on to see what's being sent and received.

php soap question: howto create custom namespace

I am using php soap. I can't get my soap body to include a custom namespace.
Here is an example of what I want:
<?xml version='1.0' encoding='UTF8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<tns:MyCall xmlns:tns="my.sx"><tns:op>getStuff</tns:op><tns:args />
</tns:MyCall>
</soapenv:Body>
</soapenv:Envelope>
I can't get the namespace called tns to appear inside the body, or the elements op and args.
Can anyone suggest how this is done?
My code looks like:
$client = CreateSoapClient();
try{
$result = $client->__soapCall(
'MyCall',
array(), // no params
array(
'uri' => 'urn:Myurn',
'soapaction' => 'urn:Myurn'
));
?>
You need to define your namespace in your WSDL. It's probably easiest to just use the Zend WSDL autogenerator, which I believe will let you set the namespace. Let me know if you need more information.

Categories