I'm working on a project where I need to pass parameters to a .NET web service. Below is what the .NET SOAP request structure looks like.
<soap:Body>
<ListRequest xmlns="My.API.1.0">
<ID>guid</ID>
<Parameters>
<Values xmlns="api.test">
<fv ID="string">
<Value />
<ValueTranslation>string</ValueTranslation>
</fv>
<fv ID="string">
<Value />
<ValueTranslation>string</ValueTranslation>
</fv>
</Values>
</Parameters>
</ListRequest>
</soap:Body>
Below is part of the PHP code.
$params=array('ID'=> $listID,
'Parameters' =>
array(
array('ID' => 'ROOM', 'ValueTranslation' => '99999')));
$result=$soapClient->ListRequest($params);
Whenever I make the call without the parameter the request works and gets the results back to the php page. But when I introduce the parameters, I get "the function GETLIST expects parameter 'ROOM', which was not supplied" error. I've a feeling the the issue is the way I'm mapping the Parameter structure but I can't figure out how to map the SOAP xml above to a php code.
Thank you all for your help!
I don't think that you can specify parameters for a soap request using assoc arrays. Try using objects instead, it have worked for me in the past
$params = new stdClass();
$params->ID = $listId;
$params->Parameters = new stdClass();
$params->Parameters->Values = new stdClass();
$params->Parameters->Values->fv = array();
//Child of "Parameters"
$p = new stdClass();
$p->ID = 'ROOM';
$p->ValueTranslation = '99999';
$params->Parameters->Values->fv[] = $p;
$soap->ListRequest($params);
Another solution is to use an XML DOM and handle the creation of the XML data yourself (Then simply pass it as string to the function you want to call in SOAP)
Furthermore the datastructure that you make with the assoc array does not match the structure it should be.. (take a good look at the XML you are posting and you will see what you are missing)
Edit: Think i made something that should reflect your schema now.
Related
So I have soap connection to some services. The App I'm creating is fully object oriented. The data for messages I'm sending are therefore in PHP in stdClass objects (it is extended by my Classes - representants of data in future xml document with predeclared public $variables). Everything is fine as long as I have to create documents like:
<Request>
<paramsList>
<someParam></someParam>
<someParam></someParam>
<someParam></someParam>
<someParam></someParam>
</paramsList>
<moreData></moreData>
</Request>
Now the problem is to create structure like:
<Request>
<paramsList>
<someParam id="3"></someParam>
<someParam id="4"></someParam>
<someParam id="18"></someParam>
<someParam id="512"></someParam>
</paramsList>
<moreData isImportant="false"></moreData>
</Request>
Is it possible (and if - how?) to add these things to stdObject so I don't have to change execution of SOAP functions from this simple form:
$client->functionToExecute($stdObjectOrientedData);
Example object and usage:
class Request extends \stdClass
{
public $paramsList;
public $moreData;
}
$objectToSend = new \stdObject();
$request = new Request();
$request->paramsList = $someParamsObjectsArray;
$request->moreData = $someData;
$objectToSend->Request = $request;
$client->functionToExecute($objectToSend);
In case someone visit this question, here is the answer.
If WSDL file is created properly, PHP will generate response with using normal StdClass objects. just name properties as they are named in request.
If WSDL has errors, only thing you can do is to make requests manualy from templates.
I am trying to access a Web Service (SOAP) from a provider. I have not control on the server response. For this I am using, Zend_Soap_Client passing the WDSL and options in the constructor, I can do getFunctions, but when try to access the first Soap method I get
[Sender] looks like we got no XML document
After looking around and checking the answer I get from the server with soapUI I see that the answer is missing the XML declaration:
<?xml version="1.0" encoding="XXXXXXX"?>
So, is there aby way to make the Zend_Soap_Client omit the XML validation based in the XML declaration? Assuming that the lacking of the declaration is my problem.
Here is the code I use for this:
private $_connection_settings = array('login' => self::API_user, 'pwd' => self::API_password, 'key'=> self::API_Key);
private static $CONNEXION_PARAMS = array(
'soap_version' => SOAP_1_1,
'encoding' => 'UTF-8'
);
...
//somewhere in my code:
$client = new Zend_Soap_Client('http://server_URL?wsdl', self::$CONNEXION_PARAMS);
$response = $client->fistSoapMethod($this->_connection_settings);
And response is not assigned.
Thanks!
No other warnings/errors from your code besides the SOAP Fault?
Not sure it is the WSDL. Can always try validating the WSDL using an online tool.
Have you used getLastResponse() and getLastRequest() methods? Sounds like you might be sending some garbage at the start of your request. Another thing I always do when testing is turn off WSDL caching.
ini_set("soap.wsdl_cache_enabled", 0);
I am struggling to get PHP's inbuilt SoapClient to interpret the response coming back from the web service I'm trying to call.
SoapUI is able to interrogate this soap method and return good results.
I am also able to get nusoap_client to return correct results (but am not able to use nusoap for other reasons and think I'm stuck with SoapClient).
Using SoapClient, I can see that seemingly good data is being returned, but instead of the results being parsed and broken into easily consumed arrays of values, the XML response string is being stuffed into a single field in an object (labelled 'any').
My code and results are shown below :
$client = new SoapClient($url);
$results = $client->GetPropertiesByProjectAndContractStatus($params);
var_dump($results);
The output from the above code is below :
object(stdClass)[3]
public 'GetListingsByGUIDResult' =>
object(stdClass)[4]
public 'any' => string '<xs:schema xmlns="" ........ (long xml here) ....
Now, perhaps it's possible that the service I am using is returning some xml which has something wrong with it (although it seems fine to my eye). nusoap and SoapUI both have no problems using it either.
So I'm wondering what is it with SoapClient that is different.
I have a function that grabs that result and turns it into a dom object so you can use the dom functions to extract data.
protected function getElementsFromResult($elementName, $simpleresult) {
$dom = new DOMDocument ();
$dom->preserveWhiteSpace = FALSE;
if ($simpleresult == null) {
echo 'null';
return null;
} else {
$dom->loadXML ( $simpleresult->any );
return $dom->getElementsByTagName ( $elementName );
}
$elementName is the name of the elements you want from the result and $simpleresult is the object containing the 'any' string.
This happens when the data returned is not specified in the WSDL that you are using. Anything not in the WSDL will get lumped into this "any" element at the end of parsing the XML.
If this is happening then you should ensure that your script is using the correct WSDL for the SOAP service you're using.
For example, if you're using an old WSDL and new elements are now in use in the service, they will end up inside this "any" element!
Did you try using the SOAP_SINGLE_ELEMENT_ARRAYS feature?
<?php
$client = new SoapClient($url, array('features' => SOAP_SINGLE_ELEMENT_ARRAYS));
I'm trying to use Zend_Soap_Client to communicate with an ASP.net web service. Here's my client call:
$client = new Zend_Soap_Client(null, array(
'location' => 'http://example.com/service.asmx',
'uri' => 'http://example.com/'
));
$user = new UserDetail();
$result = $client->UserDetails($user);
However this always gives me the error:
System.NullReferenceException: Object reference not set to an instance of an object. at Service.UserDetails(UserDetail UserDetail)
some googling revealed that this is quite a common problem. The most common solution seemed to be to pass the parameters as an array, so I tried:
$result = $client->UserDetails(array('UserDetail' => $user));
but this gave the same error. I also tried passing the params as a stdClass object, nesting the array in another with 'params' as the key, and a few other things but the error is always the same.
I have the ASP code for the web service itself, the relevant method is:
public Result UserDetails(UserDetail UserDetail) {
[some stuff]
Hashtable ht = new Hashtable();
ht = UserDetail.GenerateData();
}
the error is caused by the GenerateData() call.
I assume the UserDetails method is getting null instead of my object as the parameter, but I'm not sure how I should be calling the method, or how I can debug this further. The majority of the Zend_Soap_Client examples I've found seem to be using WSDL, which this service is not; not sure if that is relevant. Any help appreciated!
I eventually solved this with:
$userDetails = new UserDetails();
$userDetails->UserDetail = $user;
$client->UserDetails($userDetails);
it seems ASP.net expects (and returns) params to be nested in an object/array with the same name as the method being called.
If you have any possibility to change the asp.net code I'd suggest you try an implementation of the method UserDetails without parameters just to make sure that code isn't broken.
I would then create a consumer-method in asp.net, debug the http-request and see how the userdetail-object is serialized/broken down in array form. Then it's "just" a matter of creating a similar http request from php.
The built-in PHP extension for SOAP doesn't validate everything in the incoming SOAP request against the XML Schema in the WSDL. It does check for the existence of basic entities, but when you have something complicated like simpleType restrictions the extension pretty much ignores their existence.
What is the best way to validate the SOAP request against XML Schema contained in the WSDL?
Besides the native PHP5 SOAP libs, I can also tell you that neither the PEAR nor Zend SOAP libs will do schema validation of messages at present. (I don't know of any PHP SOAP implementation that does, unfortunately.)
What I would do is load the XML message into a DOMDocument object and use DOMDocument's methods to validate against the schema.
Been digging around on this matter a view hours.
Neither the native PHP SoapServer nore the NuSOAP Library does any Validation.
PHP SoapServer simply makes a type cast.
For Example if you define
<xsd:element name="SomeParameter" type="xsd:boolean" />
and submit
<get:SomeParameter>dfgdfg</get:SomeParameter>
you'll get the php Type boolean (true)
NuSOAP simply casts everthing to string although it recognizes simple types:
from the nuSOAP debug log:
nusoap_xmlschema: processing typed element SomeParameter of type http://www.w3.org/2001/XMLSchema:boolean
So the best way is joelhardi solution to validate yourself or use some xml Parser like XERCES
Typically one doesn't validate against the WSDL. If the WSDL is designed properly there should be an underlying xml schema (XSD) to validate the body of the request against. Your XML parser should be able to do this.
The rest is up to how you implement the web service and which SOAP engine you are using. I am not directly familiar with the PHP engine. For WSDL/interface level "validation" I usually do something like this:
Does the body of the request match a known request type and is it valid (by XSD)?
Does the message make sense in this context and can i map it to an operation/handler?
If so, start processing it
Otherwise: error
Using native SoapServer PHP is a little bit tricky but is possible too:
function validate(string $xmlEnvelope, string $wsdl) : ?array{
libxml_use_internal_errors(true);
//extracting schema from WSDL
$xml = new DOMDocument();
$wsdl_string = file_get_contents($wsdl);
//extracting namespaces from WSDL
$outer = new SimpleXMLElement($wsdl_string);
$wsdl_namespaces = $outer->getDocNamespaces();
//extracting the schema tag inside WSDL
$xml->loadXML($wsdl_string);
$xpath = new DOMXPath($xml);
$xpath->registerNamespace('xsd', 'http://www.w3.org/2001/XMLSchema');
$schemaNode = $xpath->evaluate('//xsd:schema');
$schemaXML = "";
foreach ($schemaNode as $node) {
//add namespaces from WSDL to schema
foreach($wsdl_namespaces as $prefix => $ns){
$node->setAttribute("xmlns:$prefix", $ns);
}
$schemaXML .= simplexml_import_dom($node)
->asXML();
}
//capturing de XML envelope
$xml = new DOMDocument();
$xml->loadXML($xmlEnvelope);
//extracting namespaces from soap Envelope
$outer = new SimpleXMLElement($xmlEnvelope);
$envelope_namespaces = $outer->getDocNamespaces();
$xpath = new DOMXPath($xml);
$xpath->registerNamespace('soapEnv', 'http://schemas.xmlsoap.org/soap/envelope/');
$envelopeBody = $xpath->evaluate('//soapEnv:Body/*[1]');
$envelopeBodyXML = "";
foreach ($envelopeBody as $node) {
//add namespaces from envelope to the body content
foreach($envelope_namespaces as $prefix => $ns){
$node->setAttribute("xmlns:$prefix", $ns);
}
$envelopeBodyXML .= simplexml_import_dom($node)
->asXML();
}
$doc = new DOMDocument();
$doc->loadXML($envelopeBodyXML); // load xml
$is_valid_xml = $doc->schemaValidateSource($schemaXML); // path to xsd file
return libxml_get_errors();
}
and inside your SoapServer function implementation:
function myFunction($param) {
$xmlEnvelope = file_get_contents("php://input");
$errors = validate($xmlEnvelope, $wsdl);
}
I was not able to find any simple way to perform the validation and in the end had validation code in the business logic.
Some time ago I've create a proof of concept web service with PHP using NuSOAP. I don't know if it validates the input, but I would assume it does.