Getting Started with File-Based Routing
File-based routing lets you add modern routing to existing PHP files without restructuring your project. Each PHP file handles its own routes.
Installation
composer require php-compatible/router
Server Configuration
File-based routing requires your server to pass the URL to each PHP file.
Apache (.htaccess)
RewriteEngine On
# If file exists, serve it with url parameter
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_FILENAME} \.php$
RewriteRule ^(.*)$ $1?url=$1 [QSA,L]
# If directory has index.php, use it
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_FILENAME}/index.php -f
RewriteRule ^(.*)/?$ $1/index.php?url=$1 [QSA,L]
Nginx
location ~ \.php$ {
fastcgi_param QUERY_STRING url=$uri&$query_string;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
include fastcgi_params;
}
location / {
try_files $uri $uri/ $uri.php?url=$uri&$args;
}
PHP Built-in Server
Create a router.php file:
<?php
if (php_sapi_name() === 'cli-server') {
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
// PHP file exists - set url and let PHP serve it
if (preg_match('/\.php$/', $path) && file_exists(__DIR__ . $path)) {
$_GET['url'] = preg_replace('/\.php$/', '', ltrim($path, '/'));
return false;
}
// Directory with index.php
if (is_dir(__DIR__ . $path) && file_exists(__DIR__ . $path . '/index.php')) {
$_GET['url'] = trim($path, '/');
include __DIR__ . $path . '/index.php';
return true;
}
// Try to find matching PHP file
if (file_exists(__DIR__ . $path . '.php')) {
$_GET['url'] = ltrim($path, '/');
include __DIR__ . $path . '.php';
return true;
}
}
http_response_code(404);
echo 'Not Found';
Run with:
php -S localhost:8080 router.php
Your First File-Based Route
Create /wwwroot/api/users.php:
<?php
require_once __DIR__ . '/../../vendor/autoload.php';
file_router(function() {
// GET /api/users
route(method(GET), url_path('/'), function() {
echo json_response(HTTP_OK, array(
'users' => array(
array('id' => 1, 'name' => 'Alice'),
array('id' => 2, 'name' => 'Bob'),
)
));
});
// POST /api/users
route(method(POST), url_path('/'), function() {
$data = json_body();
echo json_response(HTTP_CREATED, array('user' => $data));
});
// GET /api/users/123
route(method(GET), url_path_params('/:id'), function() {
echo json_response(HTTP_OK, array('user_id' => $_GET[':id']));
});
});
How It Works
file_router() automatically calculates the URL path based on the file's location:
/wwwroot/
├── index.php → matches /
├── api/
│ ├── users.php → matches /api/users
│ └── posts.php → matches /api/posts
└── admin/
└── index.php → matches /admin/
Why File-Based?
This approach is perfect when:
- You have an existing site with PHP files everywhere
- You can't restructure to a single entry point
- You want to add routing gradually, file by file
- You're preparing for a future framework migration
Document Your Methods
The whole point of adding this router is to prepare for future upgrades. Always use explicit HTTP methods (GET, POST, etc.) instead of ALL — this documents your API and makes migration easy.
Subsite Installation
If your application is installed in a subdirectory (e.g., /myapp/):
<?php
require_once __DIR__ . '/../vendor/autoload.php';
set_root_url('/myapp');
file_router(function() {
// Routes are now relative to /myapp + file path
route(method(GET), url_path('/'), function() {
echo json_response(HTTP_OK, array('status' => 'ok'));
});
});
Build URLs with the root prefix:
$homeUrl = root_url('/'); // Returns: /myapp/
$apiUrl = root_url('/api/users'); // Returns: /myapp/api/users
Next Steps
- HTTP Methods — Match GET, POST, PUT, DELETE
- URL Matching — Static paths and parameters
- Request Handling — JSON, forms, files
- Server Configuration — Detailed server setup