Μετάβαση στο περιεχόμενο

Μεταφορά Fetch

Το προεπιλεγμένο fetch() στο Phasis περνά από το ext-curl του PHP — CURLOPT_FOLLOWLOCATION=false (ο βρόχος ανακατευθύνσεων της προδιαγραφής εκτελείται στη μεριά του Phasis), με το CURLOPT_PROGRESSFUNCTION να ελέγχει περιοδικά το AbortSignal. Για τις περισσότερες ενσωματώσεις, αυτό αρκεί.

Για οτιδήποτε άλλο — διακομιστές μεσολάβησης, προσαρμοσμένα DNS, τον υπάρχοντα πελάτη HTTP της εφαρμογής υποδοχής, απομόνωση δικτύου ή προσομοίωση σε δοκιμές — η μεταφορά είναι εναλλάξιμη.

Η διεπαφή

PHP
public function setFetchTransport(callable $transport): void

Η κλήσιμη συνάρτησή σας λαμβάνει:

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

Το $request είναι συσχετιστικός πίνακας:

Κλειδί Τύπος Σημειώσεις
method string Ήδη με κεφαλαία (GET, POST, …)
url string Επιλυμένο, απόλυτο URL μετά τον αναλυτή URL και την αλυσίδα ανακατευθύνσεων
headers list<list<string>> Ζεύγη [name, value]· η πεζοκεφαλαιοποίηση ακολουθεί την κανονικοποίηση της προδιαγραφής· οι εγγραφές πολλαπλών τιμών είναι ξεχωριστά ζεύγη (Set-Cookie κ.λπ.)
body string Ακατέργαστα byte. Κενή συμβολοσειρά για μεθόδους χωρίς σώμα.
redirect string "follow" / "error" / "manual" (η μεταφορά σας ΔΕΝ πρέπει να ακολουθεί — ο βρόχος ανακατευθύνσεων εκτελείται στο Phasis)
timeout int Προϋπολογισμός χρόνου πραγματικού ρολογιού σε χιλιοστά του δευτερολέπτου. 0 = χωρίς όριο.
credentials string "omit" / "same-origin" / "include" — μόνο ενημερωτικά· τα cookies συνδέονται ξεχωριστά μέσω του setCookieJar().

Το $signal είναι null αν το αίτημα δεν είχε σήμα, διαφορετικά είναι JsObject που παρέχει τα aborted και reason. Ελέγχετέ το κατά τη διάρκεια μεγάλων μεταφορών.

Μορφή επιστροφής:

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

Αν η μεταφορά προκαλέσει εξαίρεση, το Phasis την πιάνει και τη μετατρέπει:

  • Ένα Phasis\BuiltIn\Fetch\TransportException με $e->kind === "aborted" → το fetch απορρίπτεται με AbortError DOMException.
  • kind === "timeout" → TypeError που περιέχει το μήνυμα.
  • kind === "network-error" (προεπιλογή) → TypeError.
  • Οτιδήποτε άλλο → TypeError με το μήνυμα της εξαίρεσης.

Παραδείγματα με βήματα

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

Αποκλεισμός όλων

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

Έτσι κάθε κλήση fetch() απορρίπτεται με TypeError. Χρήσιμο για απομόνωση μη έμπιστων JS που παρέχει ο χρήστης.

Mock για δοκιμές

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 πολιτικής

Η μεταφορά εκτελεί το αίτημα· το hook πολιτικής αποφασίζει αν θα εκτελεστεί καθόλου. Χρησιμοποιήστε το για επιβολή μόνο μέσω HTTPS, εισαγωγή κεφαλίδων, επανεγγραφή URL, λίστες επιτρεπόμενων ή σύνδεση διαπιστευτηρίων ανά μισθωτή.

PHP
public function setFetchPolicy(callable $hook): void

Υπογραφή hook: function (JsObject $request): ?JsObject. Η επιστροφή null επιτρέπει το αίτημα ως έχει. Η επιστροφή ενός Request το επανεγγράφει. Η ρίψη εξαίρεσης απορρίπτει το fetch με το σφάλμα που προκλήθηκε.

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 πολιτικής εκτελείται πριν από τη μεταφορά — έτσι δεν μπορεί να το παρακάμψει ούτε μια ελαττωματική ή μη έμπιστη μεταφορά.

Σύνοψη προεπιλεγμένης συμπεριφοράς

Θέμα Προεπιλογή
Μεταφορά CurlTransport (PHP ext-curl)
Ανακατευθύνσεις Ακολουθεί έως 20, σύμφωνα με την προδιαγραφή. Το redirect: "manual" επιστρέφει την απόκριση ανακατεύθυνσης με type: "opaqueredirect".
Χρονικό όριο 30 s πραγματικού χρόνου, 10 s για σύνδεση
Επαλήθευση TLS CURLOPT_SSL_VERIFYPEER = true (χωρίς δυνατότητα απενεργοποίησης — παρακάμψτε μέσω αντικατάστασης μεταφοράς)
User-Agent Το Phasis/0.1 προστίθεται μόνο αν δεν έχει οριστεί
Cookies Δεν αποθηκεύονται. Χρησιμοποιήστε το setCookieJar() για ενεργοποίηση.
Hook πολιτικής Κανένα. Όλα τα αιτήματα επιτρέπονται, εκτός αν αποκλειστούν ρητά.
AbortSignal Λαμβάνεται υπόψη κατά τη μεταφορά μέσω περιοδικού ελέγχου της callback προόδου του curl.

Δείτε επίσης