mcpField
P
php-guidelines-from-spatie

php-guidelines-from-spatie

github.com/freekmurze开发1.0k157 次安装1 周前更新
代码仓库

关于 php-guidelines-from-spatie

php-guidelines-from-spatie 是面向 PHP 与 Laravel 开发的编码规范技能,汇总类型声明、命名、控制流、测试和框架约定。它适合希望让代码审查或生成遵循一致风格的开发者。

主要功能

  1. 01遵循 PSR-1、PSR-2 与 PSR-12
  2. 02优先采用 Laravel 约定
  3. 03规范类型声明与文档块
  4. 04提供命名和文件结构规则
  5. 05涵盖测试与代码质量实践

适用场景

  1. 01编写符合约定的 Laravel 代码
  2. 02审查 PHP 命名与类型规范
  3. 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