PHP SoapClient Multiple Arguments with the same name? - php

I'm trying to do a SOAP call in PHP, it works normally, but i'm with a doubt: How do I can pass arguments to XML creating new nodes according to an array of product's quantity? See this...
That's my XML in SoapUI (with the parts that are important: ITEMSITM > TITEMSITM. The first TITEMSITM is with the fields, the others its the same thing):
<soapenv:Header/>
<soapenv:Body>
<ns:MANUTENCAOSITM>
<ns:SITM>
<ns:CABECALHOSITM>
...
</ns:CABECALHOSITM>
<ns:ITEMSITM>
<!--Zero or more repetitions:-->
<ns:TITEMSITM>
<ns:CODIGOPRODUTO>0000265</ns:CODIGOPRODUTO>
<ns:DESCRICAOPRODUTO>REQ.CAT.0,410 POLI (PL10)</ns:DESCRICAOPRODUTO>
<ns:PERCENTUALDESCONTO>-1.03</ns:PERCENTUALDESCONTO>
<ns:PESOUNITARIO>0.41</ns:PESOUNITARIO>
<ns:PRECOBONIFICADO>10</ns:PRECOBONIFICADO>
<ns:PRECOTABELA>9.700</ns:PRECOTABELA>
<ns:PRECOUNITARIO>9.6</ns:PRECOUNITARIO>
<ns:QUANTIDADEBONIFICADA>20</ns:QUANTIDADEBONIFICADA>
<ns:QUANTIDADEVENDA>200</ns:QUANTIDADEVENDA>
<ns:SALDOBONIFICADO>0</ns:SALDOBONIFICADO>
<ns:TOTALBRUTO>1940.000</ns:TOTALBRUTO>
<ns:TOTALLIQUIDO>1920.000</ns:TOTALLIQUIDO>
<ns:TOTALPESO>82.000</ns:TOTALPESO>
<ns:VALORBONIFICADO>9.700</ns:VALORBONIFICADO>
<ns:VALORLIQUIDO>8.9550</ns:VALORLIQUIDO>
</ns:TITEMSITM>
<ns:TITEMSITM>
...
</ns:TITEMSITM>
<ns:TITEMSITM>
...
</ns:TITEMSITM>
</ns:ITEMSITM>
<ns:RODAPESITM>
<ns:CRESCIMENTOANTERIOR>?</ns:CRESCIMENTOANTERIOR>
<ns:TOTALINVESTIMENTO>0.1303</ns:TOTALINVESTIMENTO>
</ns:RODAPESITM>
</ns:SITM>
<ns:TIPOOPERACAO>3</ns:TIPOOPERACAO>
</ns:MANUTENCAOSITM>
</soapenv:Body>
I need to repeat this node (TITEMSITM) for each product in PHP, but it doesn't work, it just store the last item, like this code below that I try to do, but with no success.
$arguments = array(
'SITM' => array(
'CABECALHOSITM' => $pars1,
'ITEMSITM' => array(
'TITEMSITM' => $parsItem[0],
'TITEMSITM' => $parsItem[1],
'TITEMSITM' => $parsItem[2]
// ...
),
'RODAPESITM' => $pars2
),
'TIPOOPERACAO' => $pars3
);
$inserirItens = $cliente->MANUTENCAOSITM($arguments);
The code above calls with no problems, but when I print_r or var_dump the $arguments, I see that the repetition of the TITEMSITM sends only one product. I think it's simple, but I'm not getting. Can someone help me, please?
References:
PHP SoapClient - Multiple attributes with the same key
SoapClient: how to pass multiple elements with same name?

Here is the code style I used:
$wsdl = 'https://your.api/path?wsdl';
$client = new SoapClient($wsdl);
$multipleSearchValues = [1, 2, 3, 4];
$queryData = ['yourFieldName' => $multipleSearchValues];
$results = $client->YourApiMethod($queryData);
print_r($results);

$ITEMSITM = new stdClass();
foreach ($parsItem as $item) {
$ITEMSITM->TITEMSITM[] = $item;
}
The reasons this should work is because it will closely emulate data structures of your WSDL.

Related

Passing an argument to callback function working on an array

I'm using an external class (Zebra_cURL) to execute multiple HTTP GET requests. It worked in the following way:
$items = array(
0=>array('url' => 'url0'),
1=>array('url' => 'url1'),
2=>array('url' => 'url2'),
3=>array('url' => 'url3'),
);
$curl = new Zebra_cURL();
$curl->get(array_column($urls,'url'),'scan_item',$moreimfo);
function scan_item($result,$moreimfo){
$items[$key]['size'] = strlen($result->body);
}
So my callback should fill up my $items array with more info for each url (in my case - size of the page). So there is a missing $key variable.
This class supports extra parameters in the callbacks ($moreimfo in my case). BUT as I understand the data passing to each callback will be always the same.
$result object containing the original url info ($result->info['url']). So I COULD use it to find needed array element. However this looks too inefficient in case the size of an array will be big enough.
I think that I should find how to pass an array member key information for EACH callback execution. Is it possible without modifying the original class?
If you use the url as key in the $items array the solution could be something like
<?php
$items = [
'url0'=>array('url' => 'url0'),
'url1'=>array('url' => 'url1'),
'url2'=>array('url' => 'url2'),
'url3'=>array('url' => 'url3'),
];
$curl = new Zebra_cURL();
$curl->get(
array_keys($items),
function($result) use (&$items) {
$key = $result->info['url'];
$items[$key]['size'] = strlen($result->body);
}
);
using an anymous function that "Imports" the $items array via reference.
While it doesn't solve the original problem of passing a reference to the according array element to the callback, the following should be very fast (as noted in the comments, PHP Arrays are implemented using a hashtable).
$items = array(
0=>array('url' => 'url0'),
1=>array('url' => 'url1'),
2=>array('url' => 'url2'),
3=>array('url' => 'url3'),
);
$lookup=array();
foreach($lookup as $k=>$v) {
$lookup[$v['url']]=$k;
}
$curl = new Zebra_cURL();
$curl->get(array_column($urls,'url'),'scan_item',$moreimfo);
function scan_item($result,$moreimfo){
global $lookup,$items;
$items[$lookup[$result->info['url']]]['size'] = strlen($result->body);
}
Probably you may consider using an OOP-approach, with the callback as a method, then the global-izing of the arrays shouldn't be necessary if you use $this->anyMember

Soap xml is passing reference in php

I am calling a webservice using soap in php but I am getting the error in xml as response from the server.
The problem is that when creating the xml for the request Php introduces the id in the xml and then wherever it finds the same node it just passes the id as the reference.
Eg:-
<ns1:ChargeBU id=\"ref1\">
<ns1:ChargeBreakUp>
<ns1:PriceId>0</ns1:PriceId>
<ns1:ChargeType>TboMarkup</ns1:ChargeType>
<ns1:Amount>35</ns1:Amount>
</ns1:ChargeBreakUp><ns1:ChargeBreakUp>
<ns1:PriceId>0</ns1:PriceId>
<ns1:ChargeType>OtherCharges</ns1:ChargeType>
<ns1:Amount>0.00</ns1:Amount>
</ns1:ChargeBreakUp>
</ns1:ChargeBU>
and then when it finds the same node it does this
<ns1:ChargeBU href=\"#ref1\"/>
So how can i prevent this so that it includes the full node again instead of just passing the reference ??
I had the same issue but couldn't figure out anything to do differently within SoapClient to fix it. i ended up overriding __doRequest() to modify the xml before sending to remove the reference id's from the elements and replace the reference elements with the elements they reference. if you do this, be sure to fix __getLastRequest() as shown here.
Edit: Using unserialize(serialize($input)) before sending seems to have fixed this for me.
you can create a new copy (instance) of that array to prevent php to use refs for the same values.
for example, we have:
$item = array(
"id" => 1,
"name" => "test value"
);
and our request/response:
$response = array(
"item1" => $item,
"item2" => $item
);
by default, php will replace item2 value with reference to item1 (both items point to the same array)
in order to prevent such behaviour, we need to create two different items with the same structure, something like:
function copyArray($source){
$result = array();
foreach($source as $key => $item){
$result[$key] = (is_array($item) ? copyArray($item) : $item);
}
return $result;
}
and request/response:
$response = array(
"item1" => copyArray($item),
"item2" => copyArray($item)
);
the same by structure items are in fact different arrays in memory and php will not generate any refs in this case
I did some research, and SOAP extension, nuSOAP, WSO2 are not supported since 2010. They are full of unfixed bugs, I don't recommend to use them.
Use Zend 2 SOAP instead which does not use any unsupported extension, or if you are a Symfony fan, then try out the BeSimple SOAP boundle, which tries to fix the bugs of the SOAP extension. Don't reinvent the wheel!
I have changed the function a little bit because. if the $source is not an array ,we have a small problem in the foreach
function copyArray(Array $source){
$result = array();
if($source) { // not for empty arrays
foreach($source as $key => $item){
$result[$key] = (is_array($item) ? copyArray($item) : $item);
}
}
return $result;
}

How to structure complex nested soap parameters

Alright, this problem is driving me up the wall. I'm unsuccessfully trying to connect to a web service using PHP and SOAP. I can't figure out what's wrong, and what's more this is a brand new service and their "documentation" is POOR. So I have no idea if the problem isn't actually on their end, but I don't have enough experience with SOAP to be able to know that for sure. I pray someone can help me.
I have been able to connect to the service by putting the XML directly into SOAP UI, but whenever I try to use the SoapClient it breaks down. The XML structure I am looking to send looks like
<Envelope xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://a.uri" xmlns:ns3="http://tempuri.org/">
<Body>
<GetAuthorization>
<ns1:registrationObj ns1:type="ns2:RegistrationAuthorization">
<ns2:Company>####</ns2:Company>
<ns2:Computer>####</ns2:Computer>
<ns2:Facility>####</ns2:Facility>
</ns1:registrationObj>
</GetAuthorization>
</Body>
</Envelope>
I have tried approaches too numerous to list. Using __soapCall, and $client->method(), SoapVar, and SoapParam. On the whole I find the documentation for PHP's SoapClient to be a bit sparse. But I can't even get the structure of the call to match what I want it to (dumped via __getLastRequest())
One thing I have noticed is the client keeps dropping the first element of my array (on those instances when I try to pass the parameters in as a plain array. So:
$params = array("Company" => "abc",
"Computer" => "def",
"Facility" => "ghi");
$result = $soap_client->__soapCall('GetAuthorization',$params);
returns
<env:Body>
<ns1:GetAuthorization/>
<param1>def</param1>
<param2>ghi</param2>
</env:Body>
note how in this instance the GetAuthorization self closed AND dropped the first array element. I have experienced both separately as well (and it is worth noting that I have gotten the paramaters to be named correctly also. I have gone through so many iterations I can't remember what attempts yield which results. Nonetheless, SOAP is NOT behaving like I would expect it to. It fails to encapsulate data properly and/or drops random elements.
$parameters =
array("ra" => new SoapVar(array(
"CompanyId" => new SoapVar("####", SOAP_ENC_OBJECT, 'guid', 'http://schemas.microsoft.com/2003/10/Serialization/', 'CompanyId', 'http://schemas.datacontract.org/x/y/z.xx'),
"ComputerId" => new SoapVar("{####}", SOAP_ENC_OBJECT, 'string', 'http://www.w3.org/2001/XMLSchema', 'ComputerId', 'http://schemas.datacontract.org/x/y/z.xx'),
"FacilityId" => new SoapVar("####", SOAP_ENC_OBJECT, 'guid', 'http://schemas.microsoft.com/2003/10/Serialization/', 'FacilityId', 'http://schemas.datacontract.org/x/y/z.xx')
), SOAP_ENC_OBJECT, 'RegistrationAuthorization', 'http://schemas.datacontract.org/x/y/z.xx', 'ra', "http://schemas.datacontract.org/x/y/z.xx"
)));
$result = $auth_client->GetAuthorization($parameters);
Is the structure I was trying to push through originally (before I simplified it to try to figure out what was wrong), figuring that since I need so much control over the namespacing of the parameters I would need to. BUT that just makes the request with a self-closed element.
Can someone PLEASE tell me how to structure the call to yield the appropriate XML structure? Is it possible this is on the Service's end and there is something wrong with the WSDL? (I'm not sure exactly what the WSDL is responsible for on the back end.)
I do apologize for the vagueness of the question, but I feel so lost I'm not even sure of the right one to ask. :-(
It should work:
<?php
$client = new \SoapClient(null, array(
'uri' => 'http://localhost/stack/21150482/',
'location' => 'http://localhost/stack/21150482/server.php',
'trace' => true
));
try {
$company = new \SoapVar('XXXXX', XSD_STRING, null, null, 'Company', 'http://a.uri');
$computer = new \SoapVar('XXXXX', XSD_STRING, null, null, 'Computer', 'http://a.uri');
$facility = new \SoapVar('XXXXX', XSD_STRING, null, null, 'Facility', 'http://a.uri');
$registrationObj = new \SoapVar(
array($company,$computer,$facility),
SOAP_ENC_OBJECT,
'RegistrationAuthorization',
'http://a.uri',
'registrationObj',
'http://www.w3.org/2001/XMLSchema-instance'
);
$client->GetAuthorization($registrationObj);
} catch (\Exception $e) {
var_dump($e->getMessage());
}
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($client->__getLastRequest());
print '<pre>';
print htmlspecialchars($dom->saveXML());
My result is:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://localhost/stack/21150482/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://a.uri" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:GetAuthorization>
<xsi:registrationObj xsi:type="ns2:RegistrationAuthorization">
<ns2:Company xsi:type="xsd:string">XXXXX</ns2:Company>
<ns2:Computer xsi:type="xsd:string">XXXXX</ns2:Computer>
<ns2:Facility xsi:type="xsd:string">XXXXX</ns2:Facility>
</xsi:registrationObj>
</ns1:GetAuthorization>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Different approach I have used is:
<?php
//note the "(object)" casting
$params = (object)[
'registrationObj' => (object)[
'Company' => 'abc',
'Computer' => 'def',
'Facility' => 'ghi'
]
];
$soap_client = new SoapClient('localhost/soap');
//direct call of the method GetAuthorization (without __soapCall)
$return = $soap_client->GetAuthorization($params);
Should work for PHP >= 5.4

How to form SoapClient method parameters for XML attributes?

I'm trying to integrate a SOAP service into our app. For the UpdateCart method, their documentation states that it requires four properties. However, in their WSDL, which can be found here https://mews.mouser.com/cartservice.asmx?WSDL, it shows that it only requires a single property. Normally I would call the method via a SoapClient like so:
$soapClient->UpdateCart(array('xmlCartMessage' => $value));
Following their documentation, I should call it like so:
$soapClient->UpdateCart(array(
'CartGUID' => $value1,
'Requestor' => $value2,
'MouserPartNumber' => $value3,
'Quantity' => $value4
));
However, it doesn't work. It gives me the following error:
"faultstring":"Server was unable to process request. ---> String reference not set to an instance of a String.\nParameter name: s","faultcode":"soap:Server","detail":""
Their documentation states that the request for UpdateCart should look like this:
<?xml version="1.0" encoding="utf-8"?>
<CartMessage CartGUID="" Requestor=“ADI” xmlns="http://tempuri.org/XMLSchema.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CartItem MouserPartNumber="595-430F157IPM" Quantity="37" TransactionID="1" CountryCode=""/>
</CartMessage>
I noticed that the parameters are encoded as attributes rather than tags with values. How am I supposed to do it using SoapClient?
You can try using $soapClient->__doRequest() to submit the request. For example, assuming $soapClient is initialised and that their example request is valid, try something like:
$request = '<?xml version="1.0" encoding="utf-8"?>
<CartMessage CartGUID="" Requestor=“ADI” xmlns="http://tempuri.org/XMLSchema.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CartItem MouserPartNumber="595-430F157IPM" Quantity="37" TransactionID="1" CountryCode=""/>
</CartMessage>
';
$location = "https://mews.mouser.com/cartservice.asmx?WSDL";
$action = "http://tempuri.org/UpdateCart";
$result = $soapClient->__doRequest($request, $location, $action, 1);
$location and $action will need to be set based on the their WSDL.
See: http://www.php.net/manual/en/soapclient.dorequest.php
If you want to create a SOAP call like:
<Element Name="John" Group="USER">DATA</Attribute>
You should try something like this:
array('Element ' => array('_' => 'DATA', 'Name'=>'John', 'Group'=>'USER'));
So in Your case I assume it should be:
$CartItem = array('CartItem' => array('_' => '', 'CartGUID'=>$value1, 'Requestor'=>$value2));
$CartMessage = array('CartItem' => array('_' => $CartItem, 'MouserPartNumber'=>$value3, 'Quantity'=>$value4));
$soapClient->UpdateCart($CartMessage);

PHP SOAP Request includes multiple identical tags

OK, so I have this external SOAP based webservice, and PHP SoapClient. Everything is fine with basic requests, but I need to create a parameter set that looks like this:
<DatasetList>
<DatasetID>K0001</DatasetID>
<DatasetID>K0002</DatasetID>
</DatasetList>
For a single nested DatasetID tag I'd do:
$req = array( "DatasetList" => array( "DatasetId" => "K0001" ));
$client->getWebserviceCall($req);
but I need multiple DatasetID tags... I've tried assigning DatasetID as an array, but I just get:
<DatasetList>
<DatasetID>Array</DatasetID>
</DatasetList>
Anyone help?
Did you try the array this way?
$req = array( "DatasetList" => array("DatasetID" => array("K0001", "K0002));
You can do this only by wrote the Part with the identical tags by hand. But, the rest of values can you define in a array:
// Define multiple identical Tags for a part of the Array
$soap_var= new SoapVar('
<DatasetID>1</DatasetID>
<DatasetID>2</DatasetID>
';
// Define the other Values in the normal Way as an array
$req = array(
"DatasetList" => $soap_var,
'value2'=>array('other'=>'values'
);

Categories