Fetchtransport
De standaard-fetch() in Phasis loopt via PHP's ext-curl — CURLOPT_FOLLOWLOCATION=false (de redirect-loop uit de specificatie draait in Phasis-land), waarbij CURLOPT_PROGRESSFUNCTION de AbortSignal controleert. Voor de meeste toepassingen is dit prima.
Voor alle andere gevallen — proxy's, aangepaste DNS, de bestaande HTTP-client van de hostapp, netwerksandboxing en mocks in tests — is het transport verwisselbaar.
De interface
public function setFetchTransport(callable $transport): void
Je callable ontvangt:
function (array $request, ?\Phasis\Value\JsObject $signal): array
$request is een associatieve array:
| Sleutel | Type | Opmerkingen |
|---|---|---|
method |
string |
Al omgezet naar hoofdletters (GET, POST, …) |
url |
string |
Opgeloste absolute URL na de parser voor URL en de redirectketen |
headers |
list<list<string>> |
Paren van [name, value]; hoofdlettergebruik volgens de normalisatie van de specificatie; waarden met meerdere waarden vormen aparte paren (zoals Set-Cookie) |
body |
string |
Ruwe bytes. Lege tekenreeks voor methoden zonder body. |
redirect |
string |
"follow" / "error" / "manual" (je transport mag deze NIET volgen — de redirect-loop draait in Phasis) |
timeout |
int |
Budget voor wandkloktijd in milliseconden. 0 = geen limiet. |
credentials |
string |
"omit" / "same-origin" / "include" — alleen ter informatie; cookies worden afzonderlijk gekoppeld via setCookieJar(). |
$signal is null als er geen signaal aan het verzoek is meegegeven; anders is het een JsObject met aborted en reason. Controleer dit bij lange overdrachten.
Retourstructuur:
return [
'status' => 200, // int
'statusText' => 'OK', // string
'headers' => [['content-type', 'text/plain'], ['x-custom', '1']],
'body' => '...response bytes...', // string
];
Als het transport een fout gooit, vangt Phasis deze op en vertaalt deze:
- Een
Phasis\BuiltIn\Fetch\TransportExceptionmet$e->kind === "aborted"→fetchwordt afgewezen metAbortError DOMException. kind === "timeout"→TypeErrormet het bericht.kind === "network-error"(standaard) →TypeError.- Al het overige →
TypeErrormet het foutbericht.
Voorbeelden
Guzzle
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
$client = new Client(['allow_redirects' => false]);
$engine->setFetchTransport(function (array $req, $signal) use ($client) {
try {
$r = $client->request($req['method'], $req['url'], [
'headers' => collect($req['headers'])->groupBy(0)->map->pluck(1)->all(),
'body' => $req['body'],
'timeout' => max(1, $req['timeout'] / 1000.0),
'http_errors' => false,
]);
} catch (GuzzleException $e) {
throw new \Phasis\BuiltIn\Fetch\TransportException($e->getMessage(), 'network-error');
}
$headers = [];
foreach ($r->getHeaders() as $name => $values) {
foreach ($values as $v) {
$headers[] = [$name, $v];
}
}
return [
'status' => $r->getStatusCode(),
'statusText' => $r->getReasonPhrase(),
'headers' => $headers,
'body' => (string) $r->getBody(),
];
});
Symfony HttpClient
use Symfony\Component\HttpClient\HttpClient;
$client = HttpClient::create(['max_redirects' => 0]);
$engine->setFetchTransport(function (array $req, $signal) use ($client) {
$headers = [];
foreach ($req['headers'] as [$name, $value]) {
$headers[$name][] = $value;
}
$r = $client->request($req['method'], $req['url'], [
'headers' => $headers,
'body' => $req['body'],
'timeout' => $req['timeout'] / 1000.0,
'max_redirects'=> 0,
]);
return [
'status' => $r->getStatusCode(),
'statusText' => '',
'headers' => collect($r->getHeaders(false))
->flatMap(fn ($values, $name) => array_map(fn ($v) => [$name, $v], $values))
->values()->all(),
'body' => $r->getContent(false),
];
});
Alles weigeren
$engine->setFetchTransport(function () {
throw new \Phasis\BuiltIn\Fetch\TransportException(
'network access disabled in this sandbox',
'network-error',
);
});
Zo wordt elke aanroep van fetch() afgewezen met TypeError. Handig om door gebruikers aangeleverde JS in een sandbox uit te voeren.
Mock voor tests
$canned = [
'https://api.example.com/users' => ['status' => 200, 'body' => '{"users":[]}'],
'https://api.example.com/404' => ['status' => 404, 'body' => 'not found'],
];
$engine->setFetchTransport(function (array $req) use ($canned) {
$r = $canned[$req['url']] ?? ['status' => 500, 'body' => 'no mock'];
return [
'status' => $r['status'],
'statusText' => $r['status'] === 200 ? 'OK' : 'ERR',
'headers' => [['content-type', 'application/json']],
'body' => $r['body'],
];
});
De beleidsfunctie
Het transport voert het verzoek uit; de beleidsfunctie bepaalt of het überhaupt wordt uitgevoerd. Gebruik deze voor handhaving van alleen HTTPS, het toevoegen van headers, het herschrijven van URL, allowlists of het koppelen van inloggegevens per tenant.
public function setFetchPolicy(callable $hook): void
Handtekening van de functie: function (JsObject $request): ?JsObject. Retourneer null om het verzoek ongewijzigd toe te staan. Retourneer een Request om het te herschrijven. Een gegooide fout zorgt ervoor dat de fetch met die fout wordt afgewezen.
$allowedHosts = ['api.example.com', 'cdn.example.com'];
$engine->setFetchPolicy(function ($req) use ($allowedHosts) {
$url = parse_url($req->get('url')->value);
if (!in_array($url['host'] ?? '', $allowedHosts, true)) {
throw new \RuntimeException("host not allowed: " . ($url['host'] ?? ''));
}
// Inject internal-only auth header
$req->get('headers')->call('set', 'X-Internal-Token', $secret);
return null;
});
De beleidsfunctie wordt uitgevoerd vóór het transport — zelfs een transport met fouten of zonder vertrouwen kan deze dus niet omzeilen.
Overzicht van standaardgedrag
| Onderdeel | Standaard |
|---|---|
| Transport | CurlTransport (PHP ext-curl) |
| Redirects | Volgt er maximaal 20 volgens de specificatie. redirect: "manual" retourneert de redirect-response met type: "opaqueredirect". |
| Time-out | 30 s wandkloktijd, 10 s voor verbinding maken |
| TLS-verificatie | CURLOPT_SSL_VERIFYPEER = true (kan niet worden uitgeschakeld — vervang het transport om dit aan te passen) |
| User-Agent | Phasis/0.1 wordt alleen toegevoegd als deze nog niet is ingesteld |
| Cookies | Worden niet opgeslagen. Gebruik setCookieJar() om ze in te schakelen. |
| Beleidsfunctie | Geen. Alle verzoeken worden toegestaan, tenzij ze expliciet worden geweigerd. |
| AbortSignal | Wordt tijdens de overdracht gecontroleerd via de voortgangsfunctie van curl. |
Zie ook
- API-referentie — Fetch-hooks — beknopt overzicht van de handtekeningen.
- Web-API's — alle mogelijkheden van Fetch Pack.
- Cookiepot — cookies tussen fetches bewaren met een PHP- of JS-cookiepotobject.