Cookie管理
デフォルトでは、Phasisのfetch()はレスポンスのSet-Cookieを無視し、リクエストにCookieヘッダーを付けません。サーバー側に埋め込む場合には適切な初期設定です。通常、あるユーザーのレスポンスが別のユーザーのリクエストに影響することは望ましくありません。
セッション状態に依存する操作、ログイン後のスクレイピング、テストでのブラウザー動作の再現などでCookieを使う場合は、$engine->setCookieJar($jar)でjarを設定してください。
インターフェース
jarは、次の2つのメソッドに応答する任意のオブジェクトです。
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
PHPオブジェクトと、対応するメンバーを持つJS側のJsObjectsのどちらも使えます。nullを渡すとjarがクリアされます。
Phasisはリクエストの各ホップでget(url)を呼び出すため、別ホストへのリダイレクトでは新しいCookieが取得されます。また、レスポンス内の各Set-Cookieヘッダーに対してset(url, line)を呼び出します。ドメイン照合、パスの範囲、有効期限、SameSite、SecureといったCookieのポリシーはjarの責任であり、Phasisの責任ではありません。
メモリ内jar(テスト用)
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());
正常系のテスト用フィクスチャには十分ですが、Domain=、Path=、Expires=、およびSecure/HttpOnly/SameSite属性は無視します。信頼境界を越える用途には使わないでください。
Symfony BrowserKit
SymfonyのHTTPライブラリをすでにインストールしている場合は、BrowserKit\CookieJarがポリシーを適切に処理します。
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()));
JS側のjar
jarがJS側にあり、たとえばJavaScriptから内容を確認したり初期データを設定したりする場合は、JSオブジェクトとしてバインドします。
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());
};
$engine->eval($script);
$engine->setCookieJar($engine->eval('$jar'));
受け手がPHPオブジェクトでもJsObjectでも、同じget/setメソッドが呼び出されます。
リクエストごとのスコープ
jarはrealm上にあり、setCookieJar(null)でクリアするまで、すべてのfetch()を通じて保持されます。jarを単一のリクエストフローだけで使うには、明示的に設定してください。
$engine->setCookieJar($jar);
$engine->eval('await runLoginFlow()');
$engine->setCookieJar(null);
PHPからの確認
$engine->getCookieJar()は渡された値をそのまま返すため、テストのアサーションに便利です。
$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/'));
関連項目
- Fetchトランスポート — Cookie jarはPhasisのリダイレクトループ内で動作し、インストール済みのトランスポートに重ねて使われます。
- Web API(WPT) — Cookie動作を対象とするFetch Packテスト。