From 557b1d99dbbdedd3e11587a0fdc8ad7901061126 Mon Sep 17 00:00:00 2001 From: Aad Mathijssen Date: Fri, 4 Mar 2022 19:54:37 +0100 Subject: [PATCH 1/8] Resolve incorrect violation code by resetting the hasDisallowedAnnotation property at then end of processing --- Magento2/Sniffs/Security/XssTemplateSniff.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Magento2/Sniffs/Security/XssTemplateSniff.php b/Magento2/Sniffs/Security/XssTemplateSniff.php index 3b4385e8..2ba8e8c0 100644 --- a/Magento2/Sniffs/Security/XssTemplateSniff.php +++ b/Magento2/Sniffs/Security/XssTemplateSniff.php @@ -135,6 +135,7 @@ public function process(File $phpcsFile, $stackPtr) $statement = array_shift($this->statements); $this->detectUnescapedString($statement); } + $this->hasDisallowedAnnotation = false; } /** From 7c702e0b668036273309fc6cd78f970c452d1999 Mon Sep 17 00:00:00 2001 From: Dan Wallis Date: Tue, 5 Apr 2022 09:48:28 +0100 Subject: [PATCH 2/8] Add @author to forbidden tag list Ref https://devdocs.magento.com/guides/v2.4/coding-standards/docblock-standard-general.html#documentation-space --- .../Commenting/ClassAndInterfacePHPDocFormattingSniff.php | 1 + .../Commenting/ClassAndInterfacePHPDocFormattingUnitTest.1.inc | 2 +- .../Commenting/ClassAndInterfacePHPDocFormattingUnitTest.2.inc | 2 +- .../Commenting/ClassAndInterfacePHPDocFormattingUnitTest.php | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Magento2/Sniffs/Commenting/ClassAndInterfacePHPDocFormattingSniff.php b/Magento2/Sniffs/Commenting/ClassAndInterfacePHPDocFormattingSniff.php index 2d715c50..20c12851 100644 --- a/Magento2/Sniffs/Commenting/ClassAndInterfacePHPDocFormattingSniff.php +++ b/Magento2/Sniffs/Commenting/ClassAndInterfacePHPDocFormattingSniff.php @@ -24,6 +24,7 @@ class ClassAndInterfacePHPDocFormattingSniff implements Sniff * @var string[] List of tags that can not be used in comments */ public $forbiddenTags = [ + '@author', '@category', '@package', '@subpackage' diff --git a/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.1.inc b/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.1.inc index e5895e32..e2727967 100644 --- a/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.1.inc +++ b/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.1.inc @@ -60,7 +60,7 @@ class EmptyHandler * @api is ok here * @deprecated can be used in this context * @see is ok here - * @author is actually ok + * @author is not ment to be used * @category is irrelevant * @package is not ment to be used * @subpackage does not belong here diff --git a/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.2.inc b/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.2.inc index 96900de2..e45e15ff 100644 --- a/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.2.inc +++ b/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.2.inc @@ -60,7 +60,7 @@ interface EmptyHandler * @api is ok here * @deprecated can be used in this context * @see is ok here - * @author is actually ok + * @author is not ment to be used * @category is irrelevant * @package is not ment to be used * @subpackage does not belong here diff --git a/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.php b/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.php index 9b5b2f85..b6a1c206 100644 --- a/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.php +++ b/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.php @@ -29,6 +29,7 @@ public function getWarningList($testFile = '') 35 => 1, 44 => 1, 52 => 1, + 63 => 1, 64 => 1, 65 => 1, 66 => 1, From 1f76b15da9af73ee88b41ad2ddedaa0f5ab29a8a Mon Sep 17 00:00:00 2001 From: Dan Wallis Date: Wed, 6 Jul 2022 15:51:56 +0100 Subject: [PATCH 3/8] Correct spelling/grammar --- .../ClassAndInterfacePHPDocFormattingUnitTest.1.inc | 4 ++-- .../ClassAndInterfacePHPDocFormattingUnitTest.2.inc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.1.inc b/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.1.inc index e2727967..146cded2 100644 --- a/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.1.inc +++ b/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.1.inc @@ -60,9 +60,9 @@ class EmptyHandler * @api is ok here * @deprecated can be used in this context * @see is ok here - * @author is not ment to be used + * @author should not be used * @category is irrelevant - * @package is not ment to be used + * @package should not be used * @subpackage does not belong here */ class ExampleHandler diff --git a/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.2.inc b/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.2.inc index e45e15ff..b6decf6c 100644 --- a/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.2.inc +++ b/Magento2/Tests/Commenting/ClassAndInterfacePHPDocFormattingUnitTest.2.inc @@ -60,9 +60,9 @@ interface EmptyHandler * @api is ok here * @deprecated can be used in this context * @see is ok here - * @author is not ment to be used + * @author should not be used * @category is irrelevant - * @package is not ment to be used + * @package should not be used * @subpackage does not belong here */ interface ExampleHandler From 1798cf49dd752411f373e2fb3e7d028289fc7311 Mon Sep 17 00:00:00 2001 From: Dan Wallis Date: Sat, 15 Oct 2022 12:02:16 +0100 Subject: [PATCH 4/8] Fix PHP error with short class names --- Magento2/Sniffs/Less/ClassNamingSniff.php | 3 ++- Magento2/Tests/Less/ClassNamingUnitTest.less | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Magento2/Sniffs/Less/ClassNamingSniff.php b/Magento2/Sniffs/Less/ClassNamingSniff.php index e684172b..9a2e1dd0 100644 --- a/Magento2/Sniffs/Less/ClassNamingSniff.php +++ b/Magento2/Sniffs/Less/ClassNamingSniff.php @@ -66,7 +66,8 @@ public function process(File $phpcsFile, $stackPtr) [implode("", $matches[0])] ); } - if (strpos($className, self::STRING_HELPER_CLASSES_PREFIX, 2) !== false) { + + if (strlen($className) > 1 && strpos($className, self::STRING_HELPER_CLASSES_PREFIX, 2) !== false) { $phpcsFile->addError( 'CSS class names should be separated with "-" (dash) instead of "_" (underscore)', $stackPtr, diff --git a/Magento2/Tests/Less/ClassNamingUnitTest.less b/Magento2/Tests/Less/ClassNamingUnitTest.less index ba60c205..f67a938b 100644 --- a/Magento2/Tests/Less/ClassNamingUnitTest.less +++ b/Magento2/Tests/Less/ClassNamingUnitTest.less @@ -30,3 +30,8 @@ .category-title { background: green; } + +// @see https://github.com/magento/magento-coding-standard/issues/425 +.a { + text-decoration: none; +} From 255e0290f9b50cae5e1efc5973bd1f7df200489f Mon Sep 17 00:00:00 2001 From: Dan Wallis Date: Thu, 16 Feb 2023 12:01:10 +0000 Subject: [PATCH 5/8] Add another test case --- Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.inc | 2 ++ Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.php | 1 + 2 files changed, 3 insertions(+) diff --git a/Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.inc b/Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.inc index b254ebc0..14f7ba1e 100644 --- a/Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.inc +++ b/Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.inc @@ -3,3 +3,5 @@ + + diff --git a/Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.php b/Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.php index 159ddedb..c7a68fe5 100644 --- a/Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.php +++ b/Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.php @@ -24,6 +24,7 @@ public function getWarningList() { return [ 5 => 1, + 7 => 1, ]; } } From 4612aa3600a8bd0b46cf1bacd8fd9ed45c53e8fe Mon Sep 17 00:00:00 2001 From: Dan Wallis Date: Thu, 16 Feb 2023 12:02:29 +0000 Subject: [PATCH 6/8] Auto-fix short echo syntax --- Magento2/Sniffs/PHP/ShortEchoSyntaxSniff.php | 21 ++++++++++++++++++- .../PHP/ShortEchoSyntaxUnitTest.inc.fixed | 7 +++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.inc.fixed diff --git a/Magento2/Sniffs/PHP/ShortEchoSyntaxSniff.php b/Magento2/Sniffs/PHP/ShortEchoSyntaxSniff.php index 946bb273..da1aa279 100644 --- a/Magento2/Sniffs/PHP/ShortEchoSyntaxSniff.php +++ b/Magento2/Sniffs/PHP/ShortEchoSyntaxSniff.php @@ -37,11 +37,30 @@ public function process(File $phpcsFile, $stackPtr) $nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); if ($tokens[$nextToken]['code'] == T_ECHO) { - $phpcsFile->addWarning( + $fix = $phpcsFile->addFixableWarning( 'Short echo tag syntax must be used; expected "fixer->beginChangeset(); + + if (($nextToken - $stackPtr) === 1) { + $phpcsFile->fixer->replaceToken($stackPtr, 'fixer->replaceToken($stackPtr, 'fixer->replaceToken($i, ''); + } + } + + $phpcsFile->fixer->replaceToken($nextToken, ''); + $phpcsFile->fixer->endChangeset(); + } } } } diff --git a/Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.inc.fixed b/Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.inc.fixed new file mode 100644 index 00000000..a3073e65 --- /dev/null +++ b/Magento2/Tests/PHP/ShortEchoSyntaxUnitTest.inc.fixed @@ -0,0 +1,7 @@ + + + + + + + From 0197a5ff31b4a5a816047e70cb015d00dde0d698 Mon Sep 17 00:00:00 2001 From: soumah Date: Wed, 26 Apr 2023 10:36:55 -0500 Subject: [PATCH 7/8] ACP2E-1887: Remove phpcsutils classes and add dependency on phpcsstandards/phpcsutils --- .../PHPCSUtils/BackCompat/BCTokens.php | 109 --- .../Helpers/PHPCSUtils/BackCompat/Helper.php | 242 ------ .../TestUtils/UtilityMethodTestCase.php | 450 ----------- .../Helpers/PHPCSUtils/Tokens/Collections.php | 369 --------- .../Helpers/PHPCSUtils/Utils/Conditions.php | 119 --- .../PHPCSUtils/Utils/FunctionDeclarations.php | 754 ------------------ .../PHPCSUtils/Utils/GetTokensAsString.php | 188 ----- .../PHPCSUtils/Utils/ObjectDeclarations.php | 363 --------- .../Helpers/PHPCSUtils/Utils/Parentheses.php | 155 ---- Magento2/Helpers/PHPCSUtils/Utils/Scopes.php | 80 -- .../Helpers/PHPCSUtils/Utils/TextStrings.php | 133 --- .../PHPCSUtils/Utils/UseStatements.php | 412 ---------- .../ChangedIntToBoolParamTypeSniff.php | 2 +- .../ForbiddenFinalPrivateMethodsSniff.php | 4 +- ...llingDestructAfterConstructorExitSniff.php | 15 +- ...emovedOptionalBeforeRequiredParamSniff.php | 18 +- .../Tests/PHPCompatibility/BaseSniffTest.php | 2 +- .../Util/CoreMethodTestFrame.php | 51 -- .../Util/TestHelperPHPCompatibility.php | 92 --- composer.json | 8 +- composer.lock | 154 +++- 21 files changed, 178 insertions(+), 3542 deletions(-) delete mode 100644 Magento2/Helpers/PHPCSUtils/BackCompat/BCTokens.php delete mode 100644 Magento2/Helpers/PHPCSUtils/BackCompat/Helper.php delete mode 100644 Magento2/Helpers/PHPCSUtils/TestUtils/UtilityMethodTestCase.php delete mode 100644 Magento2/Helpers/PHPCSUtils/Tokens/Collections.php delete mode 100644 Magento2/Helpers/PHPCSUtils/Utils/Conditions.php delete mode 100644 Magento2/Helpers/PHPCSUtils/Utils/FunctionDeclarations.php delete mode 100644 Magento2/Helpers/PHPCSUtils/Utils/GetTokensAsString.php delete mode 100644 Magento2/Helpers/PHPCSUtils/Utils/ObjectDeclarations.php delete mode 100644 Magento2/Helpers/PHPCSUtils/Utils/Parentheses.php delete mode 100644 Magento2/Helpers/PHPCSUtils/Utils/Scopes.php delete mode 100644 Magento2/Helpers/PHPCSUtils/Utils/TextStrings.php delete mode 100644 Magento2/Helpers/PHPCSUtils/Utils/UseStatements.php delete mode 100644 Magento2/Tests/PHPCompatibility/Util/CoreMethodTestFrame.php delete mode 100644 Magento2/Tests/PHPCompatibility/Util/TestHelperPHPCompatibility.php diff --git a/Magento2/Helpers/PHPCSUtils/BackCompat/BCTokens.php b/Magento2/Helpers/PHPCSUtils/BackCompat/BCTokens.php deleted file mode 100644 index 4542f9c6..00000000 --- a/Magento2/Helpers/PHPCSUtils/BackCompat/BCTokens.php +++ /dev/null @@ -1,109 +0,0 @@ - => - */ - private static $ooScopeTokens = [ - \T_CLASS => \T_CLASS, - \T_ANON_CLASS => \T_ANON_CLASS, - \T_INTERFACE => \T_INTERFACE, - \T_TRAIT => \T_TRAIT, - ]; - - /** - * Tokens that open class and object scopes. - * - * Retrieve the OO scope tokens array in a cross-version compatible manner. - * - * Changelog for the PHPCS native array: - * - Introduced in PHPCS 3.1.0. - * - * @see \PHP_CodeSniffer\Util\Tokens::$ooScopeTokens Original array. - * - * @since 1.0.0 - * - * @return array => Token array. - */ - public static function ooScopeTokens() - { - return self::$ooScopeTokens; - } - - /** - * Tokens that represent arithmetic operators. - * - * Retrieve the PHPCS arithmetic tokens array in a cross-version compatible manner. - * - * Changelog for the PHPCS native array: - * - Introduced in PHPCS 0.5.0. - * - PHPCS 2.9.0: The PHP 5.6 `T_POW` token was added to the array. - * The `T_POW` token was introduced in PHPCS 2.4.0. - * - * @see \PHP_CodeSniffer\Util\Tokens::$arithmeticTokens Original array. - * - * @since 1.0.0 - * - * @return array => Token array or an empty array for PHPCS versions in - * which the PHPCS native comment tokens did not exist yet. - */ - public static function arithmeticTokens() - { - return Tokens::$arithmeticTokens + [\T_POW => \T_POW]; - } - - /** - * Tokens that represent assignment operators. - * - * Retrieve the PHPCS assignment tokens array in a cross-version compatible manner. - * - * Changelog for the PHPCS native array: - * - Introduced in PHPCS 0.0.5. - * - PHPCS 2.9.0: The PHP 7.4 `T_COALESCE_EQUAL` token was added to the array. - * The `T_COALESCE_EQUAL` token was introduced in PHPCS 2.8.1. - * - PHPCS 3.2.0: The JS `T_ZSR_EQUAL` token was added to the array. - * The `T_ZSR_EQUAL` token was introduced in PHPCS 2.8.0. - * - * @see \PHP_CodeSniffer\Util\Tokens::$assignmentTokens Original array. - * - * @since 1.0.0 - * - * @return array => Token array. - */ - public static function assignmentTokens() - { - $tokens = Tokens::$assignmentTokens; - - /* - * The `T_COALESCE_EQUAL` token may be available pre-PHPCS 2.8.1 depending on - * the PHP version used to run PHPCS. - */ - if (\defined('T_COALESCE_EQUAL')) { - $tokens[\T_COALESCE_EQUAL] = \T_COALESCE_EQUAL; - } - - if (\defined('T_ZSR_EQUAL')) { - $tokens[\T_ZSR_EQUAL] = \T_ZSR_EQUAL; - } - - return $tokens; - } -} diff --git a/Magento2/Helpers/PHPCSUtils/BackCompat/Helper.php b/Magento2/Helpers/PHPCSUtils/BackCompat/Helper.php deleted file mode 100644 index d82f39c2..00000000 --- a/Magento2/Helpers/PHPCSUtils/BackCompat/Helper.php +++ /dev/null @@ -1,242 +0,0 @@ -config` property should work - * in PHPCS 3.x and higher. - * - * @return bool Whether the setting of the data was successfull. - */ - public static function setConfigData($key, $value, $temp = false, $config = null) - { - if (\method_exists('\PHP_CodeSniffer\Config', 'setConfigData') === false) { - // PHPCS 2.x. - return \PHP_CodeSniffer::setConfigData($key, $value, $temp); - } - - if (isset($config) === true) { - // PHPCS 3.x and 4.x. - return $config->setConfigData($key, $value, $temp); - } - - if (\version_compare(self::getVersion(), '3.99.99', '>') === true) { - throw new RuntimeException('Passing the $config parameter is required in PHPCS 4.x'); - } - - // PHPCS 3.x. - return \PHP_CodeSniffer\Config::setConfigData($key, $value, $temp); - } - - /** - * Get the value of a single PHP_CodeSniffer config key. - * - * @see Helper::getCommandLineData() Alternative for the same which is more reliable - * if the `$phpcsFile` object is available. - * - * @since 1.0.0 - * - * @param string $key The name of the config value. - * - * @return string|null - */ - public static function getConfigData($key) - { - if (\method_exists('\PHP_CodeSniffer\Config', 'getConfigData') === false) { - // PHPCS 2.x. - return \PHP_CodeSniffer::getConfigData($key); - } - - // PHPCS 3.x. - return \PHP_CodeSniffer\Config::getConfigData($key); - } - - /** - * Get the value of a CLI overrulable single PHP_CodeSniffer config key. - * - * Use this for config keys which can be set in the `CodeSniffer.conf` file, - * on the command-line or in a ruleset. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being processed. - * @param string $key The name of the config value. - * - * @return string|null - */ - public static function getCommandLineData(File $phpcsFile, $key) - { - if (\class_exists('\PHP_CodeSniffer\Config') === false) { - // PHPCS 2.x. - $config = $phpcsFile->phpcs->cli->getCommandLineValues(); - if (isset($config[$key])) { - return $config[$key]; - } - } else { - // PHPCS 3.x. - $config = $phpcsFile->config; - if (isset($config->{$key})) { - return $config->{$key}; - } - } - - return null; - } - - /** - * Get the applicable tab width as passed to PHP_CodeSniffer from the - * command-line or the ruleset. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being processed. - * - * @return int Tab width. Defaults to the PHPCS native default of 4. - */ - public static function getTabWidth(File $phpcsFile) - { - $tabWidth = self::getCommandLineData($phpcsFile, 'tabWidth'); - if ($tabWidth > 0) { - return (int) $tabWidth; - } - - return self::DEFAULT_TABWIDTH; - } - - /** - * Get the applicable (file) encoding as passed to PHP_CodeSniffer from the - * command-line or the ruleset. - * - * @since 1.0.0-alpha3 - * - * @param \PHP_CodeSniffer\Files\File|null $phpcsFile Optional. The current file being processed. - * - * @return string Encoding. Defaults to the PHPCS native default, which is 'utf-8' - * for PHPCS 3.x and was 'iso-8859-1' for PHPCS 2.x. - */ - public static function getEncoding(File $phpcsFile = null) - { - static $default; - - if (isset($default) === false) { - $default = 'utf-8'; - if (\version_compare(self::getVersion(), '2.99.99', '<=') === true) { - // In PHPCS 2.x, the default encoding is `iso-8859-1`. - $default = 'iso-8859-1'; - } - } - - if ($phpcsFile instanceof File) { - // Most reliable. - $encoding = self::getCommandLineData($phpcsFile, 'encoding'); - if ($encoding === null) { - $encoding = $default; - } - - return $encoding; - } - - // Less reliable. - $encoding = self::getConfigData('encoding'); - if ($encoding === null) { - $encoding = $default; - } - - return $encoding; - } - - /** - * Check whether the "--ignore-annotations" option is in effect. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File|null $phpcsFile Optional. The current file being processed. - * - * @return bool `TRUE` if annotations should be ignored, `FALSE` otherwise. - */ - public static function ignoreAnnotations(File $phpcsFile = null) - { - if (\class_exists('\PHP_CodeSniffer\Config') === false) { - // PHPCS 2.x does not support `--ignore-annotations`. - return false; - } - - // PHPCS 3.x. - if (isset($phpcsFile, $phpcsFile->config->annotations)) { - return ! $phpcsFile->config->annotations; - } - - $annotations = \PHP_CodeSniffer\Config::getConfigData('annotations'); - if (isset($annotations)) { - return ! $annotations; - } - - return false; - } -} diff --git a/Magento2/Helpers/PHPCSUtils/TestUtils/UtilityMethodTestCase.php b/Magento2/Helpers/PHPCSUtils/TestUtils/UtilityMethodTestCase.php deleted file mode 100644 index 7ffcc1e3..00000000 --- a/Magento2/Helpers/PHPCSUtils/TestUtils/UtilityMethodTestCase.php +++ /dev/null @@ -1,450 +0,0 @@ -getTargetToken($commentString, [\T_TOKEN_CONSTANT, \T_ANOTHER_TOKEN]); - * $class = new ClassUnderTest(); - * $result = $class->MyMethod(self::$phpcsFile, $stackPtr); - * // Or for static utility methods: - * $result = ClassUnderTest::MyMethod(self::$phpcsFile, $stackPtr); - * - * $this->assertSame($expected, $result); - * } - * - * /** - * * Data Provider. - * * - * * @see ClassUnderTestUnitTest::testMyMethod() For the array format. - * * - * * @return array - * * / - * public function dataMyMethod() - * { - * return array( - * array('/* testTestCaseDescription * /', false), - * ); - * } - * } - * ``` - * - * Note: - * - Remove the space between the comment closers `* /` for a working example. - * - Each test case separator comment MUST start with `/* test`. - * This is to allow the {@see UtilityMethodTestCase::getTargetToken()} method to - * distinquish between the test separation comments and comments which may be part - * of the test case. - * - The test case file and unit test file should be placed in the same directory. - * - For working examples using this abstract class, have a look at the unit tests - * for the PHPCSUtils utility functions themselves. - * - * @since 1.0.0 - */ -abstract class UtilityMethodTestCase extends TestCase -{ - - /** - * The PHPCS version the tests are being run on. - * - * @since 1.0.0-alpha3 - * - * @var string - */ - protected static $phpcsVersion = '0'; - - /** - * The file extension of the test case file (without leading dot). - * - * This allows concrete test classes to overrule the default `"inc"` with, for instance, - * `"js"` or `"css"` when applicable. - * - * @since 1.0.0 - * - * @var string - */ - protected static $fileExtension = 'inc'; - - /** - * Full path to the test case file associated with the concrete test class. - * - * Optional. If left empty, the case file will be presumed to be in - * the same directory and named the same as the test class, but with an - * `"inc"` file extension. - * - * @since 1.0.0 - * - * @var string - */ - protected static $caseFile = ''; - - /** - * The tab width setting to use when tokenizing the file. - * - * This allows for test case files to use a different tab width than the default. - * - * @since 1.0.0 - * - * @var int - */ - protected static $tabWidth = 4; - - /** - * The \PHP_CodeSniffer\Files\File object containing the parsed contents of the test case file. - * - * @since 1.0.0 - * - * @var \PHP_CodeSniffer\Files\File - */ - protected static $phpcsFile; - - /** - * Set the name of a sniff to pass to PHPCS to limit the run (and force it to record errors). - * - * Normally, this propery won't need to be overloaded, but for utility methods which record - * violations and contain fixers, setting a dummy sniff name equal to the sniff name passed - * in the error code for `addError()`/`addWarning()` during the test, will allow for testing - * the recording of these violations, as well as testing the fixer. - * - * @since 1.0.0 - * - * @var array - */ - protected static $selectedSniff = ['Dummy.Dummy.Dummy']; - - /** - * Initialize PHPCS & tokenize the test case file. - * - * The test case file for a unit test class has to be in the same directory - * directory and use the same file name as the test class, using the `.inc` extension - * or be explicitly set using the {@see UtilityMethodTestCase::$fileExtension}/ - * {@see UtilityMethodTestCase::$caseFile} properties. - * - * Note: This is a PHPUnit cross-version compatible {@see \PHPUnit\Framework\TestCase::setUpBeforeClass()} - * method. - * - * @since 1.0.0 - * - * @beforeClass - * - * @return void - */ - public static function setUpTestFile() - { - parent::setUpBeforeClass(); - - self::$phpcsVersion = Helper::getVersion(); - - $caseFile = static::$caseFile; - if (\is_string($caseFile) === false || $caseFile === '') { - $testClass = \get_called_class(); - $testFile = (new ReflectionClass($testClass))->getFileName(); - $caseFile = \substr($testFile, 0, -3) . static::$fileExtension; - } - - if (\is_readable($caseFile) === false) { - parent::fail("Test case file missing. Expected case file location: $caseFile"); - } - - $contents = \file_get_contents($caseFile); - - if (\version_compare(self::$phpcsVersion, '2.99.99', '>')) { - // PHPCS 3.x. - $config = new \PHP_CodeSniffer\Config(); - - /* - * We just need to provide a standard so PHPCS will tokenize the file. - * The standard itself doesn't actually matter for testing utility methods, - * so use the smallest one to get the fastest results. - */ - $config->standards = ['PSR1']; - - /* - * Limiting the run to just one sniff will make it, yet again, slightly faster. - * Picked the simplest/fastest sniff available which is registered in PSR1. - */ - $config->sniffs = static::$selectedSniff; - - // Disable caching. - $config->cache = false; - - // Also set a tab-width to enable testing tab-replaced vs `orig_content`. - $config->tabWidth = static::$tabWidth; - - $ruleset = new \PHP_CodeSniffer\Ruleset($config); - - // Make sure the file gets parsed correctly based on the file type. - $contents = 'phpcs_input_file: ' . $caseFile . \PHP_EOL . $contents; - - self::$phpcsFile = new \PHP_CodeSniffer\Files\DummyFile($contents, $ruleset, $config); - - // Only tokenize the file, do not process it. - try { - self::$phpcsFile->parse(); - } catch (TokenizerException $e) { - // PHPCS 3.5.0 and higher. - } catch (RuntimeException $e) { - // PHPCS 3.0.0 < 3.5.0. - } - } else { - // PHPCS 2.x. - $phpcs = new \PHP_CodeSniffer(null, static::$tabWidth); - self::$phpcsFile = new \PHP_CodeSniffer_File( - $caseFile, - [], - [], - $phpcs - ); - - /* - * Using error silencing to drown out "undefined index" notices for tokenizer - * issues in PHPCS 2.x which won't get fixed anymore anyway. - */ - @self::$phpcsFile->start($contents); - } - - // Fail the test if the case file failed to tokenize. - if (self::$phpcsFile->numTokens === 0) { - parent::fail("Tokenizing of the test case file failed for case file: $caseFile"); - } - } - - /** - * Skip JS and CSS related tests on PHPCS 4.x. - * - * PHPCS 4.x drops support for the JS and CSS tokenizers. - * This method takes care of automatically skipping tests involving JS/CSS case files - * when the tests are being run with PHPCS 4.x. - * - * Note: This is a PHPUnit cross-version compatible {@see \PHPUnit\Framework\TestCase::setUp()} - * method. - * - * @since 1.0.0-alpha3 - * - * @before - * - * @return void - */ - public function skipJSCSSTestsOnPHPCS4() - { - if (static::$fileExtension !== 'js' && static::$fileExtension !== 'css') { - return; - } - - if (\version_compare(self::$phpcsVersion, '3.99.99', '<=')) { - return; - } - - $this->markTestSkipped('JS and CSS support has been removed in PHPCS 4.'); - } - - /** - * Clean up after finished test. - * - * Note: This is a PHPUnit cross-version compatible {@see \PHPUnit\Framework\TestCase::tearDownAfterClass()} - * method. - * - * @since 1.0.0 - * - * @afterClass - * - * @return void - */ - public static function resetTestFile() - { - self::$phpcsFile = null; - } - - /** - * Check whether or not the PHP 8.0 identifier name tokens will be in use. - * - * The expected token positions/token counts for certain tokens will differ depending - * on whether the PHP 8.0 identifier name tokenization is used or the PHP < 8.0 - * identifier name tokenization. - * - * Tests can use this method to determine which flavour of tokenization to expect and - * to set test expectations accordingly. - * - * @codeCoverageIgnore Nothing to test. - * - * @since 1.0.0 - * - * @return bool - */ - public static function usesPhp8NameTokens() - { - $version = Helper::getVersion(); - if ((\version_compare(\PHP_VERSION_ID, '80000', '>=') === true - && \version_compare($version, '3.5.7', '<') === true) - || \version_compare($version, '4.0.0', '>=') === true - ) { - return true; - } - - return false; - } - - /** - * Get the token pointer for a target token based on a specific comment. - * - * Note: the test delimiter comment MUST start with `/* test` to allow this function to - * distinguish between comments used *in* a test and test delimiters. - * - * If the delimiter comment is not found, the test will automatically be failed. - * - * @since 1.0.0 - * - * @param string $commentString The complete delimiter comment to look for as a string. - * This string should include the comment opener and closer. - * @param int|string|array $tokenType The type of token(s) to look for. - * @param string $tokenContent Optional. The token content for the target token. - * @param bool $failTest Optional. Whether the test should be marked as failed when - * the target token cannot be found. Defaults to `true`. - * When set to `false`, a catchable PHP native `RuntimeException` - * will be thrown instead. - * - * @return int - * - * @throws \RuntimeException When the target token cannot be found and `$failTest` has been set to `false`. - */ - public function getTargetToken($commentString, $tokenType, $tokenContent = null, $failTest = true) - { - $start = (self::$phpcsFile->numTokens - 1); - $comment = self::$phpcsFile->findPrevious( - \T_COMMENT, - $start, - null, - false, - $commentString - ); - - if ($comment === false) { - $msg = 'Failed to find the test marker: ' . $commentString; - $this->fail($msg); - } - - $tokens = self::$phpcsFile->getTokens(); - $end = ($start + 1); - - // Limit the token finding to between this and the next delimiter comment. - for ($i = ($comment + 1); $i < $end; $i++) { - if ($tokens[$i]['code'] !== \T_COMMENT) { - continue; - } - - if (\stripos($tokens[$i]['content'], '/* test') === 0) { - $end = $i; - break; - } - } - - $target = self::$phpcsFile->findNext( - $tokenType, - ($comment + 1), - $end, - false, - $tokenContent - ); - - if ($target === false) { - $msg = 'Failed to find test target token for comment string: ' . $commentString; - if ($tokenContent !== null) { - $msg .= ' With token content: ' . $tokenContent; - } - - if ($failTest === false) { - throw new PHPRuntimeException($msg); - } - - $this->fail($msg); - } - - return $target; - } - - /** - * Helper method to tell PHPUnit to expect a PHPCS Exception in a PHPUnit and PHPCS cross-version - * compatible manner. - * - * @since 1.0.0 - * - * @param string $msg The expected exception message. - * @param string $type The PHPCS native exception type to expect. Either 'runtime' or 'tokenizer'. - * Defaults to 'runtime'. - * - * @return void - */ - public function expectPhpcsException($msg, $type = 'runtime') - { - $exception = 'PHP_CodeSniffer\Exceptions\RuntimeException'; - if ($type === 'tokenizer') { - $exception = 'PHP_CodeSniffer\Exceptions\TokenizerException'; - } - - if (\method_exists($this, 'expectException')) { - // PHPUnit 5+. - $this->expectException($exception); - $this->expectExceptionMessage($msg); - } else { - // PHPUnit 4. - $this->setExpectedException($exception, $msg); - } - } -} diff --git a/Magento2/Helpers/PHPCSUtils/Tokens/Collections.php b/Magento2/Helpers/PHPCSUtils/Tokens/Collections.php deleted file mode 100644 index bc8e30a4..00000000 --- a/Magento2/Helpers/PHPCSUtils/Tokens/Collections.php +++ /dev/null @@ -1,369 +0,0 @@ - => - */ - public static $magicConstants = [ - \T_CLASS_C => \T_CLASS_C, - \T_DIR => \T_DIR, - \T_FILE => \T_FILE, - \T_FUNC_C => \T_FUNC_C, - \T_LINE => \T_LINE, - \T_METHOD_C => \T_METHOD_C, - \T_NS_C => \T_NS_C, - \T_TRAIT_C => \T_TRAIT_C, - ]; - - /** - * Object operators. - * - * @since 1.0.0-alpha3 - * - * @var array => - */ - public static $objectOperators = [ - \T_OBJECT_OPERATOR => \T_OBJECT_OPERATOR, - \T_DOUBLE_COLON => \T_DOUBLE_COLON, - ]; - - /** - * Tokens types used for "forwarding" calls within OO structures. - * - * @link https://www.php.net/language.oop5.paamayim-nekudotayim PHP Manual on OO forwarding calls - * - * @since 1.0.0-alpha3 - * - * @var array => - */ - public static $OOHierarchyKeywords = [ - \T_PARENT => \T_PARENT, - \T_SELF => \T_SELF, - \T_STATIC => \T_STATIC, - ]; - - /** - * List of tokens which represent "closed" scopes. - * - * I.e. anything declared within that scope - except for other closed scopes - is - * outside of the global namespace. - * - * This list doesn't contain the `T_NAMESPACE` token on purpose as variables declared - * within a namespace scope are still global and not limited to that namespace. - * - * @since 1.0.0 - * - * @var array => - */ - public static $closedScopes = [ - \T_CLASS => \T_CLASS, - \T_ANON_CLASS => \T_ANON_CLASS, - \T_INTERFACE => \T_INTERFACE, - \T_TRAIT => \T_TRAIT, - \T_FUNCTION => \T_FUNCTION, - \T_CLOSURE => \T_CLOSURE, - ]; - - /** - * OO structures which can use the "extends" keyword. - * - * @since 1.0.0 - * - * @var array => - */ - public static $OOCanExtend = [ - \T_CLASS => \T_CLASS, - \T_ANON_CLASS => \T_ANON_CLASS, - \T_INTERFACE => \T_INTERFACE, - ]; - - /** - * Tokens which can start a - potentially multi-line - text string. - * - * @since 1.0.0 - * - * @var array => - */ - public static $textStingStartTokens = [ - \T_START_HEREDOC => \T_START_HEREDOC, - \T_START_NOWDOC => \T_START_NOWDOC, - \T_CONSTANT_ENCAPSED_STRING => \T_CONSTANT_ENCAPSED_STRING, - \T_DOUBLE_QUOTED_STRING => \T_DOUBLE_QUOTED_STRING, - ]; - - /** - * Tokens which can represent the arrow function keyword. - * - * Note: this is a method, not a property as the `T_FN` token for arrow functions may not exist. - * - * @since 1.0.0-alpha2 - * - * @return array => - */ - public static function arrowFunctionTokensBC() - { - $tokens = [ - \T_STRING => \T_STRING, - ]; - - if (\defined('T_FN') === true) { - // PHP 7.4 or PHPCS 3.5.3+. - $tokens[\T_FN] = \T_FN; - } - - return $tokens; - } - - /** - * Tokens which can represent a keyword which starts a function declaration. - * - * Note: this is a method, not a property as the `T_FN` token for arrow functions may not exist. - * - * Sister-method to the {@see Collections::functionDeclarationTokensBC()} method. - * This method supports PHPCS 3.5.3 and up. - * The {@see Collections::functionDeclarationTokensBC()} method supports PHPCS 2.6.0 and up. - * - * @see \PHPCSUtils\Tokens\Collections::functionDeclarationTokensBC() Related method (PHPCS 2.6.0+). - * - * @since 1.0.0-alpha3 - * - * @return array => - */ - public static function functionDeclarationTokens() - { - $tokens = [ - \T_FUNCTION => \T_FUNCTION, - \T_CLOSURE => \T_CLOSURE, - ]; - - if (\defined('T_FN') === true) { - // PHP 7.4 or PHPCS 3.5.3+. - $tokens[\T_FN] = \T_FN; - } - - return $tokens; - } - - /** - * Tokens which can represent a keyword which starts a function declaration. - * - * Note: this is a method, not a property as the `T_FN` token for arrow functions may not exist. - * - * Sister-method to the {@see Collections::functionDeclarationTokens()} method. - * The {@see Collections::functionDeclarationTokens()} method supports PHPCS 3.5.3 and up. - * This method supports PHPCS 2.6.0 and up. - * - * Notable difference: - * - This method accounts for when the `T_FN` token doesn't exist. - * - * Note: if this method is used, the {@see \PHPCSUtils\Utils\FunctionDeclarations::isArrowFunction()} - * method needs to be used on arrow function tokens to verify whether it really is an arrow function - * declaration or not. - * - * It is recommended to use the {@see Collections::functionDeclarationTokens()} method instead of - * this method if a standard supports does not need to support PHPCS < 3.5.3. - * - * @see \PHPCSUtils\Tokens\Collections::functionDeclarationTokens() Related method (PHPCS 3.5.3+). - * @see \PHPCSUtils\Utils\FunctionDeclarations::isArrowFunction() Arrow function verification. - * - * @since 1.0.0-alpha3 - * - * @return array => - */ - public static function functionDeclarationTokensBC() - { - $tokens = [ - \T_FUNCTION => \T_FUNCTION, - \T_CLOSURE => \T_CLOSURE, - ]; - - $tokens += self::arrowFunctionTokensBC(); - - return $tokens; - } - - /** - * The tokens used for "names", be it namespace, OO, function or constant names. - * - * Includes the tokens introduced in PHP 8.0 for "Namespaced names as single token" when available. - * - * Note: this is a method, not a property as the PHP 8.0 identifier name tokens may not exist. - * - * @link https://wiki.php.net/rfc/namespaced_names_as_token - * - * @since 1.0.0-alpha4 - * - * @return array => - */ - public static function nameTokens() - { - $tokens = [ - \T_STRING => \T_STRING, - ]; - - /* - * PHP >= 8.0 in combination with PHPCS < 3.5.7 and all PHP versions in combination - * with PHPCS >= 3.5.7, though when using PHPCS 3.5.7 < 4.0.0, these tokens are - * not yet in use, i.e. the PHP 8.0 change is "undone" for PHPCS 3.x. - */ - if (\defined('T_NAME_QUALIFIED') === true) { - $tokens[\T_NAME_QUALIFIED] = \T_NAME_QUALIFIED; - } - - if (\defined('T_NAME_FULLY_QUALIFIED') === true) { - $tokens[\T_NAME_FULLY_QUALIFIED] = \T_NAME_FULLY_QUALIFIED; - } - - if (\defined('T_NAME_RELATIVE') === true) { - $tokens[\T_NAME_RELATIVE] = \T_NAME_RELATIVE; - } - - return $tokens; - } - - /** - * Tokens types which can be encountered in a fully, partially or unqualified name. - * - * Example: - * ```php - * echo namespace\Sub\ClassName::method(); - * ``` - * - * @since 1.0.0-alpha4 - * - * @return array => - */ - public static function namespacedNameTokens() - { - $tokens = [ - \T_NS_SEPARATOR => \T_NS_SEPARATOR, - \T_NAMESPACE => \T_NAMESPACE, - ]; - - $tokens += self::nameTokens(); - - return $tokens; - } - /** - * Token types which can be encountered in a return type declaration. - * - * Note: this is a method, not a property as the `T_TYPE_UNION` token for PHP 8.0 union types may not exist. - * - * Sister-method to the {@see Collections::returnTypeTokensBC()} method. - * This method supports PHPCS 3.3.0 and up. - * The {@see Collections::returnTypeTokensBC()} method supports PHPCS 2.6.0 and up. - * - * Notable differences: - * - The {@see Collections::returnTypeTokensBC()} method will include the `T_ARRAY_HINT` - * and the `T_RETURN_TYPE` tokens when used with PHPCS 2.x and 3.x. - * These token constants will no longer exist in PHPCS 4.x. - * - * It is recommended to use this method instead of the {@see Collections::returnTypeTokensBC()} - * method if a standard does not need to support PHPCS < 3.3.0. - * - * @see \PHPCSUtils\Tokens\Collections::returnTypeTokensBC() Related method (cross-version). - * - * @since 1.0.0-alpha4 This method replaces the {@see Collections::$returnTypeTokens} property. - * @since 1.0.0-alpha4 Added support for PHP 8.0 union types. - * @since 1.0.0-alpha4 Added support for PHP 8.0 identifier name tokens. - * @since 1.0.0-alpha4 Added the T_TYPE_UNION token for union type support in PHPCS > 3.6.0. - * - * @return array => - */ - public static function returnTypeTokens() - { - $tokens = [ - \T_CALLABLE => \T_CALLABLE, - \T_SELF => \T_SELF, - \T_PARENT => \T_PARENT, - \T_STATIC => \T_STATIC, - \T_FALSE => \T_FALSE, // Union types only. - \T_NULL => \T_NULL, // Union types only. - \T_ARRAY => \T_ARRAY, // Arrow functions PHPCS < 3.5.4 + union types. - \T_BITWISE_OR => \T_BITWISE_OR, // Union types for PHPCS < 3.6.0. - ]; - - $tokens += self::namespacedNameTokens(); - - // PHPCS > 3.6.0: a new token was introduced for the union type separator. - if (\defined('T_TYPE_UNION') === true) { - $tokens[\T_TYPE_UNION] = \T_TYPE_UNION; - } - - return $tokens; - } - - /** - * Token types which can be encountered in a return type declaration (cross-version). - * - * Sister-method to the {@see Collections::returnTypeTokens()} method. - * The {@see Collections::returnTypeTokens()} method supports PHPCS 3.3.0 and up. - * This method supports PHPCS 2.6.0 and up. - * - * Notable differences: - * - This method will include the `T_ARRAY_HINT` and the `T_RETURN_TYPE` tokens when - * used with PHPCS 2.x and 3.x. - * These token constants will no longer exist in PHPCS 4.x. - * - * It is recommended to use the {@see Collections::returnTypeTokens()} method instead of - * this method if a standard does not need to support PHPCS < 3.3.0. - * - * @see \PHPCSUtils\Tokens\Collections::returnTypeTokens() Related method (PHPCS 3.3.0+). - * - * @since 1.0.0-alpha3 - * @since 1.0.0-alpha4 Added support for PHP 8.0 union types. - * @since 1.0.0-alpha4 Added support for PHP 8.0 identifier name tokens. - * - * @return array => - */ - public static function returnTypeTokensBC() - { - $tokens = self::returnTypeTokens(); - - /* - * PHPCS < 4.0. Needed for support of PHPCS 2.4.0 < 3.3.0. - * For PHPCS 3.3.0+ the constant is no longer used. - */ - if (\defined('T_RETURN_TYPE') === true) { - $tokens[\T_RETURN_TYPE] = \T_RETURN_TYPE; - } - - /* - * PHPCS < 4.0. Needed for support of PHPCS < 2.8.0 / PHPCS < 3.5.3 for arrow functions. - * For PHPCS 3.5.3+ the constant is no longer used. - */ - if (\defined('T_ARRAY_HINT') === true) { - $tokens[\T_ARRAY_HINT] = \T_ARRAY_HINT; - } - - return $tokens; - } -} diff --git a/Magento2/Helpers/PHPCSUtils/Utils/Conditions.php b/Magento2/Helpers/PHPCSUtils/Utils/Conditions.php deleted file mode 100644 index 4d34ed92..00000000 --- a/Magento2/Helpers/PHPCSUtils/Utils/Conditions.php +++ /dev/null @@ -1,119 +0,0 @@ -getTokens(); - - // Check for the existence of the token. - if (isset($tokens[$stackPtr]) === false) { - return false; - } - - // Make sure the token has conditions. - if (empty($tokens[$stackPtr]['conditions'])) { - return false; - } - - $types = (array) $types; - $conditions = $tokens[$stackPtr]['conditions']; - - if (empty($types) === true) { - // No types specified, just return the first/last condition pointer. - if ($first === false) { - \end($conditions); - } else { - \reset($conditions); - } - - return \key($conditions); - } - - if ($first === false) { - $conditions = \array_reverse($conditions, true); - } - - foreach ($conditions as $ptr => $type) { - if (isset($tokens[$ptr]) === true - && \in_array($type, $types, true) === true - ) { - // We found a token with the required type. - return $ptr; - } - } - - return false; - } - - /** - * Return the position of the last (innermost) condition of a certain type for the passed token. - * - * If no types are specified, the last condition for the token, independently of type, - * will be returned. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The position of the token we are checking. - * @param int|string|array $types Optional. The type(s) of tokens to search for. - * - * @return int|false Integer stack pointer to the condition; or `FALSE` if the token - * does not have the condition or has no conditions at all. - */ - public static function getLastCondition(File $phpcsFile, $stackPtr, $types = []) - { - return self::getCondition($phpcsFile, $stackPtr, $types, false); - } -} diff --git a/Magento2/Helpers/PHPCSUtils/Utils/FunctionDeclarations.php b/Magento2/Helpers/PHPCSUtils/Utils/FunctionDeclarations.php deleted file mode 100644 index 94641d48..00000000 --- a/Magento2/Helpers/PHPCSUtils/Utils/FunctionDeclarations.php +++ /dev/null @@ -1,754 +0,0 @@ - 'public', // Public, private, or protected - * 'scope_specified' => true, // TRUE if the scope keyword was found. - * 'return_type' => '', // The return type of the method. - * 'return_type_token' => integer, // The stack pointer to the start of the return type - * // or FALSE if there is no return type. - * 'return_type_end_token' => integer, // The stack pointer to the end of the return type - * // or FALSE if there is no return type. - * 'nullable_return_type' => false, // TRUE if the return type is nullable. - * 'is_abstract' => false, // TRUE if the abstract keyword was found. - * 'is_final' => false, // TRUE if the final keyword was found. - * 'is_static' => false, // TRUE if the static keyword was found. - * 'has_body' => false, // TRUE if the method has a body - * ); - * ``` - * - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a T_FUNCTION - * or T_CLOSURE token, nor an arrow function. - */ - public static function getProperties(File $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - $arrowOpenClose = self::getArrowFunctionOpenClose($phpcsFile, $stackPtr); - - if (isset($tokens[$stackPtr]) === false - || ($tokens[$stackPtr]['code'] !== \T_FUNCTION - && $tokens[$stackPtr]['code'] !== \T_CLOSURE - && $arrowOpenClose === false) - ) { - throw new RuntimeException('$stackPtr must be of type T_FUNCTION or T_CLOSURE or an arrow function'); - } - - if ($tokens[$stackPtr]['code'] === \T_FUNCTION) { - $valid = Tokens::$methodPrefixes; - } else { - $valid = [\T_STATIC => \T_STATIC]; - } - - $valid += Tokens::$emptyTokens; - - $scope = 'public'; - $scopeSpecified = false; - $isAbstract = false; - $isFinal = false; - $isStatic = false; - - for ($i = ($stackPtr - 1); $i > 0; $i--) { - if (isset($valid[$tokens[$i]['code']]) === false) { - break; - } - - switch ($tokens[$i]['code']) { - case \T_PUBLIC: - $scope = 'public'; - $scopeSpecified = true; - break; - case \T_PRIVATE: - $scope = 'private'; - $scopeSpecified = true; - break; - case \T_PROTECTED: - $scope = 'protected'; - $scopeSpecified = true; - break; - case \T_ABSTRACT: - $isAbstract = true; - break; - case \T_FINAL: - $isFinal = true; - break; - case \T_STATIC: - $isStatic = true; - break; - } - } - - $returnType = ''; - $returnTypeToken = false; - $returnTypeEndToken = false; - $nullableReturnType = false; - $hasBody = false; - $returnTypeTokens = Collections::returnTypeTokensBC(); - - $parenthesisCloser = null; - if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) { - $parenthesisCloser = $tokens[$stackPtr]['parenthesis_closer']; - } elseif ($arrowOpenClose !== false) { - // Arrow function in combination with PHP < 7.4 or PHPCS < 3.5.3. - $parenthesisCloser = $arrowOpenClose['parenthesis_closer']; - } - - if (isset($parenthesisCloser) === true) { - $scopeOpener = null; - if (isset($tokens[$stackPtr]['scope_opener']) === true) { - $scopeOpener = $tokens[$stackPtr]['scope_opener']; - } elseif ($arrowOpenClose !== false) { - // Arrow function in combination with PHP < 7.4 or PHPCS < 3.5.3. - $scopeOpener = $arrowOpenClose['scope_opener']; - } - - for ($i = $parenthesisCloser; $i < $phpcsFile->numTokens; $i++) { - if ($i === $scopeOpener) { - // End of function definition. - $hasBody = true; - break; - } - - if ($scopeOpener === null && $tokens[$i]['code'] === \T_SEMICOLON) { - // End of abstract/interface function definition. - break; - } - - if ($tokens[$i]['type'] === 'T_NULLABLE' - // Handle nullable tokens in PHPCS < 2.8.0. - || (\defined('T_NULLABLE') === false && $tokens[$i]['code'] === \T_INLINE_THEN) - // Handle nullable tokens with arrow functions in PHPCS 2.8.0 - 2.9.0. - || ($arrowOpenClose !== false && $tokens[$i]['code'] === \T_INLINE_THEN - && \version_compare(Helper::getVersion(), '2.9.1', '<') === true) - ) { - $nullableReturnType = true; - } - - if (isset($returnTypeTokens[$tokens[$i]['code']]) === true) { - if ($returnTypeToken === false) { - $returnTypeToken = $i; - } - - $returnType .= $tokens[$i]['content']; - $returnTypeEndToken = $i; - } - } - } - - if ($returnType !== '' && $nullableReturnType === true) { - $returnType = '?' . $returnType; - } - - return [ - 'scope' => $scope, - 'scope_specified' => $scopeSpecified, - 'return_type' => $returnType, - 'return_type_token' => $returnTypeToken, - 'return_type_end_token' => $returnTypeEndToken, - 'nullable_return_type' => $nullableReturnType, - 'is_abstract' => $isAbstract, - 'is_final' => $isFinal, - 'is_static' => $isStatic, - 'has_body' => $hasBody, - ]; - } - - /** - * Retrieve the parenthesis opener, parenthesis closer, the scope opener and the scope closer - * for an arrow function. - * - * Helper function for cross-version compatibility with both PHP as well as PHPCS. - * In PHPCS versions prior to PHPCS 3.5.3/3.5.4, the `T_FN` token is not yet backfilled - * and does not have parenthesis opener/closer nor scope opener/closer indexes assigned - * in the `$tokens` array. - * - * @see \PHPCSUtils\Utils\FunctionDeclarations::isArrowFunction() Related function. - * - * @since 1.0.0 - * @since 1.0.0-alpha4 Handling of PHP 8.0 identifier name tokens in return types, cross-version PHP & PHPCS. - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The token to retrieve the openers/closers for. - * Typically a `T_FN` or `T_STRING` token as those are the - * only two tokens which can be the arrow function keyword. - * - * @return array|false An array with the token pointers or `FALSE` if this is not an arrow function. - * The format of the return value is: - * ```php - * array( - * 'parenthesis_opener' => integer, // Stack pointer to the parenthesis opener. - * 'parenthesis_closer' => integer, // Stack pointer to the parenthesis closer. - * 'scope_opener' => integer, // Stack pointer to the scope opener (arrow). - * 'scope_closer' => integer, // Stack pointer to the scope closer. - * ) - * ``` - */ - public static function getArrowFunctionOpenClose(File $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - - if (isset($tokens[$stackPtr]) === false - || isset(Collections::arrowFunctionTokensBC()[$tokens[$stackPtr]['code']]) === false - || \strtolower($tokens[$stackPtr]['content']) !== 'fn' - ) { - return false; - } - - if ($tokens[$stackPtr]['type'] === 'T_FN' - && isset($tokens[$stackPtr]['scope_closer']) === true - && \version_compare(Helper::getVersion(), '3.5.4', '>') === true - ) { - // The keys will either all be set or none will be set, so no additional checks needed. - return [ - 'parenthesis_opener' => $tokens[$stackPtr]['parenthesis_opener'], - 'parenthesis_closer' => $tokens[$stackPtr]['parenthesis_closer'], - 'scope_opener' => $tokens[$stackPtr]['scope_opener'], - 'scope_closer' => $tokens[$stackPtr]['scope_closer'], - ]; - } - - /* - * This is either a T_STRING token pre-PHP 7.4, or T_FN on PHP 7.4 in combination - * with PHPCS < 3.5.3/4/5. - * - * Now see about finding the relevant arrow function tokens. - */ - $returnValue = []; - - $nextNonEmpty = $phpcsFile->findNext( - (Tokens::$emptyTokens + [\T_BITWISE_AND]), - ($stackPtr + 1), - null, - true - ); - if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) { - return false; - } - - $returnValue['parenthesis_opener'] = $nextNonEmpty; - if (isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false) { - return false; - } - - $returnValue['parenthesis_closer'] = $tokens[$nextNonEmpty]['parenthesis_closer']; - - $ignore = Tokens::$emptyTokens; - $ignore += Collections::returnTypeTokensBC(); - $ignore[\T_COLON] = \T_COLON; - $ignore[\T_INLINE_ELSE] = \T_INLINE_ELSE; // Return type colon on PHPCS < 2.9.1. - $ignore[\T_INLINE_THEN] = \T_INLINE_THEN; // Nullable type indicator on PHPCS < 2.9.1. - - if (\defined('T_NULLABLE') === true) { - $ignore[\T_NULLABLE] = \T_NULLABLE; - } - - $arrow = $phpcsFile->findNext( - $ignore, - ($tokens[$nextNonEmpty]['parenthesis_closer'] + 1), - null, - true - ); - - if ($arrow === false - || ($tokens[$arrow]['code'] !== \T_DOUBLE_ARROW && $tokens[$arrow]['type'] !== 'T_FN_ARROW') - ) { - return false; - } - - $returnValue['scope_opener'] = $arrow; - $inTernary = false; - $lastEndToken = null; - - for ($scopeCloser = ($arrow + 1); $scopeCloser < $phpcsFile->numTokens; $scopeCloser++) { - if (isset(self::$arrowFunctionEndTokens[$tokens[$scopeCloser]['code']]) === true - // BC for misidentified ternary else in some PHPCS versions. - && ($tokens[$scopeCloser]['code'] !== \T_COLON || $inTernary === false) - ) { - if ($lastEndToken !== null - && $tokens[$scopeCloser]['code'] === \T_CLOSE_PARENTHESIS - && $tokens[$scopeCloser]['parenthesis_opener'] < $arrow - ) { - $scopeCloser = $lastEndToken; - } - - break; - } - - if (isset(Collections::arrowFunctionTokensBC()[$tokens[$scopeCloser]['code']]) === true) { - $nested = self::getArrowFunctionOpenClose($phpcsFile, $scopeCloser); - if ($nested !== false && isset($nested['scope_closer'])) { - // We minus 1 here in case the closer can be shared with us. - $scopeCloser = ($nested['scope_closer'] - 1); - continue; - } - } - - if (isset($tokens[$scopeCloser]['scope_closer']) === true - && $tokens[$scopeCloser]['code'] !== \T_INLINE_ELSE - && $tokens[$scopeCloser]['code'] !== \T_END_HEREDOC - && $tokens[$scopeCloser]['code'] !== \T_END_NOWDOC - ) { - // We minus 1 here in case the closer can be shared with us. - $scopeCloser = ($tokens[$scopeCloser]['scope_closer'] - 1); - continue; - } - - if (isset($tokens[$scopeCloser]['parenthesis_closer']) === true) { - $scopeCloser = $tokens[$scopeCloser]['parenthesis_closer']; - $lastEndToken = $scopeCloser; - continue; - } - - if (isset($tokens[$scopeCloser]['bracket_closer']) === true) { - $scopeCloser = $tokens[$scopeCloser]['bracket_closer']; - $lastEndToken = $scopeCloser; - continue; - } - - if ($tokens[$scopeCloser]['code'] === \T_INLINE_THEN) { - $inTernary = true; - continue; - } - - if ($tokens[$scopeCloser]['code'] === \T_INLINE_ELSE) { - if ($inTernary === false) { - break; - } - - $inTernary = false; - } - } - - if ($scopeCloser === $phpcsFile->numTokens) { - return false; - } - - $returnValue['scope_closer'] = $scopeCloser; - - return $returnValue; - } - - /** - * Returns the declaration name for a function. - * - * Alias for the {@see \PHPCSUtils\Utils\ObjectDeclarations::getName()} method. - * - * @see \PHPCSUtils\BackCompat\BCFile::getDeclarationName() Original function. - * @see \PHPCSUtils\Utils\ObjectDeclarations::getName() PHPCSUtils native improved version. - * - * @since 1.0.0 - * - * @codeCoverageIgnore - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the function keyword token. - * - * @return string|null The name of the function; or `NULL` if the passed token doesn't exist, - * the function is anonymous or in case of a parse error/live coding. - * - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified token is not of type - * `T_FUNCTION`. - */ - public static function getName(File $phpcsFile, $stackPtr) - { - return ObjectDeclarations::getName($phpcsFile, $stackPtr); - } - - - /** - * Retrieves the method parameters for the specified function token. - * - * Also supports passing in a `T_USE` token for a closure use group. - * - * The returned array will contain the following information for each parameter: - * - * ```php - * 0 => array( - * 'name' => '$var', // The variable name. - * 'token' => integer, // The stack pointer to the variable name. - * 'content' => string, // The full content of the variable definition. - * 'pass_by_reference' => boolean, // Is the variable passed by reference? - * 'reference_token' => integer, // The stack pointer to the reference operator - * // or FALSE if the param is not passed by reference. - * 'variable_length' => boolean, // Is the param of variable length through use of `...` ? - * 'variadic_token' => integer, // The stack pointer to the ... operator - * // or FALSE if the param is not variable length. - * 'type_hint' => string, // The type hint for the variable. - * 'type_hint_token' => integer, // The stack pointer to the start of the type hint - * // or FALSE if there is no type hint. - * 'type_hint_end_token' => integer, // The stack pointer to the end of the type hint - * // or FALSE if there is no type hint. - * 'nullable_type' => boolean, // TRUE if the var type is preceded by the nullability - * // operator. - * 'comma_token' => integer, // The stack pointer to the comma after the param - * // or FALSE if this is the last param. - * ) - * ``` - * - * Parameters with default values have the following additional array indexes: - * ```php - * 'default' => string, // The full content of the default value. - * 'default_token' => integer, // The stack pointer to the start of the default value. - * 'default_equal_token' => integer, // The stack pointer to the equals sign. - * ``` - * - * Parameters declared using PHP 8 constructor property promotion, have these additional array indexes: - * ```php - * 'property_visibility' => string, // The property visibility as declared. - * 'visibility_token' => integer, // The stack pointer to the visibility modifier token. - * ``` - * - * Main differences with the PHPCS version: - * - Defensive coding against incorrect calls to this method. - * - More efficient and more stable checking whether a `T_USE` token is a closure use. - * - More efficient and more stable looping of the default value. - * - Clearer exception message when a non-closure use token was passed to the function. - * - To allow for backward compatible handling of arrow functions, this method will also accept - * `T_STRING` tokens and examine them to check if these are arrow functions. - * - Support for PHP 8.0 identifier name tokens in parameter types, cross-version PHP & PHPCS. - * - * @see \PHP_CodeSniffer\Files\File::getMethodParameters() Original source. - * @see \PHPCSUtils\BackCompat\BCFile::getMethodParameters() Cross-version compatible version of the original. - * - * @since 1.0.0 - * @since 1.0.0-alpha2 Added BC support for PHP 7.4 arrow functions. - * @since 1.0.0-alpha4 Added support for PHP 8.0 union types. - * @since 1.0.0-alpha4 Added support for PHP 8.0 constructor property promotion. - * @since 1.0.0-alpha4 Added support for PHP 8.0 identifier name tokenization. - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The position in the stack of the function token - * to acquire the parameters for. - * - * @return array - * - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified $stackPtr is not of - * type `T_FUNCTION`, `T_CLOSURE` or `T_USE`, - * nor an arrow function. - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If a passed `T_USE` token is not a closure - * use token. - */ - public static function getParameters(File $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - $arrowOpenClose = self::getArrowFunctionOpenClose($phpcsFile, $stackPtr); - - if (isset($tokens[$stackPtr]) === false - || ($tokens[$stackPtr]['code'] !== \T_FUNCTION - && $tokens[$stackPtr]['code'] !== \T_CLOSURE - && $tokens[$stackPtr]['code'] !== \T_USE - && $arrowOpenClose === false) - ) { - throw new RuntimeException('$stackPtr must be of type T_FUNCTION, T_CLOSURE or T_USE or an arrow function'); - } - - if ($tokens[$stackPtr]['code'] === \T_USE) { - // This will work PHPCS 3.x/4.x cross-version without much overhead. - $opener = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); - if ($opener === false - || $tokens[$opener]['code'] !== \T_OPEN_PARENTHESIS - || UseStatements::isClosureUse($phpcsFile, $stackPtr) === false - ) { - throw new RuntimeException('$stackPtr was not a valid closure T_USE'); - } - } elseif ($arrowOpenClose !== false) { - // Arrow function in combination with PHP < 7.4 or PHPCS < 3.5.3/4/5. - $opener = $arrowOpenClose['parenthesis_opener']; - } else { - if (isset($tokens[$stackPtr]['parenthesis_opener']) === false) { - // Live coding or syntax error, so no params to find. - return []; - } - - $opener = $tokens[$stackPtr]['parenthesis_opener']; - } - - if (isset($tokens[$opener]['parenthesis_closer']) === false) { - // Live coding or syntax error, so no params to find. - return []; - } - - $closer = $tokens[$opener]['parenthesis_closer']; - - $vars = []; - $currVar = null; - $paramStart = ($opener + 1); - $defaultStart = null; - $equalToken = null; - $paramCount = 0; - $passByReference = false; - $referenceToken = false; - $variableLength = false; - $variadicToken = false; - $typeHint = ''; - $typeHintToken = false; - $typeHintEndToken = false; - $nullableType = false; - $visibilityToken = null; - - for ($i = $paramStart; $i <= $closer; $i++) { - // Changed from checking 'code' to 'type' to allow for T_NULLABLE not existing in PHPCS < 2.8.0. - switch ($tokens[$i]['type']) { - case 'T_BITWISE_AND': - $passByReference = true; - $referenceToken = $i; - break; - - case 'T_VARIABLE': - $currVar = $i; - break; - - case 'T_ELLIPSIS': - $variableLength = true; - $variadicToken = $i; - break; - - case 'T_ARRAY_HINT': // PHPCS < 3.3.0. - case 'T_CALLABLE': - case 'T_SELF': - case 'T_PARENT': - case 'T_STATIC': // Self and parent are valid, static invalid, but was probably intended as type hint. - case 'T_FALSE': // Union types. - case 'T_NULL': // Union types. - case 'T_STRING': - case 'T_NAMESPACE': - case 'T_NS_SEPARATOR': - case 'T_NAME_QUALIFIED': - case 'T_NAME_FULLY_QUALIFIED': - case 'T_NAME_RELATIVE': - case 'T_BITWISE_OR': // Union type separator PHPCS < 3.6.0. - case 'T_TYPE_UNION': // Union type separator PHPCS >= 3.6.0. - if ($typeHintToken === false) { - $typeHintToken = $i; - } - - $typeHint .= $tokens[$i]['content']; - $typeHintEndToken = $i; - break; - - case 'T_NULLABLE': - case 'T_INLINE_THEN': // PHPCS < 2.8.0. - $nullableType = true; - $typeHint .= $tokens[$i]['content']; - $typeHintEndToken = $i; - break; - - case 'T_PUBLIC': - case 'T_PROTECTED': - case 'T_PRIVATE': - $visibilityToken = $i; - break; - - case 'T_CLOSE_PARENTHESIS': - case 'T_COMMA': - // If it's null, then there must be no parameters for this - // method. - if ($currVar === null) { - continue 2; - } - - $vars[$paramCount] = []; - $vars[$paramCount]['token'] = $currVar; - $vars[$paramCount]['name'] = $tokens[$currVar]['content']; - $vars[$paramCount]['content'] = \trim( - GetTokensAsString::normal($phpcsFile, $paramStart, ($i - 1)) - ); - - if ($defaultStart !== null) { - $vars[$paramCount]['default'] = \trim( - GetTokensAsString::normal($phpcsFile, $defaultStart, ($i - 1)) - ); - $vars[$paramCount]['default_token'] = $defaultStart; - $vars[$paramCount]['default_equal_token'] = $equalToken; - } - - $vars[$paramCount]['pass_by_reference'] = $passByReference; - $vars[$paramCount]['reference_token'] = $referenceToken; - $vars[$paramCount]['variable_length'] = $variableLength; - $vars[$paramCount]['variadic_token'] = $variadicToken; - $vars[$paramCount]['type_hint'] = $typeHint; - $vars[$paramCount]['type_hint_token'] = $typeHintToken; - $vars[$paramCount]['type_hint_end_token'] = $typeHintEndToken; - $vars[$paramCount]['nullable_type'] = $nullableType; - - if ($visibilityToken !== null) { - $vars[$paramCount]['property_visibility'] = $tokens[$visibilityToken]['content']; - $vars[$paramCount]['visibility_token'] = $visibilityToken; - } - - if ($tokens[$i]['code'] === \T_COMMA) { - $vars[$paramCount]['comma_token'] = $i; - } else { - $vars[$paramCount]['comma_token'] = false; - } - - // Reset the vars, as we are about to process the next parameter. - $currVar = null; - $paramStart = ($i + 1); - $defaultStart = null; - $equalToken = null; - $passByReference = false; - $referenceToken = false; - $variableLength = false; - $variadicToken = false; - $typeHint = ''; - $typeHintToken = false; - $typeHintEndToken = false; - $nullableType = false; - $visibilityToken = null; - - $paramCount++; - break; - - case 'T_EQUAL': - $defaultStart = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true); - $equalToken = $i; - - // Skip past everything in the default value before going into the next switch loop. - for ($j = ($i + 1); $j <= $closer; $j++) { - // Skip past array()'s et al as default values. - if (isset($tokens[$j]['parenthesis_opener'], $tokens[$j]['parenthesis_closer'])) { - $j = $tokens[$j]['parenthesis_closer']; - - if ($j === $closer) { - // Found the end of the parameter. - break; - } - - continue; - } - - // Skip past short arrays et al as default values. - if (isset($tokens[$j]['bracket_opener'])) { - $j = $tokens[$j]['bracket_closer']; - continue; - } - - if ($tokens[$j]['code'] === \T_COMMA) { - break; - } - } - - $i = ($j - 1); - break; - } - } - - return $vars; - } - - /** - * Check if an arbitrary token is the "fn" keyword for a PHP 7.4 arrow function. - * - * Helper function for cross-version compatibility with both PHP as well as PHPCS. - * - PHP 7.4+ will tokenize most tokens with the content "fn" as `T_FN`, even when it isn't an arrow function. - * - PHPCS < 3.5.3 will tokenize arrow functions keywords as `T_STRING`. - * - PHPCS 3.5.3/3.5.4 will tokenize the keyword differently depending on which PHP version is used - * and similar to PHP will tokenize most tokens with the content "fn" as `T_FN`, even when it's not an - * arrow function. - * > Note: the tokens tokenized by PHPCS 3.5.3 - 3.5.4 as `T_FN` are not 100% the same as those tokenized - * by PHP 7.4+ as `T_FN`. - * - * Either way, the `T_FN` token is not a reliable search vector for finding or examining - * arrow functions, at least not until PHPCS 3.5.5. - * This function solves that and will give reliable results in the same way as this is now - * solved in PHPCS 3.5.5. - * - * > Note: Bugs are still being found and reported about how PHPCS tokenizes the arrow functions. - * This method will keep up with upstream changes and backport them, in as far possible, to allow - * for sniffing arrow functions in PHPCS < current. - * - * @see \PHPCSUtils\Utils\FunctionDeclarations::getArrowFunctionOpenClose() Related function. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The token to check. Typically a T_FN or - * T_STRING token as those are the only two - * tokens which can be the arrow function keyword. - * - * @return bool `TRUE` if the token is the "fn" keyword for an arrow function. `FALSE` when it's not - * or in case of live coding/parse error. - */ - public static function isArrowFunction(File $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - if (isset($tokens[$stackPtr]) === false) { - return false; - } - - if ($tokens[$stackPtr]['type'] === 'T_FN' - && isset($tokens[$stackPtr]['scope_closer']) === true - ) { - return true; - } - - if (isset(Collections::arrowFunctionTokensBC()[$tokens[$stackPtr]['code']]) === false - || \strtolower($tokens[$stackPtr]['content']) !== 'fn' - ) { - return false; - } - - $openClose = self::getArrowFunctionOpenClose($phpcsFile, $stackPtr); - if ($openClose !== false && isset($openClose['scope_closer'])) { - return true; - } - - return false; - } -} diff --git a/Magento2/Helpers/PHPCSUtils/Utils/GetTokensAsString.php b/Magento2/Helpers/PHPCSUtils/Utils/GetTokensAsString.php deleted file mode 100644 index c7267f5c..00000000 --- a/Magento2/Helpers/PHPCSUtils/Utils/GetTokensAsString.php +++ /dev/null @@ -1,188 +0,0 @@ -getTokens(); - - if (\is_int($start) === false || isset($tokens[$start]) === false) { - throw new RuntimeException( - 'The $start position for GetTokensAsString methods must exist in the token stack' - ); - } - - if (\is_int($end) === false || $end < $start) { - return ''; - } - - $str = ''; - if ($end >= $phpcsFile->numTokens) { - $end = ($phpcsFile->numTokens - 1); - } - - $lastAdded = null; - for ($i = $start; $i <= $end; $i++) { - if ($stripComments === true && isset(Tokens::$commentTokens[$tokens[$i]['code']])) { - continue; - } - - if ($stripWhitespace === true && $tokens[$i]['code'] === \T_WHITESPACE) { - continue; - } - - if ($compact === true && $tokens[$i]['code'] === \T_WHITESPACE) { - if (isset($lastAdded) === false || $tokens[$lastAdded]['code'] !== \T_WHITESPACE) { - $str .= ' '; - $lastAdded = $i; - } - continue; - } - - // If tabs are being converted to spaces by the tokenizer, the - // original content should be used instead of the converted content. - if ($origContent === true && isset($tokens[$i]['orig_content']) === true) { - $str .= $tokens[$i]['orig_content']; - } else { - $str .= $tokens[$i]['content']; - } - - $lastAdded = $i; - } - - return $str; - } - - /** - * Retrieve the code-tokens only content of the tokens from the specified start position - * in the token stack to the specified end position (inclusive) without whitespace or comments. - * - * This is, for instance, useful to retrieve a namespace name without stray whitespace or comments. - * Use this function selectively and with care! - * - * @see \PHP_CodeSniffer\Files\File::getTokensAsString() Loosely related function. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $start The position to start from in the token stack. - * @param int $end The position to end at in the token stack (inclusive). - * - * @return string The token contents stripped off comments and whitespace. - * - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified start position does not exist. - */ - public static function noEmpties(File $phpcsFile, $start, $end) - { - return self::getString($phpcsFile, $start, $end, false, true, true); - } -} diff --git a/Magento2/Helpers/PHPCSUtils/Utils/ObjectDeclarations.php b/Magento2/Helpers/PHPCSUtils/Utils/ObjectDeclarations.php deleted file mode 100644 index fdb46adb..00000000 --- a/Magento2/Helpers/PHPCSUtils/Utils/ObjectDeclarations.php +++ /dev/null @@ -1,363 +0,0 @@ -getTokens(); - - if (isset($tokens[$stackPtr]) === false - || ($tokens[$stackPtr]['code'] === \T_ANON_CLASS || $tokens[$stackPtr]['code'] === \T_CLOSURE) - ) { - return null; - } - - $tokenCode = $tokens[$stackPtr]['code']; - - /* - * BC: Work-around JS ES6 classes not being tokenized as T_CLASS in PHPCS < 3.0.0. - */ - if (isset($phpcsFile->tokenizerType) - && $phpcsFile->tokenizerType === 'JS' - && $tokenCode === \T_STRING - && $tokens[$stackPtr]['content'] === 'class' - ) { - $tokenCode = \T_CLASS; - } - - if ($tokenCode !== \T_FUNCTION - && $tokenCode !== \T_CLASS - && $tokenCode !== \T_INTERFACE - && $tokenCode !== \T_TRAIT - ) { - throw new RuntimeException( - 'Token type "' . $tokens[$stackPtr]['type'] . '" is not T_FUNCTION, T_CLASS, T_INTERFACE or T_TRAIT' - ); - } - - if ($tokenCode === \T_FUNCTION - && \strtolower($tokens[$stackPtr]['content']) !== 'function' - ) { - // This is a function declared without the "function" keyword. - // So this token is the function name. - return $tokens[$stackPtr]['content']; - } - - /* - * Determine the name. Note that we cannot simply look for the first T_STRING - * because an (invalid) class name starting with a number will be multiple tokens. - * Whitespace or comment are however not allowed within a name. - */ - - $stopPoint = $phpcsFile->numTokens; - if ($tokenCode === \T_FUNCTION && isset($tokens[$stackPtr]['parenthesis_opener']) === true) { - $stopPoint = $tokens[$stackPtr]['parenthesis_opener']; - } elseif (isset($tokens[$stackPtr]['scope_opener']) === true) { - $stopPoint = $tokens[$stackPtr]['scope_opener']; - } - - $exclude = Tokens::$emptyTokens; - $exclude[] = \T_OPEN_PARENTHESIS; - $exclude[] = \T_OPEN_CURLY_BRACKET; - $exclude[] = \T_BITWISE_AND; - - $nameStart = $phpcsFile->findNext($exclude, ($stackPtr + 1), $stopPoint, true); - if ($nameStart === false) { - // Live coding or parse error. - return null; - } - - $tokenAfterNameEnd = $phpcsFile->findNext($exclude, $nameStart, $stopPoint); - - if ($tokenAfterNameEnd === false) { - $content = null; - - /* - * BC: In PHPCS 2.6.0, in case of live coding, the last token in a file will be tokenized - * as T_STRING, but won't have the `content` index set. - */ - if (isset($tokens[$nameStart]['content'])) { - $content = $tokens[$nameStart]['content']; - } - - return $content; - } - - // Name starts with number, so is composed of multiple tokens. - return GetTokensAsString::noEmpties($phpcsFile, $nameStart, ($tokenAfterNameEnd - 1)); - } - - /** - * Retrieves the implementation properties of a class. - * - * Main differences with the PHPCS version: - * - Bugs fixed: - * - Handling of PHPCS annotations. - * - Handling of unorthodox docblock placement. - * - A class cannot both be abstract as well as final, so this utility should not allow for that. - * - Defensive coding against incorrect calls to this method. - * - * @see \PHP_CodeSniffer\Files\File::getClassProperties() Original source. - * @see \PHPCSUtils\BackCompat\BCFile::getClassProperties() Cross-version compatible version of the original. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The position in the stack of the `T_CLASS` - * token to acquire the properties for. - * - * @return array Array with implementation properties of a class. - * The format of the return value is: - * ```php - * array( - * 'is_abstract' => false, // TRUE if the abstract keyword was found. - * 'is_final' => false, // TRUE if the final keyword was found. - * ); - * ``` - * - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a - * `T_CLASS` token. - */ - public static function getClassProperties(File $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - - if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== \T_CLASS) { - throw new RuntimeException('$stackPtr must be of type T_CLASS'); - } - - $valid = Collections::$classModifierKeywords + Tokens::$emptyTokens; - $properties = [ - 'is_abstract' => false, - 'is_final' => false, - ]; - - for ($i = ($stackPtr - 1); $i > 0; $i--) { - if (isset($valid[$tokens[$i]['code']]) === false) { - break; - } - - switch ($tokens[$i]['code']) { - case \T_ABSTRACT: - $properties['is_abstract'] = true; - break 2; - - case \T_FINAL: - $properties['is_final'] = true; - break 2; - } - } - - return $properties; - } - - /** - * Retrieves the name of the class that the specified class extends. - * - * Works for classes, anonymous classes and interfaces, though it is strongly recommended - * to use the {@see \PHPCSUtils\Utils\ObjectDeclarations::findExtendedInterfaceNames()} - * method to examine interfaces instead. Interfaces can extend multiple parent interfaces, - * and that use-case is not handled by this method. - * - * Main differences with the PHPCS version: - * - Bugs fixed: - * - Handling of PHPCS annotations. - * - Handling of comments. - * - Improved handling of parse errors. - * - The returned name will be clean of superfluous whitespace and/or comments. - * - * @see \PHP_CodeSniffer\Files\File::findExtendedClassName() Original source. - * @see \PHPCSUtils\BackCompat\BCFile::findExtendedClassName() Cross-version compatible version of - * the original. - * @see \PHPCSUtils\Utils\ObjectDeclarations::findExtendedInterfaceNames() Similar method for extended interfaces. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The stack position of the class or interface. - * - * @return string|false The extended class name or `FALSE` on error or if there - * is no extended class name. - */ - public static function findExtendedClassName(File $phpcsFile, $stackPtr) - { - $names = self::findNames($phpcsFile, $stackPtr, \T_EXTENDS, Collections::$OOCanExtend); - if ($names === false) { - return false; - } - - // Classes can only extend one parent class. - return \array_shift($names); - } - - /** - * Retrieves the names of the interfaces that the specified class implements. - * - * Main differences with the PHPCS version: - * - Bugs fixed: - * - Handling of PHPCS annotations. - * - Handling of comments. - * - Improved handling of parse errors. - * - The returned name(s) will be clean of superfluous whitespace and/or comments. - * - * @see \PHP_CodeSniffer\Files\File::findImplementedInterfaceNames() Original source. - * @see \PHPCSUtils\BackCompat\BCFile::findImplementedInterfaceNames() Cross-version compatible version of - * the original. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The stack position of the class. - * - * @return array|false Array with names of the implemented interfaces or `FALSE` on - * error or if there are no implemented interface names. - */ - public static function findImplementedInterfaceNames(File $phpcsFile, $stackPtr) - { - return self::findNames($phpcsFile, $stackPtr, \T_IMPLEMENTS, Collections::$OOCanImplement); - } - - /** - * Retrieves the names of the interfaces that the specified interface extends. - * - * @see \PHPCSUtils\Utils\ObjectDeclarations::findExtendedClassName() Similar method for extended classes. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The stack position of the interface keyword. - * - * @return array|false Array with names of the extended interfaces or `FALSE` on - * error or if there are no extended interface names. - */ - public static function findExtendedInterfaceNames(File $phpcsFile, $stackPtr) - { - return self::findNames( - $phpcsFile, - $stackPtr, - \T_EXTENDS, - [\T_INTERFACE => \T_INTERFACE] - ); - } - - /** - * Retrieves the names of the extended classes or interfaces or the implemented - * interfaces that the specific class/interface declaration extends/implements. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The stack position of the - * class/interface declaration keyword. - * @param int $keyword The token constant for the keyword to examine. - * Either `T_EXTENDS` or `T_IMPLEMENTS`. - * @param array $allowedFor Array of OO types for which use of the keyword - * is allowed. - * - * @return array|false Returns an array of names or `FALSE` on error or when the object - * being declared does not extend/implement another object. - */ - private static function findNames(File $phpcsFile, $stackPtr, $keyword, $allowedFor) - { - $tokens = $phpcsFile->getTokens(); - - if (isset($tokens[$stackPtr]) === false - || isset($allowedFor[$tokens[$stackPtr]['code']]) === false - || isset($tokens[$stackPtr]['scope_opener']) === false - ) { - return false; - } - - $scopeOpener = $tokens[$stackPtr]['scope_opener']; - $keywordPtr = $phpcsFile->findNext($keyword, ($stackPtr + 1), $scopeOpener); - if ($keywordPtr === false) { - return false; - } - - $find = [ - \T_NS_SEPARATOR, - \T_STRING, - ]; - $find += Tokens::$emptyTokens; - - $names = []; - $end = $keywordPtr; - do { - $start = ($end + 1); - $end = $phpcsFile->findNext($find, $start, ($scopeOpener + 1), true); - $name = GetTokensAsString::noEmpties($phpcsFile, $start, ($end - 1)); - - if (\trim($name) !== '') { - $names[] = $name; - } - } while ($tokens[$end]['code'] === \T_COMMA); - - if (empty($names)) { - return false; - } - - return $names; - } -} diff --git a/Magento2/Helpers/PHPCSUtils/Utils/Parentheses.php b/Magento2/Helpers/PHPCSUtils/Utils/Parentheses.php deleted file mode 100644 index 26ba1fd2..00000000 --- a/Magento2/Helpers/PHPCSUtils/Utils/Parentheses.php +++ /dev/null @@ -1,155 +0,0 @@ - => - */ - private static $extraParenthesesOwners = [ - \T_LIST => \T_LIST, - \T_ISSET => \T_ISSET, - \T_UNSET => \T_UNSET, - \T_EMPTY => \T_EMPTY, - \T_EXIT => \T_EXIT, - \T_EVAL => \T_EVAL, - \T_ANON_CLASS => \T_ANON_CLASS, - // Work-around: anon classes were, in certain circumstances, tokenized as T_CLASS prior to PHPCS 3.4.0. - \T_CLASS => \T_CLASS, - ]; - - /** - * Get the stack pointer to the parentheses owner of an open/close parenthesis. - * - * @since 1.0.0 - * @since 1.0.0-alpha2 Added BC support for PHP 7.4 arrow functions. - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The position of `T_OPEN/CLOSE_PARENTHESIS` token. - * - * @return int|false Integer stack pointer to the parentheses owner; or `FALSE` if the - * parenthesis does not have a (direct) owner or if the token passed - * was not a parenthesis. - */ - public static function getOwner(File $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - - if (isset($tokens[$stackPtr]['parenthesis_owner'])) { - return $tokens[$stackPtr]['parenthesis_owner']; - } - - /* - * - `T_FN` was only backfilled in PHPCS 3.5.3/4/5. - * - On PHP 7.4 with PHPCS < 3.5.3, T_FN will not yet be a parentheses owner. - * - On PHP < 7.4 with PHPCS < 3.5.3, T_FN will be tokenized as T_STRING and not yet be a parentheses owner. - * - * {@internal As the 'parenthesis_owner' index is only set on parentheses, we didn't need to do any - * input validation before, but now we do.} - */ - if (isset($tokens[$stackPtr]) === false - || ($tokens[$stackPtr]['code'] !== \T_OPEN_PARENTHESIS - && $tokens[$stackPtr]['code'] !== \T_CLOSE_PARENTHESIS) - ) { - return false; - } - - if ($tokens[$stackPtr]['code'] === \T_CLOSE_PARENTHESIS) { - $stackPtr = $tokens[$stackPtr]['parenthesis_opener']; - } - - $skip = Tokens::$emptyTokens; - $skip[\T_BITWISE_AND] = \T_BITWISE_AND; - - $prevNonEmpty = $phpcsFile->findPrevious($skip, ($stackPtr - 1), null, true); - if ($prevNonEmpty !== false - && (isset(self::$extraParenthesesOwners[$tokens[$prevNonEmpty]['code']]) - // Possibly an arrow function. - || FunctionDeclarations::isArrowFunction($phpcsFile, $prevNonEmpty) === true) - ) { - return $prevNonEmpty; - } - - return false; - } - - /** - * Check whether the parenthesis owner of an open/close parenthesis is within a limited - * set of valid owners. - * - * @since 1.0.0 - * @since 1.0.0-alpha2 Added BC support for PHP 7.4 arrow functions. - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The position of `T_OPEN/CLOSE_PARENTHESIS` token. - * @param int|string|array $validOwners Array of token constants for the owners - * which should be considered valid. - * - * @return bool `TRUE` if the owner is within the list of `$validOwners`; `FALSE` if not and - * if the parenthesis does not have a (direct) owner. - */ - public static function isOwnerIn(File $phpcsFile, $stackPtr, $validOwners) - { - $owner = self::getOwner($phpcsFile, $stackPtr); - if ($owner === false) { - return false; - } - - $tokens = $phpcsFile->getTokens(); - $validOwners = (array) $validOwners; - - /* - * Work around tokenizer bug where anon classes were, in certain circumstances, tokenized - * as `T_CLASS` prior to PHPCS 3.4.0. - * As `T_CLASS` is normally not an parenthesis owner, we can safely add it to the array - * without doing a version check. - */ - if (\in_array(\T_ANON_CLASS, $validOwners, true)) { - $validOwners[] = \T_CLASS; - } - - /* - * Allow for T_FN token being tokenized as T_STRING before PHPCS 3.5.3. - */ - if (\defined('T_FN') && \in_array(\T_FN, $validOwners, true)) { - $validOwners += Collections::arrowFunctionTokensBC(); - } - - return \in_array($tokens[$owner]['code'], $validOwners, true); - } -} diff --git a/Magento2/Helpers/PHPCSUtils/Utils/Scopes.php b/Magento2/Helpers/PHPCSUtils/Utils/Scopes.php deleted file mode 100644 index 3975a6b3..00000000 --- a/Magento2/Helpers/PHPCSUtils/Utils/Scopes.php +++ /dev/null @@ -1,80 +0,0 @@ -getTokens(); - $validScopes = (array) $validScopes; - - if (\in_array($tokens[$ptr]['code'], $validScopes, true) === true) { - return $ptr; - } - } - - return false; - } - - /** - * Check whether a T_FUNCTION token is a class/interface/trait method declaration. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The position in the stack of the - * `T_FUNCTION` token to verify. - * - * @return bool - */ - public static function isOOMethod(File $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - - if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== \T_FUNCTION) { - return false; - } - - if (self::validDirectScope($phpcsFile, $stackPtr, BCTokens::ooScopeTokens()) !== false) { - return true; - } - - return false; - } -} diff --git a/Magento2/Helpers/PHPCSUtils/Utils/TextStrings.php b/Magento2/Helpers/PHPCSUtils/Utils/TextStrings.php deleted file mode 100644 index aca9ce09..00000000 --- a/Magento2/Helpers/PHPCSUtils/Utils/TextStrings.php +++ /dev/null @@ -1,133 +0,0 @@ -getTokens(); - - // Must be the start of a text string token. - if (isset($tokens[$stackPtr], Collections::$textStingStartTokens[$tokens[$stackPtr]['code']]) === false) { - throw new RuntimeException( - '$stackPtr must be of type T_START_HEREDOC, T_START_NOWDOC, T_CONSTANT_ENCAPSED_STRING' - . ' or T_DOUBLE_QUOTED_STRING' - ); - } - - if ($tokens[$stackPtr]['code'] === \T_CONSTANT_ENCAPSED_STRING - || $tokens[$stackPtr]['code'] === \T_DOUBLE_QUOTED_STRING - ) { - $prev = $phpcsFile->findPrevious(\T_WHITESPACE, ($stackPtr - 1), null, true); - if ($tokens[$stackPtr]['code'] === $tokens[$prev]['code']) { - throw new RuntimeException('$stackPtr must be the start of the text string'); - } - } - - $stripNewline = false; - - switch ($tokens[$stackPtr]['code']) { - case \T_START_HEREDOC: - $stripQuotes = false; - $stripNewline = true; - $targetType = \T_HEREDOC; - $current = ($stackPtr + 1); - break; - - case \T_START_NOWDOC: - $stripQuotes = false; - $stripNewline = true; - $targetType = \T_NOWDOC; - $current = ($stackPtr + 1); - break; - - default: - $targetType = $tokens[$stackPtr]['code']; - $current = $stackPtr; - break; - } - - $string = ''; - do { - $string .= $tokens[$current]['content']; - ++$current; - } while (isset($tokens[$current]) && $tokens[$current]['code'] === $targetType); - - if ($stripNewline === true) { - // Heredoc/nowdoc: strip the new line at the end of the string to emulate how PHP sees the string. - $string = \rtrim($string, "\r\n"); - } - - if ($stripQuotes === true) { - return self::stripQuotes($string); - } - - return $string; - } - - /** - * Strip text delimiter quotes from an arbitrary string. - * - * Intended for use with the "contents" of a `T_CONSTANT_ENCAPSED_STRING` / `T_DOUBLE_QUOTED_STRING`. - * - * - Prevents stripping mis-matched quotes. - * - Prevents stripping quotes from the textual content of the string. - * - * @since 1.0.0 - * - * @param string $string The raw string. - * - * @return string String without quotes around it. - */ - public static function stripQuotes($string) - { - return \preg_replace('`^([\'"])(.*)\1$`Ds', '$2', $string); - } -} diff --git a/Magento2/Helpers/PHPCSUtils/Utils/UseStatements.php b/Magento2/Helpers/PHPCSUtils/Utils/UseStatements.php deleted file mode 100644 index 5e965d33..00000000 --- a/Magento2/Helpers/PHPCSUtils/Utils/UseStatements.php +++ /dev/null @@ -1,412 +0,0 @@ -getTokens(); - - if (isset($tokens[$stackPtr]) === false - || $tokens[$stackPtr]['code'] !== \T_USE - ) { - throw new RuntimeException('$stackPtr must be of type T_USE'); - } - - $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); - if ($next === false) { - // Live coding or parse error. - return ''; - } - - $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); - if ($prev !== false && $tokens[$prev]['code'] === \T_CLOSE_PARENTHESIS - && Parentheses::isOwnerIn($phpcsFile, $prev, \T_CLOSURE) === true - ) { - return 'closure'; - } - - $lastCondition = Conditions::getLastCondition($phpcsFile, $stackPtr); - if ($lastCondition === false || $tokens[$lastCondition]['code'] === \T_NAMESPACE) { - // Global or scoped namespace and not a closure use statement. - return 'import'; - } - - $traitScopes = BCTokens::ooScopeTokens(); - // Only classes and traits can import traits. - unset($traitScopes[\T_INTERFACE]); - - if (isset($traitScopes[$tokens[$lastCondition]['code']]) === true) { - return 'trait'; - } - - return ''; - } - - /** - * Determine whether a T_USE token represents a closure use statement. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the `T_USE` token. - * - * @return bool `TRUE` if the token passed is a closure use statement. - * `FALSE` if it's not. - * - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a - * `T_USE` token. - */ - public static function isClosureUse(File $phpcsFile, $stackPtr) - { - return (self::getType($phpcsFile, $stackPtr) === 'closure'); - } - - /** - * Determine whether a T_USE token represents a class/function/constant import use statement. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the `T_USE` token. - * - * @return bool `TRUE` if the token passed is an import use statement. - * `FALSE` if it's not. - * - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a - * `T_USE` token. - */ - public static function isImportUse(File $phpcsFile, $stackPtr) - { - return (self::getType($phpcsFile, $stackPtr) === 'import'); - } - - /** - * Determine whether a T_USE token represents a trait use statement. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the `T_USE` token. - * - * @return bool `TRUE` if the token passed is a trait use statement. - * `FALSE` if it's not. - * - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a - * `T_USE` token. - */ - public static function isTraitUse(File $phpcsFile, $stackPtr) - { - return (self::getType($phpcsFile, $stackPtr) === 'trait'); - } - - /** - * Split an import use statement into individual imports. - * - * Handles single import, multi-import and group-import use statements. - * - * @since 1.0.0 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The position in the stack of the `T_USE` token. - * - * @return array A multi-level array containing information about the use statement. - * The first level is `'name'`, `'function'` and `'const'`. These keys will always exist. - * If any statements are found for any of these categories, the second level - * will contain the alias/name as the key and the full original use name as the - * value for each of the found imports or an empty array if no imports were found - * in this use statement for a particular category. - * - * For example, for this function group use statement: - * ```php - * use function Vendor\Package\{ - * LevelA\Name as Alias, - * LevelB\Another_Name, - * }; - * ``` - * the return value would look like this: - * ```php - * array( - * 'name' => array(), - * 'function' => array( - * 'Alias' => 'Vendor\Package\LevelA\Name', - * 'Another_Name' => 'Vendor\Package\LevelB\Another_Name', - * ), - * 'const' => array(), - * ) - * ``` - * - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a - * `T_USE` token. - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the `T_USE` token is not for an import - * use statement. - */ - public static function splitImportUseStatement(File $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - - if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== \T_USE) { - throw new RuntimeException('$stackPtr must be of type T_USE'); - } - - if (self::isImportUse($phpcsFile, $stackPtr) === false) { - throw new RuntimeException('$stackPtr must be an import use statement'); - } - - $statements = [ - 'name' => [], - 'function' => [], - 'const' => [], - ]; - - $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG], ($stackPtr + 1)); - if ($endOfStatement === false) { - // Live coding or parse error. - return $statements; - } - - $endOfStatement++; - - $start = true; - $useGroup = false; - $hasAlias = false; - $baseName = ''; - $name = ''; - $type = ''; - $fixedType = false; - $alias = ''; - - for ($i = ($stackPtr + 1); $i < $endOfStatement; $i++) { - if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) { - continue; - } - - $tokenCode = $tokens[$i]['code']; - - /* - * BC: Work round a tokenizer bug related to a parse error. - * - * If `function` or `const` is used as the alias, the semi-colon after it would - * be tokenized as T_STRING. - * For `function` this was fixed in PHPCS 2.8.0. For `const` the issue still exists - * in PHPCS 3.5.2. - * - * Along the same lines, the `}` T_CLOSE_USE_GROUP would also be tokenized as T_STRING. - */ - if ($tokenCode === \T_STRING) { - if ($tokens[$i]['content'] === ';') { - $tokenCode = \T_SEMICOLON; - } elseif ($tokens[$i]['content'] === '}') { - $tokenCode = \T_CLOSE_USE_GROUP; - } - } - - switch ($tokenCode) { - case \T_STRING: - // Only when either at the start of the statement or at the start of a new sub within a group. - if ($start === true && $fixedType === false) { - $content = \strtolower($tokens[$i]['content']); - if ($content === 'function' - || $content === 'const' - ) { - $type = $content; - $start = false; - if ($useGroup === false) { - $fixedType = true; - } - - break; - } else { - $type = 'name'; - } - } - - $start = false; - - if ($hasAlias === false) { - $name .= $tokens[$i]['content']; - } - - $alias = $tokens[$i]['content']; - break; - - case \T_AS: - $hasAlias = true; - break; - - case \T_OPEN_USE_GROUP: - $start = true; - $useGroup = true; - $baseName = $name; - $name = ''; - break; - - case \T_SEMICOLON: - case \T_CLOSE_TAG: - case \T_CLOSE_USE_GROUP: - case \T_COMMA: - if ($name !== '') { - if ($useGroup === true) { - $statements[$type][$alias] = $baseName . $name; - } else { - $statements[$type][$alias] = $name; - } - } - - if ($tokenCode !== \T_COMMA) { - break 2; - } - - // Reset. - $start = true; - $name = ''; - $hasAlias = false; - if ($fixedType === false) { - $type = ''; - } - break; - - case \T_NS_SEPARATOR: - $name .= $tokens[$i]['content']; - break; - - case \T_FUNCTION: - case \T_CONST: - /* - * BC: Work around tokenizer bug in PHPCS < 3.4.1. - * - * `function`/`const` in `use function`/`use const` tokenized as T_FUNCTION/T_CONST - * instead of T_STRING when there is a comment between the keywords. - * - * @link https://github.com/squizlabs/PHP_CodeSniffer/issues/2431 - */ - if ($start === true && $fixedType === false) { - $type = \strtolower($tokens[$i]['content']); - $start = false; - if ($useGroup === false) { - $fixedType = true; - } - - break; - } - - $start = false; - - if ($hasAlias === false) { - $name .= $tokens[$i]['content']; - } - - $alias = $tokens[$i]['content']; - break; - - /* - * Fall back in case reserved keyword is (illegally) used in name. - * Parse error, but not our concern. - */ - default: - if ($hasAlias === false) { - $name .= $tokens[$i]['content']; - } - - $alias = $tokens[$i]['content']; - break; - } - } - - return $statements; - } - - /** - * Split an import use statement into individual imports and merge it with an array of previously - * seen import use statements. - * - * Beware: this method should only be used to combine the import use statements found in *one* file. - * Do NOT combine the statements of multiple files as the result will be inaccurate and unreliable. - * - * In most cases when tempted to use this method, the {@see \PHPCSUtils\AbstractSniffs\AbstractFileContextSniff} - * (upcoming) should be used instead. - * - * @see \PHPCSUtils\AbstractSniffs\AbstractFileContextSniff - * @see \PHPCSUtils\Utils\UseStatements::splitImportUseStatement() - * - * @since 1.0.0-alpha3 - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The position in the stack of the `T_USE` token. - * @param array $previousUseStatements The import `use` statements collected so far. - * This should be either the output of a - * previous call to this method or the output of - * an earlier call to the - * {@see UseStatements::splitImportUseStatement()} - * method. - * - * @return array A multi-level array containing information about the current `use` statement combined with - * the previously collected `use` statement information. - * See {@see UseStatements::splitImportUseStatement()} for more details about the array format. - */ - public static function splitAndMergeImportUseStatement(File $phpcsFile, $stackPtr, $previousUseStatements) - { - try { - $useStatements = self::splitImportUseStatement($phpcsFile, $stackPtr); - - if (isset($previousUseStatements['name']) === false) { - $previousUseStatements['name'] = $useStatements['name']; - } else { - $previousUseStatements['name'] += $useStatements['name']; - } - if (isset($previousUseStatements['function']) === false) { - $previousUseStatements['function'] = $useStatements['function']; - } else { - $previousUseStatements['function'] += $useStatements['function']; - } - if (isset($previousUseStatements['const']) === false) { - $previousUseStatements['const'] = $useStatements['const']; - } else { - $previousUseStatements['const'] += $useStatements['const']; - } - } catch (RuntimeException $e) { - // Not an import use statement. - } - - return $previousUseStatements; - } -} diff --git a/Magento2/Sniffs/PHPCompatibility/ChangedIntToBoolParamTypeSniff.php b/Magento2/Sniffs/PHPCompatibility/ChangedIntToBoolParamTypeSniff.php index 673a0fe7..f01be4f6 100644 --- a/Magento2/Sniffs/PHPCompatibility/ChangedIntToBoolParamTypeSniff.php +++ b/Magento2/Sniffs/PHPCompatibility/ChangedIntToBoolParamTypeSniff.php @@ -13,7 +13,7 @@ use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Util\Tokens; use PHPCompatibility\AbstractFunctionCallParameterSniff; -use Magento2\Helpers\PHPCSUtils\BackCompat\BCTokens; +use PHPCSUtils\BackCompat\BCTokens; /** * Detect calls to functions where the expected type of a parameter has been changed from integer to boolean. diff --git a/Magento2/Sniffs/PHPCompatibility/ForbiddenFinalPrivateMethodsSniff.php b/Magento2/Sniffs/PHPCompatibility/ForbiddenFinalPrivateMethodsSniff.php index 85b2fe11..365942e7 100644 --- a/Magento2/Sniffs/PHPCompatibility/ForbiddenFinalPrivateMethodsSniff.php +++ b/Magento2/Sniffs/PHPCompatibility/ForbiddenFinalPrivateMethodsSniff.php @@ -12,8 +12,8 @@ use PHPCompatibility\Sniff; use PHP_CodeSniffer\Files\File; -use Magento2\Helpers\PHPCSUtils\Utils\FunctionDeclarations; -use Magento2\Helpers\PHPCSUtils\Utils\Scopes; +use PHPCSUtils\Utils\FunctionDeclarations; +use PHPCSUtils\Utils\Scopes; /** * Applying the final modifier on a private method will produce a warning since PHP 8.0 diff --git a/Magento2/Sniffs/PHPCompatibility/RemovedCallingDestructAfterConstructorExitSniff.php b/Magento2/Sniffs/PHPCompatibility/RemovedCallingDestructAfterConstructorExitSniff.php index 8782039d..0c0dc944 100644 --- a/Magento2/Sniffs/PHPCompatibility/RemovedCallingDestructAfterConstructorExitSniff.php +++ b/Magento2/Sniffs/PHPCompatibility/RemovedCallingDestructAfterConstructorExitSniff.php @@ -13,11 +13,12 @@ use PHPCompatibility\Sniff; use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Util\Tokens; -use Magento2\Helpers\PHPCSUtils\Tokens\Collections; -use Magento2\Helpers\PHPCSUtils\Utils\FunctionDeclarations; -use Magento2\Helpers\PHPCSUtils\Utils\ObjectDeclarations; -use Magento2\Helpers\PHPCSUtils\Utils\Scopes; -use Magento2\Helpers\PHPCSUtils\Utils\UseStatements; +use PHPCSUtils\Tokens\Collections; +use PHPCSUtils\Utils\FunctionDeclarations; +use PHPCSUtils\Utils\MessageHelper; +use PHPCSUtils\Utils\ObjectDeclarations; +use PHPCSUtils\Utils\Scopes; +use PHPCSUtils\Utils\UseStatements; /** * As of PHP 8.0, when an object constructor exit()s, the destructor will no longer be called. @@ -100,7 +101,7 @@ public function process(File $phpcsFile, $stackPtr) // Skip over nested closed scopes as possible for efficiency. // Ignore arrow functions as they aren't closed scopes. - if (isset(Collections::$closedScopes[$tokens[$current]['code']]) === true + if (isset(Collections::closedScopes()[$tokens[$current]['code']]) === true && isset($tokens[$current]['scope_closer']) === true ) { $current = $tokens[$current]['scope_closer']; @@ -201,7 +202,7 @@ public function process(File $phpcsFile, $stackPtr) } foreach ($exits as $ptr) { - $this->addMessage($phpcsFile, $error, $ptr, $isError, $errorCode, [$tokens[$ptr]['content']]); + MessageHelper::addMessage($phpcsFile, $error, $ptr, $isError, $errorCode, [$tokens[$ptr]['content']]); } } } diff --git a/Magento2/Sniffs/PHPCompatibility/RemovedOptionalBeforeRequiredParamSniff.php b/Magento2/Sniffs/PHPCompatibility/RemovedOptionalBeforeRequiredParamSniff.php index 588ff7d2..5adff7ee 100644 --- a/Magento2/Sniffs/PHPCompatibility/RemovedOptionalBeforeRequiredParamSniff.php +++ b/Magento2/Sniffs/PHPCompatibility/RemovedOptionalBeforeRequiredParamSniff.php @@ -10,12 +10,11 @@ namespace Magento2\Sniffs\PHPCompatibility; -use PHP_CodeSniffer\Exceptions\RuntimeException; use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Util\Tokens; use PHPCompatibility\Sniff; -use Magento2\Helpers\PHPCSUtils\Tokens\Collections; -use Magento2\Helpers\PHPCSUtils\Utils\FunctionDeclarations; +use PHPCSUtils\Tokens\Collections; +use PHPCSUtils\Utils\FunctionDeclarations; /** * Declaring a required function parameter after an optional parameter is deprecated since PHP 8.0. @@ -58,7 +57,7 @@ public function register() { $this->allowedInDefault += Tokens::$emptyTokens; - return Collections::functionDeclarationTokensBC(); + return Collections::functionDeclarationTokens(); } /** @@ -78,14 +77,9 @@ public function process(File $phpcsFile, $stackPtr) return; } - try { - // Get all parameters from the function signature. - $parameters = FunctionDeclarations::getParameters($phpcsFile, $stackPtr); - if (empty($parameters)) { - return; - } - } catch (RuntimeException $e) { - // Most likely a T_STRING which wasn't an arrow function. + // Get all parameters from the function signature. + $parameters = FunctionDeclarations::getParameters($phpcsFile, $stackPtr); + if (empty($parameters)) { return; } diff --git a/Magento2/Tests/PHPCompatibility/BaseSniffTest.php b/Magento2/Tests/PHPCompatibility/BaseSniffTest.php index f6554540..6c84e617 100644 --- a/Magento2/Tests/PHPCompatibility/BaseSniffTest.php +++ b/Magento2/Tests/PHPCompatibility/BaseSniffTest.php @@ -12,7 +12,7 @@ use PHPUnit\Framework\TestCase; use PHP_CodeSniffer\Files\File; -use Magento2\Helpers\PHPCSUtils\BackCompat\Helper; +use PHPCSUtils\BackCompat\Helper; /** * Base sniff test class file. diff --git a/Magento2/Tests/PHPCompatibility/Util/CoreMethodTestFrame.php b/Magento2/Tests/PHPCompatibility/Util/CoreMethodTestFrame.php deleted file mode 100644 index e4441380..00000000 --- a/Magento2/Tests/PHPCompatibility/Util/CoreMethodTestFrame.php +++ /dev/null @@ -1,51 +0,0 @@ -=5.4", + "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "ext-json": "*", + "ext-zip": "*", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.0", + "yoast/phpunit-polyfills": "^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcbf", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "source": "https://github.com/PHPCSStandards/composer-installer" + }, + "time": "2023-01-05T11:28:13+00:00" + }, { "name": "phpcompatibility/php-compatibility", "version": "9.3.5", @@ -68,6 +146,80 @@ }, "time": "2019-12-27T09:44:58+00:00" }, + { + "name": "phpcsstandards/phpcsutils", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", + "reference": "0cfef5193e68e8ff179333d8ae937db62939b656" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/0cfef5193e68e8ff179333d8ae937db62939b656", + "reference": "0cfef5193e68e8ff179333d8ae937db62939b656", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.7.1 || 4.0.x-dev@dev" + }, + "require-dev": { + "ext-filter": "*", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcsstandards/phpcsdevcs": "^1.1.3", + "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.3", + "yoast/phpunit-polyfills": "^1.0.1" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPCSUtils/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors" + } + ], + "description": "A suite of utility functions for use with PHP_CodeSniffer", + "homepage": "https://phpcsutils.com/", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "phpcs3", + "standards", + "static analysis", + "tokens", + "utility" + ], + "support": { + "docs": "https://phpcsutils.com/", + "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues", + "source": "https://github.com/PHPCSStandards/PHPCSUtils" + }, + "time": "2023-04-17T16:27:27+00:00" + }, { "name": "phpstan/phpstan", "version": "1.9.14", From 32d4ded256d0c03071c11603a9e0ef1b4157b5da Mon Sep 17 00:00:00 2001 From: soumah Date: Tue, 18 Apr 2023 17:17:21 -0500 Subject: [PATCH 8/8] ACP2E-1868: Move sniff PHPCompatibility.TextStrings.RemovedDollarBraceStringEmbeds from PHPCompatibility to Magento Coding Standards - Added dependency on phpcsstandards/phpcsutils - Removed internal copy of utils from phpcsstandards/phpcsutils --- .../RemovedDollarBraceStringEmbedsSniff.php | 134 +++++++++ ...movedDollarBraceStringEmbedsUnitTest.1.inc | 90 +++++++ ...movedDollarBraceStringEmbedsUnitTest.2.inc | 42 +++ ...RemovedDollarBraceStringEmbedsUnitTest.php | 254 ++++++++++++++++++ Magento2/ruleset.xml | 1 + 5 files changed, 521 insertions(+) create mode 100644 Magento2/Sniffs/PHPCompatibility/RemovedDollarBraceStringEmbedsSniff.php create mode 100644 Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.1.inc create mode 100644 Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.2.inc create mode 100644 Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.php diff --git a/Magento2/Sniffs/PHPCompatibility/RemovedDollarBraceStringEmbedsSniff.php b/Magento2/Sniffs/PHPCompatibility/RemovedDollarBraceStringEmbedsSniff.php new file mode 100644 index 00000000..200cffbd --- /dev/null +++ b/Magento2/Sniffs/PHPCompatibility/RemovedDollarBraceStringEmbedsSniff.php @@ -0,0 +1,134 @@ + PHP allows embedding variables in strings with double-quotes (") and heredoc in various ways. + * > 1. Directly embedding variables (`$foo`) + * > 2. Braces outside the variable (`{$foo}`) + * > 3. Braces after the dollar sign (`${foo}`) + * > 4. Variable variables (`${expr}`, equivalent to `(string) ${expr}`) + * > + * > [...] to deprecate options 3 and 4 in PHP 8.2 and remove them in PHP 9.0. + * + * PHP version 8.2 + * PHP version 9.0 + * + * @link https://wiki.php.net/rfc/deprecate_dollar_brace_string_interpolation + * + * @since 10.0.0 + */ +class RemovedDollarBraceStringEmbedsSniff extends Sniff +{ + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 10.0.0 + * + * @return array + */ + public function register() + { + return [ + \T_DOUBLE_QUOTED_STRING, + \T_START_HEREDOC, + \T_DOLLAR_OPEN_CURLY_BRACES, + ]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 10.0.0 + * + * @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 Void or a stack pointer to skip forward. + */ + public function process(File $phpcsFile, $stackPtr) + { + if ($this->supportsAbove('8.2') === false) { + return; + } + + $tokens = $phpcsFile->getTokens(); + + /* + * Defensive coding, this code is not expected to ever actually be hit since PHPCS#3604 + * (included in 3.7.0), but _will_ be hit if a file containing a PHP 7.3 indented heredoc/nowdocs + * is scanned with PHPCS on PHP < 7.3. People shouldn't do that, but hey, we can't stop them. + */ + if ($tokens[$stackPtr]['code'] === \T_DOLLAR_OPEN_CURLY_BRACES) { + // @codeCoverageIgnoreStart + if ($tokens[($stackPtr - 1)]['code'] === \T_DOUBLE_QUOTED_STRING) { + --$stackPtr; + } else { + // Throw an error anyway, though it won't be very informative. + $message = 'Using ${} in strings is deprecated since PHP 8.2, use {$var} or {${expr}} instead.'; + $code = 'DeprecatedDollarBraceEmbed'; + $phpcsFile->addWarning($message, $stackPtr, $code); + return; + } + // @codeCoverageIgnoreEnd + } + + $endOfString = TextStrings::getEndOfCompleteTextString($phpcsFile, $stackPtr); + $startOfString = $stackPtr; + if ($tokens[$stackPtr]['code'] === \T_START_HEREDOC) { + $startOfString = ($stackPtr + 1); + } + + $contents = GetTokensAsString::normal($phpcsFile, $startOfString, $endOfString); + if (\strpos($contents, '${') === false) { + // No interpolation found or only interpolations which are still supported. + return ($endOfString + 1); + } + + $embeds = TextStrings::getEmbeds($contents); + foreach ($embeds as $offset => $embed) { + if (\strpos($embed, '${') !== 0) { + continue; + } + + // Figure out the stack pointer to throw the warning on. + $errorPtr = $startOfString; + $length = 0; + while (($length + $tokens[$errorPtr]['length']) < $offset) { + $length += $tokens[$errorPtr]['length']; + ++$errorPtr; + } + + // Type 4. + $message = 'Using %s (variable variables) in strings is deprecated since PHP 8.2, use {${expr}} instead.'; + $code = 'DeprecatedExpressionSyntax'; + if (\preg_match('`^\$\{(?P[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]+)(?:\[([\'"])?[^\$\{\}\]]+(?:\2)?\])?\}$`', $embed) === 1) { + // Type 3. + $message = 'Using ${var} in strings is deprecated since PHP 8.2, use {$var} instead. Found: %s'; + $code = 'DeprecatedVariableSyntax'; + } + + $phpcsFile->addWarning($message, $errorPtr, $code, [$embed]); + + } + + return ($endOfString + 1); + } +} diff --git a/Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.1.inc b/Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.1.inc new file mode 100644 index 00000000..4ec71d18 --- /dev/null +++ b/Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.1.inc @@ -0,0 +1,90 @@ +bar"; +$text = "some text $var some text"; + +$heredoc = <<bar}"; +echo "{$foo->bar()}"; +echo "{$foo['bar']->baz()()}"; +echo "{${$bar}}"; +echo "{$foo()}"; +echo "{${$object->getMethod()}}" +$text = "some text {$var} some text"; + +$heredoc = <<<"EOD" +some text {$var} some text +EOD; + +/* + * Not our target. + */ + +// Ordinary variable variables outside strings. +$foo = ${'bar'}; + +// Heredoc without embeds. +echo <<bar}"; +echo "${$object->getMethod()}" +$text = "some text ${(foo)} some text"; +echo "${substr('laruence', 0, 2)}"; + +echo "${foo["${bar}"]}"; +echo "${foo["${bar['baz']}"]}"; +echo "${foo->{$baz}}"; +echo "${foo->{${'a'}}}"; +echo "${foo->{"${'a'}"}}"; + +// Verify correct handling of stack pointers in multi-token code. +$text = "Line without embed +some text ${foo["${bar}"]} some text +some text ${foo["${bar['baz']}"]} some text +some text ${foo->{${'a'}}} some text +"; + +$heredoc = <<<"EOD" +some text ${(foo)} some text +EOD; diff --git a/Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.2.inc b/Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.2.inc new file mode 100644 index 00000000..f637fbf1 --- /dev/null +++ b/Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.2.inc @@ -0,0 +1,42 @@ +getMethod()} some text + some text ${foo["${bar['baz']}"]} some text + some text ${foo->{${'a'}}} some text + EOD; diff --git a/Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.php b/Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.php new file mode 100644 index 00000000..8009b2cb --- /dev/null +++ b/Magento2/Tests/PHPCompatibility/RemovedDollarBraceStringEmbedsUnitTest.php @@ -0,0 +1,254 @@ +sniffFile(__DIR__ . '/' . self::TEST_FILE, '8.2'); + $this->assertWarning($file, $line, 'Using ${var} in strings is deprecated since PHP 8.2, use {$var} instead. Found: ' . $found); + } + + /** + * Data provider. + * + * @see testRemovedDollarBraceStringEmbedsType3() + * + * @return array + */ + public function dataRemovedDollarBraceStringEmbedsType3() + { + return [ + [57, '${foo}'], + [58, '${foo[\'bar\']}'], + [59, '${foo}'], + [59, '${text}'], + [62, '${foo}'], + [65, '${foo}'], + ]; + } + + + /** + * Test that variable embeds of "type 4" - Variable variables (�${expr}�, equivalent to + * (string) ${expr}) - are correctly detected. + * + * @dataProvider dataRemovedDollarBraceStringEmbedsType4 + * + * @param int $line The line number. + * @param string $found The embedded expression found. + * + * @return void + */ + public function testRemovedDollarBraceStringEmbedsType4($line, $found) + { + $file = $this->sniffFile(__DIR__ . '/' . self::TEST_FILE, '8.2'); + $this->assertWarning($file, $line, "Using {$found} (variable variables) in strings is deprecated since PHP 8.2, use {\${expr}} instead."); + } + + /** + * Data provider. + * + * @see testRemovedDollarBraceStringEmbedsType4() + * + * @return array + */ + public function dataRemovedDollarBraceStringEmbedsType4() + { + return [ + [68, '${$bar}'], + [69, '${(foo)}'], + [70, '${foo->bar}'], + [71, '${$object->getMethod()}'], + [72, '${(foo)}'], + [73, '${substr(\'laruence\', 0, 2)}'], + [75, '${foo["${bar}"]}'], + [76, '${foo["${bar[\'baz\']}"]}'], + [77, '${foo->{$baz}}'], + [78, '${foo->{${\'a\'}}}'], + [79, '${foo->{"${\'a\'}"}}'], + [83, '${foo["${bar}"]}'], + [84, '${foo["${bar[\'baz\']}"]}'], + [85, '${foo->{${\'a\'}}}'], + [89, '${(foo)}'], + ]; + } + + + /** + * Test that variable embeds of "type 3" - Braces after the dollar sign (�${foo}�) - + * are correctly detected in PHP 7.3+ indented heredocs. + * + * @dataProvider dataRemovedDollarBraceStringEmbedsType3InIndentedHeredoc + * + * @param int $line The line number. + * @param string $found The embedded variable found. + * + * @return void + */ + public function testRemovedDollarBraceStringEmbedsType3InIndentedHeredoc($line, $found) + { + if (\PHP_VERSION_ID < 70300) { + $this->markTestSkipped('Test code involving PHP 7.3 heredocs will not tokenize correctly on PHP < 7.3'); + } + + $file = $this->sniffFile(__DIR__ . '/' . self::TEST_FILE_PHP73HEREDOCS, '8.2'); + $this->assertWarning($file, $line, 'Using ${var} in strings is deprecated since PHP 8.2, use {$var} instead. Found: ' . $found); + } + + /** + * Data provider. + * + * @see testRemovedDollarBraceStringEmbedsType3InIndentedHeredoc() + * + * @return array + */ + public function dataRemovedDollarBraceStringEmbedsType3InIndentedHeredoc() + { + return [ + [33, '${foo[\'bar\']}'], + ]; + } + + + /** + * Test that variable embeds of "type 4" - Variable variables (�${expr}�, equivalent to + * (string) ${expr}) - are correctly detected in PHP 7.3+ indented heredocs. + * + * @dataProvider dataRemovedDollarBraceStringEmbedsType4InIndentedHeredoc + * + * @param int $line The line number. + * @param string $found The embedded expression found. + * + * @return void + */ + public function testRemovedDollarBraceStringEmbedsType4InIndentedHeredoc($line, $found) + { + if (\PHP_VERSION_ID < 70300) { + $this->markTestSkipped('Test code involving PHP 7.3 heredocs will not tokenize correctly on PHP < 7.3'); + } + + $file = $this->sniffFile(__DIR__ . '/' . self::TEST_FILE_PHP73HEREDOCS, '8.2'); + $this->assertWarning($file, $line, "Using {$found} (variable variables) in strings is deprecated since PHP 8.2, use {\${expr}} instead."); + } + + /** + * Data provider. + * + * @see testRemovedDollarBraceStringEmbedsType4InIndentedHeredoc() + * + * @return array + */ + public function dataRemovedDollarBraceStringEmbedsType4InIndentedHeredoc() + { + return [ + [39, '${$object->getMethod()}'], + [40, '${foo["${bar[\'baz\']}"]}'], + [41, '${foo->{${\'a\'}}}'], + ]; + } + + + /** + * Verify the sniff does not throw false positives for valid code. + * + * @dataProvider dataTestFiles + * + * @param string $testFile File name for the test case file to use. + * @param int $lines Number of lines at the top of the file for which we don't expect errors. + * + * @return void + */ + public function testNoFalsePositives($testFile, $lines) + { + if ($testFile === self::TEST_FILE_PHP73HEREDOCS && \PHP_VERSION_ID < 70300) { + $this->markTestSkipped('Test code involving PHP 7.3 heredocs will not tokenize correctly on PHP < 7.3'); + } + + $file = $this->sniffFile(__DIR__ . '/' . $testFile, '8.2'); + + // No errors expected on the first # lines. + for ($line = 1; $line <= $lines; $line++) { + $this->assertNoViolation($file, $line); + } + } + + + /** + * Verify no notices are thrown at all. + * + * @dataProvider dataTestFiles + * + * @param string $testFile File name for the test case file to use. + * + * @return void + */ + public function testNoViolationsInFileOnValidVersion($testFile) + { + if ($testFile === self::TEST_FILE_PHP73HEREDOCS && \PHP_VERSION_ID < 70300) { + $this->markTestSkipped('Test code involving PHP 7.3 heredocs will not tokenize correctly on PHP < 7.3'); + } + + $file = $this->sniffFile(__DIR__ . '/' . $testFile, '8.1'); + $this->assertNoViolation($file); + } + + + /** + * Data provider. + * + * @return array + */ + public function dataTestFiles() + { + return [ + [self::TEST_FILE, 51], + [self::TEST_FILE_PHP73HEREDOCS, 26], + ]; + } +} diff --git a/Magento2/ruleset.xml b/Magento2/ruleset.xml index ee171871..b26c8a73 100644 --- a/Magento2/ruleset.xml +++ b/Magento2/ruleset.xml @@ -783,4 +783,5 @@ +