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
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");
});
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...
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.
Would be useful to get the actual StdClass object that the Json class is representing.
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
We have compatibility with PSR-7 and PHP-HTTP, and can probably implement PSR-18 without any fuss.
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.
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.
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.
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).
Typical usage will be to send key-value-pairs. If an array is provided, use http_build_query automatically.
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.
Currently not implemented but should probably be a feature of PHP.Gt/Curl or PHP.Gt/Http.
Should the following init parameters be respected on the server-side?
See: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
The AbortSignal callback should be passed in to cancel any ongoing requests from code external to the async loop.
PHP.Gt/Async and PHP.Gt/Promise will reduce the dependency complexity.
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.
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.
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.
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.
Interfaces:
Another class should handle the streaming, it should be provided the stream and the response object.
If Request integrity
property is set, use the correct crypto algorithm to verify.
These functions should be available procedurally rather than requiring the construction of a new Http object each time.
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.
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?
Don't write you own loop, use https://packagist.org/packages/react/event-loop as it is the most active on packagist.
<?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();
Defining an interface for containing Curl methods promotes tidy code, but most importantly allows testing of remote calls through DI.
Dependabot needs to be tamed, as per PhpGt/WebEngine#568
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?
https://github.com/github/fetch implementation (as a guide).
https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch documentation (W3C spec).
https://github.com/guzzle/promises
There is no PSR for promises yet, Guzzle seems the most stable and reliable.
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.
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.
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.