clean install

This commit is contained in:
2024-12-10 15:08:16 +01:00
commit e14eb2d8fd
31193 changed files with 3555714 additions and 0 deletions

View File

@@ -0,0 +1,221 @@
<?php
/**
* \DrupalPractice\Sniffs\Objects\GlobalClassSniff.
*
* @category PHP
* @package PHP_CodeSniffer
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace DrupalPractice\Sniffs\Objects;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use DrupalPractice\Project;
/**
* Checks that Node::load() calls and friends are not used in forms, controllers or
* services.
*
* @category PHP
* @package PHP_CodeSniffer
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class GlobalClassSniff implements Sniff
{
/**
* Core class names that should not be called statically, mostly entity
* classes.
*
* @var string[]
*/
protected $coreClasses = [
'Drupal\Core\Datetime\Entity\DateFormat',
'Drupal\Core\Entity\Entity\EntityFormDisplay',
'Drupal\Core\Entity\Entity\EntityFormMode',
'Drupal\Core\Entity\Entity\EntityViewDisplay',
'Drupal\Core\Entity\Entity\EntityViewMode',
'Drupal\Core\Field\Entity\BaseFieldOverride',
'Drupal\aggregator\Entity\Feed',
'Drupal\aggregator\Entity\Item',
'Drupal\block\Entity\Block',
'Drupal\block_content\Entity\BlockContent',
'Drupal\block_content\Entity\BlockContentType',
'Drupal\comment\Entity\Comment',
'Drupal\comment\Entity\CommentType',
'Drupal\contact\Entity\ContactForm',
'Drupal\contact\Entity\Message',
'Drupal\content_moderation\Entity\ContentModerationState',
'Drupal\editor\Entity\Editor',
'Drupal\field\Entity\FieldConfig',
'Drupal\field\Entity\FieldStorageConfig',
'Drupal\file\Entity\File',
'Drupal\filter\Entity\FilterFormat',
'Drupal\image\Entity\ImageStyle',
'Drupal\language\Entity\ConfigurableLanguage',
'Drupal\language\Entity\ContentLanguageSettings',
'Drupal\media\Entity\Media',
'Drupal\media\Entity\MediaType',
'Drupal\menu_link_content\Entity\MenuLinkContent',
'Drupal\node\Entity\Node',
'Drupal\node\Entity\NodeType',
'Drupal\path_alias\Entity\PathAlias',
'Drupal\rdf\Entity\RdfMapping',
'Drupal\responsive_image\Entity\ResponsiveImageStyle',
'Drupal\rest\Entity\RestResourceConfig',
'Drupal\search\Entity\SearchPage',
'Drupal\shortcut\Entity\Shortcut',
'Drupal\shortcut\Entity\ShortcutSet',
'Drupal\system\Entity\Action',
'Drupal\system\Entity\Menu',
'Drupal\taxonomy\Entity\Term',
'Drupal\taxonomy\Entity\Vocabulary',
'Drupal\tour\Entity\Tour',
'Drupal\user\Entity\Role',
'Drupal\user\Entity\User',
'Drupal\views\Entity\View',
'Drupal\workflows\Entity\Workflow',
'Drupal\workspaces\Entity\Workspace',
];
/**
* Class names that should not be called statically.
*
* @var string[]
*/
public $classes = [];
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array<int|string>
*/
public function register()
{
return [T_STRING];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void|int
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// We are only interested in static class method calls, not in the global
// scope.
if ($tokens[($stackPtr + 1)]['code'] !== T_DOUBLE_COLON
|| isset($tokens[($stackPtr + 2)]) === false
|| $tokens[($stackPtr + 2)]['code'] !== T_STRING
|| in_array($tokens[($stackPtr + 2)]['content'], ['load', 'loadMultiple']) === false
|| isset($tokens[($stackPtr + 3)]) === false
|| $tokens[($stackPtr + 3)]['code'] !== T_OPEN_PARENTHESIS
|| empty($tokens[$stackPtr]['conditions']) === true
) {
return;
}
// Check that this statement is not in a static function.
foreach ($tokens[$stackPtr]['conditions'] as $conditionPtr => $conditionCode) {
if ($conditionCode === T_FUNCTION && $phpcsFile->getMethodProperties($conditionPtr)['is_static'] === true) {
return;
}
}
$fullName = $this->getFullyQualifiedName($phpcsFile, $tokens[$stackPtr]['content']);
if (in_array($fullName, $this->coreClasses) === false && in_array($fullName, $this->classes) === false) {
return;
}
// Check if the class extends another class and get the name of the class
// that is extended.
$classPtr = key($tokens[$stackPtr]['conditions']);
$extendsName = $phpcsFile->findExtendedClassName($classPtr);
// Check if the class implements a container injection interface.
$containerInterfaces = [
'ContainerInjectionInterface',
'ContainerFactoryPluginInterface',
'ContainerDeriverInterface',
];
$implementedInterfaceNames = $phpcsFile->findImplementedInterfaceNames($classPtr);
$canAccessContainer = !empty($implementedInterfaceNames) && !empty(array_intersect($containerInterfaces, $implementedInterfaceNames));
if (($extendsName === false
|| in_array($extendsName, GlobalDrupalSniff::$baseClasses) === false)
&& Project::isServiceClass($phpcsFile, $classPtr) === false
&& $canAccessContainer === false
) {
return ($phpcsFile->numTokens + 1);
}
$warning = '%s::%s calls should be avoided in classes, use dependency injection instead';
$data = [
$tokens[$stackPtr]['content'],
$tokens[($stackPtr + 2)]['content'],
];
$phpcsFile->addWarning($warning, $stackPtr, 'GlobalClass', $data);
}//end process()
/**
* Retrieve the fully qualified name of the given classname.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param string $className The classname for which to retrieve the FQN.
*
* @return string
*/
protected function getFullyQualifiedName(File $phpcsFile, $className)
{
$useStatement = $phpcsFile->findNext(T_USE, 0);
while ($useStatement !== false) {
$endPtr = $phpcsFile->findEndOfStatement($useStatement);
$useEnd = ($phpcsFile->findNext([T_STRING, T_NS_SEPARATOR, T_WHITESPACE], ($useStatement + 1), null, true) - 1);
$useFullName = trim($phpcsFile->getTokensAsString(($useStatement + 1), ($useEnd - $useStatement)), '\\ ');
// Check if use statement contains an alias.
$asPtr = $phpcsFile->findNext(T_AS, ($useEnd + 1), $endPtr);
if ($asPtr !== false) {
$aliasName = trim($phpcsFile->getTokensAsString(($asPtr + 1), ($endPtr - 1 - $asPtr)));
if ($aliasName === $className) {
return $useFullName;
}
}
$parts = explode('\\', $useFullName);
$useClassName = end($parts);
// Check if the resulting classname is the classname we're looking
// for.
if ($useClassName === $className) {
return $useFullName;
}
// Check if we're currently in a multi-use statement.
$tokens = $phpcsFile->getTokens();
if ($tokens[$endPtr]['code'] === T_COMMA) {
$useStatement = $endPtr;
continue;
}
$useStatement = $phpcsFile->findNext(T_USE, ($useStatement + 1));
}//end while
return $className;
}//end getFullyQualifiedName()
}//end class

View File

@@ -0,0 +1,114 @@
<?php
/**
* \DrupalPractice\Sniffs\Objects\GlobalDrupalSniff.
*
* @category PHP
* @package PHP_CodeSniffer
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace DrupalPractice\Sniffs\Objects;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use DrupalPractice\Project;
/**
* Checks that \Drupal::service() and friends is not used in forms, controllers, services.
*
* @category PHP
* @package PHP_CodeSniffer
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class GlobalDrupalSniff implements Sniff
{
/**
* List of base classes where \Drupal should not be used in an extending class.
*
* @var string[]
*/
public static $baseClasses = [
'BlockBase',
'ConfigFormBase',
'ContentEntityForm',
'ControllerBase',
'EntityForm',
'EntityReferenceFormatterBase',
'FileFormatterBase',
'FormatterBase',
'FormBase',
'ImageFormatter',
'ImageFormatterBase',
'WidgetBase',
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array<int|string>
*/
public function register()
{
return [T_STRING];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param \PHP_CodeSniffer\Files\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(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// We are only interested in Drupal:: static method calls, not in the global
// scope.
if ($tokens[$stackPtr]['content'] !== 'Drupal'
|| $tokens[($stackPtr + 1)]['code'] !== T_DOUBLE_COLON
|| isset($tokens[($stackPtr + 2)]) === false
|| $tokens[($stackPtr + 2)]['code'] !== T_STRING
|| isset($tokens[($stackPtr + 3)]) === false
|| $tokens[($stackPtr + 3)]['code'] !== T_OPEN_PARENTHESIS
|| empty($tokens[$stackPtr]['conditions']) === true
) {
return;
}
// Check that this statement is not in a static function.
foreach ($tokens[$stackPtr]['conditions'] as $conditionPtr => $conditionCode) {
if ($conditionCode === T_FUNCTION && $phpcsFile->getMethodProperties($conditionPtr)['is_static'] === true) {
return;
}
}
// Check if the class extends another class and get the name of the class
// that is extended.
$classPtr = key($tokens[$stackPtr]['conditions']);
$extendsName = $phpcsFile->findExtendedClassName($classPtr);
// Check if the class implements ContainerInjectionInterface.
$implementedInterfaceNames = $phpcsFile->findImplementedInterfaceNames($classPtr);
$canAccessContainer = !empty($implementedInterfaceNames) && in_array('ContainerInjectionInterface', $implementedInterfaceNames);
if (($extendsName === false || in_array($extendsName, static::$baseClasses) === false)
&& Project::isServiceClass($phpcsFile, $classPtr) === false
&& $canAccessContainer === false
) {
return;
}
$warning = '\Drupal calls should be avoided in classes, use dependency injection instead';
$phpcsFile->addWarning($warning, $stackPtr, 'GlobalDrupal');
}//end process()
}//end class

View File

@@ -0,0 +1,135 @@
<?php
/**
* \DrupalPractice\Sniffs\Objects\GlobalFunctionSniff.
*
* @category PHP
* @package PHP_CodeSniffer
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace DrupalPractice\Sniffs\Objects;
use PHP_CodeSniffer\Files\File;
use DrupalPractice\Project;
use Drupal\Sniffs\Semantics\FunctionCall;
/**
* Checks that global functions like t() are not used in forms or controllers.
*
* @category PHP
* @package PHP_CodeSniffer
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class GlobalFunctionSniff extends FunctionCall
{
/**
* List of global functions that should not be called.
*
* @var string[]
*/
protected $functions = [
'drupal_get_destination' => 'the "redirect.destination" service',
'drupal_render' => 'the "renderer" service',
'entity_load' => 'the "entity_type.manager" service',
'file_load' => 'the "entity_type.manager" service',
'format_date' => 'the "date.formatter" service',
'node_load' => 'the "entity_type.manager" service',
'node_load_multiple' => 'the "entity_type.manager" service',
'node_type_load' => 'the "entity_type.manager" service',
't' => '$this->t()',
'taxonomy_term_load' => 'the "entity_type.manager" service',
'taxonomy_vocabulary_load' => 'the "entity_type.manager" service',
'user_load' => 'the "entity_type.manager" service',
'user_role_load' => 'the "entity_type.manager" service',
];
/**
* List of global functions that are covered by traits.
*
* This is a subset of the global functions list. These functions can be
* replaced by methods that are provided by the listed trait.
*
* @var string[]
*/
protected $traitFunctions = ['t' => '\Drupal\Core\StringTranslation\StringTranslationTrait'];
/**
* Processes this test, when one of its tokens is encountered.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void|int
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// Only run this sniff on Drupal 8+.
if (Project::getCoreVersion($phpcsFile) < 8) {
// No need to check this file again, mark it as done.
return ($phpcsFile->numTokens + 1);
}
// We just want to listen on function calls, nothing else.
if ($this->isFunctionCall($phpcsFile, $stackPtr) === false
// Ignore function calls in the global scope.
|| empty($tokens[$stackPtr]['conditions']) === true
// Only our list of function names.
|| isset($this->functions[$tokens[$stackPtr]['content']]) === false
) {
return;
}
// Check that this statement is not in a static function.
foreach ($tokens[$stackPtr]['conditions'] as $conditionPtr => $conditionCode) {
if ($conditionCode === T_FUNCTION && $phpcsFile->getMethodProperties($conditionPtr)['is_static'] === true) {
return;
}
}
// Check if the class extends another class and get the name of the class
// that is extended.
$classPtr = key($tokens[$stackPtr]['conditions']);
if ($tokens[$classPtr]['code'] !== T_CLASS) {
return;
}
if (isset($this->traitFunctions[$tokens[$stackPtr]['content']]) === false) {
$extendsName = $phpcsFile->findExtendedClassName($classPtr);
// Check if the class implements ContainerInjectionInterface.
$implementedInterfaceNames = $phpcsFile->findImplementedInterfaceNames($classPtr);
$canAccessContainer = !empty($implementedInterfaceNames) && in_array('ContainerInjectionInterface', $implementedInterfaceNames);
if (($extendsName === false
|| in_array($extendsName, GlobalDrupalSniff::$baseClasses) === false)
&& Project::isServiceClass($phpcsFile, $classPtr) === false
&& $canAccessContainer === false
) {
return;
}
$warning = '%s() calls should be avoided in classes, use dependency injection and %s instead';
$data = [
$tokens[$stackPtr]['content'],
$this->functions[$tokens[$stackPtr]['content']],
];
} else {
$warning = '%s() calls should be avoided in classes, use %s and %s instead';
$data = [
$tokens[$stackPtr]['content'],
$this->traitFunctions[$tokens[$stackPtr]['content']],
$this->functions[$tokens[$stackPtr]['content']],
];
}//end if
$phpcsFile->addWarning($warning, $stackPtr, 'GlobalFunction', $data);
}//end process()
}//end class

View File

@@ -0,0 +1,130 @@
<?php
/**
* DrupalPractice_Sniffs_Objects_StrictSchemaDisabledSniff.
*
* @category PHP
* @package PHP_CodeSniffer
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace DrupalPractice\Sniffs\Objects;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
/**
* Checks that $strictConfigSchema is not set to FALSE in test classes.
*
* @category PHP
* @package PHP_CodeSniffer
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class StrictSchemaDisabledSniff extends AbstractVariableSniff
{
/**
* The name of the variable in the test base class to disable config schema checking.
*/
const STRICT_CONFIG_SCHEMA_NAME = '$strictConfigSchema';
/**
* Processes this test, when one of its tokens is encountered.
*
* @param \PHP_CodeSniffer\Files\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 processMemberVar(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if (($tokens[$stackPtr]['content'] === static::STRICT_CONFIG_SCHEMA_NAME) && ($this->isTestClass($phpcsFile, $stackPtr) === true)) {
$find = [
T_FALSE,
T_TRUE,
T_NULL,
T_SEMICOLON,
];
$next = $phpcsFile->findNext($find, ($stackPtr + 1));
// If this variable is being set, the only allowed value is TRUE.
// Otherwise if FALSE or NULL, schema checking is disabled.
if ($tokens[$next]['code'] !== T_TRUE) {
$error = 'Do not disable strict config schema checking in tests. Instead ensure your module properly declares its schema for configurations.';
$data = [$tokens[$stackPtr]['content']];
$phpcsFile->addError(
$error,
$stackPtr,
'StrictConfigSchema',
$data
);
}
}//end if
}//end processMemberVar()
/**
* Determine if this class is a test class.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return bool
* Returns TRUE if the current class is a test class.
*/
protected function isTestClass(File $phpcsFile, $stackPtr)
{
// Only applies to test classes, which have Test in the name.
$tokens = $phpcsFile->getTokens();
$classPtr = key($tokens[$stackPtr]['conditions']);
$name = $phpcsFile->findNext([T_STRING], $classPtr);
return strpos($tokens[$name]['content'], 'Test') !== false;
}//end isTestClass()
/**
* Called to process normal member vars.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
* token was found.
* @param int $stackPtr The position where the token was found.
*
* @return void|int Optionally returns a stack pointer. The sniff will not be
* called again on the current file until the returned stack
* pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
* the rest of the file.
*/
protected function processVariable(File $phpcsFile, $stackPtr)
{
}//end processVariable()
/**
* Called to process variables found in double quoted strings or heredocs.
*
* Note that there may be more than one variable in the string, which will
* result only in one call for the string or one call per line for heredocs.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where this
* token was found.
* @param int $stackPtr The position where the double quoted
* string was found.
*
* @return void|int Optionally returns a stack pointer. The sniff will not be
* called again on the current file until the returned stack
* pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
* the rest of the file.
*/
protected function processVariableInString(File $phpcsFile, $stackPtr)
{
}//end processVariableInString()
}//end class

View File

@@ -0,0 +1,127 @@
<?php
/**
* \DrupalPractice\Sniffs\Objects\UnusedPrivateMethodSniff.
*
* @category PHP
* @package PHP_CodeSniffer
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace DrupalPractice\Sniffs\Objects;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
use PHP_CodeSniffer\Util\Tokens;
/**
* Checks that private methods are actually used in a class.
*
* @category PHP
* @package PHP_CodeSniffer
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class UnusedPrivateMethodSniff extends AbstractScopeSniff
{
/**
* Constructor.
*/
public function __construct()
{
parent::__construct([T_CLASS], [T_FUNCTION], false);
}//end __construct()
/**
* Processes the tokens within the scope.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being processed.
* @param int $stackPtr The position where this token was
* found.
* @param int $currScope The position of the current scope.
*
* @return void
*/
protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
{
// Only check private methods.
$methodProperties = $phpcsFile->getMethodProperties($stackPtr);
if ($methodProperties['scope'] !== 'private' || $methodProperties['is_static'] === true) {
return;
}
$tokens = $phpcsFile->getTokens();
$methodName = $phpcsFile->getDeclarationName($stackPtr);
if ($methodName === '__construct') {
return;
}
$classPtr = key($tokens[$stackPtr]['conditions']);
// Search for direct $this->methodCall() or indirect callbacks [$this,
// 'methodCall'].
$current = $tokens[$classPtr]['scope_opener'];
$end = $tokens[$classPtr]['scope_closer'];
while (($current = $phpcsFile->findNext(T_VARIABLE, ($current + 1), $end)) !== false) {
if ($tokens[$current]['content'] !== '$this') {
continue;
}
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($current + 1), null, true);
if ($next === false) {
continue;
}
if ($tokens[$next]['code'] === T_OBJECT_OPERATOR) {
$call = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
// PHP method calls are case insensitive.
if ($call === false || strcasecmp($tokens[$call]['content'], $methodName) !== 0) {
continue;
}
$parenthesis = $phpcsFile->findNext(Tokens::$emptyTokens, ($call + 1), null, true);
if ($parenthesis === false || $tokens[$parenthesis]['code'] !== T_OPEN_PARENTHESIS) {
continue;
}
// At this point this is a method call to the private method, so we
// can stop.
return;
} else if ($tokens[$next]['code'] === T_COMMA) {
$call = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
if ($call === false || substr($tokens[$call]['content'], 1, -1) !== $methodName) {
continue;
}
// At this point this is likely the private method as callback on a
// function such as array_filter().
return;
}//end if
}//end while
$warning = 'Unused private method %s()';
$data = [$methodName];
$phpcsFile->addWarning($warning, $stackPtr, 'UnusedMethod', $data);
}//end processTokenWithinScope()
/**
* Process tokens outside of scope.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being processed.
* @param int $stackPtr The position where this token was
* found.
*
* @return void
*/
protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
{
}//end processTokenOutsideScope()
}//end class