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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
<?php
/**
* Generic_Sniffs_Functions_CallTimePassByReferenceSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Florian Grandel <jerico.dev@gmail.com>
* @copyright 2009 Florian Grandel
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Generic_Sniffs_Functions_CallTimePassByReferenceSniff.
*
* Ensures that variables are not passed by reference when calling a function.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Florian Grandel <jerico.dev@gmail.com>
* @copyright 2009 Florian Grandel
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.4
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Generic_Sniffs_Functions_CallTimePassByReferenceSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_STRING);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// Skip tokens that are the names of functions or classes
// within their definitions. For example: function myFunction...
// "myFunction" is T_STRING but we should skip because it is not a
// function or method *call*.
$functionName = $stackPtr;
$findTokens = array_merge(
PHP_CodeSniffer_Tokens::$emptyTokens,
array(T_BITWISE_AND)
);
$functionKeyword = $phpcsFile->findPrevious(
$findTokens,
($stackPtr - 1),
null,
true
);
if ($tokens[$functionKeyword]['code'] === T_FUNCTION
|| $tokens[$functionKeyword]['code'] === T_CLASS
) {
return;
}
// If the next non-whitespace token after the function or method call
// is not an opening parenthesis then it cant really be a *call*.
$openBracket = $phpcsFile->findNext(
PHP_CodeSniffer_Tokens::$emptyTokens,
($functionName + 1),
null,
true
);
if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
return;
}
$closeBracket = $tokens[$openBracket]['parenthesis_closer'];
$nextSeparator = $openBracket;
while (($nextSeparator = $phpcsFile->findNext(T_VARIABLE, ($nextSeparator + 1), $closeBracket)) !== false) {
// Make sure the variable belongs directly to this function call
// and is not inside a nested function call or array.
$brackets = $tokens[$nextSeparator]['nested_parenthesis'];
$lastBracket = array_pop($brackets);
if ($lastBracket !== $closeBracket) {
continue;
}
// Checking this: $value = my_function(...[*]$arg...).
$tokenBefore = $phpcsFile->findPrevious(
PHP_CodeSniffer_Tokens::$emptyTokens,
($nextSeparator - 1),
null,
true
);
if ($tokens[$tokenBefore]['code'] === T_BITWISE_AND) {
// Checking this: $value = my_function(...[*]&$arg...).
$tokenBefore = $phpcsFile->findPrevious(
PHP_CodeSniffer_Tokens::$emptyTokens,
($tokenBefore - 1),
null,
true
);
// We have to exclude all uses of T_BITWISE_AND that are not
// references. We use a blacklist approach as we prefer false
// positives to not identifiying a pass-by-reference call at all.
// The blacklist may not yet be complete.
switch ($tokens[$tokenBefore]['code']) {
case T_VARIABLE:
case T_CLOSE_PARENTHESIS:
// In these cases T_BITWISE_AND represents
// the bitwise and operator.
continue;
break;
default:
// T_BITWISE_AND represents a pass-by-reference.
$error = 'Call-time pass-by-reference calls are prohibited';
$phpcsFile->addError($error, $tokenBefore, 'NotAllowed');
break;
}
}//end if
}//end while
}//end process()
}//end class
?>
|