From d36e8bff4a122bfd51083a0eb9c8296168dbf2f9 Mon Sep 17 00:00:00 2001 From: roettigl Date: Mon, 12 Aug 2019 10:50:33 +0200 Subject: [PATCH 1/4] #21: WIP add rule for phpdoc --- .../Commenting/PHPDocFormattingSniff.php | 108 ++++++++++++++++++ .../Commenting/PHPDocFormattingUnitTest.1.inc | 40 +++++++ .../Commenting/PHPDocFormattingUnitTest.2.inc | 81 +++++++++++++ .../Commenting/PHPDocFormattingUnitTest.php | 35 ++++++ 4 files changed, 264 insertions(+) create mode 100644 Magento2/Sniffs/Commenting/PHPDocFormattingSniff.php create mode 100644 Magento2/Tests/Commenting/PHPDocFormattingUnitTest.1.inc create mode 100644 Magento2/Tests/Commenting/PHPDocFormattingUnitTest.2.inc create mode 100644 Magento2/Tests/Commenting/PHPDocFormattingUnitTest.php diff --git a/Magento2/Sniffs/Commenting/PHPDocFormattingSniff.php b/Magento2/Sniffs/Commenting/PHPDocFormattingSniff.php new file mode 100644 index 00000000..3f1153cd --- /dev/null +++ b/Magento2/Sniffs/Commenting/PHPDocFormattingSniff.php @@ -0,0 +1,108 @@ +getTokens(); + + $funcParamStart = $tokens[$stackPtr]['parenthesis_opener']; + $funcParamCloser = $tokens[$stackPtr]['parenthesis_closer']; + $funcBodyStart = $tokens[$stackPtr]['scope_opener']; + + // search for php7 return type declarations like func() : string {} + $funcReturnTypePos = $phpcsFile->findNext([T_STRING], $funcParamCloser, $funcBodyStart); + $funcParamType = null; + if ($funcReturnTypePos !== false) { + $funcReturnType = $tokens[$funcReturnTypePos]['content']; + } + + $funcParamTypeList = $this->getFunctionParameterWithType( + array_slice( + $tokens, + $funcParamStart + 1, + $funcParamCloser - $funcParamStart + ) + ); + $paramType = null; + + if (isset($funcReturnType) && count($funcParamTypeList) !== 0) { + // function use php7 return type declarations - no check required + return; + } + + // search for php doc block + $find = Tokens::$methodPrefixes; + $find[] = T_WHITESPACE; + $commentEnd = $phpcsFile->findPrevious($find, $stackPtr - 1, null, true); + $commentStart = false; + if($commentEnd !== false) + { + $commentStart = ''; + } + } + + + /** + * Returns all parameter as list with there php 7 type declarations like + * func(string $arg1, int $arg2) + * + * @param array $tokens + * @return array + */ + private function getFunctionParameterWithType(array $tokens) + { + $paramType = null; + $functionParameterList = []; + + foreach ($tokens as $token) { + $type = $token['code']; + $content = $token['content']; + + if ($type === T_COMMA) { + $paramType = null; + continue; + } + + if ($type === T_STRING) { + $paramType = $content; + continue; + } + + if ($type === T_VARIABLE && $paramType !== null) { + $functionParameterList[] = + [ + 'content' => $content, + 'type' => $paramType + ]; + } + } + + return $functionParameterList; + } +} diff --git a/Magento2/Tests/Commenting/PHPDocFormattingUnitTest.1.inc b/Magento2/Tests/Commenting/PHPDocFormattingUnitTest.1.inc new file mode 100644 index 00000000..5d75f000 --- /dev/null +++ b/Magento2/Tests/Commenting/PHPDocFormattingUnitTest.1.inc @@ -0,0 +1,40 @@ + Date: Tue, 13 Aug 2019 00:25:00 +0200 Subject: [PATCH 2/4] 21: add new checks --- .../Sniffs/Commenting/FunctionPHPDocBlock.php | 142 +++++++++++++ .../FunctionsPHPDocFormattingSniff.php | 195 ++++++++++++++++++ .../Commenting/PHPDocFormattingSniff.php | 108 ---------- ...> FunctionsPHPDocFormattingUnitTest.1.inc} | 19 +- ...> FunctionsPHPDocFormattingUnitTest.2.inc} | 0 ... => FunctionsPHPDocFormattingUnitTest.php} | 6 +- 6 files changed, 356 insertions(+), 114 deletions(-) create mode 100644 Magento2/Sniffs/Commenting/FunctionPHPDocBlock.php create mode 100644 Magento2/Sniffs/Commenting/FunctionsPHPDocFormattingSniff.php delete mode 100644 Magento2/Sniffs/Commenting/PHPDocFormattingSniff.php rename Magento2/Tests/Commenting/{PHPDocFormattingUnitTest.1.inc => FunctionsPHPDocFormattingUnitTest.1.inc} (69%) rename Magento2/Tests/Commenting/{PHPDocFormattingUnitTest.2.inc => FunctionsPHPDocFormattingUnitTest.2.inc} (100%) rename Magento2/Tests/Commenting/{PHPDocFormattingUnitTest.php => FunctionsPHPDocFormattingUnitTest.php} (75%) diff --git a/Magento2/Sniffs/Commenting/FunctionPHPDocBlock.php b/Magento2/Sniffs/Commenting/FunctionPHPDocBlock.php new file mode 100644 index 00000000..061ace36 --- /dev/null +++ b/Magento2/Sniffs/Commenting/FunctionPHPDocBlock.php @@ -0,0 +1,142 @@ + false, + 'description' => $description, + 'return' => '', + 'parameter' => [], + 'throws' => [], + ]; + + $lastDocLine = 0; + $docType = false; + for ($i = $docStart; $i <= $docEnd; $i++) { + $token = $tokens[$i]; + $code = $token['code']; + $line = $token['line']; + $content = $token['content']; + + preg_match('/@inheritdoc*/', $content, $inheritdoc); + if (isset($inheritdoc[0])) { + $functionDeclarations['warning'] = ['The @inheritdoc tag SHOULD NOT be used.', $i]; + + return $functionDeclarations; + } + + if ($code === T_DOC_COMMENT_TAG) { + $lastDocLine = $line; + $docType = $token['content']; + continue; + } + + if ($lastDocLine !== $line) { + $lastDocLine = 0; + continue; + } + + if ($content === ' ' || $content === "\n") { + continue; + } + + preg_match_all('/[A-Za-z0-9$]*/', $content, $docTokens); + $docTokens = array_values(array_filter($docTokens[0])); + + switch ($docType) { + case '@param': + $functionDeclarations = self::addParamTagValue($docTokens, $functionDeclarations); + break; + + case '@return': + $functionDeclarations = self::addReturnTagValue($docTokens, $functionDeclarations); + break; + + case '@throws': + $functionDeclarations = self::addThrowsTagValue($docTokens, $functionDeclarations); + break; + } + } + + return $functionDeclarations; + } + + /** + * @param array $tokens + * @param array $functionDeclarations + * @return array + */ + private function addParamTagValue(array $tokens, array $functionDeclarations) + { + if (count($tokens) === 0) { + return $functionDeclarations; // empty parameter declaration + } + + $type = false; + $content = false; + + foreach ($tokens as $token) { + if (strpos($token, '$') !== false) { + $content = $token; + continue; + } + $type = $token; + } + + $functionDeclarations['parameter'][] = ['content' => $content, 'type' => $type,]; + + return $functionDeclarations; + } + + /** + * @param array $docTokens + * @param array $functionDeclarations + * @return array + */ + private function addReturnTagValue(array $docTokens, array $functionDeclarations) + { + // @todo imepelement me + return $functionDeclarations; + } + + /** + * @param array $docTokens + * @param array $functionDeclarations + * @return array + */ + private function addThrowsTagValue(array $docTokens, array $functionDeclarations) + { + // @todo imepelement me + return $functionDeclarations; + } +} diff --git a/Magento2/Sniffs/Commenting/FunctionsPHPDocFormattingSniff.php b/Magento2/Sniffs/Commenting/FunctionsPHPDocFormattingSniff.php new file mode 100644 index 00000000..0a9ce937 --- /dev/null +++ b/Magento2/Sniffs/Commenting/FunctionsPHPDocFormattingSniff.php @@ -0,0 +1,195 @@ +functionPHPDocBlock = new FunctionPHPDocBlock(); + } + + /** + * @inheritDoc + */ + public function register() + { + return [T_FUNCTION]; + } + + /** + * @inheritDoc + */ + public function process(File $phpcsFile, $stackPtr) + { + $funcReturnType = $this->anaylsePhp7ReturnDeclaration($phpcsFile, $stackPtr); + $funcParamTypeList = $this->analysePhp7ParamDeclarations($phpcsFile, $stackPtr); + $phpDocTokens = $this->getPhpDocTokens($phpcsFile, $stackPtr); + + if ($phpDocTokens === false && $funcReturnType === false && count($funcParamTypeList['missing_type'] !== 0)) { + $phpcsFile->addWarning('Use php 7 type declarations or an php doc block', $stackPtr, $this->warningCode); + + // @fixme find also __constuct that dont have return type + return; + } + + if ($phpDocTokens === false) { + // @todo impelement checks + return; + } + + $parsePhpDocTokens = $this->functionPHPDocBlock->execute( + $phpcsFile->getTokens(), + $phpDocTokens[0], + $phpDocTokens[1] + ); + + $warning = $parsePhpDocTokens['warning']; + + if ($warning !== false) { + $phpcsFile->addWarning($warning[0], $warning[1], $this->warningCode); + return; + } + + + // @todo impelement checks + } + + /** + * Search for php7 return type declarations like func() : string {} + * @param File $phpcsFile + * @param $stackPtr + * @return bool|string + */ + private function anaylsePhp7ReturnDeclaration(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $funcParamCloser = $tokens[$stackPtr]['parenthesis_closer']; + $funcReturnTypePos = $phpcsFile->findNext([T_STRING], $funcParamCloser, $tokens[$stackPtr]['scope_opener']); + + $funcReturnType = false; + if ($funcReturnTypePos !== false) { + $funcReturnType = $tokens[$funcReturnTypePos]['content']; + } + + return $funcReturnType; + } + + /** + * Search for php7 return type declarations like func(bool $arg1) {} + * @param File $phpcsFile + * @param $stackPtr + * @return array + */ + private function analysePhp7ParamDeclarations(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $funcParamStart = $tokens[$stackPtr]['parenthesis_opener']; + $funcParamCloser = $tokens[$stackPtr]['parenthesis_closer']; + + $functionParameterTokens = array_slice( + $tokens, + $funcParamStart + 1, + $funcParamCloser - ($funcParamStart + 1) + ); + + return $this->parseFunctionTokens($functionParameterTokens); + } + + /** + * Returns all parameter as list with there php 7 type declarations like + * func(string $arg1, int $arg2) + * + * @param array $tokens + * @return array + */ + private function parseFunctionTokens(array $tokens) + { + $paramType = null; + $functionParameterList = [ + 'missing_type' => [], + 'has_type' => [], + ]; + + foreach ($tokens as $token) { + $type = $token['code']; + $content = $token['content']; + + if ($type === T_COMMA) { + $paramType = null; + continue; + } + + if ($type === T_STRING) { + $paramType = $content; + continue; + } + if ($content === ' ') { + continue; + } + + $key = $paramType !== null ? 'has_type' : 'missing_type'; + $functionParameterList[$key][] = + [ + 'content' => $content, + 'type' => $paramType, + ]; + } + + return $functionParameterList; + } + + /** + * Parse the doc block for type and return declarations + * @param File $phpcsFile + * @param $stackPtr + * @return array|bool + */ + private function getPhpDocTokens(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + // search for php doc block + $find = Tokens::$methodPrefixes; + $find[] = T_WHITESPACE; + + $commentEnd = $phpcsFile->findPrevious($find, $stackPtr - 1, null, true); + $commentStart = false; + + if ($commentEnd !== false) { + $endToken = $tokens[$commentEnd]; + if ($endToken['code'] === T_DOC_COMMENT_CLOSE_TAG) { + $commentStart = $tokens[$commentEnd]['comment_opener']; + } + } + + if ($commentStart === false) { + return false; + } + + return [$commentStart + 1, $commentEnd - 1]; + } +} diff --git a/Magento2/Sniffs/Commenting/PHPDocFormattingSniff.php b/Magento2/Sniffs/Commenting/PHPDocFormattingSniff.php deleted file mode 100644 index 3f1153cd..00000000 --- a/Magento2/Sniffs/Commenting/PHPDocFormattingSniff.php +++ /dev/null @@ -1,108 +0,0 @@ -getTokens(); - - $funcParamStart = $tokens[$stackPtr]['parenthesis_opener']; - $funcParamCloser = $tokens[$stackPtr]['parenthesis_closer']; - $funcBodyStart = $tokens[$stackPtr]['scope_opener']; - - // search for php7 return type declarations like func() : string {} - $funcReturnTypePos = $phpcsFile->findNext([T_STRING], $funcParamCloser, $funcBodyStart); - $funcParamType = null; - if ($funcReturnTypePos !== false) { - $funcReturnType = $tokens[$funcReturnTypePos]['content']; - } - - $funcParamTypeList = $this->getFunctionParameterWithType( - array_slice( - $tokens, - $funcParamStart + 1, - $funcParamCloser - $funcParamStart - ) - ); - $paramType = null; - - if (isset($funcReturnType) && count($funcParamTypeList) !== 0) { - // function use php7 return type declarations - no check required - return; - } - - // search for php doc block - $find = Tokens::$methodPrefixes; - $find[] = T_WHITESPACE; - $commentEnd = $phpcsFile->findPrevious($find, $stackPtr - 1, null, true); - $commentStart = false; - if($commentEnd !== false) - { - $commentStart = ''; - } - } - - - /** - * Returns all parameter as list with there php 7 type declarations like - * func(string $arg1, int $arg2) - * - * @param array $tokens - * @return array - */ - private function getFunctionParameterWithType(array $tokens) - { - $paramType = null; - $functionParameterList = []; - - foreach ($tokens as $token) { - $type = $token['code']; - $content = $token['content']; - - if ($type === T_COMMA) { - $paramType = null; - continue; - } - - if ($type === T_STRING) { - $paramType = $content; - continue; - } - - if ($type === T_VARIABLE && $paramType !== null) { - $functionParameterList[] = - [ - 'content' => $content, - 'type' => $paramType - ]; - } - } - - return $functionParameterList; - } -} diff --git a/Magento2/Tests/Commenting/PHPDocFormattingUnitTest.1.inc b/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.1.inc similarity index 69% rename from Magento2/Tests/Commenting/PHPDocFormattingUnitTest.1.inc rename to Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.1.inc index 5d75f000..8e2190fc 100644 --- a/Magento2/Tests/Commenting/PHPDocFormattingUnitTest.1.inc +++ b/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.1.inc @@ -2,7 +2,14 @@ class EverythingIsGoodHere { + + private function executeSimpleFunctionNoDoc(): void + { + } + /** + * We have an wounderfull meaning desciption there also + * * @param string $arg1 * @param bool $arg2 */ @@ -26,7 +33,7 @@ class EverythingIsGoodHere */ private function throwsAnException(): void { - throw new \Exception(); + throw new Exception(); } /** @@ -37,4 +44,14 @@ class EverythingIsGoodHere private function presentGoodDescription(string $arg1, bool $arg2) { } + + /** + * @param bool $arg1 + * @return string + */ + private function docBlockIsEquels(bool $arg1): string + { + + return ''; + } } diff --git a/Magento2/Tests/Commenting/PHPDocFormattingUnitTest.2.inc b/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.2.inc similarity index 100% rename from Magento2/Tests/Commenting/PHPDocFormattingUnitTest.2.inc rename to Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.2.inc diff --git a/Magento2/Tests/Commenting/PHPDocFormattingUnitTest.php b/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.php similarity index 75% rename from Magento2/Tests/Commenting/PHPDocFormattingUnitTest.php rename to Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.php index b4043f92..3e542152 100644 --- a/Magento2/Tests/Commenting/PHPDocFormattingUnitTest.php +++ b/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.php @@ -10,7 +10,7 @@ /** * Class ConstantsPHPDocFormattingUnitTest */ -class PHPDocFormattingUnitTest extends AbstractSniffUnitTest +class FunctionsPHPDocFormattingUnitTest extends AbstractSniffUnitTest { /** * @inheritdoc @@ -25,10 +25,6 @@ public function getErrorList() */ public function getWarningList($testFile = '') { - if ($testFile === 'PHPDocFormattingUnitTest.1.inc') { - return []; - } - return []; } From f3a98acd625812a8752ae7849d142c205fc3e9cd Mon Sep 17 00:00:00 2001 From: roettigl Date: Sun, 15 Sep 2019 16:12:53 +0200 Subject: [PATCH 3/4] #54: Add new Rule for PHPDoc formatting --- ...lock.php => FunctionPHPDocBlockParser.php} | 42 ++++----- .../FunctionsPHPDocFormattingSniff.php | 91 +++++++++++++++---- .../FunctionsPHPDocFormattingUnitTest.1.inc | 4 + .../FunctionsPHPDocFormattingUnitTest.2.inc | 18 ++-- .../FunctionsPHPDocFormattingUnitTest.php | 16 ++++ 5 files changed, 126 insertions(+), 45 deletions(-) rename Magento2/Sniffs/Commenting/{FunctionPHPDocBlock.php => FunctionPHPDocBlockParser.php} (72%) diff --git a/Magento2/Sniffs/Commenting/FunctionPHPDocBlock.php b/Magento2/Sniffs/Commenting/FunctionPHPDocBlockParser.php similarity index 72% rename from Magento2/Sniffs/Commenting/FunctionPHPDocBlock.php rename to Magento2/Sniffs/Commenting/FunctionPHPDocBlockParser.php index 061ace36..35ed8402 100644 --- a/Magento2/Sniffs/Commenting/FunctionPHPDocBlock.php +++ b/Magento2/Sniffs/Commenting/FunctionPHPDocBlockParser.php @@ -6,7 +6,7 @@ namespace Magento2\Sniffs\Commenting; -class FunctionPHPDocBlock +class FunctionPHPDocBlockParser { /** @@ -33,10 +33,10 @@ public function execute(array $tokens, $docStart, $docEnd) } $functionDeclarations = [ - 'warning' => false, 'description' => $description, + 'tags' => [], 'return' => '', - 'parameter' => [], + 'parameters' => [], 'throws' => [], ]; @@ -48,14 +48,13 @@ public function execute(array $tokens, $docStart, $docEnd) $line = $token['line']; $content = $token['content']; - preg_match('/@inheritdoc*/', $content, $inheritdoc); - if (isset($inheritdoc[0])) { - $functionDeclarations['warning'] = ['The @inheritdoc tag SHOULD NOT be used.', $i]; - - return $functionDeclarations; - } if ($code === T_DOC_COMMENT_TAG) { + // add php tokens also without comment to tag list + if (preg_match('/@[a-z]++/', $content, $output_array)) { + $functionDeclarations['tags'][] = $content; + } + $lastDocLine = $line; $docType = $token['content']; continue; @@ -73,17 +72,22 @@ public function execute(array $tokens, $docStart, $docEnd) preg_match_all('/[A-Za-z0-9$]*/', $content, $docTokens); $docTokens = array_values(array_filter($docTokens[0])); + if (count($docTokens) === 0) { + // ignore empty parameter declaration + continue; + } + switch ($docType) { case '@param': - $functionDeclarations = self::addParamTagValue($docTokens, $functionDeclarations); + $functionDeclarations = $this->addParamTagValue($docTokens, $functionDeclarations); break; case '@return': - $functionDeclarations = self::addReturnTagValue($docTokens, $functionDeclarations); + $functionDeclarations = $this->addReturnTagValue($docTokens, $functionDeclarations); break; case '@throws': - $functionDeclarations = self::addThrowsTagValue($docTokens, $functionDeclarations); + $functionDeclarations = $this->addThrowsTagValue($docTokens, $functionDeclarations); break; } } @@ -98,10 +102,6 @@ public function execute(array $tokens, $docStart, $docEnd) */ private function addParamTagValue(array $tokens, array $functionDeclarations) { - if (count($tokens) === 0) { - return $functionDeclarations; // empty parameter declaration - } - $type = false; $content = false; @@ -113,7 +113,7 @@ private function addParamTagValue(array $tokens, array $functionDeclarations) $type = $token; } - $functionDeclarations['parameter'][] = ['content' => $content, 'type' => $type,]; + $functionDeclarations['parameters'][] = ['content' => $content, 'type' => $type,]; return $functionDeclarations; } @@ -123,9 +123,9 @@ private function addParamTagValue(array $tokens, array $functionDeclarations) * @param array $functionDeclarations * @return array */ - private function addReturnTagValue(array $docTokens, array $functionDeclarations) + private function addReturnTagValue(array $tokens, array $functionDeclarations) { - // @todo imepelement me + $functionDeclarations['return'] = $tokens[0]; return $functionDeclarations; } @@ -134,9 +134,9 @@ private function addReturnTagValue(array $docTokens, array $functionDeclarations * @param array $functionDeclarations * @return array */ - private function addThrowsTagValue(array $docTokens, array $functionDeclarations) + private function addThrowsTagValue(array $tokens, array $functionDeclarations) { - // @todo imepelement me + $functionDeclarations['throws'][] = $tokens[0]; return $functionDeclarations; } } diff --git a/Magento2/Sniffs/Commenting/FunctionsPHPDocFormattingSniff.php b/Magento2/Sniffs/Commenting/FunctionsPHPDocFormattingSniff.php index 0a9ce937..b05b2ae5 100644 --- a/Magento2/Sniffs/Commenting/FunctionsPHPDocFormattingSniff.php +++ b/Magento2/Sniffs/Commenting/FunctionsPHPDocFormattingSniff.php @@ -28,7 +28,7 @@ class FunctionsPHPDocFormattingSniff implements Sniff public function __construct() { - $this->functionPHPDocBlock = new FunctionPHPDocBlock(); + $this->functionPHPDocBlock = new FunctionPHPDocBlockParser(); } /** @@ -44,37 +44,44 @@ public function register() */ public function process(File $phpcsFile, $stackPtr) { - $funcReturnType = $this->anaylsePhp7ReturnDeclaration($phpcsFile, $stackPtr); + $funcReturnType = $this->analysePhp7ReturnDeclaration($phpcsFile, $stackPtr); $funcParamTypeList = $this->analysePhp7ParamDeclarations($phpcsFile, $stackPtr); $phpDocTokens = $this->getPhpDocTokens($phpcsFile, $stackPtr); + $hasPhp7TypeDeclarations = $funcReturnType !== false || count($funcParamTypeList['missing_type']) === 0; - if ($phpDocTokens === false && $funcReturnType === false && count($funcParamTypeList['missing_type'] !== 0)) { - $phpcsFile->addWarning('Use php 7 type declarations or an php doc block', $stackPtr, $this->warningCode); - - // @fixme find also __constuct that dont have return type + if ($phpDocTokens === false && $hasPhp7TypeDeclarations === true) { + // NO check it use all php 7 type declarations and no php doc docblock return; } - if ($phpDocTokens === false) { - // @todo impelement checks + if ($phpDocTokens === false && $hasPhp7TypeDeclarations === false) { + $phpcsFile->addWarning('Use php 7 type declarations or an php doc block', $stackPtr, $this->warningCode); + return; } - $parsePhpDocTokens = $this->functionPHPDocBlock->execute( + $phpDocTokensList = $this->functionPHPDocBlock->execute( $phpcsFile->getTokens(), $phpDocTokens[0], $phpDocTokens[1] ); - $warning = $parsePhpDocTokens['warning']; + if (array_key_exists('@inheritdoc', array_flip($phpDocTokensList['tags']))) { + $phpcsFile->addWarning('The @inheritdoc tag SHOULD NOT be used', $stackPtr, $this->warningCode); - if ($warning !== false) { - $phpcsFile->addWarning($warning[0], $warning[1], $this->warningCode); return; } + if (count($phpDocTokensList['parameters']) > 0) { + $phpcsFile = $this->comparePhp7WithDocBlock( + $funcParamTypeList, + $phpDocTokensList['parameters'], + $phpcsFile, + $stackPtr + ); + } + - // @todo impelement checks } /** @@ -83,12 +90,17 @@ public function process(File $phpcsFile, $stackPtr) * @param $stackPtr * @return bool|string */ - private function anaylsePhp7ReturnDeclaration(File $phpcsFile, $stackPtr) + private function analysePhp7ReturnDeclaration(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); + $functionNameToken = $phpcsFile->findNext(T_STRING, $stackPtr, $tokens[$stackPtr]['parenthesis_opener']); + if (strpos($tokens[$functionNameToken]['content'], '__construct') === 0) { + // magic functions start with __construct dont have php7 return type + return 'void'; + } + $funcParamCloser = $tokens[$stackPtr]['parenthesis_closer']; $funcReturnTypePos = $phpcsFile->findNext([T_STRING], $funcParamCloser, $tokens[$stackPtr]['scope_opener']); - $funcReturnType = false; if ($funcReturnTypePos !== false) { $funcReturnType = $tokens[$funcReturnTypePos]['content']; @@ -130,6 +142,7 @@ private function parseFunctionTokens(array $tokens) { $paramType = null; $functionParameterList = [ + 'count' => 0, 'missing_type' => [], 'has_type' => [], ]; @@ -151,8 +164,9 @@ private function parseFunctionTokens(array $tokens) continue; } + $functionParameterList['count']++; $key = $paramType !== null ? 'has_type' : 'missing_type'; - $functionParameterList[$key][] = + $functionParameterList[$key][$content] = [ 'content' => $content, 'type' => $paramType, @@ -192,4 +206,49 @@ private function getPhpDocTokens(File $phpcsFile, $stackPtr) return [$commentStart + 1, $commentEnd - 1]; } + + private function comparePhp7WithDocBlock(array $php7Tokens, array $docBlockTokens, File $phpcsFile, $stackPtr) + { + + $parsedDocToken = []; + foreach ($docBlockTokens as $token) { + $parameterName = $token['content']; + if (isset($parsedDocToken[$parameterName])) { + $phpcsFile->addWarning( + sprintf('Parameter %s is definitely multiple', $parameterName), + $stackPtr, + $this->warningCode + ); + return $phpcsFile; + } + + $parsedDocToken[$parameterName] = $token['type']; + } + + if (count($parsedDocToken) > $php7Tokens['count']) { + $phpcsFile->addWarning( + 'More documented parameter than real function parameter', + $stackPtr, + $this->warningCode + ); + return $phpcsFile; + } + + $parsedDocTokenKeys = array_keys($parsedDocToken); + $hasMissingTypes = count($php7Tokens['missing_type']) > 0; + if ($hasMissingTypes === true && array_keys($php7Tokens['missing_type']) !== $parsedDocTokenKeys) { + $phpcsFile->addWarning( + 'Documented parameter and real function parameter dont match', + $stackPtr, + $this->warningCode + ); + + return $phpcsFile; + } + + $t = 12; + + + return $phpcsFile; + } } diff --git a/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.1.inc b/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.1.inc index 8e2190fc..f3f34273 100644 --- a/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.1.inc +++ b/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.1.inc @@ -3,6 +3,10 @@ class EverythingIsGoodHere { + public function __construct(string $arg1) + { + } + private function executeSimpleFunctionNoDoc(): void { } diff --git a/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.2.inc b/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.2.inc index b18b6c0d..9b9d6d18 100644 --- a/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.2.inc +++ b/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.2.inc @@ -1,14 +1,23 @@ Date: Sun, 15 Sep 2019 17:16:06 +0200 Subject: [PATCH 4/4] #54: Add new Rule for PHPDoc formatting --- .../Commenting/FunctionPHPDocBlockParser.php | 7 ++- .../FunctionsPHPDocFormattingSniff.php | 61 ++++++++++++++----- .../FunctionsPHPDocFormattingUnitTest.2.inc | 11 ---- .../FunctionsPHPDocFormattingUnitTest.php | 30 +++++---- Magento2/ruleset.xml | 4 ++ 5 files changed, 70 insertions(+), 43 deletions(-) diff --git a/Magento2/Sniffs/Commenting/FunctionPHPDocBlockParser.php b/Magento2/Sniffs/Commenting/FunctionPHPDocBlockParser.php index 35ed8402..c23975e9 100644 --- a/Magento2/Sniffs/Commenting/FunctionPHPDocBlockParser.php +++ b/Magento2/Sniffs/Commenting/FunctionPHPDocBlockParser.php @@ -17,6 +17,7 @@ class FunctionPHPDocBlockParser */ public function execute(array $tokens, $docStart, $docEnd) { + $is_empty = true; $description = false; for ($i = $docStart; $i <= $docEnd; $i++) { $token = $tokens[$i]; @@ -29,10 +30,12 @@ public function execute(array $tokens, $docStart, $docEnd) if ($code === T_DOC_COMMENT_STRING && $content !== "\n") { $description = $content; + $is_empty = false; } } $functionDeclarations = [ + 'is_empty' => $is_empty, 'description' => $description, 'tags' => [], 'return' => '', @@ -114,7 +117,7 @@ private function addParamTagValue(array $tokens, array $functionDeclarations) } $functionDeclarations['parameters'][] = ['content' => $content, 'type' => $type,]; - + $functionDeclarations['is_empty'] = false; return $functionDeclarations; } @@ -126,6 +129,7 @@ private function addParamTagValue(array $tokens, array $functionDeclarations) private function addReturnTagValue(array $tokens, array $functionDeclarations) { $functionDeclarations['return'] = $tokens[0]; + $functionDeclarations['is_empty'] = false; return $functionDeclarations; } @@ -137,6 +141,7 @@ private function addReturnTagValue(array $tokens, array $functionDeclarations) private function addThrowsTagValue(array $tokens, array $functionDeclarations) { $functionDeclarations['throws'][] = $tokens[0]; + $functionDeclarations['is_empty'] = false; return $functionDeclarations; } } diff --git a/Magento2/Sniffs/Commenting/FunctionsPHPDocFormattingSniff.php b/Magento2/Sniffs/Commenting/FunctionsPHPDocFormattingSniff.php index b05b2ae5..099c2e88 100644 --- a/Magento2/Sniffs/Commenting/FunctionsPHPDocFormattingSniff.php +++ b/Magento2/Sniffs/Commenting/FunctionsPHPDocFormattingSniff.php @@ -47,7 +47,7 @@ public function process(File $phpcsFile, $stackPtr) $funcReturnType = $this->analysePhp7ReturnDeclaration($phpcsFile, $stackPtr); $funcParamTypeList = $this->analysePhp7ParamDeclarations($phpcsFile, $stackPtr); $phpDocTokens = $this->getPhpDocTokens($phpcsFile, $stackPtr); - $hasPhp7TypeDeclarations = $funcReturnType !== false || count($funcParamTypeList['missing_type']) === 0; + $hasPhp7TypeDeclarations = $funcReturnType !== false && count($funcParamTypeList['missing_type']) === 0; if ($phpDocTokens === false && $hasPhp7TypeDeclarations === true) { // NO check it use all php 7 type declarations and no php doc docblock @@ -59,21 +59,44 @@ public function process(File $phpcsFile, $stackPtr) return; } - + $tokens = $phpcsFile->getTokens(); $phpDocTokensList = $this->functionPHPDocBlock->execute( - $phpcsFile->getTokens(), + $tokens, $phpDocTokens[0], $phpDocTokens[1] ); + if ($phpDocTokensList['is_empty']) { + $phpcsFile->addWarning('Empty Docblock SHOULD NOT be used', $stackPtr, $this->warningCode); + return; + } + + $description = $phpDocTokensList['description']; + + if ($description !== false) { + $functionNameToken = $phpcsFile->findNext(T_STRING, $stackPtr, $tokens[$stackPtr]['parenthesis_opener']); + $functionName = str_replace(['_', ' ', '.', ','], '', strtolower($tokens[$functionNameToken]['content'])); + $description = str_replace(['_', ' ', '.', ','], '', strtolower($description)); + + if ($functionName === $description) { + $phpcsFile->addWarning( + sprintf( + '%s description should contain additional information beyond the name already supplies.', + ucfirst($phpDocTokensList['description']) + ), + $stackPtr, + 'InvalidDescription' + ); + } + } + if (array_key_exists('@inheritdoc', array_flip($phpDocTokensList['tags']))) { $phpcsFile->addWarning('The @inheritdoc tag SHOULD NOT be used', $stackPtr, $this->warningCode); - return; } if (count($phpDocTokensList['parameters']) > 0) { - $phpcsFile = $this->comparePhp7WithDocBlock( + $this->comparePhp7WithDocBlock( $funcParamTypeList, $phpDocTokensList['parameters'], $phpcsFile, @@ -219,7 +242,7 @@ private function comparePhp7WithDocBlock(array $php7Tokens, array $docBlockToken $stackPtr, $this->warningCode ); - return $phpcsFile; + return; } $parsedDocToken[$parameterName] = $token['type']; @@ -231,24 +254,32 @@ private function comparePhp7WithDocBlock(array $php7Tokens, array $docBlockToken $stackPtr, $this->warningCode ); - return $phpcsFile; + return; } - $parsedDocTokenKeys = array_keys($parsedDocToken); $hasMissingTypes = count($php7Tokens['missing_type']) > 0; - if ($hasMissingTypes === true && array_keys($php7Tokens['missing_type']) !== $parsedDocTokenKeys) { + if ($hasMissingTypes === false) { + return; + } + + $php7ParamKey = array_keys($php7Tokens['missing_type']); + $parsedDocTokenKeys = array_keys($parsedDocToken); + if ($php7ParamKey !== $parsedDocTokenKeys) { $phpcsFile->addWarning( 'Documented parameter and real function parameter dont match', $stackPtr, $this->warningCode ); - - return $phpcsFile; } - $t = 12; - - - return $phpcsFile; + foreach ($php7ParamKey as $parameter) { + if (!isset($parsedDocToken[$parameter]) || $parsedDocToken[$parameter] === false) { + $phpcsFile->addWarning( + sprintf('Type for parameter %s is missing', $parameter), + $stackPtr, + $this->warningCode + ); + } + } } } diff --git a/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.2.inc b/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.2.inc index 9b9d6d18..9f6a3825 100644 --- a/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.2.inc +++ b/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.2.inc @@ -69,15 +69,4 @@ class EverythingIsBadHere private function dockBlockIsEmpty(string $arg1, bool $arg2): bool { } - - /** - * - * - * @param string $arg1 - * @param bool $arg2 - * - */ - private function redundantLines($arg1, $arg2): bool - { - } } diff --git a/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.php b/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.php index 471a954e..05fe9b65 100644 --- a/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.php +++ b/Magento2/Tests/Commenting/FunctionsPHPDocFormattingUnitTest.php @@ -3,6 +3,7 @@ * Copyright © Magento. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento2\Tests\Commenting; use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest; @@ -25,23 +26,20 @@ public function getErrorList() */ public function getWarningList($testFile = '') { - - if($testFile === 'FunctionsPHPDocFormattingUnitTest.2.inc') - { - return [ - 11, - 19, - 26, - 30, - 40, - 47, - 55, - 62, - 69, - 80 - ]; + if ($testFile !== 'FunctionsPHPDocFormattingUnitTest.2.inc') { + return []; } - return []; + return [ + 11 => 1, + 19 => 1, + 26 => 2, + 30 => 1, + 40 => 1, + 47 => 1, + 55 => 2, + 62 => 1, + 69 => 1, + ]; } } diff --git a/Magento2/ruleset.xml b/Magento2/ruleset.xml index 246b6fe1..08f6b633 100644 --- a/Magento2/ruleset.xml +++ b/Magento2/ruleset.xml @@ -532,6 +532,10 @@ 5 warning + + 5 + warning + 5 warning