This is my first run into the SOAP forest, so I have no idea what I'm doing and worse, the company has zero documentation or code examples. They at least provide an example call.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<tem:GetRatesIPXML>
<tem:ipXML>
<![CDATA[
<XML>
<RateInput>
<GUID>12345</GUID>
<RepID>abc</RepID>
<ZipCode>55343</ZipCode>
<EffectiveDate>1/15/2014</EffectiveDate>
<DateOfBirth >12/15/1980</DateOfBirth >
<FilterPlanCode></FilterPlanCode>
</RateInput>
</XML>]]>
</tem:ipXML>
</tem:GetRatesIPXML>
</soapenv:Body>
</soapenv:Envelope>
I've used both SoapClient and NuSoap. I've tried everything from nested arrays to objects, strings, simpleXML. I can't seem to figure this out and after two days of googling I've reached my end.
Here's my current implementation.
require('lib/nusoap.php');
class Carrier
{
const WSDL = 'http://getrates_staging.test.com/getrates.svc?wsdl';
public function get()
{
$soapClient = new nusoap_client( self::WSDL , true);
$soapClient->soap_defencoding = 'UTF-8';
$string = ""
. "<XML>"
. "<RateInput>";
$string .= "<GUID>12345</GUID>";
$string .= "</RateInput></XML>";
$response = $soapClient->call('GetRatesIPXML' , array('ipXML'=> $string) , '' , '', false, true);
var_dump($soapClient->request);
var_dump($soapClient->getError());
var_dump($response);
}
}
$foo = new Carrier();
$foo->get();
It results in something close but all the < get escaped to < so that doesn't work. Any help is appreciated.
edit
This is about as close as I get to the desired result
class Carrier
{
const WSDL = 'http://getrates_staging.test.com/getrates.svc?wsdl';
public function get()
{
$soapClient = new SoapClient( self::WSDL , array('trace' => true));
//$soapClient->soap_defencoding = 'UTF-8';
$string = ""
. "<![CDATA[ <XML>"
. "<RateInput>";
$string .= "<GUID>12345</GUID>";
$string .= "</RateInput></XML> ]]>";
$param = new SoapVar($string, XSD_ANYXML);
$ipXML = new stdClass();
$ipXML->ipXML = $param;
try
{
$response = $soapClient->GetRatesIPXML($ipXML);
}
catch(Exception $e)
{
var_dump($e);
}
var_dump($soapClient->__getLastRequest());
var_dump($response);
}
}
$foo = new Carrier();
$foo->get();
I end up with
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/"><SOAP-ENV:Body><ns1:GetRatesIPXML><![CDATA[ <XML><RateInput><GUID>12345</GUID></RateInput></XML> ]]></ns1:GetRatesIPXML></SOAP-ENV:Body></SOAP-ENV:Envelope>
I don't get why it's dropping the surrounding <ns1:ipXML>
edit 2
At the end of the day this worked.
class Carrier
{
const WSDL = 'http://getrates_staging.test.com/getrates.svc?wsdl';
public function get()
{
$soapClient = new SoapClient( self::WSDL , array('trace' => true));
//$soapClient->soap_defencoding = 'UTF-8';
$string = ""
. "<ns1:ipXML><![CDATA[ <XML>"
. "<RateInput>";
$string .= "<GUID>1234</GUID>
<RepID>1234</RepID>
<ZipCode>55343</ZipCode>
<EffectiveDate>1/15/2016</EffectiveDate>
<DateOfBirth >07/01/1983</DateOfBirth >
<FilterPlanCode></FilterPlanCode>";
$string .= "</RateInput></XML> ]]></ns1:ipXML>";
$param = new SoapVar($string, XSD_ANYXML);
$ipXML = new stdClass();
$ipXML->ipXML = $param;
try
{
$response = $soapClient->GetRatesIPXML($ipXML);
}
catch(Exception $e)
{
var_dump($e);
}
var_dump($soapClient->__getLastRequest());
var_dump($response);
}
}
$foo = new Carrier();
$foo->get();
But it seems so hacky. If anyone has a better suggestion I'm open.
Ordinarily, XML documents are constructed (and parsed) using PHP "helper libraries" such as SimpleXML (http://php.net/manual/en/book.simplexml.php), or techniques such as the ones described here (http://www.phpeveryday.com/articles/PHP-XML-Tutorial-P848.html).
The XML document is constructed as an in-memory data structure (arrays, etc.), and then converted, in one swoop, into the XML that is to be sent.
In fact, since what you are doing is SOAP, you can go one level of abstraction above that, e.g. (http://php.net/manual/en/book.soap.php). Off-the-shelf libraries exist which will handle both the task of constructing the XML payload, and sending it, and getting server responses and decoding them. That's where you should start.
"Actum Ne Agas: Do Not Do A Thing Already Done."
Related
I have a several problems to moment to create a WSDL with REST in PHP. I read a little bit about this, supposedly REST use WADL. I need a library or framework that generated a WADL or type WSDL of my code PHP (class, methods, vars).
Thanks a lot!
I created a simulation with php but this no get the return vars
... Class
... Methods
... Vars
if(isset($_GET['wadl'])):
header('Content-Type: text/xml');
$foo = new proveedores();
$get_class = get_class($foo);
// create the new XML document
$dom = new DOMDocument('1.0', 'iso-8859-1');
// create the root element
$list_of_teams = $dom->createElement($get_class);
$dom->appendChild($list_of_teams);
$class = new ReflectionClass($get_class);
$methods = $class->getMethods();
foreach ($methods as $m) {
$method = $class->getMethod($m->name);
// create the first element
$team = $dom->createElement($m->name);
$list_of_teams->appendChild($team);
$parameters = $method -> getParameters();
if(!empty($parameters)):
foreach ($parameters as $p) {
$name = $team->appendChild($dom->createElement('parameters'));
$name->appendChild($dom->createTextNode($p -> name));
}
endif;
}
print $xml_result = $dom->saveXML();
endif;
I am new to the whole web service thing so I will try to explain my problem as much as I can understand it so far.
I created Soap server in PHP:
try {
$server = new SOAPServer(
'webservice.wsdl',
array(
'uri' => 'http://example.com/soap/server.php'
)
);
$server->setClass('soap');
$server->handle();
} catch (SOAPFault $f) {
print $f->faultstring;
}
Then I have a Soap class for the server:
class soap
{
public function sendOrders($sXml)
{
$oXml = new SimpleXMLElement($sXml);
$sOrder = $oXml->children();
$sResult = '';
foreach ($sOrder as $OrderRow) {
$sOrderNr = $OrderRow->ORDERNR;
$sState = $OrderRow->STATE;
$sTimestamp = $OrdeRow->TIMESTAMP;
$oOrder = new Order;
$oOrder->load($sOrderNr);
if ($sState == 1) {
$oOrder->checkStatusChange('Approved', $oOrder);
$oOrder->{$oOrder->getCoreTableName() . '__status'} = new Field('Approved');
$sResult .= $sOrderNr . " 1 | ";
} elseif ($sState == 0) {
$oOrder->checkStatusChange('Declined', $oOrder);
$oOrder->{$oOrder->getCoreTableName() . '__status'} = new Field('Declined');
$sResult .= $sOrderNr . " 0 | ";
}
$oOrder->save();
}
return $sResult;
}
}
I have a test client that sends simple XML with this format:
<xml>
<ORDER>
<ORDERNR>9nmf997d997701e15e30edac107b3664</ORDERNR>
<STATE>1</STATE>
<TIMESTAMP>123456789</TIMESTAMP>
</ORDER>
<ORDER>
<ORDERNR>9nmb1c3d4dfb067c04497ea51bd50d06</ORDERNR>
<STATE>0</STATE>
<TIMESTAMP>987654321</TIMESTAMP>
</ORDER>
</xml>
Now, what I need to do is create simple WSDL file for "description" of the service. I must say I have a little knowledge about this whole Web Service area so I would be grateful for any help.
I am familiar with W3C documentation http://www.w3schools.com/webservices/ws_wsdl_documents.asp
Thank you in advance.
Update: I tried to seach for some WSDL generators, none seem to be working.
Solved
A simple library called PhpWsdl did the trick.
https://code.google.com/p/php-wsdl-creator/
i got a problem in PHP. i want to get all the bus stations in india. so i have made an xml request and post that xml data to given API URL. but i did not get the result. below is the .net code which is working fine. even i have used the "HttpRequest" method in php, but it gives me an error like "Fatal error: Class 'HttpRequest' not found".
can anybody help me to give me the equivalent code in php or possible ways to get the data..?
thanks in advance.
.net code (working fine)
protected void getBusServices(string journeydate)
{
XmlDocument doc = new XmlDocument();
StringReader stream;
StreamReader reader;
XmlTextReader textreader;
HttpWebRequest req;
HttpWebResponse response;
try
{
string password = "password";
DateTime dt = Convert.ToDateTime(journeydate);
string strJrneyDate = dt.ToString("MM-dd-yyyy");
string strRtDate = dt.AddDays(3).ToString("MM-dd-yyyy");
string url = Bus_Api_Url;
string RequestCommand = "<Command>GET_STATIONS</Command> <Username>username</Username> <Password>password</Password>";
req = (HttpWebRequest)WebRequest.Create(url);
string RequestData = "RequestXML=<?xml version='1.0' encoding='utf-8' ?><BusRequest xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema'>" + RequestCommand + "</BusRequest>";
req.Method = WebRequestMethods.Http.Post;
req.ContentLength = RequestData.Length;
req.ContentType = "application/x-www-form-urlencoded";
using (StreamWriter writer = new StreamWriter(req.GetRequestStream()))
{
writer.Write(RequestData);
}
response = (HttpWebResponse)req.GetResponse();
Stream responsestream = response.GetResponseStream();
reader = new StreamReader(responsestream);
string xmlresponse = reader.ReadToEnd();
stream = new StringReader(xmlresponse);
textreader = new XmlTextReader(stream);
doc.LoadXml(xmlresponse);
DataSet ds = new DataSet();
ds.ReadXml(textreader);
}
catch (Exception ex)
{
EBUtils.Logger.LogError.Publish(ex);
}
finally
{
reader = null;
// stream = null;
reader = null;
response = null;
doc = null;
}
}
}
its really simple, check out http://php.net/manual/en/simplexml.examples-basic.php
i've used the same method lot of times, heres a little snippet from one of my active php projects.
$file = "http://the_address_of_the_xml_file.com";
if(!$xml = simplexml_load_file($file)) {
return 0;
} else { //open
$status = $xml->attributes()->statu
if($status=='ok') {//open
$bio = $xml->artist->bio;
//closed
for ($i = 0; $i < 1; $i++) {
and how about file_get_content ?
$post_data = array('xml' => $xml_str);
$stream_options = array(
'http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded' . "\r\n",
'content' => http_build_query($post_data)));
$context = stream_context_create($stream_options);
$response = file_get_contents($url, null, $context);
ha im not on top form today, in bed really unwell ;)
I've ran into trouble with SOAP, I've never had this issue before and can't find any information on line that helps me solve it.
The following code
$wsdl = "path/to/my/wsdl";
$client = new SoapClient($wsdl, array('trace' => true));
//$$textinput is passed in and is a very large string with rows in <item></item> tags
$soapInput = new SoapVar($textinput, XSD_ANYXML);
$res = $client->dataprofilingservice(array("contents" => $soapInput));
$response = $client->__getLastResponse();
var_dump($res);//outputs null
var_dump($response);//provides the proper response as I would expect.
I've tried passing params into the SoapClient constructor to define soap version but that didnt' help. I've also tried it with the trace param set to false and not present which as expected made $response null but $res was still null. I've tried the code on both a linux and windows install running Apache.
The function definition in the WSDL is (xxxx is for security reasons)
<portType name="xxxxServiceSoap">
<operation name="dataprofilingservice">
<input message="tns:dataprofilingserviceSoapIn"/>
<output message="tns:dataprofilingserviceSoapOut"/>
</operation>
</portType>
I have it working using the __getLastResponse() but its annoying me it will not work properly.
I've put together a small testing script, does anyone see any issues here. Do I need a structure for the return type?
//very simplifed dataset that would normally be
//read in from a CSV file of about 1mb
$soapInput = getSoapInput("asdf,qwer\r\nzzxvc,ewrwe\r\n23424,2113");
$wsdl = "path to wsdl";
try {
$client = new SoapClient($wsdl,array('trace' => true,'exceptions' => true));
} catch (SoapFault $fault) {
$error = 1;
var_dump($fault);
}
try {
$res = $client->dataprofilingservice(array("contents" => $soapInput));
$response = $client->__getLastResponse();
echo htmlentities($client->__getLastRequest());//proper request echoed
echo '<hr>';
var_dump($res);//null
echo "<hr>";
echo(htmlentities($response));//proper response echoed
} catch (SoapFault $fault) {
$error = 1;
var_dump($fault);
}
function getSoapInput($input){
$rows = array();
$userInputs = explode("\r\n", $input);
$userInputs = array_filter($userInputs);
//
$inputTemplate = " <contents>%s</contents>";
$rowTemplate = "<Item>%s</Item>";
//
$soapString = "";
foreach ($userInputs as $row) {
// sanitize
$row = htmlspecialchars(addslashes($row));
$xmlStr = sprintf($rowTemplate, $row);
$rows[] = $xmlStr;
}
$textinput = sprintf($inputTemplate, implode(PHP_EOL, $rows));
$soapInput = new SoapVar($textinput, XSD_ANYXML);
return $soapInput;
}
Ok after much digging it is related to relative namespaces, it appears that PHP doesn't handle them well within the WSDL or the SOAP Envelope. So since I don't have control of the SOAP Server I will continue to get the response via __getLastResponse();.
Hopefully this will save some people some time it was hard to find.
You are mixing things here. __getLastResponse() returns the bare XML string response from the server if you make use of the 'trace' => true option. That is for debugging only.
So regardless whether 'trace' => true or not, the method which you would like to call originally returns the same and that is totally normal. Setting tracing to on won't change the behaviour, it just offers an additional feature, the return value for __getLastResponse().
As the SoapClient does not throw any exception I'd say that your call is going okay and NULL is a valid return value.
You might want to provide the actual XML string and/or the WSDL defintion so that one could inspect if that's the case or not.
I'm attempting to get a shipping quote from an SOAP service. I've been able to successfully create authentication headers and query the SOAP service with basic requests that require no body parameters.
I'm able to create the proper structure for the request but the namespace values are not showing up in the request output.
Example code:
$client = new SoapClient("http://demo.smc3.com/AdminManager/services/RateWareXL?wsdl",
array('trace' => TRUE));
$headerParams = array('ns1:licenseKey' => $key,
'ns1:password' => $pass,
'ns1:username' => $user);
$soapStruct = new SoapVar($headerParams, SOAP_ENC_OBJECT);
$header = new SoapHeader($ns, 'AuthenticationToken', $soapStruct, false);
$client->__setSoapHeaders($header);
// Check if shipping is ready - base call
$ready_to_ship = $client->isReady();
The above works just fine and returns true if the shipping service is available.
So I use the following code to build the request body (only filling required fields):
I've also tried putting everything into an array and converting that to a SoapVar, I've tried including ns1: and ns2: in the body request creation but that hasn't worked either. I believe something needs to be adjusted in the request creation... not sure of the best approach..
$rate_request = $client->LTLRateShipment;
$rate_request->LTLRateShipmentRequest->destinationCountry = $destination_country;
$rate_request->LTLRateShipmentRequest->destinationPostalCode = $destination_postal_code;
$rate_request->LTLRateShipmentRequest->destinationPostalCode = $destination_postal_code;
$rate_request->LTLRateShipmentRequest->details->LTLRequestDetail->nmfcClass = $ship_class;
$rate_request->LTLRateShipmentRequest->details->LTLRequestDetail->weight = $ship_weight;
$rate_request->LTLRateShipmentRequest->originCountry = $origin_country;
$rate_request->LTLRateShipmentRequest->originPostalCode = $origin_postal_code;
$rate_request->LTLRateShipmentRequest->shipmentDateCCYYMMDD = $ship_date;
$rate_request->LTLRateShipmentRequest->tariffName = $tariff;
And it produces the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://webservices.smc.com">
<SOAP-ENV:Header>
<ns1:AuthenticationToken>
<ns1:licenseKey>xxxxxxxx</ns1:licenseKey>
<ns1:password>xxxxxxxx</ns1:password>
<ns1:username>xxxxxxxxm</ns1:username>
</ns1:AuthenticationToken>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:LTLRateShipment>
<LTLRateShipmentRequest>
<destinationCountry>USA</destinationCountry>
<destinationPostalCode>10001</destinationPostalCode>
<details>
<LTLRequestDetail>
<nmfcClass>60</nmfcClass>
<weight>300</weight>
</LTLRequestDetail>
</details>
<originCountry>USA</originCountry>
<originPostalCode>90210</originPostalCode>
<shipmentDateCCYYMMDD>20110516</shipmentDateCCYYMMDD>
<tariffName>DEMOLTLA</tariffName>
</LTLRateShipmentRequest>
</ns1:LTLRateShipment>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
But the output should include the namespaces (web: and web1: where appropriate). The above request returns an error code of missing tariffName.
Here's an example of what the xml request should look like:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:web="http://webservices.smc.com" xmlns:web1="http://web.ltl.smc.com">
<soapenv:Header>
<web:AuthenticationToken>
<web:licenseKey> string </web:licenseKey>
<web:password> string </web:password>
<web:username> string </web:username>
</web:AuthenticationToken>
</soapenv:Header>
<soapenv:Body>
<web:LTLRateShipment>
<web:LTLRateShipmentRequest>
<web1:LTL_Surcharge> string </web1:LTL_Surcharge>
<web1:TL_Surcharge> string </web1:TL_Surcharge>
<web1:destinationCity> string </web1:destinationCity>
<web1:destinationCountry> string </web1:destinationCountry>
<web1:destinationPostalCode> string </web1:destinationPostalCode>
<web1:destinationState> string </web1:destinationState>
<web1:details>
<!--Zero or more repetitions:-->
<web1:LTLRequestDetail>
<web1:nmfcClass> string </web1:nmfcClass>
<web1:weight> string </web1:weight>
</web1:LTLRequestDetail>
</web1:details>
<web1:discountApplication> string </web1:discountApplication>
<web1:mcDiscount> string </web1:mcDiscount>
<web1:orgDestToGateWayPointFlag> string </web1:orgDestToGateWayPointFlag>
<web1:originCity> string </web1:originCity>
<web1:originCountry> string </web1:originCountry>
<web1:originPostalCode> string </web1:originPostalCode>
<web1:originState> string </web1:originState>
<web1:rateAdjustmentFactor> string </web1:rateAdjustmentFactor>
<web1:shipmentDateCCYYMMDD> string </web1:shipmentDateCCYYMMDD>
<web1:shipmentID> string </web1:shipmentID>
<web1:stopAlternationWeight> string </web1:stopAlternationWeight>
<web1:surchargeApplication> string </web1:surchargeApplication>
<web1:tariffName> string </web1:tariffName>
<web1:weightBreak_Discount_1> string </web1:weightBreak_Discount_1>
</web:LTLRateShipmentRequest>
</web:LTLRateShipment>
</soapenv:Body>
</soapenv:Envelope>
Any suggestions / direction appreciated!
Ok... After too many hours of testing I finally have a solution..
I recreated the Authorization Token as a class and built the Soap Request without having to deal with any namespaces, SoapVars etc. it's surprisingly easy.
/* Object for holding authentication info
this could probably be accomplished using stdClass too */
class AuthHeader {
var $licenseKey;
var $password;
var $username;
function __construct($loginInfo) {
$this->licenseKey = $loginInfo['licenseKey'];
$this->password = $loginInfo['password'];
$this->username = $loginInfo['username'];
}
}
// set current soap header with login info
$client = new SoapClient("http://demo.smc3.com/AdminManager/services/RateWareXL?wsdl",
array('trace' => TRUE
));
// create header params array
$headerParams = array('licenseKey' => $key,
'password' => $pass,
'username' => $user);
// create AuthHeader object
$auth = new AuthHeader($headerParams);
// Turn auth header into a SOAP Header
$header = new SoapHeader($ns, 'AuthenticationToken', $auth, false);
// set the header
$client->__setSoapHeaders($header);
// Check if shipping is ready - base call
$ready_to_ship = $client->isReady();
// $last_request = $client->__getLastRequest();
$last_response = $client->__getLastResponse();
//print $last_request;
if ($last_response == true) {
print "Ready to ship\n";
// Create the shipping request
$d = new stdClass;
$d->nmfcClass = $ship_class;
$d->weight = $ship_weight;
$p = new stdClass;
$p->LTLRateShipmentRequest->destinationCountry = $destination_country;
$p->LTLRateShipmentRequest->destinationPostalCode = $destination_postal_code;
$p->LTLRateShipmentRequest->details = array($d);
$p->LTLRateShipmentRequest->originCountry = $origin_country;
$p->LTLRateShipmentRequest->originPostalCode = $origin_postal_code;
$p->LTLRateShipmentRequest->shipmentDateCCYYMMDD = $ship_date;
$p->LTLRateShipmentRequest->tariffName = $tariff;
$quote = $client->LTLRateShipment($p);
$last_request = $client->__getLastRequest();
$last_response = $client->__getLastResponse();
print "Request: " . $last_request;
print "\nResponse: " . $last_response;
}