I am working on building a web service in PHP using the SoapServer class, but I'm running into an issue with casting of complex types.
The WSDL is completely valid, and the PHP SoapClient handles it flawlessly, but there seems to be an issue with the complex types that are returned not being cast properly. This came to light when consuming the service in .Net, as I was getting exceptions that indicated the type was not present in the given namespace.
I mangled my function numerous times, changing the namespace on the element, but .Net continues to give me errors, regardless of what namespace I use.
Consider the following abbreviation of the script:
function getCommands() {
$output = array();
// ...
foreach($result as $row) {
$output[] = new SoapVar($row, SOAP_ENC_OBJECT, 'ns1:command');
}
return $output;
}
The abbreviated response:
<?xml version="1.0" encoding="utf-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:MyWebService"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:getCommandsResponse>
<return SOAP-ENC:arrayType="ns1:command[12]" xsi:type="ns1:ArrayOfCommand">
<item xsi:type="ns1:command">
<!-- ... -->
</item>
<!-- ... -->
</return>
</ns1:getCommandsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
What I've noticed is that xmlns:ns1 is defined by way of the WSDL, and it does match the namespace in the WSDL. However, the .Net SOAP client doesn't seem to understand that the command element is defined there. It does, however, understand that that's where ArrayOfCommand is defined.
So my question is multipart:
Is this a known bug with the SoapServer?
If not, am I missing something grievous in my WSDL?
Am I not encoding my objects properly?
Is this an issue with .Net? If so, what's the work-around?
I was able to resolve this issue by working over the <types/> section of my WSDL again, using the Google WSDL for reference. Then, I had to work some magic in my PHP function, casting the elements of the $command appropriate to their respective types in the WSDL, and encoding the entire command as a ns2:command. When aligned with the WSDL, this all fell together nicely and .Net is having zero difficulty with it.
I'm surprised nobody in the development community was willing to answer this, but I hope someone will be able to glean from it at least some direction on how to fix their own instance of this problem.
Related
I have a php script that is interacting with a .NET web service. I have multiple pieces that are calling different functions via soap with no issue so everything is working great.
PHP Version => 5.3.2-1ubuntu4.18
My new situation is a specific function that requires 4 parameters, all of type string, but one of which is an entire XML document.
When I look at the request I'm sending for this function, everything in the request looks great except the xml, which is encoded (all the opening and closing brackets).
It seems like there should be a strait forward way to deal with this but nothing is coming up on the net. I have tried SoapClient options and escaping but nothing is changing this.
Here is the XML being fed to the SoapClient:
<Setup>
<CustomerAccount>xxxx24838</CustomerAccount>
<MasterSource enumType="MasterSource">3</MasterSource>
<MasterReceived enumType="YesNo">1</MasterReceived>
<AID>kjsdlkfjsldkfj334</AID>
</Setup>
Here is the request being sent:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.xxxxxxx.com/">
<SOAP-ENV:Body>
<ns1:Save>
<ns1:objectName>CASetup</ns1:objectName>
<ns1:xmlData>
<Setup><CustomerAccount>xxxx24838</CustomerAccount>
<MasterSource enumType="MasterSource">3</MasterSource>
<MasterReceived enumType="YesNo">1</MasterReceived>
<AID>kjsdlkfjsldkfj334</AID>
</Setup>
</ns1:xmlData>
<ns1:operation>saveMasterAsReceived</ns1:operation>
<ns1:Brand>6</ns1:Brand>
</ns1:Save>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I have formatted just for ease of reading.
I am using php SOAP to post lead data to my client's SAGE CRM, the record get created (with crmid returned) but contains empty values. For some unknown reason my xml packet is being ignored.
The SAGE documentation does not give an xml example for adding record (addrecord) to the CRM. Can someone please help?
What is the right xml format for addrecord function?
I know this was a question back in 2013 but better have it answered in case someone else comes looking for a solution.
The following is a sample for the upload of a new opportunity into Sage CRM. I have not seen the xml you are generating but I would start by using add instead of addrecord. I have not used addrecord before so I can't help you understanding this format for uploading data.
Please note the *Specified fields as they are important. Any field to be populated which has a related *Specified field must have it set to true. Otherwise the field might not be populated.
Most of the values on the sample bellow must be replaced by actual values. The SID being the most important one.
You may enter multiple <records> within the <add> tags.
<?xml version='1.0' encoding='utf-8' ?>
<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:Header>
<SessionHeader xmlns='http://tempuri.org/type'>
<sessionId>SID</sessionId>
</SessionHeader>
</soap:Header>
<soap:Body>
<add xmlns='http://tempuri.org/type'>
<entityname>opportunity</entityname>
<records xsi:type='opportunity'>
<description>description</description>
<forecast>forecast_value</forecast>
<forecastSpecified>true</forecastSpecified>
<certainty>certainty</certainty>
<certaintySpecified>true</certaintySpecified>
<targetclose>targetclose</targetclose>
<targetcloseSpecified>true</targetcloseSpecified>
<forecast_cid>forecast_cid</forecast_cid>
<forecast_cidSpecified>true</forecast_cidSpecified>
<total_cid>total_cid</total_cid>
<total_cidSpecified>true</total_cidSpecified>
<totalorders_cid>total_orders_cid</totalorders_cid>
<totalorders_cidSpecified>true</totalorders_cidSpecified>
<totalquotes_cid>totalquotes_cid</totalquotes_cid>
<totalquotes_cidSpecified>true</totalquotes_cidSpecified>
<source>source</source>
<type>type</type>
<stage>stage</stage>
<status>status</status>
<assigneduserid>assigneduserid</assigneduserid>
<assigneduseridSpecified>true</assigneduseridSpecified>
<channelid>channelid</channelid>
<channelidSpecified>true</channelidSpecified>
<priority>priority</priority>
<currency>cid</currency>
<currencySpecified>true</currencySpecified>
<primarycompanyid>primarycompanyid</primarycompanyid>
<primarycompanyidSpecified>true</primarycompanyidSpecified>
<primarypersonid>primarypersonid</primarypersonid>
<primarypersonidSpecified>true</primarypersonidSpecified>
</records>
</add>
</soap:Body>
</soap:Envelope>
You can find the web services documentation at https://community.sagecrm.com/user_community/m/cloud_documentation/27076.aspx
Download the wsdl file to get more details about each field and entity available.
I'm dealing with an incredibly bad API that requires me to send this XML:
<?xml_version string(335) ""1.0" encoding="utf-16"?>
<GetTicketAction xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CompanyName>*name*</CompanyName>
<IntegrationLoginId>*id*</IntegrationLoginId>
<IntegrationPassword>*password*</IntegrationPassword>
<SrServiceRecid>*recordId*</SrServiceRecid>
</GetTicketAction>
via POST (as actionString) to a server that is not under my control. I've tried it with JavaScript (couldn't, cross scripting) and with CURL (got "this needs to be encrypted error"). Encryption is not mentioned anywhere in the docs, which say that it can be done with JS in IE using "full trust."
Content type is application/x-www-form-urlencoded if that helps.
Is there any way to send this with either JS and/or PHP?
After many painful years of this API slowly driving me insane, I discovered that it did NOT want me to send the XML through the standard CURL pattern of
$data = array(
"actionString" => $xml
);
Rather, I was to do:
$data = 'actionString=<?xml_version string(335) ""1.0" encoding="utf-16"?>
<GetTicketAction xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CompanyName>*name*</CompanyName>
<IntegrationLoginId>*id*</IntegrationLoginId>
<IntegrationPassword>*password*</IntegrationPassword>
<SrServiceRecid>*recordId*</SrServiceRecid>
</GetTicketAction>'
Future API writers: Let this be a warning. I'm really hoping a crazed psychopath knows where this guy sleeps.
I'm in with PHP SoapServer working in non-wsdl mode. I have the server set up to handle the data and return a response using setClass(). I tried returning an associative array, but that translates into SOAP maps with items, keys and values. I'd like to respond with the following:
<soap:Body>
<AsyncResponseOperationResponse xmlns="http://www.sample.com/">
<AsyncResponseOperationResult>
<Succeeded>true</Succeeded>
<Comments>
The operation was a success
</Comments>
</AsyncResponseOperationResult>
</AsyncResponseOperationResponse>
</soap:Body>
The variables would be whether success is true or false, and the comments.
I've been trying to read about the 'typemap' option, but it is not very well documented, and what I've found so far has only confused me further. The resources I've found so far are the php unit tests, like this one and this one, as well as this stackoverflow question
Can anyone provide me an example that does what I'm trying to do? I think I would be alright switching to wsdl mode with autodiscover (using Zend's Soap Server), if that comes up as a solution.
Edit:Until I figure out how to do it the right way, I'm just writing out all the XML manually.
header("Content-type: text/xml");
echo "<?xml version="1.0" encoding="utf-8"?><soap:Envelope ...
I am new to salesforce. Proficient in php but I haven't done any xml parsing. I have this file which I can consume. It is created when a salesforce object changes:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<notifications xmlns="http://soap.sforce.com/2005/09/outbound">
<OrganizationId>00D30000000opwSEAQ</OrganizationId>
<ActionId>04k30000000L6QPAA0</ActionId>
<SessionId xsi:nil="true"/>
<EnterpriseUrl>https://na1-api.salesforce.com/services/Soap/c/22.0/00D30000000opwS</EnterpriseUrl>
<PartnerUrl>https://na1-api.salesforce.com/services/Soap/u/22.0/00D30000000opwS</PartnerUrl>
<Notification>
<Id>04l3000000JbKClAAN</Id>
<sObject xsi:type="sf:Account" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
<sf:Id>0013000000ooziWAAQ</sf:Id>
<sf:BillingCity>New York</sf:BillingCity>
<sf:BillingCountry>US</sf:BillingCountry>
<sf:BillingPostalCode>10000</sf:BillingPostalCode>
<sf:BillingState>New York</sf:BillingState>
<sf:BillingStreet>302 E xxx St Apt C</sf:BillingStreet>
<sf:FN__Mapping_Status__c>Not Located Yet</sf:FN__Mapping_Status__c>
<sf:IsDeleted>false</sf:IsDeleted>
<sf:Member_Status__c>Active</sf:Member_Status__c>
<sf:Name>Joel Test</sf:Name>
</sObject>
</Notification>
</notifications>
</soapenv:Body>
</soapenv:Envelope>
I need to parse it so that I can take the values in the sf namespace and update a database. I can use SimpleXML to read stuff no in namespace, but I haven't been able to read namespace values. Can someone point me to example code or tutorial on how to do this?
It looks like that is a SOAP message from the Outbound Messaging feature in Salesforce. Instead of treating it as just XML, try using the native PHP SoapServer with the WSDL provided in Salesforce, as it is designed to handle SOAP messages like this. You can get the WSDL at Setup | Create | Workflow & Approvals | Outbound Messages | | Endpoint WSDL. You might also want to look at the Salesforce PHP Client, which uses the corresponding PHP SoapClient and has a utility for handling the sf: namespace and converting to a SObject object. It shouldn't be too hard to rewire the toolkit to act as a server instead of a client so you can handle the Outbound Messages.
PHP's SimpleXML will not work for reading namespace information. See this - http://www.w3schools.com/php/php_xml_simplexml.asp . Instead try something like this http://www.php.net/manual/en/function.xml-parser-create-ns.php
PHP's SimpleXML may work for the case. Just need to use children() and use the right syntax.
Therefore, to read the field value BillingCity in Salesforce Outbound Message, you may use following code:
$rcXML->children('http://schemas.xmlsoap.org/soap/envelope/')->Body->children('http://soap.sforce.com/2005/09/outbound')->notifications->Notification->sObject->children('urn:sobject.enterprise.soap.sforce.com')->BillingCity
To help you understand how it works, I write a post with sames code and xml which shall be easier to understand.
http://amigotechnotes.wordpress.com/2013/11/16/parse-xml-with-namespace-by-simplexml-in-php/
Once you may read the filed value, you may store it to other database or text file with PHP.