作者:小新
版本:V2.1.3
许可证:Apache-2.0
文档生成日期:2026-08-26
代码规模:42 个 PHP 文件,约 4388 行代码
- 第一章 框架概述
- 第二章 目录结构详解
- 第三章 快速开始
- 第四章 架构设计
- 第五章 配置系统
- 第六章 路由系统
- 第七章 控制器
- 第八章 模型与数据库
- 第九章 视图引擎
- 第十章 中间件与认证
- 第十一章 缓存系统
- 第十二章 日志系统
- 第十三章 数据验证器
- 第十四章 文件管理
- 第十五章 HTTP 客户端
- 第十六章 加密工具
- 第十七章 Redis 操作
- 第十八章 阿里云 DNS
- 第十九章 错误处理与调试
- 第二十章 CLI 命令行
- 第二十一章 安全审计
- 第二十二章 性能分析
- 第二十三章 扩展开发指南
- 第二十四章 最佳实践
- 第二十五章 常见问题 FAQ
- 第二十六章 版本历史与升级
- 附录 A 全局函数与常量速查
- 附录 B 配置项完整清单
- 附录 C 类与命名空间索引
XiaoPHP 是一款由个人开发者「小新」维护的轻量级 PHP MVC 框架,专为小型项目快速开发而设计。框架采用零 Composer 依赖的核心架构(Composer 为可选增强),通过自定义自动加载器实现类的自动装载,内置完整的 MVC 分层、路由分发、数据库 ORM、模板引擎、中间件认证、缓存、日志、验证、文件上传、加密、HTTP 客户端等常用能力。
| 特性 | 说明 |
|---|---|
| 轻量零依赖 | 核心不依赖任何 Composer 包,解压即用 |
| MVC 分层 | App/应用 → Controller/Model/View 标准分层 |
| 多应用支持 | App 目录下可创建多个独立应用,每个应用可独立开关 |
| 双路由模式 | 精确路由(自定义)+ 自动路由(约定优于配置) |
| PDO 查询构造器 | 链式调用、预处理语句防注入、事务支持 |
| 自研模板引擎 | 支持继承、区块、包含、循环、条件、自动转义、编译缓存 |
| Token 中间件 | 支持 Bearer/POST/Cookie 多模式,文件/Redis 双存储 |
| Session 认证 | 内置 Auth 类,支持角色权限、CSRF 双 Token |
| DI 容器 | 支持绑定、单例、自动装配、循环依赖检测 |
| CLI 脚手架 | 命令行创建应用、执行脚手架命令 |
| 完整错误页 | 401/403/404 自定义 HTML 错误页 + 调试信息页 |
- PHP 版本:>= 7.4(推荐 8.0+,代码使用了
declare(strict_types=1)、命名参数等特性) - 数据库:MySQL 5.7+ / MariaDB(通过 PDO 连接)
- 缓存:可选 Redis(需 phpredis 扩展),默认文件缓存
- Web 服务器:Nginx(推荐)/ Apache
- PHP 扩展:pdo_mysql、openssl、mbstring、curl、fileinfo(文件上传需要)
- 约定优于配置:自动路由按
/应用名/控制器/方法约定分发,无需手动注册每条路由。 - 零依赖核心:框架核心不捆绑第三方库,降低部署门槛和供应链风险。
- 安全优先:数据库操作全部使用 PDO 预处理;视图默认 HTML 转义;上传文件扫描 PHP 标签;Token 使用 md5 哈希存储。
- 渐进式增强:从最简单的
echo输出到完整的 MVC + ORM + 模板,开发者可按需选用能力。
XiaoPHP/ # 框架根目录
├── App/ # 应用目录(多应用)
│ ├── Index/ # 默认应用「Index」
│ │ ├── Config/ # 应用级配置(可选)
│ │ ├── Controller/ # 控制器
│ │ │ └── Index.php
│ │ ├── Function/ # 应用级函数/类库(自动加载)
│ │ ├── Model/ # 数据模型(自动加载)
│ │ ├── View/ # 视图模板
│ │ │ └── index.html
│ │ └── app.json # 应用配置(开关、版本、描述)
│ └── Loading.php # 应用加载器(扫描并 require 所有应用的 Config/Function/Model)
├── Config/ # 全局配置目录
│ ├── AliyunDns.php # 阿里云 DNS 配置
│ ├── App.php # 应用配置(调试模式、错误格式)
│ ├── Cache.php # 缓存配置
│ ├── Logs.php # 日志配置
│ ├── Middleware.php # 中间件配置
│ ├── Mysql.php # 数据库配置
│ └── Redis.php # Redis 配置
├── Public/ # Web 根目录(唯一对外暴露目录)
│ ├── index.php # 框架入口文件
│ └── nginx.htaccess # Nginx 伪静态配置示例
├── Resources/ # 静态资源目录
│ ├── PHPFile/ # 上传文件存储目录
│ └── 2.txt # 示例静态文件
├── Route/ # 自定义路由注册
│ └── Route.php # 路由定义文件
├── Scaffold/ # CLI 脚手架命令
│ └── Apptools.php # 示例脚手架命令
├── Whitelist/ # 白名单配置
│ └── Whitelist.php # 免认证路由白名单
├── XiaoPHP/ # 框架核心目录
│ ├── console.php # 核心依赖加载器(8 阶段启动)
│ ├── debug.php # 调试信息显示(异常渲染页)
│ ├── start.php # 错误/异常处理注册
│ ├── Middleware.php # 中间件类(Token 认证)
│ ├── Routing.php # 路由分发器
│ ├── Scaffold.php # CLI 脚手架调度器
│ └── System/
│ ├── Error/ # 错误页面
│ │ ├── 401.html
│ │ ├── 403.html
│ │ ├── 404.html
│ │ └── error.php # 全局 Error() 函数
│ └── Tools/
│ ├── App/ # 应用级工具
│ │ ├── AliyunDns.php # 阿里云 DNS API 封装
│ │ ├── MysqlTools.php # PDO 查询构造器 / ORM
│ │ └── RedisTools.php # Redis 封装
│ ├── Config/ # 配置管理
│ │ ├── Conf.php # 配置读取器
│ │ ├── Env.php # .env 环境变量加载器
│ │ ├── Route.php # 路由注册器
│ │ ├── ServiceProvider.php # 服务提供者
│ │ └── Whitelist.php # 白名单管理器
│ ├── encrypt/ # 加密工具
│ │ ├── AesTool.php # AES 加密
│ │ └── RSATool.php # RSA 加密
│ └── Function/ # 核心功能类
│ ├── Auth.php # Session 认证
│ ├── Cache.php # 文件缓存
│ ├── Container.php # DI 容器
│ ├── File.php # 文件上传/管理
│ ├── Helper.php # 辅助工具(大小写不敏感查找)
│ ├── Ipaddr.php # IP 地址获取
│ ├── Json.php # JSON 输出/远程获取
│ ├── Logs.php # 日志记录
│ ├── Model.php # 模型基类
│ ├── Validate.php # 数据验证器
│ ├── View.php # 模板引擎
│ └── Wget.php # HTTP 客户端
├── .env # 环境变量文件
├── composer.json # Composer 配置(可选)
├── composer.lock
└── XiaoCTL # CLI 命令行入口
| 目录 | 作用 | 是否对外暴露 |
|---|---|---|
Public/ |
唯一 Web 根目录,存放入口文件 | 是 |
App/ |
业务应用代码,多应用隔离 | 否 |
Config/ |
全局配置文件 | 否 |
XiaoPHP/ |
框架核心,禁止修改 | 否 |
Resources/ |
静态资源与上传文件 | 部分(通过路由访问) |
Route/ |
自定义路由定义 | 否 |
Whitelist/ |
免认证白名单定义 | 否 |
Scaffold/ |
CLI 脚手架命令 | 否 |
框架在运行时会自动创建以下目录:
| 目录 | 用途 |
|---|---|
Temp/Cache/ |
文件缓存存储 |
Temp/View/ |
视图编译缓存 |
logs/success/ |
成功请求日志 |
logs/error/ |
错误请求日志 |
runtime/sessions/ |
Session 存储(当系统 session 目录不可写时回退) |
XiaoPHP 无需 Composer 安装,直接解压即可:
# 解压框架
unzip XiaoPHP2.1.3.zip
cd XiaoPHP
# 设置目录权限(Web 服务器用户需要写入权限)
chmod -R 755 Public/
chmod -R 777 Temp/ logs/ runtime/ Resources/PHPFile/ 2>/dev/null || true编辑根目录下的 .env 文件:
# MySQL 数据库
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=your_database
DB_USER=your_username
DB_PASSWORD=your_password
# Redis(可选)
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
# 框架调试模式
DEBUG=false安全提示:
.env文件包含敏感信息,必须确保 Web 服务器不会直接访问到该文件。Nginx 配置中应禁止访问.env。
server {
listen 80;
server_name your-domain.com;
root /path/to/XiaoPHP/Public;
index index.php;
# 禁止访问隐藏文件
location ~ /\. {
deny all;
}
# 伪静态(框架已提供 nginx.htaccess)
location / {
try_files $uri $uri/ /index.php?$args;
}
# PHP 解析
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}浏览器访问 http://your-domain.com/,应看到「欢迎使用 XiaoPHP / 轻量级 MVC 框架 / V2.1.3」的默认欢迎页。
在 App/Index/Controller/ 下创建 Hello.php:
<?php
class Hello
{
public function Main()
{
return "Hello, XiaoPHP!";
}
public function world()
{
return json_encode(['msg' => 'Hello World']);
}
}访问:
http://your-domain.com/Index/Hello/Main→ 输出Hello, XiaoPHP!http://your-domain.com/Index/Hello/world→ 输出 JSON
路由规则:
/应用名/控制器名/方法名,方法名不区分大小写。
XiaoPHP 的一次 HTTP 请求经历以下完整流程:
用户请求
│
▼
Nginx/Apache 重写 → Public/index.php
│
├─ 1. 开启输出缓冲 ob_start()
├─ 2. 检测 Session 存储目录可写性,不可写则回退到 runtime/sessions
├─ 3. 启动 Session
├─ 4. 定义常量 SYS_PATH, SYS_ROOT
│
▼
XiaoPHP/console.php(核心加载器,8 阶段启动)
│
├─ Phase 1: 注册自定义自动加载器(spl_autoload_register)
├─ Phase 2: 加载 Composer 自动加载(如存在 vendor/autoload.php)
├─ Phase 3: 注册错误处理(error.php → debug.php → start.php)
├─ Phase 4: 加载核心基础设施(Env → Conf → Container → Helper → Ipaddr)
├─ Phase 5: 加载 Config/*.php 全局配置文件
├─ Phase 6: ServiceProvider::register() 注册核心服务到容器
├─ Phase 7: App/Loading.php 扫描并加载所有应用的 Config/Function/Model
├─ Phase 8: Web 模式加载(Middleware → Whitelist → Route → Routing)
│
▼
XiaoPHP/Routing.php(路由分发)
│
├─ 1. 从容器获取 Middleware 实例,执行 check() 认证检查
├─ 2. 解析 REQUEST_URI,去除查询字符串,转小写
├─ 3. 精确路由匹配(优先)→ Route::find()
│ └─ 命中 → dispatchRoute() → 控制器方法执行 → exit
├─ 4. 静态资源匹配(URL 含点号)→ serveStatic()
│ └─ 命中 → 输出文件 → exit
├─ 5. 自动路由分发 → dispatchAuto()
│ ├─ 解析 /应用/控制器/方法
│ ├─ 查找应用目录(大小写不敏感)
│ ├─ 查找控制器文件(大小写不敏感)
│ ├─ 加载兄弟控制器(*Base.php 优先)
│ ├─ require 控制器文件
│ ├─ 容器实例化控制器
│ ├─ 查找方法(大小写不敏感)
│ └─ 执行方法,echo 返回值 → exit
│
▼
响应输出 + 日志记录
框架在 console.php Phase 1 注册了一个自定义自动加载器,按以下优先级解析类:
| 命名空间前缀 | 映射目录 |
|---|---|
XiaoPHP\System\Config\ |
XiaoPHP/System/Tools/Config/ |
XiaoPHP\System\Tools\App\ |
XiaoPHP/System/Tools/App/ |
XiaoPHP\System\Tools\encrypt\ |
XiaoPHP/System/Tools/encrypt/ |
XiaoPHP\System\ |
XiaoPHP/System/Tools/Function/ |
XiaoPHP\(其他) |
XiaoPHP/ 根目录 |
App\ |
App/ 目录 |
Controller\Scaffold\ |
Scaffold/ 目录 |
此外,Phase 2 会尝试加载 vendor/autoload.php,因此 Composer 包也可正常使用。
注意:控制器类不使用命名空间,直接通过
require_once加载,类名与文件名一致(不区分大小写)。
XiaoPHP\System\Container 是一个功能完整的 DI 容器,支持:
- bind():注册服务绑定,支持闭包或类名
- singleton():注册单例服务
- make():解析服务实例,支持自动装配
- run():注册并立即解析
- has():检查服务是否已注册
- get():获取已缓存的实例
- set():手动设置实例
- clear():清除实例缓存
容器通过反射(Reflection)自动解析构造函数依赖:
- 有类型提示的参数 → 递归从容器解析
- 无类型提示或内置类型 → 使用默认值,无默认值则抛异常
- 支持通过
$parameters数组手动指定参数(优先于自动解析)
容器维护 $resolving 数组,解析服务时标记,解析完成后移除。若检测到循环依赖,抛出 RuntimeException 并打印依赖链。
通过 addAutowireNamespace() 可限制只允许特定命名空间的类自动装配。默认为空,即允许所有类自动装配。
XiaoPHP\System\Config\ServiceProvider 在启动 Phase 6 注册以下核心服务到容器:
| 服务类 | 别名 | 类型 |
|---|---|---|
MysqlTools |
db |
单例 |
RedisTools |
redis |
单例 |
Cache |
cache |
单例 |
Logs |
logs |
单例 |
Middleware |
middleware |
单例 |
Validate |
validate |
单例 |
同时调用 View::setConfig([]) 初始化视图缓存目录。
use XiaoPHP\System\Container;
$container = Container::getInstance();
// 通过类名获取
$db = $container->make(\XiaoPHP\System\Tools\App\MysqlTools::class);
// 通过别名获取
$db = $container->make('db');
// 控制器中可直接 new(控制器由容器实例化,支持构造注入)
class UserController
{
private $db;
public function __construct(\XiaoPHP\System\Tools\App\MysqlTools $db)
{
$this->db = $db;
}
}App/ 目录下每个子目录都是一个独立应用,具有以下结构:
App/
├── Index/ # 应用名
│ ├── Config/ # 应用级配置(自动加载)
│ ├── Controller/ # 控制器
│ ├── Function/ # 应用级函数/类库(自动加载)
│ ├── Model/ # 数据模型(自动加载)
│ ├── View/ # 视图模板
│ └── app.json # 应用配置
{
"name": "Index",
"status": "on",
"description": "status值off为关闭应用文件加载,on启用应用文件加载",
"version": "1.0.0"
}status: "on"→ 启用,自动加载该应用的 Config/Function/Model 目录下的所有 PHP 文件status: "off"→ 关闭,不加载该应用的任何文件(控制器仍可通过路由访问,但不会预加载 Model/Function)
Loading::run() 方法:
- 扫描
App/下所有子目录 - 读取每个应用的
app.json,检查status - 对启用的应用,递归扫描
Config/、Function/、Model/目录 require_once所有 PHP 文件
设计意图:Model 和 Function 类不使用命名空间,通过预加载使其在控制器中可直接
new UserModel()使用。
XiaoPHP 的配置分为三层:
.env(环境变量)→ Config/*.php(配置文件)→ Conf::get()(运行时读取)
- .env:存放环境相关的敏感配置(数据库密码、Redis 密码、调试开关),不纳入版本控制
- Config/*.php:框架配置文件,通过
Env::Load()读取 .env 中的值,提供默认值 - Conf::get():运行时读取配置,带静态缓存
.env 文件位于框架根目录,格式为标准的 KEY=VALUE:
# 注释行以 # 开头
DB_HOST=127.0.0.1
DB_PORT=3306
DEBUG=falseEnv::load() 会自动转换以下值:
true/false→ 布尔值null→ null- 纯数字 → int 或 float
- 带引号的值 → 去除引号
use XiaoPHP\System\Config\Env;
// 加载 .env 并读取指定键(第二个参数为键名)
$value = Env::load(null, 'DB_HOST'); // 返回字符串或 null
// 读取已加载的环境变量(带默认值)
$value = Env::get('CUSTOM_KEY', 'default_value');
// 加载其他环境文件,如 .env.production
Env::load('.production', 'SOME_KEY');注意:
Env::load(null, 'KEY')第一个参数是文件后缀(null 表示.env),第二个参数是要读取的键。若键值为空字符串、null 或 false,返回 null。
每个配置文件返回一个数组,通过 Conf::get('文件名') 读取:
// Config/App.php
return [
"debug" => Env::Load(null, "DEBUG") ?? 'false',
"error" => 'html',
];use XiaoPHP\System\Config\Conf;
// 读取配置(带静态缓存,第二次读取直接返回缓存)
$appConfig = Conf::get('App'); // 返回数组或 null
$mysqlConfig = Conf::get('Mysql');
// 清除配置缓存
Conf::clearCache('App'); // 清除单个
Conf::clearCache(); // 清除全部详见 附录 B 配置项完整清单。
XiaoPHP 的路由按以下优先级依次匹配,命中即终止:
1. 精确路由(自定义路由)→ 最高优先级
2. 静态资源(URL 含点号)→ 次之
3. 自动路由(约定路由)→ 兜底
在 Route/Route.php 中定义:
use XiaoPHP\System\Config\Route;
// 语法:Route::add(请求方法, URL路径, 应用名, 控制器名, 方法名)
Route::add("GET", "/", "Index", "Index"); // 默认方法 Main
Route::add("GET", "/login", "Admin", "Auth", "Login");
Route::add("POST", "/api/user", "Api", "User", "Create");
Route::add("PUT", "/api/user/{id}", "Api", "User", "Update"); // 注意:参数不会自动解析| 参数 | 说明 | 默认值 |
|---|---|---|
$method |
HTTP 方法(GET/POST/PUT/DELETE 等),不区分大小写 | 必填 |
$url |
URL 路径,以 / 开头,自动转小写 |
必填 |
$controller |
应用名(App 目录下的子目录名) | 必填 |
$bootstrap |
控制器文件名(不含 .php) | 必填 |
$action |
方法名 | 'Main' |
路由以 METHOD:URL 为键存储,值格式为 控制器/方法:App/应用名/Controller。分发时解析此字符串。
use XiaoPHP\System\Config\Route;
Route::add("GET", "/hello", "Index", "Hello");
Route::get(); // 获取所有路由数组
Route::find("/hello", "GET"); // 查找路由,返回字符串或 null无需手动注册,按 URL 约定自动分发:
/应用名/控制器名/方法名
| URL 段 | 含义 | 默认值 |
|---|---|---|
| 第 1 段 | 应用名(App 目录下的子目录) | Index |
| 第 2 段 | 控制器名(Controller 目录下的文件名,不含 .php) | Main |
| 第 3 段 | 方法名 | index |
| URL | 应用 | 控制器 | 方法 |
|---|---|---|---|
/ |
Index | Main | index |
/Index |
Index | Main | index |
/Index/Hello |
Index | Hello | Main |
/Index/Hello/world |
Index | Hello | world |
/Admin/User/list |
Admin | User | list |
自动路由的应用名、控制器名、方法名均不区分大小写:
- 目录查找:
Helper::findDirCaseInsensitive() - 文件查找:
Helper::findFileCaseInsensitive() - 方法查找:
Helper::findMethodCaseInsensitive()
自动路由对每一段 URL 进行正则过滤,只保留 a-zA-Z0-9_- 字符,防止路径遍历攻击:
$app = preg_replace("/[^a-zA-Z0-9_-]/", "", $segments[0]);当 URL 路径中包含点号(.)时,框架视为静态资源请求,从 Resources/ 目录提供文件下载:
/logo.png → Resources/logo.png
/PHPFile/xxx.jpg → Resources/PHPFile/xxx.jpg
serveStatic() 函数实现了以下安全校验:
realpath()解析真实路径- 校验真实路径是否在
Resources/目录内(防止路径遍历) - 校验是否为文件
- 设置正确的 MIME 类型、Content-Length、Accept-Ranges
- 以附件方式下载(
Content-Disposition: attachment)
注意:当前静态资源一律以附件下载方式输出,不支持内联显示。如需图片内联显示,建议直接由 Nginx 处理
Resources/目录。
精确路由分发,执行流程:
- 解析路由字符串为
[方法, 控制器/方法, 命名空间] - 验证 HTTP 请求方法是否匹配,不匹配返回 405
- 查找控制器文件(大小写不敏感)
- 加载兄弟控制器(Base 类优先)
- require 控制器文件
- 容器实例化控制器
- 查找方法(大小写不敏感)
- 执行方法并 echo 返回值
- 记录成功日志
自动路由分发,流程与 dispatchRoute 类似,但从 URL 解析应用/控制器/方法。
- 位置:
App/应用名/Controller/控制器名.php - 命名空间:无(全局命名空间)
- 类名:与文件名一致(不含 .php),首字母大写
- 方法:public 方法,可返回字符串或直接 echo
<?php
/**
* 用户控制器
*/
class User
{
public function Main()
{
return "这是用户中心";
}
public function info()
{
$id = $_GET['id'] ?? 0;
return json_encode(['id' => $id, 'name' => '张三']);
}
}控制器目录下的 *Base.php 文件会被优先加载(Helper::loadSiblingControllers() 按文件名含 Base 排序),可用于定义控制器基类:
<?php
// App/Admin/Controller/AdminBase.php
class AdminBase
{
public function __construct()
{
// 权限检查
if (!\XiaoPHP\System\Auth::check()) {
header('Location: /Admin/Auth/Login');
exit;
}
}
protected function json($data, $code = 200)
{
http_response_code($code);
header('Content-Type: application/json');
return json_encode($data, JSON_UNESCAPED_UNICODE);
}
}<?php
// App/Admin/Controller/Dashboard.php
class Dashboard extends AdminBase
{
public function Main()
{
return $this->json(['welcome' => '管理员']);
}
}控制器由 DI 容器实例化,支持构造函数类型提示自动注入:
<?php
use XiaoPHP\System\Tools\App\MysqlTools;
use XiaoPHP\System\Cache;
class Article
{
private $db;
private $cache;
public function __construct(MysqlTools $db, Cache $cache)
{
$this->db = $db;
$this->cache = $cache;
}
public function Main()
{
$list = $this->db->table('article')->order('id', 'DESC')->limit(10)->get();
return json_encode($list);
}
}框架不提供 Request 对象,直接使用 PHP 超全局变量:
$_GET['id'] ?? 0; // GET 参数
$_POST['title'] ?? ''; // POST 参数
$_FILES['file']; // 上传文件
$_SERVER['REQUEST_METHOD']; // 请求方法
$_SERVER['HTTP_X_REQUESTED_WITH']; // AJAX 判断控制器方法的返回值会被 echo 输出:
// 返回字符串
return "Hello World";
// 返回 JSON(需手动设置 Content-Type)
header('Content-Type: application/json');
return json_encode(['code' => 0, 'msg' => 'success']);
// 或使用 Json::encode()(自动设置 header 并 exit)
\XiaoPHP\System\Json::encode(['code' => 0, 'msg' => 'success']);
// 返回视图
$view = new \XiaoPHP\System\View();
return $view->set(['title' => '首页'], '')->show('index');在 .env 中配置:
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=xiaophp
DB_USER=root
DB_PASSWORD=123456Config/Mysql.php 读取这些值并提供默认值。
XiaoPHP\System\Tools\App\MysqlTools 是框架的数据库操作核心,基于 PDO 实现,支持链式调用和预处理语句。
use XiaoPHP\System\Container;
$db = Container::getInstance()->make('db');
// 或
$db = new \XiaoPHP\System\Tools\App\MysqlTools();// 查询所有记录
$users = $db->table('user')->get();
// 带条件查询
$users = $db->table('user')
->where('status', 1)
->where('age', '>=', 18)
->order('id', 'DESC')
->limit(10)
->offset(0)
->get();
// 查询单条记录
$user = $db->table('user')->where('id', 1)->first();
// 模糊查询
$users = $db->table('user')->whereLike('name', '张')->get();
$users = $db->table('user')->whereLike('name', '张', 'left')->get(); // %张
$users = $db->table('user')->whereLike('name', '张', 'right')->get(); // 张%
// 多字段模糊查询(OR)
$users = $db->table('user')
->whereMultiLike(['name', 'email', 'phone'], '关键词')
->get();
// 全字段模糊查询(自动 SHOW COLUMNS)
$users = $db->table('user')->whereFullLike('关键词')->get();
// IN 查询
$users = $db->table('user')->where('id', [1, 2, 3])->get();
// 统计
$count = $db->table('user')->where('status', 1)->count();
$sum = $db->table('order')->where('user_id', 1)->sum('amount');
// 获取最小/最大 ID
$range = $db->table('user')->getMinMaxId(); // ['min_id' => 1, 'max_id' => 100]where() 支持三种调用形式:
// 1. 两参数:where('字段', 值) → 字段 = 值
$db->table('user')->where('status', 1)->get();
// 2. 三参数:where('字段', 操作符, 值)
$db->table('user')->where('age', '>=', 18)->get();
$db->table('user')->where('name', '!=', 'admin')->get();
// 3. 两参数 + 操作符嵌入值:where('字段', '>=18')
$db->table('user')->where('age', '>=18')->get();
// 4. 数组值 → IN 查询
$db->table('user')->where('id', [1, 2, 3])->get();支持的操作符:=、>=、<=、>、<、!=、LIKE(通过 whereLike)。
$id = $db->table('user')->insert([
'name' => '张三',
'email' => 'zhangsan@example.com',
'status' => 1,
'created_at' => date('Y-m-d H:i:s'),
]);
// 返回插入记录的自增 ID// 必须带 WHERE 条件,否则报错
$affected = $db->table('user')
->where('id', 1)
->update([
'name' => '李四',
'email' => 'lisi@example.com',
]);
// 返回影响行数
// SQL 表达式更新(以括号开头的值被视为表达式)
$db->table('user')
->where('id', 1)
->update(['login_count' => '(login_count + 1)']);// 必须带 WHERE 条件,否则报错
$affected = $db->table('user')->where('id', 1)->delete();
// 返回影响行数// 通过 MysqlTools 直接操作
$db->beginTransaction();
try {
$db->table('account')->where('id', 1)->update(['balance' => '(balance - 100)']);
$db->table('account')->where('id', 2)->update(['balance' => '(balance + 100)']);
$db->commit();
} catch (\Exception $e) {
$db->rollBack();
throw $e;
}
// 或通过 Model 基类
$model = new \XiaoPHP\System\Model();
$model->beginTransaction();
// ...
$model->commit();注意:
Model基类通过$this->db->pdo访问 PDO 实例来操作事务。
- 预处理语句:所有查询值通过 PDO 预处理绑定,防止 SQL 注入
- 表名/列名白名单:
sanitizeTableName()和sanitizeColumnName()只保留a-zA-Z0-9_ - UPDATE/DELETE 必须带 WHERE:防止全表更新/删除
- 错误处理:PDO 设置
ERRMODE_EXCEPTION,数据库错误抛出异常
XiaoPHP\System\Model 提供了 ActiveRecord 风格的基类,业务模型继承它即可获得常用 CRUD 方法。
<?php
// App/Index/Model/UserModel.php
class UserModel extends \XiaoPHP\System\Model
{
protected $table = 'user'; // 表名
// 自定义方法
public function findByUsername($username)
{
return $this->db->table($this->table)
->where('username', $username)
->first();
}
public function updateLogin($id, $ip)
{
return $this->db->table($this->table)
->where('id', $id)
->update([
'last_login_ip' => $ip,
'last_login_time' => date('Y-m-d H:i:s'),
]);
}
}| 方法 | 说明 | 返回值 |
|---|---|---|
find($id) |
按主键 ID 查询 | 关联数组 / false |
all($where = []) |
查询所有(可带简单等值条件) | 二维数组 |
create($data) |
插入记录 | 插入 ID / false |
update($id, $data) |
按主键更新 | 影响行数 |
delete($id) |
按主键删除(硬删除) | 影响行数 |
save($data, $id = null) |
自动判断插入/更新 | ID / 影响行数 / false |
paginate($page, $size, $where = []) |
分页查询 | ['data'=>[], 'total'=>N, 'page'=>N, 'size'=>N] |
db() |
返回查询构造器实例 | MysqlTools |
getTable() |
获取表名 | string |
beginTransaction() |
开启事务 | - |
commit() |
提交事务 | - |
rollBack() |
回滚事务 | - |
$userModel = new UserModel();
// 查询
$user = $userModel->find(1);
$users = $userModel->all(['status' => 1]);
// 分页
$result = $userModel->paginate(1, 10, ['status' => 1]);
// 插入
$id = $userModel->create(['name' => '张三', 'status' => 1]);
// 更新
$userModel->update(1, ['name' => '李四']);
// 删除
$userModel->delete(1);
// 复杂查询(通过 db() 获取查询构造器)
$users = $userModel->db()
->where('age', '>=', 18)
->whereLike('name', '张')
->order('id', 'DESC')
->limit(10)
->get();注意:Model 基类在构造函数中
new MysqlTools(),而非从容器获取。这意味着每个 Model 实例都会创建一个新的数据库连接(PDO 默认非持久连接)。如需共享连接,建议从容器获取MysqlTools单例。
XiaoPHP\System\View 是一款自研的编译型模板引擎,具有以下特性:
- 模板继承(@extends / @section / @yield)
- 模板包含(@include)
- 条件判断(@if / @elseif / @else / @endif)
- 循环(@foreach / @for / @forelse)
- 变量输出({{ $var }} 自动转义 / {{! $var }} 不转义)
- 原始 PHP(@php ... @endphp)
- 注释({{-- 注释 --}})
- 编译缓存(模板修改自动重新编译)
- HTML 压缩(非调试模式自动压缩)
- 自动注入 generator meta 标签
<?php
use XiaoPHP\System\View;
class Index
{
public function Main()
{
$view = new View();
return $view->set([
'title' => '欢迎使用 XiaoPHP',
'h1' => '轻量级 MVC 框架',
'version' => 'V2.1.3',
], '')->show('index');
}
}$view->set($data, $path = '', $debug = null);| 参数 | 说明 |
|---|---|
$data |
分配给模板的变量数组 |
$path |
模板子目录路径(View 目录下的子目录),空字符串表示根目录 |
$debug |
单独指定调试模式(null 则使用全局配置) |
$view->show($filename);$filename:模板文件名(不含 .html 扩展名),会经过basename()和正则过滤- 返回渲染后的 HTML 字符串
视图引擎通过 debug_backtrace() 获取调用者文件路径,从而确定当前应用的 View 目录:
调用者文件:App/Index/Controller/Index.php
→ 向上两级目录:App/Index/
→ View 目录:App/Index/View/
→ 模板文件:App/Index/View/{path}/{filename}.html
这意味着视图是按应用隔离的,每个应用的控制器只能渲染自己 View 目录下的模板。
<!-- 自动 HTML 转义(推荐) -->
<h1>{{ $title }}</h1>
<!-- 不转义(用于输出 HTML) -->
<div>{{! $content }}</div>
<!-- 数组访问(点语法或方括号) -->
<p>{{ $user.name }}</p>
<p>{{ $user['name'] }}</p>
<!-- 对象属性访问 -->
<p>{{ $user->name }}</p>变量路径解析支持链式访问:
$user.profile.name会被编译为$user["profile"]["name"] ?? (is_object($user) ? $user->profile->name : null),每一级都有空合并运算符,避免未定义索引报错。
@if ($user['role'] === 'admin')
<p>管理员</p>
@elseif ($user['role'] === 'editor')
<p>编辑</p>
@else
<p>普通用户</p>
@endif支持嵌套条件,
@if的条件表达式支持括号嵌套。
<!-- foreach -->
@foreach ($users as $user)
<li>{{ $user.name }}</li>
@endforeach
<!-- foreach with key -->
@foreach ($users as $id => $user)
<li>{{ $id }}: {{ $user.name }}</li>
@endforeach
<!-- forelse(空数据有默认输出) -->
@forelse ($users as $user)
<li>{{ $user.name }}</li>
@empty
<li>暂无数据</li>
@endforelse
<!-- for 循环 -->
@for ($i = 0; $i < 10; $i++)
<span>{{ $i }}</span>
@endfor布局模板 layout.html:
<!DOCTYPE html>
<html>
<head>
<title>@yield('title')</title>
</head>
<body>
<header>公共头部</header>
<main>@yield('content')</main>
<footer>公共底部</footer>
</body>
</html>子模板 page.html:
@extends('layout')
@section('title')
页面标题
@endsection
@section('content')
<h1>页面内容</h1>
@endsection
@extends('layout')中的布局名用点号分隔目录,如@extends('layouts.main')对应View/layouts/main.html。
@include('header') <!-- 包含 View/header.html -->
@include('partials.nav') <!-- 包含 View/partials/nav.html -->被包含的模板会继承当前模板的所有变量。
@php
$now = date('Y-m-d H:i:s');
echo "当前时间:$now";
@endphp{{-- 这是模板注释,不会输出到 HTML --}}模板编译后缓存到 Temp/View/ 目录,文件名以模板文件路径的 md5 值命名:
Temp/View/
├── a1b2c3d4e5f6...php <!-- 编译后的模板 -->
└── ...
缓存失效条件:模板文件的修改时间晚于编译缓存文件的修改时间。
非调试模式下,视图引擎会自动压缩输出的 HTML:
- 删除标签间多余空白
- 合并多个空格为一个
- 删除 HTML 注释
以下标签内容受保护不被压缩:pre、textarea、script、style,以及内联 style 属性和 on* 事件属性。
$view = new View();
// 关闭自动转义(全局)
$view->setAutoEscape(false);
// 或在模板中使用 {{! $var }} 单个不转义默认开启自动转义,
{{ $var }}会编译为htmlspecialchars($var, ENT_QUOTES, 'UTF-8')。
use XiaoPHP\System\View;
View::setConfig([
'cache_dir' => '/custom/cache/path', // 编译缓存目录
'debug' => false, // 调试模式(不压缩 HTML)
'minify_html' => true, // 是否压缩 HTML
'preserve_tags' => ['pre', 'textarea', 'script', 'style'], // 保护标签
'asset_meta' => [
'name' => 'generator',
'content' => 'MyApp v1.0',
],
]);XiaoPHP 提供两套独立的认证机制:
- Token 中间件(
Middleware类):基于 Token 的无状态认证,适用于 API - Session 认证(
Auth类):基于 Session 的有状态认证,适用于后台管理
中间件在路由分发前执行 Middleware::check(),对所有非白名单路由进行 Token 认证:
- 检查当前 URL 是否在白名单中,是则跳过认证
- 按配置的认证模式依次尝试获取 Token
- 从存储(文件缓存或 Redis)中验证 Token 是否存在
- 验证失败返回 401
Config/Middleware.php:
return [
"storage" => 'cache', // 存储方式:cache | redis
"token_key" => 'token', // Token 参数名(POST/GET 方式)
"auth_mode" => 'bearer,post,cookie', // 认证方式(逗号分隔,按顺序尝试)
"cookie_name" => 'auth_token', // Cookie 名称
"cookie_expire" => '7200', // 默认过期时间(秒)
];| 模式 | 获取方式 | 说明 |
|---|---|---|
bearer |
Authorization: Bearer <token> 请求头 |
推荐,最安全 |
post |
$_POST['token'] |
适用于表单提交 |
cookie |
$_COOKIE['auth_token'] |
适用于 Web 页面 |
get |
$_GET['token'] |
不推荐,Token 会出现在 URL/Referer/历史记录中 |
use XiaoPHP\System\Container;
$middleware = Container::getInstance()->make('middleware');
// 设置 Token(登录成功后调用)
$token = bin2hex(random_bytes(32));
$middleware->setToken($token, [
'user_id' => 1,
'username' => 'admin',
], 7200); // 过期时间秒,默认 7200
// 删除 Token(登出)
$middleware->delToken($token);- 文件缓存:Token 以
token_{md5(token)}为键存储在Temp/Cache/目录 - Redis:Token 以
token_{md5(token)}为键存储在 Redis,Redis 不可用时自动回退到文件缓存
Whitelist/Whitelist.php 中定义免认证路由:
use XiaoPHP\System\Config\Whitelist;
Whitelist::add("/");
Whitelist::add("/Admin/Auth/Login");
Whitelist::add("/Api/Public/*"); // 通配符Whitelist::add($url); // 添加
Whitelist::remove($url); // 移除
Whitelist::clear(); // 清空
Whitelist::exists($url); // 判断是否存在
Whitelist::check($url); // 检查 URL 是否匹配(支持通配符 *)
Whitelist::get(); // 获取所有白名单白名单 URL 中的 * 会被转换为正则 .*,支持前缀匹配:
/Api/Public/*匹配/Api/Public/user、/Api/Public/article/list等
安全警告:不要使用
/*通配符,否则会完全绕过中间件认证。
XiaoPHP\System\Auth 提供基于 Session 的后台认证,内置角色权限和 CSRF 保护。
Auth 类假设存在 UserModel 类(需在 App/应用/Model/ 中定义),具有以下方法和字段:
class UserModel extends \XiaoPHP\System\Model
{
protected $table = 'admin_user';
public function findByUsername($username)
{
return $this->db->table($this->table)
->where('username', $username)
->first();
}
public function updateLogin($id, $ip)
{
return $this->db->table($this->table)
->where('id', $id)
->update([
'last_login_ip' => $ip,
'last_login_time' => date('Y-m-d H:i:s'),
]);
}
}用户表需包含字段:id、username、password、salt、status、nickname、email、role、avatar。
use XiaoPHP\System\Auth;
$result = Auth::attempt($username, $password);
if ($result['success']) {
// 登录成功,自动创建 Session
header('Location: /Admin/Dashboard/Main');
exit;
} else {
echo $result['message']; // 用户不存在 / 账号已被禁用 / 密码错误
}密码校验逻辑:password_verify($password . $salt, $stored_hash),即密码加盐后使用 password_hash() 存储。
Auth::login($userArray); // 直接以指定用户登录(如第三方回调后)Auth::logout(); // 清空 Session、删除 Cookie、销毁会话Auth::check(); // 是否已登录,返回 bool
Auth::user(); // 当前用户数组,未登录返回 null
Auth::id(); // 当前用户 ID,未登录返回 0
Auth::role(); // 当前角色,默认 'guest'内置角色等级:super_admin(3) > admin(2) > editor(1)
Auth::hasRole('admin'); // 是否恰好是 admin 角色
Auth::atLeast('admin'); // 是否至少是 admin 等级
Auth::requireLogin(); // 要求登录,未登录跳转 /Admin/Auth/Login
Auth::requireRole('admin'); // 要求至少 admin 权限,不足返回 403$salt = Auth::generateSalt(); // 生成 32 位十六进制盐值
$hash = Auth::hashPassword($password, $salt); // 加密密码(加盐后 password_hash)// 生成 CSRF Token(存入 Session)
$token = Auth::csrfToken();
// 表单中使用
echo '<input type="hidden" name="_csrf" value="' . $token . '">';
// 校验 CSRF Token(双 Token 机制,支持浏览器二次请求)
if (!Auth::checkCsrf($_POST['_csrf'] ?? null)) {
Error(419, 'CSRF 验证失败');
}CSRF 采用双 Token 机制:
- 每次校验成功后轮换 Token(当前变为上一个,生成新的当前)
- 当前 Token 和上一个 Token 都有效,解决浏览器自动二次请求导致的 Token 不一致问题
- 使用
hash_equals()防时序攻击
Auth 类在启动 Session 时设置:
session.cookie_httponly = 1(防止 XSS 窃取)session.cookie_samesite = Lax(防止 CSRF)- 登录成功后
session_regenerate_id(true)(防止会话固定)
XiaoPHP\System\Cache 提供基于文件的缓存,使用 serialize 序列化数据。
Config/Cache.php:
return [
"dir" => __DIR__ . '/../Temp/Cache/', // 缓存目录
"expire" => '3600', // 默认过期时间(秒)
];use XiaoPHP\System\Container;
$cache = Container::getInstance()->make('cache');
// 设置缓存
$cache->set('key', ['data' => 'value'], 3600); // 第三个参数为过期时间,默认 3600 秒
// 获取缓存
$data = $cache->get('key'); // 返回数据或 null
// 删除缓存
$cache->delete('key');
// 清空所有缓存
$cache->clear();- 缓存文件以
md5($key) . '.txt'命名 - 文件内容为
serialize(['expire' => 时间戳, 'data' => 数据]) - 获取时检查过期时间,过期则删除文件并返回 null
- 写入时使用
LOCK_EX独占锁,防止并发写入冲突
详见 第十七章 Redis 操作。中间件的 Token 存储可切换为 Redis。
Config/Logs.php:
return [
"success" => 'true', // 是否记录成功日志
"error" => 'true', // 是否记录错误日志
];框架在路由分发完成后自动记录日志:
- 成功请求:
Logs::logs(0, 200) - 错误请求:
Logs::logs(1, 404/405/401/...)
也可手动记录:
use XiaoPHP\System\Container;
$logs = Container::getInstance()->make('logs');
$logs->logs(0, 200); // 成功日志
$logs->logs(1, 500); // 错误日志日志文件按日期存储:
logs/
├── success/
│ └── 2026-08-26.logs
└── error/
└── 2026-08-26.logs
每行日志格式:
success 2026-08-26 14:30:00--[192.168.1.1]:/Index/Index/Main-code:200
- IP 获取:优先读取
X-Forwarded-For(取第一个 IP),其次HTTP_CLIENT_IP,最后REMOTE_ADDR - 日志注入防护:请求路径经过
urldecode后移除所有控制字符(\x00-\x1f\x7f)和换行符,防止日志注入 - 并发安全:使用
FILE_APPEND | LOCK_EX追加写入
XiaoPHP\System\Validate 提供 16 种常用数据验证规则,全部为静态方法。验证通过返回原始数据,失败返回 false。
| 方法 | 说明 | 示例 |
|---|---|---|
phone($data) |
中国大陆手机号(1开头,3-9第二位,11位) | Validate::phone('13800138000') |
email($data) |
邮箱地址 | Validate::email('a@b.com') |
username($data) |
用户名(2-20位,字母数字下划线中文) | Validate::username('张三123') |
password($data) |
密码(6-20位,至少含字母和数字) | Validate::password('abc123') |
idCard($data) |
18位身份证号(最后一位可为X) | Validate::idCard('110101199001011234') |
url($data) |
URL(http/https) | Validate::url('https://example.com') |
ip($data) |
IPv4 或 IPv6 | Validate::ip('192.168.1.1') |
date($data) |
日期(YYYY-MM-DD) | Validate::date('2026-08-26') |
numeric($data) |
数字(整数或浮点数) | Validate::numeric('123.45') |
between($data, $min, $max) |
数字范围(闭区间) | Validate::between(5, 1, 10) |
length($data, $min, $max) |
字符串长度(UTF-8,中文算1字符) | Validate::length('你好', 1, 10) |
time($data) |
时间(HH:MM:SS 或 HH:MM) | Validate::time('14:30:00') |
postcode($data) |
中国邮政编码(6位数字) | Validate::postcode('100000') |
qq($data) |
QQ号(5-12位,不以0开头) | Validate::qq('123456789') |
use XiaoPHP\System\Validate;
// 单个验证
if (Validate::phone($_POST['phone'])) {
// 手机号合法
}
// 批量验证
$errors = [];
if (!Validate::username($_POST['username'] ?? '')) {
$errors[] = '用户名格式不正确';
}
if (!Validate::password($_POST['password'] ?? '')) {
$errors[] = '密码需6-20位且包含字母和数字';
}
if (!Validate::email($_POST['email'] ?? '')) {
$errors[] = '邮箱格式不正确';
}
if (!empty($errors)) {
// 验证失败
}XiaoPHP\System\File 提供安全的文件上传功能,存储目录为 Resources/PHPFile/。
use XiaoPHP\System\File;
$file = new File();
// 上传(表单字段名必须为 file)
$result = $file->upload(
['image/jpeg', 'image/png', 'image/gif'], // 允许的 MIME 类型
'avatar', // 子目录(Resources/PHPFile/avatar/)
true // 是否随机命名
);
if ($result['code'] === 0) {
echo "上传成功:" . $result['filename'];
} else {
echo "上传失败:" . $result['msg'];
}| 参数 | 说明 | 默认值 |
|---|---|---|
$allowedTypes |
允许的 MIME 类型数组,空数组表示不限制 | [] |
$subdir |
存储子目录 | '' |
$randomName |
是否使用随机文件名(32位十六进制+扩展名) | true |
// 成功
['code' => 0, 'msg' => '上传成功', 'filename' => 'a1b2c3...jpg', 'metadata' => '/path/原文件名.json']
// 失败
['code' => 1, 'msg' => '失败原因']文件上传实现了多重安全防护:
- 文件大小限制:默认最大 2MB(
$maxSize = 2 * 1024 * 1024) - MIME 类型校验:使用
finfo_open(FILEINFO_MIME_TYPE)检测文件真实 MIME(非扩展名) - PHP 代码扫描:读取文件前 4096 字节,检测是否包含
<?php、<?=、<script language=php>等标签,防止上传 Webshell - 随机文件名:默认使用
bin2hex(random_bytes(16))生成随机文件名,避免路径遍历和覆盖 - 文件名过滤:非随机命名时,文件名只保留
a-zA-Z0-9_\-.字符 - 目录校验:存储目录不存在时自动创建
$file = new File();
// 获取文件信息(大小、修改时间、MIME、MD5、元数据)
$info = $file->getInfo('avatar/a1b2c3.jpg');
// 返回 ['code'=>0, 'info'=>['size'=>..., 'mtime'=>..., 'mime'=>..., 'md5'=>..., 'metadata'=>...]]
// 读取文件内容
$content = $file->read('avatar/a1b2c3.jpg');
// 删除文件(同时删除关联的 .json 元数据文件)
$result = $file->delete('avatar/a1b2c3.jpg');随机命名上传时,会在同目录下生成一个以原始文件名命名的 .json 元数据文件:
{
"filename": "a1b2c3d4e5f6...jpg",
"md5": "d41d8cd98f00b204e9800998ecf8427e"
}删除文件时会自动查找并删除对应的元数据文件。
所有文件操作通过 resolvePath() 方法解析路径:
- 拼接存储根目录和相对路径
realpath()解析真实路径- 校验真实路径是否以存储根目录开头(防止路径遍历
../../etc/passwd)
XiaoPHP\System\Wget 基于 cURL 封装,支持 GET/POST/PUT/DELETE/Download 五种请求方式。
use XiaoPHP\System\Wget;
$response = Wget::get('https://api.example.com/data');
// 成功返回响应体字符串
// 失败返回 ['error' => '错误信息', 'http_code' => 0]
// 带 SSL 验证和自定义选项
$response = Wget::get('https://api.example.com/data', true, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer token'],
CURLOPT_TIMEOUT => 10,
]);// 表单数据(数组自动 http_build_query)
$response = Wget::post('https://api.example.com/login', [
'username' => 'admin',
'password' => '123456',
]);
// JSON 数据(字符串自动设置 Content-Type: application/json)
$response = Wget::post('https://api.example.com/api', json_encode(['key' => 'value']));$response = Wget::put('https://api.example.com/user/1', ['name' => '李四']);
$response = Wget::delete('https://api.example.com/user/1');$result = Wget::download('https://example.com/file.zip', '/path/to/save.zip');
// 成功返回 true
// 失败返回 ['error' => '错误信息', 'http_code' => 0]
// 超时时间 300 秒所有方法都支持以下参数:
| 参数 | 说明 | 默认值 |
|---|---|---|
$url |
请求 URL | 必填 |
$ssl |
是否验证 SSL 证书 | false |
$options |
自定义 cURL 选项数组 | [] |
安全提示:默认
$ssl = false不验证 SSL 证书,生产环境调用 HTTPS 接口建议设为true。
XiaoPHP\System\Json 提供 JSON 相关的快捷方法。
use XiaoPHP\System\Json;
// 直接输出 JSON 并 exit(自动设置 Content-Type: application/json)
Json::encode(['code' => 0, 'msg' => 'success', 'data' => [...]])$data = Json::decode('{"key":"value"}'); // 返回关联数组// 请求远程 URL 并解析 JSON 响应
$data = Json::wdecode('https://api.example.com/data.json');
// 成功返回解析后的数组
// 失败返回 ['error' => '错误信息']XiaoPHP\System\AesTool 提供 AES 加解密,使用 SHA1PRNG 算法派生密钥(兼容 Java 端的 SecureRandom SHA1PRNG 实现)。
use XiaoPHP\System\AesTool;
$key = 'my-secret-key';
$data = 'Hello World';
// 加密(默认 AES-128-ECB)
$encrypted = AesTool::encode($data, $key);
// 返回 Base64 编码的密文
// 解密
$decrypted = AesTool::decode($encrypted, $key);
// 返回原始明文
// 指定算法和 IV
$encrypted = AesTool::encode($data, $key, 'AES-256-CBC', '16-byte-iv-here');
$decrypted = AesTool::decode($encrypted, $key, 'AES-256-CBC', '16-byte-iv-here');_sha1prng() 方法通过迭代 SHA1 哈希生成指定长度的密钥:
- 首次哈希:
sha1(key) - 后续哈希:
sha1(上一次哈希结果) - 拼接直到达到目标长度,截取前 N 字节
目标长度从算法名中提取(如 AES-128-ECB → 128 bit → 16 字节),默认 16 字节。
注意:ECB 模式不安全(相同明文产生相同密文),生产环境建议使用 CBC 或 GCM 模式并提供 IV。
XiaoPHP\System\RSATool 提供 RSA 加解密,使用 OAEP 填充。
use XiaoPHP\System\RSATool;
$rsa = new RSATool();
// 公钥加密($pubKey 为纯 Base64 字符串,不含 PEM 头尾)
$encrypted = $rsa->encode('敏感数据', $publicKeyBase64);
// 返回 Base64 编码的密文
// 私钥解密($privateKey 为完整 PEM 格式私钥)
$decrypted = $rsa->decode($encrypted, $privateKeyPem);
// 返回原始明文encode() 方法接受的公钥为纯 Base64 字符串(不含 -----BEGIN PUBLIC KEY----- 头尾),内部会自动包装为 PEM 格式。
decode() 方法接受的私钥为完整 PEM 格式字符串(包含 -----BEGIN PRIVATE KEY----- 头尾)。
注意:RSA 加密有长度限制(1024 位密钥最多加密 117 字节,2048 位最多 245 字节),适合加密短数据或对称密钥。加密大量数据应使用「RSA 加密 AES 密钥 + AES 加密数据」的混合加密方案。
.env:
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=XiaoPHP\System\Tools\App\RedisTools 封装了 phpredis 扩展,具有以下特性:
- 自动降级:Redis 扩展未安装或连接失败时,所有操作静默返回
$this(链式调用不报错) - 魔术方法:通过
__call()转发所有 Redis 命令到 phpredis 实例 - 管道支持:
pipeline()/exec()
use XiaoPHP\System\Container;
$redis = Container::getInstance()->make('redis');// 字符串
$redis->set('key', 'value', 3600); // 设置(带过期时间)
$value = $redis->get('key'); // 获取
$redis->del('key'); // 删除
$redis->exists('key'); // 是否存在
$redis->incr('counter'); // 自增
$redis->expire('key', 3600); // 设置过期
// 哈希
$redis->hSet('user:1', 'name', '张三');
$name = $redis->hGet('user:1', 'name');
$all = $redis->hGetAll('user:1');
// 列表
$redis->lPush('queue', 'task1');
$task = $redis->rPop('queue');
// 集合 / 有序集合
$redis->sAdd('tags', 'php');
$redis->zAdd('rank', 100, 'user1');由于使用
__call()魔术方法,所有 phpredis 支持的命令都可直接调用,方法名与 Redis 命令一致。
$result = $redis->pipeline()
->set('key1', 'value1')
->set('key2', 'value2')
->get('key1')
->exec();
// 返回所有命令的结果数组if ($redis->isAvailable()) {
// Redis 可用
} else {
// Redis 不可用,使用文件缓存降级
}- 主机:从配置读取,默认
127.0.0.1 - 端口:从配置读取,默认
6379 - 连接超时:2.5 秒
- 密码:配置非空时调用
auth()认证 - 析构时自动
close()关闭连接
XiaoPHP\System\Tools\App\AliyunDns 封装了阿里云云解析 DNS API(2015-01-09 版本),支持域名记录的增删改查。
注意:该类的命名空间为
XiaoPHP\app\tools(小写 app),与其他核心类的XiaoPHP\System\不同,使用时需注意。
Config/AliyunDns.php:
return [
"accessKeyId" => 'your-access-key-id',
"accessKeySecret" => 'your-access-key-secret',
];$dns = new \XiaoPHP\app\tools\AliyunDns();
// 查询所有域名
$result = $dns->domains();
// 返回 ['code'=>200, 'data'=>[...], 'total'=>N]
// 查询指定域名的解析记录
$result = $dns->list('example.com');$result = $dns->add([
'domain' => 'example.com', // 域名
'rr' => 'www', // 主机记录
'record' => 'A', // 记录类型:A/AAAA/CNAME/MX/TXT/NS/SRV
'value' => '1.2.3.4', // 记录值
'ttl' => 600, // TTL,默认 600
'priority' => 10, // MX 记录优先级(可选)
'line' => 'default', // 解析线路(可选)
]);
// 成功返回 ['code'=>200, 'message'=>'Record added successfully', 'data'=>['recordId'=>..., 'requestId'=>...]]$result = $dns->get([
'domain' => 'example.com',
'rr' => 'www',
'record' => 'A',
]);
// 单条返回 ['code'=>200, 'data'=>[...]]
// 多条返回 ['code'=>200, 'data'=>[...], 'total'=>N]
// 未找到返回 ['code'=>404, 'message'=>'Record not found']$result = $dns->update([
'recordId' => '12345', // 记录 ID(必填)
'rr' => 'www', // 可选
'record' => 'A', // 可选
'value' => '2.3.4.5', // 可选
'ttl' => 600, // 可选
]);// 方式一:通过 recordId 删除
$result = $dns->del(['recordId' => '12345']);
// 方式二:通过 domain + rr 自动查找 recordId 后删除
$result = $dns->del([
'domain' => 'example.com',
'rr' => 'www',
]);$result = $dns->setStatus([
'recordId' => '12345',
'status' => 'disable', // enable | disable
]);$result = $dns->remark([
'recordId' => '12345',
'remark' => '服务器A',
]);API 请求使用阿里云标准的 HMAC-SHA1 签名:
- 合并公共参数和业务参数
- 按参数名字典排序
- 构造规范化查询字符串
- 构造待签名字符串:
GET&%2F&{urlencode(查询字符串)} - 使用
accessKeySecret + &作为密钥,HMAC-SHA1 签名后 Base64 编码
XiaoPHP 的错误处理由三个文件协同工作:
| 文件 | 作用 |
|---|---|
XiaoPHP/start.php |
注册错误/异常/致命错误处理器 |
XiaoPHP/debug.php |
调试信息渲染(异常详情页) |
XiaoPHP/System/Error/error.php |
全局 Error() 函数(HTTP 错误响应) |
在启动 Phase 3 注册三个处理器:
- register_shutdown_function:捕获致命错误(E_ERROR、E_PARSE、E_CORE_ERROR、E_COMPILE_ERROR),转为
ErrorException并调用displayDebugInfo() - set_error_handler:将所有 PHP 错误(warning、notice 等)转为
ErrorException抛出 - set_exception_handler:捕获未处理的异常,调用
displayDebugInfo()
当 Config/App.php 中 debug = true 时,异常会渲染为详细的调试页面,包含:
- 异常类型和消息
- 出错文件和行号
- 代码片段(出错行前后各 7 行,高亮出错行)
- 完整调用堆栈
- 请求上下文(GET/POST/SESSION/COOKIE/FILES/SERVER,可折叠)
- 客户端 IP、PHP 版本、当前时间
- 返回 / 首页按钮
当 debug = false 时:
- 异常消息写入
error_log - 返回 500 状态码
- 显示「系统繁忙,请稍后再试」的简洁页面
Error($code, $info) 用于输出 HTTP 错误响应:
Error(404, '页面不存在');
Error(401, '未授权');
Error(403, '权限不足');
Error(405, '请求方法不允许');
Error(500, '服务器内部错误');由 Config/App.php 的 error 配置决定:
- html 格式:查找
XiaoPHP/System/Error/{code}.html并包含输出,变量$code和$info在模板中可用 - json 格式(默认):输出 JSON
{
"code": 404,
"date": "2026-08-26 14:30:00",
"data": {
"msg": "页面不存在"
}
}框架内置 401、403、404 三个 HTML 错误页,可直接编辑 XiaoPHP/System/Error/ 下的对应文件自定义样式。
如需新增错误码页面(如 500.html),创建对应文件即可,Error(500, ...) 会自动查找。
编辑 .env:
DEBUG=true或直接编辑 Config/App.php:
return [
"debug" => 'true', // 开启调试
"error" => 'html',
];生产环境必须关闭调试模式,否则会泄露代码、堆栈、服务器环境等敏感信息。
XiaoCTL 是框架的命令行入口文件,位于框架根目录:
php XiaoCTL它定义了 CTL 常量,使 console.php 跳过 Web 模式的中间件/路由加载,然后加载 Scaffold.php 调度器。
XiaoPHP/Scaffold.php 解析命令行参数,调用 Controller\Scaffold\ 命名空间下的类方法:
# 语法:php XiaoCTL 类名:方法名 [参数]
php XiaoCTL System:hello "Hello World"执行流程:
- 解析
类名:方法名 - 类名和方法名经过正则过滤(只保留
a-zA-Z0-9_) - 拼接为
\Controller\Scaffold\{类名} - 检查类和方法是否存在
- 静态调用方法,传入参数
- 输出返回值
在 Scaffold/ 目录下创建 PHP 文件:
<?php
// Scaffold/Dbtools.php
namespace Controller\Scaffold;
class Dbtools
{
public static function migrate($param)
{
// 数据库迁移逻辑
echo "迁移完成: $param\n";
}
public static function seed($param)
{
// 数据填充逻辑
echo "填充完成: $param\n";
}
}使用:
php XiaoCTL Dbtools:migrate users
php XiaoCTL Dbtools:seed adminApp/Loading.php 同时支持 CLI 模式创建新应用:
php App/Loading.php add进入交互式命令行:
XiaoPHP V 2.1.0
命令模式:创建应用-输入Exit退出
请输入应用名称:Admin
已生成默认配置文件: /path/App/Admin/app.json
请输入应用名称:Exit
退出程序。
创建的应用目录结构:
App/Admin/
├── Config/
├── Controller/
├── Function/
├── Model/
├── View/
└── app.json
app.json 默认内容:
{
"name": "Admin",
"status": "on",
"description": "status值off为关闭应用文件加载,on启用应用文件加载",
"version": "1.0.0"
}| 安全维度 | 评级 | 说明 |
|---|---|---|
| SQL 注入 | 优 | 全部 PDO 预处理,表名/列名白名单 |
| XSS | 良 | 视图默认自动转义,但控制器直接 echo 需自行处理 |
| CSRF | 良 | Auth 类内置双 Token CSRF,但 Token 中间件模式无 CSRF |
| 文件上传 | 优 | MIME 检测 + PHP 代码扫描 + 随机文件名 + 路径校验 |
| 路径遍历 | 优 | 静态资源和文件管理均有 realpath 校验 |
| 认证安全 | 良 | Session 认证较完善;Token 认证无刷新机制、无速率限制 |
| 密码存储 | 优 | password_hash + 盐值 |
| 会话安全 | 优 | HttpOnly + SameSite + session_regenerate_id |
| 日志注入 | 优 | 请求路径移除控制字符 |
| 信息泄露 | 中 | 调试模式关闭时安全;.env 需确保 Nginx 禁止访问 |
| SSL 验证 | 中 | HTTP 客户端默认不验证 SSL 证书 |
| 依赖安全 | 优 | 零 Composer 依赖,无供应链风险 |
位置:XiaoPHP/Middleware.php
描述:Token 验证失败直接返回 401,无失败计数和 IP 封禁机制,可能遭受暴力枚举攻击。
建议:
// 在 check() 中添加失败计数
$failKey = 'login_fail_' . md5(Ipaddr::get());
$failCount = (int)$this->cache->get($failKey);
if ($failCount >= 10) {
\Error(429, '请求过于频繁,请稍后再试');
}
// 验证失败时累加
if (!$valid) {
$this->cache->set($failKey, $failCount + 1, 300);
\Error(401, '令牌无效或已过期');
}位置:XiaoPHP/System/Tools/Function/Wget.php, Json.php
描述:$ssl 参数默认为 false,即 CURLOPT_SSL_VERIFYPEER = false,存在中间人攻击风险。
建议:生产环境调用外部 API 时显式传入 true:
Wget::get('https://api.example.com', true);位置:XiaoPHP/Routing.php → serveStatic()
描述:所有静态资源都设置 Content-Disposition: attachment,图片等资源无法在浏览器内联显示。
建议:根据 MIME 类型决定是否内联:
$inlineMimes = ['image/png', 'image/jpeg', 'image/gif', 'image/webp', 'application/pdf'];
if (in_array($mime, $inlineMimes)) {
header('Content-Disposition: inline; filename="' . basename($path) . '"');
} else {
header('Content-Disposition: attachment; filename="' . basename($path) . '"');
}位置:XiaoPHP/System/Tools/Function/Model.php
描述:Model::__construct() 中 new MysqlTools(),而非从容器获取单例。每个 Model 实例创建一个新 PDO 连接,高并发下可能耗尽数据库连接。
建议:
public function __construct()
{
$this->db = Container::getInstance()->make('db');
}位置:XiaoPHP/System/Tools/encrypt/AesTool.php
描述:默认算法为 AES-128-ECB,ECB 模式相同明文产生相同密文,不安全。
建议:使用 CBC 或 GCM 模式并提供随机 IV,将 IV 与密文一起存储/传输。
描述:.env 位于框架根目录,如果 Web 根目录配置错误(指向框架根目录而非 Public/),.env 可被直接下载。
建议:
- 确保 Nginx
root指向Public/目录 - 在 Nginx 配置中添加:
location ~ /\.env { deny all; }
位置:XiaoPHP/System/Tools/Function/Logs.php, Ipaddr.php
描述:直接信任 X-Forwarded-For 头的第一个 IP,客户端可伪造此头进行日志污染或绕过 IP 限制。
建议:在可信代理后面使用时,应从右往左跳过已知代理 IP,取第一个非代理 IP。
- 生产环境关闭调试模式(
DEBUG=false) - Nginx root 指向
Public/,禁止访问.env和隐藏文件 - 数据库使用强密码,最小权限原则
- Redis 设置密码,禁止外网访问
- 上传目录禁止执行 PHP(Nginx 配置
location ~* \.php$ { deny all; }应用于 Resources 目录) - Session Cookie 设置
secure(HTTPS 环境) - 定期清理
Temp/Cache/、Temp/View/、logs/目录 - 外部 API 调用启用 SSL 验证
- Token 认证添加速率限制
| 特性 | 说明 |
|---|---|
| 自定义自动加载 | 无 Composer 开销,类映射直接 require |
| 配置静态缓存 | Conf::get() 第二次读取直接返回静态缓存 |
| 视图编译缓存 | 模板编译为 PHP 文件,修改才重新编译 |
| 视图 HTML 压缩 | 非调试模式自动压缩,减少传输体积 |
| 服务单例 | 数据库/Redis/缓存/日志等核心服务在容器中为单例 |
| 数据库预处理 | PDO 预处理,支持查询缓存 |
| Redis 可选 | 高并发场景可切换 Token 存储到 Redis |
App/Loading.php 在每次请求时递归扫描所有启用应用的 Config/、Function/、Model/ 目录并 require_once 所有 PHP 文件。应用数量多或文件多时,启动开销较大。
优化建议:
- 使用类Map缓存(将文件路径映射序列化到缓存文件)
- 或改用 Composer 自动加载(框架已支持 vendor/autoload.php)
如安全审计所述,每个 Model 实例创建新 PDO 连接。改为容器单例可显著减少连接数。
Cache 类每次 get() 都需要 file_get_contents + unserialize,高并发下磁盘 I/O 可能成为瓶颈。
优化建议:高频缓存切换到 Redis。
Temp/View/ 目录下的编译缓存文件不会自动清理,模板大量修改后可能积累大量过期缓存。
优化建议:定期清理 Temp/View/ 目录,或实现基于访问时间的 LRU 淘汰。
whereFullLike() 每次调用执行 SHOW COLUMNS 获取表结构,然后对所有字段做 LIKE '%keyword%' 查询,无法使用索引,大表下性能极差。
优化建议:使用 whereMultiLike() 指定字段,或使用全文索引(FULLTEXT)。
- 启用 OPcache:PHP OPcache 可显著提升性能,确保
opcache.enable=1、opcache.memory_consumption=128 - 使用 Redis:将 Token 存储和高频缓存切换到 Redis
- 数据库连接池:高并发场景考虑使用持久连接(
PDO::ATTR_PERSISTENT)或连接池 - 静态资源由 Nginx 处理:图片、CSS、JS 等静态资源直接由 Nginx 提供,不经过 PHP
- 日志采样:高流量下成功日志可采样记录,减少磁盘写入
- Gzip 压缩:Nginx 启用
gzip on,减少响应传输体积
在 PHP 8.1 + OPcache + Nginx 环境下,简单的 echo 'Hello' 控制器预计可达到:
- 单进程:~2000-3000 QPS
- 4 进程 FPM:~8000-12000 QPS
带数据库查询的接口性能取决于数据库响应时间,通常在 100-500 QPS 范围。
- 在
Config/目录创建MyConfig.php:
<?php
return [
"key1" => "value1",
"key2" => ["nested" => "value"],
];- 在代码中读取:
$config = \XiaoPHP\System\Config\Conf::get('MyConfig');编辑 XiaoPHP/System/Tools/Config/ServiceProvider.php,在 register() 方法中添加:
// 注册单例
$container->singleton(MyService::class, function () {
return new MyService();
});
// 添加别名
$container->bind('myservice', function ($c) {
return $c->make(MyService::class);
});当前框架的中间件是硬编码在 Routing.php 中的 Middleware::check()。如需添加自定义中间件逻辑,有两种方式:
<?php
// App/Index/Function/MyMiddleware.php
class MyMiddleware extends \XiaoPHP\System\Middleware
{
public function check(): void
{
// 自定义逻辑
parent::check();
}
}在控制器基类的构造函数中执行中间件逻辑(详见 7.3 控制器基类)。
<?php
// App/Index/Function/MyValidate.php
class MyValidate extends \XiaoPHP\System\Validate
{
public static function mobile($data)
{
// 自定义验证逻辑
return preg_match('/^1[3-9]\d{9}$/', $data) ? $data : false;
}
}详见 第二十章 CLI 命令行。
框架的自动加载器已支持 App\ 命名空间映射到 App/ 目录。如需添加新的命名空间映射,编辑 XiaoPHP/console.php Phase 1 的自动加载器:
// 在 spl_autoload_register 回调中添加
$prefix = 'MyNamespace\\';
if (strpos($class, $prefix) === 0) {
$shortName = substr($class, strlen($prefix));
$file = __DIR__ . '/../MyNamespace/' . str_replace('\\', '/', $shortName) . '.php';
if (file_exists($file)) { require_once $file; return; }
}或使用 Composer 的 autoload 配置(推荐):
{
"autoload": {
"psr-4": {
"MyNamespace\\": "MyNamespace/"
}
}
}在 XiaoPHP/System/Error/ 目录下创建 {code}.html 文件,模板中可使用 $code 和 $info 变量。
App/
├── Admin/ # 后台管理应用
│ ├── Controller/
│ │ ├── AdminBase.php # 后台控制器基类(权限检查)
│ │ ├── Auth.php # 登录/登出
│ │ └── Dashboard.php # 仪表盘
│ ├── Model/
│ │ └── AdminUserModel.php
│ └── View/
│ ├── layout.html # 后台布局
│ └── dashboard/
├── Api/ # API 应用
│ ├── Controller/
│ │ ├── ApiBase.php # API 基类(JSON 响应、Token 可选)
│ │ └── User.php
│ └── Model/
└── Index/ # 前台应用
├── Controller/
└── View/
- 瘦控制器,胖模型:业务逻辑放在 Model 或 Function 类中,控制器只负责接收请求、调用模型、返回响应
- 使用基类:公共逻辑(权限检查、JSON 响应格式)放在基类
- 验证输入:使用
Validate类验证用户输入 - 避免直接操作超全局变量:可封装 Request 类(框架未提供,可自行扩展)
- 使用预处理:框架已默认使用,不要自行拼接 SQL
- 必须带 WHERE:UPDATE/DELETE 操作必须带 WHERE 条件
- 使用事务:多表写入操作使用事务保证一致性
- **避免 SELECT ***:查询构造器默认
SELECT *,如需指定字段可使用原生 SQL($db->pdo->query()) - 分页查询:大列表必须分页,避免一次性加载大量数据
- 索引优化:WHERE、ORDER BY、JOIN 的字段建立索引
- 默认自动转义:使用
{{ $var }}输出变量,不要随意使用{{! $var }} - 布局继承:公共部分放在布局模板中,使用
@extends+@section - 模板包含:复用的组件(导航、页脚)使用
@include - 避免复杂逻辑:模板中只做简单的条件和循环,复杂逻辑放在控制器
- 清理编译缓存:模板修改后如未生效,删除
Temp/View/目录
详见 第二十一章 安全审计 的安全最佳实践清单。
- Web 根目录:Nginx
root必须指向Public/,框架其他目录不可对外访问 - 文件权限:
Public/设为 755,Temp/、logs/、runtime/、Resources/PHPFile/设为 Web 服务器用户可写 - 环境隔离:开发、测试、生产使用不同的
.env文件 - 版本控制:
.env、Temp/、logs/、runtime/加入.gitignore - HTTPS:生产环境强制 HTTPS,Session Cookie 设置
secure - 备份:定期备份数据库和上传文件
A:检查以下几点:
- Nginx 伪静态是否配置(
try_files $uri $uri/ /index.php?$args;) - 控制器文件是否存在于
App/应用名/Controller/目录 - 控制器类名是否与文件名一致
- 方法是否为 public
A:
- 检查
.env中的数据库配置是否正确 - 确认 MySQL 服务是否启动
- 确认数据库用户权限(是否允许从当前主机连接)
- 检查 PHP 是否安装了
pdo_mysql扩展(php -m | grep pdo_mysql)
A:删除 Temp/View/ 目录下的编译缓存文件,框架会在下次请求时重新编译。
rm -rf Temp/View/*A:编辑该应用目录下的 app.json,将 status 改为 "off":
{
"name": "OldApp",
"status": "off"
}A:在 Whitelist/Whitelist.php 中添加白名单:
Whitelist::add("/Api/Public/Login");
Whitelist::add("/Api/Public/*"); // 通配符A:
// 方式一:构造函数注入(推荐)
class UserController
{
private $db;
public function __construct(\XiaoPHP\System\Tools\App\MysqlTools $db)
{
$this->db = $db;
}
}
// 方式二:容器获取
$db = \XiaoPHP\System\Container::getInstance()->make('db');
// 方式三:直接 new(不推荐,每次新建连接)
$db = new \XiaoPHP\System\Tools\App\MysqlTools();A:
// 方式一:手动设置 header(控制器方法返回值会被 echo)
header('Content-Type: application/json');
return json_encode(['code' => 0, 'msg' => 'success']);
// 方式二:使用 Json::encode()(自动 exit)
\XiaoPHP\System\Json::encode(['code' => 0, 'msg' => 'success']);A:框架默认限制 2MB,需修改两处:
XiaoPHP/System/Tools/Function/File.php中的$maxSize属性php.ini中的upload_max_filesize和post_max_size
A:编辑 Config/Middleware.php:
return [
"storage" => 'redis', // 改为 redis
// ...
];确保 .env 中 Redis 配置正确且 phpredis 扩展已安装。
A:编辑 .env:
DEBUG=true开启后异常会显示详细的调试信息页。生产环境务必关闭。
A:
php App/Loading.php add按提示输入应用名称,自动创建目录结构和 app.json。
A:框架入口会自动检测 Session 存储目录是否可写,不可写时回退到 runtime/sessions/ 目录。确保该目录存在且 Web 服务器用户有写入权限。
- 当前版本:V2.1.3
- 入口文件标注版本:V2.1.2(Public/index.php 注释)
- 应用创建命令标注版本:V2.1.0(App/Loading.php 输出)
- 欢迎页版本:V2.1.3(App/Index/Controller/Index.php)
| 文件 | 标注版本 |
|---|---|
Public/index.php |
V2.1.2 |
App/Loading.php |
V2.1.0 |
App/Index/Controller/Index.php |
V2.1.3 |
View.php 注入 meta |
XiaoPHP V2.1.3 |
从旧版本升级到 V2.1.3 时,注意以下变更:
console.php重构为 8 阶段启动加载器,替换了旧的零散 require 方式View.php模板引擎大幅优化,新增 CSS/JS 语法保护、HTML 压缩优化MysqlTools.php新增whereMultiLike()、whereFullLike()、getMinMaxId()、sum()方法Middleware.php支持 Redis 存储和多认证模式File.php新增文件上传安全扫描Auth.php新增 CSRF 双 Token 机制Container.php新增自动装配和循环依赖检测
- 备份现有项目(代码 + 数据库)
- 替换
XiaoPHP/目录为新版本 - 对比
Config/目录,合并新增配置项 - 检查
Public/index.php是否需要更新 - 删除
Temp/View/编译缓存 - 测试所有功能
| 常量 | 定义位置 | 值 |
|---|---|---|
SYS_PATH |
Public/index.php, XiaoCTL |
框架核心目录绝对路径(.../XiaoPHP) |
SYS_ROOT |
Public/index.php, XiaoCTL |
Web 根目录绝对路径(.../Public) |
CTL |
XiaoCTL |
CLI 模式标识(true) |
| 函数 | 定义位置 | 说明 |
|---|---|---|
Error($code, $info) |
XiaoPHP/System/Error/error.php |
输出 HTTP 错误响应并 exit |
displayDebugInfo(Throwable $e) |
XiaoPHP/debug.php |
渲染调试异常页 |
框架不封装 Request/Response 对象,直接使用 PHP 超全局变量:
$_GET/$_POST/$_FILES/$_COOKIE/$_SESSION/$_SERVER
| 变量名 | 说明 | 默认值 |
|---|---|---|
DB_HOST |
MySQL 主机 | localhost |
DB_PORT |
MySQL 端口 | 3306 |
DB_NAME |
数据库名 | xiaophp |
DB_USER |
数据库用户 | root |
DB_PASSWORD |
数据库密码 | 123456 |
REDIS_HOST |
Redis 主机 | 127.0.0.1 |
REDIS_PORT |
Redis 端口 | 6379 |
REDIS_PASSWORD |
Redis 密码 | (空) |
DEBUG |
调试模式 | false |
| 配置项 | 说明 | 可选值 |
|---|---|---|
debug |
调试模式 | true / false |
error |
错误响应格式 | html / json |
| 配置项 | 说明 |
|---|---|
host |
数据库主机 |
port |
数据库端口 |
user |
数据库用户名 |
password |
数据库密码 |
dbname |
数据库名 |
| 配置项 | 说明 |
|---|---|
host |
Redis 主机 |
port |
Redis 端口 |
password |
Redis 密码 |
| 配置项 | 说明 | 默认值 |
|---|---|---|
dir |
缓存文件目录 | Temp/Cache/ |
expire |
默认过期时间(秒) | 3600 |
| 配置项 | 说明 | 可选值 |
|---|---|---|
success |
是否记录成功日志 | true / false |
error |
是否记录错误日志 | true / false |
| 配置项 | 说明 | 默认值 |
|---|---|---|
storage |
Token 存储方式 | cache / redis |
token_key |
Token 参数名 | token |
auth_mode |
认证方式(逗号分隔) | bearer,post,cookie |
cookie_name |
Cookie 名称 | auth_token |
cookie_expire |
默认过期时间(秒) | 7200 |
| 配置项 | 说明 |
|---|---|
accessKeyId |
阿里云 AccessKey ID |
accessKeySecret |
阿里云 AccessKey Secret |
| 类名 | 文件路径 | 说明 |
|---|---|---|
Container |
System/Tools/Function/Container.php |
DI 容器 |
Helper |
System/Tools/Function/Helper.php |
辅助工具 |
Ipaddr |
System/Tools/Function/Ipaddr.php |
IP 获取 |
Model |
System/Tools/Function/Model.php |
模型基类 |
View |
System/Tools/Function/View.php |
模板引擎 |
Cache |
System/Tools/Function/Cache.php |
文件缓存 |
Logs |
System/Tools/Function/Logs.php |
日志 |
Validate |
System/Tools/Function/Validate.php |
验证器 |
Auth |
System/Tools/Function/Auth.php |
Session 认证 |
Json |
System/Tools/Function/Json.php |
JSON 工具 |
File |
System/Tools/Function/File.php |
文件管理 |
Wget |
System/Tools/Function/Wget.php |
HTTP 客户端 |
AesTool |
System/Tools/encrypt/AesTool.php |
AES 加密 |
RSATool |
System/Tools/encrypt/RSATool.php |
RSA 加密 |
Middleware |
Middleware.php |
Token 中间件 |
| 类名 | 文件路径 | 说明 |
|---|---|---|
Conf |
System/Tools/Config/Conf.php |
配置读取器 |
Env |
System/Tools/Config/Env.php |
环境变量加载器 |
Route |
System/Tools/Config/Route.php |
路由注册器 |
Whitelist |
System/Tools/Config/Whitelist.php |
白名单管理器 |
ServiceProvider |
System/Tools/Config/ServiceProvider.php |
服务提供者 |
| 类名 | 命名空间 | 文件路径 | 说明 |
|---|---|---|---|
MysqlTools |
XiaoPHP\System\Tools\App |
System/Tools/App/MysqlTools.php |
PDO 查询构造器 |
RedisTools |
XiaoPHP\System\Tools\App |
System/Tools/App/RedisTools.php |
Redis 封装 |
AliyunDns |
XiaoPHP\app\tools |
System/Tools/App/AliyunDns.php |
阿里云 DNS |
| 类名 | 文件路径 | 说明 |
|---|---|---|
Loading |
App/Loading.php |
应用加载器 |
Command |
App/Loading.php |
CLI 应用创建命令 |
| 类名 | 文件路径 | 说明 |
|---|---|---|
System |
Scaffold/Apptools.php |
示例脚手架命令 |