php-guidelines-from-spatie
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCore Laravel Principle
Laravel核心原则
Follow Laravel conventions first. If Laravel has a documented way to do something, use it. Only deviate when you have a clear justification.
优先遵循Laravel约定。如果Laravel已有文档化的实现方式,请直接使用。仅当你有明确的理由时才可以偏离。
PHP Standards
PHP编码标准
- Follow PSR-1, PSR-2, and PSR-12
- Use camelCase for non-public-facing strings
- Use short nullable notation: not
?stringstring|null - Always specify return types when methods return nothing
void
- 遵循PSR-1、PSR-2和PSR-12规范
- 非公开字符串使用camelCase命名
- 使用简短可空类型语法:而非
?stringstring|null - 当方法无返回值时,始终指定返回类型
void
Class Structure
类结构
- Use typed properties, not docblocks:
- Constructor property promotion when all properties can be promoted:
- One trait per line:
- 使用类型化属性,而非文档块(docblocks):
- 当所有属性都可提升时,使用构造函数属性提升:
- 每行一个trait:
Type Declarations & Docblocks
类型声明与文档块
- Use typed properties over docblocks
- Specify return types including
void - Use short nullable syntax: not
?TypeType|null - Document iterables with generics:
php
/** @return Collection<int, User> */ public function getUsers(): Collection
- 优先使用类型化属性而非文档块
- 指定返回类型,包括
void - 使用简短可空类型语法:而非
?TypeType|null - 使用泛型标注可迭代类型:
php
/** @return Collection<int, User> */ public function getUsers(): Collection
Docblock Rules
文档块规则
- Don't use docblocks for fully type-hinted methods (unless description needed)
- Always import classnames in docblocks - never use fully qualified names:
php
use \Spatie\Url\Url; /** @return Url */ - Use one-line docblocks when possible:
/** @var string */ - Most common type should be first in multi-type docblocks:
php
/** @var Collection|SomeWeirdVendor\Collection */ - If one parameter needs docblock, add docblocks for all parameters
- For iterables, always specify key and value types:
php
/** * @param array<int, MyObject> $myArray * @param int $typedArgument */ function someFunction(array $myArray, int $typedArgument) {} - Use array shape notation for fixed keys, put each key on it's own line:
php
/** @return array{ first: SomeClass, second: SomeClass } */
- 对于已完全类型提示的方法,无需使用文档块(除非需要添加描述)
- 文档块中始终导入类名 - 绝不要使用完全限定类名:
php
use \Spatie\Url\Url; /** @return Url */ - 尽可能使用单行文档块:
/** @var string */ - 多类型文档块中,最常见的类型应放在首位:
php
/** @var Collection|SomeWeirdVendor\Collection */ - 如果有一个参数需要文档块,那么所有参数都要添加文档块
- 对于可迭代类型,始终指定键和值的类型:
php
/** * @param array<int, MyObject> $myArray * @param int $typedArgument */ function someFunction(array $myArray, int $typedArgument) {} - 对于固定键的数组,使用数组结构标注,每个键单独占一行:
php
/** @return array{ first: SomeClass, second: SomeClass } */
Control Flow
控制流
- Happy path last: Handle error conditions first, success case last
- Avoid else: Use early returns instead of nested conditions
- Separate conditions: Split compound statements that use
ifinto nested&&statements for better readabilityif - Always use curly brackets even for single statements
- Ternary operators: Each part on own line unless very short
php
// 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();
}
}- 正常路径后置:先处理错误条件,最后处理成功场景
- 避免使用else:使用提前返回替代嵌套条件
- 拆分条件:将使用的复合
&&语句拆分为嵌套if语句,提升可读性if - 始终使用大括号,即使是单行语句
- 三元运算符:除非非常简短,否则每个部分单独占一行
php
// 正常路径后置
if (! $user) {
return null;
}
if (! $user->isActive()) {
return null;
}
// 处理活跃用户...
// 简短三元运算符
$name = $isFoo ? 'foo' : 'bar';
// 多行三元运算符
$result = $object instanceof Model ?
$object->name :
'A default value';
// 用三元运算符替代else
$condition
? $this->doSomething()
: $this->doSomethingElse();
// 不良写法:使用&&的复合条件
if ($user->isActive() && $user->hasPermission('edit')) {
$user->edit();
}
// 良好写法:嵌套if
if ($user->isActive()) {
if ($user->hasPermission('edit')) {
$user->edit();
}
}Laravel Conventions
Laravel约定
Routes
路由
- URLs: kebab-case ()
/open-source - Route names: camelCase ()
->name('openSource') - Parameters: camelCase ()
{userId} - Use tuple notation:
[Controller::class, 'method']
- URL:使用kebab-case命名(如)
/open-source - 路由名称:使用camelCase命名(如)
->name('openSource') - 参数:使用camelCase命名(如)
{userId} - 使用元组语法:
[Controller::class, 'method']
Controllers
控制器
- Plural resource names ()
PostsController - Stick to CRUD methods (,
index,create,store,show,edit,update)destroy - Extract new controllers for non-CRUD actions
- 资源名称使用复数形式(如)
PostsController - 坚持使用CRUD方法(、
index、create、store、show、edit、update)destroy - 对于非CRUD操作,提取为新的控制器
Configuration
配置
- Files: kebab-case ()
pdf-generator.php - Keys: snake_case ()
chrome_path - Add service configs to , don't create new files
config/services.php - Use helper, avoid
config()outside config filesenv()
- 配置文件:使用kebab-case命名(如)
pdf-generator.php - 配置键:使用snake_case命名(如)
chrome_path - 将服务配置添加到,不要创建新文件
config/services.php - 使用助手函数,避免在配置文件外使用
config()env()
Artisan Commands
Artisan命令
- Names: kebab-case ()
delete-old-records - Always provide feedback ()
$this->comment('All ok!') - Show progress for loops, summary at end
- Put output BEFORE processing item (easier debugging):
php
$items->each(function(Item $item) { $this->info("Processing item id `{$item->id}`..."); $this->processItem($item); }); $this->comment("Processed {$items->count()} items.");
- 命令名称:使用kebab-case命名(如)
delete-old-records - 始终提供反馈信息(如)
$this->comment('All ok!') - 循环操作时显示进度,结束时显示摘要
- 在处理项之前输出信息(便于调试):
php
$items->each(function(Item $item) { $this->info("Processing item id `{$item->id}`..."); $this->processItem($item); }); $this->comment("Processed {$items->count()} items.");
Strings & Formatting
字符串与格式化
- String interpolation over concatenation:
- 优先使用字符串插值而非字符串拼接:
Enums
枚举
- Use PascalCase for enum values:
- 枚举值使用PascalCase命名:
Comments
注释
Be very critical about adding comments as they often become outdated and can mislead over time. Code should be self-documenting through descriptive variable and function names.
Adding comments should never be the first tactic to make code readable.
Instead of this:
php
// Get the failed checks for this site
$checks = $site->checks()->where('status', 'failed')->get();Do this:
php
$failedChecks = $site->checks()->where('status', 'failed')->get();Guidelines:
- Don't add comments that describe what the code does - make the code describe itself
- Short, readable code doesn't need comments explaining it
- Use descriptive variable names instead of generic names + comments
- Only add comments when explaining why something non-obvious is done, not what is being done
- Never add comments to tests - test names should be descriptive enough
添加注释时要非常谨慎,因为注释往往会过时,久而久之可能会产生误导。代码应通过描述性的变量和函数名称实现自文档化。
添加注释绝不应是提升代码可读性的首选方案。
反面示例:
php
// Get the failed checks for this site
$checks = $site->checks()->where('status', 'failed')->get();正面示例:
php
$failedChecks = $site->checks()->where('status', 'failed')->get();规范:
- 不要添加描述代码功能的注释——让代码自己说明功能
- 简短易读的代码无需注释解释
- 使用描述性变量名称替代通用名称加注释的方式
- 仅当需要解释某些非显而易见的实现原因时才添加注释,而非解释代码做了什么
- 测试代码中绝不添加注释——测试方法名称应足够具有描述性
Whitespace
空白字符
- Add blank lines between statements for readability
- Exception: sequences of equivalent single-line operations
- No extra empty lines between brackets
{} - Let code "breathe" - avoid cramped formatting
- 语句之间添加空行以提升可读性
- 例外情况:连续的等效单行操作
- 括号之间不要添加多余的空行
{} - 让代码“呼吸”——避免紧凑的格式
Validation
验证
- Use array notation for multiple rules (easier for custom rule classes):
php
public function rules() { return [ 'email' => ['required', 'email'], ]; } - Custom validation rules use snake_case:
php
Validator::extend('organisation_type', function ($attribute, $value) { return OrganisationType::isValid($value); });
- 多规则验证使用数组语法(便于自定义规则类):
php
public function rules() { return [ 'email' => ['required', 'email'], ]; } - 自定义验证规则使用snake_case命名:
php
Validator::extend('organisation_type', function ($attribute, $value) { return OrganisationType::isValid($value); });
Blade Templates
Blade模板
- Indent with 4 spaces
- No spaces after control structures:
blade
@if($condition) Something @endif
- 使用4个空格进行缩进
- 控制结构后不要加空格:
blade
@if($condition) Something @endif
Authorization
授权
- Policies use camelCase:
Gate::define('editPost', ...) - Use CRUD words, but instead of
viewshow
- 策略使用camelCase命名:
Gate::define('editPost', ...) - 使用CRUD相关词汇,但用替代
viewshow
Translations
翻译
- Use function over
__():@lang
- 优先使用函数而非
__():@lang
API Routing
API路由
- Use plural resource names:
/errors - Use kebab-case:
/error-occurrences - Limit deep nesting for simplicity:
/error-occurrences/1 /errors/1/occurrences
- 资源名称使用复数形式:
/errors - 使用kebab-case命名:
/error-occurrences - 限制深度嵌套以保持简洁:
/error-occurrences/1 /errors/1/occurrences
Testing
测试
- Keep test classes in same file when possible
- Use descriptive test method names
- Follow the arrange-act-assert pattern
- 尽可能将测试类放在同一个文件中
- 使用描述性的测试方法名称
- 遵循Arrange-Act-Assert(准备-执行-断言)模式
Quick Reference
快速参考
Naming Conventions
命名约定
- Classes: PascalCase (,
UserController)OrderStatus - Methods/Variables: camelCase (,
getUserName)$firstName - Routes: kebab-case (,
/open-source)/user-profile - Config files: kebab-case ()
pdf-generator.php - Config keys: snake_case ()
chrome_path - Artisan commands: kebab-case ()
php artisan delete-old-records
- 类: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
File Structure
文件结构
- Controllers: plural resource name + (
Controller)PostsController - Views: camelCase ()
openSource.blade.php - Jobs: action-based (,
CreateUser)SendEmailNotification - Events: tense-based (,
UserRegistering)UserRegistered - Listeners: action + suffix (
Listener)SendInvitationMailListener - Commands: action + suffix (
Command)PublishScheduledPostsCommand - Mailables: purpose + suffix (
Mail)AccountActivatedMail - Resources/Transformers: plural + /
Resource(Transformer)UsersResource - Enums: descriptive name, no prefix (,
OrderStatus)BookingType
- 控制器:复数资源名称 + (如
Controller)PostsController - 视图:camelCase命名(如)
openSource.blade.php - 任务类:基于动作命名(如、
CreateUser)SendEmailNotification - 事件类:基于时态命名(如、
UserRegistering)UserRegistered - 监听器类:动作 + 后缀(如
Listener)SendInvitationMailListener - 命令类:动作 + 后缀(如
Command)PublishScheduledPostsCommand - 邮件类:用途 + 后缀(如
Mail)AccountActivatedMail - 资源/转换器:复数形式 + /
Resource(如Transformer)UsersResource - 枚举:描述性名称,无需前缀(如、
OrderStatus)BookingType
Migrations
迁移
- do not write down methods in migrations, only up methods
- 迁移文件中不要写down方法,只保留up方法
Code Quality Reminders
代码质量提醒
PHP
PHP
- Use typed properties over docblocks
- Prefer early returns over nested if/else
- Use constructor property promotion when all properties can be promoted
- Avoid statements when possible
else - Split compound conditions using
ifinto nested&&statementsif - Use string interpolation over concatenation
- Always use curly braces for control structures
- Always import namespaces with statements — never use inline fully qualified class names (e.g.
use,\Exception)\Illuminate\Support\Facades\Http - Never use single-letter variable names — use descriptive names (e.g. not
$exception,$enot$request)$r
- 优先使用类型化属性而非文档块
- 优先使用提前返回而非嵌套if/else
- 当所有属性都可提升时,使用构造函数属性提升
- 尽可能避免使用else语句
- 将使用的复合
&&条件拆分为嵌套if语句if - 优先使用字符串插值而非字符串拼接
- 控制结构始终使用大括号
- 始终使用语句导入命名空间——绝不使用内联完全限定类名(如
use、\Exception)\Illuminate\Support\Facades\Http - 绝不使用单字母变量名——使用描述性名称(如而非
$exception,$e而非$request)$r