功能概述
自定义函数分两级:全局函数放在 app/function.php,所有请求都能用;模块函数放在 app/{模块}/function.php,只有进入该模块时才会加载。两者都不需要手动引入。
- 框架只认这两个固定文件,不扫描目录,加载顺序确定。
- 全局函数在框架引导阶段加载,CLI 与 HTTP 行为一致。
全局函数:app/function.php
文件不存在也不会报错,需要时新建即可。适合放无状态、跨模块通用的小函数。
// app/function.php
if (!function_exists('format_price')) {
function format_price($cents)
{
return '¥' . number_format($cents / 100, 2);
}
}
框架在注册完自动加载之后才载入这个文件,所以函数体内可以直接使用框架类与助手函数:
if (!function_exists('site_name')) {
function site_name()
{
return config('site.name', 'StartMVC');
}
}
模块函数:app/{模块}/function.php
只有进入该模块时才加载,适合只在某个模块里用的函数(后台专用、前台专用)。
// app/admin/function.php
if (!function_exists('admin_can')) {
function admin_can($action)
{
return \app\admin\service\Auth::check($action);
}
}
访问 /admin/... 时该文件被加载,访问 /home/... 时不会加载——既省掉无谓的解析,也避免两个模块的同名函数互相污染。
- 注意:路由闭包不经过模块解析,闭包里要用模块函数时请自行
require_once。 - 建议:模块函数统一加模块前缀(
admin_、home_)。全局函数没有命名空间,两个模块定义同名函数会直接致命错误。
需要拆分多个文件时
框架不扫描目录,所以拆分的入口仍然是 app/function.php——在里面 require_once 即可,顺序由你控制:
// app/function.php
require_once __DIR__ . '/common/functions/string.php';
require_once __DIR__ . '/common/functions/format.php';
拆分出来的文件建议放在 app/common/functions/,不要新建 app/function/ 目录——function.php 与 function/ 并排会让人分不清哪个是入口。
命名与冲突
全局函数没有命名空间,重复定义是致命错误,无法用 try/catch 捕获。因此约定:
- 每个函数都用
if (!function_exists('xxx'))包裹,框架自带的助手函数也是这个写法; - 函数名不要与 PHP 内置函数、框架助手函数重名;
- 一组函数超过 5 个、或者需要共享状态时,说明它应该是一个类——放到
app/common/,按 PSR-4 自动加载,例如app\common\Str::slug()。
加载顺序
| 顺序 | 文件 | 加载时机 |
|---|---|---|
| 1 | startmvc/function.php | 框架引导,早于 Composer 自动加载 |
| 2 | extend/*/function.php | 框架引导,扩展自带的函数(按目录名升序) |
| 3 | app/function.php | 框架引导,自动加载注册之后 |
| 4 | app/{模块}/function.php | 进入该模块时(请求分发阶段) |
前三步在 CLI 与 HTTP 下都会执行,第四步只在真正分发到某个模块时执行。