object-calisthenics / phpcs-calisthenics-rules
Requires
- php: ^7.4|^8.0
- nette/utils: ^3.1
- slevomat/coding-standard: ^6.3
- squizlabs/php_codesniffer: ^3.5
Requires (Dev)
- migrify/config-transformer: ^0.3.35
- phpstan/phpdoc-parser: ^0.4.9
- phpstan/phpstan: ^0.12.38
- phpunit/phpunit: ^9.3
- rector/rector: ^0.8.6
- symplify/changelog-linker: ^8.2
- symplify/coding-standard: ^8.2
- symplify/easy-coding-standard-tester: ^8.2
- symplify/phpstan-extensions: ^8.2
- tracy/tracy: ^2.7
README
已弃用:PHP_CodeSniffer 在处理空格和字符位置方面很棒。然而,这些规则关注的是代码架构和结构。2020年,有工具可以完美地实现这一点 - PHPStan.
话虽如此,对象体操 已作为 symplify/phpstan-rules
包中的 PHPStan 规则实现。请使用它 👇
includes:
- vendor/symplify/phpstan-rules/packages/object-calisthenics/config/object-calisthenics-rules.neon
- vendor/symplify/phpstan-rules/packages/object-calisthenics/config/object-calisthenics-services.neon
对象体操是 面向对象代码中的一组规则,侧重于可维护性、可读性、可测试性和可理解性。我们 首先实用 - 它们可以一起使用或单独使用。
为什么你应该在你的项目中使用这个工具?
阅读 William Durand 的帖子 或 查看 Guilherme Blanco 的演示。
安装
composer require object-calisthenics/phpcs-calisthenics-rules --dev
使用方法
如果你知道你想要什么,可以直接跳转到具体的规则
如何快速检查 1 个规则?
vendor/bin/phpcs src tests -sp \ --standard=vendor/object-calisthenics/phpcs-calisthenics-rules/src/ObjectCalisthenics/ruleset.xml \ --sniffs=ObjectCalisthenics.Classes.ForbiddenPublicProperty
# ecs.yaml services: ObjectCalisthenics\Sniffs\Classes\ForbiddenPublicPropertySniff: ~
然后
vendor/bin/ecs check src
实现的规则 Sniffs
1. 每个方法只使用 X
级别的缩进
❌
foreach ($sniffGroups as $sniffGroup) { foreach ($sniffGroup as $sniffKey => $sniffClass) { if (! $sniffClass instanceof Sniff) { throw new InvalidClassTypeException; } } }
👍
foreach ($sniffGroups as $sniffGroup) { $this->ensureIsAllInstanceOf($sniffGroup, Sniff::class); } // ... private function ensureIsAllInstanceOf(array $objects, string $type) { // ... }
只使用此规则?
在 PHP_CodeSniffer 中
vendor/bin/phpcs ... --sniffs=ObjectCalisthenics.Metrics.MaxNestingLevel
在 ECS 中
# ecs.yaml services: ObjectCalisthenics\Sniffs\Metrics\MaxNestingLevelSniff: ~
🔧 可配置
在 PHP_CodeSniffer 中
<?xml version="1.0"?> <ruleset name="my-project"> <rule ref="ObjectCalisthenics.Metrics.MaxNestingLevel"> <properties> <property name="maxNestingLevel" value="2"/> </properties> </rule> </ruleset>
在 ECS 中
services: ObjectCalisthenics\Sniffs\Metrics\MaxNestingLevelSniff: maxNestingLevel: 2
2. 不要使用 "else" 关键字
❌
if ($status === self::DONE) { $this->finish(); } else { $this->advance(); }
👍
if ($status === self::DONE) { $this->finish(); return; } $this->advance();
只使用此规则?
在 PHP_CodeSniffer 中
vendor/bin/phpcs ... --sniffs=ObjectCalisthenics.ControlStructures.NoElse
在 ECS 中
# ecs.yaml services: ObjectCalisthenics\Sniffs\ControlStructures\NoElseSniff: ~
5. 仅在每个语句中使用一个对象操作符(《->》)
❌
$this->container->getBuilder()->addDefinition(SniffRunner::class);
👍
$containerBuilder = $this->getContainerBuilder(); $containerBuilder->addDefinition(SniffRunner::class);
使用这个规则吗?
在 PHP_CodeSniffer 中
vendor/bin/phpcs ... --sniffs=ObjectCalisthenics.CodeAnalysis.OneObjectOperatorPerLine
在 ECS 中
# ecs.yaml services: ObjectCalisthenics\Sniffs\CodeAnalysis\OneObjectOperatorPerLineSniff: ~
🔧 可配置
在 PHP_CodeSniffer 中
<?xml version="1.0"?> <ruleset name="my-project"> <rule ref="ObjectCalisthenics.CodeAnalysis.OneObjectOperatorPerLine"> <properties> <property name="variablesHoldingAFluentInterface" type="array" value="$queryBuilder,$containerBuilder"/> <property name="methodsStartingAFluentInterface" type="array" value="createQueryBuilder"/> <property name="methodsEndingAFluentInterface" type="array" value="execute,getQuery"/> </properties> </rule> </ruleset>
在 ECS 中
services: ObjectCalisthenics\Sniffs\CodeAnalysis\OneObjectOperatorPerLineSniff: variablesHoldingAFluentInterface: ["$queryBuilder", "$containerBuilder"] methodsStartingAFluentInterface: ["createQueryBuilder"] methodsEndingAFluentInterface: ["execute", "getQuery"]
6. 不要缩写
这与类、特质、接口、常量、函数和变量名称有关。
❌
class EM { // ... }
👍
class EntityMailer { // ... }
使用这个规则吗?
在 PHP_CodeSniffer 中
vendor/bin/phpcs ... --sniffs=ObjectCalisthenics.NamingConventions.ElementNameMinimalLength
在 ECS 中
# ecs.yaml services: ObjectCalisthenics\Sniffs\NamingConventions\ElementNameMinimalLengthSniff: ~
🔧 可配置
在 PHP_CodeSniffer 中
<?xml version="1.0"?> <ruleset name="my-project"> <rule ref="ObjectCalisthenics.NamingConventions.ElementNameMinimalLength"> <properties> <property name="minLength" value="3"/> <property name="allowedShortNames" type="array" value="i,id,to,up"/> </properties> </rule> </ruleset>
在 ECS 中
# ecs.yaml services: ObjectCalisthenics\Sniffs\NamingConventions\ElementNameMinimalLengthSniff: minLength: 3 allowedShortNames: ["i", "id", "to", "up"]
7. 保持你的类小型化
❌
class SimpleStartupController { // 300 lines of code }
👍
class SimpleStartupController { // 50 lines of code }
❌
class SomeClass { public function simpleLogic() { // 30 lines of code } }
👍
class SomeClass { public function simpleLogic() { // 10 lines of code } }
❌
class SomeClass { // 20 properties }
👍
class SomeClass { // 5 properties }
❌
class SomeClass { // 20 methods }
👍
class SomeClass { // 5 methods }
使用这个规则吗?
在 PHP_CodeSniffer 中
vendor/bin/phpcs ... --sniffs=ObjectCalisthenics.Files.ClassTraitAndInterfaceLength,ObjectCalisthenics.Files.FunctionLength,ObjectCalisthenics.Metrics.MethodPerClassLimit,ObjectCalisthenics.Metrics.PropertyPerClassLimit
在 ECS 中
# ecs.yaml services: ObjectCalisthenics\Sniffs\Files\ClassTraitAndInterfaceLengthSniff: ~ ObjectCalisthenics\Sniffs\Files\FunctionLengthSniff: ~ ObjectCalisthenics\Sniffs\Metrics\MethodPerClassLimitSniff: ~ ObjectCalisthenics\Sniffs\Metrics\PropertyPerClassLimitSniff: ~
🔧 可配置
在 PHP_CodeSniffer 中
<?xml version="1.0"?> <ruleset name="my-project"> <rule ref="ObjectCalisthenics.Files.ClassTraitAndInterfaceLength"> <properties> <property name="maxLength" value="200"/> </properties> </rule> <rule ref="ObjectCalisthenics.Files.FunctionLength"> <properties> <property name="maxLength" value="20"/> </properties> </rule> <rule ref="ObjectCalisthenics.Metrics.PropertyPerClassLimit"> <properties> <property name="maxCount" value="10"/> </properties> </rule> <rule ref="ObjectCalisthenics.Metrics.MethodPerClassLimit"> <properties> <property name="maxCount" value="10"/> </properties> </rule> </ruleset>
在 ECS 中
# ecs.yaml services: ObjectCalisthenics\Sniffs\Files\ClassTraitAndInterfaceLengthSniff: maxLength: 200 ObjectCalisthenics\Sniffs\Files\FunctionLengthSniff: maxLength: 20 ObjectCalisthenics\Sniffs\Metrics\PropertyPerClassLimitSniff: maxCount: 10 ObjectCalisthenics\Sniffs\Metrics\MethodPerClassLimitSniff: maxCount: 10
9. 不要使用获取器和设置器
这个规则部分与领域驱动设计相关。
- 类不应包含公共属性。
- 方法应该表示行为,而不是设置值。
❌
class ImmutableBankAccount { public $currency = 'USD';
private $amount; public function setAmount(int $amount) { $this->amount = $amount; } }
👍
class ImmutableBankAccount { private $currency = 'USD';
private $amount; public function withdrawAmount(int $withdrawnAmount) { $this->amount -= $withdrawnAmount; } }
使用这个规则吗?
在 PHP_CodeSniffer 中
vendor/bin/phpcs ... --sniffs=ObjectCalisthenics.Classes.ForbiddenPublicProperty,ObjectCalisthenics.NamingConventions.NoSetter
在 ECS 中
# ecs.yaml services: ObjectCalisthenics\Sniffs\Classes\ForbiddenPublicPropertySniff: ~ ObjectCalisthenics\Sniffs\NamingConventions\NoSetterSniff: ~
🔧 可配置
在 PHP_CodeSniffer 中
<?xml version="1.0"?> <ruleset name="my-project"> <rule ref="ObjectCalisthenics.NamingConventions.NoSetter"> <properties> <property name="allowedClasses" type="array" value="*\DataObject"/> </properties> </rule> </ruleset>
在 ECS 中
# ecs.yaml services: ObjectCalisthenics\Sniffs\NamingConventions\NoSetterSniff: allowedClasses: - '*\DataObject'
未实施规则 - 过于严格、模糊或令人烦恼
在实际使用中,我们发现这些规则过于严格、模糊或甚至令人烦恼,而不是帮助编写更干净、更实用的代码。它们也与领域驱动设计密切相关。
3. 包装原始类型和字符串 - 自PHP 7以来,您可以使用define(strict_types=1)
和标量类型提示。对于其他情况,例如电子邮件,您可以在您的域中处理。
4. 使用一等集合 - 这个规则很有意义,但在实践中过于严格,没有实际用途。即使我们的代码也完全无法通过它。
8. 不要使用具有两个以上实例变量的类 - 这取决于每个项目的具体领域。为这个制定规则没有意义。
贡献的3个规则
-
每个PR一个特性
-
每个新特性都必须由测试来覆盖
-
所有测试和样式检查必须通过
composer complete-check
那时我们将很乐意合并您的功能。