Přejít k obsahu

Fetch transport

Výchozí fetch() v Phasis používá PHP a jeho ext-curl — CURLOPT_FOLLOWLOCATION=false (přesměrovací smyčka podle specifikace běží v prostředí Phasis) a CURLOPT_PROGRESSFUNCTION kontroluje AbortSignal. Pro většinu způsobů vložení to stačí.

Pro ostatní případy — proxy, vlastní DNS, již používaný klient HTTP hostitelské aplikace, síťový sandbox nebo mockování v testech — lze transport vyměnit.

Rozhraní

PHP
public function setFetchTransport(callable $transport): void

Vaše volatelná funkce obdrží:

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

$request je asociativní pole:

Klíč Typ Poznámky
method string Již převedeno na velká písmena (GET, POST, …)
url string Vyřešené absolutní URL po zpracování parserem URL a řetězci přesměrování
headers list<list<string>> Dvojice [name, value]; velikost písmen odpovídá normalizaci specifikací; položky s více hodnotami tvoří samostatné dvojice (např. Set-Cookie)
body string Nezpracované bajty. U metod bez těla jde o prázdný řetězec.
redirect string "follow" / "error" / "manual" (transport je NESMÍ následovat — přesměrovací smyčka běží v Phasis)
timeout int Rozpočet času v milisekundách. 0 = bez limitu.
credentials string "omit" / "same-origin" / "include" — pouze informativní; cookies se předávají samostatně přes setCookieJar().

$signal je null, pokud požadavek nedostal signál; jinak jde o JsObject poskytující aborted a reason. Při dlouhých přenosech ji kontrolujte.

Návratová hodnota:

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

Pokud transport vyvolá výjimku, Phasis ji zachytí a převede:

  • Phasis\BuiltIn\Fetch\TransportException s $e->kind === "aborted" → fetch je zamítnuto s AbortError DOMException.
  • kind === "timeout" → TypeError obsahující zprávu.
  • kind === "network-error" (výchozí) → TypeError.
  • Cokoli jiného → TypeError se zprávou výjimky.

Příklady

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

Vše blokovat

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

Tím se každý požadavek fetch() zamítne s chybou TypeError. Hodí se k sandboxování JS dodaného uživatelem.

Mock pro testy

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 zásad

Transport provádí požadavek; hook zásad rozhoduje, zda se vůbec provede. Použijte ho k vynucení pravidel pouze pro HTTPS, vkládání hlaviček, přepisování URL, správě seznamů povolených položek nebo přiřazování přihlašovacích údajů jednotlivým tenantům.

PHP
public function setFetchPolicy(callable $hook): void

Signatura hooku: function (JsObject $request): ?JsObject. Návratová hodnota null požadavek povolí beze změny. Vrácení objektu Request požadavek přepíše. Vyvolaná výjimka způsobí zamítnutí fetch s touto chybou.

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

Hook zásad se spouští před transportem, takže ho nemůže obejít ani chybný či nedůvěryhodný transport.

Shrnutí výchozího chování

Oblast Výchozí nastavení
Transport CurlTransport (PHP ext-curl)
Přesměrování Podle specifikace následuje až 20 přesměrování. redirect: "manual" vrací odpověď s přesměrováním a type: "opaqueredirect".
Časový limit 30 s podle skutečného času, 10 s pro připojení
Ověření TLS CURLOPT_SSL_VERIFYPEER = true (nelze vypnout — změňte transport)
User-Agent Phasis/0.1 se vloží pouze tehdy, pokud není nastaven
Cookies Neukládají se. Zapnete je pomocí setCookieJar().
Hook zásad Žádný. Všechny požadavky jsou povoleny, pokud nejsou výslovně zakázány.
AbortSignal Respektuje se během přenosu kontrolou progress callbacku curl.

Viz také