-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Functions/DynamicCalls: various bug fixes and improvements #592
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
af82f36
Functions/DynamicCalls: annotate the unit tests
jrfnl d8f958d
Functions/DynamicCalls: remove redundant conditions [1]
jrfnl 0f55b11
Functions/DynamicCalls: remove redundant conditions [2]
jrfnl db13572
Functions/DynamicCalls: improve code readability
jrfnl b6ebf86
Functions/DynamicCalls: minor simplification
jrfnl df8c596
Functions/DynamicCalls: bug fix - ignore comments [1]
jrfnl 8e63627
Functions/DynamicCalls: bug fix - don't blindly use the next text string
jrfnl e607fb0
Functions/DynamicCalls: bug fix - allow for double quotes
jrfnl b093d62
Functions/DynamicCalls: bug fix - fix memory + performance issue
jrfnl 6b4a43d
Functions/DynamicCalls: rename private property
jrfnl 3118358
Functions/DynamicCalls: bug fix - ignore comments [2]
jrfnl 1d1a977
Functions/DynamicCalls: error message tweak
jrfnl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,47 +7,47 @@ | |
|
||
namespace WordPressVIPMinimum\Sniffs\Functions; | ||
|
||
use PHP_CodeSniffer\Util\Tokens; | ||
use WordPressVIPMinimum\Sniffs\Sniff; | ||
|
||
/** | ||
* This sniff enforces that certain functions are not | ||
* dynamically called. | ||
* This sniff enforces that certain functions are not dynamically called. | ||
* | ||
* An example: | ||
* | ||
* <code> | ||
* ```php | ||
* $func = 'func_num_args'; | ||
* $func(); | ||
* </code> | ||
* ``` | ||
* | ||
* See here: http://php.net/manual/en/migration71.incompatible.php | ||
* Note that this sniff does not catch all possible forms of dynamic calling, only some. | ||
* | ||
* Note that this sniff does not catch all possible forms of dynamic | ||
* calling, only some. | ||
* @link http://php.net/manual/en/migration71.incompatible.php | ||
*/ | ||
class DynamicCallsSniff extends Sniff { | ||
|
||
/** | ||
* Functions that should not be called dynamically. | ||
* | ||
* @var array | ||
*/ | ||
private $blacklisted_functions = [ | ||
'assert', | ||
'compact', | ||
'extract', | ||
'func_get_args', | ||
'func_get_arg', | ||
'func_num_args', | ||
'get_defined_vars', | ||
'mb_parse_str', | ||
'parse_str', | ||
private $function_names = [ | ||
'assert' => true, | ||
'compact' => true, | ||
'extract' => true, | ||
'func_get_args' => true, | ||
'func_get_arg' => true, | ||
'func_num_args' => true, | ||
'get_defined_vars' => true, | ||
'mb_parse_str' => true, | ||
'parse_str' => true, | ||
]; | ||
|
||
/** | ||
* Array of functions encountered, along with their values. | ||
* Populated on run-time. | ||
* Array of variable assignments encountered, along with their values. | ||
* | ||
* @var array | ||
* Populated at run-time. | ||
* | ||
* @var array The key is the name of the variable, the value, its assigned value. | ||
*/ | ||
private $variables_arr = []; | ||
|
||
|
@@ -61,8 +61,6 @@ class DynamicCallsSniff extends Sniff { | |
/** | ||
* Returns the token types that this sniff is interested in. | ||
* | ||
* We want everything variable- and function-related. | ||
* | ||
* @return array(int) | ||
*/ | ||
public function register() { | ||
|
@@ -87,163 +85,104 @@ public function process_token( $stackPtr ) { | |
} | ||
|
||
/** | ||
* Finds any variable-definitions in the file being processed, | ||
* and stores them internally in a private array. The data stored | ||
* is the name of the variable and its assigned value. | ||
* Finds any variable-definitions in the file being processed and stores them | ||
* internally in a private array. | ||
* | ||
* @return void | ||
*/ | ||
private function collect_variables() { | ||
/* | ||
* Make sure we are working with a variable, | ||
* get its value if so. | ||
*/ | ||
|
||
if ( | ||
$this->tokens[ $this->stackPtr ]['type'] !== | ||
'T_VARIABLE' | ||
) { | ||
return; | ||
} | ||
|
||
$current_var_name = $this->tokens[ $this->stackPtr ]['content']; | ||
|
||
/* | ||
* Find assignments ( $foo = "bar"; ) | ||
* -- do this by finding all non-whitespaces, and | ||
* check if the first one is T_EQUAL. | ||
* Find assignments ( $foo = "bar"; ) by finding all non-whitespaces, | ||
* and checking if the first one is T_EQUAL. | ||
*/ | ||
|
||
$t_item_key = $this->phpcsFile->findNext( | ||
[ T_WHITESPACE ], | ||
Tokens::$emptyTokens, | ||
$this->stackPtr + 1, | ||
null, | ||
true, | ||
null, | ||
true | ||
); | ||
|
||
if ( $t_item_key === false ) { | ||
if ( $t_item_key === false || $this->tokens[ $t_item_key ]['code'] !== T_EQUAL ) { | ||
return; | ||
} | ||
|
||
if ( $this->tokens[ $t_item_key ]['type'] !== 'T_EQUAL' ) { | ||
return; | ||
/* | ||
* Find assignments which only assign a plain text string. | ||
*/ | ||
$end_of_statement = $this->phpcsFile->findNext( [ T_SEMICOLON, T_CLOSE_TAG ], ( $t_item_key + 1 ) ); | ||
$value_ptr = null; | ||
|
||
for ( $i = $t_item_key + 1; $i < $end_of_statement; $i++ ) { | ||
if ( isset( Tokens::$emptyTokens[ $this->tokens[ $i ]['code'] ] ) === true ) { | ||
continue; | ||
} | ||
|
||
if ( $this->tokens[ $i ]['code'] !== T_CONSTANT_ENCAPSED_STRING ) { | ||
// Not a plain text string value. Value cannot be determined reliably. | ||
return; | ||
} | ||
|
||
$value_ptr = $i; | ||
} | ||
|
||
if ( $this->tokens[ $t_item_key ]['length'] !== 1 ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just curious - what tokens are There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if ( isset( $value_ptr ) === false ) { | ||
// Parse error. Bow out. | ||
return; | ||
} | ||
|
||
/* | ||
* Find encapsulated string ( "" ) | ||
* If we reached the end of the loop and the $value_ptr was set, we know for sure | ||
* this was a plain text string variable assignment. | ||
*/ | ||
$t_item_key = $this->phpcsFile->findNext( | ||
[ T_CONSTANT_ENCAPSED_STRING ], | ||
$t_item_key + 1, | ||
null, | ||
false, | ||
null, | ||
true | ||
); | ||
$current_var_value = $this->strip_quotes( $this->tokens[ $value_ptr ]['content'] ); | ||
|
||
if ( $t_item_key === false ) { | ||
if ( isset( $this->function_names[ $current_var_value ] ) === false ) { | ||
// Text string is not one of the ones we're looking for. | ||
return; | ||
} | ||
|
||
/* | ||
* We have found variable-assignment, | ||
* register its name and value in the | ||
* internal array for later usage. | ||
* Register the variable name and value in the internal array for later usage. | ||
*/ | ||
|
||
$current_var_value = | ||
$this->tokens[ $t_item_key ]['content']; | ||
|
||
$this->variables_arr[ $current_var_name ] = | ||
str_replace( "'", '', $current_var_value ); | ||
$this->variables_arr[ $current_var_name ] = $current_var_value; | ||
} | ||
|
||
/** | ||
* Find any dynamic calls being made using variables. | ||
* Report on this when found, using name of the function | ||
* in the message. | ||
* | ||
* Report on this when found, using the name of the function in the message. | ||
* | ||
* @return void | ||
*/ | ||
private function find_dynamic_calls() { | ||
/* | ||
* No variables detected; no basis for doing | ||
* anything | ||
*/ | ||
|
||
// No variables detected; no basis for doing anything. | ||
if ( empty( $this->variables_arr ) ) { | ||
return; | ||
} | ||
|
||
/* | ||
* Make sure we do have a variable to work with. | ||
*/ | ||
|
||
if ( | ||
$this->tokens[ $this->stackPtr ]['type'] !== | ||
'T_VARIABLE' | ||
) { | ||
return; | ||
} | ||
|
||
/* | ||
* If variable is not found in our registry of | ||
* variables, do nothing, as we cannot be | ||
* sure that the function being called is one of the | ||
* blacklisted ones. | ||
* If variable is not found in our registry of variables, do nothing, as we cannot be | ||
* sure that the function being called is one of the blacklisted ones. | ||
*/ | ||
|
||
if ( ! isset( | ||
$this->variables_arr[ $this->tokens[ $this->stackPtr ]['content'] ] | ||
) ) { | ||
if ( ! isset( $this->variables_arr[ $this->tokens[ $this->stackPtr ]['content'] ] ) ) { | ||
return; | ||
} | ||
|
||
/* | ||
* Check if we have an '(' next, or separated by whitespaces | ||
* from our current position. | ||
* Check if we have an '(' next. | ||
*/ | ||
|
||
$i = 0; | ||
|
||
do { | ||
$i++; | ||
} while ( | ||
$this->tokens[ $this->stackPtr + $i ]['type'] === | ||
'T_WHITESPACE' | ||
); | ||
|
||
if ( | ||
$this->tokens[ $this->stackPtr + $i ]['type'] !== | ||
'T_OPEN_PARENTHESIS' | ||
) { | ||
return; | ||
} | ||
|
||
$t_item_key = $this->stackPtr + $i; | ||
|
||
/* | ||
* We have a variable match, but make sure it contains name | ||
* of a function which is on our blacklist. | ||
*/ | ||
|
||
if ( ! in_array( | ||
$this->variables_arr[ $this->tokens[ $this->stackPtr ]['content'] ], | ||
$this->blacklisted_functions, | ||
true | ||
) ) { | ||
$next = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $this->stackPtr + 1 ), null, true ); | ||
if ( $next === false || $this->tokens[ $next ]['code'] !== T_OPEN_PARENTHESIS ) { | ||
return; | ||
} | ||
|
||
// We do, so report. | ||
$message = 'Dynamic calling is not recommended in the case of %s.'; | ||
$message = 'Dynamic calling is not recommended in the case of %s().'; | ||
$data = [ $this->variables_arr[ $this->tokens[ $this->stackPtr ]['content'] ] ]; | ||
$this->phpcsFile->addError( $message, $t_item_key, 'DynamicCalls', $data ); | ||
$this->phpcsFile->addError( $message, $this->stackPtr, 'DynamicCalls', $data ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for re-naming this!