Seemingly random SoapFault: not a valid method - php

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.

Related

PHP prints out a buffer string into web-page on certain circuntances?

I don't know how to explain it but, I'm gonna give it a try.
This problem concerns 2 servers, a local and a hosting server. Both servers are running the same PHP version which is 7.0 [with almost same configurations]. And 2 controller actions. And the problem comes from $app->run($input, $out); from the codes below.
I have in my controller that action:
/**
* #Route("/testJson")
*/
public function testJsonAction() {
$app = new \Symfony\Bundle\FrameworkBundle\Console\Application($this->get("kernel"));
$app->setAutoExit(false);
$opt = array("command" =>
"doctrine:generate:entity",
"--entity" => "GuervylEditorBundle:TestOnline",
"--fields" => "kl:string");
$input = new \Symfony\Component\Console\Input\ArrayInput($opt);
$out = new \Symfony\Component\Console\Output\BufferedOutput();
$app->run($input, $out);
$out->fetch();
return new JsonResponse(\json_encode(["a" => "b", "c" => "d"]));
}
Calling this action from the local and hosting server returns "{\u0022a\u0022:\u0022b\u0022,\u0022c\u0022:\u0022d\u0022}" and with Content-Type
application/json which is great, it's the expected result.
Now, here comes the problem:
That almost same code above, I set it inside another class, I call it from another controller action, which passes through 4 methods from different classes to call the method that has the code above [callCommand]
This is the method that implement the code:
public function callCommand($cmd, $opt, &$mykernel = null) {
if ($mykernel == NULL) {
$mykernel = new myKernel("dev", false, __DIR__ . "/../Resources/template_2.8/app");
}
$app = new \Symfony\Bundle\FrameworkBundle\Console\Application($mykernel);
$app->setAutoExit(false);
$opt = array("command" => $cmd) + $opt;
$input = new \Symfony\Component\Console\Input\ArrayInput($opt);
$out = new \Symfony\Component\Console\Output\BufferedOutput();
$app->run($input, $out);
}
From that other controller action, I also return a json content at the end. I can't show the code because it's too big.
When I call that controller action from my localhost, I get the JSON content and Content-Type: application/json which is fine.
But calling it from the hosting server I get extra texts like:
Entity generation
created ./src/Guervyl/EditorBundle/Entity/TestCase.php
> Generating entity class src/Guervyl/EditorBundle/Entity/TestCase.php: OK!
> Generating repository class src/Guervyl/EditorBundle/Repository/TestCaseRepository.php: OK!
Everything is OK! Now get to work :).
Which is the output texts from the console when calling $app->run($input, $out);. After that I get the HTTP header that I set then the json content. And also the content-type is application/x-httpd-php5.
That error only happens on a specific hosting server. I tested other hosting server the code works like on my local server.
My question is why am I getting the error on that specific hosting? Is there something I can change from the PHP.ini to fix it? Because I really need to host my website on that hosting because it offers me great features that I need but the others don't or they are too expensive.
Well, After I debugged the code I noticed that error happened because I did not set the --no-interaction option. So without that option, Symfony was waiting for input when no fields are specified for an Entity.

{"status":false,"error":"Unknown method"} error CodeIgniter RESTful API

<?php
include(APPPATH.'/libraries/REST_Controller.php');
class Quiz extends REST_Controller{
function __construct()
{
// Call the Model constructor
parent::__construct();
}
public function user_get()
{
$this->load->model('Quizmodel');
$data = $this->Quizmodel->getAll();
$this->response($data, 200);
}
function restclient()
{
$this->load->library('rest', array(
'server' => 'http://localhost/CodeIg/index.php/quiz/'
));
$userr = $this->rest->get('user','','json');
echo $userr;
}
}
?>
I am able to get JSON output if I type http://localhost/CodeIg/index.php/quiz/user in my browser, however if I type http://localhost/CodeIg/index.php/quiz/restclient it gives this error: {"status":false,"error":"Unknown method"}
I tried changing get to post but still the same error.
I referred this page https://code.tutsplus.com/tutorials/working-with-restful-services-in-codeigniter--net-8814 to do it.
You pinged me on GitHub, even though I haven't used or even thought about this code in at least 4 years.
https://github.com/chriskacerguis/codeigniter-restserver/blob/d19dc77f03521c7a725a4555407e1e4e7a85f6e1/application/libraries/REST_Controller.php#L680
This is where that error is being triggered. Throw a few breakpoints in there or var_dump()'s until you see what is causing the trouble.
You probably want to get off CodeIgniter though, and use something more actively maintained like SlimPHP or Lumen.
firstly I want as you have loaded rest api and created your controller quiz as an api to call , where you can only create your functions like user_get or restclient_get and access them the same manner you are doing.Just change you function name restclient to restclient_get then it will call instead it is even not running at this moment.

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.

Zend_Soap_Client error calling ASP.net web service: '...not set to an instance of an object'

I'm trying to use Zend_Soap_Client to communicate with an ASP.net web service. Here's my client call:
$client = new Zend_Soap_Client(null, array(
'location' => 'http://example.com/service.asmx',
'uri' => 'http://example.com/'
));
$user = new UserDetail();
$result = $client->UserDetails($user);
However this always gives me the error:
System.NullReferenceException: Object reference not set to an instance of an object. at Service.UserDetails(UserDetail UserDetail)
some googling revealed that this is quite a common problem. The most common solution seemed to be to pass the parameters as an array, so I tried:
$result = $client->UserDetails(array('UserDetail' => $user));
but this gave the same error. I also tried passing the params as a stdClass object, nesting the array in another with 'params' as the key, and a few other things but the error is always the same.
I have the ASP code for the web service itself, the relevant method is:
public Result UserDetails(UserDetail UserDetail) {
[some stuff]
Hashtable ht = new Hashtable();
ht = UserDetail.GenerateData();
}
the error is caused by the GenerateData() call.
I assume the UserDetails method is getting null instead of my object as the parameter, but I'm not sure how I should be calling the method, or how I can debug this further. The majority of the Zend_Soap_Client examples I've found seem to be using WSDL, which this service is not; not sure if that is relevant. Any help appreciated!
I eventually solved this with:
$userDetails = new UserDetails();
$userDetails->UserDetail = $user;
$client->UserDetails($userDetails);
it seems ASP.net expects (and returns) params to be nested in an object/array with the same name as the method being called.
If you have any possibility to change the asp.net code I'd suggest you try an implementation of the method UserDetails without parameters just to make sure that code isn't broken.
I would then create a consumer-method in asp.net, debug the http-request and see how the userdetail-object is serialized/broken down in array form. Then it's "just" a matter of creating a similar http request from php.

Inspect XML created by PHP SoapClient call before/without sending the request

The question:
Is there a way to view the XML that would be created with a PHP SoapClient function call BEFORE you actually send the request?
background:
I am new to WSDL communication, and I have a client who wants me to develop in PHP, a way to communicate with a WSDL service written in ASP.NET. I have gotten pretty far, but am running into an issue when it comes to passing a complex type. I have tried a couple of different things so far.
1) Setting up a single array such as $params->Person->name $params->Person->address
2) Setting up a single array $Person = array('name'=>"joe",'address' = "123");
then passing into the call as a param "Person" => $Person;
and a few others. But every time I get the error
SoapException: Server was unable to
process request ---> System.Exception:
Person is Required. at service name.
In order to further the troubleshooting, I would like to see the XML document that is being sent to see if it is creating a complex type in the way I am expecting it to.
I am creating the service using $client = new SoapClient('wsdldoc.asmx?WSDL'); calling it with $client->CreateUser($params); and then trying to see it using the function $client->__getLastRequest(); but it never makes it to the __getLastRequest because it hits a fatal error when calling CreateUser($params).
The question again:
Is there any way to view the XML created by the CreateUser($params) call WITHOUT actually sending it and causing a fatal error
Upfront remark: In order to use the __getLastRequest() method successfully, you have to set the 'trace' option to true on client construction:
$client = new SoapClient('wsdldoc.asmx?WSDL', array('trace' => TRUE));
This way, your request will still be sent (and therefore still fail), but you can inspect the sent xml afterwards by calling $client->__getLastRequest().
Main answer:
To get access to the generated XML before/without sending the request, you'd need to subclass the SoapClient in order to override the __doRequest() method:
class SoapClientDebug extends SoapClient
{
public function __doRequest($request, $location, $action, $version, $one_way = 0) {
// Add code to inspect/dissect/debug/adjust the XML given in $request here
// Uncomment the following line, if you actually want to do the request
// return parent::__doRequest($request, $location, $action, $version, $one_way);
}
}
You'd then use this extended class instead of the original SoapClient while debugging your problem.
I found this thread while working on the same problem, and was bummed because I was using classes that already extended the SoapClient() class and didn't want to screw around with it too much.
However if you add the "exceptions"=>0 tag when you initiate the class, it won't throw a Fatal Error (though it will print an exception):
SoapClient($soapURL, array("trace" => 1, "exceptions" => 0));
Doing that allowed me to run __getLastRequest() and analyze the XML I was sending.
I don't believe there is a way that you'll be able to see any XML that's being created... mainly because the function is failing on it's attempt to create/pass it.
Not sure if you tried already, but if you're having trouble trying to decide what exactly you need to pass into the function you could use:
$client->__getTypes();
http://us3.php.net/manual/en/soapclient.gettypes.php
Hope this helps!

Categories