I have a simple SPA built with AngularJS.
I'm attempting now to do 2 simple operations in a CouchDB hosted in Cloudant using a PHP file: save a document, and get all documents.
All premade examples of CRUD operations using PHP are extremely complex, using multiple dependencies and I didn't get far with them, as PHP is not really my thing.
The last one I tried is this one, it gives me a Cannot redeclare class Cloudant in /.../comanda/incoming/test/Cloudant.php on line 10 error and I didn't manage to fix it.
What I see in the code though, is how they make the server log in, and some get/put.
It looks like this:
class Cloudant {
function __construct($server,$db,$username,$password){
$username = 'user';
$password = 'pass';
$server = 'cloudant.com';
$db = 'dbname';
$this->server = "https://".$username.":".$password.'#'.$username.$server;
$this->db = $db;
$this->header_short = "Content-Type: application/json";
$this->header = $this->header_short."\r\nContent-Length: ";
}
//curl -X PUT {USERNAME}:{PASSWORD}#{USERNAME}.cloudant.com/{DB}{/ID} -d ....
function upsert_doc( $id, $data ){ // to update - include _rev in document body
return $this->call($id, array(
'method' => 'POST',
'header' => $this->header . strlen($data) . "\r\n",
'content' => $data));
}
//curl -X GET {USERNAME}:{PASSWORD}#{USERNAME}.cloudant.com/{DB}/{ID}
function get( $id ){
return $this->call($id, array(
'method' => 'GET',
'header' => $this->header_short
));
}
Any tips on how to simplify this? I'm looking into having one PHP file to make the authentication + saving JSON array, and another to authenticate + getting all documents.
The error is literally that you can't redeclare the class "Cloudant" because there's another one in your project somewhere - renaming this class should help with that.
For talking to Cloudant from PHP, it's an HTTP interface so while there are libraries, you don't necessarily need one for some simple queries as you mentioned. I usually use Guzzle but you could use the curl extension or the built-in stream handlers. Some examples of PHP code that uses Guzzle to talk to Cloudant are here in a sample app I built, this might help give you some pointers: https://github.com/ibm-cds-labs/guestbook/blob/master/src/web/classes/Guestbook/CommentService.php
Related
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
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.
How do I change the timeout for the Amazon product library PHP client? I searched the code for timeout, but it's not obvious how to pass a different timeout value.
https://github.com/Exeu/apai-io/search?utf8=%E2%9C%93&q=timeout
Based on the link you provided, there is no way to pass in a parameter for the timeout. You can see it's hardcoded right there in the constructor. However you can easily extend the class and re-write the constructor like this. Just call eRequest instead of Request.
You can see that $options is private, so you know it's not being edited elsewhere and a simple ctrl-f can tell you that it's not being changed anywhere else in that class, so these are your only options for changing it.
class eRequest extends Request{
__construct(array $options = array(), $timeout=10){
$this->options = array(
self::USERAGENT => "ApaiIO [" . ApaiIO::VERSION . "]",
self::CONNECTION_TIMEOUT => $timeout,
self::TIMEOUT => $timeout,
self::FOLLOW_LOCATION => 1
);
$this->setOptions($options);
}
}
..Or if there are other parts of the library that use this, you will have to alter the actual source of the library.
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.
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/