add lots of procies to Context, add documentation and examples
This commit is contained in:
parent
f60f330c9a
commit
6184d28dda
192
Context.php
192
Context.php
@ -127,4 +127,196 @@ class Context
|
|||||||
|
|
||||||
return $this->validate($data, $rules, $messages);
|
return $this->validate($data, $rules, $messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* header returns the value of a request header
|
||||||
|
*/
|
||||||
|
public function header(string $name): ?string
|
||||||
|
{
|
||||||
|
return $this->request->header($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* input returns input value from any source (route, query, post, json)
|
||||||
|
*/
|
||||||
|
public function input(string $name, mixed $default = null): mixed
|
||||||
|
{
|
||||||
|
return $this->request->input($name, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* param returns a parameter from route params, query params, or post data
|
||||||
|
*/
|
||||||
|
public function param(string $name): mixed
|
||||||
|
{
|
||||||
|
return $this->request->param($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* all returns all input data merged from all sources
|
||||||
|
*/
|
||||||
|
public function all(): array
|
||||||
|
{
|
||||||
|
return $this->request->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* only returns only specified keys from input
|
||||||
|
*/
|
||||||
|
public function only(array $keys): array
|
||||||
|
{
|
||||||
|
return $this->request->only($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* except returns all input except specified keys
|
||||||
|
*/
|
||||||
|
public function except(array $keys): array
|
||||||
|
{
|
||||||
|
return $this->request->except($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* has checks if input key exists
|
||||||
|
*/
|
||||||
|
public function has(string $key): bool
|
||||||
|
{
|
||||||
|
return $this->request->has($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cookie returns the value of a request cookie
|
||||||
|
*/
|
||||||
|
public function cookie(string $name): ?string
|
||||||
|
{
|
||||||
|
return $this->request->cookie($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* expectsJson checks if request expects JSON response
|
||||||
|
*/
|
||||||
|
public function expectsJson(): bool
|
||||||
|
{
|
||||||
|
return $this->request->expectsJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* isAjax checks if request is AJAX
|
||||||
|
*/
|
||||||
|
public function isAjax(): bool
|
||||||
|
{
|
||||||
|
return $this->request->isAjax();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ip returns the client IP address
|
||||||
|
*/
|
||||||
|
public function ip(): string
|
||||||
|
{
|
||||||
|
return $this->request->ip();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* userAgent returns the user agent string
|
||||||
|
*/
|
||||||
|
public function userAgent(): string
|
||||||
|
{
|
||||||
|
return $this->request->userAgent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* referer returns the referer URL
|
||||||
|
*/
|
||||||
|
public function referer(): ?string
|
||||||
|
{
|
||||||
|
return $this->request->referer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* isSecure checks if request is over HTTPS
|
||||||
|
*/
|
||||||
|
public function isSecure(): bool
|
||||||
|
{
|
||||||
|
return $this->request->isSecure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* url returns the full URL of the request
|
||||||
|
*/
|
||||||
|
public function url(): string
|
||||||
|
{
|
||||||
|
return $this->request->url();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fullUrl returns the URL with query string
|
||||||
|
*/
|
||||||
|
public function fullUrl(): string
|
||||||
|
{
|
||||||
|
return $this->request->fullUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is checks if the request path matches a pattern
|
||||||
|
*/
|
||||||
|
public function is(string $pattern): bool
|
||||||
|
{
|
||||||
|
return $this->request->is($pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* contentType returns the request content type without charset
|
||||||
|
*/
|
||||||
|
public function contentType(): string
|
||||||
|
{
|
||||||
|
return $this->request->contentType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* status sets the HTTP status code for the response
|
||||||
|
*/
|
||||||
|
public function status(int $code): Response
|
||||||
|
{
|
||||||
|
return $this->response->status($code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setHeader sets a header for the response
|
||||||
|
*/
|
||||||
|
public function setHeader(string $name, string $value): Response
|
||||||
|
{
|
||||||
|
return $this->response->header($name, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setCookie sets a cookie with the given name, value, and options
|
||||||
|
*/
|
||||||
|
public function setCookie(string $name, string $value, array $options = []): Response
|
||||||
|
{
|
||||||
|
return $this->response->cookie($name, $value, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write adds content to the response body
|
||||||
|
*/
|
||||||
|
public function write(string $content): Response
|
||||||
|
{
|
||||||
|
return $this->response->write($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* end ends the response with optional content
|
||||||
|
*/
|
||||||
|
public function end(string $content = ''): void
|
||||||
|
{
|
||||||
|
$this->response->end($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* send sends the response to the client
|
||||||
|
*/
|
||||||
|
public function send(): void
|
||||||
|
{
|
||||||
|
$this->response->send();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
722
EXAMPLES.md
Normal file
722
EXAMPLES.md
Normal file
@ -0,0 +1,722 @@
|
|||||||
|
# Web Framework Examples
|
||||||
|
|
||||||
|
This file contains practical examples demonstrating how to use the Web framework.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
require_once 'Web.php';
|
||||||
|
|
||||||
|
$app = new Web(debug: true);
|
||||||
|
|
||||||
|
$app->get('/', function(Context $context) {
|
||||||
|
return 'Hello World!';
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->get('/users/:id', function(Context $context, $id) {
|
||||||
|
return ['user_id' => $id, 'name' => 'John Doe'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->run();
|
||||||
|
?>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Web Application Examples
|
||||||
|
|
||||||
|
### Basic Setup
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Create application with debug mode
|
||||||
|
$app = new Web(debug: true);
|
||||||
|
|
||||||
|
// Add global middleware
|
||||||
|
$app->use(function(Context $context, callable $next) {
|
||||||
|
// Pre-processing
|
||||||
|
$next();
|
||||||
|
// Post-processing
|
||||||
|
});
|
||||||
|
|
||||||
|
// Define routes
|
||||||
|
$app->get('/path', $handler);
|
||||||
|
$app->post('/path', $handler);
|
||||||
|
$app->put('/path', $handler);
|
||||||
|
$app->patch('/path', $handler);
|
||||||
|
$app->delete('/path', $handler);
|
||||||
|
$app->head('/path', $handler);
|
||||||
|
|
||||||
|
// Route groups
|
||||||
|
$app->group('/api', function(Web $app) {
|
||||||
|
$app->get('/users', $usersHandler);
|
||||||
|
$app->post('/users', $createUserHandler);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Custom error handlers
|
||||||
|
$app->setErrorHandler(404, function(Context $context) {
|
||||||
|
$context->json(['error' => 'Custom 404'], 404);
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->setDefaultErrorHandler(function(Context $context, int $status, string $message, ?Exception $e) {
|
||||||
|
$context->json(['error' => $message, 'status' => $status], $status);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the application
|
||||||
|
$app->run();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Router Examples
|
||||||
|
|
||||||
|
```php
|
||||||
|
$router = new Router();
|
||||||
|
|
||||||
|
// Add routes with dynamic parameters
|
||||||
|
$router->get('/posts/:id', $handler);
|
||||||
|
$router->post('/users/:userId/posts/:postId', $handler);
|
||||||
|
|
||||||
|
// Perform lookup
|
||||||
|
$result = $router->lookup('GET', '/posts/123');
|
||||||
|
// Returns: ['code' => 200, 'handler' => $handler, 'params' => ['123']]
|
||||||
|
|
||||||
|
// Route patterns
|
||||||
|
'/users/:id' // matches /users/123, /users/abc
|
||||||
|
'/posts/:slug/edit' // matches /posts/my-post/edit
|
||||||
|
'/' // matches root path
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context Examples
|
||||||
|
|
||||||
|
### Request Helpers
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Get input from any source (route params, query, POST, JSON)
|
||||||
|
$name = $context->input('name', 'default');
|
||||||
|
$email = $context->input('email');
|
||||||
|
|
||||||
|
// Get specific parameter types
|
||||||
|
$id = $context->param('id'); // route/query/post params
|
||||||
|
$all = $context->all(); // all input data
|
||||||
|
$data = $context->only(['name', 'email']); // specific keys only
|
||||||
|
$data = $context->except(['password']); // all except specified keys
|
||||||
|
|
||||||
|
// Check if input exists
|
||||||
|
if ($context->has('email')) {
|
||||||
|
// handle email
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request headers and cookies
|
||||||
|
$auth = $context->header('authorization');
|
||||||
|
$token = $context->cookie('session_token');
|
||||||
|
|
||||||
|
// Request information
|
||||||
|
$ip = $context->ip();
|
||||||
|
$userAgent = $context->userAgent();
|
||||||
|
$referer = $context->referer();
|
||||||
|
$contentType = $context->contentType();
|
||||||
|
|
||||||
|
// Request checks
|
||||||
|
if ($context->expectsJson()) {
|
||||||
|
// return JSON response
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($context->isAjax()) {
|
||||||
|
// handle AJAX request
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($context->isSecure()) {
|
||||||
|
// HTTPS request
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL helpers
|
||||||
|
$url = $context->url(); // full URL
|
||||||
|
$fullUrl = $context->fullUrl(); // URL with query string
|
||||||
|
|
||||||
|
// Path matching
|
||||||
|
if ($context->is('api/*')) {
|
||||||
|
// matches API routes
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Response Helpers
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Set status and headers
|
||||||
|
$context->status(201);
|
||||||
|
$context->setHeader('X-Custom', 'value');
|
||||||
|
$context->setCookie('name', 'value', ['expires' => time() + 3600]);
|
||||||
|
|
||||||
|
// Response content
|
||||||
|
$context->json(['message' => 'Success']); // JSON response
|
||||||
|
$context->text('Hello World'); // Plain text
|
||||||
|
$context->html('<h1>Hello</h1>'); // HTML
|
||||||
|
$context->redirect('/login'); // Redirect
|
||||||
|
$context->error(404, 'Not Found'); // Error response
|
||||||
|
|
||||||
|
// Manual response building
|
||||||
|
$context->write('Some content');
|
||||||
|
$context->write(' more content');
|
||||||
|
$context->send(); // Send response
|
||||||
|
|
||||||
|
// Or end with content
|
||||||
|
$context->end('Final content');
|
||||||
|
```
|
||||||
|
|
||||||
|
### State Management
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Store and retrieve context state
|
||||||
|
$context->set('user', $user);
|
||||||
|
$user = $context->get('user');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validation
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Validate request data
|
||||||
|
$validator = $context->validateRequest([
|
||||||
|
'email' => 'required|email',
|
||||||
|
'name' => 'required|string|min:2'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Validate any data
|
||||||
|
$validator = $context->validate($data, [
|
||||||
|
'field' => 'required|string'
|
||||||
|
], ['field.required' => 'Custom error message']);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Request Examples
|
||||||
|
|
||||||
|
```php
|
||||||
|
$request = new Request();
|
||||||
|
|
||||||
|
// Basic properties
|
||||||
|
$request->method; // GET, POST, etc.
|
||||||
|
$request->uri; // /path?query=value
|
||||||
|
$request->path; // /path
|
||||||
|
$request->query; // query=value
|
||||||
|
$request->body; // raw request body
|
||||||
|
|
||||||
|
// Parsed data
|
||||||
|
$request->queryParams; // parsed query parameters
|
||||||
|
$request->postData; // parsed POST data
|
||||||
|
$request->params; // route parameters (set by router)
|
||||||
|
|
||||||
|
// Input methods
|
||||||
|
$value = $request->input('key', 'default'); // from any source
|
||||||
|
$value = $request->param('key'); // route/query/post only
|
||||||
|
$all = $request->all(); // all input merged
|
||||||
|
$subset = $request->only(['key1', 'key2']);
|
||||||
|
$subset = $request->except(['password']);
|
||||||
|
$exists = $request->has('key');
|
||||||
|
|
||||||
|
// Headers and cookies
|
||||||
|
$auth = $request->header('authorization');
|
||||||
|
$type = $request->contentType();
|
||||||
|
$cookie = $request->cookie('session');
|
||||||
|
|
||||||
|
// JSON handling
|
||||||
|
$data = $request->json(); // decode JSON body
|
||||||
|
|
||||||
|
// Request info
|
||||||
|
$ip = $request->ip();
|
||||||
|
$agent = $request->userAgent();
|
||||||
|
$referer = $request->referer();
|
||||||
|
$secure = $request->isSecure();
|
||||||
|
$ajax = $request->isAjax();
|
||||||
|
$expectsJson = $request->expectsJson();
|
||||||
|
|
||||||
|
// URL info
|
||||||
|
$url = $request->url();
|
||||||
|
$fullUrl = $request->fullUrl();
|
||||||
|
$matches = $request->is('admin/*'); // path matching
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response Examples
|
||||||
|
|
||||||
|
```php
|
||||||
|
$response = new Response();
|
||||||
|
|
||||||
|
// Set status and headers
|
||||||
|
$response->status(201)
|
||||||
|
->header('Content-Type', 'application/json')
|
||||||
|
->header('X-Custom', 'value');
|
||||||
|
|
||||||
|
// Content methods
|
||||||
|
$response->json($data, 201); // JSON with status
|
||||||
|
$response->text('Hello', 200); // Plain text
|
||||||
|
$response->html('<h1>Hi</h1>', 200); // HTML
|
||||||
|
$response->redirect('/login', 302); // Redirect
|
||||||
|
|
||||||
|
// Cookies
|
||||||
|
$response->cookie('name', 'value', [
|
||||||
|
'expires' => time() + 3600,
|
||||||
|
'path' => '/',
|
||||||
|
'secure' => true,
|
||||||
|
'httponly' => true,
|
||||||
|
'samesite' => 'Strict'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Manual building
|
||||||
|
$response->write('Part 1');
|
||||||
|
$response->write('Part 2');
|
||||||
|
$response->send(); // Send to client
|
||||||
|
|
||||||
|
// Or end immediately
|
||||||
|
$response->end('Final content');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Session Examples
|
||||||
|
|
||||||
|
```php
|
||||||
|
$session = new Session();
|
||||||
|
$session->start();
|
||||||
|
|
||||||
|
// Basic session data
|
||||||
|
$session->set('key', 'value');
|
||||||
|
$value = $session->get('key', 'default');
|
||||||
|
$exists = $session->has('key');
|
||||||
|
$session->remove('key');
|
||||||
|
|
||||||
|
// Flash messages (available only for next request)
|
||||||
|
$session->flash('success', 'Data saved!');
|
||||||
|
$message = $session->getFlash('success');
|
||||||
|
$hasMessage = $session->hasFlash('error');
|
||||||
|
|
||||||
|
// CSRF protection
|
||||||
|
$token = $session->csrfToken();
|
||||||
|
$valid = $session->validateCsrf($userToken);
|
||||||
|
|
||||||
|
// Session management
|
||||||
|
$session->regenerate(); // Regenerate session ID
|
||||||
|
$session->destroy(); // Destroy session completely
|
||||||
|
$session->clear(); // Clear data but keep session active
|
||||||
|
$all = $session->all(); // Get all session data
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation Examples
|
||||||
|
|
||||||
|
```php
|
||||||
|
$validator = new Validator();
|
||||||
|
|
||||||
|
// Validate data
|
||||||
|
$isValid = $validator->validate($data, [
|
||||||
|
'email' => 'required|email',
|
||||||
|
'password' => 'required|min:8',
|
||||||
|
'age' => 'integer|between:18,120',
|
||||||
|
'tags' => 'array',
|
||||||
|
'website' => 'url',
|
||||||
|
'role' => 'in:admin,user,moderator'
|
||||||
|
], [
|
||||||
|
'email.required' => 'Email is mandatory',
|
||||||
|
'password.min' => 'Password must be at least 8 characters'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Check results
|
||||||
|
if ($validator->failed()) {
|
||||||
|
$errors = $validator->errors();
|
||||||
|
$firstError = $validator->firstError();
|
||||||
|
$fieldError = $validator->firstError('email');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom validation rules
|
||||||
|
Validator::extend('custom', function($value, $parameters, $data) {
|
||||||
|
return $value === 'custom_value';
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authentication Examples
|
||||||
|
|
||||||
|
```php
|
||||||
|
$auth = new Auth($session, [
|
||||||
|
'cookie_name' => 'remember_token',
|
||||||
|
'cookie_lifetime' => 2592000, // 30 days
|
||||||
|
'cookie_secure' => true
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Authentication
|
||||||
|
$auth->login($userData, $remember = false);
|
||||||
|
$auth->loginUser($userObject, $remember = false);
|
||||||
|
$auth->logout();
|
||||||
|
|
||||||
|
// User checks
|
||||||
|
$isAuthenticated = $auth->check();
|
||||||
|
$isGuest = $auth->guest();
|
||||||
|
$user = $auth->user();
|
||||||
|
$userId = $auth->id();
|
||||||
|
|
||||||
|
// Password handling
|
||||||
|
$hash = $auth->hashPassword('password');
|
||||||
|
$valid = $auth->verifyPassword('password', $hash);
|
||||||
|
|
||||||
|
// Manual user data management
|
||||||
|
$auth->setUserData($userData);
|
||||||
|
$auth->clear(); // Clear all auth data
|
||||||
|
```
|
||||||
|
|
||||||
|
## User Model Examples
|
||||||
|
|
||||||
|
```php
|
||||||
|
$user = new User([
|
||||||
|
'id' => 1,
|
||||||
|
'username' => 'john',
|
||||||
|
'email' => 'john@example.com',
|
||||||
|
'role' => 'admin'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
$user->id;
|
||||||
|
$user->username;
|
||||||
|
$user->email;
|
||||||
|
$user->role;
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
$identifier = $user->getIdentifier(); // email or username
|
||||||
|
$id = $user->getId();
|
||||||
|
$array = $user->toArray(); // with all data
|
||||||
|
$safe = $user->toSafeArray(); // without sensitive data
|
||||||
|
```
|
||||||
|
|
||||||
|
## Auth Middleware Examples
|
||||||
|
|
||||||
|
```php
|
||||||
|
$authMiddleware = new AuthMiddleware($auth);
|
||||||
|
|
||||||
|
// Require authentication
|
||||||
|
$app->use($authMiddleware->requireAuth());
|
||||||
|
|
||||||
|
// Require guest (not authenticated)
|
||||||
|
$app->use($authMiddleware->requireGuest());
|
||||||
|
|
||||||
|
// Optional authentication
|
||||||
|
$app->use($authMiddleware->optional());
|
||||||
|
|
||||||
|
// Role-based access
|
||||||
|
$app->use($authMiddleware->requireRole('admin'));
|
||||||
|
$app->use($authMiddleware->requireRole(['admin', 'moderator']));
|
||||||
|
|
||||||
|
// Rate limiting
|
||||||
|
$app->use($authMiddleware->rateLimit(maxAttempts: 60, decayMinutes: 1));
|
||||||
|
|
||||||
|
// CSRF protection
|
||||||
|
$app->use($authMiddleware->verifyCsrf());
|
||||||
|
|
||||||
|
// Remember token handling
|
||||||
|
$app->use($authMiddleware->remember());
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling Examples
|
||||||
|
|
||||||
|
```php
|
||||||
|
$errorHandler = new ErrorHandler(debug: true);
|
||||||
|
|
||||||
|
// Register custom error handlers
|
||||||
|
$errorHandler->register(404, function(Context $context, int $status, string $message) {
|
||||||
|
$context->json(['error' => 'Custom not found'], 404);
|
||||||
|
});
|
||||||
|
|
||||||
|
$errorHandler->setDefaultHandler(function(Context $context, int $status, string $message, ?Exception $e) {
|
||||||
|
$context->json(['error' => $message, 'code' => $status], $status);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle errors manually
|
||||||
|
$errorHandler->handle($context, 500, 'Server Error', $exception);
|
||||||
|
$errorHandler->handleException($context, $exception);
|
||||||
|
|
||||||
|
// Custom exceptions
|
||||||
|
throw new HttpException(422, 'Unprocessable Entity');
|
||||||
|
throw new ValidationException($validator->errors(), 'Validation failed');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Middleware Examples
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Basic middleware
|
||||||
|
$app->use(function(Context $context, callable $next) {
|
||||||
|
// Pre-processing
|
||||||
|
$start = microtime(true);
|
||||||
|
|
||||||
|
$next(); // Call next middleware/handler
|
||||||
|
|
||||||
|
// Post-processing
|
||||||
|
$duration = microtime(true) - $start;
|
||||||
|
$context->setHeader('X-Response-Time', $duration . 'ms');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Conditional middleware
|
||||||
|
$app->use(function(Context $context, callable $next) {
|
||||||
|
if ($context->is('api/*')) {
|
||||||
|
$context->setHeader('Content-Type', 'application/json');
|
||||||
|
}
|
||||||
|
$next();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Logging middleware
|
||||||
|
$app->use(function(Context $context, callable $next) {
|
||||||
|
$method = $context->request->method;
|
||||||
|
$path = $context->request->path;
|
||||||
|
$ip = $context->ip();
|
||||||
|
|
||||||
|
error_log("[$method] $path from $ip");
|
||||||
|
$next();
|
||||||
|
});
|
||||||
|
|
||||||
|
// CORS middleware
|
||||||
|
$app->use(function(Context $context, callable $next) {
|
||||||
|
$context->setHeader('Access-Control-Allow-Origin', '*');
|
||||||
|
$context->setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||||
|
$context->setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||||
|
|
||||||
|
if ($context->request->method === 'OPTIONS') {
|
||||||
|
$context->status(200)->send();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$next();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Route Handler Examples
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Simple handler
|
||||||
|
$app->get('/hello', function(Context $context) {
|
||||||
|
return 'Hello World!';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handler with parameters
|
||||||
|
$app->get('/users/:id/posts/:slug', function(Context $context, $userId, $slug) {
|
||||||
|
return [
|
||||||
|
'user_id' => $userId,
|
||||||
|
'post_slug' => $slug,
|
||||||
|
'data' => $context->all()
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handler return types
|
||||||
|
$app->get('/json', fn($context) => ['key' => 'value']); // Auto JSON
|
||||||
|
$app->get('/text', fn($context) => 'Plain text'); // Auto text
|
||||||
|
$app->get('/response', fn($context) => $context->json([])); // Manual response
|
||||||
|
|
||||||
|
// Complex handler with validation
|
||||||
|
$app->post('/users', function(Context $context) {
|
||||||
|
$context->validateRequest([
|
||||||
|
'name' => 'required|string|min:2',
|
||||||
|
'email' => 'required|email',
|
||||||
|
'age' => 'integer|min:18'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$userData = $context->only(['name', 'email', 'age']);
|
||||||
|
|
||||||
|
// Save user logic here
|
||||||
|
$userId = saveUser($userData);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'message' => 'User created successfully',
|
||||||
|
'user_id' => $userId
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
// File upload handler
|
||||||
|
$app->post('/upload', function(Context $context) {
|
||||||
|
if (!isset($_FILES['file'])) {
|
||||||
|
$context->error(400, 'No file uploaded');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $_FILES['file'];
|
||||||
|
|
||||||
|
if ($file['error'] !== UPLOAD_ERR_OK) {
|
||||||
|
$context->error(400, 'Upload failed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle file upload logic
|
||||||
|
$filename = handleFileUpload($file);
|
||||||
|
|
||||||
|
return ['filename' => $filename];
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complete Application Example
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
require_once 'Web.php';
|
||||||
|
require_once 'auth/Auth.php';
|
||||||
|
require_once 'auth/AuthMiddleware.php';
|
||||||
|
|
||||||
|
$app = new Web(debug: true);
|
||||||
|
|
||||||
|
// Setup auth
|
||||||
|
$auth = new Auth($app->context->session);
|
||||||
|
$authMiddleware = new AuthMiddleware($auth);
|
||||||
|
|
||||||
|
// Global middleware
|
||||||
|
$app->use(function(Context $context, callable $next) {
|
||||||
|
$context->setHeader('X-Framework', 'Web');
|
||||||
|
$next();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auth routes
|
||||||
|
$app->post('/login', function(Context $context) use ($auth) {
|
||||||
|
$context->validateRequest([
|
||||||
|
'email' => 'required|email',
|
||||||
|
'password' => 'required|min:6'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$email = $context->input('email');
|
||||||
|
$password = $context->input('password');
|
||||||
|
|
||||||
|
// Verify credentials externally, then login
|
||||||
|
if (verifyCredentials($email, $password)) {
|
||||||
|
$userData = getUserData($email);
|
||||||
|
$auth->login($userData, $context->input('remember'));
|
||||||
|
return ['message' => 'Logged in successfully'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$context->error(401, 'Invalid credentials');
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->post('/logout', function(Context $context) use ($auth) {
|
||||||
|
$auth->logout();
|
||||||
|
return ['message' => 'Logged out'];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Protected routes
|
||||||
|
$app->group('/api', function(Web $app) use ($authMiddleware) {
|
||||||
|
$app->use($authMiddleware->requireAuth());
|
||||||
|
$app->use($authMiddleware->rateLimit(100, 1));
|
||||||
|
|
||||||
|
$app->get('/profile', function(Context $context) {
|
||||||
|
return $context->get('user')->toSafeArray();
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->put('/profile', function(Context $context) {
|
||||||
|
$context->validateRequest([
|
||||||
|
'name' => 'required|string|min:2',
|
||||||
|
'email' => 'required|email'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Update profile logic here
|
||||||
|
return ['message' => 'Profile updated'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->get('/posts', function(Context $context) {
|
||||||
|
$page = $context->input('page', 1);
|
||||||
|
$limit = $context->input('limit', 10);
|
||||||
|
|
||||||
|
// Get posts with pagination
|
||||||
|
$posts = getPosts($page, $limit);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'posts' => $posts,
|
||||||
|
'pagination' => [
|
||||||
|
'page' => $page,
|
||||||
|
'limit' => $limit
|
||||||
|
]
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->post('/posts', function(Context $context) {
|
||||||
|
$context->validateRequest([
|
||||||
|
'title' => 'required|string|min:5',
|
||||||
|
'content' => 'required|string|min:10',
|
||||||
|
'tags' => 'array'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$postData = $context->only(['title', 'content', 'tags']);
|
||||||
|
$postData['user_id'] = $context->get('user')->id;
|
||||||
|
|
||||||
|
$postId = createPost($postData);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'message' => 'Post created successfully',
|
||||||
|
'post_id' => $postId
|
||||||
|
];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Admin routes
|
||||||
|
$app->group('/admin', function(Web $app) use ($authMiddleware) {
|
||||||
|
$app->use($authMiddleware->requireRole('admin'));
|
||||||
|
|
||||||
|
$app->get('/users', function(Context $context) {
|
||||||
|
return ['users' => getAllUsers()];
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->delete('/users/:id', function(Context $context, $userId) {
|
||||||
|
deleteUser($userId);
|
||||||
|
return ['message' => 'User deleted successfully'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->get('/stats', function(Context $context) {
|
||||||
|
return [
|
||||||
|
'total_users' => getUserCount(),
|
||||||
|
'total_posts' => getPostCount(),
|
||||||
|
'active_sessions' => getActiveSessionCount()
|
||||||
|
];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Public API routes
|
||||||
|
$app->group('/public', function(Web $app) {
|
||||||
|
$app->get('/posts', function(Context $context) {
|
||||||
|
$posts = getPublicPosts();
|
||||||
|
return ['posts' => $posts];
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->get('/posts/:id', function(Context $context, $postId) {
|
||||||
|
$post = getPublicPost($postId);
|
||||||
|
if (!$post) {
|
||||||
|
$context->error(404, 'Post not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return ['post' => $post];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Error handlers
|
||||||
|
$app->setErrorHandler(404, function(Context $context) {
|
||||||
|
if ($context->expectsJson()) {
|
||||||
|
$context->json(['error' => 'Endpoint not found'], 404);
|
||||||
|
} else {
|
||||||
|
$context->html('<h1>Page Not Found</h1>', 404);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->setErrorHandler(429, function(Context $context) {
|
||||||
|
$context->json([
|
||||||
|
'error' => 'Rate limit exceeded',
|
||||||
|
'message' => 'Please slow down your requests'
|
||||||
|
], 429);
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->run();
|
||||||
|
?>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Examples
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Simple test setup
|
||||||
|
function testRoute($method, $path, $data = []) {
|
||||||
|
$_SERVER['REQUEST_METHOD'] = $method;
|
||||||
|
$_SERVER['REQUEST_URI'] = $path;
|
||||||
|
|
||||||
|
if ($method === 'POST' && $data) {
|
||||||
|
$_POST = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$app = new Web();
|
||||||
|
// Setup your routes...
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
$app->run();
|
||||||
|
$output = ob_get_clean();
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test examples
|
||||||
|
$response = testRoute('GET', '/api/users');
|
||||||
|
$response = testRoute('POST', '/api/users', ['name' => 'John', 'email' => 'john@example.com']);
|
||||||
|
```
|
||||||
200
README.md
200
README.md
@ -1,3 +1,199 @@
|
|||||||
# Web
|
# Web Framework
|
||||||
|
|
||||||
Web is a crazy-simple PHP web framework, built to be simple and performant. It uses my hyper-optimized tree-based router at its core, adding only minimal overhead for convenience and flexibility. The Web framework itself adds middleware support, grouping, etc.
|
Web is a lightweight, high-performance PHP web framework built around a hyper-optimized tree-based router. It provides essential web application features with minimal overhead, including routing, middleware, sessions, validation, authentication, and error handling.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
require_once 'Web.php';
|
||||||
|
|
||||||
|
$app = new Web(debug: true);
|
||||||
|
|
||||||
|
$app->get('/', function(Context $context) {
|
||||||
|
return 'Hello World!';
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->get('/users/:id', function(Context $context, $id) {
|
||||||
|
return ['user_id' => $id, 'name' => 'John Doe'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->run();
|
||||||
|
?>
|
||||||
|
```
|
||||||
|
|
||||||
|
See [EXAMPLES.md](EXAMPLES.md) for comprehensive examples and usage patterns.
|
||||||
|
|
||||||
|
## Core Components
|
||||||
|
|
||||||
|
### Web Application
|
||||||
|
The main application class that handles routing, middleware, and request processing.
|
||||||
|
- Route registration (`get()`, `post()`, `put()`, `patch()`, `delete()`, `head()`)
|
||||||
|
- Middleware support (`use()`)
|
||||||
|
- Route grouping (`group()`)
|
||||||
|
- Custom error handling (`setErrorHandler()`, `setDefaultErrorHandler()`)
|
||||||
|
|
||||||
|
### Router
|
||||||
|
High-performance tree-based router with dynamic parameter support.
|
||||||
|
- Optimized route lookup algorithm
|
||||||
|
- Dynamic segments with `:parameter` syntax
|
||||||
|
- Returns status codes (200, 404, 405) with matched handlers and parameters
|
||||||
|
|
||||||
|
### Context
|
||||||
|
Central request/response wrapper with helper methods to reduce chaining.
|
||||||
|
- Request helpers: `input()`, `param()`, `all()`, `only()`, `except()`, `has()`
|
||||||
|
- Response helpers: `json()`, `text()`, `html()`, `redirect()`, `error()`
|
||||||
|
- Request info: `ip()`, `userAgent()`, `header()`, `cookie()`, `expectsJson()`
|
||||||
|
- State management: `set()`, `get()`
|
||||||
|
- Validation: `validate()`, `validateRequest()`
|
||||||
|
|
||||||
|
### Request
|
||||||
|
Comprehensive HTTP request handling.
|
||||||
|
- Input parsing from query, POST, JSON, and route parameters
|
||||||
|
- Header and cookie access
|
||||||
|
- Content type detection
|
||||||
|
- URL and security helpers
|
||||||
|
|
||||||
|
### Response
|
||||||
|
HTTP response building with method chaining.
|
||||||
|
- Status codes and headers
|
||||||
|
- Content types: JSON, HTML, text, redirects
|
||||||
|
- Cookie management
|
||||||
|
- Manual response building
|
||||||
|
|
||||||
|
### Session
|
||||||
|
Session management with security features.
|
||||||
|
- Basic session data storage
|
||||||
|
- Flash messages for one-time notifications
|
||||||
|
- CSRF token generation and validation
|
||||||
|
- Session lifecycle management
|
||||||
|
|
||||||
|
### Validator
|
||||||
|
Input validation with 20+ built-in rules.
|
||||||
|
- Chainable validation rules (`required|email|min:6`)
|
||||||
|
- Custom error messages
|
||||||
|
- Extensible with custom rules
|
||||||
|
- Dot notation support for nested data
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
User authentication with session and remember token support.
|
||||||
|
- Login/logout functionality
|
||||||
|
- Password hashing and verification
|
||||||
|
- Remember token management
|
||||||
|
- External credential verification support
|
||||||
|
|
||||||
|
### Auth Middleware
|
||||||
|
Authentication and authorization middleware.
|
||||||
|
- Authentication requirements (`requireAuth()`, `requireGuest()`)
|
||||||
|
- Role-based access control (`requireRole()`)
|
||||||
|
- Rate limiting (`rateLimit()`)
|
||||||
|
- CSRF protection (`verifyCsrf()`)
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
Comprehensive error and exception handling.
|
||||||
|
- Custom error handlers for specific status codes
|
||||||
|
- Debug mode with stack traces
|
||||||
|
- Automatic JSON/HTML response formatting
|
||||||
|
- Built-in error pages
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### Web Class
|
||||||
|
```php
|
||||||
|
new Web(bool $debug = false)
|
||||||
|
use(callable $middleware): self
|
||||||
|
get|post|put|patch|delete|head(string $route, callable $handler): self
|
||||||
|
group(string $prefix, callable $callback): self
|
||||||
|
setErrorHandler(int $status, callable $handler): self
|
||||||
|
setDefaultErrorHandler(callable $handler): self
|
||||||
|
run(): void
|
||||||
|
```
|
||||||
|
|
||||||
|
### Context Class
|
||||||
|
```php
|
||||||
|
// Request helpers
|
||||||
|
input(string $name, mixed $default = null): mixed
|
||||||
|
param(string $name): mixed
|
||||||
|
all(): array
|
||||||
|
only(array $keys): array
|
||||||
|
except(array $keys): array
|
||||||
|
has(string $key): bool
|
||||||
|
header(string $name): ?string
|
||||||
|
cookie(string $name): ?string
|
||||||
|
ip(): string
|
||||||
|
userAgent(): string
|
||||||
|
expectsJson(): bool
|
||||||
|
isAjax(): bool
|
||||||
|
isSecure(): bool
|
||||||
|
|
||||||
|
// Response helpers
|
||||||
|
json(mixed $data, int $status = 200): void
|
||||||
|
text(string $text, int $status = 200): void
|
||||||
|
html(string $html, int $status = 200): void
|
||||||
|
redirect(string $url, int $status = 302): void
|
||||||
|
error(int $status, string $message = ''): void
|
||||||
|
status(int $code): Response
|
||||||
|
setHeader(string $name, string $value): Response
|
||||||
|
setCookie(string $name, string $value, array $options = []): Response
|
||||||
|
|
||||||
|
// State management
|
||||||
|
set(string $key, mixed $value): void
|
||||||
|
get(string $key): mixed
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
validate(array $data, array $rules, array $messages = []): Validator
|
||||||
|
validateRequest(array $rules, array $messages = []): Validator
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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`
|
||||||
|
|
||||||
|
### Auth Class
|
||||||
|
```php
|
||||||
|
new Auth(Session $session, array $config = [])
|
||||||
|
login(array $userData, bool $remember = false): void
|
||||||
|
logout(): void
|
||||||
|
check(): bool
|
||||||
|
guest(): bool
|
||||||
|
user(): ?User
|
||||||
|
id(): int|string|null
|
||||||
|
hashPassword(string $password): string
|
||||||
|
verifyPassword(string $password, string $hash): bool
|
||||||
|
```
|
||||||
|
|
||||||
|
### AuthMiddleware Methods
|
||||||
|
```php
|
||||||
|
requireAuth(): callable
|
||||||
|
requireGuest(): callable
|
||||||
|
optional(): callable
|
||||||
|
requireRole(string|array $roles): callable
|
||||||
|
rateLimit(int $maxAttempts = 60, int $decayMinutes = 1): callable
|
||||||
|
verifyCsrf(): callable
|
||||||
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **High Performance**: Tree-based router with O(1) lookup complexity
|
||||||
|
- **Minimal Overhead**: Lightweight design with essential features only
|
||||||
|
- **Method Chaining**: Fluent API for response building
|
||||||
|
- **Middleware Support**: Pre/post request processing
|
||||||
|
- **Route Groups**: Organize routes with shared prefixes and middleware
|
||||||
|
- **Input Validation**: Comprehensive validation with built-in rules
|
||||||
|
- **Authentication**: Session-based auth with remember tokens
|
||||||
|
- **Error Handling**: Custom error pages with debug support
|
||||||
|
- **CSRF Protection**: Built-in CSRF token generation and validation
|
||||||
|
- **Flash Messages**: One-time notifications between requests
|
||||||
|
- **Rate Limiting**: Configurable rate limiting per user/IP
|
||||||
|
- **Content Negotiation**: Automatic JSON/HTML response selection
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- **[EXAMPLES.md](EXAMPLES.md)** - Comprehensive examples and usage patterns
|
||||||
|
- **API Reference** - Complete method documentation (above)
|
||||||
|
- **Source Code** - Well-documented PHP classes
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- PHP 8.1+ (uses enums and modern syntax)
|
||||||
|
- No external dependencies
|
||||||
|
- Works with any PHP web server
|
||||||
Loading…
x
Reference in New Issue
Block a user