定制 sethrensei/ren-router 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

sethrensei/ren-router

Composer 安装命令:

composer require sethrensei/ren-router

包简介

PHP micro-router with RBAC, Twig support, URL extension spoofing and centralized HTTP exception handling

README 文档

README

RenRouter is a modern, lightweight and secure PHP micro-router — designed to be the routing core of a custom framework or as a standalone HTTP layer for projects that don't need a full framework.

It provides clean HTTP orchestration (routing, dispatching, views, error handling), declarative security (authentication and roles), pluggable template engines (PHP or Twig), and URL extension spoofing for stack obfuscation.

✨ Key Features

  • HTTP routing powered by AltoRouter
  • Fluent, readable route registration with get(), post(), route() shortcuts
  • RouterFactory — clean builder pattern, no positional null arguments
  • Pluggable template engines: native PHP files or Twig (Symfony-style)
  • URL extension spoofing — serve /contact.html or /about.aspx while routes stay clean internally
  • Declarative route protection: authentication and role-based access control
  • Centralized HTTP exception handling (401, 403, 404, 500) with dedicated error views
  • Controller@method string target support alongside callables and view names
  • AbstractController base class with rendering, redirects, JSON responses, flash messages and request helpers
  • PSR-3 logger support (optional)
  • PHP 8.1+ with readonly properties and never return types

🧱 Architecture

RenRouter/
├── Router.php                        Core router and dispatcher
├── RouterFactory.php                 Fluent builder for Router assembly
│
├── Controller/
│   └── AbstractController.php        Base controller (render, redirect, json, flash, guards)
│
├── Template/
│   ├── TemplateEngineInterface.php   Contract for template engines
│   ├── PhpTemplateEngine.php         Native PHP file renderer (default)
│   └── TwigEngine.php                Twig adapter (mirrors Symfony conventions)
│
├── Security/
│   └── Auth.php                      Session-based auth helper (login, logout, roles)
│
├── Http/
│   ├── Request.php                   HTTP request abstraction
│   ├── UploadedFile.php              Secure file upload wrapper
│   └── Exception/
│       ├── HttpException.php
│       ├── UnauthorizedHttpException.php   (401)
│       ├── ForbiddenHttpException.php      (403)
│       └── NotFoundHttpException.php       (404)

Using/
├── views/  (or templates/ for Twig)
│   ├── base.php                      Layout wrapper ($pg_content injected)
│   └── errors/
│       ├── 401.php
│       ├── 403.php
│       ├── 404.php
│       └── 500.php
│
└── public/
    └── index.php                     Front controller

🚀 Quick Start

1. Install

composer require sethrensei/ren-router
composer require sethrensei/ren-router:version

2. Bootstrap (PHP templates)

use RenRouter\RouterFactory;

$router = RouterFactory::create(__DIR__ . '/../views')
    ->withLogger($logger)               // PSR-3, optional
    ->withUrlExtension('.html')         // /contact becomes /contact.html publicly
    ->withSecurityRoute('auth.login')   // redirect target when not authenticated
    ->build();

$router
    ->get('/',        'home/index',  'home.app')
    ->get('/login',   'auth/login',  'auth.login')
    ->post('/login',  [$security, 'login'],  'auth.login.post')
    ->get('/logout',  [$security, 'logout'], 'auth.logout')
    ->get('/dashboard', 'app/dashboard', 'app.dashboard', ['auth' => true])
    ->get('/admin',     'admin/index',   'admin.index',   ['auth' => true, 'roles' => ['ROLE_ADMIN']])
    ->run();

3. Bootstrap (Twig)

$router = RouterFactory::create(__DIR__ . '/../templates')
    ->withTwig(
        debug:     ($_ENV['APP_ENV'] === 'DEV'),
        cachePath: __DIR__ . '/../var/cache/twig',
    )
    ->withUrlExtension('.html')
    ->build();

🔀 Route Registration

Method Signature Use case
get() get(uri, target, name, options) Single GET route
post() post(uri, target, name, options) Single POST route
route() route(uri, target, method, name, options) Any method or GET|POST

Targets can be:

// A view name (rendered by the template engine)
->get('/about', 'pages/about', 'page.about')

// A callable
->get('/ping', fn(Router $r, array $p) => print('pong'), 'app.ping')

// A controller method array
->get('/users', [$userController, 'index'], 'user.index')

// A "Class@method" string
->get('/users', 'App\Controller\UserController@index', 'user.index')

AltoRouter patterns are supported in URIs:

->get('/user/[i:id]',        ...)   // integer
->get('/post/[a:slug]',      ...)   // alphanumeric + dash
->get('/file/[*:path]',      ...)   // anything including slashes
->get('/lang/[en|fr|de:lg]', ...)   // fixed options

🔐 Security & Authorization

Security is declared at route level, not inside controllers.

->get('/dashboard', 'app/dashboard', 'app.dashboard', [
    'auth'  => true,
    'roles' => ['ROLE_USER', 'ROLE_EDITOR'],
])
Option Type Behaviour
auth bool Redirects to the security route if not authenticated
roles string|string[] Throws 403 if no matching role is found

Automatically thrown exceptions:

Situation Exception HTTP Code
Not authenticated UnauthorizedHttpException 401
Wrong role ForbiddenHttpException 403
No matching route NotFoundHttpException 404
Any other error HttpException / caught Throwable 500

Auth helper

use RenRouter\Security\Auth;

// Write (call right after credential verification)
Auth::login(['id' => 1, 'name' => 'Alice', 'roles' => ['ROLE_USER']]);
Auth::logout();
Auth::refreshSession();   // regenerate ID, keep data

// Read
Auth::check();                          // bool
Auth::id();                             // int|string|null
Auth::user();                           // full user array
Auth::get('name');                      // single field
Auth::roles();                          // string[]
Auth::hasRole('ROLE_ADMIN');            // bool
Auth::hasAnyRole(['ROLE_A', 'ROLE_B']); // bool — at least one
Auth::hasAllRoles(['ROLE_A', 'ROLE_B']);// bool — all required

🎮 Controllers

Extend AbstractController for instant access to all response helpers.

use RenRouter\Controller\AbstractController;
use RenRouter\Router;

class PostController extends AbstractController
{
    public function __construct(Router $router)
    {
        parent::__construct($router);   // inject once, use everywhere
    }

    public function index(array $params): void
    {
        $this->requireAuth();
        $this->render('posts/index', ['posts' => []]);
    }

    public function show(array $params): void
    {
        $post = PostRepository::find((int) $params['id'])
            ?? $this->notFound("Post #{$params['id']} not found.");

        $this->render('posts/show', ['post' => $post]);
    }

    public function delete(array $params): void
    {
        $this->requireRole('ROLE_ADMIN');
        // delete…
        $this->flashSuccess('Post deleted.');
        $this->redirectToRoute('posts.index');
    }

    public function store(array $params): void
    {
        $this->requireAuth();
        $data = $this->postData(only: ['title', 'body']);

        if (empty($data['title'])) {
            $this->jsonError('Title is required.', 422);
        }

        $this->json(['success' => true], 201);
    }
}

AbstractController API

Category Method Description
Guards requireAuth(?Router) Redirect to login if not authenticated
requireRole(roles, ?Router) Requires auth + matching role
denyUnless(bool, message) Throws 403 when condition is false
notFound(message): never Throws 404 immediately
Rendering render(view, data, ?Router) Renders via the template engine
renderPartial(view, data, ?Router): string Returns rendered HTML as string
Redirects redirectToRoute(name, params, status, ?Router) Named route redirect
redirect(url, status, ?Router) Raw URL redirect
JSON json(data, status): never JSON response + exit
jsonError(message, status, extra): never {error, message, code} + exit
Flash flash(type, message) Write flash to session
flashSuccess(message) Shorthand for type success
flashError(message) Shorthand for type error
getFlash(type): array Read + clear one type
getAllFlash(): array Read + clear all types
Request input(key, default, from) Read from POST/GET/both
postData(only): array All POST, optionally filtered
isMethod(method): bool Check HTTP method
isAjax(): bool XHR / JSON Accept detection

🌐 Twig Integration

TwigEngine mirrors Symfony's Twig integration exactly.

These functions are available in every template automatically:

Twig function PHP equivalent Example output
path('route', {id:1}) Router::path() /contact.html
url('route', {id:1}) Router::url() https://example.com/contact.html
asset('css/app.css') Router::asset() https://example.com/css/app.css
route_exists('name') Router::hasRoute() true / false
{# templates/home/index.twig #}
<a href="{{ path('page.about') }}">About</a>
<a href="{{ url('page.contact') }}">Contact</a>
<link rel="stylesheet" href="{{ asset('css/app.css') }}">

{% if route_exists('admin.dashboard') %}
    <a href="{{ path('admin.dashboard') }}">Admin</a>
{% endif %}

For custom extensions and globals:

$twig = TwigEngine::create(viewsPath: __DIR__ . '/templates', debug: true);

$twig->addGlobal('app_name', 'MyApp');
$twig->addFunction('format_date', fn(\DateTimeInterface $d) => $d->format('d/m/Y'));
$twig->getTwig()->addExtension(new \Twig\Extension\StringLoaderExtension());

$router = RouterFactory::create(__DIR__ . '/templates')
    ->withTemplateEngine($twig)
    ->build();

🎭 URL Extension Spoofing

Confuse security scanners and bots by exposing a fake tech stack:

// Routes are always defined without extension:
->get('/contact', 'pages/contact', 'page.contact')

// Public-facing URLs get the suffix automatically:
// /contact.html  →  ".html" camouflage (Apache/Nginx static site)
// /contact.aspx  →  ".aspx" camouflage (IIS / ASP.NET)
// /contact.jsp   →  ".jsp"  camouflage (Java / Tomcat)
RouterFactory::create(__DIR__ . '/views')
    ->withUrlExtension('.aspx')
    ->build();

Generated URLs follow the same rule:

$router->url('page.contact');   // https://example.com/contact.aspx
$router->path('page.contact');  // /contact.aspx

The extension is stripped from incoming requests before matching — your route definitions never need to change.

❗ Error Handling

Errors are caught centrally by the router. In production, dedicated view files are rendered:

views/errors/401.php   — Unauthorized
views/errors/403.php   — Forbidden
views/errors/404.php   — Not Found
views/errors/500.php   — Internal Server Error

The $exception and $code variables are available inside error views.

In development (APP_ENV=DEV), the raw exception message and stack trace are printed as plain text.

You can also map error codes to named routes:

$router->setErrorRoute(404, 'error.notfound');
$router->setErrorRoute(403, 'error.forbidden');

🔗 URL Generation

// Absolute URL
$router->url('user.show', ['id' => 42]);
// => https://example.com/user/42.html

// Relative path
$router->path('user.show', ['id' => 42]);
// => /user/42.html

// Asset (never gets the fake extension)
$router->asset('img/logo.png');
// => https://example.com/img/logo.png

// Named redirect
$router->redirect('home.app');
$router->redirectUrl('https://example.com');

📦 Requirements

PHP ≥ 8.1
AltoRouter composer require altorouter/altorouter
Twig composer require twig/twig
Symfony Dotenv composer require symfony/dotenv
HTMLPurifier composer require ezyang/htmlpurifier"
PSR-3 logger, HTTP message and factory any PSR-3 compatible package

🎯 Philosophy

RenRouter is built around three principles:

  • Clarity over magic — every behaviour is explicit and traceable
  • Security by default — auth and roles declared at the route, not buried in controllers
  • A solid, extensible core — swap the template engine, add a logger, override any component

It is not a framework. It is a reliable foundation to build one.

📄 License

MIT — free to use, modify and distribute.

sethrensei/ren-router 适用场景与选型建议

sethrensei/ren-router 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 4 次下载、GitHub Stars 达 1, 最近一次更新时间为 2026 年 06 月 16 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 sethrensei/ren-router 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 sethrensei/ren-router 我们能提供哪些服务?
定制开发 / 二次开发

基于 sethrensei/ren-router 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 4
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 47
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-16