How to change the response function name in Zend Soap Server? - php

I'm generating a WSDL and I need to change the function name in the response to match the name from the client (which I have no control over).
Here's the WSDL response I'm getting:
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://localhost:8000/soap/index.php?wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:fooFunctionResponse>
<return xsi:type="xsd:boolean">true</return>
</ns1:fooFunctionResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I need the line <ns1:fooFunctionResponse> to read <ns1:fooFunctionAcknowledgement>.
Here is my Soap server:
if (isset($_GET['wsdl'])) {
ini_set('soap.wsdl_cache_enabled', 0);
$soapAutoDiscover = new \Zend\Soap\AutoDiscover(new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence());
$soapAutoDiscover->setBindingStyle(array('style' => 'document'));
$soapAutoDiscover->setOperationBodyStyle(array('use' => 'literal'));
$soapAutoDiscover->setClass('SoapFunction');
$soapAutoDiscover->setUri(http://localhost:8000/soap/index.php);
$soapAutoDiscover->handle();
} else {
$soap = new \Zend\Soap\Server(null, array("soap_version" => SOAP_1_2, 'uri' => 'http://localhost:8000/soap/index.php?wsdl', 'classmap' => array('Identification', 'RemoteIdentification')));
$soap->setClass('SoapFunction');
$soap->handle();
}
I've found this line of code in the Zend framework (Autodiscover.php line 514) which looks like it controls the naming of the function:
$element = [
'name' => $functionName . 'Response',
'sequence' => $sequence
];
But changing it does nothing at all, the parent method is never called. I've no idea how to solve this problem, please help.
I've discovered that this line does change the function name, however I'm using SoapUI to test my API, and from SoapUI I always see Response instead of whatever I change the string to. In Chrome, I see Acknowledgement.
Why does SoapUI show a different function name?

I don't know much about Zend Framework and/or it's structure. But I think you can simply extend the class containing the response() method and overwrite the method.
// Example Zend Class
class Zend_Class {
public function response()
{
// ...
}
}
// Your own Class
class Own_Class extends Zend_Class
{
public function ResponseBoohoo()
{
return parent::response();
}
}
If I totally and utterly misunderstood you, I apologize. :)

Related

WSSE Security PHP SoapServer- Header not understood

I have a client call with a WSSE Security Header:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header><wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsse:UsernameToken wsu:Id="UsernameToken-7BCCD9337425FBA038149772606059420"><wsse:Username>USERNAME</wsse:Username><wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">PASSWORD</wsse:Password><wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">NONCE</wsse:Nonce><wsu:Created>2017-06-17T19:01:00.594Z</wsu:Created></wsse:UsernameToken></wsse:Security></soapenv:Header>
<soapenv:Body>
<ns:FUNCTION/>
</soapenv:Body>
</soapenv:Envelope>
As SoapServer I have a simple one:
// the SOAP Server options
$options=array(
'trace' => true
, 'cache_wsdl' => 0
, 'soap_version' => SOAP_1_1
, 'encoding' => 'UTF-8'
);
$wsdl = 'http://localhost/index.php?wsdl';
$server = new \SoapServer($wsdl, $options);
$server->setClass('ServerClass');
$server->handle();
$response = ob_get_clean();
echo $response;
As soon the property MustUnderstand = 1, then I become from the server the exception: Header not understood.
How to understand the header?
How to make the WSSE validation on the SoapServer side?
The solution is very tricky! I don't know why is this handled by this way from the SoapServer, but here is the solution:
class ServerClass {
public function Security($data) {
// ... do nothing
}
public function myFunction(){
// here the body function implementation
}
}
We need to define a function in our class, which is handling the soap request with the name of the header tag, which is holding the soap:mustUnderstand property. The function doesn't need to be implemented in some way.
That's all!
Mutatos' question / answer got me on the right track. I was working outside of a class structure so what worked for me was the following:
function Security($data)
{
$username = $data->UsernameToken->Username;
$password = $data->UsernameToken->Password;
//check security credentials here
}
$server = new SoapServer("schema/wsdls/FCI_BookingPullService.wsdl", array('soap_version' => SOAP_1_2));
$server->addFunction("Security");
$server->handle();
Essentially a function with the same name as the SOAP header "<wsse:Security>" (ignore the namespace) is being defined, then telling the server to use that to process the header with the 'addFunction' method.
Not ideal from a scope point of view, if that's an issue, try the class approach.

Issues with SOAP in non-WSDL mode

I am making a simple web service for communication between two sites I own.
Since it is only a basic application, I've been working without a WSDL file, so in non-WSDL mode as the PHP manual calls it.
This is basically what the client-side looks like:
$client = new SoapClient(null, array('location' => $location, 'uri' => '', 'trace' => TRUE));
$res = $client->__soapCall('myFunc', array($param));
On the server-side, I have a function called myFunc:
$soap = new SoapServer(null, array('uri' => ''));
$soap->addFunction('myFunc');
//Use the request to invoke the service
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$soap->handle();
}
When I actually try invoking the myFunc function, I get and error:
Function 'ns1:myFunc' doesn't exist
For some reason, the soap server is prepending ns1: to the function name!
Using $client->__getLastRequest(), I get:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:getTopElement>
<param0 xsi:type="xsd:string">rightcolBox</param0>
</ns1:getTopElement>
</SOAP-ENV:Body>
I'm not a SOAP expert, but it seems to me that the client is causing the error in making the request - or is the server misinterpreting it?
How can I fix this issue?
I had the same problem. When I set SoapClient options' uri to not null, the problem was solved.
$client = new SoapClient(null, array('location' => $location, 'uri' => 'foo', 'trace' => TRUE));
Yes, it can be any string, even "foo" as in this example.

PHP SoapServer formatting

I am new to SOAP and trying to format a SOAP response using PHP's SoapServer class.
It might be useful to know that this is for an EWS Push Subscription callback service and that I am using the php-ews library. I have managed to subscribe to the EWS Push Subscription but am having trouble formatting the reply which EWS is expecting from my callback service.
I need the following SOAP response returned by my PHP SOAP service:
<s:Envelope xmlns:s= "http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<SendNotificationResult xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
<SubscriptionStatus>OK</SubscriptionStatus>
</SendNotificationResult>
</s:Body>
</s:Envelope>
Instead I am getting the following output:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://localhost/ioc/ebooking_v4/services/ews/notification.php" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:SendNotificationResponse>
<return xsi:type="SOAP-ENC:Struct">
<SubscriptionStatus xsi:type="xsd:string">OK</SubscriptionStatus>
</return>
</ns1:SendNotificationResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
My PHP service code:
class ewsService {
public function SendNotification( $arg ) {
$result = new EWSType_SendNotificationResultType();
$result->SubscriptionStatus = 'OK';
return $result;
}
}
$server = new soapServer( null, array(
'uri' => $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'],
));
$server->setObject( new ewsService() );
$server->handle();
The SOAP call to my service is as follows:
<soap11:Envelope xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
<soap11:Header>
<t:RequestServerVersion xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" Version="Exchange2010_SP2" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" />
</soap11:Header>
<soap11:Body>
<m:SendNotification xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">
<m:ResponseMessages>
<m:SendNotificationResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:Notification>
<t:SubscriptionId>GQBjaGNpb2ExODEuY2lvLm9seW1waWMub3JnEAAAAFgvEpJcS3JDkpvHOMgnX60FhmhpPInRCA==</t:SubscriptionId>
<t:PreviousWatermark>AQAAAIiCiXu/q1ZPvYPyvRVVh5cajSoKAAAAAAA=</t:PreviousWatermark>
<t:MoreEvents>false</t:MoreEvents>
<t:StatusEvent>
<t:Watermark>AQAAAIiCiXu/q1ZPvYPyvRVVh5cajSoKAAAAAAA=</t:Watermark>
</t:StatusEvent>
</m:Notification>
</m:SendNotificationResponseMessage>
</m:ResponseMessages>
</m:SendNotification>
</soap11:Body>
</soap11:Envelope>
So I suppose my question is how do I to change the formatting of the SOAP response, using the PHP SoapServer class? Do I need to add a WSDL or pass a classmap to the SoapServer constructor?
If there is no way to do this using the SoapServer class, is there any other way to do this other than creating a string buffer and passing that back manually?
Any help would be greatly appreciated.
I figured this out and posted the solution on a more generic question of this problem here
For completeness I have included the answer here as well...
It would seem that I was close but needed to include the NotificationService.wsdl when instantiating the SoapServer class. The WSDL then allows that SoapServer class to format the response accordingly.
$server = new SoapServer( PHPEWS_PATH.'/wsdl/NotificationService.wsdl', array(
The WSDL was not included in the php-ews library download, however it is included with the Exchange Server installation. If like me you don't have access to the Exchange Server installation you can find the file here. I also had to add the following to the end of the WSDL because I was storing the WSDLs locally and not using autodiscovery:
<wsdl:service name="NotificationServices">
<wsdl:port name="NotificationServicePort" binding="tns:NotificationServiceBinding">
<soap:address location="" />
</wsdl:port>
</wsdl:service>
So the full working PHP code is as follows:
class ewsService {
public function SendNotification( $arg ) {
$result = new EWSType_SendNotificationResultType();
$result->SubscriptionStatus = 'OK';
//$result->SubscriptionStatus = 'Unsubscribe';
return $result;
}
}
$server = new SoapServer( PHPEWS_PATH.'/wsdl/NotificationService.wsdl', array(
'uri' => $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'],
));
$server->setObject( $service = new ewsService() );
$server->handle();
Which gives the following output:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://schemas.microsoft.com/exchange/services/2006/messages">
<SOAP-ENV:Body>
<ns1:SendNotificationResult>
<ns1:SubscriptionStatus>OK</ns1:SubscriptionStatus>
</ns1:SendNotificationResult>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Hope that helps someone else because that took me a while to figure out!

SOAP set proper xmlns headers

I have a two fold problem with setting SOAP headers. Primarily, I've never done it before and two, I can't seem to find a good solution on here for doing so. I apologize if there are exact duplicates, and please point me in the right direction if there are.
I need to set the following xmlns:xsi and xmlns:xsd data sets on the soap:Envelope. I also need to set an xmlns attribute on the first tag in the XML (rough example).
The first part needs to be added, the second part is already in there when I do a __getLastRequest(). And the third part needs to be added (just the SendPurchases xmlns attribute).
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/
xmlns:ns1="urn:[taken out for security purposes]">
<soap:Body>
<SendPurchases xmlns="urn:...">
</SendPurchases>
</soap:Body>
Would I need to use the header() for this?
I am using PHP's SOAP client. Any help at all is greatly appreciated!
EDIT:
I went with another route, thank you for all your answers though!
I had a simular problem with the envelope, I made a fix for that. I will post the fix with the data you provided, You will have to check if everything is oke:
The custom class to edit the request:
class CustomSoapClient extends SoapClient {
function __doRequest($request, $location, $action, $version) {
$request = str_replace('<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema">', '', $request);
// parent call
return parent::__doRequest($request, $location, $action, $version);
}
}
Setup the soap client:
$client = new CustomSoapClient($wsdl,
array(
'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
'exceptions' => true,
'trace' => 1,
'soap_version' => SOAP_1_2,
)
);
The request:
//notice that the envelope is in the request! also you need to change the urn
$request = '
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/xmlns:ns1="urn:[taken out for security purposes]">
<soap:Body>
<SendPurchases xmlns="urn:...">
</SendPurchases>
</soap:Body>
</SOAP-ENV:Envelope>';
$xmlvar = new SoapVar($request, XSD_ANYXML);
$result = $client->Controleer($xmlvar);
print_r($result); // finally check the result
I hope this will help you :)
A verbose example of the correct implementation using PHP's SoapClient can be found here: http://www.php.net/manual/en/soapclient.soapclient.php#97273

Getting parameter names from SOAP request with PHP SOAP extension?

Given that the following client.php creates this request XML:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://soap.dev/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:Test>
<RequestId xsi:type="xsd:int">1</RequestId>
<PartnerId xsi:type="xsd:int">99</PartnerId>
</ns1:Test>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
How do I access the names of the parameters? (RequestId and PartnerId) inside a server.php? The names are clearly there in the payload, but on server side only the values are received (1 and 99)
Sample code follows:
client.php
<?php
$client_params = array(
'location' => 'http://soap.dev/server.php',
'uri' => 'http://soap.dev/',
'trace' => 1
);
$client = new SoapClient(null, $client_params);
try {
$res = $client->Test(
new SoapParam(1, 'RequestId'),
new SoapParam(99, 'PartnerId')
);
} catch (Exception $Ex) {
print $Ex->getMessage();
}
var_dump($client->__getLastRequest());
var_dump($client->__getLastResponse());
server.php
class receiver {
public function __call ($name, $params)
{
$args = func_get_args();
// here $params equals to array(1, 99)
// I want the names as well.
return var_export($args, 1);
}
}
$server_options = array('uri' => 'http://soap.dev/');
$server = new SoapServer(null, $server_options);
$server->setClass('receiver');
$server->handle();
Please note that I can not really change the incoming request format.
Also, I am aware that I could give the names back to parameters by creating a Test function with $RequestId and $PartnerId parameters.
But what I really want to is to get name/value pairs out of incoming request.
So far the only idea I have is to simply parse the XML and this cannot be right.
Back when I had this problem I finally decided to go with the proxy function idea - Test function with parameters named ($RequestId and $PartnerId) to give names back to parameters. Its adequate solution, but certainly not the best.
I have not yet lost the hope for finding a better solution though, and here is my best idea so far
<?php
class Receiver {
private $data;
public function Test ()
{
return var_export($this->data, 1);
}
public function int ($xml)
{
// receives the following string
// <PartnerId xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:int">99</PartnerId>
$element = simplexml_load_string($xml);
$this->data[$element->getName()] = (string)$element;
}
}
$Receiver = new Receiver();
$int = array(
'type_name' => 'int'
, 'type_ns' => 'http://www.w3.org/2001/XMLSchema'
, 'from_xml' => array($Receiver, 'int')
);
$server_options = array('uri' => 'http://www.w3.org/2001/XMLSchema', 'typemap' => array($int), 'actor' => 'http://www.w3.org/2001/XMLSchema');
$server = new SoapServer(null, $server_options);
$server->setObject($Receiver);
$server->handle();
It still means parsing XML manually, but one element at a time which is a bit more sane that parsing entire incoming SOAP message.

Categories