veasin/nx-tiny

A minimalist, functional PHP framework. Focus on declarative config, tail-call optimization, and clear domain modeling.

Maintainers

Package info

github.com/veasin/nx-tiny

pkg:composer/veasin/nx-tiny

Statistics

Installs: 8

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

0.0.14 2026-05-09 03:42 UTC

This package is auto-updated.

Last update: 2026-05-09 03:43:57 UTC


README

中文版

描述

nx-tiny 是一个轻量级的、函数驱动的 PHP 框架,专为现代开发实践设计。它优先考虑配置优于代码,支持尾调用优化

它避免了复杂的类层次结构和“魔法”行为,转而采用显式的、简单的全局函数。

核心哲学

  • 配置 > 代码:所有操作都由中央配置容器驱动。
  • 函数式风格:鼓励尾调用优化和可组合的函数链。
  • 声明式路由:路由通过注解或脚本定义并自动生成。
  • 领域模型:模型代表业务逻辑和关系,而不仅仅是数据库 ORM 实体。
  • 缓存即逻辑:缓存集成在业务流程中,并内置回退机制。

安装

composer require veasin/nx-tiny

函数参考

container - 容器方法

支持双生命周期(持久/请求级)与延迟构建的配置容器,适用于 Swoole/FrankenPHP 等常驻内存场景。 在php-fpm或apache下,每次请求都是重置的,可以忽略^相关逻辑。

// 获取所有配置
$all = container();

// 清空请求级配置
container(null);

// 清空持久级配置
container(null, true);
container(null, '^');

// 读取值(支持 . 分隔,先查请求级再查持久级)
$host = container('database.host');

// 仅读取持久级(^ 前缀)
$host = container('^database.host');

// 设置值(写入请求级)
container('database.host', 'localhost');
container('app.debug', true);

// 设置值(写入持久级,^ 前缀)
container('^database.host', 'localhost');

// 删除键(设置 null)
container('database.host', null);

// 批量读取(list 数组)
$values = container(['database.host', 'app.debug']);

// 批量设置(map 数组,写入请求级)
container([
    'database.host' => '127.0.0.1',
    'database.port' => 3306,
]);

// 批量持久设置
container(['k' => 'v'], true);
container(['k' => 'v'], '^');

// 数组 key 支持 ^ 修饰符单独控制
container(['^persist.k' => 'v', 'request.k' => 'v']);

// 闭包工厂:存入闭包,用 * 后缀执行
container('version', fn() => file_get_contents('version.txt'));
$closure = container('version');  // 返回闭包本身
$result  = container('version*'); // 执行闭包返回结果

// 非闭包值忽略 *
container('plain', 'string');
container('plain*');  // 返回 'string'

env - 环境变量读取

支持系统环境变量、$_ENV.env 文件三种来源。

// 读取环境变量
$debug = env('APP_DEBUG');   // 自动类型转换: 'true' → true, 'false' → false, 'null' → null
$host  = env('DB_HOST');     // 不存在返回 null
$name  = env('APP_NAME');    // 字符串原样返回

// .env 文件路径可通过容器配置
container('#env', '/path/to/.env');
env('APP_KEY');

// 默认自动查找:从 src/ 向上搜索 .env(最多 3 层)

.env 配置:container('#env', '/path/to/.env')

args - 命令行参数解析

// 字符串输入
$args = args('-v --file=test.php input.txt');
// 结果: ['v' => true, 'file' => 'test.php', 'input.txt']

// 数组输入
$args = args(['-abc', '--verbose', '--name=John', 'data.txt']);
// 结果: ['a' => true, 'b' => true, 'c' => true, 'verbose' => true, 'name' => 'John', 'data.txt']

// 带引号的值
$args = args('--message="Hello World" --path=\'/usr/local\'');
// 结果: ['message' => 'Hello World', 'path' => '/usr/local']

method - HTTP方法获取/检查

// 获取当前请求方法
$method = method();  // 返回: 'get', 'post', 'cli' 等

// 检查是否匹配指定方法
if (method('POST')) {
    // 处理 POST 请求
}

// 预置请求方法(通过容器)
container('#method', 'put');

缓存键:#method

from - 从指定来源获取原始值,支持来源:query|cookie|file|params|header|input|body

// 从 Body 获取原始值
$id = from('id', 'body');

// 从 Query 获取原始值
$name = from('name', 'query');

// 从 Header 获取原始值
$token = from('authorization', 'header');

// 直接使用数组作为来源
$data = from('id', ['id' => 123, 'name' => 'test']);

// 获取整个来源
$body = from(null, 'body');

// 批量获取
$result = from(['id', 'name'], 'query');  // ['id' => null, 'name' => 'test']

// 预置输入数据(通过容器缓存)
container('#in.input', ['method' => 'get', 'uri' => '/test', 'params' => []]);
container('#in.params', ['id' => 123]);  // 预置路由参数
container('#in.body', ['name' => 'test']);  // 预置请求体
container('#in.headers', ['Authorization' => 'Bearer xxx']);  // 预置请求头

// 扩展 content-type 解析器
container('#in.content', [
    'application/xml' => fn($raw) => simplexml_load_string($raw),
    'default' => fn($raw) => ['raw' => $raw],
]);

缓存键:#in.input#in.params#in.body#in.headers#in.raw#in.content

filter - 数据验证与转换

// 类型转换
filter('123', 'int');        // 返回 123 (int)
filter('true', 'bool');      // 返回 true
filter('{"a":1}', 'json');   // 返回 ['a' => 1]

// 验证规则
filter('hello@example.com', 'email');  // 返回邮箱字符串
filter('150', 'int', '>100', '<200');  // 返回 150
filter('on', 'bool');                  // 返回 true

// 逗号分隔的组合规则
filter('150', 'int,>100,<200');  // 返回 150

// 自定义验证
filter('abc', fn($v) => strlen($v) > 2);  // 返回 'abc'
filter(10, 'int', '>5');                  // 返回 10
filter(3, 'int', '>5');                   // 返回 null (验证失败)

// 扩展规则(通过容器配置 #filter)
container('#filter', [
    'phone' => [null, null, [fn($v) => preg_match('/^1\d{10}$/', $v)]],
]);
filter('13800138000', 'phone');  // 返回 '13800138000'

扩展方式:container('#filter', [...])

input - 输入数据获取(获取from+验证filter)

// 获取并验证(多个规则)
$age = input('age', 'query', 'int', '>=18', '<=100');

// 组合规则
$age = input('age', 'body,int,>=18,<=100');

// 批量获取
$data = input(['id' => 'int,>0', 'name' => 'str']);

output - 输出数据

// JSON 输出
output(['status' => 'ok', 'data' => [1, 2, 3]]);

// 设置状态码
output(['error' => 'not found'], 404);

// 指定格式输出
output($data, 'json');

// 输出视图
output($viewData, 'view', 'template.php');
output($viewData, 'view', ['file' => 'template.php']);

// 文件输出(展示文件)
output(null, 'file', '/path/to/file.pdf');

// 文件下载
output(true, 'file', '/path/to/file.pdf');

// 带响应头
output(['token' => $token], 200, ['Authorization' => 'Bearer xxx']);

// 无参调用触发输出(用于 worker 模式显式发送)
output();

// 扩展输出格式(通过容器配置 #out.formats)
container('#out.formats', [
    'xml' => function($response, $formats) {
        $response['headers']['Content-Type'] = 'application/xml';
        $response['body'] = xml_encode($response['body']);
        $formats['http']($response, $formats);
    },
]);
output($data, 'xml');

// 自定义渲染回调
container('#out.callback', function($response) {
    echo json_encode($response['body']);
});

扩展方式:container('#out.formats', [...])
回调方式:container('#out.callback', fn($response) => ...)

route - 路由匹配

// 基础路由
route('GET:/users', function($next) {
    output(['users' => []]);
});

// 带参数 (:param 或 {param})
route('GET:/user/:id', function() {
    $id = input('id', 'params');
    output(['id' => $id]);
});

// POST 路由
route('POST:/api/user', function() {
    $name = input('name', 'body');
    output(['created' => $name]);
});

// 路由映射数组
route([
    'get:/api/list' => function() { return 'list'; },
    'post:/api/create' => function() { return 'create'; },
]);

// 通配符路由
route('GET:/api/*', function() {
    // 匹配 /api 下的所有路径
});

// CLI 路由
route('cli:verbose', function() { /* ... */ });
route('cli:file=*', function() { /* ... */ });

cache - 多级缓存

// APCu 缓存
$result = cache('APCu', function() {
    return db('SELECT * FROM users');
});

// Redis 缓存
$result = cache('Redis', function() {
    return expensiveOperation();
});

// 带 TTL
$result = cache(['fn' => 'Redis', 'ttl' => 3600], function() {
    return $data;
});

// 组合缓存(按顺序尝试)
$result = cache('APCu', 'Redis', function() {
    return $data;
});

// 配置方式(通过容器 cache)
container('cache', [
    'user_list' => ['APCu', 1800],  // APCu, TTL 30分钟
    'api_data' => ['Redis', 3600, 'prefix_'],  // Redis, TTL 1小时, 自定义前缀
]);
$result = cache('user_list', function() {
    return db('SELECT * FROM users', [], 'list');
});

配置方式:container('cache', [...])
Redis 配置:container('config.redis', ['host' => '127.0.0.1', 'port' => 6379, 'password' => '', 'database' => 0])

db - 数据库操作

// 查询单行
$user = db('SELECT * FROM users WHERE id = ?', [1], 'row');

// 查询列表
$users = db('SELECT * FROM users', [], 'list');

// 查询单个值
$count = db('SELECT COUNT(*) FROM users', [], 'value');

// 查询单列(返回数组)
$names = db('SELECT name FROM users', [], 'column');

// 查询键值对
$pairs = db('SELECT id, name FROM users', [], 'pairs');

// 查询分组结果
$grouped = db('SELECT status, COUNT(*) FROM users GROUP BY status', [], 'group');

// 插入并获取ID
$id = db('INSERT INTO users (name) VALUES (?)', ['John'], 'id');

// 更新并获取影响行数
$count = db('UPDATE users SET name = ? WHERE id = ?', ['Jane', 1], 'count');

// 批量插入
db('INSERT INTO users (name) VALUES (?), (?)', [['John'], ['Jane']], 'ok');

// 执行模式(返回 PDOStatement)
$stmt = db('SELECT * FROM users', [], true);

// 自定义处理
$result = db('SELECT * FROM users', [], fn($stmt, $pdo) => $stmt->fetchAll());

事务支持

// 开启事务
db('BEGIN');

// 提交事务
db('COMMIT');

// 回滚事务
db('ROLLBACK');

// 保存点
db('SAVEPOINT sp1');

// 回滚到保存点
db('ROLLBACK TO SAVEPOINT sp1');

配置数据库连接

container('db.default', [
    'dsn' => 'mysql:host=localhost;dbname=test',
    'username' => 'root',
    'password' => '',
    'options' => [],
]);

// 使用命名配置
$user = db('SELECT * FROM users WHERE id = ?', [1], 'row', 'default');

配置方式:container('db.{name}', [...])

配合 nx-sql 使用:

use nx\helpers\sql;

// 插入数据并获取ID
$id = db(sql::table('users')->insert(['name' => 'John', 'email' => 'john@test.com']), 'id');

// 查询单行
$user = db(sql::table('users')->where(['id' => 1])->select(), 'row');

// 条件查询
$activeUsers = db(sql::table('users')->where(['status' => 1])->select(), 'list');

// 更新数据
$affected = db(sql::table('users')->where(['id' => 1])->update(['name' => 'Jane']), 'count');

// 删除数据
$affected = db(sql::table('users')->where(['id' => 1])->delete(), 'count');

test - 轻量级测试

// 直接比较
test('数字比较', 5, 5);

// 函数返回值
test('函数返回值', fn() => 2+2, 4);

// 断言函数
test('范围判断', 10, fn($v) => $v > 5);

// 数组验证
test('数组验证', ['a' => 1], function($value) {
    return isset($value['a']) && $value['a'] === 1;
});

// 函数作为待测值
test('函数返回值测试', fn() => 2+2, 4);

name - 命名配置管理

// 基础用法
$key = name('user.id');  // 返回 'user.id'

// 命名空间
container('name', ['cache' => ['user' => 'cache:user:{uid}']]);
$key = name('user', ['uid' => 123], 'cache');  // 返回 'cache:user:123'

配置方式:container('name', [...])

log - 日志函数

// 基础用法(默认 level 为 info)
log('用户登录');

// 指定 level
log('发生错误', 'error');

// context 为字符串时作为 level
log('警告信息', 'warning');

// 使用 context 替换占位符 {key}
log('用户 {name} 登录', ['name' => 'admin']);

// 同时使用 context 和 level
log('错误: {msg}', ['msg' => '连接失败'], 'error');

// 非 string 消息自动 json
log(['a' => 1, 'b' => 2]);

// 支持 Stringable 对象
log(new StringableClass());

// 注入 PSR Logger
container('#log', $psrLogger);

// 注入闭包(fn 不会被容器自动执行)
container('#log.fn', fn($level, $message, $context) => ...);

PSR Logger 方式:container('#log', $logger)
闭包方式:container('#log.fn', fn(...) => ...)

预制中间件

所有预制中间件返回 callable,直接传入 middleware()run() 使用:

middleware(cors(), auth(), log(), $handler);

auth(prefix, realm) — HTTP Basic 认证

旧版 Basic 认证。从 Authorization: Basic 头提取用户名密码,调用验证器。

container('#mw:auth:validators', [fn($user, $pass) => true]);
middleware(auth(), $handler);
// 获取用户: container('#mw:auth:user')
参数 默认值 说明
$prefix #mw:auth 容器键前缀
$realm Protected WWW-Authenticate realm
容器键 说明
{prefix}:validators 验证器数组,接收 ($user, $pass) 返回 bool
{prefix}:user 认证通过后写入用户信息

basic(prefix, realm) — HTTP Basic 认证(推荐)

新版 Basic 认证。验证器返回值直接存入 user(可返回用户对象而非 true)。

container('#mw:auth:validators', [fn($user, $pass) => $user]);
middleware(basic(), $handler);

参数与容器键同 auth()

token(prefix, headerName) — Token 认证

Authorization 头或 URL 查询参数 ?token= 提取 token。

container('#mw:auth:validators', [fn($token) => $user]);
middleware(token(), $handler);
middleware(token('#mw:auth', 'X-Auth-Token'), $handler);  // 自定义请求头
参数 默认值 说明
$prefix #mw:auth 容器键前缀
$headerName Authorization 请求头名称(未取到时 fallback 查询参数 token
容器键 说明
{prefix}:validators 验证器数组,接收 ($token)
{prefix}:user 认证通过后写入用户信息

jwt(prefix, algo) — JWT 认证

Authorization: Bearer <token> 提取 JWT,HMAC 验证签名后解码 payload。

container('#mw:auth:secret', 'your-secret-key');
container('#mw:auth:validators', [fn($payload) => $user]);
middleware(jwt(), $handler);
// 获取 payload: container('#mw:auth:payload')
参数 默认值 说明
$prefix #mw:auth 容器键前缀
$algo HS256 签名算法,支持 HS256HS512
容器键 说明
{prefix}:secret HMAC 签名密钥
{prefix}:validators 验证器数组,接收 ($payload)
{prefix}:user 认证通过后写入用户信息
{prefix}:payload 自动写入解码后的 JWT payload

apikey(prefix, headerName, queryName) — API Key 认证

从请求头或 URL 查询参数提取 API Key。

container('#mw:auth:validators', [fn($apiKey) => $user]);
middleware(apikey(), $handler);
参数 默认值 说明
$prefix #mw:auth 容器键前缀
$headerName X-API-Key 请求头名称
$queryName api_key URL 查询参数名
容器键 说明
{prefix}:validators 验证器数组,接收 ($apiKey)
{prefix}:user 认证通过后写入用户信息

cors(options) — CORS 跨域

为响应添加 CORS 头,OPTIONS 预检请求直接返回空响应。

middleware(cors(), $handler);
middleware(cors(['origin' => 'https://example.com']), $handler);
参数 默认值 说明
origin '*' 允许的源,数组时随机选取
methods 'GET,POST,PUT,DELETE,OPTIONS' 允许的 HTTP 方法
headers 'Content-Type,Authorization,X-CSRF-Token' 允许的请求头
credentials false 是否允许发送凭证
max-age 86400 预检缓存时间(秒)

csrf(verify) — CSRF 防护

  • verify: false(默认):生成 token 注入响应(数组加 _token 字段,对象加 token 属性)
  • verify: true:校验请求中的 _tokenX-CSRF-Token 头,不匹配返回 419
middleware(csrf(), $handler);          // 生成 token
middleware(csrf(verify: true), $handler); // 验证 token
容器键 说明
#mw:csrf:token 存储/读取当前会话的 CSRF token

error(debug) — 异常处理

捕获所有 \Throwable 异常。调试模式返回完整堆栈,生产模式返回通用错误。

middleware(error(debug: true), $handler);  // 开发环境
middleware(error(), $handler);             // 生产环境

gzip(level) — Gzip 响应压缩

检查客户端 Accept-Encoding: gzip,压缩后内容比原内容小时启用压缩。

middleware(gzip(), $handler);
middleware(gzip(9), $handler);   // 最高压缩级别
参数 默认值 说明
$level 6 压缩级别 1-9

json(pretty) — JSON 格式化

将返回值转为 JSON 并设置 Content-Type: application/json; charset=UTF-8

middleware(json(), $handler);
middleware(json(pretty: true), $handler);  // 格式化输出

log(level) — 请求日志

记录请求方法、URI、状态码、耗时(ms)、内存(KB)。

middleware(log(), $handler);
middleware(log('warning'), $handler);
容器键 说明
#out.response:code 读取响应状态码(默认 200)

rate(maxRequests, windowSeconds, key) — 接口限流

基于滑动窗口的 IP + 路由级别限流,默认使用 APCu 存储。

middleware(rate(), $handler);              // 60次/分钟
middleware(rate(100, 60), $handler);       // 100次/分钟
middleware(rate(30, 60, 'api'), $handler); // 自定义 key 前缀
参数 默认值 说明
$maxRequests 60 时间窗口内最大请求数
$windowSeconds 60 时间窗口大小(秒)
$key 'rate' 限流键名前缀

自定义存储(通过容器):

container('#rate:storage', fn($key) => [...]);           // 读取
container('#rate:storage', fn($key, $value, $ttl) => 1); // 写入

serve(root, map) — 静态文件服务

根据 URI 在指定目录查找静态文件,自动设置 MIME 类型和缓存头。

middleware(serve('/var/www/public'), $handler);
// 扩展名映射(如将 .html 指向 index.php)
middleware(serve('/var/www/public', ['html' => 'index.php']), $handler);
参数 默认值 说明
$root 必填 静态文件根目录
$map [] 扩展名到目标文件的映射

扩展 MIME 类型:

container('#static:mimes', ['webp' => 'image/webp']);