I have a situation where I have a loop, that is going to read chunk of data from a file, send those chunk to a rest api, and continue until the EOF, but I want this to be async inside the loop, so, I don't have to wait until the API respond to read the next chunk.
I have been looking at Amphp and ReactPHP for I can't find a solution to this, or maybe I don't understand how are those libraries supposed to be used.
here is a pseudo of what I am doing.
<?php
while($file.read()){
$chunk = getNextChunk();
sendChunkAsync($chunk);
}
function getNextChunk(){
echo "reading next chunk";
// read next chunk of data
}
sample with amphp
function sendChunkAsync($chunk){
Loop::run(function () {
$uri = "https://testapi.com/api";
$client = new DefaultClient;
try {
$promises = $client->request($uri);
$responses = yield $promises;
echo "chunk processed";
} catch (Amp\Artax\HttpException $error) {
// log error
// $error->getMessage() . PHP_EOL;
}
});
}
In this case I would expect (if reading chunk is faster than getting response from api) something like this, don't take this literary, I am trying to illustrate it for you.
Reading next chunk
Reading next chunk
chunk processed
Reading next chunk
chunk processed
chunk processed
I am going to use React as I know the library better but they work in similar ways.
EDIT: updated, see comments
This will read in a file and every time it recieves a chunk of data, it will create an api call and send the data off
<?php
require_once __DIR__ . '/vendor/autoload.php';
function async_send($config, $file, callable $proccessor)
{
$config['ssl'] = true === $config['ssl'] ? 's' : '';
$client = new \GuzzleHttp\Client([
'base_uri' => 'http' . $config['ssl'] . '://' . $config['domain'] . '/rest/all/V1/',
'verify' => false,
'http_errors' => false
]);
$loop = \React\EventLoop\Factory::create();
$filesystem = \React\Filesystem\Filesystem::create($loop);
$filesystem->getContents($file)->then(function($contents) use ($config, $proccessor, $client) {
$contents = $proccessor($contents);
$client->post($config['uri'], ['body' => $contents]);
});
}
$config = [
'domain' => 'example.com',
'ssl' => true
];
//somewhere later
$configp['uri'] = 'products';
async_send($configp, __DIR__ . 'my.csv', function ($contents) {
return json_encode($contents);
});
In case someone else is trying to solve a similar problem
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use React\HttpClient\Client as ReactClient;
function async_send($loop, $filePath, callable $proccessor)
{
echo "starting";
echo "\n\r";
try {
$filesystem = \React\Filesystem\Filesystem::create($loop);
$file = $filesystem->file($filePath);
$file->open('r')
->then(function ($stream) use ($loop, $proccessor){
$stream->on('data', function ($chunk) use ($loop, $proccessor) {
$proccessor($chunk);
});
});
} catch (\Exception $e) {
echo "failed";
echo "\n\r";
}
echo "ending reading";
echo "\n\r";
}
function callApiReal($loop, $fileChunk = null)
{
echo "ready to call api". PHP_EOL;
$uri = "https://testapi.com/";
try {
$client = new ReactClient($loop);
} catch (\Exception $e) {
echo "Error";
}
echo "ready to call api";
$request = $client->request('POST', $uri, $fileChunk);
$request->on('response', function ($response) use ($uri) {
$response->on('data', function ($data_chunk) {
echo 'data chunk from api received';
echo "\n\r";
});
// subscribe to listen to the end of the response
$response->on('end', function () use ($uri) {
echo "operation has completed";
echo "\n\r";
});
});
$request->on('error', function ($error) {
// something went bad in the request
echo "Damm!";
echo "\n\r";
});
$request->end();
}
// main loop
$loop = React\EventLoop\Factory::create();
//somewhere later
async_send($loop, __DIR__ . '/my.csv', function ($chunk) use ($loop) {
echo "calling api";
callApiReal($loop, $chunk);
echo "\n\r";
});
$loop->run();
Related
I need to download multiple files with Guzzle using concurrent requests so i choose to use Pool.
Basically, I wrote a function that receives a list of URL that are the remote files i need to download and a path which is the directory where i want to save the files.
The function should save in the provided directory each file I'm sending the request to but instead the directory reamins empty and no file as been wrote inside it.
What am I doing wrong?
Here is my function with some echos for debugging purpose:
function async_multiple_files_download($files_url_list, $path) {
$client = new \GuzzleHttp\Client();
$requests = array();
for ($i = 0; $i < sizeof($files_url_list); $i++) {
$file_name = basename($files_url_list[$i]);
$request_destination_file_path = $path . DIRECTORY_SEPARATOR . $file_name;
$requests[$i] = new GuzzleHttp\Psr7\Request('GET', $files_url_list[$i], ['sink' => $request_destination_file_path]);
echo "Downloading " . basename($files_url_list[$i]) . "<br>from($files_url_list[$i])<br>to $request_destination_file_path" . "<br><br><br>";
}
$pool = new \GuzzleHttp\Pool($client, $requests, [
'concurrency' => 10,
'fulfilled' => function (\Psr\Http\Message\ResponseInterface $response, $index) {
echo 'success: '.$response->getStatusCode()."<br>";
},
'rejected' => function ($reason, $index) {
echo 'failed: '.$reason."<br>";
},
]);
$promise = $pool->promise();
$promise->wait();
}
Hi I am attempting to echo out data returned from an api I'm working with utilizing the following code:
<?php
ini_set("include_path", '/home/matthewt/php:' . ini_get("include_path") );
// This sample uses the Apache HTTP client from HTTP Components
(http://hc.apache.org/httpcomponents-client-ga/)
require_once 'HTTP/Request2.php';
$url = $request->getUrl();
$headers = array(
// Request headers
'Ocp-Apim-Subscription-Key' => 'my_id',
);
$request->setHeader($headers);
$parameters = array(
// Request parameters
);
$url->setQueryVariables($parameters);
$request->setMethod(HTTP_Request2::METHOD_GET);
// Request body
$request->setBody("{body}");
try
{
$response = $request->send();
echo $response->getBody();
}
catch (HttpException $ex)
{
echo $ex;
}
Everything up until this point works great and returns json data that basically looks like this: (I shortened the amount of fields to save space)
[{"GameID":49810,"Season":2017},{"GameID":49811,"Season":2017}]
What I need to know is how to loop through this data to print out the results for each game. I know I have to use something like this:
$arr = json_decode();
foreach($arr as $item) { //foreach element in $arr
$game_id = $item['GameID']; //etc
}
But I'm not sure what needs to be decoded...is it $response ?
Thanks
If $response->getBody() contains your json, then json_decode it, then this will be your array of objects which you can loop over.
So try this:
// snip
try
{
$response = $request->send();
$arr = json_decode($response->getBody());
foreach((array) $arr as $item) { //foreach element in $arr
echo $item->GameID; //etc
echo $item->Season; //etc
}
}
catch (HttpException $ex)
{
echo $ex;
}
I've created a REST API base on this tutorial - note that I am a newbie in php and REST...
Now, I am stuck when calling a POST request. The main return function is as follows:
// Requests from the same server don't have a HTTP_ORIGIN header
if (!array_key_exists('HTTP_ORIGIN', $_SERVER)) {
$_SERVER['HTTP_ORIGIN'] = $_SERVER['SERVER_NAME'];
}
try {
$API = new MyAPI($_REQUEST['request'], $_SERVER['HTTP_ORIGIN']);
$res = $API->processAPI();
echo $res; // contains my json as expected
return $res; // always empty string
} catch (Exception $e) {
echo "Exception: " . json_encode(Array('error' => $e->getMessage()));
}
EDIT
I've just tried something even simpler in the API caller method, namely following:
try {
$res = json_encode(Array('test' => "my message"));
// comment out one or the other to check output...
//echo $res;
return $res;
} catch (Exception $e) {
echo "Exception: " . json_encode(Array('error' => $e->getMessage()));
}
Result with echo is (the way I get responese is below... exact response is between # characters):
#{"test":"my message"}#
Result with return is
##
EDIT 2
Here is how I call the API from C#:
using (HttpClient client = new HttpClient()) {
JObject jo = new JObject();
jo.Add("usr", "username");
jo.Add("pwd", "password");
Uri url = new Uri(string.Format("{0}/{1}", RestUrl, "login"));
StringContent content = new StringContent(jo.ToString(), Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(url, content);
bool isOk = response.StatusCode == System.Net.HttpStatusCode.OK;
// this is where I get the result from...
var res = response.Content.ReadAsStringAsync().Result;
}
I really don't understand this - could someone please explain in non-php expert terms??
https://coinbase.com/api/v1/transactions/send_money?api_key=xxx
I have that URL but after the api_key paramter what comes next (I blocked out my API Key so people can't access my BTC)?
Can someone give me an example of how to properly use coinbase's send_money API?
I don't have a PHP environment handy to test this with but I think it would go like this:
Get their PHP library: https://github.com/coinbase/coinbase-php
<?php
require_once(dirname(__FILE__) . '/../lib/Coinbase.php');
// Create an application at https://coinbase.com/oauth/applications and set these values accordingly
$_CLIENT_ID = "83a481f96bf28ea4bed1ee8bdc49ba4265609efa40d40477c2a57e913c479065";
$_CLIENT_SECRET = "a8dda20b94d09e84e8fefa5e7560133d9c5af9da93ec1d3e79ad0843d2920bbb";
// Note: your redirect URL should use HTTPS.
$_REDIRECT_URL = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
$coinbaseOauth = new Coinbase_OAuth($_CLIENT_ID, $_CLIENT_SECRET, $_REDIRECT_URL);
if(isset($_GET['code'])) {
// Request tokens
$tokens = $coinbaseOauth->getTokens($_GET['code']);
// The user is now authenticated! Access and refresh tokens are in $tokens
// Store these tokens safely, and use them to make Coinbase API requests in the future.
// For example:
$coinbase = new Coinbase($coinbaseOauth, $tokens);
try {
echo 'Balance: ' . $coinbase->sendMoney($to, $amount, $notes=null, $userFee=null, $amountCurrency=null) . '<br>';
echo $coinbase->createButton("Alpaca socks", "10.00", "CAD")->embedHtml;
} catch (Coinbase_TokensExpiredException $e) {
$newTokens = $coinbaseOauth->refreshTokens($tokens);
// Store $newTokens and retry request
}
} else {
// Redirect to Coinbase authorization page
// The provided parameters specify the access your application will have to the
// user's account; for a full list, see https://coinbase.com/docs/api/overview
// You can pass as many scopes as you would like
echo "Connect with Coinbase";
}
Here is the send money code
public function sendMoney($to, $amount, $notes=null, $userFee=null, $amountCurrency=null)
{
$params = array( "transaction[to]" => $to );
if($amountCurrency !== null) {
$params["transaction[amount_string]"] = $amount;
$params["transaction[amount_currency_iso]"] = $amountCurrency;
} else {
$params["transaction[amount]"] = $amount;
}
if($notes !== null) {
$params["transaction[notes]"] = $notes;
}
if($userFee !== null) {
$params["transaction[user_fee]"] = $userFee;
}
return $this->post("transactions/send_money", $params);
}
I have a function to translate the current text string using the Free Bing translator API. I just want to make sure if anything fails or something happens with the Application ID or I go over on requests, I don't want a big error to show.
The code I have right now is:
$translate_feed = file_get_contents('http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=' . BING_APPID . '&text=' . urlencode($text) . '&from=en&to=' . $to_lan . '');
$translate = simplexml_load_string($translate_feed);
return $translate[0];
What I want to happen is if anything fails, so if I add in another character to the URL to make it invalid then I want it to just return $text so at least something shows.
Thanks!
Have you tried failing it on purpose to see what happens?
If it's an exception, just catch it and handle it...
try{
//enter code to catch
}catch(Exception $ex){
//Process the exception
}
If there is an error outputted by the function, just # to hide the error and handle the incorrect output of $translate_feed or $translate manually.
You can try failing it on purpose by simply passing an invalid URI to file_get_contents and then forcefully feed non XML or invalid XML to simplexml_load_string to see what happens.
$translate_feed = file_get_contents('http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=' . BING_APPID . '&text=' . urlencode($text) . '&from=en&to=' . $to_lan . '');
if ( $translate_feed === false )
{
echo "failed";
}
You can do like this
if(#file_get_contents("yourFilePath.txt")){
echo "success";
}
/*
It's a modified file_get_contents()
get_contents(filename, use_include_path, context, offset, maxlen)
*/
function get_contents($url, $u = false, $c = null, $o = null) {
$headers = get_headers($url);
$status = substr($headers[0], 9, 3);
if ($status == '200') {
return file_get_contents($url, $u, $c, $o);
}
return false;
}
echo get_contents('https://example.com/');
there is $http_response_header variable is being created in local scope that we can use it to check headers returned from server. Here is my implementation:
public function getData($url)
{
try {
$response = #file_get_contents($url);
if (isset($http_response_header)) {
if (!in_array('HTTP/1.1 200 OK', $http_response_header) &&
!in_array('HTTP/1.0 200 OK', $http_response_header)) {
throw new \Exception('Server did not return success header!');
}
}
return $response;
} catch (\Exception $ex) {
throw new TransportException($ex->getMessage(), $ex->getCode(), $ex);
}
}
if( !$translate_feed) return "Failed";
$translate_feed = file_get_contents('http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=' . BING_APPID . '&text=' . urlencode($text) . '&from=en&to=' . $to_lan . '');
$result=$text;
if ($translate_feed) {
$translate = simplexml_load_string($translate_feed);
if (is_array($translate)) $result=$translate[0];
}
return $result;