php-guidelines-from-spatie

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Core 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:
    ?string
    not
    string|null
  • Always specify
    void
    return types when methods return nothing
  • 遵循PSR-1、PSR-2和PSR-12规范
  • 非公开字符串使用camelCase命名
  • 使用简短可空类型语法:
    ?string
    而非
    string|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:
    ?Type
    not
    Type|null
  • Document iterables with generics:
    php
    /** @return Collection<int, User> */
    public function getUsers(): Collection
  • 优先使用类型化属性而非文档块
  • 指定返回类型,包括
    void
  • 使用简短可空类型语法:
    ?Type
    而非
    Type|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
    if
    statements that use
    &&
    into nested
    if
    statements for better readability
  • 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
    config/services.php
    , don't create new files
  • Use
    config()
    helper, avoid
    env()
    outside config files
  • 配置文件:使用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
    view
    instead of
    show
  • 策略使用camelCase命名:
    Gate::define('editPost', ...)
  • 使用CRUD相关词汇,但用
    view
    替代
    show

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 +
    Listener
    suffix (
    SendInvitationMailListener
    )
  • Commands: action +
    Command
    suffix (
    PublishScheduledPostsCommand
    )
  • Mailables: purpose +
    Mail
    suffix (
    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
    else
    statements when possible
  • Split compound
    if
    conditions using
    &&
    into nested
    if
    statements
  • Use string interpolation over concatenation
  • Always use curly braces for control structures
  • Always import namespaces with
    use
    statements — never use inline fully qualified class names (e.g.
    \Exception
    ,
    \Illuminate\Support\Facades\Http
    )
  • Never use single-letter variable names — use descriptive names (e.g.
    $exception
    not
    $e
    ,
    $request
    not
    $r
    )
  • 优先使用类型化属性而非文档块
  • 优先使用提前返回而非嵌套if/else
  • 当所有属性都可提升时,使用构造函数属性提升
  • 尽可能避免使用else语句
  • 将使用
    &&
    的复合
    if
    条件拆分为嵌套
    if
    语句
  • 优先使用字符串插值而非字符串拼接
  • 控制结构始终使用大括号
  • 始终使用
    use
    语句导入命名空间——绝不使用内联完全限定类名(如
    \Exception
    \Illuminate\Support\Facades\Http
  • 绝不使用单字母变量名——使用描述性名称(如
    $exception
    而非
    $e
    $request
    而非
    $r