phpgt / fetch Goto Github PK
View Code? Open in Web Editor NEWAsynchronous HTTP client with promises.
Home Page: https://www.php.gt/fetch
License: MIT License
Asynchronous HTTP client with promises.
Home Page: https://www.php.gt/fetch
License: MIT License
<?php
$http = new \phpgt\fetch\Http();
$http->get("http://example.com/photograph.jpg")
->then(function($response) {
return $response->blob();
})
->then(function($blob) {
file_put_contents(OUTPUT_FILE, $blob);
});
$http->get("http://example.com/page.html")
->then(function($response) {
return $response->body();
})
->then(function($body) {
echo "Got HTML: $body";
});
// Wait for promises to resolve:
// ... using another promise ...
$http->all()->then(function() {
completed_everything();
});
// ... or wait until they are resolved before continuing ...
$http->wait();
completed_everything();
There are three arrays used in this class, which are accessed in parallel. These should be a single array of objects that cover all three dimensions.
Here's a sample program:
<?php
require(__DIR__ . "/vendor/autoload.php");
use Gt\Fetch\Http;
use Gt\Fetch\BodyResponse;
$http = new Http();
$http->fetch("http://atlas.srv.brightflair.com/slow.php?i=1&num=2000")
->then(function(BodyResponse $response) {
if($response->ok) {
echo "Response 1 OK. Getting text..." . PHP_EOL;
return $response->text();
}
})
->then(function(string $text) {
echo "Full response 1 is " . strlen($text) . " bytes!" . PHP_EOL;
});
$http->wait();
echo "The script has completed. Has the final callback been called?";
The RequestResolver
class works a treat. The BodyResponse
is resolved as soon as the headers come back from the Curl request, so when "Response 1 OK. Getting text..." is written, the body contents is still streaming in via the React loop.
The problem is that even though the BodyResponse
is resolved (see snippet below), the outer Promise never resolves.
This runs: https://github.com/PhpGt/fetch/blob/97aaaae4f4b78dae921cca262534a885b92acbab/src/BodyResponse.php#L58-L68 , but the final ->then
does not run.
These functions are available on the Response
and Request
. Can't see when they would be used to obtain the Request body content, but hey...
Interfaces:
Don't write you own loop, use https://packagist.org/packages/react/event-loop as it is the most active on packagist.
Currently not implemented but should probably be a feature of PHP.Gt/Curl or PHP.Gt/Http.
Use a stream.
JavaScript context: https://jakearchibald.com/2015/thats-so-fetch/
Following off of #88 , this thought needs its own issue...
More often than not, I reach for PhpGt/Fetch only because it's my own library, and not because it's the best tool for the job. I hardly ever actually need to make an asynchronous HTTP call, but I still work with Fetch so that I'm eating my own dogfood.
But if I'm always using it for a simple HTTP request, and I always want the HTTP body without anything else, there should be helper functions on the Http
class (and possibly exposed globally like with #88).
My idea would allow something like this:
$json = $http->fetchJson("https://api.example.com/getInfo");
echo "Hello, ", $json->getString("name");
Rather than the current approach:
$http->fetch("https://api.example.com/getInfo")
->then(function(Response $response) {
return $response->json();
})->then(function(JsonObject $json) {
echo "Hello, ", $json->getString("name");
});
The AbortSignal callback should be passed in to cancel any ongoing requests from code external to the async loop.
We have compatibility with PSR-7 and PHP-HTTP, and can probably implement PSR-18 without any fuss.
Defining an interface for containing Curl methods promotes tidy code, but most importantly allows testing of remote calls through DI.
If Request integrity
property is set, use the correct crypto algorithm to verify.
curl_multi is great, but using it as in any example found online is still kind-of synchronous.
Reading large responses will still block the program until the whole response is read, unless using a stream function (almost undocumented online).
If many requests are sent in parallel, this will likely max out the CPU and cause issues. There could be a locking mechanism used to only allow a certain number of incoming handles' bytes at any one point.
The reason this is classed as a bug is because currently, using React's Promise library breaks PSR-5 as it doesn't implement the interface.
Typical usage will be to send key-value-pairs. If an array is provided, use http_build_query automatically.
Should the following init parameters be respected on the server-side?
See: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
This is a good example of implementing a timeout, by setting a timer for X seconds that calls the abort controller.
https://developer.mozilla.org/en-US/docs/Web/API/AbortController
PHP.Gt/Async and PHP.Gt/Promise will reduce the dependency complexity.
If I fetch http://localhost:1234
and nothing is serving there, the response is created without a status code (correct) but it fails because the status code can not be null.
Requests that have the added header of Content-type: application/x-www-form-urlencoded
should force their POST body to be form-urlencoded, even if a FormData or array has been provided.
https://github.com/github/fetch implementation (as a guide).
https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch documentation (W3C spec).
These functions should be available procedurally rather than requiring the construction of a new Http object each time.
Dependabot needs to be tamed, as per PhpGt/WebEngine#568
https://github.com/guzzle/promises
There is no PSR for promises yet, Guzzle seems the most stable and reliable.
This is probably something to implement in PHP.Gt/Http. The BodyResponse::formData()
function should return an actual FormData
object, and the fetch()
function should be able to set a FormData
as its body as per #44.
Needs a toString function to return the encoded text. Sub-objects should also be Json type, allowing for toString of only certain properties.
Json object should expose getString, getInt, ... functions that are throughout php.gt projects.
Json object should be iterable over keys and values.
The init parameter "cache" specifies one of the following cache modes:
See: https://developer.mozilla.org/en-US/docs/Web/API/Request/cache
Currently, there is no cache mechanism built into the library. Maybe this should be part of PHP.Gt/Curl instead?
As per https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch , if the init config has redirect = error
, an exception should be thrown if there is a redirect on the request.
Another class should handle the streaming, it should be provided the stream and the response object.
Pass the request body as a FormData object to automatically set the correct content type.
Currently trying to decide whether the FormData object should be the responsibility of this repository, or PHP.Gt/Http instead.
Would be useful to get the actual StdClass object that the Json class is representing.
Pretty major bug here... since refactoring the Response into the new BodyResponse (so we can isolate the fetch-specific functions), the body stream always comes back empty. Needs investigating.
There's a purposefully broken JSON file in the repository that can be used to reproduce this issue.
Take the first example script at example/01-basic-fetch.php, but change the URL to the broken JSON (https://github.com/PhpGt/Fetch/blob/59a3d1c447fd44d94ea389404c3a2595d98629ce/broken.json), and the JSONDecodeException will be thrown to the main thread, rather than caught and passed to the catch
function.
Full code example to reproduce:
<?php
require(implode(DIRECTORY_SEPARATOR, ["..", "vendor", "autoload.php"]));
use Gt\Fetch\Http;
use Gt\Fetch\Response\Blob;
use Gt\Fetch\Response\BodyResponse;
use Gt\Json\JsonKvpObject;
use Gt\Json\JsonPrimitive\JsonArrayPrimitive;
$http = new Http();
$http->fetch("https://raw.githubusercontent.com/PhpGt/Fetch/master/broken.json")
->then(function(BodyResponse $response) {
if(!$response->ok) {
throw new RuntimeException("Can't retrieve Github's API on $response->uri");
}
return $response->json();
})
->then(function(JsonArrayPrimitive $json) {
echo "SUCCESS: Json promise resolved!", PHP_EOL;
echo "PHP.Gt has the following repositories: ";
$repoList = [];
/** @var JsonKvpObject $item */
foreach($json->getPrimitiveValue() as $item) {
array_push($repoList, $item->getString("name"));
}
echo wordwrap(implode(", ", $repoList)) . ".";
echo PHP_EOL, PHP_EOL;
})
->catch(function(Throwable $reason) {
// THIS FUNCTION SHOULD TRIGGER, BUT DOESN'T!
echo "ERROR: ", PHP_EOL, $reason->getMessage(), PHP_EOL, PHP_EOL;
});
$http->wait();
I've tried to isolate this issue within the Promise implementation, but I can't, so I think there must be something I'm doing wrong in the way a new promise is created within the json() function, but as far as I can see, the exception is being thrown from calling $builder->fromJsonString($resolvedValue)
, but it isn't getting caught for some reason.
In PHP, binary data is represented in strings, so the BodyResponse::blob()
resolved data is exactly the same as BodyResponse::text()
. This might be OK, but is there some functionality missing in the text
function? Maybe the output should be forced into a particular charset, for example?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.