Compare commits
2 Commits
65bfec8078
...
ce1d13e862
| Author | SHA1 | Date | |
|---|---|---|---|
| ce1d13e862 | |||
| 74ffbcb651 |
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
require_once __DIR__ . '/Session.php';
|
require_once __DIR__ . '/Session.php';
|
||||||
require_once __DIR__ . '/Validator.php';
|
require_once __DIR__ . '/Validator.php';
|
||||||
|
require_once __DIR__ . '/Cookies.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context holds the request, response, and shared state for a request
|
* Context holds the request, response, and shared state for a request
|
||||||
@ -11,17 +12,20 @@ class Context
|
|||||||
public Request $request;
|
public Request $request;
|
||||||
public Response $response;
|
public Response $response;
|
||||||
public Session $session;
|
public Session $session;
|
||||||
|
public Cookies $cookie;
|
||||||
public array $state = [];
|
public array $state = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __construct creates a new Context with request and response
|
* __construct creates a new Context with request and response
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct(?Cookie $cookie = null)
|
||||||
{
|
{
|
||||||
$this->request = new Request();
|
$this->request = new Request();
|
||||||
$this->response = new Response();
|
$this->response = new Response();
|
||||||
$this->session = new Session();
|
$this->session = new Session();
|
||||||
$this->session->start();
|
$this->session->start();
|
||||||
|
$this->cookie = $cookie ?: new Cookies();
|
||||||
|
$this->response->setCookieManager($this->cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
240
Cookies.php
Normal file
240
Cookies.php
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cookie manager for consistent cookie handling
|
||||||
|
*/
|
||||||
|
class Cookies
|
||||||
|
{
|
||||||
|
private array $defaults;
|
||||||
|
|
||||||
|
public function __construct(array $defaults = [])
|
||||||
|
{
|
||||||
|
$this->defaults = array_merge([
|
||||||
|
'expires' => 0,
|
||||||
|
'path' => '/',
|
||||||
|
'domain' => '',
|
||||||
|
'secure' => false,
|
||||||
|
'httponly' => true,
|
||||||
|
'samesite' => 'Lax'
|
||||||
|
], $defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a cookie
|
||||||
|
*/
|
||||||
|
public function set(string $name, string $value, array $options = []): bool
|
||||||
|
{
|
||||||
|
$options = array_merge($this->defaults, $options);
|
||||||
|
|
||||||
|
// Convert lifetime to expires timestamp if provided
|
||||||
|
if (isset($options['lifetime'])) {
|
||||||
|
$options['expires'] = time() + $options['lifetime'];
|
||||||
|
unset($options['lifetime']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return setcookie($name, $value, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a cookie value
|
||||||
|
*/
|
||||||
|
public function get(string $name, ?string $default = null): ?string
|
||||||
|
{
|
||||||
|
return $_COOKIE[$name] ?? $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a cookie exists
|
||||||
|
*/
|
||||||
|
public function has(string $name): bool
|
||||||
|
{
|
||||||
|
return isset($_COOKIE[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a cookie
|
||||||
|
*/
|
||||||
|
public function delete(string $name, array $options = []): bool
|
||||||
|
{
|
||||||
|
$options = array_merge($this->defaults, $options);
|
||||||
|
$options['expires'] = time() - 3600;
|
||||||
|
|
||||||
|
// Remove from $_COOKIE superglobal
|
||||||
|
unset($_COOKIE[$name]);
|
||||||
|
|
||||||
|
return setcookie($name, '', $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a cookie that expires when browser closes
|
||||||
|
*/
|
||||||
|
public function setSession(string $name, string $value, array $options = []): bool
|
||||||
|
{
|
||||||
|
$options['expires'] = 0;
|
||||||
|
return $this->set($name, $value, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a cookie with specific lifetime in seconds
|
||||||
|
*/
|
||||||
|
public function setWithLifetime(string $name, string $value, int $lifetime, array $options = []): bool
|
||||||
|
{
|
||||||
|
$options['lifetime'] = $lifetime;
|
||||||
|
return $this->set($name, $value, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a cookie that expires in specified days
|
||||||
|
*/
|
||||||
|
public function setForDays(string $name, string $value, int $days, array $options = []): bool
|
||||||
|
{
|
||||||
|
return $this->setWithLifetime($name, $value, $days * 86400, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a cookie that expires in specified hours
|
||||||
|
*/
|
||||||
|
public function setForHours(string $name, string $value, int $hours, array $options = []): bool
|
||||||
|
{
|
||||||
|
return $this->setWithLifetime($name, $value, $hours * 3600, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a cookie that expires in specified minutes
|
||||||
|
*/
|
||||||
|
public function setForMinutes(string $name, string $value, int $minutes, array $options = []): bool
|
||||||
|
{
|
||||||
|
return $this->setWithLifetime($name, $value, $minutes * 60, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a forever cookie (5 years)
|
||||||
|
*/
|
||||||
|
public function forever(string $name, string $value, array $options = []): bool
|
||||||
|
{
|
||||||
|
return $this->setWithLifetime($name, $value, 157680000, $options); // 5 years
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all cookies
|
||||||
|
*/
|
||||||
|
public function all(): array
|
||||||
|
{
|
||||||
|
return $_COOKIE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all cookies
|
||||||
|
*/
|
||||||
|
public function clear(): void
|
||||||
|
{
|
||||||
|
foreach ($_COOKIE as $name => $value) $this->delete($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set default options
|
||||||
|
*/
|
||||||
|
public function setDefaults(array $defaults): void
|
||||||
|
{
|
||||||
|
$this->defaults = array_merge($this->defaults, $defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get default options
|
||||||
|
*/
|
||||||
|
public function getDefaults(): array
|
||||||
|
{
|
||||||
|
return $this->defaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a signed cookie value
|
||||||
|
*/
|
||||||
|
public function sign(string $value, string $secret): string
|
||||||
|
{
|
||||||
|
$signature = hash_hmac('sha256', $value, $secret);
|
||||||
|
return base64_encode($value . '|' . $signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify and extract signed cookie value
|
||||||
|
*/
|
||||||
|
public function verify(string $signedValue, string $secret): ?string
|
||||||
|
{
|
||||||
|
$decoded = base64_decode($signedValue);
|
||||||
|
if (!$decoded || !str_contains($decoded, '|')) return null;
|
||||||
|
|
||||||
|
[$value, $signature] = explode('|', $decoded, 2);
|
||||||
|
$expectedSignature = hash_hmac('sha256', $value, $secret);
|
||||||
|
|
||||||
|
if (!hash_equals($expectedSignature, $signature)) return null;
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a signed cookie
|
||||||
|
*/
|
||||||
|
public function setSigned(string $name, string $value, string $secret, array $options = []): bool
|
||||||
|
{
|
||||||
|
$signedValue = $this->sign($value, $secret);
|
||||||
|
return $this->set($name, $signedValue, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get and verify a signed cookie
|
||||||
|
*/
|
||||||
|
public function getSigned(string $name, string $secret, ?string $default = null): ?string
|
||||||
|
{
|
||||||
|
$signedValue = $this->get($name);
|
||||||
|
if ($signedValue === null) return $default;
|
||||||
|
|
||||||
|
$value = $this->verify($signedValue, $secret);
|
||||||
|
return $value !== null ? $value : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt a cookie value
|
||||||
|
*/
|
||||||
|
public function encrypt(string $value, string $key): string
|
||||||
|
{
|
||||||
|
$iv = random_bytes(16);
|
||||||
|
$encrypted = openssl_encrypt($value, 'AES-256-CBC', $key, 0, $iv);
|
||||||
|
return base64_encode($iv . '|' . $encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt a cookie value
|
||||||
|
*/
|
||||||
|
public function decrypt(string $encryptedValue, string $key): ?string
|
||||||
|
{
|
||||||
|
$decoded = base64_decode($encryptedValue);
|
||||||
|
if (!$decoded || !str_contains($decoded, '|')) return null;
|
||||||
|
|
||||||
|
[$iv, $encrypted] = explode('|', $decoded, 2);
|
||||||
|
$decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $key, 0, $iv);
|
||||||
|
|
||||||
|
return $decrypted !== false ? $decrypted : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an encrypted cookie
|
||||||
|
*/
|
||||||
|
public function setEncrypted(string $name, string $value, string $key, array $options = []): bool
|
||||||
|
{
|
||||||
|
$encryptedValue = $this->encrypt($value, $key);
|
||||||
|
return $this->set($name, $encryptedValue, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get and decrypt a cookie
|
||||||
|
*/
|
||||||
|
public function getEncrypted(string $name, string $key, ?string $default = null): ?string
|
||||||
|
{
|
||||||
|
$encryptedValue = $this->get($name);
|
||||||
|
if ($encryptedValue === null) return $default;
|
||||||
|
|
||||||
|
$value = $this->decrypt($encryptedValue, $key);
|
||||||
|
return $value !== null ? $value : $default;
|
||||||
|
}
|
||||||
|
}
|
||||||
139
EXAMPLES.md
139
EXAMPLES.md
@ -312,6 +312,139 @@ $session->clear(); // Clear data but keep session active
|
|||||||
$all = $session->all(); // Get all session data
|
$all = $session->all(); // Get all session data
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Cookies Examples
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Cookie management is automatically available through Context
|
||||||
|
$context->cookie; // Access the Cookies instance
|
||||||
|
|
||||||
|
// Basic cookie operations
|
||||||
|
$context->cookie->set('name', 'value'); // Session cookie
|
||||||
|
$context->cookie->get('name', 'default'); // Get cookie value
|
||||||
|
$context->cookie->has('name'); // Check if cookie exists
|
||||||
|
$context->cookie->delete('name'); // Delete cookie
|
||||||
|
|
||||||
|
// Cookie with specific lifetime
|
||||||
|
$context->cookie->setForDays('remember_me', 'token', 30); // 30 days
|
||||||
|
$context->cookie->setForHours('temp_data', 'value', 2); // 2 hours
|
||||||
|
$context->cookie->setForMinutes('flash', 'quick', 5); // 5 minutes
|
||||||
|
$context->cookie->forever('permanent', 'value'); // 5 years
|
||||||
|
|
||||||
|
// Session cookie (expires when browser closes)
|
||||||
|
$context->cookie->setSession('session_data', 'value');
|
||||||
|
|
||||||
|
// Custom lifetime in seconds
|
||||||
|
$context->cookie->setWithLifetime('custom', 'value', 7200); // 2 hours
|
||||||
|
|
||||||
|
// Cookie with options
|
||||||
|
$context->cookie->set('secure_cookie', 'value', [
|
||||||
|
'expires' => time() + 3600,
|
||||||
|
'path' => '/admin',
|
||||||
|
'domain' => '.example.com',
|
||||||
|
'secure' => true,
|
||||||
|
'httponly' => true,
|
||||||
|
'samesite' => 'Strict'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Signed cookies (integrity verification)
|
||||||
|
$secret = 'your-secret-key';
|
||||||
|
$context->cookie->setSigned('signed_data', 'sensitive', $secret);
|
||||||
|
$value = $context->cookie->getSigned('signed_data', $secret); // Returns null if tampered
|
||||||
|
|
||||||
|
// Encrypted cookies (for sensitive data)
|
||||||
|
$key = 'your-encryption-key-32-chars-long!!';
|
||||||
|
$context->cookie->setEncrypted('encrypted_data', 'very_sensitive', $key);
|
||||||
|
$value = $context->cookie->getEncrypted('encrypted_data', $key); // Returns null if corrupted
|
||||||
|
|
||||||
|
// Get all cookies
|
||||||
|
$allCookies = $context->cookie->all();
|
||||||
|
|
||||||
|
// Clear all cookies
|
||||||
|
$context->cookie->clear();
|
||||||
|
|
||||||
|
// Configure cookie defaults for the application
|
||||||
|
$app = new Web(debug: true, cookieDefaults: [
|
||||||
|
'path' => '/',
|
||||||
|
'domain' => '.example.com',
|
||||||
|
'secure' => true,
|
||||||
|
'httponly' => true,
|
||||||
|
'samesite' => 'Lax'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Or set defaults later
|
||||||
|
$context->cookie->setDefaults([
|
||||||
|
'secure' => true,
|
||||||
|
'httponly' => true
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Using cookies in route handlers
|
||||||
|
$app->get('/set-preference', function(Context $context) {
|
||||||
|
$theme = $context->query('theme', 'light');
|
||||||
|
$context->cookie->setForDays('user_theme', $theme, 365);
|
||||||
|
return ['message' => 'Theme preference saved'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->get('/get-preference', function(Context $context) {
|
||||||
|
$theme = $context->cookie->get('user_theme', 'light');
|
||||||
|
return ['theme' => $theme];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Using cookies with authentication
|
||||||
|
$app->post('/login', function(Context $context) use ($auth) {
|
||||||
|
// ... validate credentials ...
|
||||||
|
|
||||||
|
if ($context->input('remember')) {
|
||||||
|
// Auth class uses Cookies internally for remember tokens
|
||||||
|
$auth->login($userData, true);
|
||||||
|
} else {
|
||||||
|
$auth->login($userData, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set additional preference cookies
|
||||||
|
$context->cookie->setForDays('last_login', date('Y-m-d'), 30);
|
||||||
|
|
||||||
|
return ['message' => 'Logged in successfully'];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tracking with signed cookies
|
||||||
|
$app->use(function(Context $context, callable $next) {
|
||||||
|
$secret = 'tracking-secret';
|
||||||
|
$visitCount = (int)$context->cookie->getSigned('visit_count', $secret, '0');
|
||||||
|
$visitCount++;
|
||||||
|
$context->cookie->setSigned('visit_count', (string)$visitCount, $secret);
|
||||||
|
$context->set('visit_count', $visitCount);
|
||||||
|
$next();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Storing user preferences securely
|
||||||
|
$app->post('/preferences', function(Context $context) {
|
||||||
|
$preferences = $context->only(['theme', 'language', 'timezone']);
|
||||||
|
$key = 'encryption-key-must-be-32-chars!';
|
||||||
|
|
||||||
|
// Store encrypted preferences
|
||||||
|
$context->cookie->setEncrypted(
|
||||||
|
'user_prefs',
|
||||||
|
json_encode($preferences),
|
||||||
|
$key,
|
||||||
|
['expires' => time() + 31536000] // 1 year
|
||||||
|
);
|
||||||
|
|
||||||
|
return ['message' => 'Preferences saved securely'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->get('/preferences', function(Context $context) {
|
||||||
|
$key = 'encryption-key-must-be-32-chars!';
|
||||||
|
$encrypted = $context->cookie->getEncrypted('user_prefs', $key);
|
||||||
|
|
||||||
|
if ($encrypted) {
|
||||||
|
$preferences = json_decode($encrypted, true);
|
||||||
|
return ['preferences' => $preferences];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['preferences' => null];
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Validation Examples
|
## Validation Examples
|
||||||
|
|
||||||
```php
|
```php
|
||||||
@ -346,10 +479,10 @@ Validator::extend('custom', function($value, $parameters, $data) {
|
|||||||
## Authentication Examples
|
## Authentication Examples
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$auth = new Auth($session, [
|
// Auth automatically uses Cookies for remember tokens
|
||||||
|
$auth = new Auth($session, $context->cookie, [
|
||||||
'cookie_name' => 'remember_token',
|
'cookie_name' => 'remember_token',
|
||||||
'cookie_lifetime' => 2592000, // 30 days
|
'cookie_lifetime' => 2592000 // 30 days
|
||||||
'cookie_secure' => true
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
|
|||||||
34
README.md
34
README.md
@ -68,6 +68,15 @@ Session management with security features.
|
|||||||
- CSRF token generation and validation
|
- CSRF token generation and validation
|
||||||
- Session lifecycle management
|
- Session lifecycle management
|
||||||
|
|
||||||
|
### Cookies
|
||||||
|
Comprehensive cookie management with security features.
|
||||||
|
- Consistent cookie handling across the application
|
||||||
|
- Convenient lifetime methods (sessions, days, hours, minutes)
|
||||||
|
- Signed cookies for integrity verification
|
||||||
|
- Encrypted cookies for sensitive data
|
||||||
|
- Configurable defaults for all cookie operations
|
||||||
|
- Automatic integration with Context and Response
|
||||||
|
|
||||||
### Validator
|
### Validator
|
||||||
Input validation with 20+ built-in rules.
|
Input validation with 20+ built-in rules.
|
||||||
- Chainable validation rules (`required|email|min:6`)
|
- Chainable validation rules (`required|email|min:6`)
|
||||||
@ -100,7 +109,7 @@ Comprehensive error and exception handling.
|
|||||||
|
|
||||||
### Web Class
|
### Web Class
|
||||||
```php
|
```php
|
||||||
new Web(bool $debug = false)
|
new Web(bool $debug = false, array $cookieDefaults = [])
|
||||||
use(callable $middleware): self
|
use(callable $middleware): self
|
||||||
get|post|put|patch|delete|head(string $route, callable $handler): self
|
get|post|put|patch|delete|head(string $route, callable $handler): self
|
||||||
group(string $prefix, callable $callback): self
|
group(string $prefix, callable $callback): self
|
||||||
@ -150,9 +159,30 @@ validateRequest(array $rules, array $messages = []): Validator
|
|||||||
### Validator Rules
|
### Validator Rules
|
||||||
`required`, `email`, `url`, `alpha`, `alphaNum`, `numeric`, `integer`, `float`, `boolean`, `array`, `json`, `date`, `min:n`, `max:n`, `between:min,max`, `length:n`, `in:a,b,c`, `notIn:a,b,c`, `regex:/pattern/`, `confirmed`, `unique:table,column`, `exists:table,column`
|
`required`, `email`, `url`, `alpha`, `alphaNum`, `numeric`, `integer`, `float`, `boolean`, `array`, `json`, `date`, `min:n`, `max:n`, `between:min,max`, `length:n`, `in:a,b,c`, `notIn:a,b,c`, `regex:/pattern/`, `confirmed`, `unique:table,column`, `exists:table,column`
|
||||||
|
|
||||||
|
### Cookies Class
|
||||||
|
```php
|
||||||
|
new Cookies(array $defaults = [])
|
||||||
|
set(string $name, string $value, array $options = []): bool
|
||||||
|
get(string $name, ?string $default = null): ?string
|
||||||
|
has(string $name): bool
|
||||||
|
delete(string $name, array $options = []): bool
|
||||||
|
setSession(string $name, string $value, array $options = []): bool
|
||||||
|
setWithLifetime(string $name, string $value, int $lifetime, array $options = []): bool
|
||||||
|
setForDays(string $name, string $value, int $days, array $options = []): bool
|
||||||
|
setForHours(string $name, string $value, int $hours, array $options = []): bool
|
||||||
|
setForMinutes(string $name, string $value, int $minutes, array $options = []): bool
|
||||||
|
forever(string $name, string $value, array $options = []): bool
|
||||||
|
all(): array
|
||||||
|
clear(): void
|
||||||
|
setSigned(string $name, string $value, string $secret, array $options = []): bool
|
||||||
|
getSigned(string $name, string $secret, ?string $default = null): ?string
|
||||||
|
setEncrypted(string $name, string $value, string $key, array $options = []): bool
|
||||||
|
getEncrypted(string $name, string $key, ?string $default = null): ?string
|
||||||
|
```
|
||||||
|
|
||||||
### Auth Class
|
### Auth Class
|
||||||
```php
|
```php
|
||||||
new Auth(Session $session, array $config = [])
|
new Auth(Session $session, ?Cookies $cookies = null, array $config = [])
|
||||||
login(array $userData, bool $remember = false): void
|
login(array $userData, bool $remember = false): void
|
||||||
logout(): void
|
logout(): void
|
||||||
check(): bool
|
check(): bool
|
||||||
|
|||||||
49
Response.php
49
Response.php
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/Cookies.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Response class represents an HTTP response
|
* Response class represents an HTTP response
|
||||||
*/
|
*/
|
||||||
@ -9,6 +11,16 @@ class Response
|
|||||||
private array $headers = [];
|
private array $headers = [];
|
||||||
private string $body = '';
|
private string $body = '';
|
||||||
private bool $sent = false;
|
private bool $sent = false;
|
||||||
|
private ?Cookies $cookie = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the cookie manager for this response
|
||||||
|
*/
|
||||||
|
public function setCookieManager(Cookies $cookie): Response
|
||||||
|
{
|
||||||
|
$this->cookie = $cookie;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the HTTP status code for the response
|
* Set the HTTP status code for the response
|
||||||
@ -76,23 +88,28 @@ class Response
|
|||||||
*/
|
*/
|
||||||
public function cookie(string $name, string $value, array $options = []): Response
|
public function cookie(string $name, string $value, array $options = []): Response
|
||||||
{
|
{
|
||||||
$options = array_merge([
|
if ($this->cookie) {
|
||||||
'expires' => 0,
|
$this->cookie->set($name, $value, $options);
|
||||||
'path' => '/',
|
} else {
|
||||||
'domain' => '',
|
// Fallback to direct setcookie if no Cookie manager is set
|
||||||
'secure' => false,
|
$options = array_merge([
|
||||||
'httponly' => true,
|
'expires' => 0,
|
||||||
'samesite' => 'Lax'
|
'path' => '/',
|
||||||
], $options);
|
'domain' => '',
|
||||||
|
'secure' => false,
|
||||||
|
'httponly' => true,
|
||||||
|
'samesite' => 'Lax'
|
||||||
|
], $options);
|
||||||
|
|
||||||
setcookie($name, $value, [
|
setcookie($name, $value, [
|
||||||
'expires' => $options['expires'],
|
'expires' => $options['expires'],
|
||||||
'path' => $options['path'],
|
'path' => $options['path'],
|
||||||
'domain' => $options['domain'],
|
'domain' => $options['domain'],
|
||||||
'secure' => $options['secure'],
|
'secure' => $options['secure'],
|
||||||
'httponly' => $options['httponly'],
|
'httponly' => $options['httponly'],
|
||||||
'samesite' => $options['samesite']
|
'samesite' => $options['samesite']
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|||||||
7
Web.php
7
Web.php
@ -6,6 +6,7 @@ require_once __DIR__ . '/Response.php';
|
|||||||
require_once __DIR__ . '/Context.php';
|
require_once __DIR__ . '/Context.php';
|
||||||
require_once __DIR__ . '/Router.php';
|
require_once __DIR__ . '/Router.php';
|
||||||
require_once __DIR__ . '/ErrorHandler.php';
|
require_once __DIR__ . '/ErrorHandler.php';
|
||||||
|
require_once __DIR__ . '/Cookies.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Web is the application controller itself
|
* Web is the application controller itself
|
||||||
@ -16,11 +17,13 @@ class Web
|
|||||||
private array $middleware = [];
|
private array $middleware = [];
|
||||||
private Context $context;
|
private Context $context;
|
||||||
private ErrorHandler $errorHandler;
|
private ErrorHandler $errorHandler;
|
||||||
|
private Cookies $cookie;
|
||||||
|
|
||||||
public function __construct(bool $debug = false)
|
public function __construct(bool $debug = false, array $cookieDefaults = [])
|
||||||
{
|
{
|
||||||
$this->router = new Router();
|
$this->router = new Router();
|
||||||
$this->errorHandler = new ErrorHandler($debug);
|
$this->errorHandler = new ErrorHandler($debug);
|
||||||
|
$this->cookie = new Cookies($cookieDefaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function use(callable $middleware): self
|
public function use(callable $middleware): self
|
||||||
@ -118,7 +121,7 @@ class Web
|
|||||||
|
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
$this->context = new Context();
|
$this->context = new Context($this->cookie);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$next = function() {
|
$next = function() {
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
require_once __DIR__ . '/User.php';
|
require_once __DIR__ . '/User.php';
|
||||||
require_once __DIR__ . '/../Session.php';
|
require_once __DIR__ . '/../Session.php';
|
||||||
|
require_once __DIR__ . '/../Cookies.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simplified Auth handles user authentication with external verification
|
* Simplified Auth handles user authentication with external verification
|
||||||
@ -9,6 +10,7 @@ require_once __DIR__ . '/../Session.php';
|
|||||||
class Auth
|
class Auth
|
||||||
{
|
{
|
||||||
private Session $session;
|
private Session $session;
|
||||||
|
private Cookies $cookie;
|
||||||
private ?User $user = null;
|
private ?User $user = null;
|
||||||
private array $config;
|
private array $config;
|
||||||
|
|
||||||
@ -16,17 +18,17 @@ class Auth
|
|||||||
const REMEMBER_COOKIE = 'remember_token';
|
const REMEMBER_COOKIE = 'remember_token';
|
||||||
const REMEMBER_DURATION = 2592000; // 30 days in seconds
|
const REMEMBER_DURATION = 2592000; // 30 days in seconds
|
||||||
|
|
||||||
public function __construct(Session $session, array $config = [])
|
public function __construct(Session $session, ?Cookie $cookie = null, array $config = [])
|
||||||
{
|
{
|
||||||
$this->session = $session;
|
$this->session = $session;
|
||||||
|
$this->cookie = $cookie ?: new Cookies([
|
||||||
|
'path' => '/',
|
||||||
|
'httponly' => true,
|
||||||
|
'samesite' => 'Lax'
|
||||||
|
]);
|
||||||
$this->config = array_merge([
|
$this->config = array_merge([
|
||||||
'cookie_name' => self::REMEMBER_COOKIE,
|
'cookie_name' => self::REMEMBER_COOKIE,
|
||||||
'cookie_lifetime' => self::REMEMBER_DURATION,
|
'cookie_lifetime' => self::REMEMBER_DURATION
|
||||||
'cookie_path' => '/',
|
|
||||||
'cookie_domain' => '',
|
|
||||||
'cookie_secure' => false,
|
|
||||||
'cookie_httponly' => true,
|
|
||||||
'cookie_samesite' => 'Lax'
|
|
||||||
], $config);
|
], $config);
|
||||||
|
|
||||||
$this->initializeFromSession();
|
$this->initializeFromSession();
|
||||||
@ -176,17 +178,10 @@ class Auth
|
|||||||
*/
|
*/
|
||||||
private function setRememberCookie(string $value): void
|
private function setRememberCookie(string $value): void
|
||||||
{
|
{
|
||||||
setcookie(
|
$this->cookie->setWithLifetime(
|
||||||
$this->config['cookie_name'],
|
$this->config['cookie_name'],
|
||||||
$value,
|
$value,
|
||||||
[
|
$this->config['cookie_lifetime']
|
||||||
'expires' => time() + $this->config['cookie_lifetime'],
|
|
||||||
'path' => $this->config['cookie_path'],
|
|
||||||
'domain' => $this->config['cookie_domain'],
|
|
||||||
'secure' => $this->config['cookie_secure'],
|
|
||||||
'httponly' => $this->config['cookie_httponly'],
|
|
||||||
'samesite' => $this->config['cookie_samesite']
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,18 +190,7 @@ class Auth
|
|||||||
*/
|
*/
|
||||||
private function clearRememberCookie(): void
|
private function clearRememberCookie(): void
|
||||||
{
|
{
|
||||||
setcookie(
|
$this->cookie->delete($this->config['cookie_name']);
|
||||||
$this->config['cookie_name'],
|
|
||||||
'',
|
|
||||||
[
|
|
||||||
'expires' => time() - 3600,
|
|
||||||
'path' => $this->config['cookie_path'],
|
|
||||||
'domain' => $this->config['cookie_domain'],
|
|
||||||
'secure' => $this->config['cookie_secure'],
|
|
||||||
'httponly' => $this->config['cookie_httponly'],
|
|
||||||
'samesite' => $this->config['cookie_samesite']
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -214,7 +198,7 @@ class Auth
|
|||||||
*/
|
*/
|
||||||
private function getUserDataFromRememberCookie(): ?array
|
private function getUserDataFromRememberCookie(): ?array
|
||||||
{
|
{
|
||||||
$cookie = $_COOKIE[$this->config['cookie_name']] ?? null;
|
$cookie = $this->cookie->get($this->config['cookie_name']);
|
||||||
|
|
||||||
if (!$cookie || !str_contains($cookie, '|')) return null;
|
if (!$cookie || !str_contains($cookie, '|')) return null;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user