Skip to content

Fluxor PHPLightweight MVC framework

File-based routing, elegant Flow syntax, zero bloat β€” boots in under 10ms.

Fluxor

Install in one command ​

bash
composer create-project lizzyman04/fluxor-php my-app
cd my-app
composer dev            # β†’ http://localhost:8000

Requires PHP β‰₯ 8.1. Routing is powered by the standalone, zero-dependency lizzyman04/file-router package β€” nothing else.

Routes are your folder structure ​

No route tables to maintain. The file tree under app/router/ is the routing map.

FileURL
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].phpcatch-all under /api/*
app/router/api/404.php404 handler scoped to /api/*

[param] β†’ dynamic segment Β· [...param] β†’ catch-all (array) Β· (group) β†’ invisible prefix. Priority: static > dynamic > catch-all. β†’ Routing guide

Define behavior with Flow ​

php
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

Controllers receive the Request ​

php
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

A Request API that does the work ​

php
$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 β†’ handler

β†’ Request reference

Responses for every shape ​

php
Response::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);

β†’ Response reference

Views, layouts and partials ​

php
<?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, CORS and typed errors ​

php
// 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);           // β†’ 403

Preflight OPTIONS is handled for you; scoped 404.php / not-allowed.php files override error pages per directory. β†’ Middleware Β· CORS Β· Error handling

Batteries: HTTP client and global helpers ​

php
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.

Released under the MIT License.