Vai al contenuto

Archivio cookie

Per impostazione predefinita, fetch() di Phasis ignora Set-Cookie delle risposte e non invia l'header Cookie nelle richieste. È l'impostazione giusta per l'integrazione lato server: in genere non volete che la risposta di un utente influisca sulla richiesta di un altro.

Se vi servono i cookie — per gestire un flusso che dipende dallo stato della sessione, fare scraping dopo l'accesso o riprodurre il comportamento del browser nei test — collegate un archivio con $engine->setCookieJar($jar).

L'interfaccia

L'archivio può essere qualsiasi oggetto che implementi due metodi:

Codice
get(url: string): string         // returns a "Cookie:" header value, e.g. "sid=abc; pref=dark"
set(url: string, header: string): void   // receives one Set-Cookie line

Funzionano sia gli oggetti PHP sia gli JsObject lato JS con membri corrispondenti. Passando null si svuota l'archivio.

Phasis chiama get(url) una volta per ogni passaggio della richiesta (così i reindirizzamenti verso un altro host ricevono cookie aggiornati) e set(url, line) per ogni header Set-Cookie della risposta. La policy dei cookie — corrispondenza del dominio, ambito del percorso, scadenza, SameSite, Secure — è responsabilità dell'archivio, non di Phasis.

Archivio in memoria (test)

PHP
final class MemoryJar
{
    /** @var array<string, array<string, string>> host → name → "name=value" */
    private array $store = [];

    public function get(string $url): string
    {
        $host = parse_url($url, PHP_URL_HOST) ?? '';
        return implode('; ', $this->store[$host] ?? []);
    }

    public function set(string $url, string $header): void
    {
        $host = parse_url($url, PHP_URL_HOST) ?? '';
        [$nv] = explode(';', $header, 2);
        [$name, $value] = explode('=', trim($nv), 2) + ['', ''];
        if ($name !== '') {
            $this->store[$host][$name] = "$name=$value";
        }
    }
}

$engine->setCookieJar(new MemoryJar());

È sufficiente per un test di esito positivo, ma ignora Domain=, Path=, Expires= e gli attributi Secure/HttpOnly/SameSite. Non usatelo per operazioni che attraversano un confine di fiducia.

Symfony BrowserKit

Se avete già installato le librerie HTTP di Symfony, BrowserKit\CookieJar gestisce correttamente le policy.

PHP
use Symfony\Component\BrowserKit\CookieJar as BrowserCookieJar;
use Symfony\Component\BrowserKit\Cookie;

final class SymfonyJarAdapter
{
    public function __construct(private BrowserCookieJar $jar) {}

    public function get(string $url): string
    {
        $cookies = $this->jar->allValues($url);
        return implode('; ', array_map(
            fn ($n, $v) => "$n=$v",
            array_keys($cookies),
            array_values($cookies),
        ));
    }

    public function set(string $url, string $header): void
    {
        $cookie = Cookie::fromString($header, $url);
        $this->jar->set($cookie);
    }
}

$engine->setCookieJar(new SymfonyJarAdapter(new BrowserCookieJar()));

Archivio lato JS

Se l'archivio si trova lato JS — ad esempio perché volete esaminarlo o inizializzarlo da JavaScript — collegatelo tramite un oggetto JS:

JavaScript
const jar = new Map();
$jar.get = (url) => Array.from(jar.entries()).map(([k, v]) => `${k}=${v}`).join('; ');
$jar.set = (url, header) => {
  const [nv] = header.split(';');
  const [name, value] = nv.split('=');
  jar.set(name.trim(), value.trim());
};
PHP
$engine->eval($script);
$engine->setCookieJar($engine->eval('$jar'));

Vengono chiamati gli stessi metodi get/set sia che il destinatario sia un oggetto PHP o un JsObject.

Ambito per richiesta

Un archivio appartiene al realm e persiste per ogni fetch() finché non viene svuotato con setCookieJar(null). Per limitarlo a un singolo flusso di richieste, fatelo esplicitamente:

PHP
$engine->setCookieJar($jar);
$engine->eval('await runLoginFlow()');
$engine->setCookieJar(null);

Esaminare da PHP

$engine->getCookieJar() restituisce il valore passato, utile per le verifiche nei test.

PHP
$jar = new MemoryJar();
$engine->setCookieJar($jar);
$engine->eval('await fetch("https://api.example.com/login", { method: "POST" })');

assertNotEmpty($engine->getCookieJar()->get('https://api.example.com/'));

Vedi anche

  • Trasporto fetch — l'archivio cookie opera all'interno del ciclo di reindirizzamento di Phasis, sopra il trasporto installato.
  • API web (WPT) — test del Fetch Pack sul comportamento dei cookie.