php-guidelines-from-spatie by freekmurze/dotfiles
npx skills add https://github.com/freekmurze/dotfiles --skill php-guidelines-from-spatie优先遵循 Laravel 惯例。 如果 Laravel 有文档化的方式来做某事,请使用它。只有在有明确理由时才偏离。
?string 而非 string|nullvoid 返回类型使用类型化属性而非文档块
指定返回类型,包括 void
使用简短的可空语法:?Type 而非 Type|null
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
使用泛型记录可迭代对象:
/** @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 语句以提高可读性
始终使用花括号,即使是单条语句
三元运算符:除非非常简短,否则每个部分单独一行
// 成功路径最后 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(); } }
/open-source)->name('openSource')){userId})[Controller::class, 'method']PostsController)index, create, store, show, edit, update, destroy)pdf-generator.php)chrome_path)config/services.php,不要创建新文件config() 辅助函数,避免在配置文件外使用 env()名称: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.");
对于添加注释要非常谨慎,因为它们经常会过时,并可能随着时间的推移产生误导。代码应通过描述性的变量和函数名实现自文档化。
添加注释不应成为使代码可读的首要策略。
不要这样做:
// 获取此站点的失败检查
$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);
});
使用 4 个空格缩进
控制结构后没有空格:
@if($condition)
Something
@endif
Gate::define('editPost', ...)view 代替 show__() 函数而非 @lang:使用复数资源名称:/errors
使用 kebab-case:/error-occurrences
为简化起见,限制深度嵌套:
/error-occurrences/1
/errors/1/occurrences
UserController, OrderStatus)getUserName, $firstName)/open-source, /user-profile)pdf-generator.php)chrome_path)php artisan delete-old-records)Controller (PostsController)openSource.blade.php)CreateUser, SendEmailNotification)UserRegistering, UserRegistered)Listener 后缀 (SendInvitationMailListener)Command 后缀 (PublishScheduledPostsCommand)Mail 后缀 (AccountActivatedMail)Resource/Transformer (UsersResource)OrderStatus, BookingType)else 语句&& 的复合 if 条件拆分为嵌套的 if 语句use 语句导入命名空间 - 切勿使用内联的完全限定类名(例如 \Exception, \Illuminate\Support\Facades\Http)$exception 而非 $e, $request 而非 $r)每周安装量
110
代码库
GitHub 星标数
892
首次出现
2026年1月24日
安全审计
安装于
opencode95
codex87
claude-code87
github-copilot86
gemini-cli85
cursor78
Follow Laravel conventions first. If Laravel has a documented way to do something, use it. Only deviate when you have a clear justification.
?string not string|nullvoid return types when methods return nothingUse typed properties over docblocks
Specify return types including void
Use short nullable syntax: ?Type not Type|null
Document iterables with generics:
/** @return Collection<int, User> */
public function getUsers(): Collection
Don't use docblocks for fully type-hinted methods (unless description needed)
Always import classnames in docblocks - never use fully qualified names:
use \Spatie\Url\Url;
/** @return Url */
Use one-line docblocks when possible: /** @var string */
Most common type should be first in multi-type docblocks:
/** @var Collection|SomeWeirdVendor\Collection */
If one parameter needs docblock, add docblocks for all parameters
For iterables, always specify key and value types:
/**
* @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:
/** @return array{
first: SomeClass,
second: SomeClass
} */
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
// 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(); } }
/open-source)->name('openSource')){userId})[Controller::class, 'method']PostsController)index, create, store, show, edit, update, destroy)pdf-generator.php)chrome_path)config/services.php, don't create new filesconfig() helper, avoid env() outside config filesNames: 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):
$items->each(function(Item $item) {
$this->info("Processing item id `{$item->id}`...");
$this->processItem($item);
});
$this->comment("Processed {$items->count()} items.");
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:
// Get the failed checks for this site
$checks = $site->checks()->where('status', 'failed')->get();
Do this:
$failedChecks = $site->checks()->where('status', 'failed')->get();
Guidelines:
{} bracketsUse array notation for multiple rules (easier for custom rule classes):
public function rules() {
return [
'email' => ['required', 'email'],
];
}
Custom validation rules use snake_case:
Validator::extend('organisation_type', function ($attribute, $value) {
return OrganisationType::isValid($value);
});
Indent with 4 spaces
No spaces after control structures:
@if($condition)
Something
@endif
Gate::define('editPost', ...)view instead of show__() function over @lang:Use plural resource names: /errors
Use kebab-case: /error-occurrences
Limit deep nesting for simplicity:
/error-occurrences/1
/errors/1/occurrences
UserController, OrderStatus)getUserName, $firstName)/open-source, /user-profile)pdf-generator.php)chrome_path)php artisan delete-old-records)Controller (PostsController)openSource.blade.php)CreateUser, SendEmailNotification)UserRegistering, UserRegistered)Listener suffix (SendInvitationMailListener)Command suffix ()else statements when possibleif conditions using && into nested if statementsuse statements — never use inline fully qualified class names (e.g. \Exception, \Illuminate\Support\Facades\Http)$exception not $e, not )Weekly Installs
110
Repository
GitHub Stars
892
First Seen
Jan 24, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode95
codex87
claude-code87
github-copilot86
gemini-cli85
cursor78
FilamentPHP 资源生成技能 - 快速创建 Laravel 后台管理面板
1,200 周安装
PublishScheduledPostsCommandMail suffix (AccountActivatedMail)Resource/Transformer (UsersResource)OrderStatus, BookingType)$request$r