
# Буркан с бисквитки

По подразбиране `fetch()` на Phasis игнорира `Set-Cookie` от отговорите и не изпраща заглавка `Cookie` със заявките. Това е подходящата настройка по подразбиране за вграждане от страна на сървъра — обикновено не искате отговорът на един потребител да влияе върху заявката на друг.

Ако искате да използвате бисквитки — за поток, зависещ от състоянието на сесията, извличане на данни след вход или имитиране на поведението на браузър в тестове — свържете хранилище чрез `$engine->setCookieJar($jar)`.

## Интерфейсът

Хранилището може да бъде всеки обект, който реализира два метода:

```
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 от страна на `JsObject` със съответните полета. Подаването на `null` изчиства хранилището.

Phasis извиква `get(url)` веднъж за всяка стъпка на заявката (така че пренасочванията към друг хост получават актуални бисквитки) и `set(url, line)` веднъж за всяка заглавка `Set-Cookie` в отговора. *Политиката* за бисквитките — съпоставяне на домейни, ограничаване по път, срок на валидност, `SameSite`, `Secure` — е отговорност на хранилището, а не на Phasis.

## Хранилище в паметта (за тестове)

```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());
```

Това е достатъчно за тестов сценарий с успешен резултат, но пренебрегва `Domain=`, `Path=`, `Expires=` и атрибутите `Secure`/`HttpOnly`/`SameSite`. Не го използвайте за нищо, което преминава граница на доверие.

## Symfony BrowserKit

Ако вече сте инсталирали библиотеките HTTP на Symfony, `BrowserKit\CookieJar` обработва политиката правилно.

```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()));
```

## Хранилище от страната на JS

Ако хранилището ви е от страната на JS — например защото искате да го разгледате или заредите предварително от JavaScript — свържете го чрез обект JS:

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

```php
$engine->eval($script);
$engine->setCookieJar($engine->eval('$jar'));
```

Същите методи `get`/`set` се извикват независимо дали получателят е обект PHP или `JsObject`.

## Обхват за всяка заявка

Хранилището е в обхвата на средата — то се запазва при всяка `fetch()`, докато не бъде изчистено с `setCookieJar(null)`. За да ограничите хранилището до един поток от заявки, направете го изрично:

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

## Проверка от PHP

`$engine->getCookieJar()` връща подадената стойност — полезно за проверки в тестове.

```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/'));
```

## Вижте също

- [Транспорт за fetch](https://phasis.inline0.dev/bg/docs/interop/fetch-transport) — хранилището за бисквитки работи **вътре** в цикъла за пренасочване на Phasis, върху избрания от вас транспорт.
- [Уеб API (WPT)](https://phasis.inline0.dev/bg/docs/compatibility/web-apis) — тестовете на пакета Fetch, които проверяват работата с бисквитки.
