diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/Standards/Laravel/Sniffs/Commenting/FileCommentSniff.php b/Standards/Laravel/Sniffs/Commenting/FileCommentSniff.php new file mode 100644 index 0000000..d8c41d8 --- /dev/null +++ b/Standards/Laravel/Sniffs/Commenting/FileCommentSniff.php @@ -0,0 +1,545 @@ + + * @author Marc McIntyre + * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + * @link http://pear.php.net/package/PHP_CodeSniffer + */ + +/** + * Parses and verifies the doc comments for files. + * + * @category PHP + * @package PHP_CodeSniffer + * @author Greg Sherwood + * @author Marc McIntyre + * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + * @version Release: @package_version@ + * @link http://pear.php.net/package/PHP_CodeSniffer + */ + +class Laravel_Sniffs_Commenting_FileCommentSniff implements PHP_CodeSniffer_Sniff +{ + + /** + * Tags in correct order and related info. + * + * @var array + */ + protected $tags = array( + '@category' => array( + 'required' => false, + 'allow_multiple' => false, + ), + '@package' => array( + 'required' => false, + 'allow_multiple' => false, + ), + '@subpackage' => array( + 'required' => false, + 'allow_multiple' => false, + ), + '@author' => array( + 'required' => false, + 'allow_multiple' => true, + ), + '@copyright' => array( + 'required' => false, + 'allow_multiple' => true, + ), + '@license' => array( + 'required' => false, + 'allow_multiple' => false, + ), + '@version' => array( + 'required' => false, + 'allow_multiple' => false, + ), + '@link' => array( + 'required' => false, + 'allow_multiple' => true, + ), + '@see' => array( + 'required' => false, + 'allow_multiple' => true, + ), + '@since' => array( + 'required' => false, + 'allow_multiple' => false, + ), + '@deprecated' => array( + 'required' => false, + 'allow_multiple' => false, + ), + ); + + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() + { + return []; + + }//end register() + + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return int + */ + public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + // Find the next non whitespace token. + $commentStart = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true); + + // Allow declare() statements at the top of the file. + if ($tokens[$commentStart]['code'] === T_DECLARE) { + $semicolon = $phpcsFile->findNext(T_SEMICOLON, ($commentStart + 1)); + $commentStart = $phpcsFile->findNext(T_WHITESPACE, ($semicolon + 1), null, true); + } + + // Ignore vim header. + if ($tokens[$commentStart]['code'] === T_COMMENT) { + if (strstr($tokens[$commentStart]['content'], 'vim:') !== false) { + $commentStart = $phpcsFile->findNext( + T_WHITESPACE, + ($commentStart + 1), + null, + true + ); + } + } + + $errorToken = ($stackPtr + 1); + if (isset($tokens[$errorToken]) === false) { + $errorToken--; + } + + if ($tokens[$commentStart]['code'] === T_CLOSE_TAG) { + // We are only interested if this is the first open tag. + return ($phpcsFile->numTokens + 1); + } else if ($tokens[$commentStart]['code'] === T_COMMENT) { + $error = 'You must use "/**" style comments for a file comment'; + $phpcsFile->addError($error, $errorToken, 'WrongStyle'); + $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes'); + return ($phpcsFile->numTokens + 1); + } else if ($commentStart === false + || $tokens[$commentStart]['code'] !== T_DOC_COMMENT_OPEN_TAG + ) { + $phpcsFile->addError('Missing file doc comment', $errorToken, 'Missing'); + $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'no'); + return ($phpcsFile->numTokens + 1); + } else { + $phpcsFile->recordMetric($stackPtr, 'File has doc comment', 'yes'); + } + + // Check the PHP Version, which should be in some text before the first tag. + $commentEnd = $tokens[$commentStart]['comment_closer']; + $found = false; + for ($i = ($commentStart + 1); $i < $commentEnd; $i++) { + if ($tokens[$i]['code'] === T_DOC_COMMENT_TAG) { + break; + } else if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING + && strstr(strtolower($tokens[$i]['content']), 'php version') !== false + ) { + $found = true; + break; + } + } + + if ($found === false) { + $error = 'PHP version not specified'; + $phpcsFile->addWarning($error, $commentEnd, 'MissingVersion'); + } + + // Check each tag. + $this->processTags($phpcsFile, $stackPtr, $commentStart); + + // Ignore the rest of the file. + return ($phpcsFile->numTokens + 1); + + }//end process() + + + /** + * Processes each required or optional tag. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $commentStart Position in the stack where the comment started. + * + * @return void + */ + protected function processTags(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $commentStart) + { + $tokens = $phpcsFile->getTokens(); + + if (get_class($this) === 'PEAR_Sniffs_Commenting_FileCommentSniff') { + $docBlock = 'file'; + } else { + $docBlock = 'class'; + } + + $commentEnd = $tokens[$commentStart]['comment_closer']; + + $foundTags = array(); + $tagTokens = array(); + foreach ($tokens[$commentStart]['comment_tags'] as $tag) { + $name = $tokens[$tag]['content']; + if (isset($this->tags[$name]) === false) { + continue; + } + + if ($this->tags[$name]['allow_multiple'] === false && isset($tagTokens[$name]) === true) { + $error = 'Only one %s tag is allowed in a %s comment'; + $data = array( + $name, + $docBlock, + ); + $phpcsFile->addError($error, $tag, 'Duplicate'.ucfirst(substr($name, 1)).'Tag', $data); + } + + $foundTags[] = $name; + $tagTokens[$name][] = $tag; + + $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd); + if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) { + $error = 'Content missing for %s tag in %s comment'; + $data = array( + $name, + $docBlock, + ); + $phpcsFile->addError($error, $tag, 'Empty'.ucfirst(substr($name, 1)).'Tag', $data); + continue; + } + }//end foreach + + // Check if the tags are in the correct position. + $pos = 0; + foreach ($this->tags as $tag => $tagData) { + if (isset($tagTokens[$tag]) === false) { + if ($tagData['required'] === true) { + $error = 'Missing %s tag in %s comment'; + $data = array( + $tag, + $docBlock, + ); + $phpcsFile->addError($error, $commentEnd, 'Missing'.ucfirst(substr($tag, 1)).'Tag', $data); + } + + continue; + } else { + $method = 'process'.substr($tag, 1); + if (method_exists($this, $method) === true) { + // Process each tag if a method is defined. + call_user_func(array($this, $method), $phpcsFile, $tagTokens[$tag]); + } + } + + if (isset($foundTags[$pos]) === false) { + break; + } + + if ($foundTags[$pos] !== $tag) { + $error = 'The tag in position %s should be the %s tag'; + $data = array( + ($pos + 1), + $tag, + ); + $phpcsFile->addError($error, $tokens[$commentStart]['comment_tags'][$pos], ucfirst(substr($tag, 1)).'TagOrder', $data); + } + + // Account for multiple tags. + $pos++; + while (isset($foundTags[$pos]) === true && $foundTags[$pos] === $tag) { + $pos++; + } + }//end foreach + + }//end processTags() + + + /** + * Process the category tag. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param array $tags The tokens for these tags. + * + * @return void + */ + protected function processCategory(PHP_CodeSniffer_File $phpcsFile, array $tags) + { + $tokens = $phpcsFile->getTokens(); + foreach ($tags as $tag) { + if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) { + // No content. + continue; + } + + $content = $tokens[($tag + 2)]['content']; + if (PHP_CodeSniffer::isUnderscoreName($content) !== true) { + $newContent = str_replace(' ', '_', $content); + $nameBits = explode('_', $newContent); + $firstBit = array_shift($nameBits); + $newName = ucfirst($firstBit).'_'; + foreach ($nameBits as $bit) { + if ($bit !== '') { + $newName .= ucfirst($bit).'_'; + } + } + + $error = 'Category name "%s" is not valid; consider "%s" instead'; + $validName = trim($newName, '_'); + $data = array( + $content, + $validName, + ); + $phpcsFile->addError($error, $tag, 'InvalidCategory', $data); + } + }//end foreach + + }//end processCategory() + + + /** + * Process the package tag. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param array $tags The tokens for these tags. + * + * @return void + */ + protected function processPackage(PHP_CodeSniffer_File $phpcsFile, array $tags) + { + $tokens = $phpcsFile->getTokens(); + foreach ($tags as $tag) { + if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) { + // No content. + continue; + } + + $content = $tokens[($tag + 2)]['content']; + if (PHP_CodeSniffer::isUnderscoreName($content) === true) { + continue; + } + + $newContent = str_replace(' ', '_', $content); + $newContent = trim($newContent, '_'); + $newContent = preg_replace('/[^A-Za-z_]/', '', $newContent); + $nameBits = explode('_', $newContent); + $firstBit = array_shift($nameBits); + $newName = strtoupper($firstBit{0}).substr($firstBit, 1).'_'; + foreach ($nameBits as $bit) { + if ($bit !== '') { + $newName .= strtoupper($bit{0}).substr($bit, 1).'_'; + } + } + + $error = 'Package name "%s" is not valid; consider "%s" instead'; + $validName = trim($newName, '_'); + $data = array( + $content, + $validName, + ); + $phpcsFile->addError($error, $tag, 'InvalidPackage', $data); + }//end foreach + + }//end processPackage() + + + /** + * Process the subpackage tag. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param array $tags The tokens for these tags. + * + * @return void + */ + protected function processSubpackage(PHP_CodeSniffer_File $phpcsFile, array $tags) + { + $tokens = $phpcsFile->getTokens(); + foreach ($tags as $tag) { + if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) { + // No content. + continue; + } + + $content = $tokens[($tag + 2)]['content']; + if (PHP_CodeSniffer::isUnderscoreName($content) === true) { + continue; + } + + $newContent = str_replace(' ', '_', $content); + $nameBits = explode('_', $newContent); + $firstBit = array_shift($nameBits); + $newName = strtoupper($firstBit{0}).substr($firstBit, 1).'_'; + foreach ($nameBits as $bit) { + if ($bit !== '') { + $newName .= strtoupper($bit{0}).substr($bit, 1).'_'; + } + } + + $error = 'Subpackage name "%s" is not valid; consider "%s" instead'; + $validName = trim($newName, '_'); + $data = array( + $content, + $validName, + ); + $phpcsFile->addError($error, $tag, 'InvalidSubpackage', $data); + }//end foreach + + }//end processSubpackage() + + + /** + * Process the author tag(s) that this header comment has. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param array $tags The tokens for these tags. + * + * @return void + */ + protected function processAuthor(PHP_CodeSniffer_File $phpcsFile, array $tags) + { + $tokens = $phpcsFile->getTokens(); + foreach ($tags as $tag) { + if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) { + // No content. + continue; + } + + $content = $tokens[($tag + 2)]['content']; + $local = '\da-zA-Z-_+'; + // Dot character cannot be the first or last character in the local-part. + $localMiddle = $local.'.\w'; + if (preg_match('/^([^<]*)\s+<(['.$local.'](['.$localMiddle.']*['.$local.'])*@[\da-zA-Z][-.\w]*[\da-zA-Z]\.[a-zA-Z]{2,7})>$/', $content) === 0) { + $error = 'Content of the @author tag must be in the form "Display Name "'; + $phpcsFile->addError($error, $tag, 'InvalidAuthors'); + } + } + + }//end processAuthor() + + + /** + * Process the copyright tags. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param array $tags The tokens for these tags. + * + * @return void + */ + protected function processCopyright(PHP_CodeSniffer_File $phpcsFile, array $tags) + { + $tokens = $phpcsFile->getTokens(); + foreach ($tags as $tag) { + if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) { + // No content. + continue; + } + + $content = $tokens[($tag + 2)]['content']; + $matches = array(); + if (preg_match('/^([0-9]{4})((.{1})([0-9]{4}))? (.+)$/', $content, $matches) !== 0) { + // Check earliest-latest year order. + if ($matches[3] !== '') { + if ($matches[3] !== '-') { + $error = 'A hyphen must be used between the earliest and latest year'; + $phpcsFile->addError($error, $tag, 'CopyrightHyphen'); + } + + if ($matches[4] !== '' && $matches[4] < $matches[1]) { + $error = "Invalid year span \"$matches[1]$matches[3]$matches[4]\" found; consider \"$matches[4]-$matches[1]\" instead"; + $phpcsFile->addWarning($error, $tag, 'InvalidCopyright'); + } + } + } else { + $error = '@copyright tag must contain a year and the name of the copyright holder'; + $phpcsFile->addError($error, $tag, 'IncompleteCopyright'); + } + }//end foreach + + }//end processCopyright() + + + /** + * Process the license tag. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param array $tags The tokens for these tags. + * + * @return void + */ + protected function processLicense(PHP_CodeSniffer_File $phpcsFile, array $tags) + { + $tokens = $phpcsFile->getTokens(); + foreach ($tags as $tag) { + if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) { + // No content. + continue; + } + + $content = $tokens[($tag + 2)]['content']; + $matches = array(); + preg_match('/^([^\s]+)\s+(.*)/', $content, $matches); + if (count($matches) !== 3) { + $error = '@license tag must contain a URL and a license name'; + $phpcsFile->addError($error, $tag, 'IncompleteLicense'); + } + } + + }//end processLicense() + + + /** + * Process the version tag. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param array $tags The tokens for these tags. + * + * @return void + */ + protected function processVersion(PHP_CodeSniffer_File $phpcsFile, array $tags) + { + $tokens = $phpcsFile->getTokens(); + foreach ($tags as $tag) { + if ($tokens[($tag + 2)]['code'] !== T_DOC_COMMENT_STRING) { + // No content. + continue; + } + + $content = $tokens[($tag + 2)]['content']; + if (strstr($content, 'CVS:') === false + && strstr($content, 'SVN:') === false + && strstr($content, 'GIT:') === false + && strstr($content, 'HG:') === false + ) { + $error = 'Invalid version "%s" in file comment; consider "CVS: " or "SVN: " or "GIT: " or "HG: " instead'; + $data = array($content); + $phpcsFile->addWarning($error, $tag, 'InvalidVersion', $data); + } + } + + }//end processVersion() + + +}//end class diff --git a/Standards/Laravel/Sniffs/Commenting/FunctionCommentSniff.php b/Standards/Laravel/Sniffs/Commenting/FunctionCommentSniff.php new file mode 100644 index 0000000..9e7ca77 --- /dev/null +++ b/Standards/Laravel/Sniffs/Commenting/FunctionCommentSniff.php @@ -0,0 +1,378 @@ + + * @author Marc McIntyre + * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + * @link http://pear.php.net/package/PHP_CodeSniffer + */ + +/** + * Parses and verifies the doc comments for functions. + * + * @category PHP + * @package PHP_CodeSniffer + * @author Greg Sherwood + * @author Marc McIntyre + * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + * @version Release: @package_version@ + * @link http://pear.php.net/package/PHP_CodeSniffer + */ +class Laravel_Sniffs_Commenting_FunctionCommentSniff implements PHP_CodeSniffer_Sniff +{ + + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() + { + return array(T_FUNCTION); + + }//end register() + + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $find = PHP_CodeSniffer_Tokens::$methodPrefixes; + $find[] = T_WHITESPACE; + + $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true); + if ($tokens[$commentEnd]['code'] === T_COMMENT) { + // Inline comments might just be closing comments for + // control structures or functions instead of function comments + // using the wrong comment type. If there is other code on the line, + // assume they relate to that code. + $prev = $phpcsFile->findPrevious($find, ($commentEnd - 1), null, true); + if ($prev !== false && $tokens[$prev]['line'] === $tokens[$commentEnd]['line']) { + $commentEnd = $prev; + } + } + + if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG + && $tokens[$commentEnd]['code'] !== T_COMMENT + ) { + $phpcsFile->addError('Missing function doc comment', $stackPtr, 'Missing'); + $phpcsFile->recordMetric($stackPtr, 'Function has doc comment', 'no'); + return; + } else { + $phpcsFile->recordMetric($stackPtr, 'Function has doc comment', 'yes'); + } + + if ($tokens[$commentEnd]['code'] === T_COMMENT) { + $phpcsFile->addError('You must use "/**" style comments for a function comment', $stackPtr, 'WrongStyle'); + return; + } + + if ($tokens[$commentEnd]['line'] !== ($tokens[$stackPtr]['line'] - 1)) { + $error = 'There must be no blank lines after the function comment'; + $phpcsFile->addError($error, $commentEnd, 'SpacingAfter'); + } + + $commentStart = $tokens[$commentEnd]['comment_opener']; + foreach ($tokens[$commentStart]['comment_tags'] as $tag) { + if ($tokens[$tag]['content'] === '@see') { + // Make sure the tag isn't empty. + $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd); + if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) { + $error = 'Content missing for @see tag in function comment'; + $phpcsFile->addError($error, $tag, 'EmptySees'); + } + } + } + + $this->processReturn($phpcsFile, $stackPtr, $commentStart); + $this->processThrows($phpcsFile, $stackPtr, $commentStart); + $this->processParams($phpcsFile, $stackPtr, $commentStart); + + }//end process() + + + /** + * Process the return comment of this function comment. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $commentStart The position in the stack where the comment started. + * + * @return void + */ + protected function processReturn(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $commentStart) + { + $tokens = $phpcsFile->getTokens(); + + // Skip constructor and destructor. + $methodName = $phpcsFile->getDeclarationName($stackPtr); + $isSpecialMethod = ($methodName === '__construct' || $methodName === '__destruct'); + + $return = null; + foreach ($tokens[$commentStart]['comment_tags'] as $tag) { + if ($tokens[$tag]['content'] === '@return') { + if ($return !== null) { + $error = 'Only 1 @return tag is allowed in a function comment'; + $phpcsFile->addError($error, $tag, 'DuplicateReturn'); + return; + } + + $return = $tag; + } + } + + if ($isSpecialMethod === true) { + return; + } + + if ($return !== null) { + $content = $tokens[($return + 2)]['content']; + if (empty($content) === true || $tokens[($return + 2)]['code'] !== T_DOC_COMMENT_STRING) { + $error = 'Return type missing for @return tag in function comment'; + $phpcsFile->addError($error, $return, 'MissingReturnType'); + } + } else { + $error = 'Missing @return tag in function comment'; + $phpcsFile->addError($error, $tokens[$commentStart]['comment_closer'], 'MissingReturn'); + }//end if + + }//end processReturn() + + + /** + * Process any throw tags that this function comment has. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $commentStart The position in the stack where the comment started. + * + * @return void + */ + protected function processThrows(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $commentStart) + { + $tokens = $phpcsFile->getTokens(); + + $throws = array(); + foreach ($tokens[$commentStart]['comment_tags'] as $tag) { + if ($tokens[$tag]['content'] !== '@throws') { + continue; + } + + $exception = null; + $comment = null; + if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) { + $matches = array(); + preg_match('/([^\s]+)(?:\s+(.*))?/', $tokens[($tag + 2)]['content'], $matches); + $exception = $matches[1]; + if (isset($matches[2]) === true) { + $comment = $matches[2]; + } + } + + if ($exception === null) { + $error = 'Exception type missing for @throws tag in function comment'; + $phpcsFile->addError($error, $tag, 'InvalidThrows'); + } + }//end foreach + + }//end processThrows() + + + /** + * Process the function parameter comments. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $commentStart The position in the stack where the comment started. + * + * @return void + */ + protected function processParams(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $commentStart) + { + $tokens = $phpcsFile->getTokens(); + + $params = array(); + $maxType = 0; + $maxVar = 0; + foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) { + if ($tokens[$tag]['content'] !== '@param') { + continue; + } + + $type = ''; + $typeSpace = 0; + $var = ''; + $varSpace = 0; + $comment = ''; + if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) { + $matches = array(); + preg_match('/([^$&]+)(?:((?:\$|&)[^\s]+)(?:(\s+)(.*))?)?/', $tokens[($tag + 2)]['content'], $matches); + + $typeLen = strlen($matches[1]); + $type = trim($matches[1]); + $typeSpace = ($typeLen - strlen($type)); + $typeLen = strlen($type); + if ($typeLen > $maxType) { + $maxType = $typeLen; + } + + if (isset($matches[2]) === true) { + $var = $matches[2]; + $varLen = strlen($var); + if ($varLen > $maxVar) { + $maxVar = $varLen; + } + + if (isset($matches[4]) === true) { + $varSpace = strlen($matches[3]); + $comment = $matches[4]; + + // Any strings until the next tag belong to this comment. + if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) { + $end = $tokens[$commentStart]['comment_tags'][($pos + 1)]; + } else { + $end = $tokens[$commentStart]['comment_closer']; + } + + for ($i = ($tag + 3); $i < $end; $i++) { + if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) { + $comment .= ' '.$tokens[$i]['content']; + } + } + } + } else { + $error = 'Missing parameter name'; + $phpcsFile->addError($error, $tag, 'MissingParamName'); + }//end if + } else { + $error = 'Missing parameter type'; + $phpcsFile->addError($error, $tag, 'MissingParamType'); + }//end if + + $params[] = array( + 'tag' => $tag, + 'type' => $type, + 'var' => $var, + 'comment' => $comment, + 'type_space' => $typeSpace, + 'var_space' => $varSpace, + ); + }//end foreach + + $realParams = $phpcsFile->getMethodParameters($stackPtr); + $foundParams = array(); + + foreach ($params as $pos => $param) { + if ($param['var'] === '') { + continue; + } + + $foundParams[] = $param['var']; + + // Check number of spaces after the type. + $spaces = ($maxType - strlen($param['type']) + 1); + if ($param['type_space'] !== $spaces) { + $error = 'Expected %s spaces after parameter type; %s found'; + $data = array( + $spaces, + $param['type_space'], + ); + + $fix = $phpcsFile->addFixableError($error, $param['tag'], 'SpacingAfterParamType', $data); + if ($fix === true) { + $content = $param['type']; + $content .= str_repeat(' ', $spaces); + $content .= $param['var']; + $content .= str_repeat(' ', $param['var_space']); + $content .= $param['comment']; + $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content); + } + } + + // Make sure the param name is correct. + if (isset($realParams[$pos]) === true) { + $realName = $realParams[$pos]['name']; + if ($realName !== $param['var']) { + $code = 'ParamNameNoMatch'; + $data = array( + $param['var'], + $realName, + ); + + $error = 'Doc comment for parameter %s does not match '; + if (strtolower($param['var']) === strtolower($realName)) { + $error .= 'case of '; + $code = 'ParamNameNoCaseMatch'; + } + + $error .= 'actual variable name %s'; + + $phpcsFile->addError($error, $param['tag'], $code, $data); + } + } else if (substr($param['var'], -4) !== ',...') { + // We must have an extra parameter comment. + $error = 'Superfluous parameter comment'; + $phpcsFile->addError($error, $param['tag'], 'ExtraParamComment'); + }//end if + + if ($param['comment'] === '') { + continue; + } + + // Check number of spaces after the var name. + $spaces = ($maxVar - strlen($param['var']) + 1); + if ($param['var_space'] !== $spaces) { + $error = 'Expected %s spaces after parameter name; %s found'; + $data = array( + $spaces, + $param['var_space'], + ); + + $fix = $phpcsFile->addFixableError($error, $param['tag'], 'SpacingAfterParamName', $data); + if ($fix === true) { + $content = $param['type']; + $content .= str_repeat(' ', $param['type_space']); + $content .= $param['var']; + $content .= str_repeat(' ', $spaces); + $content .= $param['comment']; + $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content); + } + } + }//end foreach + + $realNames = array(); + foreach ($realParams as $realParam) { + $realNames[] = $realParam['name']; + } + + // Report missing comments. + $diff = array_diff($realNames, $foundParams); + foreach ($diff as $neededParam) { + $error = 'Doc comment for parameter "%s" missing'; + $data = array($neededParam); + $phpcsFile->addError($error, $commentStart, 'MissingParamTag', $data); + } + + }//end processParams() + + +}//end class diff --git a/Standards/Laravel/ruleset.xml b/Standards/Laravel/ruleset.xml index d498990..3a96fde 100644 --- a/Standards/Laravel/ruleset.xml +++ b/Standards/Laravel/ruleset.xml @@ -160,7 +160,7 @@ - + diff --git a/Standards/TestFiles/Filesystem.php b/Standards/TestFiles/Filesystem.php index 351497f..6faaa8e 100644 --- a/Standards/TestFiles/Filesystem.php +++ b/Standards/TestFiles/Filesystem.php @@ -8,7 +8,7 @@ class Filesystem { /** * Determine if a file exists. * - * @param string $path + * @param string $path * @return bool */ public function exists($path) @@ -19,17 +19,14 @@ public function exists($path) /** * Get the contents of a file. * - * @param string $path + * @param string $path * @return string * * @throws FileNotFoundException */ public function get($path) { - if ($this->isFile($path)) - { - return file_get_contents($path); - } + if ($this->isFile($path)) return file_get_contents($path); throw new FileNotFoundException("File does not exist at path {$path}"); } @@ -37,17 +34,14 @@ public function get($path) /** * Get the returned value of a file. * - * @param string $path + * @param string $path * @return mixed * * @throws FileNotFoundException */ public function getRequire($path) { - if ($this->isFile($path)) - { - return require $path; - } + if ($this->isFile($path)) return require $path; throw new FileNotFoundException("File does not exist at path {$path}"); } @@ -55,7 +49,7 @@ public function getRequire($path) /** * Require the given file once. * - * @param string $file + * @param string $file * @return mixed */ public function requireOnce($file) @@ -66,8 +60,8 @@ public function requireOnce($file) /** * Write the contents of a file. * - * @param string $path - * @param string $contents + * @param string $path + * @param string $contents * @return int */ public function put($path, $contents) @@ -78,8 +72,8 @@ public function put($path, $contents) /** * Prepend to a file. * - * @param string $path - * @param string $data + * @param string $path + * @param string $data * @return int */ public function prepend($path, $data) @@ -95,8 +89,8 @@ public function prepend($path, $data) /** * Append to a file. * - * @param string $path - * @param string $data + * @param string $path + * @param string $data * @return int */ public function append($path, $data) @@ -107,7 +101,7 @@ public function append($path, $data) /** * Delete the file at a given path. * - * @param string|array $paths + * @param string|array $paths * @return bool */ public function delete($paths) @@ -130,8 +124,8 @@ public function delete($paths) /** * Move a file to a new location. * - * @param string $path - * @param string $target + * @param string $path + * @param string $target * @return bool */ public function move($path, $target) @@ -142,8 +136,8 @@ public function move($path, $target) /** * Copy a file to a new location. * - * @param string $path - * @param string $target + * @param string $path + * @param string $target * @return bool */ public function copy($path, $target) @@ -154,7 +148,7 @@ public function copy($path, $target) /** * Extract the file extension from a file path. * - * @param string $path + * @param string $path * @return string */ public function extension($path) @@ -165,7 +159,7 @@ public function extension($path) /** * Get the file type of a given file. * - * @param string $path + * @param string $path * @return string */ public function type($path) @@ -176,7 +170,7 @@ public function type($path) /** * Get the file size of a given file. * - * @param string $path + * @param string $path * @return int */ public function size($path) @@ -187,7 +181,7 @@ public function size($path) /** * Get the file's last modification time. * - * @param string $path + * @param string $path * @return int */ public function lastModified($path) @@ -198,7 +192,7 @@ public function lastModified($path) /** * Determine if the given path is a directory. * - * @param string $directory + * @param string $directory * @return bool */ public function isDirectory($directory) @@ -209,7 +203,7 @@ public function isDirectory($directory) /** * Determine if the given path is writable. * - * @param string $path + * @param string $path * @return bool */ public function isWritable($path) @@ -220,7 +214,7 @@ public function isWritable($path) /** * Determine if the given path is a file. * - * @param string $file + * @param string $file * @return bool */ public function isFile($file) @@ -231,8 +225,8 @@ public function isFile($file) /** * Find path names matching a given pattern. * - * @param string $pattern - * @param int $flags + * @param string $pattern + * @param int $flags * @return array */ public function glob($pattern, $flags = 0) @@ -243,7 +237,7 @@ public function glob($pattern, $flags = 0) /** * Get an array of all files in a directory. * - * @param string $directory + * @param string $directory * @return array */ public function files($directory) @@ -258,7 +252,7 @@ public function files($directory) // To get the appropriate files, we'll simply glob the directory and filter // out any "files" that are not truly files so we do not end up with any // directories in our list, but only true files within the directory. - return array_filter($glob, function($file) { + return array_filter($glob, function ($file) { return filetype($file) == 'file'; }); } @@ -266,7 +260,7 @@ public function files($directory) /** * Get all of the files from the given directory (recursive). * - * @param string $directory + * @param string $directory * @return array */ public function allFiles($directory) @@ -277,7 +271,7 @@ public function allFiles($directory) /** * Get all of the directories within a given directory. * - * @param string $directory + * @param string $directory * @return array */ public function directories($directory) @@ -295,10 +289,10 @@ public function directories($directory) /** * Create a directory. * - * @param string $path - * @param int $mode - * @param bool $recursive - * @param bool $force + * @param string $path + * @param int $mode + * @param bool $recursive + * @param bool $force * @return bool */ public function makeDirectory($path, $mode = 0755, $recursive = false, $force = false) @@ -314,9 +308,9 @@ public function makeDirectory($path, $mode = 0755, $recursive = false, $force = /** * Copy a directory from one location to another. * - * @param string $directory - * @param string $destination - * @param int $options + * @param string $directory + * @param string $destination + * @param int $options * @return bool */ public function copyDirectory($directory, $destination, $options = null) @@ -375,8 +369,8 @@ public function copyDirectory($directory, $destination, $options = null) * * The directory itself may be optionally preserved. * - * @param string $directory - * @param bool $preserve + * @param string $directory + * @param bool $preserve * @return bool */ public function deleteDirectory($directory, $preserve = false) @@ -418,7 +412,7 @@ public function deleteDirectory($directory, $preserve = false) /** * Empty the specified directory of all files and folders. * - * @param string $directory + * @param string $directory * @return bool */ public function cleanDirectory($directory) diff --git a/composer.json b/composer.json index 9c7e418..3839301 100644 --- a/composer.json +++ b/composer.json @@ -1,12 +1,12 @@ { - "name": "pragmarx/laravelcs", + "name": "lukzgois/laravelcs", "description": "PHP_CodeSniffer custom Sniff for Laravel coding standard", "keywords": ["laravel", "code", "sniffer", "standards", "PHP_CodeSniffer"], "license": "BSD-3-Clause", "authors": [ { - "name": "Antonio Carlos Ribeiro", - "email": "acr@antoniocarlosribeiro.com", + "name": "Lucas Padilha Gois", + "email": "lucaspgois@gmail.com", "role": "Creator" } ], diff --git a/readme.md b/readme.md index b133032..6e42c08 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,10 @@ # Laravel PHP_CodeSniffer -### This is a custom Sniff to detect violations and reformat PHP source code based on [Laravel Coding Standard](http://laravel.com/docs/4.2/contributions#coding-style). +### This is a custom Sniff to detect violations and reformat PHP source code based on [Laravel Coding Standard](http://laravel.com/docs/5.0/contributions#coding-style). + +### This is a fork from the Antonio Ribeiro's Project, which can be found [here](https://github.com/antonioribeiro/laravelcs). + +It checks the same thing to the Antonio's original project, in additional this sniffer **allows inline control structures** +and **check the functions have docblocks**. Your source code will be checked for PSR-1 & the Laravel "flavor" of PSR-2. @@ -7,7 +12,7 @@ Your source code will be checked for PSR-1 & the Laravel "flavor" of PSR-2. Clone this repository - git clone http://github.com/antonioribeiro/laravelcs LaravelCodeSniffer + git clone http://github.com/lukzgois/laravelcs LaravelCodeSniffer Execute CodeSniffer @@ -17,40 +22,13 @@ Execute CodeSniffer The recommended way is to install it globally with the following command: - composer global require pragmarx/laravelcs + composer global require lukzgois/laravelcs Make sure you have `~/.composer/vendor/bin/` in your PATH. You will then be able to run PHP Code Sniffer with the Laravel Standard: - phpcs --standard=$HOME/.composer/vendor/pragmarx/laravelcs/Standards/Laravel/ /path/to/your/project/files - -###Testing the Sniff - -This Sniff was tested using the Laravel Framework source code and some changes, to comply with PSR-2, were required, [click here to see them](http://github.com/antonioribeiro/laravelcs/compare/096884846fa385e54a7e4eeb43547a9137fdf047...d78508f9e5633bc0f776f730dcc6f1e0a9c8daec). Those were the violations found and fixed in Filesystem.php: - -``` - -------------------------------------------------------------------------------- - FOUND 14 ERRORS AFFECTING 11 LINES - -------------------------------------------------------------------------------- - 29 | ERROR | [x] Inline control structures are not allowed - 44 | ERROR | [x] Inline control structures are not allowed - 113 | ERROR | [x] Expected 1 new line after closing parenthesis; found " " - 113 | ERROR | [x] Expected 1 newline after opening brace; 0 found - 113 | ERROR | [x] Inline control structures are not allowed - 113 | ERROR | [x] Closing brace must be on a line by itself - 241 | ERROR | [x] Inline control structures are not allowed - 247 | ERROR | [ ] Opening brace should be on the same line as the declaration - 249 | ERROR | [x] Closing brace indented incorrectly; expected 2 spaces, found 3 - 310 | ERROR | [x] Inline control structures are not allowed - 335 | ERROR | [x] Inline control structures are not allowed - 343 | ERROR | [x] Inline control structures are not allowed - 361 | ERROR | [x] Inline control structures are not allowed - 384 | ERROR | [x] Inline control structures are not allowed - -------------------------------------------------------------------------------- - PHPCBF CAN FIX THE 13 MARKED SNIFF VIOLATIONS AUTOMATICALLY - -------------------------------------------------------------------------------- -``` + phpcs --standard=$HOME/.composer/vendor/lukzgois/laravelcs/Standards/Laravel/ /path/to/your/project/files ###Contributing