From d9a31dc57513c194e13478fd39b970ed85876e16 Mon Sep 17 00:00:00 2001 From: Oskar Pfeifer-Bley Date: Wed, 22 Feb 2017 10:45:01 +0100 Subject: [PATCH 1/3] Exclude fluent interfaces from OneObjectOperatorPerLine --- .../OneObjectOperatorPerLineSniff.php | 62 +++++++++++++++++-- .../OneObjectOperatorPerLineSniffTest.php | 11 ++++ ...peratorPerLineSniffTestFluentInterface.inc | 21 +++++++ 3 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 tests/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniffTestFluentInterface.inc diff --git a/src/ObjectCalisthenics/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniff.php b/src/ObjectCalisthenics/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniff.php index 0a3cd03..6b6b2ef 100644 --- a/src/ObjectCalisthenics/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniff.php +++ b/src/ObjectCalisthenics/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniff.php @@ -7,6 +7,21 @@ final class OneObjectOperatorPerLineSniff implements Sniff { + /** + * @var string + */ + public $variablesHoldingAFluentInterface = '$queryBuilder'; + + /** + * @var string + */ + public $methodsStartingAFluentInterface = 'createQueryBuilder'; + + /** + * @var string + */ + public $methodsEndingAFluentInterface = 'execute,getQuery'; + /** * @var File */ @@ -27,10 +42,15 @@ final class OneObjectOperatorPerLineSniff implements Sniff */ private $tokens; + /** + * @var string + */ + private $variableName; + /** * @return int[] */ - public function register(): array + public function register() : array { return [T_VARIABLE]; } @@ -47,8 +67,8 @@ public function process(File $file, $position): void $this->callerTokens = []; -// $tokens = $file->getTokens(); $pointer = $this->ignoreWhitespace($position + 1); + $this->variableName = $this->tokens[$this->position]['content']; $token = $this->tokens[$position]; $isOwnCall = ($token['content'] === '$this'); @@ -69,7 +89,7 @@ private function ignoreWhitespace(int $start): int private function handleTwoObjectOperators(bool $isOwnCall): void { - if ($this->callerTokens && !$isOwnCall) { + if ($this->callerTokens && !$isOwnCall && !$this->isInFluentInterfaceMode()) { $this->file->addError('Only one object operator per line.', $this->position, self::class); } } @@ -87,13 +107,45 @@ private function handleExcludedFluentInterfaces(array $tmpToken, string $tmpToke if ( ($memberTokenType === 'property' && $tmpTokenType === 'property') || ($memberTokenType === 'method' && $tmpTokenType === 'property') || - ($memberTokenType === 'method' && $tmpTokenType === 'method' && $memberTokenCount > 1 && $memberToken['token']['content'] !== $tmpToken['content']) + ($memberTokenType === 'method' && $tmpTokenType === 'method' + && $memberTokenCount > 1 && $memberToken['token']['content'] !== $tmpToken['content'] && !$this->isInFluentInterfaceMode()) ) { $this->file->addError('Only one object operator per line.', $this->position, self::class); } } - private function handleObjectOperators(int $pointer, bool $isOwnCall): void + private function isInFluentInterfaceMode(): bool + { + $lastEndPoint = $this->computeLastCallOfAnyFrom(explode(',', $this->methodsEndingAFluentInterface)); + $lastStartPoint = $this->computeLastCallOfAnyFrom(explode(',', $this->methodsStartingAFluentInterface)); + + if (in_array($this->variableName, explode(',', $this->variablesHoldingAFluentInterface), true)) { + $lastStartPoint = max($lastStartPoint, -1); + } + + return $lastStartPoint > -2 + && $lastStartPoint > $lastEndPoint; + } + + /** + * @param array $methods + * + * @return int The last position of the method calls within the callerTokens + * or -2 if none of the methods has been called + */ + private function computeLastCallOfAnyFrom(array $methods): int + { + $calls = array_filter($this->callerTokens, function (array $token) use ($methods) { + return in_array($token['token']['content'], $methods); + }); + if (count($calls) > 0) { + return array_search(end($calls), $this->callerTokens); + } + + return -2; + } + + private function handleObjectOperators(int $pointer, bool $isOwnCall) { while ($this->tokens[$pointer]['code'] === T_OBJECT_OPERATOR) { $tmpToken = $this->tokens[++$pointer]; diff --git a/tests/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniffTest.php b/tests/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniffTest.php index 0dcb366..61d7baa 100644 --- a/tests/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniffTest.php +++ b/tests/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniffTest.php @@ -18,4 +18,15 @@ public function test(): void $this->assertSame(2, $errorCount); } + + public function testFluentInterfaces() + { + $codeSnifferRunner = new CodeSnifferRunner(); + $errorCount = $codeSnifferRunner->detectErrorCountInFileForSniff( + __DIR__.'/OneObjectOperatorPerLineSniffTestFluentInterface.inc', + OneObjectOperatorPerLineSniff::class + ); + + $this->assertSame(1, $errorCount); + } } diff --git a/tests/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniffTestFluentInterface.inc b/tests/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniffTestFluentInterface.inc new file mode 100644 index 0000000..1dee3bd --- /dev/null +++ b/tests/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniffTestFluentInterface.inc @@ -0,0 +1,21 @@ +connection + ->createQueryBuilder() + ->select('a.id', 'a.name') + ->from('table', 'a'); + $this->addConditions($queryBuilder); + return $queryBuilder->execute()->fetchAll(); + } + + private function addConditions($queryBuilder) + { + $queryBuilder->where('a.active = 1')->andWhere('a.email IS NOT NULL'); + } +} From 09d6d64859082a6137b33fa5529f90c6a7824af4 Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Fri, 10 Mar 2017 16:00:15 +0100 Subject: [PATCH 2/3] FluentInterface: turn string to arrays --- .../OneObjectOperatorPerLineSniff.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ObjectCalisthenics/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniff.php b/src/ObjectCalisthenics/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniff.php index 6b6b2ef..0eb0703 100644 --- a/src/ObjectCalisthenics/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniff.php +++ b/src/ObjectCalisthenics/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniff.php @@ -8,19 +8,19 @@ final class OneObjectOperatorPerLineSniff implements Sniff { /** - * @var string + * @var string[] */ - public $variablesHoldingAFluentInterface = '$queryBuilder'; + public $variablesHoldingAFluentInterface = ['$queryBuilder']; /** - * @var string + * @var string[] */ - public $methodsStartingAFluentInterface = 'createQueryBuilder'; + public $methodsStartingAFluentInterface = ['createQueryBuilder']; /** - * @var string + * @var string[] */ - public $methodsEndingAFluentInterface = 'execute,getQuery'; + public $methodsEndingAFluentInterface = ['execute', 'getQuery']; /** * @var File @@ -116,10 +116,10 @@ private function handleExcludedFluentInterfaces(array $tmpToken, string $tmpToke private function isInFluentInterfaceMode(): bool { - $lastEndPoint = $this->computeLastCallOfAnyFrom(explode(',', $this->methodsEndingAFluentInterface)); - $lastStartPoint = $this->computeLastCallOfAnyFrom(explode(',', $this->methodsStartingAFluentInterface)); + $lastEndPoint = $this->computeLastCallOfAnyFrom($this->methodsEndingAFluentInterface); + $lastStartPoint = $this->computeLastCallOfAnyFrom($this->methodsStartingAFluentInterface); - if (in_array($this->variableName, explode(',', $this->variablesHoldingAFluentInterface), true)) { + if (in_array($this->variableName, $this->variablesHoldingAFluentInterface)) { $lastStartPoint = max($lastStartPoint, -1); } From 2b9449f789e18d27e765bf94a34c2d5bb1363948 Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Fri, 10 Mar 2017 16:14:54 +0100 Subject: [PATCH 3/3] fix tests + fix property filter --- src/ObjectCalisthenics/Helper/PropertyFilter.php | 10 +++++++++- .../CodeAnalysis/OneObjectOperatorPerLineSniff.php | 4 ++-- src/ObjectCalisthenics/ruleset.xml | 2 +- .../InstancePropertyPerClassLimitSniffTest.inc | 12 +++++++++++- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/ObjectCalisthenics/Helper/PropertyFilter.php b/src/ObjectCalisthenics/Helper/PropertyFilter.php index afd5567..013c7ff 100644 --- a/src/ObjectCalisthenics/Helper/PropertyFilter.php +++ b/src/ObjectCalisthenics/Helper/PropertyFilter.php @@ -2,6 +2,8 @@ namespace ObjectCalisthenics\Helper; +use Nette\Utils\Strings; + final class PropertyFilter { /** @@ -14,7 +16,13 @@ final class PropertyFilter public static function filterOutScalarProperties(array $propertyList): array { return array_filter($propertyList, function ($property) { - return !in_array($property['type'], self::$scalarPropertyTypes, true); + foreach (self::$scalarPropertyTypes as $scalarPropertyType) { + if (Strings::startsWith($property['type'], $scalarPropertyType)) { + return false; + } + } + + return true; }); } } diff --git a/src/ObjectCalisthenics/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniff.php b/src/ObjectCalisthenics/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniff.php index 0eb0703..c76667b 100644 --- a/src/ObjectCalisthenics/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniff.php +++ b/src/ObjectCalisthenics/Sniffs/CodeAnalysis/OneObjectOperatorPerLineSniff.php @@ -38,7 +38,7 @@ final class OneObjectOperatorPerLineSniff implements Sniff private $callerTokens; /** - * mixed[] + * mixed[]. */ private $tokens; @@ -50,7 +50,7 @@ final class OneObjectOperatorPerLineSniff implements Sniff /** * @return int[] */ - public function register() : array + public function register(): array { return [T_VARIABLE]; } diff --git a/src/ObjectCalisthenics/ruleset.xml b/src/ObjectCalisthenics/ruleset.xml index 03e5b3e..860fe34 100644 --- a/src/ObjectCalisthenics/ruleset.xml +++ b/src/ObjectCalisthenics/ruleset.xml @@ -71,7 +71,7 @@ diff --git a/tests/Sniffs/CodeAnalysis/InstancePropertyPerClassLimitSniffTest.inc b/tests/Sniffs/CodeAnalysis/InstancePropertyPerClassLimitSniffTest.inc index 466bc9c..9489107 100644 --- a/tests/Sniffs/CodeAnalysis/InstancePropertyPerClassLimitSniffTest.inc +++ b/tests/Sniffs/CodeAnalysis/InstancePropertyPerClassLimitSniffTest.inc @@ -129,7 +129,17 @@ trait TestTrait trait OkTestTrait { /** - * @var array + * @var string[] */ protected $array1; + + /** + * @var string[] + */ + protected $array2; + + /** + * @var string[] + */ + protected $array3; }