Gå til indhold

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

PHP
public function setFetchTransport(callable $transport): void

Din callable modtager:

PHP
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:

PHP
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\TransportException med $e->kind === "aborted" → fetch afviser med AbortError DOMException.
  • kind === "timeout" → TypeError med fejlmeddelelsen.
  • kind === "network-error" (standard) → TypeError.
  • Alt andet → TypeError med fejlmeddelelsen.

Gennemarbejdede eksempler

Guzzle

PHP
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

PHP
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

PHP
$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

PHP
$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.

PHP
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.

PHP
$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å