muzk6 / sparrow
Sparrow Framework
Installs: 21
Dependents: 0
Suggesters: 0
Security: 0
Stars: 5
Watchers: 1
Forks: 0
Open Issues: 1
Type:project
pkg:composer/muzk6/sparrow
Requires
- php: ^7.1
- ext-json: *
- duncan3dc/blade: ^4.5
- php-amqplib/php-amqplib: ^2.9
- pimple/pimple: ^3.2
Suggests
- ext-pcntl: Required to use the Pcntl
- ext-pdo: Required to use the PDO MySql
- ext-redis: Required to use the Redis
- ext-xdebug: Required to use the Xdebug
- phpunit/phpunit: Required to use the Unit testing
- swiftmailer/swiftmailer: Required to use the Swift_Mailer
- tideways_xhprof: Required to use the tideways_xhprof
This package is auto-updated.
Last update: 2025-11-08 08:55:46 UTC
README
安装
创建项目
composer create-project --prefer-dist muzk6/sparrow your-projectname
请确保项目目录 data 有写权限
使用 docker-compose 部署开发环境
docker-compose up -d nginx php-fpm部署基础环境- 或者部署完整环境
docker-compose up -d,支持数据库、缓存、队列等服务
不建议生产环境使用 docker 部署
访问链接
- http://localhost:37061/ 项目主页
- http://localhost:37062/ 运维后台
- 登录密码位于
app/Routes/OPS/index.php常量LOGIN_PASSWD,可自行修改 - 详情查看后面章节
OPS 运维与开发
- 登录密码位于
- http://localhost:37063/ 业务后台
所有后台访问都有 IP, Cookie 白名单,配置位于 config/.../whitelist.php
目录结构
| Dir | Desc |
|---|---|
| app | 业务逻辑 |
| app/Providers | 容器服务提供层 |
| app/Routes | 路由层(控制器层) |
| app/Services | 业务服务层 |
| cli | 命令行脚本 |
| config | 配置文件,通用配置放在当前目录下 |
| config/dev | dev环境的配置 |
| core | 框架文件 |
| data | 缓存、日志数据目录,需要写权限 |
| lang | 国际化多语言 |
| private | 私有 Web 入口目录,配置的域名不应该被外网访问 |
| private/admin | Admin 后台入口 |
| private/rpc | RPC 入口目录 |
| public | 公有 Web 入口目录 |
| tests | 单元测试 |
| vendor | Composer库 |
| views | 视图文件 |
| workers | worker 文件 |
常用常量
| Name | Desc |
|---|---|
| TIME | $_SERVER['REQUEST_TIME'], 脚本启动时间,不能在 worker 里使用,否则不会变化 |
| IS_POST | 是否为 POST 请求 |
| IS_GET | 是否为 GET 请求 |
| IS_DEV | 是否为开发环境 |
| APP_LANG | 当前语言 eg. zh_CN |
| APP_ENV | 服务器环境 eg. dev |
| TEST_ENV | 是否为单元测试的环境 |
| PATH_APP | 项目业务目录 |
| PATH_ROUTES | 路由目录 |
| PATH_PUBLIC | 网站入口路径 |
| PATH_DATA | 数据目录,需要有写权限 |
| PATH_LOG | 日志目录 |
测试文件
cli/test.php为php-cli测试脚本public/test.php为php-cgi测试入口
路由
注册路由
route_get()注册回调 GET 请求route_get_re()正则匹配
route_post()注册回调 POST 请求route_post_re()正则匹配
route_any()注册回调任何请求route_any_re()正则匹配
route_middleware()注册路由中间件,顺序执行,组内优先route_group()路由分组,隔离中间件
用例
route_middleware(function () { echo '中间件a1'; }); route_middleware(function () { echo '中间件a2'; }); route_group(function () { route_middleware(function () { echo '中间件b1'; }); route_get('/', function () { return 'Just Do It!'; }); }); route_middleware(function () { echo '中间件a3'; });
输出如下:
中间件b1
中间件a1
中间件a2
Just Do It!
中间件a3
- 基本用法参考
app/Routes/index.php - 高级用法参考测试用例:
tests/feature/router_advanced.php - 如果要实现 MVC 的 Controller::action 模式,可参考
tests/feature/router_mvc.php
自定义404
在调用 app(Router::class)->dispatch() 之前像下面例子设置 404 回调:
app(Router::class)->setStatus404Handler(function () { return '自定义404页面'; // return view('...') });
请求参数
获取、过滤、表单验证、类型强转 请求参数
$_GET,$_POST支持payload
- 以下的验证失败时会抛出异常 \Core\AppException
不验证,一个一个获取
$firstName = input('post.first_name'); $lastName = input('last_name'); var_dump($firstName, $lastName);exit;
不验证,统一获取
input('post.first_name'); input('last_name'); $request = request(); var_dump($request);exit;
部分验证,一个一个获取
$firstName = input('post.first_name'); $lastName = validate('last_name')->required()->get('名字'); var_dump($firstName, $lastName);exit;
部分验证,统一获取
input('post.first_name'); validate('last_name')->required()->setTitle('名字'); $request = request(); var_dump($request);exit;
串联短路方式验证(默认)
遇到验证不通过时,立即终止后面的验证
validate('post.first_name')->required(); validate('last_name')->required()->setTitle('名字'); $request = request(); // 以串联短路方式验证
串联结果
{
"s": false,
"c": 10001000,
"m": "参数错误",
"d": {
"first_name": "不能为空"
}
}
并联验证
即使前面的验证不通过,也会继续验证后面的字段
validate('post.first_name')->required(); validate('last_name')->required()->setTitle('名字'); $request = request(true); // 以并联方式验证
并联结果
{
"s": false,
"c": 10001000,
"m": "参数错误",
"d": {
"first_name": "不能为空",
"last_name": "名字不能为空"
}
}
input() 参数说明
'get.foo:i' 中的类型转换i为整型,其它类型为:
| Name | Type |
|---|---|
| i | int |
| s | string |
| b | bool |
| a | array |
| f | float |
| d | double |
PDO 数据库
可以配置 MySQL, SQLite 等 PDO 支持的数据库
- 配置文件
config/.../mysql.php - 用例参考
tests/feature/db.php
如果想同时使用 SQLite 等数据库, 参考复制 mysql.php 为新的数据库配置文件,按需配置 dsn,再注册容器即可(参考 \Core\ServiceProvider 的 PDOEngine)
helpers 其它辅助函数用例
app() 容器
app(\App\Services\DemoService::class)取 DemoService 单例对象,自动定义容器元素和依赖注入。如果需要手动定义,可以在app/Providers里定义app(\App\Services\DemoService::class, $value)设置(或重置)容器里的元素,常用于单元测试 mock 对象
config() 配置文件
config('app.lang')- 假设当前环境是
dev - 依次搜索
config/dev/app.php, config/app.php, 存在时返回第一个结果文件的内容,都不存在时返回'' config(['app.lang' => 'en'])设置 run-time 的配置
添加新环境配置:
复制目录 config/dev 及其配置文件,在 config/env.php 中添加多一个新环境分支
trans() 多语言文本
trans(10001000)- 假设当前语言是
zh_CN, 默认语言是en - 依次搜索
lang/zh_CN.php, lang/en.php, 存在10001000这个key时返回第一个结果内容,都不存在时返回?
logfile() 文件日志
logfile('test', ['foo', 'bar'], 'login') 把内容写到data/log/login_20190328.log
各日志文件说明:
standard_xxx.logPHP 标准错误处理程序写的日志,比较精简,但只能它才能记录 Fatal Error, Parse Errorerror_xxx.log框架写的错误日志,比较详细access_xxx.log框架的写访问日志app_xx.log用户写的默认日志,文件名可以修改,由logfile()参数3控制
url() 带协议和域名的完整URL
- 当前域名URL:
url('path/to') - 其它域名URL:
url(['test', '/path/to'])
panic() 直接抛出业务异常对象
panic(10001000)等于throw new AppException('10001000')自动转为错误码对应的文本,参考翻译文件 lang/zh_CN.phppanic('foo')等于throw new AppException('foo')panic('foo', ['bar'])等于throw (new AppException('foo'))->setData(['bar'])
AppException 异常属于业务逻辑,能够作为提示通过接口返回给用户看,而其它异常则不会(安全考虑)
inject() 支持自动依赖注入的函数调用
通过回调函数的形参里声明类型,就能会自动注入
inject(function (\Core\Queue $queue) { //todo... });
request_flash(), old() 记住并使用上次的请求参数
request_flash()把本次请求的参数缓存起来old(string $name = null, string $default = '')上次请求的字段值
csrf_*() CSRF, XSRF
csrf_field()直接生成 HTMLcsrf_token()生成 tokencsrf_check()效验,token 来源于$_SERVER['HTTP_X_CSRF_TOKEN'], $_POST['_token'], $_GET['_token'], $_REQUEST['_token']
请求时带上 Token, 使用以下任意一种方法
POST请求通过表单参数_token, 后端将从$_POST['_token']读取GET请求通过?_token=, 后端将从$_GET['_token']读取- 通过指定请求头
X-CSRF-Token, 后端将从$_SERVER['HTTP_X_CSRF_TOKEN']读取
flash_*() 闪存,一性次缓存
flash_set(string $key, $value)闪存设置flash_has(string $key)存在且为真flash_exists(string $key)闪存是否存在,即使值为 nullflash_get(string $key)闪存获取并删除flash_del(string $key)闪存删除
api_format(), api_json() 格式化为接口输出的内容结构
api_format(true, ['foo' => 1])格式化为成功的内容结构 arrayapi_format($exception)格式化异常对象为失败的内容结构 arrayapi_json(),api_format()用法一样,区别是前者返回 string-jsonapi_success(),api_error()是api_json()的简写
成功提示
{
"s": true,
"c": 0,
"m": "",
"d": {
"foo": 1
}
}
路由里等价写法如下:
return ['foo' => 1]; // 只能返回消息体 d return api_success('', 0, ['foo' => 1]); // 一般用于方便返回纯 m, 例如 api_success('我是成功消息'); return api_json(true, ['foo' => 1]);
错误提示
{
"s": false,
"c": 0,
"m": "我是失败消息",
"d": {
"foo": 1
}
}
路由里等价写法如下:
panic('我是失败消息', ['foo' => 1]); // 直接抛出异常,不用 return; 另一种便捷的用法是 panic(10001000); return api_error('我是失败消息', 0, ['foo' => 1]); // 可自由指定错误码 return api_json(false, ['foo' => 1]);
assign(), view() 模板与变量
assign('firstName', 'Hello')定义模板变量return view('demo', ['title' => $title])定义模板变量的同时返回渲染内容
back(), redirect() 网页跳转
return back()跳转回上一步return redirect('/demo')跳转到/demo
缓存 redis
依赖
pecl install redis
用例
app(\Core\AppRedis::class)->setex('key', 3600, 'value') 与原生一致
登录
app(\Core\Auth::class)->login(1010); // 登录 ID 为 1010 app(\Core\Auth::class)->getUserId(); // 1010 app(\Core\Auth::class)->isLogin(); // true app(\Core\Auth::class)->logout(); // 退出登录
RPC 远程过程调用
- 服务端入口
private/rpc/index.php, 注意要使用内部域名,不能让外网访问 - 客户端调用参考
tests/feature/curl.php
消息队列
worker 遇到信号 SIGTERM, SIGHUP, SIGINT, SIGQUIT 会平滑结束进程。
如果要强行结束可使用信号 SIGKILL, 命令为 kill -s KILL <PID>
依赖
composer require php-amqplib/php-amqplib
配置
config/.../rabbitmq.php
用例
queue_publish('SPARROW_QUEUE_DEMO', ['foo' => 1, 'bar' => 2]);发布消息- 消费的 worker, 参考
workers/SPARROW_QUEUE_DEMO.php - docker 容器 php-fpm 里面已经有 supervisor, 使 worker 变为长驻进程
- 示例配置文件为
docker/php-fpm/supervisor_conf.d/SPARROW_QUEUE_DEMO.conf - 日志可通过"运维与开发"后台查看,或者在 supervisorctl 里面使用 tail 命令查看
- 必须先启动 rabbitmq 服务,再启动 worker, 否则会报错。如果遇到 rabbitmq 容器比 php-fpm 容器先启动的情况,执行这个命令即可:
docker-compose exec php-fpm bash -c "supervisorctl start all"
- 示例配置文件为
建议规则:
- 每个 worker 只消费一个队列;
- 队列名与 worker名 一致,便于定位队列名对应的 worker 文件;
- 队列名与 worker名 要有项目名前缀,防止在 Supervisor, RabbitMq 里与其它项目搞混
邮件 email
依赖
composer require swiftmailer/swiftmailer
配置
config/.../email.php
用例
参考类文档 \Core\Mail
OPS 运维与开发
用于运维监控与开发调试,包括 日志、调试、性能分析
- 默认地址为 http://localhost:37062/
- 可自行修改 nginx 配置:
docker/nginx/conf.d/ops.sparrow.conf - 注意安全性,端口和域名不要对外开放
XDebug
断点调试
配置文件位置 docker/php-fpm/php_ini/xdebug.ini
XDebug Trace
跟踪调试日志
以下任意方式可开启跟踪,
日志可在运维后台 XDebug - 跟踪文件 查看,
或者直接在项目目录 data/trace/ 里查看
注意:请确保对 data/ 目录有写权限
跟踪 fpm
- 预先配置监听,两种方法设置:
- 在运维后台
XDebug - 监听设置 - 命令行
php cli/trace.php,参数--help查看帮助
- 在运维后台
- 当前URL 主动开启:
/?_xt=name0,name0是当前日志的标识名 - Cookie 主动开启:
_xt=name0;
注意:URL, Cookie 方式的前提必须先设置 config/.../whitelist.php 白名单 IP 或 白名单 Cookie
跟踪 cli
php demo.php --trace 在任何脚本命令后面加上参数 --trace 即可
XHProf
依赖
- 扩展 tideways_xhprof
- GUI - View Full Callgraph 功能,需要安装
graphviz- Ubuntu:
sudo apt install graphviz - CentOS:
yum install graphviz
- Ubuntu:
使用
- 配置文件
config/.../xhprof.php enable设置为true, 即可记录大于指定耗时的请求
注意:请确保对 data/ 目录有写权限
维护模式
开启维护模式,关闭网站访问入口
php cli/maintain.php

