The Router class handles all URL routing in ForkBB, including route registration, request matching, and link generation with automatic token management.
Overview
The Router provides:
Route Registration - Define URL patterns with parameters
Request Routing - Match incoming requests to handlers
Link Generation - Create URLs from route markers
URL Validation - Verify URLs belong to the forum
CSRF Protection - Automatic token injection
Access the router via the Container: $this->c->Router
HTTP Methods
const GET = 'GET' ; // GET requests only
const PST = 'POST' ; // POST requests only
const DUO = [ 'GET' , 'POST' ]; // Both GET and POST
Response Codes
const OK = 200 ; // Route matched successfully
const NOT_FOUND = 404 ; // No matching route
const METHOD_NOT_ALLOWED = 405 ; // Route exists but wrong method
const NOT_IMPLEMENTED = 501 ; // HTTP method not supported
Route Registration
add
public function add (
array | string $method ,
string $route ,
string $handler ,
? string $marker = null
) : void
Registers a new route in the routing table.
HTTP method(s): Router::GET, Router::PST, or Router::DUO
URL pattern with optional parameters and anchors.
Controller and action as 'ControllerName:methodName'
Unique identifier for link generation. Required for link() method.
Static Routes:
// Simple static route
$router -> add (
$router :: GET ,
'/login' ,
'Auth:login' ,
'Login'
);
// Multiple methods
$router -> add (
$router :: DUO ,
'/search' ,
'Search:view' ,
'Search'
);
Dynamic Routes with Parameters:
// Required parameter
$router -> add (
$router :: GET ,
'/user/{id|i:[1-9]\d*}/{name}' ,
'ProfileView:view' ,
'User'
);
// Optional parameters
$router -> add (
$router :: GET ,
'/forum/{id|i:[1-9]\d*}/{name}[/{page|i:[1-9]\d*}]' ,
'Forum:view' ,
'Forum'
);
// Multiple optional parameters
$router -> add (
$router :: GET ,
'/search[/simple/{keywords}[/{page|i:[1-9]\d*}]]' ,
'Search:view' ,
'Search'
);
Route Parameter Syntax
Basic Parameter:
Typed Parameter (integer):
Custom Pattern:
{action:(?!search)[a-z_]+}
Parameter Format:
Parameter name (alphanumeric, must start with letter)
i - Integer (auto-cast to int)
s - String (default)
Custom regex pattern (default: [^/\x00-\x1f]+)
Optional Parameters:
Wrap optional segments in square brackets:
/path[/optional[/nested]]
Anchor Support:
$router -> add (
$router :: GET ,
'/post/{id|i:[1-9]\d*}#p{id}' ,
'Topic:viewPost' ,
'ViewPost'
);
Request Routing
route
public function route ( string $method , string $uri ) : array
Matches a request URI against registered routes.
HTTP method (GET, POST, HEAD, etc.)
Request URI to match (without query string)
Array with route result:
[OK, handler, args, marker] - Success
[NOT_FOUND] - No route found
[METHOD_NOT_ALLOWED, allowed] - Wrong method
[NOT_IMPLEMENTED] - Unsupported method
Example:
~/workspace/source/app/Controllers/Routing.php
$route = $router -> route ( 'GET' , '/user/42/john-doe' );
switch ( $route [ 0 ]) {
case $router :: OK :
// route[0] = 200
// route[1] = 'ProfileView:view'
// route[2] = ['id' => 42, 'name' => 'john-doe']
// route[3] = 'User'
list ( $page , $action ) = explode ( ':' , $route [ 1 ], 2 );
$page = $this -> c -> $page -> $action ( $route [ 2 ], 'GET' );
break ;
case $router :: NOT_FOUND :
$page = $this -> c -> Message -> message ( 'Not Found' , true , 404 );
break ;
case $router :: METHOD_NOT_ALLOWED :
// route[1] contains allowed methods
$page = $this -> c -> Message -> message ( 'Bad request' , true , 405 );
break ;
}
Link Generation
link
public function link ( ? string $marker = null , array $args = []) : string
Generates a URL from a route marker.
Route marker from add(). Null returns base URL.
Route parameters and special keys:
Required parameters (e.g., id, name)
Optional parameters (e.g., page)
# - URL fragment/anchor
hash - CSRF hash (auto-generated if route uses {hash})
token - CSRF token (auto-generated if route uses {token})
Complete URL with base URL and parameters.
Static Routes:
// Link to login page
$url = $this -> c -> Router -> link ( 'Login' );
// https://example.com/login
// Link with anchor
$url = $this -> c -> Router -> link ( 'Rules' , [ '#' => 'section-2' ]);
// https://example.com/rules#section-2
Dynamic Routes:
// User profile
$url = $this -> c -> Router -> link ( 'User' , [
'id' => 42 ,
'name' => 'john-doe'
]);
// https://example.com/user/42/john-doe
// Forum with pagination
$url = $this -> c -> Router -> link ( 'Forum' , [
'id' => 5 ,
'name' => 'general-discussion' ,
'page' => 2
]);
// https://example.com/forum/5/general-discussion/2
// Omit page=1 (automatically removed)
$url = $this -> c -> Router -> link ( 'Forum' , [
'id' => 5 ,
'name' => 'general' ,
'page' => 1
]);
// https://example.com/forum/5/general
Automatic CSRF Tokens:
// Route with token
$router -> add (
$router :: GET ,
'/logout/{token}' ,
'Auth:logout' ,
'Logout'
);
// Token auto-generated
$url = $this -> c -> Router -> link ( 'Logout' );
// https://example.com/logout/abc123token456
// Manual token (overrides auto-generation)
$url = $this -> c -> Router -> link ( 'Logout' , [ 'token' => 'custom' ]);
// https://example.com/logout/custom
Special Characters:
Slashes and backslashes in parameters are encoded:
$url = $this -> c -> Router -> link ( 'Search' , [
'keywords' => 'path/to/file'
]);
// Converts '/' to '(_slash_)' in URL
URL Validation
validate
public function validate (
mixed $url ,
string $defMarker ,
array $defArgs = []
) : string
Validates if a URL belongs to the forum and returns a safe URL.
URL to validate (typically string).
Default route marker if validation fails.
Arguments for default route.
The validated URL or a link to the default route.
Example:
// Validate redirect URL
$redirect = $this -> c -> Router -> validate (
$_GET [ 'redirect' ],
'Index' ,
[]
);
// Valid forum URL returns as-is
// Invalid/external URL returns link to Index
Use Cases:
Redirect after login
Return URL validation
Preventing open redirects
Real-World Examples
Complete Routing Setup
~/workspace/source/app/Controllers/Routing.php
public function routing () : Page
{
$user = $this -> c -> user ;
$r = $this -> c -> Router ;
// Guest routes
if ( $user -> isGuest ) {
$r -> add (
$r :: DUO ,
'/login' ,
'Auth:login' ,
'Login'
);
$r -> add (
$r :: DUO ,
'/login/forget' ,
'Auth:forget' ,
'Forget'
);
$r -> add (
$r :: DUO ,
'/login/{id|i:[1-9]\d*}/{key}/{hash}' ,
'Auth:changePass' ,
'ChangePassword'
);
} else {
$r -> add (
$r :: GET ,
'/logout/{token}' ,
'Auth:logout' ,
'Logout'
);
}
// Public routes
$r -> add (
$r :: GET ,
'/' ,
'Index:view' ,
'Index'
);
$r -> add (
$r :: GET ,
'/forum/{id|i:[1-9]\d*}/{name}[/{page|i:[1-9]\d*}]' ,
'Forum:view' ,
'Forum'
);
$r -> add (
$r :: GET ,
'/topic/{id|i:[1-9]\d*}/{name}[/{page|i:[1-9]\d*}]' ,
'Topic:viewTopic' ,
'Topic'
);
// Match request
$route = $r -> route ( 'GET' , '/forum/5/general-discussion/2' );
// Returns: [200, 'Forum:view', ['id'=>5, 'name'=>'general-discussion', 'page'=>2], 'Forum']
return $page ;
}
Navigation Links
// Breadcrumb navigation
$crumbs = [
[ $this -> c -> Router -> link ( 'Index' ), 'Home' ],
[ $this -> c -> Router -> link ( 'Forum' , [ 'id' => $forum -> id , 'name' => $forum -> name ]), $forum -> name ],
[ $this -> c -> Router -> link ( 'Topic' , [ 'id' => $topic -> id , 'name' => $topic -> name ]), $topic -> name ]
];
// Pagination
for ( $i = 1 ; $i <= $pages ; $i ++ ) {
$pageLinks [] = [
'url' => $this -> c -> Router -> link ( 'Forum' , [ 'id' => $forum -> id , 'name' => $forum -> name , 'page' => $i ]),
'number' => $i
];
}
Protected Actions
// Route with CSRF token
$router -> add (
$router :: GET ,
'/forum/{id|i:\d+}/markread/{token}' ,
'Misc:markread' ,
'MarkRead'
);
// Generate link (token auto-created)
$markReadUrl = $this -> c -> Router -> link ( 'MarkRead' , [ 'id' => $forum -> id ]);
// In template
< a href = "<?= $markReadUrl ?>" > Mark as Read </ a >
Complex Search Routes
// Simple search
$router -> add (
$router :: GET ,
'/search[/simple/{keywords}[/{page|i:[1-9]\d*}]]' ,
'Search:view' ,
'Search'
);
// Advanced search
$router -> add (
$router :: GET ,
'/search/advanced[/{keywords}/{author}/{forums}/{serch_in:\d}/{sort_by:\d}/{sort_dir:\d}/{show_as:\d}[/{page|i:[1-9]\d*}]]' ,
'Search:viewAdvanced' ,
'SearchAdvanced'
);
// Generate URLs
$simpleSearch = $this -> c -> Router -> link ( 'Search' , [ 'keywords' => 'php programming' ]);
$advancedSearch = $this -> c -> Router -> link ( 'SearchAdvanced' , [
'keywords' => 'database' ,
'author' => 'john' ,
'forums' => '1,2,5' ,
'serch_in' => 0 ,
'sort_by' => 1 ,
'sort_dir' => 0 ,
'show_as' => 0 ,
'page' => 2
]);
Path Substitution
The router automatically handles special characters:
Reserved Replacements:
/ → (_slash_)
\ → (_backslash_)
These are encoded in URLs and decoded during routing.
Best Practices
Use Markers for All Routes Always provide a marker for routes you’ll reference with link(). $router -> add ( $router :: GET , '/users' , 'Users:list' , 'UserList' );
Validate User Input URLs $url = $this -> c -> Router -> validate ( $_POST [ 'redirect' ], 'Index' );
Never trust user-provided URLs for redirects.
Use Optional Parameters '/topic/{id|i:[1-9]\d*}/{name}[/{page|i:[1-9]\d*}]'
Make pagination and filters optional for cleaner URLs.
Leverage Auto-Token Generation Let the router generate CSRF tokens automatically: $url = $this -> c -> Router -> link ( 'Logout' ); // Token added
Group Related Routes if ( $user -> isGuest ) {
// Guest-only routes
} else {
// Authenticated routes
}
Organize routes by authentication/permission levels.
Common Patterns
RESTful Resources
// List
$router -> add ( $router :: GET , '/users' , 'Users:index' , 'UserList' );
// View
$router -> add ( $router :: GET , '/users/{id|i:[1-9]\d*}' , 'Users:show' , 'UserView' );
// Create
$router -> add ( $router :: DUO , '/users/new' , 'Users:create' , 'UserCreate' );
// Edit
$router -> add ( $router :: DUO , '/users/{id|i:[1-9]\d*}/edit' , 'Users:edit' , 'UserEdit' );
// Delete
$router -> add ( $router :: GET , '/users/{id|i:[1-9]\d*}/delete/{token}' , 'Users:delete' , 'UserDelete' );
Nested Resources
// Forum topics
$router -> add (
$router :: GET ,
'/forum/{fid|i:[1-9]\d*}/topics[/{page|i:[1-9]\d*}]' ,
'Topics:index' ,
'ForumTopics'
);
// New topic in forum
$router -> add (
$router :: DUO ,
'/forum/{fid|i:[1-9]\d*}/topics/new' ,
'Topics:create' ,
'NewTopic'
);
See Also