'/', 'secure' => true, 'httponly' => true, 'samesite' => 'Lax', ]; /** * Dados armazenados no cookie */ private array $data = []; // ------------------------------------------------------------------------- /** * Lê o cookie existente ou cria um novo vazio * * O JSON vindo do cliente é validado — se vier adulterado ou inválido, * o cookie é resetado para um array vazio. */ public function __construct() { if (!empty($this->name) && isset($_COOKIE[$this->name])) { $decoded = json_decode($_COOKIE[$this->name], true); $this->data = is_array($decoded) ? $decoded : []; } } // ------------------------------------------------------------------------- // LEITURA // ------------------------------------------------------------------------- /** * Verifica se uma chave existe no cookie * * @example * if ($cookie->has('user_id')) { ... } */ public function has(string $key): bool { return isset($this->data[$key]); } /** * Retorna o valor de uma chave, ou um valor padrão se não existir * * @param string $key Chave a buscar * @param mixed $default Valor retornado se a chave não existir (padrão: null) * @return mixed * * @example * $theme = $cookie->get('theme', 'light'); */ public function get(string $key, mixed $default = null): mixed { return $this->data[$key] ?? $default; } /** * Retorna todos os dados do cookie como array associativo * * @example * $all = $cookie->all(); * foreach ($all as $key => $value) { ... } */ public function all(): array { return $this->data; } // ------------------------------------------------------------------------- // ESCRITA // ------------------------------------------------------------------------- /** * Adiciona ou atualiza um valor no cookie * * @throws RuntimeException se headers já foram enviados * * @example * $cookie->add('theme', 'dark'); * $cookie->add('user_id', 42); */ public function add(string $key, mixed $value): void { $this->data[$key] = $value; $this->update(); } /** * Remove uma chave do cookie * Não lança erro se a chave não existir * * @throws RuntimeException se headers já foram enviados * * @example * $cookie->del('theme'); */ public function del(string $key): void { unset($this->data[$key]); $this->update(); } /** * Persiste o estado atual do cookie no browser * Chamado automaticamente por add() e del() * * @throws RuntimeException se headers já foram enviados */ public function update(): void { $this->send($this->name, json_encode($this->data), time() + $this->ttl); } /** * Remove todos os dados e apaga o cookie do browser * Para apagar um cookie é necessário setar expires no passado * * @throws RuntimeException se headers já foram enviados * * @example * $cookie->logout(); */ public function logout(): void { $this->data = []; $this->send($this->name, '', time() - 3600); } // ------------------------------------------------------------------------- // INTERNO // ------------------------------------------------------------------------- /** * Envia o cookie com as opções de segurança configuradas * Verifica se os headers já foram enviados antes de tentar * * @throws RuntimeException */ private function send(string $name, string $value, int $expires): void { if (headers_sent($file, $line)) { throw new RuntimeException( "Não é possível definir o cookie '{$name}' — " . "headers já enviados em {$file}:{$line}" ); } setcookie($name, $value, array_merge($this->options, [ 'expires' => $expires, ])); } }