2
0
Web/EXAMPLES.md

22 KiB

Web Framework Examples

This file contains practical examples demonstrating how to use the Web framework.

Quick Start

<?php

namespace Web;

require_once 'App.php';

$app = new App(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();
?>

App (Application) Examples

Basic Setup

// Create application with debug mode
$app = new App(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(App $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

$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

// Get input from specific sources - explicit and predictable
$name = $context->input('name', 'default');		// POST data only
$search = $context->query('search', '');		   // Query params only
$data = $context->jsonValue('data', []);		   // JSON body only
$id = $context->param(0, 0);					   // First route parameter

// Examples with route parameters by index
// Route: /users/:userId/posts/:postId
// URL: /users/123/posts/456

$userId = $context->param(0);			   // "123" (first parameter)
$postId = $context->param(1);			   // "456" (second parameter)

// Examples with conflicting parameter names in different sources
// URL: /users/123?name=query_name
// POST: name=post_name
// JSON: {"name": "json_name"}

$routeId = $context->param(0);			  // "123" from route (first param)
$queryName = $context->query('name');	   // "query_name" from URL
$postName = $context->input('name');		// "post_name" from form
$jsonName = $context->jsonValue('name');	// "json_name" from JSON

// Get all input data merged from all sources (route params override all)
$all = $context->all(); 
$data = $context->only(['name', 'email']); // specific keys only
$data = $context->except(['password']); // all except specified keys

// Check if input exists (checks all sources)
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

// 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

// Store and retrieve context state
$context->set('user', $user);
$user = $context->get('user');

Validation

// 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

$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');		// POST data only
$value = $request->query('key', 'default');		// Query params only
$value = $request->jsonValue('key', 'default');	// JSON body only
$value = $request->param(0, 'default');			// Route params by index
$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

$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

$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

Cookies Examples

// 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 App(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

$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

// Auth automatically uses Cookies for remember tokens
$auth = new Auth($session, $context->cookie, [
	'cookie_name' => 'remember_token',
	'cookie_lifetime' => 2592000 // 30 days
]);

// 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

$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

$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

$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

// 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

// 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) {
	// Parameters are passed as function arguments in order
	// Or access via index: $userId = $context->param(0), $slug = $context->param(1)
	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

namespace Web;

require_once 'App.php';
require_once 'auth/Auth.php';
require_once 'auth/AuthMiddleware.php';

$app = new App(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(App $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(App $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(App $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

// Simple test setup
function testRoute($method, $path, $data = []) {
	$_SERVER['REQUEST_METHOD'] = $method;
	$_SERVER['REQUEST_URI'] = $path;
	
	if ($method === 'POST' && $data) {
		$_POST = $data;
	}
	
	$app = new App();
	// 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']);