Preskočiť na obsah

Transport fetch

Predvolený fetch() v Phasis používa PHP ext-curl — CURLOPT_FOLLOWLOCATION=false (špecifikačná slučka presmerovaní beží vo svete Phasis) a CURLOPT_PROGRESSFUNCTION kontroluje AbortSignal. Pre väčšinu spôsobov vloženia to stačí.

V ostatných prípadoch — proxy servery, vlastné DNS, existujúci klient HTTP hostiteľskej aplikácie, sieťové sandboxy či napodobňovanie v testoch — možno transport vymeniť.

Rozhranie

PHP
public function setFetchTransport(callable $transport): void

Váš volateľný objekt dostane:

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

$request je asociatívne pole:

Kľúč Typ Poznámky
method string Už prevedené na veľké písmená (GET, POST, …)
url string Vyriešená absolútna hodnota URL po parsovaní pomocou URL a reťazci presmerovaní
headers list<list<string>> Dvojice [name, value]; veľkosť písmen podľa normalizácie špecifikácie; položky s viacerými hodnotami sú samostatné dvojice (napr. Set-Cookie)
body string Nespracované bajty. Pri metódach bez tela ide o prázdny reťazec.
redirect string "follow" / "error" / "manual" (váš transport ich NESMIE nasledovať — slučka presmerovaní beží v Phasis)
timeout int Rozpočet času v milisekundách. 0 = bez obmedzenia.
credentials string "omit" / "same-origin" / "include" — informatívne; súbory cookie sa pripájajú osobitne cez setCookieJar().

$signal je null, ak požiadavke nebol pridelený signál; inak ide o JsObject sprístupňujúci aborted a reason. Pri dlhých prenosoch ho kontrolujte.

Tvar návratovej hodnoty:

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

Ak transport vyvolá výnimku, Phasis ju zachytí a prevedie:

  • Phasis\BuiltIn\Fetch\TransportException s $e->kind === "aborted" → fetch zamietne požiadavku s AbortError DOMException.
  • kind === "timeout" → TypeError s danou správou.
  • kind === "network-error" (predvolené) → TypeError.
  • Čokoľvek iné → TypeError so správou výnimky.

Príklady použitia

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šetko zakázať

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

Tým sa každé volanie fetch() zamietne s TypeError. Užitočné na izoláciu používateľom dodaného JS.

Napodobenina na testovanie

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

Hák politiky

Transport vykonáva požiadavku; o tom, či sa vôbec vykoná, rozhoduje hák politiky. Použite ho na vynucovanie iba cez HTTPS, vkladanie hlavičiek, prepisovanie URL, zoznamy povolených alebo priraďovanie prihlasovacích údajov podľa nájomcu.

PHP
public function setFetchPolicy(callable $hook): void

Podpis háku: function (JsObject $request): ?JsObject. Vrátením null požiadavku povolíte bez zmien. Vrátením Request ju prepíšete. Vyvolaná výnimka spôsobí zamietnutie 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;
});

Hák politiky sa spúšťa pred transportom — ani chybný či nedôveryhodný transport ho preto nedokáže obísť.

Súhrn predvoleného správania

Oblasť Predvolené správanie
Transport CurlTransport (PHP ext-curl)
Presmerovania Podľa špecifikácie sa sleduje najviac 20 presmerovaní. redirect: "manual" vracia odpoveď na presmerovanie Response s type: "opaqueredirect".
Časový limit 30 s podľa reálnych hodín, pripojenie 10 s
Overenie TLS CURLOPT_SSL_VERIFYPEER = true (bez možnosti vypnutia — prepíšte výmenou transportu)
User-Agent Phasis/0.1 sa vloží iba vtedy, ak nie je nastavený
Súbory cookie Neukladajú sa. Ak ich chcete zapnúť, použite setCookieJar().
Hák politiky Žiadny. Všetky požiadavky sú povolené, pokiaľ ich výslovne nezakážete.
AbortSignal Rešpektuje sa počas prenosu pomocou kontroly curl progress-callback.

Pozrite si aj