rector / type-perfect
高级类型声明检查
Requires
- php: ^7.2|^8.0
- phpstan/phpstan: ^1.11
- webmozart/assert: ^1.11
README
高级类型声明检查 PHPStan 规则。
我们使用这些规则来提升客户代码的质量,超越 PHPStan 的功能。
- 这些规则使跳过的对象类型明确,参数类型更窄,并帮助您填写更准确的对象类型提示。
- 即使您的代码未通过第 0 级,这些规则也易于启用
- 它们易于解决,并使您的代码立即更加稳固和可靠。
如果您关心代码质量和类型安全,请将这 10 条规则添加到您的 CI 中。
安装
composer require rector/type-perfect --dev
注意:请确保您使用 phpstan/extension-installer
来加载必要的服务配置或包含 vendor/rector/type-perfect/config/extension.neon
文件。
默认启用 2 个检查。第一个确保我们不会错过使用 instanceof
来让后续代码知道确切对象类型的机会
private ?SomeType $someType = null; if (! empty($this->someType)) { // ... } if (! isset($this->someType)) { // ... } // here we only know, that $this->someType is not empty/null
🙅
↓
if (! $this->someType instanceof SomeType) { return; } // here we know $this->someType is exactly SomeType
✔️
第二个规则检查我们是否使用显式的对象方法而不是魔法数组访问
$article = new Article(); $id = $article['id']; // we have no idea, what the type is
🙅
↓
$id = $article->getId(); // we know the type is int
✔️
配置
您可以通过配置启用以下规则。我们按照从简单到强大的顺序,以及我们在旧项目上应用它们的顺序,来选择它们。
您可以选择全部启用
parameters: type_perfect: no_mixed_property: true no_mixed_caller: true null_over_false: true narrow_param: true narrow_return: true
或者一个一个地启用
1. Null over False
parameters: type_perfect: null_over_false: true
布尔类型通常用于开/关、是/否响应。但有时,false
被误用为 无结果 响应,其中 null
会更准确
public function getProduct() { if (...) { return $product; } return false; }
🙅
↓
我们应该使用 null
,因为它自 PHP 7.1 起启用了严格的类型声明
public function getProduct(): ?Product { if (...) { return $product; } return null; }
✔️
2. No mixed Property
parameters: type_perfect: no_mixed_property: true
此规则关注 PHPStan 在获取属性时的盲点。如果我们有一个未知类型的属性,PHPStan 就无法分析它。它会静默地忽略它。
private $someType; public function run() { $this->someType->vale; }
它看不到 vale
属性名中的拼写错误。它应该是 value
🙅
↓
private SomeType $someType; public function run() { $this->someType->value; }
此规则确保所有属性获取都知道它们调用的类型。
✔️
3. No mixed Caller
parameters: type_perfect: no_mixed_caller: true
与上面相同,但针对方法调用
private $someType; public function run() { $this->someType->someMetho(1, 2); }
它看不到 someMetho
名称中的拼写错误,以及第二个参数必须是 string
。
🙅
↓
private SomeType $someType; public function run() { $this->someType->someMethod(1, 'active'); }
此组确保方法调用知道它们调用的类型。
✔️
4. Narrow Param Types
我们拥有的参数类型越窄,代码就越可靠。string
比 mixed
更好,int
比 scalar
更好,ExactObject
比 stdClass
更好。
parameters: type_perfect: narrow_param: true
在私有方法调用和公共方法调用的情况下,我们的项目通常知道传递给它确切的数据类型
// in one file $product->addPrice(100.52); // another file $product->addPrice(52.05);
但出于恐惧和“为了安全起见”,我们保持 addPrice()
参数类型为空、mixed
或文档块。
🙅
↓
如果在 100% 的情况下传递 float
类型,PHPStan 就知道它可以添加,并进一步改进分析
-/** - * @param float $price - */ -public function addPrice($price) +public function addPrice(float $price) { $this->price = $price; }
这就是此组的作用所在。它检查所有传递的类型,并告诉我们如何缩小参数类型声明。
✔️
5. Narrow Return Types
最后但同样重要的是,返回类型越窄,代码就越可靠。
parameters: type_perfect: narrow_return: true
它在哪里有帮助?假设我们有两种类型的谈话,它们具有不同的行为
final class ConferenceTalk extends Talk { public function bookHotel() { // ... } } final class MeetupTalk extends Talk { public function bookTrain() { // ... } }
然后我们有一个工厂(仓库或服务)返回通用的 Talk
类型
final class TalkFactory { public function createConferenceTalk(): Talk { return new ConferenceTalk(); } public function createMeetupTalk(): Talk { return new MeetupTalk(); } }
在这种情况下,我们失去了严格的类型,必须在运行时验证类型
$talk instanceof ConferenceTalk
🙅
↓
这就是这个小组发挥作用的地方。如果我们返回确切的类型,我们应该在返回类型声明中使用确切的类型,以使代码尽可能可靠
final class TalkFactory { - public function createConferenceTalk(): Talk + public function createConferenceTalk(): ConferenceTalk { return new ConferenceTalk(); } - public function createMeetupTalk(): Talk + public function createMeetupTalk(): MeetupTalk { return new MeetupTalk(); } }
✔️
逐个添加集合,修复你觉得有用的,忽略其余部分。
编码愉快!