This commit is contained in:
William 2024-11-13 18:11:32 +01:00
parent f051b15a44
commit eb25213b36
2 changed files with 104 additions and 62 deletions

View File

@ -2,41 +2,51 @@
namespace WillySoft; namespace WillySoft;
use Exception;
abstract class Route abstract class Route
{ {
static $prefix = ''; static $prefix = '';
static $groups = [[]]; static $groups = [[]];
static function match(string $methods, string $uri, callable $callback) static function match(string $methods, string $url_path, callable $callback)
{ {
if (!in_array( if (!in_array(
$_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_METHOD'],
array_map(strtoupper(...), explode('|', $methods)) array_map(strtoupper(...), explode('|', $methods))
)) return; )) return;
$request_uri_parts = explode('/', urldecode( $request_url_path_parts = explode('/', urldecode(
strtok($_SERVER['REQUEST_URI'], '?') strtok($_SERVER['REQUEST_URI'], '?')
)); ));
$uri_parts = explode('/', self::$prefix . $uri); $url_path_parts = explode('/', self::$prefix . $url_path);
$callback_args = []; $callback_args = [];
for ($i = 0; $i < count($uri_parts); $i++) { for ($i = 1; $i < count($url_path_parts); $i++) {
if ($uri_parts[$i] === '') if ($url_path_parts[$i][0] !== '$')
continue; continue;
if ($uri_parts[$i][0] !== '$') if (!isset($request_url_path_parts[$i]))
continue;
if (!isset($request_uri_parts[$i]))
return; return;
if (
$uri_parts[$i][-1] !== '?' if ($url_path_parts[$i][-1] !== '?'
&& $request_uri_parts[$i] === '' && $request_url_path_parts[$i] === ''
) return; ) return;
if ($request_uri_parts[$i] !== '') if ($url_path_parts[$i][-1] === '?'
array_push($callback_args, $request_uri_parts[$i]); && $i !== (count($url_path_parts) - 1)
$request_uri_parts[$i] = $uri_parts[$i]; ) throw new Exception(
'Only the last route parameter is allowed to be optional.'
);
if ($request_url_path_parts[$i] !== '')
array_push(
$callback_args,
htmlspecialchars($request_url_path_parts[$i])
);
$request_url_path_parts[$i] = $url_path_parts[$i];
} }
if ( if (
implode('/', $uri_parts) implode('/', $url_path_parts)
!== implode('/', $request_uri_parts) !== implode('/', $request_url_path_parts)
) return; ) return;
foreach (self::$groups as $middlewares) { foreach (self::$groups as $middlewares) {
@ -48,22 +58,22 @@ abstract class Route
die(); die();
} }
static function get(string $uri, callable $callback) static function get(string $url_path, callable $callback)
{self::match('get', $uri, $callback);} {self::match('get', $url_path, $callback);}
static function post(string $uri, callable $callback) static function post(string $url_path, callable $callback)
{self::match('post', $uri, $callback);} {self::match('post', $url_path, $callback);}
static function put(string $uri, callable $callback) static function put(string $url_path, callable $callback)
{self::match('put', $uri, $callback);} {self::match('put', $url_path, $callback);}
static function patch(string $uri, callable $callback) static function patch(string $url_path, callable $callback)
{self::match('patch', $uri, $callback);} {self::match('patch', $url_path, $callback);}
static function delete(string $uri, callable $callback) static function delete(string $url_path, callable $callback)
{self::match('delete', $uri, $callback);} {self::match('delete', $url_path, $callback);}
static function options(string $uri, callable $callback) static function options(string $url_path, callable $callback)
{self::match('options', $uri, $callback);} {self::match('options', $url_path, $callback);}
static function form(string $uri, callable $callback) static function form(string $url_path, callable $callback)
{self::match('get|post', $uri, $callback);} {self::match('get|post', $url_path, $callback);}
static function any(string $uri, callable $callback) static function any(string $url_path, callable $callback)
{self::match('get|post|put|patch|delete|options', $uri, $callback);} {self::match('get|post|put|patch|delete|options', $url_path, $callback);}
static function use(callable $middleware) static function use(callable $middleware)
{ {

View File

@ -2,26 +2,24 @@
use WillySoft\Route as App; use WillySoft\Route as App;
// generated by composer, if you are not using composer include
// the file in a different manner
// require __DIR__ . '/edit/this/location/file.php';
require __DIR__ . '/../vendor/autoload.php'; require __DIR__ . '/../vendor/autoload.php';
// middlewares provide a convenient mechanism for inspecting and //-----------------------------------------------------
// filtering requests. you can imagine them as a series of layers // Basic Routing
// that requests must pass through before they hit your application. //-----------------------------------------------------
// a layer can be used for auth, rate limiting or anything really // there are shorthands for standard HTTP request methods such as
App::use(
fn() => print('<p>Hello all routes!</p>'));
// there are shorthands for methods you would expect such as
// get|post|put|patch|delete|options // get|post|put|patch|delete|options
App::get('/', App::get('/', function() {
fn() => print('homepage')); ?>
<h1>Homepage!</h1>
<p>Welcome to the homepage</p>
<?php
});
// "any" is a shorthand that does what you would expect // form is a shorthand that accepts GET and POST request methods
// allowing all of the above to pass through
App::any('/example',
fn() => print('i match on any method'));
// form is a shorthand that accepts GET and POST methods
App::form('/submit', App::form('/submit',
fn() => print('i match on GET and POST methods')); fn() => print('i match on GET and POST methods'));
@ -30,24 +28,58 @@ App::form('/submit',
App::match('get|post|put', '/match', App::match('get|post|put', '/match',
fn() => print('i match on any method you like')); fn() => print('i match on any method you like'));
// optional route parameters // "any" is a shorthand allowing all HTTP request methods to pass
App::get('/echo/$text?', App::any('/example',
fn($text = 'You sent nothing') => print($text)); fn() => print('i match on any method'));
// required route parameters //-----------------------------------------------------
App::get('/echo_must_supply_text/$text', // Route Parameters
//-----------------------------------------------------
// capture segments of the URL_PATH within your route. for example,
// you may need to capture a users ID. you may do so by defining
// route parameters.
// parameters are injected into route callbacks based on their order.
// a required parameter will only match when a value is supplied
App::get('/echo/$text',
fn($text) => print($text)); fn($text) => print($text));
// optional parameter, only the last parameter can be optional
// or it will throw an exception
App::get('/echo_optional/$text?',
fn($text = 'You sent nothing') => print($text));
// crude friends list example
App::get('/user/$user_id/friends/$pagination?', function($user_id, $pagination = '0') {
?>
<h1>Showing friends for user: <?=$user_id?></h1>
<p>Current page: <?=$pagination?></p>
<?php
});
//-----------------------------------------------------
// Middlewares
//-----------------------------------------------------
// middlewares provide a convenient mechanism for inspecting and
// filtering requests. you can imagine them as a series of layers
// that requests must pass through before they hit your application.
// a layer can be used for auth, rate limiting or anything really
App::use(
fn() => print('<p>Hello all routes!</p>'));
//-----------------------------------------------------
// Route Groups
//-----------------------------------------------------
// group together routes and middlewares. a prefix can be added to // group together routes and middlewares. a prefix can be added to
// prefix each route in the group with a given PATH. the group will // prefix each route in the group with a given URL_PATH. the group
// be skipped if the requested PATH does not begin with the one supplied. // will be skipped if the request URL_PATH does not start with the
// middlewares defined in here will only run on routes matched from // one supplied. middlewares defined in here will only run on routes
// within or any child groups // matched from within or any child groups
App::group('/test', function() { App::group('/test', function() {
App::use( App::use(
fn() => print('<p>Hello all routes matched within this or any child groups!</p>')); fn() => print('<p>Hello all routes matched within this or any child groups!</p>'));
// this will be matched as /test/ // this will be seen as /test/
App::get('/', App::get('/',
fn() => print('Testing 123')); fn() => print('Testing 123'));
}); });