I create a simple wsdl and soap server with some methods. Now I want to ask you if is possible to call two methods with one soap request?
ex.
$server->_call(array=>('methodOne','methodTwo', $args);
or i must send two soap request?
Message batching is a non-goal of SOAP. You will have to send two requests.
No, you can't. However, since you created the server, you can can simply add higher-level methods :
// low level
method1($args){
prepare_A();
}
// low level
method2($args){
finish_A();
}
// higher level
method3($args){
method1($args);
method2($args);
}
Related
I am trying to send a SOAP request to a client's API endpoint. I am not at all familiar with SOAP, so having quite a difficult time getting this to work.
From the client's documentation
The requested ticket can be used to call all the API web methods subsequently.
public string RequestTicket(
string username,
string password
);
URL
https://www.clientsurl.net/api/v01_00/APIService.asmx?wsdl
Parameters
string username
string password
I am able to create the WSDL
$client = new Client('https://www.clientsurl.ca/api/v01_00/APIService.asmx?wsdl', ['soap_version' => SOAP_1_1]);
but not sure how to send the parameters through
$params = [
'username' => 'myusername'
'password' => 'mypassword'
];
I am also not sure what the relevance of RequestTicket is. Am I supposed to add it to the url?
The answer is probably very simple, but after tons of searching I couldn't find anything. Please help.
I have write a method to send a request
protected function soapRequest(string $method, array $arguments)
{
try {
$client = new \Zend\Soap\Client($this->getWsdl(),
[
'soap_version' => SOAP_1_1,
'cache_wsdl' => WSDL_CACHE_NONE
]);
$result = $client->{$method}($arguments);
return $result->return;
} catch (\SoapFault $s) {
...
} catch (\Exception $e) {
...
}
}
You must have a Soap method to send yours parameters.
If you don't know the method name, I advise you to run SoapUI application, very useful for debugging soap requests.
A SOAP service has a set of operations that you can call over the network. These operations can also have parameters. Basically, it's just like calling a method with parameters in code just that the invocation happens over the network with the method name and parameters being marshaled into an XML that respects the rules of the SOAP protocol.
To call the SOAP service, you can either make a HTTP request of type POST to the service's endpoint (i.e. https://www.clientsurl.ca/api/v01_00/APIService.asmx) or you can use a SOAP client. A SOAP client is some code that you can generate from the WSDL of the SOAP web service, or is some code that can dynamically read the WSDL and provide you some ways to invoke the operations described there. As opposed to making a POST HTTP request, the client takes care of these details for you and allows you to make the call over the network just like you call a local method in your code.
To call an operation of the SOAP service in your client code you have to invoke a method with parameters. The name of the method and its parameters (what names and what types) are described by the WSDL of the service.
With that being said, I'll add some details about what you posted in your question.
The requested ticket can be used to call all the API web methods subsequently.
Some service operations can require authentication in order to be be allowed to invoke them. Just like you need a username and password to access protected sections of a website for example. For a SOAP web service, his can happen in a few ways, the most common two being:
you send the username and password with each call to the web service (somehow; can be as SOAP headers, as HTTP headers with BASIC Authentication, etc).
the service exposes a method that you have to call with username and password just like point 1), but then returns an access token of some sort that you then need to provide to the rest of the web service's operations. This is just like a Login page on a website where you authenticate with username and password and then you get back a SessionID that you can use on all other requests until you decide to log out.
It seems that your service uses the second approach, and RequestTicket seems to be the operation that you need to call in order to be able to call the rest of the operations after that.
I am able to create the WSDL
You do not create the WSDL, the WSDL already exists for the web service. Also make sure you do not make a confusion between the SOAP web service and its WSDL. The code you show just creates a SOAP client from the WSDL (what I described above) to allow you to invoke operations on it.
I am also not sure what the relevance of RequestTicket is. Am I supposed to add it to the url?
Most likely RequestTicket is an operation of the web service. You should look inside the WSDL to see if it's described there. The WSDL is a little tough to swallow if you are not familiar with how it works, so your best bet is to use a tool like SoapUI to feed it the web service WSDL and have SoapUI generate sample requests for the web service. You can then also use SoapUI to test the web service to make sure you understand how it works before you try to replicate the same calls with your PHP code.
I am currently busy with a PSR-7 project with responses and requests.
Currently we are setting up an application in our index.php by doing something like:
$app = new Application();
$app->loadConfiguration(
'../config/global.yml',
);
// Should return the response?
$app->run((new ServerRequestFactory())->createServerRequestFromGlobals());
Here the run method also calls an emit method that is responsible for sending the headers and printing the body of the response.
The request and respons are now linked together in one call which makes it hard to test since you don't want to send the response with the headers straight to PHPUnit.
I have removed the emit call in the chain of the run method and added this to the index after the run method call:
// Send the response.
$app->send();
This way they are decoupled but the downside is I now have to hold a instance of my response in a response property inside my Application.php($app) class.
I want to move the response instance to the response class itself but my co-workers thinks a class should never hold an instance of itself. Yet when I look at frameworks this happens quite a lot. Is he right about this?
What arguments can I make to decouple my request and response besides easier testing?
I am pretty new to unit testing, one of the arguments I have already heard is that I should not test the full application anyways but rather separate components and therefore should not be worried about de-coupling the request and response.
I have a generic HTTP file access API which I use for the system I'm working on. To make it as flexible as possible, it returns request and response data in the form of HTTP strings.
I'm currently implementing a version which interacts with the S3, via the AWS SDK for PHP 2.
Is there an easy way to quickly get the Request and Response HTTP requests which the S3Client makes when performing operations? If not, is there a more piecemeal way which I can use to not have to fake it?
Basically, I'd like the full-text of both the Request and Response on demand, or at least access to relevant data (headers, response codes, URLs, etc) so I can properly populate the return data for my framework.
Thanks.
You can get either the request or response object from a command object. Assuming $s3 holds an instance of Aws\S3\S3Client, you could do something like this:
$command = $s3->getCommand('ListObjects', array('Bucket' => '<bucket-name>'));
$request = $command->getRequest();
$response = $command->getResponse();
Those objects have methods for viewing the body, headers, status codes, etc. and you can cast them to string to see the string form.
If you want to quickly see the request and response as you are executing commands, you can attach the wire logger, and see what comes out on STDOUT (or STDERR)
$s3->addSubscriber(\Guzzle\Plugin\Log\LogPlugin::getDebugPlugin());
$s3->listObjects(array('Bucket' => '<bucket-name>'));
You will need to look into the Guzzle\Http\Client class, which is an ancestor class to S3Client, to have a look at the methods that it makes available. You can always override some of these methods in your own child of S3Client to make accessing this information easier for you.
Ultimately the data you are looking for resides in an object of class Guzzle\Http\Message\Response, which I believe is returned from Guzzle\Http\Client::send().
So perhaps in your own implementation of S3Client you can override the send() method to send the HTTP requests, then process the response data as needed.
Could you tell me if there is a tool to generate a soap client with these requirements:
soap 1.2
the client is based on three different service (so 3 wsdl)
those services have shared types
I found out about:
http://php.net/manual/en/class.soapclient.php
and
http://www.php.net/manual/en/soapclient.soapclient.php
The thing I can't find out searching for it is especially the last two point.
Any help will be appreciated because I can't unserstand how to create it from different sources and how to call a specific service.
I can't test my solution as i don't have 2-3 web services running, but i think this solution will work (if i understood you correctly). Please if you can try it and let me know.
<?php
class wstest {
function __construct($url) {
$this->soapUrl = $url;
try{
$this->client = new SoapClient($this->soapUrl,array('login' => 'wsuser', 'password' => "some_password", "connection_timeout"=>30,'trace'=>true,'keep_alive'=>false,'features' => SOAP_SINGLE_ELEMENT_ARRAYS));
} catch (Exception $e) {
echo $e->getMessage();
}
}
};
$con = new wstest("http://firstwebservice.com/?wsdl");
$con2 = new wstest("http://secondwebservice.com/?wsdl");
$con3 = new wstest("http://thirdwebservice.com/?wsdl");
?>
I'm trying to figure out what you might want to do.
First: One WSDL === one Service === one SoapClient. You cannot mix two WSDL locations on the Soap client level, but depending on your application, might connect each services' results on a higher level.
So if you have three WSDL, then you must instantiate three SoapClient classes to be used. It's not like a single generic HTTP Client which can make requests to any existing webserver.
Second: Unless you provide a classmap to the SoapClient, the return value of any request is only a mixture of stdClass and array. There might be types defined in the WSDL, but PHP does not map them to anything unless you define it.
I would recommend using a classmap with your own defined classes that match the ComplexType definitions in the WSDL. There are some code generators to be googled that might do the job, but the Soap standard is complex, as is the definitionof WSDL, so you might end up doing work by hand.
You can perfectly live without a classmap if the data structures are small.
Third: If the three WSDL share data types, this will not affect PHP in any way. Since without classmap the responses are stdClass and Array, and the Request parameters can be the same, you won't get any benefit from this information.
If on the other hand you go the way of the classmap, I'd expect that the shared types will lead to the same classes generated, so you would also see on the PHP level that a ComplexType from Service A is identical to the ComplexType of Service B.
I'm currently dealing with an archaic payment processor that makes connecting to their service as hard as possible (including a custom client SSL cert, with a password, plus basic HTTP Auth after that). Long story short, I can't use SoapClient to make the request, but I have been able to do it with cURL.
I now have the response in a string, can I use SoapClient to parse it? I'd rather not have to parse it manually as a regular XML, since I'd have to duplicate a lot of functionality, like throwing a sensible exception when finding a <SOAP:Fault>, for example.
No, you can't.
(just answering this for posterity. Based on the lack of evidence to the contrary, you apparently can't use SoapClient to parse a SOAP response you already have)
You can define context using context option of SoapClient to tell SoapClient to use SSL certificates etc. Context may be created using stream_context_create with lots of options
Let's for a second imagine you had called SoapClient::__doRequest() and it returned your XML SOAP response into a variable called $response.
<?php
//LOAD RESPONSE INTO SIMPLEXML
$xml = simplexml_load_string($response);
//REGISTER NAMESPACES
$xml->registerXPathNamespace('soap-env', 'http://schemas.xmlsoap.org/soap/envelope/');
$xml->registerXPathNamespace('somenamespace', 'http://www.somenamespace/schema/');
//...REGISTER OTHER NAMESPACES HERE...
//LOOP THROUGH AND GRAB DATA FROM A NAMESPACE
foreach($xml->xpath('//somenamespace:MessageHeader') as $header)
{
echo($header->xpath('//somenamespace:MyData'));
}
//...ETC...
?>
That is just some example/pseudo code (not tested and won't work as-is). My point is that you manually acquired the SOAP response so now all you have to do is parse it. SimpleXML is one solution you could use to do that.