class IncludeSystemSniff
Hierarchy
- class \PHP_CodeSniffer\Sniffs\AbstractScopeSniff implements \PHP_CodeSniffer\Sniffs\Sniff
- class \PHP_CodeSniffer\Standards\MySource\Sniffs\Channels\IncludeSystemSniff extends \PHP_CodeSniffer\Sniffs\AbstractScopeSniff
Expanded class hierarchy of IncludeSystemSniff
File
-
vendor/
squizlabs/ php_codesniffer/ src/ Standards/ MySource/ Sniffs/ Channels/ IncludeSystemSniff.php, line 18
Namespace
PHP_CodeSniffer\Standards\MySource\Sniffs\ChannelsView source
class IncludeSystemSniff extends AbstractScopeSniff {
/**
* A list of classes that don't need to be included.
*
* @var array<string, bool>
*/
private $ignore = [
'self' => true,
'static' => true,
'parent' => true,
'channels' => true,
'basesystem' => true,
'dal' => true,
'init' => true,
'pdo' => true,
'util' => true,
'ziparchive' => true,
'phpunit_framework_assert' => true,
'abstractmysourceunittest' => true,
'abstractdatacleanunittest' => true,
'exception' => true,
'abstractwidgetwidgettype' => true,
'domdocument' => true,
];
/**
* Constructs an AbstractScopeSniff.
*/
public function __construct() {
parent::__construct([
T_FUNCTION,
], [
T_DOUBLE_COLON,
T_EXTENDS,
], true);
}
//end __construct()
/**
* Processes the function tokens within the class.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
* @param integer $stackPtr The position where the token was found.
* @param integer $currScope The current scope opener token.
*
* @return void
*/
protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope) {
$tokens = $phpcsFile->getTokens();
// Determine the name of the class that the static function
// is being called on.
$classNameToken = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true);
// Don't process class names represented by variables as this can be
// an inexact science.
if ($tokens[$classNameToken]['code'] === T_VARIABLE) {
return;
}
$className = $tokens[$classNameToken]['content'];
if (isset($this->ignore[strtolower($className)]) === true) {
return;
}
$includedClasses = [];
$fileName = strtolower($phpcsFile->getFilename());
$matches = [];
if (preg_match('|/systems/(.*)/([^/]+)?actions.inc$|', $fileName, $matches) !== 0) {
// This is an actions file, which means we don't
// have to include the system in which it exists.
$includedClasses[$matches[2]] = true;
// Or a system it implements.
$class = $phpcsFile->getCondition($stackPtr, T_CLASS);
$implements = $phpcsFile->findNext(T_IMPLEMENTS, $class, $class + 10);
if ($implements !== false) {
$implementsClass = $phpcsFile->findNext(T_STRING, $implements);
$implementsClassName = strtolower($tokens[$implementsClass]['content']);
if (substr($implementsClassName, -7) === 'actions') {
$includedClasses[substr($implementsClassName, 0, -7)] = true;
}
}
}
// Go searching for includeSystem and includeAsset calls within this
// function, or the inclusion of .inc files, which
// would be library files.
for ($i = $currScope + 1; $i < $stackPtr; $i++) {
$name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
if ($name !== false) {
$includedClasses[$name] = true;
// Special case for Widgets cause they are, well, special.
}
else {
if (strtolower($tokens[$i]['content']) === 'includewidget') {
$typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $i + 1);
$typeName = trim($tokens[$typeName]['content'], " '");
$includedClasses[strtolower($typeName) . 'widgettype'] = true;
}
}
}
// Now go searching for includeSystem, includeAsset or require/include
// calls outside our scope. If we are in a class, look outside the
// class. If we are not, look outside the function.
$condPtr = $currScope;
if ($phpcsFile->hasCondition($stackPtr, T_CLASS) === true) {
foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condType) {
if ($condType === T_CLASS) {
break;
}
}
}
for ($i = 0; $i < $condPtr; $i++) {
// Skip other scopes.
if (isset($tokens[$i]['scope_closer']) === true) {
$i = $tokens[$i]['scope_closer'];
continue;
}
$name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
if ($name !== false) {
$includedClasses[$name] = true;
}
}
// If we are in a testing class, we might have also included
// some systems and classes in our setUp() method.
$setupFunction = null;
if ($phpcsFile->hasCondition($stackPtr, T_CLASS) === true) {
foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condType) {
if ($condType === T_CLASS) {
// Is this is a testing class?
$name = $phpcsFile->findNext(T_STRING, $condPtr);
$name = $tokens[$name]['content'];
if (substr($name, -8) === 'UnitTest') {
// Look for a method called setUp().
$end = $tokens[$condPtr]['scope_closer'];
$function = $phpcsFile->findNext(T_FUNCTION, $condPtr + 1, $end);
while ($function !== false) {
$name = $phpcsFile->findNext(T_STRING, $function);
if ($tokens[$name]['content'] === 'setUp') {
$setupFunction = $function;
break;
}
$function = $phpcsFile->findNext(T_FUNCTION, $function + 1, $end);
}
}
}
}
//end foreach
}
//end if
if ($setupFunction !== null) {
$start = $tokens[$setupFunction]['scope_opener'] + 1;
$end = $tokens[$setupFunction]['scope_closer'];
for ($i = $start; $i < $end; $i++) {
$name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
if ($name !== false) {
$includedClasses[$name] = true;
}
}
}
//end if
if (isset($includedClasses[strtolower($className)]) === false) {
$error = 'Static method called on non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once';
$data = [
$className,
];
$phpcsFile->addError($error, $stackPtr, 'NotIncludedCall', $data);
}
}
//end processTokenWithinScope()
/**
* Processes a token within the scope that this test is listening to.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
* @param int $stackPtr The position in the stack where
* this token was found.
*
* @return void
*/
protected function processTokenOutsideScope(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['code'] === T_EXTENDS) {
// Find the class name.
$classNameToken = $phpcsFile->findNext(T_STRING, $stackPtr + 1);
$className = $tokens[$classNameToken]['content'];
}
else {
// Determine the name of the class that the static function
// is being called on. But don't process class names represented by
// variables as this can be an inexact science.
$classNameToken = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true);
if ($tokens[$classNameToken]['code'] === T_VARIABLE) {
return;
}
$className = $tokens[$classNameToken]['content'];
}
// Some systems are always available.
if (isset($this->ignore[strtolower($className)]) === true) {
return;
}
$includedClasses = [];
$fileName = strtolower($phpcsFile->getFilename());
$matches = [];
if (preg_match('|/systems/([^/]+)/([^/]+)?actions.inc$|', $fileName, $matches) !== 0) {
// This is an actions file, which means we don't
// have to include the system in which it exists
// We know the system from the path.
$includedClasses[$matches[1]] = true;
}
// Go searching for includeSystem, includeAsset or require/include
// calls outside our scope.
for ($i = 0; $i < $stackPtr; $i++) {
// Skip classes and functions as will we never get
// into their scopes when including this file, although
// we have a chance of getting into IF, WHILE etc.
if (($tokens[$i]['code'] === T_CLASS || $tokens[$i]['code'] === T_INTERFACE || $tokens[$i]['code'] === T_FUNCTION) && isset($tokens[$i]['scope_closer']) === true) {
$i = $tokens[$i]['scope_closer'];
continue;
}
$name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
if ($name !== false) {
$includedClasses[$name] = true;
// Special case for Widgets cause they are, well, special.
}
else {
if (strtolower($tokens[$i]['content']) === 'includewidget') {
$typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $i + 1);
$typeName = trim($tokens[$typeName]['content'], " '");
$includedClasses[strtolower($typeName) . 'widgettype'] = true;
}
}
}
//end for
if (isset($includedClasses[strtolower($className)]) === false) {
if ($tokens[$stackPtr]['code'] === T_EXTENDS) {
$error = 'Class extends non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once';
$data = [
$className,
];
$phpcsFile->addError($error, $stackPtr, 'NotIncludedExtends', $data);
}
else {
$error = 'Static method called on non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once';
$data = [
$className,
];
$phpcsFile->addError($error, $stackPtr, 'NotIncludedCall', $data);
}
}
}
//end processTokenOutsideScope()
/**
* Determines the included class name from given token.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
* @param array $tokens The array of file tokens.
* @param int $stackPtr The position in the tokens array of the
* potentially included class.
*
* @return string|false
*/
protected function getIncludedClassFromToken(File $phpcsFile, array $tokens, $stackPtr) {
if (strtolower($tokens[$stackPtr]['content']) === 'includesystem') {
$systemName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $stackPtr + 1);
$systemName = trim($tokens[$systemName]['content'], " '");
return strtolower($systemName);
}
else {
if (strtolower($tokens[$stackPtr]['content']) === 'includeasset') {
$typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $stackPtr + 1);
$typeName = trim($tokens[$typeName]['content'], " '");
return strtolower($typeName) . 'assettype';
}
else {
if (isset(Tokens::$includeTokens[$tokens[$stackPtr]['code']]) === true) {
$filePath = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $stackPtr + 1);
$filePath = $tokens[$filePath]['content'];
$filePath = trim($filePath, " '");
$filePath = basename($filePath, '.inc');
return strtolower($filePath);
}
}
}
return false;
}
//end getIncludedClassFromToken()
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overriden Title |
---|---|---|---|---|
AbstractScopeSniff::$listenOutside | private | property | True if this test should fire on tokens outside of the scope. | |
AbstractScopeSniff::$scopeTokens | private | property | The type of scope opener tokens that this test wishes to listen to. | |
AbstractScopeSniff::$tokens | private | property | The token types that this test wishes to listen to within the scope. | |
AbstractScopeSniff::process | final public | function | Processes the tokens that this test is listening for. | Overrides Sniff::process |
AbstractScopeSniff::register | final public | function | The method that is called to register the tokens this test wishes to listen to. |
Overrides Sniff::register |
IncludeSystemSniff::$ignore | private | property | A list of classes that don't need to be included. | |
IncludeSystemSniff::getIncludedClassFromToken | protected | function | Determines the included class name from given token. | |
IncludeSystemSniff::processTokenOutsideScope | protected | function | Processes a token within the scope that this test is listening to. | Overrides AbstractScopeSniff::processTokenOutsideScope |
IncludeSystemSniff::processTokenWithinScope | protected | function | Processes the function tokens within the class. | Overrides AbstractScopeSniff::processTokenWithinScope |
IncludeSystemSniff::__construct | public | function | Constructs an AbstractScopeSniff. | Overrides AbstractScopeSniff::__construct |