Fetch-transport
Standardtransporten fetch() i Phasis bruger PHPs ext-curl — CURLOPT_FOLLOWLOCATION=false (specifikationens omdirigeringsløkke kører i Phasis-land), mens CURLOPT_PROGRESSFUNCTION aflæser AbortSignal. Det er fint til de fleste indlejringer.
Til alt andet — proxyer, brugerdefinerede DNS, værtsappens eksisterende HTTP-klient, netværkssandkasser og mocks i tests — kan transporten udskiftes.
Grænsefladen
public function setFetchTransport(callable $transport): void
Din callable modtager:
function (array $request, ?\Phasis\Value\JsObject $signal): array
$request er et associativt array:
| Nøgle | Type | Noter |
|---|---|---|
method |
string |
Allerede skrevet med store bogstaver (GET, POST, …) |
url |
string |
Opløst, absolut URL efter URL-parseren og omdirigeringskæden |
headers |
list<list<string>> |
Par af [name, value]; store og små bogstaver følger specifikationens normalisering; værdier med flere forekomster er separate par (Set-Cookie osv.) |
body |
string |
Rå bytes. Tom streng for metoder uden body. |
redirect |
string |
"follow" / "error" / "manual" (transporten må IKKE følge dem — omdirigeringsløkken kører i Phasis) |
timeout |
int |
Budget i millisekunder efter væguret. 0 = ingen grænse. |
credentials |
string |
"omit" / "same-origin" / "include" — til orientering; cookies håndteres separat via setCookieJar(). |
$signal er null, hvis anmodningen ikke fik noget signal; ellers er det en JsObject med aborted og reason. Aflæs den under lange overførsler.
Returformat:
return [
'status' => 200, // int
'statusText' => 'OK', // string
'headers' => [['content-type', 'text/plain'], ['x-custom', '1']],
'body' => '...response bytes...', // string
];
Hvis transporten kaster en fejl, fanger Phasis den og oversætter den:
- En
Phasis\BuiltIn\Fetch\TransportExceptionmed$e->kind === "aborted"→fetchafviser medAbortError DOMException. kind === "timeout"→TypeErrormed fejlmeddelelsen.kind === "network-error"(standard) →TypeError.- Alt andet →
TypeErrormed fejlmeddelelsen.
Gennemarbejdede eksempler
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),
];
});
Afvis alt
$engine->setFetchTransport(function () {
throw new \Phasis\BuiltIn\Fetch\TransportException(
'network access disabled in this sandbox',
'network-error',
);
});
Dette får alle fetch()-kald til at afvise med TypeError. Nyttigt til at isolere brugerleveret JS.
Mock til 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'],
];
});
Policy-hooket
Transporten udfører anmodningen; policy-hooket afgør, om den overhovedet skal udføres. Brug det til håndhævelse kun for HTTPS, til indsættelse af headers, omskrivning af URL, tilladelseslister eller tilknytning af legitimationsoplysninger pr. lejer.
public function setFetchPolicy(callable $hook): void
Hook-signatur: function (JsObject $request): ?JsObject. Returnér null for at tillade anmodningen uændret. Returnering af en Request omskriver den. Et kastet objekt får fetch-kaldet til at afvise med den kastede fejl.
$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;
});
Policy-hooket kører før transporten — så selv en fejlbehæftet eller upålidelig transport ikke kan omgå det.
Oversigt over standardadfærd
| Hensyn | Standard |
|---|---|
| Transport | CurlTransport (PHP ext-curl) |
| Omdirigeringer | Følg op til 20 ifølge specifikationen. redirect: "manual" returnerer omdirigeringens Response med type: "opaqueredirect". |
| Timeout | 30 s efter væguret, 10 s til tilslutning |
| TLS-verificering | CURLOPT_SSL_VERIFYPEER = true (kan ikke fravælges — skift transport for at tilsidesætte) |
| User-Agent | Phasis/0.1 indsættes kun, hvis den ikke er angivet |
| Cookies | Ingen gemmes. Brug setCookieJar() for at aktivere dem. |
| Policy-hook | Intet. Alle anmodninger tillades, medmindre de udtrykkeligt afvises. |
| AbortSignal | Aflæses midt i anmodningen via curl-progress-callback. |
Se også
- API-reference — Fetch-hooks — kort reference over signaturer.
- Web-API'er — hele Fetch Pack-overfladen.
- Cookie-lager — bevar cookies mellem fetch-kald med et PHP- eller JS-lagerobjekt.