mirror of
https://github.com/WilliamAAK/Route2.git
synced 2025-04-19 12:37:18 +00:00
Hello World
This commit is contained in:
commit
f9162a027f
13
LICENSE
Normal file
13
LICENSE
Normal file
@ -0,0 +1,13 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Whomst Ever
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
107
README.md
Normal file
107
README.md
Normal file
@ -0,0 +1,107 @@
|
||||
# Route
|
||||
|
||||
A lightweight routing system for handling HTTP requests.
|
||||
|
||||
### Features:
|
||||
- Define routes for specific HTTP methods (GET, POST, PUT, DELETE, etc.).
|
||||
- Apply middleware to routes or groups of routes.
|
||||
- Group routes under a common prefix for better organization.
|
||||
- Support for route parameters (e.g., `/user/$id`) and optional parameters (e.g., `/user/$id?`).
|
||||
|
||||
### Example:
|
||||
```php
|
||||
// Define a GET route
|
||||
Route::get('/home', function () {
|
||||
echo "Welcome to the homepage!";
|
||||
});
|
||||
|
||||
// Define a middleware
|
||||
Route::before(function () {
|
||||
echo "Middleware executed before the route callback.";
|
||||
});
|
||||
|
||||
// Define a POST route with middleware
|
||||
Route::post('/submit', function () {
|
||||
echo "Form submitted!";
|
||||
}, function () {
|
||||
echo "Middleware executed before the callback.";
|
||||
});
|
||||
|
||||
// Group routes under a common prefix
|
||||
Route::group('/admin', function () {
|
||||
// Define a middleware for the group; it will not affect the outer routes
|
||||
Route::before(function () {
|
||||
echo "Admin Middleware executed.";
|
||||
});
|
||||
Route::get('/dashboard', function () {
|
||||
echo "Admin Dashboard";
|
||||
});
|
||||
Route::post('/settings', function () {
|
||||
echo "Admin Settings";
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
# Installation
|
||||
|
||||
## Composer
|
||||
|
||||
Composer needs to know where to locate the package. Add the repository to your project by declaring it in the `composer.json` file.
|
||||
```JSON
|
||||
{
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/WilliamAAK/Route"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"willysoft/route": "dev-master"
|
||||
}
|
||||
}
|
||||
```
|
||||
Fetch the package by running
|
||||
|
||||
composer install
|
||||
|
||||
## Manual
|
||||
|
||||
Its all in a single file; include it in your project like so
|
||||
|
||||
```PHP
|
||||
require '/YourPath/Route.php'
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
```PHP
|
||||
use WilliamAAK\Http\Route;
|
||||
|
||||
Route::get('/', fn() => echo 'hello world');
|
||||
```
|
||||
|
||||
See `Route.php` and read the comments for more information
|
||||
|
||||
## Access directly
|
||||
|
||||
The simplest way to access your routes. Just put the file in your favorite folder and run it!
|
||||
|
||||
For example
|
||||
|
||||
`http://your.site/folder/index.php/your/route`
|
||||
|
||||
## Rewrite requests
|
||||
|
||||
The more sexy way of doing it.
|
||||
|
||||
For example
|
||||
|
||||
`http://your.site/your/route`
|
||||
|
||||
### NGINX
|
||||
|
||||
With PHP already configured, add this to the server block of your configuration to make requests that don't match a file on your server to be sent to your front controller and begin routing.
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
352
Route.php
Normal file
352
Route.php
Normal file
@ -0,0 +1,352 @@
|
||||
<?php
|
||||
|
||||
namespace WilliamAAK\Http;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* A lightweight routing system for handling HTTP requests.
|
||||
*
|
||||
* ### Features:
|
||||
* - Define routes for specific HTTP methods (GET, POST, PUT, DELETE, etc.).
|
||||
* - Apply middleware to routes or groups of routes.
|
||||
* - Group routes under a common prefix for better organization.
|
||||
* - Support for route parameters (e.g., `/user/$id`) and optional parameters (e.g., `/user/$id?`).
|
||||
*
|
||||
* ### Example:
|
||||
* ```php
|
||||
* // Define a GET route
|
||||
* Route::get('/home', function () {
|
||||
* echo "Welcome to the homepage!";
|
||||
* });
|
||||
*
|
||||
* // Define a middleware
|
||||
* Route::before(function () {
|
||||
* echo "Middleware executed before the route callback.";
|
||||
* });
|
||||
*
|
||||
* // Define a POST route with middleware
|
||||
* Route::post('/submit', function () {
|
||||
* echo "Form submitted!";
|
||||
* }, function () {
|
||||
* echo "Middleware executed before the callback.";
|
||||
* });
|
||||
*
|
||||
* // Group routes under a common prefix
|
||||
* Route::group('/admin', function () {
|
||||
* // Define a middleware for the group; it will not affect the outer routes
|
||||
* Route::before(function () {
|
||||
* echo "Admin Middleware executed.";
|
||||
* });
|
||||
* Route::get('/dashboard', function () {
|
||||
* echo "Admin Dashboard";
|
||||
* });
|
||||
* Route::post('/settings', function () {
|
||||
* echo "Admin Settings";
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @package WilliamAAK\Http
|
||||
* @version 1.0.0
|
||||
* @author WilliamAAK
|
||||
*/
|
||||
class Route
|
||||
{
|
||||
static string $pathPrefix = '';
|
||||
static array $before = [ [ ] ];
|
||||
static array $after = [ [ ] ];
|
||||
|
||||
/**
|
||||
* Retrieves the relative path of the current HTTP request.
|
||||
*
|
||||
* ### Examples:
|
||||
* 1. If the request URI is `/index.php/myprofile/sessions` and the script name is `/index.php`,
|
||||
* this method will return:
|
||||
* `/myprofile/sessions`
|
||||
*
|
||||
* 2. If the request URI is `/myprofile/sessions` and rewrite rules are used to hide `/index.php`,
|
||||
* this method will return:
|
||||
* `/myprofile/sessions`
|
||||
*
|
||||
* @return string The relative request path.
|
||||
*/
|
||||
static function getRelativeRequestPath(): string
|
||||
{
|
||||
static $requestUrlPath;
|
||||
|
||||
if (isset($requestUrlPath)) {
|
||||
return $requestUrlPath;
|
||||
}
|
||||
|
||||
$requestUrlPath = urldecode(strtok($_SERVER['REQUEST_URI'], '?'));
|
||||
|
||||
$scriptName = $_SERVER['SCRIPT_NAME'];
|
||||
$scriptDir = dirname($scriptName) . '/';
|
||||
|
||||
if (str_starts_with($requestUrlPath, $scriptName)) {
|
||||
$requestUrlPath = substr($requestUrlPath, strlen($scriptName));
|
||||
}
|
||||
|
||||
if ($requestUrlPath === $scriptDir) {
|
||||
$requestUrlPath = '/';
|
||||
}
|
||||
|
||||
if ($requestUrlPath === '') {
|
||||
$requestUrlPath = '/';
|
||||
}
|
||||
|
||||
return $requestUrlPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches an incoming HTTP request to a defined route and executes the callback.
|
||||
*
|
||||
* ### Examples:
|
||||
* 1. Matching a route with parameters:
|
||||
* ```php
|
||||
* Route::match('GET', '/user/$id', function ($id) {
|
||||
* echo "User ID: $id";
|
||||
* });
|
||||
* ```
|
||||
* - Request: GET /user/123
|
||||
* - Output: "User ID: 123"
|
||||
*
|
||||
* 2. Matching a route with optional parameters:
|
||||
* ```php
|
||||
* Route::match('GET', '/user/$id?', function ($id = null) {
|
||||
* echo $id ? "User ID: $id" : "No User ID";
|
||||
* });
|
||||
* ```
|
||||
* - Request: GET /user/
|
||||
* - Output: "No User ID"
|
||||
*
|
||||
* @param string $methods Allowed HTTP methods (e.g., "GET|POST").
|
||||
* @param string $route The route path to match (e.g., "/user/$id").
|
||||
* @param callable $callback The callback to execute if the route matches.
|
||||
* @param callable|null $middleware Optional middleware to execute before the callback.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
static function match(string $methods, string $route, callable $callback, ?callable $middleware = null): void
|
||||
{
|
||||
$allowedMethods = array_map(strtoupper(...), explode('|', $methods));
|
||||
if (!in_array($_SERVER['REQUEST_METHOD'], $allowedMethods)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$requestPathParts = explode('/', self::getRelativeRequestPath());
|
||||
$routeParts = explode('/', self::$pathPrefix . $route);
|
||||
$callbackArgs = [];
|
||||
|
||||
if (count($requestPathParts) !== count($routeParts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ($i = 1; $i < count($routeParts); $i++) {
|
||||
$routePart = $routeParts[$i] ?? '';
|
||||
$requestPart = $requestPathParts[$i] ?? '';
|
||||
|
||||
if ($routePart === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$isRouteParameter = $routePart[0] === '$';
|
||||
if (!$isRouteParameter) {
|
||||
if ($routePart !== $requestPart) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$isOptional = $routePart[-1] === '?';
|
||||
if (!$isOptional && $requestPart === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($isOptional && $i !== count($routeParts) - 1) {
|
||||
throw new InvalidArgumentException('Only the last route parameter can be optional.');
|
||||
}
|
||||
|
||||
if ($requestPart !== '') {
|
||||
$callbackArgs[] = htmlspecialchars($requestPart);
|
||||
}
|
||||
|
||||
$requestPathParts[$i] = $routePart;
|
||||
}
|
||||
|
||||
if (implode('/', $routeParts) !== implode('/', $requestPathParts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($middleware !== null) {
|
||||
self::before($middleware);
|
||||
}
|
||||
|
||||
foreach (self::$before as $middlewares) {
|
||||
foreach ($middlewares as $middleware) {
|
||||
$middleware();
|
||||
}
|
||||
}
|
||||
|
||||
$callback(...$callbackArgs);
|
||||
|
||||
foreach (self::$after as $middlewares) {
|
||||
foreach ($middlewares as $middleware) {
|
||||
$middleware();
|
||||
}
|
||||
}
|
||||
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for Route::match('GET', ...).
|
||||
*/
|
||||
static function get(string $route, callable $callback, ?callable $middleware = null): void
|
||||
{self::match('get', $route, $callback, $middleware);}
|
||||
|
||||
/**
|
||||
* Shorthand for Route::match('POST', ...).
|
||||
*/
|
||||
static function post(string $route, callable $callback, ?callable $middleware = null): void
|
||||
{self::match('post', $route, $callback, $middleware);}
|
||||
|
||||
/**
|
||||
* Shorthand for Route::match('PUT', ...).
|
||||
*/
|
||||
static function put(string $route, callable $callback, ?callable $middleware = null): void
|
||||
{self::match('put', $route, $callback, $middleware);}
|
||||
|
||||
/**
|
||||
* Shorthand for Route::match('PATCH', ...).
|
||||
*/
|
||||
static function patch(string $route, callable $callback, ?callable $middleware = null): void
|
||||
{self::match('patch', $route, $callback, $middleware);}
|
||||
|
||||
/**
|
||||
* Shorthand for Route::match('DELETE', ...).
|
||||
*/
|
||||
static function delete(string $route, callable $callback, ?callable $middleware = null): void
|
||||
{self::match('delete', $route, $callback, $middleware);}
|
||||
|
||||
/**
|
||||
* Shorthand for Route::match('OPTIONS', ...).
|
||||
*/
|
||||
static function options(string $route, callable $callback, ?callable $middleware = null): void
|
||||
{self::match('options', $route, $callback, $middleware);}
|
||||
|
||||
/**
|
||||
* Shorthand for Route::match('GET|POST', ...).
|
||||
*/
|
||||
static function form(string $route, callable $callback, ?callable $middleware = null): void
|
||||
{self::match('get|post', $route, $callback, $middleware);}
|
||||
|
||||
/**
|
||||
* Shorthand for Route::match('GET|POST|PUT|PATCH|DELETE|OPTIONS', ...).
|
||||
*/
|
||||
static function any(string $route, callable $callback, ?callable $middleware = null): void
|
||||
{self::match('get|post|put|patch|delete|options', $route, $callback, $middleware);}
|
||||
|
||||
/**
|
||||
* Registers middleware to be executed before the route callback.
|
||||
*
|
||||
* ### Example:
|
||||
* ```php
|
||||
* Route::before(function () {
|
||||
* echo "Middleware executed before the route callback.";
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param callable $middleware The middleware function to execute before the route callback.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
static function before(callable $middleware): void
|
||||
{
|
||||
array_push(
|
||||
self::$before[array_key_last(self::$before)],
|
||||
$middleware
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers middleware to be executed after the route callback.
|
||||
*
|
||||
* ### Example:
|
||||
* ```php
|
||||
* Route::after(function () {
|
||||
* echo "Middleware executed after the route callback.";
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param callable $middleware The middleware function to execute after the route callback.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
static function after(callable $middleware): void
|
||||
{
|
||||
array_push(
|
||||
self::$after[array_key_last(self::$after)],
|
||||
$middleware
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Group routes and middlewares with an optional common prefix.
|
||||
* It is useful for organizing routes that share a common path or functionality.
|
||||
*
|
||||
* ### Examples:
|
||||
* 1. Grouping routes under a prefix:
|
||||
* ```php
|
||||
* Route::group('/admin', function () {
|
||||
* Route::get('/dashboard', function () {
|
||||
* echo "Admin Dashboard";
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
* - Request: GET /admin/dashboard
|
||||
* - Output: "Admin Dashboard"
|
||||
*
|
||||
* 2. Grouping middlewares and routes but this time without a prefix:
|
||||
* ```php
|
||||
* Route::group(callback: function () {
|
||||
* Route::before(function () {
|
||||
* echo "Hello routes from within or nested groups! ";
|
||||
* });
|
||||
* // Outputs: "Hello routes from within or nested groups! Hello from somewhere!"
|
||||
* Route::get('/somewhere', function () {
|
||||
* echo "Hello from somewhere!";
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* // Outputs: "I am unaffected by that group middleware."
|
||||
* Route::get('/', function () {
|
||||
* echo "I am unaffected by that group middleware.";
|
||||
* });
|
||||
* ```
|
||||
* @param string|null $prefix The optional path prefix for the group (e.g., "/admin").
|
||||
* @param callable|null $callback The callback containing the route definitions for the group.
|
||||
*
|
||||
* @throws InvalidArgumentException If the callback is not provided.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
static function group(?string $prefix = null, ?callable $callback = null): void
|
||||
{
|
||||
if ($callback === null) {
|
||||
throw new InvalidArgumentException('You must provide a callback.');
|
||||
}
|
||||
if (!str_starts_with(
|
||||
self::getRelativeRequestPath(),
|
||||
self::$pathPrefix . $prefix ?? ''
|
||||
)) return;
|
||||
self::$pathPrefix = self::$pathPrefix . $prefix ?? '';
|
||||
array_push(self::$before, []);
|
||||
array_push(self::$after, []);
|
||||
$callback();
|
||||
array_pop(self::$before);
|
||||
array_pop(self::$after);
|
||||
self::$pathPrefix = rtrim(self::$pathPrefix, $prefix ?? '');
|
||||
}
|
||||
}
|
21
composer.json
Normal file
21
composer.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "williamaak/route",
|
||||
"description": " A lightweight routing system for handling HTTP requests.",
|
||||
"type": "library",
|
||||
"require": {
|
||||
"php": ">=8.1.0"
|
||||
},
|
||||
"license": "WTFPL",
|
||||
"authors": [
|
||||
{
|
||||
"name": "WilliamAAK",
|
||||
"email": "54738571+WilliamAAK@users.noreply.github.com"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "dev",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"WilliamAAK\\": "./"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user