Basic SOAP/PHP example - php

I would like to get a working example in PHP that parses a SOAP file.
I am following an example from here: http://www.php.net/manual/en/soapclient.dorequest.php .
It does not need to use this script (copied below) in particular, but it should be fairly simple to understand for learning purposes. I was using this url (just a random link that I Googled):https://www.paypalobjects.com/wsdl/PayPalSvc.wsdl , I tried plugging it into both 'location' and 'uri' near the bottom, but in both cases it did not work.
Essentially, I am looking for a simple short script to learn from for the purposes of parsing SOAP files.
<?php
function Add($x,$y) {
return $x+$y;
}
class LocalSoapClient extends SoapClient {
function __construct($wsdl, $options) {
parent::__construct($wsdl, $options);
$this->server = new SoapServer($wsdl, $options);
$this->server->addFunction('Add');
}
function __doRequest($request, $location, $action, $version, $one_way = 0) {
ob_start();
$this->server->handle($request);
$response = ob_get_contents();
ob_end_clean();
return $response;
}
}
$x = new LocalSoapClient(NULL,array('location'=>'test://',
'uri'=>'http://testuri.org'));
var_dump($x->Add(3,4));
?>

This is very advanced example, it is definitely not good as an tutorial.
First of all, make your server and client in separate scripts. You know, SOAP is about communication between two endpoints. Also, do not subclass PHP SoapClient nor SoapServer, just use them.
Update: I have just Googled some tutorials: server and client. Cannot find any English tutorial for client and server. Just take care when googling, that the found page is for PHP SOAP. There are also nusoap, PHP Zend SOAP (very similar to PHP SOAP) and maybe some more implementations. Also good source are PHP test http://svn.php.net/viewvc/php/php-src/trunk/ext/soap/tests/

Related

Q&A: How to get POST variables with PHP on Alibaba Cloud Function Compute service

I played around with the PHP 7.2 runtime and HTTP trigger on Alibaba Cloud Function Compute. The basic example in the documentation is the following:
<? php
use RingCentral\Psr7\Response;
function handler($request, $context): Response{
/*
$body = $request->getBody()->getContents();
$queries = $request->getQueryParams();
$method = $request->getMethod();
$headers = $request->getHeaders();
$path = $request->getAttribute("path");
$requestURI = $request->getAttribute("requestURI");
$clientIP = $request->getAttribute("clientIP");
*/
return new Response(
200,
array(
"custom_header1" => "v1"
),
"hello world"
);
}
This works quite well. It's easy to get the query parameters from an URL. But the body content is only available in a whole string with
$request->getBody()->getContents();
Although the documentation says that the $request parameter follows the PSR-7 HTTP Message standard, it is not possible to use $request->getParsedBody() to deliver the values submitted by POST method. It didn't work as expected - the result remains empty.
The reason is the underlying technology. Alibaba Cloud Function Compute makes use of the event-driven React PHP library to handle the requests (you can check this by analyzing the $request object). So the $_POST array is empty and there is no "easy way to get POST data".
Luckily, Alibaba's Function Compute handler provides the body content by $request->getBody()->getContents(); as a string like
"bar=lala&foo=bar"
So a solution seems easiser than thought at the beginning, you can e.g. use PHP's own parse_str() function:
$data = [];
$body = $request->getBody()->getContents();
parse_str($body,$data);
If you place this snippet in the handler function, the POST variables are stored in the $data array and ready for further processing.
Hope that this helps somebody who asked the same questions than I. :-)
Kind regards,
Ralf
As you can see in the documentation you need to add a RequestBodyParserMiddleware as middleware to get a parsed PSR-7 request. It seems you didn't do that.
Also keep in mind that only the Content-Types: application/x-www-form-urlencoded and multipart/form-data are supported here. So make sure the client need to send these headers so the request can be parsed. If it's another Content-Type you need to use another middleware.
See: https://github.com/reactphp/http#requestbodyparsermiddleware for more information.
I hope this helps!
#legionth: I apologize that I didn't use the comment feature here, but my answer is too long. :-)
Thanks a lot for your comments - the usage of RequestBodyParserMiddleware is a great solution if you can control the server code. But in the context of Alibaba Cloud Function Compute service this seems not possible. I tried to find out more information about the invocation process - here are my results:
Function Compute makes use of the Docker image defined in https://github.com/aliyun/fc-docker/blob/master/php7.2/run/Dockerfile .
In the build process they download a PHP runtime environment from https://my-fc-testt.oss-cn-shanghai.aliyuncs.com/php7.2.tgz . (I didn't find this on GitHub, but the code is public downloadable.)
A shell script start_server.sh starts a PHP-CGI binary and runs a PHP script server.php.
In server.php a React\Http\Server is started by:
$server = new Server(function (ServerRequestInterface $request) {
[...]
});
[...]
$socket = new \React\Socket\Server(sprintf('0.0.0.0:%s', $port), $loop);
$server->listen($socket);
$loop->run();
As seen in the Function Compute documentation (& example of FC console), I can only use two functions:
/*
if you open the initializer feature, please implement the initializer function, as below:
*/
function initializer($context) {
}
and the handler function you can find in my first post.
Maybe Alibaba will extend the PHP runtime in future to make it possible to use a custom middleware, but currently I didn't find a way to do this.
Thanks again & kind regards,
Ralf

Seemingly random SoapFault: not a valid method

I'm experiencing a problem with my SOAP solution. Sometimes I get an error saying the following:
Function (functionA) is not a valid method for this service
Edit 8 months later
Although I could not find the cause of the problem I was able to work around it. Whenever I recieve an response from the API I check for the SoapFault and just send another identical request and use the answer that comes back the second time.(posted as an answer)
This occurs in calls from PHP like:
functionA() - expected response
functionA() - expected response
functionA() - SoapFault
functionA() - expected response
Same result is to be expected in all the above calls and the same parameters are used(if any). Since it's working fine for almost all calls I know that the function and the corresponding WSDL is there.
What I thougt were the problem was caching an old version which would not have that function. I tried disabling the caching with:
ini_set("soap.wsdl_cache_enabled", "0");
And makeing every call with added with a random dummy parameter as well as disabling it when I use Zend_SoapClient.
'cache_wsdl' = false
I hope someone could point me in any direction or have any direct suggestion on what could be the cause.
My code looks like:
public function __construct()
{
$wsdl = "http://catlovers.nl/index.php?wsdl&dummy=".rand(1000,9999);
$this->_client = new Zend_Soap_Client($wsdl, array(
'soapVersion' => SOAP_1_1,
'cache_wsdl' => false
));
$this->_client->setWsdlCache(false);
}
function __call($name, $arguments) // Calls are made this way
{
array_unshift($arguments, $this->_apiKey, $this->_user, $this->_password);
return call_user_func_array(array($this->_client, $name), $arguments);
}
public function getCat()
{
return ($this->__call('getCat',array()));
}
On "the other side" I have:
$server = new nusoap_server();
$server->wsdl->addComplexType('Cat', ....
$server->register( 'getCat', return Cat ...
function getCat($apikey, $email, $password)
{
$cat = $db->get("redCat");
return $cat;
}
First of all, try to call function using built-in SoapClient class and printing debug information:
$wsdl = "http://abcd.com/index.php?wsdl&dummy=".rand(1000,9999);
$soap = new SoapClient($wsdl, array(
'cache_wsdl' => WSDL_CACHE_NONE,
'trace' => true,
));
try {
var_dump($soap->functionA());
} catch ( Exception $ex ) {
var_dump($ex);
}
var_dump($soap->__getLastRequest());
var_dump($soap->__getLastRequestHeaders());
var_dump($soap->__getLastResponse());
var_dump($soap->__getLastResponseHeaders());
This way you'll know where is the problem. If everything is ok all the time, the problem is in Zend's class. If not, look what service responds. May be there is some server-side error or dummy generation with such id fails
I guess your problem is related to nusoap, because for many years I'm using PHP soap server/client and I never faced this problem. (but I always had strange problems with nusoap lib)
currently I'm using jool.nl web service helper which is very powerfull yet neat and object oriented library not only makes coding easier and cleaner but also provides you object oriented approach to web service designing. It also provides a nice web interface for your web service with documentation.
As this library uses internal PHP SOAP server I'm pretty sure you're problem will be disappear then.
I suggest you to give it a try and I'm sure if you make your first web service with this library you will never try something else.
I hope this helps you.
So the problem was still there after trying other solutions so I was never able to find underlying cause of the problem. On the other hand I found a way to work around the problem that has been working since I wrote it. This is how my call to the API looks like with user,password and key for authentication.
function __call($name, $arguments)
{
/* Using the stored data to ensure that the user is allowed to access */
/* ............... */
array_unshift($arguments, $this->_apiKey, $this->_user, $this->_password);
$call = call_user_func_array(array($this->_client, $name), $arguments);
if(isset($call->faultstring) && substr(trim($call->faultstring),0,7) == "Function")
{
$recall = call_user_func_array(array($this->_client, $name), $arguments);
return $recall;
}
else
return $call;
}
This is basicly: if it doesn't work the first time just try again.

How to generate wsdl

I am working for the first time on SOAP. After going through various tutorials I have been able to create a small and simple soap server which offers two functions. Although I have been able to create the SOAP service and test it with a client I am not being able to understand WSDL even after going through the tutorials e.g. at http://www.w3schools.com/wsdl/wsdl_binding.asp .
So how can I make this SOAP "wsdl soap" ?? Is there anyway way to autogenerate it in PHP? I don't mind writting it down manually provided I know what to write. Where do I put what code to make it "wsdl" ??
function geolocate(double $lat, double $lng)
{
$contents = file_get_contents('http://api.geonames.org/findNearby?lat='.$lat.'&lng='.$lng.'&username=imranomar');
return $contents;
}
function describe(long $geonameid)
{
return file_get_contents('http://ws.geonames.org/get?geonameId='.$geonameid.'&style=full');
}
$server = new SoapServer(null, array('uri' => "urn://localhost/firstmobile?wsdl"));
$server->addFunction('geolocate');
$server->addFunction('describe');
$server->handle();
Note: I cannot be using any framwork
I suggest you use Zend_Soap_Server as it simplify webservices developpement in PHP.
It is possible to auto generate SOAP WSDL using Zend AutoDiscovery

Problems implementing a PHP Thrift server

I'm currently trying to create a PHP Thrift server which will be accessed by a PHP client to perform addition of two integers. Just a basic implementation to get the basics of a Thrift client/server working, but I'm not totally clear on how to set up the PHP server side of things. This is being done on Amazon EC2 localhost. Excuse the names of things - I ran out of variations of the word test.
This is the basic Thrift IDL - http://pastebin.com/3KGGrDUN
namespace php gaybear
service addTest {
void ping(),
i32 add(1:i32 num1, 2:i32 num2),
}
This is currently the code for the server side of things - http://pastebin.com/CWnernxf
<?
$GLOBALS['THRIFT_ROOT'] = '/usr/lib/php';
require_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php';
require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php';
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TPhpStream.php';
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';
$GEN_DIR = '/usr/lib/php/gen-php/gaybear/';
require_once $GEN_DIR.'/addTest.php';
require_once $GEN_DIR.'/gaybear_types.php';
class addHandler {
public function ping() {
}
public function add($num1, $num2) {
return $num1 + $num2;
}
public function zip() {
}
}
$handler = new addHandler();
$processor = new addTest($handler);
$transport = new TBufferedTransport(new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W));
$protocol = new TBinaryProtocol($transport, true, true);
$transport->open();
$processor->process($protocol, $protocol);
$transport->close();
?>
I'm not sure how you go about setting up the server side of things. For other languages it seems to be as simple as defining a socket, but from reading many tutorials PHP seems to use "TPhpStream".
Is there anyone that could shed some light into creating a Thrift PHP server and getting a PHP client to call basic procedures from it? I haven't found a tutorial that has explained the creation of Thrift PHP server well enough for me to understand.
Thanks.
Not in English, but much code samples and you can try to use Google translate.
http://www.easy-coding.de/wiki/php/thrift-php-server.html

SOAP Client Error: "Error Fetching Http Headers"

I am trying to use a SOAP Client-Server in my computer and it doesn't look like it is going to work, I am getting this error Error Fetching Http Headers when I try to run my SOAP Client.
I have been looking and the solution that I have encountred is to increase the default_socket_timeout from 60 to 120 seconds and it doesn't work for me, also I have seen another solution that is putting the vhost in my apache KeepAlive Off and that didn't work.
The WSDL is working fine because I try to use it in another computer and it work.
I am running PHP Version 5.3.5-1ubuntu7.4 in Linux Mint using Zend Framework, I hope some of you can help me fix this thank you.
I'm sorry but I don't know what you are using to set up your SOAP service.....
If you can give more information about your SOAP service (poss Zend_Soap given the Zend Framework tag) etc that would be great.
Also, as a quick alternative, you say you've looked at the WSDL on another computer, perhaps try the application in an alternative environment to ensure it's not an environment issue.
May be a simple issue with your client-server code.
UPDATE: Ok so I realised the example I mentioned yesterday wasn't fully implemented so I've hacked something together quickly that you can try to see if it works in your environment.
The code is a mix of something I found here (an example of Zend_Soap_Server) and something from another SO question here (an example of a basic SOAP service test).
I've tested it at my end using ZF 1.11 and the example I'm outlining uses the default Application path you get with a new ZF project (e.g models are in directory application/models so the model shown is headed up Application_Model_Classname).
If it works, you can tweak accordingly....if it doesn't work we can try something else.
Start by creating a new SOAP controller and set the class up like this:
<?php
class SoapController extends Zend_Controller_Action
{
public function init()
{
ini_set("soap.wsdl_cache_enabled", "0"); //disable WSDL caching
$this->_helper->layout()->disableLayout(); //disable the layout
$this->_helper->viewRenderer->setNoRender(); //disable the view
}
public function indexAction ()
{
if (isset($_GET['wsdl'])) {
//return the WSDL
$this->handleWSDL();
} else {
//handle SOAP request
$this->handleSOAP();
}
}
private function handleWSDL ()
{
$strategy = new Zend_Soap_Wsdl_Strategy_AnyType();
$autodiscover = new Zend_Soap_AutoDiscover();
$autodiscover->setComplexTypeStrategy($strategy);
$autodiscover->setClass('Application_Model_SoapService');
$autodiscover->handle();
}
private function handleSOAP ()
{
$server = new Zend_Soap_Server(null,
array('uri' => "http://YOURDOMAIN/soap?wsdl"));
$server->setClass("Application_Model_SoapService");
$server->handle();
}
public function testAction()
{
$client = new Zend_Soap_Client("http://YOURDOMAIN/soap?wsdl");
try {
echo $client->testMethod('test');
} catch (Exception $e) {
echo $e;
}
}
}
In the class above, the WSDL is automatically generated using Zend_Soap_Autodiscover with a SoapService.php file at application/models/SoapService.php used as the template. Note the DocBock comments above each method in your target class are integral to this process.
Next create the SoapService.php file in the default models folder:
<?php
class Application_Model_SoapService
{
/**
* testMethod
*
* #param string $string
* #return string $testSuccess
*/
public function testMethod(string $string)
{
$testSuccess = 'Test successful, the message was: ' . $string;
return $testSuccess;
}
}
If all is working as it should be you can visit:
http://YOURDOMAIN/soap?wsdl
to see the WSDL and visit:
http://YOURDOMAIN/soap/test
to get a success message with the string you specified in the client request within the testAction() code in the SoapController class as part of the message.
Let me know if it's working or not and we can go from there.
I'll be able to have another look on Monday.

Categories