Search by

silviosln / arbor-router

silviosln

Framework-agnostic file-system based routing library for PHP, inspired by Next.js App Router.

Package info

github.com/SilvioSLN/arbor-router

pkg:composer/silviosln/arbor-router

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-09-05 14:05 UTC

This package is not auto-updated.

Last update: 2026-09-20 12:55:00 UTC


README

PHP License Tests AI Ready

πŸ‡§πŸ‡· Leia esta documentaΓ§Γ£o em PortuguΓͺs: README.pt-BR.md
πŸ€– Instructions for AI Agents: See AGENTS.md and the ai/ context layer.

Arbor Router is a modern, strictly-typed, and framework-agnostic PHP 8.4+ library providing File-System Based Routing, heavily inspired by the mental model of Next.js App Router. It eliminates the need for manual, bloated routing configuration files by mapping your directory tree inside app/ directly into URL routes, nested layouts, API endpoints, and server actions.

🌟 Key Features

  • File-System Routing: Folders and files automatically define URL endpoints (e.g. app/users/page.php handles /users).
  • Dynamic Parameters: Folders with brackets like [id] or [...slug] (catch-all) naturally extract route parameters into $params.
  • Route Groups (group): Organize layouts, auth, and middlewares without altering public URL segments.
  • Hierarchical Layout Cascading: Nested layout.php and layoutroot.php render from the inside out, wrapping child views seamlessly via {{content}} or $children.
  • Clear Context Separation:
    • page.php for visual HTML view rendering.
    • route.php for REST API endpoints with HTTP method dispatching (GET, POST, PUT, DELETE).
    • action.php for Server Actions handling mutations and form submissions.
  • Cascading Russian Doll Middleware: Directory-scoped middleware.php files compose an onion pipeline from the root down to the target route.
  • Zero Heavy Dependencies: Pure PHP 8.4+ utilizing constructor promotion, readonly classes, and property hooks. Requires only ext-json and ext-mbstring.
  • Built-in Security: Session-based Anti-CSRF protection, HTTP Security Headers (nosniff, DENY), and API route client gating.
  • Smart Content Negotiation: route.php endpoints automatically serialize return arrays into JSON, XML, or plain text based on the client's Accept header.

πŸš€ Installation

Install via Composer:

composer require silviosln/arbor-router

Requirements: PHP 8.4 or higher (ext-json and ext-mbstring).

πŸ“¦ Directory Structure & Resource Conventions

app/
β”œβ”€β”€ layoutroot.php          # Global HTML shell (<!DOCTYPE html>, <html>, <head>, <body>)
β”œβ”€β”€ not-found.php           # Custom 404 error page
β”œβ”€β”€ error.php               # Custom 500 error boundary
β”œβ”€β”€ page.php                # Homepage view ("/")
β”œβ”€β”€ about/
β”‚   └── page.php            # Visual page ("/about")
β”œβ”€β”€ (dashboard)/            # Route group: excluded from public URL
β”‚   β”œβ”€β”€ layout.php          # Shared dashboard layout (sidebar + header)
β”‚   β”œβ”€β”€ middleware.php      # Authentication guard for dashboard routes
β”‚   └── admin/
β”‚       β”œβ”€β”€ page.php        # Admin page ("/admin")
β”‚       └── products/
β”‚           β”œβ”€β”€ [id]/
β”‚           β”‚   β”œβ”€β”€ page.php        # View/Edit product ("/admin/products/123")
β”‚           β”‚   └── edit/
β”‚           β”‚       └── action.php  # Form mutation action ("/admin/products/123/edit")
β”‚           └── route.php   # REST API endpoint ("/admin/products")

πŸ›  Quick Start Guide

1. Bootstrapping the Router (public/index.php)

<?php
declare(strict_types=1);

require_once __DIR__ . '/../vendor/autoload.php';

use Arbor\Router\Router;

$router = new Router([
    'appDir'   => __DIR__ . '/../app',
    'security' => [
        'headers' => true,
        'csrf'    => true,
    ],
]);

// Dispatches the complete lifecycle (security headers, matching, execution, and sending)
$router->dispatch();

2. Static and Dynamic Pages (page.php)

<?php
// app/users/[id]/page.php
use Arbor\Router\Http\Request;

return function(Request $request, array $params): string {
    $userId = htmlspecialchars((string) $params['id'], ENT_QUOTES, 'UTF-8');
    return "<h1>User Profile #{$userId}</h1>";
};

3. Cascading Layouts (layoutroot.php and layout.php)

<?php
// app/layoutroot.php (Global HTML Shell)
return function($request, $params): string {
    return <<<HTML
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>My Arbor App</title>
    </head>
    <body class="bg-gray-50">
        <main>{{content}}</main>
    </body>
    </html>
    HTML;
};

4. REST APIs and Content Negotiation (route.php)

<?php
// app/api/products/[slug]/route.php
use Arbor\Router\Http\Request;
use Arbor\Router\Http\JsonResponse;

return [
    'GET' => function(Request $request, array $params) {
        // ContentNegotiator automatically serializes to JSON or XML
        return [
            'slug'  => $params['slug'],
            'name'  => 'Mechanical Keyboard',
            'price' => 149.99,
        ];
    },

    'DELETE' => function(Request $request, array $params) {
        return new JsonResponse(['deleted' => true], 200);
    }
];

Note: By default, route.php endpoints require the X-API-Request: true header to guard against direct browser address bar visits.

5. Server Actions & Form Mutations (action.php)

<?php
// app/products/create/action.php
use Arbor\Router\Http\Request;
use Arbor\Router\Action\ActionResult;
use Arbor\Router\Validation\Validator;

return function(Request $request, array $params, array $query, array $body, Validator $validator) {
    // 1. Data Validation
    $validation = $validator->make($body, [
        'name'  => 'required|string|min:3|max:100',
        'price' => 'required|numeric|min:0.01',
    ]);

    if ($validation->fails()) {
        return ActionResult::error($validation->errors(), 'Invalid submission')
            ->statusCode(422);
    }

    // 2. Persist data...
    $newId = 42;

    // 3. Success with automatic redirection
    return ActionResult::success('Product created successfully!')
        ->data(['id' => $newId])
        ->redirect('/products/' . $newId);
};

In your HTML form, embed the built-in CSRF token field:

<form action="/products/create" method="POST">
    <?= (new \Arbor\Router\Security\CsrfGuard())->field() ?>
    <input type="text" name="name" required>
    <input type="number" step="0.01" name="price" required>
    <button type="submit">Create</button>
</form>

6. Russian Doll Middleware (middleware.php)

<?php
// app/(dashboard)/middleware.php
use Arbor\Router\Http\RequestInterface;
use Arbor\Router\Http\RedirectResponse;
use Arbor\Router\Http\Response;

return function(RequestInterface $request, \Closure $next): Response {
    if (session_status() === PHP_SESSION_NONE) {
        session_start();
    }

    if (!isset($_SESSION['user_id'])) {
        return new RedirectResponse('/login');
    }

    return $next($request);
};

πŸ€– AI-Native Context Layer

Arbor Router is designed to be AI-Friendly. If you are building with AI coding assistants (Claude, Cursor, Copilot, ChatGPT, Gemini), explore:

βš–οΈ License

This library is licensed under the MIT License. See LICENSE for details.