Hoppa till innehåll

Fetch-transport

Standardtransporten fetch() i Phasis använder PHPs ext-curl — CURLOPT_FOLLOWLOCATION=false (specifikationens omdirigeringsloop körs i Phasis-miljön) medan CURLOPT_PROGRESSFUNCTION kontrollerar AbortSignal. Det fungerar bra för de flesta inbäddningar.

För allt annat — proxyservrar, anpassad DNS, värdappens befintliga HTTP-klient, nätverkssandlådebegränsningar eller mockning i tester — kan transporten bytas ut.

Gränssnittet

PHP
public function setFetchTransport(callable $transport): void

Din anropsbara funktion får:

PHP
function (array $request, ?\Phasis\Value\JsObject $signal): array

$request är en associativ array:

Nyckel Typ Anmärkningar
method string Redan konverterad till versaler (GET, POST, …)
url string Upplöst absolut URL efter parsning med URL och omdirigeringskedja
headers list<list<string>> Par av [name, value]; versaler och gemener normaliseras enligt specifikationen; värden med flera poster blir separata par (Set-Cookie osv.)
body string Råa byte. Tom sträng för metoder utan innehåll.
redirect string "follow" / "error" / "manual" (din transport ska INTE följa dem — omdirigeringsloopen körs i Phasis)
timeout int Tidsbudget enligt väggklockan i millisekunder. 0 = ingen gräns.
credentials string "omit" / "same-origin" / "include" — endast information; cookies hanteras separat via setCookieJar().

$signal är null om förfrågan saknar signal, annars ett JsObject med aborted och reason. Kontrollera den under långa överföringar.

Returvärde:

PHP
return [
    'status'     => 200,                          // int
    'statusText' => 'OK',                         // string
    'headers'    => [['content-type', 'text/plain'], ['x-custom', '1']],
    'body'       => '...response bytes...',       // string
];

Om transporten kastar ett undantag fångar Phasis det och omvandlar det:

  • En Phasis\BuiltIn\Fetch\TransportException med $e->kind === "aborted" → fetch avvisas med AbortError DOMException.
  • kind === "timeout" → TypeError med meddelandet.
  • kind === "network-error" (standard) → TypeError.
  • Allt annat → TypeError med undantagets meddelande.

Genomarbetade exempel

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),
    ];
});

Neka allt

PHP
$engine->setFetchTransport(function () {
    throw new \Phasis\BuiltIn\Fetch\TransportException(
        'network access disabled in this sandbox',
        'network-error',
    );
});

Det här gör att varje anrop till fetch() avvisas med TypeError. Användbart för att isolera användartillhandahållen JS i en sandlåda.

Mock för tester

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'],
    ];
});

Hook för principer

Transporten hanterar förfrågan; hooken för principer avgör om den alls ska utföras. Använd den för att endast använda HTTPS, lägga till headers, skriva om URL, använda tillåtelselistor eller koppla in autentiseringsuppgifter per klient.

PHP
public function setFetchPolicy(callable $hook): void

Hookens signatur: function (JsObject $request): ?JsObject. Om du returnerar null tillåts förfrågan som den är. Om du returnerar en Request skrivs den om. Om du kastar ett undantag avvisas hämtningen med det kastade felet.

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;
});

Hooken för principer körs före transporten — så inte ens en felaktig eller opålitlig transport kan kringgå den.

Sammanfattning av standardbeteende

Aspekt Standard
Transport CurlTransport (PHP ext-curl)
Omdirigeringar Följ upp till 20 enligt specifikationen. redirect: "manual" returnerar omdirigeringens Response med type: "opaqueredirect".
Tidsgräns 30 s enligt väggklockan, 10 s för anslutning
Verifiering av TLS CURLOPT_SSL_VERIFYPEER = true (kan inte väljas bort — åsidosätt genom att byta transport)
User-Agent Phasis/0.1 läggs till endast om den inte redan är inställd
Cookies Inga sparas. Använd setCookieJar() för att aktivera.
Hook för principer Ingen. Alla förfrågningar tillåts om de inte uttryckligen nekas.
AbortSignal Hanteras under överföringen genom en kontroll i curl-förloppsåterkopplingen.

Se även