Preskoči na vsebino

Prenos Fetch

Privzeti fetch() v Phasis deluje prek PHP ext-curl — CURLOPT_FOLLOWLOCATION=false (preusmeritvena zanka specifikacije deluje v okolju Phasis) in CURLOPT_PROGRESSFUNCTION preverja AbortSignal. Za večino vdelav je to primerno.

Za vse drugo — posredniške strežnike, lasten DNS, obstoječi odjemalec HTTP gostiteljske aplikacije, omrežno peskovnikovanje ali posnemanje v testih — je prenos prilagodljiv.

Vmesnik

PHP
public function setFetchTransport(callable $transport): void

Vaša klicna funkcija prejme:

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

$request je asociativna tabela:

Ključ Vrsta Opombe
method string Že zapisano z velikimi črkami (GET, POST, …)
url string Razrešen absolutni URL po razčlenjevalniku URL in verigi preusmeritev
headers list<list<string>> Pari vrednosti [name, value]; velikost črk normalizira specifikacija; večvrednostni vnosi so ločeni pari (Set-Cookie itd.)
body string Neobdelani bajti. Prazen niz za metode brez telesa.
redirect string "follow" / "error" / "manual" (vaš prenos jim NE sme slediti — preusmeritvena zanka deluje v Phasis)
timeout int Proračun po stenski uri v milisekundah. 0 = brez omejitve.
credentials string "omit" / "same-origin" / "include" — zgolj informativno; piškotki se povežejo ločeno prek setCookieJar().

$signal je null, če zahtevi ni bil dodeljen signal, sicer pa JsObject z vrednostma aborted in reason. Med dolgotrajnimi prenosi ga redno preverjajte.

Oblika vrnjene vrednosti:

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

Če prenos sproži izjemo, Phasis jo ujame in pretvori:

  • Phasis\BuiltIn\Fetch\TransportException z $e->kind === "aborted" → fetch zavrne z AbortError DOMException.
  • kind === "timeout" → TypeError, ki vsebuje sporočilo.
  • kind === "network-error" (privzeto) → TypeError.
  • Vse drugo → TypeError s sporočilom izjeme.

Primeri uporabe

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

Zavračanje vsega

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

To povzroči, da se vsak klic fetch() zavrne z TypeError. Uporabno za peskovnikovanje uporabniško podanega JS.

Lažni prenos za teste

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

Kavelj pravilnika

Prenos izvede zahtevo; kavelj pravilnika odloči, ali naj se sploh izvede. Uporabite ga za uveljavljanje pravil samo za HTTPS, dodajanje glav, prepisovanje URL, sezname dovoljenih ali vezavo poverilnic za posameznega najemnika.

PHP
public function setFetchPolicy(callable $hook): void

Podpis kavlja: function (JsObject $request): ?JsObject. Če vrne null, se zahteva dovoli brez sprememb. Če vrne Request, se zahteva prepiše. Sprožena izjema zavrne fetch z navedeno napako.

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

Kavelj pravilnika se izvede pred prenosom — zato ga ne more zaobiti niti napačno delujoč ali nezaupanja vreden prenos.

Povzetek privzetega vedenja

Zadeva Privzeto
Prenos CurlTransport (PHP ext-curl)
Preusmeritve Po specifikaciji sledi največ 20 preusmeritvam. redirect: "manual" vrne odgovor preusmeritve z type: "opaqueredirect".
Časovna omejitev 30 s po stenski uri, 10 s za povezavo
Preverjanje TLS CURLOPT_SSL_VERIFYPEER = true (izklop ni mogoč — preglasite z zamenjavo prenosa)
User-Agent Phasis/0.1 se doda samo, če še ni nastavljen
Piškotki Nič se ne shrani. Omogočite jih z setCookieJar().
Kavelj pravilnika Brez. Vse zahteve so dovoljene, razen če jih izrecno zavrnete.
AbortSignal Upošteva se med prenosom s preverjanjem povratnega klica napredka curl.

Glejte tudi