1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | <?php namespace EasyBib\Sniffs\Commenting; class ReturnStatementsSniff implements \PHP_CodeSniffer_Sniff { /** * @var \PHP_CodeSniffer_File */ protected $phpcsFile; /** * @var array */ protected $tokens; public function register() { return array(T_FUNCTION); } public function process(\PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $this->phpcsFile = $phpcsFile; $methodName = $this->phpcsFile->getDeclarationName($stackPtr); if (null === $methodName) { return; } $this->tokens = $phpcsFile->getTokens(); // enforce no `@return void` on __construct if ('__construct' === $methodName) { $this->findReturnVoid($stackPtr, true); return; } } /** * @param int $stackPtr Position in the stack. * @param bool $ctor Is this a __construct. * * @return void */ protected function findReturnVoid($stackPtr, $ctor = false) { $toFind = array(T_DOC_COMMENT,); $commentEnd = $this->phpcsFile->findPrevious(T_DOC_COMMENT, ($stackPtr-1)); if (false === $commentEnd) { return; // no docblock, different concern } $look = $commentEnd-1; while (true && ($look > 0)) { $docComment = $this->phpcsFile->findPrevious(T_DOC_COMMENT, ($look)); if (false === $docComment) { return; } $docRow = $this->tokens[$docComment]; $content = trim($docRow['content']); if (strstr($content, '@return void')) { if (true === $ctor) { $this->phpcsFile->addError('`@return void` for __construct is invalid.', $docComment, 'ConstructNoReturnVoid'); } else { $this->phpcsFile->addWarning('Consider adding a fluent interface instead of `@return void`', $docComment, 'AddSetReturnsVoid'); } return; } if ('/**' == $content) { return; } --$look; } return; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | <?php /** * EasyBib\Sniffs\Methods\AddSetShouldBeFluentSniff * * PHP version 5.3 * * @category QA * @package EasyBib\Sniffs * @author Till Klampaeckel <till@php.net> * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version GIT: <git_id> * @link http:// */ namespace EasyBib\Sniffs\Methods; /** * EasyBib\Sniffs\Methods\AddSetShouldBeFluentSniff * * Ensures that there are no `return` statements in a `__construct` and that the docblock * says `return $this` - if anything. * * @category QA * @package EasyBib\Sniffs * @author Till Klampaeckel <till@php.net> * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: @package_version@ * @link http:// */ class AddSetShouldBeFluentSniff implements \PHP_CodeSniffer_Sniff { public function register() { return array(T_FUNCTION); } /** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * * @return void */ public function process(\PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $methodName = $phpcsFile->getDeclarationName($stackPtr); if (null === $methodName) { return; } // check fluent interface for `set*()` and `add*()`. $methodType = substr($methodName, 0, 3); if (false === in_array($methodType, array('set', 'add'))) { return; } // for `fooAction` methods in controllers if (substr($methodName, -6) == 'Action') { return; } if (strtolower(substr($methodName, 0, 5)) === 'setup') { return; } $error = sprintf('`%s` should implement a fluent interface', $methodName); $t_return = $phpcsFile->findNext(T_RETURN, $stackPtr); if (false === $t_return) { $phpcsFile->addError( $error, $stackPtr, 'AddSetNoFluentInterface' ); return; } $tokens = $phpcsFile->getTokens(); if ($tokens[($t_return+1)]['type'] !== 'T_WHITESPACE') { $phpcsFile->addError( $error, ($t_return+1), 'AddSetNoFluentInterface' ); return; } if ($tokens[($t_return+2)]['content'] !== '$this') { $phpcsFile->addError( $error, ($t_return+2), 'AddSetNoFluentInterface' ); return; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | <?php /** * EasyBib\Sniffs\Methods\ConstructShouldNotReturnSniff * * PHP version 5.3 * * @category QA * @package EasyBib\Sniffs * @author Till Klampaeckel <till@php.net> * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version GIT: <git_id> * @link http:// */ namespace EasyBib\Sniffs\Methods; /** * EasyBib\Sniffs\Methods\ConstructShouldNotReturnSniff * * Ensures that there are no `return` statements in a `__construct` and that the docblock * says `return $this` - if anything. * * @category QA * @package EasyBib\Sniffs * @author Till Klampaeckel <till@php.net> * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: @package_version@ * @link http:// */ class ConstructShouldNotReturnSniff extends \PHP_CodeSniffer_Standards_AbstractScopeSniff { public function __construct() { parent::__construct(array(T_FUNCTION), array(T_RETURN)); } /** * Processes the tokens that this sniff is interested in. * * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param int $stackPtr The position in the stack where * the token was found. * @param int $currScope The current scope opener token. * * @return void */ protected function processTokenWithinScope(\PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope) { $methodName = $phpcsFile->getDeclarationName($currScope); if (null === $methodName) { return; } if ('__construct' !== $methodName) { return; } $tokens = $phpcsFile->getTokens(); if (!isset($tokens[$stackPtr])) { return; } if ('T_RETURN' === $tokens[$stackPtr]['type'] && 'T_SEMICOLON' !== $tokens[$stackPtr+1]['type']) { $phpcsFile->addWarning('__construct cannot return anything', $stackPtr, 'NoReturnInConstruct'); return; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | <?xml version="1.0"?> <ruleset name="EasyBib"> <description>EasyBib Coding Standard.</description> <exclude-pattern>*tests/*</exclude-pattern> <exclude-pattern>*data/*</exclude-pattern> <exclude-pattern>*var/*</exclude-pattern> <exclude-pattern>*bin/*</exclude-pattern> <exclude-pattern>*vendor/*</exclude-pattern> <exclude-pattern>*www/*</exclude-pattern> <exclude-pattern>*views/scripts/*</exclude-pattern> <exclude-pattern>*etc/*</exclude-pattern> <exclude-pattern>*examples/*</exclude-pattern> <exclude-pattern>*docs/*</exclude-pattern> <rule ref="PEAR"/> <rule ref="PSR1"/> <rule ref="PSR2"/> <rule ref="Generic.Commenting.Todo.CommentFound"> <message>Please review this TODO comment: %s</message> <severity>3</severity> </rule> <rule ref="Generic.NamingConventions.UpperCaseConstantName"/> <rule ref="Generic.NamingConventions.CamelCapsFunctionName"> <properties> <property name="strict" value="false"/> </properties> </rule> <rule ref="Generic.WhiteSpace.ScopeIndent"> <exclude name="Generic.WhiteSpace.ScopeIndent.Incorrect"/> </rule> <rule ref="PEAR.NamingConventions.ValidFunctionName"> <exclude name="PEAR.NamingConventions.ValidFunctionName.PrivateNoUnderscore"/> <exclude name="PEAR.NamingConventions.ValidFunctionName.FunctionUnderscore"/> </rule> <rule ref="PEAR.NamingConventions.ValidVariableName"> <exclude name="PEAR.NamingConventions.ValidVariableName.PrivateNoUnderscore"/> </rule> <!-- turn this off for ZF1 controllers --> <rule ref="PSR1.Classes.ClassDeclaration"> <exclude name="PSR1.Classes.ClassDeclaration.MissingNamespace"/> </rule> <rule ref="PSR2.Files.EndFileNewline"> <exclude name="PSR2.Files.EndFileNewline.NotFound"/> </rule> <rule ref="PSR2.ControlStructures.SwitchDeclaration"> <exclude name="PSR2.ControlStructures.SwitchDeclaration.caseIndent"/> </rule> <!-- turn this off because of ZF1 controllers --> <rule ref="Squiz.Classes.ValidClassName"> <exclude name="Squiz.Classes.ValidClassName.NotCamelCaps"/> </rule> <rule ref="EasyBib.Methods.ConstructShouldNotReturn"/> <rule ref="EasyBib.Methods.AddSetShouldBeFluent"/> <rule ref="EasyBib.Commenting.ReturnStatements"/> </ruleset> |
pear install PHP_CodeSniffer
This is how:
phpcs \
--standard=/path/to/repo/EasyBib/ruleset.xml \
target-dir
.travis.yml
on various projects1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | { "type": "library", "license": "BSD", "repositories": [ { "type": "pear", "url": "http://pear.php.net" } ], "autoload": { "psr-0": { "EasyBib\\PHP\\CodeSniffer": "library", "PHP_CodeSniffer": "vendor/pear-pear.php.net/PHP_CodeSniffer" } }, "require-dev": { "pear-pear/php_codesniffer": "*" } } |
1 2 3 4 5 6 | <?php require './vendor/autoload.php'; $cs = new \EasyBib\PHP\CodeSniffer\CodingStandard; $cs->getIncludedSniffs(); |