关于 php-guidelines-from-spatie
php-guidelines-from-spatie 是面向 PHP 与 Laravel 开发的编码规范技能,汇总类型声明、命名、控制流、测试和框架约定。它适合希望让代码审查或生成遵循一致风格的开发者。
主要功能
- 01遵循 PSR-1、PSR-2 与 PSR-12
- 02优先采用 Laravel 约定
- 03规范类型声明与文档块
- 04提供命名和文件结构规则
- 05涵盖测试与代码质量实践
适用场景
- 01编写符合约定的 Laravel 代码
- 02审查 PHP 命名与类型规范
- 03统一项目测试和格式习惯
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ freekmurze/ dotfiles --skill php-guidelines-from-spatie安装后在 Claude Code 中输入/php-guidelines-from-spatie确认已加载。
详细介绍
Laravel 核心原则
首先遵循 Laravel 约定。 如果 Laravel 有文档说明的实现方式,就使用它。只有在理由充分时才偏离。
PHP 标准
- 遵循 PSR-1、PSR-2 和 PSR-12
- 非面向公众的字符串使用 camelCase
- 使用简短的可空类型写法:
?string,而不是string|null - 方法不返回任何内容时,始终指定
void返回类型
类结构
- 使用带类型的属性,而非文档块:
- 所有属性都可以提升时,使用构造函数属性提升:
- 每行一个 trait:
类型声明与文档块
- 使用带类型的属性,而非文档块
- 指定返回类型,包括
void - 使用简短的可空类型写法:
?Type,而不是Type|null - 使用泛型为可迭代对象添加文档说明:
/** @return Collection<int, User> */ public function getUsers(): Collection
文档块规则
- 对于类型提示完整的方法,不要使用文档块(除非需要添加说明)
- 文档块中的类名始终使用导入形式,不要使用完全限定名称:
use \Spatie\Url\Url; /** @return Url */ - 尽可能使用单行文档块:
/** @var string */ - 多类型文档块中,最常见的类型放在前面:
/** @var Collection|SomeWeirdVendor\Collection */ - 如果有一个参数需要文档块,就为所有参数添加文档块
- 对于可迭代对象,始终指定键和值的类型:
/** * @param array<int, MyObject> $myArray * @param int $typedArgument */ function someFunction(array $myArray, int $typedArgument) {} - 固定键使用数组形状表示法,并将每个键单独放在一行:
/** @return array{ first: SomeClass, second: SomeClass } */
控制流
- 成功路径放在最后:先处理错误情况,最后处理成功情况
- 避免使用 else:使用提前返回,而不是嵌套条件
- 拆分条件:为提高可读性,将使用
&&的复合if语句拆分为嵌套的if语句 - 始终使用花括号,即使只有一条语句
- 三元运算符:除非非常简短,否则每个部分单独占一行
// Happy path last
if (! $user) {
return null;
}
if (! $user->isActive()) {
return null;
}
// Process active user...
// Short ternary
$name = $isFoo ? 'foo' : 'bar';
// Multi-line ternary
$result = $object instanceof Model ?
$object->name :
'A default value';
// Ternary instead of else
$condition
? $this->doSomething()
: $this->doSomethingElse();
// Bad: compound condition with &&
if ($user->isActive() && $user->hasPermission('edit')) {
$user->edit();
}
// Good: nested ifs
if ($user->isActive()) {
if ($user->hasPermission('edit')) {
$user->edit();
}
}
Laravel 约定
路由
- URL:使用 kebab-case(
/open-source) - 路由名称:使用 camelCase(
->name('openSource')) - 参数:使用 camelCase(
{userId}) - 使用元组表示法:
[Controller::class, 'method']
控制器
- 资源名称使用复数形式(
PostsController) - 坚持使用 CRUD 方法(
index、create、store、show、edit、update、destroy) - 为非 CRUD 操作提取新的控制器
配置
- 文件:使用 kebab-case(
pdf-generator.php) - 键:使用 snake_case(
chrome_path) - 将服务配置添加到
config/services.php,不要创建新文件 - 使用
config()辅助函数,避免在配置文件之外使用env()
Artisan 命令
- 名称:使用 kebab-case(
delete-old-records) - 始终提供反馈(
$this->comment('All ok!')) - 在循环中显示进度,并在结束时显示摘要
- 在处理项目之前输出信息(便于调试):
$items->each(function(Item $item) { $this->info("Processing item id `{$item->id}`..."); $this->processItem($item); }); $this->comment("Processed {$items->count()} items.");
字符串与格式
- 优先使用字符串插值,而不是拼接:
枚举
- 枚举值使用 PascalCase:
注释
添加注释时务必谨慎,因为注释经常会过时,并可能随着时间推移产生误导。代码应通过描述性变量名和函数名做到自我说明。
不要把添加注释作为提高代码可读性的首选方法。
不要这样写:
// Get the failed checks for this site
$checks = $site->checks()->where('status', 'failed')->get();
应该这样写:
$failedChecks = $site->checks()->where('status', 'failed')->get();
指南:
- 不要添加描述代码作用的注释——让代码自行说明
- 简短易读的代码无需注释说明
- 使用描述性变量名,而不是通用名称加注释
- 只有在解释某种不明显操作的原因时才添加注释,而不是解释操作的内容
- 不要在测试中添加注释——测试名称应当足够清晰
空白
- 在语句之间添加空行,以提高可读性
- 例外:一系列等效的单行操作
{}之间不要添加多余的空行- 让代码有“呼吸感”——避免格式过于紧凑
验证
- 多条规则使用数组表示法(便于使用自定义规则类):
public function rules() { return [ 'email' => ['required', 'email'], ]; } - 自定义验证规则使用 snake_case:
Validator::extend('organisation_type', function ($attribute, $value) { return OrganisationType::isValid($value); });
Blade 模板
- 使用 4 个空格缩进
- 控制结构之后不加空格:
@if($condition) Something @endif
授权
- Policy 使用 camelCase:
Gate::define('editPost', ...) - 使用 CRUD 术语,但用
view代替show
翻译
- 使用
__()函数,而不是@lang:
API 路由
- 使用复数资源名称:
/errors - 使用 kebab-case:
/error-occurrences - 为保持简单,限制嵌套层级:
/error-occurrences/1 /errors/1/occurrences
测试
- 尽可能将测试类放在同一文件中
- 使用描述性测试方法名称
- 遵循 arrange-act-assert 模式
- 将辅助函数放在测试文件末尾,位于
it/test代码块之后(应先阅读测试;辅助函数属于支持性细节)
快速参考
命名约定
- 类:PascalCase(
UserController、OrderStatus) - 方法/变量:camelCase(
getUserName、$firstName) - 路由:kebab-case(
/open-source、/user-profile) - 配置文件:kebab-case(
pdf-generator.php) - 配置键:snake_case(
chrome_path) - Artisan 命令:kebab-case(
php artisan delete-old-records)
文件结构
- 控制器:复数资源名称 +
Controller(PostsController) - 视图:camelCase(
openSource.blade.php) - 作业:基于操作的名称(
CreateUser、SendEmailNotification) - 事件:基于时态的名称(
UserRegistering、UserRegistered) - 监听器:操作 +
Listener后缀(SendInvitationMailListener) - 命令:操作 +
Command后缀(PublishScheduledPostsCommand) - 邮件类:用途 +
Mail后缀(AccountActivatedMail) - 资源/转换器:复数名称 +
Resource/Transformer(UsersResource) - 枚举:描述性名称,不加前缀(
OrderStatus、BookingType)
迁移
- 迁移中不要编写
down方法,只编写up方法
代码质量提示
PHP
- 使用类型化属性,而不是文档块
- 优先使用提前返回,而不是嵌套的 if/else
- 所有属性都可以提升时,使用构造函数属性提升
- 尽可能避免使用
else语句 - 使用
&&的复合if条件应拆分为嵌套的if语句 - 使用字符串插值,而不是字符串拼接
- 控制结构始终使用花括号
- 始终使用
use语句导入命名空间,绝不使用内联的完全限定类名(例如,\Exception、\Illuminate\Support\Facades\Http) - 绝不使用单字母变量名,应使用描述性名称(例如,使用
$exception而不是$e,使用$request而不是$r)
评论
支持 Markdown