Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11 from cakephp/new-rules
New rules
- Loading branch information
Showing
20 changed files
with
673 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,271 @@ | ||
<?php | ||
/** | ||
* CakePHP_Sniffs_Commenting_FunctionCommentThrowTagSniff | ||
* | ||
* PHP version 5 | ||
* | ||
* @category PHP | ||
* @package PHP_CodeSniffer_CakePHP | ||
* @author Juan Basso <jrbasso@gmail.com> | ||
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
* @version 1.0 | ||
* @link http://pear.php.net/package/PHP_CodeSniffer_CakePHP | ||
*/ | ||
|
||
if (class_exists('PHP_CodeSniffer_Standards_AbstractScopeSniff', true) === false) { | ||
$error = 'Class PHP_CodeSniffer_Standards_AbstractScopeSniff not found'; | ||
throw new PHP_CodeSniffer_Exception($error); | ||
} | ||
|
||
/** | ||
* CakePHP_Sniffs_Commenting_FunctionCommentThrowTagSniff. | ||
* | ||
* Ensures the throws in the code are declared in the PHPDoc | ||
* | ||
* @category PHP | ||
* @package PHP_CodeSniffer_CakePHP | ||
* @author Juan Basso <jrbasso@gmail.com> | ||
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
* @version 1.0 | ||
* @link http://pear.php.net/package/PHP_CodeSniffer_CakePHP | ||
*/ | ||
class CakePHP_Sniffs_Commenting_FunctionCommentThrowTagSniff extends PHP_CodeSniffer_Standards_AbstractScopeSniff { | ||
|
||
/** | ||
* Constructs a CakePHP_Sniffs_Commenting_FunctionCommentThrowTagSniff. | ||
*/ | ||
public function __construct() { | ||
parent::__construct(array(T_FUNCTION), array(T_THROW)); | ||
} | ||
|
||
/** | ||
* 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 | ||
*/ | ||
protected function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope) { | ||
// Is this the first throw token within the current function scope? | ||
// If so, we have to validate other throw tokens within the same scope. | ||
$previousThrow = $phpcsFile->findPrevious(T_THROW, ($stackPtr - 1), $currScope); | ||
if ($previousThrow !== false) { | ||
return; | ||
} | ||
|
||
// Parse the function comment. | ||
$tokens = $phpcsFile->getTokens(); | ||
$commentEnd = $phpcsFile->findPrevious(T_DOC_COMMENT, ($currScope - 1)); | ||
$commentStart = ($phpcsFile->findPrevious(T_DOC_COMMENT, ($commentEnd - 1), null, true) + 1); | ||
$comment = $phpcsFile->getTokensAsString($commentStart, ($commentEnd - $commentStart + 1)); | ||
|
||
try { | ||
$this->commentParser = new PHP_CodeSniffer_CommentParser_FunctionCommentParser($comment, $phpcsFile); | ||
$this->commentParser->parse(); | ||
} catch (PHP_CodeSniffer_CommentParser_ParserException $e) { | ||
$line = ($e->getLineWithinComment() + $commentStart); | ||
$phpcsFile->addError($e->getMessage(), $line, 'FailedParse'); | ||
return; | ||
} | ||
|
||
// Find the position where the current function scope ends. | ||
$currScopeEnd = 0; | ||
if (isset($tokens[$currScope]['scope_closer']) === true) { | ||
$currScopeEnd = $tokens[$currScope]['scope_closer']; | ||
} | ||
|
||
// Find all the exception type token within the current scope. | ||
$throwTokens = array(); | ||
$currPos = $stackPtr; | ||
if ($currScopeEnd !== 0) { | ||
while ($currPos < $currScopeEnd && $currPos !== false) { | ||
/* | ||
If we can't find a NEW, we are probably throwing | ||
a variable, so we ignore it, but they still need to | ||
provide at least one @throws tag, even through we | ||
don't know the exception class. | ||
*/ | ||
|
||
$nextToken = $phpcsFile->findNext(T_WHITESPACE, ($currPos + 1), null, true); | ||
if ($tokens[$nextToken]['code'] === T_NEW) { | ||
$currException = $phpcsFile->findNext(array(T_STRING, T_NS_SEPARATOR), $currPos, $currScopeEnd, false, null, true); | ||
if ($currException !== false) { | ||
$exception = $tokens[$currException]['content']; | ||
$i = $currException + 1; | ||
while (in_array($tokens[$i]['code'], array(T_STRING, T_NS_SEPARATOR))) { | ||
$exception .= $tokens[$i++]['content']; | ||
} | ||
$throwTokens[] = $exception; | ||
} | ||
} | ||
|
||
$currPos = $phpcsFile->findNext(T_THROW, ($currPos + 1), $currScopeEnd); | ||
} | ||
} | ||
|
||
$namespace = $this->_getNamespace($phpcsFile, $currScope); | ||
$uses = $this->_readUses($phpcsFile); | ||
$throwTokens = $this->_adjustThrows($throwTokens, $namespace, $uses); | ||
|
||
$throws = $this->commentParser->getThrows(); | ||
if (empty($throws) === true) { | ||
$error = 'Missing @throws tag in function comment'; | ||
$phpcsFile->addError($error, $commentEnd, 'Missing'); | ||
} else if (empty($throwTokens) === true) { | ||
// If token count is zero, it means that only variables are being | ||
// thrown, so we need at least one @throws tag (checked above). | ||
// Nothing more to do. | ||
return; | ||
} else { | ||
$throwTags = array(); | ||
$lineNumber = array(); | ||
foreach ($throws as $throw) { | ||
$value = ltrim($throw->getValue(), '\\'); | ||
$throwTags[] = $value; | ||
$lineNumber[$value] = $throw->getLine(); | ||
} | ||
|
||
$throwTags = array_unique($throwTags); | ||
sort($throwTags); | ||
|
||
// Make sure @throws tag count matches throw token count. | ||
$tokenCount = count($throwTokens); | ||
$tagCount = count($throwTags); | ||
if ($tokenCount !== $tagCount) { | ||
$error = 'Expected %s @throws tag(s) in function comment; %s found'; | ||
$data = array( | ||
$tokenCount, | ||
$tagCount, | ||
); | ||
$phpcsFile->addError($error, $commentEnd, 'WrongNumber', $data); | ||
return; | ||
} else { | ||
// Exception type in @throws tag must be thrown in the function. | ||
foreach ($throwTags as $i => $throwTag) { | ||
$errorPos = ($commentStart + $lineNumber[$throwTag]); | ||
if (empty($throwTag) === false && $throwTag !== $throwTokens[$i]) { | ||
$error = 'Expected "%s" but found "%s" for @throws tag exception'; | ||
$data = array( | ||
$throwTokens[$i], | ||
$throwTag, | ||
); | ||
$phpcsFile->addError($error, $errorPos, 'WrongType', $data); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Find the class namespace. | ||
* | ||
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned. | ||
* @param int $currScope Current scope | ||
* @return string | ||
*/ | ||
protected function _getNamespace(PHP_CodeSniffer_File $phpcsFile, $currScope) { | ||
$nsPos = $phpcsFile->findPrevious(T_NAMESPACE, $currScope - 1); | ||
if (!$nsPos) { | ||
return ''; | ||
} | ||
|
||
$tokens = $phpcsFile->getTokens(); | ||
$i = $nsPos + 2; // Ignore whitespace | ||
$ns = ''; | ||
while (in_array($tokens[$i]['code'], array(T_STRING, T_NS_SEPARATOR))) { | ||
$ns .= $tokens[$i]['content']; | ||
$i++; | ||
} | ||
return $ns; | ||
} | ||
|
||
/** | ||
* Read the use declarations | ||
* | ||
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned. | ||
* @return array | ||
*/ | ||
protected function _readUses(PHP_CodeSniffer_File $phpcsFile) { | ||
$pos = $phpcsFile->findNext(T_USE, 1); | ||
if (!$pos) { | ||
return array(); | ||
} | ||
|
||
$tokens = $phpcsFile->getTokens(); | ||
$pos += 2; // Ignore use keywork and whitespace | ||
$uses = array(); | ||
do { | ||
$use = $alias = ''; | ||
while (in_array($tokens[$pos]['code'], array(T_STRING, T_NS_SEPARATOR))) { | ||
$use .= $tokens[$pos]['content']; | ||
$pos++; | ||
} | ||
|
||
while (in_array($tokens[$pos]['code'], array(T_WHITESPACE, T_AS))) { | ||
$pos++; | ||
} | ||
if ($tokens[$pos]['code'] === T_STRING) { | ||
$alias = $tokens[$pos]['content']; | ||
$pos++; | ||
} | ||
|
||
if ($tokens[$pos]['code'] === T_COMMA) { | ||
$pos++; | ||
if ($tokens[$pos]['code'] === T_WHITESPACE) { | ||
$pos++; | ||
} | ||
} else { // End of uses | ||
$pos = $phpcsFile->findNext(T_USE, $pos); | ||
if ($pos) { | ||
$pos += 2; // Ignore use keywork and whitespace | ||
} | ||
} | ||
|
||
if (!$alias) { | ||
$alias = basename(str_replace('\\', '/', $use)); | ||
} | ||
|
||
$uses[$alias] = $use; | ||
} while ($pos); | ||
return $uses; | ||
} | ||
|
||
/** | ||
* Adjust the throw to use the namespace or aliases names | ||
* | ||
* @param array $throws | ||
* @param string $namespace | ||
* @param array $uses | ||
* @return array | ||
*/ | ||
protected function _adjustThrows($throws, $namespace, $uses) { | ||
$formatted = array(); | ||
foreach ($throws as $throw) { | ||
if ($throw[0] === '\\') { // Global | ||
$formatted[] = substr($throw, 1); | ||
continue; | ||
} | ||
|
||
$basename = $throw; | ||
$complement = ''; | ||
if (strpos($basename, '\\') !== false) { | ||
list($basename, $complement) = explode('\\', $basename, 2); | ||
} | ||
|
||
if (isset($uses[$basename])) { | ||
$formatted[] = trim($uses[$basename] . '\\' . $complement, '\\'); | ||
continue; | ||
} | ||
|
||
$formatted[] = trim($namespace . '\\' . $throw, '\\'); | ||
} | ||
|
||
// Only need one @throws tag for each type of exception thrown. | ||
$throws = array_unique($formatted); | ||
sort($throws); | ||
return $throws; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
<?php | ||
/** | ||
* CakePHP_Sniffs_Formatting_OneClassPerUseSniff | ||
* | ||
* PHP version 5 | ||
* | ||
* @category PHP | ||
* @package PHP_CodeSniffer_CakePHP | ||
* @author Juan Basso <jrbasso@gmail.com> | ||
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
* @version 1.0 | ||
* @link http://pear.php.net/package/PHP_CodeSniffer_CakePHP | ||
*/ | ||
|
||
/** | ||
* CakePHP_Sniffs_Formatting_OneClassPerUseSniff. | ||
* | ||
* Ensures the use contains only one class. | ||
* | ||
* @category PHP | ||
* @package PHP_CodeSniffer_CakePHP | ||
* @author Juan Basso <jrbasso@gmail.com> | ||
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
* @version 1.0 | ||
* @link http://pear.php.net/package/PHP_CodeSniffer_CakePHP | ||
*/ | ||
class CakePHP_Sniffs_Formatting_OneClassPerUseSniff implements PHP_CodeSniffer_Sniff { | ||
|
||
/** | ||
* Returns an array of tokens this test wants to listen for. | ||
* | ||
* @return array | ||
*/ | ||
public function register() { | ||
return array(T_USE); | ||
} | ||
|
||
/** | ||
* 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(); | ||
$i = 2; // Ignore use word and whitespace | ||
$filename = $phpcsFile->getFilename(); | ||
|
||
while (in_array($tokens[$stackPtr + $i]['code'], array(T_STRING, T_NS_SEPARATOR, T_WHITESPACE, T_AS))) { | ||
$i++; | ||
} | ||
|
||
if ($tokens[$stackPtr + $i]['code'] === T_COMMA) { | ||
$error = 'Only one class is allowed per use'; | ||
$phpcsFile->addError($error, $stackPtr, 'OneClassPerUse', array()); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.