View source
<?php
namespace DrupalPractice\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
class ScopeInfo {
public $owner;
public $opener;
public $closer;
public $variables = array();
function __construct($currScope) {
$this->owner = $currScope;
}
}
class VariableInfo {
public $name;
public $scopeType;
public $typeHint;
public $passByReference = false;
public $firstDeclared;
public $firstInitialized;
public $firstRead;
public $ignoreUnused = false;
public $lastAssignment;
static $scopeTypeDescriptions = array(
'local' => 'variable',
'param' => 'function parameter',
'static' => 'static variable',
'global' => 'global variable',
'bound' => 'bound variable',
);
function __construct($varName) {
$this->name = $varName;
}
}
class VariableAnalysisSniff implements Sniff {
protected $currentFile = null;
private $_scopes = array();
private $_double_quoted_variable_regexp = '|(?<!\\\\)(?:\\\\{2})*\\${?([a-zA-Z0-9_]+)}?|';
private $_passByRefFunctions = array(
'__soapCall' => array(
5,
),
'addFunction' => array(
3,
),
'addTask' => array(
3,
),
'addTaskBackground' => array(
3,
),
'addTaskHigh' => array(
3,
),
'addTaskHighBackground' => array(
3,
),
'addTaskLow' => array(
3,
),
'addTaskLowBackground' => array(
3,
),
'addTaskStatus' => array(
2,
),
'apc_dec' => array(
3,
),
'apc_fetch' => array(
2,
),
'apc_inc' => array(
3,
),
'areConfusable' => array(
3,
),
'array_multisort' => array(
1,
),
'array_pop' => array(
1,
),
'array_push' => array(
1,
),
'array_replace' => array(
1,
),
'array_replace_recursive' => array(
1,
2,
3,
'...',
),
'array_shift' => array(
1,
),
'array_splice' => array(
1,
),
'array_unshift' => array(
1,
),
'array_walk' => array(
1,
),
'array_walk_recursive' => array(
1,
),
'arsort' => array(
1,
),
'asort' => array(
1,
),
'asort' => array(
1,
),
'bindColumn' => array(
2,
),
'bindParam' => array(
2,
),
'bind_param' => array(
2,
3,
'...',
),
'bind_result' => array(
1,
2,
'...',
),
'call_user_method' => array(
2,
),
'call_user_method_array' => array(
2,
),
'curl_multi_exec' => array(
2,
),
'curl_multi_info_read' => array(
2,
),
'current' => array(
1,
),
'dbplus_curr' => array(
2,
),
'dbplus_first' => array(
2,
),
'dbplus_info' => array(
3,
),
'dbplus_last' => array(
2,
),
'dbplus_next' => array(
2,
),
'dbplus_prev' => array(
2,
),
'dbplus_tremove' => array(
3,
),
'dns_get_record' => array(
3,
4,
),
'domxml_open_file' => array(
3,
),
'domxml_open_mem' => array(
3,
),
'each' => array(
1,
),
'enchant_dict_quick_check' => array(
3,
),
'end' => array(
1,
),
'ereg' => array(
3,
),
'eregi' => array(
3,
),
'exec' => array(
2,
3,
),
'exif_thumbnail' => array(
1,
2,
3,
),
'expect_expectl' => array(
3,
),
'extract' => array(
1,
),
'filter' => array(
3,
),
'flock' => array(
2,
3,
),
'fscanf' => array(
2,
3,
'...',
),
'fsockopen' => array(
3,
4,
),
'ftp_alloc' => array(
3,
),
'get' => array(
2,
3,
),
'getByKey' => array(
4,
),
'getMulti' => array(
2,
),
'getMultiByKey' => array(
3,
),
'getimagesize' => array(
2,
),
'getmxrr' => array(
2,
3,
),
'gnupg_decryptverify' => array(
3,
),
'gnupg_verify' => array(
4,
),
'grapheme_extract' => array(
5,
),
'headers_sent' => array(
1,
2,
),
'http_build_url' => array(
4,
),
'http_get' => array(
3,
),
'http_head' => array(
3,
),
'http_negotiate_charset' => array(
2,
),
'http_negotiate_content_type' => array(
2,
),
'http_negotiate_language' => array(
2,
),
'http_post_data' => array(
4,
),
'http_post_fields' => array(
5,
),
'http_put_data' => array(
4,
),
'http_put_file' => array(
4,
),
'http_put_stream' => array(
4,
),
'http_request' => array(
5,
),
'isSuspicious' => array(
2,
),
'is_callable' => array(
3,
),
'key' => array(
1,
),
'krsort' => array(
1,
),
'ksort' => array(
1,
),
'ldap_get_option' => array(
3,
),
'ldap_parse_reference' => array(
3,
),
'ldap_parse_result' => array(
3,
4,
5,
6,
),
'localtime' => array(
2,
),
'm_completeauthorizations' => array(
2,
),
'maxdb_stmt_bind_param' => array(
3,
4,
'...',
),
'maxdb_stmt_bind_result' => array(
2,
3,
'...',
),
'mb_convert_variables' => array(
3,
4,
'...',
),
'mb_parse_str' => array(
2,
),
'mqseries_back' => array(
2,
3,
),
'mqseries_begin' => array(
3,
4,
),
'mqseries_close' => array(
4,
5,
),
'mqseries_cmit' => array(
2,
3,
),
'mqseries_conn' => array(
2,
3,
4,
),
'mqseries_connx' => array(
2,
3,
4,
5,
),
'mqseries_disc' => array(
2,
3,
),
'mqseries_get' => array(
3,
4,
5,
6,
7,
8,
9,
),
'mqseries_inq' => array(
6,
8,
9,
10,
),
'mqseries_open' => array(
2,
4,
5,
6,
),
'mqseries_put' => array(
3,
4,
6,
7,
),
'mqseries_put1' => array(
2,
3,
4,
6,
7,
),
'mqseries_set' => array(
9,
10,
),
'msg_receive' => array(
3,
5,
8,
),
'msg_send' => array(
6,
),
'mssql_bind' => array(
3,
),
'natcasesort' => array(
1,
),
'natsort' => array(
1,
),
'ncurses_color_content' => array(
2,
3,
4,
),
'ncurses_getmaxyx' => array(
2,
3,
),
'ncurses_getmouse' => array(
1,
),
'ncurses_getyx' => array(
2,
3,
),
'ncurses_instr' => array(
1,
),
'ncurses_mouse_trafo' => array(
1,
2,
),
'ncurses_mousemask' => array(
2,
),
'ncurses_pair_content' => array(
2,
3,
),
'ncurses_wmouse_trafo' => array(
2,
3,
),
'newt_button_bar' => array(
1,
),
'newt_form_run' => array(
2,
),
'newt_get_screen_size' => array(
1,
2,
),
'newt_grid_get_size' => array(
2,
3,
),
'newt_reflow_text' => array(
5,
6,
),
'newt_win_entries' => array(
7,
),
'newt_win_menu' => array(
8,
),
'next' => array(
1,
),
'oci_bind_array_by_name' => array(
3,
),
'oci_bind_by_name' => array(
3,
),
'oci_define_by_name' => array(
3,
),
'oci_fetch_all' => array(
2,
),
'ocifetchinto' => array(
2,
),
'odbc_fetch_into' => array(
2,
),
'openssl_csr_export' => array(
2,
),
'openssl_csr_new' => array(
2,
),
'openssl_open' => array(
2,
),
'openssl_pkcs12_export' => array(
2,
),
'openssl_pkcs12_read' => array(
2,
),
'openssl_pkey_export' => array(
2,
),
'openssl_private_decrypt' => array(
2,
),
'openssl_private_encrypt' => array(
2,
),
'openssl_public_decrypt' => array(
2,
),
'openssl_public_encrypt' => array(
2,
),
'openssl_random_pseudo_bytes' => array(
2,
),
'openssl_seal' => array(
2,
3,
),
'openssl_sign' => array(
2,
),
'openssl_x509_export' => array(
2,
),
'ovrimos_fetch_into' => array(
2,
),
'parse' => array(
2,
3,
),
'parseCurrency' => array(
2,
3,
),
'parse_str' => array(
2,
),
'parsekit_compile_file' => array(
2,
),
'parsekit_compile_string' => array(
2,
),
'passthru' => array(
2,
),
'pcntl_sigprocmask' => array(
3,
),
'pcntl_sigtimedwait' => array(
2,
),
'pcntl_sigwaitinfo' => array(
2,
),
'pcntl_wait' => array(
1,
),
'pcntl_waitpid' => array(
2,
),
'pfsockopen' => array(
3,
4,
),
'php_check_syntax' => array(
2,
),
'poll' => array(
1,
2,
3,
),
'preg_filter' => array(
5,
),
'preg_match' => array(
3,
),
'preg_match_all' => array(
3,
),
'preg_replace' => array(
5,
),
'preg_replace_callback' => array(
5,
),
'prev' => array(
1,
),
'proc_open' => array(
3,
),
'query' => array(
3,
),
'queryExec' => array(
2,
),
'reset' => array(
1,
),
'rsort' => array(
1,
),
'settype' => array(
1,
),
'shuffle' => array(
1,
),
'similar_text' => array(
3,
),
'socket_create_pair' => array(
4,
),
'socket_getpeername' => array(
2,
3,
),
'socket_getsockname' => array(
2,
3,
),
'socket_recv' => array(
2,
),
'socket_recvfrom' => array(
2,
5,
6,
),
'socket_select' => array(
1,
2,
3,
),
'sort' => array(
1,
),
'sortWithSortKeys' => array(
1,
),
'sqlite_exec' => array(
3,
),
'sqlite_factory' => array(
3,
),
'sqlite_open' => array(
3,
),
'sqlite_popen' => array(
3,
),
'sqlite_query' => array(
4,
),
'sqlite_query' => array(
4,
),
'sqlite_unbuffered_query' => array(
4,
),
'sscanf' => array(
3,
'...',
),
'str_ireplace' => array(
4,
),
'str_replace' => array(
4,
),
'stream_open' => array(
4,
),
'stream_select' => array(
1,
2,
3,
),
'stream_socket_accept' => array(
3,
),
'stream_socket_client' => array(
2,
3,
),
'stream_socket_recvfrom' => array(
4,
),
'stream_socket_server' => array(
2,
3,
),
'system' => array(
2,
),
'uasort' => array(
1,
),
'uksort' => array(
1,
),
'unbufferedQuery' => array(
3,
),
'usort' => array(
1,
),
'wincache_ucache_dec' => array(
3,
),
'wincache_ucache_get' => array(
2,
),
'wincache_ucache_inc' => array(
3,
),
'xdiff_string_merge3' => array(
4,
),
'xdiff_string_patch' => array(
4,
),
'xml_parse_into_struct' => array(
3,
4,
),
'xml_set_object' => array(
2,
),
'xmlrpc_decode_request' => array(
2,
),
'xmlrpc_set_type' => array(
1,
),
'xslt_set_object' => array(
2,
),
'yaml_parse' => array(
3,
),
'yaml_parse_file' => array(
3,
),
'yaml_parse_url' => array(
3,
),
'yaz_ccl_parse' => array(
3,
),
'yaz_hits' => array(
2,
),
'yaz_scan_result' => array(
2,
),
'yaz_wait' => array(
1,
),
);
public $sitePassByRefFunctions = null;
public $allowUnusedCaughtExceptions = true;
public $allowUnusedFunctionParameters = true;
public $validUnusedVariableNames = null;
public function register() {
if (empty($this->sitePassByRefFunctions) === false) {
foreach (preg_split('/\\s+/', trim($this->sitePassByRefFunctions)) as $line) {
list($function, $args) = explode(':', $line);
$this->_passByRefFunctions[$function] = explode(',', $args);
}
}
if (empty($this->validUnusedVariableNames) === false) {
$this->validUnusedVariableNames = preg_split('/\\s+/', trim($this->validUnusedVariableNames));
}
return array(
T_VARIABLE,
T_DOUBLE_QUOTED_STRING,
T_HEREDOC,
T_CLOSE_CURLY_BRACKET,
T_STRING,
);
}
public function process(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile
->getTokens();
$token = $tokens[$stackPtr];
if ($this->currentFile !== $phpcsFile) {
$this->currentFile = $phpcsFile;
}
if ($token['code'] === T_VARIABLE) {
return $this
->processVariable($phpcsFile, $stackPtr);
}
if ($token['code'] === T_DOUBLE_QUOTED_STRING || $token['code'] === T_HEREDOC) {
return $this
->processVariableInString($phpcsFile, $stackPtr);
}
if ($token['code'] === T_STRING && $token['content'] === 'compact') {
return $this
->processCompact($phpcsFile, $stackPtr);
}
if ($token['code'] === T_CLOSE_CURLY_BRACKET && isset($token['scope_condition']) === true) {
return $this
->processScopeClose($phpcsFile, $token['scope_condition']);
}
}
function normalizeVarName($varName) {
$varName = preg_replace('/[{}$]/', '', $varName);
return $varName;
}
function scopeKey($currScope) {
if ($currScope === false) {
$currScope = 'file';
}
if (isset($this->currentFile) === true) {
return $this->currentFile
->getFilename() . ':' . $currScope;
}
else {
return 'unknown file' . ':' . $currScope;
}
}
function getScopeInfo($currScope, $autoCreate = true) {
$scopeKey = $this
->scopeKey($currScope);
if (isset($this->_scopes[$scopeKey]) === false) {
if ($autoCreate === false) {
return null;
}
$this->_scopes[$scopeKey] = new ScopeInfo($currScope);
}
return $this->_scopes[$scopeKey];
}
function getVariableInfo($varName, $currScope, $autoCreate = true) {
$scopeInfo = $this
->getScopeInfo($currScope, $autoCreate);
if (isset($scopeInfo->variables[$varName]) === false) {
if ($autoCreate === false) {
return null;
}
$scopeInfo->variables[$varName] = new VariableInfo($varName);
if (is_array($this->validUnusedVariableNames) === true && in_array($varName, $this->validUnusedVariableNames) === true) {
$scopeInfo->variables[$varName]->ignoreUnused = true;
}
}
return $scopeInfo->variables[$varName];
}
function markVariableAssignment($varName, $stackPtr, $currScope) {
$varInfo = $this
->getVariableInfo($varName, $currScope);
if (isset($varInfo->scopeType) === false) {
$varInfo->scopeType = 'local';
}
if (isset($varInfo->firstInitialized) === true && $varInfo->firstInitialized <= $stackPtr) {
$varInfo->lastAssignment = $stackPtr;
return;
}
$varInfo->firstInitialized = $stackPtr;
}
function markVariableDeclaration($varName, $scopeType, $typeHint, $stackPtr, $currScope, $permitMatchingRedeclaration = false) {
$varInfo = $this
->getVariableInfo($varName, $currScope);
if (isset($varInfo->scopeType) === true) {
if ($permitMatchingRedeclaration === false || $varInfo->scopeType !== $scopeType) {
$this->currentFile
->addWarning("Redeclaration of %s %s as %s.", $stackPtr, 'VariableRedeclaration', array(
VariableInfo::$scopeTypeDescriptions[$varInfo->scopeType],
"\${$varName}",
VariableInfo::$scopeTypeDescriptions[$scopeType],
));
}
}
$varInfo->scopeType = $scopeType;
if (isset($typeHint) === true) {
$varInfo->typeHint = $typeHint;
}
if (isset($varInfo->firstDeclared) === true && $varInfo->firstDeclared <= $stackPtr) {
return;
}
if ($scopeType === 'global') {
$varInfo->firstInitialized = $stackPtr;
}
$varInfo->firstDeclared = $stackPtr;
}
function markVariableRead($varName, $stackPtr, $currScope) {
$varInfo = $this
->getVariableInfo($varName, $currScope);
if (isset($varInfo->firstRead) === true && $varInfo->firstRead <= $stackPtr) {
return;
}
$varInfo->firstRead = $stackPtr;
}
function isVariableInitialized($varName, $stackPtr, $currScope) {
$varInfo = $this
->getVariableInfo($varName, $currScope);
if (isset($varInfo->firstInitialized) === true && $varInfo->firstInitialized <= $stackPtr) {
return true;
}
return false;
}
function isVariableUndefined($varName, $stackPtr, $currScope) {
$varInfo = $this
->getVariableInfo($varName, $currScope, false);
if (isset($varInfo->firstDeclared) === true && $varInfo->firstDeclared <= $stackPtr) {
return false;
}
if (isset($varInfo->firstInitialized) === true && $varInfo->firstInitialized <= $stackPtr) {
return false;
}
return true;
}
function markVariableReadAndWarnIfUndefined(File $phpcsFile, $varName, $stackPtr, $currScope) {
$this
->markVariableRead($varName, $stackPtr, $currScope);
if ($this
->isVariableUndefined($varName, $stackPtr, $currScope) === true) {
$phpcsFile
->addWarning("Variable %s is undefined.", $stackPtr, 'UndefinedVariable', array(
"\${$varName}",
));
}
return true;
}
function findFunctionPrototype(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile
->getTokens();
if (($openPtr = $this
->findContainingBrackets($phpcsFile, $stackPtr)) === false) {
return false;
}
$functionPtr = $phpcsFile
->findPrevious(array(
T_STRING,
T_WHITESPACE,
T_BITWISE_AND,
), $openPtr - 1, null, true, null, true);
if ($functionPtr !== false && $tokens[$functionPtr]['code'] === T_FUNCTION) {
return $functionPtr;
}
return false;
}
function findVariableScope(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile
->getTokens();
$token = $tokens[$stackPtr];
$in_class = false;
if (empty($token['conditions']) === false) {
foreach (array_reverse($token['conditions'], true) as $scopePtr => $scopeCode) {
if ($scopeCode === T_FUNCTION || $scopeCode === T_CLOSURE) {
return $scopePtr;
}
if ($scopeCode === T_CLASS || $scopeCode === T_INTERFACE || $scopeCode === T_TRAIT) {
$in_class = true;
}
}
}
if (($scopePtr = $this
->findFunctionPrototype($phpcsFile, $stackPtr)) !== false) {
return $scopePtr;
}
if ($in_class === true) {
return false;
}
return 0;
}
function isNextThingAnAssign(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile
->getTokens();
$nextPtr = $phpcsFile
->findNext(T_WHITESPACE, $stackPtr + 1, null, true, null, true);
if ($nextPtr !== false) {
if ($tokens[$nextPtr]['code'] === T_EQUAL) {
return $nextPtr;
}
if ($tokens[$nextPtr]['code'] === T_OPEN_SQUARE_BRACKET) {
return $this
->isNextThingAnAssign($phpcsFile, $tokens[$nextPtr]['bracket_closer']);
}
}
return false;
}
function findWhereAssignExecuted(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile
->getTokens();
$semicolonPtr = $phpcsFile
->findNext(T_SEMICOLON, $stackPtr + 1, null, false, null, true);
$closePtr = false;
if (($openPtr = $this
->findContainingBrackets($phpcsFile, $stackPtr)) !== false) {
if (isset($tokens[$openPtr]['parenthesis_closer']) === true) {
$closePtr = $tokens[$openPtr]['parenthesis_closer'];
}
}
if ($semicolonPtr === false) {
if ($closePtr === false) {
return $stackPtr;
}
return $closePtr;
}
if ($closePtr !== false && $closePtr < $semicolonPtr) {
return $closePtr;
}
return $semicolonPtr;
}
function findContainingBrackets(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile
->getTokens();
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
$openPtrs = array_keys($tokens[$stackPtr]['nested_parenthesis']);
return end($openPtrs);
}
return false;
}
function findFunctionCall(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile
->getTokens();
if (($openPtr = $this
->findContainingBrackets($phpcsFile, $stackPtr)) !== false) {
$functionPtr = $phpcsFile
->findPrevious(T_WHITESPACE, $openPtr - 1, null, true, null, true);
if ($tokens[$functionPtr]['code'] === T_STRING) {
return $functionPtr;
}
}
return false;
}
function findFunctionCallArguments(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile
->getTokens();
if ($tokens[$stackPtr]['code'] !== T_STRING && $tokens[$stackPtr]['code'] !== T_ARRAY) {
if (($stackPtr = $this
->findFunctionCall($phpcsFile, $stackPtr)) === false) {
return false;
}
}
$openPtr = $phpcsFile
->findNext(T_WHITESPACE, $stackPtr + 1, null, true, null, true);
if ($openPtr === false || $tokens[$openPtr]['code'] !== T_OPEN_PARENTHESIS) {
return false;
}
if (isset($tokens[$openPtr]['parenthesis_closer']) === false) {
return false;
}
$closePtr = $tokens[$openPtr]['parenthesis_closer'];
$argPtrs = array();
$lastPtr = $openPtr;
$lastArgComma = $openPtr;
while (($nextPtr = $phpcsFile
->findNext(T_COMMA, $lastPtr + 1, $closePtr)) !== false) {
if ($this
->findContainingBrackets($phpcsFile, $nextPtr) === $openPtr) {
array_push($argPtrs, range($lastArgComma + 1, $nextPtr - 1));
$lastArgComma = $nextPtr;
}
$lastPtr = $nextPtr;
}
array_push($argPtrs, range($lastArgComma + 1, $closePtr - 1));
return $argPtrs;
}
protected function checkForFunctionPrototype(File $phpcsFile, $stackPtr, $varName, $currScope) {
$tokens = $phpcsFile
->getTokens();
if (($openPtr = $this
->findContainingBrackets($phpcsFile, $stackPtr)) === false) {
return false;
}
$functionPtr = $phpcsFile
->findPrevious(array(
T_STRING,
T_WHITESPACE,
T_BITWISE_AND,
), $openPtr - 1, null, true, null, true);
if ($functionPtr !== false && ($tokens[$functionPtr]['code'] === T_FUNCTION || $tokens[$functionPtr]['code'] === T_CLOSURE)) {
$this
->markVariableDeclaration($varName, 'param', null, $stackPtr, $functionPtr);
$referencePtr = $phpcsFile
->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true, null, true);
if ($referencePtr !== false && $tokens[$referencePtr]['code'] === T_BITWISE_AND) {
$varInfo = $this
->getVariableInfo($varName, $functionPtr);
$varInfo->passByReference = true;
}
if ($this
->isNextThingAnAssign($phpcsFile, $stackPtr) !== false) {
$this
->markVariableAssignment($varName, $stackPtr, $functionPtr);
}
return true;
}
if ($functionPtr !== false && $tokens[$functionPtr]['code'] === T_USE) {
$this
->markVariableRead($varName, $stackPtr, $currScope);
if ($this
->isVariableUndefined($varName, $stackPtr, $currScope) === true) {
$phpcsFile
->addWarning("Variable %s is undefined.", $stackPtr, 'UndefinedVariable', array(
"\${$varName}",
));
return true;
}
$functionPtr = $phpcsFile
->findPrevious(T_CLOSURE, $functionPtr - 1, $currScope + 1, false, null, true);
if ($functionPtr !== false) {
$this
->markVariableDeclaration($varName, 'bound', null, $stackPtr, $functionPtr);
$this
->markVariableAssignment($varName, $stackPtr, $functionPtr);
$referencePtr = $phpcsFile
->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true, null, true);
if ($referencePtr !== false && $tokens[$referencePtr]['code'] === T_BITWISE_AND) {
$varInfo = $this
->getVariableInfo($varName, $functionPtr);
$varInfo->passByReference = true;
}
return true;
}
}
return false;
}
protected function checkForCatchBlock(File $phpcsFile, $stackPtr, $varName, $currScope) {
$tokens = $phpcsFile
->getTokens();
if (($openPtr = $this
->findContainingBrackets($phpcsFile, $stackPtr)) === false) {
return false;
}
$catchPtr = $phpcsFile
->findPrevious(T_WHITESPACE, $openPtr - 1, null, true, null, true);
if ($catchPtr !== false && $tokens[$catchPtr]['code'] === T_CATCH) {
$this
->markVariableDeclaration($varName, 'local', null, $stackPtr, $currScope, true);
$this
->markVariableAssignment($varName, $stackPtr, $currScope);
if ($this->allowUnusedCaughtExceptions !== false) {
$varInfo = $this
->getVariableInfo($varName, $currScope);
$varInfo->ignoreUnused = true;
}
return true;
}
return false;
}
protected function checkForThisWithinClass(File $phpcsFile, $stackPtr, $varName, $currScope) {
$tokens = $phpcsFile
->getTokens();
$token = $tokens[$stackPtr];
if ($varName !== 'this' || empty($token['conditions']) === true) {
return false;
}
foreach (array_reverse($token['conditions'], true) as $scopePtr => $scopeCode) {
if ($scopeCode === T_CLASS || $scopeCode === T_TRAIT) {
return true;
}
}
return false;
}
protected function checkForSuperGlobal(File $phpcsFile, $stackPtr, $varName, $currScope) {
$tokens = $phpcsFile
->getTokens();
$token = $tokens[$stackPtr];
if (in_array($varName, array(
'GLOBALS',
'_SERVER',
'_GET',
'_POST',
'_FILES',
'_COOKIE',
'_SESSION',
'_REQUEST',
'_ENV',
'argv',
'argc',
)) === true) {
return true;
}
return false;
}
protected function checkForStaticMember(File $phpcsFile, $stackPtr, $varName, $currScope) {
$tokens = $phpcsFile
->getTokens();
$token = $tokens[$stackPtr];
$doubleColonPtr = $stackPtr - 1;
if ($tokens[$doubleColonPtr]['code'] !== T_DOUBLE_COLON) {
return false;
}
$classNamePtr = $stackPtr - 2;
if ($tokens[$classNamePtr]['code'] !== T_STRING && $tokens[$classNamePtr]['code'] !== T_SELF && $tokens[$classNamePtr]['code'] !== T_STATIC) {
return false;
}
if ($tokens[$classNamePtr]['code'] === T_SELF || $tokens[$classNamePtr]['code'] === T_STATIC) {
if ($tokens[$classNamePtr]['code'] === T_SELF) {
$err_class = 'SelfOutsideClass';
$err_desc = 'self::';
}
else {
$err_class = 'StaticOutsideClass';
$err_desc = 'static::';
}
if (empty($token['conditions']) === false) {
foreach (array_reverse($token['conditions'], true) as $scopePtr => $scopeCode) {
if ($tokens[$scopePtr]['code'] === T_CLOSURE) {
$phpcsFile
->addError("Use of {$err_desc}%s inside closure.", $stackPtr, $err_class, array(
"\${$varName}",
));
return true;
}
if ($scopeCode === T_CLASS || $scopeCode === T_TRAIT) {
return true;
}
}
}
$phpcsFile
->addError("Use of {$err_desc}%s outside class definition.", $stackPtr, $err_class, array(
"\${$varName}",
));
return true;
}
return true;
}
protected function checkForAssignment(File $phpcsFile, $stackPtr, $varName, $currScope) {
$tokens = $phpcsFile
->getTokens();
if (($assignPtr = $this
->isNextThingAnAssign($phpcsFile, $stackPtr)) === false) {
return false;
}
if (($writtenPtr = $this
->findWhereAssignExecuted($phpcsFile, $assignPtr)) === false) {
$writtenPtr = $stackPtr;
}
$refPtr = $phpcsFile
->findNext(T_WHITESPACE, $assignPtr + 1, null, true);
if ($tokens[$refPtr]['code'] === T_BITWISE_AND) {
$varInfo = $this
->getVariableInfo($varName, $currScope);
$varInfo->passByReference = true;
}
$this
->markVariableAssignment($varName, $writtenPtr, $currScope);
return true;
}
protected function checkForListAssignment(File $phpcsFile, $stackPtr, $varName, $currScope) {
$tokens = $phpcsFile
->getTokens();
if (($openPtr = $this
->findContainingBrackets($phpcsFile, $stackPtr)) === false) {
return false;
}
$prevPtr = $phpcsFile
->findPrevious(T_WHITESPACE, $openPtr - 1, null, true, null, true);
if ($prevPtr === false || $tokens[$prevPtr]['code'] !== T_LIST) {
return false;
}
$closePtr = $tokens[$openPtr]['parenthesis_closer'];
if (($assignPtr = $this
->isNextThingAnAssign($phpcsFile, $closePtr)) === false) {
return false;
}
$writtenPtr = $this
->findWhereAssignExecuted($phpcsFile, $assignPtr);
$this
->markVariableAssignment($varName, $writtenPtr, $currScope);
return true;
}
protected function checkForGlobalDeclaration(File $phpcsFile, $stackPtr, $varName, $currScope) {
$tokens = $phpcsFile
->getTokens();
$globalPtr = $phpcsFile
->findPrevious(array(
T_WHITESPACE,
T_VARIABLE,
T_COMMA,
), $stackPtr - 1, null, true, null, true);
if ($globalPtr === false || $tokens[$globalPtr]['code'] !== T_GLOBAL) {
return false;
}
$this
->markVariableDeclaration($varName, 'global', null, $stackPtr, $currScope);
$varInfo = $this
->getVariableInfo($varName, $currScope);
$varInfo->passByReference = true;
return true;
}
protected function checkForStaticDeclaration(File $phpcsFile, $stackPtr, $varName, $currScope) {
$tokens = $phpcsFile
->getTokens();
$staticPtr = $phpcsFile
->findPrevious(array(
T_WHITESPACE,
T_VARIABLE,
T_COMMA,
T_EQUAL,
T_MINUS,
T_LNUMBER,
T_DNUMBER,
T_CONSTANT_ENCAPSED_STRING,
T_STRING,
T_DOUBLE_COLON,
T_START_HEREDOC,
T_HEREDOC,
T_END_HEREDOC,
T_START_NOWDOC,
T_NOWDOC,
T_END_NOWDOC,
), $stackPtr - 1, null, true, null, true);
if ($staticPtr === false || $tokens[$staticPtr]['code'] !== T_STATIC) {
return false;
}
$lateStaticBindingPtr = $phpcsFile
->findNext(T_WHITESPACE, $staticPtr + 1, null, true, null, true);
if ($lateStaticBindingPtr !== false && $tokens[$lateStaticBindingPtr]['code'] === T_DOUBLE_COLON) {
return false;
}
$this
->markVariableDeclaration($varName, 'static', null, $stackPtr, $currScope);
if ($this
->isNextThingAnAssign($phpcsFile, $stackPtr) !== false) {
$this
->markVariableAssignment($varName, $stackPtr, $currScope);
}
return true;
}
protected function checkForForeachLoopVar(File $phpcsFile, $stackPtr, $varName, $currScope) {
$tokens = $phpcsFile
->getTokens();
if (($openPtr = $this
->findContainingBrackets($phpcsFile, $stackPtr)) === false) {
return false;
}
if ($phpcsFile
->findPrevious(T_AS, $stackPtr - 1, $openPtr) === false) {
return false;
}
$this
->markVariableAssignment($varName, $stackPtr, $currScope);
if ($phpcsFile
->findPrevious(T_DOUBLE_ARROW, $stackPtr - 1, $openPtr) !== false) {
$this
->markVariableRead($varName, $stackPtr, $currScope);
}
if (($refPtr = $phpcsFile
->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true)) !== false && $tokens[$refPtr]['code'] === T_BITWISE_AND) {
$varInfo = $this
->getVariableInfo($varName, $currScope);
$varInfo->passByReference = true;
}
return true;
}
protected function checkForPassByReferenceFunctionCall(File $phpcsFile, $stackPtr, $varName, $currScope) {
$tokens = $phpcsFile
->getTokens();
$token = $tokens[$stackPtr];
if (($functionPtr = $this
->findFunctionCall($phpcsFile, $stackPtr)) === false) {
return false;
}
$functionName = $tokens[$functionPtr]['content'];
if (isset($this->_passByRefFunctions[$functionName]) === false) {
return false;
}
$refArgs = $this->_passByRefFunctions[$functionName];
if (($argPtrs = $this
->findFunctionCallArguments($phpcsFile, $stackPtr)) === false) {
return false;
}
$argPos = false;
foreach ($argPtrs as $idx => $ptrs) {
if (in_array($stackPtr, $ptrs) === true) {
$argPos = $idx + 1;
break;
}
}
if ($argPos === false) {
return false;
}
if (in_array($argPos, $refArgs) === false) {
if (($elipsis = array_search('...', $refArgs)) === false) {
return false;
}
if ($argPos < $refArgs[$elipsis - 1]) {
return false;
}
}
foreach ($argPtrs[$argPos - 1] as $ptr) {
if ($ptr === $stackPtr) {
continue;
}
if ($tokens[$ptr]['code'] !== T_WHITESPACE) {
return false;
}
}
$this
->markVariableAssignment($varName, $stackPtr, $currScope);
$this
->markVariableRead($varName, $stackPtr, $currScope);
return true;
}
protected function checkForSymbolicObjectProperty(File $phpcsFile, $stackPtr, $varName, $currScope) {
$tokens = $phpcsFile
->getTokens();
$token = $tokens[$stackPtr];
$objectOperatorPtr = $phpcsFile
->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true, null, true);
if ($objectOperatorPtr === false || $tokens[$objectOperatorPtr]['code'] !== T_OBJECT_OPERATOR) {
return false;
}
$this
->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $stackPtr, $currScope);
return true;
}
protected function processMemberVar(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile
->getTokens();
$token = $tokens[$stackPtr];
}
protected function processVariable(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile
->getTokens();
$token = $tokens[$stackPtr];
$varName = $this
->normalizeVarName($token['content']);
if (($currScope = $this
->findVariableScope($phpcsFile, $stackPtr)) === false) {
return;
}
if ($this
->checkForSymbolicObjectProperty($phpcsFile, $stackPtr, $varName, $currScope) === true) {
return;
}
if ($this
->checkForFunctionPrototype($phpcsFile, $stackPtr, $varName, $currScope) === true) {
return;
}
if ($this
->checkForCatchBlock($phpcsFile, $stackPtr, $varName, $currScope) === true) {
return;
}
if ($this
->checkForThisWithinClass($phpcsFile, $stackPtr, $varName, $currScope) === true) {
return;
}
if ($this
->checkForSuperGlobal($phpcsFile, $stackPtr, $varName, $currScope) === true) {
return;
}
if ($this
->checkForStaticMember($phpcsFile, $stackPtr, $varName, $currScope) === true) {
return;
}
if ($this
->checkForAssignment($phpcsFile, $stackPtr, $varName, $currScope) === true) {
return;
}
if ($this
->checkForListAssignment($phpcsFile, $stackPtr, $varName, $currScope) === true) {
return;
}
if ($this
->checkForGlobalDeclaration($phpcsFile, $stackPtr, $varName, $currScope) === true) {
return;
}
if ($this
->checkForStaticDeclaration($phpcsFile, $stackPtr, $varName, $currScope) === true) {
return;
}
if ($this
->checkForForeachLoopVar($phpcsFile, $stackPtr, $varName, $currScope) === true) {
return;
}
if ($this
->checkForPassByReferenceFunctionCall($phpcsFile, $stackPtr, $varName, $currScope) === true) {
return;
}
$this
->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $stackPtr, $currScope);
}
protected function processVariableInString(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile
->getTokens();
$token = $tokens[$stackPtr];
$runMatch = preg_match_all($this->_double_quoted_variable_regexp, $token['content'], $matches);
if ($runMatch === 0 || $runMatch === false) {
return;
}
$currScope = $this
->findVariableScope($phpcsFile, $stackPtr);
foreach ($matches[1] as $varName) {
$varName = $this
->normalizeVarName($varName);
if ($this
->checkForThisWithinClass($phpcsFile, $stackPtr, $varName, $currScope) === true) {
continue;
}
if ($this
->checkForSuperGlobal($phpcsFile, $stackPtr, $varName, $currScope) === true) {
continue;
}
$this
->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $stackPtr, $currScope);
}
}
protected function processCompactArguments(File $phpcsFile, $stackPtr, $arguments, $currScope) {
$tokens = $phpcsFile
->getTokens();
foreach ($arguments as $argumentPtrs) {
$argumentPtrs = array_values(array_filter($argumentPtrs, function ($argumentPtr) use ($tokens) {
return $tokens[$argumentPtr]['code'] !== T_WHITESPACE;
}));
if (empty($argumentPtrs) === true) {
continue;
}
if (isset($tokens[$argumentPtrs[0]]) === false) {
continue;
}
$argument_first_token = $tokens[$argumentPtrs[0]];
if ($argument_first_token['code'] === T_ARRAY) {
if (($array_arguments = $this
->findFunctionCallArguments($phpcsFile, $argumentPtrs[0])) !== false) {
$this
->processCompactArguments($phpcsFile, $stackPtr, $array_arguments, $currScope);
}
continue;
}
if (count($argumentPtrs) > 1) {
continue;
}
if ($argument_first_token['code'] === T_CONSTANT_ENCAPSED_STRING) {
$varName = substr($argument_first_token['content'], 1, -1);
$this
->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $argumentPtrs[0], $currScope);
continue;
}
if ($argument_first_token['code'] === T_DOUBLE_QUOTED_STRING) {
if (preg_match($this->_double_quoted_variable_regexp, $argument_first_token['content']) === 1) {
continue;
}
$varName = substr($argument_first_token['content'], 1, -1);
$this
->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $argumentPtrs[0], $currScope);
continue;
}
}
}
protected function processCompact(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile
->getTokens();
$token = $tokens[$stackPtr];
$currScope = $this
->findVariableScope($phpcsFile, $stackPtr);
if (($arguments = $this
->findFunctionCallArguments($phpcsFile, $stackPtr)) !== false) {
$this
->processCompactArguments($phpcsFile, $stackPtr, $arguments, $currScope);
}
}
protected function processScopeClose(File $phpcsFile, $stackPtr) {
$scopeInfo = $this
->getScopeInfo($stackPtr, false);
if (is_null($scopeInfo) === true) {
return;
}
foreach ($scopeInfo->variables as $varInfo) {
if ($varInfo->ignoreUnused === true || isset($varInfo->firstRead) === true) {
continue;
}
if ($this->allowUnusedFunctionParameters === true && $varInfo->scopeType === 'param') {
continue;
}
if ($varInfo->passByReference === true && isset($varInfo->lastAssignment) === true) {
continue;
}
if (isset($varInfo->firstDeclared) === true) {
$phpcsFile
->addWarning("Unused %s %s.", $varInfo->firstDeclared, 'UnusedVariable', array(
VariableInfo::$scopeTypeDescriptions[$varInfo->scopeType],
"\${$varInfo->name}",
));
}
else {
if (isset($varInfo->firstInitialized) === true) {
$phpcsFile
->addWarning("Unused %s %s.", $varInfo->firstInitialized, 'UnusedVariable', array(
VariableInfo::$scopeTypeDescriptions[$varInfo->scopeType],
"\${$varInfo->name}",
));
}
}
}
}
}