I have a block of code that's almost as barebones as it gets, but for some reason, it just refuses to run:
<?php
require_once "unirest/src/Unirest.php";
$photo_url = "http://api.animetrics.com/img/test/sc.jpg";
// These code snippets use an open-source library.
$response = Unirest::post("<--URL-->",
array(
"X-Mashape-Key" => "<--API Key-->",
"Content-Type" => "application/x-www-form-urlencoded",
"Accept" => "application/json"
),
array(
"selector" => "FACE, EYES, FULL",
"url" => "http://api.animetrics.com/img/test/sc.jpg"
)
);
echo $response;
?>
This code block was taken directly from the mashape website, and I simply downloaded the Unirest files. I'm also sure that my path is correct.
I investigated a bit and tried adding a static class function into the Unirest file to print something out, and unsurprisingly, it didn't work.
Here's the code chunk I added:
<?php
namespace Unirest;
echo "in file";
$file = new File();
$file->printa("abc");
class File
{
public static function printa($a) {
echo $a;
}
....
Within the same file, $file->printa("abc"); worked perfectly, but when called from a different file, File::printa("abc"); or Unirest::printa("abc"); or File\Unirest::printa("abc"); just refuse to run.
I'm not sure but am I misunderstanding something about namespaces? I would have thought that Unirest::printa("abc"); is the correct way to access a static class function?
I'd appreciate any advice regarding this, thanks.
Since the release of Unirest 2.0, the method & class signature has changed. unfortunately the Mashape sample snippets are yet to be updated.
Instead of calling Unirest::post you should be calling Unirest\Request::post, please refer to the unirest documentation for more details.
We'll be updating the Mashape samples soon to reflect this change.
I'm the author of unirest-php and I work at Mashape.
Related
Update
This seems to relate in some way to the reading of the stream when outputting. The function used by Slim to output the body looks like this, where $body implements StreamInterface and $this->responseChunkSize is 4096:
$amountToRead = $body->getSize();
while ($amountToRead > 0 && !$body->eof()) {
$length = min($this->responseChunkSize, $amountToRead);
$data = $body->read($length);
echo $data;
$amountToRead -= strlen($data);
if (connection_status() !== CONNECTION_NORMAL) {
break;
}
}
It appears the $body->eof() call (which is just a wrapper for PHP's feof() function) is returning true even though the full file has not been read. Not sure why that would be though. I also verified that this does not occur if I just do an fopen() on the file and create a Stream from it, then run the same code. It only happens when the stream is the product of the external REST API call via Guzzle.
Original Post
I have a service built using Slim (v4.4) that calls an external REST API using Guzzle (v6.5.3) that returns a file. This is running in Windows, web server is IIS/FastCGI (I know, unusual). PHP version is 7.3.10. The call from Slim to the external REST API retrieves the file just fine, but when my app calls the service, some files get corrupted, seems some data gets lost based on what I see in the file size. The call from the service to the external REST API is fairly simple:
$file_response = $guzzleClient->request('GET', "{$base_url}/docs/{$file_id}", [
'headers' => [
'Authorization' => "token {$token}"
]
]);
The above call works fine and returns the file correctly, I can either display it to screen or use the 'sink' option in Guzzle to save to a file, it works fine. But when I try to call the service that wraps that call, it fails. I tried a couple things. Firstly, I was just returning the response as is since it conforms to the interface required anyway. My Slim route looks like this:
$app->group('/files', function (Group $group) {
$group->get('/{file_id}', GetFileAction::class);
});
The GetFileAction class has a method like this:
public function __invoke(Request $request, Response $response, $args): Response {
...Guzzle request returning $file_response here...
return $file_response;
}
My app is also using Guzzle to call the service, the call looks like this:
$guzzleClient->request(
'GET',
"{$base_url}/files/{$file_id}",
[
'auth' => [$username, $password],
'sink' => $file_path
]
);
I wondered if returning the Guzzle response in Slim might be causing some unexpected result, so I tried returning this in the service instead:
return $response->withBody(new \Slim\Psr7\Stream($file_response->getBody()->detach()));
Same result. Obviously if somebody who has run into this exact same problem can help out it would be great, but if not some pointers on how I could try to debug the handling of the streams would likely be helpful.
I've confirmed this is linked to a weird issue with the feof() function returning true even though it hasn't read the full file. The solution I came up with involved creating a different Response Emitter than the default Slim 4 one (mostly the same) and overwrite the emitBody function so it does not rely on feof(). I did so like this:
$length = min($this->responseChunkSizeCopy, $amountToRead);
while ($amountToRead > 0 && ($data = $body->read($length)) !== false) {
echo $data;
$amountToRead -= $length;
$length = min($this->responseChunkSizeCopy, $amountToRead);
if (connection_status() !== CONNECTION_NORMAL) {
break;
}
}
So far this has worked well based on my testing. I have no idea why feof() is not working as expected and didn't really find anything that seemed to specifically address it. Maybe it's a Windows specific thing, and since PHP is less common on Windows it's not a common occurrence. But leaving this solution here in case it can help someone.
I'm trying to achieve a similar goal—using Slim to proxy and forward incoming requests to another service via a Guzzle client—and encountered a similar problem when returning the Guzzle response.
In my case the problem was that the other service was incorrectly returning a Transfer-Encoding: chunked header in the response.
Your mileage may vary, but the solution was to replace this with a correct Content-Length header in the returned response:
return $response
->withoutHeader('Transfer-Encoding')
->withHeader('Content-Length', $response->getBody()->getSize());
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.
I am trying to install the CodeIgniter RESTClient and RESTServer libraries for my solution. (philsturgeon-codeigniter-restclient and chriskacerguis-codeigniter-restserver ).
I managed to get the rest server up and running, but I am encountering issues with the rest client.
These are the steps I did to come where I am now:
Copy the Rest.php file (downloaded from GitHub) and put it in the libraries folder
Download the Curl library and put it in libraries
Modified the code in Rest.php to uncomment $this->_ci->load->library('curl'); (if I hover over the usages of the curl library in this file I get the following message):
Field 'curl' not found in CI_Controller
I create a new controller called "Restclient" to test my API. In this controller I created the following method:
function rest_client_example($id)
{
$this->load->library('rest', array(
'server' => 'localhost/codeigniter/api/users/'
));
$user = $this->rest->get('volunteer', array('id' => $id), 'json');
var_dump($user);
}
Browsing to http://localhost/codeigniter/api/restclient/rest_client_example/25 then gives me
D:\wamp\www\codeigniter\application\controllers\api\Restclient.php:36:null
When executing the following code instead of the above, I do get a correct result:
$this->load->library('curl');
$t = $this->curl->simple_get('api/users/volunteer', array('id'=>$id));
var_dump($t);
So I do know that the curl is working.
My guess is that I am doing something wrong with the loading of the curl library?
I know your question is specific to the Libraries mentioned here. Have you tried anything else? I've had really good success with guzzle http
https://github.com/guzzle/guzzle
I'm attempting to implement https://github.com/PHP-FFMpeg/PHP-FFMpeg
I copied the src/FFMpeg folder to my includes folder and made sure my autoloader knows where to find everything.
as a test I made a script that simply does:
$ffmpeg = FFMpeg\FFMpeg::create();
$video = $ffmpeg->open('video.mpg');
I get:
Fatal error: Class 'Doctrine\Common\Cache\ArrayCache' not found in /var/www/php/include/FFMpeg/FFProbe.php on line 203
My question is: does PHP-FFMPeg require Doctrine, because that is not stated in the documentation. What version do I need? Are there other prerequisites?
I could create a new question for this, but I'm not sure if I should. I now have PHP-ffmpeg implemented. I'm using Laravel, however that should be irrelevant for this question. I'm trying to enable progress monitoring. It works, however I need to pass in an ID so I can update the correct key in memcache.
$id = 12345;
$format->on('progress', function ($audio, $format, $percentage) {
//this works perfect, but doesn't tell me which item is being updated
Cache::put("progress", $percentage, .25);
//this does not work as I am unable to pass in $id, if I add it as the 4th argument above it will display the number of threads or something
//Cache::put("{$id}_progress", $percentage, .25);
});
I need clarification on the "on" method. I looked through https://ffmpeg-php.readthedocs.org/en/latest/_static/API/ and was not able to figure out how this method works. Any help would be appreciated.
You should follow the recommended instructions in the README.
Composer is the easiest way to install PHP-FFMpeg dependencies
The "on" method called on the format is an implementation of EventEmitter.
As you can see here : https://ffmpeg-php.readthedocs.org/en/latest/_static/API/FFMpeg/Format/ProgressableInterface.html it extends the EventEmitterInterface of https://github.com/igorw/evenement.
If you're really interested about how it works under the hood, have a look at here :
The progress listener is created here : https://github.com/PHP-FFMpeg/PHP-FFMpeg/blob/master/src/FFMpeg/Format/Audio/DefaultAudio.php#L96 and added at execution here https://github.com/PHP-FFMpeg/PHP-FFMpeg/blob/master/src/FFMpeg/Media/Video.php#L151
This is actually possible because FFMpegDriver extends the Driver provided by https://github.com/alchemy-fr/BinaryDriver
Hope this helps :)
So I have the Paypal REST API installed ( https://github.com/paypal/rest-api-sdk-php ) with Composer in my_drupal_module/lib/Drupal/ and now I want to use the namespaces in a function in my module. I understood that I need something like xautoload ( https://drupal.org/project/xautoload ) to do that so I tried something like:
$payer = new \Drupal\vendor\PayPal\Api\Payer;
with and without the first slash, and with and without parentheses at the end but it didn't work. I added:
require DIR . '/lib/Drupal/vendor/autoload.php';
but still nothing so I commented it. Meanwhile I found this: https://drupal.org/node/1976206 that explains this issue but it is unclear to me what exactly to write in hook_xautoload() or direct registration for the setup that I have. Can someone please help?
Nevermind. I solved it. Thanks to proloy03 who gave me the idea: https://drupal.org/node/2096621
You don't need xautoload to load the classes and namespace just implement hook_init to require it like so:
function my_module_init() {
require __DIR__ . '/vendor/autoload.php';
}
and then in your function write:
$payer = new PayPal\Api\Payer();
and it all works.
With xautload you can do something like this...
In mymodule.module
function mymodule_libraries_info() {
'paypal' => array(
'name' => 'PayPal SDK',
'xautoload' => function($adapter) {
$adapter->composerJson('composer.json');
}
}
Declaring this allow you to use the namespace. Like
use PayPal\Api\Amount;
Check xautoload.api.php
Your assumption that you need XAutoload to use the Paypal API installed by Composer is wrong.
The only thing you need to make any code installed by Composer available is to include the Composer autoloader in your code execution path when you need any of the provided classes. The easiest form of this is to simply require the Composer autoloader in the very first index.php (or whatever Drupal uses) file that answers every request (possibly instantiating more Drupal classes, modules and stuff).
Once the code execution comes to your own code, the autoloading allows your code to include all the classes you included with Composer.
How to use the code? Don't invent your own namespace for the existing code, it will not work. "\Drupal\vendor\PayPal\Api\Payer" sounds pretty wrong to me, I suspect the correct namespace is "\PayPal\Api\Payer". And yes, that file does indeed exist. So you have to use that.
If it still does not work, complaining the class could not be loaded, then the Composer autoloader file is not included in your scripts code path.