π― File-based Routing
Folders are URL segments β like Next.js. [param] for dynamic, [...param] for catch-all, (group) for invisible prefixes, scoped 404/405 handlers.
File-based routing, elegant Flow syntax, zero bloat β boots in under 10ms.
composer create-project lizzyman04/fluxor-php my-app
cd my-app
composer dev # β http://localhost:8000Requires PHP β₯ 8.1. Routing is powered by the standalone, zero-dependency lizzyman04/file-router package β nothing else.
No route tables to maintain. The file tree under app/router/ is the routing map.
| File | URL |
|---|---|
app/router/index.php | / |
app/router/about.php | /about |
app/router/users/[id].php | /users/{id} |
app/router/posts/[cat]/[id].php | /posts/{cat}/{id} |
app/router/(admin)/dashboard.php | /dashboard |
app/router/api/[...slug].php | catch-all under /api/* |
app/router/api/404.php | 404 handler scoped to /api/* |
[param] β dynamic segment Β· [...param] β catch-all (array) Β· (group) β invisible prefix. Priority: static > dynamic > catch-all. β Routing guide
use Fluxor\Core\Routing\Flow;
use Fluxor\Core\Http\Response;
Flow::GET()->do(fn($req) => Response::json(['ok' => true]));
Flow::POST()->do(fn($req) => Response::success($req->all(), 'Created', 201));
Flow::GET()->name('home')->do(fn($req) => Response::view('home'));
$url = Flow::route('home'); // generate a named-route URL
Flow::use(fn($req) => $req->isAuthenticated() ? null : Response::redirect('/login'));
Flow::GET()->to(HomeController::class, 'index'); // bind to a controller->do() for closures, ->to() for controllers, ->name() for named routes, Flow::use() for middleware, Flow::cors() for per-route CORS. β Flow syntax
namespace App\Controllers;
use Fluxor\Core\Controller;
use Fluxor\Core\Http\Request;
use Fluxor\Core\Http\Response;
class UserController extends Controller
{
public function show(Request $request)
{
return Response::json(['id' => $request->param('id')]);
}
public function store(Request $request)
{
if (! $request->validateCsrf()) {
return Response::error('Invalid CSRF token', 419);
}
$data = $request->only(['name', 'email']);
return Response::success($data, 'Created', 201);
}
}Each action method takes the Request as its argument β explicit, testable, no hidden state. β Controllers guide
$req->param('id'); // route param from [id].php
$req->input('email', 'default'); // POST / GET / JSON body, with fallback
$req->only(['name', 'email']); // pick a subset
$req->filled('email'); // exists and non-empty
$req->wantsJson(); // Accept: application/json
$req->validateCsrf(); // guard mutating routes
$req->isAuthenticated(); // auth check
$req->setAttribute('user', $user); // pass data from middleware β handlerResponse::json($data)->status(201)->header('X-Foo', 'bar');
Response::success($data, 'OK'); // {"success":true,"message":"OK","data":{...}}
Response::error('Nope', 422, $details); // {"success":false,"message":"Nope","details":{...}}
Response::view('home', ['title' => 'Hi']);
Response::redirect('/dashboard');
Response::download('/tmp/report.pdf', 'report.pdf');
Response::json($d)->withCookie('token', $t, time() + 3600);<?php View::extend('layouts/main'); ?>
<?php View::section('content'); ?>
<h1><?= View::e($title) ?></h1> <!-- auto-escaped -->
<?= View::include('components/card', ['post' => $post]) ?>
<?php View::endSection(); ?>Layouts pull sections with View::yield('content'). β Views guide
// Middleware β null continues, a Response stops, false β 403
Flow::use(fn($req) => $req->isAuthenticated() ? null : Response::redirect('/login'));
// CORS β global fluent config (public/index.php, before $app->run())
$app->cors()->allowOrigin('https://example.com')->allowCredentials(true)->enable();
// β¦or per route (before any Flow::METHOD())
Flow::cors(['allowed_origins' => ['https://example.com'], 'allowed_methods' => ['GET', 'POST']]);
// Typed exceptions map to status codes automatically
use Fluxor\Exceptions\{NotFoundException, ValidationException, HttpException};
throw new NotFoundException('User not found'); // β 404
throw new ValidationException(['email' => 'Invalid']); // β 422
throw new HttpException('Access denied', 403); // β 403Preflight OPTIONS is handled for you; scoped 404.php / not-allowed.php files override error pages per directory. β Middleware Β· CORS Β· Error handling
use Fluxor\Core\Http\Fetch;
$user = Fetch::get('https://api.example.com/users/1')
->header('Authorization', 'Bearer token')->json();
app(); // App singleton | app('view') β a service
base_path('storage/logs'); // absolute path | base_url('api/users') β full URL
asset('css/app.css'); // public/ asset URL | config('app_name', 'Fluxor')
env('APP_ENV', 'production');// .env value | abort(404) / redirect('/home')
dd($var); dump($var); // dev-time debuggingβ HTTP client Β· Helpers
Ready to build? Start with the Installation guide, then the full guide and API reference. Full docs live at lizzyman04.com/fluxor-php.