Huymada icon

Cookie

Huymada | PRO | 03/13/26 03:34:51 PM UTC (Edited) | 0 ⭐ | 383 👁️ | Never ⏰ | []
PHP |

4.81 KB

|

None

|

0 👍

/

0 👎

<?php
/**
 * Cookie - Controle de cookies com segurança e boas práticas
 * @version 2.0
 */
class Cookie {
 
    /**
     * Nome do cookie
     */
    public string $name = '';
 
    /**
     * Tempo de vida em segundos (padrão: 7 dias)
     */
    public int $ttl = 3600 * 24 * 7;
 
    /**
     * Opções de segurança do cookie
     * secure   → só trafega via HTTPS
     * httponly → inacessível via JavaScript (protege contra XSS)
     * samesite → protege contra CSRF (Lax cobre a maioria dos casos)
     */
    public array $options = [
        'path'     => '/',
        '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,
        ]));
    }
 
}

Comments