Skip to content

Instantly share code, notes, and snippets.

@voku
Created December 12, 2020 23:02
Show Gist options
  • Save voku/d7d530ce2c072f5b4bdaafb040d1eb55 to your computer and use it in GitHub Desktop.
Save voku/d7d530ce2c072f5b4bdaafb040d1eb55 to your computer and use it in GitHub Desktop.
<?php
declare(strict_types=1);
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_Codesniffer\Util\Tokens;
final class VdmgForbiddenGlobalSniff implements Sniff {
/**
* @var string[]
*
* TODO@lars: maybe we can enable more restrictions for global variables in the future
*/
private $globalVariables = [
'$argv',
'$Brand', // TODO@lars -> remove
'$catalog_status_array',
'$contact_anrede_select_array',
'$contact_country_select_array',
'$contact_lieferant_type_select_array',
'$controlling_diff_mode_select_array',
];
/**
* @param File $file
* @param int $stackPtr
*
* @return void
*/
public function process(File $file, $stackPtr): void {
$fileNameAndPath = $file->getFilename();
/*
if (
strpos($fileNameAndPath, 'application_top.php') !== false
||
strpos($fileNameAndPath, 'defines.php') !== false
) {
return;
}
*/
$tokensAll = $file->getTokens();
$currentToken = $tokensAll[$stackPtr];
if (!is_array($currentToken)) {
return;
}
if ($currentToken['content'] === '$GLOBALS') {
$bracketPtr = $file->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
// Check if this is an array.
if (
$bracketPtr === false
||
$tokensAll[$bracketPtr]['code'] !== \T_OPEN_SQUARE_BRACKET
||
!isset($tokensAll[$bracketPtr]['bracket_closer'])
) {
return;
}
// Retrieve the array key and avoid getting tripped up by some simple obfuscation.
$var_name = '';
$start = $bracketPtr + 1;
for ($ptr = $start; $ptr < $tokensAll[$bracketPtr]['bracket_closer']; $ptr++) {
/*
* If the globals array key contains a variable, constant, function call
* or interpolated variable, bow out.
*/
if (
$tokensAll[$ptr]['code'] === \T_VARIABLE
||
$tokensAll[$ptr]['code'] === \T_STRING
||
$tokensAll[$ptr]['code'] === \T_DOUBLE_QUOTED_STRING
) {
return;
}
if ($tokensAll[$ptr]['code'] === \T_CONSTANT_ENCAPSED_STRING) {
$var_name .= preg_replace('`^([\'"])(.*)\1$`Ds', '$2', $tokensAll[$ptr]['content']);
}
}
if ($var_name === '') {
// Shouldn't happen, but just in case.
return;
}
if (in_array('$' . $var_name, $this->globalVariables, true)) {
return;
}
$file->addWarning(
sprintf('GLOBALS["%s"] should not be used: use ServiceContainer::* or YOURConst::* classes', $var_name),
$stackPtr,
$currentToken['content']
);
} else {
// Are we a global declaration?
// Search backwards for first token that isn't whitespace/comment, comma or variable.
$ignore = Tokens::$emptyTokens;
$ignore[\T_VARIABLE] = \T_VARIABLE;
$ignore[T_COMMA] = T_COMMA;
$globalPtr = $file->findPrevious($ignore, $stackPtr - 1, null, true, null, true);
if (
$globalPtr === false
||
$tokensAll[$globalPtr]['code'] !== \T_GLOBAL
) {
return;
}
if (in_array($currentToken['content'], $this->globalVariables, true)) {
return;
}
$file->addWarning(
sprintf('global "%s" should not be used: use ServiceContainer::* or YOURConst::* classes', $currentToken['content']),
$stackPtr,
$currentToken['content']
);
}
}
/**
* @return int[]
*/
public function register() {
return [
\T_VARIABLE,
];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment