This commit is contained in:
Paminger
2016-03-20 12:53:17 +01:00
parent 0c3c47f848
commit 9689fd5a01
137 changed files with 8480 additions and 405 deletions
@@ -0,0 +1,59 @@
<?php
/**
* FHComplete
*/
/**
* Ensures doc block alignments.
*/
class FHComplete_Sniffs_Commenting_DocBlockAlignmentSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_DOC_COMMENT_OPEN_TAG);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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();
$leftWall = array(
T_CLASS,
T_NAMESPACE,
T_INTERFACE,
T_TRAIT,
T_USE
);
$oneIndentation = array(
T_FUNCTION,
T_VARIABLE,
T_CONST
);
$allTokens = array_merge($leftWall, $oneIndentation);
$notFlatFile = $phpcsFile->findNext(T_NAMESPACE, 0);
$next = $phpcsFile->findNext($allTokens, $stackPtr + 1);
if ($next && $notFlatFile) {
$notWalled = (in_array($tokens[$next]['code'], $leftWall) && $tokens[$stackPtr]['column'] !== 1);
$notIndented = (in_array($tokens[$next]['code'], $oneIndentation) && $tokens[$stackPtr]['column'] !== 5);
if ($notWalled || $notIndented) {
$phpcsFile->addError('Expected docblock to be aligned with code.', $stackPtr, 'NotAllowed');
}
}
return;
}
}
@@ -0,0 +1,487 @@
<?php
/**
* Parses and verifies the doc comments for functions.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @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
*/
if (class_exists('PEAR_Sniffs_Commenting_FunctionCommentSniff', true) === false) {
throw new PHP_CodeSniffer_Exception('Class PEAR_Sniffs_Commenting_FunctionCommentSniff not found');
}
/**
* Parses and verifies the doc comments for functions.
*
* Verifies that :
* <ul>
* <li>A comment exists</li>
* <li>There is a blank newline after the short description</li>
* <li>There is a blank newline between the long and short description</li>
* <li>There is a blank newline between the long description and tags</li>
* <li>Parameter names represent those in the method</li>
* <li>Parameter comments are in the correct order</li>
* <li>Parameter comments are complete</li>
* <li>A type hint is provided for array and custom class</li>
* <li>Type hint matches the actual variable/class type</li>
* <li>A blank line is present before the first and after the last parameter</li>
* <li>A return type exists</li>
* <li>Any throw tag must have a comment</li>
* <li>The tag order and indentation are correct</li>
* </ul>
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @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 FHComplete_Sniffs_Commenting_FunctionCommentSniff extends PEAR_Sniffs_Commenting_FunctionCommentSniff
{
/**
* Is the comment an inheritdoc?
*
* @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 boolean True if the comment is an inheritdoc
*/
protected function isInheritDoc(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$start = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr - 1);
$end = $phpcsFile->findNext(T_DOC_COMMENT_CLOSE_TAG, $start);
$content = $phpcsFile->getTokensAsString($start, ($end - $start));
return preg_match('#{@inheritDoc}#', $content) === 1;
} // end isInheritDoc()
/**
* 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)
{
if ($this->isInheritDoc($phpcsFile, $stackPtr)) {
return;
}
$tokens = $phpcsFile->getTokens();
// Skip constructor and destructor.
$className = '';
foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condition) {
if ($condition === T_CLASS || $condition === T_INTERFACE) {
$className = $phpcsFile->getDeclarationName($condPtr);
$className = strtolower(ltrim($className, '_'));
}
}
$methodName = $phpcsFile->getDeclarationName($stackPtr);
$isSpecialMethod = ($methodName === '__construct' || $methodName === '__destruct');
if ($methodName !== '_') {
$methodName = strtolower(ltrim($methodName, '_'));
}
$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 {
// Check return type (can be multiple, separated by '|').
$typeNames = explode('|', $content);
$suggestedNames = array();
foreach ($typeNames as $i => $typeName) {
if ($typeName === 'integer') {
$suggestedName = 'int';
} elseif ($typeName === 'boolean') {
$suggestedName = 'bool';
} elseif (in_array($typeName, array('int', 'bool'))) {
$suggestedName = $typeName;
} else {
$suggestedName = PHP_CodeSniffer::suggestType($typeName);
}
if (in_array($suggestedName, $suggestedNames) === false) {
$suggestedNames[] = $suggestedName;
}
}
$suggestedType = implode('|', $suggestedNames);
if ($content !== $suggestedType) {
$error = 'Function return type "%s" is invalid';
$error = 'Expected "%s" but found "%s" for function return type';
$data = array(
$suggestedType,
$content,
);
$phpcsFile->addError($error, $return, 'InvalidReturn', $data);
}
// If the return type is void, make sure there is
// no return statement in the function.
if ($content === 'void') {
if (isset($tokens[$stackPtr]['scope_closer']) === true) {
$endToken = $tokens[$stackPtr]['scope_closer'];
for ($returnToken = $stackPtr; $returnToken < $endToken; $returnToken++) {
if ($tokens[$returnToken]['code'] === T_CLOSURE) {
$returnToken = $tokens[$returnToken]['scope_closer'];
continue;
}
if ($tokens[$returnToken]['code'] === T_RETURN) {
break;
}
}
if ($returnToken !== $endToken) {
// If the function is not returning anything, just
// exiting, then there is no problem.
$semicolon = $phpcsFile->findNext(T_WHITESPACE, ($returnToken + 1), null, true);
if ($tokens[$semicolon]['code'] !== T_SEMICOLON) {
$error = 'Function return type is void, but function contains return statement';
$phpcsFile->addWarning($error, $return, 'InvalidReturnVoid');
}
}
}//end if
} elseif (!preg_match('/^mixed/', $content)) {
// If return type is not void, there needs to be a return statement
// somewhere in the function that returns something.
if (isset($tokens[$stackPtr]['scope_closer']) === true) {
$endToken = $tokens[$stackPtr]['scope_closer'];
$returnToken = $phpcsFile->findNext(T_RETURN, $stackPtr, $endToken);
if ($returnToken === false) {
$error = 'Function return type is not void, but function has no return statement';
$phpcsFile->addWarning($error, $return, 'InvalidNoReturn');
} elseif (!preg_match('/void/', $content)) {
$semicolon = $phpcsFile->findNext(T_WHITESPACE, ($returnToken + 1), null, true);
if ($tokens[$semicolon]['code'] === T_SEMICOLON) {
$error = 'Function return type is not void, but function is returning void here';
$phpcsFile->addWarning($error, $returnToken, 'InvalidReturnNotVoid');
}
}
}
}//end if
}//end if
} else {
$error = 'Missing @return tag in function comment';
$phpcsFile->addWarning($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 $pos => $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 and comment missing for @throws tag in function comment';
$phpcsFile->addWarning($error, $tag, 'InvalidThrows');
} elseif ($comment === null) {
$error = 'Comment missing for @throws tag in function comment';
$phpcsFile->addWarning($error, $tag, 'EmptyThrows');
} else {
// 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'];
}
}
// Starts with a capital letter and ends with a fullstop.
$firstChar = $comment{0};
if (strtoupper($firstChar) !== $firstChar) {
$error = '@throws tag comment must start with a capital letter';
$phpcsFile->addWarning($error, ($tag + 2), 'ThrowsNotCapital');
}
$lastChar = substr($comment, -1);
if ($lastChar !== '.') {
$error = '@throws tag comment must end with a full stop';
$phpcsFile->addWarning($error, ($tag + 2), 'ThrowsNoFullStop');
}
}//end if
}//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)
{
if ($this->isInheritDoc($phpcsFile, $stackPtr)) {
return;
}
$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 = '';
$commentLines = array();
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];
$commentLines[] = array(
'comment' => $comment,
'token' => ($tag + 2),
'indent' => $varSpace,
);
// 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) {
$indent = 0;
if ($tokens[($i - 1)]['code'] === T_DOC_COMMENT_WHITESPACE) {
$indent = strlen($tokens[($i - 1)]['content']);
}
$comment .= ' '.$tokens[$i]['content'];
$commentLines[] = array(
'comment' => $tokens[$i]['content'],
'token' => $i,
'indent' => $indent,
);
}
}
} else {
$error = 'Missing parameter comment';
$phpcsFile->addError($error, $tag, 'MissingParamComment');
$commentLines[] = array('comment' => '');
}//end if
} 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,
'commentLines' => $commentLines,
'type_space' => $typeSpace,
'var_space' => $varSpace,
);
}//end foreach
$realParams = $phpcsFile->getMethodParameters($stackPtr);
$foundParams = array();
foreach ($params as $pos => $param) {
// If the type is empty, the whole line is empty.
if ($param['type'] === '') {
continue;
}
// Check the param type value.
$typeNames = explode('|', $param['type']);
foreach ($typeNames as $typeName) {
if ($typeName === 'integer') {
$suggestedName = 'int';
} elseif ($typeName === 'boolean') {
$suggestedName = 'bool';
} elseif (in_array($typeName, array('int', 'bool'))) {
$suggestedName = $typeName;
} else {
$suggestedName = PHP_CodeSniffer::suggestType($typeName);
}
if ($typeName !== $suggestedName) {
$error = 'Expected "%s" but found "%s" for parameter type';
$data = array(
$suggestedName,
$typeName,
);
$fix = $phpcsFile->addFixableError($error, $param['tag'], 'IncorrectParamVarName', $data);
if ($fix === true) {
$content = $suggestedName;
$content .= str_repeat(' ', $param['type_space']);
$content .= $param['var'];
$content .= str_repeat(' ', $param['var_space']);
$content .= $param['commentLines'][0]['comment'];
$phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content);
}
}
}//end foreach
if ($param['var'] === '') {
continue;
}
$foundParams[] = $param['var'];
// 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->addWarning($error, $param['tag'], $code, $data);
}
} elseif (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;
}
// Param comments must start with a capital letter and end with the full stop.
$firstChar = $param['comment']{0};
if (preg_match('|\p{Lu}|u', $firstChar) === 0) {
$error = 'Parameter comment must start with a capital letter';
$phpcsFile->addWarning($error, $param['tag'], 'ParamCommentNotCapital');
}
$lastChar = substr($param['comment'], -1);
if ($lastChar !== '.') {
$error = 'Parameter comment must end with a full stop';
$phpcsFile->addWarning($error, $param['tag'], 'ParamCommentFullStop');
}
}//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->addWarning($error, $commentStart, 'MissingParamTag', $data);
}
}//end processParams()
}//end class
@@ -0,0 +1,87 @@
<?php
/**
* FHComplete
*/
/**
* Asserts that function comments use the short form for types:
* - bool instead of boolean
* - int instead of integer
*/
class FHComplete_Sniffs_Commenting_FunctionCommentTypeSniff implements PHP_CodeSniffer_Sniff {
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_DOC_COMMENT);
}
/**
* 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();
// We are only interested in function/class/interface doc block comments.
$nextToken = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true);
$ignore = array(
T_CLASS,
T_INTERFACE,
T_FUNCTION,
T_PUBLIC,
T_PRIVATE,
T_PROTECTED,
T_STATIC,
T_ABSTRACT,
);
if (in_array($tokens[$nextToken]['code'], $ignore) === false) {
// Could be a file comment.
$prevToken = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($tokens[$prevToken]['code'] !== T_OPEN_TAG) {
return;
}
}
$types = array(
'boolean' => 'bool',
'integer' => 'int',
);
foreach ($types as $from => $to) {
$this->_check($phpcsFile, $stackPtr, $from, $to);
}
}
/**
* MyFHComplete_Sniffs_Commenting_DocBlockTypeSniff::_check()
*
* @param int $stackPtr
* @param string $from
* @param string $to
* @return void
*/
protected function _check(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $from, $to)
{
$tokens = $phpcsFile->getTokens();
$content = $tokens[$stackPtr]['content'];
$matches = array();
if (preg_match('/\@(\w+)\s+([\w\\|\\\\]*?)' . $from . '\b/i', $content, $matches) === 0) {
return;
}
$error = 'Please use "' . $to . '" instead of "' . $from . '" for types in doc blocks.';
$phpcsFile->addWarning($error, $stackPtr, 'WrongType');
}
}
@@ -0,0 +1,58 @@
<?php
/**
* Verifies that control statements conform to their coding standards.
*
* PHP version 5
*/
if (class_exists('PHP_CodeSniffer_Standards_AbstractPatternSniff', true) === false) {
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_Standards_AbstractPatternSniff not found');
}
/**
* Verifies that control statements conform to their coding standards.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @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 FHComplete_Sniffs_ControlStructures_ControlSignatureSniff extends PHP_CodeSniffer_Standards_AbstractPatternSniff
{
/**
* If true, comments will be ignored if they are found in the code.
*
* @var boolean
*/
public $ignoreComments = true;
/**
* Returns the patterns that this test wishes to verify.
*
* @return string[]
*/
protected function getPatterns()
{
return array(
'try {EOL...}\s+catch (...)EOL...{EOL...EOL...}',
'do+EOL...{EOL...EOL...} while (...);EOL',
'while (...)EOL...{EOL',
'for (...) {EOL',
'if (...)EOL...{EOL',
'foreach (...)EOL...{EOL',
'}EOL...\s+else if (...)EOL...{EOL',
'}EOL...\s+elseif (...)EOL...{EOL',
'}EOL...\s+else+EOL...{EOL',
'do+EOL...{EOL',
);
}//end getPatterns()
}//end class
@@ -0,0 +1,48 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Ensures that elseif is used instead of else if
*
*/
class FHComplete_Sniffs_ControlStructures_ElseIfDeclarationSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_ELSE);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* Checks that ELSEIF is used instead of ELSE IF.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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();
$nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
if ($tokens[$nextToken]['code'] !== T_IF) {
return;
}
$error = 'Usage of ELSE IF not allowed; use ELSEIF instead';
$phpcsFile->addError($error, $stackPtr, 'NotAllowed');
}
}
@@ -0,0 +1,56 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Ensures that while and do-while use curly brackets
*
*/
class FHComplete_Sniffs_ControlStructures_WhileStructuresSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_DO, T_WHILE);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* Checks that while and do-while use curly brackets
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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();
$nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
if ($tokens[$nextToken]['code'] === T_OPEN_PARENTHESIS) {
$closer = $tokens[$nextToken]['parenthesis_closer'];
$diff = $closer - $stackPtr;
$nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + $diff + 1), null, true);
}
if ($tokens[$stackPtr]['code'] === T_WHILE && $tokens[$nextToken]['code'] === T_SEMICOLON) {
/* This while is probably part of a do-while construction, skip it .. */
return;
}
if ($tokens[$nextToken]['code'] !== T_OPEN_CURLY_BRACKET && $tokens[$nextToken]['code'] !== T_COLON) {
$error = 'Curly brackets required in a do-while or while loop';
$phpcsFile->addError($error, $stackPtr, 'NotAllowed');
}
}
}
@@ -0,0 +1,48 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Ensures the use contains only one class.
*
*/
class FHComplete_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 integer $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());
}
}
}
@@ -0,0 +1,142 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Ensures all the use are in alphabetical order.
*
*/
class FHComplete_Sniffs_Formatting_UseInAlphabeticalOrderSniff implements PHP_CodeSniffer_Sniff
{
/**
* Processed files
*
* @var array
*/
protected $_processed = array();
/**
* The list of use statements, their content and scope.
*
* @var array
*/
protected $_uses = array();
/**
* 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 integer $stackPtr The position of the current token in the stack passed in $tokens.
* @return void
*/
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
if (isset($this->_processed[$phpcsFile->getFilename()])) {
return;
}
$filename = $phpcsFile->getFilename();
$this->_uses = array();
$next = $stackPtr;
while ($next !== false) {
$this->_checkUseToken($phpcsFile, $next);
$next = $phpcsFile->findNext(T_USE, $next + 1);
}
// Prevent multiple uses in the same file from entering
$this->_processed[$phpcsFile->getFilename()] = true;
foreach ($this->_uses as $scope => $used) {
$defined = $sorted = array_keys($used);
natcasesort($sorted);
$sorted = array_values($sorted);
if ($sorted === $defined) {
continue;
}
foreach ($defined as $i => $name) {
if ($name !== $sorted[$i]) {
$error = 'Use classes must be in alphabetical order. Was expecting ' . $sorted[$i];
$phpcsFile->addError($error, $used[$name], 'UseInAlphabeticalOrder', array());
}
}
}
}
/**
* Check all the use tokens in a file.
*
* @param PHP_CodeSniffer_File $phpcsFile The file to check.
* @param integer $stackPtr The index of the first use token.
* @return void
*/
protected function _checkUseToken($phpcsFile, $stackPtr) {
// If the use token is for a closure we want to ignore it.
$isClosure = $this->_isClosure($phpcsFile, $stackPtr);
if ($isClosure) {
return;
}
$tokens = $phpcsFile->getTokens();
// Only one USE declaration allowed per statement.
$next = $phpcsFile->findNext(array(T_COMMA, T_SEMICOLON), ($stackPtr + 1));
if ($tokens[$next]['code'] === T_COMMA) {
$error = 'There must be one USE keyword per declaration';
$phpcsFile->addError($error, $stackPtr, 'MultipleDeclarations');
}
$content = '';
$end = $phpcsFile->findNext(array(T_SEMICOLON, T_OPEN_CURLY_BRACKET), $stackPtr);
$useTokens = array_slice($tokens, $stackPtr, $end - $stackPtr, true);
foreach ($useTokens as $index => $token) {
if ($token['code'] === T_STRING || $token['code'] === T_NS_SEPARATOR) {
$content .= $token['content'];
}
}
// Check for class scoping on use. Traits should be
// ordered independently.
$scope = 0;
if (!empty($token['conditions'])) {
$scope = key($token['conditions']);
}
$this->_uses[$scope][$content] = $stackPtr;
}
/**
* Check if the current stackPtr is a use token that is for a closure.
*
* @param PHP_CodeSniffer_File $phpcsFile
* @param integer $stackPtr
* @return boolean
*/
protected function _isClosure($phpcsFile, $stackPtr) {
return $phpcsFile->findPrevious(
array(T_CLOSURE),
($stackPtr - 1),
null,
false,
null,
true
);
}
}
@@ -0,0 +1,35 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Ensures there is a space after the function keyword for closures.
*
*/
class FHComplete_Sniffs_Functions_ClosureDeclarationSniff implements PHP_CodeSniffer_Sniff {
public function register()
{
return array(T_CLOSURE);
}
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$spaces = 0;
if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
$spaces = strlen($tokens[($stackPtr + 1)]['content']);
}
if ($spaces !== 1) {
$error = 'Expected 1 space after closure\'s function keyword; %s found';
$data = array($spaces);
$phpcsFile->addError($error, $stackPtr, 'SpaceAfterFunction', $data);
}
}
}
@@ -0,0 +1,26 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
if (class_exists('Squiz_Sniffs_Functions_FunctionDeclarationArgumentSpacingSniff', true) === false) {
$error = 'Class Squiz_Sniffs_Functions_FunctionDeclarationArgumentSpacingSniff not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* Ensures the spacing of function declaration arguments is correct.
*
*/
class FHComplete_Sniffs_Functions_FunctionDeclarationArgumentSpacingSniff extends
Squiz_Sniffs_Functions_FunctionDeclarationArgumentSpacingSniff {
/**
* How many spaces should surround the equals signs.
*
* @var int
*/
public $equalsSpacing = 1;
}
@@ -0,0 +1,98 @@
<?php
/**
* PSR1_Sniffs_Methods_CamelCapsMethodNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @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
*/
if (class_exists('Generic_Sniffs_NamingConventions_CamelCapsFunctionNameSniff', true) === false) {
throw new PHP_CodeSniffer_Exception('Class Generic_Sniffs_NamingConventions_CamelCapsFunctionNameSniff not found');
}
/**
* PSR1_Sniffs_Methods_CamelCapsMethodNameSniff.
*
* Ensures method names are defined using camel case.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @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 FHComplete_Sniffs_NamingConventions_CamelCapsMethodNameSniff extends Generic_Sniffs_NamingConventions_CamelCapsFunctionNameSniff
{
/**
* Processes the tokens within the scope.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being processed.
* @param int $stackPtr The position where this token was
* found.
* @param int $currScope The position of the current scope.
*
* @return void
*/
protected function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope)
{
$methodName = $phpcsFile->getDeclarationName($stackPtr);
if ($methodName === null
|| substr($methodName,-4) === '_get'
|| substr($methodName,-5) === '_post'
|| substr($methodName,-4) === '_put'
|| substr($methodName,-6) === '_patch'
|| substr($methodName,-7) === '_delete'
)
// Ignore closures.
return;
// Ignore magic methods.
if (preg_match('|^__|', $methodName) !== 0) {
$magicPart = strtolower(substr($methodName, 2));
if (isset($this->magicMethods[$magicPart]) === true
|| isset($this->methodsDoubleUnderscore[$magicPart]) === true
) {
return;
}
}
$testName = ltrim($methodName, '_');
if ($testName !== '' && PHP_CodeSniffer::isCamelCaps($testName, false, true, false) === false) {
$error = 'Method name "%s" is not in camel caps format';
$className = $phpcsFile->getDeclarationName($currScope);
$errorData = array($className.'::'.$methodName);
$phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData);
$phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'no');
} else {
$phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'yes');
}
}//end processTokenWithinScope()
/**
* Processes the tokens outside the scope.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being processed.
* @param int $stackPtr The position where this token was
* found.
*
* @return void
*/
protected function processTokenOutsideScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
}//end processTokenOutsideScope()
}//end class
@@ -0,0 +1,223 @@
<?php
/**
* FHComplete_Sniffs_NamingConventions_UpperCaseConstantNameSniff.
*
* PHP version 5
*
* Ensures that constant names are all uppercase.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2012 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
* @version Release: 1.5.0RC3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class FHComplete_Sniffs_NamingConventions_UpperCaseConstantNameSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_STRING);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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();
$constName = $tokens[$stackPtr]['content'];
// If this token is in a heredoc, ignore it.
if ($phpcsFile->hasCondition($stackPtr, T_START_HEREDOC) === true) {
return;
}
// Special case for PHPUnit.
if ($constName === 'PHPUnit_MAIN_METHOD') {
return;
}
// If the next non-whitespace token after this token
// is not an opening parenthesis then it is not a function call.
$openBracket = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
$functionKeyword = $phpcsFile->findPrevious(
array(
T_WHITESPACE,
T_COMMA,
T_COMMENT,
T_STRING,
T_NS_SEPARATOR,
),
($stackPtr - 1),
null,
true
);
$declarations = array(
T_FUNCTION,
T_CLASS,
T_INTERFACE,
T_TRAIT,
T_IMPLEMENTS,
T_EXTENDS,
T_INSTANCEOF,
T_NEW,
T_NAMESPACE,
T_USE,
T_AS,
T_GOTO,
T_INSTEADOF,
T_PROTECTED,
T_PRIVATE,
T_PUBLIC
);
if (in_array($tokens[$functionKeyword]['code'], $declarations) === true) {
// This is just a declaration; no constants here.
return;
}
if ($tokens[$functionKeyword]['code'] === T_CONST) {
// This is a class constant.
if (strtoupper($constName) !== $constName) {
$error = 'Class constants must be uppercase; expected %s but found %s';
$data = array(
strtoupper($constName),
$constName,
);
$phpcsFile->addError($error, $stackPtr, 'ClassConstantNotUpperCase', $data);
}
return;
}
// Is this a class name?
$nextPtr = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
if ($tokens[$nextPtr]['code'] === T_DOUBLE_COLON) {
return;
}
// Is this a namespace name?
if ($tokens[$nextPtr]['code'] === T_NS_SEPARATOR) {
return;
}
// Is this an insteadof name?
if ($tokens[$nextPtr]['code'] === T_INSTEADOF) {
return;
}
// Is this an as name?
if ($tokens[$nextPtr]['code'] === T_AS) {
return;
}
// Is this a type hint?
if ($tokens[$nextPtr]['code'] === T_VARIABLE
|| $phpcsFile->isReference($nextPtr) === true
) {
return;
}
// Is this a member var name?
$prevPtr = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($tokens[$prevPtr]['code'] === T_OBJECT_OPERATOR) {
return;
}
// Is this a variable name, in the form ${varname} ?
if ($tokens[$prevPtr]['code'] === T_OPEN_CURLY_BRACKET) {
$nextPtr = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
if ($tokens[$nextPtr]['code'] === T_CLOSE_CURLY_BRACKET) {
return;
}
}
// Is this a namespace name?
if ($tokens[$prevPtr]['code'] === T_NS_SEPARATOR) {
return;
}
// Is this an instance of declare()
$prevPtrDeclare = $phpcsFile->findPrevious(
array(T_WHITESPACE, T_OPEN_PARENTHESIS),
($stackPtr - 1),
null,
true
);
if ($tokens[$prevPtrDeclare]['code'] === T_DECLARE) {
return;
}
// Is this a goto label target?
if ($tokens[$nextPtr]['code'] === T_COLON) {
if (in_array($tokens[$prevPtr]['code'], array(T_SEMICOLON, T_OPEN_CURLY_BRACKET, T_COLON), true)) {
return;
}
}
// This is a real constant.
if (strtoupper($constName) !== $constName) {
$error = 'Constants must be uppercase; expected %s but found %s';
$data = array(
strtoupper($constName),
$constName,
);
$phpcsFile->addError($error, $stackPtr, 'ConstantNotUpperCase', $data);
}
} elseif (strtolower($constName) === 'define' || strtolower($constName) === 'constant') {
/*
This may be a "define" or "constant" function call.
*/
// Make sure this is not a method call.
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($tokens[$prev]['code'] === T_OBJECT_OPERATOR
|| $tokens[$prev]['code'] === T_DOUBLE_COLON
) {
return;
}
// The next non-whitespace token must be the constant name.
$constPtr = $phpcsFile->findNext(T_WHITESPACE, ($openBracket + 1), null, true);
if ($tokens[$constPtr]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
return;
}
$constName = $tokens[$constPtr]['content'];
// Check for constants like self::CONSTANT.
$prefix = '';
$splitPos = strpos($constName, '::');
if ($splitPos !== false) {
$prefix = substr($constName, 0, ($splitPos + 2));
$constName = substr($constName, ($splitPos + 2));
}
if (strtoupper($constName) !== $constName) {
$error = 'Constants must be uppercase; expected %s but found %s';
$data = array(
$prefix . strtoupper($constName),
$prefix . $constName,
);
$phpcsFile->addError($error, $stackPtr, 'ConstantNotUpperCase', $data);
}
}
}
}
@@ -0,0 +1,47 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Ensures curly brackets are on the same line as the Class declaration
*
*/
class FHComplete_Sniffs_NamingConventions_ValidClassBracketsSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_CLASS);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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();
$found = $phpcsFile->findNext(T_OPEN_CURLY_BRACKET, $stackPtr);
if ($tokens[$found - 1]['code'] != T_WHITESPACE) {
$error = 'Expected 1 space after class declaration, found 0';
$phpcsFile->addError($error, $found - 1, 'InvalidSpacing', array());
return;
}
if (strlen($tokens[$found - 1]['content']) > 1 || $tokens[$found - 2]['code'] == T_WHITESPACE) {
$error = 'Expected 1 space after class declaration, found ' . strlen($tokens[$found - 1]['content']);
$phpcsFile->addError($error, $found - 1, 'InvalidSpacing', array());
}
}
}
@@ -0,0 +1,138 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Ensures method names are correct depending on whether they are public
* or private, and that functions are named correctly.
*
*/
class FHComplete_Sniffs_NamingConventions_ValidFunctionNameSniff extends PHP_CodeSniffer_Standards_AbstractScopeSniff {
/**
* A list of all PHP magic methods.
*
* @var array
*/
protected $_magicMethods = array(
'construct',
'destruct',
'call',
'callStatic',
'debugInfo',
'get',
'set',
'isset',
'unset',
'sleep',
'wakeup',
'toString',
'set_state',
'clone',
'invoke',
);
/**
* Constructs a PEAR_Sniffs_NamingConventions_ValidFunctionNameSniff.
*/
public function __construct() {
parent::__construct(array(T_CLASS, T_INTERFACE), array(T_FUNCTION), true);
}
/**
* Processes the tokens within the scope.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being processed.
* @param integer $stackPtr The position where this token was found.
* @param integer $currScope The position of the current scope.
* @return void
*/
protected function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope) {
$methodName = $phpcsFile->getDeclarationName($stackPtr);
if ($methodName === null) {
// Ignore closures.
return;
}
$className = $phpcsFile->getDeclarationName($currScope);
$errorData = array($className . '::' . $methodName);
// PHP4 constructors are allowed to break our rules.
if ($methodName === $className) {
return;
}
// PHP4 destructors are allowed to break our rules.
if ($methodName === '_' . $className) {
return;
}
// Ignore magic methods
if (preg_match('/^__(' . implode('|', $this->_magicMethods) . ')$/', $methodName)) {
return;
}
$methodProps = $phpcsFile->getMethodProperties($stackPtr);
if ($methodProps['scope_specified'] === false) {
// Let another sniffer take care of that
return;
}
$isPublic = $methodProps['scope'] === 'public';
$isProtected = $methodProps['scope'] === 'protected';
$isPrivate = $methodProps['scope'] === 'private';
$scope = $methodProps['scope'];
if ($isPublic === true) {
if ($methodName[0] === '_') {
$error = 'Public method name "%s" must not be prefixed with underscore';
$phpcsFile->addError($error, $stackPtr, 'PublicWithUnderscore', $errorData);
return;
}
// Underscored public methods in controller are allowed to break our rules.
if (substr($className, -10) === 'Controller') {
return;
}
// Underscored public methods in shells are allowed to break our rules.
if (substr($className, -5) === 'Shell') {
return;
}
// Underscored public methods in tasks are allowed to break our rules.
if (substr($className, -4) === 'Task') {
return;
}
} elseif ($isPrivate === true) {
if (substr($methodName, 0, 2) !== '__') {
$error = 'Private method name "%s" must be prefixed with 2 underscores';
$phpcsFile->addError($error, $stackPtr, 'PrivateNoUnderscore', $errorData);
return;
} else {
$filename = $phpcsFile->getFilename();
if (strpos($filename, '/lib/Cake/') === true) {
$warning = 'Private method name "%s" in FHComplete core is discouraged';
$phpcsFile->addWarning($warning, $stackPtr, 'PrivateMethodInCore', $errorData);
}
}
} else {
if ($methodName[0] !== '_' || substr($methodName, 0, 2) === '__') {
$error = 'Protected method name "%s" must be prefixed with one underscore';
$phpcsFile->addError($error, $stackPtr, 'ProtectedNoUnderscore', $errorData);
return;
}
}
}
/**
* Processes the tokens outside the scope.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being processed.
* @param integer $stackPtr The position where this token was found.
* @return void
*/
protected function processTokenOutsideScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr) {
}
}
@@ -0,0 +1,48 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Ensures trait names are correct depending on the folder of the file.
*
*/
class FHComplete_Sniffs_NamingConventions_ValidTraitNameSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* If the constant is not defined, ignore because probably the PHP version
* is under 5.4.0 and don't have traits in use
*
* @return array
*/
public function register()
{
if (!defined('T_TRAIT')) {
return array();
}
return array(T_TRAIT);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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();
$traitName = $tokens[$stackPtr + 2]['content'];
if (substr($traitName, -5) !== 'Trait') {
$error = 'Traits must have a "Trait" suffix.';
$phpcsFile->addError($error, $stackPtr, 'InvalidTraitName', array());
}
}
}
@@ -0,0 +1,171 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
if (class_exists('PHP_CodeSniffer_Standards_AbstractVariableSniff', true) === false) {
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_Standards_AbstractVariableSniff not found');
}
/**
* Checks the naming of variables and member variables.
*
*/
class FHComplete_Sniffs_NamingConventions_ValidVariableNameSniff extends PHP_CodeSniffer_Standards_AbstractVariableSniff {
/**
* Processes this test, when one of its tokens is encountered.
*
* Processes variables, we skip processing object properties because
* they could come from things like PDO which doesn't follow the normal
* conventions and causes additional failures.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $stackPtr The position of the current token in the
* stack passed in $tokens.
* @return void
*/
protected function processVariable(PHP_CodeSniffer_File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile->getTokens();
$varName = ltrim($tokens[$stackPtr]['content'], '$');
$phpReservedVars = array(
'_SERVER',
'_GET',
'_POST',
'_REQUEST',
'_SESSION',
'_ENV',
'_COOKIE',
'_FILES',
'GLOBALS',
);
// If it's a php reserved var, then its ok.
if (in_array($varName, $phpReservedVars) === true) {
return;
}
// There is no way for us to know if the var is public or private,
// so we have to ignore a leading underscore if there is one and just
// check the main part of the variable name.
$originalVarName = $varName;
if (substr($varName, 0, 1) === '_') {
$objOperator = $phpcsFile->findPrevious(array(T_WHITESPACE), ($stackPtr - 1), null, true);
if ($tokens[$objOperator]['code'] === T_DOUBLE_COLON) {
// The variable lives within a class, and is referenced like
// this: MyClass::$_variable, so we don't know its scope.
$inClass = true;
} else {
$inClass = $phpcsFile->hasCondition($stackPtr, array(T_TRAIT, T_CLASS, T_INTERFACE));
}
if ($inClass === true) {
$varName = ltrim($varName, '_');
}
}
// $title_for_layout is allowed on controllers
$fileName = basename($phpcsFile->getFilename(), '.php');
if ((substr($fileName, -10) === 'Controller') && ($varName == 'title_for_layout')) {
return;
}
if ($this->_isValidVar($varName) === false) {
$error = 'Variable "%s" is not in valid camel caps format';
$data = array($originalVarName);
$phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $data);
}
}
/**
* Processes class member variables.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $stackPtr The position of the current token in the
* stack passed in $tokens.
* @return void
*/
protected function processMemberVar(PHP_CodeSniffer_File $phpcsFile, $stackPtr) {
}
/**
* Processes the variable found within a double quoted string.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $stackPtr The position of the double quoted string.
* @return void
*/
protected function processVariableInString(PHP_CodeSniffer_File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile->getTokens();
$phpReservedVars = array(
'_SERVER',
'_GET',
'_POST',
'_REQUEST',
'_SESSION',
'_ENV',
'_COOKIE',
'_FILES',
'GLOBALS',
);
if (preg_match_all('|[^\\\]\$([a-zA-Z0-9_]+)|', $tokens[$stackPtr]['content'], $matches) !== 0) {
foreach ($matches[1] as $varName) {
// If it's a php reserved var, then its ok.
if (in_array($varName, $phpReservedVars) === true) {
continue;
}
// There is no way for us to know if the var is public or private,
// so we have to ignore a leading underscore if there is one and just
// check the main part of the variable name.
$originalVarName = $varName;
if (substr($varName, 0, 1) === '_') {
if ($phpcsFile->hasCondition($stackPtr, array(T_CLASS, T_INTERFACE)) === true) {
$varName = substr($varName, 1);
}
}
if ($this->_isValidVar($varName) === false) {
$error = 'Variable "%s" is not in valid camel caps format';
$data = array($originalVarName);
$phpcsFile->addError($error, $stackPtr, 'StringVarNotCamelCaps', $data);
}
}
}
}
/**
* Check that a variable is a valid shape.
*
* Variables in FHComplete can either be $fooBar, $FooBar, $_fooBar, or $_FooBar.
*
* @param string $string The variable to check.
* @param boolea $public Whether or not the variable is public.
* @return boolean
*/
protected function _isValidVar($string, $public = true) {
$firstChar = '[a-zA-Z]';
if (!$public) {
$firstChar = '[_]{1,2}' . $firstChar;
}
if (preg_match("|^$firstChar|", $string) === 0) {
return false;
}
$firstStringCount = 1;
if (preg_match("|^__|", $string)) {
$firstStringCount = 2;
}
// Check that the name only contains legal characters.
$legalChars = 'a-zA-Z0-9';
if (preg_match("|[^$legalChars]|", substr($string, $firstStringCount)) > 0) {
return false;
}
return true;
}
}
@@ -0,0 +1,53 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Disallow short open tags
*
* But permit short-open echo tags (<?=) [T_OPEN_TAG_WITH_ECHO] as they are part of PHP 5.4+
*
*/
class FHComplete_Sniffs_PHP_DisallowShortOpenTagSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* If short open tags are NOT enabled, <? is not considered a T_OPEN_TAG
* So include T_INLINE_HTML which is what "<?" is detected as
*
* @return array
*/
public function register()
{
return array(
T_OPEN_TAG,
T_INLINE_HTML
);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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();
$openTag = $tokens[$stackPtr];
if (trim($openTag['content']) === '<?') {
$error = 'Short PHP opening tag used; expected "<?php" but found "%s"';
$data = array(trim($openTag['content']));
$phpcsFile->addError($error, $stackPtr, 'Found', $data);
}
}
}
@@ -0,0 +1,74 @@
<?php
/**
* FHComplete
*/
/**
* Asserts that type casts are in the short form:
* - bool instead of boolean
* - int instead of integer
*/
class FHComplete_Sniffs_PHP_TypeCastingSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* Note, that this sniff only checks the value and casing of a cast.
* It does not check for whitespace issues regarding casts, as
* - Squiz.WhiteSpace.CastSpacing.ContainsWhiteSpace checks for whitespace in the cast
* - Generic.Formatting.NoSpaceAfterCast.SpaceFound checks for whitespace after the cast
*
* @return array
*/
public function register()
{
return array_merge(PHP_CodeSniffer_Tokens::$castTokens, array(T_BOOLEAN_NOT));
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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();
// Process !! casts
if ($tokens[$stackPtr]['code'] == T_BOOLEAN_NOT) {
$nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
if (!$nextToken) {
return;
}
if ($tokens[$nextToken]['code'] != T_BOOLEAN_NOT) {
return;
}
$error = 'Usage of !! cast is not allowed. Please use (bool) to cast.';
$phpcsFile->addError($error, $stackPtr, 'NotAllowed');
return;
}
// Only allow short forms if both short and long forms are possible
$matching = array(
'(boolean)' => '(bool)',
'(integer)' => '(int)',
);
$content = $tokens[$stackPtr]['content'];
$key = strtolower($content);
if (isset($matching[$key])) {
$error = 'Please use ' . $matching[$key] . ' instead of ' . $content . '.';
$phpcsFile->addError($error, $stackPtr, 'NotAllowed');
return;
}
if ($content !== $key) {
$error = 'Please use ' . $key . ' instead of ' . $content . '.';
$phpcsFile->addError($error, $stackPtr, 'NotAllowed');
return;
}
}
}
@@ -0,0 +1,48 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Makes sure there are spaces between the concatenation operator (.) and
* the strings being concatenated.
*
*/
class FHComplete_Sniffs_Strings_ConcatenationSpacingSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_STRING_CONCAT);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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();
if ($tokens[($stackPtr - 1)]['code'] == T_WHITESPACE) {
$message = 'Expected 0 spaces before ., but 1 found';
$phpcsFile->addError($message, $stackPtr, 'FoundBefore');
}
if ($tokens[($stackPtr + 1)]['code'] == ' ')
{
$message = 'Expected 0 spaces after ., but 1 found';
$phpcsFile->addError($message, $stackPtr, 'FoundAfter');
}
}
}
@@ -0,0 +1,54 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Ensures no whitespaces and one whitespace is placed around each comma
*
*/
class FHComplete_Sniffs_WhiteSpace_CommaSpacingSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_COMMA);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile All the tokens found in the document.
* @param integer $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();
$next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
if ($tokens[$next]['code'] !== T_WHITESPACE && ($next !== $stackPtr + 2)) {
// Last character in a line is ok.
if ($tokens[$next]['line'] === $tokens[$stackPtr]['line']) {
$error = 'Missing space after comma';
$phpcsFile->addError($error, $next);
}
}
$previous = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($tokens[$previous]['code'] !== T_WHITESPACE && ($previous !== $stackPtr - 1)) {
$error = 'Space before comma, expected none, though';
$phpcsFile->addError($error, $next);
}
}
}
@@ -0,0 +1,53 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Checks the separation between methods in a class or interface.
*
*/
class FHComplete_Sniffs_WhiteSpace_FunctionCallSpacingSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_ISSET,
T_EMPTY,
T_STRING,
);
}
/**
* Processes this sniff, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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 the next non-empty token.
$openBracket = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
// Not a function call.
return;
}
// Look for funcName (
if (($stackPtr + 1) !== $openBracket) {
$error = 'Space before opening parenthesis of function call not allowed';
$phpcsFile->addError($error, $stackPtr, 'SpaceBeforeOpenBracket');
}
}
}
@@ -0,0 +1,69 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Checks that there is one empty line before the closing brace of a function.
*
*/
class FHComplete_Sniffs_WhiteSpace_FunctionClosingBraceSpaceSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_FUNCTION);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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();
if (isset($tokens[$stackPtr]['scope_closer']) === false) {
// Probably an interface method.
return;
}
$closeBrace = $tokens[$stackPtr]['scope_closer'];
$prevContent = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBrace - 1), null, true);
$braceLine = $tokens[$closeBrace]['line'];
$prevLine = $tokens[$prevContent]['line'];
$found = ($braceLine - $prevLine - 1);
if ($phpcsFile->hasCondition($stackPtr, T_FUNCTION) === true
|| isset($tokens[$stackPtr]['nested_parenthesis']) === true
) {
// Nested function.
if ($found < 0) {
$error = 'Closing brace of nested function must be on a new line';
$phpcsFile->addError($error, $closeBrace, 'ContentBeforeClose');
} elseif ($found > 0) {
$error = 'Expected 0 blank lines before closing brace of nested function; %s found';
$data = array($found);
$phpcsFile->addError($error, $closeBrace, 'SpacingBeforeNestedClose', $data);
}
} else {
if ($found !== 0) {
$error = 'Expected 0 blank lines before closing function brace; %s found';
$data = array($found);
$phpcsFile->addError($error, $closeBrace, 'SpacingBeforeClose', $data);
}
}
}
}
@@ -0,0 +1,62 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Checks that there is no empty line after the opening brace of a function.
*
*/
class FHComplete_Sniffs_WhiteSpace_FunctionOpeningBraceSpaceSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_FUNCTION);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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();
if (isset($tokens[$stackPtr]['scope_opener']) === false) {
// Probably an interface method.
return;
}
$openBrace = $tokens[$stackPtr]['scope_opener'];
$nextContent = $phpcsFile->findNext(T_WHITESPACE, ($openBrace + 1), null, true);
if ($nextContent === $tokens[$stackPtr]['scope_closer']) {
// The next bit of content is the closing brace, so this
// is an empty function and should have a blank line
// between the opening and closing braces.
return;
}
$braceLine = $tokens[$openBrace]['line'];
$nextLine = $tokens[$nextContent]['line'];
$found = ($nextLine - $braceLine - 1);
if ($found > 0) {
$error = 'Expected 0 blank lines after opening function brace; %s found';
$data = array($found);
$phpcsFile->addError($error, $openBrace, 'SpacingAfter', $data);
}
}
}
@@ -0,0 +1,129 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Checks the separation between methods in a class or interface.
*
*/
class FHComplete_Sniffs_WhiteSpace_FunctionSpacingSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_FUNCTION);
}
/**
* Processes this sniff, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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();
/*
Check the number of blank lines
after the function.
*/
if (isset($tokens[$stackPtr]['scope_closer']) === false) {
// Must be an interface method, so the closer is the semi-colon.
$closer = $phpcsFile->findNext(T_SEMICOLON, $stackPtr);
} else {
$closer = $tokens[$stackPtr]['scope_closer'];
}
// There needs to be 1 blank lines after the closer.
$nextLineToken = null;
for ($i = $closer; $i < $phpcsFile->numTokens; $i++) {
if (strpos($tokens[$i]['content'], $phpcsFile->eolChar) === false) {
continue;
} else {
$nextLineToken = ($i + 1);
break;
}
}
if ($nextLineToken === null) {
// Never found the next line, which means
// there are 0 blank lines after the function.
$foundLines = 0;
} else {
$nextContent = $phpcsFile->findNext(array(T_WHITESPACE), ($nextLineToken + 1), null, true);
if ($nextContent === false) {
// We are at the end of the file. That is acceptable as well.
$foundLines = 1;
} else {
$foundLines = ($tokens[$nextContent]['line'] - $tokens[$nextLineToken]['line']);
}
}
/*
Check the number of blank lines
before the function.
*/
$prevLineToken = null;
for ($i = $stackPtr; $i > 0; $i--) {
if (strpos($tokens[$i]['content'], $phpcsFile->eolChar) === false) {
continue;
} else {
$prevLineToken = $i;
break;
}
}
if ($prevLineToken === null) {
// Never found the previous line, which means
// there are 0 blank lines before the function.
$foundLines = 0;
} else {
$prevContent = $phpcsFile->findPrevious(array(T_WHITESPACE, T_DOC_COMMENT), $prevLineToken, null, true);
// Before we throw an error, check that we are not throwing an error
// for another function. We don't want to error for no blank lines after
// the previous function and no blank lines before this one as well.
$currentLine = $tokens[$stackPtr]['line'];
$prevLine = ($tokens[$prevContent]['line'] - 1);
$i = ($stackPtr - 1);
$foundLines = 0;
while ($currentLine !== $prevLine && $currentLine > 1 && $i > 0) {
if (isset($tokens[$i]['scope_condition']) === true) {
$scopeCondition = $tokens[$i]['scope_condition'];
if ($tokens[$scopeCondition]['code'] === T_FUNCTION) {
// Found a previous function.
return;
}
} elseif ($tokens[$i]['code'] === T_FUNCTION) {
// Found another interface function.
return;
}
$currentLine = $tokens[$i]['line'];
if ($currentLine === $prevLine) {
break;
}
if ($tokens[($i - 1)]['line'] < $currentLine && $tokens[($i + 1)]['line'] > $currentLine) {
// This token is on a line by itself. If it is whitespace, the line is empty.
if ($tokens[$i]['code'] === T_WHITESPACE) {
$foundLines++;
}
}
$i--;
}
}
}
}
@@ -0,0 +1,42 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Ensure there is no whitespace before a semicolon.
*
*/
class FHComplete_Sniffs_WhiteSpace_ObjectOperatorSpacingSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_OBJECT_OPERATOR);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $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();
$nextType = $tokens[($stackPtr + 1)]['code'];
if (in_array($nextType, PHP_CodeSniffer_Tokens::$emptyTokens) === true) {
$error = 'Space found after object operator';
$phpcsFile->addError($error, $stackPtr, 'After');
}
}
}
@@ -0,0 +1,185 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Verifies that operators have valid spacing surrounding them.
*
*/
class FHComplete_Sniffs_WhiteSpace_OperatorSpacingSniff implements PHP_CodeSniffer_Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = array(
'PHP',
'JS',
);
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
$comparison = PHP_CodeSniffer_Tokens::$comparisonTokens;
$operators = PHP_CodeSniffer_Tokens::$operators;
$assignment = PHP_CodeSniffer_Tokens::$assignmentTokens;
return array_unique(array_merge($comparison, $operators, $assignment));
}
/**
* Processes this sniff, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The current file being checked.
* @param integer $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();
// Skip default values in function declarations.
if ($tokens[$stackPtr]['code'] === T_EQUAL
|| $tokens[$stackPtr]['code'] === T_MINUS
) {
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
$parenthesis = array_keys($tokens[$stackPtr]['nested_parenthesis']);
$bracket = array_pop($parenthesis);
if (isset($tokens[$bracket]['parenthesis_owner']) === true) {
$function = $tokens[$bracket]['parenthesis_owner'];
if ($tokens[$function]['code'] === T_FUNCTION) {
return;
}
}
}
}
if ($tokens[$stackPtr]['code'] === T_EQUAL) {
// Skip for '=&' case.
if (isset($tokens[($stackPtr + 1)]) === true && $tokens[($stackPtr + 1)]['code'] === T_BITWISE_AND) {
return;
}
}
if ($tokens[$stackPtr]['code'] === T_BITWISE_AND) {
// If its not a reference, then we expect one space either side of the
// bitwise operator.
if (!$phpcsFile->isReference($stackPtr) && !$this->isVariable($stackPtr, $tokens, $phpcsFile)) {
// Check there is one space before the & operator.
if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE) {
$error = 'Expected 1 space before "&" operator; 0 found';
$phpcsFile->addError($error, $stackPtr, 'NoSpaceBeforeAmp');
}
// Check there is one space after the & operator.
if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
$error = 'Expected 1 space after "&" operator; 0 found';
$phpcsFile->addError($error, $stackPtr, 'NoSpaceAfterAmp');
}
}
} else {
if ($tokens[$stackPtr]['code'] === T_MINUS) {
// Skip declaration of negative value in new array format; eg. $arr = [-1].
if ($tokens[($stackPtr - 1)]['code'] === T_OPEN_SHORT_ARRAY) {
return;
}
// Check minus spacing, but make sure we aren't just assigning
// a minus value or returning one.
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($tokens[$prev]['code'] === T_RETURN) {
// Just returning a negative value; eg. return -1.
return;
}
if (in_array($tokens[$prev]['code'], PHP_CodeSniffer_Tokens::$operators) === true) {
// Just trying to operate on a negative value; eg. ($var * -1).
return;
}
if (in_array($tokens[$prev]['code'], PHP_CodeSniffer_Tokens::$comparisonTokens) === true) {
// Just trying to compare a negative value; eg. ($var === -1).
return;
}
// A list of tokens that indicate that the token is not
// part of an arithmetic operation.
$invalidTokens = array(
T_COMMA,
T_OPEN_PARENTHESIS,
T_OPEN_SQUARE_BRACKET,
T_DOUBLE_ARROW,
T_COLON,
T_INLINE_THEN,
T_INLINE_ELSE,
T_CASE,
);
if (in_array($tokens[$prev]['code'], $invalidTokens) === true) {
// Just trying to use a negative value; eg. myFunction($var, -2).
return;
}
if (in_array($tokens[$prev]['code'], PHP_CodeSniffer_Tokens::$assignmentTokens) === true) {
// Just trying to assign a negative value; eg. ($var = -1).
return;
}
}
$operator = $tokens[$stackPtr]['content'];
if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE) {
$error = "Expected 1 space before \"$operator\"; 0 found";
$phpcsFile->addError($error, $stackPtr, 'NoSpaceBefore');
}
if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
$error = "Expected 1 space after \"$operator\"; 0 found";
$phpcsFile->addError($error, $stackPtr, 'NoSpaceAfter');
}
}
}
/**
* Check if the current token is inside an array.
*
* @param int $stackPtr The current token offset.
* @param array $phpcsFile The current token list.
* @return bool
*/
protected function isVariable($stackPtr, $tokens, $phpcsFile)
{
$tokenAfter = $phpcsFile->findNext(
PHP_CodeSniffer_Tokens::$emptyTokens,
($stackPtr + 1),
null,
true
);
$tokenBefore = $phpcsFile->findNext(
PHP_CodeSniffer_Tokens::$emptyTokens,
($stackPtr - 1),
null,
true
);
if ($tokens[$tokenAfter]['code'] === T_VARIABLE &&
(
$tokens[$tokenBefore]['code'] === T_OPEN_PARENTHESIS ||
$tokens[$tokenBefore]['code'] === T_COMMA ||
$tokens[$tokenBefore]['code'] === T_OPEN_SHORT_ARRAY
)
) {
return true;
}
return false;
}
}
@@ -0,0 +1,280 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Checks that control structures are structured correctly, and their content
* is indented correctly. This sniff will throw errors if tabs are used
* for indentation rather than spaces.
*
*/
class FHComplete_Sniffs_WhiteSpace_ScopeIndentSniff implements PHP_CodeSniffer_Sniff
{
/**
* The number of spaces code should be indented.
*
* @var int
*/
public $indent = 1;
/**
* Does the indent need to be exactly right.
*
* If TRUE, indent needs to be exactly $ident spaces. If FALSE,
* indent needs to be at least $ident spaces (but can be more).
*
* @var bool
*/
public $exact = false;
/**
* Any scope openers that should not cause an indent.
*
* @var array(int)
*/
protected $nonIndentingScopes = array();
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return PHP_CodeSniffer_Tokens::$scopeOpeners;
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile All the tokens found in the document.
* @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();
// If this is an inline condition (ie. there is no scope opener), then
// return, as this is not a new scope.
if (isset($tokens[$stackPtr]['scope_opener']) === false) {
return;
}
if ($tokens[$stackPtr]['code'] === T_ELSE) {
$next = $phpcsFile->findNext(
PHP_CodeSniffer_Tokens::$emptyTokens,
($stackPtr + 1),
null,
true
);
// We will handle the T_IF token in another call to process.
if ($tokens[$next]['code'] === T_IF) {
return;
}
}
// Find the first token on this line.
$firstToken = $stackPtr;
for ($i = $stackPtr; $i >= 0; $i--) {
// Record the first code token on the line.
if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === false) {
$firstToken = $i;
}
// It's the start of the line, so we've found our first php token.
if ($tokens[$i]['column'] === 1) {
break;
}
}
// Based on the conditions that surround this token, determine the
// indent that we expect this current content to be.
$expectedIndent = $this->calculateExpectedIndent($tokens, $firstToken);
$scopeOpener = $tokens[$stackPtr]['scope_opener'];
$scopeCloser = $tokens[$stackPtr]['scope_closer'];
// Some scopes are expected not to have indents.
if (in_array($tokens[$firstToken]['code'], $this->nonIndentingScopes) === false) {
$indent = ($expectedIndent + $this->indent);
} else {
$indent = $expectedIndent;
}
$newline = false;
$commentOpen = false;
$inHereDoc = false;
// Only loop over the content beween the opening and closing brace, not
// the braces themselves.
for ($i = ($scopeOpener + 1); $i < $scopeCloser; $i++) {
// If this token is another scope, skip it as it will be handled by
// another call to this sniff.
if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$scopeOpeners) === true) {
if (isset($tokens[$i]['scope_opener']) === true) {
$i = $tokens[$i]['scope_closer'];
// If the scope closer is followed by a semi-colon, the semi-colon is part
// of the closer and should also be ignored. This most commonly happens with
// CASE statements that end with "break;", where we don't want to stop
// ignoring at the break, but rather at the semi-colon.
$nextToken = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($i + 1), null, true);
if ($tokens[$nextToken]['code'] === T_SEMICOLON) {
$i = $nextToken;
}
} else {
// If this token does not have a scope_opener indice, then
// it's probably an inline scope, so let's skip to the next
// semicolon. Inline scopes include inline if's, abstract
// methods etc.
$nextToken = $phpcsFile->findNext(T_SEMICOLON, $i, $scopeCloser);
if ($nextToken !== false) {
$i = $nextToken;
}
}
continue;
}
// If this is a HEREDOC then we need to ignore it as the
// whitespace before the contents within the HEREDOC are
// considered part of the content.
if ($tokens[$i]['code'] === T_START_HEREDOC) {
$inHereDoc = true;
continue;
} elseif ($inHereDoc === true) {
if ($tokens[$i]['code'] === T_END_HEREDOC) {
$inHereDoc = false;
}
continue;
}
if ($tokens[$i]['column'] === 1) {
// We started a newline.
$newline = true;
}
if ($newline === true && $tokens[$i]['code'] !== T_WHITESPACE) {
// If we started a newline and we find a token that is not
// whitespace, then this must be the first token on the line that
// must be indented.
$newline = false;
$firstToken = $i;
$column = $tokens[$firstToken]['column'];
// Special case for non-PHP code.
if ($tokens[$firstToken]['code'] === T_INLINE_HTML) {
$trimmedContentLength = strlen(ltrim($tokens[$firstToken]['content']));
if ($trimmedContentLength === 0) {
continue;
}
$contentLength = strlen($tokens[$firstToken]['content']);
$column = ($contentLength - $trimmedContentLength + 1);
}
// If we're starting a new PHP block that has the scope closer
// as the next token we'll skip the remaining checks as the scope is closed.
if ($tokens[$firstToken]['code'] === T_OPEN_TAG &&
$scopeCloser == $firstToken + 1
) {
continue;
}
// Check to see if this constant string spans multiple lines.
// If so, then make sure that the strings on lines other than the
// first line are indented appropriately, based on their whitespace.
if (in_array($tokens[$firstToken]['code'], PHP_CodeSniffer_Tokens::$stringTokens) === true) {
if (in_array($tokens[($firstToken - 1)]['code'], PHP_CodeSniffer_Tokens::$stringTokens) === true) {
// If we find a string that directly follows another string
// then its just a string that spans multiple lines, so we
// don't need to check for indenting.
continue;
}
}
// This is a special condition for T_DOC_COMMENT and C-style
// comments, which contain whitespace between each line.
$comments = array(
T_COMMENT,
T_DOC_COMMENT
);
$isDocComment = false;
if (in_array($tokens[$firstToken]['code'], $comments) === true) {
$content = trim($tokens[$firstToken]['content']);
if (preg_match('|^/\*|', $content) !== 0) {
// Check to see if the end of the comment is on the same line
// as the start of the comment. If it is, then we don't
// have to worry about opening a comment.
if (preg_match('|\*/$|', $content) === 0) {
// We don't have to calculate the column for the
// start of the comment as there is a whitespace
// token before it.
$commentOpen = true;
$isDocComment = (substr($content, 0, 3) === '/**');
}
} elseif ($commentOpen === true) {
if ($content === '') {
// We are in a comment, but this line has nothing on it
// so let's skip it.
continue;
}
$contentLength = strlen($tokens[$firstToken]['content']);
$trimmedContentLength = strlen(ltrim($tokens[$firstToken]['content']));
$column = ($contentLength - $trimmedContentLength + 1);
if (preg_match('|\*/$|', $content) !== 0) {
$commentOpen = false;
}
}
}
}
}
}
/**
* Calculates the expected indent of a token.
*
* @param array $tokens The stack of tokens for this file.
* @param int $stackPtr The position of the token to get indent for.
* @return int
*/
protected function calculateExpectedIndent(array $tokens, $stackPtr)
{
$conditionStack = array();
// Empty conditions array (top level structure).
if (empty($tokens[$stackPtr]['conditions']) === true) {
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true
&& empty($tokens[$stackPtr]['nested_parenthesis']) === false
) {
// Wrapped in parenthesis means it is probably in a
// function call (like a closure) so we have to assume indent
// is correct here and someone else will check it more
// carefully in another sniff.
return $tokens[$stackPtr]['column'];
} else {
return 1;
}
}
$tokenConditions = $tokens[$stackPtr]['conditions'];
foreach ($tokenConditions as $id => $condition) {
// If it's an indenting scope ie. it's not in our array of
// scopes that don't indent, add it to our condition stack.
if (in_array($condition, $this->nonIndentingScopes) === false) {
$conditionStack[$id] = $condition;
}
}
return ((count($conditionStack) * $this->indent) + 1);
}
}
@@ -0,0 +1,63 @@
<?php
/**
* PHP Version 5
*
* FHComplete
*/
/**
* Check for any line starting with 2 spaces - which would indicate space indenting
* Also check for "\t " - a tab followed by a space, which is a common similar mistake
*
*/
class FHComplete_Sniffs_WhiteSpace_TabAndSpaceSniff implements PHP_CodeSniffer_Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = array(
'PHP',
'JS',
'CSS'
);
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_WHITESPACE);
}
/**
* 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();
$line = $tokens[$stackPtr]['line'];
if ($stackPtr > 0 && $tokens[($stackPtr - 1)]['line'] !== $line) {
return;
}
if (strpos($tokens[$stackPtr]['content'], " \t") !== false) {
$error = 'Space and tab found';
$phpcsFile->addError($error, $stackPtr);
}
if (strpos($tokens[$stackPtr]['content'], "\t ") !== false) {
$error = 'Tab and space found';
$phpcsFile->addError($error, $stackPtr);
}
}
}
+92
View File
@@ -0,0 +1,92 @@
<?xml version="1.0"?>
<ruleset name="FHComplete">
<description>FHComplete's coding standard</description>
<exclude-pattern>\.git</exclude-pattern>
<exclude-pattern>*/Config/*.ini.php</exclude-pattern>
<exclude-pattern>/*/tmp/</exclude-pattern>
<rule ref="PSR2">
<exclude name="PSR1.Classes.ClassDeclaration" />
<exclude name="PSR1.Methods.CamelCapsMethodName" />
<exclude name="PSR1.Files.SideEffects" />
<exclude name="Generic.WhiteSpace.DisallowTabIndent" />
<exclude name="Squiz.ControlStructures.ControlSignature" />
<exclude name="Generic.ControlStructures.InlineControlStructure" />
</rule>
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="120"/>
<property name="absoluteLineLimit" value="150"/>
</properties>
</rule>
<!--
Temporarily ignore until API docblock formatting and line length issues in core code are fixed.
<rule ref="FHComplete.Commenting.FunctionComment.ParamCommentNotCapital">
<severity>0</severity>
</rule>
<rule ref="FHComplete.Commenting.FunctionComment.ParamCommentFullStop">
<severity>0</severity>
</rule>
<rule ref="FHComplete.Commenting.FunctionComment.ThrowsNotCapital">
<severity>0</severity>
</rule>
<rule ref="FHComplete.Commenting.FunctionComment.ThrowsNoFullStop">
<severity>0</severity>
</rule>
<rule ref="FHComplete.Commenting.FunctionComment.EmptyThrows">
<severity>0</severity>
</rule>
-->
<rule ref="Squiz.Arrays.ArrayBracketSpacing"/>
<rule ref="Squiz.Classes.LowercaseClassKeywords"/>
<rule ref="Generic.CodeAnalysis.ForLoopShouldBeWhileLoop"/>
<rule ref="Generic.CodeAnalysis.ForLoopWithTestFunctionCall"/>
<rule ref="Generic.CodeAnalysis.JumbledIncrementer"/>
<rule ref="Generic.CodeAnalysis.UnconditionalIfStatement"/>
<rule ref="Generic.CodeAnalysis.UnnecessaryFinalModifier"/>
<rule ref="Squiz.Commenting.DocCommentAlignment"/>
<rule ref="Generic.Commenting.Todo"/>
<!--
We allow EOL after closing braces
-->
<rule ref="FHComplete.ControlStructures.ControlSignature"/>
<rule ref="Generic.Files.LineEndings"/>
<rule ref="Generic.Formatting.NoSpaceAfterCast"/>
<rule ref="Squiz.Operators.ValidLogicalOperators"/>
<rule ref="Generic.PHP.DeprecatedFunctions"/>
<rule ref="Squiz.PHP.DisallowSizeFunctionsInLoops"/>
<rule ref="Squiz.PHP.Eval"/>
<rule ref="Generic.PHP.ForbiddenFunctions"/>
<rule ref="Squiz.PHP.NonExecutableCode"/>
<rule ref="Generic.PHP.NoSilencedErrors"/>
<rule ref="Squiz.Scope.MemberVarScope"/>
<rule ref="Squiz.Scope.StaticThisUsage"/>
<rule ref="Squiz.WhiteSpace.CastSpacing"/>
<rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/>
<rule ref="Squiz.WhiteSpace.SemicolonSpacing"/>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"/>
<!-- Relax some src/* and tests/* rules -->
<rule ref="Squiz.Classes.ValidClassName">
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>
<rule ref="FHComplete.Commenting.FunctionComment">
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>
<!-- All rules in ./Sniffs are included automatically -->
</ruleset>
@@ -0,0 +1,67 @@
<?php
/**
* FHCompleteStandardTest
*/
class FHCompleteStandardTest extends PHPUnit_Framework_TestCase {
public function setUp() {
parent::setUp();
if (empty($this->helper)) {
$this->helper = new TestHelper();
}
}
/**
* testFiles
*
* Run simple syntax checks, if the filename ends with pass.php - expect it to pass
*/
public static function testProvider() {
$tests = array();
$standard = dirname(dirname(__FILE__));
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(dirname(__FILE__) . '/files'));
foreach ($iterator as $dir) {
if ($dir->isDir()) {
continue;
}
$file = $dir->getPathname();
$expectPass = (substr($file, -8) === 'pass.php');
$tests[] = array(
$file,
$standard,
$expectPass
);
}
return $tests;
}
/**
* _testFile
*
* @dataProvider testProvider
*
* @param string $file
* @param string $standard
* @param boolean $expectPass
*/
public function testFile($file, $standard, $expectPass) {
$outputStr = $this->helper->runPhpCs($file);
if ($expectPass) {
$this->assertNotRegExp(
"/FOUND \d+ ERROR/",
$outputStr,
basename($file) . ' - expected to pass with no errors, some were reported. '
);
} else {
$this->assertRegExp(
"/FOUND \d+ ERROR/",
$outputStr,
basename($file) . ' - expected failures, none reported. '
);
}
}
}
@@ -0,0 +1,54 @@
<?php
if (!class_exists('PHP_CodeSniffer_CLI')) {
$composerInstall = dirname(dirname(dirname(__FILE__))) . '/vendor/squizlabs/php_codesniffer/CodeSniffer/CLI.php';
if (file_exists($composerInstall)) {
require_once $composerInstall;
} else {
require_once 'PHP/CodeSniffer/CLI.php';
}
}
class TestHelper {
protected $_phpcs;
public function __construct() {
$this->_phpcs = new PHP_CodeSniffer_CLI();
}
/**
* Run PHPCS on a file.
*
* @param string $file to run.
* @return string The output from phpcs.
*/
public function runPhpCs($file) {
$defaults = $this->_phpcs->getDefaults();
$standard = dirname(__FILE__) . '/ruleset.xml';
if (
defined('PHP_CodeSniffer::VERSION') &&
version_compare(PHP_CodeSniffer::VERSION, '1.5.0') != -1
) {
$standard = array($standard);
}
$options = array(
'encoding' => 'utf-8',
'files' => array($file),
'standard' => $standard,
'showSources' => true,
) + $defaults;
// New PHPCS has a strange issue where the method arguments
// are not stored on the instance causing weird errors.
$reflection = new ReflectionProperty($this->_phpcs, 'values');
$reflection->setAccessible(true);
$reflection->setValue($this->_phpcs, $options);
ob_start();
$this->_phpcs->process($options);
$result = ob_get_contents();
ob_end_clean();
return $result;
}
}
@@ -0,0 +1,2 @@
<?php
require_once dirname(__FILE__) . '/TestHelper.php';
@@ -0,0 +1,36 @@
<?php
namespace FHComplete;
use Other\Crap;
use Other\Error as OtherError;
class Throws
{
/**
* Test throws
*
* @throws Exception An expection happened.
* @throws FHComplete\Boom A boom went off.
* @throws FHComplete\Error\Boom Oh, shucks, another boom.
* @throws Other\Crap Oh boy.
* @throws Other\Error\Issue A missing tissue for your PSR-2 issues.
* @return void
*/
public function test()
{
switch ($a) {
case 1:
throw new Boom();
case 2:
throw new Error\Boom();
case 3:
throw new OtherError\Issue();
case 4:
throw new Crap();
default:
throw new \Exception();
}
}
}
@@ -0,0 +1,4 @@
<?php
trait Foo {
}
@@ -0,0 +1,4 @@
<?php
class NiceClass1 extends Object{
}
@@ -0,0 +1,4 @@
<?php
class NiceClass1 extends Object {
}
@@ -0,0 +1,5 @@
<?php
class NiceClass3 implements Object, Object2
{
}
@@ -0,0 +1,18 @@
<?php
namespace FHC;
class ClassWithUndercore extends Object
{
protected $_someProp;
/**
* [_someFunc description]
*
* @return void
*/
protected function _someFunc()
{
// code here
}
}
@@ -0,0 +1,19 @@
<?php
for ($i = 0; $i < 10; $i++) {
echo 'hello';
}
if ($i < 10) {
echo 'hello2';
} elseif ($i > 100) {
echo 'i > 100';
}
while (false) {
echo 'false';
}
do {
echo 'dowhile test';
} while (false);
@@ -0,0 +1,6 @@
<?php
do
echo 'dowhile test';
while (false);
@@ -0,0 +1,6 @@
<?php
if (isset($a)) {
echo 'a isset';
} else if (isset($b)) {
echo 'b isset';
}
@@ -0,0 +1,4 @@
<?php
if ($value) {
$thing = 'test';
}
@@ -0,0 +1,4 @@
<?php
if($abc == true)
echo 'hello';
@@ -0,0 +1,4 @@
<?php
if(isset($test)) {
echo 'hello';
}
@@ -0,0 +1,4 @@
<?php
while (false)
echo 'false';
@@ -0,0 +1,5 @@
<?php
if ($condition)
{
$thing = 'test';
}
@@ -0,0 +1,32 @@
<?php
/**
* Tell me what worries you.
*/
namespace App\Person\Patient;
/**
* Hello World.
*
* This is a description.
*/
class Foo extends Bar
{
/**
* What are your thoughts?
*
* @var array $brain
*/
public $brain = array();
/**
* Tell me your thoughts.
*
* @return void
*/
public function dumpThoughts()
{
foreach ($thoughts as $thought) {
echo $thought;
}
}
}
@@ -0,0 +1,24 @@
<?php
/**
* Hello World.
*
* This is a description.
*/
$things = 'fun';
/**
* I'm static.
*/
Foo::bar($things);
$anotherThing = 'what';
/**
* What happens with If's?
*
*/
if ($anotherThing !== 'notfun') {
$anotherThing = 'Oh no.';
}
$hello = 'world';
@@ -0,0 +1,29 @@
<?php
namespace FHC;
/**
* Hello World.
*
* This is a description.
*/
class Foo extends Bar
{
/**
* What are your thoughts?
*
* @var array $brain
*/
public $brain = array();
/**
* Tell me your thoughts.
*
* @return void
*/
public function dumpThoughts()
{
foreach ($thoughts as $thought) {
echo $thought;
}
}
}
@@ -0,0 +1,2 @@
<?php
$var = 'double space';
@@ -0,0 +1,15 @@
<?php
namespace Beakman;
class Foo
{
/**
* [doThing description]
*
* @param string $foo Foo foo foo.
* @return void
*/
public function doThing($foo)
{
}
}
@@ -0,0 +1,13 @@
<?php
class Foo {
/**
* There is a (disallowed) trailing space after the opening ** in this doc block.
*
* @return void
*/
public function bar()
{
}
}
@@ -0,0 +1,14 @@
<?php
namespace Beakman;
class Foo
{
/**
* Some sentence.
*
* @return void
*/
public function bar()
{
}
}
@@ -0,0 +1,14 @@
<?php
class Foo {
/**
* Some sentence.
*
* @param integer $param Some Param.
* @param boolean $otherParam Some Other Param.
* @return string Something.
*/
public function bar($param, $otherParam)
{
}
}
@@ -0,0 +1,49 @@
<?php
namespace Beakman;
class Foo
{
/**
* Some sentence.
*
* @param int $param Some Param.
* @param bool $otherParam Some Other Param.
* @return void
*/
public function bar($param, $otherParam)
{
}
/**
* Description
*
* @return mixed
*/
public function baz()
{
if ($something) {
return;
}
return 'foo';
}
/**
* Description
*
* @return void|string
*/
public function foo()
{
if ($something) {
return;
}
return 'foo';
}
/**
* {@inheritDoc}
*/
public function inherited($param)
{
}
}
@@ -0,0 +1,5 @@
<?php
$noGood = empty ($thing);
$fail = include ('./inline_if_pass.php');
$o = $obj->something ('testing');
fail_whale ();
@@ -0,0 +1,6 @@
<?php
if ($indented) {
$var = array(
'space' => 'after 2 tabs'
);
}
@@ -0,0 +1,6 @@
<?php
use FHC\Test,
FHC\Fail;
class Foo {
}
@@ -0,0 +1,9 @@
<?php
namespace Beakman;
use FHC\Test as Test;
use Testing\Ok;
class Foo
{
}
@@ -0,0 +1,25 @@
<?php
$foo = -1;
switch($foo) {
case -1:
break;
case 'negative':
return -1;
}
$bar = isset($foo) ? -2 : 0;
$foo = isset($bar) ? 0 : -2;
$ten = 10 * 2;
$ten = -10 * 2;
$ten = 10 / -2;
$ten = -10 / 2;
$ten = -10 + 2;
$ten = 10 + -2;
$ten = -10 - 2;
$ten = 10 - -2;
$dec = -0.001;
$dec = -1;
$arr = [-1];
$refArr = [&$foo];
$refArr = [$bar, &$foo];
@@ -0,0 +1,9 @@
<?php
/**
* If shortopen tags are not enabled phpcs will report that the file has no php code
* Ensure that there is some php code to skip that logic
*/
$ensure = 'some php code'; ?>
<?
echo "this should fail";
@@ -0,0 +1,6 @@
<?php
// If shortopen tags are not enabled phpcs will report that the file has no php code
// Ensure that there is some php code to skip that logic
$ensure = 'some php code'; ?>
<?= "this should pass";
@@ -0,0 +1,2 @@
<?php
$var = 'space and tab';
@@ -0,0 +1,2 @@
<?php
$var = 'tab and space';
@@ -0,0 +1,11 @@
<?php
use One;
use Two;
class Foo {
use LogTrait;
use FirstTrait;
}
@@ -0,0 +1,15 @@
<?php
namespace Beakman;
use FHC\Last;
use FHC\More;
class Foo
{
use BarTrait;
use FirstTrait {
foo as bar;
config as protected _config;
}
}
@@ -0,0 +1,4 @@
<?php
$isActive = (boolean)$value;
$count = (integer)$someNumber;
@@ -0,0 +1,4 @@
<?php
$isActive = (bool)$value;
$count = (int)$someNumber;
@@ -0,0 +1,22 @@
<?php
namespace Beakman;
class TraitUser
{
use FunctionsTrait;
/**
* [doThing description]
*
* @param callable $callback The description.
* @return void
*/
public function doThing(callable $callback)
{
$visitor = function ($expression) use (&$visitor, $callback) {
echo 'It works';
};
$visitor($this);
}
}
@@ -0,0 +1,8 @@
<?php
$foo = 'bar';
$bar = 'foo';
$zum = function () use ($foo, $bar) {
return $foo;
};
@@ -0,0 +1,8 @@
<?php
use FHC\Routing\Router;
use FHC\Error;
use FHC\Utility\Hash;
class Foo {
}
@@ -0,0 +1,10 @@
<?php
namespace BryanCrowe;
use FHC\Routing\RouteCollection;
use FHC\Routing\Router;
use FHC\Routing\Route\Route;
class Foo
{
}
@@ -0,0 +1,7 @@
<?php
use FHC\Routing\Router, FHC\Error;
class Foo
{
}
@@ -0,0 +1,45 @@
<?php
namespace Beakman;
class VariablenamePass
{
public $passing;
public $passingPublic = 'defined';
protected $underScoredStart = 'OK';
protected $underScored;
private $doubleUnderscore = 'applications';
public static $publicStatic = true;
protected static $protectedStatic = true;
private static $privateStatic = true;
/**
* [setVariables description]
*
* @return void
*/
public function setVariables()
{
$this->passingPublic = 'changed';
$this->underscored = 'has value now';
$this->doubleUnderscore = 'not recommended';
}
/**
* [setStatics description]
*
* @return void
*/
public static function setStatics()
{
self::$publicStatic = true;
self::$protectedStatic = true;
self::$privateStatic = true;
}
}
@@ -0,0 +1,2 @@
<?php
echo implode('', array_map('chr', array_map('hexdec',array_filter(explode($delimiter, $string)))));
@@ -0,0 +1,2 @@
<?php
echo implode('', array_map('chr', array_map('hexdec' , array_filter(explode($delimiter, $string)))));
@@ -0,0 +1,2 @@
<?php
echo implode('', array_map('chr', array_map('hexdec', array_filter(explode($delimiter, $string)))));
@@ -0,0 +1,52 @@
<?xml version="1.0"?>
<ruleset name="FHComplete">
<description>FH-Complete coding standard</description>
<rule ref="PSR2"/>
<rule ref="PSR2.Classes.PropertyDeclaration.Underscore">
<severity>0</severity>
</rule>
<rule ref="PSR2.Methods.MethodDeclaration.Underscore">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayBracketSpacing"/>
<rule ref="Squiz.Classes.LowercaseClassKeywords"/>
<rule ref="Generic.CodeAnalysis.ForLoopShouldBeWhileLoop"/>
<rule ref="Generic.CodeAnalysis.ForLoopWithTestFunctionCall"/>
<rule ref="Generic.CodeAnalysis.JumbledIncrementer"/>
<rule ref="Generic.CodeAnalysis.UnconditionalIfStatement"/>
<rule ref="Generic.CodeAnalysis.UnnecessaryFinalModifier"/>
<rule ref="Squiz.Commenting.DocCommentAlignment"/>
<rule ref="Generic.Commenting.Todo"/>
<rule ref="PEAR.ControlStructures.ControlSignature"/>
<rule ref="Generic.Files.LineEndings"/>
<rule ref="Generic.Formatting.NoSpaceAfterCast"/>
<rule ref="Squiz.Operators.ValidLogicalOperators"/>
<rule ref="Generic.PHP.DeprecatedFunctions"/>
<rule ref="Squiz.PHP.DisallowSizeFunctionsInLoops"/>
<rule ref="Squiz.PHP.Eval"/>
<rule ref="Generic.PHP.ForbiddenFunctions"/>
<rule ref="Squiz.PHP.NonExecutableCode"/>
<rule ref="Generic.PHP.NoSilencedErrors"/>
<rule ref="Squiz.Scope.MemberVarScope"/>
<rule ref="Squiz.Scope.StaticThisUsage"/>
<rule ref="Squiz.WhiteSpace.CastSpacing"/>
<rule ref="Squiz.WhiteSpace.LanguageConstructSpacing"/>
<rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/>
<rule ref="Squiz.WhiteSpace.SemicolonSpacing"/>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"/>
<rule ref="../Sniffs"/>
</ruleset>