From 68782239d331285b5ba5493a58e4b4c9c87e8280 Mon Sep 17 00:00:00 2001 From: bigbiteci Date: Mon, 5 Jun 2023 08:59:18 +0000 Subject: [PATCH] built from 6cd3bf1caa5167fd070676f9f76c054ca34778ea --- .deployignore | 16 - .gitignore | 6 - .phpcs.xml.dist | 4 - README.md | 48 - dist/scripts/dashboard-31d6cfe0.js | 0 dist/scripts/editor-071c76ff.js | 2 + dist/scripts/editor-071c76ff.js.map | 1 + dist/styles/dashboard-2d07d2f6.css | 3 + dist/styles/dashboard-2d07d2f6.css.map | 1 + inc/asset-settings.php | 13 + package-lock.json | 9585 ----------------- package.json | 21 - src/dashboard/styles/styles.scss | 137 - src/editor/blocks/index.js | 1 - src/editor/blocks/markdown-parser/edit.js | 20 - src/editor/blocks/markdown-parser/index.js | 8 - .../register-plugins/CustomBlockFields.js | 59 - src/editor/register-plugins/index.js | 8 - src/editor/settings.js | 4 - src/entrypoints/dashboard.js | 3 - src/entrypoints/editor.js | 2 - vendor/autoload.php | 25 + vendor/autoload_packages.php | 13 + .../jetpack-autoloader/.phpcs.dir.xml | 9 + .../jetpack-autoloader/CHANGELOG.md | 383 + .../automattic/jetpack-autoloader/LICENSE.txt | 357 + .../automattic/jetpack-autoloader/SECURITY.md | 38 + .../jetpack-autoloader/composer.json | 42 + vendor/automattic/vipwpcs/CHANGELOG.md | 589 + vendor/automattic/vipwpcs/LICENSE.md | 67 + .../vipwpcs/WordPress-VIP-Go/ruleset-test.inc | 574 + .../vipwpcs/WordPress-VIP-Go/ruleset-test.php | 345 + .../vipwpcs/WordPress-VIP-Go/ruleset.xml | 273 + .../AbstractVariableRestrictionsSniff.php | 231 + .../Classes/DeclarationCompatibilitySniff.php | 390 + .../Classes/RestrictedExtendClassesSniff.php | 59 + .../Sniffs/Compatibility/ZoninatorSniff.php | 90 + .../Sniffs/Constants/ConstantStringSniff.php | 80 + .../Constants/RestrictedConstantsSniff.php | 112 + .../Sniffs/Files/IncludingFileSniff.php | 207 + .../Sniffs/Files/IncludingNonPHPFileSniff.php | 105 + .../Functions/CheckReturnValueSniff.php | 315 + .../Sniffs/Functions/DynamicCallsSniff.php | 188 + .../Functions/RestrictedFunctionsSniff.php | 371 + .../Sniffs/Functions/StripTagsSniff.php | 59 + .../Hooks/AlwaysReturnInFilterSniff.php | 299 + .../Sniffs/Hooks/PreGetPostsSniff.php | 450 + .../Sniffs/Hooks/RestrictedHooksSniff.php | 127 + .../JS/DangerouslySetInnerHTMLSniff.php | 73 + .../Sniffs/JS/HTMLExecutingFunctionsSniff.php | 134 + .../Sniffs/JS/InnerHTMLSniff.php | 87 + .../Sniffs/JS/StringConcatSniff.php | 76 + .../Sniffs/JS/StrippingTagsSniff.php | 76 + .../Sniffs/JS/WindowSniff.php | 131 + .../BatcacheWhitelistedParamsSniff.php | 113 + .../Performance/CacheValueOverrideSniff.php | 146 + .../Performance/FetchingRemoteDataSniff.php | 61 + .../Performance/LowExpiryCacheTimeSniff.php | 206 + .../Sniffs/Performance/NoPagingSniff.php | 61 + .../Sniffs/Performance/OrderByRandSniff.php | 59 + .../Sniffs/Performance/RegexpCompareSniff.php | 65 + .../Performance/RemoteRequestTimeoutSniff.php | 61 + .../TaxonomyMetaInOptionsSniff.php | 134 + .../Sniffs/Performance/WPQueryParamsSniff.php | 104 + .../EscapingVoidReturnFunctionsSniff.php | 70 + .../Security/ExitAfterRedirectSniff.php | 68 + .../Sniffs/Security/MustacheSniff.php | 77 + .../Security/PHPFilterFunctionsSniff.php | 90 + .../Security/ProperEscapingFunctionSniff.php | 277 + .../Sniffs/Security/StaticStrreplaceSniff.php | 87 + .../Sniffs/Security/TwigSniff.php | 62 + .../Sniffs/Security/UnderscorejsSniff.php | 162 + .../Sniffs/Security/VuejsSniff.php | 55 + .../WordPressVIPMinimum/Sniffs/Sniff.php | 20 + .../UserExperience/AdminBarRemovalSniff.php | 429 + .../Variables/RestrictedVariablesSniff.php | 70 + .../Sniffs/Variables/ServerVariablesSniff.php | 75 + .../Variables/VariableAnalysisSniff.php | 81 + .../WordPressVIPMinimum/ruleset-test.inc | 622 ++ .../WordPressVIPMinimum/ruleset-test.php | 325 + .../vipwpcs/WordPressVIPMinimum/ruleset.xml | 177 + vendor/automattic/vipwpcs/composer.json | 57 + .../bigbite/phpcs-config/.circleci/config.yml | 51 + .../BigBite/Docs/Files/FileNameStandard.xml | 11 + .../BigBite/Sniffs/Files/FileNameSniff.php | 222 + .../bigbite/phpcs-config/BigBite/ruleset.xml | 191 + vendor/bigbite/phpcs-config/LICENSE | 21 + vendor/bigbite/phpcs-config/composer.json | 95 + .../phpcs-config/phpcs.xml.dist.sample | 13 + vendor/bin/phpcbf | 119 + vendor/bin/phpcs | 119 + vendor/composer/ClassLoader.php | 585 + vendor/composer/InstalledVersions.php | 359 + vendor/composer/LICENSE | 21 + vendor/composer/autoload_classmap.php | 82 + vendor/composer/autoload_files.php | 15 + vendor/composer/autoload_namespaces.php | 9 + vendor/composer/autoload_psr4.php | 18 + vendor/composer/autoload_real.php | 50 + vendor/composer/autoload_static.php | 175 + .../composer/installed.json | 806 +- vendor/composer/installed.php | 160 + vendor/composer/jetpack_autoload_classmap.php | 297 + vendor/composer/jetpack_autoload_filemap.php | 33 + vendor/composer/jetpack_autoload_psr4.php | 45 + vendor/composer/platform_check.php | 26 + .../.github_changelog_generator | 34 + .../.remarkrc | 6 + .../.yamllint | 8 + .../CODE_OF_CONDUCT.md | 129 + .../LICENSE.md | 21 + .../bin/generate-changelog.sh | 49 + .../composer.json | 57 + vendor/dflydev/dot-access-data/CHANGELOG.md | 67 + vendor/dflydev/dot-access-data/LICENSE | 19 + vendor/dflydev/dot-access-data/composer.json | 67 + .../class-autoloader-handler.php | 147 + .../class-autoloader-locator.php | 90 + .../jetpack-autoloader/class-autoloader.php | 90 + vendor/jetpack-autoloader/class-container.php | 150 + .../jetpack-autoloader/class-hook-manager.php | 76 + .../class-latest-autoloader-guard.php | 86 + .../class-manifest-reader.php | 99 + .../class-path-processor.php | 194 + .../class-php-autoloader.php | 93 + .../class-plugin-locator.php | 156 + .../class-plugins-handler.php | 164 + .../class-shutdown-handler.php | 92 + .../class-version-loader.php | 164 + .../class-version-selector.php | 69 + vendor/league/commonmark/.phpstorm.meta.php | 104 + vendor/league/commonmark/CHANGELOG.md | 588 + vendor/league/commonmark/LICENSE | 28 + vendor/league/commonmark/composer.json | 125 + vendor/league/config/CHANGELOG.md | 42 + vendor/league/config/LICENSE.md | 28 + vendor/league/config/composer.json | 69 + vendor/nette/schema/composer.json | 39 + vendor/nette/schema/contributing.md | 33 + vendor/nette/schema/license.md | 60 + vendor/nette/schema/readme.md | 441 + vendor/nette/utils/.phpstorm.meta.php | 13 + vendor/nette/utils/composer.json | 52 + vendor/nette/utils/license.md | 60 + vendor/nette/utils/readme.md | 56 + vendor/psr/event-dispatcher/.editorconfig | 15 + vendor/psr/event-dispatcher/LICENSE | 21 + vendor/psr/event-dispatcher/composer.json | 26 + .../phpcs-variable-analysis/LICENSE | 71 + .../VariableAnalysis/Lib/Constants.php | 260 + .../VariableAnalysis/Lib/EnumInfo.php | 45 + .../VariableAnalysis/Lib/ForLoopInfo.php | 114 + .../VariableAnalysis/Lib/Helpers.php | 1599 +++ .../VariableAnalysis/Lib/ScopeInfo.php | 40 + .../VariableAnalysis/Lib/ScopeManager.php | 108 + .../VariableAnalysis/Lib/ScopeType.php | 12 + .../VariableAnalysis/Lib/VariableInfo.php | 109 + .../CodeAnalysis/VariableAnalysisSniff.php | 2111 ++++ .../VariableAnalysis/ruleset.xml | 4 + .../VariableAnalysis/ruleset.xml.example | 11 + .../phpcs-variable-analysis/composer.json | 61 + .../psalm-autoloader.php | 4 + .../phpcs-variable-analysis/psalm.xml | 19 + .../squizlabs/php_codesniffer/CONTRIBUTING.md | 13 + .../php_codesniffer/CodeSniffer.conf.dist | 9 + vendor/squizlabs/php_codesniffer/autoload.php | 342 + vendor/squizlabs/php_codesniffer/bin/phpcbf | 19 + .../squizlabs/php_codesniffer/bin/phpcbf.bat | 12 + vendor/squizlabs/php_codesniffer/bin/phpcs | 19 + .../squizlabs/php_codesniffer/bin/phpcs.bat | 12 + .../squizlabs/php_codesniffer/composer.json | 41 + vendor/squizlabs/php_codesniffer/licence.txt | 24 + vendor/squizlabs/php_codesniffer/phpcs.xsd | 136 + .../php_codesniffer/tests/AllTests.php | 64 + .../tests/Core/AbstractMethodUnitTest.php | 140 + .../php_codesniffer/tests/Core/AllTests.php | 63 + .../Autoloader/DetermineLoadedClassTest.php | 119 + .../tests/Core/Autoloader/TestFiles/A.inc | 3 + .../tests/Core/Autoloader/TestFiles/B.inc | 4 + .../tests/Core/Autoloader/TestFiles/C.inc | 4 + .../tests/Core/Autoloader/TestFiles/Sub/C.inc | 5 + .../tests/Core/ErrorSuppressionTest.php | 1247 +++ .../Core/File/FindEndOfStatementTest.inc | 105 + .../Core/File/FindEndOfStatementTest.php | 415 + .../Core/File/FindExtendedClassNameTest.inc | 37 + .../Core/File/FindExtendedClassNameTest.php | 93 + .../FindImplementedInterfaceNamesTest.inc | 35 + .../FindImplementedInterfaceNamesTest.php | 104 + .../Core/File/FindStartOfStatementTest.inc | 123 + .../Core/File/FindStartOfStatementTest.php | 503 + .../Core/File/GetMemberPropertiesTest.inc | 304 + .../Core/File/GetMemberPropertiesTest.php | 864 ++ .../Core/File/GetMethodParametersTest.inc | 164 + .../Core/File/GetMethodParametersTest.php | 1145 ++ .../Core/File/GetMethodPropertiesTest.inc | 152 + .../Core/File/GetMethodPropertiesTest.php | 910 ++ .../tests/Core/File/IsReferenceTest.inc | 150 + .../tests/Core/File/IsReferenceTest.php | 248 + .../tests/Core/Filters/Filter/AcceptTest.php | 154 + .../tests/Core/Filters/Filter/AcceptTest.xml | 16 + .../tests/Core/IsCamelCapsTest.php | 135 + .../RuleInclusionAbsoluteLinuxTest.php | 118 + .../RuleInclusionAbsoluteLinuxTest.xml | 11 + .../RuleInclusionAbsoluteWindowsTest.php | 119 + .../RuleInclusionAbsoluteWindowsTest.xml | 11 + .../Ruleset/RuleInclusionTest-include.xml | 10 + .../tests/Core/Ruleset/RuleInclusionTest.php | 297 + .../tests/Core/Ruleset/RuleInclusionTest.xml | 46 + .../Core/Sniffs/AbstractArraySniffTest.inc | 51 + .../Core/Sniffs/AbstractArraySniffTest.php | 290 + .../Sniffs/AbstractArraySniffTestable.php | 65 + .../AnonClassParenthesisOwnerTest.inc | 19 + .../AnonClassParenthesisOwnerTest.php | 144 + .../tests/Core/Tokenizer/ArrayKeywordTest.inc | 35 + .../tests/Core/Tokenizer/ArrayKeywordTest.php | 170 + .../tests/Core/Tokenizer/AttributesTest.inc | 90 + .../tests/Core/Tokenizer/AttributesTest.php | 658 ++ .../tests/Core/Tokenizer/BackfillEnumTest.inc | 95 + .../tests/Core/Tokenizer/BackfillEnumTest.php | 229 + .../BackfillExplicitOctalNotationTest.inc | 28 + .../BackfillExplicitOctalNotationTest.php | 114 + .../Core/Tokenizer/BackfillFnTokenTest.inc | 198 + .../Core/Tokenizer/BackfillFnTokenTest.php | 802 ++ .../Core/Tokenizer/BackfillMatchTokenTest.inc | 319 + .../Core/Tokenizer/BackfillMatchTokenTest.php | 529 + .../BackfillNumericSeparatorTest.inc | 94 + .../BackfillNumericSeparatorTest.php | 420 + .../Core/Tokenizer/BackfillReadonlyTest.inc | 100 + .../Core/Tokenizer/BackfillReadonlyTest.php | 236 + .../tests/Core/Tokenizer/BitwiseOrTest.inc | 134 + .../tests/Core/Tokenizer/BitwiseOrTest.php | 138 + .../ContextSensitiveKeywordsTest.inc | 227 + .../ContextSensitiveKeywordsTest.php | 509 + .../Core/Tokenizer/DefaultKeywordTest.inc | 203 + .../Core/Tokenizer/DefaultKeywordTest.php | 302 + .../tests/Core/Tokenizer/DoubleArrowTest.inc | 281 + .../tests/Core/Tokenizer/DoubleArrowTest.php | 237 + .../Core/Tokenizer/DoubleQuotedStringTest.inc | 52 + .../Core/Tokenizer/DoubleQuotedStringTest.php | 136 + .../tests/Core/Tokenizer/EnumCaseTest.inc | 95 + .../tests/Core/Tokenizer/EnumCaseTest.php | 157 + .../tests/Core/Tokenizer/FinallyTest.inc | 40 + .../tests/Core/Tokenizer/FinallyTest.php | 96 + .../tests/Core/Tokenizer/GotoLabelTest.inc | 56 + .../tests/Core/Tokenizer/GotoLabelTest.php | 175 + .../Tokenizer/HeredocNowdocCloserTest.inc | 43 + .../Tokenizer/HeredocNowdocCloserTest.php | 150 + .../Core/Tokenizer/HeredocStringTest.inc | 193 + .../Core/Tokenizer/HeredocStringTest.php | 153 + .../NamedFunctionCallArgumentsTest.inc | 407 + .../NamedFunctionCallArgumentsTest.php | 885 ++ .../Tokenizer/NullsafeObjectOperatorTest.inc | 29 + .../Tokenizer/NullsafeObjectOperatorTest.php | 140 + .../ScopeSettingWithNamespaceOperatorTest.inc | 19 + .../ScopeSettingWithNamespaceOperatorTest.php | 98 + .../tests/Core/Tokenizer/ShortArrayTest.inc | 111 + .../tests/Core/Tokenizer/ShortArrayTest.php | 135 + .../Tokenizer/StableCommentWhitespaceTest.inc | 139 + .../Tokenizer/StableCommentWhitespaceTest.php | 1056 ++ .../StableCommentWhitespaceWinTest.inc | 63 + .../StableCommentWhitespaceWinTest.php | 367 + .../Core/Tokenizer/TypeIntersectionTest.inc | 120 + .../Core/Tokenizer/TypeIntersectionTest.php | 138 + .../UndoNamespacedNameSingleTokenTest.inc | 147 + .../UndoNamespacedNameSingleTokenTest.php | 1295 +++ .../php_codesniffer/tests/FileList.php | 94 + .../tests/Standards/AbstractSniffUnitTest.php | 461 + .../tests/Standards/AllSniffs.php | 123 + .../php_codesniffer/tests/TestSuite.php | 35 + .../php_codesniffer/tests/TestSuite7.php | 35 + .../php_codesniffer/tests/bootstrap.php | 91 + .../deprecation-contracts/CHANGELOG.md | 5 + vendor/symfony/deprecation-contracts/LICENSE | 19 + .../deprecation-contracts/composer.json | 35 + .../deprecation-contracts/function.php | 27 + vendor/symfony/polyfill-php80/LICENSE | 19 + vendor/symfony/polyfill-php80/Php80.php | 115 + vendor/symfony/polyfill-php80/PhpToken.php | 103 + .../Resources/stubs/Attribute.php | 31 + .../Resources/stubs/PhpToken.php | 16 + .../Resources/stubs/Stringable.php | 20 + .../Resources/stubs/UnhandledMatchError.php | 16 + .../Resources/stubs/ValueError.php | 16 + vendor/symfony/polyfill-php80/bootstrap.php | 42 + vendor/symfony/polyfill-php80/composer.json | 40 + .../wp-coding-standards/wpcs/.gitattributes | 25 + vendor/wp-coding-standards/wpcs/CHANGELOG.md | 1208 +++ vendor/wp-coding-standards/wpcs/LICENSE | 21 + .../wpcs/WordPress-Core/ruleset.xml | 529 + .../wpcs/WordPress-Docs/ruleset.xml | 109 + .../wpcs/WordPress-Extra/ruleset.xml | 187 + ...stractArrayAssignmentRestrictionsSniff.php | 240 + .../AbstractClassRestrictionsSniff.php | 245 + .../AbstractFunctionParameterSniff.php | 112 + .../AbstractFunctionRestrictionsSniff.php | 342 + .../Docs/Arrays/ArrayIndentationStandard.xml | 112 + .../ArrayKeySpacingRestrictionsStandard.xml | 27 + .../MultipleStatementAlignmentStandard.xml | 46 + .../Classes/ClassInstantiationStandard.xml | 53 + .../EscapedNotTranslatedStandard.xml | 20 + .../DateTime/CurrentTimeTimestampStandard.xml | 31 + .../ValidHookNameStandard.xml | 32 + .../ValidPostTypeSlugStandard.xml | 117 + .../Docs/PHP/DisallowShortTernaryStandard.xml | 20 + .../WordPress/Docs/PHP/IniSetStandard.xml | 36 + .../Docs/Security/SafeRedirectStandard.xml | 19 + .../Docs/WP/CronIntervalStandard.xml | 41 + .../Docs/WP/DeprecatedClassesStandard.xml | 19 + .../Docs/WP/DeprecatedFunctionsStandard.xml | 19 + .../WP/DeprecatedParameterValuesStandard.xml | 19 + .../Docs/WP/DeprecatedParametersStandard.xml | 36 + .../Docs/WP/EnqueuedResourcesStandard.xml | 53 + .../Docs/WP/PostsPerPageStandard.xml | 69 + .../CastStructureSpacingStandard.xml | 23 + .../WhiteSpace/DisallowInlineTabsStandard.xml | 25 + .../WhiteSpace/PrecisionAlignmentStandard.xml | 31 + .../wpcs/WordPress/PHPCSHelper.php | 109 + .../wpcs/WordPress/Sniff.php | 3424 ++++++ .../Arrays/ArrayDeclarationSpacingSniff.php | 468 + .../Sniffs/Arrays/ArrayIndentationSniff.php | 542 + .../ArrayKeySpacingRestrictionsSniff.php | 192 + .../Arrays/CommaAfterArrayItemSniff.php | 313 + .../MultipleStatementAlignmentSniff.php | 617 ++ .../Classes/ClassInstantiationSniff.php | 204 + .../AssignmentInConditionSniff.php | 235 + .../CodeAnalysis/EmptyStatementSniff.php | 161 + .../EscapedNotTranslatedSniff.php | 90 + .../Sniffs/DB/DirectDatabaseQuerySniff.php | 265 + .../DB/PreparedSQLPlaceholdersSniff.php | 661 ++ .../WordPress/Sniffs/DB/PreparedSQLSniff.php | 210 + .../Sniffs/DB/RestrictedClassesSniff.php | 60 + .../Sniffs/DB/RestrictedFunctionsSniff.php | 66 + .../WordPress/Sniffs/DB/SlowDBQuerySniff.php | 86 + .../DateTime/CurrentTimeTimestampSniff.php | 174 + .../DateTime/RestrictedFunctionsSniff.php | 62 + .../WordPress/Sniffs/Files/FileNameSniff.php | 248 + .../PrefixAllGlobalsSniff.php | 1046 ++ .../ValidFunctionNameSniff.php | 187 + .../NamingConventions/ValidHookNameSniff.php | 289 + .../ValidPostTypeSlugSniff.php | 208 + .../ValidVariableNameSniff.php | 299 + .../Sniffs/PHP/DevelopmentFunctionsSniff.php | 66 + .../Sniffs/PHP/DisallowShortTernarySniff.php | 65 + .../PHP/DiscouragedPHPFunctionsSniff.php | 103 + .../WordPress/Sniffs/PHP/DontExtractSniff.php | 55 + .../wpcs/WordPress/Sniffs/PHP/IniSetSniff.php | 177 + .../Sniffs/PHP/NoSilencedErrorsSniff.php | 239 + .../Sniffs/PHP/POSIXFunctionsSniff.php | 76 + .../Sniffs/PHP/PregQuoteDelimiterSniff.php | 69 + .../PHP/RestrictedPHPFunctionsSniff.php | 48 + .../Sniffs/PHP/StrictComparisonsSniff.php | 56 + .../Sniffs/PHP/StrictInArraySniff.php | 105 + .../WordPress/Sniffs/PHP/TypeCastsSniff.php | 99 + .../Sniffs/PHP/YodaConditionsSniff.php | 125 + .../Sniffs/Security/EscapeOutputSniff.php | 505 + .../Security/NonceVerificationSniff.php | 178 + .../Sniffs/Security/PluginMenuSlugSniff.php | 89 + .../Sniffs/Security/SafeRedirectSniff.php | 48 + .../Security/ValidatedSanitizedInputSniff.php | 233 + .../Sniffs/Utils/I18nTextDomainFixerSniff.php | 731 ++ .../Sniffs/WP/AlternativeFunctionsSniff.php | 316 + .../Sniffs/WP/CapitalPDangitSniff.php | 292 + .../WordPress/Sniffs/WP/CronIntervalSniff.php | 235 + .../Sniffs/WP/DeprecatedClassesSniff.php | 120 + .../Sniffs/WP/DeprecatedFunctionsSniff.php | 1422 +++ .../WP/DeprecatedParameterValuesSniff.php | 216 + .../Sniffs/WP/DeprecatedParametersSniff.php | 338 + .../Sniffs/WP/DiscouragedConstantsSniff.php | 217 + .../Sniffs/WP/DiscouragedFunctionsSniff.php | 57 + .../WP/EnqueuedResourceParametersSniff.php | 223 + .../Sniffs/WP/EnqueuedResourcesSniff.php | 64 + .../WP/GlobalVariablesOverrideSniff.php | 466 + .../wpcs/WordPress/Sniffs/WP/I18nSniff.php | 789 ++ .../WordPress/Sniffs/WP/PostsPerPageSniff.php | 78 + .../Sniffs/WP/TimezoneChangeSniff.php | 88 + .../WhiteSpace/CastStructureSpacingSniff.php | 62 + .../ControlStructureSpacingSniff.php | 572 + .../WhiteSpace/DisallowInlineTabsSniff.php | 104 + .../WhiteSpace/OperatorSpacingSniff.php | 64 + .../WhiteSpace/PrecisionAlignmentSniff.php | 199 + .../wpcs/WordPress/ruleset.xml | 17 + vendor/wp-coding-standards/wpcs/composer.json | 56 + .../wpcs/phpcs.xml.dist.sample | 92 + webpack.config.js | 1 - 384 files changed, 68178 insertions(+), 10308 deletions(-) delete mode 100644 .deployignore delete mode 100644 .gitignore delete mode 100644 .phpcs.xml.dist delete mode 100644 README.md create mode 100644 dist/scripts/dashboard-31d6cfe0.js create mode 100644 dist/scripts/editor-071c76ff.js create mode 100644 dist/scripts/editor-071c76ff.js.map create mode 100644 dist/styles/dashboard-2d07d2f6.css create mode 100644 dist/styles/dashboard-2d07d2f6.css.map create mode 100644 inc/asset-settings.php delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 src/dashboard/styles/styles.scss delete mode 100644 src/editor/blocks/index.js delete mode 100644 src/editor/blocks/markdown-parser/edit.js delete mode 100644 src/editor/blocks/markdown-parser/index.js delete mode 100644 src/editor/register-plugins/CustomBlockFields.js delete mode 100644 src/editor/register-plugins/index.js delete mode 100644 src/editor/settings.js delete mode 100644 src/entrypoints/dashboard.js delete mode 100644 src/entrypoints/editor.js create mode 100644 vendor/autoload.php create mode 100644 vendor/autoload_packages.php create mode 100644 vendor/automattic/jetpack-autoloader/.phpcs.dir.xml create mode 100644 vendor/automattic/jetpack-autoloader/CHANGELOG.md create mode 100644 vendor/automattic/jetpack-autoloader/LICENSE.txt create mode 100644 vendor/automattic/jetpack-autoloader/SECURITY.md create mode 100644 vendor/automattic/jetpack-autoloader/composer.json create mode 100644 vendor/automattic/vipwpcs/CHANGELOG.md create mode 100644 vendor/automattic/vipwpcs/LICENSE.md create mode 100644 vendor/automattic/vipwpcs/WordPress-VIP-Go/ruleset-test.inc create mode 100644 vendor/automattic/vipwpcs/WordPress-VIP-Go/ruleset-test.php create mode 100644 vendor/automattic/vipwpcs/WordPress-VIP-Go/ruleset.xml create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/AbstractVariableRestrictionsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Classes/DeclarationCompatibilitySniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Classes/RestrictedExtendClassesSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Compatibility/ZoninatorSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Constants/ConstantStringSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Constants/RestrictedConstantsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Files/IncludingFileSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Files/IncludingNonPHPFileSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/CheckReturnValueSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/DynamicCallsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/RestrictedFunctionsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/StripTagsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Hooks/AlwaysReturnInFilterSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Hooks/PreGetPostsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Hooks/RestrictedHooksSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/DangerouslySetInnerHTMLSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/HTMLExecutingFunctionsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/InnerHTMLSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/StringConcatSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/StrippingTagsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/WindowSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/BatcacheWhitelistedParamsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/FetchingRemoteDataSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/LowExpiryCacheTimeSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/NoPagingSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/OrderByRandSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/RegexpCompareSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/RemoteRequestTimeoutSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/TaxonomyMetaInOptionsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/WPQueryParamsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/EscapingVoidReturnFunctionsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/ExitAfterRedirectSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/MustacheSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/PHPFilterFunctionsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/ProperEscapingFunctionSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/StaticStrreplaceSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/TwigSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/UnderscorejsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/VuejsSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Sniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/UserExperience/AdminBarRemovalSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Variables/RestrictedVariablesSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Variables/ServerVariablesSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Variables/VariableAnalysisSniff.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/ruleset-test.inc create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/ruleset-test.php create mode 100644 vendor/automattic/vipwpcs/WordPressVIPMinimum/ruleset.xml create mode 100644 vendor/automattic/vipwpcs/composer.json create mode 100644 vendor/bigbite/phpcs-config/.circleci/config.yml create mode 100644 vendor/bigbite/phpcs-config/BigBite/Docs/Files/FileNameStandard.xml create mode 100644 vendor/bigbite/phpcs-config/BigBite/Sniffs/Files/FileNameSniff.php create mode 100644 vendor/bigbite/phpcs-config/BigBite/ruleset.xml create mode 100644 vendor/bigbite/phpcs-config/LICENSE create mode 100644 vendor/bigbite/phpcs-config/composer.json create mode 100644 vendor/bigbite/phpcs-config/phpcs.xml.dist.sample create mode 100755 vendor/bin/phpcbf create mode 100755 vendor/bin/phpcs create mode 100644 vendor/composer/ClassLoader.php create mode 100644 vendor/composer/InstalledVersions.php create mode 100644 vendor/composer/LICENSE create mode 100644 vendor/composer/autoload_classmap.php create mode 100644 vendor/composer/autoload_files.php create mode 100644 vendor/composer/autoload_namespaces.php create mode 100644 vendor/composer/autoload_psr4.php create mode 100644 vendor/composer/autoload_real.php create mode 100644 vendor/composer/autoload_static.php rename composer.lock => vendor/composer/installed.json (92%) create mode 100644 vendor/composer/installed.php create mode 100644 vendor/composer/jetpack_autoload_classmap.php create mode 100644 vendor/composer/jetpack_autoload_filemap.php create mode 100644 vendor/composer/jetpack_autoload_psr4.php create mode 100644 vendor/composer/platform_check.php create mode 100644 vendor/dealerdirect/phpcodesniffer-composer-installer/.github_changelog_generator create mode 100644 vendor/dealerdirect/phpcodesniffer-composer-installer/.remarkrc create mode 100644 vendor/dealerdirect/phpcodesniffer-composer-installer/.yamllint create mode 100644 vendor/dealerdirect/phpcodesniffer-composer-installer/CODE_OF_CONDUCT.md create mode 100644 vendor/dealerdirect/phpcodesniffer-composer-installer/LICENSE.md create mode 100644 vendor/dealerdirect/phpcodesniffer-composer-installer/bin/generate-changelog.sh create mode 100644 vendor/dealerdirect/phpcodesniffer-composer-installer/composer.json create mode 100644 vendor/dflydev/dot-access-data/CHANGELOG.md create mode 100644 vendor/dflydev/dot-access-data/LICENSE create mode 100644 vendor/dflydev/dot-access-data/composer.json create mode 100644 vendor/jetpack-autoloader/class-autoloader-handler.php create mode 100644 vendor/jetpack-autoloader/class-autoloader-locator.php create mode 100644 vendor/jetpack-autoloader/class-autoloader.php create mode 100644 vendor/jetpack-autoloader/class-container.php create mode 100644 vendor/jetpack-autoloader/class-hook-manager.php create mode 100644 vendor/jetpack-autoloader/class-latest-autoloader-guard.php create mode 100644 vendor/jetpack-autoloader/class-manifest-reader.php create mode 100644 vendor/jetpack-autoloader/class-path-processor.php create mode 100644 vendor/jetpack-autoloader/class-php-autoloader.php create mode 100644 vendor/jetpack-autoloader/class-plugin-locator.php create mode 100644 vendor/jetpack-autoloader/class-plugins-handler.php create mode 100644 vendor/jetpack-autoloader/class-shutdown-handler.php create mode 100644 vendor/jetpack-autoloader/class-version-loader.php create mode 100644 vendor/jetpack-autoloader/class-version-selector.php create mode 100644 vendor/league/commonmark/.phpstorm.meta.php create mode 100644 vendor/league/commonmark/CHANGELOG.md create mode 100644 vendor/league/commonmark/LICENSE create mode 100644 vendor/league/commonmark/composer.json create mode 100644 vendor/league/config/CHANGELOG.md create mode 100644 vendor/league/config/LICENSE.md create mode 100644 vendor/league/config/composer.json create mode 100644 vendor/nette/schema/composer.json create mode 100644 vendor/nette/schema/contributing.md create mode 100644 vendor/nette/schema/license.md create mode 100644 vendor/nette/schema/readme.md create mode 100644 vendor/nette/utils/.phpstorm.meta.php create mode 100644 vendor/nette/utils/composer.json create mode 100644 vendor/nette/utils/license.md create mode 100644 vendor/nette/utils/readme.md create mode 100644 vendor/psr/event-dispatcher/.editorconfig create mode 100644 vendor/psr/event-dispatcher/LICENSE create mode 100644 vendor/psr/event-dispatcher/composer.json create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/LICENSE create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/Constants.php create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/EnumInfo.php create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ForLoopInfo.php create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/Helpers.php create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ScopeInfo.php create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ScopeManager.php create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ScopeType.php create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/VariableInfo.php create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/ruleset.xml create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/ruleset.xml.example create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/composer.json create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/psalm-autoloader.php create mode 100644 vendor/sirbrillig/phpcs-variable-analysis/psalm.xml create mode 100644 vendor/squizlabs/php_codesniffer/CONTRIBUTING.md create mode 100644 vendor/squizlabs/php_codesniffer/CodeSniffer.conf.dist create mode 100644 vendor/squizlabs/php_codesniffer/autoload.php create mode 100755 vendor/squizlabs/php_codesniffer/bin/phpcbf create mode 100644 vendor/squizlabs/php_codesniffer/bin/phpcbf.bat create mode 100755 vendor/squizlabs/php_codesniffer/bin/phpcs create mode 100755 vendor/squizlabs/php_codesniffer/bin/phpcs.bat create mode 100644 vendor/squizlabs/php_codesniffer/composer.json create mode 100644 vendor/squizlabs/php_codesniffer/licence.txt create mode 100644 vendor/squizlabs/php_codesniffer/phpcs.xsd create mode 100644 vendor/squizlabs/php_codesniffer/tests/AllTests.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/AbstractMethodUnitTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/AllTests.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Autoloader/DetermineLoadedClassTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Autoloader/TestFiles/A.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Autoloader/TestFiles/B.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Autoloader/TestFiles/C.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Autoloader/TestFiles/Sub/C.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/ErrorSuppressionTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/FindEndOfStatementTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/FindEndOfStatementTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/FindExtendedClassNameTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/FindExtendedClassNameTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/FindImplementedInterfaceNamesTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/FindImplementedInterfaceNamesTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/FindStartOfStatementTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/FindStartOfStatementTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/GetMemberPropertiesTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/GetMemberPropertiesTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodParametersTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodParametersTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodPropertiesTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodPropertiesTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/IsReferenceTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/File/IsReferenceTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Filters/Filter/AcceptTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Filters/Filter/AcceptTest.xml create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/IsCamelCapsTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteLinuxTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteLinuxTest.xml create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.xml create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionTest-include.xml create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionTest.xml create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Sniffs/AbstractArraySniffTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Sniffs/AbstractArraySniffTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Sniffs/AbstractArraySniffTestable.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/AnonClassParenthesisOwnerTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/AnonClassParenthesisOwnerTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ArrayKeywordTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ArrayKeywordTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/AttributesTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/AttributesTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillEnumTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillEnumTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillExplicitOctalNotationTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillExplicitOctalNotationTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillFnTokenTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillFnTokenTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillMatchTokenTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillMatchTokenTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillNumericSeparatorTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillNumericSeparatorTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillReadonlyTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillReadonlyTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BitwiseOrTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BitwiseOrTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DefaultKeywordTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DefaultKeywordTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleArrowTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleArrowTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleQuotedStringTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleQuotedStringTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/EnumCaseTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/EnumCaseTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/FinallyTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/FinallyTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/GotoLabelTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/GotoLabelTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/HeredocNowdocCloserTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/HeredocNowdocCloserTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/HeredocStringTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/HeredocStringTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NullsafeObjectOperatorTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NullsafeObjectOperatorTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ScopeSettingWithNamespaceOperatorTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ScopeSettingWithNamespaceOperatorTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ShortArrayTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ShortArrayTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/StableCommentWhitespaceTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/StableCommentWhitespaceTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/StableCommentWhitespaceWinTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/StableCommentWhitespaceWinTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/TypeIntersectionTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/TypeIntersectionTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/UndoNamespacedNameSingleTokenTest.inc create mode 100644 vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/UndoNamespacedNameSingleTokenTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/FileList.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Standards/AbstractSniffUnitTest.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/Standards/AllSniffs.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/TestSuite.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/TestSuite7.php create mode 100644 vendor/squizlabs/php_codesniffer/tests/bootstrap.php create mode 100644 vendor/symfony/deprecation-contracts/CHANGELOG.md create mode 100644 vendor/symfony/deprecation-contracts/LICENSE create mode 100644 vendor/symfony/deprecation-contracts/composer.json create mode 100644 vendor/symfony/deprecation-contracts/function.php create mode 100644 vendor/symfony/polyfill-php80/LICENSE create mode 100644 vendor/symfony/polyfill-php80/Php80.php create mode 100644 vendor/symfony/polyfill-php80/PhpToken.php create mode 100644 vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php create mode 100644 vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php create mode 100644 vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php create mode 100644 vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php create mode 100644 vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php create mode 100644 vendor/symfony/polyfill-php80/bootstrap.php create mode 100644 vendor/symfony/polyfill-php80/composer.json create mode 100644 vendor/wp-coding-standards/wpcs/.gitattributes create mode 100644 vendor/wp-coding-standards/wpcs/CHANGELOG.md create mode 100644 vendor/wp-coding-standards/wpcs/LICENSE create mode 100644 vendor/wp-coding-standards/wpcs/WordPress-Core/ruleset.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress-Docs/ruleset.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress-Extra/ruleset.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/AbstractArrayAssignmentRestrictionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/AbstractClassRestrictionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/AbstractFunctionParameterSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/AbstractFunctionRestrictionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/Arrays/ArrayIndentationStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/Arrays/ArrayKeySpacingRestrictionsStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/Arrays/MultipleStatementAlignmentStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/Classes/ClassInstantiationStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/CodeAnalysis/EscapedNotTranslatedStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/DateTime/CurrentTimeTimestampStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/NamingConventions/ValidHookNameStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/NamingConventions/ValidPostTypeSlugStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/PHP/DisallowShortTernaryStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/PHP/IniSetStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/Security/SafeRedirectStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/CronIntervalStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedClassesStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedFunctionsStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedParameterValuesStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedParametersStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/EnqueuedResourcesStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/PostsPerPageStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/WhiteSpace/CastStructureSpacingStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/WhiteSpace/DisallowInlineTabsStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Docs/WhiteSpace/PrecisionAlignmentStandard.xml create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/PHPCSHelper.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/ArrayDeclarationSpacingSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/ArrayIndentationSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/ArrayKeySpacingRestrictionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/CommaAfterArrayItemSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/MultipleStatementAlignmentSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Classes/ClassInstantiationSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/CodeAnalysis/AssignmentInConditionSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/CodeAnalysis/EmptyStatementSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/CodeAnalysis/EscapedNotTranslatedSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/DirectDatabaseQuerySniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/PreparedSQLPlaceholdersSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/PreparedSQLSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/RestrictedClassesSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/RestrictedFunctionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/SlowDBQuerySniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DateTime/CurrentTimeTimestampSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DateTime/RestrictedFunctionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Files/FileNameSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidFunctionNameSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidHookNameSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidPostTypeSlugSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidVariableNameSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DevelopmentFunctionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DisallowShortTernarySniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DiscouragedPHPFunctionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DontExtractSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/IniSetSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/NoSilencedErrorsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/POSIXFunctionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/PregQuoteDelimiterSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/RestrictedPHPFunctionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/StrictComparisonsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/StrictInArraySniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/TypeCastsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/YodaConditionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/EscapeOutputSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/NonceVerificationSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/PluginMenuSlugSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/SafeRedirectSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/ValidatedSanitizedInputSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Utils/I18nTextDomainFixerSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/AlternativeFunctionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/CapitalPDangitSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/CronIntervalSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedClassesSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedParameterValuesSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedParametersSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DiscouragedConstantsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DiscouragedFunctionsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/EnqueuedResourceParametersSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/EnqueuedResourcesSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/GlobalVariablesOverrideSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/I18nSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/PostsPerPageSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/TimezoneChangeSniff.php create mode 100755 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/CastStructureSpacingSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/ControlStructureSpacingSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/DisallowInlineTabsSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/OperatorSpacingSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/PrecisionAlignmentSniff.php create mode 100644 vendor/wp-coding-standards/wpcs/WordPress/ruleset.xml create mode 100644 vendor/wp-coding-standards/wpcs/composer.json create mode 100644 vendor/wp-coding-standards/wpcs/phpcs.xml.dist.sample delete mode 100644 webpack.config.js diff --git a/.deployignore b/.deployignore deleted file mode 100644 index 2bc005cd..00000000 --- a/.deployignore +++ /dev/null @@ -1,16 +0,0 @@ -# npm -node_modules -package-lock.json -package.json - -# composer -composer.lock - -# build tools -src -.phpcs.xml.dist -webpack.config.js - -# other -.gitignore -README.md diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 87a0d1b5..00000000 --- a/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules/ -vendor/ -dist/ - -.DS_Store -inc/asset-settings.php diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist deleted file mode 100644 index 34dd473a..00000000 --- a/.phpcs.xml.dist +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/README.md b/README.md deleted file mode 100644 index 57840184..00000000 --- a/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# release-notes - - - -## Installing -When installing to your site, add the following to you `composer.json` file. This will ensure that installation will use the build version of the package and allow it to be loaded using composer in the preferred path. -```json -{ - "repositories": [ - { - "type": "vcs", - "url": "git@github.com:bigbite/release-notes.git" - } - ], - "require": { - "big-bite/release-notes": "1.0.0-rc.5" - }, - "extra": { - "installer-paths": { - "plugins/{$name}/": [ - "type:wordpress-plugin" - ] - } - } -} - -``` - -## Local Development or Manual Install -Clone the repository into your `plugins` or `client-mu-plugins` directory. -``` -git clone git@github.com:bigbite/release-notes.git && cd release-notes -``` - -Install JS packages. -``` -npm install -``` - -Build all assets - additional commands can be found on the [`bigbite/build-tools` repo.](https://github.com/bigbite/build-tools#commands) -``` -npm run build:dev -``` - -Install PHP packages and create autoloader for the plugin. -``` -composer update -``` diff --git a/dist/scripts/dashboard-31d6cfe0.js b/dist/scripts/dashboard-31d6cfe0.js new file mode 100644 index 00000000..e69de29b diff --git a/dist/scripts/editor-071c76ff.js b/dist/scripts/editor-071c76ff.js new file mode 100644 index 00000000..ef37c748 --- /dev/null +++ b/dist/scripts/editor-071c76ff.js @@ -0,0 +1,2 @@ +(()=>{var e={102:(e,t,r)=>{"use strict";var n=r(135);function a(){}function o(){}o.resetWarningCache=a,e.exports=function(){function e(e,t,r,a,o,l){if(l!==n){var c=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw c.name="Invariant Violation",c}}function t(){return e}e.isRequired=e;var r={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:a};return r.PropTypes=r,r}},398:(e,t,r)=>{e.exports=r(102)()},135:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"}},t={};function r(n){var a=t[n];if(void 0!==a)return a.exports;var o=t[n]={exports:{}};return e[n](o,o.exports,r),o.exports}r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";const{PluginDocumentSettingPanel:e}=wp.editPost,{PanelRow:t,DatePicker:n,ToggleControl:a,__experimentalInputControl:o,__experimentalText:l,__experimentalVStack:c}=wp.components,{useEntityProp:s}=wp.coreData,{useSelect:i}=wp.data,{__:p}=wp.i18n,{registerPlugin:u}=wp.plugins;u("release-notes-meta",{icon:null,render:function(){const r=i((e=>e("core/editor").getCurrentPostType()),[]);if("release-note"!==r)return null;const[u,m]=s("postType",r,"meta"),{version:d,is_pre_release:_,release_date:b}=u;return React.createElement(React.Fragment,null,React.createElement(e,{initialOpen:!0,name:"release-notes",title:"Release Info"},React.createElement(t,null,React.createElement(o,{value:d,onChange:e=>m({...u,version:e}),label:p("Version Number")})),React.createElement("br",null),React.createElement(t,null,React.createElement(c,null,React.createElement(l,{variant:"label"},p("Release Date")),React.createElement(n,{currentDate:b,onChange:e=>(e=>{m({...u,release_date:e})})(new Date(e).toISOString().split("T")[0])}))),React.createElement("br",null),React.createElement(t,null,React.createElement(a,{label:p("Pre-Release Toggle"),checked:_,onChange:e=>m({...u,is_pre_release:e})}))))}});var m=r(398),d=r.n(m);const{useEffect:_}=wp.element,b=e=>{let{attributes:t,clientId:r}=e;return _((()=>{const e=wp.blocks.rawHandler({HTML:t.html});wp.data.dispatch("core/block-editor").insertBlocks(e),wp.data.dispatch("core/block-editor").removeBlock(r)}),[t]),null};b.propTypes={attributes:d().object.isRequired,clientId:d().string.isRequired};const g=b,{registerBlockType:R}=wp.blocks;R("release-notes/markdown-parser",{edit:g,save:()=>null})})()})(); +//# sourceMappingURL=editor-071c76ff.js.map \ No newline at end of file diff --git a/dist/scripts/editor-071c76ff.js.map b/dist/scripts/editor-071c76ff.js.map new file mode 100644 index 00000000..0517ec03 --- /dev/null +++ b/dist/scripts/editor-071c76ff.js.map @@ -0,0 +1 @@ +{"version":3,"file":"editor-071c76ff.js","mappings":"wCASA,IAAIA,EAAuBC,EAAQ,KAEnC,SAASC,IAAiB,CAC1B,SAASC,IAA0B,CACnCA,EAAuBC,kBAAoBF,EAE3CG,EAAOC,QAAU,WACf,SAASC,EAAKC,EAAOC,EAAUC,EAAeC,EAAUC,EAAcC,GACpE,GAAIA,IAAWb,EAAf,CAIA,IAAIc,EAAM,IAAIC,MACZ,mLAKF,MADAD,EAAIE,KAAO,sBACLF,CAPN,CAQF,CAEA,SAASG,IACP,OAAOV,CACT,CAHAA,EAAKW,WAAaX,EAMlB,IAAIY,EAAiB,CACnBC,MAAOb,EACPc,OAAQd,EACRe,KAAMf,EACNgB,KAAMhB,EACNiB,OAAQjB,EACRkB,OAAQlB,EACRmB,OAAQnB,EACRoB,OAAQpB,EAERqB,IAAKrB,EACLsB,QAASZ,EACTa,QAASvB,EACTwB,YAAaxB,EACbyB,WAAYf,EACZgB,KAAM1B,EACN2B,SAAUjB,EACVkB,MAAOlB,EACPmB,UAAWnB,EACXoB,MAAOpB,EACPqB,MAAOrB,EAEPsB,eAAgBpC,EAChBC,kBAAmBF,GAKrB,OAFAiB,EAAeqB,UAAYrB,EAEpBA,CACT,C,gBC/CEd,EAAOC,QAAUL,EAAQ,IAARA,E,uBCNnBI,EAAOC,QAFoB,8C,GCRvBmC,EAA2B,CAAC,EAGhC,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBE,IAAjBD,EACH,OAAOA,EAAatC,QAGrB,IAAID,EAASoC,EAAyBE,GAAY,CAGjDrC,QAAS,CAAC,GAOX,OAHAwC,EAAoBH,GAAUtC,EAAQA,EAAOC,QAASoC,GAG/CrC,EAAOC,OACf,CCrBAoC,EAAoBK,EAAK1C,IACxB,IAAI2C,EAAS3C,GAAUA,EAAO4C,WAC7B,IAAO5C,EAAiB,QACxB,IAAM,EAEP,OADAqC,EAAoBQ,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,CAAM,ECLdN,EAAoBQ,EAAI,CAAC5C,EAAS8C,KACjC,IAAI,IAAIC,KAAOD,EACXV,EAAoBY,EAAEF,EAAYC,KAASX,EAAoBY,EAAEhD,EAAS+C,IAC5EE,OAAOC,eAAelD,EAAS+C,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,IAE1E,ECNDX,EAAoBY,EAAI,CAACK,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,G,mBCAlF,MAAM,2BAAEI,GAA+BC,GAAGC,UACpC,SACJC,EAAQ,WACRC,EAAU,cACVC,EACAC,2BAA4BC,EAC5BC,mBAAoBC,EACpBC,qBAAsBC,GACpBV,GAAGW,YACD,cAAEC,GAAkBZ,GAAGa,UACvB,UAAEC,GAAcd,GAAGe,MACnB,GAAEC,GAAOhB,GAAGiB,MCTZ,eAAEC,GAAmBlB,GAAGmB,QAE9BD,EAAe,qBAAsB,CACnCE,KAAM,KACNC,ODQF,WACE,MAAMC,EAAWR,GAAWS,GAAWA,EAAO,eAAeC,sBAAsB,IACnF,GAAiB,iBAAbF,EAA6B,OAAO,KAExC,MAAOG,EAAMC,GAAWd,EAAc,WAAYU,EAAU,SACtD,QAAEK,EAASC,eAAgBC,EAAcC,aAAcC,GAAgBN,EAM7E,OACE,wCACE,oBAAC1B,EAA0B,CAACiC,aAAW,EAACjF,KAAK,gBAAgBkF,MAAM,gBACjE,oBAAC/B,EAAQ,KACP,oBAACI,EAAY,CACX4B,MAAOP,EACPQ,SAAWC,GAAQV,EAAQ,IAAKD,EAAME,QAASS,IAC/CC,MAAOrB,EAAG,qBAGd,+BACA,oBAACd,EAAQ,KACP,oBAACQ,EAAM,KACL,oBAACF,EAAI,CAAC8B,QAAQ,SAAStB,EAAG,iBAC1B,oBAACb,EAAU,CACToC,YAAaR,EACbI,SAAWC,GApBDA,KACpBV,EAAQ,IAAKD,EAAMK,aAAcM,GAAM,EAmBVI,CAAa,IAAIC,KAAKL,GAAKM,cAAcC,MAAM,KAAK,QAI7E,+BACA,oBAACzC,EAAQ,KACP,oBAACE,EAAa,CACZiC,MAAOrB,EAAG,sBACV4B,QAASf,EACTM,SAAWC,GAAQV,EAAQ,IAAKD,EAAMG,eAAgBQ,QAMlE,I,sBEtDA,MAAM,UAAES,GAAc7C,GAAGnC,QAEnBiF,EAAO,IAA8B,IAA7B,WAAEC,EAAU,SAAEC,GAAU,EAOpC,OANAH,GAAU,KACR,MAAMI,EAASjD,GAAGiD,OAAOC,WAAW,CAAEC,KAAMJ,EAAWK,OACvDpD,GAAGe,KAAKsC,SAAS,qBAAqBC,aAAaL,GACnDjD,GAAGe,KAAKsC,SAAS,qBAAqBE,YAAYP,EAAS,GAC1D,CAACD,IAEG,IAAI,EAGbD,EAAKU,UAAY,CACfT,WAAYxE,IAAAA,OAAAA,WACZyE,SAAUzE,IAAAA,OAAAA,YAGZ,WCjBM,kBAAEkF,GAAsBzD,GAAGiD,OAEjCQ,EAAkB,gCAAiC,CACjDC,KAAMZ,EACNa,KAAM,IAAM,M","sources":["webpack://@big-bite/release-notes/./node_modules/prop-types/factoryWithThrowingShims.js","webpack://@big-bite/release-notes/./node_modules/prop-types/index.js","webpack://@big-bite/release-notes/./node_modules/prop-types/lib/ReactPropTypesSecret.js","webpack://@big-bite/release-notes/webpack/bootstrap","webpack://@big-bite/release-notes/webpack/runtime/compat get default export","webpack://@big-bite/release-notes/webpack/runtime/define property getters","webpack://@big-bite/release-notes/webpack/runtime/hasOwnProperty shorthand","webpack://@big-bite/release-notes/./src/editor/register-plugins/CustomBlockFields.js","webpack://@big-bite/release-notes/./src/editor/register-plugins/index.js","webpack://@big-bite/release-notes/./src/editor/blocks/markdown-parser/edit.js","webpack://@big-bite/release-notes/./src/editor/blocks/markdown-parser/index.js"],"sourcesContent":["/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n'use strict';\n\nvar ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');\n\nfunction emptyFunction() {}\nfunction emptyFunctionWithReset() {}\nemptyFunctionWithReset.resetWarningCache = emptyFunction;\n\nmodule.exports = function() {\n function shim(props, propName, componentName, location, propFullName, secret) {\n if (secret === ReactPropTypesSecret) {\n // It is still safe when called from React.\n return;\n }\n var err = new Error(\n 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' +\n 'Use PropTypes.checkPropTypes() to call them. ' +\n 'Read more at http://fb.me/use-check-prop-types'\n );\n err.name = 'Invariant Violation';\n throw err;\n };\n shim.isRequired = shim;\n function getShim() {\n return shim;\n };\n // Important!\n // Keep this list in sync with production version in `./factoryWithTypeCheckers.js`.\n var ReactPropTypes = {\n array: shim,\n bigint: shim,\n bool: shim,\n func: shim,\n number: shim,\n object: shim,\n string: shim,\n symbol: shim,\n\n any: shim,\n arrayOf: getShim,\n element: shim,\n elementType: shim,\n instanceOf: getShim,\n node: shim,\n objectOf: getShim,\n oneOf: getShim,\n oneOfType: getShim,\n shape: getShim,\n exact: getShim,\n\n checkPropTypes: emptyFunctionWithReset,\n resetWarningCache: emptyFunction\n };\n\n ReactPropTypes.PropTypes = ReactPropTypes;\n\n return ReactPropTypes;\n};\n","/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nif (process.env.NODE_ENV !== 'production') {\n var ReactIs = require('react-is');\n\n // By explicitly using `prop-types` you are opting into new development behavior.\n // http://fb.me/prop-types-in-prod\n var throwOnDirectAccess = true;\n module.exports = require('./factoryWithTypeCheckers')(ReactIs.isElement, throwOnDirectAccess);\n} else {\n // By explicitly using `prop-types` you are opting into new production behavior.\n // http://fb.me/prop-types-in-prod\n module.exports = require('./factoryWithThrowingShims')();\n}\n","/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n'use strict';\n\nvar ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';\n\nmodule.exports = ReactPropTypesSecret;\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","const { PluginDocumentSettingPanel } = wp.editPost;\nconst {\n PanelRow,\n DatePicker,\n ToggleControl,\n __experimentalInputControl: InputControl,\n __experimentalText: Text,\n __experimentalVStack: VStack,\n} = wp.components;\nconst { useEntityProp } = wp.coreData;\nconst { useSelect } = wp.data;\nconst { __ } = wp.i18n;\n\n/** Adds the date selector to the post settings */\nfunction CustomBlockFields() {\n const postType = useSelect((select) => select('core/editor').getCurrentPostType(), []);\n if (postType !== 'release-note') return null;\n\n const [meta, setMeta] = useEntityProp('postType', postType, 'meta');\n const { version, is_pre_release: isPrerelease, release_date: releaseDate } = meta;\n\n const onDateChange = (val) => {\n setMeta({ ...meta, release_date: val });\n };\n\n return (\n <>\n \n \n setMeta({ ...meta, version: val })}\n label={__('Version Number')}\n />\n \n
\n \n \n {__('Release Date')}\n onDateChange(new Date(val).toISOString().split('T')[0])}\n />\n \n \n
\n \n setMeta({ ...meta, is_pre_release: val })}\n />\n \n
\n \n );\n}\n\nexport default CustomBlockFields;\n","import CustomBlockFields from './CustomBlockFields';\n\nconst { registerPlugin } = wp.plugins;\n\nregisterPlugin('release-notes-meta', {\n icon: null,\n render: CustomBlockFields,\n});\n","import PropTypes from 'prop-types';\n\nconst { useEffect } = wp.element;\n\nconst Edit = ({ attributes, clientId }) => {\n useEffect(() => {\n const blocks = wp.blocks.rawHandler({ HTML: attributes.html });\n wp.data.dispatch('core/block-editor').insertBlocks(blocks);\n wp.data.dispatch('core/block-editor').removeBlock(clientId);\n }, [attributes]);\n\n return null;\n};\n\nEdit.propTypes = {\n attributes: PropTypes.object.isRequired,\n clientId: PropTypes.string.isRequired,\n};\n\nexport default Edit;\n","import Edit from './edit';\n\nconst { registerBlockType } = wp.blocks;\n\nregisterBlockType('release-notes/markdown-parser', {\n edit: Edit,\n save: () => null,\n});\n"],"names":["ReactPropTypesSecret","require","emptyFunction","emptyFunctionWithReset","resetWarningCache","module","exports","shim","props","propName","componentName","location","propFullName","secret","err","Error","name","getShim","isRequired","ReactPropTypes","array","bigint","bool","func","number","object","string","symbol","any","arrayOf","element","elementType","instanceOf","node","objectOf","oneOf","oneOfType","shape","exact","checkPropTypes","PropTypes","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","n","getter","__esModule","d","a","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","PluginDocumentSettingPanel","wp","editPost","PanelRow","DatePicker","ToggleControl","__experimentalInputControl","InputControl","__experimentalText","Text","__experimentalVStack","VStack","components","useEntityProp","coreData","useSelect","data","__","i18n","registerPlugin","plugins","icon","render","postType","select","getCurrentPostType","meta","setMeta","version","is_pre_release","isPrerelease","release_date","releaseDate","initialOpen","title","value","onChange","val","label","variant","currentDate","onDateChange","Date","toISOString","split","checked","useEffect","Edit","attributes","clientId","blocks","rawHandler","HTML","html","dispatch","insertBlocks","removeBlock","propTypes","registerBlockType","edit","save"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/styles/dashboard-2d07d2f6.css b/dist/styles/dashboard-2d07d2f6.css new file mode 100644 index 00000000..baaaec60 --- /dev/null +++ b/dist/styles/dashboard-2d07d2f6.css @@ -0,0 +1,3 @@ +.release-note-single{background-color:#fff;border-radius:2px;margin:10px auto 24px;padding:10px 20px 23px;width:540px}.release-notes-pagination{border:1px solid #0073aa;display:block;font-size:.875rem;font-weight:700;margin:20px auto;padding:12px 20px;text-align:center;text-decoration:none;text-transform:uppercase;transition:all .2s ease-in-out;width:540px}.release-notes-pagination:focus,.release-notes-pagination:hover{background:#0073aa;color:#fff}.release-note-header{align-items:center;border-bottom:1px solid #e8eaeb;display:grid;grid-template-columns:1fr 1fr 1fr;margin-bottom:8px;padding-bottom:6px}.is-empty .release-note-header{border-bottom:none;margin-bottom:0;padding-bottom:0}.release-note-header .release-note-meta-wrapper:first-of-type{display:flex;justify-content:center}.release-note-header .release-note-meta-wrapper:last-of-type{display:flex;justify-content:flex-end}.release-note-single .release-note-header h1:before{background-color:#3fb950;border-radius:50%;content:"";display:inline-block;height:6px;margin-right:6px;vertical-align:middle;width:6px}.release-note-single.is-pre-release .release-note-header h1:before{background-color:#f56e28;border-radius:50%;content:"";display:inline-block;height:6px;margin-right:6px;vertical-align:middle;width:6px}h1.release-note-title{font-size:1.25rem}.release-note-meta:not(.empty){background-color:#e8eaeb;border-radius:4px;display:inline-block;font-family:Courier New,Courier,monospace;font-size:1.125rem;height:-moz-fit-content;height:fit-content;padding:2px 4px;width:-moz-fit-content;width:fit-content}.release-note-body *{max-width:100%}.release-note-body .release-notes-list h2{font-size:1rem}.release-note-body .release-notes-list ol,.release-note-body .release-notes-list ul{list-style:inside;margin-bottom:0;margin-top:0}.release-note-footer{border-top:1px solid #e8eaeb;margin-top:12px;padding-top:8px}.is-empty .release-note-footer{border-top:none;margin-top:0;padding-top:0}li.release-note>a{background:#3fb950!important;transition:background .2s ease-in-out!important}li.release-note>a:focus,li.release-note>a:hover{background:#238636!important;color:#fff!important}li.release-note.is-pre-release>a{background:#f56e28!important;transition:background .2s ease-in-out!important}li.release-note.is-pre-release>a:focus,li.release-note.is-pre-release>a:hover{background:#ca4a1f!important;color:#fff!important} + +/*# sourceMappingURL=dashboard-2d07d2f6.css.map*/ \ No newline at end of file diff --git a/dist/styles/dashboard-2d07d2f6.css.map b/dist/styles/dashboard-2d07d2f6.css.map new file mode 100644 index 00000000..de3f7037 --- /dev/null +++ b/dist/styles/dashboard-2d07d2f6.css.map @@ -0,0 +1 @@ +{"version":3,"file":"../styles/dashboard-2d07d2f6.css","mappings":"AAAA,qBAGE,sBAEA,kBAHA,sBAEA,uBAHA,WAIA,CAGF,0BAME,yBAJA,cAGA,kBAKA,gBADA,iBANA,kBAIA,kBACA,qBAJA,yBAQA,+BAXA,WAWA,CAEA,gEACE,mBACA,WAIJ,qBAGE,mBACA,gCAHA,aACA,kCAIA,kBADA,kBACA,CAEA,+BAGE,mBADA,gBADA,gBAEA,CAGF,8DACE,aACA,uBAGF,6DACE,aACA,yBAIJ,oDAKE,yBACA,kBALA,WACA,qBAEA,WAIA,iBADA,sBAJA,SAKA,CAGF,mEAKE,yBACA,kBALA,WACA,qBAEA,WAIA,iBADA,sBAJA,SAKA,CAGF,sBACE,kBAGF,+BAEE,yBAGA,kBAJA,qBAKA,0CAHA,mBAKA,2CAJA,gBAGA,wCACA,CAGF,qBACE,eAIA,0CACE,eAGF,oFACE,kBAEA,gBADA,YACA,CAIJ,qBACE,6BACA,gBACA,gBAEA,+BACE,gBAEA,aADA,aACA,CAIJ,kBACE,6BACA,gDAEA,gDAEE,6BADA,oBACA,CAIJ,iCACE,6BACA,gDAEA,8EAEE,6BADA,oBACA,C","sources":["webpack://@big-bite/release-notes/./src/dashboard/styles/styles.scss"],"sourcesContent":[".release-note-single {\n width: 540px;\n margin: 10px auto 24px;\n background-color: #fff;\n padding: 10px 20px 23px;\n border-radius: 2px;\n}\n\n.release-notes-pagination {\n width: 540px;\n display: block;\n padding: 12px 20px;\n text-transform: uppercase;\n font-size: 14px;\n border: 1px solid #0073aa;\n text-align: center;\n text-decoration: none;\n margin: 20px auto;\n font-weight: bold;\n\n transition: all 0.2s ease-in-out;\n\n &:hover, &:focus {\n background: #0073aa;\n color: #fff;\n }\n}\n\n.release-note-header {\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n align-items: center;\n border-bottom: 1px solid #e8eaeb;\n padding-bottom: 6px;\n margin-bottom: 8px;\n\n .is-empty & {\n padding-bottom: 0;\n margin-bottom: 0;\n border-bottom: none;\n }\n\n .release-note-meta-wrapper:first-of-type {\n display: flex;\n justify-content: center;\n }\n\n .release-note-meta-wrapper:last-of-type {\n display: flex;\n justify-content: flex-end;\n }\n}\n\n.release-note-single .release-note-header h1::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n background-color: #3fb950;\n border-radius: 50%;\n vertical-align: middle;\n margin-right: 6px;\n}\n\n.release-note-single.is-pre-release .release-note-header h1::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n background-color: #f56e28;\n border-radius: 50%;\n vertical-align: middle;\n margin-right: 6px;\n}\n\nh1.release-note-title {\n font-size: 20px;\n}\n\n.release-note-meta:not(.empty) {\n display: inline-block;\n background-color: #e8eaeb;\n font-size: 18px;\n padding: 2px 4px;\n border-radius: 4px;\n font-family: 'Courier New', Courier, monospace;\n width: fit-content;\n height: fit-content;\n}\n\n.release-note-body * {\n max-width: 100%;\n}\n\n.release-note-body .release-notes-list {\n h2 {\n font-size: 16px;\n }\n\n ul, ol {\n list-style: inside;\n margin-top: 0;\n margin-bottom: 0;\n }\n}\n\n.release-note-footer {\n border-top: 1px solid #e8eaeb;\n margin-top: 12px;\n padding-top: 8px;\n\n .is-empty & {\n border-top: none;\n padding-top: 0;\n margin-top: 0;\n }\n}\n\nli.release-note > a {\n background: #3fb950 !important;\n transition: background 0.2s ease-in-out !important;\n\n &:hover, &:focus {\n color: #fff !important;\n background: #238636 !important;\n }\n}\n\nli.release-note.is-pre-release > a {\n background: #f56e28 !important;\n transition: background 0.2s ease-in-out !important;\n\n &:hover, &:focus {\n color: #fff !important;\n background: #ca4a1f !important;\n }\n}\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/inc/asset-settings.php b/inc/asset-settings.php new file mode 100644 index 00000000..6dc7a236 --- /dev/null +++ b/inc/asset-settings.php @@ -0,0 +1,13 @@ +=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - } - }, - "sass-graph": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", - "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", - "requires": { - "glob": "^7.0.0", - "lodash": "^4.0.0", - "scss-tokenizer": "^0.2.3", - "yargs": "^13.3.2" - } - }, - "sass-loader": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", - "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", - "requires": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - } - }, - "scss-tokenizer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", - "integrity": "sha512-dYE8LhncfBUar6POCxMTm0Ln+erjeczqEvCJib5/7XNkdw1FkUGgwMPY360FY0FgPWQxHWCx29Jl3oejyGLM9Q==", - "requires": { - "js-base64": "^2.1.8", - "source-map": "^0.4.2" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha512-Y8nIfcb1s/7DcobUz1yOO1GSp7gyL+D9zLHDehT7iRESqGSxjJ448Sg7rvfgsRJCnKLdSl11uGf0s9X80cH0/A==", - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "requires": { - "randombytes": "^2.1.0" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" - }, - "seventh": { - "version": "0.7.40", - "resolved": "https://registry.npmjs.org/seventh/-/seventh-0.7.40.tgz", - "integrity": "sha512-7sxUydQx4iEh17uJUFjZDAwbffJirldZaNIJvVB/hk9mPEL3J4GpLGSL+mHFH2ydkye46DAsLGqzFJ+/Qj5foQ==", - "requires": { - "setimmediate": "^1.0.5" - } - }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "requires": { - "kind-of": "^6.0.2" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - } - } - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==" - }, - "specificity": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", - "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==" - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "requires": { - "extend-shallow": "^3.0.0" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "stdout-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", - "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", - "requires": { - "readable-stream": "^2.0.1" - } - }, - "stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "requires": { - "internal-slot": "^1.0.4" - } - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==" - }, - "string-kit": { - "version": "0.17.8", - "resolved": "https://registry.npmjs.org/string-kit/-/string-kit-0.17.8.tgz", - "integrity": "sha512-pkJoGTq6y/xMwvV9A9Ets/sRgYEQ/nxhbHsm0z/8pAB6pqkC2JF+Od9r1o8CPRkai12r0XjRsCwUiZaSnEdjdA==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "string.prototype.matchall": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", - "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4" - } - }, - "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "requires": { - "min-indent": "^1.0.0" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" - }, - "style-search": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", - "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==" - }, - "stylehacks": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", - "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", - "requires": { - "browserslist": "^4.21.4", - "postcss-selector-parser": "^6.0.4" - } - }, - "stylelint": { - "version": "13.13.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.13.1.tgz", - "integrity": "sha512-Mv+BQr5XTUrKqAXmpqm6Ddli6Ief+AiPZkRsIrAoUKFuq/ElkUh9ZMYxXD0iQNZ5ADghZKLOWz1h7hTClB7zgQ==", - "requires": { - "@stylelint/postcss-css-in-js": "^0.37.2", - "@stylelint/postcss-markdown": "^0.36.2", - "autoprefixer": "^9.8.6", - "balanced-match": "^2.0.0", - "chalk": "^4.1.1", - "cosmiconfig": "^7.0.0", - "debug": "^4.3.1", - "execall": "^2.0.0", - "fast-glob": "^3.2.5", - "fastest-levenshtein": "^1.0.12", - "file-entry-cache": "^6.0.1", - "get-stdin": "^8.0.0", - "global-modules": "^2.0.0", - "globby": "^11.0.3", - "globjoin": "^0.1.4", - "html-tags": "^3.1.0", - "ignore": "^5.1.8", - "import-lazy": "^4.0.0", - "imurmurhash": "^0.1.4", - "known-css-properties": "^0.21.0", - "lodash": "^4.17.21", - "log-symbols": "^4.1.0", - "mathml-tag-names": "^2.1.3", - "meow": "^9.0.0", - "micromatch": "^4.0.4", - "normalize-selector": "^0.2.0", - "postcss": "^7.0.35", - "postcss-html": "^0.36.0", - "postcss-less": "^3.1.4", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-safe-parser": "^4.0.2", - "postcss-sass": "^0.4.4", - "postcss-scss": "^2.1.1", - "postcss-selector-parser": "^6.0.5", - "postcss-syntax": "^0.36.2", - "postcss-value-parser": "^4.1.0", - "resolve-from": "^5.0.0", - "slash": "^3.0.0", - "specificity": "^0.4.1", - "string-width": "^4.2.2", - "strip-ansi": "^6.0.0", - "style-search": "^0.1.0", - "sugarss": "^2.0.0", - "svg-tags": "^1.0.0", - "table": "^6.6.0", - "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^3.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" - }, - "autoprefixer": { - "version": "9.8.8", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", - "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", - "requires": { - "browserslist": "^4.12.0", - "caniuse-lite": "^1.0.30001109", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "picocolors": "^0.2.1", - "postcss": "^7.0.32", - "postcss-value-parser": "^4.1.0" - } - }, - "balanced-match": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", - "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==" - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==" - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, - "postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "requires": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "stylelint-scss": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.21.0.tgz", - "integrity": "sha512-CMI2wSHL+XVlNExpauy/+DbUcB/oUZLARDtMIXkpV/5yd8nthzylYd1cdHeDMJVBXeYHldsnebUX6MoV5zPW4A==", - "requires": { - "lodash": "^4.17.15", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - } - }, - "stylelint-webpack-plugin": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stylelint-webpack-plugin/-/stylelint-webpack-plugin-3.3.0.tgz", - "integrity": "sha512-F53bapIZ9zI16ero8IWm6TrUE6SSibZBphJE9b5rR2FxtvmGmm1YmS+a5xjQzn63+cv71GVSCu4byX66fBLpEw==", - "requires": { - "globby": "^11.1.0", - "jest-worker": "^28.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } - } - }, - "sugarss": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz", - "integrity": "sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==", - "requires": { - "postcss": "^7.0.2" - }, - "dependencies": { - "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, - "postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "requires": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - } - } - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "svg-baker": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/svg-baker/-/svg-baker-1.7.0.tgz", - "integrity": "sha512-nibslMbkXOIkqKVrfcncwha45f97fGuAOn1G99YwnwTj8kF9YiM6XexPcUso97NxOm6GsP0SIvYVIosBis1xLg==", - "requires": { - "bluebird": "^3.5.0", - "clone": "^2.1.1", - "he": "^1.1.1", - "image-size": "^0.5.1", - "loader-utils": "^1.1.0", - "merge-options": "1.0.1", - "micromatch": "3.1.0", - "postcss": "^5.2.17", - "postcss-prefix-selector": "^1.6.0", - "posthtml-rename-id": "^1.0", - "posthtml-svg-mode": "^1.0.3", - "query-string": "^4.3.2", - "traverse": "^0.6.6" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==" - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==" - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==" - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "requires": { - "minimist": "^1.2.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - }, - "loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "micromatch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.0.tgz", - "integrity": "sha512-3StSelAE+hnRvMs8IdVW7Uhk8CVed5tp+kLLGlBP6WiRAXS21GPGu/Nat4WNPXj2Eoc24B02SaeoyozPMfj0/g==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.2.2", - "define-property": "^1.0.0", - "extend-shallow": "^2.0.1", - "extglob": "^2.0.2", - "fragment-cache": "^0.2.1", - "kind-of": "^5.0.2", - "nanomatch": "^1.2.1", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", - "requires": { - "has-flag": "^1.0.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "svg-baker-runtime": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/svg-baker-runtime/-/svg-baker-runtime-1.4.7.tgz", - "integrity": "sha512-Zorfwwj5+lWjk/oxwSMsRdS2sPQQdTmmsvaSpzU+i9ZWi3zugHLt6VckWfnswphQP0LmOel3nggpF5nETbt6xw==", - "requires": { - "deepmerge": "1.3.2", - "mitt": "1.1.2", - "svg-baker": "^1.7.0" - }, - "dependencies": { - "deepmerge": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.3.2.tgz", - "integrity": "sha512-qjMjTrk+RKv/sp4RPDpV5CnKhxjFI9p+GkLBOls5A8EEElldYWCWA9zceAkmfd0xIo2aU1nxiaLFoiya2sb6Cg==" - } - } - }, - "svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" - }, - "svg-sprite-loader": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/svg-sprite-loader/-/svg-sprite-loader-6.0.11.tgz", - "integrity": "sha512-TedsTf8wsHH6HgdwKjUveDZRC6q5gPloYV8A8/zZaRWP929J7x6TzQ6MvZFl+YYDJuJ0Akyuu/vNVJ+fbPuYXg==", - "requires": { - "bluebird": "^3.5.0", - "deepmerge": "1.3.2", - "domready": "1.0.8", - "escape-string-regexp": "1.0.5", - "loader-utils": "^1.1.0", - "svg-baker": "^1.5.0", - "svg-baker-runtime": "^1.4.7", - "url-slug": "2.0.0" - }, - "dependencies": { - "deepmerge": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.3.2.tgz", - "integrity": "sha512-qjMjTrk+RKv/sp4RPDpV5CnKhxjFI9p+GkLBOls5A8EEElldYWCWA9zceAkmfd0xIo2aU1nxiaLFoiya2sb6Cg==" - }, - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "svg-tags": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==" - }, - "svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "requires": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.37", - "csso": "^4.0.2", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - } - }, - "table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", - "requires": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - } - } - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" - }, - "tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "terminal-kit": { - "version": "2.11.7", - "resolved": "https://registry.npmjs.org/terminal-kit/-/terminal-kit-2.11.7.tgz", - "integrity": "sha512-Obe3T8rYOVEI+3uJM1jAK74GkyJsLqRNsA78LcVQrJJ0XfxkhNEVJZ3+pdYD2qcXt9MOx3e/fCMZK/J0lg7KUA==", - "requires": { - "@cronvel/get-pixels": "^3.4.1", - "chroma-js": "^2.4.2", - "lazyness": "^1.2.0", - "ndarray": "^1.0.19", - "nextgen-events": "^1.5.3", - "seventh": "^0.7.40", - "string-kit": "^0.17.6", - "tree-kit": "^0.7.4" - } - }, - "terser": { - "version": "5.16.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.3.tgz", - "integrity": "sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q==", - "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - } - } - }, - "terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", - "requires": { - "@jridgewell/trace-mapping": "^0.3.14", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - } - }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" - }, - "thenby": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz", - "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==" - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, - "dependencies": { - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "traverse": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz", - "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==" - }, - "tree-kit": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/tree-kit/-/tree-kit-0.7.4.tgz", - "integrity": "sha512-Of3tPmVs3b6BhzyUJ7t0olisf47kYr9qAm0XaUpURMjdBn6TwiVaaMuTFoKkkvPGojd9trKAHlrGGcGKcdR1DA==" - }, - "trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==" - }, - "trough": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", - "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==" - }, - "true-case-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", - "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", - "requires": { - "glob": "^7.1.2" - } - }, - "tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "requires": { - "minimist": "^1.2.0" - } - } - } - }, - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" - }, - "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - } - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==" - }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" - }, - "unidecode": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/unidecode/-/unidecode-0.1.8.tgz", - "integrity": "sha512-SdoZNxCWpN2tXTCrGkPF/0rL2HEq+i2gwRG1ReBvx8/0yTzC3enHfugOf8A9JBShVwwrRIkLX0YcDUGbzjbVCA==" - }, - "unified": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", - "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", - "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" - }, - "dependencies": { - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" - } - } - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" - }, - "unist-util-find-all-after": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz", - "integrity": "sha512-xaTC/AGZ0rIM2gM28YVRAFPIZpzbpDtU3dRmp7EXlNVA8ziQc4hY3H7BHXM1J49nEmiqc3svnqMReW+PGqbZKQ==", - "requires": { - "unist-util-is": "^4.0.0" - } - }, - "unist-util-is": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", - "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==" - }, - "unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", - "requires": { - "@types/unist": "^2.0.2" - } - }, - "unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - } - } - }, - "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==" - }, - "url-loader": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", - "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", - "requires": { - "loader-utils": "^2.0.0", - "mime-types": "^2.1.27", - "schema-utils": "^3.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "url-slug": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/url-slug/-/url-slug-2.0.0.tgz", - "integrity": "sha512-aiNmSsVgrjCiJ2+KWPferjT46YFKoE8i0YX04BlMVDue022Xwhg/zYlnZ6V9/mP3p8Wj7LEp0myiTkC/p6sxew==", - "requires": { - "unidecode": "0.1.8" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - } - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "dependencies": { - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" - } - } - }, - "vfile": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", - "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", - "requires": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" - } - }, - "vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", - "requires": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - } - }, - "watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "webpack": { - "version": "5.75.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", - "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "dependencies": { - "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" - }, - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" - }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", - "requires": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", - "colorette": "^2.0.14", - "commander": "^7.0.0", - "cross-spawn": "^7.0.3", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", - "webpack-merge": "^5.7.3" - } - }, - "webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", - "requires": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - } - }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "requires": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" - }, - "which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - } - }, - "wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" - }, - "zwitch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", - "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==" - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index eb51b150..00000000 --- a/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "@big-bite/release-notes", - "description": "", - "version": "1.0.0-alpha.1", - "author": "Big Bite", - "prettier": "@bigbite/build-tools/configs/prettier", - "scripts": { - "build:dev": "webpack --mode=development --env=\"project=$npm_config_project\"", - "build:dev:all": "webpack --mode=development --env=\"all-projects\"", - "build:prod": "webpack --mode=production --profile --env=\"project=$npm_config_project\"", - "build:prod:all": "webpack --mode=production --profile --env=\"all-projects\"", - "watch:dev": "webpack --mode=development --watch --env=\"project=$npm_config_project\"", - "watch:dev:all": "webpack --mode=development --watch --env=\"all-projects\"", - "watch:prod": "webpack --mode=production --watch --env=\"project=$npm_config_project\"", - "watch:prod:all": "webpack --mode=production --watch --env=\"all-projects\"" - }, - "dependencies": { - "prop-types": "^15.7.0", - "@bigbite/build-tools": "git+ssh://git@github.com:bigbite/build-tools.git#1.0.0-beta.6" - } -} diff --git a/src/dashboard/styles/styles.scss b/src/dashboard/styles/styles.scss deleted file mode 100644 index 68a652f4..00000000 --- a/src/dashboard/styles/styles.scss +++ /dev/null @@ -1,137 +0,0 @@ -.release-note-single { - width: 540px; - margin: 10px auto 24px; - background-color: #fff; - padding: 10px 20px 23px; - border-radius: 2px; -} - -.release-notes-pagination { - width: 540px; - display: block; - padding: 12px 20px; - text-transform: uppercase; - font-size: 14px; - border: 1px solid #0073aa; - text-align: center; - text-decoration: none; - margin: 20px auto; - font-weight: bold; - - transition: all 0.2s ease-in-out; - - &:hover, &:focus { - background: #0073aa; - color: #fff; - } -} - -.release-note-header { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - align-items: center; - border-bottom: 1px solid #e8eaeb; - padding-bottom: 6px; - margin-bottom: 8px; - - .is-empty & { - padding-bottom: 0; - margin-bottom: 0; - border-bottom: none; - } - - .release-note-meta-wrapper:first-of-type { - display: flex; - justify-content: center; - } - - .release-note-meta-wrapper:last-of-type { - display: flex; - justify-content: flex-end; - } -} - -.release-note-single .release-note-header h1::before { - content: ''; - display: inline-block; - width: 6px; - height: 6px; - background-color: #3fb950; - border-radius: 50%; - vertical-align: middle; - margin-right: 6px; -} - -.release-note-single.is-pre-release .release-note-header h1::before { - content: ''; - display: inline-block; - width: 6px; - height: 6px; - background-color: #f56e28; - border-radius: 50%; - vertical-align: middle; - margin-right: 6px; -} - -h1.release-note-title { - font-size: 20px; -} - -.release-note-meta:not(.empty) { - display: inline-block; - background-color: #e8eaeb; - font-size: 18px; - padding: 2px 4px; - border-radius: 4px; - font-family: 'Courier New', Courier, monospace; - width: fit-content; - height: fit-content; -} - -.release-note-body * { - max-width: 100%; -} - -.release-note-body .release-notes-list { - h2 { - font-size: 16px; - } - - ul, ol { - list-style: inside; - margin-top: 0; - margin-bottom: 0; - } -} - -.release-note-footer { - border-top: 1px solid #e8eaeb; - margin-top: 12px; - padding-top: 8px; - - .is-empty & { - border-top: none; - padding-top: 0; - margin-top: 0; - } -} - -li.release-note > a { - background: #3fb950 !important; - transition: background 0.2s ease-in-out !important; - - &:hover, &:focus { - color: #fff !important; - background: #238636 !important; - } -} - -li.release-note.is-pre-release > a { - background: #f56e28 !important; - transition: background 0.2s ease-in-out !important; - - &:hover, &:focus { - color: #fff !important; - background: #ca4a1f !important; - } -} diff --git a/src/editor/blocks/index.js b/src/editor/blocks/index.js deleted file mode 100644 index 91d80806..00000000 --- a/src/editor/blocks/index.js +++ /dev/null @@ -1 +0,0 @@ -import './markdown-parser'; diff --git a/src/editor/blocks/markdown-parser/edit.js b/src/editor/blocks/markdown-parser/edit.js deleted file mode 100644 index 7ab23c44..00000000 --- a/src/editor/blocks/markdown-parser/edit.js +++ /dev/null @@ -1,20 +0,0 @@ -import PropTypes from 'prop-types'; - -const { useEffect } = wp.element; - -const Edit = ({ attributes, clientId }) => { - useEffect(() => { - const blocks = wp.blocks.rawHandler({ HTML: attributes.html }); - wp.data.dispatch('core/block-editor').insertBlocks(blocks); - wp.data.dispatch('core/block-editor').removeBlock(clientId); - }, [attributes]); - - return null; -}; - -Edit.propTypes = { - attributes: PropTypes.object.isRequired, - clientId: PropTypes.string.isRequired, -}; - -export default Edit; diff --git a/src/editor/blocks/markdown-parser/index.js b/src/editor/blocks/markdown-parser/index.js deleted file mode 100644 index f583892b..00000000 --- a/src/editor/blocks/markdown-parser/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import Edit from './edit'; - -const { registerBlockType } = wp.blocks; - -registerBlockType('release-notes/markdown-parser', { - edit: Edit, - save: () => null, -}); diff --git a/src/editor/register-plugins/CustomBlockFields.js b/src/editor/register-plugins/CustomBlockFields.js deleted file mode 100644 index 68c119c8..00000000 --- a/src/editor/register-plugins/CustomBlockFields.js +++ /dev/null @@ -1,59 +0,0 @@ -const { PluginDocumentSettingPanel } = wp.editPost; -const { - PanelRow, - DatePicker, - ToggleControl, - __experimentalInputControl: InputControl, - __experimentalText: Text, - __experimentalVStack: VStack, -} = wp.components; -const { useEntityProp } = wp.coreData; -const { useSelect } = wp.data; -const { __ } = wp.i18n; - -/** Adds the date selector to the post settings */ -function CustomBlockFields() { - const postType = useSelect((select) => select('core/editor').getCurrentPostType(), []); - if (postType !== 'release-note') return null; - - const [meta, setMeta] = useEntityProp('postType', postType, 'meta'); - const { version, is_pre_release: isPrerelease, release_date: releaseDate } = meta; - - const onDateChange = (val) => { - setMeta({ ...meta, release_date: val }); - }; - - return ( - <> - - - setMeta({ ...meta, version: val })} - label={__('Version Number')} - /> - -
- - - {__('Release Date')} - onDateChange(new Date(val).toISOString().split('T')[0])} - /> - - -
- - setMeta({ ...meta, is_pre_release: val })} - /> - -
- - ); -} - -export default CustomBlockFields; diff --git a/src/editor/register-plugins/index.js b/src/editor/register-plugins/index.js deleted file mode 100644 index 6873bfc7..00000000 --- a/src/editor/register-plugins/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import CustomBlockFields from './CustomBlockFields'; - -const { registerPlugin } = wp.plugins; - -registerPlugin('release-notes-meta', { - icon: null, - render: CustomBlockFields, -}); diff --git a/src/editor/settings.js b/src/editor/settings.js deleted file mode 100644 index f6c4b3f6..00000000 --- a/src/editor/settings.js +++ /dev/null @@ -1,4 +0,0 @@ -export const NAMESPACE = 'big-bite-release-notes'; -export const STORE_NAME = 'big-bite/release-notes'; -export const FILTER_NAMESPACE = STORE_NAME; -export const I18N_DOMAIN = 'release-notes'; diff --git a/src/entrypoints/dashboard.js b/src/entrypoints/dashboard.js deleted file mode 100644 index 2e82c9a7..00000000 --- a/src/entrypoints/dashboard.js +++ /dev/null @@ -1,3 +0,0 @@ -import '../dashboard/styles/styles.scss'; - -// Needs something here or it won't work. diff --git a/src/entrypoints/editor.js b/src/entrypoints/editor.js deleted file mode 100644 index a6d66480..00000000 --- a/src/entrypoints/editor.js +++ /dev/null @@ -1,2 +0,0 @@ -import '../editor/register-plugins'; -import '../editor/blocks'; diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 00000000..0a16772b --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,25 @@ + + + + + + 0 + + + diff --git a/vendor/automattic/jetpack-autoloader/CHANGELOG.md b/vendor/automattic/jetpack-autoloader/CHANGELOG.md new file mode 100644 index 00000000..076c786b --- /dev/null +++ b/vendor/automattic/jetpack-autoloader/CHANGELOG.md @@ -0,0 +1,383 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [2.11.20] - 2023-05-11 +- Updated package dependencies + +## [2.11.19] - 2023-04-25 +### Fixed +- Fix example in README [#30225] + +## [2.11.18] - 2023-03-28 +### Changed +- Minor internal updates. + +## [2.11.17] - 2023-03-27 +### Fixed +- Don't error when processing packages specifying missing PSR paths. [#29669] + +## [2.11.16] - 2023-02-20 +### Changed +- Minor internal updates. + +## [2.11.15] - 2023-01-11 +### Changed +- Updated package dependencies. + +## [2.11.14] - 2022-12-19 +### Changed +- Use `Composer\ClassMapGenerator\ClassMapGenerator` when available (i.e. with composer 2.4). [#27812] + +### Fixed +- Declare fields for PHP 8.2 compatibility. [#27949] + +## [2.11.13] - 2022-12-02 +### Changed +- Updated package dependencies. [#27688] + +## [2.11.12] - 2022-11-22 +### Changed +- Updated package dependencies. [#27043] + +## [2.11.11] - 2022-10-25 +### Changed +- Sort data in generated `vendor/composer/jetpack_autoload_classmap.php` to avoid spurious diffs. [#26929] + +## [2.11.10] - 2022-10-05 + +- Tests: Clear `COMPOSER_AUTH` environment variable when running Composer for tests. [#26404] + +## [2.11.9] - 2022-09-27 +### Fixed +- Tests: Clear `COMPOSER_AUTH` environment variable when running Composer for tests. [#26404] + +## [2.11.8] - 2022-09-20 +### Fixed +- Tests: skip test if it requires a version of Composer not compatible with the running version of PHP. [#26143] + +## [2.11.7] - 2022-07-26 +### Changed +- Updated package dependencies. [#25158] + +## [2.11.6] - 2022-06-21 +### Changed +- Renaming `master` to `trunk`. + +## [2.11.5] - 2022-05-18 +### Fixed +- Fix new PHPCS sniffs. [#24366] + +## [2.11.4] - 2022-04-26 +### Changed +- Updated package dependencies. + +## [2.11.3] - 2022-04-19 +### Changed +- PHPCS: Fix `WordPress.Security.ValidatedSanitizedInput` + +## [2.11.2] - 2022-03-29 +### Changed +- Microperformance: Use === null instead of is_null + +## [2.11.1] - 2022-03-08 +### Removed +- Removed the Upgrade Handler. + +## [2.11.0] - 2022-03-08 +### Added +- On plugin update, pre-load all (non-PSR-4) classes from the plugin to avoid mid-upgrade fatals. + +## [2.10.13] - 2022-03-01 +### Fixed +- Fix tests for upstream phpunit change. + +## [2.10.12] - 2022-01-25 +### Changed +- Updated package dependencies. + +## [2.10.11] - 2022-01-04 +### Changed +- Switch to pcov for code coverage. +- Updated package dependencies + +## [2.10.10] - 2021-11-16 +### Added +- Soft return if autoloader chain is not available. + +## [2.10.9] - 2021-11-02 +### Changed +- Set `convertDeprecationsToExceptions` true in PHPUnit config. + +## [2.10.8] - 2021-10-13 +### Changed +- Updated package dependencies. + +## [2.10.7] - 2021-10-07 +### Changed +- Updated package dependencies + +## [2.10.6] - 2021-09-28 +### Changed +- Updated package dependencies. + +## [2.10.5] - 2021-08-31 +### Changed +- Run composer update on test-php command instead of phpunit +- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills). + +## [2.10.4] - 2021-08-10 +### Changed +- Updated package dependencies. + +## [2.10.3] - 2021-05-25 +### Changed +- Updated package dependencies. + +## [2.10.2] - 2021-04-27 +### Changed +- Updated package dependencies. + +## [2.10.1] - 2021-03-30 +### Added +- Composer alias for dev-master, to improve dependencies +- Tests: Added code coverage transformation + +### Changed +- Update package dependencies. + +### Fixed +- Fix coverage test +- Fix uninstallation fatal +- Update tests for changed composer 2.0.9 hash. +- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in. + +## [2.10.0] - 2021-02-09 + +- Autoloader: test suite refactor + +## [2.9.1] - 2021-02-05 + +- CI: Make tests more generic +- Autoloader: stricter type-checking on WP functions +- Autoloader: prevent transitive plugin execution + +## [2.9.0] - 2021-01-25 + +- Autoloader: revised latest autoloader inclusion semantics +- Add mirror-repo information to all current composer packages +- Monorepo: Reorganize all projects +- Autoloader: Don't cache deactivating plugins + +## [2.8.0] - 2020-12-18 + +## [2.7.1] - 2020-12-18 + +- Autoloader: Added realpath resolution to plugin paths + +## [2.7.0] - 2020-12-08 + +- Autoloader: Preemptively load unknown plugins from cache +- Removed unwanted dot +- Pin dependencies +- Packages: Update for PHP 8 testing + +## [2.6.0] - 2020-11-19 + +- Autoloader: AutoloadGenerator no longer extends Composer's AutoloadGenerator class +- Autoloader: Reuse an existing autoloader suffix if available +- Updated PHPCS: Packages and Debugger + +## [2.5.0] - 2020-10-08 + +- Autoloader: remove the defined('JETPACK_AUTOLOAD_DEV') checks from the tests + +## [2.4.0] - 2020-09-28 + +- Autoloader: remove the plugins_loaded bullet point from the README +- Packages: avoid PHPCS warnings +- Autoloader: add PSR-0 support +- Autoloader: Detect filtering of active_plugins +- Autoloader: Support unoptimized PSR-4 + +## [2.3.0] - 2020-08-21 + +- Autoloader: remove the plugin update hook + +## [2.2.0] - 2020-08-14 + +- Autoloader: don't reset the autoloader version during plugin update +- CI: Try collect js coverage + +## [2.1.0] - 2020-07-27 + +- Autoloader: convert '\' directory separators to '/' in plugin paths +- Autoloader: Avoid a PHP warning when an empty string is passed to `is_directory_plugin()`. +- Autoloader: Tests: Use a string with define + +## [2.0.2] - 2020-07-09 + +- Autoloader: Avoid a PHP warning when an empty string is passed to `is_directory_plugin()`. + +## [2.0.1] - 2020-07-02 + +- Autoloader: Tests: Use a string with define + +## [2.0.0] - 2020-06-29 + +## [2.0.0-beta] - 2020-06-29 + +- Autoloader: Support Composer v2.0 +- Autoloader: use paths to identify plugins instead of the directories +- Autoloader: fix the fatal that occurs during plugin update +- Autoloader: add fallback check for plugin path in mu-plugins +- Autoloader: use JETPACK__PLUGIN_DIR when looking for the jetpack plugin directory. +- Feature Branch: Update the Autoloader +- PHPCS: Clean up the packages +- PHPCS Updates after WPCS 2.3 + +## [1.7.0] - 2020-04-23 + +- Jetpack: Move comment notification override back to the constructor + +## [1.6.0] - 2020-03-26 + +- Autoloader: Remove file check to improve performance. + +## [1.5.0] - 2020-02-25 + +- Jetpack: instantiate manager object if it's null + +## [1.4.1] - 2020-02-14 + +- Autoloader: Load only latest version of autoload files to avoid conflicts. + +## [1.4.0] - 2020-01-23 + +- Autoloader: Remove the ignored classes + +## [1.3.8] - 2020-01-14 + +- Trying to add deterministic initialization. +- Autoloader: Remove Manager_Interface and Plugin\Tracking from ignored list +- Autoloader: Remove Jetpack_IXR_Client from ignore list + +## [1.3.7] - 2019-12-10 + +## [1.3.6] - 2019-12-09 + +- Autoloader: Use long-form sytax for array + +## [1.3.5] - 2019-11-26 + +- Fix/php notice status + +## [1.3.4] - 2019-11-08 + +- Deprecate Jetpack::is_development_mode() in favor of the packaged Status()->is_development_mode() + +## [1.3.3] - 2019-10-28 + +- Packages: Add gitattributes files to all packages that need th… + +## [1.3.2] - 2019-09-24 + +- Autoloader: Cover scenarios where composer/autoload_files.php… + +## [1.3.1] - 2019-09-20 + +- Docs: Unify usage of @package phpdoc tags + +## [1.3.0] - 2019-09-14 + +- Fix for empty namespaces. #13459 +- Connection: Move the Jetpack IXR client to the package +- Adds full connection cycle capability to the Connection package. +- Jetpack 7.5: Back compatibility package + +## [1.2.0] - 2019-06-24 + +- Jetpack DNA: Add full classmap support to Autoloader +- Move Jetpack_Sync_Main from legacy to PSR-4 + +## [1.1.0] - 2019-06-19 + +- Packages: Move autoloader tests to the package +- DNA: Move Jetpack Usage tracking to its own file +- Jetpack DNA: More isolation of Tracks Package +- Autoloader: Ignore XMLRPC_Connector if called too early +- Autoloader: Ignore Jetpack_Signature if called too early + +## 1.0.0 - 2019-06-11 + +- Add Custom Autoloader + +[2.11.20]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.19...v2.11.20 +[2.11.19]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.18...v2.11.19 +[2.11.18]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.17...v2.11.18 +[2.11.17]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.16...v2.11.17 +[2.11.16]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.15...v2.11.16 +[2.11.15]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.14...v2.11.15 +[2.11.14]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.13...v2.11.14 +[2.11.13]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.12...v2.11.13 +[2.11.12]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.11...v2.11.12 +[2.11.11]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.10...v2.11.11 +[2.11.10]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.9...v2.11.10 +[2.11.9]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.8...v2.11.9 +[2.11.8]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.7...v2.11.8 +[2.11.7]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.6...v2.11.7 +[2.11.6]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.5...v2.11.6 +[2.11.5]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.4...v2.11.5 +[2.11.4]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.3...v2.11.4 +[2.11.3]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.2...v2.11.3 +[2.11.2]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.1...v2.11.2 +[2.11.1]: https://github.com/Automattic/jetpack-autoloader/compare/v2.11.0...v2.11.1 +[2.11.0]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.13...v2.11.0 +[2.10.13]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.12...v2.10.13 +[2.10.12]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.11...v2.10.12 +[2.10.11]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.10...v2.10.11 +[2.10.10]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.9...v2.10.10 +[2.10.9]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.8...v2.10.9 +[2.10.8]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.7...v2.10.8 +[2.10.7]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.6...v2.10.7 +[2.10.6]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.5...v2.10.6 +[2.10.5]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.4...v2.10.5 +[2.10.4]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.3...v2.10.4 +[2.10.3]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.2...v2.10.3 +[2.10.2]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.1...v2.10.2 +[2.10.1]: https://github.com/Automattic/jetpack-autoloader/compare/v2.10.0...v2.10.1 +[2.10.0]: https://github.com/Automattic/jetpack-autoloader/compare/v2.9.1...v2.10.0 +[2.9.1]: https://github.com/Automattic/jetpack-autoloader/compare/v2.9.0...v2.9.1 +[2.9.0]: https://github.com/Automattic/jetpack-autoloader/compare/v2.8.0...v2.9.0 +[2.8.0]: https://github.com/Automattic/jetpack-autoloader/compare/v2.7.1...v2.8.0 +[2.7.1]: https://github.com/Automattic/jetpack-autoloader/compare/v2.7.0...v2.7.1 +[2.7.0]: https://github.com/Automattic/jetpack-autoloader/compare/v2.6.0...v2.7.0 +[2.6.0]: https://github.com/Automattic/jetpack-autoloader/compare/v2.5.0...v2.6.0 +[2.5.0]: https://github.com/Automattic/jetpack-autoloader/compare/v2.4.0...v2.5.0 +[2.4.0]: https://github.com/Automattic/jetpack-autoloader/compare/v2.3.0...v2.4.0 +[2.3.0]: https://github.com/Automattic/jetpack-autoloader/compare/v2.2.0...v2.3.0 +[2.2.0]: https://github.com/Automattic/jetpack-autoloader/compare/v2.1.0...v2.2.0 +[2.1.0]: https://github.com/Automattic/jetpack-autoloader/compare/v2.0.2...v2.1.0 +[2.0.2]: https://github.com/Automattic/jetpack-autoloader/compare/v2.0.1...v2.0.2 +[2.0.1]: https://github.com/Automattic/jetpack-autoloader/compare/v2.0.0...v2.0.1 +[2.0.0]: https://github.com/Automattic/jetpack-autoloader/compare/v2.0.0-beta...v2.0.0 +[2.0.0-beta]: https://github.com/Automattic/jetpack-autoloader/compare/v1.7.0...v2.0.0-beta +[1.7.0]: https://github.com/Automattic/jetpack-autoloader/compare/v1.6.0...v1.7.0 +[1.6.0]: https://github.com/Automattic/jetpack-autoloader/compare/v1.5.0...v1.6.0 +[1.5.0]: https://github.com/Automattic/jetpack-autoloader/compare/v1.4.1...v1.5.0 +[1.4.1]: https://github.com/Automattic/jetpack-autoloader/compare/v1.4.0...v1.4.1 +[1.4.0]: https://github.com/Automattic/jetpack-autoloader/compare/v1.3.8...v1.4.0 +[1.3.8]: https://github.com/Automattic/jetpack-autoloader/compare/v1.3.7...v1.3.8 +[1.3.7]: https://github.com/Automattic/jetpack-autoloader/compare/v1.3.6...v1.3.7 +[1.3.6]: https://github.com/Automattic/jetpack-autoloader/compare/v1.3.5...v1.3.6 +[1.3.5]: https://github.com/Automattic/jetpack-autoloader/compare/v1.3.4...v1.3.5 +[1.3.4]: https://github.com/Automattic/jetpack-autoloader/compare/v1.3.3...v1.3.4 +[1.3.3]: https://github.com/Automattic/jetpack-autoloader/compare/v1.3.2...v1.3.3 +[1.3.2]: https://github.com/Automattic/jetpack-autoloader/compare/v1.3.1...v1.3.2 +[1.3.1]: https://github.com/Automattic/jetpack-autoloader/compare/v1.3.0...v1.3.1 +[1.3.0]: https://github.com/Automattic/jetpack-autoloader/compare/v1.2.0...v1.3.0 +[1.2.0]: https://github.com/Automattic/jetpack-autoloader/compare/v1.1.0...v1.2.0 +[1.1.0]: https://github.com/Automattic/jetpack-autoloader/compare/v1.0.0...v1.1.0 diff --git a/vendor/automattic/jetpack-autoloader/LICENSE.txt b/vendor/automattic/jetpack-autoloader/LICENSE.txt new file mode 100644 index 00000000..e82774c1 --- /dev/null +++ b/vendor/automattic/jetpack-autoloader/LICENSE.txt @@ -0,0 +1,357 @@ +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +=================================== + + +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + Preamble + +The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +GNU GENERAL PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +a) You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + +b) You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. + +c) If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + +a) Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, + +b) Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +c) Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + +Copyright (C) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +Gnomovision version 69, Copyright (C) year name of author +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +`Gnomovision' (which makes passes at compilers) written by James Hacker. + +, 1 April 1989 +Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/vendor/automattic/jetpack-autoloader/SECURITY.md b/vendor/automattic/jetpack-autoloader/SECURITY.md new file mode 100644 index 00000000..b4b46c0e --- /dev/null +++ b/vendor/automattic/jetpack-autoloader/SECURITY.md @@ -0,0 +1,38 @@ +# Security Policy + +Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/). + +## Supported Versions + +Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions. + +## Reporting a Vulnerability + +[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure. + +**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.** + +Our most critical targets are: + +* Jetpack and the Jetpack composer packages (all within this repo) +* Jetpack.com -- the primary marketing site. +* cloud.jetpack.com -- a management site. +* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites. + +For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic). + +_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._ + +## Guidelines + +We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines: + +* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines). +* Pen-testing Production: + * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above). + * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC. + * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels. + * To be eligible for a bounty, all of these guidelines must be followed. +* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability. + +We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties. diff --git a/vendor/automattic/jetpack-autoloader/composer.json b/vendor/automattic/jetpack-autoloader/composer.json new file mode 100644 index 00000000..27994e7b --- /dev/null +++ b/vendor/automattic/jetpack-autoloader/composer.json @@ -0,0 +1,42 @@ +{ + "name": "automattic/jetpack-autoloader", + "description": "Creates a custom autoloader for a plugin or theme.", + "type": "composer-plugin", + "license": "GPL-2.0-or-later", + "require": { + "composer-plugin-api": "^1.1 || ^2.0" + }, + "require-dev": { + "yoast/phpunit-polyfills": "1.0.4", + "automattic/jetpack-changelogger": "^3.3.2" + }, + "autoload": { + "classmap": [ + "src/AutoloadGenerator.php" + ], + "psr-4": { + "Automattic\\Jetpack\\Autoloader\\": "src" + } + }, + "scripts": { + "phpunit": [ + "./vendor/phpunit/phpunit/phpunit --colors=always" + ], + "test-php": [ + "@composer phpunit" + ] + }, + "minimum-stability": "dev", + "prefer-stable": true, + "extra": { + "autotagger": true, + "class": "Automattic\\Jetpack\\Autoloader\\CustomAutoloaderPlugin", + "mirror-repo": "Automattic/jetpack-autoloader", + "changelogger": { + "link-template": "https://github.com/Automattic/jetpack-autoloader/compare/v${old}...v${new}" + }, + "branch-alias": { + "dev-trunk": "2.11.x-dev" + } + } +} diff --git a/vendor/automattic/vipwpcs/CHANGELOG.md b/vendor/automattic/vipwpcs/CHANGELOG.md new file mode 100644 index 00000000..b3a805c8 --- /dev/null +++ b/vendor/automattic/vipwpcs/CHANGELOG.md @@ -0,0 +1,589 @@ +# Change Log for VIP Coding Standards + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [2.3.3] - 2021-09-29 + +Props: gudmdharalds, jrfnl, BrookeDot, rebeccahum + +## Changed +- [#690](https://github.com/Automattic/VIP-Coding-Standards/pull/690): Ruleset: do not flag undefined variables in file scope or unused variables before require statement. +- [#691](https://github.com/Automattic/VIP-Coding-Standards/pull/691): Composer: use VariableAnalysis 2.11.1. +- [#694](https://github.com/Automattic/VIP-Coding-Standards/pull/694): PHPCS: enable caching for quicker scanning. +- [#697](https://github.com/Automattic/VIP-Coding-Standards/pull/697): ProperEscapingFunction: upgrade htmlAttrNotByEscHTML to default severity level. + +## Removed +- [#692](https://github.com/Automattic/VIP-Coding-Standards/pull/692): RestrictedFunctions: remove dbDelta group. + +## [2.3.2] - 2021-04-28 + +Props: jrfnl + +### Fixed +- [#681](https://github.com/Automattic/VIP-Coding-Standards/pull/681): ProperEscapingFunction: improve attribute matching accuracy for notAttrEscAttr. + +## [2.3.1] - 2021-04-23 + +Props: jrfnl + +### Fixed +- [#668](https://github.com/Automattic/VIP-Coding-Standards/pull/668): ProperEscapingFunction: fix overreach of comma usage in non-echo expressions for notAttrEscAttr. +- [#670](https://github.com/Automattic/VIP-Coding-Standards/pull/670): ProperEscapingFunction: improve "action" match precision for hrefSrcEscUrl. + +### Deprecated +- [#670](https://github.com/Automattic/VIP-Coding-Standards/pull/670): ProperEscapingFunction: private properties `$url_attrs` and `$attr_endings` are deprecated along with the public methods `is_html_attr()` and `attr_expects_url()`. + +## [2.3.0] - 2021-04-19 + +Props: jrfnl, rebeccahum, kevinfodness, GaryJones. + +** There is a minor breaking change in the ProperEscapingFunction sniff from PR [#624](https://github.com/Automattic/VIP-Coding-Standards/pull/624). The `escaping_function` property can no longer be overruled via custom rulesets. Please remove any usages of the property in custom rulesets. + +** Composer now requires the [phpcodesniffer-composer-installer](https://github.com/Dealerdirect/phpcodesniffer-composer-installer) plugin per [#583](https://github.com/Automattic/VIP-Coding-Standards/pull/583). Note: If you either include it in the "require-dev" of your `composer.json`, use another Composer PHPCS plugin, or run bash commands to register PHPCS standards, please remove it from those sources to prevent interferences or version constraint conflicts. + +### Added +- [#581](https://github.com/Automattic/VIP-Coding-Standards/pull/581): AlwaysReturnInFilter: flag abstract methods for manual inspection. +- [#583](https://github.com/Automattic/VIP-Coding-Standards/pull/583): Composer: require phpcs-composer-installer plugin. +- [#586](https://github.com/Automattic/VIP-Coding-Standards/pull/586): IncludingNonPHPFile: recognition of .phar file extensions. +- [#589](https://github.com/Automattic/VIP-Coding-Standards/pull/589): WPQueryParams: flags 'exclude' array key. +- [#595](https://github.com/Automattic/VIP-Coding-Standards/pull/595): Underscorejs: checks for additional print syntaxes and now throws an additional error for each occurrence of unescaped notation. +- [#624](https://github.com/Automattic/VIP-Coding-Standards/pull/624): ProperEscapingFunction: account for additional escaping functions and check for `esc_attr()` usage in non-HTML attributes. +- [#638](https://github.com/Automattic/VIP-Coding-Standards/pull/638): IncludingFile: new public property `$allowedKeywords` for allowing custom partial keywords in constants to reduce false positives. + +### Changed +- [#586](https://github.com/Automattic/VIP-Coding-Standards/pull/586): IncludingNonPHPFile: various performance improvements. +- [#587](https://github.com/Automattic/VIP-Coding-Standards/pull/587): LowExpiryCacheTime: new warning added for manual inspection along with various improvements. +- [#592](https://github.com/Automattic/VIP-Coding-Standards/pull/592): DynamicCalls: various improvements. +- [#595](https://github.com/Automattic/VIP-Coding-Standards/pull/595): Underscorejs: various improvements. +- [#618](https://github.com/Automattic/VIP-Coding-Standards/pull/618): RestrictedFunctions: upgrade setcookie() to error at sniff level and remove Batcache references from messaging. +- [#620](https://github.com/Automattic/VIP-Coding-Standards/pull/620): Ruleset: silence UnusedVariable from VariableAnalysis to reduce noise. +- [#630](https://github.com/Automattic/VIP-Coding-Standards/pull/630): VariableAnalysis: fix incompatibility for VariableAnalysis standard with previously deprecated native VIPCS sniff. +- [#639](https://github.com/Automattic/VIP-Coding-Standards/pull/639): RestrictedFunctions: remove site_option group. +- [#644](https://github.com/Automattic/VIP-Coding-Standards/pull/644): RestrictedFunctions: remove wp_cache_get_multi group. +- [#645](https://github.com/Automattic/VIP-Coding-Standards/pull/645): Ruleset: silence WordPress.WP.AlternativeFunctions.file_system_read_readfile. +- [#646](https://github.com/Automattic/VIP-Coding-Standards/pull/646): Ruleset: silence WordPress.WP.AlternativeFunctions.file_system_read_fclose. +- [#647](https://github.com/Automattic/VIP-Coding-Standards/pull/647): RestrictedFunctions: remove get_super_admins group. +- [#649](https://github.com/Automattic/VIP-Coding-Standards/pull/649): RestrictedFunctions: downgrade switch_to_blog() to warning and change messaging. +- [#652](https://github.com/Automattic/VIP-Coding-Standards/pull/652): RestrictedFunctions/RestrictedVariables: remove usermeta related errors. + +### Fixed +- [#444](https://github.com/Automattic/VIP-Coding-Standards/pull/444): ConstantString: only error when a plain constant is passed as constant name parameter. +- [#581](https://github.com/Automattic/VIP-Coding-Standards/pull/581): AlwaysReturnInFilter: fix runtime failure on abstract methods. +- [#584](https://github.com/Automattic/VIP-Coding-Standards/pull/584): Performance: more selective sniffing for efficiency. +- [#586](https://github.com/Automattic/VIP-Coding-Standards/pull/586): IncludingNonPHPFile: various bug fixes such as recognition of interpolated strings and case insensitivity in file extensions. +- [#587](https://github.com/Automattic/VIP-Coding-Standards/pull/587): LowExpiryCacheTime: allow arithmetic operators, simple floats, numerical strings, zeroes and parentheses in calculations, and FQN time constants. +- [#592](https://github.com/Automattic/VIP-Coding-Standards/pull/592): DynamicCalls: ignore comments, allow double quotes and remove potential memory leak. +- [#595](https://github.com/Automattic/VIP-Coding-Standards/pull/595): Underscorejs: fixed false positive for when a variable is `_.escape()`-ed. +- [#624](https://github.com/Automattic/VIP-Coding-Standards/pull/624): ProperEscapingFunction: slash escaped quotes and non-quoted strings in HTML attributes are now parsed as expected. + +### Removed +- [#624](https://github.com/Automattic/VIP-Coding-Standards/pull/624): ProperEscapingFunction: remove `$escaping_functions` public property. + +### Maintenance +- [#582](https://github.com/Automattic/VIP-Coding-Standards/pull/582): CI: re-try composer install on failure. +- [#599](https://github.com/Automattic/VIP-Coding-Standards/pull/599): CI: add build against PHP 8. +- [#606](https://github.com/Automattic/VIP-Coding-Standards/pull/606): Ruleset: remove redundant rule ref. +- [#607](https://github.com/Automattic/VIP-Coding-Standards/pull/607): Ruleset: remove redundant rule ref. +- [#608](https://github.com/Automattic/VIP-Coding-Standards/pull/608): Ruleset: remove duplicate rule ref. +- [#611](https://github.com/Automattic/VIP-Coding-Standards/pull/611): Ruleset: remove redundant notice type declaration. +- [#617](https://github.com/Automattic/VIP-Coding-Standards/pull/617): Ruleset: remove redundant notice type declaration. +- [#619](https://github.com/Automattic/VIP-Coding-Standards/pull/619): Docs: Update links to wpvip.com. +- [#631](https://github.com/Automattic/VIP-Coding-Standards/pull/631): QA: remove unused use statements. +- [#632](https://github.com/Automattic/VIP-Coding-Standards/pull/632): Docs: various minor improvements (typos, alignment and code examples). +- [#633](https://github.com/Automattic/VIP-Coding-Standards/pull/633): CI: switch to GitHub Actions. +- [#635](https://github.com/Automattic/VIP-Coding-Standards/pull/635): Ruleset: remove redundant rule ref. +- [#653](https://github.com/Automattic/VIP-Coding-Standards/pull/653): CI: use parallel linting of PHP files. +- [#655](https://github.com/Automattic/VIP-Coding-Standards/pull/655): QA: remove redundant ignore annotations. +- [#656](https://github.com/Automattic/VIP-Coding-Standards/pull/656): CI: always check that sniffs are feature complete. +- [#657](https://github.com/Automattic/VIP-Coding-Standards/pull/657): CI: add "quicktest" stage for non-PR/merge builds. +- [#658](https://github.com/Automattic/VIP-Coding-Standards/pull/658): Release template: add checkbox for dependency check. + +## [2.2.0] - 2020-09-09 + +Props: GaryJones, jrfnl, rebeccahum. + +Technically, there's a breaking change due to the use of the VariableAnalysis package over the previous sniff. If you have `WordPressVIPMinimum.Variables.Variables` references in your PHPCS config file or in inline ignore comments, then these will need to be updated to `VariableAnalysis.CodeAnalysis.VariableAnalysis`. + +### Added +- [#494](https://github.com/Automattic/VIP-Coding-Standards/pull/494): `.gitattributes` file. +- [#495](https://github.com/Automattic/VIP-Coding-Standards/pull/495): `CODEOWNERS` file. +- [#450](https://github.com/Automattic/VIP-Coding-Standards/pull/450): [VariableAnalysis](https://github.com/sirbrillig/phpcs-variable-analysis/) package. +- [#560](https://github.com/Automattic/VIP-Coding-Standards/pull/560): Allow checking test code coverage. +- [#579](https://github.com/Automattic/VIP-Coding-Standards/pull/560): Docs: Add comparisons and props to change log for old versions. + +### Changed +- [#500](https://github.com/Automattic/VIP-Coding-Standards/pull/500): Travis: change from "trusty" to "xenial". +- [#501](https://github.com/Automattic/VIP-Coding-Standards/pull/501): Move and improve `CONTRIBUTING.md`. +- [#502](https://github.com/Automattic/VIP-Coding-Standards/pull/502): CS Ruleset: minor tweaks. +- [#508](https://github.com/Automattic/VIP-Coding-Standards/pull/508): RulesetTest: don't use the system default version of PHP. +- [#558](https://github.com/Automattic/VIP-Coding-Standards/pull/558): Test bootstrap: various minor tweaks. +- [#571](https://github.com/Automattic/VIP-Coding-Standards/pull/571): CS: change yoda conditions to non-yoda. +- [#573](https://github.com/Automattic/VIP-Coding-Standards/pull/573): Composer: Change minimum stability to stable. + +### Fixed +- [#503](https://github.com/Automattic/VIP-Coding-Standards/pull/503): RulesetTest, fix compatibility with Windows. +- [#504](https://github.com/Automattic/VIP-Coding-Standards/pull/504): RulesetTest: fail the build on failing ruleset tests, fix the failing ruleset test, and fix the test script to handle 0 values. +- [#505](https://github.com/Automattic/VIP-Coding-Standards/pull/505): DeclarationCompatibility: fix incorrect signature check for `Walker::walk()`. +- [#509](https://github.com/Automattic/VIP-Coding-Standards/pull/509): RulesetTest: Revert #485 and fix one of the three causes properly. +- [#559](https://github.com/Automattic/VIP-Coding-Standards/pull/559): Variables/RestrictedVariables: fix namespace of unit test file, fix the test. +- [#561](https://github.com/Automattic/VIP-Coding-Standards/pull/561): Functions/RestrictedFunctions: fix false positive on class instantiation. +- [#563](https://github.com/Automattic/VIP-Coding-Standards/pull/563): Hooks/AlwaysReturnInFilter: add support for hook-ins using short arrays. +- [#564](https://github.com/Automattic/VIP-Coding-Standards/pull/564): Hooks/PreGetPosts: add support for hook-ins using short arrays. +- [#565](https://github.com/Automattic/VIP-Coding-Standards/pull/565): PreGetPosts: improve the `isEarlyMainQueryCheck()` method. +- [#566](https://github.com/Automattic/VIP-Coding-Standards/pull/566): RestrictedFunctions: fix false negative - functions in `config_settings` would never match. +- [#569](https://github.com/Automattic/VIP-Coding-Standards/pull/569): RestrictedVariables: don't report on "use" in `isset()`. +- [#575](https://github.com/Automattic/VIP-Coding-Standards/pull/575): ProperEscaping: Fix message for action attribute. +- [#576](https://github.com/Automattic/VIP-Coding-Standards/pull/576): Docs: Update notes for releasing. + +### Deprecated +- [#450](https://github.com/Automattic/VIP-Coding-Standards/pull/450): Deprecate Variables/VariableAnalysisSniff. Will be removed in the next major release. + +## [2.1.0] - 2020-07-07 + +Bumps requirements to PHPCS 3.5.5+ and WPCS 2.3.0+. + +Props: GaryJones, jenkoian, kevinfodness, rebeccahum. + +### Added +- `get_page_by_path()` restricted function warning, to suggest `wpcom_vip_get_page_by_path()` function. +- `stats_get_csv()` restricted function error, since this is a Jetpack-only function. +- Expanded list of HTMLExecutingFunctions to include `after`, `appendTo`, `before`, `insertAfter`, `insertBefore`, `prepend`, `prependTo`, `replaceAll` and `replaceWith`. +- Support PHP 5.4+ (down from 5.6+). +- PHP 8 nightly testing. + +### Changed +- Expand message for `wp_remote_get()` usage. +- Downgrade `append()` usage violation from Error to Warning for VIP Go, to be consistent with the other HTMLExecutingFunctions. +- Downgrade AdminBarRemoval sniff from Error to Warning for VIP Go. +- Add `get_parent_theme_file_path()` to safelist of path functions for `WordPressVIPMinimum.Files.IncludingFile` sniff. +- Allow short array syntax and fix tests within the VIPCS own coding standards. +- Update issue templates. + +### Fixed +- Use new `WordPress.DateTime.RestrictedFunctions` sniff instead of deprecated `WordPress.WP.TimezoneChange`. +- Fixed warnings and information items in Travis. + +### Removed +- `get_super_admins()` restricted function rule for VIP Go. +- `WordPressVIPMinimum.VersionControl.MergeConflict` sniff in favour of `Generic.VersionControl.GitMergeConflict`. + +## [2.0.0] - 2019-07-12 + +This release switches from having WPCS `1.*` as a dependency, to WPCS `2.*`. It is not compatible with WPCS `1.*`. + +The sniffs in WPCS `2.*` are more accurate, so you may see new violations there weren't being reported before, and a reduction in violations for false positives. + +Props: GaryJones, hanifn, paulscreiber, rebeccahum, tomjn. + +### Added +- Switch to using WPCS `2.*`. + - Remove reference to WPCS's `PHPAliases.php`. + - Remove WPCS `1.*`'s `WordPress.VIP` references from rulesets. + - Bump PHPCS minimum required version to 3.3.1. + - Update the WPCS namespace. + - Update ruleset and ruleset test to account for WPCS 2's switch to `WordPress.PHP.IniSet` sniff. + - Update ruleset test for WPCS security sniffs. + - Update `DiscouragedPHPFunctions` group exclusion in `WordPressVIPMinimum` ruleset. + +### Changed +- Downgrade use of file operation functions from Error to Warning: + - `delete` + - `file_put_contents` + - `flock` + - `fputcsv` + - `fputs` + - `fwrite` + - `ftruncate` + - `is_writable` + - `is_writeable` + - `link` + - `rename` + - `symlink` + - `tempnam` + - `touch` + - `unlink` + - `fclose` + - `fopen` + - `file_get_contents` +- Simplify Travis config. +- Switch references from `vip.wordpress.com` to `wpvip.com`. +- Documentation updates. +- Switch development to a `git-flow` workflow. + +## Fixed +- Fixed CS violations in VIPCS code. + +## [1.0.0] - 2019-04-24 + +This release contains many breaking changes. + +It requires PHP `>= 5.6`, PHPCS `3.2.3+`, and WPCS `1.*`. It does not work with WPCS `2.*`. + +Props: GaryJones, rebeccahum, whyisjake, WPprodigy. + +### Reorganisation and Renaming +The sniffs in VIPCS have been reorganised into different categories, with new sniff names and new violation codes. The changes are detailed in the table below. If you reference any of the old violations in your custom ruleset (to change severity, type, or message), or with `// phpcs:ignore` or `// phpcs:disable`, you will need to updates these references to the new violation codes. + +| Original Violation | New Violation | +|--------------------|---------------| +| `WordPressVIPMinimum.Actions.PreGetPostSniff.PreGetPosts` | `WordPressVIPMinimum.Hooks.PreGetPosts.PreGetPosts` | +| `WordPressVIPMinimum.Cache.BatcacheWhitelistedParams.strippedGetParam` | `WordPressVIPMinimum.Performance.BatcacheWhitelistedParams.StrippedGetParam` | +| `WordPressVIPMinimum.Cache.CacheValueOverride.CacheValueOverride` | `WordPressVIPMinimum.Performance.CacheValueOverride.CacheValueOverride` | +| `WordPressVIPMinimum.Cache.LowExpiryCacheTime.LowCacheTime` | `WordPressVIPMinimum.Performance.LowExpiryCacheTime.LowCacheTime` | +| `WordPressVIPMinimum.Classes.DeclarationCompatibility.DeclarationCompatibility` | No change | +| `WordPressVIPMinimum.Classes.RestrictedExtendClasses.wp_cli_wp_cli_command` | `WordPressVIPMinimum.Classes.RestrictedExtendClasses.wp_cli` | +| `WordPressVIPMinimum.Constants.ConstantsRestrictions.ConstantRestrictions` | `WordPressVIPMinimum.Constants.RestrictedConstants.DefiningRestrictedConstant`
`WordPressVIPMinimum.Constants.RestrictedConstants.UsingRestrictedConstant` | +| `WordPressVIPMinimum.Constants.ConstantString.NotCheckingConstantName` | No change | +| `WordPressVIPMinimum.Files.IncludingFile.IncludingFile` | `WordPressVIPMinimum.Files.IncludingFile.UsingVariable`
`WordPressVIPMinimum.Files.IncludingFile.UsingCustomConstant`
`WordPressVIPMinimum.Files.IncludingFile.UsingCustomFunction`
`WordPressVIPMinimum.Files.IncludingFile.NotAbsolutePath`
`WordPressVIPMinimum.Files.IncludingFile.ExternalURL`
`WordPressVIPMinimum.Files.IncludingFile.RestrictedConstant` | +| `WordPressVIPMinimum.Files.IncludingNonPHPFile.IncludingSVGCSSFile` | `WordPressVIPMinimum.Files.IncludingNonPHPFile.IncludingSVGCSSFile` | +| `WordPressVIPMinimum.Files.IncludingNonPHPFile.IncludingNonPHPFile` | `WordPressVIPMinimum.Files.IncludingNonPHPFile.IncludingNonPHPFile` | +| `WordPressVIPMinimum.Filters.AlwaysReturn.voidReturn` | `WordPressVIPMinimum.Hooks.AlwaysReturnInFilter.VoidReturn` | +| `WordPressVIPMinimum.Filters.AlwaysReturn.missingReturnStatement` | `WordPressVIPMinimum.Hooks.AlwaysReturnInFilter.MissingReturnStatement` | +| `WordPressVIPMinimum.Filters.RestrictedHook.UploadMimes` | `WordPressVIPMinimum.Hooks.RestrictedHooks.upload_mimes_upload_mimes` | +| `WordPressVIPMinimum.Filters.RestrictedHook.HighTimeout` | `WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_http_request_args`
`WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_http_request_timeout` | +| `WordPressVIPMinimum.Functions.CheckReturnValue.CheckReturnValue` | `WordPressVIPMinimum.Functions.CheckReturnValue.DirectFunctionCall`
`WordPressVIPMinimum.Functions.CheckReturnValue.NonCheckedVariable` | +| `WordPressVIPMinimum.Functions.CreateFunction.CreateFunction` | `WordPressVIPMinimum.Functions.RestrictedFunctions.create_function_create_function` | +| `WordPressVIPMinimum.Functions.DynamicCalls.DynamicCalls` | No change | +| `WordPressVIPMinimum.Functions.StripTags.StripTagsOneParameter` | No change | +| `WordPressVIPMinimum.Functions.StripTags.StripTagsTwoParameters` | No change | +| `WordPressVIPMinimum.JS.DangerouslySetInnerHTML.dangerouslySetInnerHTML` | `WordPressVIPMinimum.JS.DangerouslySetInnerHTML.Found` | +| `WordPressVIPMinimum.JS.HTMLExecutingFunctions.html` | No change | +| `WordPressVIPMinimum.JS.HTMLExecutingFunctions.append` | No change | +| `WordPressVIPMinimum.JS.HTMLExecutingFunctions.write` | No change | +| `WordPressVIPMinimum.JS.HTMLExecutingFunctions.writeln` | No change | +| `WordPressVIPMinimum.JS.InnerHTML.innerHTML` | `WordPressVIPMinimum.JS.InnerHTML.Found` | +| `WordPressVIPMinimum.JS.StringConcat.StringConcatNext` | `WordPressVIPMinimum.JS.StringConcat.Found` | +| `WordPressVIPMinimum.JS.StrippingTags.VulnerableTagStripping` | No change | +| `WordPressVIPMinimum.JS.Window.VarAssignment` | No change | +| `WordPressVIPMinimum.JS.Window.location` | No change | +| `WordPressVIPMinimum.JS.Window.name` | No change | +| `WordPressVIPMinimum.JS.Window.status` | No change | +| `WordPressVIPMinimum.Plugins.Zoninator.Zoninator` | `WordPressVIPMinimum.Compatibility.Zoninator.RequiresRESTAPI` | +| `WordPressVIPMinimum.TemplatingEngines.UnescapedOutputMustache.{{{` | `WordPressVIPMinimum.Security.Mustache.OutputNotation` | +| `WordPressVIPMinimum.TemplatingEngines.UnescapedOutputMustache.{{&` | `WordPressVIPMinimum.Security.Mustache.VariableNotation` | +| `WordPressVIPMinimum.TemplatingEngines.UnescapedOutputMustache.delimeterChange` | `WordPressVIPMinimum.Security.Mustache.DelimiterChange` | +| `WordPressVIPMinimum.TemplatingEngines.UnescapedOutputMustache.SafeString` | `WordPressVIPMinimum.Security.Mustache.SafeString` | +| `WordPressVIPMinimum.TemplatingEngines.UnescapedOutputTwig.autoescape false` | `WordPressVIPMinimum.Security.Twig.AutoescapeFalse` | +| `WordPressVIPMinimum.TemplatingEngines.UnescapedOutputTwig.raw` | `WordPressVIPMinimum.Security.Twig.RawFound` | +| `WordPressVIPMinimum.TemplatingEngines.UnescapedOutputUnderscorejs.<%=` | `WordPressVIPMinimum.Security.Underscorejs.OutputNotation` | +| `WordPressVIPMinimum.TemplatingEngines.UnescapedOutputUnderscorejs.interpolate` | `WordPressVIPMinimum.Security.Underscorejs.InterpolateFound` | +| `WordPressVIPMinimum.TemplatingEngines.UnescapedOutputVuejs.v-html` | `WordPressVIPMinimum.Security.Vuejs.Found` | +| `WordPressVIPMinimum.Variables.ServerVariables.BasicAuthentication` | No change | +| `WordPressVIPMinimum.Variables.ServerVariables.UserControlledHeaders` | No change | +| `WordPressVIPMinimum.Variables.VariableAnalysis.VariableRedeclaration` | No change | +| `WordPressVIPMinimum.Variables.VariableAnalysis.UndefinedVariables` | `WordPressVIPMinimum.Variables.VariableAnalysis.UndefinedVariable` | +| `WordPressVIPMinimum.Variables.VariableAnalysis.$...` | `WordPressVIPMinimum.Variables.VariableAnalysis.SelfInsideClosure`
`WordPressVIPMinimum.Variables.VariableAnalysis.SelfOutsideClass`
`WordPressVIPMinimum.Variables.VariableAnalysis.StaticInsideClosure`
`WordPressVIPMinimum.Variables.VariableAnalysis.StaticOutsideClass` | +| `WordPressVIPMinimum.Variables.VariableAnalysis.UnusedVariable` | No change | +| `WordPressVIPMinimum.VIP.ErrorControl.ErrorControl` | Replaced with `Generic.PHP.NoSilencedErrors` | +| `WordPressVIPMinimum.VIP.EscapingVoidReturnFunctions.escapingVoidReturningFunction` | `WordPressVIPMinimum.Security.EscapingVoidReturnFunctions.Found` | +| `WordPressVIPMinimum.VIP.ExitAfterRedirect.NoExitInConditional` | `WordPressVIPMinimum.Security.ExitAfterRedirect.NoExitInConditional` | +| `WordPressVIPMinimum.VIP.ExitAfterRedirect.NoExit` | `WordPressVIPMinimum.Security.ExitAfterRedirect.NoExit` | +| `WordPressVIPMinimum.VIP.FetchingRemoteData.fileGetContentsUknown` | `WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown` | +| `WordPressVIPMinimum.VIP.FetchingRemoteData.fileGetContentsRemoteFile` | `WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsRemoteFile` | +| `WordPressVIPMinimum.VIP.FlushRewriteRules.FlushRewriteRules` | Replaced with `WordPressVIPMinimum.Functions.RestrictedFunctions.flush_rewrite_rules_flush_rewrite_rules` and `WordPressVIPMinimum.Functions.RestrictedFunctions.flush_rewrite_rules_flush_rewrite_rules` | +| `WordPressVIPMinimum.VIP.MergeConflict.HEAD` | `WordPressVIPMinimum.MergeConflict.MergeConflict.Start` | +| `WordPressVIPMinimum.VIP.MergeConflict.DELIMITER` | `WordPressVIPMinimum.MergeConflict.MergeConflict.End`
`WordPressVIPMinimum.MergeConflict.MergeConflict.Separator` | +| `WordPressVIPMinimum.VIP.PHPFilterFunctions.MissingThirdParameter` | `WordPressVIPMinimum.Security.PHPFilterFunctions.MissingThirdParameter` | +| `WordPressVIPMinimum.VIP.PHPFilterFunctions.RestrictedFilter` | `WordPressVIPMinimum.Security.PHPFilterFunctions.RestrictedFilter` | +| `WordPressVIPMinimum.VIP.PHPFilterFunctions.MissingSecondParameter` | `WordPressVIPMinimum.Security.PHPFilterFunctions.MissingSecondParameter` | +| `WordPressVIPMinimum.VIP.ProperEscapingFunction.hrefSrcEscUrl` | `WordPressVIPMinimum.Security.ProperEscapingFunction.hrefSrcEscUrl` | +| `WordPressVIPMinimum.VIP.ProperEscapingFunction.htmlAttrNotByEscHTML` | `WordPressVIPMinimum.Security.ProperEscapingFunction.htmlAttrNotByEscHTML` | +| `WordPressVIPMinimum.VIP.RegexpCompare.compare_compare` | `WordPressVIPMinimum.Performance.RegexCompare.compare_compare` | +| `WordPressVIPMinimum.VIP.RegexpCompare.compare_meta_compare` | `WordPressVIPMinimum.Performance.RegexCompare.compare_meta_compare` | +| `WordPressVIPMinimum.VIP.RemoteRequestTimeout.timeout_timeout` | `WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.wp_cache_get_multi.wp_cache_get_multi` | `WordPressVIPMinimum.Functions.RestrictedFunctions.wp_cache_get_multi_wp_cache_get_multi` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.opcache_opcache_reset` | `WordPressVIPMinimum.Functions.RestrictedFunctions.opcache_opcache_reset` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.opcache_opcache_invalidate` | `WordPressVIPMinimum.Functions.RestrictedFunctions.opcache_opcache_invalidate` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.opcache_opcache_compile_file` | `WordPressVIPMinimum.Functions.RestrictedFunctions.opcache_opcache_compile_file` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.config_settings_opcache_is_script_cached` | `WordPressVIPMinimum.Functions.RestrictedFunctions.config_settings_opcache_is_script_cached` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.config_settings_opcache_get_status` | `WordPressVIPMinimum.Functions.RestrictedFunctions.config_settings_opcache_get_status` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.config_settings_opcache_get_configuration` | `WordPressVIPMinimum.Functions.RestrictedFunctions.config_settings_opcache_get_configuration` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.get_super_admins_get_super_admins` | `WordPressVIPMinimum.Functions.RestrictedFunctions.get_super_admins_get_super_admins` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.internal_wpcom_vip_irc` | `WordPressVIPMinimum.Functions.RestrictedFunctions.internal_wpcom_vip_irc` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.rewrite_rules_flush_rewrite_rules` | `WordPressVIPMinimum.Functions.RestrictedFunctions.flush_rewrite_rules_flush_rewrite_rules` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.attachment_url_to_postid_attachment_url_to_postid` | `WordPressVIPMinimum.Functions.RestrictedFunctions.attachment_url_to_postid_attachment_url_to_postid` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.dbDelta_dbDelta` | `WordPressVIPMinimum.Functions.RestrictedFunctions.dbDelta_dbDelta` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.switch_to_blog_switch_to_blog` | `WordPressVIPMinimum.Functions.RestrictedFunctions.switch_to_blog_switch_to_blog` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.get_page_by_title_get_page_by_title` | `WordPressVIPMinimum.Functions.RestrictedFunctions.get_page_by_title_get_page_by_title` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.url_to_postid_url_to_postid` | `WordPressVIPMinimum.Functions.RestrictedFunctions.url_to_postid_url_to_postid` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.url_to_postid_url_to_post_id` | Removed | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.custom_role_add_role` | `WordPressVIPMinimum.Functions.RestrictedFunctions.custom_role_add_role` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.user_meta_get_user_meta` | `WordPressVIPMinimum.Functions.RestrictedFunctions.user_meta_get_user_meta` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.user_meta_update_user_meta` | `WordPressVIPMinimum.Functions.RestrictedFunctions.user_meta_update_user_meta` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.user_meta_delete_user_meta` | `WordPressVIPMinimum.Functions.RestrictedFunctions.user_meta_delete_user_meta` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.user_meta_add_user_meta` | `WordPressVIPMinimum.Functions.RestrictedFunctions.user_meta_add_user_meta` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.term_exists_term_exists` | `WordPressVIPMinimum.Functions.RestrictedFunctions.term_exists_term_exists` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.count_user_posts_count_user_posts` | `WordPressVIPMinimum.Functions.RestrictedFunctions.count_user_posts_count_user_posts` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.wp_old_slug_redirect_wp_old_slug_redirect` | `WordPressVIPMinimum.Functions.RestrictedFunctions.wp_old_slug_redirect_wp_old_slug_redirect` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.get_adjacent_post_get_adjacent_post` | `WordPressVIPMinimum.Functions.RestrictedFunctions.get_adjacent_post_get_adjacent_post` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.get_adjacent_post_get_previous_post` | `WordPressVIPMinimum.Functions.RestrictedFunctions.get_adjacent_post_get_previous_post` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.get_adjacent_post_get_previous_post_link` | `WordPressVIPMinimum.Functions.RestrictedFunctions.get_adjacent_post_get_previous_post_link` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.get_adjacent_post_get_next_post` | `WordPressVIPMinimum.Functions.RestrictedFunctions.get_adjacent_post_get_next_post` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.get_adjacent_post_get_next_post_link` | `WordPressVIPMinimum.Functions.RestrictedFunctions.get_adjacent_post_get_next_post_link` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.get_intermediate_image_sizes_get_intermediate_image_sizes` | `WordPressVIPMinimum.Functions.RestrictedFunctions.get_intermediate_image_sizes_get_intermediate_image_sizes` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.wp_is_mobile_wp_is_mobile` | `WordPressVIPMinimum.Functions.RestrictedFunctions.wp_is_mobile_wp_is_mobile` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.wp_mail_wp_mail` | `WordPressVIPMinimum.Functions.RestrictedFunctions.wp_mail_wp_mail` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.wp_mail_mail` | `WordPressVIPMinimum.Functions.RestrictedFunctions.wp_mail_mail` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.is_multi_author_is_multi_author` | `WordPressVIPMinimum.Functions.RestrictedFunctions.is_multi_author_is_multi_author` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.advanced_custom_fields_the_sub_field` | `WordPressVIPMinimum.Functions.RestrictedFunctions.advanced_custom_fields_the_sub_field` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.advanced_custom_fields_the_field` | `WordPressVIPMinimum.Functions.RestrictedFunctions.advanced_custom_fields_the_field` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.wp_remote_get_wp_remote_get` | `WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.cookies_setcookie` | `WordPressVIPMinimum.Functions.RestrictedFunctions.cookies_setcookie` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.get_posts_get_posts` | `WordPressVIPMinimum.Functions.RestrictedFunctions.get_posts_get_posts` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.get_posts_wp_get_recent_posts` | `WordPressVIPMinimum.Functions.RestrictedFunctions.get_posts_wp_get_recent_posts` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.get_posts_get_children` | `WordPressVIPMinimum.Functions.RestrictedFunctions.get_posts_get_children` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.wpcom_vip_get_term_link_wpcom_vip_get_term_link` | `WordPressVIPMinimum.Functions.RestrictedFunctions.wpcom_vip_get_term_link_wpcom_vip_get_term_link` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.wpcom_vip_get_term_by_wpcom_vip_get_term_by` | `WordPressVIPMinimum.Functions.RestrictedFunctions.wpcom_vip_get_term_by_wpcom_vip_get_term_by` | +| `WordPressVIPMinimum.VIP.RestrictedFunctions.wpcom_vip_get_category_by_slug_wpcom_vip_get_category_by_slug` | `WordPressVIPMinimum.Functions.RestrictedFunctions.wpcom_vip_get_category_by_slug_wpcom_vip_get_category_by_slug` | +| `WordPressVIPMinimum.VIP.Robotstxt.RobotstxtSniff` | `WordPressVIPMinimum.Hooks.RestrictedHooks.robotstxt_do_robotstxt`
`WordPressVIPMinimum.Hooks.RestrictedHooks.robotstxt_robots_txt` | +| `WordPressVIPMinimum.VIP.StaticStrreplace.StaticStrreplace` | `WordPressVIPMinimum.Security.StaticStrreplace.StaticStrreplace` | +| `WordPressVIPMinimum.VIP.TaxonomyMetaInOptions.PossibleTermMetaInOptions` | `WordPressVIPMinimum.Performance.TaxonomyMetaInOptions.PossibleTermMetaInOptions` | +| `WordPressVIPMinimum.VIP.WPQueryParams.suppressFiltersTrue` | `WordPressVIPMinimum.Performance.WPQueryParams.SuppressFiltersTrue` | +| `WordPressVIPMinimum.VIP.WPQueryParams.post__not_in` | `WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn` | + +### Added +- New violations: + - `WordPressVIPMinimum.Functions.RestrictedFunctions.chmod_chgrp` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.chmod_chown` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.chmod_chmod` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.chmod_lchgrp` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.chmod_lchown` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.directory_mkdir` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.directory_rmdir` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_delete` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_file_put_contents` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_flock` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_fputcsv` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_fputs` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_ftruncate` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_fwrite` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_is_writable` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_is_writeable` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_link` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_rename` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_symlink` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_tempnam` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_touch` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_unlink` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_abort` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_cache_expire` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_cache_limiter` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_commit` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_create_id` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_decode` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_destroy` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_encode` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_gc` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_get_cookie_params` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_id` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_is_registered` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_module_name` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_name` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_regenerate_id` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_register_shutdown` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_register` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_reset` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_save_path` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_set_cookie_params` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_set_save_handler` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_start` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_status` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_unregister` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_unset` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.session_session_write_close` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.site_option_add_site_option` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.site_option_delete_site_option` + - `WordPressVIPMinimum.Functions.RestrictedFunctions.site_option_update_site_option` + - `WordPressVIPMinimum.Performance.NoPaging.nopaging_nopaging` + - `WordPressVIPMinimum.Performance.OrderByRand.orderby_orderby` + - `WordPressVIPMinimum.UserExperience.AdminBarRemoval.HidingDetected` + - `WordPressVIPMinimum.UserExperience.AdminBarRemoval.RemovalDetected` + - `WordPressVIPMinimum.Variables.RestrictedVariables.user_meta__wpdb__users` + - `WordPressVIPMinimum.Variables.RestrictedVariables.user_meta__wpdb__usermeta` + - `WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___COOKIE` + - `WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___SERVER__HTTP_USER_AGENT__` + - `WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___SERVER__REMOTE_ADDR__` + - `WordPressVIPMinimum.Variables.RestrictedVariables.session___session` +- `WordPress-VIP-Go` ruleset test. +- docs about ruleset tests. +- XSD reference and ruleset validation. +- `phpcodesniffer-composer-installer` plugin. +- Copy `has_html_open_tag()` from WPCS. +- Copy `AbstractVariableRestrictionsSniff` from WPCS. + +### Changed +- Switch from PHPCS 2.* class names to PHPCS 3.* namespaced classes. +- Refactor all sniffs to extend `VIPCS\Sniff`, which is an extension of `WordPress\Sniff`. +- Tidied up: + - unused imports + - unused local variables + - unused parameters + - unused private field + - duplicate array keys + - redundant self-assignment + - assignment in conditions + - not returning void function calls + - undefined class fields + - strict comparisons + - missing scope keywords + - parentheses to clarify one specific conditional + - consolidate multiple `isset()` calls + - consolidate positive nested `if()`’s + - difference in case for function calls + - simplified return statements + - switched to `__DIR__` + - switched FQCN to import statements + - use static property + - use more performant `strpos()` instead of `substr()` + - split or remove `else` / `elseif` workflows for lower complexity and more comprehension + - misuse of `array_push()` + - misuse of `array_values()` + - misuse of `in_array()` + - useless `return` + - redundant `continue` + - comments that were naming parameters + - default assignments of `null` to class properties + - function parameters that match default arg values + - redundant parentheses +- Ruleset Test improvements: + - Move mostly duplicate `PHPCS_Ruleset_Test` classes into new `RulesetTest` class. + - Refactor new class: + - Accept a ruleset name in the constructor + - Change public method from `run()` to `passes()`. + - Break out logic into smaller private methods to make the logic more self-documenting. + - Refactor variable names in some methods. + - Decode JSON into objects, not arrays + - Fix incorrect reference to local `$expected` to `$this->expected`. Somehow, this was still working regardless. + - Fix bug where it doesn't catch proper number of errors/warnings on a line basis due to order of operations of incrementating after assignment. + - Add further documentation. + - Change ruleset test class usage, including adding the name to the "tests passed!" message. + - Replaced WPCS whitelisting `// XSS OK` comments in this files with `// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped` comments. + - Change naming of tests from "integration test" to "ruleset tests", to make it more intuitive exactly what these are (composer script, Travis, bin filename). +- Improve `addError()` and `addWarning()` calls +- Remove `Generic.NamingConventions.ConstructorName.OldStyle` from `WordPress-VIP-Go` ruleset +- Travis: Restrict PHPUnit versions to match PHPCS +- Travis: Use `7.4snapshot` instead of nightly, switch from Trusty to Xenial, remove `sudo: false`. +- `EscapingVoidReturnFunctions`: Fix docs and improve logic +- `AlwaysReturnSniff`: trigger errors instead of warnings, don't give violation for when callback args is passed by reference +- Change `exec()` and `shell_exec()` to be flagged as Error. +- Disallow long array syntax in VIPCS sniff code. +- Remove a `WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition` exclusion in the PHPCS config for VIPCS itself. +- Update docs. + +### Fixed +- Bumped PHPCompatibility `testVersion` to match PHP requirement. +- Silence `Generic.PHP.DisallowShortOpenTag.EchoFound` for `WordPress-VIP-Go` ruleset: ` + +
  • This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
  • +
  • You may copy and distribute verbatim copies of the Program’s source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
  • +
  • You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: +
      +
    1. You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
    2. +
    3. You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
    4. +
    5. If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
    6. +
    + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
  • +
  • You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: +
      +
    1. Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
    2. +
    3. Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
    4. +
    5. Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
    6. +
    +
  • +
  • You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
  • +
  • You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
  • +
  • Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
  • +
  • If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
  • +
  • If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
  • +
  • The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
  • +
  • If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
  • +
  • BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
  • +
  • IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
  • + + + +Included Files +========================== + +This project includes: + +- [WordPress-Coding-Standards](https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards), which is Copyright © 2009 John Godley and contributors. Released under the MIT license https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/blob/develop/LICENSE +- [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer), Copyright © 2012, Squiz Pty Ltd (ABN 77 084 670 600). Released under the following license: https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt diff --git a/vendor/automattic/vipwpcs/WordPress-VIP-Go/ruleset-test.inc b/vendor/automattic/vipwpcs/WordPress-VIP-Go/ruleset-test.inc new file mode 100644 index 00000000..61e56955 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPress-VIP-Go/ruleset-test.inc @@ -0,0 +1,574 @@ + 999, // Warning + Message. + 'posts_per_page' => 1, // OK. + 'posts_per_page' => '1', // OK. +); +_query_posts( 'posts_per_page=999' ); // Warning + Message. +$query_args['posts_per_page'] = 999; // Warning + Message. +$query_args['posts_per_page'] = 1; // OK. + +// WordPressVIPMinimum.Hooks.RestrictedHooks.upload_mimes +add_filter( 'upload_mimes', 'foo' ); // Warning. + +// WordPressVIPMinimum.Security.PHPFilterFunctions +filter_var( $url ); // Warning. +filter_var_array( $array ); // Warning. +filter_input_array( $array ); // Warning. +filter_var( $url, FILTER_SANITIZE_URL ); // Ok. +filter_input( INPUT_GET, 'foo' ); // Warning. +filter_input( INPUT_GET, 'foo', FILTER_SANITIZE_STRING ); // Ok. +filter_var( $url, FILTER_DEFAULT ); // Warning. +filter_var_array( $array, FILTER_UNSAFE_RAW ); // Warning. +filter_var_array( $array, FILTER_SANITIZE_STRING ); // Ok. +filter_input_array( $array,FILTER_SANITIZE_STRING ); // Ok. +filter_input( INPUT_GET, 'foo', FILTER_DEFAULT ); // Warning. + + + + +// WordPressVIPMinimum.Functions.RestrictedFunctions.wp_mail_wp_mail +wp_mail(); // Warning. +mail(); // Warning. + +// WordPressVIPMinimum.Functions.RestrictedFunctions.attachment_url_to_postid_attachment_url_to_postid +wpcom_vip_attachment_url_to_postid( $url ); // Ok. +attachment_url_to_postid( $url ); // Warning + Message. + +// WordPressVIPMinimum.Functions.RestrictedFunctions.get_adjacent_post_get_adjacent_post +wpcom_vip_get_adjacent_post(); // Ok. +get_adjacent_post(); // Warning. +get_previous_post(); // Warning. +get_next_post(); // Warning. +get_previous_post_link(); // Warning. +get_next_post_link(); // Warning. + +// WordPressVIPMinimum.Functions.RestrictedFunctions.get_page_by_title_get_page_by_title +wpcom_vip_get_page_by_title(); // Ok. +get_page_by_title( $page_title ); // Warning + Message. + +// WordPressVIPMinimum.Functions.RestrictedFunctions.get_posts_get_children +get_child(); // Ok. +get_children(); // Warning + Message. + +// WordPressVIPMinimum.Functions.RestrictedFunctions.get_posts_get_posts +get_posts(); // Warning. +get_post( 123 ); // Ok. + +// WordPressVIPMinimum.Functions.RestrictedFunctions.get_posts_wp_get_recent_posts +wp_get_recent_posts(); // Warning. + +// WordPressVIPMinimum.Functions.RestrictedFunctions.url_to_postid_url_to_postid +wpcom_vip_url_to_postid( $url ); // Ok. +url_to_postid( $url ); // Warning + Message. + +// WordPressVIPMinimum.Functions.RestrictedFunctions.wp_old_slug_redirect_wp_old_slug_redirect +wpcom_vip_old_slug_redirect(); // Ok. +wp_old_slug_redirect(); // Warning. + +// WordPress.CodeAnalysis.AssignmentInCondition.Found +if ($a = 123) { // Warning. +} + +// WordPress.PHP.DiscouragedPHPFunctions.urlencode_urlencode +urlencode(); // Warning. +rawurlencode(); // Ok. + +// WordPress.PHP.DontExtract +extract( array( 'a' => 1 ) ); // Error. +$obj->extract(); // Ok. + +// WordPress.PHP.StrictComparisons.LooseComparison +true == $true; // Warning. +false === $true; // Ok. + +// WordPress.PHP.StrictInArray.MissingTrueStrict +in_array( 1, array( '1', 1, true ), true ); // Ok. +in_array( 1, array( '1', 1, true ) ); // Warning. +in_array( 1, array( '1', 1, true ), false ); // Warning. +array_search( 1, $array, false ); // Warning. +array_keys( array( '1', 1, true ), 'my_key' ); // Warning. + +// WordPress.Security.EscapeOutput.UnsafePrintingFunction +_e( $some_nasty_var ); // Error. +_ex( $some_nasty_var, 'context' ); // Error. +echo esc_html_x( 'Something', 'context' ); // Ok. +echo esc_html_x( $some_nasty_var, 'context' ); // Ok. + +// WordPress.WP.GlobalVariablesOverride.OverrideProhibited +global $wpdb; +$wpdb = 'test'; // Error. +$GLOBALS['domain']['subkey'] = 'something else'; // Error. + +// WordPress.WP.EnqueuedResources.NonEnqueuedScript +echo wp_kses( ' + + + +'; // Warning + Message. + +// WordPressVIPMinimum.Performance.LowExpiryCacheTime.LowCacheTime +wp_cache_set( 'test', $data, $group, 100 ); // Warning. +wp_cache_add( 'test', $data, $group, 2*MINUTE_IN_SECONDS ); // Warning. +wp_cache_replace( $testing, $data, '', 1.5 * MINUTE_IN_SECONDS ); // Warning. + +// WordPressVIPMinimum.Files.IncludingFile +include_once ( MY_CONSTANT . "my_file.php" ); // Warning. +require_once( custom_function( 'test_file.php' ) ); // Warning. +require_once "my_file.php"; // Warning. +require '../../my_file.php'; // Warning. +include("http://www.google.com/bad_file.php"); // Warning. + +// VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable +function foo_bar_bar() { + $b . 'test'; // Warning. +} + +// VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable +function foo_bar_foo() { + $a = 'Hello'; // OK. Unused variables warning silenced. +} + +// WordPressVIPMinimum.UserExperience.AdminBarRemoval +add_filter( 'show_admin_bar', '__return_false' ); // Warning. +add_filter( 'show_admin_bar', '__return_true' ); // Ok. +show_admin_bar( false ); // Warning. +show_admin_bar( true ); // Ok. +add_filter( 'show_admin_bar', 'my_own_return_false' ); // Warning. +echo ''; +?> '; // Error. +echo ''; // Error. +echo ''; // OK. +?>Hello +Hey + + array( 1, 2, 3 ), // Warning. +] ); + +/* Rules that are being silenced and should NOT be flagged. */ + +// WordPress.DB.SlowDBQuery.slow_db_query_meta_key +$query = new WP_Query( ['meta_key' => 'foo' ] ); // Ok. +$args = 'foo=bar&meta_key=foo'; // Ok. + +// WordPressVIPMinimum.Performance.BatcacheWhitelistedParams +if ( isset( $_GET['migSource'] ) && wp_verify_nonce( sanitize_text_field( $_GET['migSource'] ) ) ) { + $test = sanitize_text_field( $_GET['migSource'] ); // Ok. +} + + + + + + + + + + + + + + + + +// WordPressVIPMinimum.Functions.RestrictedFunctions.site_option_delete_site_option +delete_site_option( $foo ); // Ok. + +// WordPressVIPMinimum.Functions.RestrictedFunctions.site_option_update_site_option +update_site_option( $bar, $foo, true ); // Ok. + +// WordPressVIPMinimum.Functions.RestrictedFunctions.site_option_add_site_option +add_site_option( 'foo', $bar ); // Ok. + +// Generic.PHP.DisallowShortOpenTag.EchoFound +?> My term link'; // Error. + +// WordPressVIPMinimum.Functions.DynamicCalls +$my_notokay_func = 'extract'; +$my_notokay_func(); // Error. + +// WordPressVIPMinimum.Functions.RestrictedFunctions + +opcache_reset(); // Error. +opcache_invalidate( 'test_script.php' ); // Error. +opcache_compile_file( $var ); // Error. +opcache_is_script_cached( 'test_script.php' ); // Error. +opcache_get_status(); // Error. +opcache_get_configuration(); // Error. +get_super_admins(); // OK. +wpcom_vip_irc(); // Error. +flush_rewrite_rules(); // Error. +$wp_rewrite->flush_rules(); // Error. +\add_role(); // Error. +term_exists(); // Error. +count_user_posts(); // Error. +get_intermediate_image_sizes(); // Error. +wp_is_mobile(); // Error. +session_abort(); // Error. +session_cache_expire(); // Error. +session_cache_limiter(); // Error. +session_commit(); // Error. +session_create_id(); // Error. +session_decode(); // Error. +session_destroy(); // Error. +session_encode(); // Error. +session_gc(); // Error. +session_get_cookie_params(); // Error. +session_id(); // Error. +session_is_registered(); // Error. +session_module_name(); // Error. +session_name(); // Error. +session_regenerate_id(); // Error. +session_register_shutdown(); // Error. +session_register(); // Error. +session_reset(); // Error. +session_save_path(); // Error. +session_set_cookie_params(); // Error. +session_set_save_handler(); // Error. +session_start(); // Error. +session_status(); // Error. +session_unregister(); // Error. +session_unset(); // Error. +session_write_close(); // Error. +delete(); // Warning. +file_put_contents( $file, $text, FILE_APPEND ); // Warning. +while ( $count > $loop ) { + if ( flock( $fp, LOCK_EX ) ) { // Warning. + fwrite( $fp, $text ); // Warning. + } +} +fputcsv(); // Warning. +fputs(); // Warning. +ftruncate(); // Warning. +is_writable(); // Warning. +is_writeable(); // Warning. +link(); // Warning. +rename(); // Warning. +symlink(); // Warning. +tempnam(); // Warning. +touch(); // Warning. +unlink(); // Warning. +mkdir(); // Error. +rmdir(); // Error. +chgrp(); // Error. +chown(); // Error. +chmod(); // Error. +lchgrp(); // Error. +lchown(); // Error. +the_sub_field( 'field' ); // Warning. +the_field( 'field' ); // Warning. +wp_remote_get( $url ); // Warning. +get_posts(); // Warning. +function test_function( $a, $b ) { // OK. Unused variables warning silenced. + return create_function( '$a, $b', 'return ( $b / $a ); '); // Warning. +} +wpcom_vip_get_term_link(); // Warning. +wpcom_vip_get_term_by(); // Warning. +wpcom_vip_get_category_by_slug(); // Warning. + +// WordPressVIPMinimum.Functions.StripTagsSniff +strip_tags( 'Test', $text ); // Warning. + +// WordPressVIPMinimum.Hooks.AlwaysReturnInFilter +function bad_example_function_thing() { // Error. + if ( 1 === 0 ) { + if ( 1 === 1 ) { + return 'ahoj'; + } else { + return 'hello'; + } + } +} +add_filter( 'bad_example_function_filter', 'bad_example_function_thing' ); +add_filter( 'another_bad_example_closure', function() { // Error. + return; +} ); + +// WordPressVIPMinimum.Hooks.PreGetPosts +add_action( 'pre_get_posts', function( $wp_query ) { + if ( ! $wp_query->is_search() ) { + $wp_query->set( 'cat', '-5' ); // Warning. + } +} ); + +// WordPressVIPMinimum.Hooks.RestrictedHooks +add_action( 'http_request_timeout', 'bad_example_function' ); // Warning. +add_filter('http_request_args', 'bad_example_function' ); // Warning. +add_action( 'do_robotstxt', 'my_do_robotstxt'); // Warning. +add_filter( 'robots_txt', function() { // Warning. + return 'test'; +} ); + +// WordPressVIPMinimum.Performance.CacheValueOverride +$bad_wp_users = wp_cache_get( md5( self::CACHE_KEY . '_wp_users'), self::CACHE_GROUP ); +$bad_wp_users = false; // Error. + +// WordPressVIPMinimum.Performance.NoPaging +$args = array( + 'nopaging' => true, // Error. +); +_query_posts( 'nopaging=true' ); // Error. + +// WordPressVIPMinimum.Performance.OrderByRand +$args = array( + "orderby" => "RAND", // Error. +); +$query_args['orderby'] = 'rand'; // Error. + +// WordPressVIPMinimum.Performance.RegexpCompare +$query_args = array( + 'posts_per_page' => 1, + 'post_status' => 'draft', + 'meta_compare' => 'REGEXP', // Error. +); +$query_args = [ + 'post_status' => 'publish', + 'meta_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + [ + 'compare' => 'REGEXP', // Error. + ] + ] +]; + +// WordPressVIPMinimum.Performance.RemoteRequestTimeout +wp_remote_post( $obj->endpoint, array( + 'method' => 'POST', + 'timeout' => 45, // Error. + 'httpversion' => '1.1', + 'blocking' => false, + 'body' => wp_json_encode( $obj->logs, JSON_UNESCAPED_SLASHES ), + ) +); + +// WordPressVIPMinimum.Performance.TaxonomyMetaInOptions +get_option( "taxonomy_rating_$obj->term_id" ); // Warning. +update_option( 'taxonomy_rating_' . $category_id ); // Warning. + +// WordPressVIPMinimum.Performance.WPQueryParams +$query_args = array( + 'suppress_filters' => true, // Error. +); + +// WordPressVIPMinimum.Security.EscapingVoidReturnFunctions.Found +esc_js( _deprecated_argument() ); // Error. +esc_js( _deprecated_constructor() ); // Error. +esc_js( _deprecated_file( 'filename' ) ); // Error. +esc_js( _deprecated_function() ); // Error. +esc_js( _deprecated_hook() ); // Error. +esc_js( _doing_it_wrong() ); // Error. +esc_html( printf( 'foo', [] ) ); // Error. +esc_attr( user_error( 'foo', '' ) ); // Error. +esc_attr( vprintf( 'foo', [] ) ); // Error. +esc_attr( wp_die( 'foo' ) ); // Error. +esc_attr( wp_dropdown_pages() ); // Error. + +// WordPressVIPMinimum.Security.ExitAfterRedirect +function redirect_test() { + wp_safe_redirect( 'https.//vip.wordpress.com' ); // Error. +} +wp_redirect( 'https://vip.wordpress.com' ); // Error. + +// WordPressVIPMinimum.Security.Mustache +echo '{{{data}}}'; // Warning. +?> + + + _.templateSettings = { + interpolate: /\{\{(.+?)\}\}/g" . // Warning. +"}; + "; + +// WordPressVIPMinimum.Security.Vuejs +?>
    +

    +
    +<<<<<<< HEAD // Error. + +>>>>>>> // Error. diff --git a/vendor/automattic/vipwpcs/WordPress-VIP-Go/ruleset-test.php b/vendor/automattic/vipwpcs/WordPress-VIP-Go/ruleset-test.php new file mode 100644 index 00000000..af455144 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPress-VIP-Go/ruleset-test.php @@ -0,0 +1,345 @@ + [ + 50 => 1, + 53 => 1, + 56 => 1, + 72 => 1, + 83 => 1, + 165 => 1, + 180 => 1, + 181 => 1, + 187 => 1, + 188 => 1, + 252 => 1, + 255 => 1, + 256 => 1, + 258 => 1, + 259 => 1, + 318 => 1, + 329 => 1, + 334 => 1, + 337 => 1, + 341 => 1, + 342 => 1, + 346 => 1, + 350 => 1, + 351 => 1, + 352 => 1, + 353 => 1, + 354 => 1, + 355 => 1, + 357 => 1, + 358 => 1, + 359 => 1, + 360 => 1, + 361 => 1, + 362 => 1, + 363 => 1, + 364 => 1, + 365 => 1, + 366 => 1, + 367 => 1, + 368 => 1, + 369 => 1, + 370 => 1, + 371 => 1, + 372 => 1, + 373 => 1, + 374 => 1, + 375 => 1, + 376 => 1, + 377 => 1, + 378 => 1, + 379 => 1, + 380 => 1, + 381 => 1, + 382 => 1, + 383 => 1, + 384 => 1, + 385 => 1, + 386 => 1, + 387 => 1, + 388 => 1, + 389 => 1, + 390 => 1, + 409 => 1, + 410 => 1, + 411 => 1, + 412 => 1, + 413 => 1, + 414 => 1, + 415 => 1, + 431 => 1, + 441 => 1, + 462 => 1, + 466 => 1, + 468 => 1, + 472 => 1, + 474 => 1, + 480 => 1, + 486 => 1, + 494 => 1, + 507 => 1, + 511 => 1, + 512 => 1, + 513 => 1, + 514 => 1, + 515 => 1, + 516 => 1, + 517 => 1, + 518 => 1, + 519 => 1, + 520 => 1, + 521 => 1, + 525 => 1, + 527 => 1, + 545 => 1, + 560 => 1, + 564 => 1, + 565 => 1, + 566 => 1, + 567 => 1, + 572 => 1, + 574 => 1, + ], + 'warnings' => [ + 4 => 1, + 7 => 1, + 10 => 1, + 14 => 1, + 17 => 1, + 20 => 1, + 23 => 1, + 26 => 1, + 29 => 1, + 32 => 1, + 35 => 1, + 38 => 1, + 41 => 1, + 44 => 1, + 47 => 1, + 63 => 1, + 66 => 1, + 85 => 1, + 90 => 1, + 94 => 1, + 95 => 1, + 99 => 1, + 102 => 1, + 103 => 1, + 104 => 1, + 106 => 1, + 108 => 1, + 109 => 1, + 112 => 1, + 118 => 1, + 119 => 1, + 123 => 1, + 127 => 1, + 128 => 1, + 129 => 1, + 130 => 1, + 131 => 1, + 135 => 1, + 139 => 1, + 142 => 1, + 146 => 1, + 150 => 1, + 154 => 1, + 157 => 1, + 161 => 1, + 169 => 1, + 174 => 1, + 175 => 1, + 176 => 1, + 177 => 1, + 191 => 1, + 192 => 1, + 195 => 1, + 196 => 1, + 199 => 1, + 200 => 1, + 201 => 1, + 204 => 1, + 205 => 1, + 206 => 1, + 207 => 1, + 208 => 1, + 212 => 1, + 221 => 1, + 223 => 1, + 225 => 1, + 228 => 1, + 229 => 1, + 230 => 1, + 235 => 1, + 236 => 1, + 237 => 1, + 245 => 1, + 246 => 1, + 247 => 1, + 265 => 1, + 269 => 1, + 273 => 1, + 322 => 1, + 326 => 1, + 332 => 1, + 391 => 1, + 392 => 1, + 394 => 1, + 395 => 1, + 398 => 1, + 399 => 1, + 400 => 1, + 401 => 1, + 402 => 1, + 403 => 1, + 404 => 1, + 405 => 1, + 406 => 1, + 407 => 1, + 408 => 1, + 416 => 1, + 417 => 1, + 418 => 1, + 419 => 1, + 421 => 1, + 423 => 1, + 424 => 1, + 425 => 1, + 428 => 1, + 448 => 1, + 453 => 1, + 454 => 1, + 455 => 1, + 456 => 1, + 502 => 1, + 503 => 1, + 530 => 1, + 533 => 1, + 540 => 1, + 550 => 1, + 556 => 1, + ], + 'messages' => [ + 4 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as delete(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 7 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as file_put_contents(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 10 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as flock(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 14 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as fputcsv(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 17 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as fputs(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 20 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as fwrite(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 23 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as ftruncate(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 26 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as is_writable(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 29 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as is_writeable(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 32 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as link(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 35 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as rename(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 38 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as symlink(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 41 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as tempnam(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 44 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as touch(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 47 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as unlink(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 50 => [ + 'Due to server-side caching, server-side based client related logic might not work. We recommend implementing client side logic in JavaScript instead.', + ], + 53 => [ + 'Due to server-side caching, server-side based client related logic might not work. We recommend implementing client side logic in JavaScript instead.', + ], + 56 => [ + 'Due to server-side caching, server-side based client related logic might not work. We recommend implementing client side logic in JavaScript instead.', + ], + 63 => [ + 'File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as fopen(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/', + ], + 66 => [ + 'file_get_contents() is uncached. If the function is being used to fetch a remote file (e.g. a URL starting with https://), please use wpcom_vip_file_get_contents() to ensure the results are cached. For more details, please see: https://docs.wpvip.com/technical-references/code-quality-and-best-practices/retrieving-remote-data/', + ], + 90 => [ + 'Having more than 100 posts returned per page may lead to severe performance problems.', + ], + 94 => [ + 'Having more than 100 posts returned per page may lead to severe performance problems.', + ], + 95 => [ + 'Having more than 100 posts returned per page may lead to severe performance problems.', + ], + 123 => [ + 'attachment_url_to_postid() is uncached, please use wpcom_vip_attachment_url_to_postid() instead.', + ], + 135 => [ + 'get_page_by_title() is uncached, please use wpcom_vip_get_page_by_title() instead.', + ], + 139 => [ + 'get_children() is uncached and performs a no limit query. Please use get_posts or WP_Query instead. Please see: https://docs.wpvip.com/technical-references/caching/uncached-functions/', + ], + 150 => [ + 'url_to_postid() is uncached, please use wpcom_vip_url_to_postid() instead.', + ], + 191 => [ + 'Scripts should be registered/enqueued via `wp_enqueue_script`. This can improve the site\'s performance due to script concatenation.', + ], + 192 => [ + 'Scripts should be registered/enqueued via `wp_enqueue_script`. This can improve the site\'s performance due to script concatenation.', + ], + 195 => [ + 'Stylesheets should be registered/enqueued via `wp_enqueue_style`. This can improve the site\'s performance due to styles concatenation.', + ], + 196 => [ + 'Stylesheets should be registered/enqueued via `wp_enqueue_style`. This can improve the site\'s performance due to styles concatenation.', + ], + ], +]; + +require __DIR__ . '/../tests/RulesetTest.php'; + +// Run the tests! +$test = new RulesetTest( 'WordPress-VIP-Go', $expected ); +if ( $test->passes() ) { + printf( 'All WordPress-VIP-Go tests passed!' . PHP_EOL ); + exit( 0 ); +} + +exit( 1 ); diff --git a/vendor/automattic/vipwpcs/WordPress-VIP-Go/ruleset.xml b/vendor/automattic/vipwpcs/WordPress-VIP-Go/ruleset.xml new file mode 100644 index 00000000..58386a58 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPress-VIP-Go/ruleset.xml @@ -0,0 +1,273 @@ + + + WordPress VIP Go Coding Standards + + + + + + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + warning + 6 + Removal of admin bar is highly discouraged for user roles of "administrator" and "vip_support" -- if these roles are already excluded, this warning can be ignored. + + + warning + 6 + Hiding of admin bar is highly discouraged for user roles of "administrator" and "vip_support" -- if these roles are already excluded, this warning can be ignored. + + + 6 + + + error + 6 + + + error + 6 + + + + + + + File system operations only work on the `/tmp/` and `wp-content/uploads/` directories. To avoid unexpected results, please use helper functions like `get_temp_dir()` or `wp_get_upload_dir()` to get the proper directory path when using functions such as %s(). For more details, please see: https://docs.wpvip.com/technical-references/vip-go-files-system/local-file-operations/ + + + %s() is uncached. If the function is being used to fetch a remote file (e.g. a URL starting with https://), please use wpcom_vip_file_get_contents() to ensure the results are cached. For more details, please see: https://docs.wpvip.com/technical-references/code-quality-and-best-practices/retrieving-remote-data/ + + + + + + + warning + 10 + + + Having more than 100 posts returned per page may lead to severe performance problems. + + + 10 + + + 10 + + + 7 + + + + + + warning + %s() is uncached, please use wpcom_vip_attachment_url_to_postid() instead. + + + warning + + + warning + + + warning + + + warning + + + warning + + + warning + %s() is uncached, please use wpcom_vip_get_page_by_title() instead. + + + warning + 3 + %s() is uncached and performs a no limit query. Please use get_posts or WP_Query instead. Please see: https://docs.wpvip.com/technical-references/caching/uncached-functions/ + + + 3 + + + 3 + + + warning + %s() is uncached, please use wpcom_vip_url_to_postid() instead. + + + warning + + + + + 1 + + + 1 + + + 3 + + + 3 + + + 3 + + + 3 + + + + 1 + + + warning + 3 + Scripts should be registered/enqueued via `wp_enqueue_script`. This can improve the site's performance due to script concatenation. + + + warning + 3 + Stylesheets should be registered/enqueued via `wp_enqueue_style`. This can improve the site's performance due to styles concatenation. + + + 3 + + + warning + 3 + + + 3 + + + 3 + + + 1 + + + 1 + + + 3 + + + 3 + + + + + 0 + + + + + + + + + + + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/AbstractVariableRestrictionsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/AbstractVariableRestrictionsSniff.php new file mode 100644 index 00000000..e46373a3 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/AbstractVariableRestrictionsSniff.php @@ -0,0 +1,231 @@ +setup_groups() === false ) { + return []; + } + + return [ + \T_VARIABLE, + \T_OBJECT_OPERATOR, + \T_DOUBLE_COLON, + \T_OPEN_SQUARE_BRACKET, + \T_DOUBLE_QUOTED_STRING, + \T_HEREDOC, + ]; + } + + /** + * Groups of variables to restrict. + * + * This method should be overridden in extending classes. + * + * Example: groups => array( + * 'wpdb' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Dont use this one please!', + * 'variables' => array( '$val', '$var' ), + * 'object_vars' => array( '$foo->bar', .. ), + * 'array_members' => array( '$foo['bar']', .. ), + * ) + * ) + * + * @return array + */ + abstract public function getGroups(); + + /** + * Cache the groups. + * + * @return bool True if the groups were setup. False if not. + */ + protected function setup_groups() { + $this->groups_cache = $this->getGroups(); + + if ( empty( $this->groups_cache ) && empty( self::$groups ) ) { + return false; + } + + // Allow for adding extra unit tests. + if ( ! empty( self::$groups ) ) { + $this->groups_cache = array_merge( $this->groups_cache, self::$groups ); + } + + return true; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + * + * @throws \PHP_CodeSniffer\Exceptions\RuntimeException Exception. + */ + public function process_token( $stackPtr ) { + + $token = $this->tokens[ $stackPtr ]; + + $this->excluded_groups = static::merge_custom_array( $this->exclude ); + if ( array_diff_key( $this->groups_cache, $this->excluded_groups ) === [] ) { + // All groups have been excluded. + // Don't remove the listener as the exclude property can be changed inline. + return; + } + + // Check if it is a function not a variable. + if ( \in_array( $token['code'], [ \T_OBJECT_OPERATOR, \T_DOUBLE_COLON ], true ) ) { // This only works for object vars and array members. + $method = $this->phpcsFile->findNext( \T_WHITESPACE, $stackPtr + 1, null, true ); + $possible_parenthesis = $this->phpcsFile->findNext( \T_WHITESPACE, $method + 1, null, true ); + if ( $this->tokens[ $possible_parenthesis ]['code'] === \T_OPEN_PARENTHESIS ) { + return; // So .. it is a function after all ! + } + } + + if ( $this->is_in_isset_or_empty( $stackPtr ) === true ) { + // Checking whether a variable exists is not the same as using it. + return; + } + + foreach ( $this->groups_cache as $groupName => $group ) { + + if ( isset( $this->excluded_groups[ $groupName ] ) ) { + continue; + } + + $patterns = []; + + // Simple variable. + if ( \in_array( $token['code'], [ \T_VARIABLE, \T_DOUBLE_QUOTED_STRING, \T_HEREDOC ], true ) && ! empty( $group['variables'] ) ) { + $patterns = array_merge( $patterns, $group['variables'] ); + $var = $token['content']; + + } + + if ( \in_array( $token['code'], [ \T_OBJECT_OPERATOR, \T_DOUBLE_COLON, \T_DOUBLE_QUOTED_STRING, \T_HEREDOC ], true ) && ! empty( $group['object_vars'] ) ) { + // Object var, ex: $foo->bar / $foo::bar / Foo::bar / Foo::$bar . + $patterns = array_merge( $patterns, $group['object_vars'] ); + + $owner = $this->phpcsFile->findPrevious( [ \T_VARIABLE, \T_STRING ], $stackPtr ); + $child = $this->phpcsFile->findNext( [ \T_STRING, \T_VARIABLE ], $stackPtr ); + $var = implode( '', [ $this->tokens[ $owner ]['content'], $token['content'], $this->tokens[ $child ]['content'] ] ); + + } + + if ( \in_array( $token['code'], [ \T_OPEN_SQUARE_BRACKET, \T_DOUBLE_QUOTED_STRING, \T_HEREDOC ], true ) && ! empty( $group['array_members'] ) ) { + // Array members. + $patterns = array_merge( $patterns, $group['array_members'] ); + + if ( isset( $token['bracket_closer'] ) ) { + $owner = $this->phpcsFile->findPrevious( \T_VARIABLE, $stackPtr ); + $inside = $this->phpcsFile->getTokensAsString( $stackPtr, $token['bracket_closer'] - $stackPtr + 1 ); + $var = implode( '', [ $this->tokens[ $owner ]['content'], $inside ] ); + } + } + + if ( empty( $patterns ) ) { + continue; + } + + $patterns = array_map( [ $this, 'test_patterns' ], $patterns ); + $pattern = implode( '|', $patterns ); + $delim = ( $token['code'] !== \T_OPEN_SQUARE_BRACKET && $token['code'] !== \T_HEREDOC ) ? '\b' : ''; + + if ( $token['code'] === \T_DOUBLE_QUOTED_STRING || $token['code'] === \T_HEREDOC ) { + $var = $token['content']; + } + + if ( empty( $var ) || preg_match( '#(' . $pattern . ')' . $delim . '#', $var, $match ) !== 1 ) { + continue; + } + + $this->addMessage( + $group['message'], + $stackPtr, + $group['type'] === 'error', + $this->string_to_errorcode( $groupName . '_' . $match[1] ), + [ $var ] + ); + + return; // Show one error only. + } + } + + /** + * Transform a wildcard pattern to a usable regex pattern. + * + * @param string $pattern Pattern. + * @return string + */ + private function test_patterns( $pattern ) { + $pattern = preg_quote( $pattern, '#' ); + $pattern = preg_replace( + [ '#\\\\\*#', '[\'"]' ], + [ '.*', '\'' ], + $pattern + ); + return $pattern; + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Classes/DeclarationCompatibilitySniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Classes/DeclarationCompatibilitySniff.php new file mode 100644 index 00000000..6b294807 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Classes/DeclarationCompatibilitySniff.php @@ -0,0 +1,390 @@ + [ + 'widget' => [ 'args', 'instance' ], + 'update' => [ 'new_instance', 'old_instance' ], + 'form' => [ 'instance' ], + 'WP_Widget' => [ + 'id_base', + 'name', + 'widget_options' => [ + 'default' => 'array()', + ], + 'constol_options' => [ + 'default' => 'array()', + ], + ], + 'get_field_name' => [ 'field_name' ], + 'get_field_id' => [ 'field_name' ], + '_register' => [], + '_set' => [ 'number' ], + '_get_display_callback' => [], + '_get_update_callback' => [], + '_get_form_callback' => [], + 'is_preview' => [], + 'display_callback' => [ + 'args', + 'widget_args' => [ + 'default' => '1', + ], + ], + 'update_callback' => [ + 'deprecated' => [ + 'default' => '1', + ], + ], + 'form_callback' => [ + 'widget_args' => [ + 'default' => '1', + ], + ], + 'register_one' => [ + 'number' => [ + 'default' => '-1', + ], + ], + 'save_settings' => [ 'settings' ], + 'get_settings' => [], + ], + 'Walker' => [ + 'start_lvl' => [ + 'output' => [ + 'pass_by_reference' => true, + ], + 'depth' => [ + 'default' => '0', + ], + 'args' => [ + 'default' => 'array()', + ], + ], + 'end_lvl' => [ + 'output' => [ + 'pass_by_reference' => true, + ], + 'depth' => [ + 'default' => '0', + ], + 'args' => [ + 'default' => 'array()', + ], + ], + 'start_el' => [ + 'output' => [ + 'pass_by_reference' => true, + ], + 'object', + 'depth' => [ + 'default' => '0', + ], + 'args' => [ + 'default' => 'array()', + ], + 'current_object_id' => [ + 'default' => '0', + ], + ], + 'end_el' => [ + 'output' => [ + 'pass_by_reference' => true, + ], + 'object', + 'depth' => [ + 'default' => '0', + ], + 'args' => [ + 'default' => 'array()', + ], + ], + 'display_element' => [ + 'element', + 'children_elements' => [ + 'pass_by_reference' => true, + ], + 'max_depth', + 'depth', + 'args', + 'output' => [ + 'pass_by_reference' => true, + ], + ], + 'walk' => [ + 'elements', + 'max_depth', + 'args' => [ + 'variable_length' => true, + ], + ], + 'paged_walk' => [ + 'elements', + 'max_depth', + 'page_num', + 'per_page', + 'args' => [ + 'variable_length' => true, + ], + ], + 'get_number_of_root_elements' => [ + 'elements', + ], + 'unset_children' => [ + 'el', + 'children_elements' => [ + 'pass_by_reference' => true, + ], + ], + ], + ]; + + /** + * List of grouped classes with same methods (as they extend the same parent class) + * + * @var string[] + */ + public $checkClassesGroups = [ + 'Walker' => [ + 'Walker_Category_Checklist', + 'Walker_Category', + 'Walker_CategoryDropdown', + 'Walker_PageDropdown', + 'Walker_Nav_Menu', + 'Walker_Page', + 'Walker_Comment', + ], + ]; + + /** + * Constructs the test with the tokens it wishes to listen for. + */ + public function __construct() { + parent::__construct( [ T_CLASS ], [ T_FUNCTION ], true ); + } + + /** + * Processes this test when one of its tokens is encountered. + * + * @param File $phpcsFile The PHP_CodeSniffer file where the token was found. + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * @param int $currScope A pointer to the start of the scope. + * + * @return void + */ + protected function processTokenWithinScope( File $phpcsFile, $stackPtr, $currScope ) { + + $className = $phpcsFile->getDeclarationName( $currScope ); + + if ( $className !== $this->currentClass ) { + $this->loadFunctionNamesInScope( $phpcsFile, $currScope ); + $this->currentClass = $className; + } + + $methodName = $phpcsFile->getDeclarationName( $stackPtr ); + + $parentClassName = $phpcsFile->findExtendedClassName( $currScope ); + if ( $parentClassName === false ) { + // This class does not extend any other class. + return; + } + + // Meed to define the originalParentClassName since we might override the parentClassName due to signature notations grouping. + $originalParentClassName = $parentClassName; + + if ( array_key_exists( $parentClassName, $this->checkClasses ) === false ) { + // This class does not extend a class we are interested in. + foreach ( $this->checkClassesGroups as $parent => $children ) { + // But it might be one of the grouped classes. + foreach ( $children as $child ) { + if ( $child === $parentClassName ) { + $parentClassName = $parent; + break 2; + } + } + } + if ( array_key_exists( $parentClassName, $this->checkClasses ) === false ) { + // This class really does not extend a class we are interested in. + return; + } + } + + if ( array_key_exists( $methodName, $this->checkClasses[ $parentClassName ] ) === false && + in_array( $methodName, $this->checkClasses[ $parentClassName ], true ) === false + ) { + // This method is not a one we are interested in. + return; + } + + $signatureParams = $phpcsFile->getMethodParameters( $stackPtr ); + + $parentSignature = $this->checkClasses[ $parentClassName ][ $methodName ]; + + if ( count( $signatureParams ) > count( $parentSignature ) ) { + $extra_params = array_slice( $signatureParams, count( $parentSignature ) - count( $signatureParams ) ); + $all_extra_params_have_default = true; + foreach ( $extra_params as $extra_param ) { + if ( array_key_exists( 'default', $extra_param ) === false || $extra_param['default'] !== 'true' ) { + $all_extra_params_have_default = false; + } + } + if ( $all_extra_params_have_default === true ) { + return; // We're good. + } + } + + if ( count( $signatureParams ) !== count( $parentSignature ) ) { + $this->addError( $originalParentClassName, $methodName, $signatureParams, $parentSignature, $phpcsFile, $stackPtr ); + return; + } + + $i = 0; + foreach ( $parentSignature as $key => $param ) { + if ( is_array( $param ) === true ) { + if ( + ( + array_key_exists( 'default', $param ) === true && + array_key_exists( 'default', $signatureParams[ $i ] ) === false + ) || ( + array_key_exists( 'pass_by_reference', $param ) === true && + $param['pass_by_reference'] !== $signatureParams[ $i ]['pass_by_reference'] + ) || ( + array_key_exists( 'variable_length', $param ) === true && + $param['variable_length'] !== $signatureParams[ $i ]['variable_length'] + ) + ) { + $this->addError( $originalParentClassName, $methodName, $signatureParams, $parentSignature, $phpcsFile, $stackPtr ); + return; + } + } + $i++; + } + } + + /** + * Generates an error with nice current and parent class method notations + * + * @param string $parentClassName The name of the extended (parent) class. + * @param string $methodName The name of the method currently being examined. + * @param array $currentMethodSignature The list of params and their options of the method which is being examined. + * @param array $parentMethodSignature The list of params and their options of the parent class method. + * @param File $phpcsFile The PHP_CodeSniffer file where the token was found. + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + private function addError( $parentClassName, $methodName, $currentMethodSignature, $parentMethodSignature, $phpcsFile, $stackPtr ) { + + $currentSignature = sprintf( '%s::%s(%s)', $this->currentClass, $methodName, implode( ', ', $this->generateParamList( $currentMethodSignature ) ) ); + + $parentSignature = sprintf( '%s::%s(%s)', $parentClassName, $methodName, implode( ', ', $this->generateParamList( $parentMethodSignature ) ) ); + + $message = 'Declaration of `%s` should be compatible with `%s`.'; + $data = [ $currentSignature, $parentSignature ]; + $phpcsFile->addError( $message, $stackPtr, 'DeclarationCompatibility', $data ); + } + + /** + * Generates an array of params as they appear in the signature. + * + * @param array $methodSignature Signature of a method. + * + * @return array + */ + private function generateParamList( $methodSignature ) { + $paramList = []; + foreach ( $methodSignature as $param => $options ) { + $paramName = '$'; + if ( is_array( $options ) === false ) { + $paramList[] = '$' . $options; + continue; + } + + if ( array_key_exists( 'name', $options ) === true ) { + $paramName = $options['name']; + } else { + $paramName .= $param; + } + + if ( array_key_exists( 'variable_length', $options ) === true && $options['variable_length'] === true ) { + $paramName = '...' . $paramName; + } + + if ( array_key_exists( 'pass_by_reference', $options ) === true && $options['pass_by_reference'] === true ) { + $paramName = '&' . $paramName; + } + + if ( array_key_exists( 'default', $options ) === true && empty( $options['default'] ) === false ) { + $paramName .= ' = ' . trim( $options['default'] ); + } + + $paramList[] = $paramName; + } + + return $paramList; + } + + /** + * Extracts all the function names found in the given scope. + * + * @param File $phpcsFile The current file being scanned. + * @param int $currScope A pointer to the start of the scope. + * + * @return void + */ + protected function loadFunctionNamesInScope( File $phpcsFile, $currScope ) { + $this->functionList = []; + $tokens = $phpcsFile->getTokens(); + + for ( $i = ( $tokens[ $currScope ]['scope_opener'] + 1 ); $i < $tokens[ $currScope ]['scope_closer']; $i++ ) { + if ( $tokens[ $i ]['code'] !== T_FUNCTION ) { + continue; + } + + $next = $phpcsFile->findNext( T_STRING, $i ); + $this->functionList[] = trim( $tokens[ $next ]['content'] ); + } + } + + /** + * Do nothing outside the scope. Has to be implemented accordingly to parent abstract class. + * + * @param File $phpcsFile PHPCS File. + * @param int $stackPtr Stack position. + */ + public function processTokenOutsideScope( File $phpcsFile, $stackPtr ) {} +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Classes/RestrictedExtendClassesSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Classes/RestrictedExtendClassesSniff.php new file mode 100644 index 00000000..b92ffbd8 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Classes/RestrictedExtendClassesSniff.php @@ -0,0 +1,59 @@ + [ + 'type' => 'warning', + 'message' => 'We recommend extending `WPCOM_VIP_CLI_Command` instead of `WP_CLI_Command` and using the helper functions available in it (such as `stop_the_insanity()`), see https://vip.wordpress.com/documentation/writing-bin-scripts/ for more information.', + 'classes' => [ + 'WP_CLI_Command', + ], + ], + ]; + } + + /** + * Process a matched token. + * + * @param int $stackPtr The position of the current token in the stack. + * @param array $group_name The name of the group which was matched. + * @param string $matched_content The token content (class name) which was matched. + * + * @return void + */ + public function process_matched_token( $stackPtr, $group_name, $matched_content ) { + $tokens = $this->phpcsFile->getTokens(); + + if ( $tokens[ $stackPtr ]['code'] !== T_EXTENDS ) { + // If not extending, bail. + return; + } + + foreach ( $this->getGroups() as $group => $group_args ) { + $this->phpcsFile->{ 'add' . $group_args['type'] }( $group_args['message'], $stackPtr, $group ); + } + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Compatibility/ZoninatorSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Compatibility/ZoninatorSniff.php new file mode 100644 index 00000000..1dae2b96 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Compatibility/ZoninatorSniff.php @@ -0,0 +1,90 @@ +tokens[ $stackPtr ]['content'] !== 'wpcom_vip_load_plugin' ) { + return; + } + + $openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); + + if ( $this->tokens[ $openBracket ]['code'] !== T_OPEN_PARENTHESIS ) { + // Not a function call. + return; + } + + $plugin_name = $this->phpcsFile->findNext( Tokens::$emptyTokens, $openBracket + 1, null, true ); + + if ( $this->remove_wrapping_quotation_marks( $this->tokens[ $plugin_name ]['content'] ) !== 'zoninator' ) { + return; + } + + $comma = $this->phpcsFile->findNext( Tokens::$emptyTokens, $plugin_name + 1, null, true ); + + if ( ! $comma || $this->tokens[ $comma ]['code'] !== 'PHPCS_T_COMMA' ) { + // We are loading the default version. + return; + } + + $folder = $this->phpcsFile->findNext( Tokens::$emptyTokens, $comma + 1, null, true ); + + $comma = $this->phpcsFile->findNext( Tokens::$emptyTokens, $folder + 1, null, true ); + + if ( ! $comma || $this->tokens[ $comma ]['code'] !== 'PHPCS_T_COMMA' ) { + // We are loading the default version. + return; + } + + $version = $this->phpcsFile->findNext( Tokens::$emptyTokens, $comma + 1, null, true ); + $version = $this->remove_wrapping_quotation_marks( $this->tokens[ $version ]['content'] ); + + if ( version_compare( $version, '0.8', '>=' ) === true ) { + $message = 'Zoninator of version >= v0.8 requires WordPress core REST API. Please, make sure the `wpcom_vip_load_wp_rest_api()` is being called on all sites loading this file.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'RequiresRESTAPI' ); + } + } + + /** + * Removes the quotation marks around T_CONSTANT_ENCAPSED_STRING. + * + * @param string $string T_CONSTANT_ENCAPSED_STRING containing wrapping quotation marks. + * + * @return string String w/o wrapping quotation marks. + */ + public function remove_wrapping_quotation_marks( $string ) { + return trim( str_replace( '"', "'", $string ), "'" ); + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Constants/ConstantStringSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Constants/ConstantStringSniff.php new file mode 100644 index 00000000..d6506c8f --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Constants/ConstantStringSniff.php @@ -0,0 +1,80 @@ +tokens[ $stackPtr ]['content'], [ 'define', 'defined' ], true ) === false ) { + return; + } + + // Find the next non-empty token. + $nextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true ); + + if ( $this->tokens[ $nextToken ]['code'] !== T_OPEN_PARENTHESIS ) { + // Not a function call. + return; + } + + if ( isset( $this->tokens[ $nextToken ]['parenthesis_closer'] ) === false ) { + // Not a function call. + return; + } + + $param = $this->get_function_call_parameter( $stackPtr, 1 ); + if ( $param === false ) { + // Target parameter not found. + return; + } + + $search = Tokens::$emptyTokens; + $search[ T_STRING ] = T_STRING; + + $has_only_tstring = $this->phpcsFile->findNext( $search, $param['start'], $param['end'] + 1, true ); + if ( $has_only_tstring !== false ) { + // Came across something other than a T_STRING token. Ignore. + return; + } + + $tstring_token = $this->phpcsFile->findNext( T_STRING, $param['start'], $param['end'] + 1 ); + + $message = 'Constant name, as a string, should be used along with `%s()`.'; + $data = [ $this->tokens[ $stackPtr ]['content'] ]; + $this->phpcsFile->addError( $message, $tstring_token, 'NotCheckingConstantName', $data ); + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Constants/RestrictedConstantsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Constants/RestrictedConstantsSniff.php new file mode 100644 index 00000000..92d1f7dc --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Constants/RestrictedConstantsSniff.php @@ -0,0 +1,112 @@ +tokens[ $stackPtr ]['code'] === T_STRING ) { + $constantName = $this->tokens[ $stackPtr ]['content']; + } else { + $constantName = trim( $this->tokens[ $stackPtr ]['content'], "\"'" ); + } + + if ( in_array( $constantName, $this->restrictedConstantNames, true ) === false && in_array( $constantName, $this->restrictedConstantDeclaration, true ) === false ) { + // Not the constant we are looking for. + return; + } + + if ( $this->tokens[ $stackPtr ]['code'] === T_STRING && in_array( $constantName, $this->restrictedConstantNames, true ) === true ) { + $message = 'Code is touching the `%s` constant. Make sure it\'s used appropriately.'; + $data = [ $constantName ]; + $this->phpcsFile->addWarning( $message, $stackPtr, 'UsingRestrictedConstant', $data ); + return; + } + + // Find the previous non-empty token. + $openBracket = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $stackPtr - 1, null, true, null, true ); + + if ( $this->tokens[ $openBracket ]['code'] !== T_OPEN_PARENTHESIS ) { + // Not a function call. + return; + } + + if ( isset( $this->tokens[ $openBracket ]['parenthesis_closer'] ) === false ) { + // Not a function call. + return; + } + + // Find the previous non-empty token. + $search = Tokens::$emptyTokens; + $search[] = T_BITWISE_AND; + $previous = $this->phpcsFile->findPrevious( $search, $openBracket - 1, null, true ); + if ( $this->tokens[ $previous ]['code'] === T_FUNCTION ) { + // It's a function definition, not a function call. + return; + } + + if ( in_array( $this->tokens[ $previous ]['code'], Tokens::$functionNameTokens, true ) === true ) { + $data = [ $constantName ]; + if ( $this->tokens[ $previous ]['content'] === 'define' ) { + $message = 'The definition of `%s` constant is prohibited. Please use a different name.'; + $this->phpcsFile->addError( $message, $previous, 'DefiningRestrictedConstant', $data ); + } elseif ( in_array( $constantName, $this->restrictedConstantNames, true ) === true ) { + $message = 'Code is touching the `%s` constant. Make sure it\'s used appropriately.'; + $this->phpcsFile->addWarning( $message, $previous, 'UsingRestrictedConstant', $data ); + } + } + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Files/IncludingFileSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Files/IncludingFileSniff.php new file mode 100644 index 00000000..4cbbfb12 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Files/IncludingFileSniff.php @@ -0,0 +1,207 @@ + 'get_template_directory', + 'STYLESHEETPATH' => 'get_stylesheet_directory', + ]; + + /** + * List of allowed constants. + * + * @var array + */ + public $allowedConstants = [ + 'ABSPATH', + 'WP_CONTENT_DIR', + 'WP_PLUGIN_DIR', + ]; + + /** + * List of keywords allowed for use in custom constants. + * Note: Customizing this property will overwrite current default values. + * + * @var array + */ + public $allowedKeywords = [ + 'PATH', + 'DIR', + ]; + + /** + * Functions used for modify slashes. + * + * @var array + */ + public $slashingFunctions = [ + 'trailingslashit', + 'user_trailingslashit', + 'untrailingslashit', + ]; + + /** + * Groups of functions to restrict. + * + * @return array + */ + public function getGroups() { + return []; + } + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + return Tokens::$includeTokens; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + public function process_token( $stackPtr ) { + $nextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true ); + + if ( $this->tokens[ $nextToken ]['code'] === T_OPEN_PARENTHESIS ) { + // The construct is using parenthesis, grab the next non empty token. + $nextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, $nextToken + 1, null, true, null, true ); + } + + if ( $this->tokens[ $nextToken ]['code'] === T_DIR || $this->tokens[ $nextToken ]['content'] === '__DIR__' ) { + // The construct is using __DIR__ which is fine. + return; + } + + if ( $this->tokens[ $nextToken ]['code'] === T_VARIABLE ) { + $message = 'File inclusion using variable (`%s`). Probably needs manual inspection.'; + $data = [ $this->tokens[ $nextToken ]['content'] ]; + $this->phpcsFile->addWarning( $message, $nextToken, 'UsingVariable', $data ); + return; + } + + if ( $this->tokens[ $nextToken ]['code'] === T_STRING ) { + if ( in_array( $this->tokens[ $nextToken ]['content'], $this->getPathFunctions, true ) === true ) { + // The construct is using one of the function for getting correct path which is fine. + return; + } + + if ( in_array( $this->tokens[ $nextToken ]['content'], $this->allowedConstants, true ) === true ) { + // The construct is using one of the allowed constants which is fine. + return; + } + + if ( $this->has_custom_path( $this->tokens[ $nextToken ]['content'] ) === true ) { + // The construct is using a constant with an allowed keyword. + return; + } + + if ( array_key_exists( $this->tokens[ $nextToken ]['content'], $this->restrictedConstants ) === true ) { + // The construct is using one of the restricted constants. + $message = '`%s` constant might not be defined or available. Use `%s()` instead.'; + $data = [ $this->tokens[ $nextToken ]['content'], $this->restrictedConstants[ $this->tokens[ $nextToken ]['content'] ] ]; + $this->phpcsFile->addError( $message, $nextToken, 'RestrictedConstant', $data ); + return; + } + + $nextNextToken = $this->phpcsFile->findNext( array_merge( Tokens::$emptyTokens, [ T_COMMENT ] ), $nextToken + 1, null, true, null, true ); + if ( preg_match( '/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $this->tokens[ $nextToken ]['content'] ) === 1 && $this->tokens[ $nextNextToken ]['code'] !== T_OPEN_PARENTHESIS ) { + // The construct is using custom constant, which needs manual inspection. + $message = 'File inclusion using custom constant (`%s`). Probably needs manual inspection.'; + $data = [ $this->tokens[ $nextToken ]['content'] ]; + $this->phpcsFile->addWarning( $message, $nextToken, 'UsingCustomConstant', $data ); + return; + } + + if ( strpos( $this->tokens[ $nextToken ]['content'], '$' ) === 0 ) { + $message = 'File inclusion using variable (`%s`). Probably needs manual inspection.'; + $data = [ $this->tokens[ $nextToken ]['content'] ]; + $this->phpcsFile->addWarning( $message, $nextToken, 'UsingVariable', $data ); + return; + } + + if ( in_array( $this->tokens[ $nextToken ]['content'], $this->slashingFunctions, true ) === true ) { + // The construct is using one of the slashing functions, it's probably correct. + return; + } + + if ( $this->is_targetted_token( $nextToken ) ) { + $message = 'File inclusion using custom function ( `%s()` ). Must return local file source, as external URLs are prohibited on WordPress VIP. Probably needs manual inspection.'; + $data = [ $this->tokens[ $nextToken ]['content'] ]; + $this->phpcsFile->addWarning( $message, $nextToken, 'UsingCustomFunction', $data ); + return; + } + + $message = 'Absolute include path must be used. Use `get_template_directory()`, `get_stylesheet_directory()` or `plugin_dir_path()`.'; + $this->phpcsFile->addError( $message, $nextToken, 'NotAbsolutePath' ); + return; + } + + if ( $this->tokens[ $nextToken ]['code'] === T_CONSTANT_ENCAPSED_STRING && filter_var( str_replace( [ '"', "'" ], '', $this->tokens[ $nextToken ]['content'] ), FILTER_VALIDATE_URL ) ) { + $message = 'Include path must be local file source, external URLs are prohibited on WordPress VIP.'; + $this->phpcsFile->addError( $message, $nextToken, 'ExternalURL' ); + return; + } + + $message = 'Absolute include path must be used. Use `get_template_directory()`, `get_stylesheet_directory()` or `plugin_dir_path()`.'; + $this->phpcsFile->addError( $message, $nextToken, 'NotAbsolutePath' ); + } + + /** + * Check if a content string contains a keyword in custom paths. + * + * @param string $content Content string. + * + * @return bool True if the string partially matches a keyword in $allowedCustomKeywords, false otherwise. + */ + private function has_custom_path( $content ) { + foreach ( $this->allowedKeywords as $keyword ) { + if ( strpos( $content, $keyword ) !== false ) { + return true; + } + } + + return false; + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Files/IncludingNonPHPFileSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Files/IncludingNonPHPFileSniff.php new file mode 100644 index 00000000..de30c6e5 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Files/IncludingNonPHPFileSniff.php @@ -0,0 +1,105 @@ + true, + 'inc' => true, + 'phar' => true, + ]; + + /** + * File extensions used for SVG and CSS files. + * + * @var array Key is the extension, value is irrelevant. + */ + private $svg_css_extensions = [ + 'css' => true, + 'svg' => true, + ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + return Tokens::$includeTokens; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @return void + */ + public function process_token( $stackPtr ) { + $end_of_statement = $this->phpcsFile->findEndOfStatement( $stackPtr ); + $curStackPtr = ( $end_of_statement + 1 ); + + do { + $curStackPtr = $this->phpcsFile->findPrevious( Tokens::$stringTokens, $curStackPtr - 1, $stackPtr ); + if ( $curStackPtr === false ) { + return; + } + + $stringWithoutEnclosingQuotationMarks = trim( $this->tokens[ $curStackPtr ]['content'], "\"'" ); + + $isFileName = preg_match( '`\.([a-z]{2,})$`i', $stringWithoutEnclosingQuotationMarks, $regexMatches ); + + if ( $isFileName !== 1 ) { + continue; + } + + $extension = strtolower( $regexMatches[1] ); + if ( isset( $this->php_extensions[ $extension ] ) === true ) { + return; + } + + $message = 'Local non-PHP file should be loaded via `file_get_contents` rather than via `%s`. Found: %s'; + $data = [ + strtolower( $this->tokens[ $stackPtr ]['content'] ), + $this->tokens[ $curStackPtr ]['content'], + ]; + $code = 'IncludingNonPHPFile'; + + if ( isset( $this->svg_css_extensions[ $extension ] ) === true ) { + // Be more specific for SVG and CSS files. + $message = 'Local SVG and CSS files should be loaded via `file_get_contents` rather than via `%s`. Found: %s'; + $code = 'IncludingSVGCSSFile'; + } + + $this->phpcsFile->addError( $message, $curStackPtr, $code, $data ); + + // Don't throw more than one error for any one statement. + return; + + } while ( $curStackPtr > $stackPtr ); + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/CheckReturnValueSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/CheckReturnValueSniff.php new file mode 100644 index 00000000..86a7348e --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/CheckReturnValueSniff.php @@ -0,0 +1,315 @@ + + * echo esc_url( wpcom_vip_get_term_link( $term ) ); + * + */ +class CheckReturnValueSniff extends Sniff { + + /** + * Pairs we are about to check. + * + * @var array + */ + public $catch = [ + 'esc_url' => [ + 'get_term_link', + ], + 'wp_list_pluck' => [ + 'get_the_tags', + 'get_the_terms', + ], + 'foreach' => [ + 'get_post_meta', + 'get_term_meta', + 'get_the_terms', + 'get_the_tags', + ], + 'array_key_exists' => [ + 'get_option', + ], + ]; + + /** + * Tokens we are about to examine, which are not functions. + * + * @var array + */ + public $notFunctions = [ + 'foreach' => T_FOREACH, + ]; + + /** + * Returns the token types that this sniff is interested in. + * + * @return array(int) + */ + public function register() { + return [ T_STRING ]; + } + + /** + * Processes the tokens that this sniff is interested in. + * + * @param int $stackPtr The position in the stack where + * the token was found. + * + * @return void + */ + public function process_token( $stackPtr ) { + + $this->findDirectFunctionCalls( $stackPtr ); + $this->findNonCheckedVariables( $stackPtr ); + } + + /** + * Check whether the currently examined code is a function call. + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @return bool + */ + private function isFunctionCall( $stackPtr ) { + + if ( $this->tokens[ $stackPtr ]['code'] !== T_STRING ) { + return false; + } + + // Find the next non-empty token. + $openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); + + if ( $this->tokens[ $openBracket ]['code'] !== T_OPEN_PARENTHESIS ) { + // Not a function call. + return false; + } + + // Find the previous non-empty token. + $search = Tokens::$emptyTokens; + $search[] = T_BITWISE_AND; + $previous = $this->phpcsFile->findPrevious( $search, $stackPtr - 1, null, true ); + + // It's a function definition, not a function call, so return false. + return ! ( $this->tokens[ $previous ]['code'] === T_FUNCTION ); + } + + /** + * Check whether the examined code is a variable assignment. + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @return bool + */ + private function isVariableAssignment( $stackPtr ) { + + // Find the previous non-empty token. + $search = Tokens::$emptyTokens; + $search[] = T_BITWISE_AND; + $previous = $this->phpcsFile->findPrevious( $search, $stackPtr - 1, null, true ); + + if ( $this->tokens[ $previous ]['code'] !== T_EQUAL ) { + // It's not a variable assignment. + return false; + } + + $previous = $this->phpcsFile->findPrevious( $search, $previous - 1, null, true ); + + if ( $this->tokens[ $previous ]['code'] !== T_VARIABLE ) { + // It's not a variable assignment. + return false; + } + + return $previous; + } + + /** + * Find instances in which a function call is directly passed to another one w/o checking the return type + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + */ + public function findDirectFunctionCalls( $stackPtr ) { + + $functionName = $this->tokens[ $stackPtr ]['content']; + + if ( array_key_exists( $functionName, $this->catch ) === false ) { + // Not a function we are looking for. + return; + } + + if ( $this->isFunctionCall( $stackPtr ) === false ) { + // Not a function call. + return; + } + + // Find the next non-empty token. + $openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); + + // Find the closing bracket. + $closeBracket = $this->tokens[ $openBracket ]['parenthesis_closer']; + + $startNext = $openBracket + 1; + $next = $this->phpcsFile->findNext( T_STRING, $startNext, $closeBracket, false, null, true ); + while ( $next ) { + if ( in_array( $this->tokens[ $next ]['content'], $this->catch[ $functionName ], true ) === true ) { + $message = "`%s`'s return type must be checked before calling `%s` using that value."; + $data = [ $this->tokens[ $next ]['content'], $functionName ]; + $this->phpcsFile->addError( $message, $next, 'DirectFunctionCall', $data ); + } + $next = $this->phpcsFile->findNext( T_STRING, $next + 1, $closeBracket, false, null, true ); + } + } + + /** + * Deals with situations in which the variable is being used later in the code along with a function which is known for causing issues. + * + * This only catches situations in which the variable is not being used with some other function before it's interacting with function we look for. + * That's currently necessary in order to prevent false positives. + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @return void + */ + public function findNonCheckedVariables( $stackPtr ) { + + $functionName = $this->tokens[ $stackPtr ]['content']; + + $isFunctionWeLookFor = false; + + $callees = []; + + foreach ( $this->catch as $callee => $checkReturnArray ) { + if ( in_array( $functionName, $checkReturnArray, true ) === true ) { + $isFunctionWeLookFor = true; + $callees[] = $callee; + } + } + + if ( $isFunctionWeLookFor === false ) { + // Not a function we are looking for. + return; + } + + if ( $this->isFunctionCall( $stackPtr ) === false ) { + // Not a function call. + return; + } + + $variablePos = $this->isVariableAssignment( $stackPtr ); + + if ( $variablePos === false ) { + // Not a variable assignment. + return; + } + + $variableToken = $this->tokens[ $variablePos ]; + $variableName = $variableToken['content']; + + // Find the next non-empty token. + $openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); + + // Find the closing bracket. + $closeBracket = $this->tokens[ $openBracket ]['parenthesis_closer']; + + if ( in_array( $functionName, [ 'get_post_meta', 'get_term_meta' ], true ) === true ) { + // Since the get_post_meta and get_term_meta always returns an array if $single is set to `true` we need to check for the value of it's third param before proceeding. + $params = []; + $paramNo = 1; + $prevCommaPos = $openBracket + 1; + + for ( $i = $openBracket + 1; $i <= $closeBracket; $i++ ) { + + if ( $this->tokens[ $i ]['code'] === T_OPEN_PARENTHESIS ) { + $i = $this->tokens[ $i ]['parenthesis_closer']; + } + + if ( $this->tokens[ $i ]['code'] === T_COMMA ) { + $params[ $paramNo++ ] = trim( array_reduce( array_slice( $this->tokens, $prevCommaPos, $i - $prevCommaPos ), [ $this, 'reduce_array' ] ) ); + $prevCommaPos = $i + 1; + } + + if ( $i === $closeBracket ) { + $params[ $paramNo ] = trim( array_reduce( array_slice( $this->tokens, $prevCommaPos, $i - $prevCommaPos ), [ $this, 'reduce_array' ] ) ); + break; + } + } + + if ( array_key_exists( 3, $params ) === false || $params[3] === 'false' ) { + // Third param of get_post_meta is not set (default to false) or is set to false. + // Means the function returns an array. We are good then. + return; + } + } + + $nextVariableOccurrence = $this->phpcsFile->findNext( T_VARIABLE, $closeBracket + 1, null, false, $variableName ); + + // Find previous non-empty token, which is not an open parenthesis, comma nor variable. + $search = Tokens::$emptyTokens; + $search[] = T_OPEN_PARENTHESIS; + // This allows us to check for variables which are passed as second parameter of a function e.g.: array_key_exists. + $search[] = T_COMMA; + $search[] = T_VARIABLE; + $search[] = T_CONSTANT_ENCAPSED_STRING; + + $nextFunctionCallWithVariable = $this->phpcsFile->findPrevious( $search, $nextVariableOccurrence - 1, null, true ); + + foreach ( $callees as $callee ) { + $notFunctionsCallee = array_key_exists( $callee, $this->notFunctions ) ? (array) $this->notFunctions[ $callee ] : []; + // Check whether the found token is one of the function calls (or foreach call) we are interested in. + if ( in_array( $this->tokens[ $nextFunctionCallWithVariable ]['code'], array_merge( [ T_STRING ], $notFunctionsCallee ), true ) === true + && $this->tokens[ $nextFunctionCallWithVariable ]['content'] === $callee + ) { + $this->addNonCheckedVariableError( $nextFunctionCallWithVariable, $variableName, $callee ); + return; + } + + $search = array_merge( Tokens::$emptyTokens, [ T_EQUAL ] ); + $next = $this->phpcsFile->findNext( $search, $nextVariableOccurrence + 1, null, true ); + if ( $this->tokens[ $next ]['code'] === T_STRING + && $this->tokens[ $next ]['content'] === $callee + ) { + $this->addNonCheckedVariableError( $next, $variableName, $callee ); + return; + } + } + } + + /** + * Function used as as callback for the array_reduce call. + * + * @param string $carry The final string. + * @param array $item Processed item. + * + * @return string + */ + public function reduce_array( $carry, $item ) { + return $carry . $item['content']; + } + + /** + * Consolidated violation. + * + * @param int $stackPtr The position in the stack where the token was found. + * @param string $variableName Variable name. + * @param string $callee Function name. + */ + private function addNonCheckedVariableError( $stackPtr, $variableName, $callee ) { + $message = 'Type of `%s` must be checked before calling `%s()` using that variable.'; + $data = [ $variableName, $callee ]; + $this->phpcsFile->addError( $message, $stackPtr, 'NonCheckedVariable', $data ); + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/DynamicCallsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/DynamicCallsSniff.php new file mode 100644 index 00000000..c069696f --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/DynamicCallsSniff.php @@ -0,0 +1,188 @@ + true, + 'compact' => true, + 'extract' => true, + 'func_get_args' => true, + 'func_get_arg' => true, + 'func_num_args' => true, + 'get_defined_vars' => true, + 'mb_parse_str' => true, + 'parse_str' => true, + ]; + + /** + * Array of variable assignments encountered, along with their values. + * + * Populated at run-time. + * + * @var array The key is the name of the variable, the value, its assigned value. + */ + private $variables_arr = []; + + /** + * The position in the stack where the token was found. + * + * @var int + */ + private $stackPtr; + + /** + * Returns the token types that this sniff is interested in. + * + * @return array(int) + */ + public function register() { + return [ T_VARIABLE => T_VARIABLE ]; + } + + /** + * Processes the tokens that this sniff is interested in. + * + * @param int $stackPtr The position in the stack where the token was found. + * + * @return void + */ + public function process_token( $stackPtr ) { + $this->stackPtr = $stackPtr; + + // First collect all variables encountered and their values. + $this->collect_variables(); + + // Then find all dynamic calls, and report them. + $this->find_dynamic_calls(); + } + + /** + * Finds any variable-definitions in the file being processed and stores them + * internally in a private array. + * + * @return void + */ + private function collect_variables() { + + $current_var_name = $this->tokens[ $this->stackPtr ]['content']; + + /* + * Find assignments ( $foo = "bar"; ) by finding all non-whitespaces, + * and checking if the first one is T_EQUAL. + */ + $t_item_key = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + $this->stackPtr + 1, + null, + true, + null, + true + ); + + if ( $t_item_key === false || $this->tokens[ $t_item_key ]['code'] !== T_EQUAL ) { + return; + } + + /* + * Find assignments which only assign a plain text string. + */ + $end_of_statement = $this->phpcsFile->findNext( [ T_SEMICOLON, T_CLOSE_TAG ], ( $t_item_key + 1 ) ); + $value_ptr = null; + + for ( $i = $t_item_key + 1; $i < $end_of_statement; $i++ ) { + if ( isset( Tokens::$emptyTokens[ $this->tokens[ $i ]['code'] ] ) === true ) { + continue; + } + + if ( $this->tokens[ $i ]['code'] !== T_CONSTANT_ENCAPSED_STRING ) { + // Not a plain text string value. Value cannot be determined reliably. + return; + } + + $value_ptr = $i; + } + + if ( isset( $value_ptr ) === false ) { + // Parse error. Bow out. + return; + } + + /* + * If we reached the end of the loop and the $value_ptr was set, we know for sure + * this was a plain text string variable assignment. + */ + $current_var_value = $this->strip_quotes( $this->tokens[ $value_ptr ]['content'] ); + + if ( isset( $this->disallowed_functions[ $current_var_value ] ) === false ) { + // Text string is not one of the ones we're looking for. + return; + } + + /* + * Register the variable name and value in the internal array for later usage. + */ + $this->variables_arr[ $current_var_name ] = $current_var_value; + } + + /** + * Find any dynamic calls being made using variables. + * + * Report on this when found, using the name of the function in the message. + * + * @return void + */ + private function find_dynamic_calls() { + // No variables detected; no basis for doing anything. + if ( empty( $this->variables_arr ) ) { + return; + } + + /* + * If variable is not found in our registry of variables, do nothing, as we cannot be + * sure that the function being called is one of the disallowed ones. + */ + if ( ! isset( $this->variables_arr[ $this->tokens[ $this->stackPtr ]['content'] ] ) ) { + return; + } + + /* + * Check if we have an '(' next. + */ + $next = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $this->stackPtr + 1 ), null, true ); + if ( $next === false || $this->tokens[ $next ]['code'] !== T_OPEN_PARENTHESIS ) { + return; + } + + $message = 'Dynamic calling is not recommended in the case of %s().'; + $data = [ $this->variables_arr[ $this->tokens[ $this->stackPtr ]['content'] ] ]; + $this->phpcsFile->addError( $message, $this->stackPtr, 'DynamicCalls', $data ); + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/RestrictedFunctionsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/RestrictedFunctionsSniff.php new file mode 100644 index 00000000..8f3bba86 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/RestrictedFunctionsSniff.php @@ -0,0 +1,371 @@ + [ + 'type' => 'error', + 'message' => '`%s` is prohibited on the WordPress VIP platform due to memory corruption.', + 'functions' => [ + 'opcache_reset', + 'opcache_invalidate', + 'opcache_compile_file', + ], + ], + 'config_settings' => [ + 'type' => 'error', + 'message' => '`%s` is not recommended for use on the WordPress VIP platform due to potential setting changes.', + 'functions' => [ + 'opcache_is_script_cached', + 'opcache_get_status', + 'opcache_get_configuration', + ], + ], + 'internal' => [ + 'type' => 'error', + 'message' => '`%1$s()` is for internal use only.', + 'functions' => [ + 'wpcom_vip_irc', + ], + ], + 'flush_rewrite_rules' => [ + 'type' => 'error', + 'message' => '`%s` should not be used in any normal circumstances in the theme code.', + 'functions' => [ + 'flush_rewrite_rules', + ], + ], + 'flush_rules' => [ + 'type' => 'error', + 'message' => '`%s` should not be used in any normal circumstances in the theme code.', + 'functions' => [ + 'flush_rules', + ], + 'object_var' => [ + '$wp_rewrite' => true, + ], + ], + 'attachment_url_to_postid' => [ + 'type' => 'error', + 'message' => '`%s()` is prohibited, please use `wpcom_vip_attachment_url_to_postid()` instead.', + 'functions' => [ + 'attachment_url_to_postid', + ], + ], + // @link https://docs.wpvip.com/technical-references/code-review/vip-notices/#h-switch_to_blog + 'switch_to_blog' => [ + 'type' => 'warning', + 'message' => '%s() may not work as expected since it only changes the database context for the blog and does not load the plugins or theme of that site. Filters or hooks on the blog you are switching to will not run.', + 'functions' => [ + 'switch_to_blog', + ], + ], + 'get_page_by_title' => [ + 'type' => 'error', + 'message' => '%s() is prohibited, please use wpcom_vip_get_page_by_title() instead.', + 'functions' => [ + 'get_page_by_title', + ], + ], + 'url_to_postid' => [ + 'type' => 'error', + 'message' => '%s() is prohibited, please use wpcom_vip_url_to_postid() instead.', + 'functions' => [ + 'url_to_postid', + ], + ], + // @link https://docs.wpvip.com/how-tos/customize-user-roles/ + 'custom_role' => [ + 'type' => 'error', + 'message' => 'Use wpcom_vip_add_role() instead of %s().', + 'functions' => [ + 'add_role', + ], + ], + 'term_exists' => [ + 'type' => 'error', + 'message' => '%s() is highly discouraged due to not being cached; please use wpcom_vip_term_exists() instead.', + 'functions' => [ + 'term_exists', + ], + ], + 'count_user_posts' => [ + 'type' => 'error', + 'message' => '%s() is highly discouraged due to not being cached; please use wpcom_vip_count_user_posts() instead.', + 'functions' => [ + 'count_user_posts', + ], + ], + 'wp_old_slug_redirect' => [ + 'type' => 'error', + 'message' => '%s() is highly discouraged due to not being cached; please use wpcom_vip_old_slug_redirect() instead.', + 'functions' => [ + 'wp_old_slug_redirect', + ], + ], + 'get_adjacent_post' => [ + 'type' => 'error', + 'message' => '%s() is highly discouraged due to not being cached; please use wpcom_vip_get_adjacent_post() instead.', + 'functions' => [ + 'get_adjacent_post', + 'get_previous_post', + 'get_previous_post_link', + 'get_next_post', + 'get_next_post_link', + ], + ], + 'get_intermediate_image_sizes' => [ + 'type' => 'error', + 'message' => 'Intermediate images do not exist on the VIP platform, and thus get_intermediate_image_sizes() returns an empty array() on the platform. This behavior is intentional to prevent WordPress from generating multiple thumbnails when images are uploaded.', + 'functions' => [ + 'get_intermediate_image_sizes', + ], + ], + // @link https://docs.wpvip.com/technical-references/code-review/vip-warnings/#h-mobile-detection + 'wp_is_mobile' => [ + 'type' => 'error', + 'message' => '%s() found. When targeting mobile visitors, jetpack_is_mobile() should be used instead of wp_is_mobile. It is more robust and works better with full page caching.', + 'functions' => [ + 'wp_is_mobile', + ], + ], + 'session' => [ + 'type' => 'error', + 'message' => 'The use of PHP session function %s() is prohibited.', + 'functions' => [ + 'session_abort', + 'session_cache_expire', + 'session_cache_limiter', + 'session_commit', + 'session_create_id', + 'session_decode', + 'session_destroy', + 'session_encode', + 'session_gc', + 'session_get_cookie_params', + 'session_id', + 'session_is_registered', + 'session_module_name', + 'session_name', + 'session_regenerate_id', + 'session_register_shutdown', + 'session_register', + 'session_reset', + 'session_save_path', + 'session_set_cookie_params', + 'session_set_save_handler', + 'session_start', + 'session_status', + 'session_unregister', + 'session_unset', + 'session_write_close', + ], + ], + 'file_ops' => [ + 'type' => 'error', + 'message' => 'Filesystem writes are forbidden, please do not use %s().', + 'functions' => [ + 'delete', + 'file_put_contents', + 'flock', + 'fputcsv', + 'fputs', + 'fwrite', + 'ftruncate', + 'is_writable', + 'is_writeable', + 'link', + 'rename', + 'symlink', + 'tempnam', + 'touch', + 'unlink', + ], + ], + 'directory' => [ + 'type' => 'error', + 'message' => 'Filesystem writes are forbidden, please do not use %s().', + 'functions' => [ + 'mkdir', + 'rmdir', + ], + ], + 'chmod' => [ + 'type' => 'error', + 'message' => 'Filesystem writes are forbidden, please do not use %s().', + 'functions' => [ + 'chgrp', + 'chown', + 'chmod', + 'lchgrp', + 'lchown', + ], + ], + 'stats_get_csv' => [ + 'type' => 'error', + 'message' => 'Using `%s` outside of Jetpack context pollutes the stats_cache entry in the wp_options table. We recommend building a custom function instead.', + 'functions' => [ + 'stats_get_csv', + ], + ], + 'wp_mail' => [ + 'type' => 'warning', + 'message' => '`%s` should be used sparingly. For any bulk emailing should be handled by a 3rd party service, in order to prevent domain or IP addresses being flagged as spam.', + 'functions' => [ + 'wp_mail', + 'mail', + ], + ], + 'is_multi_author' => [ + 'type' => 'warning', + 'message' => '`%s` can be very slow on large sites and likely not needed on many VIP sites since they tend to have more than one author.', + 'functions' => [ + 'is_multi_author', + ], + ], + 'advanced_custom_fields' => [ + 'type' => 'warning', + 'message' => '`%1$s` does not escape output by default, please echo and escape with the `get_*()` variant function instead (i.e. `get_field()`).', + 'functions' => [ + 'the_sub_field', + 'the_field', + ], + ], + // @link https://docs.wpvip.com/technical-references/code-review/vip-warnings/#h-remote-calls + 'wp_remote_get' => [ + 'type' => 'warning', + 'message' => '%s() is highly discouraged. Please use vip_safe_wp_remote_get() instead which is designed to more gracefully handle failure than wp_remote_get() does.', + 'functions' => [ + 'wp_remote_get', + ], + ], + // @link https://docs.wpvip.com/technical-references/code-review/vip-errors/#h-cache-constraints + 'cookies' => [ + 'type' => 'error', + 'message' => 'Due to server-side caching, server-side based client related logic might not work. We recommend implementing client side logic in JavaScript instead.', + 'functions' => [ + 'setcookie', + ], + ], + // @todo Introduce a sniff specific to get_posts() that checks for suppress_filters=>false being supplied. + 'get_posts' => [ + 'type' => 'warning', + 'message' => '%s() is uncached unless the "suppress_filters" parameter is set to false. If the suppress_filter parameter is set to false this can be safely ignored. More Info: https://docs.wpvip.com/technical-references/caching/uncached-functions/.', + 'functions' => [ + 'get_posts', + 'wp_get_recent_posts', + 'get_children', + ], + ], + 'create_function' => [ + 'type' => 'warning', + 'message' => '%s() is highly discouraged, as it can execute arbritary code (additionally, it\'s deprecated as of PHP 7.2): https://docs.wpvip.com/technical-references/code-review/vip-warnings/#h-eval-and-create_function. )', + 'functions' => [ + 'create_function', + ], + ], + 'get_page_by_path' => [ + 'type' => 'warning', + 'message' => '%s() is highly discouraged due to not being cached; please use wpcom_vip_get_page_by_path() instead.', + 'functions' => [ + 'get_page_by_path', + ], + ], + ]; + + $deprecated_vip_helpers = [ + 'get_term_link' => 'wpcom_vip_get_term_link', + 'get_term_by' => 'wpcom_vip_get_term_by', + 'get_category_by_slug' => 'wpcom_vip_get_category_by_slug', + ]; + foreach ( $deprecated_vip_helpers as $restricted => $helper ) { + $groups[ $helper ] = [ + 'type' => 'warning', + 'message' => "`%s()` is deprecated, please use `{$restricted}()` instead.", + 'functions' => [ + $helper, + ], + ]; + } + + return $groups; + } + + /** + * Verify the current token is a function call or a method call on a specific object variable. + * + * This differs to the parent class method that it overrides, by also checking to see if the + * function call is actually a method call on a specific object variable. This works best with global objects, + * such as the `flush_rules()` method on the `$wp_rewrite` object. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return bool + */ + public function is_targetted_token( $stackPtr ) { + // Exclude function definitions, class methods, and namespaced calls. + if ( $this->tokens[ $stackPtr ]['code'] === \T_STRING && isset( $this->tokens[ $stackPtr - 1 ] ) ) { + // Check if this is really a function. + $next = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); + if ( $next !== false && $this->tokens[ $next ]['code'] !== T_OPEN_PARENTHESIS ) { + return false; + } + + $prev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $stackPtr - 1, null, true ); + if ( $prev !== false ) { + + // Start difference to parent class method. + // Check to see if function is a method on a specific object variable. + if ( ! empty( $this->groups[ $this->tokens[ $stackPtr ]['content'] ]['object_var'] ) ) { + $prevPrev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $stackPtr - 2, null, true ); + + return $this->tokens[ $prev ]['code'] === \T_OBJECT_OPERATOR && isset( $this->groups[ $this->tokens[ $stackPtr ]['content'] ]['object_var'][ $this->tokens[ $prevPrev ]['content'] ] ); + } // End difference to parent class method. + + // Skip sniffing if calling a same-named method, or on function definitions. + $skipped = [ + \T_FUNCTION => \T_FUNCTION, + \T_CLASS => \T_CLASS, + \T_AS => \T_AS, // Use declaration alias. + \T_DOUBLE_COLON => \T_DOUBLE_COLON, + \T_OBJECT_OPERATOR => \T_OBJECT_OPERATOR, + \T_NEW => \T_NEW, + ]; + if ( isset( $skipped[ $this->tokens[ $prev ]['code'] ] ) ) { + return false; + } + // Skip namespaced functions, ie: `\foo\bar()` not `\bar()`. + if ( $this->tokens[ $prev ]['code'] === \T_NS_SEPARATOR ) { + $pprev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $prev - 1, null, true ); + if ( $pprev !== false && $this->tokens[ $pprev ]['code'] === \T_STRING ) { + return false; + } + } + } + return true; + } + return false; + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/StripTagsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/StripTagsSniff.php new file mode 100644 index 00000000..5434427d --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Functions/StripTagsSniff.php @@ -0,0 +1,59 @@ + true, + ]; + + /** + * Process the parameters of a matched function. + * + * @param int $stackPtr The position of the current token in the stack. + * @param array $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + if ( count( $parameters ) === 1 ) { + $message = '`strip_tags()` does not strip CSS and JS in between the script and style tags. Use `wp_strip_all_tags()` to strip all tags.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'StripTagsOneParameter' ); + } elseif ( isset( $parameters[2] ) ) { + $message = '`strip_tags()` does not strip CSS and JS in between the script and style tags. Use `wp_kses()` instead to allow only the HTML you need.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'StripTagsTwoParameters' ); + } + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Hooks/AlwaysReturnInFilterSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Hooks/AlwaysReturnInFilterSniff.php new file mode 100644 index 00000000..8029e732 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Hooks/AlwaysReturnInFilterSniff.php @@ -0,0 +1,299 @@ +tokens[ $stackPtr ]['content']; + + if ( $functionName !== 'add_filter' ) { + return; + } + + $this->filterNamePtr = $this->phpcsFile->findNext( + array_merge( Tokens::$emptyTokens, [ T_OPEN_PARENTHESIS ] ), + $stackPtr + 1, + null, + true, + null, + true + ); + + if ( ! $this->filterNamePtr ) { + // Something is wrong. + return; + } + + $callbackPtr = $this->phpcsFile->findNext( + array_merge( Tokens::$emptyTokens, [ T_COMMA ] ), + $this->filterNamePtr + 1, + null, + true, + null, + true + ); + + if ( ! $callbackPtr ) { + // Something is wrong. + return; + } + + if ( $this->tokens[ $callbackPtr ]['code'] === 'PHPCS_T_CLOSURE' ) { + $this->processFunctionBody( $callbackPtr ); + } elseif ( $this->tokens[ $callbackPtr ]['code'] === T_ARRAY + || $this->tokens[ $callbackPtr ]['code'] === T_OPEN_SHORT_ARRAY + ) { + $this->processArray( $callbackPtr ); + } elseif ( in_array( $this->tokens[ $callbackPtr ]['code'], Tokens::$stringTokens, true ) === true ) { + $this->processString( $callbackPtr ); + } + } + + /** + * Process array. + * + * @param int $stackPtr The position in the stack where the token was found. + */ + private function processArray( $stackPtr ) { + + $open_close = $this->find_array_open_close( $stackPtr ); + if ( $open_close === false ) { + return; + } + + $previous = $this->phpcsFile->findPrevious( + Tokens::$emptyTokens, + $open_close['closer'] - 1, + null, + true + ); + + if ( in_array( T_CLASS, $this->tokens[ $stackPtr ]['conditions'], true ) === true ) { + $classPtr = array_search( T_CLASS, $this->tokens[ $stackPtr ]['conditions'], true ); + if ( $classPtr ) { + $classToken = $this->tokens[ $classPtr ]; + $this->processString( $previous, $classToken['scope_opener'], $classToken['scope_closer'] ); + return; + } + } + + $this->processString( $previous ); + } + + /** + * Process string. + * + * @param int $stackPtr The position in the stack where the token was found. + * @param int $start The start of the token. + * @param int $end The end of the token. + */ + private function processString( $stackPtr, $start = 0, $end = null ) { + + $callbackFunctionName = substr( $this->tokens[ $stackPtr ]['content'], 1, -1 ); + + $callbackFunctionPtr = $this->phpcsFile->findNext( + T_STRING, + $start, + $end, + false, + $callbackFunctionName + ); + + if ( ! $callbackFunctionPtr ) { + // We were not able to find the function callback in the file. + return; + } + + $this->processFunction( $callbackFunctionPtr, $start, $end ); + } + + /** + * Process function. + * + * @param int $stackPtr The position in the stack where the token was found. + * @param int $start The start of the token. + * @param int $end The end of the token. + */ + private function processFunction( $stackPtr, $start = 0, $end = null ) { + + $functionName = $this->tokens[ $stackPtr ]['content']; + + $offset = $start; + while ( $this->phpcsFile->findNext( [ T_FUNCTION ], $offset, $end ) !== false ) { + $functionStackPtr = $this->phpcsFile->findNext( [ T_FUNCTION ], $offset, $end ); + $functionNamePtr = $this->phpcsFile->findNext( Tokens::$emptyTokens, $functionStackPtr + 1, null, true, null, true ); + if ( $this->tokens[ $functionNamePtr ]['code'] === T_STRING && $this->tokens[ $functionNamePtr ]['content'] === $functionName ) { + $this->processFunctionBody( $functionStackPtr ); + return; + } + $offset = $functionStackPtr + 1; + } + } + + /** + * Process function's body + * + * @param int $stackPtr The position in the stack where the token was found. + */ + private function processFunctionBody( $stackPtr ) { + + $filterName = $this->tokens[ $this->filterNamePtr ]['content']; + + $methodProps = $this->phpcsFile->getMethodProperties( $stackPtr ); + if ( $methodProps['is_abstract'] === true ) { + $message = 'The callback for the `%s` filter hook-in points to an abstract method. Please ensure that child class implementations of this method always return a value.'; + $data = [ $filterName ]; + $this->phpcsFile->addWarning( $message, $stackPtr, 'AbstractMethod', $data ); + return; + } + + if ( isset( $this->tokens[ $stackPtr ]['scope_opener'], $this->tokens[ $stackPtr ]['scope_closer'] ) === false ) { + // Live coding, parse or tokenizer error. + return; + } + + $argPtr = $this->phpcsFile->findNext( + array_merge( Tokens::$emptyTokens, [ T_STRING, T_OPEN_PARENTHESIS ] ), + $stackPtr + 1, + null, + true, + null, + true + ); + + // If arg is being passed by reference, we can skip. + if ( $this->tokens[ $argPtr ]['code'] === T_BITWISE_AND ) { + return; + } + + $functionBodyScopeStart = $this->tokens[ $stackPtr ]['scope_opener']; + $functionBodyScopeEnd = $this->tokens[ $stackPtr ]['scope_closer']; + + $returnTokenPtr = $this->phpcsFile->findNext( + [ T_RETURN ], + $functionBodyScopeStart + 1, + $functionBodyScopeEnd + ); + + $insideIfConditionalReturn = 0; + $outsideConditionalReturn = 0; + + while ( $returnTokenPtr ) { + if ( $this->isInsideIfConditonal( $returnTokenPtr ) ) { + $insideIfConditionalReturn++; + } else { + $outsideConditionalReturn++; + } + if ( $this->isReturningVoid( $returnTokenPtr ) ) { + $message = 'Please, make sure that a callback to `%s` filter is returning void intentionally.'; + $data = [ $filterName ]; + $this->phpcsFile->addError( $message, $functionBodyScopeStart, 'VoidReturn', $data ); + } + $returnTokenPtr = $this->phpcsFile->findNext( + [ T_RETURN ], + $returnTokenPtr + 1, + $functionBodyScopeEnd + ); + } + + if ( $insideIfConditionalReturn >= 0 && $outsideConditionalReturn === 0 ) { + $message = 'Please, make sure that a callback to `%s` filter is always returning some value.'; + $data = [ $filterName ]; + $this->phpcsFile->addError( $message, $functionBodyScopeStart, 'MissingReturnStatement', $data ); + + } + } + + /** + * Is the current token inside a conditional? + * + * @param int $stackPtr The position in the stack where the token was found. + * + * @return bool + */ + private function isInsideIfConditonal( $stackPtr ) { + + // This check helps us in situations a class or a function is wrapped + // inside a conditional as a whole. Eg.: inside `class_exists`. + if ( end( $this->tokens[ $stackPtr ]['conditions'] ) === T_FUNCTION ) { + return false; + } + + // Similar case may be a conditional closure. + if ( end( $this->tokens[ $stackPtr ]['conditions'] ) === 'PHPCS_T_CLOSURE' ) { + return false; + } + + // Loop over the array of conditions and look for an IF. + reset( $this->tokens[ $stackPtr ]['conditions'] ); + + if ( array_key_exists( 'conditions', $this->tokens[ $stackPtr ] ) === true + && is_array( $this->tokens[ $stackPtr ]['conditions'] ) === true + && empty( $this->tokens[ $stackPtr ]['conditions'] ) === false + ) { + foreach ( $this->tokens[ $stackPtr ]['conditions'] as $tokenPtr => $tokenCode ) { + if ( $this->tokens[ $stackPtr ]['conditions'][ $tokenPtr ] === T_IF ) { + return true; + } + } + } + return false; + } + + /** + * Is the token returning void + * + * @param int $stackPtr The position in the stack where the token was found. + * + * @return bool + **/ + private function isReturningVoid( $stackPtr ) { + + $nextToReturnTokenPtr = $this->phpcsFile->findNext( + [ Tokens::$emptyTokens ], + $stackPtr + 1, + null, + true + ); + + return $this->tokens[ $nextToReturnTokenPtr ]['code'] === T_SEMICOLON; + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Hooks/PreGetPostsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Hooks/PreGetPostsSniff.php new file mode 100644 index 00000000..75b54729 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Hooks/PreGetPostsSniff.php @@ -0,0 +1,450 @@ +tokens[ $stackPtr ]['content']; + + if ( $functionName !== 'add_action' ) { + // We are interested in add_action calls only. + return; + } + + $actionNamePtr = $this->phpcsFile->findNext( + array_merge( Tokens::$emptyTokens, [ T_OPEN_PARENTHESIS ] ), + $stackPtr + 1, + null, + true, + null, + true + ); + + if ( ! $actionNamePtr ) { + // Something is wrong. + return; + } + + if ( substr( $this->tokens[ $actionNamePtr ]['content'], 1, -1 ) !== 'pre_get_posts' ) { + // This is not setting a callback for pre_get_posts action. + return; + } + + $callbackPtr = $this->phpcsFile->findNext( + array_merge( Tokens::$emptyTokens, [ T_COMMA ] ), + $actionNamePtr + 1, + null, + true, + null, + true + ); + + if ( ! $callbackPtr ) { + // Something is wrong. + return; + } + + if ( $this->tokens[ $callbackPtr ]['code'] === 'PHPCS_T_CLOSURE' ) { + $this->processClosure( $callbackPtr ); + } elseif ( $this->tokens[ $callbackPtr ]['code'] === T_ARRAY + || $this->tokens[ $callbackPtr ]['code'] === T_OPEN_SHORT_ARRAY + ) { + $this->processArray( $callbackPtr ); + } elseif ( in_array( $this->tokens[ $callbackPtr ]['code'], Tokens::$stringTokens, true ) === true ) { + $this->processString( $callbackPtr ); + } + } + + /** + * Process array. + * + * @param int $stackPtr The position in the stack where the token was found. + */ + private function processArray( $stackPtr ) { + + $open_close = $this->find_array_open_close( $stackPtr ); + if ( $open_close === false ) { + return; + } + + $previous = $this->phpcsFile->findPrevious( + Tokens::$emptyTokens, + $open_close['closer'] - 1, + null, + true + ); + + $this->processString( $previous ); + } + + /** + * Process string. + * + * @param int $stackPtr The position in the stack where the token was found. + */ + private function processString( $stackPtr ) { + + $callbackFunctionName = substr( $this->tokens[ $stackPtr ]['content'], 1, -1 ); + + $callbackFunctionPtr = $this->phpcsFile->findNext( + T_STRING, + 0, + null, + false, + $callbackFunctionName + ); + + if ( ! $callbackFunctionPtr ) { + // We were not able to find the function callback in the file. + return; + } + + $this->processFunction( $callbackFunctionPtr ); + } + + /** + * Process function. + * + * @param int $stackPtr The position in the stack where the token was found. + */ + private function processFunction( $stackPtr ) { + + $wpQueryObjectNamePtr = $this->phpcsFile->findNext( + [ T_VARIABLE ], + $stackPtr + 1, + null, + false, + null, + true + ); + + if ( ! $wpQueryObjectNamePtr ) { + // Something is wrong. + return; + } + + $wpQueryObjectVariableName = $this->tokens[ $wpQueryObjectNamePtr ]['content']; + + $functionDefinitionPtr = $this->phpcsFile->findPrevious( [ T_FUNCTION ], $wpQueryObjectNamePtr - 1 ); + + if ( ! $functionDefinitionPtr ) { + // Something is wrong. + return; + } + + $this->processFunctionBody( $functionDefinitionPtr, $wpQueryObjectVariableName ); + } + + /** + * Process closure. + * + * @param int $stackPtr The position in the stack where the token was found. + */ + private function processClosure( $stackPtr ) { + + $wpQueryObjectNamePtr = $this->phpcsFile->findNext( + [ T_VARIABLE ], + $stackPtr + 1, + null, + false, + null, + true + ); + + if ( ! $wpQueryObjectNamePtr ) { + // Something is wrong. + return; + } + + $this->processFunctionBody( $stackPtr, $this->tokens[ $wpQueryObjectNamePtr ]['content'] ); + } + + /** + * Process function's body + * + * @param int $stackPtr The position in the stack where the token was found. + * @param string $variableName Variable name. + */ + private function processFunctionBody( $stackPtr, $variableName ) { + + $functionBodyScopeStart = $this->tokens[ $stackPtr ]['scope_opener']; + $functionBodyScopeEnd = $this->tokens[ $stackPtr ]['scope_closer']; + + $wpQueryVarUsed = $this->phpcsFile->findNext( + [ T_VARIABLE ], + $functionBodyScopeStart + 1, + $functionBodyScopeEnd, + false, + $variableName + ); + while ( $wpQueryVarUsed ) { + if ( $this->isPartOfIfConditional( $wpQueryVarUsed ) ) { + if ( $this->isEarlyMainQueryCheck( $wpQueryVarUsed ) ) { + return; + } + } elseif ( $this->isInsideIfConditonal( $wpQueryVarUsed ) ) { + if ( ! $this->isParentConditionalCheckingMainQuery( $wpQueryVarUsed ) ) { + $this->addPreGetPostsWarning( $wpQueryVarUsed ); + } + } elseif ( $this->isWPQueryMethodCall( $wpQueryVarUsed, 'set' ) ) { + $this->addPreGetPostsWarning( $wpQueryVarUsed ); + } + $wpQueryVarUsed = $this->phpcsFile->findNext( + [ T_VARIABLE ], + $wpQueryVarUsed + 1, + $functionBodyScopeEnd, + false, + $variableName + ); + } + } + + /** + * Consolidated violation. + * + * @param int $stackPtr The position in the stack where the token was found. + */ + private function addPreGetPostsWarning( $stackPtr ) { + $message = 'Main WP_Query is being modified without `$query->is_main_query()` check. Needs manual inspection.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'PreGetPosts' ); + } + + /** + * Is parent conditional checking is_main_query? + * + * @param int $stackPtr The position in the stack where the token was found. + * + * @return bool + */ + private function isParentConditionalCheckingMainQuery( $stackPtr ) { + + if ( array_key_exists( 'conditions', $this->tokens[ $stackPtr ] ) === false + || is_array( $this->tokens[ $stackPtr ]['conditions'] ) === false + || empty( $this->tokens[ $stackPtr ]['conditions'] ) === true + ) { + return false; + } + + $conditionStackPtrs = array_keys( $this->tokens[ $stackPtr ]['conditions'] ); + $lastConditionStackPtr = array_pop( $conditionStackPtrs ); + + while ( $this->tokens[ $stackPtr ]['conditions'][ $lastConditionStackPtr ] === T_IF ) { + + $next = $this->phpcsFile->findNext( + [ T_VARIABLE ], + $lastConditionStackPtr + 1, + null, + false, + $this->tokens[ $stackPtr ]['content'], + true + ); + while ( $next ) { + if ( $this->isWPQueryMethodCall( $next, 'is_main_query' ) === true ) { + return true; + } + $next = $this->phpcsFile->findNext( + [ T_VARIABLE ], + $next + 1, + null, + false, + $this->tokens[ $stackPtr ]['content'], + true + ); + } + + $lastConditionStackPtr = array_pop( $conditionStackPtrs ); + } + + return false; + } + + + /** + * Is the current code an early main query check? + * + * @param int $stackPtr The position in the stack where the token was found. + * + * @return bool + */ + private function isEarlyMainQueryCheck( $stackPtr ) { + + if ( ! $this->isWPQueryMethodCall( $stackPtr, 'is_main_query' ) ) { + return false; + } + + if ( array_key_exists( 'nested_parenthesis', $this->tokens[ $stackPtr ] ) === false + || empty( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) === true + ) { + return false; + } + + $parentheses = $this->tokens[ $stackPtr ]['nested_parenthesis']; + do { + $nestedParenthesisEnd = array_shift( $parentheses ); + if ( $nestedParenthesisEnd === null ) { + // Nothing left in the array. No parenthesis found with a non-closure owner. + return false; + } + + if ( isset( $this->tokens[ $nestedParenthesisEnd ]['parenthesis_owner'] ) + && $this->tokens[ $this->tokens[ $nestedParenthesisEnd ]['parenthesis_owner'] ]['code'] !== T_CLOSURE + ) { + break; + } + } while ( true ); + + $owner = $this->tokens[ $nestedParenthesisEnd ]['parenthesis_owner']; + if ( isset( $this->tokens[ $owner ]['scope_opener'], $this->tokens[ $owner ]['scope_closer'] ) === false ) { + // This may be an inline control structure (no braces). + $next = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + ( $nestedParenthesisEnd + 1 ), + null, + true + ); + + if ( $next !== false && $this->tokens[ $next ]['code'] === T_RETURN ) { + return true; + } + + return false; + } + + $next = $this->phpcsFile->findNext( + [ T_RETURN ], + $this->tokens[ $this->tokens[ $nestedParenthesisEnd ]['parenthesis_owner'] ]['scope_opener'], + $this->tokens[ $this->tokens[ $nestedParenthesisEnd ]['parenthesis_owner'] ]['scope_closer'], + false, + 'return', + true + ); + + if ( $next ) { + return true; + } + + return false; + } + + /** + * Is the current code a WP_Query call? + * + * @param int $stackPtr The position in the stack where the token was found. + * @param null $method Method. + * + * @return bool + */ + private function isWPQueryMethodCall( $stackPtr, $method = null ) { + $next = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + $stackPtr + 1, + null, + true, + null, + true + ); + + if ( ! $next || $this->tokens[ $next ]['type'] !== 'T_OBJECT_OPERATOR' ) { + return false; + } + + if ( $method === null ) { + return true; + } + + $next = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + $next + 1, + null, + true, + null, + true + ); + + return $next && $this->tokens[ $next ]['code'] === T_STRING && $method === $this->tokens[ $next ]['content']; + } + + /** + * Is the current token a part of a conditional? + * + * @param int $stackPtr The position in the stack where the token was found. + * + * @return bool + */ + private function isPartOfIfConditional( $stackPtr ) { + + if ( array_key_exists( 'nested_parenthesis', $this->tokens[ $stackPtr ] ) === true + && is_array( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) === true + && empty( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) === false + ) { + $previousLocalIf = $this->phpcsFile->findPrevious( + [ T_IF ], + $stackPtr - 1, + null, + false, + null, + true + ); + if ( $previousLocalIf !== false + && $this->tokens[ $previousLocalIf ]['parenthesis_opener'] < $stackPtr + && $this->tokens[ $previousLocalIf ]['parenthesis_closer'] > $stackPtr + ) { + return true; + } + } + return false; + } + + /** + * Is the current token inside a conditional? + * + * @param int $stackPtr The position in the stack where the token was found. + * + * @return bool + */ + private function isInsideIfConditonal( $stackPtr ) { + + if ( array_key_exists( 'conditions', $this->tokens[ $stackPtr ] ) === true + && is_array( $this->tokens[ $stackPtr ]['conditions'] ) === true + && empty( $this->tokens[ $stackPtr ]['conditions'] ) === false + ) { + $conditionStackPtrs = array_keys( $this->tokens[ $stackPtr ]['conditions'] ); + $lastConditionStackPtr = array_pop( $conditionStackPtrs ); + return $this->tokens[ $stackPtr ]['conditions'][ $lastConditionStackPtr ] === T_IF; + } + return false; + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Hooks/RestrictedHooksSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Hooks/RestrictedHooksSniff.php new file mode 100644 index 00000000..43054c2b --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Hooks/RestrictedHooksSniff.php @@ -0,0 +1,127 @@ + true, + 'add_action' => true, + ]; + + /** + * List of restricted filters by groups. + * + * @var array + */ + private $restricted_hook_groups = [ + 'upload_mimes' => [ + // TODO: This error message needs a link to the VIP Documentation, see https://github.com/Automattic/VIP-Coding-Standards/issues/235. + 'type' => 'Warning', + 'msg' => 'Please ensure that the mimes being filtered do not include insecure types (i.e. SVG, SWF, etc.). Manual inspection required.', + 'hooks' => [ + 'upload_mimes', + ], + ], + 'http_request' => [ + // https://docs.wpvip.com/technical-references/code-quality-and-best-practices/retrieving-remote-data/. + 'type' => 'Warning', + 'msg' => 'Please ensure that the timeout being filtered is not greater than 3s since remote requests require the user to wait for completion before the rest of the page will load. Manual inspection required.', + 'hooks' => [ + 'http_request_timeout', + 'http_request_args', + ], + ], + 'robotstxt' => [ + // https://docs.wpvip.com/how-tos/modify-the-robots-txt-file/. + 'type' => 'Warning', + 'msg' => 'Don\'t forget to flush the robots.txt cache by going to Settings > Reading and toggling the privacy settings.', + 'hooks' => [ + 'do_robotstxt', + 'robots_txt', + ], + ], + ]; + + /** + * Process the parameters of a matched function. + * + * @param int $stackPtr The position of the current token in the stack. + * @param array $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + foreach ( $this->restricted_hook_groups as $group => $group_args ) { + foreach ( $group_args['hooks'] as $hook ) { + if ( $this->normalize_hook_name_from_parameter( $parameters[1] ) === $hook ) { + $addMethod = 'add' . $group_args['type']; + $this->phpcsFile->{$addMethod}( $group_args['msg'], $stackPtr, $hook ); + } + } + } + } + + /** + * Normalize hook name parameter. + * + * @param array $parameter Array with information about a parameter. + * @return string Normalized hook name. + */ + private function normalize_hook_name_from_parameter( $parameter ) { + // If concatenation is found, build hook name. + $concat_ptr = $this->phpcsFile->findNext( + T_STRING_CONCAT, + $parameter['start'], + $parameter['end'], + false, + null, + true + ); + + if ( $concat_ptr ) { + $hook_name = ''; + for ( $i = $parameter['start'] + 1; $i < $parameter['end']; $i++ ) { + if ( $this->tokens[ $i ]['code'] === T_CONSTANT_ENCAPSED_STRING ) { + $hook_name .= str_replace( [ "'", '"' ], '', $this->tokens[ $i ]['content'] ); + } + } + } else { + $hook_name = $parameter['raw']; + } + + // Remove quotes (double and single), and use lowercase. + return strtolower( str_replace( [ "'", '"' ], '', $hook_name ) ); + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/DangerouslySetInnerHTMLSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/DangerouslySetInnerHTMLSniff.php new file mode 100644 index 00000000..9355756e --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/DangerouslySetInnerHTMLSniff.php @@ -0,0 +1,73 @@ +tokens[ $stackPtr ]['content'] !== 'dangerouslySetInnerHTML' ) { + // Looking for dangerouslySetInnerHTML only. + return; + } + + $nextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true ); + + if ( $this->tokens[ $nextToken ]['code'] !== T_EQUAL ) { + // Not an assignment. + return; + } + + $nextNextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, $nextToken + 1, null, true, null, true ); + + if ( $this->tokens[ $nextNextToken ]['code'] !== T_OBJECT ) { + // Not react syntax. + return; + } + + $message = "Any HTML passed to `%s` gets executed. Please make sure it's properly escaped."; + $data = [ $this->tokens[ $stackPtr ]['content'] ]; + $this->phpcsFile->addError( $message, $stackPtr, 'Found', $data ); + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/HTMLExecutingFunctionsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/HTMLExecutingFunctionsSniff.php new file mode 100644 index 00000000..66126d28 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/HTMLExecutingFunctionsSniff.php @@ -0,0 +1,134 @@ + content or target. + * Value indicates whether the function's arg is the content to be inserted, or the target where the inserted + * content is to be inserted before/after/replaced. For the latter, the content is in the preceding method's arg. + * + * @var array + */ + public $HTMLExecutingFunctions = [ + 'after' => 'content', // jQuery. + 'append' => 'content', // jQuery. + 'appendTo' => 'target', // jQuery. + 'before' => 'content', // jQuery. + 'html' => 'content', // jQuery. + 'insertAfter' => 'target', // jQuery. + 'insertBefore' => 'target', // jQuery. + 'prepend' => 'content', // jQuery. + 'prependTo' => 'target', // jQuery. + 'replaceAll' => 'target', // jQuery. + 'replaceWith' => 'content', // jQuery. + 'write' => 'content', + 'writeln' => 'content', + ]; + + /** + * A list of tokenizers this sniff supports. + * + * @var string[] + */ + public $supportedTokenizers = [ 'JS' ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + return [ + T_STRING, + ]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @return void + */ + public function process_token( $stackPtr ) { + + if ( ! isset( $this->HTMLExecutingFunctions[ $this->tokens[ $stackPtr ]['content'] ] ) ) { + // Looking for specific functions only. + return; + } + + if ( $this->HTMLExecutingFunctions[ $this->tokens[ $stackPtr ]['content'] ] === 'content' ) { + $nextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true ); + + if ( $this->tokens[ $nextToken ]['code'] !== T_OPEN_PARENTHESIS ) { + // Not a function. + return; + } + + $parenthesis_closer = $this->tokens[ $nextToken ]['parenthesis_closer']; + + while ( $nextToken < $parenthesis_closer ) { + $nextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, $nextToken + 1, null, true, null, true ); + if ( $this->tokens[ $nextToken ]['code'] === T_STRING ) { // Contains a variable, function call or something else dynamic. + $message = 'Any HTML passed to `%s` gets executed. Make sure it\'s properly escaped.'; + $data = [ $this->tokens[ $stackPtr ]['content'] ]; + $this->phpcsFile->addWarning( $message, $stackPtr, $this->tokens[ $stackPtr ]['content'], $data ); + + return; + } + } + } elseif ( $this->HTMLExecutingFunctions[ $this->tokens[ $stackPtr ]['content'] ] === 'target' ) { + $prevToken = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $stackPtr - 1, null, true, null, true ); + + if ( $this->tokens[ $prevToken ]['code'] !== T_OBJECT_OPERATOR ) { + return; + } + + $prevPrevToken = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $prevToken - 1, null, true, null, true ); + + if ( $this->tokens[ $prevPrevToken ]['code'] !== T_CLOSE_PARENTHESIS ) { + // Not a function call, but may be a variable containing an element reference, so just + // flag all remaining instances of these target HTML executing functions. + $message = 'Any HTML used with `%s` gets executed. Make sure it\'s properly escaped.'; + $data = [ $this->tokens[ $stackPtr ]['content'] ]; + $this->phpcsFile->addWarning( $message, $stackPtr, $this->tokens[ $stackPtr ]['content'], $data ); + + return; + } + + // Check if it's a function call (typically $() ) that contains a dynamic part. + $parenthesis_opener = $this->tokens[ $prevPrevToken ]['parenthesis_opener']; + + while ( $prevPrevToken > $parenthesis_opener ) { + $prevPrevToken = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $prevPrevToken - 1, null, true, null, true ); + if ( $this->tokens[ $prevPrevToken ]['code'] === T_STRING ) { // Contains a variable, function call or something else dynamic. + $message = 'Any HTML used with `%s` gets executed. Make sure it\'s properly escaped.'; + $data = [ $this->tokens[ $stackPtr ]['content'] ]; + $this->phpcsFile->addWarning( $message, $stackPtr, $this->tokens[ $stackPtr ]['content'], $data ); + + return; + } + } + } + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/InnerHTMLSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/InnerHTMLSniff.php new file mode 100644 index 00000000..aac49116 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/InnerHTMLSniff.php @@ -0,0 +1,87 @@ +tokens[ $stackPtr ]['content'] !== 'innerHTML' ) { + // Looking for .innerHTML only. + return; + } + + $prevToken = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $stackPtr - 1, null, true, null, true ); + + if ( $this->tokens[ $prevToken ]['code'] !== T_OBJECT_OPERATOR ) { + return; + } + + $nextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true ); + + if ( $this->tokens[ $nextToken ]['code'] !== T_EQUAL ) { + // Not an assignment. + return; + } + + $nextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, $nextToken + 1, null, true, null, true ); + $foundVariable = false; + + while ( $nextToken !== false && $this->tokens[ $nextToken ]['code'] !== T_SEMICOLON ) { + + if ( $this->tokens[ $nextToken ]['code'] === T_STRING ) { + $foundVariable = true; + break; + } + + $nextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, $nextToken + 1, null, true, null, true ); + } + + if ( $foundVariable === true ) { + $message = 'Any HTML passed to `%s` gets executed. Consider using `.textContent` or make sure that used variables are properly escaped.'; + $data = [ $this->tokens[ $stackPtr ]['content'] ]; + $this->phpcsFile->addWarning( $message, $stackPtr, 'Found', $data ); + } + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/StringConcatSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/StringConcatSniff.php new file mode 100644 index 00000000..74fab5fc --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/StringConcatSniff.php @@ -0,0 +1,76 @@ +phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true ); + + if ( $this->tokens[ $nextToken ]['code'] === T_CONSTANT_ENCAPSED_STRING && strpos( $this->tokens[ $nextToken ]['content'], '<' ) !== false && preg_match( '/\<\/[a-zA-Z]+/', $this->tokens[ $nextToken ]['content'] ) === 1 ) { + $data = [ '+' . $this->tokens[ $nextToken ]['content'] ]; + $this->addFoundError( $stackPtr, $data ); + } + + $prevToken = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $stackPtr - 1, null, true, null, true ); + + if ( $this->tokens[ $prevToken ]['code'] === T_CONSTANT_ENCAPSED_STRING && strpos( $this->tokens[ $prevToken ]['content'], '<' ) !== false && preg_match( '/\<[a-zA-Z]+/', $this->tokens[ $prevToken ]['content'] ) === 1 ) { + $data = [ $this->tokens[ $nextToken ]['content'] . '+' ]; + $this->addFoundError( $stackPtr, $data ); + } + } + + /** + * Consolidated violation. + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * @param array $data Replacements for the error message. + */ + private function addFoundError( $stackPtr, array $data ) { + $message = 'HTML string concatenation detected, this is a security risk, use DOM node construction or a templating language instead: %s.'; + $this->phpcsFile->addError( $message, $stackPtr, 'Found', $data ); + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/StrippingTagsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/StrippingTagsSniff.php new file mode 100644 index 00000000..8017fe58 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/StrippingTagsSniff.php @@ -0,0 +1,76 @@ +tokens[ $stackPtr ]['content'] !== 'html' ) { + // Looking for html() only. + return; + } + + $nextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true ); + + if ( $this->tokens[ $nextToken ]['code'] !== T_OPEN_PARENTHESIS ) { + // Not a function. + return; + } + + $afterFunctionCall = $this->phpcsFile->findNext( Tokens::$emptyTokens, $this->tokens[ $nextToken ]['parenthesis_closer'] + 1, null, true, null, true ); + + if ( $this->tokens[ $afterFunctionCall ]['code'] !== T_OBJECT_OPERATOR ) { + return; + } + + $nextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, $afterFunctionCall + 1, null, true, null, true ); + + if ( $this->tokens[ $nextToken ]['code'] === T_STRING && $this->tokens[ $nextToken ]['content'] === 'text' ) { + $message = 'Vulnerable tag stripping approach detected.'; + $this->phpcsFile->addError( $message, $stackPtr, 'VulnerableTagStripping' ); + } + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/WindowSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/WindowSniff.php new file mode 100644 index 00000000..e233e479 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/JS/WindowSniff.php @@ -0,0 +1,131 @@ + [ + 'href' => true, + 'protocol' => true, + 'host' => true, + 'hostname' => true, + 'pathname' => true, + 'search' => true, + 'hash' => true, + 'username' => true, + 'port' => true, + 'password' => true, + ], + 'name' => true, + 'status' => true, + ]; + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @return void + */ + public function process_token( $stackPtr ) { + + if ( $this->tokens[ $stackPtr ]['content'] !== 'window' ) { + // Doesn't begin with 'window', bail. + return; + } + + $nextTokenPtr = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true ); + $nextToken = $this->tokens[ $nextTokenPtr ]['code']; + if ( $nextToken !== T_OBJECT_OPERATOR && $nextToken !== T_OPEN_SQUARE_BRACKET ) { + // No . or [' next, bail. + return; + } + + $nextNextTokenPtr = $this->phpcsFile->findNext( Tokens::$emptyTokens, $nextTokenPtr + 1, null, true, null, true ); + if ( $nextNextTokenPtr === false ) { + // Something went wrong, bail. + return; + } + + $nextNextToken = str_replace( [ '"', "'" ], '', $this->tokens[ $nextNextTokenPtr ]['content'] ); + if ( ! isset( $this->windowProperties[ $nextNextToken ] ) ) { + // Not in $windowProperties, bail. + return; + } + + $nextNextNextTokenPtr = $this->phpcsFile->findNext( array_merge( [ T_CLOSE_SQUARE_BRACKET ], Tokens::$emptyTokens ), $nextNextTokenPtr + 1, null, true, null, true ); + $nextNextNextToken = $this->tokens[ $nextNextNextTokenPtr ]['code']; + + $nextNextNextNextToken = false; + if ( $nextNextNextToken === T_OBJECT_OPERATOR || $nextNextNextToken === T_OPEN_SQUARE_BRACKET ) { + $nextNextNextNextTokenPtr = $this->phpcsFile->findNext( Tokens::$emptyTokens, $nextNextNextTokenPtr + 1, null, true, null, true ); + if ( $nextNextNextNextTokenPtr === false ) { + // Something went wrong, bail. + return; + } + + $nextNextNextNextToken = str_replace( [ '"', "'" ], '', $this->tokens[ $nextNextNextNextTokenPtr ]['content'] ); + if ( ! isset( $this->windowProperties[ $nextNextToken ][ $nextNextNextNextToken ] ) ) { + // Not in $windowProperties, bail. + return; + } + } + + $windowProperty = 'window.'; + $windowProperty .= $nextNextNextNextToken ? $nextNextToken . '.' . $nextNextNextNextToken : $nextNextToken; + $data = [ $windowProperty ]; + + $prevTokenPtr = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, $stackPtr - 1, null, true, null, true ); + + if ( $this->tokens[ $prevTokenPtr ]['code'] === T_EQUAL ) { + // Variable assignment. + $message = 'Data from JS global "%s" may contain user-supplied values and should be checked.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'VarAssignment', $data ); + + return; + } + + $message = 'Data from JS global "%s" may contain user-supplied values and should be sanitized before output to prevent XSS.'; + $this->phpcsFile->addError( $message, $stackPtr, $nextNextToken, $data ); + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/BatcacheWhitelistedParamsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/BatcacheWhitelistedParamsSniff.php new file mode 100644 index 00000000..1b42498b --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/BatcacheWhitelistedParamsSniff.php @@ -0,0 +1,113 @@ +tokens[ $stackPtr ]['content'] !== '$_GET' ) { + return; + } + + $key = $this->phpcsFile->findNext( array_merge( Tokens::$emptyTokens, [ T_OPEN_SQUARE_BRACKET ] ), $stackPtr + 1, null, true ); + + if ( $this->tokens[ $key ]['code'] !== T_CONSTANT_ENCAPSED_STRING ) { + return; + } + + $variable_name = $this->tokens[ $key ]['content']; + + $variable_name = substr( $variable_name, 1, -1 ); + + if ( in_array( $variable_name, $this->whitelistes_batcache_params, true ) === true ) { + $message = 'Batcache whitelisted GET param, `%s`, found. Batcache whitelisted parameters get stripped and are not available in PHP.'; + $data = [ $variable_name ]; + $this->phpcsFile->addWarning( $message, $stackPtr, 'StrippedGetParam', $data ); + + return; + } + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php new file mode 100644 index 00000000..806bebef --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/CacheValueOverrideSniff.php @@ -0,0 +1,146 @@ + + * echo esc_url( wpcom_vip_get_term_link( $term ) ); + * + * + * @package VIPCS\WordPressVIPMinimum + */ +class CacheValueOverrideSniff extends Sniff { + + /** + * Returns the token types that this sniff is interested in. + * + * @return array(int) + */ + public function register() { + return [ T_STRING ]; + } + + + /** + * Processes the tokens that this sniff is interested in. + * + * @param int $stackPtr The position in the stack where the token was found. + * + * @return void + */ + public function process_token( $stackPtr ) { + + $functionName = $this->tokens[ $stackPtr ]['content']; + + if ( $functionName !== 'wp_cache_get' ) { + // Not a function we are looking for. + return; + } + + if ( $this->isFunctionCall( $stackPtr ) === false ) { + // Not a function call. + return; + } + + $variablePos = $this->isVariableAssignment( $stackPtr ); + + if ( $variablePos === false ) { + // Not a variable assignment. + return; + } + + $variableToken = $this->tokens[ $variablePos ]; + $variableName = $variableToken['content']; + + // Find the next non-empty token. + $openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); + + // Find the closing bracket. + $closeBracket = $this->tokens[ $openBracket ]['parenthesis_closer']; + + $nextVariableOccurrence = $this->phpcsFile->findNext( T_VARIABLE, $closeBracket + 1, null, false, $variableName ); + + $rightAfterNextVariableOccurence = $this->phpcsFile->findNext( Tokens::$emptyTokens, $nextVariableOccurrence + 1, null, true, null, true ); + + if ( $this->tokens[ $rightAfterNextVariableOccurence ]['code'] !== T_EQUAL ) { + // Not a value override. + return; + } + + $valueAfterEqualSign = $this->phpcsFile->findNext( Tokens::$emptyTokens, $rightAfterNextVariableOccurence + 1, null, true, null, true ); + + if ( $this->tokens[ $valueAfterEqualSign ]['code'] === T_FALSE ) { + $message = 'Obtained cached value in `%s` is being overridden. Disabling caching?'; + $data = [ $variableName ]; + $this->phpcsFile->addError( $message, $nextVariableOccurrence, 'CacheValueOverride', $data ); + } + } + + /** + * Check whether the examined code is a function call. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return bool + */ + private function isFunctionCall( $stackPtr ) { + + // Find the next non-empty token. + $openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); + + if ( $this->tokens[ $openBracket ]['code'] !== T_OPEN_PARENTHESIS ) { + // Not a function call. + return false; + } + + // Find the previous non-empty token. + $search = Tokens::$emptyTokens; + $search[] = T_BITWISE_AND; + $previous = $this->phpcsFile->findPrevious( $search, $stackPtr - 1, null, true ); + + // It's a function definition, not a function call, so return false. + return ! ( $this->tokens[ $previous ]['code'] === T_FUNCTION ); + } + + /** + * Check whether the examined code is a variable assignment. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return bool + */ + private function isVariableAssignment( $stackPtr ) { + + // Find the previous non-empty token. + $search = Tokens::$emptyTokens; + $search[] = T_BITWISE_AND; + $previous = $this->phpcsFile->findPrevious( $search, $stackPtr - 1, null, true ); + + if ( $this->tokens[ $previous ]['code'] !== T_EQUAL ) { + // It's not a variable assignment. + return false; + } + + $previous = $this->phpcsFile->findPrevious( $search, $previous - 1, null, true ); + + if ( $this->tokens[ $previous ]['code'] !== T_VARIABLE ) { + // It's not a variable assignment. + return false; + } + + return $previous; + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/FetchingRemoteDataSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/FetchingRemoteDataSniff.php new file mode 100644 index 00000000..e3fc4330 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/FetchingRemoteDataSniff.php @@ -0,0 +1,61 @@ +tokens[ $stackPtr ]['content']; + if ( $functionName !== 'file_get_contents' ) { + return; + } + + $data = [ $this->tokens[ $stackPtr ]['content'] ]; + + $fileNameStackPtr = $this->phpcsFile->findNext( Tokens::$stringTokens, $stackPtr + 1, null, false, null, true ); + if ( $fileNameStackPtr === false ) { + $message = '`%s()` is highly discouraged for remote requests, please use `wpcom_vip_file_get_contents()` or `vip_safe_wp_remote_get()` instead. If it\'s for a local file please use WP_Filesystem instead.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'FileGetContentsUnknown', $data ); + } + + $fileName = $this->tokens[ $fileNameStackPtr ]['content']; + + $isRemoteFile = ( strpos( $fileName, '://' ) !== false ); + if ( $isRemoteFile === true ) { + $message = '`%s()` is highly discouraged for remote requests, please use `wpcom_vip_file_get_contents()` or `vip_safe_wp_remote_get()` instead.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'FileGetContentsRemoteFile', $data ); + } + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/LowExpiryCacheTimeSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/LowExpiryCacheTimeSniff.php new file mode 100644 index 00000000..23639560 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/LowExpiryCacheTimeSniff.php @@ -0,0 +1,206 @@ + true, + 'wp_cache_add' => true, + 'wp_cache_replace' => true, + ]; + + /** + * List of WP time constants, see https://codex.wordpress.org/Easier_Expression_of_Time_Constants. + * + * @var array + */ + protected $wp_time_constants = [ + 'MINUTE_IN_SECONDS' => 60, + 'HOUR_IN_SECONDS' => 3600, + 'DAY_IN_SECONDS' => 86400, + 'WEEK_IN_SECONDS' => 604800, + 'MONTH_IN_SECONDS' => 2592000, + 'YEAR_IN_SECONDS' => 31536000, + ]; + + /** + * Process the parameters of a matched function. + * + * @param int $stackPtr The position of the current token in the stack. + * @param array $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + if ( isset( $parameters[4] ) === false ) { + // If no cache expiry time, bail (i.e. we don't want to flag for something like feeds where it is cached indefinitely until a hook runs). + return; + } + + $param = $parameters[4]; + $tokensAsString = ''; + $reportPtr = null; + $openParens = 0; + + $message = 'Cache expiry time could not be determined. Please inspect that the fourth parameter passed to %s() evaluates to 300 seconds or more. Found: "%s"'; + $error_code = 'CacheTimeUndetermined'; + $data = [ $matched_content, $parameters[4]['raw'] ]; + + for ( $i = $param['start']; $i <= $param['end']; $i++ ) { + if ( isset( Tokens::$emptyTokens[ $this->tokens[ $i ]['code'] ] ) === true ) { + $tokensAsString .= ' '; + continue; + } + + if ( $this->tokens[ $i ]['code'] === T_NS_SEPARATOR ) { + /* + * Ignore namespace separators. If it's part of a global WP time constant, it will be + * handled correctly. If it's used in any other context, another token *will* trigger the + * "undetermined" warning anyway. + */ + continue; + } + + if ( isset( $reportPtr ) === false ) { + // Set the report pointer to the first non-empty token we encounter. + $reportPtr = $i; + } + + if ( $this->tokens[ $i ]['code'] === T_LNUMBER + || $this->tokens[ $i ]['code'] === T_DNUMBER + ) { + // Integer or float. + $tokensAsString .= $this->tokens[ $i ]['content']; + continue; + } + + if ( $this->tokens[ $i ]['code'] === T_FALSE + || $this->tokens[ $i ]['code'] === T_NULL + ) { + $tokensAsString .= 0; + continue; + } + + if ( $this->tokens[ $i ]['code'] === T_TRUE ) { + $tokensAsString .= 1; + continue; + } + + if ( isset( Tokens::$arithmeticTokens[ $this->tokens[ $i ]['code'] ] ) === true ) { + $tokensAsString .= $this->tokens[ $i ]['content']; + continue; + } + + // If using time constants, we need to convert to a number. + if ( $this->tokens[ $i ]['code'] === T_STRING + && isset( $this->wp_time_constants[ $this->tokens[ $i ]['content'] ] ) === true + ) { + $tokensAsString .= $this->wp_time_constants[ $this->tokens[ $i ]['content'] ]; + continue; + } + + if ( $this->tokens[ $i ]['code'] === T_OPEN_PARENTHESIS ) { + $tokensAsString .= $this->tokens[ $i ]['content']; + ++$openParens; + continue; + } + + if ( $this->tokens[ $i ]['code'] === T_CLOSE_PARENTHESIS ) { + $tokensAsString .= $this->tokens[ $i ]['content']; + --$openParens; + continue; + } + + if ( $this->tokens[ $i ]['code'] === T_CONSTANT_ENCAPSED_STRING ) { + $content = $this->strip_quotes( $this->tokens[ $i ]['content'] ); + if ( is_numeric( $content ) === true ) { + $tokensAsString .= $content; + continue; + } + } + + // Encountered an unexpected token. Manual inspection needed. + $this->phpcsFile->addWarning( $message, $reportPtr, $error_code, $data ); + + return; + } + + if ( $tokensAsString === '' ) { + // Nothing found to evaluate. + return; + } + + $tokensAsString = trim( $tokensAsString ); + + if ( $openParens !== 0 ) { + /* + * Shouldn't be possible as that would indicate a parse error in the original code, + * but let's prevent getting parse errors in the `eval`-ed code. + */ + if ( $openParens > 0 ) { + $tokensAsString .= str_repeat( ')', $openParens ); + } else { + $tokensAsString = str_repeat( '(', abs( $openParens ) ) . $tokensAsString; + } + } + + $time = @eval( "return $tokensAsString;" ); // phpcs:ignore Squiz.PHP.Eval,WordPress.PHP.NoSilencedErrors -- No harm here. + + if ( $time === false ) { + /* + * The eval resulted in a parse error. This will only happen for backfilled + * arithmetic operator tokens, like T_POW, on PHP versions in which the token + * did not exist. In that case, flag for manual inspection. + */ + $this->phpcsFile->addWarning( $message, $reportPtr, $error_code, $data ); + return; + } + + if ( $time < 300 && (int) $time !== 0 ) { + $message = 'Low cache expiry time of %s seconds detected. It is recommended to have 300 seconds or more.'; + $data = [ $time ]; + + if ( (string) $time !== $tokensAsString ) { + $message .= ' Found: "%s"'; + $data[] = $tokensAsString; + } + + $this->phpcsFile->addWarning( $message, $reportPtr, 'LowCacheTime', $data ); + } + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/NoPagingSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/NoPagingSniff.php new file mode 100644 index 00000000..9e23fc4f --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/NoPagingSniff.php @@ -0,0 +1,61 @@ + [ + 'type' => 'error', + 'keys' => [ + 'nopaging', + ], + ], + ]; + } + + /** + * Callback to process each confirmed key, to check value. + * + * @param string $key Array index / key. + * @param mixed $val Assigned value. + * @param int $line Token line. + * @param array $group Group definition. + * @return mixed FALSE if no match, TRUE if matches, STRING if matches + * with custom error message passed to ->process(). + */ + public function callback( $key, $val, $line, $group ) { + $key = strtolower( $key ); + + if ( $key === 'nopaging' && ( $val === 'true' || $val === 1 ) ) { + return 'Disabling pagination is prohibited in VIP context, do not set `%s` to `%s` ever.'; + } + + return false; + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/OrderByRandSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/OrderByRandSniff.php new file mode 100644 index 00000000..e6e64c6f --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/OrderByRandSniff.php @@ -0,0 +1,59 @@ + rand. + * + * @link https://docs.wpvip.com/technical-references/code-review/#order-by-rand + * + * @package VIPCS\WordPressVIPMinimum + * + * @since 0.5.0 + */ +class OrderByRandSniff extends AbstractArrayAssignmentRestrictionsSniff { + + /** + * Groups of variables to restrict. + * + * @return array + */ + public function getGroups() { + return [ + 'orderby' => [ + 'type' => 'error', + 'keys' => [ + 'orderby', + ], + ], + ]; + } + + /** + * Callback to process each confirmed key, to check value + * This must be extended to add the logic to check assignment value + * + * @param string $key Array index / key. + * @param mixed $val Assigned value. + * @param int $line Token line. + * @param array $group Group definition. + * @return mixed FALSE if no match, TRUE if matches, STRING if matches with custom error message passed to ->process(). + */ + public function callback( $key, $val, $line, $group ) { + if ( strtolower( $val ) === 'rand' ) { + return 'Detected forbidden query_var "%s" of "%s". Use vip_get_random_posts() instead.'; + } + + return false; + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/RegexpCompareSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/RegexpCompareSniff.php new file mode 100644 index 00000000..dea5fd5f --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/RegexpCompareSniff.php @@ -0,0 +1,65 @@ + array( + * 'groupname' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Dont use this one please!', + * 'keys' => array( 'key1', 'another_key' ), + * 'callback' => array( 'class', 'method' ), // Optional. + * ) + * ) + * + * @return array + */ + public function getGroups() { + return [ + 'compare' => [ + 'type' => 'error', + 'keys' => [ + 'compare', + 'meta_compare', + ], + ], + ]; + } + + /** + * Callback to process each confirmed key, to check value. + * This must be extended to add the logic to check assignment value. + * + * @param string $key Array index / key. + * @param mixed $val Assigned value. + * @param int $line Token line. + * @param array $group Group definition. + * @return mixed FALSE if no match, TRUE if matches, STRING if matches + * with custom error message passed to ->process(). + */ + public function callback( $key, $val, $line, $group ) { + if ( strpos( $val, 'NOT REGEXP' ) === 0 + || strpos( $val, 'REGEXP' ) === 0 + || in_array( $val, [ 'REGEXP', 'NOT REGEXP' ], true ) === true + ) { + return 'Detected regular expression comparison. `%s` is set to `%s`.'; + } + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/RemoteRequestTimeoutSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/RemoteRequestTimeoutSniff.php new file mode 100644 index 00000000..974532f4 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/RemoteRequestTimeoutSniff.php @@ -0,0 +1,61 @@ + array( + * 'groupname' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Dont use this one please!', + * 'keys' => array( 'key1', 'another_key' ), + * 'callback' => array( 'class', 'method' ), // Optional. + * ) + * ) + * + * @return array + */ + public function getGroups() { + return [ + 'timeout' => [ + 'type' => 'error', + 'keys' => [ + 'timeout', + ], + ], + ]; + } + + /** + * Callback to process each confirmed key, to check value. + * This must be extended to add the logic to check assignment value. + * + * @param string $key Array index / key. + * @param mixed $val Assigned value. + * @param int $line Token line. + * @param array $group Group definition. + * @return mixed FALSE if no match, TRUE if matches, STRING if matches + * with custom error message passed to ->process(). + */ + public function callback( $key, $val, $line, $group ) { + if ( (int) $val > 3 ) { + return 'Detected high remote request timeout. `%s` is set to `%d`.'; + } + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/TaxonomyMetaInOptionsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/TaxonomyMetaInOptionsSniff.php new file mode 100644 index 00000000..c9c815be --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/TaxonomyMetaInOptionsSniff.php @@ -0,0 +1,134 @@ +tokens[ $stackPtr ]['content'], $this->option_functions, true ) === false ) { + return; + } + + $openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); + + if ( $this->tokens[ $openBracket ]['code'] !== T_OPEN_PARENTHESIS ) { + return; + } + + $param_ptr = $this->phpcsFile->findNext( Tokens::$emptyTokens, $openBracket + 1, null, true ); + + if ( $this->tokens[ $param_ptr ]['code'] === T_DOUBLE_QUOTED_STRING ) { + foreach ( $this->taxonomy_term_patterns as $taxonomy_term_pattern ) { + if ( strpos( $this->tokens[ $param_ptr ]['content'], $taxonomy_term_pattern ) !== false ) { + $this->addPossibleTermMetaInOptionsWarning( $stackPtr ); + return; + } + } + } elseif ( $this->tokens[ $param_ptr ]['code'] === T_CONSTANT_ENCAPSED_STRING ) { + + $string_concat = $this->phpcsFile->findNext( Tokens::$emptyTokens, $param_ptr + 1, null, true ); + if ( $this->tokens[ $string_concat ]['code'] !== T_STRING_CONCAT ) { + return; + } + + $variable_name = $this->phpcsFile->findNext( Tokens::$emptyTokens, $string_concat + 1, null, true ); + if ( $this->tokens[ $variable_name ]['code'] !== T_VARIABLE ) { + return; + } + + foreach ( $this->taxonomy_term_patterns as $taxonomy_term_pattern ) { + if ( strpos( $this->tokens[ $variable_name ]['content'], $taxonomy_term_pattern ) !== false ) { + $this->addPossibleTermMetaInOptionsWarning( $stackPtr ); + return; + } + } + + $object_operator = $this->phpcsFile->findNext( Tokens::$emptyTokens, $variable_name + 1, null, true ); + if ( $this->tokens[ $object_operator ]['code'] !== T_OBJECT_OPERATOR ) { + return; + } + + $object_property = $this->phpcsFile->findNext( Tokens::$emptyTokens, $object_operator + 1, null, true ); + if ( $this->tokens[ $object_property ]['code'] !== T_STRING ) { + return; + } + + foreach ( $this->taxonomy_term_patterns as $taxonomy_term_pattern ) { + if ( strpos( $this->tokens[ $object_property ]['content'], $taxonomy_term_pattern ) !== false ) { + $this->addPossibleTermMetaInOptionsWarning( $stackPtr ); + return; + } + } + } + } + + /** + * Helper method for composing the Warning for all possible cases. + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @return void + */ + public function addPossibleTermMetaInOptionsWarning( $stackPtr ) { + $message = 'Possible detection of storing taxonomy term meta in options table. Needs manual inspection. All such data should be stored in term_meta.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'PossibleTermMetaInOptions' ); + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/WPQueryParamsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/WPQueryParamsSniff.php new file mode 100644 index 00000000..9b15ef63 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Performance/WPQueryParamsSniff.php @@ -0,0 +1,104 @@ + array( + * 'groupname' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Dont use this one please!', + * 'keys' => array( 'key1', 'another_key' ), + * 'callback' => array( 'class', 'method' ), // Optional. + * ) + * ) + * + * @return array + */ + public function getGroups() { + return [ + 'PostNotIn' => [ + 'type' => 'warning', + 'message' => 'Using `exclude`, which is subsequently used by `post__not_in`, should be done with caution, see https://docs.wpvip.com/how-tos/improve-performance-by-removing-usage-of-post__not_in/ for more information.', + 'keys' => [ + 'exclude', + ], + ], + ]; + } + + /** + * Process this test when one of its tokens is encountered + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @return void + */ + public function process_token( $stackPtr ) { + + if ( trim( $this->tokens[ $stackPtr ]['content'], '\'' ) === 'suppress_filters' ) { + + $next_token = $this->phpcsFile->findNext( array_merge( Tokens::$emptyTokens, [ T_EQUAL, T_CLOSE_SQUARE_BRACKET, T_DOUBLE_ARROW ] ), $stackPtr + 1, null, true ); + + if ( $this->tokens[ $next_token ]['code'] === T_TRUE ) { + // https://docs.wpvip.com/technical-references/caching/uncached-functions/. + $message = 'Setting `suppress_filters` to `true` is prohibited.'; + $this->phpcsFile->addError( $message, $stackPtr, 'SuppressFiltersTrue' ); + } + } + + if ( trim( $this->tokens[ $stackPtr ]['content'], '\'' ) === 'post__not_in' ) { + $message = 'Using `post__not_in` should be done with caution, see https://docs.wpvip.com/how-tos/improve-performance-by-removing-usage-of-post__not_in/ for more information.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'PostNotIn' ); + } + + parent::process_token( $stackPtr ); + } + + /** + * Callback to process a confirmed key which doesn't need custom logic, but should always error. + * + * @param string $key Array index / key. + * @param mixed $val Assigned value. + * @param int $line Token line. + * @param array $group Group definition. + * @return mixed FALSE if no match, TRUE if matches, STRING if matches + * with custom error message passed to ->process(). + */ + public function callback( $key, $val, $line, $group ) { + return true; + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/EscapingVoidReturnFunctionsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/EscapingVoidReturnFunctionsSniff.php new file mode 100644 index 00000000..5c7f4e72 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/EscapingVoidReturnFunctionsSniff.php @@ -0,0 +1,70 @@ +tokens[ $stackPtr ]['content'], 'esc_' ) !== 0 && strpos( $this->tokens[ $stackPtr ]['content'], 'wp_kses' ) !== 0 ) { + // Not what we are looking for. + return; + } + + $next_token = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); + + if ( $this->tokens[ $next_token ]['code'] !== T_OPEN_PARENTHESIS ) { + // Not a function call. + return; + } + + $next_token = $this->phpcsFile->findNext( Tokens::$emptyTokens, $next_token + 1, null, true ); + + if ( $this->tokens[ $next_token ]['code'] !== T_STRING ) { + // Not what we are looking for. + return; + } + + if ( isset( $this->printingFunctions[ $this->tokens[ $next_token ]['content'] ] ) ) { + $message = 'Attempting to escape `%s()` which is printing its output.'; + $data = [ $this->tokens[ $next_token ]['content'] ]; + $this->phpcsFile->addError( $message, $stackPtr, 'Found', $data ); + return; + } + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/ExitAfterRedirectSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/ExitAfterRedirectSniff.php new file mode 100644 index 00000000..dfacc425 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/ExitAfterRedirectSniff.php @@ -0,0 +1,68 @@ +tokens[ $stackPtr ]['content'] !== 'wp_redirect' && $this->tokens[ $stackPtr ]['content'] !== 'wp_safe_redirect' ) { + return; + } + + $openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); + + if ( $this->tokens[ $openBracket ]['code'] !== T_OPEN_PARENTHESIS ) { + return; + } + + $next_token = $this->phpcsFile->findNext( array_merge( Tokens::$emptyTokens, [ T_SEMICOLON, T_CLOSE_PARENTHESIS ] ), $this->tokens[ $openBracket ]['parenthesis_closer'] + 1, null, true ); + + $message = '`%s()` should almost always be followed by a call to `exit;`.'; + $data = [ $this->tokens[ $stackPtr ]['content'] ]; + + if ( $this->tokens[ $next_token ]['code'] === T_OPEN_CURLY_BRACKET ) { + $is_exit_in_scope = false; + for ( $i = $this->tokens[ $next_token ]['scope_opener']; $i <= $this->tokens[ $next_token ]['scope_closer']; $i++ ) { + if ( $this->tokens[ $i ]['code'] === T_EXIT ) { + $is_exit_in_scope = true; + } + } + if ( $is_exit_in_scope === false ) { + $this->phpcsFile->addError( $message, $stackPtr, 'NoExitInConditional', $data ); + } + } elseif ( $this->tokens[ $next_token ]['code'] !== T_EXIT ) { + $this->phpcsFile->addError( $message, $stackPtr, 'NoExit', $data ); + } + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/MustacheSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/MustacheSniff.php new file mode 100644 index 00000000..c6465c2d --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/MustacheSniff.php @@ -0,0 +1,77 @@ +tokens[ $stackPtr ]['content'], '{{{' ) !== false || strpos( $this->tokens[ $stackPtr ]['content'], '}}}' ) !== false ) { + // Mustache unescaped output notation. + $message = 'Found Mustache unescaped output notation: "{{{}}}".'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'OutputNotation' ); + } + + if ( strpos( $this->tokens[ $stackPtr ]['content'], '{{&' ) !== false ) { + // Mustache unescaped variable notation. + $message = 'Found Mustache unescape variable notation: "{{&".'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'VariableNotation' ); + } + + if ( strpos( $this->tokens[ $stackPtr ]['content'], '{{=' ) !== false ) { + // Mustache delimiter change. + $new_delimiter = trim( str_replace( [ '{{=', '=}}' ], '', substr( $this->tokens[ $stackPtr ]['content'], 0, strpos( $this->tokens[ $stackPtr ]['content'], '=}}' ) + 3 ) ) ); + $message = 'Found Mustache delimiter change notation. New delimiter is: %s.'; + $data = [ $new_delimiter ]; + $this->phpcsFile->addWarning( $message, $stackPtr, 'DelimiterChange', $data ); + } + + if ( strpos( $this->tokens[ $stackPtr ]['content'], 'SafeString' ) !== false ) { + // Handlebars.js Handlebars.SafeString does not get escaped. + $message = 'Found Handlebars.SafeString call which does not get escaped.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'SafeString' ); + } + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/PHPFilterFunctionsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/PHPFilterFunctionsSniff.php new file mode 100644 index 00000000..84278541 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/PHPFilterFunctionsSniff.php @@ -0,0 +1,90 @@ + true, + 'filter_input' => true, + 'filter_var_array' => true, + 'filter_input_array' => true, + ]; + + /** + * List of restricted filter names. + * + * @var array + */ + private $restricted_filters = [ + 'FILTER_DEFAULT' => true, + 'FILTER_UNSAFE_RAW' => true, + ]; + + /** + * Process the parameters of a matched function. + * + * @param int $stackPtr The position of the current token in the stack. + * @param array $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + if ( $matched_content === 'filter_input' ) { + if ( count( $parameters ) === 2 ) { + $message = 'Missing third parameter for "%s".'; + $data = [ $matched_content ]; + $this->phpcsFile->addWarning( $message, $stackPtr, 'MissingThirdParameter', $data ); + } + + if ( isset( $parameters[3], $this->restricted_filters[ $parameters[3]['raw'] ] ) ) { + $message = 'Please use an appropriate filter to sanitize, as "%s" does no filtering, see: http://php.net/manual/en/filter.filters.sanitize.php.'; + $data = [ strtoupper( $parameters[3]['raw'] ) ]; + $this->phpcsFile->addWarning( $message, $stackPtr, 'RestrictedFilter', $data ); + } + } else { + if ( count( $parameters ) === 1 ) { + $message = 'Missing second parameter for "%s".'; + $data = [ $matched_content ]; + $this->phpcsFile->addWarning( $message, $stackPtr, 'MissingSecondParameter', $data ); + } + + if ( isset( $parameters[2], $this->restricted_filters[ $parameters[2]['raw'] ] ) ) { + $message = 'Please use an appropriate filter to sanitize, as "%s" does no filtering, see http://php.net/manual/en/filter.filters.sanitize.php.'; + $data = [ strtoupper( $parameters[2]['raw'] ) ]; + $this->phpcsFile->addWarning( $message, $stackPtr, 'RestrictedFilter', $data ); + } + } + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/ProperEscapingFunctionSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/ProperEscapingFunctionSniff.php new file mode 100644 index 00000000..9b9513f0 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/ProperEscapingFunctionSniff.php @@ -0,0 +1,277 @@ +href|src|url|(^|\s+)action)?(?<=[a-z0-9_-])=(?:\\\\)?["\']*$`i'; + + /** + * List of escaping functions which are being tested. + * + * @var array + */ + protected $escaping_functions = [ + 'esc_url' => 'url', + 'esc_attr' => 'attr', + 'esc_attr__' => 'attr', + 'esc_attr_x' => 'attr', + 'esc_attr_e' => 'attr', + 'esc_html' => 'html', + 'esc_html__' => 'html', + 'esc_html_x' => 'html', + 'esc_html_e' => 'html', + ]; + + /** + * List of tokens we can skip. + * + * @var array + */ + private $echo_or_concat_tokens = + [ + T_ECHO => T_ECHO, + T_OPEN_TAG => T_OPEN_TAG, + T_OPEN_TAG_WITH_ECHO => T_OPEN_TAG_WITH_ECHO, + T_STRING_CONCAT => T_STRING_CONCAT, + T_NS_SEPARATOR => T_NS_SEPARATOR, + ]; + + /** + * List of attributes associated with url outputs. + * + * @deprecated 2.3.1 Currently unused by the sniff, but needed for + * for public methods which extending sniffs may be + * relying on. + * + * @var array + */ + private $url_attrs = [ + 'href', + 'src', + 'url', + 'action', + ]; + + /** + * List of syntaxes for inside attribute detection. + * + * @deprecated 2.3.1 Currently unused by the sniff, but needed for + * for public methods which extending sniffs may be + * relying on. + * + * @var array + */ + private $attr_endings = [ + '=', + '="', + "='", + "=\\'", + '=\\"', + ]; + + /** + * Keep track of whether or not we're currently in the first statement of a short open echo tag. + * + * @var int|false Integer stack pointer to the end of the first statement in the current + * short open echo tag or false when not in a short open echo tag. + */ + private $in_short_echo = false; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + $this->echo_or_concat_tokens += Tokens::$emptyTokens; + + return [ + T_STRING, + T_OPEN_TAG_WITH_ECHO, + ]; + } + + /** + * Process this test when one of its tokens is encountered + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @return void + */ + public function process_token( $stackPtr ) { + /* + * Short open echo tags will act as an echo for the first expression and + * allow for passing multiple comma-separated parameters. + * However, short open echo tags also allow for additional statements after, but + * those have to be full PHP statements, not expressions. + * + * This snippet of code will keep track of whether or not we're in the first + * expression in a short open echo tag. + * $phpcsFile->findStartOfStatement() unfortunately is useless, as it will return + * the first token in the statement, which can be anything - variable, text string - + * without any indication of whether this is the start of a normal statement or + * a short open echo expression. + * So, if we used that, we'd need to walk back from every start of statement to + * the previous non-empty to see if it is the short open echo tag. + */ + if ( $this->tokens[ $stackPtr ]['code'] === T_OPEN_TAG_WITH_ECHO ) { + $end_of_echo = $this->phpcsFile->findNext( [ T_SEMICOLON, T_CLOSE_TAG ], ( $stackPtr + 1 ) ); + if ( $end_of_echo === false ) { + $this->in_short_echo = $this->phpcsFile->numTokens; + } else { + $this->in_short_echo = $end_of_echo; + } + + return; + } + + if ( $this->in_short_echo !== false && $this->in_short_echo < $stackPtr ) { + $this->in_short_echo = false; + } + + $function_name = strtolower( $this->tokens[ $stackPtr ]['content'] ); + + if ( isset( $this->escaping_functions[ $function_name ] ) === false ) { + return; + } + + $next_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + if ( $next_non_empty === false || $this->tokens[ $next_non_empty ]['code'] !== T_OPEN_PARENTHESIS ) { + // Not a function call. + return; + } + + $ignore = $this->echo_or_concat_tokens; + if ( $this->in_short_echo !== false ) { + $ignore[ T_COMMA ] = T_COMMA; + } else { + $start_of_statement = $this->phpcsFile->findStartOfStatement( $stackPtr, T_COMMA ); + if ( $this->tokens[ $start_of_statement ]['code'] === T_ECHO ) { + $ignore[ T_COMMA ] = T_COMMA; + } + } + + $html = $this->phpcsFile->findPrevious( $ignore, $stackPtr - 1, null, true ); + + // Use $textStringTokens b/c heredoc and nowdoc tokens will never be encountered in this context anyways.. + if ( $html === false || isset( Tokens::$textStringTokens[ $this->tokens[ $html ]['code'] ] ) === false ) { + return; + } + + $data = [ $function_name ]; + + $content = $this->tokens[ $html ]['content']; + if ( isset( Tokens::$stringTokens[ $this->tokens[ $html ]['code'] ] ) === true ) { + $content = Sniff::strip_quotes( $content ); + } + + $escaping_type = $this->escaping_functions[ $function_name ]; + + if ( $escaping_type === 'attr' && $this->is_outside_html_attr_context( $content ) ) { + $message = 'Wrong escaping function, using `%s()` in a context outside of HTML attributes may not escape properly.'; + $this->phpcsFile->addError( $message, $html, 'notAttrEscAttr', $data ); + return; + } + + if ( preg_match( self::ATTR_END_REGEX, $content, $matches ) !== 1 ) { + return; + } + + if ( $escaping_type !== 'url' && empty( $matches['attrname'] ) === false ) { + $message = 'Wrong escaping function. href, src, and action attributes should be escaped by `esc_url()`, not by `%s()`.'; + $this->phpcsFile->addError( $message, $stackPtr, 'hrefSrcEscUrl', $data ); + return; + } + + if ( $escaping_type === 'html' ) { + $message = 'Wrong escaping function. HTML attributes should be escaped by `esc_attr()`, not by `%s()`.'; + $this->phpcsFile->addError( $message, $stackPtr, 'htmlAttrNotByEscHTML', $data ); + return; + } + } + + /** + * Tests whether provided string ends with open attribute which expects a URL value. + * + * @deprecated 2.3.1 + * + * @param string $content Haystack in which we look for an open attribute which exects a URL value. + * + * @return bool True if string ends with open attribute which expects a URL value. + */ + public function attr_expects_url( $content ) { + $attr_expects_url = false; + foreach ( $this->url_attrs as $attr ) { + foreach ( $this->attr_endings as $ending ) { + if ( $this->endswith( $content, $attr . $ending ) === true ) { + $attr_expects_url = true; + break; + } + } + } + return $attr_expects_url; + } + + /** + * Tests whether provided string ends with open HMTL attribute. + * + * @deprecated 2.3.1 + * + * @param string $content Haystack in which we look for open HTML attribute. + * + * @return bool True if string ends with open HTML attribute. + */ + public function is_html_attr( $content ) { + $is_html_attr = false; + foreach ( $this->attr_endings as $ending ) { + if ( $this->endswith( $content, $ending ) === true ) { + $is_html_attr = true; + break; + } + } + return $is_html_attr; + } + + /** + * Tests whether an attribute escaping function is being used outside of an HTML tag. + * + * @param string $content Haystack where we look for the end of a HTML tag. + * + * @return bool True if the passed string ends a HTML tag. + */ + public function is_outside_html_attr_context( $content ) { + return $this->endswith( trim( $content ), '>' ); + } + + /** + * A helper function which tests whether string ends with some other. + * + * @param string $haystack String which is being tested. + * @param string $needle The substring, which we try to locate on the end of the $haystack. + * + * @return bool True if haystack ends with needle. + */ + public function endswith( $haystack, $needle ) { + return substr( $haystack, -strlen( $needle ) ) === $needle; + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/StaticStrreplaceSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/StaticStrreplaceSniff.php new file mode 100644 index 00000000..3d57edcc --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/StaticStrreplaceSniff.php @@ -0,0 +1,87 @@ +tokens[ $stackPtr ]['content'] !== 'str_replace' ) { + return; + } + + $openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $stackPtr + 1, null, true ); + + if ( $this->tokens[ $openBracket ]['code'] !== T_OPEN_PARENTHESIS ) { + return; + } + + $next_start_ptr = $openBracket + 1; + for ( $i = 0; $i < 3; $i++ ) { + $param_ptr = $this->phpcsFile->findNext( array_merge( Tokens::$emptyTokens, [ T_COMMA ] ), $next_start_ptr, null, true ); + + if ( $this->tokens[ $param_ptr ]['code'] === T_ARRAY ) { + $openBracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, $param_ptr + 1, null, true ); + if ( $this->tokens[ $openBracket ]['code'] !== T_OPEN_PARENTHESIS ) { + return; + } + + // Find the closing bracket. + $closeBracket = $this->tokens[ $openBracket ]['parenthesis_closer']; + + $array_item_ptr = $this->phpcsFile->findNext( array_merge( Tokens::$emptyTokens, [ T_COMMA ] ), $openBracket + 1, $closeBracket, true ); + while ( $array_item_ptr !== false ) { + + if ( $this->tokens[ $array_item_ptr ]['code'] !== T_CONSTANT_ENCAPSED_STRING ) { + return; + } + $array_item_ptr = $this->phpcsFile->findNext( array_merge( Tokens::$emptyTokens, [ T_COMMA ] ), $array_item_ptr + 1, $closeBracket, true ); + } + + $next_start_ptr = $closeBracket + 1; + continue; + + } + + if ( $this->tokens[ $param_ptr ]['code'] !== T_CONSTANT_ENCAPSED_STRING ) { + return; + } + + $next_start_ptr = $param_ptr + 1; + + } + + $message = 'This code pattern is often used to run a very dangerous shell programs on your server. The code in these files needs to be reviewed, and possibly cleaned.'; + $this->phpcsFile->addError( $message, $stackPtr, 'StaticStrreplace' ); + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/TwigSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/TwigSniff.php new file mode 100644 index 00000000..9c87efa7 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/TwigSniff.php @@ -0,0 +1,62 @@ +tokens[ $stackPtr ]['content'] ) === 1 ) { + // Twig autoescape disabled. + $message = 'Found Twig autoescape disabling notation.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'AutoescapeFalse' ); + } + + if ( preg_match( '/\|\s*raw/', $this->tokens[ $stackPtr ]['content'] ) === 1 ) { + // Twig default unescape filter. + $message = 'Found Twig default unescape filter: "|raw".'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'RawFound' ); + } + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/UnderscorejsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/UnderscorejsSniff.php new file mode 100644 index 00000000..6ea36135 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/UnderscorejsSniff.php @@ -0,0 +1,162 @@ +|$)`'; + + /** + * Regex to match execute notations containing a print command + * and retrieve a code snippet. + * + * @var string + */ + const UNESCAPED_PRINT_REGEX = '`<%\s*(?:print\s*\(.+?\)\s*;|__p\s*\+=.+?)\s*%>`'; + + /** + * Regex to match the "interpolate" keyword when used to overrule the ERB-style delimiters. + * + * @var string + */ + const INTERPOLATE_KEYWORD_REGEX = '`(?:templateSettings\.interpolate|\.interpolate\s*=\s*/|interpolate\s*:\s*/)`'; + + /** + * A list of tokenizers this sniff supports. + * + * @var string[] + */ + public $supportedTokenizers = [ 'JS', 'PHP' ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + $targets = Tokens::$textStringTokens; + $targets[] = T_PROPERTY; + $targets[] = T_STRING; + + return $targets; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @return void + */ + public function process_token( $stackPtr ) { + /* + * Ignore Gruntfile.js files as they are configuration, not code. + */ + $file_name = $this->strip_quotes( $this->phpcsFile->getFileName() ); + $file_name = strtolower( basename( $file_name ) ); + + if ( $file_name === 'gruntfile.js' ) { + return; + } + + /* + * Check for delimiter change in JS files. + */ + if ( $this->tokens[ $stackPtr ]['code'] === T_STRING + || $this->tokens[ $stackPtr ]['code'] === T_PROPERTY + ) { + if ( $this->phpcsFile->tokenizerType !== 'JS' ) { + // These tokens are only relevant for JS files. + return; + } + + if ( $this->tokens[ $stackPtr ]['content'] !== 'interpolate' ) { + return; + } + + // Check the context to prevent false positives. + if ( $this->tokens[ $stackPtr ]['code'] === T_STRING ) { + $prev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true ); + if ( $prev === false || $this->tokens[ $prev ]['code'] !== T_OBJECT_OPERATOR ) { + return; + } + + $prevPrev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true ); + $next = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + if ( ( $prevPrev === false + || $this->tokens[ $prevPrev ]['code'] !== T_STRING + || $this->tokens[ $prevPrev ]['content'] !== 'templateSettings' ) + && ( $next === false + || $this->tokens[ $next ]['code'] !== T_EQUAL ) + ) { + return; + } + } + + // Underscore.js delimiter change. + $message = 'Found Underscore.js delimiter change notation.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'InterpolateFound' ); + + return; + } + + $content = $this->strip_quotes( $this->tokens[ $stackPtr ]['content'] ); + + $match_count = preg_match_all( self::UNESCAPED_INTERPOLATE_REGEX, $content, $matches ); + if ( $match_count > 0 ) { + foreach ( $matches[0] as $match ) { + if ( strpos( $match, '_.escape(' ) !== false ) { + continue; + } + + // Underscore.js unescaped output. + $message = 'Found Underscore.js unescaped output notation: "%s".'; + $data = [ $match ]; + $this->phpcsFile->addWarning( $message, $stackPtr, 'OutputNotation', $data ); + } + } + + $match_count = preg_match_all( self::UNESCAPED_PRINT_REGEX, $content, $matches ); + if ( $match_count > 0 ) { + foreach ( $matches[0] as $match ) { + if ( strpos( $match, '_.escape(' ) !== false ) { + continue; + } + + // Underscore.js unescaped output. + $message = 'Found Underscore.js unescaped print execution: "%s".'; + $data = [ $match ]; + $this->phpcsFile->addWarning( $message, $stackPtr, 'PrintExecution', $data ); + } + } + + if ( $this->phpcsFile->tokenizerType !== 'JS' + && preg_match( self::INTERPOLATE_KEYWORD_REGEX, $content ) > 0 + ) { + // Underscore.js delimiter change. + $message = 'Found Underscore.js delimiter change notation.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'InterpolateFound' ); + } + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/VuejsSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/VuejsSniff.php new file mode 100644 index 00000000..df9ac30d --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Security/VuejsSniff.php @@ -0,0 +1,55 @@ +tokens[ $stackPtr ]['content'], 'v-html' ) !== false ) { + // Vue autoescape disabled. + $message = 'Found Vue.js non-escaped (raw) HTML directive.'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'RawHTMLDirectiveFound' ); + } + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Sniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Sniff.php new file mode 100644 index 00000000..dfad0381 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Sniff.php @@ -0,0 +1,20 @@ + true, + 'add_filter' => true, + ]; + + /** + * CSS properties this sniff is looking for. + * + * @var array + */ + protected $target_css_properties = [ + 'visibility' => [ + 'type' => '!=', + 'value' => 'hidden', + ], + 'display' => [ + 'type' => '!=', + 'value' => 'none', + ], + 'opacity' => [ + 'type' => '>', + 'value' => 0.3, + ], + ]; + + /** + * CSS selectors this sniff is looking for. + * + * @var array + */ + protected $target_css_selectors = [ + '.show-admin-bar', + '#wpadminbar', + ]; + + /** + * String tokens within PHP files we want to deal with. + * + * Set from the register() method. + * + * @var array + */ + private $string_tokens = []; + + /** + * Regex template for use with the CSS selectors in combination with PHP text strings. + * + * @var string + */ + private $target_css_selectors_regex = '`(?:%s).*?\{(.*)$`'; + + /** + * Property to keep track of whether a ' ) !== false ) { + // Make sure we check any content on this line before the closing style tag. + $this->in_style[ $file_name ] = false; + $content = trim( substr( $content, 0, strpos( $content, '' ) ) ); + } + } elseif ( $this->has_html_open_tag( 'style', $stackPtr, $content ) === true ) { + // Ok, found a ' ) === false ) { + // Make sure we check any content on this line after the opening style tag. + $this->in_style[ $file_name ] = true; + $content = trim( substr( $content, strpos( $content, '' ); + $content = trim( substr( $content, $start, $end - $start ) ); + unset( $start, $end ); + } + } else { + return; + } + + // Are we in one of the target selectors ? + if ( $this->in_target_selector[ $file_name ] === true ) { + if ( strpos( $content, '}' ) !== false ) { + // Make sure we check any content on this line before the selector closing brace. + $this->in_target_selector[ $file_name ] = false; + $content = trim( substr( $content, 0, strpos( $content, '}' ) ) ); + } + } elseif ( preg_match( $this->target_css_selectors_regex, $content, $matches ) > 0 ) { + // Ok, found a new target selector. + $content = ''; + + if ( isset( $matches[1] ) && $matches[1] !== '' ) { + if ( strpos( $matches[1], '}' ) === false ) { + // Make sure we check any content on this line before the closing brace. + $this->in_target_selector[ $file_name ] = true; + $content = trim( $matches[1] ); + } else { + // Ok, we have the selector open and close brace on the same line. + $content = trim( substr( $matches[1], 0, strpos( $matches[1], '}' ) ) ); + } + } else { + $this->in_target_selector[ $file_name ] = true; + } + } else { + return; + } + unset( $matches ); + + // Now let's do the check for the CSS properties. + if ( ! empty( $content ) ) { + foreach ( $this->target_css_properties as $property => $requirements ) { + if ( strpos( $content, $property ) !== false ) { + $error = true; + + // Check the value of the CSS property. + if ( $this->remove_only === true && preg_match( '`' . preg_quote( $property, '`' ) . '\s*:\s*(.+?)\s*(?:!important)?;`', $content, $matches ) > 0 ) { + $value = trim( $matches[1] ); + $valid = $this->validate_css_property_value( $value, $requirements['type'], $requirements['value'] ); + if ( $valid === true ) { + $error = false; + } + } + + if ( $error === true ) { + $this->addHidingDetectedError( $stackPtr ); + } + } + } + } + } + + /** + * Processes this test for T_STYLE tokens in CSS files. + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @return void + */ + protected function process_css_style( $stackPtr ) { + if ( ! isset( $this->target_css_properties[ $this->tokens[ $stackPtr ]['content'] ] ) ) { + // Not one of the CSS properties we're interested in. + return; + } + + $css_property = $this->target_css_properties[ $this->tokens[ $stackPtr ]['content'] ]; + + // Check if the CSS selector matches. + $opener = $this->phpcsFile->findPrevious( \T_OPEN_CURLY_BRACKET, $stackPtr ); + if ( $opener !== false ) { + for ( $i = ( $opener - 1 ); $i >= 0; $i-- ) { + if ( isset( Tokens::$commentTokens[ $this->tokens[ $i ]['code'] ] ) + || $this->tokens[ $i ]['code'] === \T_CLOSE_CURLY_BRACKET + ) { + break; + } + } + $start = ( $i + 1 ); + $selector = trim( $this->phpcsFile->getTokensAsString( $start, $opener - $start ) ); + unset( $i ); + + foreach ( $this->target_css_selectors as $target_selector ) { + if ( strpos( $selector, $target_selector ) !== false ) { + $error = true; + + if ( $this->remove_only === true ) { + // Check the value of the CSS property. + $valuePtr = $this->phpcsFile->findNext( [ \T_COLON, \T_WHITESPACE ], $stackPtr + 1, null, true ); + $value = $this->tokens[ $valuePtr ]['content']; + $valid = $this->validate_css_property_value( $value, $css_property['type'], $css_property['value'] ); + if ( $valid === true ) { + $error = false; + } + } + + if ( $error === true ) { + $this->addHidingDetectedError( $stackPtr ); + } + } + } + } + } + + /** + * Consolidated violation. + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + */ + private function addHidingDetectedError( $stackPtr ) { + $message = 'Hiding of the admin bar is not allowed.'; + $this->phpcsFile->addError( $message, $stackPtr, 'HidingDetected' ); + } + + /** + * Verify if a CSS property value complies with an expected value. + * + * {@internal This is a method stub, doing only what is needed for this sniff. + * If at some point in the future other sniff would need similar functionality, + * this method should be moved to the WordPress_Sniff class and expanded to cover + * all types of comparisons.}} + * + * @param mixed $value The value of CSS property. + * @param string $compare_type The type of comparison to use for the validation. + * @param string $compare_value The value to compare against. + * + * @return bool True if the property value complies, false otherwise. + */ + protected function validate_css_property_value( $value, $compare_type, $compare_value ) { + switch ( $compare_type ) { + case '!=': + return $value !== $compare_value; + + case '>': + return $value > $compare_value; + } + return false; + } + + /** + * Check if a content string contains a specific HTML open tag. + * + * @param string $tag_name The name of the HTML tag without brackets. So if + * searching for ' open tag, false otherwise. + */ + public function has_html_open_tag( $tag_name, $stackPtr = null, $content = null ) { + if ( $content === null && isset( $stackPtr ) ) { + $content = $this->tokens[ $stackPtr ]['content']; + } + + return $content !== null && strpos( $content, '<' . $tag_name ) !== false; + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Variables/RestrictedVariablesSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Variables/RestrictedVariablesSniff.php new file mode 100644 index 00000000..fe19452d --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Variables/RestrictedVariablesSniff.php @@ -0,0 +1,70 @@ + array( + * 'wpdb' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Dont use this one please!', + * 'variables' => array( '$val', '$var' ), + * 'object_vars' => array( '$foo->bar', .. ), + * 'array_members' => array( '$foo['bar']', .. ), + * ) + * ) + * + * @return array + */ + public function getGroups() { + return [ + 'user_meta' => [ + 'type' => 'error', + 'message' => 'Usage of users tables is highly discouraged in VIP context', + 'object_vars' => [ + '$wpdb->users', + ], + ], + 'session' => [ + 'type' => 'error', + 'message' => 'Usage of $_SESSION variable is prohibited.', + 'variables' => [ + '$_SESSION', + ], + ], + + // @link https://docs.wpvip.com/technical-references/code-review/vip-errors/#h-cache-constraints + 'cache_constraints' => [ + 'type' => 'warning', + 'message' => 'Due to server-side caching, server-side based client related logic might not work. We recommend implementing client side logic in JavaScript instead.', + 'variables' => [ + '$_COOKIE', + ], + 'array_members' => [ + '$_SERVER[\'HTTP_USER_AGENT\']', + '$_SERVER[\'REMOTE_ADDR\']', + ], + ], + ]; + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Variables/ServerVariablesSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Variables/ServerVariablesSniff.php new file mode 100644 index 00000000..03e52e55 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Variables/ServerVariablesSniff.php @@ -0,0 +1,75 @@ + [ + 'PHP_AUTH_USER' => true, + 'PHP_AUTH_PW' => true, + ], + 'userControlledVariables' => [ + 'HTTP_X_IP_TRAIL' => true, + 'HTTP_X_FORWARDED_FOR' => true, + 'REMOTE_ADDR' => true, + ], + ]; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + return [ + T_VARIABLE, + ]; + } + + /** + * Process this test when one of its tokens is encountered + * + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @return void + */ + public function process_token( $stackPtr ) { + + if ( $this->tokens[ $stackPtr ]['content'] !== '$_SERVER' ) { + // Not the variable we are looking for. + return; + } + + $variableNamePtr = $this->phpcsFile->findNext( [ T_CONSTANT_ENCAPSED_STRING ], $stackPtr + 1, null, false, null, true ); + $variableName = str_replace( [ "'", '"' ], '', $this->tokens[ $variableNamePtr ]['content'] ); + + if ( isset( $this->restrictedVariables['authVariables'][ $variableName ] ) ) { + $message = 'Basic authentication should not be handled via PHP code.'; + $this->phpcsFile->addError( $message, $stackPtr, 'BasicAuthentication' ); + } elseif ( isset( $this->restrictedVariables['userControlledVariables'][ $variableName ] ) ) { + $message = 'Header "%s" is user-controlled and should be properly validated before use.'; + $data = [ $variableName ]; + $this->phpcsFile->addError( $message, $stackPtr, 'UserControlledHeaders', $data ); + } + } + +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Variables/VariableAnalysisSniff.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Variables/VariableAnalysisSniff.php new file mode 100644 index 00000000..18e1ed27 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/Sniffs/Variables/VariableAnalysisSniff.php @@ -0,0 +1,81 @@ + + * @copyright 2011-2012 Sam Graham + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/PHP_CodeSniffer + */ + +namespace WordPressVIPMinimum\Sniffs\Variables; + +use PHP_CodeSniffer\Files\File; + +/** + * Checks for undefined function variables. + * + * This sniff checks that all function variables + * are defined in the function body. + * + * @category PHP + * @package PHP_CodeSniffer + * @author Sam Graham + * @copyright 2011 Sam Graham + * @link http://pear.php.net/package/PHP_CodeSniffer + * + * @deprecated 2.2.0 Use the `VariableAnalysis.CodeAnalysis.VariableAnalysis` sniff instead. + * This `WordPressVIPMinimum.Variables.VariableAnalysis sniff will be removed in VIPCS 3.0.0. + */ +class VariableAnalysisSniff extends \VariableAnalysis\Sniffs\CodeAnalysis\VariableAnalysisSniff { + + /** + * Keep track of whether the warnings have been thrown to prevent + * the messages being thrown for every token triggering the sniff. + * + * @since 2.2.0 + * + * @var array + */ + private $thrown = [ + 'DeprecatedSniff' => false, + 'FoundPropertyForDeprecatedSniff' => false, + ]; + + /** + * Don't use. + * + * @since 2.2.0 Added to allow for throwing the deprecation notices. + * @deprecated 2.2.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 int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process( File $phpcsFile, $stackPtr ) { + + if ( $this->thrown['DeprecatedSniff'] === false ) { + $this->thrown['DeprecatedSniff'] = $phpcsFile->addWarning( + 'The "WordPressVIPMinimum.Variables.VariableAnalysis" sniff has been deprecated. Use the "VariableAnalysis.CodeAnalysis.VariableAnalysis" sniff instead. Please update your custom ruleset.', + 0, + 'DeprecatedSniff' + ); + } + if ( ! empty( $this->exclude ) && $this->thrown['FoundPropertyForDeprecatedSniff'] === false ) { + $this->thrown['FoundPropertyForDeprecatedSniff'] = $phpcsFile->addWarning( + 'The "WordPressVIPMinimum.Variables.VariableAnalysis" sniff has been deprecated. Use the "CodeAnalysis.VariableAnalysis" sniff instead. "exclude" property setting found. Please update your custom ruleset.', + 0, + 'FoundPropertyForDeprecatedSniff' + ); + } + + parent::process( $phpcsFile, $stackPtr ); + } +} diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/ruleset-test.inc b/vendor/automattic/vipwpcs/WordPressVIPMinimum/ruleset-test.inc new file mode 100644 index 00000000..803e890f --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/ruleset-test.inc @@ -0,0 +1,622 @@ + + + + 999, // Warning. +); +_query_posts( 'posts_per_page=999' ); // Warning. +$query_args['posts_per_page'] = 999; // Warning. + +// WordPress.DateTime.RestrictedFunctions +date_default_timezone_set( 'FooBar' ); // Error. + +// WordPress.DB.PreparedSQL +$b = function () { + global $wpdb; + $listofthings = wp_cache_get( 'foo' ); + if ( ! $listofthings ) { + $foo = "column = 'test'"; + + $listofthings = $wpdb->query( 'SELECT something FROM somewhere WHERE ' . $foo ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Error. + wp_cache_set( 'foo', $listofthings ); + } +}; + +// WordPress.DB.DirectDatabaseQuery +$baz = $wpdb->get_results( $wpdb->prepare( 'SELECT X FROM Y ' ) ); // Warning x 2. + +// WordPress.DB.SlowDBQuery +$test = [ + 'tax_query' => [], // Warning. +]; +new WP_Query( array( + 'meta_query' => [], // Warning. + 'meta_key' => 'foo', // Warning. + 'meta_value' => 'bar', // Warning. +) ); + +// WordPress.WP.GlobalVariablesOverride +$GLOBALS['wpdb'] = 'test'; // Error. + +// WordPress.PHP.StrictComparisons +if ( true == $true ) { // Warning. +} + +// WordPress.CodeAnalysis.AssignmentInCondition +if ( $test = get_post( $post ) ) { // Warning. +} + +// WordPress.PHP.StrictInArray +if ( true === in_array( $foo, $bar ) ) { // Warning. +} + +// WordPress.Functions.DontExtract +extract( $foobar ); // Error. + +// WordPress.WP.CronInterval +function my_add_weekly( $schedules ) { + $schedules['every_6_mins'] = array( + 'interval' => 360, + 'display' => __( 'Once every 6 minutes' ) + ); + return $schedules; +} +add_filter( 'cron_schedules', 'my_add_weekly'); // Warning. + +// Generic.NamingConventions.ConstructorName +class TestClass extends MyClass +{ + function __construct() { + parent::MYCLASS(); // Error. + parent::__construct(); + } +} +class OldClass +{ + function OldClass() // Error. + { + } +} + +// Generic.NamingConventions.ConstructorName +class TestClass extends MyClass { + function TestClass() { // Error. + parent::MyClass(); // Error. + parent::__construct(); + } +} + +// Generic.PHP.DisallowShortOpenTag +?> +// if (empty($this)) {echo 'This is will not work';} + +// Squiz.PHP.Eval +eval('$var = 4;'); // Error + Message. + +// WordPress.PHP.DiscouragedPHPFunctions +base64_decode( 'VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw=='); // Ok - exclude obfuscation group. +base64_encode( 'This is an encoded string' ); // Ok - exclude obfuscation group. +convert_uudecode( "+22!L;W9E(%!(4\"$`\n`" ); // Ok - exclude obfuscation group. +convert_uuencode( "test\ntext text\r\n" ); // Ok - exclude obfuscation group. +str_rot13( 'The quick brown fox jumps over the lazy dog.' ); // Ok - exclude obfuscation group. +serialize(); // Warning. +unserialize(); // Warning. +urlencode(); // Warning. +passthru( 'cat myfile.zip', $err ); // Warning. +$process = proc_open( 'php', $descriptorspec, $pipes, $cwd, $env ); // Warning. +$last_line = system( 'ls', $retval ); // Warning. +$handle = popen( '/bin/ls', 'r' ); // Warning. + +// WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting +error_reporting(); // Error. + +// WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_ini_restore +ini_restore(); // Error. + +// WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_apache_setenv +apache_setenv(); // Error. + +// WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv +putenv(); // Error. + +// WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_set_include_path +set_include_path(); // Error. + +// WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_restore_include_path +restore_include_path(); // Error. + +// WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_magic_quotes_runtime +magic_quotes_runtime(); // Error. + +// WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_set_magic_quotes_runtime +set_magic_quotes_runtime(); // Error. + +// WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_dl +dl(); // Error. + +// WordPress.PHP.DiscouragedPHPFunctions.system_calls_exec +exec( 'whoami' ); // Error. + +// WordPress.PHP.DiscouragedPHPFunctions.system_calls_shell_exec +$output = shell_exec( 'ls -lart' ); // Error. + +// WordPress.PHP.DevelopmentFunctions +var_dump(); // Warning. +var_export(); // Warning. +print_r(); // Warning. +trigger_error( 'message' ); // Warning. +set_error_handler(); // Warning. +debug_backtrace(); // Warning. +debug_print_backtrace(); // Warning. +wp_debug_backtrace_summary(); // Warning. + +// WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_phpinfo +phpinfo(); // Error. + +// WordPress.PHP.DevelopmentFunctions.error_log_error_log +error_log(); // Error. + +// WordPress.PHP.IniSet +ini_set( 'auto_detect_line_endings', true ); // Ok. +ini_set( 'highlight.bg', '#000000' ); // Ok. +ini_set( 'highlight.comment', '#000000' ); // Ok. +ini_set( 'highlight.default', '#000000' ); // Ok. +ini_set( 'highlight.html', '#000000' ); // Ok. +ini_set( 'highlight.keyword', '#000000' ); // Ok. +ini_set( 'highlight.string', '#000000' ); // Ok. +ini_set( 'short_open_tag', 1 ); // Ok. +ini_set( 'bcmath.scale', 1 ); // Error. +ini_set( 'display_errors', 1 ); // Error. +ini_set( 'error_reporting', 1 ); // Error. +ini_set( 'filter.default', 1 ); // Error. +ini_set( 'filter.default_flags', 1 ); // Error. +ini_set( 'iconv.input_encoding', 1 ); // Error. +ini_set( 'iconv.internal_encoding', 1 ); // Error. +ini_set( 'iconv.output_encoding', 1 ); // Error. +ini_set( 'ignore_user_abort', 1 ); // Error. +ini_set( 'log_errors', 1 ); // Error. +ini_set( 'max_execution_time', 1 ); // Error. +ini_set( 'memory_limit', 1 ); // Error. +ini_set( 'short_open_tag', 'off' ); // Error. +ini_set( 'foo', true ); // Warning. +ini_alter( 'auto_detect_line_endings', true ); // Ok. +ini_alter( 'highlight.bg', '#000000' ); // Ok. +ini_alter( 'highlight.comment', '#000000' ); // Ok. +ini_alter( 'highlight.default', '#000000' ); // Ok. +ini_alter( 'highlight.html', '#000000' ); // Ok. +ini_alter( 'highlight.keyword', '#000000' ); // Ok. +ini_alter( 'highlight.string', '#000000' ); // Ok. +ini_alter( 'short_open_tag', 1 ); // Ok. +ini_alter( 'bcmath.scale', 1 ); // Error. +ini_alter( 'display_errors', 1 ); // Error. +ini_alter( 'error_reporting', 1 ); // Error. +ini_alter( 'filter.default', 1 ); // Error. +ini_alter( 'filter.default_flags', 1 ); // Error. +ini_alter( 'iconv.input_encoding', 1 ); // Error. +ini_alter( 'iconv.internal_encoding', 1 ); // Error. +ini_alter( 'iconv.output_encoding', 1 ); // Error. +ini_alter( 'ignore_user_abort', 1 ); // Error. +ini_alter( 'log_errors', 1 ); // Error. +ini_alter( 'max_execution_time', 1 ); // Error. +ini_alter( 'memory_limit', 1 ); // Error. +ini_alter( 'short_open_tag', 'off' ); // Error. +ini_alter( 'foo', true ); // Warning. + +// WordPress.WP.AlternativeFunctions +curl_init(); // Warning + Message. +curl_close( $ch ); // Warning + Message. +CURL_getinfo(); // Warning + Message. +parse_url( 'http://example.com/' ); // Warning. +$json = json_encode( $thing ); // Warning. +readfile(); // Warning. +fclose(); // Warning. +fopen(); // Warning. +fread(); // Warning. +fsockopen(); // Warning. +pfsockopen(); // Warning. +srand(); // Warning. +mt_srand(); // Warning. +rand(); // Warning. +mt_rand(); // Warning. + +// WordPressVIPMinimum.Functions.RestrictedFunctions.get_posts_get_children +get_children(); // Error + Message. + +// VariableAnalysis.CodeAnalysis.VariableAnalysis +function foo() { + $a = 'Hello'; + $c = compact( $a, $b ); // Warning x 2. + try { + do_something_silly(); + } catch ( Exception $e ) {} // Ok. +} + +/* The below rules are implicitly included via WordPressVIPMinimum */ + +// WordPressVIPMinimum.Classes.DeclarationCompatibility +class MyWidget extends WP_Widget { + function widget() { // Error. + } +} + +// WordPressVIPMinimum.Classes.RestrictedExtendClasses +class BadTestClass extends WP_CLI_Command { } // Warning. + +// WordPressVIPMinimum.Compatibility.ZoninatorSniff +wpcom_vip_load_plugin( 'zoninator', 'plugins', '0.8' ); // Warning. + +// WordPressVIPMinimum.Constants.ConstantString +define( WPCOM_VIP ); // Error. + +// WordPressVIPMinimum.Constants.RestrictedConstants +if ( A8C_PROXIED_REQUEST === true ) { // Warning. +} +define( 'JETPACK_DEV_DEBUG', true ); // Error. + +// WordPressVIPMinimum.Files.IncludingFile +include ( MY_CONSTANT . "my_file.php" ); // Warning. +require_once( custom_function( 'test_file.php' ) ); // Warning. +require '../my_file.php'; // Error. +include_once("http://www.google.com/bad_file.php"); // Error. + +// WordPressVIPMinimum.Files.IncludingNonPHPFile +require_once __DIR__ . "/my_file.svg"; // Error. + +// WordPressVIPMinimum.Functions.CheckReturnValue +$my_theme_options = get_option( 'my_theme', false ); +if ( array_key_exists( 'key', $my_theme_options ) ) { } // Error. +echo 'My term link'; // Error. + +// WordPressVIPMinimum.Functions.DynamicCalls +$my_notokay_func = 'extract'; +$my_notokay_func(); // Error. + +// WordPressVIPMinimum.Functions.RestrictedFunctions + +opcache_reset(); // Error. +opcache_invalidate( 'test_script.php' ); // Error. +opcache_compile_file( $var ); // Error. +opcache_is_script_cached( 'test_script.php' ); // Error. +opcache_get_status(); // Error. +opcache_get_configuration(); // Error. + +wpcom_vip_irc(); // Error. +flush_rewrite_rules(); // Error. +$wp_rewrite->flush_rules(); // Error. +attachment_url_to_postid( $url ); // Error. + +switch_to_blog( $blogid ); // Warning. +get_page_by_title( $page_title ); // Error. +url_to_postid( $url ); // Error. +\add_role(); // Error. + + + + +term_exists(); // Error. +count_user_posts(); // Error. +wp_old_slug_redirect(); // Error. +get_adjacent_post(); // Error. +get_previous_post(); // Error. +get_previous_post_link(); // Error. +get_next_post(); // Error. +get_next_post_link(); // Error. +get_intermediate_image_sizes(); // Error. +wp_is_mobile(); // Error. +session_abort(); // Error. +session_cache_expire(); // Error. +session_cache_limiter(); // Error. +session_commit(); // Error. +session_create_id(); // Error. +session_decode(); // Error. +session_destroy(); // Error. +session_encode(); // Error. +session_gc(); // Error. +session_get_cookie_params(); // Error. +session_id(); // Error. +session_is_registered(); // Error. +session_module_name(); // Error. +session_name(); // Error. +session_regenerate_id(); // Error. +session_register_shutdown(); // Error. +session_register(); // Error. +session_reset(); // Error. +session_save_path(); // Error. +session_set_cookie_params(); // Error. +session_set_save_handler(); // Error. +session_start(); // Error. +session_status(); // Error. +session_unregister(); // Error. +session_unset(); // Error. +session_write_close(); // Error. +delete(); // Error. +file_put_contents( $file, $text, FILE_APPEND ); // Error. +while ( $count > $loop ) { + if ( flock( $fp, LOCK_EX ) ) { // Error. + fwrite( $fp, $text ); // Error. + } +} +fputcsv(); // Error. +fputs(); // Error. +ftruncate(); // Error. +is_writable(); // Error. +is_writeable(); // Error. +link(); // Error. +rename(); // Error. +symlink(); // Error. +tempnam(); // Error. +touch(); // Error. +unlink(); // Error. +mkdir(); // Error. +rmdir(); // Error. +chgrp(); // Error. +chown(); // Error. +chmod(); // Error. +lchgrp(); // Error. +lchown(); // Error. + + + +wp_mail(); // Warning. +mail(); // Warning. +is_multi_author(); // Warning. +the_sub_field( 'field' ); // Warning. +the_field( 'field' ); // Warning. +wp_remote_get( $url ); // Warning. +setcookie( 'cookie[three]', 'cookiethree' ); // Error. +get_posts(); // Warning. +wp_get_recent_posts(); // Warning. +$wp_random_testing = create_function( '$a, $b', 'return ( $b / $a ); '); // Warning. +wpcom_vip_get_term_link(); // Warning. +wpcom_vip_get_term_by(); // Warning. +wpcom_vip_get_category_by_slug(); // Warning. + +// WordPressVIPMinimum.Functions.StripTagsSniff +strip_tags( 'Testing' ); // Warning. +strip_tags( 'Test', $text ); // Warning. + +// WordPressVIPMinimum.Hooks.AlwaysReturnInFilter +function bad_example_function_thing() { // Error. + if ( 1 === 0 ) { + if ( 1 === 1 ) { + return 'ahoj'; + } else { + return 'hello'; + } + } +} +add_filter( 'bad_example_function_filter', 'bad_example_function_thing' ); +add_filter( 'another_bad_example_closure', function() { // Error. + return; +} ); + +// WordPressVIPMinimum.Hooks.PreGetPosts +add_action( 'pre_get_posts', function( $wp_query ) { + if ( ! $wp_query->is_search() ) { + $wp_query->set( 'cat', '-5' ); // Warning. + } +} ); + +// WordPressVIPMinimum.Hooks.RestrictedHooks +add_filter( 'upload_mimes', 'bad_example_function' ); // Warning. +add_action( 'http_request_timeout', 'bad_example_function' ); // Warning. +add_filter('http_request_args', 'bad_example_function' ); // Warning. +add_action( 'do_robotstxt', 'my_do_robotstxt'); // Warning. +add_filter( 'robots_txt', function() { // Warning. + return 'test'; +} ); + +// WordPressVIPMinimum.Performance.BatcacheWhitelistedParams +// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated +$test = sanitize_text_field( $_GET["utm_medium"] ); // Warning. + +// WordPressVIPMinimum.Performance.CacheValueOverride +$bad_wp_users = wp_cache_get( md5( self::CACHE_KEY . '_wp_users'), self::CACHE_GROUP ); +$bad_wp_users = false; // Error. + +// WordPressVIPMinimum.Performance.FetchingRemoteData +$external_resource = file_get_contents( 'https://example.com' ); // Warning. + +// WordPressVIPMinimum.Performance.LowExpiryCacheTime +wp_cache_set( 'test', $data, $group, 100 ); // Warning. +wp_cache_add( 123, $data, null, 1.5 * MINUTE_IN_SECONDS ); // Warning. +wp_cache_replace( 'test', $data, $group, 2*MINUTE_IN_SECONDS ); // Warning. + +// WordPressVIPMinimum.Performance.NoPaging +$args = array( + 'nopaging' => true, // Error. +); +_query_posts( 'nopaging=true' ); // Error. + +// WordPressVIPMinimum.Performance.OrderByRand +$args = array( + "orderby" => "RAND", // Error. +); +$query_args['orderby'] = 'rand'; // Error. + +// WordPressVIPMinimum.Performance.RegexpCompare +$query_args = array( + 'posts_per_page' => 1, + 'post_status' => 'draft', + 'meta_compare' => 'REGEXP', // Error. +); +$query_args = [ + 'post_status' => 'publish', + 'meta_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + [ + 'compare' => 'REGEXP', // Error. + ] + ] +]; + +// WordPressVIPMinimum.Performance.RemoteRequestTimeout +wp_remote_post( $stdClass->endpoint, array( + 'method' => 'POST', + 'timeout' => 45, // Error. + 'httpversion' => '1.1', + 'blocking' => false, + 'body' => wp_json_encode( $stdClass->logs, JSON_UNESCAPED_SLASHES ), + ) +); + +// WordPressVIPMinimum.Performance.TaxonomyMetaInOptions +get_option( "taxonomy_rating_$obj->term_id" ); // Warning. +update_option( 'taxonomy_rating_' . $category_id ); // Warning. + +// WordPressVIPMinimum.Performance.WPQueryParams +$query_args = array( + 'post__not_in' => $posts_not_in, // Warning. + 'suppress_filters' => true, // Error. +); + +// WordPressVIPMinimum.Security.EscapingVoidReturnFunctions.Found +esc_js( _deprecated_argument() ); // Error. +esc_js( _deprecated_constructor() ); // Error. +esc_js( _deprecated_file( 'filename' ) ); // Error. +esc_js( _deprecated_function() ); // Error. +esc_js( _deprecated_hook() ); // Error. +esc_js( _doing_it_wrong() ); // Error. +esc_html( printf( 'foo', [] ) ); // Error. +esc_attr( user_error( 'foo', '' ) ); // Error. +esc_attr( vprintf( 'foo', [] ) ); // Error. +esc_attr( wp_die( 'foo' ) ); // Error. +esc_attr( wp_dropdown_pages() ); // Error. + +// WordPressVIPMinimum.Security.ExitAfterRedirect +function redirect_test() { + wp_safe_redirect( 'https.//vip.wordpress.com' ); // Error. +} +wp_redirect( 'https://vip.wordpress.com' ); // Error. + +// WordPressVIPMinimum.Security.PHPFilterFunctions +filter_input( INPUT_GET, 'foo' ); // Warning. +filter_input( INPUT_GET, "foo", FILTER_UNSAFE_RAW ); // Warning. +filter_var( $url, FILTER_DEFAULT ); // Warning. +filter_var_array( $array ); // Warning. +filter_input_array( $array ); // Warning. + +// WordPressVIPMinimum.Security.Mustache +echo '{{{data}}}'; // Warning. +?> + + '; // Error. +echo ''; // Error. + +// WordPressVIPMinimum.Security.StaticStrreplace +str_replace( 'foo', array( 'bar', 'foo' ), 'foobar' ); // Error. + +// WordPressVIPMinimum.Security.Underscorejs +echo ""; + +// WordPressVIPMinimum.Security.Vuejs +?>
    +

    +
    +#wpadminbar { + visibility: hidden; /* Error. */ + display: none; /* Error. */ + opacity: 0; /* Error. */ +} +'; +echo ''; // Error. +?> users"; // Error. +$x = foo( sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- Warning. +foo( $_SESSION['bar'] ); // Error. + +// WordPressVIPMinimum.Variables.ServerVariables +// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotValidated,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized +$test = $_SERVER['PHP_AUTH_PW']; // Error. +bar( $_SERVER['HTTP_X_IP_TRAIL'] ); // Error. +$_SERVER['HTTP_X_FORWARDED_FOR']; // Error. +$_SERVER["REMOTE_ADDR"]; // Error. +// phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotValidated,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + +class MyClass { + function my_function() { + return function() { + $this->my_callback(); // OK - new VariableAnalysis doesn't flag $this as undefined in closure. + }; + } + + function my_callback() {} +} + +// Generic.VersionControl.GitMergeConflict +?> +<<<<<<< HEAD // Error. + +>>>>>>> // Error. + + + + + diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/ruleset-test.php b/vendor/automattic/vipwpcs/WordPressVIPMinimum/ruleset-test.php new file mode 100644 index 00000000..19d74bf1 --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/ruleset-test.php @@ -0,0 +1,325 @@ + [ + 4 => 1, + 7 => 1, + 11 => 1, + 16 => 1, + 17 => 1, + 21 => 1, + 27 => 2, + 35 => 1, + 45 => 1, + 54 => 1, + 73 => 1, + 88 => 1, + 104 => 1, + 110 => 1, + 117 => 1, + 118 => 1, + 124 => 1, + 130 => 1, + 147 => 1, + 150 => 1, + 153 => 1, + 156 => 1, + 159 => 1, + 162 => 1, + 165 => 1, + 168 => 1, + 171 => 1, + 174 => 1, + 177 => 1, + 190 => 1, + 193 => 1, + 204 => 1, + 205 => 1, + 206 => 1, + 207 => 1, + 208 => 1, + 209 => 1, + 210 => 1, + 211 => 1, + 212 => 1, + 213 => 1, + 214 => 1, + 215 => 1, + 216 => 1, + 226 => 1, + 227 => 1, + 228 => 1, + 229 => 1, + 230 => 1, + 231 => 1, + 232 => 1, + 233 => 1, + 234 => 1, + 235 => 1, + 236 => 1, + 237 => 1, + 238 => 1, + 259 => 1, + 274 => 1, + 285 => 1, + 290 => 1, + 295 => 1, + 296 => 1, + 299 => 1, + 303 => 1, + 304 => 1, + 308 => 1, + 312 => 1, + 313 => 1, + 314 => 1, + 315 => 1, + 316 => 1, + 317 => 1, + 319 => 1, + 320 => 1, + 321 => 1, + 322 => 1, + 325 => 1, + 326 => 1, + 327 => 1, + 332 => 1, + 333 => 1, + 334 => 1, + 335 => 1, + 336 => 1, + 337 => 1, + 338 => 1, + 339 => 1, + 340 => 1, + 341 => 1, + 342 => 1, + 343 => 1, + 344 => 1, + 345 => 1, + 346 => 1, + 347 => 1, + 348 => 1, + 349 => 1, + 350 => 1, + 351 => 1, + 352 => 1, + 353 => 1, + 354 => 1, + 355 => 1, + 356 => 1, + 357 => 1, + 358 => 1, + 359 => 1, + 360 => 1, + 361 => 1, + 362 => 1, + 363 => 1, + 364 => 1, + 365 => 1, + 366 => 1, + 367 => 1, + 368 => 1, + 369 => 1, + 371 => 1, + 372 => 1, + 375 => 1, + 376 => 1, + 377 => 1, + 378 => 1, + 379 => 1, + 380 => 1, + 381 => 1, + 382 => 1, + 383 => 1, + 384 => 1, + 385 => 1, + 386 => 1, + 387 => 1, + 388 => 1, + 389 => 1, + 390 => 1, + 391 => 1, + 392 => 1, + 402 => 1, + 415 => 1, + 425 => 1, + 451 => 1, + 463 => 1, + 465 => 1, + 469 => 1, + 471 => 1, + 477 => 1, + 483 => 1, + 491 => 1, + 505 => 1, + 509 => 1, + 510 => 1, + 511 => 1, + 512 => 1, + 513 => 1, + 514 => 1, + 515 => 1, + 516 => 1, + 517 => 1, + 518 => 1, + 519 => 1, + 523 => 1, + 525 => 1, + 550 => 1, + 551 => 1, + 554 => 1, + 569 => 1, + 570 => 1, + 573 => 1, + 574 => 1, + 575 => 1, + 578 => 1, + 581 => 1, + 582 => 1, + 583 => 1, + 588 => 1, + 590 => 1, + 594 => 1, + 595 => 1, + 596 => 1, + 597 => 1, + 612 => 1, + 614 => 1, + 618 => 1, + ], + 'warnings' => [ + 32 => 1, + 39 => 1, + 41 => 1, + 42 => 1, + 60 => 2, + 64 => 1, + 67 => 1, + 68 => 1, + 69 => 1, + 76 => 1, + 80 => 1, + 84 => 1, + 98 => 1, + 126 => 1, + 138 => 1, + 139 => 1, + 140 => 1, + 141 => 1, + 142 => 1, + 143 => 1, + 144 => 1, + 180 => 1, + 181 => 1, + 182 => 1, + 183 => 1, + 184 => 1, + 185 => 1, + 186 => 1, + 187 => 1, + 217 => 1, + 239 => 1, + 242 => 1, + 243 => 1, + 244 => 1, + 245 => 1, + 246 => 1, + 247 => 1, + 248 => 1, + 249 => 1, + 250 => 1, + 251 => 1, + 252 => 1, + 253 => 1, + 254 => 1, + 255 => 1, + 256 => 1, + 264 => 2, + 279 => 1, + 282 => 1, + 288 => 1, + 293 => 1, + 294 => 1, + 324 => 1, + 396 => 1, + 397 => 1, + 398 => 1, + 399 => 1, + 400 => 1, + 401 => 1, + 403 => 1, + 404 => 1, + 405 => 1, + 406 => 1, + 407 => 1, + 408 => 1, + 411 => 1, + 412 => 1, + 432 => 1, + 437 => 1, + 438 => 1, + 439 => 1, + 440 => 1, + 441 => 1, + 447 => 1, + 454 => 1, + 457 => 1, + 458 => 1, + 459 => 1, + 499 => 1, + 500 => 1, + 504 => 1, + 528 => 1, + 529 => 1, + 530 => 1, + 531 => 1, + 532 => 1, + 535 => 1, + 538 => 1, + 545 => 1, + 559 => 1, + 565 => 1, + 589 => 1, + ], + 'messages' => [ + 130 => [ + '`eval()` is a security risk, please refrain from using it.', + ], + 242 => [ + 'Using cURL functions is highly discouraged within VIP context. Please see: https://docs.wpvip.com/technical-references/code-quality-and-best-practices/retrieving-remote-data/.', + ], + 243 => [ + 'Using cURL functions is highly discouraged within VIP context. Please see: https://docs.wpvip.com/technical-references/code-quality-and-best-practices/retrieving-remote-data/.', + ], + 244 => [ + 'Using cURL functions is highly discouraged within VIP context. Please see: https://docs.wpvip.com/technical-references/code-quality-and-best-practices/retrieving-remote-data/.', + ], + 259 => [ + '`get_children()` performs a no-LIMIT query by default, make sure to set a reasonable `posts_per_page`. `get_children()` will do a -1 query by default, a maximum of 100 should be used.', + ], + ], +]; + +require __DIR__ . '/../tests/RulesetTest.php'; + +// Run the tests! +$test = new RulesetTest( 'WordPressVIPMinimum', $expected ); +if ( $test->passes() ) { + printf( 'All WordPressVIPMinimum tests passed!' . PHP_EOL ); + exit( 0 ); +} + +exit( 1 ); diff --git a/vendor/automattic/vipwpcs/WordPressVIPMinimum/ruleset.xml b/vendor/automattic/vipwpcs/WordPressVIPMinimum/ruleset.xml new file mode 100644 index 00000000..2ce2511c --- /dev/null +++ b/vendor/automattic/vipwpcs/WordPressVIPMinimum/ruleset.xml @@ -0,0 +1,177 @@ + + + WordPress VIP Minimum Coding Standards + + + + + + + + + *.twig + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + *.php + *.inc + *.js + *.css + + + + + + error + `eval()` is a security risk, please refrain from using it. + + + + + + + + + + + + + error + + + error + + + error + + + error + + + error + + + error + + + error + + + error + + + error + + + error + + + error + + + + + + + + + + error + + + error + + + + + + + + + + + + + + + + + + Using cURL functions is highly discouraged within VIP context. Please see: https://docs.wpvip.com/technical-references/code-quality-and-best-practices/retrieving-remote-data/. + + + Using cURL functions is highly discouraged within VIP context. Please see: https://docs.wpvip.com/technical-references/code-quality-and-best-practices/retrieving-remote-data/. + + + Using cURL functions is highly discouraged within VIP context. Please see: https://docs.wpvip.com/technical-references/code-quality-and-best-practices/retrieving-remote-data/. + + + + error + `%1$s()` performs a no-LIMIT query by default, make sure to set a reasonable `posts_per_page`. `%1$s()` will do a -1 query by default, a maximum of 100 should be used. + + + + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + diff --git a/vendor/automattic/vipwpcs/composer.json b/vendor/automattic/vipwpcs/composer.json new file mode 100644 index 00000000..5a7ee54b --- /dev/null +++ b/vendor/automattic/vipwpcs/composer.json @@ -0,0 +1,57 @@ +{ + "name": "automattic/vipwpcs", + "type": "phpcodesniffer-standard", + "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress VIP minimum coding conventions", + "keywords": [ + "phpcs", + "standards", + "WordPress" + ], + "license": "MIT", + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/Automattic/VIP-Coding-Standards/graphs/contributors" + } + ], + "require": { + "php": ">=5.4", + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7", + "sirbrillig/phpcs-variable-analysis": "^2.11.1", + "squizlabs/php_codesniffer": "^3.5.5", + "wp-coding-standards/wpcs": "^2.3" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.0", + "php-parallel-lint/php-console-highlighter": "^0.5", + "phpcompatibility/php-compatibility": "^9", + "phpcsstandards/phpcsdevtools": "^1.0", + "phpunit/phpunit": "^4 || ^5 || ^6 || ^7" + }, + "scripts": { + "install-codestandards": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run", + "ruleset": "bin/ruleset-tests", + "lint": [ + "bin/php-lint", + "bin/xml-lint" + ], + "phpcs": "bin/phpcs", + "phpunit": "bin/unit-tests", + "coverage": "bin/unit-tests-coverage", + "check-complete": [ + "@php ./vendor/phpcsstandards/phpcsdevtools/bin/phpcs-check-feature-completeness -q ./WordPressVIPMinimum" + ], + "test": [ + "@lint", + "@ruleset", + "@phpunit", + "@phpcs", + "@check-complete" + ] + }, + "support": { + "issues": "https://github.com/Automattic/VIP-Coding-Standards/issues", + "wiki": "https://github.com/Automattic/VIP-Coding-Standards/wiki", + "source": "https://github.com/Automattic/VIP-Coding-Standards" + } +} diff --git a/vendor/bigbite/phpcs-config/.circleci/config.yml b/vendor/bigbite/phpcs-config/.circleci/config.yml new file mode 100644 index 00000000..63a9a662 --- /dev/null +++ b/vendor/bigbite/phpcs-config/.circleci/config.yml @@ -0,0 +1,51 @@ +version: 2.1 + +jobs: + php_74_tests: + docker: + - image: cimg/php:7.4 + steps: + - checkout + - run: + command: | + composer install + name: Composer install + - run: + command: | + composer run all-checks-strict + name: Run all checks + php_80_tests: + docker: + - image: cimg/php:8.0 + steps: + - checkout + - run: + command: | + composer install --ignore-platform-reqs + name: Composer install + - run: + command: | + composer run all-checks-strict + name: Run all checks + notify_slack: + docker: + - image: cimg/node:17.2.0 + steps: + - run: + command: | + curl -X POST -H 'Content-type: application/json' --data '{"text":"New PHPCS Config Release","blocks":[{"type":"header","text":{"type":"plain_text","text":"New PHPCS Config Release"}},{"type":"section","fields":[{"type":"mrkdwn","text":"*Version:* "},{"type":"mrkdwn","text":" *|* "}]}]}' ${SLACK_WEBHOOK} + name: Send Slack update to channel + +workflows: + run_tests: + jobs: + - php_74_tests + - php_80_tests + run_notify_slack: + jobs: + - notify_slack: + filters: + tags: + only: /v?[0-9]+\.[0-9]+\.[0-9]+(-(rc|beta|alpha).[0-9]+)?/ + branches: + ignore: /.*/ diff --git a/vendor/bigbite/phpcs-config/BigBite/Docs/Files/FileNameStandard.xml b/vendor/bigbite/phpcs-config/BigBite/Docs/Files/FileNameStandard.xml new file mode 100644 index 00000000..e75d80ad --- /dev/null +++ b/vendor/bigbite/phpcs-config/BigBite/Docs/Files/FileNameStandard.xml @@ -0,0 +1,11 @@ + + + trait-my-trait.php + - interface My_Interface {} => interface-my-interface.php + - class My_Class {} => class-my-class.php + - abstract class My_Abstract_Class {} => abstract-class-my-abstract-class.php + ]]> + + diff --git a/vendor/bigbite/phpcs-config/BigBite/Sniffs/Files/FileNameSniff.php b/vendor/bigbite/phpcs-config/BigBite/Sniffs/Files/FileNameSniff.php new file mode 100644 index 00000000..bcbe1645 --- /dev/null +++ b/vendor/bigbite/phpcs-config/BigBite/Sniffs/Files/FileNameSniff.php @@ -0,0 +1,222 @@ +phpcsFile->getFileName() ); + + if ( 'STDIN' === $file || ! $file ) { + return; + } + + if ( $this->is_disabled_by_comments() ) { + return; + } + + $fileName = basename( $file ); + + list( $ext, $file ) = explode( '.', strrev( $fileName ), 2 ); + + $expected = $this->kebab( strrev( $file ) ) . '.' . strrev( $ext ); + + /* + * Generic check for lowercase hyphenated file names. + */ + if ( $fileName !== $expected && ( false === $this->is_theme || 1 !== preg_match( self::THEME_EXCEPTIONS_REGEX, $fileName ) ) ) { + $this->phpcsFile->addError( + 'Filenames should be all lowercase with hyphens as word separators. Expected %s, but found %s.', + 0, + 'NotHyphenatedLowercase', + [ $expected, $fileName ] + ); + } + + unset( $expected ); + + if ( true !== $this->strict_class_file_names ) { + return ( $this->phpcsFile->numTokens + 1 ); + } + + $this->check_maybe_class( $stackPtr, $fileName ); + $this->check_maybe_trait( $stackPtr, $fileName ); + $this->check_maybe_interface( $stackPtr, $fileName ); + + // Only run this sniff once per file, no need to run it again. + return ( $this->phpcsFile->numTokens + 1 ); + } + + /** + * Respect phpcs:disable comments as long as they are not accompanied by an enable (PHPCS 3.2+). + * + * @return bool + */ + protected function is_disabled_by_comments() { + if ( ! \defined( '\T_PHPCS_DISABLE' ) || ! \defined( '\T_PHPCS_ENABLE' ) ) { + return false; + } + + $i = -1; + while ( $i = $this->phpcsFile->findNext( \T_PHPCS_DISABLE, ( $i + 1 ) ) ) { + if ( empty( $this->tokens[ $i ]['sniffCodes'] ) + || isset( $this->tokens[ $i ]['sniffCodes']['BigBite'] ) + || isset( $this->tokens[ $i ]['sniffCodes']['BigBite.Files'] ) + || isset( $this->tokens[ $i ]['sniffCodes']['BigBite.Files.FileName'] ) + ) { + do { + $i = $this->phpcsFile->findNext( \T_PHPCS_ENABLE, ( $i + 1 ) ); + } while ( false !== $i + && ! empty( $this->tokens[ $i ]['sniffCodes'] ) + && ! isset( $this->tokens[ $i ]['sniffCodes']['BigBite'] ) + && ! isset( $this->tokens[ $i ]['sniffCodes']['BigBite.Files'] ) + && ! isset( $this->tokens[ $i ]['sniffCodes']['BigBite.Files.FileName'] ) ); + + if ( false === $i ) { + // The entire (rest of the) file is disabled. + return true; + } + } + } + + return false; + } + + /** + * Check files containing a class for the "class-" prefix, + * and that the rest of the file name reflects the class name. + * Accounts for abstract classes. + * + * @param mixed $stackPtr the token stack + * @param string $fileName the name of the file + * + * @return void + */ + protected function check_maybe_class( $stackPtr, $fileName ) { + $has_class = $this->phpcsFile->findNext( \T_CLASS, $stackPtr ); + + if ( false === $has_class || false !== $this->is_test_class( $has_class ) ) { + return; + } + + $is_abstract = $this->phpcsFile->findPrevious( \T_ABSTRACT, $has_class ); + $class_name = $this->phpcsFile->getDeclarationName( $has_class ); + + if ( is_null( $class_name ) ) { + $class_name = 'anonymous'; + } + + $expected = 'class-' . $this->kebab( $class_name ); + $err_message = 'Class file names should be based on the class name with "class-" prepended. Expected %s, but found %s.'; + + if ( $is_abstract ) { + $expected = 'abstract-' . $expected; + $err_message = 'Abstract class file names should be based on the class name with "abstract-class-" prepended. Expected %s, but found %s.'; + } + + if ( substr( $fileName, 0, -4 ) === $expected ) { + return; + } + + $this->phpcsFile->addError( $err_message, 0, 'InvalidClassFileName', [ $expected . '.php', $fileName ] ); + } + + /** + * Check files containing a trait for the "trait-" prefix, + * and that the rest of the file name reflects the trait name. + * + * @param mixed $stackPtr the token stack + * @param string $fileName the name of the file + * + * @return void + */ + protected function check_maybe_trait( $stackPtr, $fileName ) { + $has_trait = $this->phpcsFile->findNext( \T_TRAIT, $stackPtr ); + + if ( false === $has_trait || false !== $this->is_test_class( $has_trait ) ) { + return; + } + + $trait_name = $this->phpcsFile->getDeclarationName( $has_trait ); + $expected = 'trait-' . $this->kebab( $trait_name ); + $err_message = 'Trait file names should be based on the class name with "trait-" prepended. Expected %s, but found %s.'; + + if ( substr( $fileName, 0, -4 ) === $expected ) { + return; + } + + $this->phpcsFile->addError( $err_message, 0, 'InvalidTraitFileName', [ $expected . '.php', $fileName ] ); + } + + /** + * Check files containing an interface for the "interface-" prefix, + * and that the rest of the file name reflects the interface name. + * + * @param mixed $stackPtr the token stack + * @param string $fileName the name of the file + * + * @return void + */ + protected function check_maybe_interface( $stackPtr, $fileName ) { + $has_interface = $this->phpcsFile->findNext( \T_INTERFACE, $stackPtr ); + + if ( false === $has_interface || false !== $this->is_test_class( $has_interface ) ) { + return; + } + + $interface_name = $this->phpcsFile->getDeclarationName( $has_interface ); + $expected = 'interface-' . $this->kebab( $interface_name ); + $err_message = 'Interface file names should be based on the interface name with "interface-" prepended. Expected %s, but found %s.'; + + if ( substr( $fileName, 0, -4 ) === $expected ) { + return; + } + + $this->phpcsFile->addError( $err_message, 0, 'InvalidInterfaceFileName', [ $expected . '.php', $fileName ] ); + } + + /** + * Convert a string to kebab-case + * + * @param string $string the string to texturise + * + * @return string + */ + protected function kebab( $string = '' ) { + $kebab = preg_replace( '/(?>(?!^[A-Z]))([a-z])([A-Z])/', '$1-$2', $string ); + $kebab = strtolower( $kebab ); + $kebab = str_replace( '_', '-', $kebab ); + + // allow wordpress to be one word + if ( false !== strpos( $string, 'WordPress' ) ) { + $kebab = str_replace( 'word-press', 'wordpress', $kebab ); + } + + return $kebab; + } + +} diff --git a/vendor/bigbite/phpcs-config/BigBite/ruleset.xml b/vendor/bigbite/phpcs-config/BigBite/ruleset.xml new file mode 100644 index 00000000..1be8a7d4 --- /dev/null +++ b/vendor/bigbite/phpcs-config/BigBite/ruleset.xml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + */.git/* + + */wp-activate.php + */wp-blog-header.php + */wp-comments-post.php + */wp-config-sample.php + */wp-config.php + */wp-cron.php + */wp-links-opml.php + */wp-load.php + */wp-login.php + */wp-mail.php + */wp-settings.php + */wp-signup.php + */wp-trackback.php + */mu-plugins/* + */private/* + */upgrade/* + */uploads/* + */wp-admin/* + */wp-content/advanced-cache.php + */wp-content/db.php + */wp-content/object-cache.php + */wp-includes/* + xmlrpc.php + + */tests/* + */cypress/* + + */node_modules/* + */vendor/* + + */vip-config/env-local-config.php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/bigbite/phpcs-config/LICENSE b/vendor/bigbite/phpcs-config/LICENSE new file mode 100644 index 00000000..86607f12 --- /dev/null +++ b/vendor/bigbite/phpcs-config/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Big Bite + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/bigbite/phpcs-config/composer.json b/vendor/bigbite/phpcs-config/composer.json new file mode 100644 index 00000000..7f690ce3 --- /dev/null +++ b/vendor/bigbite/phpcs-config/composer.json @@ -0,0 +1,95 @@ +{ + "name": "bigbite/phpcs-config", + "description": "Big Bite's PHP Coding Standards.", + "type": "phpcodesniffer-standard", + "license": "MIT", + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", + "squizlabs/php_codesniffer" : "^3.6.2", + "wp-coding-standards/wpcs": "^2.3.0", + "automattic/vipwpcs": "^2.3.3", + "php": ">=7.2" + }, + "require-dev": { + "phpcsstandards/phpcsdevtools": "^1.1.0", + "phpcompatibility/php-compatibility": "^9.3.5", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpunit/phpunit": "^7.5.20", + "phpstan/phpstan": "^1.5" + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + }, + "prefer-stable" : true, + "minimum-stability": "dev", + "non-feature-branches": ["chore/*", "hotfix/*"], + "authors": [ + { + "name": "Paul Taylor", + "email": "paul@bigbite.net" + }, + { + "name": "Jon McPartland", + "email": "jon@bigbite.net" + } + ], + "archive": { + "exclude": [ + "/.editorconfig", + "/.gitignore", + "/.phpcs.xml.dist", + "/Tests", + "/phpunit.xml.dist" + ] + }, + "scripts-descriptions": { + "lint": "Runs PHP syntax error checks", + "phpcs": "Runs PHP coding standard checks", + "test": "Runs PHP unit tests", + "analyse": "Runs PHPStan Static Analysis checks", + "is-complete": "Checks that all custom Sniffs are accompanied by unit tests", + "is-complete-strict": "Checks that all custom Sniffs are acommpanied by unit tests and documentation", + "install-cs": "Triggers registration of PHPCS rulesets with PHPCS", + "all-checks": "Runs the scripts 'lint', 'phpcs', 'is-complete', 'test', 'analyse' in that order", + "all-checks-strict": "Runs the scripts 'lint', 'phpcs', 'is-complete-strict', 'test', 'analyse' in that order" + }, + "scripts": { + "lint": [ + "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --exclude vendor --exclude .git" + ], + "phpcs": [ + "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs ." + ], + "test": [ + "@php ./vendor/bin/phpunit --filter BigBite ./vendor/squizlabs/php_codesniffer/tests/AllTests.php" + ], + "analyse": [ + "./vendor/bin/phpstan" + ], + "is-complete": [ + "@php ./vendor/phpcsstandards/phpcsdevtools/bin/phpcs-check-feature-completeness -q ./BigBite" + ], + "is-complete-strict": [ + "@php ./vendor/phpcsstandards/phpcsdevtools/bin/phpcs-check-feature-completeness ./BigBite" + ], + "install-cs": [ + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run" + ], + "all-checks": [ + "@lint", + "@phpcs", + "@is-complete", + "@test", + "@analyse" + ], + "all-checks-strict": [ + "@lint", + "@phpcs", + "@is-complete-strict", + "@test", + "@analyse" + ] + } +} diff --git a/vendor/bigbite/phpcs-config/phpcs.xml.dist.sample b/vendor/bigbite/phpcs-config/phpcs.xml.dist.sample new file mode 100644 index 00000000..3d0337b5 --- /dev/null +++ b/vendor/bigbite/phpcs-config/phpcs.xml.dist.sample @@ -0,0 +1,13 @@ + + + This is an example project + + /root/path/to/file.php + + + + + + + + diff --git a/vendor/bin/phpcbf b/vendor/bin/phpcbf new file mode 100755 index 00000000..1c0c79c4 --- /dev/null +++ b/vendor/bin/phpcbf @@ -0,0 +1,119 @@ +#!/usr/bin/env php +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + return include("phpvfscomposer://" . __DIR__ . '/..'.'/squizlabs/php_codesniffer/bin/phpcbf'); + } +} + +return include __DIR__ . '/..'.'/squizlabs/php_codesniffer/bin/phpcbf'; diff --git a/vendor/bin/phpcs b/vendor/bin/phpcs new file mode 100755 index 00000000..04e658cf --- /dev/null +++ b/vendor/bin/phpcs @@ -0,0 +1,119 @@ +#!/usr/bin/env php +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + return include("phpvfscomposer://" . __DIR__ . '/..'.'/squizlabs/php_codesniffer/bin/phpcs'); + } +} + +return include __DIR__ . '/..'.'/squizlabs/php_codesniffer/bin/phpcs'; diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php new file mode 100644 index 00000000..a72151c7 --- /dev/null +++ b/vendor/composer/ClassLoader.php @@ -0,0 +1,585 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var ?string */ + private $vendorDir; + + // PSR-4 + /** + * @var array[] + * @psalm-var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array[] + * @psalm-var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var array[] + * @psalm-var array + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * @var array[] + * @psalm-var array> + */ + private $prefixesPsr0 = array(); + /** + * @var array[] + * @psalm-var array + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var string[] + * @psalm-var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var bool[] + * @psalm-var array + */ + private $missingClasses = array(); + + /** @var ?string */ + private $apcuPrefix; + + /** + * @var self[] + */ + private static $registeredLoaders = array(); + + /** + * @param ?string $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return string[] + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array[] + * @psalm-return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return array[] + * @psalm-return array + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return array[] + * @psalm-return array + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return string[] Array of classname => path + * @psalm-return array + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param string[] $classMap Class to filename map + * @psalm-param array $classMap + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + $includeFile = self::$includeFile; + $includeFile($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders indexed by their corresponding vendor directories. + * + * @return self[] + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } + + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } +} diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php new file mode 100644 index 00000000..51e734a7 --- /dev/null +++ b/vendor/composer/InstalledVersions.php @@ -0,0 +1,359 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints((string) $constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + + if (self::$canGetVendors) { + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require $vendorDir.'/composer/installed.php'; + $installed[] = self::$installedByVendor[$vendorDir] = $required; + if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + self::$installed = $installed[count($installed) - 1]; + } + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; + } else { + self::$installed = array(); + } + } + + if (self::$installed !== array()) { + $installed[] = self::$installed; + } + + return $installed; + } +} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE new file mode 100644 index 00000000..f27399a0 --- /dev/null +++ b/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 00000000..3bf97c6a --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,82 @@ + $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', + 'Automattic\\Jetpack\\Autoloader\\AutoloadGenerator' => $vendorDir . '/automattic/jetpack-autoloader/src/AutoloadGenerator.php', + 'Big_Bite\\Release_Notes\\AdminBar' => $baseDir . '/inc/class-admin-bar.php', + 'Big_Bite\\Release_Notes\\Archive' => $baseDir . '/inc/class-archive.php', + 'Big_Bite\\Release_Notes\\Loader' => $baseDir . '/inc/class-loader.php', + 'Big_Bite\\Release_Notes\\PostType' => $baseDir . '/inc/class-post-type.php', + 'Big_Bite\\Release_Notes\\RegisterSettings' => $baseDir . '/inc/class-register-settings.php', + 'Big_Bite\\Release_Notes\\ReleaseNote' => $baseDir . '/inc/class-release-note.php', + 'Big_Bite\\Release_Notes\\ReleasePublish' => $baseDir . '/inc/class-release-publish.php', + 'Big_Bite\\Release_Notes\\RestEndpoints' => $baseDir . '/inc/class-rest-endpoints.php', + 'Big_Bite\\Release_Notes\\Widget' => $baseDir . '/inc/class-widget.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', + 'Nette\\ArgumentOutOfRangeException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\DeprecatedException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\DirectoryNotFoundException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\FileNotFoundException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\HtmlStringable' => $vendorDir . '/nette/utils/src/HtmlStringable.php', + 'Nette\\IOException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\InvalidArgumentException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\InvalidStateException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\Iterators\\CachingIterator' => $vendorDir . '/nette/utils/src/Iterators/CachingIterator.php', + 'Nette\\Iterators\\Mapper' => $vendorDir . '/nette/utils/src/Iterators/Mapper.php', + 'Nette\\Localization\\ITranslator' => $vendorDir . '/nette/utils/src/compatibility.php', + 'Nette\\Localization\\Translator' => $vendorDir . '/nette/utils/src/Translator.php', + 'Nette\\MemberAccessException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\NotImplementedException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\NotSupportedException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\OutOfRangeException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\Schema\\Context' => $vendorDir . '/nette/schema/src/Schema/Context.php', + 'Nette\\Schema\\DynamicParameter' => $vendorDir . '/nette/schema/src/Schema/DynamicParameter.php', + 'Nette\\Schema\\Elements\\AnyOf' => $vendorDir . '/nette/schema/src/Schema/Elements/AnyOf.php', + 'Nette\\Schema\\Elements\\Base' => $vendorDir . '/nette/schema/src/Schema/Elements/Base.php', + 'Nette\\Schema\\Elements\\Structure' => $vendorDir . '/nette/schema/src/Schema/Elements/Structure.php', + 'Nette\\Schema\\Elements\\Type' => $vendorDir . '/nette/schema/src/Schema/Elements/Type.php', + 'Nette\\Schema\\Expect' => $vendorDir . '/nette/schema/src/Schema/Expect.php', + 'Nette\\Schema\\Helpers' => $vendorDir . '/nette/schema/src/Schema/Helpers.php', + 'Nette\\Schema\\Message' => $vendorDir . '/nette/schema/src/Schema/Message.php', + 'Nette\\Schema\\Processor' => $vendorDir . '/nette/schema/src/Schema/Processor.php', + 'Nette\\Schema\\Schema' => $vendorDir . '/nette/schema/src/Schema/Schema.php', + 'Nette\\Schema\\ValidationException' => $vendorDir . '/nette/schema/src/Schema/ValidationException.php', + 'Nette\\SmartObject' => $vendorDir . '/nette/utils/src/SmartObject.php', + 'Nette\\StaticClass' => $vendorDir . '/nette/utils/src/StaticClass.php', + 'Nette\\UnexpectedValueException' => $vendorDir . '/nette/utils/src/exceptions.php', + 'Nette\\Utils\\ArrayHash' => $vendorDir . '/nette/utils/src/Utils/ArrayHash.php', + 'Nette\\Utils\\ArrayList' => $vendorDir . '/nette/utils/src/Utils/ArrayList.php', + 'Nette\\Utils\\Arrays' => $vendorDir . '/nette/utils/src/Utils/Arrays.php', + 'Nette\\Utils\\AssertionException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Callback' => $vendorDir . '/nette/utils/src/Utils/Callback.php', + 'Nette\\Utils\\DateTime' => $vendorDir . '/nette/utils/src/Utils/DateTime.php', + 'Nette\\Utils\\FileInfo' => $vendorDir . '/nette/utils/src/Utils/FileInfo.php', + 'Nette\\Utils\\FileSystem' => $vendorDir . '/nette/utils/src/Utils/FileSystem.php', + 'Nette\\Utils\\Finder' => $vendorDir . '/nette/utils/src/Utils/Finder.php', + 'Nette\\Utils\\Floats' => $vendorDir . '/nette/utils/src/Utils/Floats.php', + 'Nette\\Utils\\Helpers' => $vendorDir . '/nette/utils/src/Utils/Helpers.php', + 'Nette\\Utils\\Html' => $vendorDir . '/nette/utils/src/Utils/Html.php', + 'Nette\\Utils\\IHtmlString' => $vendorDir . '/nette/utils/src/compatibility.php', + 'Nette\\Utils\\Image' => $vendorDir . '/nette/utils/src/Utils/Image.php', + 'Nette\\Utils\\ImageException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Json' => $vendorDir . '/nette/utils/src/Utils/Json.php', + 'Nette\\Utils\\JsonException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\ObjectHelpers' => $vendorDir . '/nette/utils/src/Utils/ObjectHelpers.php', + 'Nette\\Utils\\Paginator' => $vendorDir . '/nette/utils/src/Utils/Paginator.php', + 'Nette\\Utils\\Random' => $vendorDir . '/nette/utils/src/Utils/Random.php', + 'Nette\\Utils\\Reflection' => $vendorDir . '/nette/utils/src/Utils/Reflection.php', + 'Nette\\Utils\\RegexpException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Strings' => $vendorDir . '/nette/utils/src/Utils/Strings.php', + 'Nette\\Utils\\Type' => $vendorDir . '/nette/utils/src/Utils/Type.php', + 'Nette\\Utils\\UnknownImageFileException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Validators' => $vendorDir . '/nette/utils/src/Utils/Validators.php', + 'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', + 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', + 'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', + 'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', +); diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php new file mode 100644 index 00000000..0a399884 --- /dev/null +++ b/vendor/composer/autoload_files.php @@ -0,0 +1,15 @@ + $vendorDir . '/symfony/deprecation-contracts/function.php', + 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', + '29d445a10083e232e454774a2e15db2f' => $baseDir . '/inc/constants.php', + '5e7a2cc0d82b2d6b34a44b9a4f082702' => $baseDir . '/inc/asset-settings.php', + 'd1d9e0e109eef7257563d13e6e17e269' => $baseDir . '/inc/utils.php', + '8ec6a49adce1bc1fea1de3ce2bf3218f' => $baseDir . '/inc/setup.php', +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 00000000..15a2ff3a --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/sirbrillig/phpcs-variable-analysis/VariableAnalysis'), + 'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'), + 'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'), + 'League\\Config\\' => array($vendorDir . '/league/config/src'), + 'League\\CommonMark\\' => array($vendorDir . '/league/commonmark/src'), + 'Dflydev\\DotAccessData\\' => array($vendorDir . '/dflydev/dot-access-data/src'), + 'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => array($vendorDir . '/dealerdirect/phpcodesniffer-composer-installer/src'), + 'Big_Bite\\release-notes\\' => array($baseDir . '/inc'), + 'Automattic\\Jetpack\\Autoloader\\' => array($vendorDir . '/automattic/jetpack-autoloader/src'), +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 00000000..774fcf04 --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,50 @@ +register(true); + + $filesToLoad = \Composer\Autoload\ComposerStaticInit0d6c61fd0f46cf60f9125159b17958ae::$files; + $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; + } + }, null, null); + foreach ($filesToLoad as $fileIdentifier => $file) { + $requireFile($fileIdentifier, $file); + } + + return $loader; + } +} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php new file mode 100644 index 00000000..c79e6f6c --- /dev/null +++ b/vendor/composer/autoload_static.php @@ -0,0 +1,175 @@ + __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', + 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', + '29d445a10083e232e454774a2e15db2f' => __DIR__ . '/../..' . '/inc/constants.php', + '5e7a2cc0d82b2d6b34a44b9a4f082702' => __DIR__ . '/../..' . '/inc/asset-settings.php', + 'd1d9e0e109eef7257563d13e6e17e269' => __DIR__ . '/../..' . '/inc/utils.php', + '8ec6a49adce1bc1fea1de3ce2bf3218f' => __DIR__ . '/../..' . '/inc/setup.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'V' => + array ( + 'VariableAnalysis\\' => 17, + ), + 'S' => + array ( + 'Symfony\\Polyfill\\Php80\\' => 23, + ), + 'P' => + array ( + 'Psr\\EventDispatcher\\' => 20, + ), + 'L' => + array ( + 'League\\Config\\' => 14, + 'League\\CommonMark\\' => 18, + ), + 'D' => + array ( + 'Dflydev\\DotAccessData\\' => 22, + 'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => 55, + ), + 'B' => + array ( + 'Big_Bite\\release-notes\\' => 23, + ), + 'A' => + array ( + 'Automattic\\Jetpack\\Autoloader\\' => 30, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'VariableAnalysis\\' => + array ( + 0 => __DIR__ . '/..' . '/sirbrillig/phpcs-variable-analysis/VariableAnalysis', + ), + 'Symfony\\Polyfill\\Php80\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-php80', + ), + 'Psr\\EventDispatcher\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/event-dispatcher/src', + ), + 'League\\Config\\' => + array ( + 0 => __DIR__ . '/..' . '/league/config/src', + ), + 'League\\CommonMark\\' => + array ( + 0 => __DIR__ . '/..' . '/league/commonmark/src', + ), + 'Dflydev\\DotAccessData\\' => + array ( + 0 => __DIR__ . '/..' . '/dflydev/dot-access-data/src', + ), + 'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => + array ( + 0 => __DIR__ . '/..' . '/dealerdirect/phpcodesniffer-composer-installer/src', + ), + 'Big_Bite\\release-notes\\' => + array ( + 0 => __DIR__ . '/../..' . '/inc', + ), + 'Automattic\\Jetpack\\Autoloader\\' => + array ( + 0 => __DIR__ . '/..' . '/automattic/jetpack-autoloader/src', + ), + ); + + public static $classMap = array ( + 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', + 'Automattic\\Jetpack\\Autoloader\\AutoloadGenerator' => __DIR__ . '/..' . '/automattic/jetpack-autoloader/src/AutoloadGenerator.php', + 'Big_Bite\\Release_Notes\\AdminBar' => __DIR__ . '/../..' . '/inc/class-admin-bar.php', + 'Big_Bite\\Release_Notes\\Archive' => __DIR__ . '/../..' . '/inc/class-archive.php', + 'Big_Bite\\Release_Notes\\Loader' => __DIR__ . '/../..' . '/inc/class-loader.php', + 'Big_Bite\\Release_Notes\\PostType' => __DIR__ . '/../..' . '/inc/class-post-type.php', + 'Big_Bite\\Release_Notes\\RegisterSettings' => __DIR__ . '/../..' . '/inc/class-register-settings.php', + 'Big_Bite\\Release_Notes\\ReleaseNote' => __DIR__ . '/../..' . '/inc/class-release-note.php', + 'Big_Bite\\Release_Notes\\ReleasePublish' => __DIR__ . '/../..' . '/inc/class-release-publish.php', + 'Big_Bite\\Release_Notes\\RestEndpoints' => __DIR__ . '/../..' . '/inc/class-rest-endpoints.php', + 'Big_Bite\\Release_Notes\\Widget' => __DIR__ . '/../..' . '/inc/class-widget.php', + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'Nette\\ArgumentOutOfRangeException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\DeprecatedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\DirectoryNotFoundException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\FileNotFoundException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\HtmlStringable' => __DIR__ . '/..' . '/nette/utils/src/HtmlStringable.php', + 'Nette\\IOException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\InvalidArgumentException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\InvalidStateException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\Iterators\\CachingIterator' => __DIR__ . '/..' . '/nette/utils/src/Iterators/CachingIterator.php', + 'Nette\\Iterators\\Mapper' => __DIR__ . '/..' . '/nette/utils/src/Iterators/Mapper.php', + 'Nette\\Localization\\ITranslator' => __DIR__ . '/..' . '/nette/utils/src/compatibility.php', + 'Nette\\Localization\\Translator' => __DIR__ . '/..' . '/nette/utils/src/Translator.php', + 'Nette\\MemberAccessException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\NotImplementedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\NotSupportedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\OutOfRangeException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\Schema\\Context' => __DIR__ . '/..' . '/nette/schema/src/Schema/Context.php', + 'Nette\\Schema\\DynamicParameter' => __DIR__ . '/..' . '/nette/schema/src/Schema/DynamicParameter.php', + 'Nette\\Schema\\Elements\\AnyOf' => __DIR__ . '/..' . '/nette/schema/src/Schema/Elements/AnyOf.php', + 'Nette\\Schema\\Elements\\Base' => __DIR__ . '/..' . '/nette/schema/src/Schema/Elements/Base.php', + 'Nette\\Schema\\Elements\\Structure' => __DIR__ . '/..' . '/nette/schema/src/Schema/Elements/Structure.php', + 'Nette\\Schema\\Elements\\Type' => __DIR__ . '/..' . '/nette/schema/src/Schema/Elements/Type.php', + 'Nette\\Schema\\Expect' => __DIR__ . '/..' . '/nette/schema/src/Schema/Expect.php', + 'Nette\\Schema\\Helpers' => __DIR__ . '/..' . '/nette/schema/src/Schema/Helpers.php', + 'Nette\\Schema\\Message' => __DIR__ . '/..' . '/nette/schema/src/Schema/Message.php', + 'Nette\\Schema\\Processor' => __DIR__ . '/..' . '/nette/schema/src/Schema/Processor.php', + 'Nette\\Schema\\Schema' => __DIR__ . '/..' . '/nette/schema/src/Schema/Schema.php', + 'Nette\\Schema\\ValidationException' => __DIR__ . '/..' . '/nette/schema/src/Schema/ValidationException.php', + 'Nette\\SmartObject' => __DIR__ . '/..' . '/nette/utils/src/SmartObject.php', + 'Nette\\StaticClass' => __DIR__ . '/..' . '/nette/utils/src/StaticClass.php', + 'Nette\\UnexpectedValueException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php', + 'Nette\\Utils\\ArrayHash' => __DIR__ . '/..' . '/nette/utils/src/Utils/ArrayHash.php', + 'Nette\\Utils\\ArrayList' => __DIR__ . '/..' . '/nette/utils/src/Utils/ArrayList.php', + 'Nette\\Utils\\Arrays' => __DIR__ . '/..' . '/nette/utils/src/Utils/Arrays.php', + 'Nette\\Utils\\AssertionException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Callback' => __DIR__ . '/..' . '/nette/utils/src/Utils/Callback.php', + 'Nette\\Utils\\DateTime' => __DIR__ . '/..' . '/nette/utils/src/Utils/DateTime.php', + 'Nette\\Utils\\FileInfo' => __DIR__ . '/..' . '/nette/utils/src/Utils/FileInfo.php', + 'Nette\\Utils\\FileSystem' => __DIR__ . '/..' . '/nette/utils/src/Utils/FileSystem.php', + 'Nette\\Utils\\Finder' => __DIR__ . '/..' . '/nette/utils/src/Utils/Finder.php', + 'Nette\\Utils\\Floats' => __DIR__ . '/..' . '/nette/utils/src/Utils/Floats.php', + 'Nette\\Utils\\Helpers' => __DIR__ . '/..' . '/nette/utils/src/Utils/Helpers.php', + 'Nette\\Utils\\Html' => __DIR__ . '/..' . '/nette/utils/src/Utils/Html.php', + 'Nette\\Utils\\IHtmlString' => __DIR__ . '/..' . '/nette/utils/src/compatibility.php', + 'Nette\\Utils\\Image' => __DIR__ . '/..' . '/nette/utils/src/Utils/Image.php', + 'Nette\\Utils\\ImageException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Json' => __DIR__ . '/..' . '/nette/utils/src/Utils/Json.php', + 'Nette\\Utils\\JsonException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\ObjectHelpers' => __DIR__ . '/..' . '/nette/utils/src/Utils/ObjectHelpers.php', + 'Nette\\Utils\\Paginator' => __DIR__ . '/..' . '/nette/utils/src/Utils/Paginator.php', + 'Nette\\Utils\\Random' => __DIR__ . '/..' . '/nette/utils/src/Utils/Random.php', + 'Nette\\Utils\\Reflection' => __DIR__ . '/..' . '/nette/utils/src/Utils/Reflection.php', + 'Nette\\Utils\\RegexpException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Strings' => __DIR__ . '/..' . '/nette/utils/src/Utils/Strings.php', + 'Nette\\Utils\\Type' => __DIR__ . '/..' . '/nette/utils/src/Utils/Type.php', + 'Nette\\Utils\\UnknownImageFileException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php', + 'Nette\\Utils\\Validators' => __DIR__ . '/..' . '/nette/utils/src/Utils/Validators.php', + 'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', + 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', + 'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', + 'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit0d6c61fd0f46cf60f9125159b17958ae::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit0d6c61fd0f46cf60f9125159b17958ae::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit0d6c61fd0f46cf60f9125159b17958ae::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/composer.lock b/vendor/composer/installed.json similarity index 92% rename from composer.lock rename to vendor/composer/installed.json index 7629e723..e6fbe09b 100644 --- a/composer.lock +++ b/vendor/composer/installed.json @@ -1,14 +1,9 @@ { - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "1d36fcaf4111c0a7dc2d324dad69c75f", "packages": [ { "name": "automattic/jetpack-autoloader", "version": "v2.11.20", + "version_normalized": "2.11.20.0", "source": { "type": "git", "url": "https://github.com/Automattic/jetpack-autoloader.git", @@ -27,6 +22,7 @@ "automattic/jetpack-changelogger": "^3.3.2", "yoast/phpunit-polyfills": "1.0.4" }, + "time": "2023-05-11T05:50:38+00:00", "type": "composer-plugin", "extra": { "autotagger": true, @@ -39,6 +35,7 @@ "dev-trunk": "2.11.x-dev" } }, + "installation-source": "dist", "autoload": { "psr-4": { "Automattic\\Jetpack\\Autoloader\\": "src" @@ -55,11 +52,244 @@ "support": { "source": "https://github.com/Automattic/jetpack-autoloader/tree/v2.11.20" }, - "time": "2023-05-11T05:50:38+00:00" + "install-path": "../automattic/jetpack-autoloader" + }, + { + "name": "automattic/vipwpcs", + "version": "2.3.3", + "version_normalized": "2.3.3.0", + "source": { + "type": "git", + "url": "https://github.com/Automattic/VIP-Coding-Standards.git", + "reference": "6cd0a6a82bc0ac988dbf9d6a7c2e293dc8ac640b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Automattic/VIP-Coding-Standards/zipball/6cd0a6a82bc0ac988dbf9d6a7c2e293dc8ac640b", + "reference": "6cd0a6a82bc0ac988dbf9d6a7c2e293dc8ac640b", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7", + "php": ">=5.4", + "sirbrillig/phpcs-variable-analysis": "^2.11.1", + "squizlabs/php_codesniffer": "^3.5.5", + "wp-coding-standards/wpcs": "^2.3" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^0.5", + "php-parallel-lint/php-parallel-lint": "^1.0", + "phpcompatibility/php-compatibility": "^9", + "phpcsstandards/phpcsdevtools": "^1.0", + "phpunit/phpunit": "^4 || ^5 || ^6 || ^7" + }, + "time": "2021-09-29T16:20:23+00:00", + "type": "phpcodesniffer-standard", + "installation-source": "dist", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/Automattic/VIP-Coding-Standards/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress VIP minimum coding conventions", + "keywords": [ + "phpcs", + "standards", + "wordpress" + ], + "support": { + "issues": "https://github.com/Automattic/VIP-Coding-Standards/issues", + "source": "https://github.com/Automattic/VIP-Coding-Standards", + "wiki": "https://github.com/Automattic/VIP-Coding-Standards/wiki" + }, + "install-path": "../automattic/vipwpcs" + }, + { + "name": "bigbite/phpcs-config", + "version": "dev-main", + "version_normalized": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/bigbite/phpcs-config.git", + "reference": "adefd69c6754a6dc25e9fe51baee73a090cef541" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bigbite/phpcs-config/zipball/adefd69c6754a6dc25e9fe51baee73a090cef541", + "reference": "adefd69c6754a6dc25e9fe51baee73a090cef541", + "shasum": "" + }, + "require": { + "automattic/vipwpcs": "^2.3.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", + "php": ">=7.2", + "squizlabs/php_codesniffer": "^3.6.2", + "wp-coding-standards/wpcs": "^2.3.0" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.3.5", + "phpcsstandards/phpcsdevtools": "^1.1.0", + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^7.5.20" + }, + "time": "2023-05-10T08:31:34+00:00", + "default-branch": true, + "type": "phpcodesniffer-standard", + "installation-source": "dist", + "archive": { + "exclude": [ + "/.editorconfig", + "/.gitignore", + "/.phpcs.xml.dist", + "/Tests", + "/phpunit.xml.dist" + ] + }, + "scripts": { + "lint": [ + "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --exclude vendor --exclude .git" + ], + "phpcs": [ + "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs ." + ], + "test": [ + "@php ./vendor/bin/phpunit --filter BigBite ./vendor/squizlabs/php_codesniffer/tests/AllTests.php" + ], + "analyse": [ + "./vendor/bin/phpstan" + ], + "is-complete": [ + "@php ./vendor/phpcsstandards/phpcsdevtools/bin/phpcs-check-feature-completeness -q ./BigBite" + ], + "is-complete-strict": [ + "@php ./vendor/phpcsstandards/phpcsdevtools/bin/phpcs-check-feature-completeness ./BigBite" + ], + "install-cs": [ + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run" + ], + "all-checks": [ + "@lint", + "@phpcs", + "@is-complete", + "@test", + "@analyse" + ], + "all-checks-strict": [ + "@lint", + "@phpcs", + "@is-complete-strict", + "@test", + "@analyse" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paul Taylor", + "email": "paul@bigbite.net" + }, + { + "name": "Jon McPartland", + "email": "jon@bigbite.net" + } + ], + "description": "Big Bite's PHP Coding Standards.", + "support": { + "source": "https://github.com/bigbite/phpcs-config/tree/main", + "issues": "https://github.com/bigbite/phpcs-config/issues" + }, + "install-path": "../bigbite/phpcs-config" + }, + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v0.7.2", + "version_normalized": "0.7.2.0", + "source": { + "type": "git", + "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", + "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.0" + }, + "time": "2022-02-04T12:51:07+00:00", + "type": "composer-plugin", + "extra": { + "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Dealerdirect\\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/Dealerdirect/phpcodesniffer-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/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, + "install-path": "../dealerdirect/phpcodesniffer-composer-installer" }, { "name": "dflydev/dot-access-data", "version": "v3.0.2", + "version_normalized": "3.0.2.0", "source": { "type": "git", "url": "https://github.com/dflydev/dflydev-dot-access-data.git", @@ -81,12 +311,14 @@ "squizlabs/php_codesniffer": "^3.5", "vimeo/psalm": "^4.0.0" }, + "time": "2022-10-27T11:44:00+00:00", "type": "library", "extra": { "branch-alias": { "dev-main": "3.x-dev" } }, + "installation-source": "dist", "autoload": { "psr-4": { "Dflydev\\DotAccessData\\": "src/" @@ -130,11 +362,12 @@ "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" }, - "time": "2022-10-27T11:44:00+00:00" + "install-path": "../dflydev/dot-access-data" }, { "name": "league/commonmark", "version": "2.4.0", + "version_normalized": "2.4.0.0", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", @@ -176,12 +409,14 @@ "suggest": { "symfony/yaml": "v2.3+ required if using the Front Matter extension" }, + "time": "2023-03-24T15:16:10+00:00", "type": "library", "extra": { "branch-alias": { "dev-main": "2.5-dev" } }, + "installation-source": "dist", "autoload": { "psr-4": { "League\\CommonMark\\": "src" @@ -236,11 +471,12 @@ "type": "tidelift" } ], - "time": "2023-03-24T15:16:10+00:00" + "install-path": "../league/commonmark" }, { "name": "league/config", "version": "v1.2.0", + "version_normalized": "1.2.0.0", "source": { "type": "git", "url": "https://github.com/thephpleague/config.git", @@ -264,12 +500,14 @@ "unleashedtech/php-coding-standard": "^3.1", "vimeo/psalm": "^4.7.3" }, + "time": "2022-12-11T20:36:23+00:00", "type": "library", "extra": { "branch-alias": { "dev-main": "1.2-dev" } }, + "installation-source": "dist", "autoload": { "psr-4": { "League\\Config\\": "src" @@ -318,11 +556,12 @@ "type": "github" } ], - "time": "2022-12-11T20:36:23+00:00" + "install-path": "../league/config" }, { "name": "nette/schema", "version": "v1.2.3", + "version_normalized": "1.2.3.0", "source": { "type": "git", "url": "https://github.com/nette/schema.git", @@ -343,12 +582,14 @@ "phpstan/phpstan-nette": "^1.0", "tracy/tracy": "^2.7" }, + "time": "2022-10-13T01:24:26+00:00", "type": "library", "extra": { "branch-alias": { "dev-master": "1.2-dev" } }, + "installation-source": "dist", "autoload": { "classmap": [ "src/" @@ -380,11 +621,12 @@ "issues": "https://github.com/nette/schema/issues", "source": "https://github.com/nette/schema/tree/v1.2.3" }, - "time": "2022-10-13T01:24:26+00:00" + "install-path": "../nette/schema" }, { "name": "nette/utils", "version": "v4.0.0", + "version_normalized": "4.0.0.0", "source": { "type": "git", "url": "https://github.com/nette/utils.git", @@ -418,12 +660,14 @@ "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()", "ext-xml": "to use Strings::length() etc. when mbstring is not available" }, + "time": "2023-02-02T10:41:53+00:00", "type": "library", "extra": { "branch-alias": { "dev-master": "4.0-dev" } }, + "installation-source": "dist", "autoload": { "classmap": [ "src/" @@ -467,11 +711,12 @@ "issues": "https://github.com/nette/utils/issues", "source": "https://github.com/nette/utils/tree/v4.0.0" }, - "time": "2023-02-02T10:41:53+00:00" + "install-path": "../nette/utils" }, { "name": "psr/event-dispatcher", "version": "1.0.0", + "version_normalized": "1.0.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/event-dispatcher.git", @@ -486,12 +731,14 @@ "require": { "php": ">=7.2.0" }, + "time": "2019-01-08T18:20:26+00:00", "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, + "installation-source": "dist", "autoload": { "psr-4": { "Psr\\EventDispatcher\\": "src/" @@ -517,39 +764,163 @@ "issues": "https://github.com/php-fig/event-dispatcher/issues", "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" }, - "time": "2019-01-08T18:20:26+00:00" + "install-path": "../psr/event-dispatcher" }, { - "name": "symfony/deprecation-contracts", - "version": "v3.0.2", + "name": "sirbrillig/phpcs-variable-analysis", + "version": "v2.11.16", + "version_normalized": "2.11.16.0", "source": { "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", + "reference": "dc5582dc5a93a235557af73e523c389aac9a8e88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/dc5582dc5a93a235557af73e523c389aac9a8e88", + "reference": "dc5582dc5a93a235557af73e523c389aac9a8e88", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=5.4.0", + "squizlabs/php_codesniffer": "^3.5.6" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0", + "phpcsstandards/phpcsdevcs": "^1.1", + "phpstan/phpstan": "^1.7", + "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0", + "sirbrillig/phpcs-import-detection": "^1.1", + "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0@beta" }, + "time": "2023-03-31T16:46:32+00:00", + "type": "phpcodesniffer-standard", + "installation-source": "dist", "autoload": { - "files": [ - "function.php" - ] + "psr-4": { + "VariableAnalysis\\": "VariableAnalysis/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Sam Graham", + "email": "php-codesniffer-variableanalysis@illusori.co.uk" + }, + { + "name": "Payton Swick", + "email": "payton@foolord.com" + } + ], + "description": "A PHPCS sniff to detect problems with variables.", + "keywords": [ + "phpcs", + "static analysis" + ], + "support": { + "issues": "https://github.com/sirbrillig/phpcs-variable-analysis/issues", + "source": "https://github.com/sirbrillig/phpcs-variable-analysis", + "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki" + }, + "install-path": "../sirbrillig/phpcs-variable-analysis" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.7.2", + "version_normalized": "3.7.2.0", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "time": "2023-02-22T23:07:41+00:00", + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "installation-source": "dist", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "install-path": "../squizlabs/php_codesniffer" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.0.2", + "version_normalized": "3.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "shasum": "" + }, + "require": { + "php": ">=8.0.2" + }, + "time": "2022-01-02T09:55:41+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "function.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -584,11 +955,12 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "install-path": "../symfony/deprecation-contracts" }, { "name": "symfony/polyfill-php80", "version": "v1.27.0", + "version_normalized": "1.27.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -603,6 +975,7 @@ "require": { "php": ">=7.1" }, + "time": "2022-11-03T14:55:06+00:00", "type": "library", "extra": { "branch-alias": { @@ -613,6 +986,7 @@ "url": "https://github.com/symfony/polyfill" } }, + "installation-source": "dist", "autoload": { "files": [ "bootstrap.php" @@ -667,351 +1041,12 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" - } - ], - "packages-dev": [ - { - "name": "automattic/vipwpcs", - "version": "2.3.3", - "source": { - "type": "git", - "url": "https://github.com/Automattic/VIP-Coding-Standards.git", - "reference": "6cd0a6a82bc0ac988dbf9d6a7c2e293dc8ac640b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Automattic/VIP-Coding-Standards/zipball/6cd0a6a82bc0ac988dbf9d6a7c2e293dc8ac640b", - "reference": "6cd0a6a82bc0ac988dbf9d6a7c2e293dc8ac640b", - "shasum": "" - }, - "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7", - "php": ">=5.4", - "sirbrillig/phpcs-variable-analysis": "^2.11.1", - "squizlabs/php_codesniffer": "^3.5.5", - "wp-coding-standards/wpcs": "^2.3" - }, - "require-dev": { - "php-parallel-lint/php-console-highlighter": "^0.5", - "php-parallel-lint/php-parallel-lint": "^1.0", - "phpcompatibility/php-compatibility": "^9", - "phpcsstandards/phpcsdevtools": "^1.0", - "phpunit/phpunit": "^4 || ^5 || ^6 || ^7" - }, - "type": "phpcodesniffer-standard", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Contributors", - "homepage": "https://github.com/Automattic/VIP-Coding-Standards/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress VIP minimum coding conventions", - "keywords": [ - "phpcs", - "standards", - "wordpress" - ], - "support": { - "issues": "https://github.com/Automattic/VIP-Coding-Standards/issues", - "source": "https://github.com/Automattic/VIP-Coding-Standards", - "wiki": "https://github.com/Automattic/VIP-Coding-Standards/wiki" - }, - "time": "2021-09-29T16:20:23+00:00" - }, - { - "name": "bigbite/phpcs-config", - "version": "dev-main", - "source": { - "type": "git", - "url": "https://github.com/bigbite/phpcs-config.git", - "reference": "adefd69c6754a6dc25e9fe51baee73a090cef541" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/bigbite/phpcs-config/zipball/adefd69c6754a6dc25e9fe51baee73a090cef541", - "reference": "adefd69c6754a6dc25e9fe51baee73a090cef541", - "shasum": "" - }, - "require": { - "automattic/vipwpcs": "^2.3.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", - "php": ">=7.2", - "squizlabs/php_codesniffer": "^3.6.2", - "wp-coding-standards/wpcs": "^2.3.0" - }, - "require-dev": { - "php-parallel-lint/php-parallel-lint": "^1.3.2", - "phpcompatibility/php-compatibility": "^9.3.5", - "phpcsstandards/phpcsdevtools": "^1.1.0", - "phpstan/phpstan": "^1.5", - "phpunit/phpunit": "^7.5.20" - }, - "default-branch": true, - "type": "phpcodesniffer-standard", - "archive": { - "exclude": [ - "/.editorconfig", - "/.gitignore", - "/.phpcs.xml.dist", - "/Tests", - "/phpunit.xml.dist" - ] - }, - "scripts": { - "lint": [ - "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --exclude vendor --exclude .git" - ], - "phpcs": [ - "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs ." - ], - "test": [ - "@php ./vendor/bin/phpunit --filter BigBite ./vendor/squizlabs/php_codesniffer/tests/AllTests.php" - ], - "analyse": [ - "./vendor/bin/phpstan" - ], - "is-complete": [ - "@php ./vendor/phpcsstandards/phpcsdevtools/bin/phpcs-check-feature-completeness -q ./BigBite" - ], - "is-complete-strict": [ - "@php ./vendor/phpcsstandards/phpcsdevtools/bin/phpcs-check-feature-completeness ./BigBite" - ], - "install-cs": [ - "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run" - ], - "all-checks": [ - "@lint", - "@phpcs", - "@is-complete", - "@test", - "@analyse" - ], - "all-checks-strict": [ - "@lint", - "@phpcs", - "@is-complete-strict", - "@test", - "@analyse" - ] - }, - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paul Taylor", - "email": "paul@bigbite.net" - }, - { - "name": "Jon McPartland", - "email": "jon@bigbite.net" - } - ], - "description": "Big Bite's PHP Coding Standards.", - "support": { - "source": "https://github.com/bigbite/phpcs-config/tree/main", - "issues": "https://github.com/bigbite/phpcs-config/issues" - }, - "time": "2023-05-10T08:31:34+00:00" - }, - { - "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v0.7.2", - "source": { - "type": "git", - "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3", - "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" - }, - "require-dev": { - "composer/composer": "*", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" - }, - "autoload": { - "psr-4": { - "Dealerdirect\\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/Dealerdirect/phpcodesniffer-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/dealerdirect/phpcodesniffer-composer-installer/issues", - "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" - }, - "time": "2022-02-04T12:51:07+00:00" - }, - { - "name": "sirbrillig/phpcs-variable-analysis", - "version": "v2.11.16", - "source": { - "type": "git", - "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", - "reference": "dc5582dc5a93a235557af73e523c389aac9a8e88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/dc5582dc5a93a235557af73e523c389aac9a8e88", - "reference": "dc5582dc5a93a235557af73e523c389aac9a8e88", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "squizlabs/php_codesniffer": "^3.5.6" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0", - "phpcsstandards/phpcsdevcs": "^1.1", - "phpstan/phpstan": "^1.7", - "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0", - "sirbrillig/phpcs-import-detection": "^1.1", - "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0@beta" - }, - "type": "phpcodesniffer-standard", - "autoload": { - "psr-4": { - "VariableAnalysis\\": "VariableAnalysis/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Sam Graham", - "email": "php-codesniffer-variableanalysis@illusori.co.uk" - }, - { - "name": "Payton Swick", - "email": "payton@foolord.com" - } - ], - "description": "A PHPCS sniff to detect problems with variables.", - "keywords": [ - "phpcs", - "static analysis" - ], - "support": { - "issues": "https://github.com/sirbrillig/phpcs-variable-analysis/issues", - "source": "https://github.com/sirbrillig/phpcs-variable-analysis", - "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki" - }, - "time": "2023-03-31T16:46:32+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.2", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2023-02-22T23:07:41+00:00" + "install-path": "../symfony/polyfill-php80" }, { "name": "wp-coding-standards/wpcs", "version": "2.3.0", + "version_normalized": "2.3.0.0", "source": { "type": "git", "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", @@ -1036,7 +1071,9 @@ "suggest": { "dealerdirect/phpcodesniffer-composer-installer": "^0.6 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically." }, + "time": "2020-05-13T23:57:56+00:00", "type": "phpcodesniffer-standard", + "installation-source": "dist", "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" @@ -1058,17 +1095,16 @@ "source": "https://github.com/WordPress/WordPress-Coding-Standards", "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" }, - "time": "2020-05-13T23:57:56+00:00" + "install-path": "../wp-coding-standards/wpcs" } ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": { - "bigbite/phpcs-config": 20 - }, - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "plugin-api-version": "2.3.0" + "dev": true, + "dev-package-names": [ + "automattic/vipwpcs", + "bigbite/phpcs-config", + "dealerdirect/phpcodesniffer-composer-installer", + "sirbrillig/phpcs-variable-analysis", + "squizlabs/php_codesniffer", + "wp-coding-standards/wpcs" + ] } diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php new file mode 100644 index 00000000..50e6f8f3 --- /dev/null +++ b/vendor/composer/installed.php @@ -0,0 +1,160 @@ + array( + 'name' => 'big-bite/release-notes', + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => '6cd3bf1caa5167fd070676f9f76c054ca34778ea', + 'type' => 'wordpress-plugin', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev' => true, + ), + 'versions' => array( + 'automattic/jetpack-autoloader' => array( + 'pretty_version' => 'v2.11.20', + 'version' => '2.11.20.0', + 'reference' => '6067cbfb8d42d57a1fcf7bd11c7f20f80fe75db7', + 'type' => 'composer-plugin', + 'install_path' => __DIR__ . '/../automattic/jetpack-autoloader', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'automattic/vipwpcs' => array( + 'pretty_version' => '2.3.3', + 'version' => '2.3.3.0', + 'reference' => '6cd0a6a82bc0ac988dbf9d6a7c2e293dc8ac640b', + 'type' => 'phpcodesniffer-standard', + 'install_path' => __DIR__ . '/../automattic/vipwpcs', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'big-bite/release-notes' => array( + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => '6cd3bf1caa5167fd070676f9f76c054ca34778ea', + 'type' => 'wordpress-plugin', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'bigbite/phpcs-config' => array( + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => 'adefd69c6754a6dc25e9fe51baee73a090cef541', + 'type' => 'phpcodesniffer-standard', + 'install_path' => __DIR__ . '/../bigbite/phpcs-config', + 'aliases' => array( + 0 => '9999999-dev', + ), + 'dev_requirement' => true, + ), + 'dealerdirect/phpcodesniffer-composer-installer' => array( + 'pretty_version' => 'v0.7.2', + 'version' => '0.7.2.0', + 'reference' => '1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db', + 'type' => 'composer-plugin', + 'install_path' => __DIR__ . '/../dealerdirect/phpcodesniffer-composer-installer', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'dflydev/dot-access-data' => array( + 'pretty_version' => 'v3.0.2', + 'version' => '3.0.2.0', + 'reference' => 'f41715465d65213d644d3141a6a93081be5d3549', + 'type' => 'library', + 'install_path' => __DIR__ . '/../dflydev/dot-access-data', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'league/commonmark' => array( + 'pretty_version' => '2.4.0', + 'version' => '2.4.0.0', + 'reference' => 'd44a24690f16b8c1808bf13b1bd54ae4c63ea048', + 'type' => 'library', + 'install_path' => __DIR__ . '/../league/commonmark', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'league/config' => array( + 'pretty_version' => 'v1.2.0', + 'version' => '1.2.0.0', + 'reference' => '754b3604fb2984c71f4af4a9cbe7b57f346ec1f3', + 'type' => 'library', + 'install_path' => __DIR__ . '/../league/config', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'nette/schema' => array( + 'pretty_version' => 'v1.2.3', + 'version' => '1.2.3.0', + 'reference' => 'abbdbb70e0245d5f3bf77874cea1dfb0c930d06f', + 'type' => 'library', + 'install_path' => __DIR__ . '/../nette/schema', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'nette/utils' => array( + 'pretty_version' => 'v4.0.0', + 'version' => '4.0.0.0', + 'reference' => 'cacdbf5a91a657ede665c541eda28941d4b09c1e', + 'type' => 'library', + 'install_path' => __DIR__ . '/../nette/utils', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/event-dispatcher' => array( + 'pretty_version' => '1.0.0', + 'version' => '1.0.0.0', + 'reference' => 'dbefd12671e8a14ec7f180cab83036ed26714bb0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/event-dispatcher', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'sirbrillig/phpcs-variable-analysis' => array( + 'pretty_version' => 'v2.11.16', + 'version' => '2.11.16.0', + 'reference' => 'dc5582dc5a93a235557af73e523c389aac9a8e88', + 'type' => 'phpcodesniffer-standard', + 'install_path' => __DIR__ . '/../sirbrillig/phpcs-variable-analysis', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'squizlabs/php_codesniffer' => array( + 'pretty_version' => '3.7.2', + 'version' => '3.7.2.0', + 'reference' => 'ed8e00df0a83aa96acf703f8c2979ff33341f879', + 'type' => 'library', + 'install_path' => __DIR__ . '/../squizlabs/php_codesniffer', + 'aliases' => array(), + 'dev_requirement' => true, + ), + 'symfony/deprecation-contracts' => array( + 'pretty_version' => 'v3.0.2', + 'version' => '3.0.2.0', + 'reference' => '26954b3d62a6c5fd0ea8a2a00c0353a14978d05c', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/deprecation-contracts', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/polyfill-php80' => array( + 'pretty_version' => 'v1.27.0', + 'version' => '1.27.0.0', + 'reference' => '7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-php80', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'wp-coding-standards/wpcs' => array( + 'pretty_version' => '2.3.0', + 'version' => '2.3.0.0', + 'reference' => '7da1894633f168fe244afc6de00d141f27517b62', + 'type' => 'phpcodesniffer-standard', + 'install_path' => __DIR__ . '/../wp-coding-standards/wpcs', + 'aliases' => array(), + 'dev_requirement' => true, + ), + ), +); diff --git a/vendor/composer/jetpack_autoload_classmap.php b/vendor/composer/jetpack_autoload_classmap.php new file mode 100644 index 00000000..67a0d197 --- /dev/null +++ b/vendor/composer/jetpack_autoload_classmap.php @@ -0,0 +1,297 @@ + array( + 'version' => '1.27.0.0', + 'path' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php' + ), + 'Automattic\\Jetpack\\Autoloader\\AutoloadGenerator' => array( + 'version' => '2.11.20.0', + 'path' => $vendorDir . '/automattic/jetpack-autoloader/src/AutoloadGenerator.php' + ), + 'Big_Bite\\Release_Notes\\AdminBar' => array( + 'version' => 'dev-main', + 'path' => $baseDir . '/inc/class-admin-bar.php' + ), + 'Big_Bite\\Release_Notes\\Archive' => array( + 'version' => 'dev-main', + 'path' => $baseDir . '/inc/class-archive.php' + ), + 'Big_Bite\\Release_Notes\\Loader' => array( + 'version' => 'dev-main', + 'path' => $baseDir . '/inc/class-loader.php' + ), + 'Big_Bite\\Release_Notes\\PostType' => array( + 'version' => 'dev-main', + 'path' => $baseDir . '/inc/class-post-type.php' + ), + 'Big_Bite\\Release_Notes\\RegisterSettings' => array( + 'version' => 'dev-main', + 'path' => $baseDir . '/inc/class-register-settings.php' + ), + 'Big_Bite\\Release_Notes\\ReleaseNote' => array( + 'version' => 'dev-main', + 'path' => $baseDir . '/inc/class-release-note.php' + ), + 'Big_Bite\\Release_Notes\\ReleasePublish' => array( + 'version' => 'dev-main', + 'path' => $baseDir . '/inc/class-release-publish.php' + ), + 'Big_Bite\\Release_Notes\\RestEndpoints' => array( + 'version' => 'dev-main', + 'path' => $baseDir . '/inc/class-rest-endpoints.php' + ), + 'Big_Bite\\Release_Notes\\Widget' => array( + 'version' => 'dev-main', + 'path' => $baseDir . '/inc/class-widget.php' + ), + 'Nette\\ArgumentOutOfRangeException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/exceptions.php' + ), + 'Nette\\DeprecatedException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/exceptions.php' + ), + 'Nette\\DirectoryNotFoundException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/exceptions.php' + ), + 'Nette\\FileNotFoundException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/exceptions.php' + ), + 'Nette\\HtmlStringable' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/HtmlStringable.php' + ), + 'Nette\\IOException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/exceptions.php' + ), + 'Nette\\InvalidArgumentException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/exceptions.php' + ), + 'Nette\\InvalidStateException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/exceptions.php' + ), + 'Nette\\Iterators\\CachingIterator' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Iterators/CachingIterator.php' + ), + 'Nette\\Iterators\\Mapper' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Iterators/Mapper.php' + ), + 'Nette\\Localization\\ITranslator' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/compatibility.php' + ), + 'Nette\\Localization\\Translator' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Translator.php' + ), + 'Nette\\MemberAccessException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/exceptions.php' + ), + 'Nette\\NotImplementedException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/exceptions.php' + ), + 'Nette\\NotSupportedException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/exceptions.php' + ), + 'Nette\\OutOfRangeException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/exceptions.php' + ), + 'Nette\\Schema\\Context' => array( + 'version' => '1.2.3.0', + 'path' => $vendorDir . '/nette/schema/src/Schema/Context.php' + ), + 'Nette\\Schema\\DynamicParameter' => array( + 'version' => '1.2.3.0', + 'path' => $vendorDir . '/nette/schema/src/Schema/DynamicParameter.php' + ), + 'Nette\\Schema\\Elements\\AnyOf' => array( + 'version' => '1.2.3.0', + 'path' => $vendorDir . '/nette/schema/src/Schema/Elements/AnyOf.php' + ), + 'Nette\\Schema\\Elements\\Base' => array( + 'version' => '1.2.3.0', + 'path' => $vendorDir . '/nette/schema/src/Schema/Elements/Base.php' + ), + 'Nette\\Schema\\Elements\\Structure' => array( + 'version' => '1.2.3.0', + 'path' => $vendorDir . '/nette/schema/src/Schema/Elements/Structure.php' + ), + 'Nette\\Schema\\Elements\\Type' => array( + 'version' => '1.2.3.0', + 'path' => $vendorDir . '/nette/schema/src/Schema/Elements/Type.php' + ), + 'Nette\\Schema\\Expect' => array( + 'version' => '1.2.3.0', + 'path' => $vendorDir . '/nette/schema/src/Schema/Expect.php' + ), + 'Nette\\Schema\\Helpers' => array( + 'version' => '1.2.3.0', + 'path' => $vendorDir . '/nette/schema/src/Schema/Helpers.php' + ), + 'Nette\\Schema\\Message' => array( + 'version' => '1.2.3.0', + 'path' => $vendorDir . '/nette/schema/src/Schema/Message.php' + ), + 'Nette\\Schema\\Processor' => array( + 'version' => '1.2.3.0', + 'path' => $vendorDir . '/nette/schema/src/Schema/Processor.php' + ), + 'Nette\\Schema\\Schema' => array( + 'version' => '1.2.3.0', + 'path' => $vendorDir . '/nette/schema/src/Schema/Schema.php' + ), + 'Nette\\Schema\\ValidationException' => array( + 'version' => '1.2.3.0', + 'path' => $vendorDir . '/nette/schema/src/Schema/ValidationException.php' + ), + 'Nette\\SmartObject' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/SmartObject.php' + ), + 'Nette\\StaticClass' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/StaticClass.php' + ), + 'Nette\\UnexpectedValueException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/exceptions.php' + ), + 'Nette\\Utils\\ArrayHash' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/ArrayHash.php' + ), + 'Nette\\Utils\\ArrayList' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/ArrayList.php' + ), + 'Nette\\Utils\\Arrays' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Arrays.php' + ), + 'Nette\\Utils\\AssertionException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/exceptions.php' + ), + 'Nette\\Utils\\Callback' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Callback.php' + ), + 'Nette\\Utils\\DateTime' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/DateTime.php' + ), + 'Nette\\Utils\\FileInfo' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/FileInfo.php' + ), + 'Nette\\Utils\\FileSystem' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/FileSystem.php' + ), + 'Nette\\Utils\\Finder' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Finder.php' + ), + 'Nette\\Utils\\Floats' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Floats.php' + ), + 'Nette\\Utils\\Helpers' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Helpers.php' + ), + 'Nette\\Utils\\Html' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Html.php' + ), + 'Nette\\Utils\\IHtmlString' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/compatibility.php' + ), + 'Nette\\Utils\\Image' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Image.php' + ), + 'Nette\\Utils\\ImageException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/exceptions.php' + ), + 'Nette\\Utils\\Json' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Json.php' + ), + 'Nette\\Utils\\JsonException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/exceptions.php' + ), + 'Nette\\Utils\\ObjectHelpers' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/ObjectHelpers.php' + ), + 'Nette\\Utils\\Paginator' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Paginator.php' + ), + 'Nette\\Utils\\Random' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Random.php' + ), + 'Nette\\Utils\\Reflection' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Reflection.php' + ), + 'Nette\\Utils\\RegexpException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/exceptions.php' + ), + 'Nette\\Utils\\Strings' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Strings.php' + ), + 'Nette\\Utils\\Type' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Type.php' + ), + 'Nette\\Utils\\UnknownImageFileException' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/exceptions.php' + ), + 'Nette\\Utils\\Validators' => array( + 'version' => '4.0.0.0', + 'path' => $vendorDir . '/nette/utils/src/Utils/Validators.php' + ), + 'PhpToken' => array( + 'version' => '1.27.0.0', + 'path' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php' + ), + 'Stringable' => array( + 'version' => '1.27.0.0', + 'path' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php' + ), + 'UnhandledMatchError' => array( + 'version' => '1.27.0.0', + 'path' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php' + ), + 'ValueError' => array( + 'version' => '1.27.0.0', + 'path' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php' + ), +); diff --git a/vendor/composer/jetpack_autoload_filemap.php b/vendor/composer/jetpack_autoload_filemap.php new file mode 100644 index 00000000..9917e66d --- /dev/null +++ b/vendor/composer/jetpack_autoload_filemap.php @@ -0,0 +1,33 @@ + array( + 'version' => '3.0.2.0', + 'path' => $vendorDir . '/symfony/deprecation-contracts/function.php' + ), + 'a4a119a56e50fbb293281d9a48007e0e' => array( + 'version' => '1.27.0.0', + 'path' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php' + ), + '29d445a10083e232e454774a2e15db2f' => array( + 'version' => 'dev-main', + 'path' => $baseDir . '/inc/constants.php' + ), + '5e7a2cc0d82b2d6b34a44b9a4f082702' => array( + 'version' => 'dev-main', + 'path' => $baseDir . '/inc/asset-settings.php' + ), + 'd1d9e0e109eef7257563d13e6e17e269' => array( + 'version' => 'dev-main', + 'path' => $baseDir . '/inc/utils.php' + ), + '8ec6a49adce1bc1fea1de3ce2bf3218f' => array( + 'version' => 'dev-main', + 'path' => $baseDir . '/inc/setup.php' + ), +); diff --git a/vendor/composer/jetpack_autoload_psr4.php b/vendor/composer/jetpack_autoload_psr4.php new file mode 100644 index 00000000..8b29a47c --- /dev/null +++ b/vendor/composer/jetpack_autoload_psr4.php @@ -0,0 +1,45 @@ + array( + 'version' => '2.11.16.0', + 'path' => array( $vendorDir . '/sirbrillig/phpcs-variable-analysis/VariableAnalysis' ) + ), + 'Symfony\\Polyfill\\Php80\\' => array( + 'version' => '1.27.0.0', + 'path' => array( $vendorDir . '/symfony/polyfill-php80' ) + ), + 'Psr\\EventDispatcher\\' => array( + 'version' => '1.0.0.0', + 'path' => array( $vendorDir . '/psr/event-dispatcher/src' ) + ), + 'League\\Config\\' => array( + 'version' => '1.2.0.0', + 'path' => array( $vendorDir . '/league/config/src' ) + ), + 'League\\CommonMark\\' => array( + 'version' => '2.4.0.0', + 'path' => array( $vendorDir . '/league/commonmark/src' ) + ), + 'Dflydev\\DotAccessData\\' => array( + 'version' => '3.0.2.0', + 'path' => array( $vendorDir . '/dflydev/dot-access-data/src' ) + ), + 'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => array( + 'version' => '0.7.2.0', + 'path' => array( $vendorDir . '/dealerdirect/phpcodesniffer-composer-installer/src' ) + ), + 'Big_Bite\\release-notes\\' => array( + 'version' => 'dev-main', + 'path' => array( $baseDir . '/inc' ) + ), + 'Automattic\\Jetpack\\Autoloader\\' => array( + 'version' => '2.11.20.0', + 'path' => array( $vendorDir . '/automattic/jetpack-autoloader/src' ) + ), +); diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php new file mode 100644 index 00000000..b168ddd5 --- /dev/null +++ b/vendor/composer/platform_check.php @@ -0,0 +1,26 @@ += 80002)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.2". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR + ); +} diff --git a/vendor/dealerdirect/phpcodesniffer-composer-installer/.github_changelog_generator b/vendor/dealerdirect/phpcodesniffer-composer-installer/.github_changelog_generator new file mode 100644 index 00000000..a56afb45 --- /dev/null +++ b/vendor/dealerdirect/phpcodesniffer-composer-installer/.github_changelog_generator @@ -0,0 +1,34 @@ +add-issues-wo-labels=true +add-pr-wo-labels=true +author=true +breaking-labels=backwards-incompatible,Backwards incompatible,breaking +breaking-prefix=### Breaking changes +bug-labels=bug - confirmed +bug-prefix=### Fixes +compare-link=true +date-format=%Y-%m-%d +deprecated-labels=deprecated,Deprecated,Type: Deprecated +deprecated-prefix=### Deprecates +enhancement-labels=improvement,documentation,builds / deploys / releases,feature request +enhancement-prefix=### Changes +exclude-labels=bug - unconfirmed,can't reproduce / won't fix,invalid,triage +filter-issues-by-milestone=true +header= +http-cache=true +issues=true +issue-prefix=### Closes +merge-prefix=### Pull request(s) without label +output= +project=phpcodesniffer-composer-installer +pulls=true +removed-labels=removed,Removed,Type: Removed +removed-prefix=### Removes +security-labels=security,Security,Type: Security +security-prefix=### Security +summary-labels=Release summary,release-summary,Summary,summary +unreleased=true +unreleased-label=Unreleased +unreleased-only=true +user=Dealerdirect +usernames-as-github-logins=true +verbose=false diff --git a/vendor/dealerdirect/phpcodesniffer-composer-installer/.remarkrc b/vendor/dealerdirect/phpcodesniffer-composer-installer/.remarkrc new file mode 100644 index 00000000..bfa065da --- /dev/null +++ b/vendor/dealerdirect/phpcodesniffer-composer-installer/.remarkrc @@ -0,0 +1,6 @@ +{ + "plugins": [ + "remark-preset-lint-recommended", + ["remark-lint-list-item-indent", "space"] + ] +} diff --git a/vendor/dealerdirect/phpcodesniffer-composer-installer/.yamllint b/vendor/dealerdirect/phpcodesniffer-composer-installer/.yamllint new file mode 100644 index 00000000..ef433bd4 --- /dev/null +++ b/vendor/dealerdirect/phpcodesniffer-composer-installer/.yamllint @@ -0,0 +1,8 @@ +--- +extends: default + +rules: + line-length: + level: warning + max: 120 + truthy: {allowed-values: ["true", "false", "on"]} diff --git a/vendor/dealerdirect/phpcodesniffer-composer-installer/CODE_OF_CONDUCT.md b/vendor/dealerdirect/phpcodesniffer-composer-installer/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..a408703d --- /dev/null +++ b/vendor/dealerdirect/phpcodesniffer-composer-installer/CODE_OF_CONDUCT.md @@ -0,0 +1,129 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/vendor/dealerdirect/phpcodesniffer-composer-installer/LICENSE.md b/vendor/dealerdirect/phpcodesniffer-composer-installer/LICENSE.md new file mode 100644 index 00000000..893aafb7 --- /dev/null +++ b/vendor/dealerdirect/phpcodesniffer-composer-installer/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016-2022 Dealerdirect B.V. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/dealerdirect/phpcodesniffer-composer-installer/bin/generate-changelog.sh b/vendor/dealerdirect/phpcodesniffer-composer-installer/bin/generate-changelog.sh new file mode 100644 index 00000000..42dd8d8f --- /dev/null +++ b/vendor/dealerdirect/phpcodesniffer-composer-installer/bin/generate-changelog.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +set -o errexit -o errtrace -o nounset -o pipefail + +: ${GITHUB_CHANGELOG_GENERATOR:=github_changelog_generator} +: ${GEM:=gem} + +generate_changelog() { + local -r sVersion="${1?One parameter required: }" + + if ! command -v "${GITHUB_CHANGELOG_GENERATOR}" >/dev/null 2>&1;then + echo "This script requires the '${GITHUB_CHANGELOG_GENERATOR}' Ruby Gem" + + if ! command -v "${GEM}" >/dev/null 2>&1;then + echo "Could not find the '${GEM}' command needed to install 'github_changelog_generator'!" >&2 + echo 'Aborting.' + exit 67 + else + echo "Installing '${GITHUB_CHANGELOG_GENERATOR}'..." + gem install github_changelog_generator + fi + fi + + local -r sChangelog="$( + "${GITHUB_CHANGELOG_GENERATOR}" \ + --user Dealerdirect \ + --project phpcodesniffer-composer-installer \ + --token "$(cat ~/.github-token)" \ + --future-release "${sVersion}" \ + --enhancement-label '### Changes' \ + --bugs-label '### Fixes' \ + --issues-label '### Closes' \ + --usernames-as-github-logins \ + --bug-labels 'bug - confirmed' \ + --enhancement-labels 'improvement','documentation','builds / deploys / releases','feature request' \ + --exclude-labels 'bug - unconfirmed',"can't reproduce / won't fix",'invalid','triage' \ + --unreleased-only \ + --output '' 2>/dev/null + )" || echo "There was a problem running '${GITHUB_CHANGELOG_GENERATOR}'" + + echo "${sChangelog}" | sed -E 's/\[\\(#[0-9]+)\]\([^)]+\)/\1/' | head -n -3 +} + +if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then + export -f generate_changelog +else + generate_changelog "${@}" + exit $? +fi diff --git a/vendor/dealerdirect/phpcodesniffer-composer-installer/composer.json b/vendor/dealerdirect/phpcodesniffer-composer-installer/composer.json new file mode 100644 index 00000000..e5b72c9e --- /dev/null +++ b/vendor/dealerdirect/phpcodesniffer-composer-installer/composer.json @@ -0,0 +1,57 @@ +{ + "name": "dealerdirect/phpcodesniffer-composer-installer", + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "type": "composer-plugin", + "keywords": [ + "composer", "installer", "plugin", + "phpcs", "phpcbf", "codesniffer", "phpcodesniffer", "php_codesniffer", + "standard", "standards", "style guide", "stylecheck", + "qa", "quality", "code quality", "tests" + ], + "homepage": "http://www.dealerdirect.com", + "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/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors" + } + ], + "support": { + "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, + "require": { + "php": ">=5.3", + "composer-plugin-api": "^1.0 || ^2.0", + "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "phpcompatibility/php-compatibility": "^9.0", + "php-parallel-lint/php-parallel-lint": "^1.3.1" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "autoload": { + "psr-4": { + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "extra": { + "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "scripts": { + "install-codestandards": [ + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run" + ], + "lint": [ + "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --exclude vendor --exclude .git" + ] + } +} diff --git a/vendor/dflydev/dot-access-data/CHANGELOG.md b/vendor/dflydev/dot-access-data/CHANGELOG.md new file mode 100644 index 00000000..8e92c238 --- /dev/null +++ b/vendor/dflydev/dot-access-data/CHANGELOG.md @@ -0,0 +1,67 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [3.0.2] - 2022-10-27 + +### Fixed + + - Added missing return types to docblocks (#44, #45) + +## [3.0.1] - 2021-08-13 + +### Added + + - Adds ReturnTypeWillChange to suppress PHP 8.1 warnings (#40) + +## [3.0.0] - 2021-01-01 + +### Added + - Added support for both `.` and `/`-delimited key paths (#24) + - Added parameter and return types to everything; enabled strict type checks (#18) + - Added new exception classes to better identify certain types of errors (#20) + - `Data` now implements `ArrayAccess` (#17) + - Added ability to merge non-associative array values (#31, #32) + +### Changed + - All thrown exceptions are now instances or subclasses of `DataException` (#20) + - Calling `get()` on a missing key path without providing a default will throw a `MissingPathException` instead of returning `null` (#29) + - Bumped supported PHP versions to 7.1 - 8.x (#18) + +### Fixed + - Fixed incorrect merging of array values into string values (#32) + - Fixed `get()` method behaving as if keys with `null` values didn't exist + +## [2.0.0] - 2017-12-21 + +### Changed + - Bumped supported PHP versions to 7.0 - 7.4 (#12) + - Switched to PSR-4 autoloading + +## [1.1.0] - 2017-01-20 + +### Added + - Added new `has()` method to check for the existence of the given key (#4, #7) + +## [1.0.1] - 2015-08-12 + +### Added + - Added new optional `$default` parameter to the `get()` method (#2) + +## [1.0.0] - 2012-07-17 + +**Initial release!** + +[Unreleased]: https://github.com/dflydev/dflydev-dot-access-data/compare/v3.0.2...main +[3.0.2]: https://github.com/dflydev/dflydev-dot-access-data/compare/v3.0.1...v3.0.2 +[3.0.1]: https://github.com/dflydev/dflydev-dot-access-data/compare/v3.0.0...v3.0.1 +[3.0.0]: https://github.com/dflydev/dflydev-dot-access-data/compare/v2.0.0...v3.0.0 +[2.0.0]: https://github.com/dflydev/dflydev-dot-access-data/compare/v1.1.0...v2.0.0 +[1.1.0]: https://github.com/dflydev/dflydev-dot-access-data/compare/v1.0.1...v1.1.0 +[1.0.1]: https://github.com/dflydev/dflydev-dot-access-data/compare/v1.0.0...v1.0.1 +[1.0.0]: https://github.com/dflydev/dflydev-dot-access-data/releases/tag/v1.0.0 diff --git a/vendor/dflydev/dot-access-data/LICENSE b/vendor/dflydev/dot-access-data/LICENSE new file mode 100644 index 00000000..b6880d43 --- /dev/null +++ b/vendor/dflydev/dot-access-data/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 Dragonfly Development Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/dflydev/dot-access-data/composer.json b/vendor/dflydev/dot-access-data/composer.json new file mode 100644 index 00000000..44dc5ede --- /dev/null +++ b/vendor/dflydev/dot-access-data/composer.json @@ -0,0 +1,67 @@ +{ + "name": "dflydev/dot-access-data", + "type": "library", + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": ["dot", "access", "data", "notation"], + "license": "MIT", + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Dflydev\\DotAccessData\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "scripts": { + "phpcs": "phpcs", + "phpstan": "phpstan analyse", + "phpunit": "phpunit --no-coverage", + "psalm": "psalm", + "test": [ + "@phpcs", + "@phpstan", + "@psalm", + "@phpunit" + ] + } +} diff --git a/vendor/jetpack-autoloader/class-autoloader-handler.php b/vendor/jetpack-autoloader/class-autoloader-handler.php new file mode 100644 index 00000000..d6a63de3 --- /dev/null +++ b/vendor/jetpack-autoloader/class-autoloader-handler.php @@ -0,0 +1,147 @@ +php_autoloader = $php_autoloader; + $this->hook_manager = $hook_manager; + $this->manifest_reader = $manifest_reader; + $this->version_selector = $version_selector; + } + + /** + * Checks to see whether or not an autoloader is currently in the process of initializing. + * + * @return bool + */ + public function is_initializing() { + // If no version has been set it means that no autoloader has started initializing yet. + global $jetpack_autoloader_latest_version; + if ( ! isset( $jetpack_autoloader_latest_version ) ) { + return false; + } + + // When the version is set but the classmap is not it ALWAYS means that this is the + // latest autoloader and is being included by an older one. + global $jetpack_packages_classmap; + if ( empty( $jetpack_packages_classmap ) ) { + return true; + } + + // Version 2.4.0 added a new global and altered the reset semantics. We need to check + // the other global as well since it may also point at initialization. + // Note: We don't need to check for the class first because every autoloader that + // will set the latest version global requires this class in the classmap. + $replacing_version = $jetpack_packages_classmap[ AutoloadGenerator::class ]['version']; + if ( $this->version_selector->is_dev_version( $replacing_version ) || version_compare( $replacing_version, '2.4.0.0', '>=' ) ) { + global $jetpack_autoloader_loader; + if ( ! isset( $jetpack_autoloader_loader ) ) { + return true; + } + } + + return false; + } + + /** + * Activates an autoloader using the given plugins and activates it. + * + * @param string[] $plugins The plugins to initialize the autoloader for. + */ + public function activate_autoloader( $plugins ) { + global $jetpack_packages_psr4; + $jetpack_packages_psr4 = array(); + $this->manifest_reader->read_manifests( $plugins, 'vendor/composer/jetpack_autoload_psr4.php', $jetpack_packages_psr4 ); + + global $jetpack_packages_classmap; + $jetpack_packages_classmap = array(); + $this->manifest_reader->read_manifests( $plugins, 'vendor/composer/jetpack_autoload_classmap.php', $jetpack_packages_classmap ); + + global $jetpack_packages_filemap; + $jetpack_packages_filemap = array(); + $this->manifest_reader->read_manifests( $plugins, 'vendor/composer/jetpack_autoload_filemap.php', $jetpack_packages_filemap ); + + $loader = new Version_Loader( + $this->version_selector, + $jetpack_packages_classmap, + $jetpack_packages_psr4, + $jetpack_packages_filemap + ); + + $this->php_autoloader->register_autoloader( $loader ); + + // Now that the autoloader is active we can load the filemap. + $loader->load_filemap(); + } + + /** + * Resets the active autoloader and all related global state. + */ + public function reset_autoloader() { + $this->php_autoloader->unregister_autoloader(); + $this->hook_manager->reset(); + + // Clear all of the autoloader globals so that older autoloaders don't do anything strange. + global $jetpack_autoloader_latest_version; + $jetpack_autoloader_latest_version = null; + + global $jetpack_packages_classmap; + $jetpack_packages_classmap = array(); // Must be array to avoid exceptions in old autoloaders! + + global $jetpack_packages_psr4; + $jetpack_packages_psr4 = array(); // Must be array to avoid exceptions in old autoloaders! + + global $jetpack_packages_filemap; + $jetpack_packages_filemap = array(); // Must be array to avoid exceptions in old autoloaders! + } +} diff --git a/vendor/jetpack-autoloader/class-autoloader-locator.php b/vendor/jetpack-autoloader/class-autoloader-locator.php new file mode 100644 index 00000000..d789d9c0 --- /dev/null +++ b/vendor/jetpack-autoloader/class-autoloader-locator.php @@ -0,0 +1,90 @@ +version_selector = $version_selector; + } + + /** + * Finds the path to the plugin with the latest autoloader. + * + * @param array $plugin_paths An array of plugin paths. + * @param string $latest_version The latest version reference. + * + * @return string|null + */ + public function find_latest_autoloader( $plugin_paths, &$latest_version ) { + $latest_plugin = null; + + foreach ( $plugin_paths as $plugin_path ) { + $version = $this->get_autoloader_version( $plugin_path ); + if ( ! $this->version_selector->is_version_update_required( $latest_version, $version ) ) { + continue; + } + + $latest_version = $version; + $latest_plugin = $plugin_path; + } + + return $latest_plugin; + } + + /** + * Gets the path to the autoloader. + * + * @param string $plugin_path The path to the plugin. + * + * @return string + */ + public function get_autoloader_path( $plugin_path ) { + return trailingslashit( $plugin_path ) . 'vendor/autoload_packages.php'; + } + + /** + * Gets the version for the autoloader. + * + * @param string $plugin_path The path to the plugin. + * + * @return string|null + */ + public function get_autoloader_version( $plugin_path ) { + $classmap = trailingslashit( $plugin_path ) . 'vendor/composer/jetpack_autoload_classmap.php'; + if ( ! file_exists( $classmap ) ) { + return null; + } + + $classmap = require $classmap; + if ( isset( $classmap[ AutoloadGenerator::class ] ) ) { + return $classmap[ AutoloadGenerator::class ]['version']; + } + + return null; + } +} diff --git a/vendor/jetpack-autoloader/class-autoloader.php b/vendor/jetpack-autoloader/class-autoloader.php new file mode 100644 index 00000000..191a110c --- /dev/null +++ b/vendor/jetpack-autoloader/class-autoloader.php @@ -0,0 +1,90 @@ +get( Autoloader_Handler::class ); + + // If the autoloader is already initializing it means that it has included us as the latest. + $was_included_by_autoloader = $autoloader_handler->is_initializing(); + + /** @var Plugin_Locator $plugin_locator */ + $plugin_locator = $container->get( Plugin_Locator::class ); + + /** @var Plugins_Handler $plugins_handler */ + $plugins_handler = $container->get( Plugins_Handler::class ); + + // The current plugin is the one that we are attempting to initialize here. + $current_plugin = $plugin_locator->find_current_plugin(); + + // The active plugins are those that we were able to discover on the site. This list will not + // include mu-plugins, those activated by code, or those who are hidden by filtering. We also + // want to take care to not consider the current plugin unknown if it was included by an + // autoloader. This avoids the case where a plugin will be marked "active" while deactivated + // due to it having the latest autoloader. + $active_plugins = $plugins_handler->get_active_plugins( true, ! $was_included_by_autoloader ); + + // The cached plugins are all of those that were active or discovered by the autoloader during a previous request. + // Note that it's possible this list will include plugins that have since been deactivated, but after a request + // the cache should be updated and the deactivated plugins will be removed. + $cached_plugins = $plugins_handler->get_cached_plugins(); + + // We combine the active list and cached list to preemptively load classes for plugins that are + // presently unknown but will be loaded during the request. While this may result in us considering packages in + // deactivated plugins there shouldn't be any problems as a result and the eventual consistency is sufficient. + $all_plugins = array_merge( $active_plugins, $cached_plugins ); + + // In particular we also include the current plugin to address the case where it is the latest autoloader + // but also unknown (and not cached). We don't want it in the active list because we don't know that it + // is active but we need it in the all plugins list so that it is considered by the autoloader. + $all_plugins[] = $current_plugin; + + // We require uniqueness in the array to avoid processing the same plugin more than once. + $all_plugins = array_values( array_unique( $all_plugins ) ); + + /** @var Latest_Autoloader_Guard $guard */ + $guard = $container->get( Latest_Autoloader_Guard::class ); + if ( $guard->should_stop_init( $current_plugin, $all_plugins, $was_included_by_autoloader ) ) { + return; + } + + // Initialize the autoloader using the handler now that we're ready. + $autoloader_handler->activate_autoloader( $all_plugins ); + + /** @var Hook_Manager $hook_manager */ + $hook_manager = $container->get( Hook_Manager::class ); + + // Register a shutdown handler to clean up the autoloader. + $hook_manager->add_action( 'shutdown', new Shutdown_Handler( $plugins_handler, $cached_plugins, $was_included_by_autoloader ) ); + + // phpcs:enable Generic.Commenting.DocComment.MissingShort + } +} diff --git a/vendor/jetpack-autoloader/class-container.php b/vendor/jetpack-autoloader/class-container.php new file mode 100644 index 00000000..24e8ed68 --- /dev/null +++ b/vendor/jetpack-autoloader/class-container.php @@ -0,0 +1,150 @@ + 'Hook_Manager', + ); + + /** + * A map of all the dependencies we've registered with the container and created. + * + * @var array + */ + protected $dependencies; + + /** + * The constructor. + */ + public function __construct() { + $this->dependencies = array(); + + $this->register_shared_dependencies(); + $this->register_dependencies(); + $this->initialize_globals(); + } + + /** + * Gets a dependency out of the container. + * + * @param string $class The class to fetch. + * + * @return mixed + * @throws \InvalidArgumentException When a class that isn't registered with the container is fetched. + */ + public function get( $class ) { + if ( ! isset( $this->dependencies[ $class ] ) ) { + throw new \InvalidArgumentException( "Class '$class' is not registered with the container." ); + } + + return $this->dependencies[ $class ]; + } + + /** + * Registers all of the dependencies that are shared between all instances of the autoloader. + */ + private function register_shared_dependencies() { + global $jetpack_autoloader_container_shared; + if ( ! isset( $jetpack_autoloader_container_shared ) ) { + $jetpack_autoloader_container_shared = array(); + } + + $key = self::SHARED_DEPENDENCY_KEYS[ Hook_Manager::class ]; + if ( ! isset( $jetpack_autoloader_container_shared[ $key ] ) ) { + require_once __DIR__ . '/class-hook-manager.php'; + $jetpack_autoloader_container_shared[ $key ] = new Hook_Manager(); + } + $this->dependencies[ Hook_Manager::class ] = &$jetpack_autoloader_container_shared[ $key ]; + } + + /** + * Registers all of the dependencies with the container. + */ + private function register_dependencies() { + require_once __DIR__ . '/class-path-processor.php'; + $this->dependencies[ Path_Processor::class ] = new Path_Processor(); + + require_once __DIR__ . '/class-plugin-locator.php'; + $this->dependencies[ Plugin_Locator::class ] = new Plugin_Locator( + $this->get( Path_Processor::class ) + ); + + require_once __DIR__ . '/class-version-selector.php'; + $this->dependencies[ Version_Selector::class ] = new Version_Selector(); + + require_once __DIR__ . '/class-autoloader-locator.php'; + $this->dependencies[ Autoloader_Locator::class ] = new Autoloader_Locator( + $this->get( Version_Selector::class ) + ); + + require_once __DIR__ . '/class-php-autoloader.php'; + $this->dependencies[ PHP_Autoloader::class ] = new PHP_Autoloader(); + + require_once __DIR__ . '/class-manifest-reader.php'; + $this->dependencies[ Manifest_Reader::class ] = new Manifest_Reader( + $this->get( Version_Selector::class ) + ); + + require_once __DIR__ . '/class-plugins-handler.php'; + $this->dependencies[ Plugins_Handler::class ] = new Plugins_Handler( + $this->get( Plugin_Locator::class ), + $this->get( Path_Processor::class ) + ); + + require_once __DIR__ . '/class-autoloader-handler.php'; + $this->dependencies[ Autoloader_Handler::class ] = new Autoloader_Handler( + $this->get( PHP_Autoloader::class ), + $this->get( Hook_Manager::class ), + $this->get( Manifest_Reader::class ), + $this->get( Version_Selector::class ) + ); + + require_once __DIR__ . '/class-latest-autoloader-guard.php'; + $this->dependencies[ Latest_Autoloader_Guard::class ] = new Latest_Autoloader_Guard( + $this->get( Plugins_Handler::class ), + $this->get( Autoloader_Handler::class ), + $this->get( Autoloader_Locator::class ) + ); + + // Register any classes that we will use elsewhere. + require_once __DIR__ . '/class-version-loader.php'; + require_once __DIR__ . '/class-shutdown-handler.php'; + } + + /** + * Initializes any of the globals needed by the autoloader. + */ + private function initialize_globals() { + /* + * This global was retired in version 2.9. The value is set to 'false' to maintain + * compatibility with older versions of the autoloader. + */ + global $jetpack_autoloader_including_latest; + $jetpack_autoloader_including_latest = false; + + // Not all plugins can be found using the locator. In cases where a plugin loads the autoloader + // but was not discoverable, we will record them in this array to track them as "active". + global $jetpack_autoloader_activating_plugins_paths; + if ( ! isset( $jetpack_autoloader_activating_plugins_paths ) ) { + $jetpack_autoloader_activating_plugins_paths = array(); + } + } +} diff --git a/vendor/jetpack-autoloader/class-hook-manager.php b/vendor/jetpack-autoloader/class-hook-manager.php new file mode 100644 index 00000000..2dd81541 --- /dev/null +++ b/vendor/jetpack-autoloader/class-hook-manager.php @@ -0,0 +1,76 @@ +registered_hooks = array(); + } + + /** + * Adds an action to WordPress and registers it internally. + * + * @param string $tag The name of the action which is hooked. + * @param callable $callable The function to call. + * @param int $priority Used to specify the priority of the action. + * @param int $accepted_args Used to specify the number of arguments the callable accepts. + */ + public function add_action( $tag, $callable, $priority = 10, $accepted_args = 1 ) { + $this->registered_hooks[ $tag ][] = array( + 'priority' => $priority, + 'callable' => $callable, + ); + + add_action( $tag, $callable, $priority, $accepted_args ); + } + + /** + * Adds a filter to WordPress and registers it internally. + * + * @param string $tag The name of the filter which is hooked. + * @param callable $callable The function to call. + * @param int $priority Used to specify the priority of the filter. + * @param int $accepted_args Used to specify the number of arguments the callable accepts. + */ + public function add_filter( $tag, $callable, $priority = 10, $accepted_args = 1 ) { + $this->registered_hooks[ $tag ][] = array( + 'priority' => $priority, + 'callable' => $callable, + ); + + add_filter( $tag, $callable, $priority, $accepted_args ); + } + + /** + * Removes all of the registered hooks. + */ + public function reset() { + foreach ( $this->registered_hooks as $tag => $hooks ) { + foreach ( $hooks as $hook ) { + remove_filter( $tag, $hook['callable'], $hook['priority'] ); + } + } + $this->registered_hooks = array(); + } +} diff --git a/vendor/jetpack-autoloader/class-latest-autoloader-guard.php b/vendor/jetpack-autoloader/class-latest-autoloader-guard.php new file mode 100644 index 00000000..aba1bc9d --- /dev/null +++ b/vendor/jetpack-autoloader/class-latest-autoloader-guard.php @@ -0,0 +1,86 @@ +plugins_handler = $plugins_handler; + $this->autoloader_handler = $autoloader_handler; + $this->autoloader_locator = $autoloader_locator; + } + + /** + * Indicates whether or not the autoloader should be initialized. Note that this function + * has the side-effect of actually loading the latest autoloader in the event that this + * is not it. + * + * @param string $current_plugin The current plugin we're checking. + * @param string[] $plugins The active plugins to check for autoloaders in. + * @param bool $was_included_by_autoloader Indicates whether or not this autoloader was included by another. + * + * @return bool True if we should stop initialization, otherwise false. + */ + public function should_stop_init( $current_plugin, $plugins, $was_included_by_autoloader ) { + global $jetpack_autoloader_latest_version; + + // We need to reset the autoloader when the plugins change because + // that means the autoloader was generated with a different list. + if ( $this->plugins_handler->have_plugins_changed( $plugins ) ) { + $this->autoloader_handler->reset_autoloader(); + } + + // When the latest autoloader has already been found we don't need to search for it again. + // We should take care however because this will also trigger if the autoloader has been + // included by an older one. + if ( isset( $jetpack_autoloader_latest_version ) && ! $was_included_by_autoloader ) { + return true; + } + + $latest_plugin = $this->autoloader_locator->find_latest_autoloader( $plugins, $jetpack_autoloader_latest_version ); + if ( isset( $latest_plugin ) && $latest_plugin !== $current_plugin ) { + require $this->autoloader_locator->get_autoloader_path( $latest_plugin ); + return true; + } + + return false; + } +} diff --git a/vendor/jetpack-autoloader/class-manifest-reader.php b/vendor/jetpack-autoloader/class-manifest-reader.php new file mode 100644 index 00000000..26eacc91 --- /dev/null +++ b/vendor/jetpack-autoloader/class-manifest-reader.php @@ -0,0 +1,99 @@ +version_selector = $version_selector; + } + + /** + * Reads all of the manifests in the given plugin paths. + * + * @param array $plugin_paths The paths to the plugins we're loading the manifest in. + * @param string $manifest_path The path that we're loading the manifest from in each plugin. + * @param array $path_map The path map to add the contents of the manifests to. + * + * @return array $path_map The path map we've built using the manifests in each plugin. + */ + public function read_manifests( $plugin_paths, $manifest_path, &$path_map ) { + $file_paths = array_map( + function ( $path ) use ( $manifest_path ) { + return trailingslashit( $path ) . $manifest_path; + }, + $plugin_paths + ); + + foreach ( $file_paths as $path ) { + $this->register_manifest( $path, $path_map ); + } + + return $path_map; + } + + /** + * Registers a plugin's manifest file with the path map. + * + * @param string $manifest_path The absolute path to the manifest that we're loading. + * @param array $path_map The path map to add the contents of the manifest to. + */ + protected function register_manifest( $manifest_path, &$path_map ) { + if ( ! is_readable( $manifest_path ) ) { + return; + } + + $manifest = require $manifest_path; + if ( ! is_array( $manifest ) ) { + return; + } + + foreach ( $manifest as $key => $data ) { + $this->register_record( $key, $data, $path_map ); + } + } + + /** + * Registers an entry from the manifest in the path map. + * + * @param string $key The identifier for the entry we're registering. + * @param array $data The data for the entry we're registering. + * @param array $path_map The path map to add the contents of the manifest to. + */ + protected function register_record( $key, $data, &$path_map ) { + if ( isset( $path_map[ $key ]['version'] ) ) { + $selected_version = $path_map[ $key ]['version']; + } else { + $selected_version = null; + } + + if ( $this->version_selector->is_version_update_required( $selected_version, $data['version'] ) ) { + $path_map[ $key ] = array( + 'version' => $data['version'], + 'path' => $data['path'], + ); + } + } +} diff --git a/vendor/jetpack-autoloader/class-path-processor.php b/vendor/jetpack-autoloader/class-path-processor.php new file mode 100644 index 00000000..a97a7e5c --- /dev/null +++ b/vendor/jetpack-autoloader/class-path-processor.php @@ -0,0 +1,194 @@ +get_normalized_constants(); + foreach ( $constants as $constant => $constant_path ) { + $len = strlen( $constant_path ); + if ( substr( $path, 0, $len ) !== $constant_path ) { + continue; + } + + return substr_replace( $path, '{{' . $constant . '}}', 0, $len ); + } + + return $path; + } + + /** + * Given a path this will replace any of the path constant tokens with the expanded path. + * + * @param string $tokenized_path The path we want to process. + * + * @return string The expanded path. + */ + public function untokenize_path_constants( $tokenized_path ) { + $tokenized_path = wp_normalize_path( $tokenized_path ); + + $constants = $this->get_normalized_constants(); + foreach ( $constants as $constant => $constant_path ) { + $constant = '{{' . $constant . '}}'; + + $len = strlen( $constant ); + if ( substr( $tokenized_path, 0, $len ) !== $constant ) { + continue; + } + + return $this->get_real_path( substr_replace( $tokenized_path, $constant_path, 0, $len ) ); + } + + return $tokenized_path; + } + + /** + * Given a file and an array of places it might be, this will find the absolute path and return it. + * + * @param string $file The plugin or theme file to resolve. + * @param array $directories_to_check The directories we should check for the file if it isn't an absolute path. + * + * @return string|false Returns the absolute path to the directory, otherwise false. + */ + public function find_directory_with_autoloader( $file, $directories_to_check ) { + $file = wp_normalize_path( $file ); + + if ( ! $this->is_absolute_path( $file ) ) { + $file = $this->find_absolute_plugin_path( $file, $directories_to_check ); + if ( ! isset( $file ) ) { + return false; + } + } + + // We need the real path for consistency with __DIR__ paths. + $file = $this->get_real_path( $file ); + + // phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged + $directory = @is_file( $file ) ? dirname( $file ) : $file; + if ( ! @is_file( $directory . '/vendor/composer/jetpack_autoload_classmap.php' ) ) { + return false; + } + // phpcs:enable WordPress.PHP.NoSilencedErrors.Discouraged + + return $directory; + } + + /** + * Fetches an array of normalized paths keyed by the constant they came from. + * + * @return string[] The normalized paths keyed by the constant. + */ + private function get_normalized_constants() { + $raw_constants = array( + // Order the constants from most-specific to least-specific. + 'WP_PLUGIN_DIR', + 'WPMU_PLUGIN_DIR', + 'WP_CONTENT_DIR', + 'ABSPATH', + ); + + $constants = array(); + foreach ( $raw_constants as $raw ) { + if ( ! defined( $raw ) ) { + continue; + } + + $path = wp_normalize_path( constant( $raw ) ); + if ( isset( $path ) ) { + $constants[ $raw ] = $path; + } + } + + return $constants; + } + + /** + * Indicates whether or not a path is absolute. + * + * @param string $path The path to check. + * + * @return bool True if the path is absolute, otherwise false. + */ + private function is_absolute_path( $path ) { + if ( 0 === strlen( $path ) || '.' === $path[0] ) { + return false; + } + + // Absolute paths on Windows may begin with a drive letter. + if ( preg_match( '/^[a-zA-Z]:[\/\\\\]/', $path ) ) { + return true; + } + + // A path starting with / or \ is absolute; anything else is relative. + return ( '/' === $path[0] || '\\' === $path[0] ); + } + + /** + * Given a file and a list of directories to check, this method will try to figure out + * the absolute path to the file in question. + * + * @param string $normalized_path The normalized path to the plugin or theme file to resolve. + * @param array $directories_to_check The directories we should check for the file if it isn't an absolute path. + * + * @return string|null The absolute path to the plugin file, otherwise null. + */ + private function find_absolute_plugin_path( $normalized_path, $directories_to_check ) { + // We're only able to find the absolute path for plugin/theme PHP files. + if ( ! is_string( $normalized_path ) || '.php' !== substr( $normalized_path, -4 ) ) { + return null; + } + + foreach ( $directories_to_check as $directory ) { + $normalized_check = wp_normalize_path( trailingslashit( $directory ) ) . $normalized_path; + // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged + if ( @is_file( $normalized_check ) ) { + return $normalized_check; + } + } + + return null; + } + + /** + * Given a path this will figure out the real path that we should be using. + * + * @param string $path The path to resolve. + * + * @return string The resolved path. + */ + private function get_real_path( $path ) { + // We want to resolve symbolic links for consistency with __DIR__ paths. + // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged + $real_path = @realpath( $path ); + if ( false === $real_path ) { + // Let the autoloader deal with paths that don't exist. + $real_path = $path; + } + + // Using realpath will make it platform-specific so we must normalize it after. + if ( $path !== $real_path ) { + $real_path = wp_normalize_path( $real_path ); + } + + return $real_path; + } +} diff --git a/vendor/jetpack-autoloader/class-php-autoloader.php b/vendor/jetpack-autoloader/class-php-autoloader.php new file mode 100644 index 00000000..ff578717 --- /dev/null +++ b/vendor/jetpack-autoloader/class-php-autoloader.php @@ -0,0 +1,93 @@ +unregister_autoloader(); + + // Set the global so that it can be used to load classes. + global $jetpack_autoloader_loader; + $jetpack_autoloader_loader = $version_loader; + + // Ensure that the autoloader is first to avoid contention with others. + spl_autoload_register( array( self::class, 'load_class' ), true, true ); + } + + /** + * Unregisters the active autoloader so that it will no longer autoload classes. + */ + public function unregister_autoloader() { + // Remove any v2 autoloader that we've already registered. + $autoload_chain = spl_autoload_functions(); + if ( ! $autoload_chain ) { + return; + } + foreach ( $autoload_chain as $autoloader ) { + // We can identify a v2 autoloader using the namespace. + $namespace_check = null; + + // Functions are recorded as strings. + if ( is_string( $autoloader ) ) { + $namespace_check = $autoloader; + } elseif ( is_array( $autoloader ) && is_string( $autoloader[0] ) ) { + // Static method calls have the class as the first array element. + $namespace_check = $autoloader[0]; + } else { + // Since the autoloader has only ever been a function or a static method we don't currently need to check anything else. + continue; + } + + // Check for the namespace without the generated suffix. + if ( 'Automattic\\Jetpack\\Autoloader\\jp' === substr( $namespace_check, 0, 32 ) ) { + spl_autoload_unregister( $autoloader ); + } + } + + // Clear the global now that the autoloader has been unregistered. + global $jetpack_autoloader_loader; + $jetpack_autoloader_loader = null; + } + + /** + * Loads a class file if one could be found. + * + * Note: This function is static so that the autoloader can be easily unregistered. If + * it was a class method we would have to unwrap the object to check the namespace. + * + * @param string $class_name The name of the class to autoload. + * + * @return bool Indicates whether or not a class file was loaded. + */ + public static function load_class( $class_name ) { + global $jetpack_autoloader_loader; + if ( ! isset( $jetpack_autoloader_loader ) ) { + return; + } + + $file = $jetpack_autoloader_loader->find_class_file( $class_name ); + if ( ! isset( $file ) ) { + return false; + } + + require $file; + return true; + } +} diff --git a/vendor/jetpack-autoloader/class-plugin-locator.php b/vendor/jetpack-autoloader/class-plugin-locator.php new file mode 100644 index 00000000..e8498e36 --- /dev/null +++ b/vendor/jetpack-autoloader/class-plugin-locator.php @@ -0,0 +1,156 @@ +path_processor = $path_processor; + } + + /** + * Finds the path to the current plugin. + * + * @return string $path The path to the current plugin. + * + * @throws \RuntimeException If the current plugin does not have an autoloader. + */ + public function find_current_plugin() { + // Escape from `vendor/__DIR__` to root plugin directory. + $plugin_directory = dirname( dirname( __DIR__ ) ); + + // Use the path processor to ensure that this is an autoloader we're referencing. + $path = $this->path_processor->find_directory_with_autoloader( $plugin_directory, array() ); + if ( false === $path ) { + throw new \RuntimeException( 'Failed to locate plugin ' . $plugin_directory ); + } + + return $path; + } + + /** + * Checks a given option for plugin paths. + * + * @param string $option_name The option that we want to check for plugin information. + * @param bool $site_option Indicates whether or not we want to check the site option. + * + * @return array $plugin_paths The list of absolute paths we've found. + */ + public function find_using_option( $option_name, $site_option = false ) { + $raw = $site_option ? get_site_option( $option_name ) : get_option( $option_name ); + if ( false === $raw ) { + return array(); + } + + return $this->convert_plugins_to_paths( $raw ); + } + + /** + * Checks for plugins in the `action` request parameter. + * + * @param string[] $allowed_actions The actions that we're allowed to return plugins for. + * + * @return array $plugin_paths The list of absolute paths we've found. + */ + public function find_using_request_action( $allowed_actions ) { + // phpcs:disable WordPress.Security.NonceVerification.Recommended + + /** + * Note: we're not actually checking the nonce here because it's too early + * in the execution. The pluggable functions are not yet loaded to give + * plugins a chance to plug their versions. Therefore we're doing the bare + * minimum: checking whether the nonce exists and it's in the right place. + * The request will fail later if the nonce doesn't pass the check. + */ + if ( empty( $_REQUEST['_wpnonce'] ) ) { + return array(); + } + + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated just below. + $action = isset( $_REQUEST['action'] ) ? wp_unslash( $_REQUEST['action'] ) : false; + if ( ! in_array( $action, $allowed_actions, true ) ) { + return array(); + } + + $plugin_slugs = array(); + switch ( $action ) { + case 'activate': + case 'deactivate': + if ( empty( $_REQUEST['plugin'] ) ) { + break; + } + + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated by convert_plugins_to_paths. + $plugin_slugs[] = wp_unslash( $_REQUEST['plugin'] ); + break; + + case 'activate-selected': + case 'deactivate-selected': + if ( empty( $_REQUEST['checked'] ) ) { + break; + } + + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated by convert_plugins_to_paths. + $plugin_slugs = wp_unslash( $_REQUEST['checked'] ); + break; + } + + // phpcs:enable WordPress.Security.NonceVerification.Recommended + return $this->convert_plugins_to_paths( $plugin_slugs ); + } + + /** + * Given an array of plugin slugs or paths, this will convert them to absolute paths and filter + * out the plugins that are not directory plugins. Note that array keys will also be included + * if they are plugin paths! + * + * @param string[] $plugins Plugin paths or slugs to filter. + * + * @return string[] + */ + private function convert_plugins_to_paths( $plugins ) { + if ( ! is_array( $plugins ) || empty( $plugins ) ) { + return array(); + } + + // We're going to look for plugins in the standard directories. + $path_constants = array( WP_PLUGIN_DIR, WPMU_PLUGIN_DIR ); + + $plugin_paths = array(); + foreach ( $plugins as $key => $value ) { + $path = $this->path_processor->find_directory_with_autoloader( $key, $path_constants ); + if ( $path ) { + $plugin_paths[] = $path; + } + + $path = $this->path_processor->find_directory_with_autoloader( $value, $path_constants ); + if ( $path ) { + $plugin_paths[] = $path; + } + } + + return $plugin_paths; + } +} diff --git a/vendor/jetpack-autoloader/class-plugins-handler.php b/vendor/jetpack-autoloader/class-plugins-handler.php new file mode 100644 index 00000000..90bbfb6c --- /dev/null +++ b/vendor/jetpack-autoloader/class-plugins-handler.php @@ -0,0 +1,164 @@ +plugin_locator = $plugin_locator; + $this->path_processor = $path_processor; + } + + /** + * Gets all of the active plugins we can find. + * + * @param bool $include_deactivating When true, plugins deactivating this request will be considered active. + * @param bool $record_unknown When true, the current plugin will be marked as active and recorded when unknown. + * + * @return string[] + */ + public function get_active_plugins( $include_deactivating, $record_unknown ) { + global $jetpack_autoloader_activating_plugins_paths; + + // We're going to build a unique list of plugins from a few different sources + // to find all of our "active" plugins. While we need to return an integer + // array, we're going to use an associative array internally to reduce + // the amount of time that we're going to spend checking uniqueness + // and merging different arrays together to form the output. + $active_plugins = array(); + + // Make sure that plugins which have activated this request are considered as "active" even though + // they probably won't be present in any option. + if ( is_array( $jetpack_autoloader_activating_plugins_paths ) ) { + foreach ( $jetpack_autoloader_activating_plugins_paths as $path ) { + $active_plugins[ $path ] = $path; + } + } + + // This option contains all of the plugins that have been activated. + $plugins = $this->plugin_locator->find_using_option( 'active_plugins' ); + foreach ( $plugins as $path ) { + $active_plugins[ $path ] = $path; + } + + // This option contains all of the multisite plugins that have been activated. + if ( is_multisite() ) { + $plugins = $this->plugin_locator->find_using_option( 'active_sitewide_plugins', true ); + foreach ( $plugins as $path ) { + $active_plugins[ $path ] = $path; + } + } + + // These actions contain plugins that are being activated/deactivated during this request. + $plugins = $this->plugin_locator->find_using_request_action( array( 'activate', 'activate-selected', 'deactivate', 'deactivate-selected' ) ); + foreach ( $plugins as $path ) { + $active_plugins[ $path ] = $path; + } + + // When the current plugin isn't considered "active" there's a problem. + // Since we're here, the plugin is active and currently being loaded. + // We can support this case (mu-plugins and non-standard activation) + // by adding the current plugin to the active list and marking it + // as an unknown (activating) plugin. This also has the benefit + // of causing a reset because the active plugins list has + // been changed since it was saved in the global. + $current_plugin = $this->plugin_locator->find_current_plugin(); + if ( $record_unknown && ! in_array( $current_plugin, $active_plugins, true ) ) { + $active_plugins[ $current_plugin ] = $current_plugin; + $jetpack_autoloader_activating_plugins_paths[] = $current_plugin; + } + + // When deactivating plugins aren't desired we should entirely remove them from the active list. + if ( ! $include_deactivating ) { + // These actions contain plugins that are being deactivated during this request. + $plugins = $this->plugin_locator->find_using_request_action( array( 'deactivate', 'deactivate-selected' ) ); + foreach ( $plugins as $path ) { + unset( $active_plugins[ $path ] ); + } + } + + // Transform the array so that we don't have to worry about the keys interacting with other array types later. + return array_values( $active_plugins ); + } + + /** + * Gets all of the cached plugins if there are any. + * + * @return string[] + */ + public function get_cached_plugins() { + $cached = get_transient( self::TRANSIENT_KEY ); + if ( ! is_array( $cached ) || empty( $cached ) ) { + return array(); + } + + // We need to expand the tokens to an absolute path for this webserver. + return array_map( array( $this->path_processor, 'untokenize_path_constants' ), $cached ); + } + + /** + * Saves the plugin list to the cache. + * + * @param array $plugins The plugin list to save to the cache. + */ + public function cache_plugins( $plugins ) { + // We store the paths in a tokenized form so that that webservers with different absolute paths don't break. + $plugins = array_map( array( $this->path_processor, 'tokenize_path_constants' ), $plugins ); + + set_transient( self::TRANSIENT_KEY, $plugins ); + } + + /** + * Checks to see whether or not the plugin list given has changed when compared to the + * shared `$jetpack_autoloader_cached_plugin_paths` global. This allows us to deal + * with cases where the active list may change due to filtering.. + * + * @param string[] $plugins The plugins list to check against the global cache. + * + * @return bool True if the plugins have changed, otherwise false. + */ + public function have_plugins_changed( $plugins ) { + global $jetpack_autoloader_cached_plugin_paths; + + if ( $jetpack_autoloader_cached_plugin_paths !== $plugins ) { + $jetpack_autoloader_cached_plugin_paths = $plugins; + return true; + } + + return false; + } +} diff --git a/vendor/jetpack-autoloader/class-shutdown-handler.php b/vendor/jetpack-autoloader/class-shutdown-handler.php new file mode 100644 index 00000000..068f55b7 --- /dev/null +++ b/vendor/jetpack-autoloader/class-shutdown-handler.php @@ -0,0 +1,92 @@ +plugins_handler = $plugins_handler; + $this->cached_plugins = $cached_plugins; + $this->was_included_by_autoloader = $was_included_by_autoloader; + } + + /** + * Handles the shutdown of the autoloader. + */ + public function __invoke() { + // Don't save a broken cache if an error happens during some plugin's initialization. + if ( ! did_action( 'plugins_loaded' ) ) { + // Ensure that the cache is emptied to prevent consecutive failures if the cache is to blame. + if ( ! empty( $this->cached_plugins ) ) { + $this->plugins_handler->cache_plugins( array() ); + } + + return; + } + + // Load the active plugins fresh since the list we pulled earlier might not contain + // plugins that were activated but did not reset the autoloader. This happens + // when a plugin is in the cache but not "active" when the autoloader loads. + // We also want to make sure that plugins which are deactivating are not + // considered "active" so that they will be removed from the cache now. + try { + $active_plugins = $this->plugins_handler->get_active_plugins( false, ! $this->was_included_by_autoloader ); + } catch ( \Exception $ex ) { + // When the package is deleted before shutdown it will throw an exception. + // In the event this happens we should erase the cache. + if ( ! empty( $this->cached_plugins ) ) { + $this->plugins_handler->cache_plugins( array() ); + } + return; + } + + // The paths should be sorted for easy comparisons with those loaded from the cache. + // Note we don't need to sort the cached entries because they're already sorted. + sort( $active_plugins ); + + // We don't want to waste time saving a cache that hasn't changed. + if ( $this->cached_plugins === $active_plugins ) { + return; + } + + $this->plugins_handler->cache_plugins( $active_plugins ); + } +} diff --git a/vendor/jetpack-autoloader/class-version-loader.php b/vendor/jetpack-autoloader/class-version-loader.php new file mode 100644 index 00000000..1c67af04 --- /dev/null +++ b/vendor/jetpack-autoloader/class-version-loader.php @@ -0,0 +1,164 @@ +version_selector = $version_selector; + $this->classmap = $classmap; + $this->psr4_map = $psr4_map; + $this->filemap = $filemap; + } + + /** + * Finds the file path for the given class. + * + * @param string $class_name The class to find. + * + * @return string|null $file_path The path to the file if found, null if no class was found. + */ + public function find_class_file( $class_name ) { + $data = $this->select_newest_file( + isset( $this->classmap[ $class_name ] ) ? $this->classmap[ $class_name ] : null, + $this->find_psr4_file( $class_name ) + ); + if ( ! isset( $data ) ) { + return null; + } + + return $data['path']; + } + + /** + * Load all of the files in the filemap. + */ + public function load_filemap() { + if ( empty( $this->filemap ) ) { + return; + } + + foreach ( $this->filemap as $file_identifier => $file_data ) { + if ( empty( $GLOBALS['__composer_autoload_files'][ $file_identifier ] ) ) { + require_once $file_data['path']; + + $GLOBALS['__composer_autoload_files'][ $file_identifier ] = true; + } + } + } + + /** + * Compares different class sources and returns the newest. + * + * @param array|null $classmap_data The classmap class data. + * @param array|null $psr4_data The PSR-4 class data. + * + * @return array|null $data + */ + private function select_newest_file( $classmap_data, $psr4_data ) { + if ( ! isset( $classmap_data ) ) { + return $psr4_data; + } elseif ( ! isset( $psr4_data ) ) { + return $classmap_data; + } + + if ( $this->version_selector->is_version_update_required( $classmap_data['version'], $psr4_data['version'] ) ) { + return $psr4_data; + } + + return $classmap_data; + } + + /** + * Finds the file for a given class in a PSR-4 namespace. + * + * @param string $class_name The class to find. + * + * @return array|null $data The version and path path to the file if found, null otherwise. + */ + private function find_psr4_file( $class_name ) { + if ( ! isset( $this->psr4_map ) ) { + return null; + } + + // Don't bother with classes that have no namespace. + $class_index = strrpos( $class_name, '\\' ); + if ( ! $class_index ) { + return null; + } + $class_for_path = str_replace( '\\', '/', $class_name ); + + // Search for the namespace by iteratively cutting off the last segment until + // we find a match. This allows us to check the most-specific namespaces + // first as well as minimize the amount of time spent looking. + for ( + $class_namespace = substr( $class_name, 0, $class_index ); + ! empty( $class_namespace ); + $class_namespace = substr( $class_namespace, 0, strrpos( $class_namespace, '\\' ) ) + ) { + $namespace = $class_namespace . '\\'; + if ( ! isset( $this->psr4_map[ $namespace ] ) ) { + continue; + } + $data = $this->psr4_map[ $namespace ]; + + foreach ( $data['path'] as $path ) { + $path .= '/' . substr( $class_for_path, strlen( $namespace ) ) . '.php'; + if ( file_exists( $path ) ) { + return array( + 'version' => $data['version'], + 'path' => $path, + ); + } + } + } + + return null; + } +} diff --git a/vendor/jetpack-autoloader/class-version-selector.php b/vendor/jetpack-autoloader/class-version-selector.php new file mode 100644 index 00000000..4a6a9ca5 --- /dev/null +++ b/vendor/jetpack-autoloader/class-version-selector.php @@ -0,0 +1,69 @@ +is_dev_version( $selected_version ) ) { + return false; + } + + if ( $this->is_dev_version( $compare_version ) ) { + if ( $use_dev_versions ) { + return true; + } else { + return false; + } + } + + if ( version_compare( $selected_version, $compare_version, '<' ) ) { + return true; + } + + return false; + } + + /** + * Checks whether the given package version is a development version. + * + * @param String $version The package version. + * + * @return bool True if the version is a dev version, else false. + */ + public function is_dev_version( $version ) { + if ( 'dev-' === substr( $version, 0, 4 ) || '9999999-dev' === $version ) { + return true; + } + + return false; + } +} diff --git a/vendor/league/commonmark/.phpstorm.meta.php b/vendor/league/commonmark/.phpstorm.meta.php new file mode 100644 index 00000000..ca1bec7e --- /dev/null +++ b/vendor/league/commonmark/.phpstorm.meta.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PHPSTORM_META +{ + expectedArguments(\League\CommonMark\Util\HtmlElement::__construct(), 0, 'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kdb', 'keygen', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'menuitem', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr'); + + expectedArguments(\League\CommonMark\Extension\CommonMark\Node\Block\Heading::__construct(), 0, 1, 2, 3, 4, 5, 6); + expectedReturnValues(\League\CommonMark\Extension\CommonMark\Node\Block\Heading::getLevel(), 1, 2, 3, 4, 5, 6); + + registerArgumentsSet('league_commonmark_htmlblock_types', \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_1_CODE_CONTAINER, \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_2_COMMENT, \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_3, \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_4, \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_5_CDATA, \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_6_BLOCK_ELEMENT, \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_7_MISC_ELEMENT); + expectedArguments(\League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::__construct(), 0, argumentsSet('league_commonmark_htmlblock_types')); + expectedArguments(\League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::setType(), 0, argumentsSet('league_commonmark_htmlblock_types')); + expectedReturnValues(\League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::getType(), argumentsSet('league_commonmark_htmlblock_types')); + expectedArguments(\League\CommonMark\Util\RegexHelper::getHtmlBlockOpenRegex(), 0, argumentsSet('league_commonmark_htmlblock_types')); + expectedArguments(\League\CommonMark\Util\RegexHelper::getHtmlBlockCloseRegex(), 0, argumentsSet('league_commonmark_htmlblock_types')); + + registerArgumentsSet('league_commonmark_newline_types', \League\CommonMark\Node\Inline\Newline::HARDBREAK, \League\CommonMark\Node\Inline\Newline::SOFTBREAK); + expectedArguments(\League\CommonMark\Node\Inline\Newline::__construct(), 0, argumentsSet('league_commonmark_newline_types')); + expectedReturnValues(\League\CommonMark\Node\Inline\Newline::getType(), argumentsSet('league_commonmark_newline_types')); + + registerArgumentsSet('league_commonmark_options', + 'html_input', + 'allow_unsafe_links', + 'max_nesting_level', + 'renderer', + 'renderer/block_separator', + 'renderer/inner_separator', + 'renderer/soft_break', + 'commonmark', + 'commonmark/enable_em', + 'commonmark/enable_strong', + 'commonmark/use_asterisk', + 'commonmark/use_underscore', + 'commonmark/unordered_list_markers', + 'disallowed_raw_html', + 'disallowed_raw_html/disallowed_tags', + 'external_link', + 'external_link/html_class', + 'external_link/internal_hosts', + 'external_link/nofollow', + 'external_link/noopener', + 'external_link/noreferrer', + 'external_link/open_in_new_window', + 'footnote', + 'footnote/backref_class', + 'footnote/backref_symbol', + 'footnote/container_add_hr', + 'footnote/container_class', + 'footnote/ref_class', + 'footnote/ref_id_prefix', + 'footnote/footnote_class', + 'footnote/footnote_id_prefix', + 'heading_permalink', + 'heading_permalink/apply_id_to_heading', + 'heading_permalink/heading_class', + 'heading_permalink/html_class', + 'heading_permalink/fragment_prefix', + 'heading_permalink/id_prefix', + 'heading_permalink/inner_contents', + 'heading_permalink/insert', + 'heading_permalink/max_heading_level', + 'heading_permalink/min_heading_level', + 'heading_permalink/symbol', + 'heading_permalink/title', + 'mentions', + 'smartpunct/double_quote_closer', + 'smartpunct/double_quote_opener', + 'smartpunct/single_quote_closer', + 'smartpunct/single_quote_opener', + 'slug_normalizer', + 'slug_normalizer/instance', + 'slug_normalizer/max_length', + 'slug_normalizer/unique', + 'table', + 'table/wrap', + 'table/wrap/attributes', + 'table/wrap/enabled', + 'table/wrap/tag', + 'table/alignment_attributes', + 'table/alignment_attributes/left', + 'table/alignment_attributes/center', + 'table/alignment_attributes/right', + 'table_of_contents', + 'table_of_contents/html_class', + 'table_of_contents/max_heading_level', + 'table_of_contents/min_heading_level', + 'table_of_contents/normalize', + 'table_of_contents/placeholder', + 'table_of_contents/position', + 'table_of_contents/style', + ); + expectedArguments(\League\Config\ConfigurationInterface::get(), 0, argumentsSet('league_commonmark_options')); + expectedArguments(\League\Config\ConfigurationInterface::exists(), 0, argumentsSet('league_commonmark_options')); + expectedArguments(\League\Config\MutableConfigurationInterface::set(), 0, argumentsSet('league_commonmark_options')); +} diff --git a/vendor/league/commonmark/CHANGELOG.md b/vendor/league/commonmark/CHANGELOG.md new file mode 100644 index 00000000..9c43dbf0 --- /dev/null +++ b/vendor/league/commonmark/CHANGELOG.md @@ -0,0 +1,588 @@ +# Change Log +All notable changes to this project will be documented in this file. +Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) principles. + +**Upgrading from 1.x?** See for additional information. + +## [Unreleased][unreleased] + +## [2.4.0] - 2023-03-24 + +### Added + +- Added generic `CommonMarkException` marker interface for all exceptions thrown by the library +- Added several new specific exception types implementing that marker interface: + - `AlreadyInitializedException` + - `InvalidArgumentException` + - `IOException` + - `LogicException` + - `MissingDependencyException` + - `NoMatchingRendererException` + - `ParserLogicException` +- Added more configuration options to the Heading Permalinks extension (#939): + - `heading_permalink/apply_id_to_heading` - When `true`, the `id` attribute will be applied to the heading element itself instead of the `` tag + - `heading_permalink/heading_class` - class to apply to the heading element + - `heading_permalink/insert` - now accepts `none` to prevent the creation of the `` link +- Added new `table/alignment_attributes` configuration option to control how table cell alignment is rendered (#959) + +### Changed + +- Change several thrown exceptions from `RuntimeException` to `LogicException` (or something extending it), including: + - `CallbackGenerator`s that fail to set a URL or return an expected value + - `MarkdownParser` when deactivating the last block parser or attempting to get an active block parser when they've all been closed + - Adding items to an already-initialized `Environment` + - Rendering a `Node` when no renderer has been registered for it +- `HeadingPermalinkProcessor` now throws `InvalidConfigurationException` instead of `RuntimeException` when invalid config values are given. +- `HtmlElement::setAttribute()` no longer requires the second parameter for boolean attributes +- Several small micro-optimizations +- Changed Strikethrough to only allow 1 or 2 tildes per the updated GFM spec + +### Fixed + +- Fixed inaccurate `@throws` docblocks throughout the codebase, including `ConverterInterface`, `MarkdownConverter`, and `MarkdownConverterInterface`. + - These previously suggested that only `\RuntimeException`s were thrown, which was inaccurate as `\LogicException`s were also possible. + +## [2.3.9] - 2023-02-15 + +### Fixed + +- Fixed autolink extension not detecting some URIs with underscores (#956) + +## [2.3.8] - 2022-12-10 + +### Fixed + +- Fixed parsing issues when `mb_internal_encoding()` is set to something other than `UTF-8` (#951) + +## [2.3.7] - 2022-11-03 + +### Fixed + +- Fixed `TaskListItemMarkerRenderer` not including HTML attributes set on the node by other extensions (#947) + +## [2.3.6] - 2022-10-30 + +### Fixed + +- Fixed unquoted attribute parsing when closing curly brace is followed by certain characters (like a `.`) (#943) + +## [2.3.5] - 2022-07-29 + +### Fixed + +- Fixed error using `InlineParserEngine` when no inline parsers are registered in the `Environment` (#908) + +## [2.3.4] - 2022-07-17 + +### Changed + +- Made a number of small tweaks to the embed extension's parsing behavior to fix #898: + - Changed `EmbedStartParser` to always capture embed-like lines in container blocks, regardless of parent block type + - Changed `EmbedProcessor` to also remove `Embed` blocks that aren't direct children of the `Document` + - Increased the priority of `EmbedProcessor` to `1010` + +### Fixed + +- Fixed `EmbedExtension` not parsing embeds following a list block (#898) + +## [2.3.3] - 2022-06-07 + +### Fixed + +- Fixed `DomainFilteringAdapter` not reindexing the embed list (#884, #885) + +## [2.3.2] - 2022-06-03 + +### Fixed + +- Fixed FootnoteExtension stripping extra characters from tab-indented footnotes (#881) + +## [2.2.5] - 2022-06-03 + +### Fixed + +- Fixed FootnoteExtension stripping extra characters from tab-indented footnotes (#881) + +## [2.3.1] - 2022-05-14 + +### Fixed + +- Fixed AutolinkExtension not ignoring trailing strikethrough syntax (#867) + +## [2.2.4] - 2022-05-14 + +### Fixed + +- Fixed AutolinkExtension not ignoring trailing strikethrough syntax (#867) + +## [2.3.0] - 2022-04-07 + +### Added + +- Added new `EmbedExtension` (#805) +- Added `DocumentRendererInterface` as a replacement for the now-deprecated `MarkdownRendererInterface` + +### Deprecated + +- Deprecated `MarkdownRendererInterface`; use `DocumentRendererInterface` instead + +## [2.2.3] - 2022-02-26 + +### Fixed + +- Fixed front matter parsing with Windows line endings (#821) + +## [2.1.3] - 2022-02-26 + +### Fixed + +- Fixed front matter parsing with Windows line endings (#821) + +## [2.0.4] - 2022-02-26 + +### Fixed + +- Fixed front matter parsing with Windows line endings (#821) + +## [2.2.2] - 2022-02-13 + +### Fixed + +- Fixed double-escaping of image alt text (#806, #810) +- Fixed Psalm typehints for event class names + +## [2.2.1] - 2022-01-25 + +### Fixed + + - Fixed `symfony/deprecation-contracts` constraint + +### Removed + + - Removed deprecation trigger from `MarkdownConverterInterface` to reduce noise + +## [2.2.0] - 2022-01-22 + +### Added + + - Added new `ConverterInterface` + - Added new `MarkdownToXmlConverter` class + - Added new `HtmlDecorator` class which can wrap existing renderers with additional HTML tags + - Added new `table/wrap` config to apply an optional wrapping/container element around a table (#780) + +### Changed + + - `HtmlElement` contents can now consist of any `Stringable`, not just `HtmlElement` and `string` + +### Deprecated + + - Deprecated `MarkdownConverterInterface` and its `convertToHtml()` method; use `ConverterInterface` and `convert()` instead + +## [2.1.2] - 2022-02-13 + +### Fixed + +- Fixed double-escaping of image alt text (#806, #810) +- Fixed Psalm typehints for event class names + +## [2.1.1] - 2022-01-02 + +### Added + + - Added missing return type to `Environment::dispatch()` to fix deprecation warning (#778) + +## [2.1.0] - 2021-12-05 + +### Added + +- Added support for ext-yaml in FrontMatterExtension (#715) +- Added support for symfony/yaml v6.0 in FrontMatterExtension (#739) +- Added new `heading_permalink/aria_hidden` config option (#741) + +### Fixed + + - Fixed PHP 8.1 deprecation warning (#759, #762) + +## [2.0.3] - 2022-02-13 + +### Fixed + +- Fixed double-escaping of image alt text (#806, #810) +- Fixed Psalm typehints for event class names + +## [2.0.2] - 2021-08-14 + +### Changed + +- Bumped minimum version of league/config to support PHP 8.1 + +### Fixed + +- Fixed ability to register block parsers that identify lines starting with letters (#706) + +## [2.0.1] - 2021-07-31 + +### Fixed + +- Fixed nested autolinks (#689) +- Fixed description lists being parsed incorrectly (#692) +- Fixed Table of Contents not respecting Heading Permalink prefixes (#690) + +## [2.0.0] - 2021-07-24 + +No changes were introduced since the previous RC2 release. +See all entries below for a list of changes between 1.x and 2.0. + +## [2.0.0-rc2] - 2021-07-17 + +### Fixed + +- Fixed Mentions inside of links creating nested links against the spec's rules (#688) + +## [2.0.0-rc1] - 2021-07-10 + +No changes were introduced since the previous release. + +## [2.0.0-beta3] - 2021-07-03 + +### Changed + + - Any leading UTF-8 BOM will be stripped from the input + - The `getEnvironment()` method of `CommonMarkConverter` and `GithubFlavoredMarkdownConverter` will always return the concrete, configurable `Environment` for upgrading convenience + - Optimized AST iteration + - Lots of small micro-optimizations + +## [2.0.0-beta2] - 2021-06-27 + +### Added + +- Added new `Node::iterator()` method and `NodeIterator` class for faster AST iteration (#683, #684) + +### Changed + +- Made compatible with CommonMark spec 0.30.0 +- Optimized link label parsing +- Optimized AST iteration for a 50% performance boost in some event listeners (#683, #684) + +### Fixed + +- Fixed processing instructions with EOLs +- Fixed case-insensitive matching for HTML tag types +- Fixed type 7 HTML blocks incorrectly interrupting lazy paragraphs +- Fixed newlines in reference labels not collapsing into spaces +- Fixed link label normalization with escaped newlines +- Fixed unnecessary AST iteration when no default attributes are configured + +## [2.0.0-beta1] - 2021-06-20 + +### Added + + - **Added three new extensions:** + - `FrontMatterExtension` ([see documentation](https://commonmark.thephpleague.com/extensions/front-matter/)) + - `DescriptionListExtension` ([see documentation](https://commonmark.thephpleague.com/extensions/description-lists/)) + - `DefaultAttributesExtension` ([see documentation](https://commonmark.thephpleague.com/extensions/default-attributes/)) + - **Added new `XmlRenderer` to simplify AST debugging** ([see documentation](https://commonmark.thephpleague.com/xml/)) (#431) + - **Added the ability to configure disallowed raw HTML tags** (#507) + - **Added the ability for Mentions to use multiple characters for their symbol** (#514, #550) + - **Added the ability to delegate event dispatching to PSR-14 compliant event dispatcher libraries** + - **Added new configuration options:** + - Added `heading_permalink/min_heading_level` and `heading_permalink/max_heading_level` options to control which headings get permalinks (#519) + - Added `heading_permalink/fragment_prefix` to allow customizing the URL fragment prefix (#602) + - Added `footnote/backref_symbol` option for customizing backreference link appearance (#522) + - Added `slug_normalizer/max_length` option to control the maximum length of generated URL slugs + - Added `slug_normalizer/unique` option to control whether unique slugs should be generated per-document or per-environment + - **Added purity markers throughout the codebase** (verified with Psalm) + - Added `Query` class to simplify Node traversal when looking to take action on certain Nodes + - Added new `HtmlFilter` and `StringContainerHelper` utility classes + - Added new `AbstractBlockContinueParser` class to simplify the creation of custom block parsers + - Added several new classes and interfaces: + - `BlockContinue` + - `BlockContinueParserInterface` + - `BlockContinueParserWithInlinesInterface` + - `BlockStart` + - `BlockStartParserInterface` + - `ChildNodeRendererInterface` + - `ConfigurableExtensionInterface` + - `CursorState` + - `DashParser` (extracted from `PunctuationParser`) + - `DelimiterParser` + - `DocumentBlockParser` + - `DocumentPreRenderEvent` + - `DocumentRenderedEvent` + - `EllipsesParser` (extracted from `PunctuationParser`) + - `ExpressionInterface` + - `FallbackNodeXmlRenderer` + - `InlineParserEngineInterface` + - `InlineParserMatch` + - `MarkdownParserState` + - `MarkdownParserStateInterface` + - `MarkdownRendererInterface` + - `Query` + - `RawMarkupContainerInterface` + - `ReferenceableInterface` + - `RenderedContent` + - `RenderedContentInterface` + - `ReplaceUnpairedQuotesListener` + - `SpecReader` + - `TableOfContentsRenderer` + - `UniqueSlugNormalizer` + - `UniqueSlugNormalizerInterface` + - `XmlRenderer` + - `XmlNodeRendererInterface` + - Added several new methods: + - `Cursor::getCurrentCharacter()` + - `Environment::createDefaultConfiguration()` + - `Environment::setEventDispatcher()` + - `EnvironmentInterface::getExtensions()` + - `EnvironmentInterface::getInlineParsers()` + - `EnvironmentInterface::getSlugNormalizer()` + - `FencedCode::setInfo()` + - `Heading::setLevel()` + - `HtmlRenderer::renderDocument()` + - `InlineParserContext::getFullMatch()` + - `InlineParserContext::getFullMatchLength()` + - `InlineParserContext::getMatches()` + - `InlineParserContext::getSubMatches()` + - `LinkParserHelper::parsePartialLinkLabel()` + - `LinkParserHelper::parsePartialLinkTitle()` + - `Node::assertInstanceOf()` + - `RegexHelper::isLetter()` + - `StringContainerInterface::setLiteral()` + - `TableCell::getType()` + - `TableCell::setType()` + - `TableCell::getAlign()` + - `TableCell::setAlign()` + +### Changed + + - **Changed the converter return type** + - `CommonMarkConverter::convertToHtml()` now returns an instance of `RenderedContentInterface`. This can be cast to a string for backward compatibility with 1.x. + - **Table of Contents items are no longer wrapped with `

    ` tags** (#613) + - **Heading Permalinks now link to element IDs instead of using `name` attributes** (#602) + - **Heading Permalink IDs and URL fragments now have a `content` prefix by default** (#602) + - **Changes to configuration options:** + - `enable_em` has been renamed to `commonmark/enable_em` + - `enable_strong` has been renamed to `commonmark/enable_strong` + - `use_asterisk` has been renamed to `commonmark/use_asterisk` + - `use_underscore` has been renamed to `commonmark/use_underscore` + - `unordered_list_markers` has been renamed to `commonmark/unordered_list_markers` + - `mentions/*/symbol` has been renamed to `mentions/*/prefix` + - `mentions/*/regex` has been renamed to `mentions/*/pattern` and requires partial regular expressions (without delimiters or flags) + - `max_nesting_level` now defaults to `PHP_INT_MAX` and no longer supports floats + - `heading_permalink/slug_normalizer` has been renamed to `slug_normalizer/instance` + - **Event dispatching is now fully PSR-14 compliant** + - **Moved and renamed several classes** - [see the full list here](https://commonmark.thephpleague.com/2.0/upgrading/#classesnamespaces-renamed) + - The `HeadingPermalinkExtension` and `FootnoteExtension` were modified to ensure they never produce a slug which conflicts with slugs created by the other extension + - `SlugNormalizer::normalizer()` now supports optional prefixes and max length options passed in via the `$context` argument + - The `AbstractBlock::$data` and `AbstractInline::$data` arrays were replaced with a `Data` array-like object on the base `Node` class + - **Implemented a new approach to block parsing.** This was a massive change, so here are the highlights: + - Functionality previously found in block parsers and node elements has moved to block parser factories and block parsers, respectively ([more details](https://commonmark.thephpleague.com/2.0/upgrading/#new-block-parsing-approach)) + - `ConfigurableEnvironmentInterface::addBlockParser()` is now `EnvironmentBuilderInterface::addBlockParserFactory()` + - `ReferenceParser` was re-implemented and works completely different than before + - The paragraph parser no longer needs to be added manually to the environment + - **Implemented a new approach to inline parsing** where parsers can now specify longer strings or regular expressions they want to parse (instead of just single characters): + - `InlineParserInterface::getCharacters()` is now `getMatchDefinition()` and returns an instance of `InlineParserMatch` + - `InlineParserContext::__construct()` now requires the contents to be provided as a `Cursor` instead of a `string` + - **Implemented delimiter parsing as a special type of inline parser** (via the new `DelimiterParser` class) + - **Changed block and inline rendering to use common methods and interfaces** + - `BlockRendererInterface` and `InlineRendererInterface` were replaced by `NodeRendererInterface` with slightly different parameters. All core renderers now implement this interface. + - `ConfigurableEnvironmentInterface::addBlockRenderer()` and `addInlineRenderer()` were combined into `EnvironmentBuilderInterface::addRenderer()` + - `EnvironmentInterface::getBlockRenderersForClass()` and `getInlineRenderersForClass()` are now just `getRenderersForClass()` + - **Completely refactored the Configuration implementation** + - All configuration-specific classes have been moved into a new `league/config` package with a new namespace + - `Configuration` objects must now be configured with a schema and all options must match that schema - arbitrary keys are no longer permitted + - `Configuration::__construct()` no longer accepts the default configuration values - use `Configuration::merge()` instead + - `ConfigurationInterface` now only contains a `get(string $key)`; this method no longer allows arbitrary default values to be returned if the option is missing + - `ConfigurableEnvironmentInterface` was renamed to `EnvironmentBuilderInterface` + - `ExtensionInterface::register()` now requires an `EnvironmentBuilderInterface` param instead of `ConfigurableEnvironmentInterface` + - **Added missing return types to virtually every class and interface method** + - Re-implemented the GFM Autolink extension using the new inline parser approach instead of document processors + - `EmailAutolinkProcessor` is now `EmailAutolinkParser` + - `UrlAutolinkProcessor` is now `UrlAutolinkParser` + - `HtmlElement` can now properly handle array (i.e. `class`) and boolean (i.e. `checked`) attribute values + - `HtmlElement` automatically flattens any attributes with array values into space-separated strings, removing duplicate entries + - Combined separate classes/interfaces into one: + - `DisallowedRawHtmlRenderer` replaces `DisallowedRawHtmlBlockRenderer` and `DisallowedRawHtmlInlineRenderer` + - `NodeRendererInterface` replaces `BlockRendererInterface` and `InlineRendererInterface` + - Renamed the following methods: + - `Environment` and `ConfigurableEnvironmentInterface`: + - `addBlockParser()` is now `addBlockStartParser()` + - `ReferenceMap` and `ReferenceMapInterface`: + - `addReference()` is now `add()` + - `getReference()` is now `get()` + - `listReferences()` is now `getIterator()` + - Various node (block/inline) classes: + - `getContent()` is now `getLiteral()` + - `setContent()` is now `setLiteral()` + - Moved and renamed the following constants: + - `EnvironmentInterface::HTML_INPUT_ALLOW` is now `HtmlFilter::ALLOW` + - `EnvironmentInterface::HTML_INPUT_ESCAPE` is now `HtmlFilter::ESCAPE` + - `EnvironmentInterface::HTML_INPUT_STRIP` is now `HtmlFilter::STRIP` + - `TableCell::TYPE_HEAD` is now `TableCell::TYPE_HEADER` + - `TableCell::TYPE_BODY` is now `TableCell::TYPE_DATA` + - Changed the visibility of the following properties: + - `AttributesInline::$attributes` is now `private` + - `AttributesInline::$block` is now `private` + - `TableCell::$align` is now `private` + - `TableCell::$type` is now `private` + - `TableSection::$type` is now `private` + - Several methods which previously returned `$this` now return `void` + - `Delimiter::setPrevious()` + - `Node::replaceChildren()` + - `Context::setTip()` + - `Context::setContainer()` + - `Context::setBlocksParsed()` + - `AbstractStringContainer::setContent()` + - `AbstractWebResource::setUrl()` + - Several classes are now marked `final`: + - `ArrayCollection` + - `Emphasis` + - `FencedCode` + - `Heading` + - `HtmlBlock` + - `HtmlElement` + - `HtmlInline` + - `IndentedCode` + - `Newline` + - `Strikethrough` + - `Strong` + - `Text` + - `Heading` nodes no longer directly contain a copy of their inner text + - `StringContainerInterface` can now be used for inlines, not just blocks + - `ArrayCollection` only supports integer keys + - `HtmlElement` now implements `Stringable` + - `Cursor::saveState()` and `Cursor::restoreState()` now use `CursorState` objects instead of arrays + - `NodeWalker::next()` now enters, traverses any children, and leaves all elements which may have children (basically all blocks plus any inlines with children). Previously, it only did this for elements explicitly marked as "containers". + - `InvalidOptionException` was removed + - Anything with a `getReference(): ReferenceInterface` method now implements `ReferencableInterface` + - The `SmartPunct` extension now replaces all unpaired `Quote` elements with `Text` elements towards the end of parsing, making the `QuoteRenderer` unnecessary + - Several changes made to the Footnote extension: + - Footnote identifiers can no longer contain spaces + - Anonymous footnotes can now span subsequent lines + - Footnotes can now contain multiple lines of content, including sub-blocks, by indenting them + - Footnote event listeners now have numbered priorities (but still execute in the same order) + - Footnotes must now be separated from previous content by a blank line + - The line numbers (keys) returned via `MarkdownInput::getLines()` now start at 1 instead of 0 + - `DelimiterProcessorCollectionInterface` now extends `Countable` + - `RegexHelper::PARTIAL_` constants must always be used in case-insensitive contexts + - `HeadingPermalinkProcessor` no longer accepts text normalizers via the constructor - these must be provided via configuration instead + - Blocks which can't contain inlines will no longer be asked to render inlines + - `AnonymousFootnoteRefParser` and `HeadingPermalinkProcessor` now implement `EnvironmentAwareInterface` instead of `ConfigurationAwareInterface` + - The second argument to `TextNormalizerInterface::normalize()` must now be an array + - The `title` attribute for `Link` and `Image` nodes is now stored using a dedicated property instead of stashing it in `$data` + - `ListData::$delimiter` now returns either `ListBlock::DELIM_PERIOD` or `ListBlock::DELIM_PAREN` instead of the literal delimiter + +### Fixed + + - **Fixed parsing of footnotes without content** + - **Fixed rendering of orphaned footnotes and footnote refs** + - **Fixed some URL autolinks breaking too early** (#492) + - Fixed `AbstractStringContainer` not actually being `abstract` + +### Removed + + - **Removed support for PHP 7.1, 7.2, and 7.3** (#625, #671) + - **Removed all previously-deprecated functionality:** + - Removed the ability to pass custom `Environment` instances into the `CommonMarkConverter` and `GithubFlavoredMarkdownConverter` constructors + - Removed the `Converter` class and `ConverterInterface` + - Removed the `bin/commonmark` script + - Removed the `Html5Entities` utility class + - Removed the `InlineMentionParser` (use `MentionParser` instead) + - Removed `DefaultSlugGenerator` and `SlugGeneratorInterface` from the `Extension/HeadingPermalink/Slug` sub-namespace (use the new ones under `./SlugGenerator` instead) + - Removed the following `ArrayCollection` methods: + - `add()` + - `set()` + - `get()` + - `remove()` + - `isEmpty()` + - `contains()` + - `indexOf()` + - `containsKey()` + - `replaceWith()` + - `removeGaps()` + - Removed the `ConfigurableEnvironmentInterface::setConfig()` method + - Removed the `ListBlock::TYPE_UNORDERED` constant + - Removed the `CommonMarkConverter::VERSION` constant + - Removed the `HeadingPermalinkRenderer::DEFAULT_INNER_CONTENTS` constant + - Removed the `heading_permalink/inner_contents` configuration option + - **Removed now-unused classes:** + - `AbstractStringContainerBlock` + - `BlockRendererInterface` + - `Context` + - `ContextInterface` + - `Converter` + - `ConverterInterface` + - `InlineRendererInterface` + - `PunctuationParser` (was split into two classes: `DashParser` and `EllipsesParser`) + - `QuoteRenderer` + - `UnmatchedBlockCloser` + - Removed the following methods, properties, and constants: + - `AbstractBlock::$open` + - `AbstractBlock::$lastLineBlank` + - `AbstractBlock::isContainer()` + - `AbstractBlock::canContain()` + - `AbstractBlock::isCode()` + - `AbstractBlock::matchesNextLine()` + - `AbstractBlock::endsWithBlankLine()` + - `AbstractBlock::setLastLineBlank()` + - `AbstractBlock::shouldLastLineBeBlank()` + - `AbstractBlock::isOpen()` + - `AbstractBlock::finalize()` + - `AbstractBlock::getData()` + - `AbstractInline::getData()` + - `ConfigurableEnvironmentInterface::addBlockParser()` + - `ConfigurableEnvironmentInterface::mergeConfig()` + - `Delimiter::setCanClose()` + - `EnvironmentInterface::getConfig()` + - `EnvironmentInterface::getInlineParsersForCharacter()` + - `EnvironmentInterface::getInlineParserCharacterRegex()` + - `HtmlRenderer::renderBlock()` + - `HtmlRenderer::renderBlocks()` + - `HtmlRenderer::renderInline()` + - `HtmlRenderer::renderInlines()` + - `Node::isContainer()` + - `RegexHelper::matchAll()` (use the new `matchFirst()` method instead) + - `RegexHelper::REGEX_WHITESPACE` + - Removed the second `$contents` argument from the `Heading` constructor + +### Deprecated + +**The following things have been deprecated and will not be supported in v3.0:** + + - `Environment::mergeConfig()` (set configuration before instantiation instead) + - `Environment::createCommonMarkEnvironment()` and `Environment::createGFMEnvironment()` + - Alternative 1: Use `CommonMarkConverter` or `GithubFlavoredMarkdownConverter` if you don't need to customize the environment + - Alternative 2: Instantiate a new `Environment` and add the necessary extensions yourself + +[unreleased]: https://github.com/thephpleague/commonmark/compare/2.4.0...main +[2.4.0]: https://github.com/thephpleague/commonmark/compare/2.3.9...2.4.0 +[2.3.9]: https://github.com/thephpleague/commonmark/compare/2.3.8...2.3.9 +[2.3.8]: https://github.com/thephpleague/commonmark/compare/2.3.7...2.3.8 +[2.3.7]: https://github.com/thephpleague/commonmark/compare/2.3.6...2.3.7 +[2.3.6]: https://github.com/thephpleague/commonmark/compare/2.3.5...2.3.6 +[2.3.5]: https://github.com/thephpleague/commonmark/compare/2.3.4...2.3.5 +[2.3.4]: https://github.com/thephpleague/commonmark/compare/2.3.3...2.3.4 +[2.3.3]: https://github.com/thephpleague/commonmark/compare/2.3.2...2.3.3 +[2.3.2]: https://github.com/thephpleague/commonmark/compare/2.3.2...main +[2.3.1]: https://github.com/thephpleague/commonmark/compare/2.3.0...2.3.1 +[2.3.0]: https://github.com/thephpleague/commonmark/compare/2.2.3...2.3.0 +[2.2.5]: https://github.com/thephpleague/commonmark/compare/2.2.4...2.2.5 +[2.2.4]: https://github.com/thephpleague/commonmark/compare/2.2.3...2.2.4 +[2.2.3]: https://github.com/thephpleague/commonmark/compare/2.2.2...2.2.3 +[2.2.2]: https://github.com/thephpleague/commonmark/compare/2.2.1...2.2.2 +[2.2.1]: https://github.com/thephpleague/commonmark/compare/2.2.0...2.2.1 +[2.2.0]: https://github.com/thephpleague/commonmark/compare/2.1.1...2.2.0 +[2.1.3]: https://github.com/thephpleague/commonmark/compare/2.1.2...2.1.3 +[2.1.2]: https://github.com/thephpleague/commonmark/compare/2.1.1...2.1.2 +[2.1.1]: https://github.com/thephpleague/commonmark/compare/2.0.2...2.1.1 +[2.1.0]: https://github.com/thephpleague/commonmark/compare/2.0.2...2.1.0 +[2.0.4]: https://github.com/thephpleague/commonmark/compare/2.0.3...2.0.4 +[2.0.3]: https://github.com/thephpleague/commonmark/compare/2.0.2...2.0.3 +[2.0.2]: https://github.com/thephpleague/commonmark/compare/2.0.1...2.0.2 +[2.0.1]: https://github.com/thephpleague/commonmark/compare/2.0.0...2.0.1 +[2.0.0]: https://github.com/thephpleague/commonmark/compare/2.0.0-rc2...2.0.0 +[2.0.0-rc2]: https://github.com/thephpleague/commonmark/compare/2.0.0-rc1...2.0.0-rc2 +[2.0.0-rc1]: https://github.com/thephpleague/commonmark/compare/2.0.0-beta3...2.0.0-rc1 +[2.0.0-beta3]: https://github.com/thephpleague/commonmark/compare/2.0.0-beta2...2.0.0-beta3 +[2.0.0-beta2]: https://github.com/thephpleague/commonmark/compare/2.0.0-beta1...2.0.0-beta2 +[2.0.0-beta1]: https://github.com/thephpleague/commonmark/compare/1.6...2.0.0-beta1 diff --git a/vendor/league/commonmark/LICENSE b/vendor/league/commonmark/LICENSE new file mode 100644 index 00000000..5f04fad7 --- /dev/null +++ b/vendor/league/commonmark/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2014-2022, Colin O'Dell. All rights reserved. Some code based on commonmark.js (copyright 2014-2018, John MacFarlane) and commonmark-java (copyright 2015-2016, Atlassian Pty Ltd) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/league/commonmark/composer.json b/vendor/league/commonmark/composer.json new file mode 100644 index 00000000..3dfb755f --- /dev/null +++ b/vendor/league/commonmark/composer.json @@ -0,0 +1,125 @@ +{ + "name": "league/commonmark", + "type": "library", + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "keywords": ["markdown","parser","commonmark","gfm","github","flavored","github-flavored","md"], + "homepage": "https://commonmark.thephpleague.com", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "require": { + "php": "^7.4 || ^8.0", + "ext-mbstring": "*", + "league/config": "^1.1.1", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "ext-json": "*", + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.30.0", + "commonmark/commonmark.js": "0.30.0", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0" + }, + "minimum-stability": "beta", + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "repositories": [ + { + "type": "package", + "package": { + "name": "commonmark/commonmark.js", + "version": "0.30.0", + "dist": { + "url": "https://github.com/commonmark/commonmark.js/archive/0.30.0.zip", + "type": "zip" + } + } + }, + { + "type": "package", + "package": { + "name": "commonmark/cmark", + "version": "0.30.0", + "dist": { + "url": "https://github.com/commonmark/cmark/archive/0.30.0.zip", + "type": "zip" + } + } + }, + { + "type": "package", + "package": { + "name": "github/gfm", + "version": "0.29.0", + "dist": { + "url": "https://github.com/github/cmark-gfm/archive/0.29.0.gfm.9.zip", + "type": "zip" + } + } + } + ], + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "League\\CommonMark\\Tests\\Unit\\": "tests/unit", + "League\\CommonMark\\Tests\\Functional\\": "tests/functional", + "League\\CommonMark\\Tests\\PHPStan\\": "tests/phpstan" + } + }, + "scripts": { + "phpcs": "phpcs", + "phpstan": "phpstan analyse", + "phpunit": "phpunit --no-coverage", + "psalm": "psalm --stats", + "test": [ + "@phpcs", + "@phpstan", + "@psalm", + "@phpunit" + ] + }, + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + } + }, + "config": { + "allow-plugins": { + "composer/package-versions-deprecated": true, + "dealerdirect/phpcodesniffer-composer-installer": true + }, + "sort-packages": true + } +} diff --git a/vendor/league/config/CHANGELOG.md b/vendor/league/config/CHANGELOG.md new file mode 100644 index 00000000..9a7813ac --- /dev/null +++ b/vendor/league/config/CHANGELOG.md @@ -0,0 +1,42 @@ +# Change Log +All notable changes to this project will be documented in this file. +Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) principles. + +## [Unreleased][unreleased] + +## [1.2.0] - 2022-12-11 + +### Changed + +- Values can now be set prior to the corresponding schema being registered. +- `exists()` and `get()` now only trigger validation for the relevant schema, not the entire config at once. + +## [1.1.1] - 2021-08-14 + +### Changed + + - Bumped the minimum version of dflydev/dot-access-data for PHP 8.1 support + +## [1.1.0] - 2021-06-19 + +### Changed + +- Bumped the minimum PHP version to 7.4+ +- Bumped the minimum version of nette/schema to 1.2.0 + +## [1.0.1] - 2021-05-31 + +### Fixed + +- Fixed the `ConfigurationExceptionInterface` marker interface not extending `Throwable` (#2) + +## [1.0.0] - 2021-05-31 + +Initial release! 🎉 + +[unreleased]: https://github.com/thephpleague/config/compare/v1.2.0...main +[1.2.0]: https://github.com/thephpleague/config/compare/v1.1.1...v.1.2.0 +[1.1.1]: https://github.com/thephpleague/config/compare/v1.1.0...v1.1.1 +[1.1.0]: https://github.com/thephpleague/config/compare/v1.0.1...v1.1.0 +[1.0.1]: https://github.com/thephpleague/config/compare/v1.0.0...v1.0.1 +[1.0.0]: https://github.com/thephpleague/config/releases/tag/v1.0.0 diff --git a/vendor/league/config/LICENSE.md b/vendor/league/config/LICENSE.md new file mode 100644 index 00000000..1a444a17 --- /dev/null +++ b/vendor/league/config/LICENSE.md @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2022, Colin O'Dell. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/league/config/composer.json b/vendor/league/config/composer.json new file mode 100644 index 00000000..3cd8d87e --- /dev/null +++ b/vendor/league/config/composer.json @@ -0,0 +1,69 @@ +{ + "name": "league/config", + "type": "library", + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "keywords": ["configuration","config","schema","array","nested","dot","dot-access"], + "homepage": "https://config.thephpleague.com", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "require": { + "php": "^7.4 || ^8.0", + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "League\\Config\\Tests\\": "tests" + } + }, + "scripts": { + "phpcs": "phpcs", + "phpstan": "phpstan analyse", + "phpunit": "phpunit --no-coverage", + "psalm": "psalm", + "test": [ + "@phpcs", + "@phpstan", + "@psalm", + "@phpunit" + ] + }, + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + } +} diff --git a/vendor/nette/schema/composer.json b/vendor/nette/schema/composer.json new file mode 100644 index 00000000..bece68ac --- /dev/null +++ b/vendor/nette/schema/composer.json @@ -0,0 +1,39 @@ +{ + "name": "nette/schema", + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "keywords": ["nette", "config"], + "homepage": "https://nette.org", + "license": ["BSD-3-Clause", "GPL-2.0-only", "GPL-3.0-only"], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "require": { + "php": ">=7.1 <8.3", + "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0" + }, + "require-dev": { + "nette/tester": "^2.3 || ^2.4", + "tracy/tracy": "^2.7", + "phpstan/phpstan-nette": "^1.0" + }, + "autoload": { + "classmap": ["src/"] + }, + "minimum-stability": "dev", + "scripts": { + "phpstan": "phpstan analyse", + "tester": "tester tests -s" + }, + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + } +} diff --git a/vendor/nette/schema/contributing.md b/vendor/nette/schema/contributing.md new file mode 100644 index 00000000..184152c0 --- /dev/null +++ b/vendor/nette/schema/contributing.md @@ -0,0 +1,33 @@ +How to contribute & use the issue tracker +========================================= + +Nette welcomes your contributions. There are several ways to help out: + +* Create an issue on GitHub, if you have found a bug +* Write test cases for open bug issues +* Write fixes for open bug/feature issues, preferably with test cases included +* Contribute to the [documentation](https://nette.org/en/writing) + +Issues +------ + +Please **do not use the issue tracker to ask questions**. We will be happy to help you +on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette). + +A good bug report shouldn't leave others needing to chase you up for more +information. Please try to be as detailed as possible in your report. + +**Feature requests** are welcome. But take a moment to find out whether your idea +fits with the scope and aims of the project. It's up to *you* to make a strong +case to convince the project's developers of the merits of this feature. + +Contributing +------------ + +If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing). + +The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them. + +Please do not fix whitespace, format code, or make a purely cosmetic patch. + +Thanks! :heart: diff --git a/vendor/nette/schema/license.md b/vendor/nette/schema/license.md new file mode 100644 index 00000000..cf741bd0 --- /dev/null +++ b/vendor/nette/schema/license.md @@ -0,0 +1,60 @@ +Licenses +======== + +Good news! You may use Nette Framework under the terms of either +the New BSD License or the GNU General Public License (GPL) version 2 or 3. + +The BSD License is recommended for most projects. It is easy to understand and it +places almost no restrictions on what you can do with the framework. If the GPL +fits better to your project, you can use the framework under this license. + +You don't have to notify anyone which license you are using. You can freely +use Nette Framework in commercial projects as long as the copyright header +remains intact. + +Please be advised that the name "Nette Framework" is a protected trademark and its +usage has some limitations. So please do not use word "Nette" in the name of your +project or top-level domain, and choose a name that stands on its own merits. +If your stuff is good, it will not take long to establish a reputation for yourselves. + + +New BSD License +--------------- + +Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of "Nette Framework" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as is" and +any express or implied warranties, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose are +disclaimed. In no event shall the copyright owner or contributors be liable for +any direct, indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or services; +loss of use, data, or profits; or business interruption) however caused and on +any theory of liability, whether in contract, strict liability, or tort +(including negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. + + +GNU General Public License +-------------------------- + +GPL licenses are very very long, so instead of including them here we offer +you URLs with full text: + +- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html) +- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html) diff --git a/vendor/nette/schema/readme.md b/vendor/nette/schema/readme.md new file mode 100644 index 00000000..20b5de2a --- /dev/null +++ b/vendor/nette/schema/readme.md @@ -0,0 +1,441 @@ +Nette Schema +************ + +[![Downloads this Month](https://img.shields.io/packagist/dm/nette/schema.svg)](https://packagist.org/packages/nette/schema) +[![Tests](https://github.com/nette/schema/workflows/Tests/badge.svg?branch=master)](https://github.com/nette/schema/actions) +[![Coverage Status](https://coveralls.io/repos/github/nette/schema/badge.svg?branch=master)](https://coveralls.io/github/nette/schema?branch=master) +[![Latest Stable Version](https://poser.pugx.org/nette/schema/v/stable)](https://github.com/nette/schema/releases) +[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/schema/blob/master/license.md) + + +Introduction +============ + +A practical library for validation and normalization of data structures against a given schema with a smart & easy-to-understand API. + +Documentation can be found on the [website](https://doc.nette.org/schema). + +Installation: + +```shell +composer require nette/schema +``` + +It requires PHP version 7.1 and supports PHP up to 8.2. + + +[Support Me](https://github.com/sponsors/dg) +-------------------------------------------- + +Do you like Nette Schema? Are you looking forward to the new features? + +[![Buy me a coffee](https://files.nette.org/icons/donation-3.svg)](https://github.com/sponsors/dg) + +Thank you! + + +Basic Usage +----------- + +In variable `$schema` we have a validation schema (what exactly this means and how to create it we will say later) and in variable `$data` we have a data structure that we want to validate and normalize. This can be, for example, data sent by the user through an API, configuration file, etc. + +The task is handled by the [Nette\Schema\Processor](https://api.nette.org/3.0/Nette/Schema/Processor.html) class, which processes the input and either returns normalized data or throws an [Nette\Schema\ValidationException](https://api.nette.org/3.0/Nette/Schema/ValidationException.html) exception on error. + +```php +$processor = new Nette\Schema\Processor; + +try { + $normalized = $processor->process($schema, $data); +} catch (Nette\Schema\ValidationException $e) { + echo 'Data is invalid: ' . $e->getMessage(); +} +``` + +Method `$e->getMessages()` returns array of all message strings and `$e->getMessageObjects()` return all messages as [Nette\Schema\Message](https://api.nette.org/3.1/Nette/Schema/Message.html) objects. + + +Defining Schema +--------------- + +And now let's create a schema. The class [Nette\Schema\Expect](https://api.nette.org/3.0/Nette/Schema/Expect.html) is used to define it, we actually define expectations of what the data should look like. Let's say that the input data must be a structure (e.g. an array) containing elements `processRefund` of type bool and `refundAmount` of type int. + +```php +use Nette\Schema\Expect; + +$schema = Expect::structure([ + 'processRefund' => Expect::bool(), + 'refundAmount' => Expect::int(), +]); +``` + +We believe that the schema definition looks clear, even if you see it for the very first time. + +Lets send the following data for validation: + +```php +$data = [ + 'processRefund' => true, + 'refundAmount' => 17, +]; + +$normalized = $processor->process($schema, $data); // OK, it passes +``` + +The output, i.e. the value `$normalized`, is the object `stdClass`. If we want the output to be an array, we add a cast to schema `Expect::structure([...])->castTo('array')`. + +All elements of the structure are optional and have a default value `null`. Example: + +```php +$data = [ + 'refundAmount' => 17, +]; + +$normalized = $processor->process($schema, $data); // OK, it passes +// $normalized = {'processRefund' => null, 'refundAmount' => 17} +``` + +The fact that the default value is `null` does not mean that it would be accepted in the input data `'processRefund' => null`. No, the input must be boolean, i.e. only `true` or `false`. We would have to explicitly allow `null` via `Expect::bool()->nullable()`. + +An item can be made mandatory using `Expect::bool()->required()`. We change the default value to `false` using `Expect::bool()->default(false)` or shortly using `Expect::bool(false)`. + +And what if we wanted to accept `1` and `0` besides booleans? Then we list the allowed values, which we will also normalize to boolean: + +```php +$schema = Expect::structure([ + 'processRefund' => Expect::anyOf(true, false, 1, 0)->castTo('bool'), + 'refundAmount' => Expect::int(), +]); + +$normalized = $processor->process($schema, $data); +is_bool($normalized->processRefund); // true +``` + +Now you know the basics of how the schema is defined and how the individual elements of the structure behave. We will now show what all the other elements can be used in defining a schema. + + + +Data Types: type() +------------------ + +All standard PHP data types can be listed in the schema: + +```php +Expect::string($default = null) +Expect::int($default = null) +Expect::float($default = null) +Expect::bool($default = null) +Expect::null() +Expect::array($default = []) +``` + +And then all types [supported by the Validators](https://doc.nette.org/validators#toc-validation-rules) via `Expect::type('scalar')` or abbreviated `Expect::scalar()`. Also class or interface names are accepted, e.g. `Expect::type('AddressEntity')`. + +You can also use union notation: + +```php +Expect::type('bool|string|array') +``` + +The default value is always `null` except for `array` and `list`, where it is an empty array. (A list is an array indexed in ascending order of numeric keys from zero, that is, a non-associative array). + + +Array of Values: arrayOf() listOf() +----------------------------------- + +The array is too general structure, it is more useful to specify exactly what elements it can contain. For example, an array whose elements can only be strings: + +```php +$schema = Expect::arrayOf('string'); + +$processor->process($schema, ['hello', 'world']); // OK +$processor->process($schema, ['a' => 'hello', 'b' => 'world']); // OK +$processor->process($schema, ['key' => 123]); // ERROR: 123 is not a string +``` + +The list is an indexed array: + +```php +$schema = Expect::listOf('string'); + +$processor->process($schema, ['a', 'b']); // OK +$processor->process($schema, ['a', 123]); // ERROR: 123 is not a string +$processor->process($schema, ['key' => 'a']); // ERROR: is not a list +$processor->process($schema, [1 => 'a', 0 => 'b']); // ERROR: is not a list +``` + +The parameter can also be a schema, so we can write: + +```php +Expect::arrayOf(Expect::bool()) +``` + +The default value is an empty array. If you specify default value, it will be merged with the passed data. This can be disabled using `mergeDefaults(false)`. + + +Enumeration: anyOf() +-------------------- + +`anyOf()` is a set of values ​​or schemas that a value can be. Here's how to write an array of elements that can be either `'a'`, `true`, or `null`: + +```php +$schema = Expect::listOf( + Expect::anyOf('a', true, null) +); + +$processor->process($schema, ['a', true, null, 'a']); // OK +$processor->process($schema, ['a', false]); // ERROR: false does not belong there +``` + +The enumeration elements can also be schemas: + +```php +$schema = Expect::listOf( + Expect::anyOf(Expect::string(), true, null) +); + +$processor->process($schema, ['foo', true, null, 'bar']); // OK +$processor->process($schema, [123]); // ERROR +``` + +The default value is `null`. + + +Structures +---------- + +Structures are objects with defined keys. Each of these key => value pairs is referred to as a "property": + +Structures accept arrays and objects and return objects `stdClass` (unless you change it with `castTo('array')`, etc.). + +By default, all properties are optional and have a default value of `null`. You can define mandatory properties using `required()`: + +```php +$schema = Expect::structure([ + 'required' => Expect::string()->required(), + 'optional' => Expect::string(), // the default value is null +]); + +$processor->process($schema, ['optional' => '']); +// ERROR: item 'required' is missing + +$processor->process($schema, ['required' => 'foo']); +// OK, returns {'required' => 'foo', 'optional' => null} +``` + +Although `null` is the default value of the `optional` property, it is not allowed in the input data (the value must be a string). Properties accepting `null` are defined using `nullable()`: + +```php +$schema = Expect::structure([ + 'optional' => Expect::string(), + 'nullable' => Expect::string()->nullable(), +]); + +$processor->process($schema, ['optional' => null]); +// ERROR: 'optional' expects to be string, null given. + +$processor->process($schema, ['nullable' => null]); +// OK, returns {'optional' => null, 'nullable' => null} +``` + +By default, there can be no extra items in the input data: + +```php +$schema = Expect::structure([ + 'key' => Expect::string(), +]); + +$processor->process($schema, ['additional' => 1]); +// ERROR: Unexpected item 'additional' +``` + +Which we can change with `otherItems()`. As a parameter, we will specify the schema for each extra element: + +```php +$schema = Expect::structure([ + 'key' => Expect::string(), +])->otherItems(Expect::int()); + +$processor->process($schema, ['additional' => 1]); // OK +$processor->process($schema, ['additional' => true]); // ERROR +``` + +Deprecations +------------ + +You can deprecate property using the `deprecated([string $message])` method. Deprecation notices are returned by `$processor->getWarnings()` (since v1.1): + +```php +$schema = Expect::structure([ + 'old' => Expect::int()->deprecated('The item %path% is deprecated'), +]); + +$processor->process($schema, ['old' => 1]); // OK +$processor->getWarnings(); // ["The item 'old' is deprecated"] +``` + +Ranges: min() max() +------------------- + +Use `min()` and `max()` to limit the number of elements for arrays: + +```php +// array, at least 10 items, maximum 20 items +Expect::array()->min(10)->max(20); +``` + +For strings, limit their length: + +```php +// string, at least 10 characters long, maximum 20 characters +Expect::string()->min(10)->max(20); +``` + +For numbers, limit their value: + +```php +// integer, between 10 and 20 inclusive +Expect::int()->min(10)->max(20); +``` + +Of course, it is possible to mention only `min()`, or only `max()`: + +```php +// string, maximum 20 characters +Expect::string()->max(20); +``` + + +Regular Expressions: pattern() +------------------------------ + +Using `pattern()`, you can specify a regular expression which the **whole** input string must match (i.e. as if it were wrapped in characters `^` a `$`): + +```php +// just 9 digits +Expect::string()->pattern('\d{9}'); +``` + + +Custom Assertions: assert() +--------------------------- + +You can add any other restrictions using `assert(callable $fn)`. + +```php +$countIsEven = function ($v) { return count($v) % 2 === 0; }; + +$schema = Expect::arrayOf('string') + ->assert($countIsEven); // the count must be even + +$processor->process($schema, ['a', 'b']); // OK +$processor->process($schema, ['a', 'b', 'c']); // ERROR: 3 is not even +``` + +Or + +```php +Expect::string()->assert('is_file'); // the file must exist +``` + +You can add your own description for each assertions. It will be part of the error message. + +```php +$schema = Expect::arrayOf('string') + ->assert($countIsEven, 'Even items in array'); + +$processor->process($schema, ['a', 'b', 'c']); +// Failed assertion "Even items in array" for item with value array. +``` + +The method can be called repeatedly to add more assertions. + + +Mapping to Objects: from() +-------------------------- + +You can generate structure schema from the class. Example: + +```php +class Config +{ + /** @var string */ + public $name; + /** @var string|null */ + public $password; + /** @var bool */ + public $admin = false; +} + +$schema = Expect::from(new Config); + +$data = [ + 'name' => 'jeff', +]; + +$normalized = $processor->process($schema, $data); +// $normalized instanceof Config +// $normalized = {'name' => 'jeff', 'password' => null, 'admin' => false} +``` + +If you are using PHP 7.4 or higher, you can use native types: + +```php +class Config +{ + public string $name; + public ?string $password; + public bool $admin = false; +} + +$schema = Expect::from(new Config); +``` + +Anonymous classes are also supported: + +```php +$schema = Expect::from(new class { + public string $name; + public ?string $password; + public bool $admin = false; +}); +``` + +Because the information obtained from the class definition may not be sufficient, you can add a custom schema for the elements with the second parameter: + +```php +$schema = Expect::from(new Config, [ + 'name' => Expect::string()->pattern('\w:.*'), +]); +``` + + +Casting: castTo() +----------------- + +Successfully validated data can be cast: + +```php +Expect::scalar()->castTo('string'); +``` + +In addition to native PHP types, you can also cast to classes: + +```php +Expect::scalar()->castTo('AddressEntity'); +``` + + +Normalization: before() +----------------------- + +Prior to the validation itself, the data can be normalized using the method `before()`. As an example, let's have an element that must be an array of strings (eg `['a', 'b', 'c']`), but receives input in the form of a string `a b c`: + +```php +$explode = function ($v) { return explode(' ', $v); }; + +$schema = Expect::arrayOf('string') + ->before($explode); + +$normalized = $processor->process($schema, 'a b c'); +// OK, returns ['a', 'b', 'c'] +``` diff --git a/vendor/nette/utils/.phpstorm.meta.php b/vendor/nette/utils/.phpstorm.meta.php new file mode 100644 index 00000000..25851af6 --- /dev/null +++ b/vendor/nette/utils/.phpstorm.meta.php @@ -0,0 +1,13 @@ +=8.0 <8.3" + }, + "require-dev": { + "nette/tester": "^2.4", + "tracy/tracy": "^2.9", + "phpstan/phpstan": "^1.0", + "jetbrains/phpstorm-attributes": "dev-master" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "suggest": { + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-xml": "to use Strings::length() etc. when mbstring is not available", + "ext-gd": "to use Image", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "autoload": { + "classmap": ["src/"] + }, + "minimum-stability": "dev", + "scripts": { + "phpstan": "phpstan analyse", + "tester": "tester tests -s" + }, + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + } +} diff --git a/vendor/nette/utils/license.md b/vendor/nette/utils/license.md new file mode 100644 index 00000000..cf741bd0 --- /dev/null +++ b/vendor/nette/utils/license.md @@ -0,0 +1,60 @@ +Licenses +======== + +Good news! You may use Nette Framework under the terms of either +the New BSD License or the GNU General Public License (GPL) version 2 or 3. + +The BSD License is recommended for most projects. It is easy to understand and it +places almost no restrictions on what you can do with the framework. If the GPL +fits better to your project, you can use the framework under this license. + +You don't have to notify anyone which license you are using. You can freely +use Nette Framework in commercial projects as long as the copyright header +remains intact. + +Please be advised that the name "Nette Framework" is a protected trademark and its +usage has some limitations. So please do not use word "Nette" in the name of your +project or top-level domain, and choose a name that stands on its own merits. +If your stuff is good, it will not take long to establish a reputation for yourselves. + + +New BSD License +--------------- + +Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of "Nette Framework" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as is" and +any express or implied warranties, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose are +disclaimed. In no event shall the copyright owner or contributors be liable for +any direct, indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or services; +loss of use, data, or profits; or business interruption) however caused and on +any theory of liability, whether in contract, strict liability, or tort +(including negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. + + +GNU General Public License +-------------------------- + +GPL licenses are very very long, so instead of including them here we offer +you URLs with full text: + +- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html) +- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html) diff --git a/vendor/nette/utils/readme.md b/vendor/nette/utils/readme.md new file mode 100644 index 00000000..ff2ab46c --- /dev/null +++ b/vendor/nette/utils/readme.md @@ -0,0 +1,56 @@ +Nette Utility Classes +===================== + +[![Downloads this Month](https://img.shields.io/packagist/dm/nette/utils.svg)](https://packagist.org/packages/nette/utils) +[![Tests](https://github.com/nette/utils/workflows/Tests/badge.svg?branch=master)](https://github.com/nette/utils/actions) +[![Coverage Status](https://coveralls.io/repos/github/nette/utils/badge.svg?branch=master)](https://coveralls.io/github/nette/utils?branch=master) +[![Latest Stable Version](https://poser.pugx.org/nette/utils/v/stable)](https://github.com/nette/utils/releases) +[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/utils/blob/master/license.md) + + +Introduction +------------ + +In package nette/utils you will find a set of [useful classes](https://doc.nette.org/utils) for everyday use: + +- [Arrays](https://doc.nette.org/utils/arrays) - manipulate arrays +- [Callback](https://doc.nette.org/utils/callback) - PHP callbacks +- [Date and Time](https://doc.nette.org/utils/datetime) - modify times and dates +- [Filesystem](https://doc.nette.org/utils/filesystem) - copying, renaming, … +- [Finder](https://doc.nette.org/utils/finder) - finds files and directories +- [Helper Functions](https://doc.nette.org/utils/helpers) +- [HTML elements](https://doc.nette.org/utils/html-elements) - generate HTML +- [Images](https://doc.nette.org/utils/images) - crop, resize, rotate images +- [JSON](https://doc.nette.org/utils/json) - encoding and decoding +- [Generating Random Strings](https://doc.nette.org/utils/random) +- [Paginator](https://doc.nette.org/utils/paginator) - pagination math +- [PHP Reflection](https://doc.nette.org/utils/reflection) +- [Strings](https://doc.nette.org/utils/strings) - useful text functions +- [SmartObject](https://doc.nette.org/utils/smartobject) - PHP object enhancements +- [Validation](https://doc.nette.org/utils/validators) - validate inputs +- [Type](https://doc.nette.org/utils/type) - PHP data type + + +Installation +------------ + +The recommended way to install is via Composer: + +``` +composer require nette/utils +``` + +- Nette Utils 4.0 is compatible with PHP 8.0 to 8.2 +- Nette Utils 3.2 is compatible with PHP 7.2 to 8.2 +- Nette Utils 3.1 is compatible with PHP 7.1 to 8.0 +- Nette Utils 3.0 is compatible with PHP 7.1 to 8.0 +- Nette Utils 2.5 is compatible with PHP 5.6 to 8.0 + +[Support Me](https://github.com/sponsors/dg) +-------------------------------------------- + +Do you like Nette Utils? Are you looking forward to the new features? + +[![Buy me a coffee](https://files.nette.org/icons/donation-3.svg)](https://github.com/sponsors/dg) + +Thank you! diff --git a/vendor/psr/event-dispatcher/.editorconfig b/vendor/psr/event-dispatcher/.editorconfig new file mode 100644 index 00000000..164f092d --- /dev/null +++ b/vendor/psr/event-dispatcher/.editorconfig @@ -0,0 +1,15 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[Makefile] +indent_style = tab diff --git a/vendor/psr/event-dispatcher/LICENSE b/vendor/psr/event-dispatcher/LICENSE new file mode 100644 index 00000000..3f1559b2 --- /dev/null +++ b/vendor/psr/event-dispatcher/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 PHP-FIG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/psr/event-dispatcher/composer.json b/vendor/psr/event-dispatcher/composer.json new file mode 100644 index 00000000..667a7144 --- /dev/null +++ b/vendor/psr/event-dispatcher/composer.json @@ -0,0 +1,26 @@ +{ + "name": "psr/event-dispatcher", + "description": "Standard interfaces for event handling.", + "type": "library", + "keywords": ["psr", "psr-14", "events"], + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=7.2.0" + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/sirbrillig/phpcs-variable-analysis/LICENSE b/vendor/sirbrillig/phpcs-variable-analysis/LICENSE new file mode 100644 index 00000000..ba156525 --- /dev/null +++ b/vendor/sirbrillig/phpcs-variable-analysis/LICENSE @@ -0,0 +1,71 @@ +BSD Licence Agreement +----------------------------------------------------------------------- + +This software is available to you under the BSD license, +available in the LICENSE file accompanying this software. +You may obtain a copy of the License at + +http://www.opensource.org/licenses/bsd-license.php + +----------------------------------------------------------------------- + +Copyright (c) 2011, Sam Graham +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------- + +Portions of this sofware derived from work Copyright (c) 2010, Monotek d.o.o, +released under a BSD License available at: + +http://www.opensource.org/licenses/bsd-license.php + +----------------------------------------------------------------------- + +Portions of this software derived from work Copyright (c), 2006 Squiz +Pty Ltd (ABN 77 084 670 600), available under the following license: + +BSD Licence Agreement +----------------------------------------------------------------------- + +This software is available to you under the BSD license, +available in the LICENSE file accompanying this software. +You may obtain a copy of the License at + +http://matrix.squiz.net/developer/tools/php_cs/licence + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Copyright (c), 2006 Squiz Pty Ltd (ABN 77 084 670 600). +All rights reserved. diff --git a/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/Constants.php b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/Constants.php new file mode 100644 index 00000000..95580c08 --- /dev/null +++ b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/Constants.php @@ -0,0 +1,260 @@ +> + */ + public static function getPassByReferenceFunctions() + { + return [ + '__soapCall' => [5], + 'addFunction' => [3], + 'addTask' => [3], + 'addTaskBackground' => [3], + 'addTaskHigh' => [3], + 'addTaskHighBackground' => [3], + 'addTaskLow' => [3], + 'addTaskLowBackground' => [3], + 'addTaskStatus' => [2], + 'apc_dec' => [3], + 'apc_fetch' => [2], + 'apc_inc' => [3], + 'apcu_dec' => [3], + 'apcu_fetch' => [2], + 'apcu_inc' => [3], + 'areConfusable' => [3], + 'arsort' => [1], + 'asort' => [1], + 'bindColumn' => [2], + 'bindParam' => [2], + 'bind_param' => [2, 3, '...'], + 'bind_result' => [1, 2, '...'], + 'call_user_method' => [2], + 'call_user_method_array' => [2], + 'curl_multi_exec' => [2], + 'curl_multi_info_read' => [2], + 'current' => [1], + 'dbplus_curr' => [2], + 'dbplus_first' => [2], + 'dbplus_info' => [3], + 'dbplus_last' => [2], + 'dbplus_next' => [2], + 'dbplus_prev' => [2], + 'dbplus_tremove' => [3], + 'dns_get_record' => [3, 4], + 'domxml_open_file' => [3], + 'domxml_open_mem' => [3], + 'each' => [1], + 'enchant_dict_quick_check' => [3], + 'end' => [1], + 'ereg' => [3], + 'eregi' => [3], + 'exec' => [2, 3], + 'exif_thumbnail' => [1, 2, 3], + 'expect_expectl' => [3], + 'extract' => [1], + 'filter' => [3], + 'flock' => [2,3], + 'fscanf' => [2, 3, '...'], + 'fsockopen' => [3, 4], + 'ftp_alloc' => [3], + 'get' => [2, 3], + 'getByKey' => [4], + 'getMulti' => [2], + 'getMultiByKey' => [3], + 'getimagesize' => [2], + 'getmxrr' => [2, 3], + 'gnupg_decryptverify' => [3], + 'gnupg_verify' => [4], + 'grapheme_extract' => [5], + 'headers_sent' => [1, 2], + 'http_build_url' => [4], + 'http_get' => [3], + 'http_head' => [3], + 'http_negotiate_charset' => [2], + 'http_negotiate_content_type' => [2], + 'http_negotiate_language' => [2], + 'http_post_data' => [4], + 'http_post_fields' => [5], + 'http_put_data' => [4], + 'http_put_file' => [4], + 'http_put_stream' => [4], + 'http_request' => [5], + 'isSuspicious' => [2], + 'is_callable' => [3], + 'key' => [1], + 'krsort' => [1], + 'ksort' => [1], + 'ldap_get_option' => [3], + 'ldap_parse_reference' => [3], + 'ldap_parse_result' => [3, 4, 5, 6], + 'localtime' => [2], + 'm_completeauthorizations' => [2], + 'maxdb_stmt_bind_param' => [3, 4, '...'], + 'maxdb_stmt_bind_result' => [2, 3, '...'], + 'mb_convert_variables' => [3, 4, '...'], + 'mb_parse_str' => [2], + 'mqseries_back' => [2, 3], + 'mqseries_begin' => [3, 4], + 'mqseries_close' => [4, 5], + 'mqseries_cmit' => [2, 3], + 'mqseries_conn' => [2, 3, 4], + 'mqseries_connx' => [2, 3, 4, 5], + 'mqseries_disc' => [2, 3], + 'mqseries_get' => [3, 4, 5, 6, 7, 8, 9], + 'mqseries_inq' => [6, 8, 9, 10], + 'mqseries_open' => [2, 4, 5, 6], + 'mqseries_put' => [3, 4, 6, 7], + 'mqseries_put1' => [2, 3, 4, 6, 7], + 'mqseries_set' => [9, 10], + 'msg_receive' => [3, 5, 8], + 'msg_send' => [6], + 'mssql_bind' => [3], + 'natcasesort' => [1], + 'natsort' => [1], + 'ncurses_color_content' => [2, 3, 4], + 'ncurses_getmaxyx' => [2, 3], + 'ncurses_getmouse' => [1], + 'ncurses_getyx' => [2, 3], + 'ncurses_instr' => [1], + 'ncurses_mouse_trafo' => [1, 2], + 'ncurses_mousemask' => [2], + 'ncurses_pair_content' => [2, 3], + 'ncurses_wmouse_trafo' => [2, 3], + 'newt_button_bar' => [1], + 'newt_form_run' => [2], + 'newt_get_screen_size' => [1, 2], + 'newt_grid_get_size' => [2, 3], + 'newt_reflow_text' => [5, 6], + 'newt_win_entries' => [7], + 'newt_win_menu' => [8], + 'next' => [1], + 'oci_bind_array_by_name' => [3], + 'oci_bind_by_name' => [3], + 'oci_define_by_name' => [3], + 'oci_fetch_all' => [2], + 'ocifetchinto' => [2], + 'odbc_fetch_into' => [2], + 'openssl_csr_export' => [2], + 'openssl_csr_new' => [2], + 'openssl_open' => [2], + 'openssl_pkcs12_export' => [2], + 'openssl_pkcs12_read' => [2], + 'openssl_pkey_export' => [2], + 'openssl_private_decrypt' => [2], + 'openssl_private_encrypt' => [2], + 'openssl_public_decrypt' => [2], + 'openssl_public_encrypt' => [2], + 'openssl_random_pseudo_bytes' => [2], + 'openssl_seal' => [2, 3], + 'openssl_sign' => [2], + 'openssl_x509_export' => [2], + 'ovrimos_fetch_into' => [2], + 'parse' => [2,3], + 'parseCurrency' => [2, 3], + 'parse_str' => [2], + 'parsekit_compile_file' => [2], + 'parsekit_compile_string' => [2], + 'passthru' => [2], + 'pcntl_sigprocmask' => [3], + 'pcntl_sigtimedwait' => [2], + 'pcntl_sigwaitinfo' => [2], + 'pcntl_wait' => [1], + 'pcntl_waitpid' => [2], + 'pfsockopen' => [3, 4], + 'php_check_syntax' => [2], + 'poll' => [1, 2, 3], + 'preg_filter' => [5], + 'preg_match' => [3], + 'preg_match_all' => [3], + 'preg_replace' => [5], + 'preg_replace_callback' => [5], + 'prev' => [1], + 'proc_open' => [3], + 'query' => [3], + 'queryExec' => [2], + 'reset' => [1], + 'rsort' => [1], + 'settype' => [1], + 'shuffle' => [1], + 'similar_text' => [3], + 'socket_create_pair' => [4], + 'socket_getpeername' => [2, 3], + 'socket_getsockname' => [2, 3], + 'socket_recv' => [2], + 'socket_recvfrom' => [2, 5, 6], + 'socket_select' => [1, 2, 3], + 'sort' => [1], + 'sortWithSortKeys' => [1], + 'sqlite_exec' => [3], + 'sqlite_factory' => [3], + 'sqlite_open' => [3], + 'sqlite_popen' => [3], + 'sqlite_query' => [4], + 'sqlite_unbuffered_query' => [4], + 'sscanf' => [3, '...'], + 'str_ireplace' => [4], + 'str_replace' => [4], + 'stream_open' => [4], + 'stream_select' => [1, 2, 3], + 'stream_socket_accept' => [3], + 'stream_socket_client' => [2, 3], + 'stream_socket_recvfrom' => [4], + 'stream_socket_server' => [2, 3], + 'system' => [2], + 'uasort' => [1], + 'uksort' => [1], + 'unbufferedQuery' => [3], + 'usort' => [1], + 'wincache_ucache_dec' => [3], + 'wincache_ucache_get' => [2], + 'wincache_ucache_inc' => [3], + 'xdiff_string_merge3' => [4], + 'xdiff_string_patch' => [4], + 'xml_parse_into_struct' => [3, 4], + 'xml_set_object' => [2], + 'xmlrpc_decode_request' => [2], + 'xmlrpc_set_type' => [1], + 'xslt_set_object' => [2], + 'yaml_parse' => [3], + 'yaml_parse_file' => [3], + 'yaml_parse_url' => [3], + 'yaz_ccl_parse' => [3], + 'yaz_hits' => [2], + 'yaz_scan_result' => [2], + 'yaz_wait' => [1], + ]; + } + + /** + * @return array> + */ + public static function getWordPressPassByReferenceFunctions() + { + return [ + 'wp_parse_str' => [2], + 'wp_cache_get' => [4], + ]; + } + + /** + * A regexp for matching variable names in double-quoted strings. + * + * @return string + */ + public static function getDoubleQuotedVarRegexp() + { + return '|(?enumIndex = $enumIndex; + $this->blockStart = $blockStart; + $this->blockEnd = $blockEnd; + } +} diff --git a/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ForLoopInfo.php b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ForLoopInfo.php new file mode 100644 index 00000000..e067873c --- /dev/null +++ b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ForLoopInfo.php @@ -0,0 +1,114 @@ + + */ + public $incrementVariables = []; + + /** + * @param int $forIndex + * @param int $blockStart + * @param int $blockEnd + * @param int $initStart + * @param int $initEnd + * @param int $conditionStart + * @param int $conditionEnd + * @param int $incrementStart + * @param int $incrementEnd + */ + public function __construct( + $forIndex, + $blockStart, + $blockEnd, + $initStart, + $initEnd, + $conditionStart, + $conditionEnd, + $incrementStart, + $incrementEnd + ) { + $this->forIndex = $forIndex; + $this->blockStart = $blockStart; + $this->blockEnd = $blockEnd; + $this->initStart = $initStart; + $this->initEnd = $initEnd; + $this->conditionStart = $conditionStart; + $this->conditionEnd = $conditionEnd; + $this->incrementStart = $incrementStart; + $this->incrementEnd = $incrementEnd; + } +} diff --git a/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/Helpers.php b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/Helpers.php new file mode 100644 index 00000000..f0cc6d5f --- /dev/null +++ b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/Helpers.php @@ -0,0 +1,1599 @@ + + */ + public static function getPossibleEndOfFileTokens() + { + return array_merge( + array_values(Tokens::$emptyTokens), + [ + T_INLINE_HTML, + T_CLOSE_TAG, + ] + ); + } + + /** + * @param int|bool $value + * + * @return ?int + */ + public static function getIntOrNull($value) + { + return is_int($value) ? $value : null; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return ?int + */ + public static function findContainingOpeningSquareBracket(File $phpcsFile, $stackPtr) + { + $previousStatementPtr = self::getPreviousStatementPtr($phpcsFile, $stackPtr); + return self::getIntOrNull($phpcsFile->findPrevious([T_OPEN_SHORT_ARRAY, T_OPEN_SQUARE_BRACKET], $stackPtr - 1, $previousStatementPtr)); + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return int + */ + public static function getPreviousStatementPtr(File $phpcsFile, $stackPtr) + { + $result = $phpcsFile->findPrevious([T_SEMICOLON, T_CLOSE_CURLY_BRACKET], $stackPtr - 1); + return is_bool($result) ? 1 : $result; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return ?int + */ + public static function findContainingOpeningBracket(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + if (isset($tokens[$stackPtr]['nested_parenthesis'])) { + /** + * @var array + */ + $openPtrs = array_keys($tokens[$stackPtr]['nested_parenthesis']); + return (int)end($openPtrs); + } + return null; + } + + /** + * @param array{conditions: (int|string)[], content: string} $token + * + * @return bool + */ + public static function areAnyConditionsAClass(array $token) + { + $conditions = $token['conditions']; + $classlikeCodes = [T_CLASS, T_ANON_CLASS, T_TRAIT]; + if (defined('T_ENUM')) { + $classlikeCodes[] = T_ENUM; + } + $classlikeCodes[] = 'PHPCS_T_ENUM'; + foreach (array_reverse($conditions, true) as $scopeCode) { + if (in_array($scopeCode, $classlikeCodes, true)) { + return true; + } + } + return false; + } + + /** + * Return true if the token conditions are within a function before they are + * within a class. + * + * @param array{conditions: (int|string)[], content: string} $token + * + * @return bool + */ + public static function areConditionsWithinFunctionBeforeClass(array $token) + { + $conditions = $token['conditions']; + $classlikeCodes = [T_CLASS, T_ANON_CLASS, T_TRAIT]; + if (defined('T_ENUM')) { + $classlikeCodes[] = T_ENUM; + } + $classlikeCodes[] = 'PHPCS_T_ENUM'; + foreach (array_reverse($conditions, true) as $scopeCode) { + if (in_array($scopeCode, $classlikeCodes)) { + return false; + } + if ($scopeCode === T_FUNCTION) { + return true; + } + } + return false; + } + + /** + * Return true if the token conditions are within an if block before they are + * within a class or function. + * + * @param (int|string)[] $conditions + * + * @return int|string|null + */ + public static function getClosestIfPositionIfBeforeOtherConditions(array $conditions) + { + $conditionsInsideOut = array_reverse($conditions, true); + if (empty($conditions)) { + return null; + } + $scopeCode = reset($conditionsInsideOut); + if ($scopeCode === T_IF) { + return key($conditionsInsideOut); + } + return null; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isTokenFunctionParameter(File $phpcsFile, $stackPtr) + { + return is_int(self::getFunctionIndexForFunctionParameter($phpcsFile, $stackPtr)); + } + + /** + * Return true if the token is inside the arguments of a function call. + * + * For example, the variable `$foo` in `doSomething($foo)` is inside the + * arguments to the call to `doSomething()`. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isTokenInsideFunctionCallArgument(File $phpcsFile, $stackPtr) + { + return is_int(self::getFunctionIndexForFunctionCallArgument($phpcsFile, $stackPtr)); + } + + /** + * Find the index of the function keyword for a token in a function + * definition's parameters. + * + * Does not work for tokens inside the "use". + * + * Will also work for the parenthesis that make up the function definition's + * parameters list. + * + * For arguments inside a function call, rather than a definition, use + * `getFunctionIndexForFunctionCallArgument`. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return ?int + */ + public static function getFunctionIndexForFunctionParameter(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $token = $tokens[$stackPtr]; + if ($token['code'] === 'PHPCS_T_OPEN_PARENTHESIS') { + $startOfArguments = $stackPtr; + } elseif ($token['code'] === 'PHPCS_T_CLOSE_PARENTHESIS') { + if (empty($token['parenthesis_opener'])) { + return null; + } + $startOfArguments = $token['parenthesis_opener']; + } else { + if (empty($token['nested_parenthesis'])) { + return null; + } + $startingParenthesis = array_keys($token['nested_parenthesis']); + $startOfArguments = end($startingParenthesis); + } + + if (! is_int($startOfArguments)) { + return null; + } + + $nonFunctionTokenTypes = Tokens::$emptyTokens; + $nonFunctionTokenTypes[] = T_STRING; + $nonFunctionTokenTypes[] = T_BITWISE_AND; + $functionPtr = self::getIntOrNull($phpcsFile->findPrevious($nonFunctionTokenTypes, $startOfArguments - 1, null, true, null, true)); + if (! is_int($functionPtr)) { + return null; + } + $functionToken = $tokens[$functionPtr]; + + $functionTokenTypes = [ + T_FUNCTION, + T_CLOSURE, + ]; + if (!in_array($functionToken['code'], $functionTokenTypes, true) && ! self::isArrowFunction($phpcsFile, $functionPtr)) { + return null; + } + return $functionPtr; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isTokenInsideFunctionUseImport(File $phpcsFile, $stackPtr) + { + return is_int(self::getUseIndexForUseImport($phpcsFile, $stackPtr)); + } + + /** + * Find the token index of the "use" for a token inside a function use import + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return ?int + */ + public static function getUseIndexForUseImport(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $nonUseTokenTypes = Tokens::$emptyTokens; + $nonUseTokenTypes[] = T_VARIABLE; + $nonUseTokenTypes[] = T_ELLIPSIS; + $nonUseTokenTypes[] = T_COMMA; + $nonUseTokenTypes[] = T_BITWISE_AND; + $openParenPtr = self::getIntOrNull($phpcsFile->findPrevious($nonUseTokenTypes, $stackPtr - 1, null, true, null, true)); + if (! is_int($openParenPtr) || $tokens[$openParenPtr]['code'] !== T_OPEN_PARENTHESIS) { + return null; + } + + $usePtr = self::getIntOrNull($phpcsFile->findPrevious(array_values($nonUseTokenTypes), $openParenPtr - 1, null, true, null, true)); + if (! is_int($usePtr) || $tokens[$usePtr]['code'] !== T_USE) { + return null; + } + return $usePtr; + } + + /** + * Return the index of a function's name token from inside the function. + * + * $stackPtr must be inside the function body or parameters for this to work. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return ?int + */ + public static function findFunctionCall(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $openPtr = self::findContainingOpeningBracket($phpcsFile, $stackPtr); + if (is_int($openPtr)) { + // First non-whitespace thing and see if it's a T_STRING function name + $functionPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $openPtr - 1, null, true, null, true); + if (is_int($functionPtr) && $tokens[$functionPtr]['code'] === T_STRING) { + return $functionPtr; + } + } + return null; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return array> + */ + public static function findFunctionCallArguments(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + // Slight hack: also allow this to find args for array constructor. + if (($tokens[$stackPtr]['code'] !== T_STRING) && ($tokens[$stackPtr]['code'] !== T_ARRAY)) { + // Assume $stackPtr is something within the brackets, find our function call + $stackPtr = self::findFunctionCall($phpcsFile, $stackPtr); + if ($stackPtr === null) { + return []; + } + } + + // $stackPtr is the function name, find our brackets after it + $openPtr = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true); + if (($openPtr === false) || ($tokens[$openPtr]['code'] !== T_OPEN_PARENTHESIS)) { + return []; + } + + if (!isset($tokens[$openPtr]['parenthesis_closer'])) { + return []; + } + $closePtr = $tokens[$openPtr]['parenthesis_closer']; + + $argPtrs = []; + $lastPtr = $openPtr; + $lastArgComma = $openPtr; + $nextPtr = $phpcsFile->findNext([T_COMMA], $lastPtr + 1, $closePtr); + while (is_int($nextPtr)) { + if (self::findContainingOpeningBracket($phpcsFile, $nextPtr) === $openPtr) { + // Comma is at our level of brackets, it's an argument delimiter. + $range = range($lastArgComma + 1, $nextPtr - 1); + $range = array_filter($range, function ($element) { + return is_int($element); + }); + array_push($argPtrs, $range); + $lastArgComma = $nextPtr; + } + $lastPtr = $nextPtr; + $nextPtr = $phpcsFile->findNext([T_COMMA], $lastPtr + 1, $closePtr); + } + $range = range($lastArgComma + 1, $closePtr - 1); + $range = array_filter($range, function ($element) { + return is_int($element); + }); + array_push($argPtrs, $range); + + return $argPtrs; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return ?int + */ + public static function getNextAssignPointer(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + // Is the next non-whitespace an assignment? + $nextPtr = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true); + if (is_int($nextPtr) + && isset(Tokens::$assignmentTokens[$tokens[$nextPtr]['code']]) + // Ignore double arrow to prevent triggering on `foreach ( $array as $k => $v )`. + && $tokens[$nextPtr]['code'] !== T_DOUBLE_ARROW + ) { + return $nextPtr; + } + return null; + } + + /** + * @param string $varName + * + * @return string + */ + public static function normalizeVarName($varName) + { + $result = preg_replace('/[{}$]/', '', $varName); + return $result ? $result : $varName; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName (optional) if it differs from the normalized 'content' of the token at $stackPtr + * + * @return ?int + */ + public static function findVariableScope(File $phpcsFile, $stackPtr, $varName = null) + { + $tokens = $phpcsFile->getTokens(); + $token = $tokens[$stackPtr]; + $varName = isset($varName) ? $varName : self::normalizeVarName($token['content']); + + $arrowFunctionIndex = self::getContainingArrowFunctionIndex($phpcsFile, $stackPtr); + $isTokenInsideArrowFunctionBody = is_int($arrowFunctionIndex); + if ($isTokenInsideArrowFunctionBody) { + // Get the list of variables defined by the arrow function + // If this matches any of them, the scope is the arrow function, + // otherwise, it uses the enclosing scope. + if ($arrowFunctionIndex) { + $variableNames = self::getVariablesDefinedByArrowFunction($phpcsFile, $arrowFunctionIndex); + self::debug('findVariableScope: looking for', $varName, 'in arrow function variables', $variableNames); + if (in_array($varName, $variableNames, true)) { + return $arrowFunctionIndex; + } + } + } + + return self::findVariableScopeExceptArrowFunctions($phpcsFile, $stackPtr); + } + + /** + * Return the token index of the scope start for a token + * + * For a variable within a function body, or a variable within a function + * definition argument list, this will return the function keyword's index. + * + * For a variable within a "use" import list within a function definition, + * this will return the enclosing scope, not the function keyword. This is + * important to note because the "use" keyword performs double-duty, defining + * variables for the function's scope, and consuming the variables in the + * enclosing scope. Use `getUseIndexForUseImport` to determine if this + * token needs to be treated as a "use". + * + * For a variable within an arrow function definition argument list, + * this will return the arrow function's keyword index. + * + * For a variable in an arrow function body, this will return the enclosing + * function's index, which may be incorrect. + * + * Since a variable in an arrow function's body may be imported from the + * enclosing scope, it's important to test to see if the variable is in an + * arrow function and also check its enclosing scope separately. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return ?int + */ + public static function findVariableScopeExceptArrowFunctions(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $allowedTypes = [ + T_VARIABLE, + T_DOUBLE_QUOTED_STRING, + T_HEREDOC, + T_STRING, + ]; + if (! in_array($tokens[$stackPtr]['code'], $allowedTypes, true)) { + throw new \Exception("Cannot find variable scope for non-variable {$tokens[$stackPtr]['type']}"); + } + + $startOfTokenScope = self::getStartOfTokenScope($phpcsFile, $stackPtr); + if (is_int($startOfTokenScope) && $startOfTokenScope > 0) { + return $startOfTokenScope; + } + + // If there is no "conditions" array, this is a function definition argument. + if (self::isTokenFunctionParameter($phpcsFile, $stackPtr)) { + $functionPtr = self::getFunctionIndexForFunctionParameter($phpcsFile, $stackPtr); + if (! is_int($functionPtr)) { + throw new \Exception("Function index not found for function argument index {$stackPtr}"); + } + return $functionPtr; + } + + self::debug('Cannot find function scope for variable at', $stackPtr); + return $startOfTokenScope; + } + + /** + * Return the token index of the scope start for a variable token + * + * This will only work for a variable within a function's body. Otherwise, + * see `findVariableScope`, which is more complex. + * + * Note that if used on a variable in an arrow function, it will return the + * enclosing function's scope, which may be incorrect. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return ?int + */ + private static function getStartOfTokenScope(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $token = $tokens[$stackPtr]; + + $inClass = false; + $conditions = isset($token['conditions']) ? $token['conditions'] : []; + $functionTokenTypes = [ + T_FUNCTION, + T_CLOSURE, + ]; + foreach (array_reverse($conditions, true) as $scopePtr => $scopeCode) { + if (in_array($scopeCode, $functionTokenTypes, true) || self::isArrowFunction($phpcsFile, $scopePtr)) { + return $scopePtr; + } + if (isset(Tokens::$ooScopeTokens[$scopeCode]) === true) { + $inClass = true; + } + } + + if ($inClass) { + // If this is inside a class and not inside a function, this is either a + // class member variable definition, or a function argument. If it is a + // variable definition, it has no scope on its own (it can only be used + // with an object reference). If it is a function argument, we need to do + // more work (see `findVariableScopeExceptArrowFunctions`). + return null; + } + + // If we can't find a scope, let's use the first token of the file. + return 0; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isTokenInsideArrowFunctionDefinition(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $token = $tokens[$stackPtr]; + $openParenIndices = isset($token['nested_parenthesis']) ? $token['nested_parenthesis'] : []; + if (empty($openParenIndices)) { + return false; + } + $openParenPtr = $openParenIndices[0]; + return self::isArrowFunction($phpcsFile, $openParenPtr - 1); + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return ?int + */ + public static function getContainingArrowFunctionIndex(File $phpcsFile, $stackPtr) + { + $arrowFunctionIndex = self::getPreviousArrowFunctionIndex($phpcsFile, $stackPtr); + if (! is_int($arrowFunctionIndex)) { + return null; + } + $arrowFunctionInfo = self::getArrowFunctionOpenClose($phpcsFile, $arrowFunctionIndex); + if (! $arrowFunctionInfo) { + return null; + } + $arrowFunctionScopeStart = $arrowFunctionInfo['scope_opener']; + $arrowFunctionScopeEnd = $arrowFunctionInfo['scope_closer']; + if ($stackPtr > $arrowFunctionScopeStart && $stackPtr < $arrowFunctionScopeEnd) { + return $arrowFunctionIndex; + } + return null; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return ?int + */ + private static function getPreviousArrowFunctionIndex(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $enclosingScopeIndex = self::findVariableScopeExceptArrowFunctions($phpcsFile, $stackPtr); + for ($index = $stackPtr - 1; $index > $enclosingScopeIndex; $index--) { + $token = $tokens[$index]; + if ($token['content'] === 'fn' && self::isArrowFunction($phpcsFile, $index)) { + return $index; + } + } + return null; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isArrowFunction(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + if (defined('T_FN') && $tokens[$stackPtr]['code'] === T_FN) { + return true; + } + if ($tokens[$stackPtr]['content'] !== 'fn') { + return false; + } + // Make sure next non-space token is an open parenthesis + $openParenIndex = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true); + if (! is_int($openParenIndex) || $tokens[$openParenIndex]['code'] !== T_OPEN_PARENTHESIS) { + return false; + } + // Find the associated close parenthesis + $closeParenIndex = $tokens[$openParenIndex]['parenthesis_closer']; + // Make sure the next token is a fat arrow + $fatArrowIndex = $phpcsFile->findNext(Tokens::$emptyTokens, $closeParenIndex + 1, null, true); + if (! is_int($fatArrowIndex)) { + return false; + } + if ($tokens[$fatArrowIndex]['code'] !== T_DOUBLE_ARROW && $tokens[$fatArrowIndex]['type'] !== 'T_FN_ARROW') { + return false; + } + return true; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return ?array + */ + public static function getArrowFunctionOpenClose(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + if ($tokens[$stackPtr]['content'] !== 'fn') { + return null; + } + // Make sure next non-space token is an open parenthesis + $openParenIndex = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true); + if (! is_int($openParenIndex) || $tokens[$openParenIndex]['code'] !== T_OPEN_PARENTHESIS) { + return null; + } + // Find the associated close parenthesis + $closeParenIndex = $tokens[$openParenIndex]['parenthesis_closer']; + // Make sure the next token is a fat arrow or a return type + $fatArrowIndex = $phpcsFile->findNext(Tokens::$emptyTokens, $closeParenIndex + 1, null, true); + if (! is_int($fatArrowIndex)) { + return null; + } + if ( + $tokens[$fatArrowIndex]['code'] !== T_DOUBLE_ARROW && + $tokens[$fatArrowIndex]['type'] !== 'T_FN_ARROW' && + $tokens[$fatArrowIndex]['code'] !== T_COLON + ) { + return null; + } + + // Find the scope closer + $scopeCloserIndex = null; + $foundCurlyPairs = 0; + $foundArrayPairs = 0; + $foundParenPairs = 0; + $arrowBodyStart = $tokens[$stackPtr]['parenthesis_closer'] + 1; + $lastToken = self::getLastNonEmptyTokenIndexInFile($phpcsFile); + for ($index = $arrowBodyStart; $index < $lastToken; $index++) { + $token = $tokens[$index]; + if (empty($token['code'])) { + $scopeCloserIndex = $index; + break; + } + + $code = $token['code']; + + // A semicolon is always a closer. + if ($code === T_SEMICOLON) { + $scopeCloserIndex = $index; + break; + } + + // Track pair opening tokens. + if ($code === T_OPEN_CURLY_BRACKET) { + $foundCurlyPairs += 1; + continue; + } + if ($code === T_OPEN_SHORT_ARRAY || $code === T_OPEN_SQUARE_BRACKET) { + $foundArrayPairs += 1; + continue; + } + if ($code === T_OPEN_PARENTHESIS) { + $foundParenPairs += 1; + continue; + } + + // A pair closing is only an arrow func closer if there was no matching opening token. + if ($code === T_CLOSE_CURLY_BRACKET) { + if ($foundCurlyPairs === 0) { + $scopeCloserIndex = $index; + break; + } + $foundCurlyPairs -= 1; + continue; + } + if ($code === T_CLOSE_SHORT_ARRAY || $code === T_CLOSE_SQUARE_BRACKET) { + if ($foundArrayPairs === 0) { + $scopeCloserIndex = $index; + break; + } + $foundArrayPairs -= 1; + continue; + } + if ($code === T_CLOSE_PARENTHESIS) { + if ($foundParenPairs === 0) { + $scopeCloserIndex = $index; + break; + } + $foundParenPairs -= 1; + continue; + } + + // A comma is a closer only if we are not inside an opening token. + if ($code === T_COMMA) { + if (empty($foundArrayPairs) && empty($foundParenPairs) && empty($foundCurlyPairs)) { + $scopeCloserIndex = $index; + break; + } + continue; + } + } + + if (! is_int($scopeCloserIndex)) { + return null; + } + + return [ + 'scope_opener' => $stackPtr, + 'scope_closer' => $scopeCloserIndex, + ]; + } + + /** + * Determine if a token is a list opener for list assignment/destructuring. + * + * The index provided can be either the opening square brace of a short list + * assignment like the first character of `[$a] = $b;` or the `list` token of + * an expression like `list($a) = $b;` or the opening parenthesis of that + * expression. + * + * @param File $phpcsFile + * @param int $listOpenerIndex + * + * @return bool + */ + private static function isListAssignment(File $phpcsFile, $listOpenerIndex) + { + $tokens = $phpcsFile->getTokens(); + // Match `[$a] = $b;` except for when the previous token is a parenthesis. + if ($tokens[$listOpenerIndex]['code'] === T_OPEN_SHORT_ARRAY) { + return true; + } + // Match `list($a) = $b;` + if ($tokens[$listOpenerIndex]['code'] === T_LIST) { + return true; + } + + // If $listOpenerIndex is the open parenthesis of `list($a) = $b;`, then + // match that too. + if ($tokens[$listOpenerIndex]['code'] === T_OPEN_PARENTHESIS) { + $previousTokenPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $listOpenerIndex - 1, null, true); + if ( + isset($tokens[$previousTokenPtr]) + && $tokens[$previousTokenPtr]['code'] === T_LIST + ) { + return true; + } + return true; + } + + // If the list opener token is a square bracket that is preceeded by a + // close parenthesis that has an owner which is a scope opener, then this + // is a list assignment and not an array access. + // + // Match `if (true) [$a] = $b;` + if ($tokens[$listOpenerIndex]['code'] === T_OPEN_SQUARE_BRACKET) { + $previousTokenPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $listOpenerIndex - 1, null, true); + if ( + isset($tokens[$previousTokenPtr]) + && $tokens[$previousTokenPtr]['code'] === T_CLOSE_PARENTHESIS + && isset($tokens[$previousTokenPtr]['parenthesis_owner']) + && isset(Tokens::$scopeOpeners[$tokens[$tokens[$previousTokenPtr]['parenthesis_owner']]['code']]) + ) { + return true; + } + } + + return false; + } + + /** + * Return a list of indices for variables assigned within a list assignment. + * + * The index provided can be either the opening square brace of a short list + * assignment like the first character of `[$a] = $b;` or the `list` token of + * an expression like `list($a) = $b;` or the opening parenthesis of that + * expression. + * + * @param File $phpcsFile + * @param int $listOpenerIndex + * + * @return ?array + */ + public static function getListAssignments(File $phpcsFile, $listOpenerIndex) + { + $tokens = $phpcsFile->getTokens(); + self::debug('getListAssignments', $listOpenerIndex, $tokens[$listOpenerIndex]); + + // First find the end of the list + $closePtr = null; + if (isset($tokens[$listOpenerIndex]['parenthesis_closer'])) { + $closePtr = $tokens[$listOpenerIndex]['parenthesis_closer']; + } + if (isset($tokens[$listOpenerIndex]['bracket_closer'])) { + $closePtr = $tokens[$listOpenerIndex]['bracket_closer']; + } + if (! $closePtr) { + return null; + } + + // Find the assignment (equals sign) which, if this is a list assignment, should be the next non-space token + $assignPtr = $phpcsFile->findNext(Tokens::$emptyTokens, $closePtr + 1, null, true); + + // If the next token isn't an assignment, check for nested brackets because we might be a nested assignment + if (! is_int($assignPtr) || $tokens[$assignPtr]['code'] !== T_EQUAL) { + // Collect the enclosing list open/close tokens ($parents is an assoc array keyed by opener index and the value is the closer index) + $parents = isset($tokens[$listOpenerIndex]['nested_parenthesis']) ? $tokens[$listOpenerIndex]['nested_parenthesis'] : []; + // There's no record of nested brackets for short lists; we'll have to find the parent ourselves + if (empty($parents)) { + $parentSquareBracket = self::findContainingOpeningSquareBracket($phpcsFile, $listOpenerIndex); + if (is_int($parentSquareBracket)) { + // Collect the opening index, but we don't actually need the closing paren index so just make that 0 + $parents = [$parentSquareBracket => 0]; + } + } + // If we have no parents, this is not a nested assignment and therefore is not an assignment + if (empty($parents)) { + return null; + } + + // Recursively check to see if the parent is a list assignment (we only need to check one level due to the recursion) + $isNestedAssignment = null; + $parentListOpener = array_keys(array_reverse($parents, true))[0]; + $isNestedAssignment = self::getListAssignments($phpcsFile, $parentListOpener); + if ($isNestedAssignment === null) { + return null; + } + } + + $variablePtrs = []; + + $currentPtr = $listOpenerIndex; + $variablePtr = 0; + while ($currentPtr < $closePtr && is_int($variablePtr)) { + $variablePtr = $phpcsFile->findNext([T_VARIABLE], $currentPtr + 1, $closePtr); + if (is_int($variablePtr)) { + $variablePtrs[] = $variablePtr; + } + ++$currentPtr; + } + + if (! self::isListAssignment($phpcsFile, $listOpenerIndex)) { + return null; + } + + return $variablePtrs; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return string[] + */ + public static function getVariablesDefinedByArrowFunction(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $arrowFunctionToken = $tokens[$stackPtr]; + $variableNames = []; + self::debug('looking for variables in arrow function token', $arrowFunctionToken); + for ($index = $arrowFunctionToken['parenthesis_opener']; $index < $arrowFunctionToken['parenthesis_closer']; $index++) { + $token = $tokens[$index]; + if ($token['code'] === T_VARIABLE) { + $variableNames[] = self::normalizeVarName($token['content']); + } + } + self::debug('found these variables in arrow function token', $variableNames); + return $variableNames; + } + + /** + * @return void + */ + public static function debug() + { + $messages = func_get_args(); + if (! defined('PHP_CODESNIFFER_VERBOSITY')) { + return; + } + if (PHP_CODESNIFFER_VERBOSITY <= 3) { + return; + } + $output = PHP_EOL . 'VariableAnalysisSniff: DEBUG:'; + foreach ($messages as $message) { + if (is_string($message) || is_numeric($message)) { + $output .= ' "' . $message . '"'; + continue; + } + $output .= PHP_EOL . var_export($message, true) . PHP_EOL; + } + $output .= PHP_EOL; + echo $output; + } + + /** + * @param string $pattern + * @param string $value + * + * @return string[] + */ + public static function splitStringToArray($pattern, $value) + { + $result = preg_split($pattern, $value); + return is_array($result) ? $result : []; + } + + /** + * @param string $varName + * + * @return bool + */ + public static function isVariableANumericVariable($varName) + { + return is_numeric(substr($varName, 0, 1)); + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isVariableInsideElseCondition(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $nonFunctionTokenTypes = Tokens::$emptyTokens; + $nonFunctionTokenTypes[] = T_OPEN_PARENTHESIS; + $nonFunctionTokenTypes[] = T_INLINE_HTML; + $nonFunctionTokenTypes[] = T_CLOSE_TAG; + $nonFunctionTokenTypes[] = T_VARIABLE; + $nonFunctionTokenTypes[] = T_ELLIPSIS; + $nonFunctionTokenTypes[] = T_COMMA; + $nonFunctionTokenTypes[] = T_STRING; + $nonFunctionTokenTypes[] = T_BITWISE_AND; + $elsePtr = self::getIntOrNull($phpcsFile->findPrevious($nonFunctionTokenTypes, $stackPtr - 1, null, true, null, true)); + $elseTokenTypes = [ + T_ELSE, + T_ELSEIF, + ]; + if (is_int($elsePtr) && in_array($tokens[$elsePtr]['code'], $elseTokenTypes, true)) { + return true; + } + return false; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isVariableInsideElseBody(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $token = $tokens[$stackPtr]; + $conditions = isset($token['conditions']) ? $token['conditions'] : []; + $elseTokenTypes = [ + T_ELSE, + T_ELSEIF, + ]; + foreach (array_reverse($conditions, true) as $scopeCode) { + if (in_array($scopeCode, $elseTokenTypes, true)) { + return true; + } + } + + // Some else body code will not have conditions because it is inline (no + // curly braces) so we have to look in other ways. + $previousSemicolonPtr = $phpcsFile->findPrevious([T_SEMICOLON], $stackPtr - 1); + if (! is_int($previousSemicolonPtr)) { + $previousSemicolonPtr = 0; + } + $elsePtr = $phpcsFile->findPrevious([T_ELSE, T_ELSEIF], $stackPtr - 1, $previousSemicolonPtr); + if (is_int($elsePtr)) { + return true; + } + + return false; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return int[] + */ + public static function getAttachedBlockIndicesForElse(File $phpcsFile, $stackPtr) + { + $currentElsePtr = $phpcsFile->findPrevious([T_ELSE, T_ELSEIF], $stackPtr - 1); + if (! is_int($currentElsePtr)) { + throw new \Exception("Cannot find expected else at {$stackPtr}"); + } + + $ifPtr = $phpcsFile->findPrevious([T_IF], $currentElsePtr - 1); + if (! is_int($ifPtr)) { + throw new \Exception("Cannot find if for else at {$stackPtr}"); + } + $blockIndices = [$ifPtr]; + + $previousElseIfPtr = $currentElsePtr; + do { + $elseIfPtr = $phpcsFile->findPrevious([T_ELSEIF], $previousElseIfPtr - 1, $ifPtr); + if (is_int($elseIfPtr)) { + $blockIndices[] = $elseIfPtr; + $previousElseIfPtr = $elseIfPtr; + } + } while (is_int($elseIfPtr)); + + return $blockIndices; + } + + /** + * @param int $needle + * @param int $scopeStart + * @param int $scopeEnd + * + * @return bool + */ + public static function isIndexInsideScope($needle, $scopeStart, $scopeEnd) + { + return ($needle > $scopeStart && $needle < $scopeEnd); + } + + /** + * @param File $phpcsFile + * @param int $scopeStartIndex + * + * @return int + */ + public static function getScopeCloseForScopeOpen(File $phpcsFile, $scopeStartIndex) + { + $tokens = $phpcsFile->getTokens(); + $scopeCloserIndex = isset($tokens[$scopeStartIndex]['scope_closer']) ? $tokens[$scopeStartIndex]['scope_closer'] : 0; + + if (self::isArrowFunction($phpcsFile, $scopeStartIndex)) { + $arrowFunctionInfo = self::getArrowFunctionOpenClose($phpcsFile, $scopeStartIndex); + $scopeCloserIndex = $arrowFunctionInfo ? $arrowFunctionInfo['scope_closer'] : $scopeCloserIndex; + } + + if ($scopeStartIndex === 0) { + $scopeCloserIndex = self::getLastNonEmptyTokenIndexInFile($phpcsFile); + } + return $scopeCloserIndex; + } + + /** + * @param File $phpcsFile + * + * @return int + */ + public static function getLastNonEmptyTokenIndexInFile(File $phpcsFile) + { + $tokens = $phpcsFile->getTokens(); + foreach (array_reverse($tokens, true) as $index => $token) { + if (! in_array($token['code'], self::getPossibleEndOfFileTokens(), true)) { + return $index; + } + } + self::debug('no non-empty token found for end of file'); + return 0; + } + + /** + * @param VariableInfo $varInfo + * @param ScopeInfo $scopeInfo + * + * @return bool + */ + public static function areFollowingArgumentsUsed(VariableInfo $varInfo, ScopeInfo $scopeInfo) + { + $foundVarPosition = false; + foreach ($scopeInfo->variables as $variable) { + if ($variable === $varInfo) { + $foundVarPosition = true; + continue; + } + if (! $foundVarPosition) { + continue; + } + if ($variable->scopeType !== ScopeType::PARAM) { + continue; + } + if ($variable->firstRead) { + return true; + } + } + return false; + } + + /** + * @param File $phpcsFile + * @param VariableInfo $varInfo + * @param ScopeInfo $scopeInfo + * + * @return bool + */ + public static function isRequireInScopeAfter(File $phpcsFile, VariableInfo $varInfo, ScopeInfo $scopeInfo) + { + $requireTokens = [ + T_REQUIRE, + T_REQUIRE_ONCE, + T_INCLUDE, + T_INCLUDE_ONCE, + ]; + $indexToStartSearch = $varInfo->firstDeclared; + if (! empty($varInfo->firstInitialized)) { + $indexToStartSearch = $varInfo->firstInitialized; + } + $tokens = $phpcsFile->getTokens(); + $indexToStopSearch = isset($tokens[$scopeInfo->scopeStartIndex]['scope_closer']) ? $tokens[$scopeInfo->scopeStartIndex]['scope_closer'] : null; + if (! is_int($indexToStartSearch) || ! is_int($indexToStopSearch)) { + return false; + } + $requireTokenIndex = $phpcsFile->findNext($requireTokens, $indexToStartSearch + 1, $indexToStopSearch); + if (is_int($requireTokenIndex)) { + return true; + } + return false; + } + + /** + * Find the index of the function keyword for a token in a function call's arguments + * + * For the variable `$foo` in the expression `doSomething($foo)`, this will + * return the index of the `doSomething` token. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return ?int + */ + public static function getFunctionIndexForFunctionCallArgument(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $token = $tokens[$stackPtr]; + if (empty($token['nested_parenthesis'])) { + return null; + } + /** + * @var array + */ + $startingParenthesis = array_keys($token['nested_parenthesis']); + $startOfArguments = end($startingParenthesis); + if (! is_int($startOfArguments)) { + return null; + } + + $nonFunctionTokenTypes = Tokens::$emptyTokens; + $functionPtr = self::getIntOrNull($phpcsFile->findPrevious($nonFunctionTokenTypes, $startOfArguments - 1, null, true, null, true)); + if (! is_int($functionPtr) || ! isset($tokens[$functionPtr]['code'])) { + return null; + } + if ( + $tokens[$functionPtr]['content'] === 'function' + || ($tokens[$functionPtr]['content'] === 'fn' && self::isArrowFunction($phpcsFile, $functionPtr)) + ) { + // If there is a function/fn keyword before the beginning of the parens, + // this is a function definition and not a function call. + return null; + } + if (! empty($tokens[$functionPtr]['scope_opener'])) { + // If the alleged function name has a scope, this is not a function call. + return null; + } + + $functionNameType = $tokens[$functionPtr]['code']; + if (! in_array($functionNameType, Tokens::$functionNameTokens, true)) { + // If the alleged function name is not a variable or a string, this is + // not a function call. + return null; + } + + if ($tokens[$functionPtr]['level'] !== $tokens[$stackPtr]['level']) { + // If the variable is inside a different scope than the function name, + // the function call doesn't apply to the variable. + return null; + } + + return $functionPtr; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isVariableInsideIssetOrEmpty(File $phpcsFile, $stackPtr) + { + $functionIndex = self::getFunctionIndexForFunctionCallArgument($phpcsFile, $stackPtr); + if (! is_int($functionIndex)) { + return false; + } + $tokens = $phpcsFile->getTokens(); + if (! isset($tokens[$functionIndex])) { + return false; + } + $allowedFunctionNames = [ + 'isset', + 'empty', + ]; + if (in_array($tokens[$functionIndex]['content'], $allowedFunctionNames, true)) { + return true; + } + return false; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isVariableArrayPushShortcut(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $nonFunctionTokenTypes = Tokens::$emptyTokens; + + $arrayPushOperatorIndex1 = self::getIntOrNull($phpcsFile->findNext($nonFunctionTokenTypes, $stackPtr + 1, null, true, null, true)); + if (! is_int($arrayPushOperatorIndex1)) { + return false; + } + if (! isset($tokens[$arrayPushOperatorIndex1]['content']) || $tokens[$arrayPushOperatorIndex1]['content'] !== '[') { + return false; + } + + $arrayPushOperatorIndex2 = self::getIntOrNull($phpcsFile->findNext($nonFunctionTokenTypes, $arrayPushOperatorIndex1 + 1, null, true, null, true)); + if (! is_int($arrayPushOperatorIndex2)) { + return false; + } + if (! isset($tokens[$arrayPushOperatorIndex2]['content']) || $tokens[$arrayPushOperatorIndex2]['content'] !== ']') { + return false; + } + + $arrayPushOperatorIndex3 = self::getIntOrNull($phpcsFile->findNext($nonFunctionTokenTypes, $arrayPushOperatorIndex2 + 1, null, true, null, true)); + if (! is_int($arrayPushOperatorIndex3)) { + return false; + } + if (! isset($tokens[$arrayPushOperatorIndex3]['content']) || $tokens[$arrayPushOperatorIndex3]['content'] !== '=') { + return false; + } + + return true; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isVariableInsideUnset(File $phpcsFile, $stackPtr) + { + $functionIndex = self::getFunctionIndexForFunctionCallArgument($phpcsFile, $stackPtr); + if (! is_int($functionIndex)) { + return false; + } + $tokens = $phpcsFile->getTokens(); + if (! isset($tokens[$functionIndex])) { + return false; + } + if ($tokens[$functionIndex]['content'] === 'unset') { + return true; + } + return false; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isTokenInsideAssignmentRHS(File $phpcsFile, $stackPtr) + { + $previousStatementPtr = $phpcsFile->findPrevious([T_SEMICOLON, T_CLOSE_CURLY_BRACKET, T_OPEN_CURLY_BRACKET, T_COMMA], $stackPtr - 1); + if (! is_int($previousStatementPtr)) { + $previousStatementPtr = 1; + } + $previousTokenPtr = $phpcsFile->findPrevious([T_EQUAL], $stackPtr - 1, $previousStatementPtr); + if (is_int($previousTokenPtr)) { + return true; + } + return false; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isTokenInsideAssignmentLHS(File $phpcsFile, $stackPtr) + { + // Is the next non-whitespace an assignment? + $assignPtr = self::getNextAssignPointer($phpcsFile, $stackPtr); + if (! is_int($assignPtr)) { + return false; + } + + // Is this a variable variable? If so, it's not an assignment to the current variable. + if (self::isTokenVariableVariable($phpcsFile, $stackPtr)) { + self::debug('found variable variable'); + return false; + } + return true; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isTokenVariableVariable(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); + if ($prev === false) { + return false; + } + if ($tokens[$prev]['code'] === T_DOLLAR) { + return true; + } + if ($tokens[$prev]['code'] !== T_OPEN_CURLY_BRACKET) { + return false; + } + + $prevPrev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev - 1), null, true); + if ($prevPrev !== false && $tokens[$prevPrev]['code'] === T_DOLLAR) { + return true; + } + return false; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return EnumInfo|null + */ + public static function makeEnumInfo(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $token = $tokens[$stackPtr]; + + if (isset($token['scope_opener'])) { + $blockStart = $token['scope_opener']; + $blockEnd = $token['scope_closer']; + } else { + // Enums before phpcs could detect them do not have scopes so we have to + // find them ourselves. + + $blockStart = $phpcsFile->findNext([T_OPEN_CURLY_BRACKET], $stackPtr + 1); + if (! is_int($blockStart)) { + return null; + } + $blockEnd = $tokens[$blockStart]['bracket_closer']; + } + + return new EnumInfo( + $stackPtr, + $blockStart, + $blockEnd + ); + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return ForLoopInfo + */ + public static function makeForLoopInfo(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $token = $tokens[$stackPtr]; + $forIndex = $stackPtr; + $blockStart = $token['parenthesis_closer']; + if (isset($token['scope_opener'])) { + $blockStart = $token['scope_opener']; + $blockEnd = $token['scope_closer']; + } else { + // Some for loop blocks will not have scope positions because it they are + // inline (no curly braces) so we have to find the end of their scope by + // looking for the end of the next statement. + $nextSemicolonIndex = $phpcsFile->findNext([T_SEMICOLON], $token['parenthesis_closer']); + if (! is_int($nextSemicolonIndex)) { + $nextSemicolonIndex = $token['parenthesis_closer'] + 1; + } + $blockEnd = $nextSemicolonIndex; + } + $initStart = intval($token['parenthesis_opener']) + 1; + $initEnd = null; + $conditionStart = null; + $conditionEnd = null; + $incrementStart = null; + $incrementEnd = $token['parenthesis_closer'] - 1; + + $semicolonCount = 0; + $forLoopLevel = $tokens[$forIndex]['level']; + $forLoopNestedParensCount = 1; + + if (isset($tokens[$forIndex]['nested_parenthesis'])) { + $forLoopNestedParensCount = count($tokens[$forIndex]['nested_parenthesis']) + 1; + } + + for ($i = $initStart; ($i <= $incrementEnd && $semicolonCount < 2); $i++) { + if ($tokens[$i]['code'] !== T_SEMICOLON) { + continue; + } + + if ($tokens[$i]['level'] !== $forLoopLevel) { + continue; + } + + if (count($tokens[$i]['nested_parenthesis']) !== $forLoopNestedParensCount) { + continue; + } + + switch ($semicolonCount) { + case 0: + $initEnd = $i; + $conditionStart = $initEnd + 1; + break; + case 1: + $conditionEnd = $i; + $incrementStart = $conditionEnd + 1; + break; + } + $semicolonCount += 1; + } + + if ($initEnd === null || $conditionStart === null || $conditionEnd === null || $incrementStart === null) { + throw new \Exception("Cannot parse for loop at position {$forIndex}"); + } + + return new ForLoopInfo( + $forIndex, + $blockStart, + $blockEnd, + $initStart, + $initEnd, + $conditionStart, + $conditionEnd, + $incrementStart, + $incrementEnd + ); + } + + /** + * @param int $stackPtr + * @param array $forLoops + * @return ForLoopInfo|null + */ + public static function getForLoopForIncrementVariable($stackPtr, $forLoops) + { + foreach ($forLoops as $forLoop) { + if ($stackPtr > $forLoop->incrementStart && $stackPtr < $forLoop->incrementEnd) { + return $forLoop; + } + } + return null; + } + + /** + * Return true if the token looks like constructor promotion. + * + * Call on a parameter variable token only. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isConstructorPromotion(File $phpcsFile, $stackPtr) + { + $functionIndex = self::getFunctionIndexForFunctionParameter($phpcsFile, $stackPtr); + if (! $functionIndex) { + return false; + } + + $tokens = $phpcsFile->getTokens(); + + // If the previous token is a visibility keyword, this is constructor + // promotion. eg: `public $foobar`. + $prevIndex = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), $functionIndex, true); + if (! is_int($prevIndex)) { + return false; + } + $prevToken = $tokens[$prevIndex]; + if (in_array($prevToken['code'], Tokens::$scopeModifiers, true)) { + return true; + } + + // If the previous token is not a visibility keyword, but the one before it + // is, the previous token was probably a typehint and this is constructor + // promotion. eg: `public boolean $foobar`. + $prev2Index = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevIndex - 1), $functionIndex, true); + if (! is_int($prev2Index)) { + return false; + } + $prev2Token = $tokens[$prev2Index]; + if (in_array($prev2Token['code'], Tokens::$scopeModifiers, true)) { + return true; + } + + // If the previous token is not a visibility keyword, but the one two + // before it is, and one of the tokens is `readonly`, the previous token + // was probably a typehint and this is constructor promotion. eg: `public + // readonly boolean $foobar`. + $prev3Index = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev2Index - 1), $functionIndex, true); + if (! is_int($prev3Index)) { + return false; + } + $prev3Token = $tokens[$prev3Index]; + $wasPreviousReadonly = $prevToken['content'] === 'readonly' || $prev2Token['content'] === 'readonly'; + if (in_array($prev3Token['code'], Tokens::$scopeModifiers, true) && $wasPreviousReadonly) { + return true; + } + + return false; + } + + /** + * Return true if the token is inside an abstract class. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isInAbstractClass(File $phpcsFile, $stackPtr) + { + $classIndex = $phpcsFile->getCondition($stackPtr, T_CLASS); + if (! is_int($classIndex)) { + return false; + } + $classProperties = $phpcsFile->getClassProperties($classIndex); + return $classProperties['is_abstract']; + } + + /** + * Return true if the function body is empty or contains only `return;` + * + * @param File $phpcsFile + * @param int $stackPtr The index of the function keyword. + * + * @return bool + */ + public static function isFunctionBodyEmpty(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + if ($tokens[$stackPtr]['code'] !== T_FUNCTION) { + return false; + } + $functionScopeStart = $tokens[$stackPtr]['scope_opener']; + $functionScopeEnd = $tokens[$stackPtr]['scope_closer']; + $tokensToIgnore = array_merge( + Tokens::$emptyTokens, + [ + T_RETURN, + T_SEMICOLON, + T_OPEN_CURLY_BRACKET, + T_CLOSE_CURLY_BRACKET, + ] + ); + for ($i = $functionScopeStart; $i < $functionScopeEnd; $i++) { + if (! in_array($tokens[$i]['code'], $tokensToIgnore, true)) { + return false; + } + } + return true; + } +} diff --git a/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ScopeInfo.php b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ScopeInfo.php new file mode 100644 index 00000000..45d12849 --- /dev/null +++ b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ScopeInfo.php @@ -0,0 +1,40 @@ +scopeStartIndex = $scopeStartIndex; + $this->scopeEndIndex = $scopeEndIndex; + } +} diff --git a/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ScopeManager.php b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ScopeManager.php new file mode 100644 index 00000000..ae3b0297 --- /dev/null +++ b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ScopeManager.php @@ -0,0 +1,108 @@ +> + */ + private $scopes = []; + + /** + * Add a scope's start and end index to our record for the file. + * + * @param File $phpcsFile + * @param int $scopeStartIndex + * + * @return ScopeInfo + */ + public function recordScopeStartAndEnd(File $phpcsFile, $scopeStartIndex) + { + $scopeEndIndex = Helpers::getScopeCloseForScopeOpen($phpcsFile, $scopeStartIndex); + $filename = $phpcsFile->getFilename(); + if (! isset($this->scopes[$filename])) { + $this->scopes[$filename] = []; + } + Helpers::debug('recording scope for file', $filename, 'start/end', $scopeStartIndex, $scopeEndIndex); + $scope = new ScopeInfo($scopeStartIndex, $scopeEndIndex); + $this->scopes[$filename][$scopeStartIndex] = $scope; + return $scope; + } + + /** + * Return the scopes for a file. + * + * @param string $filename + * + * @return ScopeInfo[] + */ + public function getScopesForFilename($filename) + { + if (empty($this->scopes[$filename])) { + return []; + } + return array_values($this->scopes[$filename]); + } + + /** + * Return the scope for a scope start index. + * + * @param string $filename + * @param int $scopeStartIndex + * + * @return ScopeInfo|null + */ + public function getScopeForScopeStart($filename, $scopeStartIndex) + { + if (empty($this->scopes[$filename][$scopeStartIndex])) { + return null; + } + return $this->scopes[$filename][$scopeStartIndex]; + } + + /** + * Find scopes closed by a scope close index. + * + * @param string $filename + * @param int $scopeEndIndex + * + * @return ScopeInfo[] + */ + public function getScopesForScopeEnd($filename, $scopeEndIndex) + { + $scopePairsForFile = $this->getScopesForFilename($filename); + $scopeIndicesThisCloses = array_reduce( + $scopePairsForFile, + /** + * @param ScopeInfo[] $found + * @param ScopeInfo $scope + * + * @return ScopeInfo[] + */ + function ($found, $scope) use ($scopeEndIndex) { + if (! is_int($scope->scopeEndIndex)) { + Helpers::debug('No scope closer found for scope start', $scope->scopeStartIndex); + return $found; + } + + if ($scopeEndIndex === $scope->scopeEndIndex) { + $found[] = $scope; + } + return $found; + }, + [] + ); + return $scopeIndicesThisCloses; + } +} diff --git a/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ScopeType.php b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ScopeType.php new file mode 100644 index 00000000..33171e48 --- /dev/null +++ b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Lib/ScopeType.php @@ -0,0 +1,12 @@ + + */ + public static $scopeTypeDescriptions = [ + ScopeType::LOCAL => 'variable', + ScopeType::PARAM => 'function parameter', + ScopeType::STATICSCOPE => 'static variable', + ScopeType::GLOBALSCOPE => 'global variable', + ScopeType::BOUND => 'bound variable', + ]; + + /** + * @param string $varName + */ + public function __construct($varName) + { + $this->name = $varName; + } +} diff --git a/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php new file mode 100644 index 00000000..14295620 --- /dev/null +++ b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php @@ -0,0 +1,2111 @@ + + */ + private $forLoops = []; + + /** + * A list of enum blocks, keyed by the index of their first token in this file. + * + * @var array + */ + private $enums = []; + + /** + * A list of custom functions which pass in variables to be initialized by + * reference (eg `preg_match()`) and therefore should not require those + * variables to be defined ahead of time. The list is space separated and + * each entry is of the form `functionName:1,2`. The function name comes + * first followed by a colon and a comma-separated list of argument numbers + * (starting from 1) which should be considered variable definitions. The + * special value `...` in the arguments list will cause all arguments after + * the last number to be considered variable definitions. + * + * @var string|null + */ + public $sitePassByRefFunctions = null; + + /** + * If set, allows common WordPress pass-by-reference functions in addition to + * the standard PHP ones. + * + * @var bool + */ + public $allowWordPressPassByRefFunctions = false; + + /** + * Allow exceptions in a catch block to be unused without warning. + * + * @var bool + */ + public $allowUnusedCaughtExceptions = true; + + /** + * Allow function parameters to be unused without provoking unused-var warning. + * + * @var bool + */ + public $allowUnusedFunctionParameters = false; + + /** + * If set, ignores undefined variables in the file scope (the top-level + * scope of a file). + * + * @var bool + */ + public $allowUndefinedVariablesInFileScope = false; + + /** + * If set, ignores unused variables in the file scope (the top-level + * scope of a file). + * + * @var bool + */ + public $allowUnusedVariablesInFileScope = false; + + /** + * A space-separated list of names of placeholder variables that you want to + * ignore from unused variable warnings. For example, to ignore the variables + * `$junk` and `$unused`, this could be set to `'junk unused'`. + * + * @var string|null + */ + public $validUnusedVariableNames = null; + + /** + * A PHP regexp string for variables that you want to ignore from unused + * variable warnings. For example, to ignore the variables `$_junk` and + * `$_unused`, this could be set to `'/^_/'`. + * + * @var string|null + */ + public $ignoreUnusedRegexp = null; + + /** + * A space-separated list of names of placeholder variables that you want to + * ignore from undefined variable warnings. For example, to ignore the variables + * `$post` and `$undefined`, this could be set to `'post undefined'`. + * + * @var string|null + */ + public $validUndefinedVariableNames = null; + + /** + * A PHP regexp string for variables that you want to ignore from undefined + * variable warnings. For example, to ignore the variables `$_junk` and + * `$_unused`, this could be set to `'/^_/'`. + * + * @var string|null + */ + public $validUndefinedVariableRegexp = null; + + /** + * Allows unused arguments in a function definition if they are + * followed by an argument which is used. + * + * @var bool + */ + public $allowUnusedParametersBeforeUsed = true; + + /** + * If set to true, unused values from the `key => value` syntax + * in a `foreach` loop will never be marked as unused. + * + * @var bool + */ + public $allowUnusedForeachVariables = true; + + /** + * If set to true, unused variables in a function before a require or import + * statement will not be marked as unused because they may be used in the + * required file. + * + * @var bool + */ + public $allowUnusedVariablesBeforeRequire = false; + + public function __construct() + { + $this->scopeManager = new ScopeManager(); + } + + /** + * Decide which tokens to scan. + * + * @return (int|string)[] + */ + public function register() + { + $types = [ + T_VARIABLE, + T_DOUBLE_QUOTED_STRING, + T_HEREDOC, + T_CLOSE_CURLY_BRACKET, + T_FUNCTION, + T_CLOSURE, + T_STRING, + T_COMMA, + T_SEMICOLON, + T_CLOSE_PARENTHESIS, + T_FOR, + T_ENDFOR, + ]; + if (defined('T_FN')) { + $types[] = T_FN; + } + if (defined('T_ENUM')) { + $types[] = T_ENUM; + } + return $types; + } + + /** + * @param string $functionName + * + * @return array + */ + private function getPassByReferenceFunction($functionName) + { + $passByRefFunctions = Constants::getPassByReferenceFunctions(); + if (!empty($this->sitePassByRefFunctions)) { + $lines = Helpers::splitStringToArray('/\s+/', trim($this->sitePassByRefFunctions)); + foreach ($lines as $line) { + list ($function, $args) = explode(':', $line); + $passByRefFunctions[$function] = explode(',', $args); + } + } + if ($this->allowWordPressPassByRefFunctions) { + $passByRefFunctions = array_merge($passByRefFunctions, Constants::getWordPressPassByReferenceFunctions()); + } + return isset($passByRefFunctions[$functionName]) ? $passByRefFunctions[$functionName] : []; + } + + /** + * Scan and process a token. + * + * This is the main processing function of the sniff. Will run on every token + * for which `register()` returns true. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $scopeStartTokenTypes = [ + T_FUNCTION, + T_CLOSURE, + ]; + + $token = $tokens[$stackPtr]; + + // Cache the current PHPCS File in an instance variable so it can be more + // easily accessed in other places which aren't passed the object. + if ($this->currentFile !== $phpcsFile) { + $this->currentFile = $phpcsFile; + $this->forLoops = []; + $this->enums = []; + } + + // Add the global scope for the current file to our scope indexes. + $scopesForFilename = $this->scopeManager->getScopesForFilename($phpcsFile->getFilename()); + if (empty($scopesForFilename)) { + $this->scopeManager->recordScopeStartAndEnd($phpcsFile, 0); + } + + // Report variables defined but not used in the current scope as unused + // variables if the current token closes scopes. + $this->searchForAndProcessClosingScopesAt($phpcsFile, $stackPtr); + + // Scan variables that were postponed because they exist in the increment + // expression of a for loop if the current token closes a loop. + $this->processClosingForLoopsAt($phpcsFile, $stackPtr); + + // Find and process variables to perform two jobs: to record variable + // definition or use, and to report variables as undefined if they are used + // without having been first defined. + if ($token['code'] === T_VARIABLE) { + $this->processVariable($phpcsFile, $stackPtr); + return; + } + if (($token['code'] === T_DOUBLE_QUOTED_STRING) || ($token['code'] === T_HEREDOC)) { + $this->processVariableInString($phpcsFile, $stackPtr); + return; + } + if (($token['code'] === T_STRING) && ($token['content'] === 'compact')) { + $this->processCompact($phpcsFile, $stackPtr); + return; + } + + // Record for loop boundaries so we can delay scanning the third for loop + // expression until after the loop has been scanned. + if ($token['code'] === T_FOR) { + $this->recordForLoop($phpcsFile, $stackPtr); + return; + } + + // Record enums so we can detect them even before phpcs was able to. + if ($token['content'] === 'enum') { + $enumInfo = Helpers::makeEnumInfo($phpcsFile, $stackPtr); + // The token might not actually be an enum so let's avoid returning if + // it's not. + if ($enumInfo) { + $this->enums[$stackPtr] = $enumInfo; + return; + } + } + + // If the current token is a call to `get_defined_vars()`, consider that a + // usage of all variables in the current scope. + if ($this->isGetDefinedVars($phpcsFile, $stackPtr)) { + Helpers::debug('get_defined_vars is being called'); + $this->markAllVariablesRead($phpcsFile, $stackPtr); + return; + } + + // If the current token starts a scope, record that scope's start and end + // indexes so that we can determine if variables in that scope are defined + // and/or used. + if ( + in_array($token['code'], $scopeStartTokenTypes, true) || + Helpers::isArrowFunction($phpcsFile, $stackPtr) + ) { + Helpers::debug('found scope condition', $token); + $this->scopeManager->recordScopeStartAndEnd($phpcsFile, $stackPtr); + return; + } + } + + /** + * Record the boundaries of a for loop. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return void + */ + private function recordForLoop($phpcsFile, $stackPtr) + { + $this->forLoops[$stackPtr] = Helpers::makeForLoopInfo($phpcsFile, $stackPtr); + } + + /** + * Find scopes closed by a token and process their variables. + * + * Calls `processScopeClose()` for each closed scope. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return void + */ + private function searchForAndProcessClosingScopesAt($phpcsFile, $stackPtr) + { + $scopeIndicesThisCloses = $this->scopeManager->getScopesForScopeEnd($phpcsFile->getFilename(), $stackPtr); + + $tokens = $phpcsFile->getTokens(); + $token = $tokens[$stackPtr]; + $line = $token['line']; + foreach ($scopeIndicesThisCloses as $scopeIndexThisCloses) { + Helpers::debug('found closing scope at index', $stackPtr, 'line', $line, 'for scopes starting at:', $scopeIndexThisCloses->scopeStartIndex); + $this->processScopeClose($phpcsFile, $scopeIndexThisCloses->scopeStartIndex); + } + } + + /** + * Scan variables that were postponed because they exist in the increment expression of a for loop. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return void + */ + private function processClosingForLoopsAt($phpcsFile, $stackPtr) + { + $forLoopsThisCloses = []; + foreach ($this->forLoops as $forLoop) { + if ($forLoop->blockEnd === $stackPtr) { + $forLoopsThisCloses[] = $forLoop; + } + } + + foreach ($forLoopsThisCloses as $forLoop) { + foreach ($forLoop->incrementVariables as $varIndex => $varInfo) { + Helpers::debug('processing delayed for loop increment variable at', $varIndex, $varInfo); + $this->processVariable($phpcsFile, $varIndex, ['ignore-for-loops' => true]); + } + } + } + + /** + * Return true if the token is a call to `get_defined_vars()`. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + protected function isGetDefinedVars(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $token = $tokens[$stackPtr]; + if (! $token || $token['content'] !== 'get_defined_vars') { + return false; + } + // Make sure this is a function call + $parenPointer = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if (! $parenPointer || $tokens[$parenPointer]['code'] !== T_OPEN_PARENTHESIS) { + return false; + } + return true; + } + + /** + * @return string + */ + protected function getFilename() + { + return $this->currentFile ? $this->currentFile->getFilename() : 'unknown file'; + } + + /** + * @param int $currScope + * + * @return ScopeInfo + */ + protected function getOrCreateScopeInfo($currScope) + { + $scope = $this->scopeManager->getScopeForScopeStart($this->getFilename(), $currScope); + if (! $scope) { + if (! $this->currentFile) { + throw new \Exception('Cannot create scope info; current file is not set.'); + } + $scope = $this->scopeManager->recordScopeStartAndEnd($this->currentFile, $currScope); + } + return $scope; + } + + /** + * @param string $varName + * @param int $currScope + * + * @return VariableInfo|null + */ + protected function getVariableInfo($varName, $currScope) + { + $scopeInfo = $this->scopeManager->getScopeForScopeStart($this->getFilename(), $currScope); + return ($scopeInfo && isset($scopeInfo->variables[$varName])) ? $scopeInfo->variables[$varName] : null; + } + + /** + * Returns variable data for a variable at an index. + * + * The variable will also be added to the list of variables stored in its + * scope so that its use or non-use can be reported when those scopes end by + * `processScopeClose()`. + * + * @param string $varName + * @param int $currScope + * + * @return VariableInfo + */ + protected function getOrCreateVariableInfo($varName, $currScope) + { + Helpers::debug("getOrCreateVariableInfo: starting for '{$varName}'"); + $scopeInfo = $this->getOrCreateScopeInfo($currScope); + if (isset($scopeInfo->variables[$varName])) { + Helpers::debug("getOrCreateVariableInfo: found variable for '{$varName}'", $scopeInfo->variables[$varName]); + return $scopeInfo->variables[$varName]; + } + Helpers::debug("getOrCreateVariableInfo: creating a new variable for '{$varName}' in scope", $scopeInfo); + $scopeInfo->variables[$varName] = new VariableInfo($varName); + $validUnusedVariableNames = (empty($this->validUnusedVariableNames)) + ? [] + : Helpers::splitStringToArray('/\s+/', trim($this->validUnusedVariableNames)); + $validUndefinedVariableNames = (empty($this->validUndefinedVariableNames)) + ? [] + : Helpers::splitStringToArray('/\s+/', trim($this->validUndefinedVariableNames)); + if (in_array($varName, $validUnusedVariableNames)) { + $scopeInfo->variables[$varName]->ignoreUnused = true; + } + if (isset($this->ignoreUnusedRegexp) && preg_match($this->ignoreUnusedRegexp, $varName) === 1) { + $scopeInfo->variables[$varName]->ignoreUnused = true; + } + if ($scopeInfo->scopeStartIndex === 0 && $this->allowUndefinedVariablesInFileScope) { + $scopeInfo->variables[$varName]->ignoreUndefined = true; + } + if (in_array($varName, $validUndefinedVariableNames)) { + $scopeInfo->variables[$varName]->ignoreUndefined = true; + } + if (isset($this->validUndefinedVariableRegexp) && preg_match($this->validUndefinedVariableRegexp, $varName) === 1) { + $scopeInfo->variables[$varName]->ignoreUndefined = true; + } + Helpers::debug("getOrCreateVariableInfo: scope for '{$varName}' is now", $scopeInfo); + return $scopeInfo->variables[$varName]; + } + + /** + * Record that a variable has been defined and assigned a value. + * + * If a variable has been defined within a scope, it will not be marked as + * undefined when that variable is later used. If it is not used, it will be + * marked as unused when that scope ends. + * + * Sometimes it's possible to assign something to a variable without + * definining it (eg: assignment to a reference); in that case, use + * `markVariableAssignmentWithoutInitialization()`. + * + * @param string $varName + * @param int $stackPtr + * @param int $currScope + * + * @return void + */ + protected function markVariableAssignment($varName, $stackPtr, $currScope) + { + Helpers::debug('markVariableAssignment: starting for', $varName); + $this->markVariableAssignmentWithoutInitialization($varName, $stackPtr, $currScope); + Helpers::debug('markVariableAssignment: marked as assigned without initialization', $varName); + $varInfo = $this->getOrCreateVariableInfo($varName, $currScope); + if (isset($varInfo->firstInitialized) && ($varInfo->firstInitialized <= $stackPtr)) { + Helpers::debug('markVariableAssignment: variable is already initialized', $varName); + return; + } + $varInfo->firstInitialized = $stackPtr; + Helpers::debug('markVariableAssignment: marked as initialized', $varName); + } + + /** + * Record that a variable has been assigned a value. + * + * Does not record that a variable has been defined, which is the usual state + * of affairs. For that, use `markVariableAssignment()`. + * + * This is useful for assignments to references. + * + * @param string $varName + * @param int $stackPtr + * @param int $currScope + * + * @return void + */ + protected function markVariableAssignmentWithoutInitialization($varName, $stackPtr, $currScope) + { + $varInfo = $this->getOrCreateVariableInfo($varName, $currScope); + + // Is the variable referencing another variable? If so, mark that variable used also. + if ($varInfo->referencedVariableScope !== null && $varInfo->referencedVariableScope !== $currScope) { + // Don't do this if the referenced variable does not exist; eg: if it's going to be bound at runtime like in array_walk + if ($this->getVariableInfo($varInfo->name, $varInfo->referencedVariableScope)) { + Helpers::debug('markVariableAssignmentWithoutInitialization: marking referenced variable as assigned also', $varName); + $this->markVariableAssignment($varInfo->name, $stackPtr, $varInfo->referencedVariableScope); + } + } + + if (empty($varInfo->scopeType)) { + $varInfo->scopeType = ScopeType::LOCAL; + } + $varInfo->allAssignments[] = $stackPtr; + } + + /** + * Record that a variable has been defined within a scope. + * + * @param string $varName + * @param ScopeType::PARAM|ScopeType::BOUND|ScopeType::LOCAL|ScopeType::GLOBALSCOPE|ScopeType::STATICSCOPE $scopeType + * @param ?string $typeHint + * @param int $stackPtr + * @param int $currScope + * @param ?bool $permitMatchingRedeclaration + * + * @return void + */ + protected function markVariableDeclaration( + $varName, + $scopeType, + $typeHint, + $stackPtr, + $currScope, + $permitMatchingRedeclaration = false + ) { + Helpers::debug("marking variable '{$varName}' declared in scope starting at token", $currScope); + $varInfo = $this->getOrCreateVariableInfo($varName, $currScope); + + if (! empty($varInfo->scopeType)) { + if (($permitMatchingRedeclaration === false) || ($varInfo->scopeType !== $scopeType)) { + // Issue redeclaration/reuse warning + // Note: we check off scopeType not firstDeclared, this is so that + // we catch declarations that come after implicit declarations like + // use of a variable as a local. + $this->addWarning( + 'Redeclaration of %s %s as %s.', + $stackPtr, + 'VariableRedeclaration', + [ + VariableInfo::$scopeTypeDescriptions[$varInfo->scopeType], + "\${$varName}", + VariableInfo::$scopeTypeDescriptions[$scopeType], + ] + ); + } + } + + $varInfo->scopeType = $scopeType; + if (isset($typeHint)) { + $varInfo->typeHint = $typeHint; + } + if (isset($varInfo->firstDeclared) && ($varInfo->firstDeclared <= $stackPtr)) { + Helpers::debug("variable '{$varName}' was already marked declared", $varInfo); + return; + } + $varInfo->firstDeclared = $stackPtr; + $varInfo->allAssignments[] = $stackPtr; + Helpers::debug("variable '{$varName}' marked declared", $varInfo); + } + + /** + * @param string $message + * @param int $stackPtr + * @param string $code + * @param string[] $data + * + * @return void + */ + protected function addWarning($message, $stackPtr, $code, $data) + { + if (! $this->currentFile) { + throw new \Exception('Cannot add warning; current file is not set.'); + } + $this->currentFile->addWarning( + $message, + $stackPtr, + $code, + $data + ); + } + + /** + * Record that a variable has been used within a scope. + * + * If the variable has not been defined first, this will still mark it used. + * To display a warning for undefined variables, use + * `markVariableReadAndWarnIfUndefined()`. + * + * @param string $varName + * @param int $stackPtr + * @param int $currScope + * + * @return void + */ + protected function markVariableRead($varName, $stackPtr, $currScope) + { + $varInfo = $this->getOrCreateVariableInfo($varName, $currScope); + if (isset($varInfo->firstRead) && ($varInfo->firstRead <= $stackPtr)) { + return; + } + $varInfo->firstRead = $stackPtr; + } + + /** + * Return true if a variable is defined within a scope. + * + * @param string $varName + * @param int $stackPtr + * @param int $currScope + * + * @return bool + */ + protected function isVariableUndefined($varName, $stackPtr, $currScope) + { + $varInfo = $this->getOrCreateVariableInfo($varName, $currScope); + Helpers::debug('isVariableUndefined', $varInfo, 'at', $stackPtr); + if ($varInfo->ignoreUndefined) { + return false; + } + if (isset($varInfo->firstDeclared) && $varInfo->firstDeclared <= $stackPtr) { + return false; + } + if (isset($varInfo->firstInitialized) && $varInfo->firstInitialized <= $stackPtr) { + return false; + } + // If we are inside a for loop increment expression, check to see if the + // variable was defined inside the for loop. + foreach ($this->forLoops as $forLoop) { + if ($stackPtr > $forLoop->incrementStart && $stackPtr < $forLoop->incrementEnd) { + Helpers::debug('isVariableUndefined looking at increment expression for loop', $forLoop); + if ( + isset($varInfo->firstInitialized) + && $varInfo->firstInitialized > $forLoop->blockStart + && $varInfo->firstInitialized < $forLoop->blockEnd + ) { + return false; + } + } + } + // If we are inside a for loop body, check to see if the variable was + // defined in that loop's third expression. + foreach ($this->forLoops as $forLoop) { + if ($stackPtr > $forLoop->blockStart && $stackPtr < $forLoop->blockEnd) { + foreach ($forLoop->incrementVariables as $forLoopVarInfo) { + if ($varInfo === $forLoopVarInfo) { + return false; + } + } + } + } + return true; + } + + /** + * Record a variable use and report a warning if the variable is undefined. + * + * @param File $phpcsFile + * @param string $varName + * @param int $stackPtr + * @param int $currScope + * + * @return void + */ + protected function markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $stackPtr, $currScope) + { + $this->markVariableRead($varName, $stackPtr, $currScope); + if ($this->isVariableUndefined($varName, $stackPtr, $currScope) === true) { + Helpers::debug("variable $varName looks undefined"); + + if (Helpers::isVariableArrayPushShortcut($phpcsFile, $stackPtr)) { + $this->warnAboutUndefinedArrayPushShortcut($phpcsFile, $varName, $stackPtr); + // Mark the variable as defined if it's of the form `$x[] = 1;` + $this->markVariableAssignment($varName, $stackPtr, $currScope); + return; + } + + if (Helpers::isVariableInsideUnset($phpcsFile, $stackPtr)) { + $this->warnAboutUndefinedUnset($phpcsFile, $varName, $stackPtr); + return; + } + + $this->warnAboutUndefinedVariable($phpcsFile, $varName, $stackPtr); + } + } + + /** + * Mark all variables within a scope as being used. + * + * This will prevent any of the variables in that scope from being reported + * as unused. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return void + */ + protected function markAllVariablesRead(File $phpcsFile, $stackPtr) + { + $currScope = Helpers::findVariableScope($phpcsFile, $stackPtr); + if ($currScope === null) { + return; + } + $scopeInfo = $this->getOrCreateScopeInfo($currScope); + $count = count($scopeInfo->variables); + Helpers::debug("marking all $count variables in scope as read"); + foreach ($scopeInfo->variables as $varInfo) { + $this->markVariableRead($varInfo->name, $stackPtr, $scopeInfo->scopeStartIndex); + } + } + + /** + * Process a parameter definition if it is inside a function definition. + * + * This does not include variables imported by a "use" statement. + * + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * @param int $outerScope + * + * @return void + */ + protected function processVariableAsFunctionParameter(File $phpcsFile, $stackPtr, $varName, $outerScope) + { + Helpers::debug('processVariableAsFunctionParameter', $stackPtr, $varName); + $tokens = $phpcsFile->getTokens(); + + $functionPtr = Helpers::getFunctionIndexForFunctionParameter($phpcsFile, $stackPtr); + if (! is_int($functionPtr)) { + throw new \Exception("Function index not found for function argument index {$stackPtr}"); + } + + Helpers::debug('processVariableAsFunctionParameter found function definition', $tokens[$functionPtr]); + $this->markVariableDeclaration($varName, ScopeType::PARAM, null, $stackPtr, $functionPtr); + + // Are we pass-by-reference? + $referencePtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $stackPtr - 1, null, true, null, true); + if (($referencePtr !== false) && ($tokens[$referencePtr]['code'] === T_BITWISE_AND)) { + Helpers::debug('processVariableAsFunctionParameter found pass-by-reference to scope', $outerScope); + $varInfo = $this->getOrCreateVariableInfo($varName, $functionPtr); + $varInfo->referencedVariableScope = $outerScope; + } + + // Are we optional with a default? + if (Helpers::getNextAssignPointer($phpcsFile, $stackPtr) !== null) { + Helpers::debug('processVariableAsFunctionParameter optional with default'); + $this->markVariableAssignment($varName, $stackPtr, $functionPtr); + } + + // Are we using constructor promotion? If so, that counts as both definition and use. + if (Helpers::isConstructorPromotion($phpcsFile, $stackPtr)) { + Helpers::debug('processVariableAsFunctionParameter constructor promotion'); + $this->markVariableRead($varName, $stackPtr, $outerScope); + } + } + + /** + * Process a variable definition if it is inside a function's "use" import. + * + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * @param int $outerScope The start of the scope outside the function definition + * + * @return void + */ + protected function processVariableAsUseImportDefinition(File $phpcsFile, $stackPtr, $varName, $outerScope) + { + $tokens = $phpcsFile->getTokens(); + + Helpers::debug('processVariableAsUseImportDefinition', $stackPtr, $varName, $outerScope); + + $endOfArgsPtr = $phpcsFile->findPrevious([T_CLOSE_PARENTHESIS], $stackPtr - 1, null); + if (! is_int($endOfArgsPtr)) { + throw new \Exception("Arguments index not found for function use index {$stackPtr} when processing variable {$varName}"); + } + $functionPtr = Helpers::getFunctionIndexForFunctionParameter($phpcsFile, $endOfArgsPtr); + if (! is_int($functionPtr)) { + throw new \Exception("Function index not found for function use index {$stackPtr} (using {$endOfArgsPtr}) when processing variable {$varName}"); + } + + // Use is both a read (in the enclosing scope) and a define (in the function scope) + $this->markVariableRead($varName, $stackPtr, $outerScope); + + // If it's undefined in the enclosing scope, the use is wrong + if ($this->isVariableUndefined($varName, $stackPtr, $outerScope) === true) { + Helpers::debug("variable '{$varName}' in function definition looks undefined in scope", $outerScope); + $this->warnAboutUndefinedVariable($phpcsFile, $varName, $stackPtr); + return; + } + + $this->markVariableDeclaration($varName, ScopeType::BOUND, null, $stackPtr, $functionPtr); + $this->markVariableAssignment($varName, $stackPtr, $functionPtr); + + // Are we pass-by-reference? If so, then any assignment to the variable in + // the function scope also should count for the enclosing scope. + $referencePtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $stackPtr - 1, null, true, null, true); + if (is_int($referencePtr) && $tokens[$referencePtr]['code'] === T_BITWISE_AND) { + Helpers::debug("variable '{$varName}' in function definition looks passed by reference"); + $varInfo = $this->getOrCreateVariableInfo($varName, $functionPtr); + $varInfo->referencedVariableScope = $outerScope; + } + } + + /** + * Process a class property that is being defined. + * + * Property definitions are ignored currently because all property access is + * legal, even to undefined properties. + * + * Can be called for any token and will return false if the variable is not + * of this type. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + protected function processVariableAsClassProperty(File $phpcsFile, $stackPtr) + { + $propertyDeclarationKeywords = [ + T_PUBLIC, + T_PRIVATE, + T_PROTECTED, + T_VAR, + ]; + $stopAtPtr = $stackPtr - 2; + $visibilityPtr = $phpcsFile->findPrevious($propertyDeclarationKeywords, $stackPtr - 1, $stopAtPtr > 0 ? $stopAtPtr : 0); + if ($visibilityPtr) { + return true; + } + $staticPtr = $phpcsFile->findPrevious(T_STATIC, $stackPtr - 1, $stopAtPtr > 0 ? $stopAtPtr : 0); + if (! $staticPtr) { + return false; + } + $stopAtPtr = $staticPtr - 2; + $visibilityPtr = $phpcsFile->findPrevious($propertyDeclarationKeywords, $staticPtr - 1, $stopAtPtr > 0 ? $stopAtPtr : 0); + if ($visibilityPtr) { + return true; + } + // it's legal to use `static` to define properties as well as to + // define variables, so make sure we are not in a function before + // assuming it's a property. + $tokens = $phpcsFile->getTokens(); + + /** @var array{conditions?: (int|string)[], content?: string}|null */ + $token = $tokens[$stackPtr]; + if ($token && !empty($token['conditions']) && !empty($token['content']) && !Helpers::areConditionsWithinFunctionBeforeClass($token)) { + return Helpers::areAnyConditionsAClass($token); + } + return false; + } + + /** + * Process a variable that is being accessed inside a catch block. + * + * Can be called for any token and will return false if the variable is not + * of this type. + * + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * @param int $currScope + * + * @return bool + */ + protected function processVariableAsCatchBlock(File $phpcsFile, $stackPtr, $varName, $currScope) + { + $tokens = $phpcsFile->getTokens(); + + // Are we a catch block parameter? + $openPtr = Helpers::findContainingOpeningBracket($phpcsFile, $stackPtr); + if ($openPtr === null) { + return false; + } + + $catchPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $openPtr - 1, null, true, null, true); + if (($catchPtr !== false) && ($tokens[$catchPtr]['code'] === T_CATCH)) { + // Scope of the exception var is actually the function, not just the catch block. + $this->markVariableDeclaration($varName, ScopeType::LOCAL, null, $stackPtr, $currScope, true); + $this->markVariableAssignment($varName, $stackPtr, $currScope); + if ($this->allowUnusedCaughtExceptions) { + $varInfo = $this->getOrCreateVariableInfo($varName, $currScope); + $varInfo->ignoreUnused = true; + } + return true; + } + return false; + } + + /** + * Process a variable that is being accessed as a member of `$this`. + * + * Looks for variables of the form `$this->myVariable`. + * + * Can be called for any token and will return false if the variable is not + * of this type. + * + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * + * @return bool + */ + protected function processVariableAsThisWithinClass(File $phpcsFile, $stackPtr, $varName) + { + $tokens = $phpcsFile->getTokens(); + $token = $tokens[$stackPtr]; + + // Are we $this within a class? + if (($varName !== 'this') || empty($token['conditions'])) { + return false; + } + + // Handle enums specially since their condition may not exist in old phpcs. + $inEnum = false; + foreach ($this->enums as $enum) { + if ($stackPtr > $enum->blockStart && $stackPtr < $enum->blockEnd) { + $inEnum = true; + } + } + + $inFunction = false; + foreach (array_reverse($token['conditions'], true) as $scopeCode) { + // $this within a closure is valid + if ($scopeCode === T_CLOSURE && $inFunction === false) { + return true; + } + + $classlikeCodes = [T_CLASS, T_ANON_CLASS, T_TRAIT]; + if (defined('T_ENUM')) { + $classlikeCodes[] = T_ENUM; + } + if (in_array($scopeCode, $classlikeCodes, true)) { + return true; + } + + if ($scopeCode === T_FUNCTION && $inEnum) { + return true; + } + + // Handle nested function declarations. + if ($scopeCode === T_FUNCTION) { + if ($inFunction === true) { + break; + } + + $inFunction = true; + } + } + + return false; + } + + /** + * Process a superglobal variable that is being accessed. + * + * Can be called for any token and will return false if the variable is not + * of this type. + * + * @param string $varName + * + * @return bool + */ + protected function processVariableAsSuperGlobal($varName) + { + $superglobals = [ + 'GLOBALS', + '_SERVER', + '_GET', + '_POST', + '_FILES', + '_COOKIE', + '_SESSION', + '_REQUEST', + '_ENV', + 'argv', + 'argc', + 'http_response_header', + 'HTTP_RAW_POST_DATA', + ]; + // Are we a superglobal variable? + return (in_array($varName, $superglobals, true)); + } + + /** + * Process a variable that is being accessed with static syntax. + * + * That is, this will record the use of a variable of the form + * `MyClass::$myVariable` or `self::$myVariable`. + * + * Can be called for any token and will return false if the variable is not + * of this type. + * + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + protected function processVariableAsStaticMember(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $doubleColonPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $stackPtr - 1, null, true); + if ($doubleColonPtr === false || $tokens[$doubleColonPtr]['code'] !== T_DOUBLE_COLON) { + return false; + } + $classNamePtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $doubleColonPtr - 1, null, true); + $staticReferences = [ + T_STRING, + T_SELF, + T_PARENT, + T_STATIC, + T_VARIABLE, + ]; + if ($classNamePtr === false || ! in_array($tokens[$classNamePtr]['code'], $staticReferences, true)) { + return false; + } + // "When calling static methods, the function call is stronger than the + // static property operator" so look for a function call. + $parenPointer = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + if ($parenPointer !== false && $tokens[$parenPointer]['code'] === T_OPEN_PARENTHESIS) { + return false; + } + return true; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * + * @return bool + */ + protected function processVariableAsStaticOutsideClass(File $phpcsFile, $stackPtr, $varName) + { + // Are we refering to self:: outside a class? + + $tokens = $phpcsFile->getTokens(); + + /** @var array{conditions?: (int|string)[], content?: string}|null */ + $token = $tokens[$stackPtr]; + + $doubleColonPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $stackPtr - 1, null, true); + if ($doubleColonPtr === false || $tokens[$doubleColonPtr]['code'] !== T_DOUBLE_COLON) { + return false; + } + $classNamePtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $doubleColonPtr - 1, null, true); + if ($classNamePtr === false) { + return false; + } + $code = $tokens[$classNamePtr]['code']; + $staticReferences = [ + T_SELF, + T_STATIC, + ]; + if (! in_array($code, $staticReferences, true)) { + return false; + } + $errorClass = $code === T_SELF ? 'SelfOutsideClass' : 'StaticOutsideClass'; + $staticRefType = $code === T_SELF ? 'self::' : 'static::'; + if (!empty($token['conditions']) && !empty($token['content']) && Helpers::areAnyConditionsAClass($token)) { + return false; + } + $phpcsFile->addError( + "Use of {$staticRefType}%s outside class definition.", + $stackPtr, + $errorClass, + ["\${$varName}"] + ); + return true; + } + + /** + * Process a variable that is being assigned. + * + * This will record that the variable has been defined within a scope so that + * later we can determine if it it unused and we can guarantee that any + * future uses of the variable are not using an undefined variable. + * + * References (on either side of an assignment) behave differently and this + * function handles those cases as well. + * + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * @param int $currScope + * + * @return void + */ + protected function processVariableAsAssignment(File $phpcsFile, $stackPtr, $varName, $currScope) + { + Helpers::debug("processVariableAsAssignment: starting for '{$varName}'"); + $assignPtr = Helpers::getNextAssignPointer($phpcsFile, $stackPtr); + if (! is_int($assignPtr)) { + return; + } + + // If the right-hand-side of the assignment to this variable is a reference + // variable, then this variable is a reference to that one, and as such any + // assignment to this variable (except another assignment by reference, + // which would change the binding) has a side effect of changing the + // referenced variable and therefore should count as both an assignment and + // a read. + $tokens = $phpcsFile->getTokens(); + $referencePtr = $phpcsFile->findNext(Tokens::$emptyTokens, $assignPtr + 1, null, true, null, true); + if (is_int($referencePtr) && $tokens[$referencePtr]['code'] === T_BITWISE_AND) { + Helpers::debug('processVariableAsAssignment: found reference variable'); + $varInfo = $this->getOrCreateVariableInfo($varName, $currScope); + // If the variable was already declared, but was not yet read, it is + // unused because we're about to change the binding; that is, unless we + // are inside a conditional block because in that case the condition may + // never activate. + $scopeInfo = $this->getOrCreateScopeInfo($currScope); + $ifPtr = Helpers::getClosestIfPositionIfBeforeOtherConditions($tokens[$referencePtr]['conditions']); + $lastAssignmentPtr = $varInfo->firstDeclared; + if (! $ifPtr && $lastAssignmentPtr) { + $this->processScopeCloseForVariable($phpcsFile, $varInfo, $scopeInfo); + } + if ($ifPtr && $lastAssignmentPtr && $ifPtr <= $lastAssignmentPtr) { + $this->processScopeCloseForVariable($phpcsFile, $varInfo, $scopeInfo); + } + // The referenced variable may have a different name, but we don't + // actually need to mark it as used in this case because the act of this + // assignment will mark it used on the next token. + $varInfo->referencedVariableScope = $currScope; + $this->markVariableDeclaration($varName, ScopeType::LOCAL, null, $stackPtr, $currScope, true); + // An assignment to a reference is a binding and should not count as + // initialization since it doesn't change any values. + $this->markVariableAssignmentWithoutInitialization($varName, $stackPtr, $currScope); + return; + } + + Helpers::debug('processVariableAsAssignment: marking as assignment in scope', $currScope); + $this->markVariableAssignment($varName, $stackPtr, $currScope); + + // If the left-hand-side of the assignment (the variable we are examining) + // is itself a reference, then that counts as a read as well as a write. + $varInfo = $this->getOrCreateVariableInfo($varName, $currScope); + if ($varInfo->isDynamicReference) { + Helpers::debug('processVariableAsAssignment: also marking as a use because variable is a reference'); + $this->markVariableRead($varName, $stackPtr, $currScope); + } + } + + /** + * Processes variables destructured from an array using shorthand list assignment. + * + * This will record the definition and assignment of variables defined using + * the format: + * + * ``` + * [ $foo, $bar, $baz ] = $ary; + * ``` + * + * Can be called for any token and will return false if the variable is not + * of this type. + * + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * @param int $currScope + * + * @return bool + */ + protected function processVariableAsListShorthandAssignment(File $phpcsFile, $stackPtr, $varName, $currScope) + { + // OK, are we within a [ ... ] construct? + $openPtr = Helpers::findContainingOpeningSquareBracket($phpcsFile, $stackPtr); + if (! is_int($openPtr)) { + return false; + } + + // OK, we're a [ ... ] construct... are we being assigned to? + $assignments = Helpers::getListAssignments($phpcsFile, $openPtr); + if (! $assignments) { + return false; + } + $matchingAssignment = array_reduce($assignments, function ($thisAssignment, $assignment) use ($stackPtr) { + if ($assignment === $stackPtr) { + return $assignment; + } + return $thisAssignment; + }); + if (! $matchingAssignment) { + return false; + } + + // Yes, we're being assigned. + $this->markVariableAssignment($varName, $stackPtr, $currScope); + return true; + } + + /** + * Processes variables destructured from an array using list assignment. + * + * This will record the definition and assignment of variables defined using + * the format: + * + * ``` + * list( $foo, $bar, $baz ) = $ary; + * ``` + * + * Can be called for any token and will return false if the variable is not + * of this type. + * + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * @param int $currScope + * + * @return bool + */ + protected function processVariableAsListAssignment(File $phpcsFile, $stackPtr, $varName, $currScope) + { + $tokens = $phpcsFile->getTokens(); + + // OK, are we within a list (...) construct? + $openPtr = Helpers::findContainingOpeningBracket($phpcsFile, $stackPtr); + if ($openPtr === null) { + return false; + } + + $prevPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $openPtr - 1, null, true, null, true); + if ((is_bool($prevPtr)) || ($tokens[$prevPtr]['code'] !== T_LIST)) { + return false; + } + + // OK, we're a list (...) construct... are we being assigned to? + $assignments = Helpers::getListAssignments($phpcsFile, $prevPtr); + if (! $assignments) { + return false; + } + $matchingAssignment = array_reduce($assignments, function ($thisAssignment, $assignment) use ($stackPtr) { + if ($assignment === $stackPtr) { + return $assignment; + } + return $thisAssignment; + }); + if (! $matchingAssignment) { + return false; + } + + // Yes, we're being assigned. + $this->markVariableAssignment($varName, $stackPtr, $currScope); + return true; + } + + /** + * Process a variable being defined (imported, really) with the `global` keyword. + * + * Can be called for any token and will return false if the variable is not + * of this type. + * + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * @param int $currScope + * + * @return bool + */ + protected function processVariableAsGlobalDeclaration(File $phpcsFile, $stackPtr, $varName, $currScope) + { + $tokens = $phpcsFile->getTokens(); + + // Are we a global declaration? + // Search backwards for first token that isn't whitespace/comment, comma or variable. + $ignore = Tokens::$emptyTokens; + $ignore[T_VARIABLE] = T_VARIABLE; + $ignore[T_COMMA] = T_COMMA; + + $globalPtr = $phpcsFile->findPrevious($ignore, $stackPtr - 1, null, true, null, true); + if (($globalPtr === false) || ($tokens[$globalPtr]['code'] !== T_GLOBAL)) { + return false; + } + + // It's a global declaration. + $this->markVariableDeclaration($varName, ScopeType::GLOBALSCOPE, null, $stackPtr, $currScope); + return true; + } + + /** + * Process a variable as a static declaration within a function. + * + * Specifically, this looks for variable definitions of the form `static + * $foo = 'hello';` or `static int $foo;` inside a function definition. + * + * This will not operate on variables that are written in a class definition + * outside of a function like `static $foo;` or `public static ?int $foo = + * 'bar';` because class properties (static or instance) are currently not + * tracked by this sniff. This is because a class property might be unused + * inside the class, but used outside the class (we cannot easily know if it + * is unused); this is also because it's common and legal to define class + * properties when they are assigned and that assignment can happen outside a + * class (we cannot easily know if the use of a property is undefined). These + * sorts of checks are better performed by static analysis tools that can see + * a whole project rather than a linter which can only easily see a file or + * some lines. + * + * If found, such a variable will be marked as declared (and possibly + * assigned, if it includes an initial value) within the scope of the + * function body. + * + * This will not operate on variables that use late static binding + * (`static::$foobar`) or the parameters of static methods even though they + * include the word `static` in the same statement. + * + * This only finds the defintions of static variables. Their use is handled + * by `processVariableAsStaticMember()`. + * + * Can be called for any token and will return false if the variable is not + * of this type. + * + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * @param int $currScope + * + * @return bool + */ + protected function processVariableAsStaticDeclaration(File $phpcsFile, $stackPtr, $varName, $currScope) + { + $tokens = $phpcsFile->getTokens(); + + // Search backwards for a `static` keyword that occurs before the start of the statement. + $startOfStatement = $phpcsFile->findPrevious([T_SEMICOLON, T_OPEN_CURLY_BRACKET, T_FN_ARROW, T_OPEN_PARENTHESIS], $stackPtr - 1, null, false, null, true); + $staticPtr = $phpcsFile->findPrevious([T_STATIC], $stackPtr - 1, null, false, null, true); + if (! is_int($startOfStatement)) { + $startOfStatement = 1; + } + if (! is_int($staticPtr)) { + return false; + } + // PHPCS is bad at finding the start of statements so we have to do it ourselves. + if ($staticPtr < $startOfStatement) { + return false; + } + + // Is the 'static' keyword an anonymous static function declaration? If so, + // this is not a static variable declaration. + $tokenAfterStatic = $phpcsFile->findNext(Tokens::$emptyTokens, $staticPtr + 1, null, true, null, true); + $functionTokenTypes = [ + T_FUNCTION, + T_CLOSURE, + T_FN, + ]; + if (is_int($tokenAfterStatic) && in_array($tokens[$tokenAfterStatic]['code'], $functionTokenTypes, true)) { + return false; + } + + // Is the token inside function parameters? If so, this is not a static + // declaration because we must be inside a function body. + if (Helpers::isTokenFunctionParameter($phpcsFile, $stackPtr)) { + return false; + } + + // Is the token inside a function call? If so, this is not a static + // declaration. + if (Helpers::isTokenInsideFunctionCallArgument($phpcsFile, $stackPtr)) { + return false; + } + + // Is the keyword a late static binding? If so, this isn't the static + // keyword we're looking for, but since static:: isn't allowed in a + // compile-time constant, we also know we can't be part of a static + // declaration anyway, so there's no need to look any further. + $lateStaticBindingPtr = $phpcsFile->findNext(T_WHITESPACE, $staticPtr + 1, null, true, null, true); + if (($lateStaticBindingPtr !== false) && ($tokens[$lateStaticBindingPtr]['code'] === T_DOUBLE_COLON)) { + return false; + } + + $this->markVariableDeclaration($varName, ScopeType::STATICSCOPE, null, $stackPtr, $currScope); + if (Helpers::getNextAssignPointer($phpcsFile, $stackPtr) !== null) { + $this->markVariableAssignment($varName, $stackPtr, $currScope); + } + return true; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * @param int $currScope + * + * @return bool + */ + protected function processVariableAsForeachLoopVar(File $phpcsFile, $stackPtr, $varName, $currScope) + { + $tokens = $phpcsFile->getTokens(); + + // Are we a foreach loopvar? + $openParenPtr = Helpers::findContainingOpeningBracket($phpcsFile, $stackPtr); + if (! is_int($openParenPtr)) { + return false; + } + $foreachPtr = Helpers::getIntOrNull($phpcsFile->findPrevious(Tokens::$emptyTokens, $openParenPtr - 1, null, true)); + if (! is_int($foreachPtr)) { + return false; + } + if ($tokens[$foreachPtr]['code'] === T_LIST) { + $openParenPtr = Helpers::findContainingOpeningBracket($phpcsFile, $foreachPtr); + if (! is_int($openParenPtr)) { + return false; + } + $foreachPtr = Helpers::getIntOrNull($phpcsFile->findPrevious(Tokens::$emptyTokens, $openParenPtr - 1, null, true)); + if (! is_int($foreachPtr)) { + return false; + } + } + if ($tokens[$foreachPtr]['code'] !== T_FOREACH) { + return false; + } + + // Is there an 'as' token between us and the foreach? + if ($phpcsFile->findPrevious(T_AS, $stackPtr - 1, $openParenPtr) === false) { + return false; + } + $this->markVariableAssignment($varName, $stackPtr, $currScope); + $varInfo = $this->getOrCreateVariableInfo($varName, $currScope); + + // Is this the value of a key => value foreach? + if ($phpcsFile->findPrevious(T_DOUBLE_ARROW, $stackPtr - 1, $openParenPtr) !== false) { + $varInfo->isForeachLoopAssociativeValue = true; + } + + // Are we pass-by-reference? + $referencePtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $stackPtr - 1, null, true, null, true); + if (($referencePtr !== false) && ($tokens[$referencePtr]['code'] === T_BITWISE_AND)) { + Helpers::debug('processVariableAsForeachLoopVar: found foreach loop variable assigned by reference'); + $varInfo->isDynamicReference = true; + } + + return true; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * @param int $currScope + * + * @return bool + */ + protected function processVariableAsPassByReferenceFunctionCall(File $phpcsFile, $stackPtr, $varName, $currScope) + { + $tokens = $phpcsFile->getTokens(); + + // Are we pass-by-reference to known pass-by-reference function? + $functionPtr = Helpers::findFunctionCall($phpcsFile, $stackPtr); + if ($functionPtr === null || ! isset($tokens[$functionPtr])) { + return false; + } + + // Is our function a known pass-by-reference function? + $functionName = $tokens[$functionPtr]['content']; + $refArgs = $this->getPassByReferenceFunction($functionName); + if (! $refArgs) { + return false; + } + + $argPtrs = Helpers::findFunctionCallArguments($phpcsFile, $stackPtr); + + // We're within a function call arguments list, find which arg we are. + $argPos = false; + foreach ($argPtrs as $idx => $ptrs) { + if (in_array($stackPtr, $ptrs)) { + $argPos = $idx + 1; + break; + } + } + if ($argPos === false) { + return false; + } + if (!in_array($argPos, $refArgs)) { + // Our arg wasn't mentioned explicitly, are we after an elipsis catch-all? + $elipsis = array_search('...', $refArgs); + if ($elipsis === false) { + return false; + } + $elipsis = (int)$elipsis; + if ($argPos < $refArgs[$elipsis - 1]) { + return false; + } + } + + // Our argument position matches that of a pass-by-ref argument, + // check that we're the only part of the argument expression. + foreach ($argPtrs[$argPos - 1] as $ptr) { + if ($ptr === $stackPtr) { + continue; + } + if (isset(Tokens::$emptyTokens[$tokens[$ptr]['code']]) === false) { + return false; + } + } + + // Just us, we can mark it as a write. + $this->markVariableAssignment($varName, $stackPtr, $currScope); + // It's a read as well for purposes of used-variables. + $this->markVariableRead($varName, $stackPtr, $currScope); + return true; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * @param int $currScope + * + * @return bool + */ + protected function processVariableAsSymbolicObjectProperty(File $phpcsFile, $stackPtr, $varName, $currScope) + { + $tokens = $phpcsFile->getTokens(); + + // Are we a symbolic object property/function derefeference? + // Search backwards for first token that isn't whitespace, is it a "->" operator? + $objectOperatorPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $stackPtr - 1, null, true, null, true); + if (($objectOperatorPtr === false) || ($tokens[$objectOperatorPtr]['code'] !== T_OBJECT_OPERATOR)) { + return false; + } + + $this->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $stackPtr, $currScope); + return true; + } + + /** + * Process a normal variable in the code. + * + * Most importantly, this function determines if the variable use is a "read" + * (using the variable for something) or a "write" (an assignment) or, + * sometimes, both at once. + * + * It also determines the scope of the variable (where it begins and ends). + * + * Using these two pieces of information, we can determine if the variable is + * being used ("read") without having been defined ("write"). + * + * We can also determine, once the scan has hit the end of a scope, if any of + * the variables within that scope have been defined ("write") without being + * used ("read"). That behavior, however, happens in the `processScopeClose()` + * function using the data gathered by this function. + * + * Some variables are used in more complex ways, so there are other similar + * functions to this one, like `processVariableInString`, and + * `processCompact`. They have the same purpose as this function, though. + * + * If the 'ignore-for-loops' option is true, we will ignore the special + * processing for the increment variables of for loops. This will prevent + * infinite loops. + * + * @param File $phpcsFile The PHP_CodeSniffer file where this token was found. + * @param int $stackPtr The position where the token was found. + * @param array $options See above. + * + * @return void + */ + protected function processVariable(File $phpcsFile, $stackPtr, $options = []) + { + $tokens = $phpcsFile->getTokens(); + $token = $tokens[$stackPtr]; + + // Get the name of the variable. + $varName = Helpers::normalizeVarName($token['content']); + Helpers::debug("examining token for variable '{$varName}' on line {$token['line']}", $token); + + // Find the start of the current scope. + $currScope = Helpers::findVariableScope($phpcsFile, $stackPtr); + if ($currScope === null) { + Helpers::debug('no scope found'); + return; + } + Helpers::debug("start of scope for variable '{$varName}' is", $currScope); + + // Determine if variable is being assigned ("write") or used ("read"). + + // Read methods that preempt assignment: + // Are we a $object->$property type symbolic reference? + + // Possible assignment methods: + // Is a mandatory function/closure parameter + // Is an optional function/closure parameter with non-null value + // Is closure use declaration of a variable defined within containing scope + // catch (...) block start + // $this within a class. + // $GLOBALS, $_REQUEST, etc superglobals. + // $var part of class::$var static member + // Assignment via = + // Assignment via list (...) = + // Declares as a global + // Declares as a static + // Assignment via foreach (... as ...) { } + // Pass-by-reference to known pass-by-reference function + + // Are we inside the third expression of a for loop? Store such variables + // for processing after the loop ends by `processClosingForLoopsAt()`. + if (empty($options['ignore-for-loops'])) { + $forLoop = Helpers::getForLoopForIncrementVariable($stackPtr, $this->forLoops); + if ($forLoop) { + Helpers::debug('found variable inside for loop third expression'); + $varInfo = $this->getOrCreateVariableInfo($varName, $currScope); + $forLoop->incrementVariables[$stackPtr] = $varInfo; + return; + } + } + + // Are we a $object->$property type symbolic reference? + if ($this->processVariableAsSymbolicObjectProperty($phpcsFile, $stackPtr, $varName, $currScope)) { + Helpers::debug('found symbolic object property'); + return; + } + + // Are we a function or closure parameter? + if (Helpers::isTokenFunctionParameter($phpcsFile, $stackPtr)) { + Helpers::debug('found function definition parameter'); + $this->processVariableAsFunctionParameter($phpcsFile, $stackPtr, $varName, $currScope); + return; + } + + // Are we a variable being imported into a function's scope with "use"? + if (Helpers::isTokenInsideFunctionUseImport($phpcsFile, $stackPtr)) { + Helpers::debug('found use scope import definition'); + $this->processVariableAsUseImportDefinition($phpcsFile, $stackPtr, $varName, $currScope); + return; + } + + // Are we a catch parameter? + if ($this->processVariableAsCatchBlock($phpcsFile, $stackPtr, $varName, $currScope)) { + Helpers::debug('found catch block'); + return; + } + + // Are we $this within a class? + if ($this->processVariableAsThisWithinClass($phpcsFile, $stackPtr, $varName)) { + Helpers::debug('found this usage within a class'); + return; + } + + // Are we a $GLOBALS, $_REQUEST, etc superglobal? + if ($this->processVariableAsSuperGlobal($varName)) { + Helpers::debug('found superglobal'); + return; + } + + // Check for static members used outside a class + if ($this->processVariableAsStaticOutsideClass($phpcsFile, $stackPtr, $varName)) { + Helpers::debug('found static usage outside of class'); + return; + } + + // $var part of class::$var static member + if ($this->processVariableAsStaticMember($phpcsFile, $stackPtr)) { + Helpers::debug('found static member'); + return; + } + + if ($this->processVariableAsClassProperty($phpcsFile, $stackPtr)) { + Helpers::debug('found class property definition'); + return; + } + + // Is the next non-whitespace an assignment? + if (Helpers::isTokenInsideAssignmentLHS($phpcsFile, $stackPtr)) { + Helpers::debug('found assignment'); + $this->processVariableAsAssignment($phpcsFile, $stackPtr, $varName, $currScope); + if (Helpers::isTokenInsideAssignmentRHS($phpcsFile, $stackPtr) || Helpers::isTokenInsideFunctionCallArgument($phpcsFile, $stackPtr)) { + Helpers::debug("found assignment that's also inside an expression"); + $this->markVariableRead($varName, $stackPtr, $currScope); + return; + } + return; + } + + // OK, are we within a list (...) = construct? + if ($this->processVariableAsListAssignment($phpcsFile, $stackPtr, $varName, $currScope)) { + Helpers::debug('found list assignment'); + return; + } + + // OK, are we within a [...] = construct? + if ($this->processVariableAsListShorthandAssignment($phpcsFile, $stackPtr, $varName, $currScope)) { + Helpers::debug('found list shorthand assignment'); + return; + } + + // Are we a global declaration? + if ($this->processVariableAsGlobalDeclaration($phpcsFile, $stackPtr, $varName, $currScope)) { + Helpers::debug('found global declaration'); + return; + } + + // Are we a static declaration? + if ($this->processVariableAsStaticDeclaration($phpcsFile, $stackPtr, $varName, $currScope)) { + Helpers::debug('found static declaration'); + return; + } + + // Are we a foreach loopvar? + if ($this->processVariableAsForeachLoopVar($phpcsFile, $stackPtr, $varName, $currScope)) { + Helpers::debug('found foreach loop variable'); + return; + } + + // Are we pass-by-reference to known pass-by-reference function? + if ($this->processVariableAsPassByReferenceFunctionCall($phpcsFile, $stackPtr, $varName, $currScope)) { + Helpers::debug('found pass by reference'); + return; + } + + // Are we a numeric variable used for constructs like preg_replace? + if (Helpers::isVariableANumericVariable($varName)) { + Helpers::debug('found numeric variable'); + return; + } + + if (Helpers::isVariableInsideElseCondition($phpcsFile, $stackPtr) || Helpers::isVariableInsideElseBody($phpcsFile, $stackPtr)) { + Helpers::debug('found variable inside else condition or body'); + $this->processVaribleInsideElse($phpcsFile, $stackPtr, $varName, $currScope); + return; + } + + // Are we an isset or empty call? + if (Helpers::isVariableInsideIssetOrEmpty($phpcsFile, $stackPtr)) { + Helpers::debug('found isset or empty'); + $this->markVariableRead($varName, $stackPtr, $currScope); + return; + } + + // OK, we don't appear to be a write to the var, assume we're a read. + Helpers::debug('looks like a variable read'); + $this->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $stackPtr, $currScope); + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * @param string $varName + * @param int $currScope + * + * @return void + */ + protected function processVaribleInsideElse(File $phpcsFile, $stackPtr, $varName, $currScope) + { + // Find all assignments to this variable inside the current scope. + $varInfo = $this->getOrCreateVariableInfo($varName, $currScope); + $allAssignmentIndices = array_unique($varInfo->allAssignments); + // Find the attached 'if' and 'elseif' block start and end indices. + $blockIndices = Helpers::getAttachedBlockIndicesForElse($phpcsFile, $stackPtr); + + // If all of the assignments are within the previous attached blocks, then warn about undefined. + $tokens = $phpcsFile->getTokens(); + $assignmentsInsideAttachedBlocks = []; + foreach ($allAssignmentIndices as $index) { + foreach ($blockIndices as $blockIndex) { + $blockToken = $tokens[$blockIndex]; + Helpers::debug('for variable inside else, looking at assignment', $index, 'at block index', $blockIndex, 'which is token', $blockToken); + if (isset($blockToken['scope_opener']) && isset($blockToken['scope_closer'])) { + $scopeOpener = $blockToken['scope_opener']; + $scopeCloser = $blockToken['scope_closer']; + } else { + // If the `if` statement has no scope, it is probably inline, which + // means its scope is from the end of the condition up until the next + // semicolon + $scopeOpener = isset($blockToken['parenthesis_closer']) ? $blockToken['parenthesis_closer'] : $blockIndex + 1; + $scopeCloser = $phpcsFile->findNext([T_SEMICOLON], $scopeOpener); + if (! $scopeCloser) { + throw new \Exception("Cannot find scope for if condition block at index {$stackPtr} while examining variable {$varName}"); + } + } + Helpers::debug('for variable inside else, looking at scope', $index, 'between', $scopeOpener, 'and', $scopeCloser); + if (Helpers::isIndexInsideScope($index, $scopeOpener, $scopeCloser)) { + $assignmentsInsideAttachedBlocks[] = $index; + } + } + } + + if (count($assignmentsInsideAttachedBlocks) === count($allAssignmentIndices)) { + if (! $varInfo->ignoreUndefined) { + Helpers::debug("variable $varName inside else looks undefined"); + $this->warnAboutUndefinedVariable($phpcsFile, $varName, $stackPtr); + } + return; + } + + Helpers::debug('looks like a variable read inside else'); + $this->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $stackPtr, $currScope); + } + + /** + * Called to process variables found in double quoted strings. + * + * Note that there may be more than one variable in the string, which will + * result only in one call for the string. + * + * @param File $phpcsFile The PHP_CodeSniffer file where this token was found. + * @param int $stackPtr The position where the double quoted string was found. + * + * @return void + */ + protected function processVariableInString(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $token = $tokens[$stackPtr]; + + if (!preg_match_all(Constants::getDoubleQuotedVarRegexp(), $token['content'], $matches)) { + return; + } + Helpers::debug('examining token for variable in string', $token); + + foreach ($matches[1] as $varName) { + $varName = Helpers::normalizeVarName($varName); + + // Are we $this within a class? + if ($this->processVariableAsThisWithinClass($phpcsFile, $stackPtr, $varName)) { + continue; + } + + if ($this->processVariableAsSuperGlobal($varName)) { + continue; + } + + // Are we a numeric variable used for constructs like preg_replace? + if (Helpers::isVariableANumericVariable($varName)) { + continue; + } + + $currScope = Helpers::findVariableScope($phpcsFile, $stackPtr, $varName); + if ($currScope === null) { + continue; + } + + $this->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $stackPtr, $currScope); + } + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * @param array> $arguments The stack pointers of each argument + * @param int $currScope + * + * @return void + */ + protected function processCompactArguments(File $phpcsFile, $stackPtr, $arguments, $currScope) + { + $tokens = $phpcsFile->getTokens(); + + foreach ($arguments as $argumentPtrs) { + $argumentPtrs = array_values(array_filter($argumentPtrs, function ($argumentPtr) use ($tokens) { + return isset(Tokens::$emptyTokens[$tokens[$argumentPtr]['code']]) === false; + })); + if (empty($argumentPtrs)) { + continue; + } + if (!isset($tokens[$argumentPtrs[0]])) { + continue; + } + $argumentFirstToken = $tokens[$argumentPtrs[0]]; + if ($argumentFirstToken['code'] === T_ARRAY) { + // It's an array argument, recurse. + $arrayArguments = Helpers::findFunctionCallArguments($phpcsFile, $argumentPtrs[0]); + $this->processCompactArguments($phpcsFile, $stackPtr, $arrayArguments, $currScope); + continue; + } + if (count($argumentPtrs) > 1) { + // Complex argument, we can't handle it, ignore. + continue; + } + if ($argumentFirstToken['code'] === T_CONSTANT_ENCAPSED_STRING) { + // Single-quoted string literal, ie compact('whatever'). + // Substr is to strip the enclosing single-quotes. + $varName = substr($argumentFirstToken['content'], 1, -1); + $this->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $argumentPtrs[0], $currScope); + continue; + } + if ($argumentFirstToken['code'] === T_DOUBLE_QUOTED_STRING) { + // Double-quoted string literal. + if (preg_match(Constants::getDoubleQuotedVarRegexp(), $argumentFirstToken['content'])) { + // Bail if the string needs variable expansion, that's runtime stuff. + continue; + } + // Substr is to strip the enclosing double-quotes. + $varName = substr($argumentFirstToken['content'], 1, -1); + $this->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $argumentPtrs[0], $currScope); + continue; + } + } + } + + /** + * Called to process variables named in a call to compact(). + * + * @param File $phpcsFile The PHP_CodeSniffer file where this token was found. + * @param int $stackPtr The position where the call to compact() was found. + * + * @return void + */ + protected function processCompact(File $phpcsFile, $stackPtr) + { + $currScope = Helpers::findVariableScope($phpcsFile, $stackPtr); + if ($currScope === null) { + return; + } + + $arguments = Helpers::findFunctionCallArguments($phpcsFile, $stackPtr); + $this->processCompactArguments($phpcsFile, $stackPtr, $arguments, $currScope); + } + + /** + * Called to process the end of a scope. + * + * Note that although triggered by the closing curly brace of the scope, + * $stackPtr is the scope conditional, not the closing curly brace. + * + * @param File $phpcsFile The PHP_CodeSniffer file where this token was found. + * @param int $stackPtr The position of the scope conditional. + * + * @return void + */ + protected function processScopeClose(File $phpcsFile, $stackPtr) + { + $scopeInfo = $this->scopeManager->getScopeForScopeStart($phpcsFile->getFilename(), $stackPtr); + if (is_null($scopeInfo)) { + return; + } + foreach ($scopeInfo->variables as $varInfo) { + $this->processScopeCloseForVariable($phpcsFile, $varInfo, $scopeInfo); + } + } + + /** + * Warn about an unused variable if it has not been used within a scope. + * + * @param File $phpcsFile + * @param VariableInfo $varInfo + * @param ScopeInfo $scopeInfo + * + * @return void + */ + protected function processScopeCloseForVariable(File $phpcsFile, VariableInfo $varInfo, ScopeInfo $scopeInfo) + { + Helpers::debug('processScopeCloseForVariable', $varInfo); + if ($varInfo->ignoreUnused || isset($varInfo->firstRead)) { + return; + } + if ($this->allowUnusedFunctionParameters && $varInfo->scopeType === ScopeType::PARAM) { + return; + } + if ($this->allowUnusedParametersBeforeUsed && $varInfo->scopeType === ScopeType::PARAM && Helpers::areFollowingArgumentsUsed($varInfo, $scopeInfo)) { + Helpers::debug("variable {$varInfo->name} at end of scope has unused following args"); + return; + } + if ($this->allowUnusedForeachVariables && $varInfo->isForeachLoopAssociativeValue) { + return; + } + if ($varInfo->referencedVariableScope !== null && isset($varInfo->firstInitialized)) { + // If we're pass-by-reference then it's a common pattern to + // use the variable to return data to the caller, so any + // assignment also counts as "variable use" for the purposes + // of "unused variable" warnings. + return; + } + if ($varInfo->scopeType === ScopeType::GLOBALSCOPE && isset($varInfo->firstInitialized)) { + // If we imported this variable from the global scope, any further use of + // the variable, including assignment, should count as "variable use" for + // the purposes of "unused variable" warnings. + return; + } + if (empty($varInfo->firstDeclared) && empty($varInfo->firstInitialized)) { + return; + } + if ($this->allowUnusedVariablesBeforeRequire && Helpers::isRequireInScopeAfter($phpcsFile, $varInfo, $scopeInfo)) { + return; + } + if ($scopeInfo->scopeStartIndex === 0 && $this->allowUnusedVariablesInFileScope) { + return; + } + if ( + ! empty($varInfo->firstDeclared) + && $varInfo->scopeType === ScopeType::PARAM + && Helpers::isInAbstractClass( + $phpcsFile, + Helpers::getFunctionIndexForFunctionParameter($phpcsFile, $varInfo->firstDeclared) ?: 0 + ) + && Helpers::isFunctionBodyEmpty( + $phpcsFile, + Helpers::getFunctionIndexForFunctionParameter($phpcsFile, $varInfo->firstDeclared) ?: 0 + ) + ) { + // Allow non-abstract methods inside an abstract class to have unused + // parameters if the method body does nothing. Such methods are + // effectively optional abstract methods so their unused parameters + // should be ignored as we do with abstract method parameters. + return; + } + + $this->warnAboutUnusedVariable($phpcsFile, $varInfo); + } + + /** + * Register warnings for a variable that is defined but not used. + * + * @param File $phpcsFile + * @param VariableInfo $varInfo + * + * @return void + */ + protected function warnAboutUnusedVariable(File $phpcsFile, VariableInfo $varInfo) + { + foreach (array_unique($varInfo->allAssignments) as $indexForWarning) { + Helpers::debug("variable {$varInfo->name} at end of scope looks unused"); + $phpcsFile->addWarning( + 'Unused %s %s.', + $indexForWarning, + 'UnusedVariable', + [ + VariableInfo::$scopeTypeDescriptions[$varInfo->scopeType ?: ScopeType::LOCAL], + "\${$varInfo->name}", + ] + ); + } + } + + /** + * @param File $phpcsFile + * @param string $varName + * @param int $stackPtr + * + * @return void + */ + protected function warnAboutUndefinedVariable(File $phpcsFile, $varName, $stackPtr) + { + $phpcsFile->addWarning( + 'Variable %s is undefined.', + $stackPtr, + 'UndefinedVariable', + ["\${$varName}"] + ); + } + + /** + * @param File $phpcsFile + * @param string $varName + * @param int $stackPtr + * + * @return void + */ + protected function warnAboutUndefinedArrayPushShortcut(File $phpcsFile, $varName, $stackPtr) + { + $phpcsFile->addWarning( + 'Array variable %s is undefined.', + $stackPtr, + 'UndefinedVariable', + ["\${$varName}"] + ); + } + + /** + * @param File $phpcsFile + * @param string $varName + * @param int $stackPtr + * + * @return void + */ + protected function warnAboutUndefinedUnset(File $phpcsFile, $varName, $stackPtr) + { + $phpcsFile->addWarning( + 'Variable %s inside unset call is undefined.', + $stackPtr, + 'UndefinedUnsetVariable', + ["\${$varName}"] + ); + } +} diff --git a/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/ruleset.xml b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/ruleset.xml new file mode 100644 index 00000000..be1815a6 --- /dev/null +++ b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/ruleset.xml @@ -0,0 +1,4 @@ + + + Plugin for PHP_CodeSniffer static analysis tool that adds analysis of problematic variable use. + diff --git a/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/ruleset.xml.example b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/ruleset.xml.example new file mode 100644 index 00000000..4104f567 --- /dev/null +++ b/vendor/sirbrillig/phpcs-variable-analysis/VariableAnalysis/ruleset.xml.example @@ -0,0 +1,11 @@ + + + Plugin for PHP_CodeSniffer static analysis tool that adds analysis of problematic variable use. + + + + + diff --git a/vendor/sirbrillig/phpcs-variable-analysis/composer.json b/vendor/sirbrillig/phpcs-variable-analysis/composer.json new file mode 100644 index 00000000..54c7563f --- /dev/null +++ b/vendor/sirbrillig/phpcs-variable-analysis/composer.json @@ -0,0 +1,61 @@ +{ + "name": "sirbrillig/phpcs-variable-analysis", + "description": "A PHPCS sniff to detect problems with variables.", + "type": "phpcodesniffer-standard", + "keywords" : [ "phpcs", "static analysis" ], + "license": "BSD-2-Clause", + "authors": [ + { + "name": "Sam Graham", + "email": "php-codesniffer-variableanalysis@illusori.co.uk" + }, + { + "name": "Payton Swick", + "email": "payton@foolord.com" + } + ], + "support" : { + "issues": "https://github.com/sirbrillig/phpcs-variable-analysis/issues", + "wiki" : "https://github.com/sirbrillig/phpcs-variable-analysis/wiki", + "source": "https://github.com/sirbrillig/phpcs-variable-analysis" + }, + "config": { + "sort-order": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + }, + "autoload": { + "psr-4": { + "VariableAnalysis\\": "VariableAnalysis/" + } + }, + "autoload-dev": { + "psr-4": { + "VariableAnalysis\\Tests\\": "Tests/" + } + }, + "minimum-stability": "dev", + "prefer-stable": true, + "scripts": { + "test": "./vendor/bin/phpunit --no-coverage", + "coverage": "./vendor/bin/phpunit", + "lint": "./vendor/bin/phpcs", + "fix": "./vendor/bin/phpcbf", + "phpstan": "./vendor/bin/phpstan analyse", + "psalm": "./vendor/bin/psalm --no-cache", + "static-analysis": "composer phpstan && composer psalm" + }, + "require" : { + "php" : ">=5.4.0", + "squizlabs/php_codesniffer": "^3.5.6" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0", + "sirbrillig/phpcs-import-detection": "^1.1", + "phpcsstandards/phpcsdevcs": "^1.1", + "phpstan/phpstan": "^1.7", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0", + "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0@beta" + } +} diff --git a/vendor/sirbrillig/phpcs-variable-analysis/psalm-autoloader.php b/vendor/sirbrillig/phpcs-variable-analysis/psalm-autoloader.php new file mode 100644 index 00000000..5f2cc85e --- /dev/null +++ b/vendor/sirbrillig/phpcs-variable-analysis/psalm-autoloader.php @@ -0,0 +1,4 @@ + + + + + + + + + + + + diff --git a/vendor/squizlabs/php_codesniffer/CONTRIBUTING.md b/vendor/squizlabs/php_codesniffer/CONTRIBUTING.md new file mode 100644 index 00000000..5cc73635 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/CONTRIBUTING.md @@ -0,0 +1,13 @@ +Contributing +------------- + +Before you contribute code to PHP\_CodeSniffer, please make sure it conforms to the PHPCS coding standard and that the PHP\_CodeSniffer unit tests still pass. The easiest way to contribute is to work on a checkout of the repository, or your own fork, rather than an installed PEAR version. If you do this, you can run the following commands to check if everything is ready to submit: + + cd PHP_CodeSniffer + php bin/phpcs + +Which should display no coding standard errors. And then: + + phpunit + +Which should give you no failures or errors. You can ignore any skipped tests as these are for external tools. diff --git a/vendor/squizlabs/php_codesniffer/CodeSniffer.conf.dist b/vendor/squizlabs/php_codesniffer/CodeSniffer.conf.dist new file mode 100644 index 00000000..f95058fa --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/CodeSniffer.conf.dist @@ -0,0 +1,9 @@ + 'PSR2', + 'report_format' => 'summary', + 'show_warnings' => '0', + 'show_progress' => '1', + 'report_width' => '120', +); +?> diff --git a/vendor/squizlabs/php_codesniffer/autoload.php b/vendor/squizlabs/php_codesniffer/autoload.php new file mode 100644 index 00000000..0dcf1b4c --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/autoload.php @@ -0,0 +1,342 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer; + +if (class_exists('PHP_CodeSniffer\Autoload', false) === false) { + class Autoload + { + + /** + * The composer autoloader. + * + * @var \Composer\Autoload\ClassLoader + */ + private static $composerAutoloader = null; + + /** + * A mapping of file names to class names. + * + * @var array + */ + private static $loadedClasses = []; + + /** + * A mapping of class names to file names. + * + * @var array + */ + private static $loadedFiles = []; + + /** + * A list of additional directories to search during autoloading. + * + * This is typically a list of coding standard directories. + * + * @var string[] + */ + private static $searchPaths = []; + + + /** + * Loads a class. + * + * This method only loads classes that exist in the PHP_CodeSniffer namespace. + * All other classes are ignored and loaded by subsequent autoloaders. + * + * @param string $class The name of the class to load. + * + * @return bool + */ + public static function load($class) + { + // Include the composer autoloader if there is one, but re-register it + // so this autoloader runs before the composer one as we need to include + // all files so we can figure out what the class/interface/trait name is. + if (self::$composerAutoloader === null) { + // Make sure we don't try to load any of Composer's classes + // while the autoloader is being setup. + if (strpos($class, 'Composer\\') === 0) { + return; + } + + if (strpos(__DIR__, 'phar://') !== 0 + && @file_exists(__DIR__.'/../../autoload.php') === true + ) { + self::$composerAutoloader = include __DIR__.'/../../autoload.php'; + if (self::$composerAutoloader instanceof \Composer\Autoload\ClassLoader) { + self::$composerAutoloader->unregister(); + self::$composerAutoloader->register(); + } else { + // Something went wrong, so keep going without the autoloader + // although namespaced sniffs might error. + self::$composerAutoloader = false; + } + } else { + self::$composerAutoloader = false; + } + }//end if + + $ds = DIRECTORY_SEPARATOR; + $path = false; + + if (substr($class, 0, 16) === 'PHP_CodeSniffer\\') { + if (substr($class, 0, 22) === 'PHP_CodeSniffer\Tests\\') { + $isInstalled = !is_dir(__DIR__.$ds.'tests'); + if ($isInstalled === false) { + $path = __DIR__.$ds.'tests'; + } else { + $path = '@test_dir@'.$ds.'PHP_CodeSniffer'.$ds.'CodeSniffer'; + } + + $path .= $ds.substr(str_replace('\\', $ds, $class), 22).'.php'; + } else { + $path = __DIR__.$ds.'src'.$ds.substr(str_replace('\\', $ds, $class), 16).'.php'; + } + } + + // See if the composer autoloader knows where the class is. + if ($path === false && self::$composerAutoloader !== false) { + $path = self::$composerAutoloader->findFile($class); + } + + // See if the class is inside one of our alternate search paths. + if ($path === false) { + foreach (self::$searchPaths as $searchPath => $nsPrefix) { + $className = $class; + if ($nsPrefix !== '' && substr($class, 0, strlen($nsPrefix)) === $nsPrefix) { + $className = substr($class, (strlen($nsPrefix) + 1)); + } + + $path = $searchPath.$ds.str_replace('\\', $ds, $className).'.php'; + if (is_file($path) === true) { + break; + } + + $path = false; + } + } + + if ($path !== false && is_file($path) === true) { + self::loadFile($path); + return true; + } + + return false; + + }//end load() + + + /** + * Includes a file and tracks what class or interface was loaded as a result. + * + * @param string $path The path of the file to load. + * + * @return string The fully qualified name of the class in the loaded file. + */ + public static function loadFile($path) + { + if (strpos(__DIR__, 'phar://') !== 0) { + $path = realpath($path); + if ($path === false) { + return false; + } + } + + if (isset(self::$loadedClasses[$path]) === true) { + return self::$loadedClasses[$path]; + } + + $classesBeforeLoad = [ + 'classes' => get_declared_classes(), + 'interfaces' => get_declared_interfaces(), + 'traits' => get_declared_traits(), + ]; + + include $path; + + $classesAfterLoad = [ + 'classes' => get_declared_classes(), + 'interfaces' => get_declared_interfaces(), + 'traits' => get_declared_traits(), + ]; + + $className = self::determineLoadedClass($classesBeforeLoad, $classesAfterLoad); + + self::$loadedClasses[$path] = $className; + self::$loadedFiles[$className] = $path; + return self::$loadedClasses[$path]; + + }//end loadFile() + + + /** + * Determine which class was loaded based on the before and after lists of loaded classes. + * + * @param array $classesBeforeLoad The classes/interfaces/traits before the file was included. + * @param array $classesAfterLoad The classes/interfaces/traits after the file was included. + * + * @return string The fully qualified name of the class in the loaded file. + */ + public static function determineLoadedClass($classesBeforeLoad, $classesAfterLoad) + { + $className = null; + + $newClasses = array_diff($classesAfterLoad['classes'], $classesBeforeLoad['classes']); + if (PHP_VERSION_ID < 70400) { + $newClasses = array_reverse($newClasses); + } + + // Since PHP 7.4 get_declared_classes() does not guarantee any order, making + // it impossible to use order to determine which is the parent an which is the child. + // Let's reduce the list of candidates by removing all the classes known to be "parents". + // That way, at the end, only the "main" class just included will remain. + $newClasses = array_reduce( + $newClasses, + function ($remaining, $current) { + return array_diff($remaining, class_parents($current)); + }, + $newClasses + ); + + foreach ($newClasses as $name) { + if (isset(self::$loadedFiles[$name]) === false) { + $className = $name; + break; + } + } + + if ($className === null) { + $newClasses = array_reverse(array_diff($classesAfterLoad['interfaces'], $classesBeforeLoad['interfaces'])); + foreach ($newClasses as $name) { + if (isset(self::$loadedFiles[$name]) === false) { + $className = $name; + break; + } + } + } + + if ($className === null) { + $newClasses = array_reverse(array_diff($classesAfterLoad['traits'], $classesBeforeLoad['traits'])); + foreach ($newClasses as $name) { + if (isset(self::$loadedFiles[$name]) === false) { + $className = $name; + break; + } + } + } + + return $className; + + }//end determineLoadedClass() + + + /** + * Adds a directory to search during autoloading. + * + * @param string $path The path to the directory to search. + * @param string $nsPrefix The namespace prefix used by files under this path. + * + * @return void + */ + public static function addSearchPath($path, $nsPrefix='') + { + self::$searchPaths[$path] = rtrim(trim((string) $nsPrefix), '\\'); + + }//end addSearchPath() + + + /** + * Retrieve the namespaces and paths registered by external standards. + * + * @return array + */ + public static function getSearchPaths() + { + return self::$searchPaths; + + }//end getSearchPaths() + + + /** + * Gets the class name for the given file path. + * + * @param string $path The name of the file. + * + * @throws \Exception If the file path has not been loaded. + * @return string + */ + public static function getLoadedClassName($path) + { + if (isset(self::$loadedClasses[$path]) === false) { + throw new \Exception("Cannot get class name for $path; file has not been included"); + } + + return self::$loadedClasses[$path]; + + }//end getLoadedClassName() + + + /** + * Gets the file path for the given class name. + * + * @param string $class The name of the class. + * + * @throws \Exception If the class name has not been loaded + * @return string + */ + public static function getLoadedFileName($class) + { + if (isset(self::$loadedFiles[$class]) === false) { + throw new \Exception("Cannot get file name for $class; class has not been included"); + } + + return self::$loadedFiles[$class]; + + }//end getLoadedFileName() + + + /** + * Gets the mapping of file names to class names. + * + * @return array + */ + public static function getLoadedClasses() + { + return self::$loadedClasses; + + }//end getLoadedClasses() + + + /** + * Gets the mapping of class names to file names. + * + * @return array + */ + public static function getLoadedFiles() + { + return self::$loadedFiles; + + }//end getLoadedFiles() + + + }//end class + + // Register the autoloader before any existing autoloaders to ensure + // it gets a chance to hear about every autoload request, and record + // the file and class name for it. + spl_autoload_register(__NAMESPACE__.'\Autoload::load', true, true); +}//end if diff --git a/vendor/squizlabs/php_codesniffer/bin/phpcbf b/vendor/squizlabs/php_codesniffer/bin/phpcbf new file mode 100755 index 00000000..45b43f43 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/bin/phpcbf @@ -0,0 +1,19 @@ +#!/usr/bin/env php + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +if (is_file(__DIR__.'/../autoload.php') === true) { + include_once __DIR__.'/../autoload.php'; +} else { + include_once 'PHP/CodeSniffer/autoload.php'; +} + +$runner = new PHP_CodeSniffer\Runner(); +$exitCode = $runner->runPHPCBF(); +exit($exitCode); diff --git a/vendor/squizlabs/php_codesniffer/bin/phpcbf.bat b/vendor/squizlabs/php_codesniffer/bin/phpcbf.bat new file mode 100644 index 00000000..5b07a7d9 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/bin/phpcbf.bat @@ -0,0 +1,12 @@ +@echo off +REM PHP Code Beautifier and Fixer fixes violations of a defined coding standard. +REM +REM @author Greg Sherwood +REM @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) +REM @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + +if "%PHP_PEAR_PHP_BIN%" neq "" ( + set PHPBIN=%PHP_PEAR_PHP_BIN% +) else set PHPBIN=php + +"%PHPBIN%" "%~dp0\phpcbf" %* diff --git a/vendor/squizlabs/php_codesniffer/bin/phpcs b/vendor/squizlabs/php_codesniffer/bin/phpcs new file mode 100755 index 00000000..52d28cdf --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/bin/phpcs @@ -0,0 +1,19 @@ +#!/usr/bin/env php + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +if (is_file(__DIR__.'/../autoload.php') === true) { + include_once __DIR__.'/../autoload.php'; +} else { + include_once 'PHP/CodeSniffer/autoload.php'; +} + +$runner = new PHP_CodeSniffer\Runner(); +$exitCode = $runner->runPHPCS(); +exit($exitCode); diff --git a/vendor/squizlabs/php_codesniffer/bin/phpcs.bat b/vendor/squizlabs/php_codesniffer/bin/phpcs.bat new file mode 100755 index 00000000..9f9be720 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/bin/phpcs.bat @@ -0,0 +1,12 @@ +@echo off +REM PHP_CodeSniffer detects violations of a defined coding standard. +REM +REM @author Greg Sherwood +REM @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) +REM @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + +if "%PHP_PEAR_PHP_BIN%" neq "" ( + set PHPBIN=%PHP_PEAR_PHP_BIN% +) else set PHPBIN=php + +"%PHPBIN%" "%~dp0\phpcs" %* diff --git a/vendor/squizlabs/php_codesniffer/composer.json b/vendor/squizlabs/php_codesniffer/composer.json new file mode 100644 index 00000000..37f41a0b --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/composer.json @@ -0,0 +1,41 @@ +{ + "name": "squizlabs/php_codesniffer", + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "type": "library", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki", + "source": "https://github.com/squizlabs/PHP_CodeSniffer" + }, + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "require": { + "php": ">=5.4.0", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "ext-simplexml": "*" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ] +} diff --git a/vendor/squizlabs/php_codesniffer/licence.txt b/vendor/squizlabs/php_codesniffer/licence.txt new file mode 100644 index 00000000..9f95b677 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/licence.txt @@ -0,0 +1,24 @@ +Copyright (c) 2012, Squiz Pty Ltd (ABN 77 084 670 600) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/squizlabs/php_codesniffer/phpcs.xsd b/vendor/squizlabs/php_codesniffer/phpcs.xsd new file mode 100644 index 00000000..d93dd868 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/phpcs.xsd @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/squizlabs/php_codesniffer/tests/AllTests.php b/vendor/squizlabs/php_codesniffer/tests/AllTests.php new file mode 100644 index 00000000..9d099c1e --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/AllTests.php @@ -0,0 +1,64 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests; + +if ($GLOBALS['PHP_CODESNIFFER_PEAR'] === false) { + include_once 'Core/AllTests.php'; + include_once 'Standards/AllSniffs.php'; +} else { + include_once 'CodeSniffer/Core/AllTests.php'; + include_once 'CodeSniffer/Standards/AllSniffs.php'; + include_once 'FileList.php'; +} + +// PHPUnit 7 made the TestSuite run() method incompatible with +// older PHPUnit versions due to return type hints, so maintain +// two different suite objects. +$phpunit7 = false; +if (class_exists('\PHPUnit\Runner\Version') === true) { + $version = \PHPUnit\Runner\Version::id(); + if ($version[0] === '7') { + $phpunit7 = true; + } +} + +if ($phpunit7 === true) { + include_once 'TestSuite7.php'; +} else { + include_once 'TestSuite.php'; +} + +class PHP_CodeSniffer_AllTests +{ + + + /** + * Add all PHP_CodeSniffer test suites into a single test suite. + * + * @return \PHPUnit\Framework\TestSuite + */ + public static function suite() + { + $GLOBALS['PHP_CODESNIFFER_STANDARD_DIRS'] = []; + $GLOBALS['PHP_CODESNIFFER_TEST_DIRS'] = []; + + // Use a special PHP_CodeSniffer test suite so that we can + // unset our autoload function after the run. + $suite = new TestSuite('PHP CodeSniffer'); + + $suite->addTest(Core\AllTests::suite()); + $suite->addTest(Standards\AllSniffs::suite()); + + return $suite; + + }//end suite() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/AbstractMethodUnitTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/AbstractMethodUnitTest.php new file mode 100644 index 00000000..4d4f5469 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/AbstractMethodUnitTest.php @@ -0,0 +1,140 @@ + + * @copyright 2018-2019 Juliette Reinders Folmer. All rights reserved. + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core; + +use PHP_CodeSniffer\Config; +use PHP_CodeSniffer\Ruleset; +use PHP_CodeSniffer\Files\DummyFile; +use PHPUnit\Framework\TestCase; + +abstract class AbstractMethodUnitTest extends TestCase +{ + + /** + * The file extension of the test case file (without leading dot). + * + * This allows child classes to overrule the default `inc` with, for instance, + * `js` or `css` when applicable. + * + * @var string + */ + protected static $fileExtension = 'inc'; + + /** + * The \PHP_CodeSniffer\Files\File object containing the parsed contents of the test case file. + * + * @var \PHP_CodeSniffer\Files\File + */ + protected static $phpcsFile; + + + /** + * Initialize & tokenize \PHP_CodeSniffer\Files\File with code from 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. + * + * @return void + */ + public static function setUpBeforeClass() + { + $config = new Config(); + $config->standards = ['PSR1']; + + $ruleset = new Ruleset($config); + + // Default to a file with the same name as the test class. Extension is property based. + $relativeCN = str_replace(__NAMESPACE__, '', get_called_class()); + $relativePath = str_replace('\\', DIRECTORY_SEPARATOR, $relativeCN); + $pathToTestFile = realpath(__DIR__).$relativePath.'.'.static::$fileExtension; + + // Make sure the file gets parsed correctly based on the file type. + $contents = 'phpcs_input_file: '.$pathToTestFile.PHP_EOL; + $contents .= file_get_contents($pathToTestFile); + + self::$phpcsFile = new DummyFile($contents, $ruleset, $config); + self::$phpcsFile->process(); + + }//end setUpBeforeClass() + + + /** + * Clean up after finished test. + * + * @return void + */ + public static function tearDownAfterClass() + { + self::$phpcsFile = null; + + }//end tearDownAfterClass() + + + /** + * Get the token pointer for a target token based on a specific comment found on the line before. + * + * Note: the test delimiter comment MUST start with "/* test" to allow this function to + * distinguish between comments used *in* a test and test delimiters. + * + * @param string $commentString The delimiter comment to look for. + * @param int|string|array $tokenType The type of token(s) to look for. + * @param string $tokenContent Optional. The token content for the target token. + * + * @return int + */ + public function getTargetToken($commentString, $tokenType, $tokenContent=null) + { + $start = (self::$phpcsFile->numTokens - 1); + $comment = self::$phpcsFile->findPrevious( + T_COMMENT, + $start, + null, + false, + $commentString + ); + + $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; + } + + $this->assertFalse(true, $msg); + } + + return $target; + + }//end getTargetToken() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/AllTests.php b/vendor/squizlabs/php_codesniffer/tests/Core/AllTests.php new file mode 100644 index 00000000..304690ef --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/AllTests.php @@ -0,0 +1,63 @@ + + * @author Juliette Reinders Folmer + * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core; + +use PHP_CodeSniffer\Tests\FileList; +use PHPUnit\TextUI\TestRunner; +use PHPUnit\Framework\TestSuite; + +class AllTests +{ + + + /** + * Prepare the test runner. + * + * @return void + */ + public static function main() + { + TestRunner::run(self::suite()); + + }//end main() + + + /** + * Add all core unit tests into a test suite. + * + * @return \PHPUnit\Framework\TestSuite + */ + public static function suite() + { + $suite = new TestSuite('PHP CodeSniffer Core'); + + $testFileIterator = new FileList(__DIR__, '', '`Test\.php$`Di'); + foreach ($testFileIterator->fileIterator as $file) { + if (strpos($file, 'AbstractMethodUnitTest.php') !== false) { + continue; + } + + include_once $file; + + $class = str_replace(__DIR__, '', $file); + $class = str_replace('.php', '', $class); + $class = str_replace('/', '\\', $class); + $class = 'PHP_CodeSniffer\Tests\Core'.$class; + + $suite->addTestSuite($class); + } + + return $suite; + + }//end suite() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Autoloader/DetermineLoadedClassTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Autoloader/DetermineLoadedClassTest.php new file mode 100644 index 00000000..c0f38fa6 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Autoloader/DetermineLoadedClassTest.php @@ -0,0 +1,119 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Autoloader; + +use PHP_CodeSniffer\Autoload; +use PHPUnit\Framework\TestCase; + +class DetermineLoadedClassTest extends TestCase +{ + + + /** + * Load the test files. + * + * @return void + */ + public static function setUpBeforeClass() + { + include __DIR__.'/TestFiles/Sub/C.inc'; + + }//end setUpBeforeClass() + + + /** + * Test for when class list is ordered. + * + * @return void + */ + public function testOrdered() + { + $classesBeforeLoad = [ + 'classes' => [], + 'interfaces' => [], + 'traits' => [], + ]; + + $classesAfterLoad = [ + 'classes' => [ + 'PHP_CodeSniffer\Tests\Core\Autoloader\A', + 'PHP_CodeSniffer\Tests\Core\Autoloader\B', + 'PHP_CodeSniffer\Tests\Core\Autoloader\C', + 'PHP_CodeSniffer\Tests\Core\Autoloader\Sub\C', + ], + 'interfaces' => [], + 'traits' => [], + ]; + + $className = Autoload::determineLoadedClass($classesBeforeLoad, $classesAfterLoad); + $this->assertEquals('PHP_CodeSniffer\Tests\Core\Autoloader\Sub\C', $className); + + }//end testOrdered() + + + /** + * Test for when class list is out of order. + * + * @return void + */ + public function testUnordered() + { + $classesBeforeLoad = [ + 'classes' => [], + 'interfaces' => [], + 'traits' => [], + ]; + + $classesAfterLoad = [ + 'classes' => [ + 'PHP_CodeSniffer\Tests\Core\Autoloader\A', + 'PHP_CodeSniffer\Tests\Core\Autoloader\Sub\C', + 'PHP_CodeSniffer\Tests\Core\Autoloader\C', + 'PHP_CodeSniffer\Tests\Core\Autoloader\B', + ], + 'interfaces' => [], + 'traits' => [], + ]; + + $className = Autoload::determineLoadedClass($classesBeforeLoad, $classesAfterLoad); + $this->assertEquals('PHP_CodeSniffer\Tests\Core\Autoloader\Sub\C', $className); + + $classesAfterLoad = [ + 'classes' => [ + 'PHP_CodeSniffer\Tests\Core\Autoloader\A', + 'PHP_CodeSniffer\Tests\Core\Autoloader\C', + 'PHP_CodeSniffer\Tests\Core\Autoloader\Sub\C', + 'PHP_CodeSniffer\Tests\Core\Autoloader\B', + ], + 'interfaces' => [], + 'traits' => [], + ]; + + $className = Autoload::determineLoadedClass($classesBeforeLoad, $classesAfterLoad); + $this->assertEquals('PHP_CodeSniffer\Tests\Core\Autoloader\Sub\C', $className); + + $classesAfterLoad = [ + 'classes' => [ + 'PHP_CodeSniffer\Tests\Core\Autoloader\Sub\C', + 'PHP_CodeSniffer\Tests\Core\Autoloader\A', + 'PHP_CodeSniffer\Tests\Core\Autoloader\C', + 'PHP_CodeSniffer\Tests\Core\Autoloader\B', + ], + 'interfaces' => [], + 'traits' => [], + ]; + + $className = Autoload::determineLoadedClass($classesBeforeLoad, $classesAfterLoad); + $this->assertEquals('PHP_CodeSniffer\Tests\Core\Autoloader\Sub\C', $className); + + }//end testUnordered() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Autoloader/TestFiles/A.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Autoloader/TestFiles/A.inc new file mode 100644 index 00000000..c1433718 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Autoloader/TestFiles/A.inc @@ -0,0 +1,3 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core; + +use PHP_CodeSniffer\Config; +use PHP_CodeSniffer\Ruleset; +use PHP_CodeSniffer\Files\DummyFile; +use PHPUnit\Framework\TestCase; + +class ErrorSuppressionTest extends TestCase +{ + + + /** + * Test suppressing a single error. + * + * @param string $before Annotation to place before the code. + * @param string $after Annotation to place after the code. + * @param int $expectedErrors Optional. Number of errors expected. + * Defaults to 0. + * + * @dataProvider dataSuppressError + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testSuppressError($before, $after, $expectedErrors=0) + { + static $config, $ruleset; + + if (isset($config, $ruleset) === false) { + $config = new Config(); + $config->standards = ['Generic']; + $config->sniffs = ['Generic.PHP.LowerCaseConstant']; + + $ruleset = new Ruleset($config); + } + + $content = 'process(); + + $this->assertSame($expectedErrors, $file->getErrorCount()); + $this->assertCount($expectedErrors, $file->getErrors()); + + }//end testSuppressError() + + + /** + * Data provider. + * + * @see testSuppressError() + * + * @return array + */ + public function dataSuppressError() + { + return [ + 'no suppression' => [ + 'before' => '', + 'after' => '', + 'expectedErrors' => 1, + ], + + // Inline slash comments. + 'disable/enable: slash comment' => [ + 'before' => '// phpcs:disable'.PHP_EOL, + 'after' => '// phpcs:enable', + ], + 'disable/enable: multi-line slash comment, tab indented' => [ + 'before' => "\t".'// For reasons'.PHP_EOL."\t".'// phpcs:disable'.PHP_EOL."\t", + 'after' => "\t".'// phpcs:enable', + ], + 'disable/enable: slash comment, with @' => [ + 'before' => '// @phpcs:disable'.PHP_EOL, + 'after' => '// @phpcs:enable', + ], + 'disable/enable: slash comment, mixed case' => [ + 'before' => '// PHPCS:Disable'.PHP_EOL, + 'after' => '// pHPcs:enabLE', + ], + + // Inline hash comments. + 'disable/enable: hash comment' => [ + 'before' => '# phpcs:disable'.PHP_EOL, + 'after' => '# phpcs:enable', + ], + 'disable/enable: multi-line hash comment, tab indented' => [ + 'before' => "\t".'# For reasons'.PHP_EOL."\t".'# phpcs:disable'.PHP_EOL."\t", + 'after' => "\t".'# phpcs:enable', + ], + 'disable/enable: hash comment, with @' => [ + 'before' => '# @phpcs:disable'.PHP_EOL, + 'after' => '# @phpcs:enable', + ], + 'disable/enable: hash comment, mixed case' => [ + 'before' => '# PHPCS:Disable'.PHP_EOL, + 'after' => '# pHPcs:enabLE', + ], + + // Inline star (block) comments. + 'disable/enable: star comment' => [ + 'before' => '/* phpcs:disable */'.PHP_EOL, + 'after' => '/* phpcs:enable */', + ], + 'disable/enable: multi-line star comment' => [ + 'before' => '/*'.PHP_EOL.' phpcs:disable'.PHP_EOL.' */'.PHP_EOL, + 'after' => '/*'.PHP_EOL.' phpcs:enable'.PHP_EOL.' */', + ], + 'disable/enable: multi-line star comment, each line starred' => [ + 'before' => '/*'.PHP_EOL.' * phpcs:disable'.PHP_EOL.' */'.PHP_EOL, + 'after' => '/*'.PHP_EOL.' * phpcs:enable'.PHP_EOL.' */', + ], + 'disable/enable: multi-line star comment, each line starred, tab indented' => [ + 'before' => "\t".'/*'.PHP_EOL."\t".' * phpcs:disable'.PHP_EOL."\t".' */'.PHP_EOL."\t", + 'after' => "\t".'/*'.PHP_EOL.' * phpcs:enable'.PHP_EOL.' */', + ], + + // Docblock comments. + 'disable/enable: single line docblock comment' => [ + 'before' => '/** phpcs:disable */'.PHP_EOL, + 'after' => '/** phpcs:enable */', + ], + + // Deprecated syntax. + 'old style: slash comment' => [ + 'before' => '// @codingStandardsIgnoreStart'.PHP_EOL, + 'after' => '// @codingStandardsIgnoreEnd', + ], + 'old style: star comment' => [ + 'before' => '/* @codingStandardsIgnoreStart */'.PHP_EOL, + 'after' => '/* @codingStandardsIgnoreEnd */', + ], + 'old style: multi-line star comment' => [ + 'before' => '/*'.PHP_EOL.' @codingStandardsIgnoreStart'.PHP_EOL.' */'.PHP_EOL, + 'after' => '/*'.PHP_EOL.' @codingStandardsIgnoreEnd'.PHP_EOL.' */', + ], + 'old style: single line docblock comment' => [ + 'before' => '/** @codingStandardsIgnoreStart */'.PHP_EOL, + 'after' => '/** @codingStandardsIgnoreEnd */', + ], + ]; + + }//end dataSuppressError() + + + /** + * Test suppressing 1 out of 2 errors. + * + * @param string $before Annotation to place before the code. + * @param string $between Annotation to place between the code. + * @param int $expectedErrors Optional. Number of errors expected. + * Defaults to 1. + * + * @dataProvider dataSuppressSomeErrors + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testSuppressSomeErrors($before, $between, $expectedErrors=1) + { + static $config, $ruleset; + + if (isset($config, $ruleset) === false) { + $config = new Config(); + $config->standards = ['Generic']; + $config->sniffs = ['Generic.PHP.LowerCaseConstant']; + + $ruleset = new Ruleset($config); + } + + $content = <<process(); + + $this->assertSame($expectedErrors, $file->getErrorCount()); + $this->assertCount($expectedErrors, $file->getErrors()); + + }//end testSuppressSomeErrors() + + + /** + * Data provider. + * + * @see testSuppressSomeErrors() + * + * @return array + */ + public function dataSuppressSomeErrors() + { + return [ + 'no suppression' => [ + 'before' => '', + 'between' => '', + 'expectedErrors' => 2, + ], + + // With suppression. + 'disable/enable: slash comment' => [ + 'before' => '// phpcs:disable', + 'between' => '// phpcs:enable', + ], + 'disable/enable: slash comment, with @' => [ + 'before' => '// @phpcs:disable', + 'between' => '// @phpcs:enable', + ], + 'disable/enable: hash comment' => [ + 'before' => '# phpcs:disable', + 'between' => '# phpcs:enable', + ], + 'disable/enable: hash comment, with @' => [ + 'before' => '# @phpcs:disable', + 'between' => '# @phpcs:enable', + ], + 'disable/enable: single line docblock comment' => [ + 'before' => '/** phpcs:disable */', + 'between' => '/** phpcs:enable */', + ], + + // Deprecated syntax. + 'old style: slash comment' => [ + 'before' => '// @codingStandardsIgnoreStart', + 'between' => '// @codingStandardsIgnoreEnd', + ], + 'old style: single line docblock comment' => [ + 'before' => '/** @codingStandardsIgnoreStart */', + 'between' => '/** @codingStandardsIgnoreEnd */', + ], + ]; + + }//end dataSuppressSomeErrors() + + + /** + * Test suppressing a single warning. + * + * @param string $before Annotation to place before the code. + * @param string $after Annotation to place after the code. + * @param int $expectedWarnings Optional. Number of warnings expected. + * Defaults to 0. + * + * @dataProvider dataSuppressWarning + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testSuppressWarning($before, $after, $expectedWarnings=0) + { + static $config, $ruleset; + + if (isset($config, $ruleset) === false) { + $config = new Config(); + $config->standards = ['Generic']; + $config->sniffs = ['Generic.Commenting.Todo']; + + $ruleset = new Ruleset($config); + } + + $content = <<process(); + + $this->assertSame($expectedWarnings, $file->getWarningCount()); + $this->assertCount($expectedWarnings, $file->getWarnings()); + + }//end testSuppressWarning() + + + /** + * Data provider. + * + * @see testSuppressWarning() + * + * @return array + */ + public function dataSuppressWarning() + { + return [ + 'no suppression' => [ + 'before' => '', + 'after' => '', + 'expectedWarnings' => 1, + ], + + // With suppression. + 'disable/enable: slash comment' => [ + 'before' => '// phpcs:disable', + 'after' => '// phpcs:enable', + ], + 'disable/enable: slash comment, with @' => [ + 'before' => '// @phpcs:disable', + 'after' => '// @phpcs:enable', + ], + 'disable/enable: single line docblock comment' => [ + 'before' => '/** phpcs:disable */', + 'after' => '/** phpcs:enable */', + ], + + // Deprecated syntax. + 'old style: slash comment' => [ + 'before' => '// @codingStandardsIgnoreStart', + 'after' => '// @codingStandardsIgnoreEnd', + ], + 'old style: single line docblock comment' => [ + 'before' => '/** @codingStandardsIgnoreStart */', + 'after' => '/** @codingStandardsIgnoreEnd */', + ], + ]; + + }//end dataSuppressWarning() + + + /** + * Test suppressing a single error using a single line ignore. + * + * @param string $before Annotation to place before the code. + * @param string $after Optional. Annotation to place after the code. + * Defaults to an empty string. + * @param int $expectedErrors Optional. Number of errors expected. + * Defaults to 1. + * + * @dataProvider dataSuppressLine + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testSuppressLine($before, $after='', $expectedErrors=1) + { + static $config, $ruleset; + + if (isset($config, $ruleset) === false) { + $config = new Config(); + $config->standards = ['Generic']; + $config->sniffs = ['Generic.PHP.LowerCaseConstant']; + + $ruleset = new Ruleset($config); + } + + $content = <<process(); + + $this->assertSame($expectedErrors, $file->getErrorCount()); + $this->assertCount($expectedErrors, $file->getErrors()); + + }//end testSuppressLine() + + + /** + * Data provider. + * + * @see testSuppressLine() + * + * @return array + */ + public function dataSuppressLine() + { + return [ + 'no suppression' => [ + 'before' => '', + 'after' => '', + 'expectedErrors' => 2, + ], + + // With suppression on line before. + 'ignore: line before, slash comment' => ['before' => '// phpcs:ignore'], + 'ignore: line before, slash comment, with @' => ['before' => '// @phpcs:ignore'], + 'ignore: line before, hash comment' => ['before' => '# phpcs:ignore'], + 'ignore: line before, hash comment, with @' => ['before' => '# @phpcs:ignore'], + 'ignore: line before, star comment' => ['before' => '/* phpcs:ignore */'], + 'ignore: line before, star comment, with @' => ['before' => '/* @phpcs:ignore */'], + + // With suppression as trailing comment on code line. + 'ignore: end of line, slash comment' => [ + 'before' => '', + 'after' => ' // phpcs:ignore', + ], + 'ignore: end of line, slash comment, with @' => [ + 'before' => '', + 'after' => ' // @phpcs:ignore', + ], + 'ignore: end of line, hash comment' => [ + 'before' => '', + 'after' => ' # phpcs:ignore', + ], + 'ignore: end of line, hash comment, with @' => [ + 'before' => '', + 'after' => ' # @phpcs:ignore', + ], + + // Deprecated syntax. + 'old style: line before, slash comment' => ['before' => '// @codingStandardsIgnoreLine'], + 'old style: end of line, slash comment' => [ + 'before' => '', + 'after' => ' // @codingStandardsIgnoreLine', + ], + ]; + + }//end dataSuppressLine() + + + /** + * Test suppressing a single error using a single line ignore in the middle of a line. + * + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testSuppressLineMidLine() + { + $config = new Config(); + $config->standards = ['Generic']; + $config->sniffs = ['Generic.PHP.LowerCaseConstant']; + + $ruleset = new Ruleset($config); + + $content = 'process(); + + $this->assertSame(0, $file->getErrorCount()); + $this->assertCount(0, $file->getErrors()); + + }//end testSuppressLineMidLine() + + + /** + * Test suppressing a single error using a single line ignore within a docblock. + * + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testSuppressLineWithinDocblock() + { + $config = new Config(); + $config->standards = ['Generic']; + $config->sniffs = ['Generic.Files.LineLength']; + + $ruleset = new Ruleset($config); + + // Process with @ suppression on line before inside docblock. + $comment = str_repeat('a ', 50); + $content = <<process(); + + $this->assertSame(0, $file->getErrorCount()); + $this->assertCount(0, $file->getErrors()); + + }//end testSuppressLineWithinDocblock() + + + /** + * Test that using a single line ignore does not interfere with other suppressions. + * + * @param string $before Annotation to place before the code. + * @param string $after Annotation to place after the code. + * + * @dataProvider dataNestedSuppressLine + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testNestedSuppressLine($before, $after) + { + static $config, $ruleset; + + if (isset($config, $ruleset) === false) { + $config = new Config(); + $config->standards = ['Generic']; + $config->sniffs = ['Generic.PHP.LowerCaseConstant']; + + $ruleset = new Ruleset($config); + } + + $content = <<process(); + + $this->assertSame(0, $file->getErrorCount()); + $this->assertCount(0, $file->getErrors()); + + }//end testNestedSuppressLine() + + + /** + * Data provider. + * + * @see testNestedSuppressLine() + * + * @return array + */ + public function dataNestedSuppressLine() + { + return [ + // Process with disable/enable suppression and no single line suppression. + 'disable/enable: slash comment, no single line suppression' => [ + 'before' => '// phpcs:disable', + 'after' => '// phpcs:enable', + ], + 'disable/enable: slash comment, with @, no single line suppression' => [ + 'before' => '// @phpcs:disable', + 'after' => '// @phpcs:enable', + ], + 'disable/enable: hash comment, no single line suppression' => [ + 'before' => '# phpcs:disable', + 'after' => '# phpcs:enable', + ], + 'old style: slash comment, no single line suppression' => [ + 'before' => '// @codingStandardsIgnoreStart', + 'after' => '// @codingStandardsIgnoreEnd', + ], + + // Process with line suppression nested within disable/enable suppression. + 'disable/enable: slash comment, next line nested single line suppression' => [ + 'before' => '// phpcs:disable'.PHP_EOL.'// phpcs:ignore', + 'after' => '// phpcs:enable', + ], + 'disable/enable: slash comment, with @, next line nested single line suppression' => [ + 'before' => '// @phpcs:disable'.PHP_EOL.'// @phpcs:ignore', + 'after' => '// @phpcs:enable', + ], + 'disable/enable: hash comment, next line nested single line suppression' => [ + 'before' => '# @phpcs:disable'.PHP_EOL.'# @phpcs:ignore', + 'after' => '# @phpcs:enable', + ], + 'old style: slash comment, next line nested single line suppression' => [ + 'before' => '// @codingStandardsIgnoreStart'.PHP_EOL.'// @codingStandardsIgnoreLine', + 'after' => '// @codingStandardsIgnoreEnd', + ], + ]; + + }//end dataNestedSuppressLine() + + + /** + * Test suppressing a scope opener. + * + * @param string $before Annotation to place before the scope opener. + * @param string $after Annotation to place after the scope opener. + * @param int $expectedErrors Optional. Number of errors expected. + * Defaults to 0. + * + * @dataProvider dataSuppressScope + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testSuppressScope($before, $after, $expectedErrors=0) + { + static $config, $ruleset; + + if (isset($config, $ruleset) === false) { + $config = new Config(); + $config->standards = ['PEAR']; + $config->sniffs = ['PEAR.Functions.FunctionDeclaration']; + + $ruleset = new Ruleset($config); + } + + $content = 'foo(); + } +} +EOD; + $file = new DummyFile($content, $ruleset, $config); + $file->process(); + + $this->assertSame($expectedErrors, $file->getErrorCount()); + $this->assertCount($expectedErrors, $file->getErrors()); + + }//end testSuppressScope() + + + /** + * Data provider. + * + * @see testSuppressScope() + * + * @return array + */ + public function dataSuppressScope() + { + return [ + 'no suppression' => [ + 'before' => '', + 'after' => '', + 'expectedErrors' => 1, + ], + + // Process with suppression. + 'disable/enable: slash comment' => [ + 'before' => '//phpcs:disable', + 'after' => '//phpcs:enable', + ], + 'disable/enable: slash comment, with @' => [ + 'before' => '//@phpcs:disable', + 'after' => '//@phpcs:enable', + ], + 'disable/enable: hash comment' => [ + 'before' => '#phpcs:disable', + 'after' => '#phpcs:enable', + ], + 'disable/enable: single line docblock comment' => [ + 'before' => '/** phpcs:disable */', + 'after' => '/** phpcs:enable */', + ], + 'disable/enable: single line docblock comment, with @' => [ + 'before' => '/** @phpcs:disable */', + 'after' => '/** @phpcs:enable */', + ], + + // Deprecated syntax. + 'old style: start/end, slash comment' => [ + 'before' => '//@codingStandardsIgnoreStart', + 'after' => '//@codingStandardsIgnoreEnd', + ], + 'old style: start/end, single line docblock comment' => [ + 'before' => '/** @codingStandardsIgnoreStart */', + 'after' => '/** @codingStandardsIgnoreEnd */', + ], + ]; + + }//end dataSuppressScope() + + + /** + * Test suppressing a whole file. + * + * @param string $before Annotation to place before the code. + * @param string $after Optional. Annotation to place after the code. + * Defaults to an empty string. + * @param int $expectedWarnings Optional. Number of warnings expected. + * Defaults to 0. + * + * @dataProvider dataSuppressFile + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testSuppressFile($before, $after='', $expectedWarnings=0) + { + static $config, $ruleset; + + if (isset($config, $ruleset) === false) { + $config = new Config(); + $config->standards = ['Generic']; + $config->sniffs = ['Generic.Commenting.Todo']; + + $ruleset = new Ruleset($config); + } + + $content = <<process(); + + $this->assertSame($expectedWarnings, $file->getWarningCount()); + $this->assertCount($expectedWarnings, $file->getWarnings()); + + }//end testSuppressFile() + + + /** + * Data provider. + * + * @see testSuppressFile() + * + * @return array + */ + public function dataSuppressFile() + { + return [ + 'no suppression' => [ + 'before' => '', + 'after' => '', + 'expectedErrors' => 1, + ], + + // Process with suppression. + 'ignoreFile: start of file, slash comment' => ['before' => '// phpcs:ignoreFile'], + 'ignoreFile: start of file, slash comment, with @' => ['before' => '// @phpcs:ignoreFile'], + 'ignoreFile: start of file, slash comment, mixed case' => ['before' => '// PHPCS:Ignorefile'], + 'ignoreFile: start of file, hash comment' => ['before' => '# phpcs:ignoreFile'], + 'ignoreFile: start of file, hash comment, with @' => ['before' => '# @phpcs:ignoreFile'], + 'ignoreFile: start of file, single-line star comment' => ['before' => '/* phpcs:ignoreFile */'], + 'ignoreFile: start of file, multi-line star comment' => [ + 'before' => '/*'.PHP_EOL.' phpcs:ignoreFile'.PHP_EOL.' */', + ], + 'ignoreFile: start of file, single-line docblock comment' => ['before' => '/** phpcs:ignoreFile */'], + + // Process late comment. + 'ignoreFile: late comment, slash comment' => [ + 'before' => '', + 'after' => '// phpcs:ignoreFile', + ], + + // Deprecated syntax. + 'old style: start of file, slash comment' => ['before' => '// @codingStandardsIgnoreFile'], + 'old style: start of file, single-line star comment' => ['before' => '/* @codingStandardsIgnoreFile */'], + 'old style: start of file, multi-line star comment' => [ + 'before' => '/*'.PHP_EOL.' @codingStandardsIgnoreFile'.PHP_EOL.' */', + ], + 'old style: start of file, single-line docblock comment' => ['before' => '/** @codingStandardsIgnoreFile */'], + + // Deprecated syntax, late comment. + 'old style: late comment, slash comment' => [ + 'before' => '', + 'after' => '// @codingStandardsIgnoreFile', + ], + ]; + + }//end dataSuppressFile() + + + /** + * Test disabling specific sniffs. + * + * @param string $before Annotation to place before the code. + * @param int $expectedErrors Optional. Number of errors expected. + * Defaults to 0. + * @param int $expectedWarnings Optional. Number of warnings expected. + * Defaults to 0. + * + * @dataProvider dataDisableSelected + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testDisableSelected($before, $expectedErrors=0, $expectedWarnings=0) + { + static $config, $ruleset; + + if (isset($config, $ruleset) === false) { + $config = new Config(); + $config->standards = ['Generic']; + $config->sniffs = [ + 'Generic.PHP.LowerCaseConstant', + 'Generic.Commenting.Todo', + ]; + + $ruleset = new Ruleset($config); + } + + $content = <<process(); + + $this->assertSame($expectedErrors, $file->getErrorCount()); + $this->assertCount($expectedErrors, $file->getErrors()); + + $this->assertSame($expectedWarnings, $file->getWarningCount()); + $this->assertCount($expectedWarnings, $file->getWarnings()); + + }//end testDisableSelected() + + + /** + * Data provider. + * + * @see testDisableSelected() + * + * @return array + */ + public function dataDisableSelected() + { + return [ + // Single sniff. + 'disable: single sniff' => [ + 'before' => '// phpcs:disable Generic.Commenting.Todo', + 'expectedErrors' => 1, + ], + 'disable: single sniff with reason' => [ + 'before' => '# phpcs:disable Generic.Commenting.Todo -- for reasons', + 'expectedErrors' => 1, + ], + 'disable: single sniff, docblock' => [ + 'before' => '/**'.PHP_EOL.' * phpcs:disable Generic.Commenting.Todo'.PHP_EOL.' */ ', + 'expectedErrors' => 1, + ], + 'disable: single sniff, docblock, with @' => [ + 'before' => '/**'.PHP_EOL.' * @phpcs:disable Generic.Commenting.Todo'.PHP_EOL.' */ ', + 'expectedErrors' => 1, + ], + + // Multiple sniffs. + 'disable: multiple sniffs in one comment' => ['before' => '// phpcs:disable Generic.Commenting.Todo,Generic.PHP.LowerCaseConstant'], + 'disable: multiple sniff in multiple comments' => [ + 'before' => '// phpcs:disable Generic.Commenting.Todo'.PHP_EOL.'// phpcs:disable Generic.PHP.LowerCaseConstant', + ], + + // Selectiveness variations. + 'disable: complete category' => [ + 'before' => '// phpcs:disable Generic.Commenting', + 'expectedErrors' => 1, + ], + 'disable: whole standard' => ['before' => '// phpcs:disable Generic'], + 'disable: single errorcode' => [ + 'before' => '# @phpcs:disable Generic.Commenting.Todo.TaskFound', + 'expectedErrors' => 1, + ], + 'disable: single errorcode and a category' => ['before' => '// phpcs:disable Generic.PHP.LowerCaseConstant.Found,Generic.Commenting'], + + // Wrong category/sniff/code. + 'disable: wrong error code and category' => [ + 'before' => '/**'.PHP_EOL.' * phpcs:disable Generic.PHP.LowerCaseConstant.Upper,Generic.Comments'.PHP_EOL.' */ ', + 'expectedErrors' => 1, + 'expectedWarnings' => 1, + ], + 'disable: wrong category, docblock' => [ + 'before' => '/**'.PHP_EOL.' * phpcs:disable Generic.Files'.PHP_EOL.' */ ', + 'expectedErrors' => 1, + 'expectedWarnings' => 1, + ], + 'disable: wrong category, docblock, with @' => [ + 'before' => '/**'.PHP_EOL.' * @phpcs:disable Generic.Files'.PHP_EOL.' */ ', + 'expectedErrors' => 1, + 'expectedWarnings' => 1, + ], + ]; + + }//end dataDisableSelected() + + + /** + * Test re-enabling specific sniffs that have been disabled. + * + * @param string $code Code pattern to check. + * @param int $expectedErrors Number of errors expected. + * @param int $expectedWarnings Number of warnings expected. + * + * @dataProvider dataEnableSelected + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testEnableSelected($code, $expectedErrors, $expectedWarnings) + { + static $config, $ruleset; + + if (isset($config, $ruleset) === false) { + $config = new Config(); + $config->standards = ['Generic']; + $config->sniffs = [ + 'Generic.PHP.LowerCaseConstant', + 'Generic.Commenting.Todo', + ]; + + $ruleset = new Ruleset($config); + } + + $content = 'process(); + + $this->assertSame($expectedErrors, $file->getErrorCount()); + $this->assertCount($expectedErrors, $file->getErrors()); + + $this->assertSame($expectedWarnings, $file->getWarningCount()); + $this->assertCount($expectedWarnings, $file->getWarnings()); + + }//end testEnableSelected() + + + /** + * Data provider. + * + * @see testEnableSelected() + * + * @return array + */ + public function dataEnableSelected() + { + return [ + 'disable/enable: a single sniff' => [ + 'code' => ' + // phpcs:disable Generic.Commenting.Todo + $var = FALSE; + //TODO: write some code + // phpcs:enable Generic.Commenting.Todo + //TODO: write some code', + 'expectedErrors' => 1, + 'expectedWarnings' => 1, + ], + 'disable/enable: multiple sniffs' => [ + 'code' => ' + // phpcs:disable Generic.Commenting.Todo,Generic.PHP.LowerCaseConstant + $var = FALSE; + //TODO: write some code + // phpcs:enable Generic.Commenting.Todo,Generic.PHP.LowerCaseConstant + //TODO: write some code + $var = FALSE;', + 'expectedErrors' => 1, + 'expectedWarnings' => 1, + ], + 'disable: multiple sniffs; enable: one' => [ + 'code' => ' + # phpcs:disable Generic.Commenting.Todo,Generic.PHP.LowerCaseConstant + $var = FALSE; + //TODO: write some code + # phpcs:enable Generic.Commenting.Todo + //TODO: write some code + $var = FALSE;', + 'expectedErrors' => 0, + 'expectedWarnings' => 1, + ], + 'disable/enable: complete category' => [ + 'code' => ' + // phpcs:disable Generic.Commenting + $var = FALSE; + //TODO: write some code + // phpcs:enable Generic.Commenting + //TODO: write some code', + 'expectedErrors' => 1, + 'expectedWarnings' => 1, + ], + 'disable/enable: whole standard' => [ + 'code' => ' + // phpcs:disable Generic + $var = FALSE; + //TODO: write some code + // phpcs:enable Generic + //TODO: write some code', + 'expectedErrors' => 0, + 'expectedWarnings' => 1, + ], + 'disable: whole standard; enable: category from the standard' => [ + 'code' => ' + // phpcs:disable Generic + $var = FALSE; + //TODO: write some code + // phpcs:enable Generic.Commenting + //TODO: write some code', + 'expectedErrors' => 0, + 'expectedWarnings' => 1, + ], + 'disable: a category; enable: the whole standard containing the category' => [ + 'code' => ' + # phpcs:disable Generic.Commenting + $var = FALSE; + //TODO: write some code + # phpcs:enable Generic + //TODO: write some code', + 'expectedErrors' => 1, + 'expectedWarnings' => 1, + ], + 'disable: single sniff; enable: the category containing the sniff' => [ + 'code' => ' + // phpcs:disable Generic.Commenting.Todo + $var = FALSE; + //TODO: write some code + // phpcs:enable Generic.Commenting + //TODO: write some code', + 'expectedErrors' => 1, + 'expectedWarnings' => 1, + ], + 'disable: whole standard; enable: single sniff from the standard' => [ + 'code' => ' + // phpcs:disable Generic + $var = FALSE; + //TODO: write some code + // phpcs:enable Generic.Commenting.Todo + //TODO: write some code', + 'expectedErrors' => 0, + 'expectedWarnings' => 1, + ], + 'disable: whole standard; enable: single sniff from the standard; disable: that same sniff; enable: everything' => [ + 'code' => ' + // phpcs:disable Generic + $var = FALSE; + //TODO: write some code + // phpcs:enable Generic.Commenting.Todo + //TODO: write some code + // phpcs:disable Generic.Commenting.Todo + //TODO: write some code + // phpcs:enable + //TODO: write some code', + 'expectedErrors' => 0, + 'expectedWarnings' => 2, + ], + 'disable: whole standard; enable: single sniff from the standard; enable: other sniff from the standard' => [ + 'code' => ' + // phpcs:disable Generic + $var = FALSE; + //TODO: write some code + // phpcs:enable Generic.Commenting.Todo + //TODO: write some code + $var = FALSE; + // phpcs:enable Generic.PHP.LowerCaseConstant + //TODO: write some code + $var = FALSE;', + 'expectedErrors' => 1, + 'expectedWarnings' => 2, + ], + ]; + + }//end dataEnableSelected() + + + /** + * Test ignoring specific sniffs. + * + * @param string $before Annotation to place before the code. + * @param int $expectedErrors Number of errors expected. + * @param int $expectedWarnings Number of warnings expected. + * + * @dataProvider dataIgnoreSelected + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testIgnoreSelected($before, $expectedErrors, $expectedWarnings) + { + static $config, $ruleset; + + if (isset($config, $ruleset) === false) { + $config = new Config(); + $config->standards = ['Generic']; + $config->sniffs = [ + 'Generic.PHP.LowerCaseConstant', + 'Generic.Commenting.Todo', + ]; + + $ruleset = new Ruleset($config); + } + + $content = <<process(); + + $this->assertSame($expectedErrors, $file->getErrorCount()); + $this->assertCount($expectedErrors, $file->getErrors()); + + $this->assertSame($expectedWarnings, $file->getWarningCount()); + $this->assertCount($expectedWarnings, $file->getWarnings()); + + }//end testIgnoreSelected() + + + /** + * Data provider. + * + * @see testIgnoreSelected() + * + * @return array + */ + public function dataIgnoreSelected() + { + return [ + 'no suppression' => [ + 'before' => '', + 'expectedErrors' => 2, + 'expectedWarnings' => 2, + ], + + // With suppression. + 'ignore: single sniff' => [ + 'before' => '// phpcs:ignore Generic.Commenting.Todo', + 'expectedErrors' => 2, + 'expectedWarnings' => 1, + ], + 'ignore: multiple sniffs' => [ + 'before' => '// phpcs:ignore Generic.Commenting.Todo,Generic.PHP.LowerCaseConstant', + 'expectedErrors' => 1, + 'expectedWarnings' => 1, + ], + 'disable: single sniff; ignore: single sniff' => [ + 'before' => '// phpcs:disable Generic.Commenting.Todo'.PHP_EOL.'// phpcs:ignore Generic.PHP.LowerCaseConstant', + 'expectedErrors' => 1, + 'expectedWarnings' => 0, + ], + 'ignore: category of sniffs' => [ + 'before' => '# phpcs:ignore Generic.Commenting', + 'expectedErrors' => 2, + 'expectedWarnings' => 1, + ], + 'ignore: whole standard' => [ + 'before' => '// phpcs:ignore Generic', + 'expectedErrors' => 1, + 'expectedWarnings' => 1, + ], + ]; + + }//end dataIgnoreSelected() + + + /** + * Test ignoring specific sniffs. + * + * @param string $code Code pattern to check. + * @param int $expectedErrors Number of errors expected. + * @param int $expectedWarnings Number of warnings expected. + * + * @dataProvider dataCommenting + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testCommenting($code, $expectedErrors, $expectedWarnings) + { + static $config, $ruleset; + + if (isset($config, $ruleset) === false) { + $config = new Config(); + $config->standards = ['Generic']; + $config->sniffs = [ + 'Generic.PHP.LowerCaseConstant', + 'Generic.Commenting.Todo', + ]; + + $ruleset = new Ruleset($config); + } + + $content = 'process(); + + $this->assertSame($expectedErrors, $file->getErrorCount()); + $this->assertCount($expectedErrors, $file->getErrors()); + + $this->assertSame($expectedWarnings, $file->getWarningCount()); + $this->assertCount($expectedWarnings, $file->getWarnings()); + + }//end testCommenting() + + + /** + * Data provider. + * + * @see testCommenting() + * + * @return array + */ + public function dataCommenting() + { + return [ + 'ignore: single sniff' => [ + 'code' => ' + // phpcs:ignore Generic.Commenting.Todo -- Because reasons + $var = FALSE; //TODO: write some code + $var = FALSE; //TODO: write some code', + 'expectedErrors' => 2, + 'expectedWarnings' => 1, + ], + 'disable: single sniff; enable: same sniff - test whitespace handling around reason delimiter' => [ + 'code' => ' + // phpcs:disable Generic.Commenting.Todo --Because reasons + $var = FALSE; + //TODO: write some code + // phpcs:enable Generic.Commenting.Todo -- Because reasons + //TODO: write some code', + 'expectedErrors' => 1, + 'expectedWarnings' => 1, + ], + 'disable: single sniff, multi-line comment' => [ + 'code' => ' + /* + Disable some checks + phpcs:disable Generic.Commenting.Todo + */ + $var = FALSE; + //TODO: write some code', + 'expectedErrors' => 1, + 'expectedWarnings' => 0, + ], + 'ignore: single sniff, multi-line slash comment' => [ + 'code' => ' + // Turn off a check for the next line of code. + // phpcs:ignore Generic.Commenting.Todo + $var = FALSE; //TODO: write some code + $var = FALSE; //TODO: write some code', + 'expectedErrors' => 2, + 'expectedWarnings' => 1, + ], + 'enable before disable, sniff not in standard' => [ + 'code' => ' + // phpcs:enable Generic.PHP.NoSilencedErrors -- Because reasons + $var = @delete( $filename ); + ', + 'expectedErrors' => 0, + 'expectedWarnings' => 0, + ], + ]; + + }//end dataCommenting() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/FindEndOfStatementTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/File/FindEndOfStatementTest.inc new file mode 100644 index 00000000..83516798 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/FindEndOfStatementTest.inc @@ -0,0 +1,105 @@ + fn() => return 1, + 'b' => fn() => return 1, +]; + +/* testStaticArrowFunction */ +static fn ($a) => $a; + +return 0; + +/* testArrowFunctionReturnValue */ +fn(): array => [a($a, $b)]; + +/* testArrowFunctionAsArgument */ +$foo = foo( + fn() => bar() +); + +/* testArrowFunctionWithArrayAsArgument */ +$foo = foo( + fn() => [$row[0], $row[3]] +); + +$match = match ($a) { + /* testMatchCase */ + 1 => 'foo', + /* testMatchDefault */ + default => 'bar' +}; + +$match = match ($a) { + /* testMatchMultipleCase */ + 1, 2, => $a * $b, + /* testMatchDefaultComma */ + default, => 'something' +}; + +match ($pressedKey) { + /* testMatchFunctionCall */ + Key::RETURN_ => save($value, $user) +}; + +$result = match (true) { + /* testMatchFunctionCallArm */ + str_contains($text, 'Welcome') || str_contains($text, 'Hello') => 'en', + str_contains($text, 'Bienvenue') || str_contains($text, 'Bonjour') => 'fr', + default => 'pl' +}; + +/* testMatchClosure */ +$result = match ($key) { + 1 => function($a, $b) {}, + 2 => function($b, $c) {}, +}; + +/* testMatchArray */ +$result = match ($key) { + 1 => [1,2,3], + 2 => [1 => one(), 2 => two()], +}; + +/* testNestedMatch */ +$result = match ($key) { + 1 => match ($key) { + 1 => 'one', + 2 => 'two', + }, + 2 => match ($key) { + 1 => 'two', + 2 => 'one', + }, +}; diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/FindEndOfStatementTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/File/FindEndOfStatementTest.php new file mode 100644 index 00000000..7bff26b5 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/FindEndOfStatementTest.php @@ -0,0 +1,415 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\File; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class FindEndOfStatementTest extends AbstractMethodUnitTest +{ + + + /** + * Test a simple assignment. + * + * @return void + */ + public function testSimpleAssignment() + { + $start = $this->getTargetToken('/* testSimpleAssignment */', T_VARIABLE); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 5), $found); + + }//end testSimpleAssignment() + + + /** + * Test a direct call to a control structure. + * + * @return void + */ + public function testControlStructure() + { + $start = $this->getTargetToken('/* testControlStructure */', T_WHILE); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 6), $found); + + }//end testControlStructure() + + + /** + * Test the assignment of a closure. + * + * @return void + */ + public function testClosureAssignment() + { + $start = $this->getTargetToken('/* testClosureAssignment */', T_VARIABLE, '$a'); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 13), $found); + + }//end testClosureAssignment() + + + /** + * Test using a heredoc in a function argument. + * + * @return void + */ + public function testHeredocFunctionArg() + { + // Find the end of the function. + $start = $this->getTargetToken('/* testHeredocFunctionArg */', T_STRING, 'myFunction'); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 10), $found); + + // Find the end of the heredoc. + $start += 2; + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 4), $found); + + // Find the end of the last arg. + $start = ($found + 2); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame($start, $found); + + }//end testHeredocFunctionArg() + + + /** + * Test parts of a switch statement. + * + * @return void + */ + public function testSwitch() + { + // Find the end of the switch. + $start = $this->getTargetToken('/* testSwitch */', T_SWITCH); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 28), $found); + + // Find the end of the case. + $start += 9; + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 8), $found); + + // Find the end of default case. + $start += 11; + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 6), $found); + + }//end testSwitch() + + + /** + * Test statements that are array values. + * + * @return void + */ + public function testStatementAsArrayValue() + { + // Test short array syntax. + $start = $this->getTargetToken('/* testStatementAsArrayValue */', T_NEW); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 2), $found); + + // Test long array syntax. + $start += 12; + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 2), $found); + + // Test same statement outside of array. + $start += 10; + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 3), $found); + + }//end testStatementAsArrayValue() + + + /** + * Test a use group. + * + * @return void + */ + public function testUseGroup() + { + $start = $this->getTargetToken('/* testUseGroup */', T_USE); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 23), $found); + + }//end testUseGroup() + + + /** + * Test arrow function as array value. + * + * @return void + */ + public function testArrowFunctionArrayValue() + { + $start = $this->getTargetToken('/* testArrowFunctionArrayValue */', T_FN); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 9), $found); + + }//end testArrowFunctionArrayValue() + + + /** + * Test static arrow function. + * + * @return void + */ + public function testStaticArrowFunction() + { + $static = $this->getTargetToken('/* testStaticArrowFunction */', T_STATIC); + $fn = $this->getTargetToken('/* testStaticArrowFunction */', T_FN); + + $endOfStatementStatic = self::$phpcsFile->findEndOfStatement($static); + $endOfStatementFn = self::$phpcsFile->findEndOfStatement($fn); + + $this->assertSame($endOfStatementFn, $endOfStatementStatic); + + }//end testStaticArrowFunction() + + + /** + * Test arrow function with return value. + * + * @return void + */ + public function testArrowFunctionReturnValue() + { + $start = $this->getTargetToken('/* testArrowFunctionReturnValue */', T_FN); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 18), $found); + + }//end testArrowFunctionReturnValue() + + + /** + * Test arrow function used as a function argument. + * + * @return void + */ + public function testArrowFunctionAsArgument() + { + $start = $this->getTargetToken('/* testArrowFunctionAsArgument */', T_FN); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 8), $found); + + }//end testArrowFunctionAsArgument() + + + /** + * Test arrow function with arrays used as a function argument. + * + * @return void + */ + public function testArrowFunctionWithArrayAsArgument() + { + $start = $this->getTargetToken('/* testArrowFunctionWithArrayAsArgument */', T_FN); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 17), $found); + + }//end testArrowFunctionWithArrayAsArgument() + + + /** + * Test simple match expression case. + * + * @return void + */ + public function testMatchCase() + { + $start = $this->getTargetToken('/* testMatchCase */', T_LNUMBER); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 5), $found); + + $start = $this->getTargetToken('/* testMatchCase */', T_CONSTANT_ENCAPSED_STRING); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 1), $found); + + }//end testMatchCase() + + + /** + * Test simple match expression default case. + * + * @return void + */ + public function testMatchDefault() + { + $start = $this->getTargetToken('/* testMatchDefault */', T_MATCH_DEFAULT); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 4), $found); + + $start = $this->getTargetToken('/* testMatchDefault */', T_CONSTANT_ENCAPSED_STRING); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame($start, $found); + + }//end testMatchDefault() + + + /** + * Test multiple comma-separated match expression case values. + * + * @return void + */ + public function testMatchMultipleCase() + { + $start = $this->getTargetToken('/* testMatchMultipleCase */', T_LNUMBER); + $found = self::$phpcsFile->findEndOfStatement($start); + $this->assertSame(($start + 13), $found); + + $start += 6; + $found = self::$phpcsFile->findEndOfStatement($start); + $this->assertSame(($start + 7), $found); + + }//end testMatchMultipleCase() + + + /** + * Test match expression default case with trailing comma. + * + * @return void + */ + public function testMatchDefaultComma() + { + $start = $this->getTargetToken('/* testMatchDefaultComma */', T_MATCH_DEFAULT); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 5), $found); + + }//end testMatchDefaultComma() + + + /** + * Test match expression with function call. + * + * @return void + */ + public function testMatchFunctionCall() + { + $start = $this->getTargetToken('/* testMatchFunctionCall */', T_STRING); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 12), $found); + + $start += 8; + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 1), $found); + + }//end testMatchFunctionCall() + + + /** + * Test match expression with function call in the arm. + * + * @return void + */ + public function testMatchFunctionCallArm() + { + // Check the first case. + $start = $this->getTargetToken('/* testMatchFunctionCallArm */', T_STRING); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 21), $found); + + // Check the second case. + $start += 24; + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 21), $found); + + }//end testMatchFunctionCallArm() + + + /** + * Test match expression with closure. + * + * @return void + */ + public function testMatchClosure() + { + $start = $this->getTargetToken('/* testMatchClosure */', T_LNUMBER); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 14), $found); + + $start += 17; + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 14), $found); + + }//end testMatchClosure() + + + /** + * Test match expression with array declaration. + * + * @return void + */ + public function testMatchArray() + { + $start = $this->getTargetToken('/* testMatchArray */', T_LNUMBER); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 11), $found); + + $start += 14; + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 22), $found); + + }//end testMatchArray() + + + /** + * Test nested match expressions. + * + * @return void + */ + public function testNestedMatch() + { + $start = $this->getTargetToken('/* testNestedMatch */', T_LNUMBER); + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 30), $found); + + $start += 21; + $found = self::$phpcsFile->findEndOfStatement($start); + + $this->assertSame(($start + 5), $found); + + }//end testNestedMatch() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/FindExtendedClassNameTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/File/FindExtendedClassNameTest.inc new file mode 100644 index 00000000..aead06cd --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/FindExtendedClassNameTest.inc @@ -0,0 +1,37 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\File; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class FindExtendedClassNameTest extends AbstractMethodUnitTest +{ + + + /** + * Test retrieving the name of the class being extended by another class + * (or interface). + * + * @param string $identifier Comment which precedes the test case. + * @param bool $expected Expected function output. + * + * @dataProvider dataExtendedClass + * + * @return void + */ + public function testFindExtendedClassName($identifier, $expected) + { + $OOToken = $this->getTargetToken($identifier, [T_CLASS, T_ANON_CLASS, T_INTERFACE]); + $result = self::$phpcsFile->findExtendedClassName($OOToken); + $this->assertSame($expected, $result); + + }//end testFindExtendedClassName() + + + /** + * Data provider for the FindExtendedClassName test. + * + * @see testFindExtendedClassName() + * + * @return array + */ + public function dataExtendedClass() + { + return [ + [ + '/* testExtendedClass */', + 'testFECNClass', + ], + [ + '/* testNamespacedClass */', + '\PHP_CodeSniffer\Tests\Core\File\testFECNClass', + ], + [ + '/* testNonExtendedClass */', + false, + ], + [ + '/* testInterface */', + false, + ], + [ + '/* testInterfaceThatExtendsInterface */', + 'testFECNInterface', + ], + [ + '/* testInterfaceThatExtendsFQCNInterface */', + '\PHP_CodeSniffer\Tests\Core\File\testFECNInterface', + ], + [ + '/* testNestedExtendedClass */', + false, + ], + [ + '/* testNestedExtendedAnonClass */', + 'testFECNAnonClass', + ], + [ + '/* testClassThatExtendsAndImplements */', + 'testFECNClass', + ], + [ + '/* testClassThatImplementsAndExtends */', + 'testFECNClass', + ], + ]; + + }//end dataExtendedClass() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/FindImplementedInterfaceNamesTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/File/FindImplementedInterfaceNamesTest.inc new file mode 100644 index 00000000..44c0f643 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/FindImplementedInterfaceNamesTest.inc @@ -0,0 +1,35 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\File; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class FindImplementedInterfaceNamesTest extends AbstractMethodUnitTest +{ + + + /** + * Test retrieving the name(s) of the interfaces being implemented by a class. + * + * @param string $identifier Comment which precedes the test case. + * @param bool $expected Expected function output. + * + * @dataProvider dataImplementedInterface + * + * @return void + */ + public function testFindImplementedInterfaceNames($identifier, $expected) + { + $OOToken = $this->getTargetToken($identifier, [T_CLASS, T_ANON_CLASS, T_INTERFACE, T_ENUM]); + $result = self::$phpcsFile->findImplementedInterfaceNames($OOToken); + $this->assertSame($expected, $result); + + }//end testFindImplementedInterfaceNames() + + + /** + * Data provider for the FindImplementedInterfaceNames test. + * + * @see testFindImplementedInterfaceNames() + * + * @return array + */ + public function dataImplementedInterface() + { + return [ + [ + '/* testImplementedClass */', + ['testFIINInterface'], + ], + [ + '/* testMultiImplementedClass */', + [ + 'testFIINInterface', + 'testFIINInterface2', + ], + ], + [ + '/* testNamespacedClass */', + ['\PHP_CodeSniffer\Tests\Core\File\testFIINInterface'], + ], + [ + '/* testNonImplementedClass */', + false, + ], + [ + '/* testInterface */', + false, + ], + [ + '/* testClassThatExtendsAndImplements */', + [ + 'InterfaceA', + '\NameSpaced\Cat\InterfaceB', + ], + ], + [ + '/* testClassThatImplementsAndExtends */', + [ + '\InterfaceA', + 'InterfaceB', + ], + ], + [ + '/* testBackedEnumWithoutImplements */', + false, + ], + [ + '/* testEnumImplements */', + ['Colorful'], + ], + [ + '/* testBackedEnumImplements */', + [ + 'Colorful', + '\Deck', + ], + ], + ]; + + }//end dataImplementedInterface() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/FindStartOfStatementTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/File/FindStartOfStatementTest.inc new file mode 100644 index 00000000..ce9dfad3 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/FindStartOfStatementTest.inc @@ -0,0 +1,123 @@ + $foo + $bar, 'b' => true]; + +/* testUseGroup */ +use Vendor\Package\{ClassA as A, ClassB, ClassC as C}; + +$a = [ + /* testArrowFunctionArrayValue */ + 'a' => fn() => return 1, + 'b' => fn() => return 1, +]; + +/* testStaticArrowFunction */ +static fn ($a) => $a; + +/* testArrowFunctionReturnValue */ +fn(): array => [a($a, $b)]; + +/* testArrowFunctionAsArgument */ +$foo = foo( + fn() => bar() +); + +/* testArrowFunctionWithArrayAsArgument */ +$foo = foo( + fn() => [$row[0], $row[3]] +); + +$match = match ($a) { + /* testMatchCase */ + 1 => 'foo', + /* testMatchDefault */ + default => 'bar' +}; + +$match = match ($a) { + /* testMatchMultipleCase */ + 1, 2, => $a * $b, + /* testMatchDefaultComma */ + default, => 'something' +}; + +match ($pressedKey) { + /* testMatchFunctionCall */ + Key::RETURN_ => save($value, $user) +}; + +$result = match (true) { + /* testMatchFunctionCallArm */ + str_contains($text, 'Welcome') || str_contains($text, 'Hello') => 'en', + str_contains($text, 'Bienvenue') || str_contains($text, 'Bonjour') => 'fr', + default => 'pl' +}; + +/* testMatchClosure */ +$result = match ($key) { + 1 => function($a, $b) {}, + 2 => function($b, $c) {}, +}; + +/* testMatchArray */ +$result = match ($key) { + 1 => [1,2,3], + 2 => [1 => one($a, $b), 2 => two($b, $c)], + 3 => [], +}; + +/* testNestedMatch */ +$result = match ($key) { + 1 => match ($key) { + 1 => 'one', + 2 => 'two', + }, + 2 => match ($key) { + 1 => 'two', + 2 => 'one', + }, +}; + +return 0; + +/* testOpenTag */ +?> +

    Test

    +', foo(), ''; + +/* testOpenTagWithEcho */ +?> +

    Test

    +', foo(), ''; diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/FindStartOfStatementTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/File/FindStartOfStatementTest.php new file mode 100644 index 00000000..ff859eca --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/FindStartOfStatementTest.php @@ -0,0 +1,503 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\File; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class FindStartOfStatementTest extends AbstractMethodUnitTest +{ + + + /** + * Test a simple assignment. + * + * @return void + */ + public function testSimpleAssignment() + { + $start = $this->getTargetToken('/* testSimpleAssignment */', T_SEMICOLON); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 5), $found); + + }//end testSimpleAssignment() + + + /** + * Test a function call. + * + * @return void + */ + public function testFunctionCall() + { + $start = $this->getTargetToken('/* testFunctionCall */', T_CLOSE_PARENTHESIS); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 6), $found); + + }//end testFunctionCall() + + + /** + * Test a function call. + * + * @return void + */ + public function testFunctionCallArgument() + { + $start = $this->getTargetToken('/* testFunctionCallArgument */', T_VARIABLE, '$b'); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame($start, $found); + + }//end testFunctionCallArgument() + + + /** + * Test a direct call to a control structure. + * + * @return void + */ + public function testControlStructure() + { + $start = $this->getTargetToken('/* testControlStructure */', T_CLOSE_CURLY_BRACKET); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 6), $found); + + }//end testControlStructure() + + + /** + * Test the assignment of a closure. + * + * @return void + */ + public function testClosureAssignment() + { + $start = $this->getTargetToken('/* testClosureAssignment */', T_CLOSE_CURLY_BRACKET); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 12), $found); + + }//end testClosureAssignment() + + + /** + * Test using a heredoc in a function argument. + * + * @return void + */ + public function testHeredocFunctionArg() + { + // Find the start of the function. + $start = $this->getTargetToken('/* testHeredocFunctionArg */', T_SEMICOLON); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 10), $found); + + // Find the start of the heredoc. + $start -= 4; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 4), $found); + + // Find the start of the last arg. + $start += 2; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame($start, $found); + + }//end testHeredocFunctionArg() + + + /** + * Test parts of a switch statement. + * + * @return void + */ + public function testSwitch() + { + // Find the start of the switch. + $start = $this->getTargetToken('/* testSwitch */', T_CLOSE_CURLY_BRACKET); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 47), $found); + + // Find the start of default case. + $start -= 5; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 6), $found); + + // Find the start of the second case. + $start -= 12; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 5), $found); + + // Find the start of the first case. + $start -= 13; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 8), $found); + + // Test inside the first case. + $start--; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 1), $found); + + }//end testSwitch() + + + /** + * Test statements that are array values. + * + * @return void + */ + public function testStatementAsArrayValue() + { + // Test short array syntax. + $start = $this->getTargetToken('/* testStatementAsArrayValue */', T_STRING, 'Datetime'); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 2), $found); + + // Test long array syntax. + $start += 12; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 2), $found); + + // Test same statement outside of array. + $start++; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 9), $found); + + // Test with an array index. + $start += 17; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 5), $found); + + }//end testStatementAsArrayValue() + + + /** + * Test a use group. + * + * @return void + */ + public function testUseGroup() + { + $start = $this->getTargetToken('/* testUseGroup */', T_SEMICOLON); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 23), $found); + + }//end testUseGroup() + + + /** + * Test arrow function as array value. + * + * @return void + */ + public function testArrowFunctionArrayValue() + { + $start = $this->getTargetToken('/* testArrowFunctionArrayValue */', T_COMMA); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 9), $found); + + }//end testArrowFunctionArrayValue() + + + /** + * Test static arrow function. + * + * @return void + */ + public function testStaticArrowFunction() + { + $start = $this->getTargetToken('/* testStaticArrowFunction */', T_SEMICOLON); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 11), $found); + + }//end testStaticArrowFunction() + + + /** + * Test arrow function with return value. + * + * @return void + */ + public function testArrowFunctionReturnValue() + { + $start = $this->getTargetToken('/* testArrowFunctionReturnValue */', T_SEMICOLON); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 18), $found); + + }//end testArrowFunctionReturnValue() + + + /** + * Test arrow function used as a function argument. + * + * @return void + */ + public function testArrowFunctionAsArgument() + { + $start = $this->getTargetToken('/* testArrowFunctionAsArgument */', T_FN); + $start += 8; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 8), $found); + + }//end testArrowFunctionAsArgument() + + + /** + * Test arrow function with arrays used as a function argument. + * + * @return void + */ + public function testArrowFunctionWithArrayAsArgument() + { + $start = $this->getTargetToken('/* testArrowFunctionWithArrayAsArgument */', T_FN); + $start += 17; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 17), $found); + + }//end testArrowFunctionWithArrayAsArgument() + + + /** + * Test simple match expression case. + * + * @return void + */ + public function testMatchCase() + { + $start = $this->getTargetToken('/* testMatchCase */', T_COMMA); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 1), $found); + + }//end testMatchCase() + + + /** + * Test simple match expression default case. + * + * @return void + */ + public function testMatchDefault() + { + $start = $this->getTargetToken('/* testMatchDefault */', T_CONSTANT_ENCAPSED_STRING, "'bar'"); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame($start, $found); + + }//end testMatchDefault() + + + /** + * Test multiple comma-separated match expression case values. + * + * @return void + */ + public function testMatchMultipleCase() + { + $start = $this->getTargetToken('/* testMatchMultipleCase */', T_MATCH_ARROW); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 6), $found); + + $start += 6; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 4), $found); + + }//end testMatchMultipleCase() + + + /** + * Test match expression default case with trailing comma. + * + * @return void + */ + public function testMatchDefaultComma() + { + $start = $this->getTargetToken('/* testMatchDefaultComma */', T_MATCH_ARROW); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 3), $found); + + $start += 2; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame($start, $found); + + }//end testMatchDefaultComma() + + + /** + * Test match expression with function call. + * + * @return void + */ + public function testMatchFunctionCall() + { + $start = $this->getTargetToken('/* testMatchFunctionCall */', T_CLOSE_PARENTHESIS); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 6), $found); + + }//end testMatchFunctionCall() + + + /** + * Test match expression with function call in the arm. + * + * @return void + */ + public function testMatchFunctionCallArm() + { + // Check the first case. + $start = $this->getTargetToken('/* testMatchFunctionCallArm */', T_MATCH_ARROW); + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 18), $found); + + // Check the second case. + $start += 24; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 18), $found); + + }//end testMatchFunctionCallArm() + + + /** + * Test match expression with closure. + * + * @return void + */ + public function testMatchClosure() + { + $start = $this->getTargetToken('/* testMatchClosure */', T_LNUMBER); + $start += 14; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 10), $found); + + $start += 17; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 10), $found); + + }//end testMatchClosure() + + + /** + * Test match expression with array declaration. + * + * @return void + */ + public function testMatchArray() + { + // Start of first case statement. + $start = $this->getTargetToken('/* testMatchArray */', T_LNUMBER); + $found = self::$phpcsFile->findStartOfStatement($start); + $this->assertSame($start, $found); + + // Comma after first statement. + $start += 11; + $found = self::$phpcsFile->findStartOfStatement($start); + $this->assertSame(($start - 7), $found); + + // Start of second case statement. + $start += 3; + $found = self::$phpcsFile->findStartOfStatement($start); + $this->assertSame($start, $found); + + // Comma after first statement. + $start += 30; + $found = self::$phpcsFile->findStartOfStatement($start); + $this->assertSame(($start - 26), $found); + + }//end testMatchArray() + + + /** + * Test nested match expressions. + * + * @return void + */ + public function testNestedMatch() + { + $start = $this->getTargetToken('/* testNestedMatch */', T_LNUMBER); + $start += 30; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 26), $found); + + $start -= 4; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 1), $found); + + $start -= 3; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 2), $found); + + }//end testNestedMatch() + + + /** + * Test PHP open tag. + * + * @return void + */ + public function testOpenTag() + { + $start = $this->getTargetToken('/* testOpenTag */', T_OPEN_TAG); + $start += 2; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 1), $found); + + }//end testOpenTag() + + + /** + * Test PHP short open echo tag. + * + * @return void + */ + public function testOpenTagWithEcho() + { + $start = $this->getTargetToken('/* testOpenTagWithEcho */', T_OPEN_TAG_WITH_ECHO); + $start += 3; + $found = self::$phpcsFile->findStartOfStatement($start); + + $this->assertSame(($start - 1), $found); + + }//end testOpenTagWithEcho() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMemberPropertiesTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMemberPropertiesTest.inc new file mode 100644 index 00000000..f40b6021 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMemberPropertiesTest.inc @@ -0,0 +1,304 @@ + 'a', 'b' => 'b' ), + /* testGroupPrivate 3 */ + $varQ = 'string', + /* testGroupPrivate 4 */ + $varR = 123, + /* testGroupPrivate 5 */ + $varS = ONE / self::THREE, + /* testGroupPrivate 6 */ + $varT = [ + 'a' => 'a', + 'b' => 'b' + ], + /* testGroupPrivate 7 */ + $varU = __DIR__ . "/base"; + + + /* testMethodParam */ + public function methodName($param) { + /* testImportedGlobal */ + global $importedGlobal = true; + + /* testLocalVariable */ + $localVariable = true; + } + + /* testPropertyAfterMethod */ + private static $varV = true; + + /* testMessyNullableType */ + public /* comment + */ ? //comment + array $foo = []; + + /* testNamespaceType */ + public \MyNamespace\MyClass $foo; + + /* testNullableNamespaceType 1 */ + private ?ClassName $nullableClassType; + + /* testNullableNamespaceType 2 */ + protected ?Folder\ClassName $nullableClassType2; + + /* testMultilineNamespaceType */ + public \MyNamespace /** comment *\/ comment */ + \MyClass /* comment */ + \Foo $foo; + +} + +interface Base +{ + /* testInterfaceProperty */ + protected $anonymous; +} + +/* testGlobalVariable */ +$globalVariable = true; + +/* testNotAVariable */ +return; + +$a = ( $foo == $bar ? new stdClass() : + new class() { + /* testNestedProperty 1 */ + public $var = true; + + /* testNestedMethodParam 1 */ + public function something($var = false) {} + } +); + +function_call( 'param', new class { + /* testNestedProperty 2 */ + public $year = 2017; + + /* testNestedMethodParam 2 */ + public function __construct( $open, $post_id ) {} +}, 10, 2 ); + +class PHP8Mixed { + /* testPHP8MixedTypeHint */ + public static miXed $mixed; + + /* testPHP8MixedTypeHintNullable */ + // Intentional fatal error - nullability is not allowed with mixed, but that's not the concern of the method. + private ?mixed $nullableMixed; +} + +class NSOperatorInType { + /* testNamespaceOperatorTypeHint */ + public ?namespace\Name $prop; +} + +$anon = class() { + /* testPHP8UnionTypesSimple */ + public int|float $unionTypeSimple; + + /* testPHP8UnionTypesTwoClasses */ + private MyClassA|\Package\MyClassB $unionTypesTwoClasses; + + /* testPHP8UnionTypesAllBaseTypes */ + protected array|bool|int|float|NULL|object|string $unionTypesAllBaseTypes; + + /* testPHP8UnionTypesAllPseudoTypes */ + // Intentional fatal error - mixing types which cannot be combined, but that's not the concern of the method. + var false|mixed|self|parent|iterable|Resource $unionTypesAllPseudoTypes; + + /* testPHP8UnionTypesIllegalTypes */ + // Intentional fatal error - types which are not allowed for properties, but that's not the concern of the method. + public callable|static|void $unionTypesIllegalTypes; + + /* testPHP8UnionTypesNullable */ + // Intentional fatal error - nullability is not allowed with union types, but that's not the concern of the method. + public ?int|float $unionTypesNullable; + + /* testPHP8PseudoTypeNull */ + // Intentional fatal error - null pseudotype is only allowed in union types, but that's not the concern of the method. + public null $pseudoTypeNull; + + /* testPHP8PseudoTypeFalse */ + // Intentional fatal error - false pseudotype is only allowed in union types, but that's not the concern of the method. + public false $pseudoTypeFalse; + + /* testPHP8PseudoTypeFalseAndBool */ + // Intentional fatal error - false pseudotype is not allowed in combination with bool, but that's not the concern of the method. + public bool|FALSE $pseudoTypeFalseAndBool; + + /* testPHP8ObjectAndClass */ + // Intentional fatal error - object is not allowed in combination with class name, but that's not the concern of the method. + public object|ClassName $objectAndClass; + + /* testPHP8PseudoTypeIterableAndArray */ + // Intentional fatal error - iterable pseudotype is not allowed in combination with array or Traversable, but that's not the concern of the method. + public iterable|array|Traversable $pseudoTypeIterableAndArray; + + /* testPHP8DuplicateTypeInUnionWhitespaceAndComment */ + // Intentional fatal error - duplicate types are not allowed in union types, but that's not the concern of the method. + public int |string| /*comment*/ INT $duplicateTypeInUnion; + + /* testPHP81Readonly */ + public readonly int $readonly; + + /* testPHP81ReadonlyWithNullableType */ + public readonly ?array $array; + + /* testPHP81ReadonlyWithUnionType */ + public readonly string|int $readonlyWithUnionType; + + /* testPHP81ReadonlyWithUnionTypeWithNull */ + protected ReadOnly string|null $readonlyWithUnionTypeWithNull; + + /* testPHP81OnlyReadonlyWithUnionType */ + readonly string|int $onlyReadonly; +}; + +$anon = class { + /* testPHP8PropertySingleAttribute */ + #[PropertyWithAttribute] + public string $foo; + + /* testPHP8PropertyMultipleAttributes */ + #[PropertyWithAttribute(foo: 'bar'), MyAttribute] + protected ?int|float $bar; + + /* testPHP8PropertyMultilineAttribute */ + #[ + PropertyWithAttribute(/* comment */ 'baz') + ] + private mixed $baz; +}; + +enum Suit +{ + /* testEnumProperty */ + protected $anonymous; +} + +enum Direction implements ArrayAccess +{ + case Up; + case Down; + + /* testEnumMethodParamNotProperty */ + public function offsetGet($val) { ... } +} + +$anon = class() { + /* testPHP81IntersectionTypes */ + public Foo&Bar $intersectionType; + + /* testPHP81MoreIntersectionTypes */ + public Foo&Bar&Baz $moreIntersectionTypes; + + /* testPHP81IllegalIntersectionTypes */ + // Intentional fatal error - types which are not allowed for intersection type, but that's not the concern of the method. + public int&string $illegalIntersectionType; + + /* testPHP81NulltableIntersectionType */ + // Intentional fatal error - nullability is not allowed with intersection type, but that's not the concern of the method. + public ?Foo&Bar $nullableIntersectionType; +}; diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMemberPropertiesTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMemberPropertiesTest.php new file mode 100644 index 00000000..934d8e28 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMemberPropertiesTest.php @@ -0,0 +1,864 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\File; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class GetMemberPropertiesTest extends AbstractMethodUnitTest +{ + + + /** + * Test the getMemberProperties() method. + * + * @param string $identifier Comment which precedes the test case. + * @param bool $expected Expected function output. + * + * @dataProvider dataGetMemberProperties + * + * @return void + */ + public function testGetMemberProperties($identifier, $expected) + { + $variable = $this->getTargetToken($identifier, T_VARIABLE); + $result = self::$phpcsFile->getMemberProperties($variable); + + $this->assertArraySubset($expected, $result, true); + + }//end testGetMemberProperties() + + + /** + * Data provider for the GetMemberProperties test. + * + * @see testGetMemberProperties() + * + * @return array + */ + public function dataGetMemberProperties() + { + return [ + [ + '/* testVar */', + [ + 'scope' => 'public', + 'scope_specified' => false, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testVarType */', + [ + 'scope' => 'public', + 'scope_specified' => false, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '?int', + 'nullable_type' => true, + ], + ], + [ + '/* testPublic */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testPublicType */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'string', + 'nullable_type' => false, + ], + ], + [ + '/* testProtected */', + [ + 'scope' => 'protected', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testProtectedType */', + [ + 'scope' => 'protected', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'bool', + 'nullable_type' => false, + ], + ], + [ + '/* testPrivate */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testPrivateType */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'array', + 'nullable_type' => false, + ], + ], + [ + '/* testStatic */', + [ + 'scope' => 'public', + 'scope_specified' => false, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testStaticType */', + [ + 'scope' => 'public', + 'scope_specified' => false, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '?string', + 'nullable_type' => true, + ], + ], + [ + '/* testStaticVar */', + [ + 'scope' => 'public', + 'scope_specified' => false, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testVarStatic */', + [ + 'scope' => 'public', + 'scope_specified' => false, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testPublicStatic */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testProtectedStatic */', + [ + 'scope' => 'protected', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testPrivateStatic */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testNoPrefix */', + [ + 'scope' => 'public', + 'scope_specified' => false, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testPublicStaticWithDocblock */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testProtectedStaticWithDocblock */', + [ + 'scope' => 'protected', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testPrivateStaticWithDocblock */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testGroupType 1 */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'float', + 'nullable_type' => false, + ], + ], + [ + '/* testGroupType 2 */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'float', + 'nullable_type' => false, + ], + ], + [ + '/* testGroupNullableType 1 */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '?string', + 'nullable_type' => true, + ], + ], + [ + '/* testGroupNullableType 2 */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '?string', + 'nullable_type' => true, + ], + ], + [ + '/* testGroupProtectedStatic 1 */', + [ + 'scope' => 'protected', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testGroupProtectedStatic 2 */', + [ + 'scope' => 'protected', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testGroupProtectedStatic 3 */', + [ + 'scope' => 'protected', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testGroupPrivate 1 */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testGroupPrivate 2 */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testGroupPrivate 3 */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testGroupPrivate 4 */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testGroupPrivate 5 */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testGroupPrivate 6 */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testGroupPrivate 7 */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testMessyNullableType */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '?array', + 'nullable_type' => true, + ], + ], + [ + '/* testNamespaceType */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '\MyNamespace\MyClass', + 'nullable_type' => false, + ], + ], + [ + '/* testNullableNamespaceType 1 */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '?ClassName', + 'nullable_type' => true, + ], + ], + [ + '/* testNullableNamespaceType 2 */', + [ + 'scope' => 'protected', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '?Folder\ClassName', + 'nullable_type' => true, + ], + ], + [ + '/* testMultilineNamespaceType */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '\MyNamespace\MyClass\Foo', + 'nullable_type' => false, + ], + ], + [ + '/* testPropertyAfterMethod */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testInterfaceProperty */', + [], + ], + [ + '/* testNestedProperty 1 */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testNestedProperty 2 */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8MixedTypeHint */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => true, + 'is_readonly' => false, + 'type' => 'miXed', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8MixedTypeHintNullable */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '?mixed', + 'nullable_type' => true, + ], + ], + [ + '/* testNamespaceOperatorTypeHint */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '?namespace\Name', + 'nullable_type' => true, + ], + ], + [ + '/* testPHP8UnionTypesSimple */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'int|float', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8UnionTypesTwoClasses */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'MyClassA|\Package\MyClassB', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8UnionTypesAllBaseTypes */', + [ + 'scope' => 'protected', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'array|bool|int|float|NULL|object|string', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8UnionTypesAllPseudoTypes */', + [ + 'scope' => 'public', + 'scope_specified' => false, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'false|mixed|self|parent|iterable|Resource', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8UnionTypesIllegalTypes */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + // Missing static, but that's OK as not an allowed syntax. + 'type' => 'callable||void', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8UnionTypesNullable */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '?int|float', + 'nullable_type' => true, + ], + ], + [ + '/* testPHP8PseudoTypeNull */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'null', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8PseudoTypeFalse */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'false', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8PseudoTypeFalseAndBool */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'bool|FALSE', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8ObjectAndClass */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'object|ClassName', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8PseudoTypeIterableAndArray */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'iterable|array|Traversable', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8DuplicateTypeInUnionWhitespaceAndComment */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'int|string|INT', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP81Readonly */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => true, + 'type' => 'int', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP81ReadonlyWithNullableType */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => true, + 'type' => '?array', + 'nullable_type' => true, + ], + ], + [ + '/* testPHP81ReadonlyWithUnionType */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => true, + 'type' => 'string|int', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP81ReadonlyWithUnionTypeWithNull */', + [ + 'scope' => 'protected', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => true, + 'type' => 'string|null', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP81OnlyReadonlyWithUnionType */', + [ + 'scope' => 'public', + 'scope_specified' => false, + 'is_static' => false, + 'is_readonly' => true, + 'type' => 'string|int', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8PropertySingleAttribute */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'string', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP8PropertyMultipleAttributes */', + [ + 'scope' => 'protected', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => '?int|float', + 'nullable_type' => true, + ], + ], + [ + '/* testPHP8PropertyMultilineAttribute */', + [ + 'scope' => 'private', + 'scope_specified' => true, + 'is_static' => false, + 'is_readonly' => false, + 'type' => 'mixed', + 'nullable_type' => false, + ], + ], + [ + '/* testEnumProperty */', + [], + ], + [ + '/* testPHP81IntersectionTypes */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'type' => 'Foo&Bar', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP81MoreIntersectionTypes */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'type' => 'Foo&Bar&Baz', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP81IllegalIntersectionTypes */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'type' => 'int&string', + 'nullable_type' => false, + ], + ], + [ + '/* testPHP81NulltableIntersectionType */', + [ + 'scope' => 'public', + 'scope_specified' => true, + 'is_static' => false, + 'type' => '?Foo&Bar', + 'nullable_type' => true, + ], + ], + ]; + + }//end dataGetMemberProperties() + + + /** + * Test receiving an expected exception when a non property is passed. + * + * @param string $identifier Comment which precedes the test case. + * + * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException + * @expectedExceptionMessage $stackPtr is not a class member var + * + * @dataProvider dataNotClassProperty + * + * @return void + */ + public function testNotClassPropertyException($identifier) + { + $variable = $this->getTargetToken($identifier, T_VARIABLE); + $result = self::$phpcsFile->getMemberProperties($variable); + + }//end testNotClassPropertyException() + + + /** + * Data provider for the NotClassPropertyException test. + * + * @see testNotClassPropertyException() + * + * @return array + */ + public function dataNotClassProperty() + { + return [ + ['/* testMethodParam */'], + ['/* testImportedGlobal */'], + ['/* testLocalVariable */'], + ['/* testGlobalVariable */'], + ['/* testNestedMethodParam 1 */'], + ['/* testNestedMethodParam 2 */'], + ['/* testEnumMethodParamNotProperty */'], + ]; + + }//end dataNotClassProperty() + + + /** + * Test receiving an expected exception when a non variable is passed. + * + * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException + * @expectedExceptionMessage $stackPtr must be of type T_VARIABLE + * + * @return void + */ + public function testNotAVariableException() + { + $next = $this->getTargetToken('/* testNotAVariable */', T_RETURN); + $result = self::$phpcsFile->getMemberProperties($next); + + }//end testNotAVariableException() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodParametersTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodParametersTest.inc new file mode 100644 index 00000000..dc465491 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodParametersTest.inc @@ -0,0 +1,164 @@ + $b; + +/* testPHP8MixedTypeHint */ +function mixedTypeHint(mixed &...$var1) {} + +/* testPHP8MixedTypeHintNullable */ +// Intentional fatal error - nullability is not allowed with mixed, but that's not the concern of the method. +function mixedTypeHintNullable(?Mixed $var1) {} + +/* testNamespaceOperatorTypeHint */ +function namespaceOperatorTypeHint(?namespace\Name $var1) {} + +/* testPHP8UnionTypesSimple */ +function unionTypeSimple(int|float $number, self|parent &...$obj) {} + +/* testPHP8UnionTypesWithSpreadOperatorAndReference */ +function globalFunctionWithSpreadAndReference(float|null &$paramA, string|int ...$paramB) {} + +/* testPHP8UnionTypesSimpleWithBitwiseOrInDefault */ +$fn = fn(int|float $var = CONSTANT_A | CONSTANT_B) => $var; + +/* testPHP8UnionTypesTwoClasses */ +function unionTypesTwoClasses(MyClassA|\Package\MyClassB $var) {} + +/* testPHP8UnionTypesAllBaseTypes */ +function unionTypesAllBaseTypes(array|bool|callable|int|float|null|object|string $var) {} + +/* testPHP8UnionTypesAllPseudoTypes */ +// Intentional fatal error - mixing types which cannot be combined, but that's not the concern of the method. +function unionTypesAllPseudoTypes(false|mixed|self|parent|iterable|Resource $var) {} + +/* testPHP8UnionTypesNullable */ +// Intentional fatal error - nullability is not allowed with union types, but that's not the concern of the method. +$closure = function (?int|float $number) {}; + +/* testPHP8PseudoTypeNull */ +// Intentional fatal error - null pseudotype is only allowed in union types, but that's not the concern of the method. +function pseudoTypeNull(null $var = null) {} + +/* testPHP8PseudoTypeFalse */ +// Intentional fatal error - false pseudotype is only allowed in union types, but that's not the concern of the method. +function pseudoTypeFalse(false $var = false) {} + +/* testPHP8PseudoTypeFalseAndBool */ +// Intentional fatal error - false pseudotype is not allowed in combination with bool, but that's not the concern of the method. +function pseudoTypeFalseAndBool(bool|false $var = false) {} + +/* testPHP8ObjectAndClass */ +// Intentional fatal error - object is not allowed in combination with class name, but that's not the concern of the method. +function objectAndClass(object|ClassName $var) {} + +/* testPHP8PseudoTypeIterableAndArray */ +// Intentional fatal error - iterable pseudotype is not allowed in combination with array or Traversable, but that's not the concern of the method. +function pseudoTypeIterableAndArray(iterable|array|Traversable $var) {} + +/* testPHP8DuplicateTypeInUnionWhitespaceAndComment */ +// Intentional fatal error - duplicate types are not allowed in union types, but that's not the concern of the method. +function duplicateTypeInUnion( int | string /*comment*/ | INT $var) {} + +class ConstructorPropertyPromotionNoTypes { + /* testPHP8ConstructorPropertyPromotionNoTypes */ + public function __construct( + public $x = 0.0, + protected $y = '', + private $z = null, + ) {} +} + +class ConstructorPropertyPromotionWithTypes { + /* testPHP8ConstructorPropertyPromotionWithTypes */ + public function __construct(protected float|int $x, public ?string &$y = 'test', private mixed $z) {} +} + +class ConstructorPropertyPromotionAndNormalParams { + /* testPHP8ConstructorPropertyPromotionAndNormalParam */ + public function __construct(public int $promotedProp, ?int $normalArg) {} +} + +class ConstructorPropertyPromotionWithReadOnly { + /* testPHP81ConstructorPropertyPromotionWithReadOnly */ + public function __construct(public readonly ?int $promotedProp, readonly private string|bool &$promotedToo) {} +} + +/* testPHP8ConstructorPropertyPromotionGlobalFunction */ +// Intentional fatal error. Property promotion not allowed in non-constructor, but that's not the concern of this method. +function globalFunction(private $x) {} + +abstract class ConstructorPropertyPromotionAbstractMethod { + /* testPHP8ConstructorPropertyPromotionAbstractMethod */ + // Intentional fatal error. + // 1. Property promotion not allowed in abstract method, but that's not the concern of this method. + // 2. Variadic arguments not allowed in property promotion, but that's not the concern of this method. + // 3. The callable type is not supported for properties, but that's not the concern of this method. + abstract public function __construct(public callable $y, private ...$x); +} + +/* testCommentsInParameter */ +function commentsInParams( + // Leading comment. + ?MyClass /*-*/ & /*-*/.../*-*/ $param /*-*/ = /*-*/ 'default value' . /*-*/ 'second part' // Trailing comment. +) {} + +/* testParameterAttributesInFunctionDeclaration */ +class ParametersWithAttributes( + public function __construct( + #[\MyExample\MyAttribute] private string $constructorPropPromTypedParamSingleAttribute, + #[MyAttr([1, 2])] + Type|false + $typedParamSingleAttribute, + #[MyAttribute(1234), MyAttribute(5678)] ?int $nullableTypedParamMultiAttribute, + #[WithoutArgument] #[SingleArgument(0)] $nonTypedParamTwoAttributes, + #[MyAttribute(array("key" => "value"))] + &...$otherParam, + ) {} +} + +/* testPHP8IntersectionTypes */ +function intersectionTypes(Foo&Bar $obj1, Boo&Bar $obj2) {} + +/* testPHP81IntersectionTypesWithSpreadOperatorAndReference */ +function globalFunctionWithSpreadAndReference(Boo&Bar &$paramA, Foo&Bar ...$paramB) {} + +/* testPHP81MoreIntersectionTypes */ +function moreIntersectionTypes(MyClassA&\Package\MyClassB&\Package\MyClassC $var) {} + +/* testPHP81IllegalIntersectionTypes */ +// Intentional fatal error - simple types are not allowed with intersection types, but that's not the concern of the method. +$closure = function (string&int $numeric_string) {}; + +/* testPHP81NullableIntersectionTypes */ +// Intentional fatal error - nullability is not allowed with intersection types, but that's not the concern of the method. +$closure = function (?Foo&Bar $object) {}; diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodParametersTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodParametersTest.php new file mode 100644 index 00000000..ba4d7544 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodParametersTest.php @@ -0,0 +1,1145 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\File; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class GetMethodParametersTest extends AbstractMethodUnitTest +{ + + + /** + * Verify pass-by-reference parsing. + * + * @return void + */ + public function testPassByReference() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => '&$var', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'variable_length' => false, + 'type_hint' => '', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPassByReference() + + + /** + * Verify array hint parsing. + * + * @return void + */ + public function testArrayHint() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => 'array $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'array', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testArrayHint() + + + /** + * Verify variable. + * + * @return void + */ + public function testVariable() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => '$var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testVariable() + + + /** + * Verify default value parsing with a single function param. + * + * @return void + */ + public function testSingleDefaultValue() + { + $expected = []; + $expected[0] = [ + 'name' => '$var1', + 'content' => '$var1=self::CONSTANT', + 'has_attributes' => false, + 'default' => 'self::CONSTANT', + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testSingleDefaultValue() + + + /** + * Verify default value parsing. + * + * @return void + */ + public function testDefaultValues() + { + $expected = []; + $expected[0] = [ + 'name' => '$var1', + 'content' => '$var1=1', + 'has_attributes' => false, + 'default' => '1', + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '', + 'nullable_type' => false, + ]; + $expected[1] = [ + 'name' => '$var2', + 'content' => "\$var2='value'", + 'has_attributes' => false, + 'default' => "'value'", + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testDefaultValues() + + + /** + * Verify type hint parsing. + * + * @return void + */ + public function testTypeHint() + { + $expected = []; + $expected[0] = [ + 'name' => '$var1', + 'content' => 'foo $var1', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'foo', + 'nullable_type' => false, + ]; + + $expected[1] = [ + 'name' => '$var2', + 'content' => 'bar $var2', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'bar', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testTypeHint() + + + /** + * Verify self type hint parsing. + * + * @return void + */ + public function testSelfTypeHint() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => 'self $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'self', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testSelfTypeHint() + + + /** + * Verify nullable type hint parsing. + * + * @return void + */ + public function testNullableTypeHint() + { + $expected = []; + $expected[0] = [ + 'name' => '$var1', + 'content' => '?int $var1', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '?int', + 'nullable_type' => true, + ]; + + $expected[1] = [ + 'name' => '$var2', + 'content' => '?\bar $var2', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '?\bar', + 'nullable_type' => true, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testNullableTypeHint() + + + /** + * Verify "bitwise and" in default value !== pass-by-reference. + * + * @return void + */ + public function testBitwiseAndConstantExpressionDefaultValue() + { + $expected = []; + $expected[0] = [ + 'name' => '$a', + 'content' => '$a = 10 & 20', + 'default' => '10 & 20', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testBitwiseAndConstantExpressionDefaultValue() + + + /** + * Verify that arrow functions are supported. + * + * @return void + */ + public function testArrowFunction() + { + $expected = []; + $expected[0] = [ + 'name' => '$a', + 'content' => 'int $a', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'int', + 'nullable_type' => false, + ]; + + $expected[1] = [ + 'name' => '$b', + 'content' => '...$b', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => true, + 'type_hint' => '', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testArrowFunction() + + + /** + * Verify recognition of PHP8 mixed type declaration. + * + * @return void + */ + public function testPHP8MixedTypeHint() + { + $expected = []; + $expected[0] = [ + 'name' => '$var1', + 'content' => 'mixed &...$var1', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'variable_length' => true, + 'type_hint' => 'mixed', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8MixedTypeHint() + + + /** + * Verify recognition of PHP8 mixed type declaration with nullability. + * + * @return void + */ + public function testPHP8MixedTypeHintNullable() + { + $expected = []; + $expected[0] = [ + 'name' => '$var1', + 'content' => '?Mixed $var1', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '?Mixed', + 'nullable_type' => true, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8MixedTypeHintNullable() + + + /** + * Verify recognition of type declarations using the namespace operator. + * + * @return void + */ + public function testNamespaceOperatorTypeHint() + { + $expected = []; + $expected[0] = [ + 'name' => '$var1', + 'content' => '?namespace\Name $var1', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '?namespace\Name', + 'nullable_type' => true, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testNamespaceOperatorTypeHint() + + + /** + * Verify recognition of PHP8 union type declaration. + * + * @return void + */ + public function testPHP8UnionTypesSimple() + { + $expected = []; + $expected[0] = [ + 'name' => '$number', + 'content' => 'int|float $number', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'int|float', + 'nullable_type' => false, + ]; + $expected[1] = [ + 'name' => '$obj', + 'content' => 'self|parent &...$obj', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'variable_length' => true, + 'type_hint' => 'self|parent', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesSimple() + + + /** + * Verify recognition of PHP8 union type declaration when the variable has either a spread operator or a reference. + * + * @return void + */ + public function testPHP8UnionTypesWithSpreadOperatorAndReference() + { + $expected = []; + $expected[0] = [ + 'name' => '$paramA', + 'content' => 'float|null &$paramA', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'variable_length' => false, + 'type_hint' => 'float|null', + 'nullable_type' => false, + ]; + $expected[1] = [ + 'name' => '$paramB', + 'content' => 'string|int ...$paramB', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => true, + 'type_hint' => 'string|int', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesWithSpreadOperatorAndReference() + + + /** + * Verify recognition of PHP8 union type declaration with a bitwise or in the default value. + * + * @return void + */ + public function testPHP8UnionTypesSimpleWithBitwiseOrInDefault() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => 'int|float $var = CONSTANT_A | CONSTANT_B', + 'default' => 'CONSTANT_A | CONSTANT_B', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'int|float', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesSimpleWithBitwiseOrInDefault() + + + /** + * Verify recognition of PHP8 union type declaration with two classes. + * + * @return void + */ + public function testPHP8UnionTypesTwoClasses() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => 'MyClassA|\Package\MyClassB $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'MyClassA|\Package\MyClassB', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesTwoClasses() + + + /** + * Verify recognition of PHP8 union type declaration with all base types. + * + * @return void + */ + public function testPHP8UnionTypesAllBaseTypes() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => 'array|bool|callable|int|float|null|object|string $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'array|bool|callable|int|float|null|object|string', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesAllBaseTypes() + + + /** + * Verify recognition of PHP8 union type declaration with all pseudo types. + * + * @return void + */ + public function testPHP8UnionTypesAllPseudoTypes() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => 'false|mixed|self|parent|iterable|Resource $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'false|mixed|self|parent|iterable|Resource', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesAllPseudoTypes() + + + /** + * Verify recognition of PHP8 union type declaration with (illegal) nullability. + * + * @return void + */ + public function testPHP8UnionTypesNullable() + { + $expected = []; + $expected[0] = [ + 'name' => '$number', + 'content' => '?int|float $number', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '?int|float', + 'nullable_type' => true, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesNullable() + + + /** + * Verify recognition of PHP8 type declaration with (illegal) single type null. + * + * @return void + */ + public function testPHP8PseudoTypeNull() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => 'null $var = null', + 'default' => 'null', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'null', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8PseudoTypeNull() + + + /** + * Verify recognition of PHP8 type declaration with (illegal) single type false. + * + * @return void + */ + public function testPHP8PseudoTypeFalse() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => 'false $var = false', + 'default' => 'false', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'false', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8PseudoTypeFalse() + + + /** + * Verify recognition of PHP8 type declaration with (illegal) type false combined with type bool. + * + * @return void + */ + public function testPHP8PseudoTypeFalseAndBool() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => 'bool|false $var = false', + 'default' => 'false', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'bool|false', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8PseudoTypeFalseAndBool() + + + /** + * Verify recognition of PHP8 type declaration with (illegal) type object combined with a class name. + * + * @return void + */ + public function testPHP8ObjectAndClass() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => 'object|ClassName $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'object|ClassName', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8ObjectAndClass() + + + /** + * Verify recognition of PHP8 type declaration with (illegal) type iterable combined with array/Traversable. + * + * @return void + */ + public function testPHP8PseudoTypeIterableAndArray() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => 'iterable|array|Traversable $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'iterable|array|Traversable', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8PseudoTypeIterableAndArray() + + + /** + * Verify recognition of PHP8 type declaration with (illegal) duplicate types. + * + * @return void + */ + public function testPHP8DuplicateTypeInUnionWhitespaceAndComment() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => 'int | string /*comment*/ | INT $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'int|string|INT', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8DuplicateTypeInUnionWhitespaceAndComment() + + + /** + * Verify recognition of PHP8 constructor property promotion without type declaration, with defaults. + * + * @return void + */ + public function testPHP8ConstructorPropertyPromotionNoTypes() + { + $expected = []; + $expected[0] = [ + 'name' => '$x', + 'content' => 'public $x = 0.0', + 'default' => '0.0', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '', + 'nullable_type' => false, + 'property_visibility' => 'public', + 'property_readonly' => false, + ]; + $expected[1] = [ + 'name' => '$y', + 'content' => 'protected $y = \'\'', + 'default' => "''", + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '', + 'nullable_type' => false, + 'property_visibility' => 'protected', + 'property_readonly' => false, + ]; + $expected[2] = [ + 'name' => '$z', + 'content' => 'private $z = null', + 'default' => 'null', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '', + 'nullable_type' => false, + 'property_visibility' => 'private', + 'property_readonly' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8ConstructorPropertyPromotionNoTypes() + + + /** + * Verify recognition of PHP8 constructor property promotion with type declarations. + * + * @return void + */ + public function testPHP8ConstructorPropertyPromotionWithTypes() + { + $expected = []; + $expected[0] = [ + 'name' => '$x', + 'content' => 'protected float|int $x', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'float|int', + 'nullable_type' => false, + 'property_visibility' => 'protected', + 'property_readonly' => false, + ]; + $expected[1] = [ + 'name' => '$y', + 'content' => 'public ?string &$y = \'test\'', + 'default' => "'test'", + 'has_attributes' => false, + 'pass_by_reference' => true, + 'variable_length' => false, + 'type_hint' => '?string', + 'nullable_type' => true, + 'property_visibility' => 'public', + 'property_readonly' => false, + ]; + $expected[2] = [ + 'name' => '$z', + 'content' => 'private mixed $z', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'mixed', + 'nullable_type' => false, + 'property_visibility' => 'private', + 'property_readonly' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8ConstructorPropertyPromotionWithTypes() + + + /** + * Verify recognition of PHP8 constructor with both property promotion as well as normal parameters. + * + * @return void + */ + public function testPHP8ConstructorPropertyPromotionAndNormalParam() + { + $expected = []; + $expected[0] = [ + 'name' => '$promotedProp', + 'content' => 'public int $promotedProp', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'int', + 'nullable_type' => false, + 'property_visibility' => 'public', + 'property_readonly' => false, + ]; + $expected[1] = [ + 'name' => '$normalArg', + 'content' => '?int $normalArg', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '?int', + 'nullable_type' => true, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8ConstructorPropertyPromotionAndNormalParam() + + + /** + * Verify recognition of PHP8 constructor with property promotion using PHP 8.1 readonly keyword. + * + * @return void + */ + public function testPHP81ConstructorPropertyPromotionWithReadOnly() + { + $expected = []; + $expected[0] = [ + 'name' => '$promotedProp', + 'content' => 'public readonly ?int $promotedProp', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '?int', + 'nullable_type' => true, + 'property_visibility' => 'public', + 'property_readonly' => true, + ]; + $expected[1] = [ + 'name' => '$promotedToo', + 'content' => 'readonly private string|bool &$promotedToo', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'variable_length' => false, + 'type_hint' => 'string|bool', + 'nullable_type' => false, + 'property_visibility' => 'private', + 'property_readonly' => true, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP81ConstructorPropertyPromotionWithReadOnly() + + + /** + * Verify behaviour when a non-constructor function uses PHP 8 property promotion syntax. + * + * @return void + */ + public function testPHP8ConstructorPropertyPromotionGlobalFunction() + { + $expected = []; + $expected[0] = [ + 'name' => '$x', + 'content' => 'private $x', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '', + 'nullable_type' => false, + 'property_visibility' => 'private', + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8ConstructorPropertyPromotionGlobalFunction() + + + /** + * Verify behaviour when an abstract constructor uses PHP 8 property promotion syntax. + * + * @return void + */ + public function testPHP8ConstructorPropertyPromotionAbstractMethod() + { + $expected = []; + $expected[0] = [ + 'name' => '$y', + 'content' => 'public callable $y', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'callable', + 'nullable_type' => false, + 'property_visibility' => 'public', + ]; + $expected[1] = [ + 'name' => '$x', + 'content' => 'private ...$x', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => true, + 'type_hint' => '', + 'nullable_type' => false, + 'property_visibility' => 'private', + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8ConstructorPropertyPromotionAbstractMethod() + + + /** + * Verify and document behaviour when there are comments within a parameter declaration. + * + * @return void + */ + public function testCommentsInParameter() + { + $expected = []; + $expected[0] = [ + 'name' => '$param', + 'content' => '// Leading comment. + ?MyClass /*-*/ & /*-*/.../*-*/ $param /*-*/ = /*-*/ \'default value\' . /*-*/ \'second part\' // Trailing comment.', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'variable_length' => true, + 'type_hint' => '?MyClass', + 'nullable_type' => true, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testCommentsInParameter() + + + /** + * Verify behaviour when parameters have attributes attached. + * + * @return void + */ + public function testParameterAttributesInFunctionDeclaration() + { + $expected = []; + $expected[0] = [ + 'name' => '$constructorPropPromTypedParamSingleAttribute', + 'content' => '#[\MyExample\MyAttribute] private string $constructorPropPromTypedParamSingleAttribute', + 'has_attributes' => true, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'string', + 'nullable_type' => false, + 'property_visibility' => 'private', + ]; + $expected[1] = [ + 'name' => '$typedParamSingleAttribute', + 'content' => '#[MyAttr([1, 2])] + Type|false + $typedParamSingleAttribute', + 'has_attributes' => true, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'Type|false', + 'nullable_type' => false, + ]; + $expected[2] = [ + 'name' => '$nullableTypedParamMultiAttribute', + 'content' => '#[MyAttribute(1234), MyAttribute(5678)] ?int $nullableTypedParamMultiAttribute', + 'has_attributes' => true, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '?int', + 'nullable_type' => true, + ]; + $expected[3] = [ + 'name' => '$nonTypedParamTwoAttributes', + 'content' => '#[WithoutArgument] #[SingleArgument(0)] $nonTypedParamTwoAttributes', + 'has_attributes' => true, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '', + 'nullable_type' => false, + ]; + $expected[4] = [ + 'name' => '$otherParam', + 'content' => '#[MyAttribute(array("key" => "value"))] + &...$otherParam', + 'has_attributes' => true, + 'pass_by_reference' => true, + 'variable_length' => true, + 'type_hint' => '', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testParameterAttributesInFunctionDeclaration() + + + /** + * Verify recognition of PHP8.1 intersection type declaration. + * + * @return void + */ + public function testPHP8IntersectionTypes() + { + $expected = []; + $expected[0] = [ + 'name' => '$obj1', + 'content' => 'Foo&Bar $obj1', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'Foo&Bar', + 'nullable_type' => false, + ]; + $expected[1] = [ + 'name' => '$obj2', + 'content' => 'Boo&Bar $obj2', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'Boo&Bar', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8IntersectionTypes() + + + /** + * Verify recognition of PHP8 intersection type declaration when the variable has either a spread operator or a reference. + * + * @return void + */ + public function testPHP81IntersectionTypesWithSpreadOperatorAndReference() + { + $expected = []; + $expected[0] = [ + 'name' => '$paramA', + 'content' => 'Boo&Bar &$paramA', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'variable_length' => false, + 'type_hint' => 'Boo&Bar', + 'nullable_type' => false, + ]; + $expected[1] = [ + 'name' => '$paramB', + 'content' => 'Foo&Bar ...$paramB', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => true, + 'type_hint' => 'Foo&Bar', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP81IntersectionTypesWithSpreadOperatorAndReference() + + + /** + * Verify recognition of PHP8.1 intersection type declaration with more types. + * + * @return void + */ + public function testPHP81MoreIntersectionTypes() + { + $expected = []; + $expected[0] = [ + 'name' => '$var', + 'content' => 'MyClassA&\Package\MyClassB&\Package\MyClassC $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'MyClassA&\Package\MyClassB&\Package\MyClassC', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP81MoreIntersectionTypes() + + + /** + * Verify recognition of PHP8.1 intersection type declaration with illegal simple types. + * + * @return void + */ + public function testPHP81IllegalIntersectionTypes() + { + $expected = []; + $expected[0] = [ + 'name' => '$numeric_string', + 'content' => 'string&int $numeric_string', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => 'string&int', + 'nullable_type' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP81IllegalIntersectionTypes() + + + /** + * Verify recognition of PHP8.1 intersection type declaration with (illegal) nullability. + * + * @return void + */ + public function testPHP81NullableIntersectionTypes() + { + $expected = []; + $expected[0] = [ + 'name' => '$object', + 'content' => '?Foo&Bar $object', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'variable_length' => false, + 'type_hint' => '?Foo&Bar', + 'nullable_type' => true, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP81NullableIntersectionTypes() + + + /** + * Test helper. + * + * @param string $commentString The comment which preceeds the test. + * @param array $expected The expected function output. + * + * @return void + */ + private function getMethodParametersTestHelper($commentString, $expected) + { + $function = $this->getTargetToken($commentString, [T_FUNCTION, T_CLOSURE, T_FN]); + $found = self::$phpcsFile->getMethodParameters($function); + + $this->assertArraySubset($expected, $found, true); + + }//end getMethodParametersTestHelper() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodPropertiesTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodPropertiesTest.inc new file mode 100644 index 00000000..0c592369 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodPropertiesTest.inc @@ -0,0 +1,152 @@ + $number + 1, + $numbers +); + +class ReturnMe { + /* testReturnTypeStatic */ + private function myFunction(): static { + return $this; + } +} + +/* testPHP8MixedTypeHint */ +function mixedTypeHint() :mixed {} + +/* testPHP8MixedTypeHintNullable */ +// Intentional fatal error - nullability is not allowed with mixed, but that's not the concern of the method. +function mixedTypeHintNullable(): ?mixed {} + +/* testNamespaceOperatorTypeHint */ +function namespaceOperatorTypeHint() : ?namespace\Name {} + +/* testPHP8UnionTypesSimple */ +function unionTypeSimple($number) : int|float {} + +/* testPHP8UnionTypesTwoClasses */ +$fn = fn($var): MyClassA|\Package\MyClassB => $var; + +/* testPHP8UnionTypesAllBaseTypes */ +function unionTypesAllBaseTypes() : array|bool|callable|int|float|null|Object|string {} + +/* testPHP8UnionTypesAllPseudoTypes */ +// Intentional fatal error - mixing types which cannot be combined, but that's not the concern of the method. +function unionTypesAllPseudoTypes($var) : false|MIXED|self|parent|static|iterable|Resource|void {} + +/* testPHP8UnionTypesNullable */ +// Intentional fatal error - nullability is not allowed with union types, but that's not the concern of the method. +$closure = function () use($a) :?int|float {}; + +/* testPHP8PseudoTypeNull */ +// Intentional fatal error - null pseudotype is only allowed in union types, but that's not the concern of the method. +function pseudoTypeNull(): null {} + +/* testPHP8PseudoTypeFalse */ +// Intentional fatal error - false pseudotype is only allowed in union types, but that's not the concern of the method. +function pseudoTypeFalse(): false {} + +/* testPHP8PseudoTypeFalseAndBool */ +// Intentional fatal error - false pseudotype is not allowed in combination with bool, but that's not the concern of the method. +function pseudoTypeFalseAndBool(): bool|false {} + +/* testPHP8ObjectAndClass */ +// Intentional fatal error - object is not allowed in combination with class name, but that's not the concern of the method. +function objectAndClass(): object|ClassName {} + +/* testPHP8PseudoTypeIterableAndArray */ +// Intentional fatal error - iterable pseudotype is not allowed in combination with array or Traversable, but that's not the concern of the method. +interface FooBar { + public function pseudoTypeIterableAndArray(): iterable|array|Traversable; +} + +/* testPHP8DuplicateTypeInUnionWhitespaceAndComment */ +// Intentional fatal error - duplicate types are not allowed in union types, but that's not the concern of the method. +function duplicateTypeInUnion(): int | /*comment*/ string | INT {} + +/* testPHP81NeverType */ +function never(): never {} + +/* testPHP81NullableNeverType */ +// Intentional fatal error - nullability is not allowed with never, but that's not the concern of the method. +function nullableNever(): ?never {} + +/* testPHP8IntersectionTypes */ +function intersectionTypes(): Foo&Bar {} + +/* testPHP81MoreIntersectionTypes */ +function moreIntersectionTypes(): MyClassA&\Package\MyClassB&\Package\MyClassC {} + +/* testPHP81IntersectionArrowFunction */ +$fn = fn($var): MyClassA&\Package\MyClassB => $var; + +/* testPHP81IllegalIntersectionTypes */ +// Intentional fatal error - simple types are not allowed with intersection types, but that's not the concern of the method. +$closure = function (): string&int {}; + +/* testPHP81NullableIntersectionTypes */ +// Intentional fatal error - nullability is not allowed with intersection types, but that's not the concern of the method. +$closure = function (): ?Foo&Bar {}; diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodPropertiesTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodPropertiesTest.php new file mode 100644 index 00000000..66f4eea3 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/GetMethodPropertiesTest.php @@ -0,0 +1,910 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\File; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class GetMethodPropertiesTest extends AbstractMethodUnitTest +{ + + + /** + * Test a basic function. + * + * @return void + */ + public function testBasicFunction() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testBasicFunction() + + + /** + * Test a function with a return type. + * + * @return void + */ + public function testReturnFunction() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'array', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testReturnFunction() + + + /** + * Test a closure used as a function argument. + * + * @return void + */ + public function testNestedClosure() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'int', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testNestedClosure() + + + /** + * Test a basic method. + * + * @return void + */ + public function testBasicMethod() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testBasicMethod() + + + /** + * Test a private static method. + * + * @return void + */ + public function testPrivateStaticMethod() + { + $expected = [ + 'scope' => 'private', + 'scope_specified' => true, + 'return_type' => '', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => true, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPrivateStaticMethod() + + + /** + * Test a basic final method. + * + * @return void + */ + public function testFinalMethod() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => true, + 'return_type' => '', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => true, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testFinalMethod() + + + /** + * Test a protected method with a return type. + * + * @return void + */ + public function testProtectedReturnMethod() + { + $expected = [ + 'scope' => 'protected', + 'scope_specified' => true, + 'return_type' => 'int', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testProtectedReturnMethod() + + + /** + * Test a public method with a return type. + * + * @return void + */ + public function testPublicReturnMethod() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => true, + 'return_type' => 'array', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPublicReturnMethod() + + + /** + * Test a public method with a nullable return type. + * + * @return void + */ + public function testNullableReturnMethod() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => true, + 'return_type' => '?array', + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testNullableReturnMethod() + + + /** + * Test a public method with a nullable return type. + * + * @return void + */ + public function testMessyNullableReturnMethod() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => true, + 'return_type' => '?array', + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testMessyNullableReturnMethod() + + + /** + * Test a method with a namespaced return type. + * + * @return void + */ + public function testReturnNamespace() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '\MyNamespace\MyClass', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testReturnNamespace() + + + /** + * Test a method with a messy namespaces return type. + * + * @return void + */ + public function testReturnMultilineNamespace() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '\MyNamespace\MyClass\Foo', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testReturnMultilineNamespace() + + + /** + * Test a basic abstract method. + * + * @return void + */ + public function testAbstractMethod() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '', + 'nullable_return_type' => false, + 'is_abstract' => true, + 'is_final' => false, + 'is_static' => false, + 'has_body' => false, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testAbstractMethod() + + + /** + * Test an abstract method with a return type. + * + * @return void + */ + public function testAbstractReturnMethod() + { + $expected = [ + 'scope' => 'protected', + 'scope_specified' => true, + 'return_type' => 'bool', + 'nullable_return_type' => false, + 'is_abstract' => true, + 'is_final' => false, + 'is_static' => false, + 'has_body' => false, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testAbstractReturnMethod() + + + /** + * Test a basic interface method. + * + * @return void + */ + public function testInterfaceMethod() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => false, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testInterfaceMethod() + + + /** + * Test a static arrow function. + * + * @return void + */ + public function testArrowFunction() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'int', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => true, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testArrowFunction() + + + /** + * Test a function with return type "static". + * + * @return void + */ + public function testReturnTypeStatic() + { + $expected = [ + 'scope' => 'private', + 'scope_specified' => true, + 'return_type' => 'static', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testReturnTypeStatic() + + + /** + * Test a function with return type "mixed". + * + * @return void + */ + public function testPHP8MixedTypeHint() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'mixed', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8MixedTypeHint() + + + /** + * Test a function with return type "mixed" and nullability. + * + * @return void + */ + public function testPHP8MixedTypeHintNullable() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?mixed', + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8MixedTypeHintNullable() + + + /** + * Test a function with return type using the namespace operator. + * + * @return void + */ + public function testNamespaceOperatorTypeHint() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?namespace\Name', + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testNamespaceOperatorTypeHint() + + + /** + * Verify recognition of PHP8 union type declaration. + * + * @return void + */ + public function testPHP8UnionTypesSimple() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'int|float', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesSimple() + + + /** + * Verify recognition of PHP8 union type declaration with two classes. + * + * @return void + */ + public function testPHP8UnionTypesTwoClasses() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'MyClassA|\Package\MyClassB', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesTwoClasses() + + + /** + * Verify recognition of PHP8 union type declaration with all base types. + * + * @return void + */ + public function testPHP8UnionTypesAllBaseTypes() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'array|bool|callable|int|float|null|Object|string', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesAllBaseTypes() + + + /** + * Verify recognition of PHP8 union type declaration with all pseudo types. + * + * @return void + */ + public function testPHP8UnionTypesAllPseudoTypes() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'false|MIXED|self|parent|static|iterable|Resource|void', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesAllPseudoTypes() + + + /** + * Verify recognition of PHP8 union type declaration with (illegal) nullability. + * + * @return void + */ + public function testPHP8UnionTypesNullable() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?int|float', + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesNullable() + + + /** + * Verify recognition of PHP8 type declaration with (illegal) single type null. + * + * @return void + */ + public function testPHP8PseudoTypeNull() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'null', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8PseudoTypeNull() + + + /** + * Verify recognition of PHP8 type declaration with (illegal) single type false. + * + * @return void + */ + public function testPHP8PseudoTypeFalse() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'false', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8PseudoTypeFalse() + + + /** + * Verify recognition of PHP8 type declaration with (illegal) type false combined with type bool. + * + * @return void + */ + public function testPHP8PseudoTypeFalseAndBool() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'bool|false', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8PseudoTypeFalseAndBool() + + + /** + * Verify recognition of PHP8 type declaration with (illegal) type object combined with a class name. + * + * @return void + */ + public function testPHP8ObjectAndClass() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'object|ClassName', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8ObjectAndClass() + + + /** + * Verify recognition of PHP8 type declaration with (illegal) type iterable combined with array/Traversable. + * + * @return void + */ + public function testPHP8PseudoTypeIterableAndArray() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => true, + 'return_type' => 'iterable|array|Traversable', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => false, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8PseudoTypeIterableAndArray() + + + /** + * Verify recognition of PHP8 type declaration with (illegal) duplicate types. + * + * @return void + */ + public function testPHP8DuplicateTypeInUnionWhitespaceAndComment() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'int|string|INT', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8DuplicateTypeInUnionWhitespaceAndComment() + + + /** + * Verify recognition of PHP8.1 type "never". + * + * @return void + */ + public function testPHP81NeverType() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'never', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP81NeverType() + + + /** + * Verify recognition of PHP8.1 type "never" with (illegal) nullability. + * + * @return void + */ + public function testPHP81NullableNeverType() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?never', + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP81NullableNeverType() + + + /** + * Verify recognition of PHP8.1 intersection type declaration. + * + * @return void + */ + public function testPHP8IntersectionTypes() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'Foo&Bar', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8IntersectionTypes() + + + /** + * Verify recognition of PHP8.1 intersection type declaration with more types. + * + * @return void + */ + public function testPHP81MoreIntersectionTypes() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'MyClassA&\Package\MyClassB&\Package\MyClassC', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP81MoreIntersectionTypes() + + + /** + * Verify recognition of PHP8.1 intersection type declaration in arrow function. + * + * @return void + */ + public function testPHP81IntersectionArrowFunction() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'MyClassA&\Package\MyClassB', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP81IntersectionArrowFunction() + + + /** + * Verify recognition of PHP8.1 intersection type declaration with illegal simple types. + * + * @return void + */ + public function testPHP81IllegalIntersectionTypes() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'string&int', + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP81IllegalIntersectionTypes() + + + /** + * Verify recognition of PHP8.1 intersection type declaration with (illegal) nullability. + * + * @return void + */ + public function testPHP81NullableIntersectionTypes() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?Foo&Bar', + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP81NullableIntersectionTypes() + + + /** + * Test helper. + * + * @param string $commentString The comment which preceeds the test. + * @param array $expected The expected function output. + * + * @return void + */ + private function getMethodPropertiesTestHelper($commentString, $expected) + { + $function = $this->getTargetToken($commentString, [T_FUNCTION, T_CLOSURE, T_FN]); + $found = self::$phpcsFile->getMethodProperties($function); + + $this->assertArraySubset($expected, $found, true); + + }//end getMethodPropertiesTestHelper() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/IsReferenceTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/File/IsReferenceTest.inc new file mode 100644 index 00000000..f71e2639 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/IsReferenceTest.inc @@ -0,0 +1,150 @@ + $first, 'b' => $something & $somethingElse ]; + +/* testBitwiseAndF */ +$a = array( 'a' => $first, 'b' => $something & \MyClass::$somethingElse ); + +/* testBitwiseAndG */ +$a = $something & $somethingElse; + +/* testBitwiseAndH */ +function myFunction($a = 10 & 20) {} + +/* testBitwiseAndI */ +$closure = function ($a = MY_CONSTANT & parent::OTHER_CONSTANT) {}; + +/* testFunctionReturnByReference */ +function &myFunction() {} + +/* testFunctionPassByReferenceA */ +function myFunction( &$a ) {} + +/* testFunctionPassByReferenceB */ +function myFunction( $a, &$b ) {} + +/* testFunctionPassByReferenceC */ +$closure = function ( &$a ) {}; + +/* testFunctionPassByReferenceD */ +$closure = function ( $a, &$b ) {}; + +/* testFunctionPassByReferenceE */ +function myFunction(array &$one) {} + +/* testFunctionPassByReferenceF */ +$closure = function (\MyClass &$one) {}; + +/* testFunctionPassByReferenceG */ +$closure = function ($param, &...$moreParams) {}; + +/* testForeachValueByReference */ +foreach( $array as $key => &$value ) {} + +/* testForeachKeyByReference */ +foreach( $array as &$key => $value ) {} + +/* testArrayValueByReferenceA */ +$a = [ 'a' => &$something ]; + +/* testArrayValueByReferenceB */ +$a = [ 'a' => $something, 'b' => &$somethingElse ]; + +/* testArrayValueByReferenceC */ +$a = [ &$something ]; + +/* testArrayValueByReferenceD */ +$a = [ $something, &$somethingElse ]; + +/* testArrayValueByReferenceE */ +$a = array( 'a' => &$something ); + +/* testArrayValueByReferenceF */ +$a = array( 'a' => $something, 'b' => &$somethingElse ); + +/* testArrayValueByReferenceG */ +$a = array( &$something ); + +/* testArrayValueByReferenceH */ +$a = array( $something, &$somethingElse ); + +/* testAssignByReferenceA */ +$b = &$something; + +/* testAssignByReferenceB */ +$b =& $something; + +/* testAssignByReferenceC */ +$b .= &$something; + +/* testAssignByReferenceD */ +$myValue = &$obj->getValue(); + +/* testAssignByReferenceE */ +$collection = &collector(); + +/* testPassByReferenceA */ +functionCall(&$something, $somethingElse); + +/* testPassByReferenceB */ +functionCall($something, &$somethingElse); + +/* testPassByReferenceC */ +functionCall($something, &$this->somethingElse); + +/* testPassByReferenceD */ +functionCall($something, &self::$somethingElse); + +/* testPassByReferenceE */ +functionCall($something, &parent::$somethingElse); + +/* testPassByReferenceF */ +functionCall($something, &static::$somethingElse); + +/* testPassByReferenceG */ +functionCall($something, &SomeClass::$somethingElse); + +/* testPassByReferenceH */ +functionCall(&\SomeClass::$somethingElse); + +/* testPassByReferenceI */ +functionCall($something, &\SomeNS\SomeClass::$somethingElse); + +/* testPassByReferenceJ */ +functionCall($something, &namespace\SomeClass::$somethingElse); + +/* testNewByReferenceA */ +$foobar2 = &new Foobar(); + +/* testNewByReferenceB */ +functionCall( $something , &new Foobar() ); + +/* testUseByReference */ +$closure = function() use (&$var){}; + +/* testArrowFunctionReturnByReference */ +fn&($x) => $x; + +/* testArrowFunctionPassByReferenceA */ +$fn = fn(array &$one) => 1; + +/* testArrowFunctionPassByReferenceB */ +$fn = fn($param, &...$moreParams) => 1; + +/* testClosureReturnByReference */ +$closure = function &($param) use ($value) {}; diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/File/IsReferenceTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/File/IsReferenceTest.php new file mode 100644 index 00000000..ea2dddba --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/File/IsReferenceTest.php @@ -0,0 +1,248 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\File; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class IsReferenceTest extends AbstractMethodUnitTest +{ + + + /** + * Test correctly identifying whether a "bitwise and" token is a reference or not. + * + * @param string $identifier Comment which precedes the test case. + * @param bool $expected Expected function output. + * + * @dataProvider dataIsReference + * + * @return void + */ + public function testIsReference($identifier, $expected) + { + $bitwiseAnd = $this->getTargetToken($identifier, T_BITWISE_AND); + $result = self::$phpcsFile->isReference($bitwiseAnd); + $this->assertSame($expected, $result); + + }//end testIsReference() + + + /** + * Data provider for the IsReference test. + * + * @see testIsReference() + * + * @return array + */ + public function dataIsReference() + { + return [ + [ + '/* testBitwiseAndA */', + false, + ], + [ + '/* testBitwiseAndB */', + false, + ], + [ + '/* testBitwiseAndC */', + false, + ], + [ + '/* testBitwiseAndD */', + false, + ], + [ + '/* testBitwiseAndE */', + false, + ], + [ + '/* testBitwiseAndF */', + false, + ], + [ + '/* testBitwiseAndG */', + false, + ], + [ + '/* testBitwiseAndH */', + false, + ], + [ + '/* testBitwiseAndI */', + false, + ], + [ + '/* testFunctionReturnByReference */', + true, + ], + [ + '/* testFunctionPassByReferenceA */', + true, + ], + [ + '/* testFunctionPassByReferenceB */', + true, + ], + [ + '/* testFunctionPassByReferenceC */', + true, + ], + [ + '/* testFunctionPassByReferenceD */', + true, + ], + [ + '/* testFunctionPassByReferenceE */', + true, + ], + [ + '/* testFunctionPassByReferenceF */', + true, + ], + [ + '/* testFunctionPassByReferenceG */', + true, + ], + [ + '/* testForeachValueByReference */', + true, + ], + [ + '/* testForeachKeyByReference */', + true, + ], + [ + '/* testArrayValueByReferenceA */', + true, + ], + [ + '/* testArrayValueByReferenceB */', + true, + ], + [ + '/* testArrayValueByReferenceC */', + true, + ], + [ + '/* testArrayValueByReferenceD */', + true, + ], + [ + '/* testArrayValueByReferenceE */', + true, + ], + [ + '/* testArrayValueByReferenceF */', + true, + ], + [ + '/* testArrayValueByReferenceG */', + true, + ], + [ + '/* testArrayValueByReferenceH */', + true, + ], + [ + '/* testAssignByReferenceA */', + true, + ], + [ + '/* testAssignByReferenceB */', + true, + ], + [ + '/* testAssignByReferenceC */', + true, + ], + [ + '/* testAssignByReferenceD */', + true, + ], + [ + '/* testAssignByReferenceE */', + true, + ], + [ + '/* testPassByReferenceA */', + true, + ], + [ + '/* testPassByReferenceB */', + true, + ], + [ + '/* testPassByReferenceC */', + true, + ], + [ + '/* testPassByReferenceD */', + true, + ], + [ + '/* testPassByReferenceE */', + true, + ], + [ + '/* testPassByReferenceF */', + true, + ], + [ + '/* testPassByReferenceG */', + true, + ], + [ + '/* testPassByReferenceH */', + true, + ], + [ + '/* testPassByReferenceI */', + true, + ], + [ + '/* testPassByReferenceJ */', + true, + ], + [ + '/* testNewByReferenceA */', + true, + ], + [ + '/* testNewByReferenceB */', + true, + ], + [ + '/* testUseByReference */', + true, + ], + [ + '/* testArrowFunctionReturnByReference */', + true, + ], + [ + '/* testArrowFunctionPassByReferenceA */', + true, + ], + [ + '/* testArrowFunctionPassByReferenceB */', + true, + ], + [ + '/* testClosureReturnByReference */', + true, + ], + ]; + + }//end dataIsReference() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Filters/Filter/AcceptTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Filters/Filter/AcceptTest.php new file mode 100644 index 00000000..2c9f57cd --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Filters/Filter/AcceptTest.php @@ -0,0 +1,154 @@ + + * @author Juliette Reinders Folmer + * @copyright 2019 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Filters\Filter; + +use PHP_CodeSniffer\Config; +use PHP_CodeSniffer\Filters\Filter; +use PHP_CodeSniffer\Ruleset; +use PHPUnit\Framework\TestCase; + +class AcceptTest extends TestCase +{ + + /** + * The Config object. + * + * @var \PHP_CodeSniffer\Config + */ + protected static $config; + + /** + * The Ruleset object. + * + * @var \PHP_CodeSniffer\Ruleset + */ + protected static $ruleset; + + + /** + * Initialize the test. + * + * @return void + */ + public function setUp() + { + if ($GLOBALS['PHP_CODESNIFFER_PEAR'] === true) { + // PEAR installs test and sniff files into different locations + // so these tests will not pass as they directly reference files + // by relative location. + $this->markTestSkipped('Test cannot run from a PEAR install'); + } + + }//end setUp() + + + /** + * Initialize the config and ruleset objects based on the `AcceptTest.xml` ruleset file. + * + * @return void + */ + public static function setUpBeforeClass() + { + if ($GLOBALS['PHP_CODESNIFFER_PEAR'] === true) { + // This test will be skipped. + return; + } + + $standard = __DIR__.'/'.basename(__FILE__, '.php').'.xml'; + self::$config = new Config(["--standard=$standard", "--ignore=*/somethingelse/*"]); + self::$ruleset = new Ruleset(self::$config); + + }//end setUpBeforeClass() + + + /** + * Test filtering a file list for excluded paths. + * + * @param array $inputPaths List of file paths to be filtered. + * @param array $expectedOutput Expected filtering result. + * + * @dataProvider dataExcludePatterns + * + * @return void + */ + public function testExcludePatterns($inputPaths, $expectedOutput) + { + $fakeDI = new \RecursiveArrayIterator($inputPaths); + $filter = new Filter($fakeDI, '/', self::$config, self::$ruleset); + $iterator = new \RecursiveIteratorIterator($filter); + $files = []; + + foreach ($iterator as $file) { + $files[] = $file; + } + + $this->assertEquals($expectedOutput, $files); + + }//end testExcludePatterns() + + + /** + * Data provider. + * + * @see testExcludePatterns + * + * @return array + */ + public function dataExcludePatterns() + { + $testCases = [ + // Test top-level exclude patterns. + [ + [ + '/path/to/src/Main.php', + '/path/to/src/Something/Main.php', + '/path/to/src/Somethingelse/Main.php', + '/path/to/src/SomethingelseEvenLonger/Main.php', + '/path/to/src/Other/Main.php', + ], + [ + '/path/to/src/Main.php', + '/path/to/src/SomethingelseEvenLonger/Main.php', + ], + ], + + // Test ignoring standard/sniff specific exclude patterns. + [ + [ + '/path/to/src/generic-project/Main.php', + '/path/to/src/generic/Main.php', + '/path/to/src/anything-generic/Main.php', + ], + [ + '/path/to/src/generic-project/Main.php', + '/path/to/src/generic/Main.php', + '/path/to/src/anything-generic/Main.php', + ], + ], + ]; + + // Allow these tests to work on Windows as well. + if (DIRECTORY_SEPARATOR === '\\') { + foreach ($testCases as $key => $case) { + foreach ($case as $nr => $param) { + foreach ($param as $file => $value) { + $testCases[$key][$nr][$file] = strtr($value, '/', '\\'); + } + } + } + } + + return $testCases; + + }//end dataExcludePatterns() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Filters/Filter/AcceptTest.xml b/vendor/squizlabs/php_codesniffer/tests/Core/Filters/Filter/AcceptTest.xml new file mode 100644 index 00000000..3d3e1de0 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Filters/Filter/AcceptTest.xml @@ -0,0 +1,16 @@ + + + Ruleset to test the filtering based on exclude patterns. + + + */something/* + + */Other/Main\.php$ + + + + /anything/* + + /YetAnother/Main\.php + + diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/IsCamelCapsTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/IsCamelCapsTest.php new file mode 100644 index 00000000..b60d524b --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/IsCamelCapsTest.php @@ -0,0 +1,135 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core; + +use PHP_CodeSniffer\Util\Common; +use PHPUnit\Framework\TestCase; + +class IsCamelCapsTest extends TestCase +{ + + + /** + * Test valid public function/method names. + * + * @return void + */ + public function testValidNotClassFormatPublic() + { + $this->assertTrue(Common::isCamelCaps('thisIsCamelCaps', false, true, true)); + $this->assertTrue(Common::isCamelCaps('thisISCamelCaps', false, true, false)); + + }//end testValidNotClassFormatPublic() + + + /** + * Test invalid public function/method names. + * + * @return void + */ + public function testInvalidNotClassFormatPublic() + { + $this->assertFalse(Common::isCamelCaps('_thisIsCamelCaps', false, true, true)); + $this->assertFalse(Common::isCamelCaps('thisISCamelCaps', false, true, true)); + $this->assertFalse(Common::isCamelCaps('ThisIsCamelCaps', false, true, true)); + + $this->assertFalse(Common::isCamelCaps('3thisIsCamelCaps', false, true, true)); + $this->assertFalse(Common::isCamelCaps('*thisIsCamelCaps', false, true, true)); + $this->assertFalse(Common::isCamelCaps('-thisIsCamelCaps', false, true, true)); + + $this->assertFalse(Common::isCamelCaps('this*IsCamelCaps', false, true, true)); + $this->assertFalse(Common::isCamelCaps('this-IsCamelCaps', false, true, true)); + $this->assertFalse(Common::isCamelCaps('this_IsCamelCaps', false, true, true)); + $this->assertFalse(Common::isCamelCaps('this_is_camel_caps', false, true, true)); + + }//end testInvalidNotClassFormatPublic() + + + /** + * Test valid private method names. + * + * @return void + */ + public function testValidNotClassFormatPrivate() + { + $this->assertTrue(Common::isCamelCaps('_thisIsCamelCaps', false, false, true)); + $this->assertTrue(Common::isCamelCaps('_thisISCamelCaps', false, false, false)); + $this->assertTrue(Common::isCamelCaps('_i18N', false, false, true)); + $this->assertTrue(Common::isCamelCaps('_i18n', false, false, true)); + + }//end testValidNotClassFormatPrivate() + + + /** + * Test invalid private method names. + * + * @return void + */ + public function testInvalidNotClassFormatPrivate() + { + $this->assertFalse(Common::isCamelCaps('thisIsCamelCaps', false, false, true)); + $this->assertFalse(Common::isCamelCaps('_thisISCamelCaps', false, false, true)); + $this->assertFalse(Common::isCamelCaps('_ThisIsCamelCaps', false, false, true)); + $this->assertFalse(Common::isCamelCaps('__thisIsCamelCaps', false, false, true)); + $this->assertFalse(Common::isCamelCaps('__thisISCamelCaps', false, false, false)); + + $this->assertFalse(Common::isCamelCaps('3thisIsCamelCaps', false, false, true)); + $this->assertFalse(Common::isCamelCaps('*thisIsCamelCaps', false, false, true)); + $this->assertFalse(Common::isCamelCaps('-thisIsCamelCaps', false, false, true)); + $this->assertFalse(Common::isCamelCaps('_this_is_camel_caps', false, false, true)); + + }//end testInvalidNotClassFormatPrivate() + + + /** + * Test valid class names. + * + * @return void + */ + public function testValidClassFormatPublic() + { + $this->assertTrue(Common::isCamelCaps('ThisIsCamelCaps', true, true, true)); + $this->assertTrue(Common::isCamelCaps('ThisISCamelCaps', true, true, false)); + $this->assertTrue(Common::isCamelCaps('This3IsCamelCaps', true, true, false)); + + }//end testValidClassFormatPublic() + + + /** + * Test invalid class names. + * + * @return void + */ + public function testInvalidClassFormat() + { + $this->assertFalse(Common::isCamelCaps('thisIsCamelCaps', true)); + $this->assertFalse(Common::isCamelCaps('This-IsCamelCaps', true)); + $this->assertFalse(Common::isCamelCaps('This_Is_Camel_Caps', true)); + + }//end testInvalidClassFormat() + + + /** + * Test invalid class names with the private flag set. + * + * Note that the private flag is ignored if the class format + * flag is set, so these names are all invalid. + * + * @return void + */ + public function testInvalidClassFormatPrivate() + { + $this->assertFalse(Common::isCamelCaps('_ThisIsCamelCaps', true, true)); + $this->assertFalse(Common::isCamelCaps('_ThisIsCamelCaps', true, false)); + + }//end testInvalidClassFormatPrivate() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteLinuxTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteLinuxTest.php new file mode 100644 index 00000000..8b138e0b --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteLinuxTest.php @@ -0,0 +1,118 @@ + + * @copyright 2019 Juliette Reinders Folmer. All rights reserved. + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Ruleset; + +use PHP_CodeSniffer\Config; +use PHP_CodeSniffer\Ruleset; +use PHPUnit\Framework\TestCase; + +class RuleInclusionAbsoluteLinuxTest extends TestCase +{ + + /** + * The Ruleset object. + * + * @var \PHP_CodeSniffer\Ruleset + */ + protected $ruleset; + + /** + * Path to the ruleset file. + * + * @var string + */ + private $standard = ''; + + /** + * The original content of the ruleset. + * + * @var string + */ + private $contents = ''; + + + /** + * Initialize the config and ruleset objects. + * + * @return void + */ + public function setUp() + { + if ($GLOBALS['PHP_CODESNIFFER_PEAR'] === true) { + // PEAR installs test and sniff files into different locations + // so these tests will not pass as they directly reference files + // by relative location. + $this->markTestSkipped('Test cannot run from a PEAR install'); + } + + $this->standard = __DIR__.'/'.basename(__FILE__, '.php').'.xml'; + $repoRootDir = dirname(dirname(dirname(__DIR__))); + + // On-the-fly adjust the ruleset test file to be able to test sniffs included with absolute paths. + $contents = file_get_contents($this->standard); + $this->contents = $contents; + + $newPath = $repoRootDir; + if (DIRECTORY_SEPARATOR === '\\') { + $newPath = str_replace('\\', '/', $repoRootDir); + } + + $adjusted = str_replace('%path_slash_forward%', $newPath, $contents); + + if (file_put_contents($this->standard, $adjusted) === false) { + $this->markTestSkipped('On the fly ruleset adjustment failed'); + } + + // Initialize the config and ruleset objects for the test. + $config = new Config(["--standard={$this->standard}"]); + $this->ruleset = new Ruleset($config); + + }//end setUp() + + + /** + * Reset ruleset file. + * + * @return void + */ + public function tearDown() + { + file_put_contents($this->standard, $this->contents); + + }//end tearDown() + + + /** + * Test that sniffs registed with a Linux absolute path are correctly recognized and that + * properties are correctly set for them. + * + * @return void + */ + public function testLinuxStylePathRuleInclusion() + { + // Test that the sniff is correctly registered. + $this->assertObjectHasAttribute('sniffCodes', $this->ruleset); + $this->assertCount(1, $this->ruleset->sniffCodes); + $this->assertArrayHasKey('Generic.Formatting.SpaceAfterNot', $this->ruleset->sniffCodes); + $this->assertSame( + 'PHP_CodeSniffer\Standards\Generic\Sniffs\Formatting\SpaceAfterNotSniff', + $this->ruleset->sniffCodes['Generic.Formatting.SpaceAfterNot'] + ); + + // Test that the sniff properties are correctly set. + $this->assertSame( + '10', + $this->ruleset->sniffs['PHP_CodeSniffer\Standards\Generic\Sniffs\Formatting\SpaceAfterNotSniff']->spacing + ); + + }//end testLinuxStylePathRuleInclusion() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteLinuxTest.xml b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteLinuxTest.xml new file mode 100644 index 00000000..64d1aae6 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteLinuxTest.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.php new file mode 100644 index 00000000..f8e3255b --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.php @@ -0,0 +1,119 @@ + + * @copyright 2019 Juliette Reinders Folmer. All rights reserved. + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Ruleset; + +use PHP_CodeSniffer\Config; +use PHP_CodeSniffer\Ruleset; +use PHPUnit\Framework\TestCase; + +class RuleInclusionAbsoluteWindowsTest extends TestCase +{ + + /** + * The Ruleset object. + * + * @var \PHP_CodeSniffer\Ruleset + */ + protected $ruleset; + + /** + * Path to the ruleset file. + * + * @var string + */ + private $standard = ''; + + /** + * The original content of the ruleset. + * + * @var string + */ + private $contents = ''; + + + /** + * Initialize the config and ruleset objects. + * + * @return void + */ + public function setUp() + { + if (DIRECTORY_SEPARATOR === '/') { + $this->markTestSkipped('Windows specific test'); + } + + if ($GLOBALS['PHP_CODESNIFFER_PEAR'] === true) { + // PEAR installs test and sniff files into different locations + // so these tests will not pass as they directly reference files + // by relative location. + $this->markTestSkipped('Test cannot run from a PEAR install'); + } + + $this->standard = __DIR__.'/'.basename(__FILE__, '.php').'.xml'; + $repoRootDir = dirname(dirname(dirname(__DIR__))); + + // On-the-fly adjust the ruleset test file to be able to test sniffs included with absolute paths. + $contents = file_get_contents($this->standard); + $this->contents = $contents; + + $adjusted = str_replace('%path_slash_back%', $repoRootDir, $contents); + + if (file_put_contents($this->standard, $adjusted) === false) { + $this->markTestSkipped('On the fly ruleset adjustment failed'); + } + + // Initialize the config and ruleset objects for the test. + $config = new Config(["--standard={$this->standard}"]); + $this->ruleset = new Ruleset($config); + + }//end setUp() + + + /** + * Reset ruleset file. + * + * @return void + */ + public function tearDown() + { + if (DIRECTORY_SEPARATOR !== '/') { + file_put_contents($this->standard, $this->contents); + } + + }//end tearDown() + + + /** + * Test that sniffs registed with a Windows absolute path are correctly recognized and that + * properties are correctly set for them. + * + * @return void + */ + public function testWindowsStylePathRuleInclusion() + { + // Test that the sniff is correctly registered. + $this->assertObjectHasAttribute('sniffCodes', $this->ruleset); + $this->assertCount(1, $this->ruleset->sniffCodes); + $this->assertArrayHasKey('Generic.Formatting.SpaceAfterCast', $this->ruleset->sniffCodes); + $this->assertSame( + 'PHP_CodeSniffer\Standards\Generic\Sniffs\Formatting\SpaceAfterCastSniff', + $this->ruleset->sniffCodes['Generic.Formatting.SpaceAfterCast'] + ); + + // Test that the sniff property is correctly set. + $this->assertSame( + '10', + $this->ruleset->sniffs['PHP_CodeSniffer\Standards\Generic\Sniffs\Formatting\SpaceAfterCastSniff']->spacing + ); + + }//end testWindowsStylePathRuleInclusion() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.xml b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.xml new file mode 100644 index 00000000..15710d20 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionTest-include.xml b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionTest-include.xml new file mode 100644 index 00000000..ca116d45 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionTest-include.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionTest.php new file mode 100644 index 00000000..24abe8da --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionTest.php @@ -0,0 +1,297 @@ + + * @copyright 2019 Juliette Reinders Folmer. All rights reserved. + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Ruleset; + +use PHP_CodeSniffer\Config; +use PHP_CodeSniffer\Ruleset; +use PHPUnit\Framework\TestCase; + +class RuleInclusionTest extends TestCase +{ + + /** + * The Ruleset object. + * + * @var \PHP_CodeSniffer\Ruleset + */ + protected static $ruleset; + + /** + * Path to the ruleset file. + * + * @var string + */ + private static $standard = ''; + + /** + * The original content of the ruleset. + * + * @var string + */ + private static $contents = ''; + + + /** + * Initialize the test. + * + * @return void + */ + public function setUp() + { + if ($GLOBALS['PHP_CODESNIFFER_PEAR'] === true) { + // PEAR installs test and sniff files into different locations + // so these tests will not pass as they directly reference files + // by relative location. + $this->markTestSkipped('Test cannot run from a PEAR install'); + } + + }//end setUp() + + + /** + * Initialize the config and ruleset objects based on the `RuleInclusionTest.xml` ruleset file. + * + * @return void + */ + public static function setUpBeforeClass() + { + if ($GLOBALS['PHP_CODESNIFFER_PEAR'] === true) { + // This test will be skipped. + return; + } + + $standard = __DIR__.'/'.basename(__FILE__, '.php').'.xml'; + self::$standard = $standard; + + // On-the-fly adjust the ruleset test file to be able to test + // sniffs included with relative paths. + $contents = file_get_contents($standard); + self::$contents = $contents; + + $repoRootDir = basename(dirname(dirname(dirname(__DIR__)))); + + $newPath = $repoRootDir; + if (DIRECTORY_SEPARATOR === '\\') { + $newPath = str_replace('\\', '/', $repoRootDir); + } + + $adjusted = str_replace('%path_root_dir%', $newPath, $contents); + + if (file_put_contents($standard, $adjusted) === false) { + self::markTestSkipped('On the fly ruleset adjustment failed'); + } + + $config = new Config(["--standard=$standard"]); + self::$ruleset = new Ruleset($config); + + }//end setUpBeforeClass() + + + /** + * Reset ruleset file. + * + * @return void + */ + public function tearDown() + { + file_put_contents(self::$standard, self::$contents); + + }//end tearDown() + + + /** + * Test that sniffs are registered. + * + * @return void + */ + public function testHasSniffCodes() + { + $this->assertObjectHasAttribute('sniffCodes', self::$ruleset); + $this->assertCount(14, self::$ruleset->sniffCodes); + + }//end testHasSniffCodes() + + + /** + * Test that sniffs are correctly registered, independently on the syntax used to include the sniff. + * + * @param string $key Expected array key. + * @param string $value Expected array value. + * + * @dataProvider dataRegisteredSniffCodes + * + * @return void + */ + public function testRegisteredSniffCodes($key, $value) + { + $this->assertArrayHasKey($key, self::$ruleset->sniffCodes); + $this->assertSame($value, self::$ruleset->sniffCodes[$key]); + + }//end testRegisteredSniffCodes() + + + /** + * Data provider. + * + * @see self::testRegisteredSniffCodes() + * + * @return array + */ + public function dataRegisteredSniffCodes() + { + return [ + [ + 'PSR1.Classes.ClassDeclaration', + 'PHP_CodeSniffer\Standards\PSR1\Sniffs\Classes\ClassDeclarationSniff', + ], + [ + 'PSR1.Files.SideEffects', + 'PHP_CodeSniffer\Standards\PSR1\Sniffs\Files\SideEffectsSniff', + ], + [ + 'PSR1.Methods.CamelCapsMethodName', + 'PHP_CodeSniffer\Standards\PSR1\Sniffs\Methods\CamelCapsMethodNameSniff', + ], + [ + 'Generic.PHP.DisallowAlternativePHPTags', + 'PHP_CodeSniffer\Standards\Generic\Sniffs\PHP\DisallowAlternativePHPTagsSniff', + ], + [ + 'Generic.PHP.DisallowShortOpenTag', + 'PHP_CodeSniffer\Standards\Generic\Sniffs\PHP\DisallowShortOpenTagSniff', + ], + [ + 'Generic.Files.ByteOrderMark', + 'PHP_CodeSniffer\Standards\Generic\Sniffs\Files\ByteOrderMarkSniff', + ], + [ + 'Squiz.Classes.ValidClassName', + 'PHP_CodeSniffer\Standards\Squiz\Sniffs\Classes\ValidClassNameSniff', + ], + [ + 'Generic.NamingConventions.UpperCaseConstantName', + 'PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions\UpperCaseConstantNameSniff', + ], + [ + 'Zend.NamingConventions.ValidVariableName', + 'PHP_CodeSniffer\Standards\Zend\Sniffs\NamingConventions\ValidVariableNameSniff', + ], + [ + 'Generic.Arrays.ArrayIndent', + 'PHP_CodeSniffer\Standards\Generic\Sniffs\Arrays\ArrayIndentSniff', + ], + [ + 'Generic.Metrics.CyclomaticComplexity', + 'PHP_CodeSniffer\Standards\Generic\Sniffs\Metrics\CyclomaticComplexitySniff', + ], + [ + 'Generic.Files.LineLength', + 'PHP_CodeSniffer\Standards\Generic\Sniffs\Files\LineLengthSniff', + ], + [ + 'Generic.NamingConventions.CamelCapsFunctionName', + 'PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions\CamelCapsFunctionNameSniff', + ], + [ + 'Generic.Metrics.NestingLevel', + 'PHP_CodeSniffer\Standards\Generic\Sniffs\Metrics\NestingLevelSniff', + ], + ]; + + }//end dataRegisteredSniffCodes() + + + /** + * Test that setting properties for standards, categories, sniffs works for all supported rule + * inclusion methods. + * + * @param string $sniffClass The name of the sniff class. + * @param string $propertyName The name of the changed property. + * @param mixed $expectedValue The value expected for the property. + * + * @dataProvider dataSettingProperties + * + * @return void + */ + public function testSettingProperties($sniffClass, $propertyName, $expectedValue) + { + $this->assertObjectHasAttribute('sniffs', self::$ruleset); + $this->assertArrayHasKey($sniffClass, self::$ruleset->sniffs); + $this->assertObjectHasAttribute($propertyName, self::$ruleset->sniffs[$sniffClass]); + + $actualValue = self::$ruleset->sniffs[$sniffClass]->$propertyName; + $this->assertSame($expectedValue, $actualValue); + + }//end testSettingProperties() + + + /** + * Data provider. + * + * @see self::testSettingProperties() + * + * @return array + */ + public function dataSettingProperties() + { + return [ + 'ClassDeclarationSniff' => [ + 'PHP_CodeSniffer\Standards\PSR1\Sniffs\Classes\ClassDeclarationSniff', + 'setforallsniffs', + true, + ], + 'SideEffectsSniff' => [ + 'PHP_CodeSniffer\Standards\PSR1\Sniffs\Files\SideEffectsSniff', + 'setforallsniffs', + true, + ], + 'ValidVariableNameSniff' => [ + 'PHP_CodeSniffer\Standards\Zend\Sniffs\NamingConventions\ValidVariableNameSniff', + 'setforallincategory', + true, + ], + 'ArrayIndentSniff' => [ + 'PHP_CodeSniffer\Standards\Generic\Sniffs\Arrays\ArrayIndentSniff', + 'indent', + '2', + ], + 'LineLengthSniff' => [ + 'PHP_CodeSniffer\Standards\Generic\Sniffs\Files\LineLengthSniff', + 'lineLimit', + '10', + ], + 'CamelCapsFunctionNameSniff' => [ + 'PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions\CamelCapsFunctionNameSniff', + 'strict', + false, + ], + 'NestingLevelSniff-nestingLevel' => [ + 'PHP_CodeSniffer\Standards\Generic\Sniffs\Metrics\NestingLevelSniff', + 'nestingLevel', + '2', + ], + 'NestingLevelSniff-setforsniffsinincludedruleset' => [ + 'PHP_CodeSniffer\Standards\Generic\Sniffs\Metrics\NestingLevelSniff', + 'setforsniffsinincludedruleset', + true, + ], + + // Testing that setting a property at error code level does *not* work. + 'CyclomaticComplexitySniff' => [ + 'PHP_CodeSniffer\Standards\Generic\Sniffs\Metrics\CyclomaticComplexitySniff', + 'complexity', + 10, + ], + ]; + + }//end dataSettingProperties() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionTest.xml b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionTest.xml new file mode 100644 index 00000000..06ce040e --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Ruleset/RuleInclusionTest.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Sniffs/AbstractArraySniffTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Sniffs/AbstractArraySniffTest.inc new file mode 100644 index 00000000..48822958 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Sniffs/AbstractArraySniffTest.inc @@ -0,0 +1,51 @@ +1,'2'=>2,'3'=>3]; + +/* testMissingKeys */ +$foo = ['1'=>1,2,'3'=>3]; + +/* testMultiTokenKeys */ +$paths = array( + Init::ROOT_DIR.'/a' => 'a', + Init::ROOT_DIR.'/b' => 'b', +); + +/* testMissingKeysCoalesceTernary */ +return [ + $a => static function () { return [1,2,3]; }, + $b ?? $c, + $d ? [$e] : [$f], +]; + +/* testTernaryValues */ +$foo = [ + '1' => $row['status'] === 'rejected' + ? self::REJECTED_CODE + : self::VERIFIED_CODE, + '2' => in_array($row['status'], array('notverified', 'unverified'), true) + ? self::STATUS_PENDING + : self::STATUS_VERIFIED, + '3' => strtotime($row['date']), +]; + +/* testHeredocValues */ +$foo = array( + << '1', + 2 => fn ($x) => yield 'a' => $x, + 3 => '3', +); diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Sniffs/AbstractArraySniffTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Sniffs/AbstractArraySniffTest.php new file mode 100644 index 00000000..20c28d60 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Sniffs/AbstractArraySniffTest.php @@ -0,0 +1,290 @@ + + * @copyright 2006-2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Sniffs; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class AbstractArraySniffTest extends AbstractMethodUnitTest +{ + + /** + * The sniff objects we are testing. + * + * This extends the \PHP_CodeSniffer\Sniffs\AbstractArraySniff class to make the + * internal workings of the sniff observable. + * + * @var \PHP_CodeSniffer\Sniffs\AbstractArraySniffTestable + */ + protected static $sniff; + + + /** + * Initialize & tokenize \PHP_CodeSniffer\Files\File with code from 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. + * + * @return void + */ + public static function setUpBeforeClass() + { + self::$sniff = new AbstractArraySniffTestable(); + parent::setUpBeforeClass(); + + }//end setUpBeforeClass() + + + /** + * Test an array of simple values only. + * + * @return void + */ + public function testSimpleValues() + { + $token = $this->getTargetToken('/* testSimpleValues */', T_OPEN_SHORT_ARRAY); + self::$sniff->process(self::$phpcsFile, $token); + + $expected = [ + 0 => ['value_start' => ($token + 1)], + 1 => ['value_start' => ($token + 3)], + 2 => ['value_start' => ($token + 5)], + ]; + + $this->assertSame($expected, self::$sniff->indicies); + + }//end testSimpleValues() + + + /** + * Test an array of simple keys and values. + * + * @return void + */ + public function testSimpleKeyValues() + { + $token = $this->getTargetToken('/* testSimpleKeyValues */', T_OPEN_SHORT_ARRAY); + self::$sniff->process(self::$phpcsFile, $token); + + $expected = [ + 0 => [ + 'index_start' => ($token + 1), + 'index_end' => ($token + 1), + 'arrow' => ($token + 2), + 'value_start' => ($token + 3), + ], + 1 => [ + 'index_start' => ($token + 5), + 'index_end' => ($token + 5), + 'arrow' => ($token + 6), + 'value_start' => ($token + 7), + ], + 2 => [ + 'index_start' => ($token + 9), + 'index_end' => ($token + 9), + 'arrow' => ($token + 10), + 'value_start' => ($token + 11), + ], + ]; + + $this->assertSame($expected, self::$sniff->indicies); + + }//end testSimpleKeyValues() + + + /** + * Test an array of simple keys and values. + * + * @return void + */ + public function testMissingKeys() + { + $token = $this->getTargetToken('/* testMissingKeys */', T_OPEN_SHORT_ARRAY); + self::$sniff->process(self::$phpcsFile, $token); + + $expected = [ + 0 => [ + 'index_start' => ($token + 1), + 'index_end' => ($token + 1), + 'arrow' => ($token + 2), + 'value_start' => ($token + 3), + ], + 1 => [ + 'value_start' => ($token + 5), + ], + 2 => [ + 'index_start' => ($token + 7), + 'index_end' => ($token + 7), + 'arrow' => ($token + 8), + 'value_start' => ($token + 9), + ], + ]; + + $this->assertSame($expected, self::$sniff->indicies); + + }//end testMissingKeys() + + + /** + * Test an array with keys that span multiple tokens. + * + * @return void + */ + public function testMultiTokenKeys() + { + $token = $this->getTargetToken('/* testMultiTokenKeys */', T_ARRAY); + self::$sniff->process(self::$phpcsFile, $token); + + $expected = [ + 0 => [ + 'index_start' => ($token + 4), + 'index_end' => ($token + 8), + 'arrow' => ($token + 10), + 'value_start' => ($token + 12), + ], + 1 => [ + 'index_start' => ($token + 16), + 'index_end' => ($token + 20), + 'arrow' => ($token + 22), + 'value_start' => ($token + 24), + ], + ]; + + $this->assertSame($expected, self::$sniff->indicies); + + }//end testMultiTokenKeys() + + + /** + * Test an array of simple keys and values. + * + * @return void + */ + public function testMissingKeysCoalesceTernary() + { + $token = $this->getTargetToken('/* testMissingKeysCoalesceTernary */', T_OPEN_SHORT_ARRAY); + self::$sniff->process(self::$phpcsFile, $token); + + $expected = [ + 0 => [ + 'index_start' => ($token + 3), + 'index_end' => ($token + 3), + 'arrow' => ($token + 5), + 'value_start' => ($token + 7), + ], + 1 => [ + 'value_start' => ($token + 31), + ], + 2 => [ + 'value_start' => ($token + 39), + ], + ]; + + $this->assertSame($expected, self::$sniff->indicies); + + }//end testMissingKeysCoalesceTernary() + + + /** + * Test an array of ternary values. + * + * @return void + */ + public function testTernaryValues() + { + $token = $this->getTargetToken('/* testTernaryValues */', T_OPEN_SHORT_ARRAY); + self::$sniff->process(self::$phpcsFile, $token); + + $expected = [ + 0 => [ + 'index_start' => ($token + 3), + 'index_end' => ($token + 3), + 'arrow' => ($token + 5), + 'value_start' => ($token + 7), + ], + 1 => [ + 'index_start' => ($token + 32), + 'index_end' => ($token + 32), + 'arrow' => ($token + 34), + 'value_start' => ($token + 36), + ], + 2 => [ + 'index_start' => ($token + 72), + 'index_end' => ($token + 72), + 'arrow' => ($token + 74), + 'value_start' => ($token + 76), + ], + ]; + + $this->assertSame($expected, self::$sniff->indicies); + + }//end testTernaryValues() + + + /** + * Test an array of heredocs. + * + * @return void + */ + public function testHeredocValues() + { + $token = $this->getTargetToken('/* testHeredocValues */', T_ARRAY); + self::$sniff->process(self::$phpcsFile, $token); + + $expected = [ + 0 => [ + 'value_start' => ($token + 4), + ], + 1 => [ + 'value_start' => ($token + 10), + ], + ]; + + $this->assertSame($expected, self::$sniff->indicies); + + }//end testHeredocValues() + + + /** + * Test an array of with an arrow function as a value. + * + * @return void + */ + public function testArrowFunctionValue() + { + $token = $this->getTargetToken('/* testArrowFunctionValue */', T_ARRAY); + self::$sniff->process(self::$phpcsFile, $token); + + $expected = [ + 0 => [ + 'index_start' => ($token + 4), + 'index_end' => ($token + 4), + 'arrow' => ($token + 6), + 'value_start' => ($token + 8), + ], + 1 => [ + 'index_start' => ($token + 12), + 'index_end' => ($token + 12), + 'arrow' => ($token + 14), + 'value_start' => ($token + 16), + ], + 2 => [ + 'index_start' => ($token + 34), + 'index_end' => ($token + 34), + 'arrow' => ($token + 36), + 'value_start' => ($token + 38), + ], + ]; + + $this->assertSame($expected, self::$sniff->indicies); + + }//end testArrowFunctionValue() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Sniffs/AbstractArraySniffTestable.php b/vendor/squizlabs/php_codesniffer/tests/Core/Sniffs/AbstractArraySniffTestable.php new file mode 100644 index 00000000..a224012f --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Sniffs/AbstractArraySniffTestable.php @@ -0,0 +1,65 @@ + + * @copyright 2006-2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Sniffs; + +use PHP_CodeSniffer\Sniffs\AbstractArraySniff; + +class AbstractArraySniffTestable extends AbstractArraySniff +{ + + /** + * The array indicies that were found during processing. + * + * @var array + */ + public $indicies = []; + + + /** + * Processes a single-line array definition. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $arrayStart The token that starts the array definition. + * @param int $arrayEnd The token that ends the array definition. + * @param array $indices An array of token positions for the array keys, + * double arrows, and values. + * + * @return void + */ + public function processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices) + { + $this->indicies = $indices; + + }//end processSingleLineArray() + + + /** + * Processes a multi-line array definition. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $arrayStart The token that starts the array definition. + * @param int $arrayEnd The token that ends the array definition. + * @param array $indices An array of token positions for the array keys, + * double arrows, and values. + * + * @return void + */ + public function processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices) + { + $this->indicies = $indices; + + }//end processMultiLineArray() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/AnonClassParenthesisOwnerTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/AnonClassParenthesisOwnerTest.inc new file mode 100644 index 00000000..5867691c --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/AnonClassParenthesisOwnerTest.inc @@ -0,0 +1,19 @@ + + * @copyright 2019 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class AnonClassParenthesisOwnerTest extends AbstractMethodUnitTest +{ + + + /** + * Test that anonymous class tokens without parenthesis do not get assigned a parenthesis owner. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataAnonClassNoParentheses + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testAnonClassNoParentheses($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $anonClass = $this->getTargetToken($testMarker, T_ANON_CLASS); + $this->assertFalse(array_key_exists('parenthesis_owner', $tokens[$anonClass])); + $this->assertFalse(array_key_exists('parenthesis_opener', $tokens[$anonClass])); + $this->assertFalse(array_key_exists('parenthesis_closer', $tokens[$anonClass])); + + }//end testAnonClassNoParentheses() + + + /** + * Test that the next open/close parenthesis after an anonymous class without parenthesis + * do not get assigned the anonymous class as a parenthesis owner. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataAnonClassNoParentheses + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testAnonClassNoParenthesesNextOpenClose($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + $function = $this->getTargetToken($testMarker, T_FUNCTION); + + $opener = $this->getTargetToken($testMarker, T_OPEN_PARENTHESIS); + $this->assertTrue(array_key_exists('parenthesis_owner', $tokens[$opener])); + $this->assertSame($function, $tokens[$opener]['parenthesis_owner']); + + $closer = $this->getTargetToken($testMarker, T_CLOSE_PARENTHESIS); + $this->assertTrue(array_key_exists('parenthesis_owner', $tokens[$closer])); + $this->assertSame($function, $tokens[$closer]['parenthesis_owner']); + + }//end testAnonClassNoParenthesesNextOpenClose() + + + /** + * Data provider. + * + * @see testAnonClassNoParentheses() + * @see testAnonClassNoParenthesesNextOpenClose() + * + * @return array + */ + public function dataAnonClassNoParentheses() + { + return [ + ['/* testNoParentheses */'], + ['/* testNoParenthesesAndEmptyTokens */'], + ]; + + }//end dataAnonClassNoParentheses() + + + /** + * Test that anonymous class tokens with parenthesis get assigned a parenthesis owner, + * opener and closer; and that the opener/closer get the anonymous class assigned as owner. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataAnonClassWithParentheses + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testAnonClassWithParentheses($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + $anonClass = $this->getTargetToken($testMarker, T_ANON_CLASS); + $opener = $this->getTargetToken($testMarker, T_OPEN_PARENTHESIS); + $closer = $this->getTargetToken($testMarker, T_CLOSE_PARENTHESIS); + + $this->assertTrue(array_key_exists('parenthesis_owner', $tokens[$anonClass])); + $this->assertTrue(array_key_exists('parenthesis_opener', $tokens[$anonClass])); + $this->assertTrue(array_key_exists('parenthesis_closer', $tokens[$anonClass])); + $this->assertSame($anonClass, $tokens[$anonClass]['parenthesis_owner']); + $this->assertSame($opener, $tokens[$anonClass]['parenthesis_opener']); + $this->assertSame($closer, $tokens[$anonClass]['parenthesis_closer']); + + $this->assertTrue(array_key_exists('parenthesis_owner', $tokens[$opener])); + $this->assertTrue(array_key_exists('parenthesis_opener', $tokens[$opener])); + $this->assertTrue(array_key_exists('parenthesis_closer', $tokens[$opener])); + $this->assertSame($anonClass, $tokens[$opener]['parenthesis_owner']); + $this->assertSame($opener, $tokens[$opener]['parenthesis_opener']); + $this->assertSame($closer, $tokens[$opener]['parenthesis_closer']); + + $this->assertTrue(array_key_exists('parenthesis_owner', $tokens[$closer])); + $this->assertTrue(array_key_exists('parenthesis_opener', $tokens[$closer])); + $this->assertTrue(array_key_exists('parenthesis_closer', $tokens[$closer])); + $this->assertSame($anonClass, $tokens[$closer]['parenthesis_owner']); + $this->assertSame($opener, $tokens[$closer]['parenthesis_opener']); + $this->assertSame($closer, $tokens[$closer]['parenthesis_closer']); + + }//end testAnonClassWithParentheses() + + + /** + * Data provider. + * + * @see testAnonClassWithParentheses() + * + * @return array + */ + public function dataAnonClassWithParentheses() + { + return [ + ['/* testWithParentheses */'], + ['/* testWithParenthesesAndEmptyTokens */'], + ]; + + }//end dataAnonClassWithParentheses() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ArrayKeywordTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ArrayKeywordTest.inc new file mode 100644 index 00000000..ce5c553c --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ArrayKeywordTest.inc @@ -0,0 +1,35 @@ + 10); + +/* testArrayWithComment */ +$var = Array /*comment*/ (1 => 10); + +/* testNestingArray */ +$var = array( + /* testNestedArray */ + array( + 'key' => 'value', + + /* testClosureReturnType */ + 'closure' => function($a) use($global) : Array {}, + ), +); + +/* testFunctionDeclarationParamType */ +function foo(array $a) {} + +/* testFunctionDeclarationReturnType */ +function foo($a) : int|array|null {} + +class Bar { + /* testClassConst */ + const ARRAY = []; + + /* testClassMethod */ + public function array() {} +} diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ArrayKeywordTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ArrayKeywordTest.php new file mode 100644 index 00000000..237258a6 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ArrayKeywordTest.php @@ -0,0 +1,170 @@ + + * @copyright 2021 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class ArrayKeywordTest extends AbstractMethodUnitTest +{ + + + /** + * Test that the array keyword is correctly tokenized as `T_ARRAY`. + * + * @param string $testMarker The comment prefacing the target token. + * @param string $testContent Optional. The token content to look for. + * + * @dataProvider dataArrayKeyword + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createTokenMap + * + * @return void + */ + public function testArrayKeyword($testMarker, $testContent='array') + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, [T_ARRAY, T_STRING], $testContent); + $tokenArray = $tokens[$token]; + + $this->assertSame(T_ARRAY, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_ARRAY (code)'); + $this->assertSame('T_ARRAY', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_ARRAY (type)'); + + $this->assertArrayHasKey('parenthesis_owner', $tokenArray, 'Parenthesis owner is not set'); + $this->assertArrayHasKey('parenthesis_opener', $tokenArray, 'Parenthesis opener is not set'); + $this->assertArrayHasKey('parenthesis_closer', $tokenArray, 'Parenthesis closer is not set'); + + }//end testArrayKeyword() + + + /** + * Data provider. + * + * @see testArrayKeyword() + * + * @return array + */ + public function dataArrayKeyword() + { + return [ + 'empty array' => ['/* testEmptyArray */'], + 'array with space before parenthesis' => ['/* testArrayWithSpace */'], + 'array with comment before parenthesis' => [ + '/* testArrayWithComment */', + 'Array', + ], + 'nested: outer array' => ['/* testNestingArray */'], + 'nested: inner array' => ['/* testNestedArray */'], + ]; + + }//end dataArrayKeyword() + + + /** + * Test that the array keyword when used in a type declaration is correctly tokenized as `T_STRING`. + * + * @param string $testMarker The comment prefacing the target token. + * @param string $testContent Optional. The token content to look for. + * + * @dataProvider dataArrayType + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createTokenMap + * + * @return void + */ + public function testArrayType($testMarker, $testContent='array') + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, [T_ARRAY, T_STRING], $testContent); + $tokenArray = $tokens[$token]; + + $this->assertSame(T_STRING, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (code)'); + $this->assertSame('T_STRING', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (type)'); + + $this->assertArrayNotHasKey('parenthesis_owner', $tokenArray, 'Parenthesis owner is set'); + $this->assertArrayNotHasKey('parenthesis_opener', $tokenArray, 'Parenthesis opener is set'); + $this->assertArrayNotHasKey('parenthesis_closer', $tokenArray, 'Parenthesis closer is set'); + + }//end testArrayType() + + + /** + * Data provider. + * + * @see testArrayType() + * + * @return array + */ + public function dataArrayType() + { + return [ + 'closure return type' => [ + '/* testClosureReturnType */', + 'Array', + ], + 'function param type' => ['/* testFunctionDeclarationParamType */'], + 'function union return type' => ['/* testFunctionDeclarationReturnType */'], + ]; + + }//end dataArrayType() + + + /** + * Verify that the retokenization of `T_ARRAY` tokens to `T_STRING` is handled correctly + * for tokens with the contents 'array' which aren't in actual fact the array keyword. + * + * @param string $testMarker The comment prefacing the target token. + * @param string $testContent The token content to look for. + * + * @dataProvider dataNotArrayKeyword + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createTokenMap + * + * @return void + */ + public function testNotArrayKeyword($testMarker, $testContent='array') + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, [T_ARRAY, T_STRING], $testContent); + $tokenArray = $tokens[$token]; + + $this->assertSame(T_STRING, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (code)'); + $this->assertSame('T_STRING', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (type)'); + + $this->assertArrayNotHasKey('parenthesis_owner', $tokenArray, 'Parenthesis owner is set'); + $this->assertArrayNotHasKey('parenthesis_opener', $tokenArray, 'Parenthesis opener is set'); + $this->assertArrayNotHasKey('parenthesis_closer', $tokenArray, 'Parenthesis closer is set'); + + }//end testNotArrayKeyword() + + + /** + * Data provider. + * + * @see testNotArrayKeyword() + * + * @return array + */ + public function dataNotArrayKeyword() + { + return [ + 'class-constant-name' => [ + '/* testClassConst */', + 'ARRAY', + ], + 'class-method-name' => ['/* testClassMethod */'], + ]; + + }//end dataNotArrayKeyword() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/AttributesTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/AttributesTest.inc new file mode 100644 index 00000000..e539adf8 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/AttributesTest.inc @@ -0,0 +1,90 @@ + 'foobar'])] +function attribute_with_params_on_function_test() {} + +/* testAttributeWithShortClosureParameter */ +#[AttributeWithParams(static fn ($value) => ! $value)] +function attribute_with_short_closure_param_test() {} + +/* testTwoAttributeOnTheSameLine */ +#[CustomAttribute] #[AttributeWithParams('foo')] +function two_attribute_on_same_line_test() {} + +/* testAttributeAndCommentOnTheSameLine */ +#[CustomAttribute] // This is a comment +function attribute_and_line_comment_on_same_line_test() {} + +/* testAttributeGrouping */ +#[CustomAttribute, AttributeWithParams('foo'), AttributeWithParams('foo', bar: ['bar' => 'foobar'])] +function attribute_grouping_test() {} + +/* testAttributeMultiline */ +#[ + CustomAttribute, + AttributeWithParams('foo'), + AttributeWithParams('foo', bar: ['bar' => 'foobar']) +] +function attribute_multiline_test() {} + +/* testAttributeMultilineWithComment */ +#[ + CustomAttribute, // comment + AttributeWithParams(/* another comment */ 'foo'), + AttributeWithParams('foo', bar: ['bar' => 'foobar']) +] +function attribute_multiline_with_comment_test() {} + +/* testSingleAttributeOnParameter */ +function single_attribute_on_parameter_test(#[ParamAttribute] int $param) {} + +/* testMultipleAttributesOnParameter */ +function multiple_attributes_on_parameter_test(#[ParamAttribute, AttributeWithParams(/* another comment */ 'foo')] int $param) {} + +/* testFqcnAttribute */ +#[Boo\QualifiedName, \Foo\FullyQualifiedName('foo')] +function fqcn_attrebute_test() {} + +/* testNestedAttributes */ +#[Boo\QualifiedName(fn (#[AttributeOne('boo')] $value) => (string) $value)] +function nested_attributes_test() {} + +/* testMultilineAttributesOnParameter */ +function multiline_attributes_on_parameter_test(#[ + AttributeWithParams( + 'foo' + ) + ] int $param) {} + +/* testAttributeContainingTextLookingLikeCloseTag */ +#[DeprecationReason('reason: ')] +function attribute_containing_text_looking_like_close_tag() {} + +/* testAttributeContainingMultilineTextLookingLikeCloseTag */ +#[DeprecationReason( + 'reason: ' +)] +function attribute_containing_mulitline_text_looking_like_close_tag() {} + +/* testInvalidAttribute */ +#[ThisIsNotAnAttribute +function invalid_attribute_test() {} diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/AttributesTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/AttributesTest.php new file mode 100644 index 00000000..8ac826f2 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/AttributesTest.php @@ -0,0 +1,658 @@ + + * @copyright 2019 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class AttributesTest extends AbstractMethodUnitTest +{ + + + /** + * Test that attributes are parsed correctly. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param int $length The number of tokens between opener and closer. + * @param array $tokenCodes The codes of tokens inside the attributes. + * + * @dataProvider dataAttribute + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\PHP::findCloser + * @covers PHP_CodeSniffer\Tokenizers\PHP::parsePhpAttribute + * + * @return void + */ + public function testAttribute($testMarker, $length, $tokenCodes) + { + $tokens = self::$phpcsFile->getTokens(); + + $attribute = $this->getTargetToken($testMarker, T_ATTRIBUTE); + $this->assertArrayHasKey('attribute_closer', $tokens[$attribute]); + + $closer = $tokens[$attribute]['attribute_closer']; + $this->assertSame(($attribute + $length), $closer); + + $this->assertSame(T_ATTRIBUTE_END, $tokens[$closer]['code']); + + $this->assertSame($tokens[$attribute]['attribute_opener'], $tokens[$closer]['attribute_opener']); + $this->assertSame($tokens[$attribute]['attribute_closer'], $tokens[$closer]['attribute_closer']); + + $map = array_map( + function ($token) use ($attribute, $length) { + $this->assertArrayHasKey('attribute_closer', $token); + $this->assertSame(($attribute + $length), $token['attribute_closer']); + + return $token['code']; + }, + array_slice($tokens, ($attribute + 1), ($length - 1)) + ); + + $this->assertSame($tokenCodes, $map); + + }//end testAttribute() + + + /** + * Data provider. + * + * @see testAttribute() + * + * @return array + */ + public function dataAttribute() + { + return [ + [ + '/* testAttribute */', + 2, + [ T_STRING ], + ], + [ + '/* testAttributeWithParams */', + 7, + [ + T_STRING, + T_OPEN_PARENTHESIS, + T_STRING, + T_DOUBLE_COLON, + T_STRING, + T_CLOSE_PARENTHESIS, + ], + ], + [ + '/* testAttributeWithNamedParam */', + 10, + [ + T_STRING, + T_OPEN_PARENTHESIS, + T_PARAM_NAME, + T_COLON, + T_WHITESPACE, + T_STRING, + T_DOUBLE_COLON, + T_STRING, + T_CLOSE_PARENTHESIS, + ], + ], + [ + '/* testAttributeOnFunction */', + 2, + [ T_STRING ], + ], + [ + '/* testAttributeOnFunctionWithParams */', + 17, + [ + T_STRING, + T_OPEN_PARENTHESIS, + T_CONSTANT_ENCAPSED_STRING, + T_COMMA, + T_WHITESPACE, + T_PARAM_NAME, + T_COLON, + T_WHITESPACE, + T_OPEN_SHORT_ARRAY, + T_CONSTANT_ENCAPSED_STRING, + T_WHITESPACE, + T_DOUBLE_ARROW, + T_WHITESPACE, + T_CONSTANT_ENCAPSED_STRING, + T_CLOSE_SHORT_ARRAY, + T_CLOSE_PARENTHESIS, + ], + ], + [ + '/* testAttributeWithShortClosureParameter */', + 17, + [ + T_STRING, + T_OPEN_PARENTHESIS, + T_STATIC, + T_WHITESPACE, + T_FN, + T_WHITESPACE, + T_OPEN_PARENTHESIS, + T_VARIABLE, + T_CLOSE_PARENTHESIS, + T_WHITESPACE, + T_FN_ARROW, + T_WHITESPACE, + T_BOOLEAN_NOT, + T_WHITESPACE, + T_VARIABLE, + T_CLOSE_PARENTHESIS, + ], + ], + [ + '/* testAttributeGrouping */', + 26, + [ + T_STRING, + T_COMMA, + T_WHITESPACE, + T_STRING, + T_OPEN_PARENTHESIS, + T_CONSTANT_ENCAPSED_STRING, + T_CLOSE_PARENTHESIS, + T_COMMA, + T_WHITESPACE, + T_STRING, + T_OPEN_PARENTHESIS, + T_CONSTANT_ENCAPSED_STRING, + T_COMMA, + T_WHITESPACE, + T_PARAM_NAME, + T_COLON, + T_WHITESPACE, + T_OPEN_SHORT_ARRAY, + T_CONSTANT_ENCAPSED_STRING, + T_WHITESPACE, + T_DOUBLE_ARROW, + T_WHITESPACE, + T_CONSTANT_ENCAPSED_STRING, + T_CLOSE_SHORT_ARRAY, + T_CLOSE_PARENTHESIS, + ], + ], + [ + '/* testAttributeMultiline */', + 31, + [ + T_WHITESPACE, + T_WHITESPACE, + T_STRING, + T_COMMA, + T_WHITESPACE, + T_WHITESPACE, + T_STRING, + T_OPEN_PARENTHESIS, + T_CONSTANT_ENCAPSED_STRING, + T_CLOSE_PARENTHESIS, + T_COMMA, + T_WHITESPACE, + T_WHITESPACE, + T_STRING, + T_OPEN_PARENTHESIS, + T_CONSTANT_ENCAPSED_STRING, + T_COMMA, + T_WHITESPACE, + T_PARAM_NAME, + T_COLON, + T_WHITESPACE, + T_OPEN_SHORT_ARRAY, + T_CONSTANT_ENCAPSED_STRING, + T_WHITESPACE, + T_DOUBLE_ARROW, + T_WHITESPACE, + T_CONSTANT_ENCAPSED_STRING, + T_CLOSE_SHORT_ARRAY, + T_CLOSE_PARENTHESIS, + T_WHITESPACE, + ], + ], + [ + '/* testFqcnAttribute */', + 13, + [ + T_STRING, + T_NS_SEPARATOR, + T_STRING, + T_COMMA, + T_WHITESPACE, + T_NS_SEPARATOR, + T_STRING, + T_NS_SEPARATOR, + T_STRING, + T_OPEN_PARENTHESIS, + T_CONSTANT_ENCAPSED_STRING, + T_CLOSE_PARENTHESIS, + ], + ], + ]; + + }//end dataAttribute() + + + /** + * Test that multiple attributes on the same line are parsed correctly. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\PHP::findCloser + * @covers PHP_CodeSniffer\Tokenizers\PHP::parsePhpAttribute + * + * @return void + */ + public function testTwoAttributesOnTheSameLine() + { + $tokens = self::$phpcsFile->getTokens(); + + $attribute = $this->getTargetToken('/* testTwoAttributeOnTheSameLine */', T_ATTRIBUTE); + $this->assertArrayHasKey('attribute_closer', $tokens[$attribute]); + + $closer = $tokens[$attribute]['attribute_closer']; + $this->assertSame(T_WHITESPACE, $tokens[($closer + 1)]['code']); + $this->assertSame(T_ATTRIBUTE, $tokens[($closer + 2)]['code']); + $this->assertArrayHasKey('attribute_closer', $tokens[($closer + 2)]); + + }//end testTwoAttributesOnTheSameLine() + + + /** + * Test that attribute followed by a line comment is parsed correctly. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\PHP::findCloser + * @covers PHP_CodeSniffer\Tokenizers\PHP::parsePhpAttribute + * + * @return void + */ + public function testAttributeAndLineComment() + { + $tokens = self::$phpcsFile->getTokens(); + + $attribute = $this->getTargetToken('/* testAttributeAndCommentOnTheSameLine */', T_ATTRIBUTE); + $this->assertArrayHasKey('attribute_closer', $tokens[$attribute]); + + $closer = $tokens[$attribute]['attribute_closer']; + $this->assertSame(T_WHITESPACE, $tokens[($closer + 1)]['code']); + $this->assertSame(T_COMMENT, $tokens[($closer + 2)]['code']); + + }//end testAttributeAndLineComment() + + + /** + * Test that attributes on function declaration parameters are parsed correctly. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param int $position The token position (starting from T_FUNCTION) of T_ATTRIBUTE token. + * @param int $length The number of tokens between opener and closer. + * @param array $tokenCodes The codes of tokens inside the attributes. + * + * @dataProvider dataAttributeOnParameters + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\PHP::findCloser + * @covers PHP_CodeSniffer\Tokenizers\PHP::parsePhpAttribute + * + * @return void + */ + public function testAttributeOnParameters($testMarker, $position, $length, array $tokenCodes) + { + $tokens = self::$phpcsFile->getTokens(); + + $function = $this->getTargetToken($testMarker, T_FUNCTION); + $attribute = ($function + $position); + + $this->assertSame(T_ATTRIBUTE, $tokens[$attribute]['code']); + $this->assertArrayHasKey('attribute_closer', $tokens[$attribute]); + + $this->assertSame(($attribute + $length), $tokens[$attribute]['attribute_closer']); + + $closer = $tokens[$attribute]['attribute_closer']; + $this->assertSame(T_WHITESPACE, $tokens[($closer + 1)]['code']); + $this->assertSame(T_STRING, $tokens[($closer + 2)]['code']); + $this->assertSame('int', $tokens[($closer + 2)]['content']); + + $this->assertSame(T_VARIABLE, $tokens[($closer + 4)]['code']); + $this->assertSame('$param', $tokens[($closer + 4)]['content']); + + $map = array_map( + function ($token) use ($attribute, $length) { + $this->assertArrayHasKey('attribute_closer', $token); + $this->assertSame(($attribute + $length), $token['attribute_closer']); + + return $token['code']; + }, + array_slice($tokens, ($attribute + 1), ($length - 1)) + ); + + $this->assertSame($tokenCodes, $map); + + }//end testAttributeOnParameters() + + + /** + * Data provider. + * + * @see testAttributeOnParameters() + * + * @return array + */ + public function dataAttributeOnParameters() + { + return [ + [ + '/* testSingleAttributeOnParameter */', + 4, + 2, + [T_STRING], + ], + [ + '/* testMultipleAttributesOnParameter */', + 4, + 10, + [ + T_STRING, + T_COMMA, + T_WHITESPACE, + T_STRING, + T_OPEN_PARENTHESIS, + T_COMMENT, + T_WHITESPACE, + T_CONSTANT_ENCAPSED_STRING, + T_CLOSE_PARENTHESIS, + ], + ], + [ + '/* testMultilineAttributesOnParameter */', + 4, + 13, + [ + T_WHITESPACE, + T_WHITESPACE, + T_STRING, + T_OPEN_PARENTHESIS, + T_WHITESPACE, + T_WHITESPACE, + T_CONSTANT_ENCAPSED_STRING, + T_WHITESPACE, + T_WHITESPACE, + T_CLOSE_PARENTHESIS, + T_WHITESPACE, + T_WHITESPACE, + ], + ], + ]; + + }//end dataAttributeOnParameters() + + + /** + * Test that an attribute containing text which looks like a PHP close tag is tokenized correctly. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param int $length The number of tokens between opener and closer. + * @param array $expectedTokensAttribute The codes of tokens inside the attributes. + * @param array $expectedTokensAfter The codes of tokens after the attributes. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::parsePhpAttribute + * + * @dataProvider dataAttributeOnTextLookingLikeCloseTag + * + * @return void + */ + public function testAttributeContainingTextLookingLikeCloseTag($testMarker, $length, array $expectedTokensAttribute, array $expectedTokensAfter) + { + $tokens = self::$phpcsFile->getTokens(); + + $attribute = $this->getTargetToken($testMarker, T_ATTRIBUTE); + + $this->assertSame('T_ATTRIBUTE', $tokens[$attribute]['type']); + $this->assertArrayHasKey('attribute_closer', $tokens[$attribute]); + + $closer = $tokens[$attribute]['attribute_closer']; + $this->assertSame(($attribute + $length), $closer); + $this->assertSame(T_ATTRIBUTE_END, $tokens[$closer]['code']); + $this->assertSame('T_ATTRIBUTE_END', $tokens[$closer]['type']); + + $this->assertSame($tokens[$attribute]['attribute_opener'], $tokens[$closer]['attribute_opener']); + $this->assertSame($tokens[$attribute]['attribute_closer'], $tokens[$closer]['attribute_closer']); + + $i = ($attribute + 1); + foreach ($expectedTokensAttribute as $item) { + list($expectedType, $expectedContents) = $item; + $this->assertSame($expectedType, $tokens[$i]['type']); + $this->assertSame($expectedContents, $tokens[$i]['content']); + $this->assertArrayHasKey('attribute_opener', $tokens[$i]); + $this->assertArrayHasKey('attribute_closer', $tokens[$i]); + ++$i; + } + + $i = ($closer + 1); + foreach ($expectedTokensAfter as $expectedCode) { + $this->assertSame($expectedCode, $tokens[$i]['code']); + ++$i; + } + + }//end testAttributeContainingTextLookingLikeCloseTag() + + + /** + * Data provider. + * + * @see dataAttributeOnTextLookingLikeCloseTag() + * + * @return array + */ + public function dataAttributeOnTextLookingLikeCloseTag() + { + return [ + [ + '/* testAttributeContainingTextLookingLikeCloseTag */', + 5, + [ + [ + 'T_STRING', + 'DeprecationReason', + ], + [ + 'T_OPEN_PARENTHESIS', + '(', + ], + [ + 'T_CONSTANT_ENCAPSED_STRING', + "'reason: '", + ], + [ + 'T_CLOSE_PARENTHESIS', + ')', + ], + [ + 'T_ATTRIBUTE_END', + ']', + ], + ], + [ + T_WHITESPACE, + T_FUNCTION, + T_WHITESPACE, + T_STRING, + T_OPEN_PARENTHESIS, + T_CLOSE_PARENTHESIS, + T_WHITESPACE, + T_OPEN_CURLY_BRACKET, + T_CLOSE_CURLY_BRACKET, + ], + ], + [ + '/* testAttributeContainingMultilineTextLookingLikeCloseTag */', + 8, + [ + [ + 'T_STRING', + 'DeprecationReason', + ], + [ + 'T_OPEN_PARENTHESIS', + '(', + ], + [ + 'T_WHITESPACE', + "\n", + ], + [ + 'T_WHITESPACE', + " ", + ], + [ + 'T_CONSTANT_ENCAPSED_STRING', + "'reason: '", + ], + [ + 'T_WHITESPACE', + "\n", + ], + [ + 'T_CLOSE_PARENTHESIS', + ')', + ], + [ + 'T_ATTRIBUTE_END', + ']', + ], + ], + [ + T_WHITESPACE, + T_FUNCTION, + T_WHITESPACE, + T_STRING, + T_OPEN_PARENTHESIS, + T_CLOSE_PARENTHESIS, + T_WHITESPACE, + T_OPEN_CURLY_BRACKET, + T_CLOSE_CURLY_BRACKET, + ], + ], + ]; + + }//end dataAttributeOnTextLookingLikeCloseTag() + + + /** + * Test that invalid attribute (or comment starting with #[ and without ]) are parsed correctly. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\PHP::findCloser + * @covers PHP_CodeSniffer\Tokenizers\PHP::parsePhpAttribute + * + * @return void + */ + public function testInvalidAttribute() + { + $tokens = self::$phpcsFile->getTokens(); + + $attribute = $this->getTargetToken('/* testInvalidAttribute */', T_ATTRIBUTE); + + $this->assertArrayHasKey('attribute_closer', $tokens[$attribute]); + $this->assertNull($tokens[$attribute]['attribute_closer']); + + }//end testInvalidAttribute() + + + /** + * Test that nested attributes are parsed correctly. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\PHP::findCloser + * @covers PHP_CodeSniffer\Tokenizers\PHP::parsePhpAttribute + * + * @return void + */ + public function testNestedAttributes() + { + $tokens = self::$phpcsFile->getTokens(); + $tokenCodes = [ + T_STRING, + T_NS_SEPARATOR, + T_STRING, + T_OPEN_PARENTHESIS, + T_FN, + T_WHITESPACE, + T_OPEN_PARENTHESIS, + T_ATTRIBUTE, + T_STRING, + T_OPEN_PARENTHESIS, + T_CONSTANT_ENCAPSED_STRING, + T_CLOSE_PARENTHESIS, + T_ATTRIBUTE_END, + T_WHITESPACE, + T_VARIABLE, + T_CLOSE_PARENTHESIS, + T_WHITESPACE, + T_FN_ARROW, + T_WHITESPACE, + T_STRING_CAST, + T_WHITESPACE, + T_VARIABLE, + T_CLOSE_PARENTHESIS, + ]; + + $attribute = $this->getTargetToken('/* testNestedAttributes */', T_ATTRIBUTE); + $this->assertArrayHasKey('attribute_closer', $tokens[$attribute]); + + $closer = $tokens[$attribute]['attribute_closer']; + $this->assertSame(($attribute + 24), $closer); + + $this->assertSame(T_ATTRIBUTE_END, $tokens[$closer]['code']); + + $this->assertSame($tokens[$attribute]['attribute_opener'], $tokens[$closer]['attribute_opener']); + $this->assertSame($tokens[$attribute]['attribute_closer'], $tokens[$closer]['attribute_closer']); + + $this->assertArrayNotHasKey('nested_attributes', $tokens[$attribute]); + $this->assertArrayHasKey('nested_attributes', $tokens[($attribute + 8)]); + $this->assertSame([$attribute => ($attribute + 24)], $tokens[($attribute + 8)]['nested_attributes']); + + $test = function (array $tokens, $length, $nestedMap) use ($attribute) { + foreach ($tokens as $token) { + $this->assertArrayHasKey('attribute_closer', $token); + $this->assertSame(($attribute + $length), $token['attribute_closer']); + $this->assertSame($nestedMap, $token['nested_attributes']); + } + }; + + $test(array_slice($tokens, ($attribute + 1), 7), 24, [$attribute => $attribute + 24]); + $test(array_slice($tokens, ($attribute + 8), 1), 8 + 5, [$attribute => $attribute + 24]); + + // Length here is 8 (nested attribute offset) + 5 (real length). + $test( + array_slice($tokens, ($attribute + 9), 4), + 8 + 5, + [ + $attribute => $attribute + 24, + $attribute + 8 => $attribute + 13, + ] + ); + + $test(array_slice($tokens, ($attribute + 13), 1), 8 + 5, [$attribute => $attribute + 24]); + $test(array_slice($tokens, ($attribute + 14), 10), 24, [$attribute => $attribute + 24]); + + $map = array_map( + static function ($token) { + return $token['code']; + }, + array_slice($tokens, ($attribute + 1), 23) + ); + + $this->assertSame($tokenCodes, $map); + + }//end testNestedAttributes() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillEnumTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillEnumTest.inc new file mode 100644 index 00000000..82bfe24f --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillEnumTest.inc @@ -0,0 +1,95 @@ +enum = 'foo'; + } +} + +/* testEnumUsedAsFunctionName */ +function enum() +{ +} + +/* testDeclarationContainingComment */ +enum /* comment */ Name +{ + case SOME_CASE; +} + +enum /* testEnumUsedAsEnumName */ Enum +{ +} + +/* testEnumUsedAsNamespaceName */ +namespace Enum; +/* testEnumUsedAsPartOfNamespaceName */ +namespace My\Enum\Collection; +/* testEnumUsedInObjectInitialization */ +$obj = new Enum; +/* testEnumAsFunctionCall */ +$var = enum($a, $b); +/* testEnumAsFunctionCallWithNamespace */ +var = namespace\enum(); +/* testClassConstantFetchWithEnumAsClassName */ +echo Enum::CONSTANT; +/* testClassConstantFetchWithEnumAsConstantName */ +echo ClassName::ENUM; + +/* testParseErrorMissingName */ +enum { + case SOME_CASE; +} + +/* testParseErrorLiveCoding */ +// This must be the last test in the file. +enum diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillEnumTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillEnumTest.php new file mode 100644 index 00000000..33cff3a2 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillEnumTest.php @@ -0,0 +1,229 @@ + + * @copyright 2021 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class BackfillEnumTest extends AbstractMethodUnitTest +{ + + + /** + * Test that the "enum" keyword is tokenized as such. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param string $testContent The token content to look for. + * @param int $openerOffset Offset to find expected scope opener. + * @param int $closerOffset Offset to find expected scope closer. + * + * @dataProvider dataEnums + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testEnums($testMarker, $testContent, $openerOffset, $closerOffset) + { + $tokens = self::$phpcsFile->getTokens(); + + $enum = $this->getTargetToken($testMarker, [T_ENUM, T_STRING], $testContent); + + $this->assertSame(T_ENUM, $tokens[$enum]['code']); + $this->assertSame('T_ENUM', $tokens[$enum]['type']); + + $this->assertArrayHasKey('scope_condition', $tokens[$enum]); + $this->assertArrayHasKey('scope_opener', $tokens[$enum]); + $this->assertArrayHasKey('scope_closer', $tokens[$enum]); + + $this->assertSame($enum, $tokens[$enum]['scope_condition']); + + $scopeOpener = $tokens[$enum]['scope_opener']; + $scopeCloser = $tokens[$enum]['scope_closer']; + + $expectedScopeOpener = ($enum + $openerOffset); + $expectedScopeCloser = ($enum + $closerOffset); + + $this->assertSame($expectedScopeOpener, $scopeOpener); + $this->assertArrayHasKey('scope_condition', $tokens[$scopeOpener]); + $this->assertArrayHasKey('scope_opener', $tokens[$scopeOpener]); + $this->assertArrayHasKey('scope_closer', $tokens[$scopeOpener]); + $this->assertSame($enum, $tokens[$scopeOpener]['scope_condition']); + $this->assertSame($scopeOpener, $tokens[$scopeOpener]['scope_opener']); + $this->assertSame($scopeCloser, $tokens[$scopeOpener]['scope_closer']); + + $this->assertSame($expectedScopeCloser, $scopeCloser); + $this->assertArrayHasKey('scope_condition', $tokens[$scopeCloser]); + $this->assertArrayHasKey('scope_opener', $tokens[$scopeCloser]); + $this->assertArrayHasKey('scope_closer', $tokens[$scopeCloser]); + $this->assertSame($enum, $tokens[$scopeCloser]['scope_condition']); + $this->assertSame($scopeOpener, $tokens[$scopeCloser]['scope_opener']); + $this->assertSame($scopeCloser, $tokens[$scopeCloser]['scope_closer']); + + }//end testEnums() + + + /** + * Data provider. + * + * @see testEnums() + * + * @return array + */ + public function dataEnums() + { + return [ + [ + '/* testPureEnum */', + 'enum', + 4, + 12, + ], + [ + '/* testBackedIntEnum */', + 'enum', + 7, + 29, + ], + [ + '/* testBackedStringEnum */', + 'enum', + 8, + 30, + ], + [ + '/* testComplexEnum */', + 'enum', + 11, + 72, + ], + [ + '/* testEnumWithEnumAsClassName */', + 'enum', + 6, + 7, + ], + [ + '/* testEnumIsCaseInsensitive */', + 'EnUm', + 4, + 5, + ], + [ + '/* testDeclarationContainingComment */', + 'enum', + 6, + 14, + ], + ]; + + }//end dataEnums() + + + /** + * Test that "enum" when not used as the keyword is still tokenized as `T_STRING`. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param string $testContent The token content to look for. + * + * @dataProvider dataNotEnums + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testNotEnums($testMarker, $testContent) + { + $tokens = self::$phpcsFile->getTokens(); + + $target = $this->getTargetToken($testMarker, [T_ENUM, T_STRING], $testContent); + $this->assertSame(T_STRING, $tokens[$target]['code']); + $this->assertSame('T_STRING', $tokens[$target]['type']); + + }//end testNotEnums() + + + /** + * Data provider. + * + * @see testNotEnums() + * + * @return array + */ + public function dataNotEnums() + { + return [ + [ + '/* testEnumAsClassNameAfterEnumKeyword */', + 'Enum', + ], + [ + '/* testEnumUsedAsClassName */', + 'Enum', + ], + [ + '/* testEnumUsedAsClassConstantName */', + 'ENUM', + ], + [ + '/* testEnumUsedAsMethodName */', + 'enum', + ], + [ + '/* testEnumUsedAsPropertyName */', + 'enum', + ], + [ + '/* testEnumUsedAsFunctionName */', + 'enum', + ], + [ + '/* testEnumUsedAsEnumName */', + 'Enum', + ], + [ + '/* testEnumUsedAsNamespaceName */', + 'Enum', + ], + [ + '/* testEnumUsedAsPartOfNamespaceName */', + 'Enum', + ], + [ + '/* testEnumUsedInObjectInitialization */', + 'Enum', + ], + [ + '/* testEnumAsFunctionCall */', + 'enum', + ], + [ + '/* testEnumAsFunctionCallWithNamespace */', + 'enum', + ], + [ + '/* testClassConstantFetchWithEnumAsClassName */', + 'Enum', + ], + [ + '/* testClassConstantFetchWithEnumAsConstantName */', + 'ENUM', + ], + [ + '/* testParseErrorMissingName */', + 'enum', + ], + [ + '/* testParseErrorLiveCoding */', + 'enum', + ], + ]; + + }//end dataNotEnums() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillExplicitOctalNotationTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillExplicitOctalNotationTest.inc new file mode 100644 index 00000000..154d4895 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillExplicitOctalNotationTest.inc @@ -0,0 +1,28 @@ + + * @copyright 2019 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class BackfillExplicitOctalNotationTest extends AbstractMethodUnitTest +{ + + + /** + * Test that explicitly-defined octal values are tokenized as a single number and not as a number and a string. + * + * @param string $marker The comment which prefaces the target token in the test file. + * @param string $value The expected content of the token + * @param int|string $nextToken The expected next token. + * @param string $nextContent The expected content of the next token. + * + * @dataProvider dataExplicitOctalNotation + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testExplicitOctalNotation($marker, $value, $nextToken, $nextContent) + { + $tokens = self::$phpcsFile->getTokens(); + + $number = $this->getTargetToken($marker, [T_LNUMBER]); + + $this->assertSame($value, $tokens[$number]['content'], 'Content of integer token does not match expectation'); + + $this->assertSame($nextToken, $tokens[($number + 1)]['code'], 'Next token is not the expected type, but '.$tokens[($number + 1)]['type']); + $this->assertSame($nextContent, $tokens[($number + 1)]['content'], 'Next token did not have the expected contents'); + + }//end testExplicitOctalNotation() + + + /** + * Data provider. + * + * @see testExplicitOctalNotation() + * + * @return array + */ + public function dataExplicitOctalNotation() + { + return [ + [ + 'marker' => '/* testExplicitOctal */', + 'value' => '0o137041', + 'nextToken' => T_SEMICOLON, + 'nextContent' => ';', + ], + [ + 'marker' => '/* testExplicitOctalCapitalised */', + 'value' => '0O137041', + 'nextToken' => T_SEMICOLON, + 'nextContent' => ';', + ], + [ + 'marker' => '/* testExplicitOctalWithNumericSeparator */', + 'value' => '0o137_041', + 'nextToken' => T_SEMICOLON, + 'nextContent' => ';', + ], + [ + 'marker' => '/* testInvalid1 */', + 'value' => '0', + 'nextToken' => T_STRING, + 'nextContent' => 'o_137', + ], + [ + 'marker' => '/* testInvalid2 */', + 'value' => '0', + 'nextToken' => T_STRING, + 'nextContent' => 'O_41', + ], + [ + 'marker' => '/* testInvalid3 */', + 'value' => '0', + 'nextToken' => T_STRING, + 'nextContent' => 'o91', + ], + [ + 'marker' => '/* testInvalid4 */', + 'value' => '0O2', + 'nextToken' => T_LNUMBER, + 'nextContent' => '82', + ], + [ + 'marker' => '/* testInvalid5 */', + 'value' => '0o2', + 'nextToken' => T_LNUMBER, + 'nextContent' => '8_2', + ], + [ + 'marker' => '/* testInvalid6 */', + 'value' => '0o2', + 'nextToken' => T_STRING, + 'nextContent' => '_82', + ], + ]; + + }//end dataExplicitOctalNotation() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillFnTokenTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillFnTokenTest.inc new file mode 100644 index 00000000..13f165b7 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillFnTokenTest.inc @@ -0,0 +1,198 @@ + $x + $y; + +/* testMixedCase */ +$fn1 = Fn($x) => $x + $y; + +/* testWhitespace */ +$fn1 = fn ($x) => $x + $y; + +/* testComment */ +$fn1 = fn /* comment here */ ($x) => $x + $y; + +/* testHeredoc */ +$fn1 = fn() => << /* testNestedInner */ fn($y) => $x * $y + $z; + +/* testNestedSharedCloserOuter */ +$foo = foo(fn() => /* testNestedSharedCloserInner */ fn() => bar() === true); + +/* testFunctionCall */ +$extended = fn($c) => $callable($factory($c), $c); + +/* testChainedFunctionCall */ +$result = Collection::from([1, 2]) + ->map(fn($v) => $v * 2) + ->reduce(/* testFunctionArgument */ fn($tmp, $v) => $tmp + $v, 0); + +/* testClosure */ +$extended = fn($c) => $callable(function() { + for ($x = 1; $x < 10; $x++) { + echo $x; + } + + echo 'done'; +}, $c); + +/* testArrayIndex */ +$found = in_array_cb($needle, $haystack, fn($array, $needle) => $array[2] === $needle); + +$result = array_map( + /* testReturnType */ + static fn(int $number) : int => $number + 1, + $numbers +); + +/* testReference */ +fn&($x) => $x; + +/* testGrouped */ +(fn($x) => $x) + $y; + +/* testArrayValue */ +$a = [ + 'a' => fn() => return 1, +]; + +/* testArrayValueNoTrailingComma */ +$a = [ + 'a' => fn() => foo() +]; + +/* testYield */ +$a = fn($x) => yield 'k' => $x; + +/* testNullableNamespace */ +$a = fn(?\DateTime $x) : ?\DateTime => $x; + +/* testNamespaceOperatorInTypes */ +$fn = fn(namespace\Foo $a) : ?namespace\Foo => $a; + +/* testSelfReturnType */ +fn(self $a) : self => $a; + +/* testParentReturnType */ +fn(parent $a) : parent => $a; + +/* testCallableReturnType */ +fn(callable $a) : callable => $a; + +/* testArrayReturnType */ +fn(array $a) : array => $a; + +/* testStaticReturnType */ +fn(array $a) : static => $a; + +/* testUnionParamType */ +$arrowWithUnionParam = fn(int|float $param) : SomeClass => new SomeClass($param); + +/* testUnionReturnType */ +$arrowWithUnionReturn = fn($param) : int|float => $param | 10; + +/* testTernary */ +$fn = fn($a) => $a ? /* testTernaryThen */ fn() : string => 'a' : /* testTernaryElse */ fn() : string => 'b'; + +/* testTernaryWithTypes */ +$fn = fn(int|null $a) : array|null => $a ? null : []; + +function matchInArrow($x) { + /* testWithMatchValue */ + $fn = fn($x) => match(true) { + 1, 2, 3, 4, 5 => 'foo', + default => 'bar', + }; +} + +function matchInArrowAndMore($x) { + /* testWithMatchValueAndMore */ + $fn = fn($x) => match(true) { + 1, 2, 3, 4, 5 => 'foo', + default => 'bar', + } . 'suffix'; +} + +function arrowFunctionInMatchWithTrailingComma($x) { + return match ($x) { + /* testInMatchNotLastValue */ + 1 => fn($y) => callMe($y), + /* testInMatchLastValueWithTrailingComma */ + default => fn($y) => callThem($y), + }; +} + +function arrowFunctionInMatchNoTrailingComma1($x) { + return match ($x) { + 1 => fn($y) => callMe($y), + /* testInMatchLastValueNoTrailingComma1 */ + default => fn($y) => callThem($y) + }; +} + +function arrowFunctionInMatchNoTrailingComma2($x) { + return match ($x) { + /* testInMatchLastValueNoTrailingComma2 */ + default => fn($y) => 5 * $y + }; +} + +/* testConstantDeclaration */ +const FN = 'a'; + +/* testConstantDeclarationLower */ +const fn = 'a'; + +class Foo { + /* testStaticMethodName */ + public static function fn($param) { + /* testNestedInMethod */ + $fn = fn($c) => $callable($factory($c), $c); + } + + public function foo() { + /* testPropertyAssignment */ + $this->fn = 'a'; + } +} + +$anon = new class() { + /* testAnonClassMethodName */ + protected function fN($param) { + } +} + +/* testNonArrowStaticMethodCall */ +$a = Foo::fn($param); + +/* testNonArrowConstantAccess */ +$a = MyClass::FN; + +/* testNonArrowConstantAccessMixed */ +$a = MyClass::Fn; + +/* testNonArrowObjectMethodCall */ +$a = $obj->fn($param); + +/* testNonArrowObjectMethodCallUpper */ +$a = $obj->FN($param); + +/* testNonArrowNamespacedFunctionCall */ +$a = MyNS\Sub\Fn($param); + +/* testNonArrowNamespaceOperatorFunctionCall */ +$a = namespace\fn($param); + +/* testNonArrowFunctionNameWithUnionTypes */ +function fn(int|float $param) : string|null {} + +/* testLiveCoding */ +// Intentional parse error. This has to be the last test in the file. +$fn = fn diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillFnTokenTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillFnTokenTest.php new file mode 100644 index 00000000..4d0f4c06 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillFnTokenTest.php @@ -0,0 +1,802 @@ + + * @copyright 2019 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class BackfillFnTokenTest extends AbstractMethodUnitTest +{ + + + /** + * Test simple arrow functions. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testSimple() + { + foreach (['/* testStandard */', '/* testMixedCase */'] as $comment) { + $token = $this->getTargetToken($comment, T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 5, 12); + } + + }//end testSimple() + + + /** + * Test whitespace inside arrow function definitions. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testWhitespace() + { + $token = $this->getTargetToken('/* testWhitespace */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 6, 13); + + }//end testWhitespace() + + + /** + * Test comments inside arrow function definitions. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testComment() + { + $token = $this->getTargetToken('/* testComment */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 8, 15); + + }//end testComment() + + + /** + * Test heredocs inside arrow function definitions. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testHeredoc() + { + $token = $this->getTargetToken('/* testHeredoc */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 4, 9); + + }//end testHeredoc() + + + /** + * Test nested arrow functions. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testNestedOuter() + { + $token = $this->getTargetToken('/* testNestedOuter */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 5, 25); + + }//end testNestedOuter() + + + /** + * Test nested arrow functions. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testNestedInner() + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken('/* testNestedInner */', T_FN); + $this->backfillHelper($token, true); + + $expectedScopeOpener = ($token + 5); + $expectedScopeCloser = ($token + 16); + + $this->assertSame($expectedScopeOpener, $tokens[$token]['scope_opener'], 'Scope opener is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$token]['scope_closer'], 'Scope closer is not the semicolon token'); + + $opener = $tokens[$token]['scope_opener']; + $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], 'Opener scope opener is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], 'Opener scope closer is not the semicolon token'); + + $closer = $tokens[$token]['scope_closer']; + $this->assertSame(($token - 4), $tokens[$closer]['scope_opener'], 'Closer scope opener is not the arrow token of the "outer" arrow function (shared scope closer)'); + $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], 'Closer scope closer is not the semicolon token'); + + }//end testNestedInner() + + + /** + * Test nested arrow functions with a shared closer. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testNestedSharedCloser() + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken('/* testNestedSharedCloserOuter */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 4, 20); + + $token = $this->getTargetToken('/* testNestedSharedCloserInner */', T_FN); + $this->backfillHelper($token, true); + + $expectedScopeOpener = ($token + 4); + $expectedScopeCloser = ($token + 12); + + $this->assertSame($expectedScopeOpener, $tokens[$token]['scope_opener'], 'Scope opener for "inner" arrow function is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$token]['scope_closer'], 'Scope closer for "inner" arrow function is not the TRUE token'); + + $opener = $tokens[$token]['scope_opener']; + $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], 'Opener scope opener for "inner" arrow function is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], 'Opener scope closer for "inner" arrow function is not the semicolon token'); + + $closer = $tokens[$token]['scope_closer']; + $this->assertSame(($token - 4), $tokens[$closer]['scope_opener'], 'Closer scope opener for "inner" arrow function is not the arrow token of the "outer" arrow function (shared scope closer)'); + $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], 'Closer scope closer for "inner" arrow function is not the TRUE token'); + + }//end testNestedSharedCloser() + + + /** + * Test arrow functions that call functions. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testFunctionCall() + { + $token = $this->getTargetToken('/* testFunctionCall */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 5, 17); + + }//end testFunctionCall() + + + /** + * Test arrow functions that are included in chained calls. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testChainedFunctionCall() + { + $token = $this->getTargetToken('/* testChainedFunctionCall */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 5, 12, 'bracket'); + + }//end testChainedFunctionCall() + + + /** + * Test arrow functions that are used as function arguments. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testFunctionArgument() + { + $token = $this->getTargetToken('/* testFunctionArgument */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 8, 15, 'comma'); + + }//end testFunctionArgument() + + + /** + * Test arrow functions that use closures. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testClosure() + { + $token = $this->getTargetToken('/* testClosure */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 5, 60, 'comma'); + + }//end testClosure() + + + /** + * Test arrow functions using an array index. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testArrayIndex() + { + $token = $this->getTargetToken('/* testArrayIndex */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 8, 17, 'comma'); + + }//end testArrayIndex() + + + /** + * Test arrow functions with a return type. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testReturnType() + { + $token = $this->getTargetToken('/* testReturnType */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 11, 18, 'comma'); + + }//end testReturnType() + + + /** + * Test arrow functions that return a reference. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testReference() + { + $token = $this->getTargetToken('/* testReference */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 6, 9); + + }//end testReference() + + + /** + * Test arrow functions that are grouped by parenthesis. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testGrouped() + { + $token = $this->getTargetToken('/* testGrouped */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 5, 8); + + }//end testGrouped() + + + /** + * Test arrow functions that are used as array values. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testArrayValue() + { + $token = $this->getTargetToken('/* testArrayValue */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 4, 9, 'comma'); + + }//end testArrayValue() + + + /** + * Test arrow functions that are used as array values with no trailing comma. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testArrayValueNoTrailingComma() + { + $token = $this->getTargetToken('/* testArrayValueNoTrailingComma */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 4, 8, 'closing parenthesis'); + + }//end testArrayValueNoTrailingComma() + + + /** + * Test arrow functions that use the yield keyword. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testYield() + { + $token = $this->getTargetToken('/* testYield */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 5, 14); + + }//end testYield() + + + /** + * Test arrow functions that use nullable namespace types. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testNullableNamespace() + { + $token = $this->getTargetToken('/* testNullableNamespace */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 15, 18); + + }//end testNullableNamespace() + + + /** + * Test arrow functions that use the namespace operator in the return type. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testNamespaceOperatorInTypes() + { + $token = $this->getTargetToken('/* testNamespaceOperatorInTypes */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 16, 19); + + }//end testNamespaceOperatorInTypes() + + + /** + * Test arrow functions that use self/parent/callable/array/static return types. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testKeywordReturnTypes() + { + $tokens = self::$phpcsFile->getTokens(); + + $testMarkers = [ + 'Self', + 'Parent', + 'Callable', + 'Array', + 'Static', + ]; + + foreach ($testMarkers as $marker) { + $token = $this->getTargetToken('/* test'.$marker.'ReturnType */', T_FN); + $this->backfillHelper($token); + + $expectedScopeOpener = ($token + 11); + $expectedScopeCloser = ($token + 14); + + $this->assertSame($expectedScopeOpener, $tokens[$token]['scope_opener'], "Scope opener is not the arrow token (for $marker)"); + $this->assertSame($expectedScopeCloser, $tokens[$token]['scope_closer'], "Scope closer is not the semicolon token(for $marker)"); + + $opener = $tokens[$token]['scope_opener']; + $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], "Opener scope opener is not the arrow token(for $marker)"); + $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], "Opener scope closer is not the semicolon token(for $marker)"); + + $closer = $tokens[$token]['scope_closer']; + $this->assertSame($expectedScopeOpener, $tokens[$closer]['scope_opener'], "Closer scope opener is not the arrow token(for $marker)"); + $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], "Closer scope closer is not the semicolon token(for $marker)"); + } + + }//end testKeywordReturnTypes() + + + /** + * Test arrow function with a union parameter type. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testUnionParamType() + { + $token = $this->getTargetToken('/* testUnionParamType */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 13, 21); + + }//end testUnionParamType() + + + /** + * Test arrow function with a union return type. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testUnionReturnType() + { + $token = $this->getTargetToken('/* testUnionReturnType */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 11, 18); + + }//end testUnionReturnType() + + + /** + * Test arrow functions used in ternary operators. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testTernary() + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken('/* testTernary */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 5, 40); + + $token = $this->getTargetToken('/* testTernaryThen */', T_FN); + $this->backfillHelper($token); + + $expectedScopeOpener = ($token + 8); + $expectedScopeCloser = ($token + 12); + + $this->assertSame($expectedScopeOpener, $tokens[$token]['scope_opener'], 'Scope opener for THEN is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$token]['scope_closer'], 'Scope closer for THEN is not the semicolon token'); + + $opener = $tokens[$token]['scope_opener']; + $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], 'Opener scope opener for THEN is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], 'Opener scope closer for THEN is not the semicolon token'); + + $closer = $tokens[$token]['scope_closer']; + $this->assertSame($expectedScopeOpener, $tokens[$closer]['scope_opener'], 'Closer scope opener for THEN is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], 'Closer scope closer for THEN is not the semicolon token'); + + $token = $this->getTargetToken('/* testTernaryElse */', T_FN); + $this->backfillHelper($token, true); + + $expectedScopeOpener = ($token + 8); + $expectedScopeCloser = ($token + 11); + + $this->assertSame($expectedScopeOpener, $tokens[$token]['scope_opener'], 'Scope opener for ELSE is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$token]['scope_closer'], 'Scope closer for ELSE is not the semicolon token'); + + $opener = $tokens[$token]['scope_opener']; + $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], 'Opener scope opener for ELSE is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], 'Opener scope closer for ELSE is not the semicolon token'); + + $closer = $tokens[$token]['scope_closer']; + $this->assertSame(($token - 24), $tokens[$closer]['scope_opener'], 'Closer scope opener for ELSE is not the arrow token of the "outer" arrow function (shared scope closer)'); + $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], 'Closer scope closer for ELSE is not the semicolon token'); + + }//end testTernary() + + + /** + * Test typed arrow functions used in ternary operators. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testTernaryWithTypes() + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken('/* testTernaryWithTypes */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 15, 27); + + }//end testTernaryWithTypes() + + + /** + * Test arrow function returning a match control structure. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testWithMatchValue() + { + $token = $this->getTargetToken('/* testWithMatchValue */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 5, 44); + + }//end testWithMatchValue() + + + /** + * Test arrow function returning a match control structure with something behind it. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testWithMatchValueAndMore() + { + $token = $this->getTargetToken('/* testWithMatchValueAndMore */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 5, 48); + + }//end testWithMatchValueAndMore() + + + /** + * Test match control structure returning arrow functions. + * + * @param string $testMarker The comment prefacing the target token. + * @param int $openerOffset The expected offset of the scope opener in relation to the testMarker. + * @param int $closerOffset The expected offset of the scope closer in relation to the testMarker. + * @param string $expectedCloserType The type of token expected for the scope closer. + * @param string $expectedCloserFriendlyName A friendly name for the type of token expected for the scope closer + * to be used in the error message for failing tests. + * + * @dataProvider dataInMatchValue + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testInMatchValue($testMarker, $openerOffset, $closerOffset, $expectedCloserType, $expectedCloserFriendlyName) + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, $openerOffset, $closerOffset, $expectedCloserFriendlyName); + + $this->assertSame($expectedCloserType, $tokens[($token + $closerOffset)]['type'], 'Mismatched scope closer type'); + + }//end testInMatchValue() + + + /** + * Data provider. + * + * @see testInMatchValue() + * + * @return array + */ + public function dataInMatchValue() + { + return [ + 'not_last_value' => [ + '/* testInMatchNotLastValue */', + 5, + 11, + 'T_COMMA', + 'comma', + ], + 'last_value_with_trailing_comma' => [ + '/* testInMatchLastValueWithTrailingComma */', + 5, + 11, + 'T_COMMA', + 'comma', + ], + 'last_value_without_trailing_comma_1' => [ + '/* testInMatchLastValueNoTrailingComma1 */', + 5, + 10, + 'T_CLOSE_PARENTHESIS', + 'close parenthesis', + ], + 'last_value_without_trailing_comma_2' => [ + '/* testInMatchLastValueNoTrailingComma2 */', + 5, + 11, + 'T_VARIABLE', + '$y variable', + ], + ]; + + }//end dataInMatchValue() + + + /** + * Test arrow function nested within a method declaration. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testNestedInMethod() + { + $token = $this->getTargetToken('/* testNestedInMethod */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 5, 17); + + }//end testNestedInMethod() + + + /** + * Verify that "fn" keywords which are not arrow functions get tokenized as T_STRING and don't + * have the extra token array indexes. + * + * @param string $testMarker The comment prefacing the target token. + * @param string $testContent The token content to look for. + * + * @dataProvider dataNotAnArrowFunction + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testNotAnArrowFunction($testMarker, $testContent='fn') + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, [T_STRING, T_FN], $testContent); + $tokenArray = $tokens[$token]; + + $this->assertSame('T_STRING', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING'); + + $this->assertArrayNotHasKey('scope_condition', $tokenArray, 'Scope condition is set'); + $this->assertArrayNotHasKey('scope_opener', $tokenArray, 'Scope opener is set'); + $this->assertArrayNotHasKey('scope_closer', $tokenArray, 'Scope closer is set'); + $this->assertArrayNotHasKey('parenthesis_owner', $tokenArray, 'Parenthesis owner is set'); + $this->assertArrayNotHasKey('parenthesis_opener', $tokenArray, 'Parenthesis opener is set'); + $this->assertArrayNotHasKey('parenthesis_closer', $tokenArray, 'Parenthesis closer is set'); + + }//end testNotAnArrowFunction() + + + /** + * Data provider. + * + * @see testNotAnArrowFunction() + * + * @return array + */ + public function dataNotAnArrowFunction() + { + return [ + ['/* testFunctionName */'], + [ + '/* testConstantDeclaration */', + 'FN', + ], + [ + '/* testConstantDeclarationLower */', + 'fn', + ], + ['/* testStaticMethodName */'], + ['/* testPropertyAssignment */'], + [ + '/* testAnonClassMethodName */', + 'fN', + ], + ['/* testNonArrowStaticMethodCall */'], + [ + '/* testNonArrowConstantAccess */', + 'FN', + ], + [ + '/* testNonArrowConstantAccessMixed */', + 'Fn', + ], + ['/* testNonArrowObjectMethodCall */'], + [ + '/* testNonArrowObjectMethodCallUpper */', + 'FN', + ], + [ + '/* testNonArrowNamespacedFunctionCall */', + 'Fn', + ], + ['/* testNonArrowNamespaceOperatorFunctionCall */'], + ['/* testNonArrowFunctionNameWithUnionTypes */'], + ['/* testLiveCoding */'], + ]; + + }//end dataNotAnArrowFunction() + + + /** + * Helper function to check that all token keys are correctly set for T_FN tokens. + * + * @param int $token The T_FN token to check. + * @param bool $skipScopeCloserCheck Whether to skip the scope closer check. + * This should be set to "true" when testing nested arrow functions, + * where the "inner" arrow function shares a scope closer with the + * "outer" arrow function, as the 'scope_condition' for the scope closer + * of the "inner" arrow function will point to the "outer" arrow function. + * + * @return void + */ + private function backfillHelper($token, $skipScopeCloserCheck=false) + { + $tokens = self::$phpcsFile->getTokens(); + + $this->assertTrue(array_key_exists('scope_condition', $tokens[$token]), 'Scope condition is not set'); + $this->assertTrue(array_key_exists('scope_opener', $tokens[$token]), 'Scope opener is not set'); + $this->assertTrue(array_key_exists('scope_closer', $tokens[$token]), 'Scope closer is not set'); + $this->assertSame($tokens[$token]['scope_condition'], $token, 'Scope condition is not the T_FN token'); + $this->assertTrue(array_key_exists('parenthesis_owner', $tokens[$token]), 'Parenthesis owner is not set'); + $this->assertTrue(array_key_exists('parenthesis_opener', $tokens[$token]), 'Parenthesis opener is not set'); + $this->assertTrue(array_key_exists('parenthesis_closer', $tokens[$token]), 'Parenthesis closer is not set'); + $this->assertSame($tokens[$token]['parenthesis_owner'], $token, 'Parenthesis owner is not the T_FN token'); + + $opener = $tokens[$token]['scope_opener']; + $this->assertTrue(array_key_exists('scope_condition', $tokens[$opener]), 'Opener scope condition is not set'); + $this->assertTrue(array_key_exists('scope_opener', $tokens[$opener]), 'Opener scope opener is not set'); + $this->assertTrue(array_key_exists('scope_closer', $tokens[$opener]), 'Opener scope closer is not set'); + $this->assertSame($tokens[$opener]['scope_condition'], $token, 'Opener scope condition is not the T_FN token'); + $this->assertSame(T_FN_ARROW, $tokens[$opener]['code'], 'Arrow scope opener not tokenized as T_FN_ARROW (code)'); + $this->assertSame('T_FN_ARROW', $tokens[$opener]['type'], 'Arrow scope opener not tokenized as T_FN_ARROW (type)'); + + $closer = $tokens[$token]['scope_closer']; + $this->assertTrue(array_key_exists('scope_condition', $tokens[$closer]), 'Closer scope condition is not set'); + $this->assertTrue(array_key_exists('scope_opener', $tokens[$closer]), 'Closer scope opener is not set'); + $this->assertTrue(array_key_exists('scope_closer', $tokens[$closer]), 'Closer scope closer is not set'); + if ($skipScopeCloserCheck === false) { + $this->assertSame($tokens[$closer]['scope_condition'], $token, 'Closer scope condition is not the T_FN token'); + } + + $opener = $tokens[$token]['parenthesis_opener']; + $this->assertTrue(array_key_exists('parenthesis_owner', $tokens[$opener]), 'Opening parenthesis owner is not set'); + $this->assertSame($tokens[$opener]['parenthesis_owner'], $token, 'Opening parenthesis owner is not the T_FN token'); + + $closer = $tokens[$token]['parenthesis_closer']; + $this->assertTrue(array_key_exists('parenthesis_owner', $tokens[$closer]), 'Closing parenthesis owner is not set'); + $this->assertSame($tokens[$closer]['parenthesis_owner'], $token, 'Closing parenthesis owner is not the T_FN token'); + + }//end backfillHelper() + + + /** + * Helper function to check that the scope opener/closer positions are correctly set for T_FN tokens. + * + * @param int $token The T_FN token to check. + * @param int $openerOffset The expected offset of the scope opener in relation to + * the fn keyword. + * @param int $closerOffset The expected offset of the scope closer in relation to + * the fn keyword. + * @param string $expectedCloserType Optional. The type of token expected for the scope closer. + * + * @return void + */ + private function scopePositionTestHelper($token, $openerOffset, $closerOffset, $expectedCloserType='semicolon') + { + $tokens = self::$phpcsFile->getTokens(); + $expectedScopeOpener = ($token + $openerOffset); + $expectedScopeCloser = ($token + $closerOffset); + + $this->assertSame($expectedScopeOpener, $tokens[$token]['scope_opener'], 'Scope opener is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$token]['scope_closer'], 'Scope closer is not the '.$expectedCloserType.' token'); + + $opener = $tokens[$token]['scope_opener']; + $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], 'Opener scope opener is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], 'Opener scope closer is not the '.$expectedCloserType.' token'); + + $closer = $tokens[$token]['scope_closer']; + $this->assertSame($expectedScopeOpener, $tokens[$closer]['scope_opener'], 'Closer scope opener is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], 'Closer scope closer is not the '.$expectedCloserType.' token'); + + }//end scopePositionTestHelper() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillMatchTokenTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillMatchTokenTest.inc new file mode 100644 index 00000000..095a4d7d --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillMatchTokenTest.inc @@ -0,0 +1,319 @@ + 'Zero', + 1 => 'One', + 2 => 'Two', + }; +} + +function matchNoTrailingComma($bool) { + /* testMatchNoTrailingComma */ + echo match ($bool) { + true => "true\n", + false => "false\n" + }; +} + +function matchWithDefault($i) { + /* testMatchWithDefault */ + return match ($i) { + 1 => 1, + 2 => 2, + default => 'default', + }; +} + +function matchExpressionInCondition($i) { + /* testMatchExpressionInCondition */ + return match (true) { + $i >= 50 => '50+', + $i >= 40 => '40-50', + $i >= 30 => '30-40', + $i >= 20 => '20-30', + $i >= 10 => '10-20', + default => '0-10', + }; +} + +function matchMultiCase($day) { + /* testMatchMultiCase */ + return match ($day) { + 1, 7 => false, + 2, 3, 4, 5, 6 => true, + }; +} + +function matchMultiCaseTrailingCommaInCase($bool) { + /* testMatchMultiCaseTrailingCommaInCase */ + echo match ($bool) { + false, + 0, + => "false\n", + true, + 1, + => "true\n", + default, + => "not bool\n", + }; +} + +assert((function () { + /* testMatchInClosureNotLowercase */ + Match ('foo') { + 'foo', 'bar' => false, + 'baz' => 'a', + default => 'b', + }; +})()); + +function matchInArrowFunction($x) { + /* testMatchInArrowFunction */ + $fn = fn($x) => match(true) { + 1, 2, 3, 4, 5 => 'foo', + default => 'bar', + }; +} + +function arrowFunctionInMatchNoTrailingComma($x) { + /* testArrowFunctionInMatchNoTrailingComma */ + return match ($x) { + 1 => fn($y) => callMe($y), + default => fn($y) => callThem($y) + }; +} + +/* testMatchInFunctionCallParamNotLowercase */ +var_dump(MATCH ( 'foo' ) { + 'foo' => dump_and_return('foo'), + 'bar' => dump_and_return('bar'), +}); + +/* testMatchInMethodCallParam */ +Test::usesValue(match(true) { true => $i }); + +/* testMatchDiscardResult */ +match (1) { + 1 => print "Executed\n", +}; + +/* testMatchWithDuplicateConditionsWithComments */ +echo match /*comment*/ ( $value /*comment*/ ) { + // Comment. + 2, 1 => '2, 1', + 1 => 1, + 3 => 3, + 4 => 4, + 5 => 5, +}; + +/* testNestedMatchOuter */ +$x = match ($y) { + /* testNestedMatchInner */ + default => match ($z) { 1 => 1 }, +}; + +/* testMatchInTernaryCondition */ +$x = match ($test) { 1 => 'a', 2 => 'b' } ? + /* testMatchInTernaryThen */ match ($test) { 1 => 'a', 2 => 'b' } : + /* testMatchInTernaryElse */ match ($notTest) { 3 => 'a', 4 => 'b' }; + +/* testMatchInArrayValue */ +$array = array( + match ($test) { 1 => 'a', 2 => 'b' }, +); + +/* testMatchInArrayKey */ +$array = [ + match ($test) { 1 => 'a', 2 => 'b' } => 'dynamic keys, woho!', +]; + +/* testMatchreturningArray */ +$matcher = match ($x) { + 0 => array( 0 => 1, 'a' => 2, 'b' => 3 ), + 1 => [1, 2, 3], + 2 => array( 1, [1, 2, 3], 2, 3), + 3 => [ 0 => 1, 'a' => array(1, 2, 3), 'b' => 2, 3], +}; + +/* testSwitchContainingMatch */ +switch ($something) { + /* testMatchWithDefaultNestedInSwitchCase1 */ + case 'foo': + $var = [1, 2, 3]; + $var = match ($i) { + 1 => 1, + default => 'default', + }; + continue 2; + + /* testMatchWithDefaultNestedInSwitchCase2 */ + case 'bar' ; + $i = callMe($a, $b); + return match ($i) { + 1 => 1, + default => 'default', + }; + + /* testMatchWithDefaultNestedInSwitchDefault */ + default: + echo 'something', match ($i) { + 1 => 1, + default => 'default', + }; + break; +} + +/* testMatchContainingSwitch */ +$x = match ($y) { + 5, 8 => function($z) { + /* testSwitchNestedInMatch1 */ + switch($z) { + case 'a': + $var = [1, 2, 3]; + return 'a'; + /* testSwitchDefaultNestedInMatchCase */ + default: + $i = callMe($a, $b); + return 'default1'; + } + }, + default => function($z) { + /* testSwitchNestedInMatch2 */ + switch($z) { + case 'a'; + $i = callMe($a, $b); + return 'b'; + /* testSwitchDefaultNestedInMatchDefault */ + default; + $var = [1, 2, 3]; + return 'default2'; + } + } +}; + +/* testMatchNoCases */ +// Intentional fatal error. +$x = match (true) {}; + +/* testMatchMultiDefault */ +// Intentional fatal error. +echo match (1) { + default => 'foo', + 1 => 'bar', + 2 => 'baz', + default => 'qux', +}; + +/* testNoMatchStaticMethodCall */ +$a = Foo::match($param); + +/* testNoMatchClassConstantAccess */ +$a = MyClass::MATCH; + +/* testNoMatchClassConstantArrayAccessMixedCase */ +$a = MyClass::Match[$a]; + +/* testNoMatchMethodCall */ +$a = $obj->match($param); + +/* testNoMatchMethodCallUpper */ +$a = $obj??->MATCH()->chain($param); + +/* testNoMatchPropertyAccess */ +$a = $obj->match; + +/* testNoMatchNamespacedFunctionCall */ +// Intentional fatal error. +$a = MyNS\Sub\match($param); + +/* testNoMatchNamespaceOperatorFunctionCall */ +// Intentional fatal error. +$a = namespace\match($param); + +interface MatchInterface { + /* testNoMatchInterfaceMethodDeclaration */ + public static function match($param); +} + +class MatchClass { + /* testNoMatchClassConstantDeclarationLower */ + const match = 'a'; + + /* testNoMatchClassMethodDeclaration */ + public static function match($param) { + /* testNoMatchPropertyAssignment */ + $this->match = 'a'; + } +} + +/* testNoMatchClassInstantiation */ +$obj = new Match(); + +$anon = new class() { + /* testNoMatchAnonClassMethodDeclaration */ + protected function maTCH($param) { + } +}; + +/* testNoMatchClassDeclaration */ +// Intentional fatal error. Match is now a reserved keyword. +class Match {} + +/* testNoMatchInterfaceDeclaration */ +// Intentional fatal error. Match is now a reserved keyword. +interface Match {} + +/* testNoMatchTraitDeclaration */ +// Intentional fatal error. Match is now a reserved keyword. +trait Match {} + +/* testNoMatchConstantDeclaration */ +// Intentional fatal error. Match is now a reserved keyword. +const MATCH = '1'; + +/* testNoMatchFunctionDeclaration */ +// Intentional fatal error. Match is now a reserved keyword. +function match() {} + +/* testNoMatchNamespaceDeclaration */ +// Intentional fatal error. Match is now a reserved keyword. +namespace Match {} + +/* testNoMatchExtendedClassDeclaration */ +// Intentional fatal error. Match is now a reserved keyword. +class Foo extends Match {} + +/* testNoMatchImplementedClassDeclaration */ +// Intentional fatal error. Match is now a reserved keyword. +class Bar implements Match {} + +/* testNoMatchInUseStatement */ +// Intentional fatal error in PHP < 8. Match is now a reserved keyword. +use Match\me; + +function brokenMatchNoCurlies($x) { + /* testNoMatchMissingCurlies */ + // Intentional fatal error. New control structure is not supported without curly braces. + return match ($x) + 0 => 'Zero', + 1 => 'One', + 2 => 'Two', + ; +} + +function brokenMatchAlternativeSyntax($x) { + /* testNoMatchAlternativeSyntax */ + // Intentional fatal error. Alternative syntax is not supported. + return match ($x) : + 0 => 'Zero', + 1 => 'One', + 2 => 'Two', + endmatch; +} + +/* testLiveCoding */ +// Intentional parse error. This has to be the last test in the file. +echo match diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillMatchTokenTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillMatchTokenTest.php new file mode 100644 index 00000000..80f909ac --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillMatchTokenTest.php @@ -0,0 +1,529 @@ + + * @copyright 2020-2021 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; +use PHP_CodeSniffer\Util\Tokens; + +class BackfillMatchTokenTest extends AbstractMethodUnitTest +{ + + + /** + * Test tokenization of match expressions. + * + * @param string $testMarker The comment prefacing the target token. + * @param int $openerOffset The expected offset of the scope opener in relation to the testMarker. + * @param int $closerOffset The expected offset of the scope closer in relation to the testMarker. + * @param string $testContent The token content to look for. + * + * @dataProvider dataMatchExpression + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testMatchExpression($testMarker, $openerOffset, $closerOffset, $testContent='match') + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, [T_STRING, T_MATCH], $testContent); + $tokenArray = $tokens[$token]; + + $this->assertSame(T_MATCH, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_MATCH (code)'); + $this->assertSame('T_MATCH', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_MATCH (type)'); + + $this->scopeTestHelper($token, $openerOffset, $closerOffset); + $this->parenthesisTestHelper($token); + + }//end testMatchExpression() + + + /** + * Data provider. + * + * @see testMatchExpression() + * + * @return array + */ + public function dataMatchExpression() + { + return [ + 'simple_match' => [ + '/* testMatchSimple */', + 6, + 33, + ], + 'no_trailing_comma' => [ + '/* testMatchNoTrailingComma */', + 6, + 24, + ], + 'with_default_case' => [ + '/* testMatchWithDefault */', + 6, + 33, + ], + 'expression_in_condition' => [ + '/* testMatchExpressionInCondition */', + 6, + 77, + ], + 'multicase' => [ + '/* testMatchMultiCase */', + 6, + 40, + ], + 'multicase_trailing_comma_in_case' => [ + '/* testMatchMultiCaseTrailingCommaInCase */', + 6, + 47, + ], + 'in_closure_not_lowercase' => [ + '/* testMatchInClosureNotLowercase */', + 6, + 36, + 'Match', + ], + 'in_arrow_function' => [ + '/* testMatchInArrowFunction */', + 5, + 36, + ], + 'arrow_function_in_match_no_trailing_comma' => [ + '/* testArrowFunctionInMatchNoTrailingComma */', + 6, + 44, + ], + 'in_function_call_param_not_lowercase' => [ + '/* testMatchInFunctionCallParamNotLowercase */', + 8, + 32, + 'MATCH', + ], + 'in_method_call_param' => [ + '/* testMatchInMethodCallParam */', + 5, + 13, + ], + 'discard_result' => [ + '/* testMatchDiscardResult */', + 6, + 18, + ], + 'duplicate_conditions_and_comments' => [ + '/* testMatchWithDuplicateConditionsWithComments */', + 12, + 59, + ], + 'nested_match_outer' => [ + '/* testNestedMatchOuter */', + 6, + 33, + ], + 'nested_match_inner' => [ + '/* testNestedMatchInner */', + 6, + 14, + ], + 'ternary_condition' => [ + '/* testMatchInTernaryCondition */', + 6, + 21, + ], + 'ternary_then' => [ + '/* testMatchInTernaryThen */', + 6, + 21, + ], + 'ternary_else' => [ + '/* testMatchInTernaryElse */', + 6, + 21, + ], + 'array_value' => [ + '/* testMatchInArrayValue */', + 6, + 21, + ], + 'array_key' => [ + '/* testMatchInArrayKey */', + 6, + 21, + ], + 'returning_array' => [ + '/* testMatchreturningArray */', + 6, + 125, + ], + 'nested_in_switch_case_1' => [ + '/* testMatchWithDefaultNestedInSwitchCase1 */', + 6, + 25, + ], + 'nested_in_switch_case_2' => [ + '/* testMatchWithDefaultNestedInSwitchCase2 */', + 6, + 25, + ], + 'nested_in_switch_default' => [ + '/* testMatchWithDefaultNestedInSwitchDefault */', + 6, + 25, + ], + 'match_with_nested_switch' => [ + '/* testMatchContainingSwitch */', + 6, + 180, + ], + 'no_cases' => [ + '/* testMatchNoCases */', + 6, + 7, + ], + 'multi_default' => [ + '/* testMatchMultiDefault */', + 6, + 40, + ], + ]; + + }//end dataMatchExpression() + + + /** + * Verify that "match" keywords which are not match control structures get tokenized as T_STRING + * and don't have the extra token array indexes. + * + * @param string $testMarker The comment prefacing the target token. + * @param string $testContent The token content to look for. + * + * @dataProvider dataNotAMatchStructure + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testNotAMatchStructure($testMarker, $testContent='match') + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, [T_STRING, T_MATCH], $testContent); + $tokenArray = $tokens[$token]; + + $this->assertSame(T_STRING, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (code)'); + $this->assertSame('T_STRING', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (type)'); + + $this->assertArrayNotHasKey('scope_condition', $tokenArray, 'Scope condition is set'); + $this->assertArrayNotHasKey('scope_opener', $tokenArray, 'Scope opener is set'); + $this->assertArrayNotHasKey('scope_closer', $tokenArray, 'Scope closer is set'); + $this->assertArrayNotHasKey('parenthesis_owner', $tokenArray, 'Parenthesis owner is set'); + $this->assertArrayNotHasKey('parenthesis_opener', $tokenArray, 'Parenthesis opener is set'); + $this->assertArrayNotHasKey('parenthesis_closer', $tokenArray, 'Parenthesis closer is set'); + + $next = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($token + 1), null, true); + if ($next !== false && $tokens[$next]['code'] === T_OPEN_PARENTHESIS) { + $this->assertArrayNotHasKey('parenthesis_owner', $tokenArray, 'Parenthesis owner is set for opener after'); + } + + }//end testNotAMatchStructure() + + + /** + * Data provider. + * + * @see testNotAMatchStructure() + * + * @return array + */ + public function dataNotAMatchStructure() + { + return [ + 'static_method_call' => ['/* testNoMatchStaticMethodCall */'], + 'class_constant_access' => [ + '/* testNoMatchClassConstantAccess */', + 'MATCH', + ], + 'class_constant_array_access' => [ + '/* testNoMatchClassConstantArrayAccessMixedCase */', + 'Match', + ], + 'method_call' => ['/* testNoMatchMethodCall */'], + 'method_call_uppercase' => [ + '/* testNoMatchMethodCallUpper */', + 'MATCH', + ], + 'property_access' => ['/* testNoMatchPropertyAccess */'], + 'namespaced_function_call' => ['/* testNoMatchNamespacedFunctionCall */'], + 'namespace_operator_function_call' => ['/* testNoMatchNamespaceOperatorFunctionCall */'], + 'interface_method_declaration' => ['/* testNoMatchInterfaceMethodDeclaration */'], + 'class_constant_declaration' => ['/* testNoMatchClassConstantDeclarationLower */'], + 'class_method_declaration' => ['/* testNoMatchClassMethodDeclaration */'], + 'property_assigment' => ['/* testNoMatchPropertyAssignment */'], + 'class_instantiation' => [ + '/* testNoMatchClassInstantiation */', + 'Match', + ], + 'anon_class_method_declaration' => [ + '/* testNoMatchAnonClassMethodDeclaration */', + 'maTCH', + ], + 'class_declaration' => [ + '/* testNoMatchClassDeclaration */', + 'Match', + ], + 'interface_declaration' => [ + '/* testNoMatchInterfaceDeclaration */', + 'Match', + ], + 'trait_declaration' => [ + '/* testNoMatchTraitDeclaration */', + 'Match', + ], + 'constant_declaration' => [ + '/* testNoMatchConstantDeclaration */', + 'MATCH', + ], + 'function_declaration' => ['/* testNoMatchFunctionDeclaration */'], + 'namespace_declaration' => [ + '/* testNoMatchNamespaceDeclaration */', + 'Match', + ], + 'class_extends_declaration' => [ + '/* testNoMatchExtendedClassDeclaration */', + 'Match', + ], + 'class_implements_declaration' => [ + '/* testNoMatchImplementedClassDeclaration */', + 'Match', + ], + 'use_statement' => [ + '/* testNoMatchInUseStatement */', + 'Match', + ], + 'unsupported_inline_control_structure' => ['/* testNoMatchMissingCurlies */'], + 'unsupported_alternative_syntax' => ['/* testNoMatchAlternativeSyntax */'], + 'live_coding' => ['/* testLiveCoding */'], + ]; + + }//end dataNotAMatchStructure() + + + /** + * Verify that the tokenization of switch structures is not affected by the backfill. + * + * @param string $testMarker The comment prefacing the target token. + * @param int $openerOffset The expected offset of the scope opener in relation to the testMarker. + * @param int $closerOffset The expected offset of the scope closer in relation to the testMarker. + * + * @dataProvider dataSwitchExpression + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testSwitchExpression($testMarker, $openerOffset, $closerOffset) + { + $token = $this->getTargetToken($testMarker, T_SWITCH); + + $this->scopeTestHelper($token, $openerOffset, $closerOffset); + $this->parenthesisTestHelper($token); + + }//end testSwitchExpression() + + + /** + * Data provider. + * + * @see testSwitchExpression() + * + * @return array + */ + public function dataSwitchExpression() + { + return [ + 'switch_containing_match' => [ + '/* testSwitchContainingMatch */', + 6, + 174, + ], + 'match_containing_switch_1' => [ + '/* testSwitchNestedInMatch1 */', + 5, + 63, + ], + 'match_containing_switch_2' => [ + '/* testSwitchNestedInMatch2 */', + 5, + 63, + ], + ]; + + }//end dataSwitchExpression() + + + /** + * Verify that the tokenization of a switch case/default structure containing a match structure + * or contained *in* a match structure is not affected by the backfill. + * + * @param string $testMarker The comment prefacing the target token. + * @param int $openerOffset The expected offset of the scope opener in relation to the testMarker. + * @param int $closerOffset The expected offset of the scope closer in relation to the testMarker. + * + * @dataProvider dataSwitchCaseVersusMatch + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testSwitchCaseVersusMatch($testMarker, $openerOffset, $closerOffset) + { + $token = $this->getTargetToken($testMarker, [T_CASE, T_DEFAULT]); + + $this->scopeTestHelper($token, $openerOffset, $closerOffset); + + }//end testSwitchCaseVersusMatch() + + + /** + * Data provider. + * + * @see testSwitchCaseVersusMatch() + * + * @return array + */ + public function dataSwitchCaseVersusMatch() + { + return [ + 'switch_with_nested_match_case_1' => [ + '/* testMatchWithDefaultNestedInSwitchCase1 */', + 3, + 55, + ], + 'switch_with_nested_match_case_2' => [ + '/* testMatchWithDefaultNestedInSwitchCase2 */', + 4, + 21, + ], + 'switch_with_nested_match_default_case' => [ + '/* testMatchWithDefaultNestedInSwitchDefault */', + 1, + 38, + ], + 'match_with_nested_switch_case' => [ + '/* testSwitchDefaultNestedInMatchCase */', + 1, + 18, + ], + 'match_with_nested_switch_default_case' => [ + '/* testSwitchDefaultNestedInMatchDefault */', + 1, + 20, + ], + ]; + + }//end dataSwitchCaseVersusMatch() + + + /** + * Helper function to verify that all scope related array indexes for a control structure + * are set correctly. + * + * @param string $token The control structure token to check. + * @param int $openerOffset The expected offset of the scope opener in relation to + * the control structure token. + * @param int $closerOffset The expected offset of the scope closer in relation to + * the control structure token. + * @param bool $skipScopeCloserCheck Whether to skip the scope closer check. + * This should be set to "true" when testing nested arrow functions, + * where the "inner" arrow function shares a scope closer with the + * "outer" arrow function, as the 'scope_condition' for the scope closer + * of the "inner" arrow function will point to the "outer" arrow function. + * + * @return void + */ + private function scopeTestHelper($token, $openerOffset, $closerOffset, $skipScopeCloserCheck=false) + { + $tokens = self::$phpcsFile->getTokens(); + $tokenArray = $tokens[$token]; + $tokenType = $tokenArray['type']; + $expectedScopeOpener = ($token + $openerOffset); + $expectedScopeCloser = ($token + $closerOffset); + + $this->assertArrayHasKey('scope_condition', $tokenArray, 'Scope condition is not set'); + $this->assertArrayHasKey('scope_opener', $tokenArray, 'Scope opener is not set'); + $this->assertArrayHasKey('scope_closer', $tokenArray, 'Scope closer is not set'); + $this->assertSame($token, $tokenArray['scope_condition'], 'Scope condition is not the '.$tokenType.' token'); + $this->assertSame($expectedScopeOpener, $tokenArray['scope_opener'], 'Scope opener of the '.$tokenType.' token incorrect'); + $this->assertSame($expectedScopeCloser, $tokenArray['scope_closer'], 'Scope closer of the '.$tokenType.' token incorrect'); + + $opener = $tokenArray['scope_opener']; + $this->assertArrayHasKey('scope_condition', $tokens[$opener], 'Opener scope condition is not set'); + $this->assertArrayHasKey('scope_opener', $tokens[$opener], 'Opener scope opener is not set'); + $this->assertArrayHasKey('scope_closer', $tokens[$opener], 'Opener scope closer is not set'); + $this->assertSame($token, $tokens[$opener]['scope_condition'], 'Opener scope condition is not the '.$tokenType.' token'); + $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], $tokenType.' opener scope opener token incorrect'); + $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], $tokenType.' opener scope closer token incorrect'); + + $closer = $tokenArray['scope_closer']; + $this->assertArrayHasKey('scope_condition', $tokens[$closer], 'Closer scope condition is not set'); + $this->assertArrayHasKey('scope_opener', $tokens[$closer], 'Closer scope opener is not set'); + $this->assertArrayHasKey('scope_closer', $tokens[$closer], 'Closer scope closer is not set'); + if ($skipScopeCloserCheck === false) { + $this->assertSame($token, $tokens[$closer]['scope_condition'], 'Closer scope condition is not the '.$tokenType.' token'); + } + + $this->assertSame($expectedScopeOpener, $tokens[$closer]['scope_opener'], $tokenType.' closer scope opener token incorrect'); + $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], $tokenType.' closer scope closer token incorrect'); + + if (($opener + 1) !== $closer) { + for ($i = ($opener + 1); $i < $closer; $i++) { + $this->assertArrayHasKey( + $token, + $tokens[$i]['conditions'], + $tokenType.' condition not added for token belonging to the '.$tokenType.' structure' + ); + } + } + + }//end scopeTestHelper() + + + /** + * Helper function to verify that all parenthesis related array indexes for a control structure + * token are set correctly. + * + * @param int $token The position of the control structure token. + * + * @return void + */ + private function parenthesisTestHelper($token) + { + $tokens = self::$phpcsFile->getTokens(); + $tokenArray = $tokens[$token]; + $tokenType = $tokenArray['type']; + + $this->assertArrayHasKey('parenthesis_owner', $tokenArray, 'Parenthesis owner is not set'); + $this->assertArrayHasKey('parenthesis_opener', $tokenArray, 'Parenthesis opener is not set'); + $this->assertArrayHasKey('parenthesis_closer', $tokenArray, 'Parenthesis closer is not set'); + $this->assertSame($token, $tokenArray['parenthesis_owner'], 'Parenthesis owner is not the '.$tokenType.' token'); + + $opener = $tokenArray['parenthesis_opener']; + $this->assertArrayHasKey('parenthesis_owner', $tokens[$opener], 'Opening parenthesis owner is not set'); + $this->assertSame($token, $tokens[$opener]['parenthesis_owner'], 'Opening parenthesis owner is not the '.$tokenType.' token'); + + $closer = $tokenArray['parenthesis_closer']; + $this->assertArrayHasKey('parenthesis_owner', $tokens[$closer], 'Closing parenthesis owner is not set'); + $this->assertSame($token, $tokens[$closer]['parenthesis_owner'], 'Closing parenthesis owner is not the '.$tokenType.' token'); + + }//end parenthesisTestHelper() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillNumericSeparatorTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillNumericSeparatorTest.inc new file mode 100644 index 00000000..d8559705 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillNumericSeparatorTest.inc @@ -0,0 +1,94 @@ + + * @copyright 2019 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class BackfillNumericSeparatorTest extends AbstractMethodUnitTest +{ + + + /** + * Test that numbers using numeric separators are tokenized correctly. + * + * @param array $testData The data required for the specific test case. + * + * @dataProvider dataTestBackfill + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testBackfill($testData) + { + $tokens = self::$phpcsFile->getTokens(); + $number = $this->getTargetToken($testData['marker'], [T_LNUMBER, T_DNUMBER]); + + $this->assertSame(constant($testData['type']), $tokens[$number]['code']); + $this->assertSame($testData['type'], $tokens[$number]['type']); + $this->assertSame($testData['value'], $tokens[$number]['content']); + + }//end testBackfill() + + + /** + * Data provider. + * + * @see testBackfill() + * + * @return array + */ + public function dataTestBackfill() + { + $testHexType = 'T_LNUMBER'; + if (PHP_INT_MAX < 0xCAFEF00D) { + $testHexType = 'T_DNUMBER'; + } + + $testHexMultipleType = 'T_LNUMBER'; + if (PHP_INT_MAX < 0x42726F776E) { + $testHexMultipleType = 'T_DNUMBER'; + } + + $testIntMoreThanMaxType = 'T_LNUMBER'; + if (PHP_INT_MAX < 10223372036854775807) { + $testIntMoreThanMaxType = 'T_DNUMBER'; + } + + return [ + [ + [ + 'marker' => '/* testSimpleLNumber */', + 'type' => 'T_LNUMBER', + 'value' => '1_000_000_000', + ], + ], + [ + [ + 'marker' => '/* testSimpleDNumber */', + 'type' => 'T_DNUMBER', + 'value' => '107_925_284.88', + ], + ], + [ + [ + 'marker' => '/* testFloat */', + 'type' => 'T_DNUMBER', + 'value' => '6.674_083e-11', + ], + ], + [ + [ + 'marker' => '/* testFloat2 */', + 'type' => 'T_DNUMBER', + 'value' => '6.674_083e+11', + ], + ], + [ + [ + 'marker' => '/* testFloat3 */', + 'type' => 'T_DNUMBER', + 'value' => '1_2.3_4e1_23', + ], + ], + [ + [ + 'marker' => '/* testHex */', + 'type' => $testHexType, + 'value' => '0xCAFE_F00D', + ], + ], + [ + [ + 'marker' => '/* testHexMultiple */', + 'type' => $testHexMultipleType, + 'value' => '0x42_72_6F_77_6E', + ], + ], + [ + [ + 'marker' => '/* testHexInt */', + 'type' => 'T_LNUMBER', + 'value' => '0x42_72_6F', + ], + ], + [ + [ + 'marker' => '/* testBinary */', + 'type' => 'T_LNUMBER', + 'value' => '0b0101_1111', + ], + ], + [ + [ + 'marker' => '/* testOctal */', + 'type' => 'T_LNUMBER', + 'value' => '0137_041', + ], + ], + [ + [ + 'marker' => '/* testExplicitOctal */', + 'type' => 'T_LNUMBER', + 'value' => '0o137_041', + ], + ], + [ + [ + 'marker' => '/* testExplicitOctalCapitalised */', + 'type' => 'T_LNUMBER', + 'value' => '0O137_041', + ], + ], + [ + [ + 'marker' => '/* testIntMoreThanMax */', + 'type' => $testIntMoreThanMaxType, + 'value' => '10_223_372_036_854_775_807', + ], + ], + ]; + + }//end dataTestBackfill() + + + /** + * Test that numbers using numeric separators which are considered parse errors and/or + * which aren't relevant to the backfill, do not incorrectly trigger the backfill anyway. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param array $expectedTokens The token type and content of the expected token sequence. + * + * @dataProvider dataNoBackfill + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testNoBackfill($testMarker, $expectedTokens) + { + $tokens = self::$phpcsFile->getTokens(); + $number = $this->getTargetToken($testMarker, [T_LNUMBER, T_DNUMBER]); + + foreach ($expectedTokens as $key => $expectedToken) { + $i = ($number + $key); + $this->assertSame($expectedToken['code'], $tokens[$i]['code']); + $this->assertSame($expectedToken['content'], $tokens[$i]['content']); + } + + }//end testNoBackfill() + + + /** + * Data provider. + * + * @see testBackfill() + * + * @return array + */ + public function dataNoBackfill() + { + return [ + [ + '/* testInvalid1 */', + [ + [ + 'code' => T_LNUMBER, + 'content' => '100', + ], + [ + 'code' => T_STRING, + 'content' => '_', + ], + ], + ], + [ + '/* testInvalid2 */', + [ + [ + 'code' => T_LNUMBER, + 'content' => '1', + ], + [ + 'code' => T_STRING, + 'content' => '__1', + ], + ], + ], + [ + '/* testInvalid3 */', + [ + [ + 'code' => T_LNUMBER, + 'content' => '1', + ], + [ + 'code' => T_STRING, + 'content' => '_', + ], + [ + 'code' => T_DNUMBER, + 'content' => '.0', + ], + ], + ], + [ + '/* testInvalid4 */', + [ + [ + 'code' => T_DNUMBER, + 'content' => '1.', + ], + [ + 'code' => T_STRING, + 'content' => '_0', + ], + ], + ], + [ + '/* testInvalid5 */', + [ + [ + 'code' => T_LNUMBER, + 'content' => '0', + ], + [ + 'code' => T_STRING, + 'content' => 'x_123', + ], + ], + ], + [ + '/* testInvalid6 */', + [ + [ + 'code' => T_LNUMBER, + 'content' => '0', + ], + [ + 'code' => T_STRING, + 'content' => 'b_101', + ], + ], + ], + [ + '/* testInvalid7 */', + [ + [ + 'code' => T_LNUMBER, + 'content' => '1', + ], + [ + 'code' => T_STRING, + 'content' => '_e2', + ], + ], + ], + [ + '/* testInvalid8 */', + [ + [ + 'code' => T_LNUMBER, + 'content' => '1', + ], + [ + 'code' => T_STRING, + 'content' => 'e_2', + ], + ], + ], + [ + '/* testInvalid9 */', + [ + [ + 'code' => T_LNUMBER, + 'content' => '107_925_284', + ], + [ + 'code' => T_WHITESPACE, + 'content' => ' ', + ], + [ + 'code' => T_DNUMBER, + 'content' => '.88', + ], + ], + ], + [ + '/* testInvalid10 */', + [ + [ + 'code' => T_LNUMBER, + 'content' => '107_925_284', + ], + [ + 'code' => T_COMMENT, + 'content' => '/*comment*/', + ], + [ + 'code' => T_DNUMBER, + 'content' => '.88', + ], + ], + ], + [ + '/* testInvalid11 */', + [ + [ + 'code' => T_LNUMBER, + 'content' => '0', + ], + [ + 'code' => T_STRING, + 'content' => 'o_137', + ], + ], + ], + [ + '/* testInvalid12 */', + [ + [ + 'code' => T_LNUMBER, + 'content' => '0', + ], + [ + 'code' => T_STRING, + 'content' => 'O_41', + ], + ], + ], + [ + '/* testCalc1 */', + [ + [ + 'code' => T_LNUMBER, + 'content' => '667_083', + ], + [ + 'code' => T_WHITESPACE, + 'content' => ' ', + ], + [ + 'code' => T_MINUS, + 'content' => '-', + ], + [ + 'code' => T_WHITESPACE, + 'content' => ' ', + ], + [ + 'code' => T_LNUMBER, + 'content' => '11', + ], + ], + ], + [ + '/* test Calc2 */', + [ + [ + 'code' => T_DNUMBER, + 'content' => '6.674_08e3', + ], + [ + 'code' => T_WHITESPACE, + 'content' => ' ', + ], + [ + 'code' => T_PLUS, + 'content' => '+', + ], + [ + 'code' => T_WHITESPACE, + 'content' => ' ', + ], + [ + 'code' => T_LNUMBER, + 'content' => '11', + ], + ], + ], + ]; + + }//end dataNoBackfill() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillReadonlyTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillReadonlyTest.inc new file mode 100644 index 00000000..eaf0b4b3 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillReadonlyTest.inc @@ -0,0 +1,100 @@ +readonly = 'foo'; + + /* testReadonlyPropertyInTernaryOperator */ + $isReadonly = $this->readonly ? true : false; + } +} + +/* testReadonlyUsedAsFunctionName */ +function readonly() +{ +} + +/* testReadonlyUsedAsNamespaceName */ +namespace Readonly; +/* testReadonlyUsedAsPartOfNamespaceName */ +namespace My\Readonly\Collection; +/* testReadonlyAsFunctionCall */ +$var = readonly($a, $b); +/* testClassConstantFetchWithReadonlyAsConstantName */ +echo ClassName::READONLY; + +/* testReadonlyUsedAsFunctionCallWithSpaceBetweenKeywordAndParens */ +$var = readonly /* comment */ (); + +/* testParseErrorLiveCoding */ +// This must be the last test in the file. +readonly diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillReadonlyTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillReadonlyTest.php new file mode 100644 index 00000000..dddc18eb --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillReadonlyTest.php @@ -0,0 +1,236 @@ + + * @copyright 2021 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class BackfillReadonlyTest extends AbstractMethodUnitTest +{ + + + /** + * Test that the "readonly" keyword is tokenized as such. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param string $testContent The token content to look for. + * + * @dataProvider dataReadonly + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testReadonly($testMarker, $testContent) + { + $tokens = self::$phpcsFile->getTokens(); + + $target = $this->getTargetToken($testMarker, [T_READONLY, T_STRING], $testContent); + $this->assertSame(T_READONLY, $tokens[$target]['code']); + $this->assertSame('T_READONLY', $tokens[$target]['type']); + + }//end testReadonly() + + + /** + * Data provider. + * + * @see testReadonly() + * + * @return array + */ + public function dataReadonly() + { + return [ + [ + '/* testReadonlyProperty */', + 'readonly', + ], + [ + '/* testVarReadonlyProperty */', + 'readonly', + ], + [ + '/* testReadonlyVarProperty */', + 'readonly', + ], + [ + '/* testStaticReadonlyProperty */', + 'readonly', + ], + [ + '/* testReadonlyStaticProperty */', + 'readonly', + ], + [ + '/* testConstReadonlyProperty */', + 'readonly', + ], + [ + '/* testReadonlyPropertyWithoutType */', + 'readonly', + ], + [ + '/* testPublicReadonlyProperty */', + 'readonly', + ], + [ + '/* testProtectedReadonlyProperty */', + 'readonly', + ], + [ + '/* testPrivateReadonlyProperty */', + 'readonly', + ], + [ + '/* testPublicReadonlyPropertyWithReadonlyFirst */', + 'readonly', + ], + [ + '/* testProtectedReadonlyPropertyWithReadonlyFirst */', + 'readonly', + ], + [ + '/* testPrivateReadonlyPropertyWithReadonlyFirst */', + 'readonly', + ], + [ + '/* testReadonlyWithCommentsInDeclaration */', + 'readonly', + ], + [ + '/* testReadonlyWithNullableProperty */', + 'readonly', + ], + [ + '/* testReadonlyNullablePropertyWithUnionTypeHintAndNullFirst */', + 'readonly', + ], + [ + '/* testReadonlyNullablePropertyWithUnionTypeHintAndNullLast */', + 'readonly', + ], + [ + '/* testReadonlyPropertyWithArrayTypeHint */', + 'readonly', + ], + [ + '/* testReadonlyPropertyWithSelfTypeHint */', + 'readonly', + ], + [ + '/* testReadonlyPropertyWithParentTypeHint */', + 'readonly', + ], + [ + '/* testReadonlyPropertyWithFullyQualifiedTypeHint */', + 'readonly', + ], + [ + '/* testReadonlyIsCaseInsensitive */', + 'ReAdOnLy', + ], + [ + '/* testReadonlyConstructorPropertyPromotion */', + 'readonly', + ], + [ + '/* testReadonlyConstructorPropertyPromotionWithReference */', + 'ReadOnly', + ], + [ + '/* testReadonlyPropertyInAnonymousClass */', + 'readonly', + ], + [ + '/* testReadonlyUsedAsFunctionCallWithSpaceBetweenKeywordAndParens */', + 'readonly', + ], + [ + '/* testParseErrorLiveCoding */', + 'readonly', + ], + ]; + + }//end dataReadonly() + + + /** + * Test that "readonly" when not used as the keyword is still tokenized as `T_STRING`. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param string $testContent The token content to look for. + * + * @dataProvider dataNotReadonly + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testNotReadonly($testMarker, $testContent) + { + $tokens = self::$phpcsFile->getTokens(); + + $target = $this->getTargetToken($testMarker, [T_READONLY, T_STRING], $testContent); + $this->assertSame(T_STRING, $tokens[$target]['code']); + $this->assertSame('T_STRING', $tokens[$target]['type']); + + }//end testNotReadonly() + + + /** + * Data provider. + * + * @see testNotReadonly() + * + * @return array + */ + public function dataNotReadonly() + { + return [ + [ + '/* testReadonlyUsedAsClassConstantName */', + 'READONLY', + ], + [ + '/* testReadonlyUsedAsMethodName */', + 'readonly', + ], + [ + '/* testReadonlyUsedAsPropertyName */', + 'readonly', + ], + [ + '/* testReadonlyPropertyInTernaryOperator */', + 'readonly', + ], + [ + '/* testReadonlyUsedAsFunctionName */', + 'readonly', + ], + [ + '/* testReadonlyUsedAsNamespaceName */', + 'Readonly', + ], + [ + '/* testReadonlyUsedAsPartOfNamespaceName */', + 'Readonly', + ], + [ + '/* testReadonlyAsFunctionCall */', + 'readonly', + ], + [ + '/* testClassConstantFetchWithReadonlyAsConstantName */', + 'READONLY', + ], + ]; + + }//end dataNotReadonly() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BitwiseOrTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BitwiseOrTest.inc new file mode 100644 index 00000000..bfdbdc18 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BitwiseOrTest.inc @@ -0,0 +1,134 @@ + $param | $int; + +/* testTypeUnionArrowReturnType */ +$arrowWithReturnType = fn ($param) : int|null => $param * 10; + +/* testBitwiseOrInArrayKey */ +$array = array( + A | B => /* testBitwiseOrInArrayValue */ B | C +); + +/* testBitwiseOrInShortArrayKey */ +$array = [ + A | B => /* testBitwiseOrInShortArrayValue */ B | C +]; + +/* testBitwiseOrTryCatch */ +try { +} catch ( ExceptionA | ExceptionB $e ) { +} + +/* testBitwiseOrNonArrowFnFunctionCall */ +$obj->fn($something | $else); + +/* testTypeUnionNonArrowFunctionDeclaration */ +function &fn(int|false $something) {} + +/* testLiveCoding */ +// Intentional parse error. This has to be the last test in the file. +return function( type| diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BitwiseOrTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BitwiseOrTest.php new file mode 100644 index 00000000..d56e7340 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BitwiseOrTest.php @@ -0,0 +1,138 @@ + + * @copyright 2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class BitwiseOrTest extends AbstractMethodUnitTest +{ + + + /** + * Test that non-union type bitwise or tokens are still tokenized as bitwise or. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataBitwiseOr + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testBitwiseOr($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $opener = $this->getTargetToken($testMarker, [T_BITWISE_OR, T_TYPE_UNION]); + $this->assertSame(T_BITWISE_OR, $tokens[$opener]['code']); + $this->assertSame('T_BITWISE_OR', $tokens[$opener]['type']); + + }//end testBitwiseOr() + + + /** + * Data provider. + * + * @see testBitwiseOr() + * + * @return array + */ + public function dataBitwiseOr() + { + return [ + ['/* testBitwiseOr1 */'], + ['/* testBitwiseOr2 */'], + ['/* testBitwiseOrPropertyDefaultValue */'], + ['/* testBitwiseOrParamDefaultValue */'], + ['/* testBitwiseOr3 */'], + ['/* testBitwiseOrClosureParamDefault */'], + ['/* testBitwiseOrArrowParamDefault */'], + ['/* testBitwiseOrArrowExpression */'], + ['/* testBitwiseOrInArrayKey */'], + ['/* testBitwiseOrInArrayValue */'], + ['/* testBitwiseOrInShortArrayKey */'], + ['/* testBitwiseOrInShortArrayValue */'], + ['/* testBitwiseOrTryCatch */'], + ['/* testBitwiseOrNonArrowFnFunctionCall */'], + ['/* testLiveCoding */'], + ]; + + }//end dataBitwiseOr() + + + /** + * Test that bitwise or tokens when used as part of a union type are tokenized as `T_TYPE_UNION`. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataTypeUnion + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testTypeUnion($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $opener = $this->getTargetToken($testMarker, [T_BITWISE_OR, T_TYPE_UNION]); + $this->assertSame(T_TYPE_UNION, $tokens[$opener]['code']); + $this->assertSame('T_TYPE_UNION', $tokens[$opener]['type']); + + }//end testTypeUnion() + + + /** + * Data provider. + * + * @see testTypeUnion() + * + * @return array + */ + public function dataTypeUnion() + { + return [ + ['/* testTypeUnionPropertySimple */'], + ['/* testTypeUnionPropertyReverseModifierOrder */'], + ['/* testTypeUnionPropertyMulti1 */'], + ['/* testTypeUnionPropertyMulti2 */'], + ['/* testTypeUnionPropertyMulti3 */'], + ['/* testTypeUnionPropertyNamespaceRelative */'], + ['/* testTypeUnionPropertyPartiallyQualified */'], + ['/* testTypeUnionPropertyFullyQualified */'], + ['/* testTypeUnionPropertyWithReadOnlyKeyword */'], + ['/* testTypeUnionPropertyWithReadOnlyKeywordFirst */'], + ['/* testTypeUnionPropertyWithStaticAndReadOnlyKeywords */'], + ['/* testTypeUnionPropertyWithVarAndReadOnlyKeywords */'], + ['/* testTypeUnionPropertyWithOnlyReadOnlyKeyword */'], + ['/* testTypeUnionParam1 */'], + ['/* testTypeUnionParam2 */'], + ['/* testTypeUnionParam3 */'], + ['/* testTypeUnionParamNamespaceRelative */'], + ['/* testTypeUnionParamPartiallyQualified */'], + ['/* testTypeUnionParamFullyQualified */'], + ['/* testTypeUnionReturnType */'], + ['/* testTypeUnionConstructorPropertyPromotion */'], + ['/* testTypeUnionAbstractMethodReturnType1 */'], + ['/* testTypeUnionAbstractMethodReturnType2 */'], + ['/* testTypeUnionReturnTypeNamespaceRelative */'], + ['/* testTypeUnionReturnPartiallyQualified */'], + ['/* testTypeUnionReturnFullyQualified */'], + ['/* testTypeUnionClosureParamIllegalNullable */'], + ['/* testTypeUnionWithReference */'], + ['/* testTypeUnionWithSpreadOperator */'], + ['/* testTypeUnionClosureReturn */'], + ['/* testTypeUnionArrowParam */'], + ['/* testTypeUnionArrowReturnType */'], + ['/* testTypeUnionNonArrowFunctionDeclaration */'], + ]; + + }//end dataTypeUnion() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.inc new file mode 100644 index 00000000..82fe5643 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.inc @@ -0,0 +1,227 @@ + 'a', + 2 => 'b', + /* testMatchDefaultIsKeyword */ default => 'default', +}; + +$closure = /* testFnIsKeyword */ fn () => 'string'; + +function () { + /* testYieldIsKeyword */ yield $f; + /* testYieldFromIsKeyword */ yield from someFunction(); +}; + +/* testDeclareIsKeyword */ declare(ticks=1): +/* testEndDeclareIsKeyword */ enddeclare; + +if (true /* testAndIsKeyword */ and false /* testOrIsKeyword */ or null /* testXorIsKeyword */ xor 0) { + +} + +$anonymousClass = new /* testAnonymousClassIsKeyword */ class {}; +$anonymousClass2 = new class /* testExtendsInAnonymousClassIsKeyword */ extends SomeParent {}; +$anonymousClass3 = new class /* testImplementsInAnonymousClassIsKeyword */ implements SomeInterface {}; + +$instantiated1 = new /* testClassInstantiationParentIsKeyword */ parent(); +$instantiated2 = new /* testClassInstantiationSelfIsKeyword */ SELF; +$instantiated3 = new /* testClassInstantiationStaticIsKeyword */ static($param); + +class Foo extends /* testNamespaceInNameIsKeyword */ namespace\Exception +{} + +function /* testKeywordAfterFunctionShouldBeString */ eval() {} +function /* testKeywordAfterFunctionByRefShouldBeString */ &switch() {} diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.php new file mode 100644 index 00000000..a747e573 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.php @@ -0,0 +1,509 @@ + + * @copyright 2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; +use PHP_CodeSniffer\Util\Tokens; + +class ContextSensitiveKeywordsTest extends AbstractMethodUnitTest +{ + + + /** + * Test that context sensitive keyword is tokenized as string when it should be string. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataStrings + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testStrings($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, (Tokens::$contextSensitiveKeywords + [T_STRING])); + + $this->assertSame(T_STRING, $tokens[$token]['code']); + $this->assertSame('T_STRING', $tokens[$token]['type']); + + }//end testStrings() + + + /** + * Data provider. + * + * @see testStrings() + * + * @return array + */ + public function dataStrings() + { + return [ + ['/* testAbstract */'], + ['/* testArray */'], + ['/* testAs */'], + ['/* testBreak */'], + ['/* testCallable */'], + ['/* testCase */'], + ['/* testCatch */'], + ['/* testClass */'], + ['/* testClone */'], + ['/* testConst */'], + ['/* testContinue */'], + ['/* testDeclare */'], + ['/* testDefault */'], + ['/* testDo */'], + ['/* testEcho */'], + ['/* testElse */'], + ['/* testElseIf */'], + ['/* testEmpty */'], + ['/* testEndDeclare */'], + ['/* testEndFor */'], + ['/* testEndForeach */'], + ['/* testEndIf */'], + ['/* testEndSwitch */'], + ['/* testEndWhile */'], + ['/* testEnum */'], + ['/* testEval */'], + ['/* testExit */'], + ['/* testExtends */'], + ['/* testFinal */'], + ['/* testFinally */'], + ['/* testFn */'], + ['/* testFor */'], + ['/* testForeach */'], + ['/* testFunction */'], + ['/* testGlobal */'], + ['/* testGoto */'], + ['/* testIf */'], + ['/* testImplements */'], + ['/* testInclude */'], + ['/* testIncludeOnce */'], + ['/* testInstanceOf */'], + ['/* testInsteadOf */'], + ['/* testInterface */'], + ['/* testIsset */'], + ['/* testList */'], + ['/* testMatch */'], + ['/* testNamespace */'], + ['/* testNew */'], + ['/* testParent */'], + ['/* testPrint */'], + ['/* testPrivate */'], + ['/* testProtected */'], + ['/* testPublic */'], + ['/* testReadonly */'], + ['/* testRequire */'], + ['/* testRequireOnce */'], + ['/* testReturn */'], + ['/* testSelf */'], + ['/* testStatic */'], + ['/* testSwitch */'], + ['/* testThrows */'], + ['/* testTrait */'], + ['/* testTry */'], + ['/* testUnset */'], + ['/* testUse */'], + ['/* testVar */'], + ['/* testWhile */'], + ['/* testYield */'], + ['/* testYieldFrom */'], + ['/* testAnd */'], + ['/* testOr */'], + ['/* testXor */'], + + ['/* testKeywordAfterNamespaceShouldBeString */'], + ['/* testNamespaceNameIsString1 */'], + ['/* testNamespaceNameIsString2 */'], + ['/* testNamespaceNameIsString3 */'], + + ['/* testKeywordAfterFunctionShouldBeString */'], + ['/* testKeywordAfterFunctionByRefShouldBeString */'], + ]; + + }//end dataStrings() + + + /** + * Test that context sensitive keyword is tokenized as keyword when it should be keyword. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param string $expectedTokenType The expected token type. + * + * @dataProvider dataKeywords + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testKeywords($testMarker, $expectedTokenType) + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, (Tokens::$contextSensitiveKeywords + [T_ANON_CLASS, T_MATCH_DEFAULT, T_PARENT, T_SELF, T_STRING])); + + $this->assertSame(constant($expectedTokenType), $tokens[$token]['code']); + $this->assertSame($expectedTokenType, $tokens[$token]['type']); + + }//end testKeywords() + + + /** + * Data provider. + * + * @see testKeywords() + * + * @return array + */ + public function dataKeywords() + { + return [ + [ + '/* testNamespaceIsKeyword */', + 'T_NAMESPACE', + ], + [ + '/* testAbstractIsKeyword */', + 'T_ABSTRACT', + ], + [ + '/* testClassIsKeyword */', + 'T_CLASS', + ], + [ + '/* testExtendsIsKeyword */', + 'T_EXTENDS', + ], + [ + '/* testImplementsIsKeyword */', + 'T_IMPLEMENTS', + ], + [ + '/* testUseIsKeyword */', + 'T_USE', + ], + [ + '/* testInsteadOfIsKeyword */', + 'T_INSTEADOF', + ], + [ + '/* testAsIsKeyword */', + 'T_AS', + ], + [ + '/* testConstIsKeyword */', + 'T_CONST', + ], + [ + '/* testPrivateIsKeyword */', + 'T_PRIVATE', + ], + [ + '/* testProtectedIsKeyword */', + 'T_PROTECTED', + ], + [ + '/* testPublicIsKeyword */', + 'T_PUBLIC', + ], + [ + '/* testVarIsKeyword */', + 'T_VAR', + ], + [ + '/* testStaticIsKeyword */', + 'T_STATIC', + ], + [ + '/* testReadonlyIsKeyword */', + 'T_READONLY', + ], + [ + '/* testFinalIsKeyword */', + 'T_FINAL', + ], + [ + '/* testFunctionIsKeyword */', + 'T_FUNCTION', + ], + [ + '/* testCallableIsKeyword */', + 'T_CALLABLE', + ], + [ + '/* testSelfIsKeyword */', + 'T_SELF', + ], + [ + '/* testParentIsKeyword */', + 'T_PARENT', + ], + [ + '/* testReturnIsKeyword */', + 'T_RETURN', + ], + + [ + '/* testInterfaceIsKeyword */', + 'T_INTERFACE', + ], + [ + '/* testTraitIsKeyword */', + 'T_TRAIT', + ], + [ + '/* testEnumIsKeyword */', + 'T_ENUM', + ], + + [ + '/* testNewIsKeyword */', + 'T_NEW', + ], + [ + '/* testInstanceOfIsKeyword */', + 'T_INSTANCEOF', + ], + [ + '/* testCloneIsKeyword */', + 'T_CLONE', + ], + + [ + '/* testIfIsKeyword */', + 'T_IF', + ], + [ + '/* testEmptyIsKeyword */', + 'T_EMPTY', + ], + [ + '/* testElseIfIsKeyword */', + 'T_ELSEIF', + ], + [ + '/* testElseIsKeyword */', + 'T_ELSE', + ], + [ + '/* testEndIfIsKeyword */', + 'T_ENDIF', + ], + + [ + '/* testForIsKeyword */', + 'T_FOR', + ], + [ + '/* testEndForIsKeyword */', + 'T_ENDFOR', + ], + + [ + '/* testForeachIsKeyword */', + 'T_FOREACH', + ], + [ + '/* testEndForeachIsKeyword */', + 'T_ENDFOREACH', + ], + + [ + '/* testSwitchIsKeyword */', + 'T_SWITCH', + ], + [ + '/* testCaseIsKeyword */', + 'T_CASE', + ], + [ + '/* testDefaultIsKeyword */', + 'T_DEFAULT', + ], + [ + '/* testEndSwitchIsKeyword */', + 'T_ENDSWITCH', + ], + [ + '/* testBreakIsKeyword */', + 'T_BREAK', + ], + [ + '/* testContinueIsKeyword */', + 'T_CONTINUE', + ], + + [ + '/* testDoIsKeyword */', + 'T_DO', + ], + [ + '/* testWhileIsKeyword */', + 'T_WHILE', + ], + [ + '/* testEndWhileIsKeyword */', + 'T_ENDWHILE', + ], + + [ + '/* testTryIsKeyword */', + 'T_TRY', + ], + [ + '/* testThrowIsKeyword */', + 'T_THROW', + ], + [ + '/* testCatchIsKeyword */', + 'T_CATCH', + ], + [ + '/* testFinallyIsKeyword */', + 'T_FINALLY', + ], + + [ + '/* testGlobalIsKeyword */', + 'T_GLOBAL', + ], + [ + '/* testEchoIsKeyword */', + 'T_ECHO', + ], + [ + '/* testPrintIsKeyword */', + 'T_PRINT', + ], + [ + '/* testDieIsKeyword */', + 'T_EXIT', + ], + [ + '/* testEvalIsKeyword */', + 'T_EVAL', + ], + [ + '/* testExitIsKeyword */', + 'T_EXIT', + ], + [ + '/* testIssetIsKeyword */', + 'T_ISSET', + ], + [ + '/* testUnsetIsKeyword */', + 'T_UNSET', + ], + + [ + '/* testIncludeIsKeyword */', + 'T_INCLUDE', + ], + [ + '/* testIncludeOnceIsKeyword */', + 'T_INCLUDE_ONCE', + ], + [ + '/* testRequireIsKeyword */', + 'T_REQUIRE', + ], + [ + '/* testRequireOnceIsKeyword */', + 'T_REQUIRE_ONCE', + ], + + [ + '/* testListIsKeyword */', + 'T_LIST', + ], + [ + '/* testGotoIsKeyword */', + 'T_GOTO', + ], + [ + '/* testMatchIsKeyword */', + 'T_MATCH', + ], + [ + '/* testMatchDefaultIsKeyword */', + 'T_MATCH_DEFAULT', + ], + [ + '/* testFnIsKeyword */', + 'T_FN', + ], + + [ + '/* testYieldIsKeyword */', + 'T_YIELD', + ], + [ + '/* testYieldFromIsKeyword */', + 'T_YIELD_FROM', + ], + + [ + '/* testDeclareIsKeyword */', + 'T_DECLARE', + ], + [ + '/* testEndDeclareIsKeyword */', + 'T_ENDDECLARE', + ], + + [ + '/* testAndIsKeyword */', + 'T_LOGICAL_AND', + ], + [ + '/* testOrIsKeyword */', + 'T_LOGICAL_OR', + ], + [ + '/* testXorIsKeyword */', + 'T_LOGICAL_XOR', + ], + + [ + '/* testAnonymousClassIsKeyword */', + 'T_ANON_CLASS', + ], + [ + '/* testExtendsInAnonymousClassIsKeyword */', + 'T_EXTENDS', + ], + [ + '/* testImplementsInAnonymousClassIsKeyword */', + 'T_IMPLEMENTS', + ], + [ + '/* testClassInstantiationParentIsKeyword */', + 'T_PARENT', + ], + [ + '/* testClassInstantiationSelfIsKeyword */', + 'T_SELF', + ], + [ + '/* testClassInstantiationStaticIsKeyword */', + 'T_STATIC', + ], + [ + '/* testNamespaceInNameIsKeyword */', + 'T_NAMESPACE', + ], + ]; + + }//end dataKeywords() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DefaultKeywordTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DefaultKeywordTest.inc new file mode 100644 index 00000000..648149d2 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DefaultKeywordTest.inc @@ -0,0 +1,203 @@ + 1, + 2 => 2, + /* testSimpleMatchDefault */ + default => 'default', + }; +} + +function switchWithDefault($i) { + switch ($i) { + case 1: + return 1; + case 2: + return 2; + /* testSimpleSwitchDefault */ + default: + return 'default'; + } +} + +function switchWithDefaultAndCurlies($i) { + switch ($i) { + case 1: + return 1; + case 2: + return 2; + /* testSimpleSwitchDefaultWithCurlies */ + default: { + return 'default'; + } + } +} + +function matchWithDefaultInSwitch() { + switch ($something) { + case 'foo': + $var = [1, 2, 3]; + $var = match ($i) { + 1 => 1, + /* testMatchDefaultNestedInSwitchCase1 */ + default => 'default', + }; + continue; + + case 'bar' : + $i = callMe($a, $b); + return match ($i) { + 1 => 1, + /* testMatchDefaultNestedInSwitchCase2 */ + default => 'default', + }; + + /* testSwitchDefault */ + default; + echo 'something', match ($i) { + 1, => 1, + /* testMatchDefaultNestedInSwitchDefault */ + default, => 'default', + }; + break; + } +} + +function switchWithDefaultInMatch() { + $x = match ($y) { + 5, 8 => function($z) { + switch($z) { + case 'a'; + $var = [1, 2, 3]; + return 'a'; + /* testSwitchDefaultNestedInMatchCase */ + default: + $var = [1, 2, 3]; + return 'default1'; + } + }, + /* testMatchDefault */ + default => function($z) { + switch($z) { + case 'a': + $i = callMe($a, $b); + return 'b'; + /* testSwitchDefaultNestedInMatchDefault */ + default: + $i = callMe($a, $b); + return 'default2'; + } + } + }; +} + +function shortArrayWithConstantKey() { + $arr = [ + /* testClassConstantAsShortArrayKey */ + SomeClass::DEFAULT => 1, + /* testClassPropertyAsShortArrayKey */ + SomeClass->DEFAULT => 1, + /* testNamespacedConstantAsShortArrayKey */ + // Intentional parse error PHP < 8.0. Reserved keyword used as namespaced constant. + SomeNamespace\DEFAULT => 1, + /* testFQNGlobalConstantAsShortArrayKey */ + // Intentional parse error in PHP < 8.0. Reserved keyword used as global constant. + \DEFAULT => 1, + ]; +} + +function longArrayWithConstantKey() { + $arr = array( + /* testClassConstantAsLongArrayKey */ + SomeClass::DEFAULT => 1, + ); +} + +function yieldWithConstantKey() { + /* testClassConstantAsYieldKey */ + yield SomeClass::DEFAULT => 1; +} + +function longArrayWithConstantKeyNestedInMatch() { + return match($x) { + /* testMatchDefaultWithNestedLongArrayWithClassConstantKey */ + DEFAULT => array( + /* testClassConstantAsLongArrayKeyNestedInMatch */ + SomeClass::DEFAULT => match($x) { + /* testMatchDefaultWithNestedLongArrayWithClassConstantKeyLevelDown */ + DEFAULT => array( + /* testClassConstantAsLongArrayKeyNestedInMatchLevelDown */ + SomeClass::DEFAULT => 1, + ), + }, + ), + }; +} + +function shortArrayWithConstantKeyNestedInMatch() { + return match($x) { + /* testMatchDefaultWithNestedShortArrayWithClassConstantKey */ + DEFAULT => [ + /* testClassConstantAsShortArrayKeyNestedInMatch */ + SomeClass::DEFAULT => match($x) { + /* testMatchDefaultWithNestedShortArrayWithClassConstantKeyLevelDown */ + DEFAULT => [ + /* testClassConstantAsShortArrayKeyNestedInMatchLevelDown */ + SomeClass::DEFAULT => 1, + ], + }, + ], + }; +} + + +function longArrayWithConstantKeyWithNestedMatch() { + return array( + /* testClassConstantAsLongArrayKeyWithNestedMatch */ + SomeClass::DEFAULT => match($x) { + /* testMatchDefaultNestedInLongArray */ + DEFAULT => 'foo' + }, + ); +} + +function shortArrayWithConstantKeyWithNestedMatch() { + return [ + /* testClassConstantAsShortArrayKeyWithNestedMatch */ + SomeClass::DEFAULT => match($x) { + /* testMatchDefaultNestedInShortArray */ + DEFAULT => 'foo' + }, + ]; +} + +function switchWithConstantNonDefault($i) { + switch ($i) { + /* testClassConstantInSwitchCase */ + case SomeClass::DEFAULT: + return 1; + + /* testClassPropertyInSwitchCase */ + case SomeClass->DEFAULT: + return 2; + + /* testNamespacedConstantInSwitchCase */ + // Intentional parse error PHP < 8.0. Reserved keyword used as constant. + case SomeNamespace\DEFAULT: + return 2; + + /* testNamespaceRelativeConstantInSwitchCase */ + // Intentional parse error PHP < 8.0. Reserved keyword used as global constant. + case namespace\DEFAULT: + return 2; + } +} + +class Foo { + /* testClassConstant */ + const DEFAULT = 'foo'; + + /* testMethodDeclaration */ + public function default() {} +} diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DefaultKeywordTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DefaultKeywordTest.php new file mode 100644 index 00000000..9a5b061a --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DefaultKeywordTest.php @@ -0,0 +1,302 @@ + + * @copyright 2020-2021 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class DefaultKeywordTest extends AbstractMethodUnitTest +{ + + + /** + * Test the retokenization of the `default` keyword for match structure to `T_MATCH_DEFAULT`. + * + * Note: Cases and default structures within a match structure do *NOT* get case/default scope + * conditions, in contrast to case and default structures in switch control structures. + * + * @param string $testMarker The comment prefacing the target token. + * @param string $testContent The token content to look for. + * + * @dataProvider dataMatchDefault + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::recurseScopeMap + * + * @return void + */ + public function testMatchDefault($testMarker, $testContent='default') + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, [T_MATCH_DEFAULT, T_DEFAULT, T_STRING], $testContent); + $tokenArray = $tokens[$token]; + + $this->assertSame(T_MATCH_DEFAULT, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_MATCH_DEFAULT (code)'); + $this->assertSame('T_MATCH_DEFAULT', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_MATCH_DEFAULT (type)'); + + $this->assertArrayNotHasKey('scope_condition', $tokenArray, 'Scope condition is set'); + $this->assertArrayNotHasKey('scope_opener', $tokenArray, 'Scope opener is set'); + $this->assertArrayNotHasKey('scope_closer', $tokenArray, 'Scope closer is set'); + + }//end testMatchDefault() + + + /** + * Data provider. + * + * @see testMatchDefault() + * + * @return array + */ + public function dataMatchDefault() + { + return [ + 'simple_match_default' => ['/* testSimpleMatchDefault */'], + 'match_default_in_switch_case_1' => ['/* testMatchDefaultNestedInSwitchCase1 */'], + 'match_default_in_switch_case_2' => ['/* testMatchDefaultNestedInSwitchCase2 */'], + 'match_default_in_switch_default' => ['/* testMatchDefaultNestedInSwitchDefault */'], + 'match_default_containing_switch' => ['/* testMatchDefault */'], + + 'match_default_with_nested_long_array_and_default_key' => [ + '/* testMatchDefaultWithNestedLongArrayWithClassConstantKey */', + 'DEFAULT', + ], + 'match_default_with_nested_long_array_and_default_key_2' => [ + '/* testMatchDefaultWithNestedLongArrayWithClassConstantKeyLevelDown */', + 'DEFAULT', + ], + 'match_default_with_nested_short_array_and_default_key' => [ + '/* testMatchDefaultWithNestedShortArrayWithClassConstantKey */', + 'DEFAULT', + ], + 'match_default_with_nested_short_array_and_default_key_2' => [ + '/* testMatchDefaultWithNestedShortArrayWithClassConstantKeyLevelDown */', + 'DEFAULT', + ], + 'match_default_in_long_array' => [ + '/* testMatchDefaultNestedInLongArray */', + 'DEFAULT', + ], + 'match_default_in_short_array' => [ + '/* testMatchDefaultNestedInShortArray */', + 'DEFAULT', + ], + ]; + + }//end dataMatchDefault() + + + /** + * Verify that the retokenization of `T_DEFAULT` tokens in match constructs, doesn't negatively + * impact the tokenization of `T_DEFAULT` tokens in switch control structures. + * + * Note: Cases and default structures within a switch control structure *do* get case/default scope + * conditions. + * + * @param string $testMarker The comment prefacing the target token. + * @param int $openerOffset The expected offset of the scope opener in relation to the testMarker. + * @param int $closerOffset The expected offset of the scope closer in relation to the testMarker. + * @param int|null $conditionStop The expected offset at which tokens stop having T_DEFAULT as a scope condition. + * @param string $testContent The token content to look for. + * + * @dataProvider dataSwitchDefault + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::recurseScopeMap + * + * @return void + */ + public function testSwitchDefault($testMarker, $openerOffset, $closerOffset, $conditionStop=null, $testContent='default') + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, [T_MATCH_DEFAULT, T_DEFAULT, T_STRING], $testContent); + $tokenArray = $tokens[$token]; + $expectedScopeOpener = ($token + $openerOffset); + $expectedScopeCloser = ($token + $closerOffset); + + $this->assertSame(T_DEFAULT, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_DEFAULT (code)'); + $this->assertSame('T_DEFAULT', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_DEFAULT (type)'); + + $this->assertArrayHasKey('scope_condition', $tokenArray, 'Scope condition is not set'); + $this->assertArrayHasKey('scope_opener', $tokenArray, 'Scope opener is not set'); + $this->assertArrayHasKey('scope_closer', $tokenArray, 'Scope closer is not set'); + $this->assertSame($token, $tokenArray['scope_condition'], 'Scope condition is not the T_DEFAULT token'); + $this->assertSame($expectedScopeOpener, $tokenArray['scope_opener'], 'Scope opener of the T_DEFAULT token incorrect'); + $this->assertSame($expectedScopeCloser, $tokenArray['scope_closer'], 'Scope closer of the T_DEFAULT token incorrect'); + + $opener = $tokenArray['scope_opener']; + $this->assertArrayHasKey('scope_condition', $tokens[$opener], 'Opener scope condition is not set'); + $this->assertArrayHasKey('scope_opener', $tokens[$opener], 'Opener scope opener is not set'); + $this->assertArrayHasKey('scope_closer', $tokens[$opener], 'Opener scope closer is not set'); + $this->assertSame($token, $tokens[$opener]['scope_condition'], 'Opener scope condition is not the T_DEFAULT token'); + $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], 'T_DEFAULT opener scope opener token incorrect'); + $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], 'T_DEFAULT opener scope closer token incorrect'); + + $closer = $tokenArray['scope_closer']; + $this->assertArrayHasKey('scope_condition', $tokens[$closer], 'Closer scope condition is not set'); + $this->assertArrayHasKey('scope_opener', $tokens[$closer], 'Closer scope opener is not set'); + $this->assertArrayHasKey('scope_closer', $tokens[$closer], 'Closer scope closer is not set'); + $this->assertSame($token, $tokens[$closer]['scope_condition'], 'Closer scope condition is not the T_DEFAULT token'); + $this->assertSame($expectedScopeOpener, $tokens[$closer]['scope_opener'], 'T_DEFAULT closer scope opener token incorrect'); + $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], 'T_DEFAULT closer scope closer token incorrect'); + + if (($opener + 1) !== $closer) { + $end = $closer; + if (isset($conditionStop) === true) { + $end = $conditionStop; + } + + for ($i = ($opener + 1); $i < $end; $i++) { + $this->assertArrayHasKey( + $token, + $tokens[$i]['conditions'], + 'T_DEFAULT condition not added for token belonging to the T_DEFAULT structure' + ); + } + } + + }//end testSwitchDefault() + + + /** + * Data provider. + * + * @see testSwitchDefault() + * + * @return array + */ + public function dataSwitchDefault() + { + return [ + 'simple_switch_default' => [ + '/* testSimpleSwitchDefault */', + 1, + 4, + ], + 'simple_switch_default_with_curlies' => [ + // For a default structure with curly braces, the scope opener + // will be the open curly and the closer the close curly. + // However, scope conditions will not be set for open to close, + // but only for the open token up to the "break/return/continue" etc. + '/* testSimpleSwitchDefaultWithCurlies */', + 3, + 12, + 6, + ], + 'switch_default_toplevel' => [ + '/* testSwitchDefault */', + 1, + 43, + ], + 'switch_default_nested_in_match_case' => [ + '/* testSwitchDefaultNestedInMatchCase */', + 1, + 20, + ], + 'switch_default_nested_in_match_default' => [ + '/* testSwitchDefaultNestedInMatchDefault */', + 1, + 18, + ], + ]; + + }//end dataSwitchDefault() + + + /** + * Verify that the retokenization of `T_DEFAULT` tokens in match constructs, doesn't negatively + * impact the tokenization of `T_STRING` tokens with the contents 'default' which aren't in + * actual fact the default keyword. + * + * @param string $testMarker The comment prefacing the target token. + * @param string $testContent The token content to look for. + * + * @dataProvider dataNotDefaultKeyword + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testNotDefaultKeyword($testMarker, $testContent='DEFAULT') + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, [T_MATCH_DEFAULT, T_DEFAULT, T_STRING], $testContent); + $tokenArray = $tokens[$token]; + + $this->assertSame(T_STRING, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (code)'); + $this->assertSame('T_STRING', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (type)'); + + $this->assertArrayNotHasKey('scope_condition', $tokenArray, 'Scope condition is set'); + $this->assertArrayNotHasKey('scope_opener', $tokenArray, 'Scope opener is set'); + $this->assertArrayNotHasKey('scope_closer', $tokenArray, 'Scope closer is set'); + + }//end testNotDefaultKeyword() + + + /** + * Data provider. + * + * @see testNotDefaultKeyword() + * + * @return array + */ + public function dataNotDefaultKeyword() + { + return [ + 'class-constant-as-short-array-key' => ['/* testClassConstantAsShortArrayKey */'], + 'class-property-as-short-array-key' => ['/* testClassPropertyAsShortArrayKey */'], + 'namespaced-constant-as-short-array-key' => ['/* testNamespacedConstantAsShortArrayKey */'], + 'fqn-global-constant-as-short-array-key' => ['/* testFQNGlobalConstantAsShortArrayKey */'], + 'class-constant-as-long-array-key' => ['/* testClassConstantAsLongArrayKey */'], + 'class-constant-as-yield-key' => ['/* testClassConstantAsYieldKey */'], + + 'class-constant-as-long-array-key-nested-in-match' => ['/* testClassConstantAsLongArrayKeyNestedInMatch */'], + 'class-constant-as-long-array-key-nested-in-match-2' => ['/* testClassConstantAsLongArrayKeyNestedInMatchLevelDown */'], + 'class-constant-as-short-array-key-nested-in-match' => ['/* testClassConstantAsShortArrayKeyNestedInMatch */'], + 'class-constant-as-short-array-key-nested-in-match-2' => ['/* testClassConstantAsShortArrayKeyNestedInMatchLevelDown */'], + 'class-constant-as-long-array-key-with-nested-match' => ['/* testClassConstantAsLongArrayKeyWithNestedMatch */'], + 'class-constant-as-short-array-key-with-nested-match' => ['/* testClassConstantAsShortArrayKeyWithNestedMatch */'], + + 'class-constant-in-switch-case' => ['/* testClassConstantInSwitchCase */'], + 'class-property-in-switch-case' => ['/* testClassPropertyInSwitchCase */'], + 'namespaced-constant-in-switch-case' => ['/* testNamespacedConstantInSwitchCase */'], + 'namespace-relative-constant-in-switch-case' => ['/* testNamespaceRelativeConstantInSwitchCase */'], + + 'class-constant-declaration' => ['/* testClassConstant */'], + 'class-method-declaration' => [ + '/* testMethodDeclaration */', + 'default', + ], + ]; + + }//end dataNotDefaultKeyword() + + + /** + * Test a specific edge case where a scope opener would be incorrectly set. + * + * @link https://github.com/squizlabs/PHP_CodeSniffer/issues/3326 + * + * @return void + */ + public function testIssue3326() + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken('/* testClassConstant */', [T_SEMICOLON]); + $tokenArray = $tokens[$token]; + + $this->assertArrayNotHasKey('scope_condition', $tokenArray, 'Scope condition is set'); + $this->assertArrayNotHasKey('scope_opener', $tokenArray, 'Scope opener is set'); + $this->assertArrayNotHasKey('scope_closer', $tokenArray, 'Scope closer is set'); + + }//end testIssue3326() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleArrowTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleArrowTest.inc new file mode 100644 index 00000000..b67b0660 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleArrowTest.inc @@ -0,0 +1,281 @@ + 'Zero', + ); +} + +function simpleShortArray($x) { + return [ + /* testShortArrayArrowSimple */ + 0 => 'Zero', + ]; +} + +function simpleLongList($x) { + list( + /* testLongListArrowSimple */ + 0 => $a, + ) = $x; +} + +function simpleShortList($x) { + [ + /* testShortListArrowSimple */ + 0 => $a, + ] = $x; +} + +function simpleYield($x) { + $i = 0; + foreach (explode("\n", $x) as $line) { + /* testYieldArrowSimple */ + yield ++$i => $line; + } +} + +function simpleForeach($x) { + /* testForeachArrowSimple */ + foreach ($x as $k => $value) {} +} + +function simpleMatch($x) { + return match ($x) { + /* testMatchArrowSimpleSingleCase */ + 0 => 'Zero', + /* testMatchArrowSimpleMultiCase */ + 2, 4, 6 => 'Zero', + /* testMatchArrowSimpleSingleCaseWithTrailingComma */ + 1, => 'Zero', + /* testMatchArrowSimpleMultiCaseWithTrailingComma */ + 3, 5, => 'Zero', + }; +} + +function simpleArrowFunction($y) { + /* testFnArrowSimple */ + return fn ($y) => callMe($y); +} + +function matchNestedInMatch() { + $x = match ($y) { + /* testMatchArrowNestedMatchOuter */ + default, => match ($z) { + /* testMatchArrowNestedMatchInner */ + 1 => 1 + }, + }; +} + +function matchNestedInLongArrayValue() { + $array = array( + /* testLongArrayArrowWithNestedMatchValue1 */ + 'a' => match ($test) { + /* testMatchArrowInLongArrayValue1 */ + 1 => 'a', + /* testMatchArrowInLongArrayValue2 */ + 2 => 'b' + }, + /* testLongArrayArrowWithNestedMatchValue2 */ + $i => match ($test) { + /* testMatchArrowInLongArrayValue3 */ + 1 => 'a', + }, + ); +} + +function matchNestedInShortArrayValue() { + $array = [ + /* testShortArrayArrowWithNestedMatchValue1 */ + 'a' => match ($test) { + /* testMatchArrowInShortArrayValue1 */ + 1 => 'a', + /* testMatchArrowInShortArrayValue2 */ + 2 => 'b' + }, + /* testShortArrayArrowWithNestedMatchValue2 */ + $i => match ($test) { + /* testMatchArrowInShortArrayValue3 */ + 1 => 'a', + }, + ]; +} + +function matchNestedInLongArrayKey() { + $array = array( + match ($test) { /* testMatchArrowInLongArrayKey1 */ 1 => 'a', /* testMatchArrowInLongArrayKey2 */ 2 => 'b' } + /* testLongArrayArrowWithMatchKey */ + => 'dynamic keys, woho!', + ); +} + +function matchNestedInShortArrayKey() { + $array = [ + match ($test) { /* testMatchArrowInShortArrayKey1 */ 1 => 'a', /* testMatchArrowInShortArrayKey2 */ 2 => 'b' } + /* testShortArrayArrowWithMatchKey */ + => 'dynamic keys, woho!', + ]; +} + +function arraysNestedInMatch() { + $matcher = match ($x) { + /* testMatchArrowWithLongArrayBodyWithKeys */ + 0 => array( + /* testLongArrayArrowInMatchBody1 */ + 0 => 1, + /* testLongArrayArrowInMatchBody2 */ + 'a' => 2, + /* testLongArrayArrowInMatchBody3 */ + 'b' => 3 + ), + /* testMatchArrowWithShortArrayBodyWithoutKeys */ + 1 => [1, 2, 3], + /* testMatchArrowWithLongArrayBodyWithoutKeys */ + 2 => array( 1, [1, 2, 3], 2, 3), + /* testMatchArrowWithShortArrayBodyWithKeys */ + 3 => [ + /* testShortArrayArrowInMatchBody1 */ + 0 => 1, + /* testShortArrayArrowInMatchBody2 */ + 'a' => array(1, 2, 3), + /* testShortArrayArrowInMatchBody3 */ + 'b' => 2, + 3 + ], + /* testShortArrayArrowinMatchCase1 */ + [4 => 'a', /* testShortArrayArrowinMatchCase2 */ 5 => 6] + /* testMatchArrowWithShortArrayWithKeysAsCase */ + => 'match with array as case value', + /* testShortArrayArrowinMatchCase3 */ + [4 => 'a'], /* testLongArrayArrowinMatchCase4 */ array(5 => 6), + /* testMatchArrowWithMultipleArraysWithKeysAsCase */ + => 'match with multiple arrays as case value', + }; +} + +function matchNestedInArrowFunction($x) { + /* testFnArrowWithMatchInValue */ + $fn = fn($x) => match(true) { + /* testMatchArrowInFnBody1 */ + 1, 2, 3, 4, 5 => 'foo', + /* testMatchArrowInFnBody2 */ + default => 'bar', + }; +} + +function arrowFunctionsNestedInMatch($x) { + return match ($x) { + /* testMatchArrowWithFnBody1 */ + 1 => /* testFnArrowInMatchBody1 */ fn($y) => callMe($y), + /* testMatchArrowWithFnBody2 */ + default => /* testFnArrowInMatchBody2 */ fn($y) => callThem($y) + }; +} + +function matchShortArrayMismash() { + $array = [ + match ($test) { + /* testMatchArrowInComplexShortArrayKey1 */ + 1 => [ /* testShortArrayArrowInComplexMatchValueinShortArrayKey */ 1 => 'a'], + /* testMatchArrowInComplexShortArrayKey2 */ + 2 => 'b' + /* testShortArrayArrowInComplexMatchArrayMismash */ + } => match ($test) { + /* testMatchArrowInComplexShortArrayValue1 */ + 1 => [ /* testShortArrayArrowInComplexMatchValueinShortArrayValue */ 1 => 'a'], + /* testMatchArrowInComplexShortArrayValue2 */ + 2 => /* testFnArrowInComplexMatchValueInShortArrayValue */ fn($y) => callMe($y) + }, + ]; +} + + +function longListInMatch($x, $y) { + return match($x) { + /* testMatchArrowWithLongListBody */ + 1 => list('a' => $a, /* testLongListArrowInMatchBody */ 'b' => $b, 'c' => list('d' => $c)) = $y, + /* testLongListArrowInMatchCase */ + list('a' => $a, 'b' => $b) = $y /* testMatchArrowWithLongListInCase */ => 'something' + }; +} + +function shortListInMatch($x, $y) { + return match($x) { + /* testMatchArrowWithShortListBody */ + 1 => ['a' => $a, 'b' => $b, 'c' => /* testShortListArrowInMatchBody */ ['d' => $c]] = $y, + /* testShortListArrowInMatchCase */ + ['a' => $a, 'b' => $b] = $y /* testMatchArrowWithShortListInCase */ => 'something' + }; +} + +function matchInLongList() { + /* testMatchArrowInLongListKey */ + list(match($x) {1 => 1, 2 => 2} /* testLongListArrowWithMatchInKey */ => $a) = $array; +} + +function matchInShortList() { + /* testMatchArrowInShortListKey */ + [match($x) {1 => 1, 2 => 2} /* testShortListArrowWithMatchInKey */ => $a] = $array; +} + +function longArrayWithConstantKey() { + $arr = array( + /* testLongArrayArrowWithClassConstantKey */ + SomeClass::DEFAULT => 1, + ); +} + +function shortArrayWithConstantKey() { + $arr = [ + /* testShortArrayArrowWithClassConstantKey */ + SomeClass::DEFAULT => 1, + ]; +} + +function yieldWithConstantKey() { + /* testYieldArrowWithClassConstantKey */ + yield SomeClass::DEFAULT => 1; +} + +function longArrayWithConstantKeyNestedInMatch() { + return match($x) { + /* testMatchArrowWithNestedLongArrayWithClassConstantKey */ + default => array( + /* testLongArrayArrowWithClassConstantKeyNestedInMatch */ + SomeClass::DEFAULT => 1, + ), + }; +} + +function shortArrayWithConstantKeyNestedInMatch() { + return match($x) { + /* testMatchArrowWithNestedShortArrayWithClassConstantKey */ + default => [ + /* testShortArrayArrowWithClassConstantKeyNestedInMatch */ + SomeClass::DEFAULT => 1, + ], + }; +} + + +function longArrayWithConstantKeyWithNestedMatch() { + return array( + /* testLongArrayArrowWithClassConstantKeyWithNestedMatch */ + SomeClass::DEFAULT => match($x) { + /* testMatchArrowNestedInLongArrayWithClassConstantKey */ + default => 'foo' + }, + ); +} + +function shortArrayWithConstantKeyWithNestedMatch() { + return [ + /* testShortArrayArrowWithClassConstantKeyWithNestedMatch */ + SomeClass::DEFAULT => match($x) { + /* testMatchArrowNestedInShortArrayWithClassConstantKey */ + default => 'foo' + }, + ]; +} diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleArrowTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleArrowTest.php new file mode 100644 index 00000000..ad1a411f --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleArrowTest.php @@ -0,0 +1,237 @@ + + * @copyright 2020-2021 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class DoubleArrowTest extends AbstractMethodUnitTest +{ + + + /** + * Test that "normal" double arrows are correctly tokenized as `T_DOUBLE_ARROW`. + * + * @param string $testMarker The comment prefacing the target token. + * + * @dataProvider dataDoubleArrow + * @coversNothing + * + * @return void + */ + public function testDoubleArrow($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, [T_DOUBLE_ARROW, T_MATCH_ARROW, T_FN_ARROW]); + $tokenArray = $tokens[$token]; + + $this->assertSame(T_DOUBLE_ARROW, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_DOUBLE_ARROW (code)'); + $this->assertSame('T_DOUBLE_ARROW', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_DOUBLE_ARROW (type)'); + + }//end testDoubleArrow() + + + /** + * Data provider. + * + * @see testDoubleArrow() + * + * @return array + */ + public function dataDoubleArrow() + { + return [ + 'simple_long_array' => ['/* testLongArrayArrowSimple */'], + 'simple_short_array' => ['/* testShortArrayArrowSimple */'], + 'simple_long_list' => ['/* testLongListArrowSimple */'], + 'simple_short_list' => ['/* testShortListArrowSimple */'], + 'simple_yield' => ['/* testYieldArrowSimple */'], + 'simple_foreach' => ['/* testForeachArrowSimple */'], + + 'long_array_with_match_value_1' => ['/* testLongArrayArrowWithNestedMatchValue1 */'], + 'long_array_with_match_value_2' => ['/* testLongArrayArrowWithNestedMatchValue2 */'], + 'short_array_with_match_value_1' => ['/* testShortArrayArrowWithNestedMatchValue1 */'], + 'short_array_with_match_value_2' => ['/* testShortArrayArrowWithNestedMatchValue2 */'], + + 'long_array_with_match_key' => ['/* testLongArrayArrowWithMatchKey */'], + 'short_array_with_match_key' => ['/* testShortArrayArrowWithMatchKey */'], + + 'long_array_in_match_body_1' => ['/* testLongArrayArrowInMatchBody1 */'], + 'long_array_in_match_body_2' => ['/* testLongArrayArrowInMatchBody2 */'], + 'long_array_in_match_body_3' => ['/* testLongArrayArrowInMatchBody3 */'], + 'short_array_in_match_body_1' => ['/* testShortArrayArrowInMatchBody1 */'], + 'short_array_in_match_body_2' => ['/* testShortArrayArrowInMatchBody2 */'], + 'short_array_in_match_body_3' => ['/* testShortArrayArrowInMatchBody3 */'], + + 'short_array_in_match_case_1' => ['/* testShortArrayArrowinMatchCase1 */'], + 'short_array_in_match_case_2' => ['/* testShortArrayArrowinMatchCase2 */'], + 'short_array_in_match_case_3' => ['/* testShortArrayArrowinMatchCase3 */'], + 'long_array_in_match_case_4' => ['/* testLongArrayArrowinMatchCase4 */'], + + 'in_complex_short_array_key_match_value' => ['/* testShortArrayArrowInComplexMatchValueinShortArrayKey */'], + 'in_complex_short_array_toplevel' => ['/* testShortArrayArrowInComplexMatchArrayMismash */'], + 'in_complex_short_array_value_match_value' => ['/* testShortArrayArrowInComplexMatchValueinShortArrayValue */'], + + 'long_list_in_match_body' => ['/* testLongListArrowInMatchBody */'], + 'long_list_in_match_case' => ['/* testLongListArrowInMatchCase */'], + 'short_list_in_match_body' => ['/* testShortListArrowInMatchBody */'], + 'short_list_in_match_case' => ['/* testShortListArrowInMatchCase */'], + 'long_list_with_match_in_key' => ['/* testLongListArrowWithMatchInKey */'], + 'short_list_with_match_in_key' => ['/* testShortListArrowWithMatchInKey */'], + + 'long_array_with_constant_default_in_key' => ['/* testLongArrayArrowWithClassConstantKey */'], + 'short_array_with_constant_default_in_key' => ['/* testShortArrayArrowWithClassConstantKey */'], + 'yield_with_constant_default_in_key' => ['/* testYieldArrowWithClassConstantKey */'], + + 'long_array_with_default_in_key_in_match' => ['/* testLongArrayArrowWithClassConstantKeyNestedInMatch */'], + 'short_array_with_default_in_key_in_match' => ['/* testShortArrayArrowWithClassConstantKeyNestedInMatch */'], + 'long_array_with_default_in_key_with_match' => ['/* testLongArrayArrowWithClassConstantKeyWithNestedMatch */'], + 'short_array_with_default_in_key_with_match' => ['/* testShortArrayArrowWithClassConstantKeyWithNestedMatch */'], + ]; + + }//end dataDoubleArrow() + + + /** + * Test that double arrows in match expressions which are the demarkation between a case and the return value + * are correctly tokenized as `T_MATCH_ARROW`. + * + * @param string $testMarker The comment prefacing the target token. + * + * @dataProvider dataMatchArrow + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testMatchArrow($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, [T_DOUBLE_ARROW, T_MATCH_ARROW, T_FN_ARROW]); + $tokenArray = $tokens[$token]; + + $this->assertSame(T_MATCH_ARROW, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_MATCH_ARROW (code)'); + $this->assertSame('T_MATCH_ARROW', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_MATCH_ARROW (type)'); + + }//end testMatchArrow() + + + /** + * Data provider. + * + * @see testMatchArrow() + * + * @return array + */ + public function dataMatchArrow() + { + return [ + 'single_case' => ['/* testMatchArrowSimpleSingleCase */'], + 'multi_case' => ['/* testMatchArrowSimpleMultiCase */'], + 'single_case_with_trailing_comma' => ['/* testMatchArrowSimpleSingleCaseWithTrailingComma */'], + 'multi_case_with_trailing_comma' => ['/* testMatchArrowSimpleMultiCaseWithTrailingComma */'], + 'match_nested_outer' => ['/* testMatchArrowNestedMatchOuter */'], + 'match_nested_inner' => ['/* testMatchArrowNestedMatchInner */'], + + 'in_long_array_value_1' => ['/* testMatchArrowInLongArrayValue1 */'], + 'in_long_array_value_2' => ['/* testMatchArrowInLongArrayValue2 */'], + 'in_long_array_value_3' => ['/* testMatchArrowInLongArrayValue3 */'], + 'in_short_array_value_1' => ['/* testMatchArrowInShortArrayValue1 */'], + 'in_short_array_value_2' => ['/* testMatchArrowInShortArrayValue2 */'], + 'in_short_array_value_3' => ['/* testMatchArrowInShortArrayValue3 */'], + + 'in_long_array_key_1' => ['/* testMatchArrowInLongArrayKey1 */'], + 'in_long_array_key_2' => ['/* testMatchArrowInLongArrayKey2 */'], + 'in_short_array_key_1' => ['/* testMatchArrowInShortArrayKey1 */'], + 'in_short_array_key_2' => ['/* testMatchArrowInShortArrayKey2 */'], + + 'with_long_array_value_with_keys' => ['/* testMatchArrowWithLongArrayBodyWithKeys */'], + 'with_short_array_value_without_keys' => ['/* testMatchArrowWithShortArrayBodyWithoutKeys */'], + 'with_long_array_value_without_keys' => ['/* testMatchArrowWithLongArrayBodyWithoutKeys */'], + 'with_short_array_value_with_keys' => ['/* testMatchArrowWithShortArrayBodyWithKeys */'], + + 'with_short_array_with_keys_as_case' => ['/* testMatchArrowWithShortArrayWithKeysAsCase */'], + 'with_multiple_arrays_with_keys_as_case' => ['/* testMatchArrowWithMultipleArraysWithKeysAsCase */'], + + 'in_fn_body_case' => ['/* testMatchArrowInFnBody1 */'], + 'in_fn_body_default' => ['/* testMatchArrowInFnBody2 */'], + 'with_fn_body_case' => ['/* testMatchArrowWithFnBody1 */'], + 'with_fn_body_default' => ['/* testMatchArrowWithFnBody2 */'], + + 'in_complex_short_array_key_1' => ['/* testMatchArrowInComplexShortArrayKey1 */'], + 'in_complex_short_array_key_2' => ['/* testMatchArrowInComplexShortArrayKey2 */'], + 'in_complex_short_array_value_1' => ['/* testMatchArrowInComplexShortArrayValue1 */'], + 'in_complex_short_array_value_2' => ['/* testMatchArrowInComplexShortArrayValue2 */'], + + 'with_long_list_in_body' => ['/* testMatchArrowWithLongListBody */'], + 'with_long_list_in_case' => ['/* testMatchArrowWithLongListInCase */'], + 'with_short_list_in_body' => ['/* testMatchArrowWithShortListBody */'], + 'with_short_list_in_case' => ['/* testMatchArrowWithShortListInCase */'], + 'in_long_list_key' => ['/* testMatchArrowInLongListKey */'], + 'in_short_list_key' => ['/* testMatchArrowInShortListKey */'], + + 'with_long_array_value_with_default_key' => ['/* testMatchArrowWithNestedLongArrayWithClassConstantKey */'], + 'with_short_array_value_with_default_key' => ['/* testMatchArrowWithNestedShortArrayWithClassConstantKey */'], + 'in_long_array_value_with_default_key' => ['/* testMatchArrowNestedInLongArrayWithClassConstantKey */'], + 'in_short_array_value_with_default_key' => ['/* testMatchArrowNestedInShortArrayWithClassConstantKey */'], + ]; + + }//end dataMatchArrow() + + + /** + * Test that double arrows used as the scope opener for an arrow function + * are correctly tokenized as `T_FN_ARROW`. + * + * @param string $testMarker The comment prefacing the target token. + * + * @dataProvider dataFnArrow + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testFnArrow($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken($testMarker, [T_DOUBLE_ARROW, T_MATCH_ARROW, T_FN_ARROW]); + $tokenArray = $tokens[$token]; + + $this->assertSame(T_FN_ARROW, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_FN_ARROW (code)'); + $this->assertSame('T_FN_ARROW', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_FN_ARROW (type)'); + + }//end testFnArrow() + + + /** + * Data provider. + * + * @see testFnArrow() + * + * @return array + */ + public function dataFnArrow() + { + return [ + 'simple_fn' => ['/* testFnArrowSimple */'], + + 'with_match_as_value' => ['/* testFnArrowWithMatchInValue */'], + 'in_match_value_case' => ['/* testFnArrowInMatchBody1 */'], + 'in_match_value_default' => ['/* testFnArrowInMatchBody2 */'], + + 'in_complex_match_value_in_short_array' => ['/* testFnArrowInComplexMatchValueInShortArrayValue */'], + ]; + + }//end dataFnArrow() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleQuotedStringTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleQuotedStringTest.inc new file mode 100644 index 00000000..62535b1e --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleQuotedStringTest.inc @@ -0,0 +1,52 @@ +bar"; +/* testProperty2 */ +"{$foo->bar}"; + +/* testMethod1 */ +"{$foo->bar()}"; + +/* testClosure1 */ +"{$foo()}"; + +/* testChain1 */ +"{$foo['bar']->baz()()}"; + +/* testVariableVar1 */ +"${$bar}"; +/* testVariableVar2 */ +"${(foo)}"; +/* testVariableVar3 */ +"${foo->bar}"; + +/* testNested1 */ +"${foo["${bar}"]}"; +/* testNested2 */ +"${foo["${bar['baz']}"]}"; +/* testNested3 */ +"${foo->{$baz}}"; +/* testNested4 */ +"${foo->{${'a'}}}"; +/* testNested5 */ +"${foo->{"${'a'}"}}"; + +/* testParseError */ +"${foo["${bar diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleQuotedStringTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleQuotedStringTest.php new file mode 100644 index 00000000..cc9fe49e --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/DoubleQuotedStringTest.php @@ -0,0 +1,136 @@ + + * @copyright 2022 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class DoubleQuotedStringTest extends AbstractMethodUnitTest +{ + + + /** + * Test that double quoted strings contain the complete string. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param string $expectedContent The expected content of the double quoted string. + * + * @dataProvider dataDoubleQuotedString + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testDoubleQuotedString($testMarker, $expectedContent) + { + $tokens = self::$phpcsFile->getTokens(); + + $target = $this->getTargetToken($testMarker, T_DOUBLE_QUOTED_STRING); + $this->assertSame($expectedContent, $tokens[$target]['content']); + + }//end testDoubleQuotedString() + + + /** + * Data provider. + * + * @see testDoubleQuotedString() + * + * @return array + */ + public function dataDoubleQuotedString() + { + return [ + [ + 'testMarker' => '/* testSimple1 */', + 'expectedContent' => '"$foo"', + ], + [ + 'testMarker' => '/* testSimple2 */', + 'expectedContent' => '"{$foo}"', + ], + [ + 'testMarker' => '/* testSimple3 */', + 'expectedContent' => '"${foo}"', + ], + [ + 'testMarker' => '/* testDIM1 */', + 'expectedContent' => '"$foo[bar]"', + ], + [ + 'testMarker' => '/* testDIM2 */', + 'expectedContent' => '"{$foo[\'bar\']}"', + ], + [ + 'testMarker' => '/* testDIM3 */', + 'expectedContent' => '"${foo[\'bar\']}"', + ], + [ + 'testMarker' => '/* testProperty1 */', + 'expectedContent' => '"$foo->bar"', + ], + [ + 'testMarker' => '/* testProperty2 */', + 'expectedContent' => '"{$foo->bar}"', + ], + [ + 'testMarker' => '/* testMethod1 */', + 'expectedContent' => '"{$foo->bar()}"', + ], + [ + 'testMarker' => '/* testClosure1 */', + 'expectedContent' => '"{$foo()}"', + ], + [ + 'testMarker' => '/* testChain1 */', + 'expectedContent' => '"{$foo[\'bar\']->baz()()}"', + ], + [ + 'testMarker' => '/* testVariableVar1 */', + 'expectedContent' => '"${$bar}"', + ], + [ + 'testMarker' => '/* testVariableVar2 */', + 'expectedContent' => '"${(foo)}"', + ], + [ + 'testMarker' => '/* testVariableVar3 */', + 'expectedContent' => '"${foo->bar}"', + ], + [ + 'testMarker' => '/* testNested1 */', + 'expectedContent' => '"${foo["${bar}"]}"', + ], + [ + 'testMarker' => '/* testNested2 */', + 'expectedContent' => '"${foo["${bar[\'baz\']}"]}"', + ], + [ + 'testMarker' => '/* testNested3 */', + 'expectedContent' => '"${foo->{$baz}}"', + ], + [ + 'testMarker' => '/* testNested4 */', + 'expectedContent' => '"${foo->{${\'a\'}}}"', + ], + [ + 'testMarker' => '/* testNested5 */', + 'expectedContent' => '"${foo->{"${\'a\'}"}}"', + ], + [ + 'testMarker' => '/* testParseError */', + 'expectedContent' => '"${foo["${bar +', + ], + ]; + + }//end dataDoubleQuotedString() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/EnumCaseTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/EnumCaseTest.inc new file mode 100644 index 00000000..13b87242 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/EnumCaseTest.inc @@ -0,0 +1,95 @@ + + * @copyright 2021 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class EnumCaseTest extends AbstractMethodUnitTest +{ + + + /** + * Test that the enum "case" is converted to T_ENUM_CASE. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataEnumCases + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::recurseScopeMap + * + * @return void + */ + public function testEnumCases($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $enumCase = $this->getTargetToken($testMarker, [T_ENUM_CASE, T_CASE]); + + $this->assertSame(T_ENUM_CASE, $tokens[$enumCase]['code']); + $this->assertSame('T_ENUM_CASE', $tokens[$enumCase]['type']); + + $this->assertArrayNotHasKey('scope_condition', $tokens[$enumCase], 'Scope condition is set'); + $this->assertArrayNotHasKey('scope_opener', $tokens[$enumCase], 'Scope opener is set'); + $this->assertArrayNotHasKey('scope_closer', $tokens[$enumCase], 'Scope closer is set'); + + }//end testEnumCases() + + + /** + * Data provider. + * + * @see testEnumCases() + * + * @return array + */ + public function dataEnumCases() + { + return [ + ['/* testPureEnumCase */'], + ['/* testBackingIntegerEnumCase */'], + ['/* testBackingStringEnumCase */'], + ['/* testEnumCaseInComplexEnum */'], + ['/* testEnumCaseIsCaseInsensitive */'], + ['/* testEnumCaseAfterSwitch */'], + ['/* testEnumCaseAfterSwitchWithEndSwitch */'], + ]; + + }//end dataEnumCases() + + + /** + * Test that "case" that is not enum case is still tokenized as `T_CASE`. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataNotEnumCases + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::recurseScopeMap + * + * @return void + */ + public function testNotEnumCases($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $case = $this->getTargetToken($testMarker, [T_ENUM_CASE, T_CASE]); + + $this->assertSame(T_CASE, $tokens[$case]['code']); + $this->assertSame('T_CASE', $tokens[$case]['type']); + + $this->assertArrayHasKey('scope_condition', $tokens[$case], 'Scope condition is not set'); + $this->assertArrayHasKey('scope_opener', $tokens[$case], 'Scope opener is not set'); + $this->assertArrayHasKey('scope_closer', $tokens[$case], 'Scope closer is not set'); + + }//end testNotEnumCases() + + + /** + * Data provider. + * + * @see testNotEnumCases() + * + * @return array + */ + public function dataNotEnumCases() + { + return [ + ['/* testCaseWithSemicolonIsNotEnumCase */'], + ['/* testCaseWithConstantIsNotEnumCase */'], + ['/* testCaseWithConstantAndIdenticalIsNotEnumCase */'], + ['/* testCaseWithAssigmentToConstantIsNotEnumCase */'], + ['/* testIsNotEnumCaseIsCaseInsensitive */'], + ['/* testCaseInSwitchWhenCreatingEnumInSwitch1 */'], + ['/* testCaseInSwitchWhenCreatingEnumInSwitch2 */'], + ]; + + }//end dataNotEnumCases() + + + /** + * Test that "case" that is not enum case is still tokenized as `T_CASE`. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataKeywordAsEnumCaseNameShouldBeString + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testKeywordAsEnumCaseNameShouldBeString($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $enumCaseName = $this->getTargetToken($testMarker, [T_STRING, T_INTERFACE, T_TRAIT, T_ENUM, T_FUNCTION, T_FALSE, T_DEFAULT, T_ARRAY]); + + $this->assertSame(T_STRING, $tokens[$enumCaseName]['code']); + $this->assertSame('T_STRING', $tokens[$enumCaseName]['type']); + + }//end testKeywordAsEnumCaseNameShouldBeString() + + + /** + * Data provider. + * + * @see testKeywordAsEnumCaseNameShouldBeString() + * + * @return array + */ + public function dataKeywordAsEnumCaseNameShouldBeString() + { + return [ + ['/* testKeywordAsEnumCaseNameShouldBeString1 */'], + ['/* testKeywordAsEnumCaseNameShouldBeString2 */'], + ['/* testKeywordAsEnumCaseNameShouldBeString3 */'], + ['/* testKeywordAsEnumCaseNameShouldBeString4 */'], + ]; + + }//end dataKeywordAsEnumCaseNameShouldBeString() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/FinallyTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/FinallyTest.inc new file mode 100644 index 00000000..e65600b6 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/FinallyTest.inc @@ -0,0 +1,40 @@ +finally = 'foo'; + } +} diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/FinallyTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/FinallyTest.php new file mode 100644 index 00000000..2b28bc5d --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/FinallyTest.php @@ -0,0 +1,96 @@ + + * @copyright 2021 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class FinallyTest extends AbstractMethodUnitTest +{ + + + /** + * Test that the finally keyword is tokenized as such. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataFinallyKeyword + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testFinallyKeyword($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $target = $this->getTargetToken($testMarker, [T_FINALLY, T_STRING]); + $this->assertSame(T_FINALLY, $tokens[$target]['code']); + $this->assertSame('T_FINALLY', $tokens[$target]['type']); + + }//end testFinallyKeyword() + + + /** + * Data provider. + * + * @see testFinallyKeyword() + * + * @return array + */ + public function dataFinallyKeyword() + { + return [ + ['/* testTryCatchFinally */'], + ['/* testTryFinallyCatch */'], + ['/* testTryFinally */'], + ]; + + }//end dataFinallyKeyword() + + + /** + * Test that 'finally' when not used as the reserved keyword is tokenized as `T_STRING`. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataFinallyNonKeyword + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testFinallyNonKeyword($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $target = $this->getTargetToken($testMarker, [T_FINALLY, T_STRING]); + $this->assertSame(T_STRING, $tokens[$target]['code']); + $this->assertSame('T_STRING', $tokens[$target]['type']); + + }//end testFinallyNonKeyword() + + + /** + * Data provider. + * + * @see testFinallyNonKeyword() + * + * @return array + */ + public function dataFinallyNonKeyword() + { + return [ + ['/* testFinallyUsedAsClassConstantName */'], + ['/* testFinallyUsedAsMethodName */'], + ['/* testFinallyUsedAsPropertyName */'], + ]; + + }//end dataFinallyNonKeyword() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/GotoLabelTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/GotoLabelTest.inc new file mode 100644 index 00000000..12df5d29 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/GotoLabelTest.inc @@ -0,0 +1,56 @@ + +
    + +property: + // Do something. + break; +} + +switch (true) { + /* testNotGotoDeclarationGlobalConstantInTernary */ + case $x === ($cond) ? CONST_A : CONST_B: + // Do something. + break; +} + +/* testNotGotoDeclarationEnumWithType */ +enum Suit: string implements Colorful, CardGame {} diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/GotoLabelTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/GotoLabelTest.php new file mode 100644 index 00000000..0f937cc8 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/GotoLabelTest.php @@ -0,0 +1,175 @@ + + * @copyright 2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class GotoLabelTest extends AbstractMethodUnitTest +{ + + + /** + * Verify that the label in a goto statement is tokenized as T_STRING. + * + * @param string $testMarker The comment prefacing the target token. + * @param string $testContent The token content to expect. + * + * @dataProvider dataGotoStatement + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testGotoStatement($testMarker, $testContent) + { + $tokens = self::$phpcsFile->getTokens(); + + $label = $this->getTargetToken($testMarker, T_STRING); + + $this->assertInternalType('int', $label); + $this->assertSame($testContent, $tokens[$label]['content']); + + }//end testGotoStatement() + + + /** + * Data provider. + * + * @see testGotoStatement() + * + * @return array + */ + public function dataGotoStatement() + { + return [ + [ + '/* testGotoStatement */', + 'marker', + ], + [ + '/* testGotoStatementInLoop */', + 'end', + ], + ]; + + }//end dataGotoStatement() + + + /** + * Verify that the label in a goto declaration is tokenized as T_GOTO_LABEL. + * + * @param string $testMarker The comment prefacing the target token. + * @param string $testContent The token content to expect. + * + * @dataProvider dataGotoDeclaration + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testGotoDeclaration($testMarker, $testContent) + { + $tokens = self::$phpcsFile->getTokens(); + + $label = $this->getTargetToken($testMarker, T_GOTO_LABEL); + + $this->assertInternalType('int', $label); + $this->assertSame($testContent, $tokens[$label]['content']); + + }//end testGotoDeclaration() + + + /** + * Data provider. + * + * @see testGotoDeclaration() + * + * @return array + */ + public function dataGotoDeclaration() + { + return [ + [ + '/* testGotoDeclaration */', + 'marker:', + ], + [ + '/* testGotoDeclarationOutsideLoop */', + 'end:', + ], + ]; + + }//end dataGotoDeclaration() + + + /** + * Verify that the constant used in a switch - case statement is not confused with a goto label. + * + * @param string $testMarker The comment prefacing the target token. + * @param string $testContent The token content to expect. + * + * @dataProvider dataNotAGotoDeclaration + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testNotAGotoDeclaration($testMarker, $testContent) + { + $tokens = self::$phpcsFile->getTokens(); + $target = $this->getTargetToken($testMarker, [T_GOTO_LABEL, T_STRING], $testContent); + + $this->assertSame(T_STRING, $tokens[$target]['code']); + $this->assertSame('T_STRING', $tokens[$target]['type']); + + }//end testNotAGotoDeclaration() + + + /** + * Data provider. + * + * @see testNotAGotoDeclaration() + * + * @return array + */ + public function dataNotAGotoDeclaration() + { + return [ + [ + '/* testNotGotoDeclarationGlobalConstant */', + 'CONSTANT', + ], + [ + '/* testNotGotoDeclarationNamespacedConstant */', + 'CONSTANT', + ], + [ + '/* testNotGotoDeclarationClassConstant */', + 'CONSTANT', + ], + [ + '/* testNotGotoDeclarationClassProperty */', + 'property', + ], + [ + '/* testNotGotoDeclarationGlobalConstantInTernary */', + 'CONST_A', + ], + [ + '/* testNotGotoDeclarationGlobalConstantInTernary */', + 'CONST_B', + ], + [ + '/* testNotGotoDeclarationEnumWithType */', + 'Suit', + ], + ]; + + }//end dataNotAGotoDeclaration() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/HeredocNowdocCloserTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/HeredocNowdocCloserTest.inc new file mode 100644 index 00000000..a800980b --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/HeredocNowdocCloserTest.inc @@ -0,0 +1,43 @@ + + * @copyright 2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Config; +use PHP_CodeSniffer\Ruleset; +use PHP_CodeSniffer\Files\DummyFile; +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +/** + * Heredoc/nowdoc closer token test. + * + * @requires PHP 7.3 + */ +class HeredocNowdocCloserTest extends AbstractMethodUnitTest +{ + + + /** + * Initialize & tokenize \PHP_CodeSniffer\Files\File with code from the test case file. + * + * {@internal This is a near duplicate of the original method. Only difference is that + * tab replacement is enabled for this test.} + * + * @return void + */ + public static function setUpBeforeClass() + { + $config = new Config(); + $config->standards = ['PSR1']; + $config->tabWidth = 4; + + $ruleset = new Ruleset($config); + + // Default to a file with the same name as the test class. Extension is property based. + $relativeCN = str_replace(__NAMESPACE__, '', get_called_class()); + $relativePath = str_replace('\\', DIRECTORY_SEPARATOR, $relativeCN); + $pathToTestFile = realpath(__DIR__).$relativePath.'.'.static::$fileExtension; + + // Make sure the file gets parsed correctly based on the file type. + $contents = 'phpcs_input_file: '.$pathToTestFile.PHP_EOL; + $contents .= file_get_contents($pathToTestFile); + + self::$phpcsFile = new DummyFile($contents, $ruleset, $config); + self::$phpcsFile->process(); + + }//end setUpBeforeClass() + + + /** + * Verify that leading (indent) whitespace in a heredoc/nowdoc closer token get the tab replacement treatment. + * + * @param string $testMarker The comment prefacing the target token. + * @param array $expected Expectations for the token array. + * + * @dataProvider dataHeredocNowdocCloserTabReplacement + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createPositionMap + * + * @return void + */ + public function testHeredocNowdocCloserTabReplacement($testMarker, $expected) + { + $tokens = self::$phpcsFile->getTokens(); + + $closer = $this->getTargetToken($testMarker, [T_END_HEREDOC, T_END_NOWDOC]); + + foreach ($expected as $key => $value) { + if ($key === 'orig_content' && $value === null) { + $this->assertArrayNotHasKey($key, $tokens[$closer], "Unexpected 'orig_content' key found in the token array."); + continue; + } + + $this->assertArrayHasKey($key, $tokens[$closer], "Key $key not found in the token array."); + $this->assertSame($value, $tokens[$closer][$key], "Value for key $key does not match expectation."); + } + + }//end testHeredocNowdocCloserTabReplacement() + + + /** + * Data provider. + * + * @see testHeredocNowdocCloserTabReplacement() + * + * @return array + */ + public function dataHeredocNowdocCloserTabReplacement() + { + return [ + [ + 'testMarker' => '/* testHeredocCloserNoIndent */', + 'expected' => [ + 'length' => 3, + 'content' => 'EOD', + 'orig_content' => null, + ], + ], + [ + 'testMarker' => '/* testNowdocCloserNoIndent */', + 'expected' => [ + 'length' => 3, + 'content' => 'EOD', + 'orig_content' => null, + ], + ], + [ + 'testMarker' => '/* testHeredocCloserSpaceIndent */', + 'expected' => [ + 'length' => 7, + 'content' => ' END', + 'orig_content' => null, + ], + ], + [ + 'testMarker' => '/* testNowdocCloserSpaceIndent */', + 'expected' => [ + 'length' => 8, + 'content' => ' END', + 'orig_content' => null, + ], + ], + [ + 'testMarker' => '/* testHeredocCloserTabIndent */', + 'expected' => [ + 'length' => 8, + 'content' => ' END', + 'orig_content' => ' END', + ], + ], + [ + 'testMarker' => '/* testNowdocCloserTabIndent */', + 'expected' => [ + 'length' => 7, + 'content' => ' END', + 'orig_content' => ' END', + ], + ], + ]; + + }//end dataHeredocNowdocCloserTabReplacement() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/HeredocStringTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/HeredocStringTest.inc new file mode 100644 index 00000000..ae43e24a --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/HeredocStringTest.inc @@ -0,0 +1,193 @@ +bar +EOD; + +/* testProperty2 */ +$heredoc = <<<"EOD" +{$foo->bar} +EOD; + +/* testMethod1 */ +$heredoc = <<bar()} +EOD; + +/* testClosure1 */ +$heredoc = <<<"EOD" +{$foo()} +EOD; + +/* testChain1 */ +$heredoc = <<baz()()} +EOD; + +/* testVariableVar1 */ +$heredoc = <<<"EOD" +${$bar} +EOD; + +/* testVariableVar2 */ +$heredoc = <<bar} +EOD; + +/* testNested1 */ +$heredoc = <<{$baz}} +EOD; + +/* testNested4 */ +$heredoc = <<<"EOD" +${foo->{${'a'}}} +EOD; + +/* testNested5 */ +$heredoc = <<{"${'a'}"}} +EOD; + +/* testSimple1Wrapped */ +$heredoc = <<bar Something +EOD; + +/* testProperty2Wrapped */ +$heredoc = <<<"EOD" +Do {$foo->bar} Something +EOD; + +/* testMethod1Wrapped */ +$heredoc = <<bar()} Something +EOD; + +/* testClosure1Wrapped */ +$heredoc = <<<"EOD" +Do {$foo()} Something +EOD; + +/* testChain1Wrapped */ +$heredoc = <<baz()()} Something +EOD; + +/* testVariableVar1Wrapped */ +$heredoc = <<<"EOD" +Do ${$bar} Something +EOD; + +/* testVariableVar2Wrapped */ +$heredoc = <<bar} Something +EOD; + +/* testNested1Wrapped */ +$heredoc = <<{$baz}} Something +EOD; + +/* testNested4Wrapped */ +$heredoc = <<<"EOD" +Do ${foo->{${'a'}}} Something +EOD; + +/* testNested5Wrapped */ +$heredoc = <<{"${'a'}"}} Something +EOD; diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/HeredocStringTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/HeredocStringTest.php new file mode 100644 index 00000000..2c808be9 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/HeredocStringTest.php @@ -0,0 +1,153 @@ + + * @copyright 2022 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class HeredocStringTest extends AbstractMethodUnitTest +{ + + + /** + * Test that heredoc strings contain the complete interpolated string. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param string $expectedContent The expected content of the heredoc string. + * + * @dataProvider dataHeredocString + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testHeredocString($testMarker, $expectedContent) + { + $tokens = self::$phpcsFile->getTokens(); + + $target = $this->getTargetToken($testMarker, T_HEREDOC); + $this->assertSame($expectedContent."\n", $tokens[$target]['content']); + + }//end testHeredocString() + + + /** + * Test that heredoc strings contain the complete interpolated string when combined with other texts. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param string $expectedContent The expected content of the heredoc string. + * + * @dataProvider dataHeredocString + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testHeredocStringWrapped($testMarker, $expectedContent) + { + $tokens = self::$phpcsFile->getTokens(); + + $testMarker = substr($testMarker, 0, -3).'Wrapped */'; + $target = $this->getTargetToken($testMarker, T_HEREDOC); + $this->assertSame('Do '.$expectedContent." Something\n", $tokens[$target]['content']); + + }//end testHeredocStringWrapped() + + + /** + * Data provider. + * + * @see testHeredocString() + * + * @return array + */ + public function dataHeredocString() + { + return [ + [ + 'testMarker' => '/* testSimple1 */', + 'expectedContent' => '$foo', + ], + [ + 'testMarker' => '/* testSimple2 */', + 'expectedContent' => '{$foo}', + ], + [ + 'testMarker' => '/* testSimple3 */', + 'expectedContent' => '${foo}', + ], + [ + 'testMarker' => '/* testDIM1 */', + 'expectedContent' => '$foo[bar]', + ], + [ + 'testMarker' => '/* testDIM2 */', + 'expectedContent' => '{$foo[\'bar\']}', + ], + [ + 'testMarker' => '/* testDIM3 */', + 'expectedContent' => '${foo[\'bar\']}', + ], + [ + 'testMarker' => '/* testProperty1 */', + 'expectedContent' => '$foo->bar', + ], + [ + 'testMarker' => '/* testProperty2 */', + 'expectedContent' => '{$foo->bar}', + ], + [ + 'testMarker' => '/* testMethod1 */', + 'expectedContent' => '{$foo->bar()}', + ], + [ + 'testMarker' => '/* testClosure1 */', + 'expectedContent' => '{$foo()}', + ], + [ + 'testMarker' => '/* testChain1 */', + 'expectedContent' => '{$foo[\'bar\']->baz()()}', + ], + [ + 'testMarker' => '/* testVariableVar1 */', + 'expectedContent' => '${$bar}', + ], + [ + 'testMarker' => '/* testVariableVar2 */', + 'expectedContent' => '${(foo)}', + ], + [ + 'testMarker' => '/* testVariableVar3 */', + 'expectedContent' => '${foo->bar}', + ], + [ + 'testMarker' => '/* testNested1 */', + 'expectedContent' => '${foo["${bar}"]}', + ], + [ + 'testMarker' => '/* testNested2 */', + 'expectedContent' => '${foo["${bar[\'baz\']}"]}', + ], + [ + 'testMarker' => '/* testNested3 */', + 'expectedContent' => '${foo->{$baz}}', + ], + [ + 'testMarker' => '/* testNested4 */', + 'expectedContent' => '${foo->{${\'a\'}}}', + ], + [ + 'testMarker' => '/* testNested5 */', + 'expectedContent' => '${foo->{"${\'a\'}"}}', + ], + ]; + + }//end dataHeredocString() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.inc new file mode 100644 index 00000000..b9c0df24 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.inc @@ -0,0 +1,407 @@ +getPos(skip: false), + count: count(array_or_countable: $array), + value: 50 +); + +array_fill( + start_index: /* testNestedFunctionCallInner1 */ $obj->getPos(skip: false), + count: /* testNestedFunctionCallInner2 */ count(array_or_countable: $array), + value: 50 +); + +/* testNamespaceOperatorFunction */ +namespace\function_name(label:$string, more: false); + +/* testNamespaceRelativeFunction */ +Partially\Qualified\function_name(label:$string, more: false); + +/* testNamespacedFQNFunction */ +\Fully\Qualified\function_name(label: $string, more:false); + +/* testVariableFunction */ +$fn(label: $string, more:false); + +/* testVariableVariableFunction */ +${$fn}(label: $string, more:false); + +/* testMethodCall */ +$obj->methodName(label: $foo, more: $bar); + +/* testVariableMethodCall */ +$obj->{$var}(label: $foo, more: $bar); + +/* testClassInstantiation */ +$obj = new MyClass(label: $string, more:false); + +/* testClassInstantiationSelf */ +$obj = new self(label: $string, more:true); + +/* testClassInstantiationStatic */ +$obj = new static(label: $string, more:false); + +/* testAnonClass */ +$anon = new class(label: $string, more: false) { + public function __construct($label, $more) {} +}; + +function myfoo( $💩💩💩, $Пасха, $_valid) {} +/* testNonAsciiNames */ +foo(💩💩💩: [], Пасха: 'text', _valid: 123); + +/* testMixedPositionalAndNamedArgsWithTernary */ +foo( $cond ? true : false, name: $value2 ); + +/* testNamedArgWithTernary */ +foo( label: $cond ? true : false, more: $cond ? CONSTANT_A : CONSTANT_B ); + +/* testTernaryWithFunctionCallsInThenElse */ +echo $cond ? foo( label: $something ) : foo( more: $something_else ); + +/* testTernaryWithConstantsInThenElse */ +echo $cond ? CONSTANT_NAME : OTHER_CONSTANT; + +switch ($s) { + /* testSwitchCaseWithConstant */ + case MY_CONSTANT: + // Do something. + break; + + /* testSwitchCaseWithClassProperty */ + case $obj->property: + // Do something. + break; + + /* testSwitchDefault */ + default: + // Do something. + break; +} + +/* testTernaryWithClosuresAndReturnTypes */ +$closure = $cond ? function() : bool {return true;} : function() : int {return 123;}; + +/* testTernaryWithArrowFunctionsAndReturnTypes */ +$fn = $cond ? fn() : bool => true : fn() : int => 123; + + +/* testCompileErrorNamedBeforePositional */ +// Not the concern of PHPCS. Should still be handled. +test(param: $bar, $foo); + +/* testDuplicateName1 */ +// Error Exception, but not the concern of PHPCS. Should still be handled. +test(param: 1, /* testDuplicateName2 */ param: 2); + +/* testIncorrectOrderWithVariadic */ +// Error Exception, but not the concern of PHPCS. Should still be handled. +array_fill(start_index: 0, ...[100, 50]); + +/* testCompileErrorIncorrectOrderWithVariadic */ +// Not the concern of PHPCS. Should still be handled. +test(...$values, param: $value); // Compile-time error + +/* testParseErrorNoValue */ +// Not the concern of PHPCS. Should still be handled. +test(param1:, param2:); + +/* testParseErrorDynamicName */ +// Parse error. Ignore. +function_name($variableStoringParamName: $value); + +/* testParseErrorExit */ +// Exit is a language construct, not a function. Named params not supported, handle it anyway. +exit(status: $value); + +/* testParseErrorEmpty */ +// Empty is a language construct, not a function. Named params not supported, handle it anyway. +empty(variable: $value); + +/* testParseErrorEval */ +// Eval is a language construct, not a function. Named params not supported, handle it anyway. +eval(code: $value); + +/* testParseErrorArbitraryParentheses */ +// Parse error. Not named param, handle it anyway. +$calc = (something: $value / $other); + + +/* testReservedKeywordAbstract1 */ +foobar(abstract: $value, /* testReservedKeywordAbstract2 */ abstract: $value); + +/* testReservedKeywordAnd1 */ +foobar(and: $value, /* testReservedKeywordAnd2 */ and: $value); + +/* testReservedKeywordArray1 */ +foobar(array: $value, /* testReservedKeywordArray2 */ array: $value); + +/* testReservedKeywordAs1 */ +foobar(as: $value, /* testReservedKeywordAs2 */ as: $value); + +/* testReservedKeywordBreak1 */ +foobar(break: $value, /* testReservedKeywordBreak2 */ break: $value); + +/* testReservedKeywordCallable1 */ +foobar(callable: $value, /* testReservedKeywordCallable2 */ callable: $value); + +/* testReservedKeywordCase1 */ +foobar(case: $value, /* testReservedKeywordCase2 */ case: $value); + +/* testReservedKeywordCatch1 */ +foobar(catch: $value, /* testReservedKeywordCatch2 */ catch: $value); + +/* testReservedKeywordClass1 */ +foobar(class: $value, /* testReservedKeywordClass2 */ class: $value); + +/* testReservedKeywordClone1 */ +foobar(clone: $value, /* testReservedKeywordClone2 */ clone: $value); + +/* testReservedKeywordConst1 */ +foobar(const: $value, /* testReservedKeywordConst2 */ const: $value); + +/* testReservedKeywordContinue1 */ +foobar(continue: $value, /* testReservedKeywordContinue2 */ continue: $value); + +/* testReservedKeywordDeclare1 */ +foobar(declare: $value, /* testReservedKeywordDeclare2 */ declare: $value); + +/* testReservedKeywordDefault1 */ +foobar(default: $value, /* testReservedKeywordDefault2 */ default: $value); + +/* testReservedKeywordDie1 */ +foobar(die: $value, /* testReservedKeywordDie2 */ die: $value); + +/* testReservedKeywordDo1 */ +foobar(do: $value, /* testReservedKeywordDo2 */ do: $value); + +/* testReservedKeywordEcho1 */ +foobar(echo: $value, /* testReservedKeywordEcho2 */ echo: $value); + +/* testReservedKeywordElse1 */ +foobar(else: $value, /* testReservedKeywordElse2 */ else: $value); + +/* testReservedKeywordElseif1 */ +foobar(elseif: $value, /* testReservedKeywordElseif2 */ elseif: $value); + +/* testReservedKeywordEmpty1 */ +foobar(empty: $value, /* testReservedKeywordEmpty2 */ empty: $value); + +/* testReservedKeywordEnum1 */ +foobar(enum: $value, /* testReservedKeywordEnum2 */ enum: $value); + +/* testReservedKeywordEnddeclare1 */ +foobar(enddeclare: $value, /* testReservedKeywordEnddeclare2 */ enddeclare: $value); + +/* testReservedKeywordEndfor1 */ +foobar(endfor: $value, /* testReservedKeywordEndfor2 */ endfor: $value); + +/* testReservedKeywordEndforeach1 */ +foobar(endforeach: $value, /* testReservedKeywordEndforeach2 */ endforeach: $value); + +/* testReservedKeywordEndif1 */ +foobar(endif: $value, /* testReservedKeywordEndif2 */ endif: $value); + +/* testReservedKeywordEndswitch1 */ +foobar(endswitch: $value, /* testReservedKeywordEndswitch2 */ endswitch: $value); + +/* testReservedKeywordEndwhile1 */ +foobar(endwhile: $value, /* testReservedKeywordEndwhile2 */ endwhile: $value); + +/* testReservedKeywordEval1 */ +foobar(eval: $value, /* testReservedKeywordEval2 */ eval: $value); + +/* testReservedKeywordExit1 */ +foobar(exit: $value, /* testReservedKeywordExit2 */ exit: $value); + +/* testReservedKeywordExtends1 */ +foobar(extends: $value, /* testReservedKeywordExtends2 */ extends: $value); + +/* testReservedKeywordFinal1 */ +foobar(final: $value, /* testReservedKeywordFinal2 */ final: $value); + +/* testReservedKeywordFinally1 */ +foobar(finally: $value, /* testReservedKeywordFinally2 */ finally: $value); + +/* testReservedKeywordFn1 */ +foobar(fn: $value, /* testReservedKeywordFn2 */ fn: $value); + +/* testReservedKeywordFor1 */ +foobar(for: $value, /* testReservedKeywordFor2 */ for: $value); + +/* testReservedKeywordForeach1 */ +foobar(foreach: $value, /* testReservedKeywordForeach2 */ foreach: $value); + +/* testReservedKeywordFunction1 */ +foobar(function: $value, /* testReservedKeywordFunction2 */ function: $value); + +/* testReservedKeywordGlobal1 */ +foobar(global: $value, /* testReservedKeywordGlobal2 */ global: $value); + +/* testReservedKeywordGoto1 */ +foobar(goto: $value, /* testReservedKeywordGoto2 */ goto: $value); + +/* testReservedKeywordIf1 */ +foobar(if: $value, /* testReservedKeywordIf2 */ if: $value); + +/* testReservedKeywordImplements1 */ +foobar(implements: $value, /* testReservedKeywordImplements2 */ implements: $value); + +/* testReservedKeywordInclude1 */ +foobar(include: $value, /* testReservedKeywordInclude2 */ include: $value); + +/* testReservedKeywordInclude_once1 */ +foobar(include_once: $value, /* testReservedKeywordInclude_once2 */ include_once: $value); + +/* testReservedKeywordInstanceof1 */ +foobar(instanceof: $value, /* testReservedKeywordInstanceof2 */ instanceof: $value); + +/* testReservedKeywordInsteadof1 */ +foobar(insteadof: $value, /* testReservedKeywordInsteadof2 */ insteadof: $value); + +/* testReservedKeywordInterface1 */ +foobar(interface: $value, /* testReservedKeywordInterface2 */ interface: $value); + +/* testReservedKeywordIsset1 */ +foobar(isset: $value, /* testReservedKeywordIsset2 */ isset: $value); + +/* testReservedKeywordList1 */ +foobar(list: $value, /* testReservedKeywordList2 */ list: $value); + +/* testReservedKeywordMatch1 */ +foobar(match: $value, /* testReservedKeywordMatch2 */ match: $value); + +/* testReservedKeywordNamespace1 */ +foobar(namespace: $value, /* testReservedKeywordNamespace2 */ namespace: $value); + +/* testReservedKeywordNew1 */ +foobar(new: $value, /* testReservedKeywordNew2 */ new: $value); + +/* testReservedKeywordOr1 */ +foobar(or: $value, /* testReservedKeywordOr2 */ or: $value); + +/* testReservedKeywordPrint1 */ +foobar(print: $value, /* testReservedKeywordPrint2 */ print: $value); + +/* testReservedKeywordPrivate1 */ +foobar(private: $value, /* testReservedKeywordPrivate2 */ private: $value); + +/* testReservedKeywordProtected1 */ +foobar(protected: $value, /* testReservedKeywordProtected2 */ protected: $value); + +/* testReservedKeywordPublic1 */ +foobar(public: $value, /* testReservedKeywordPublic2 */ public: $value); + +/* testReservedKeywordReadonly1 */ +foobar(readonly: $value, /* testReservedKeywordReadonly2 */ readonly: $value); + +/* testReservedKeywordRequire1 */ +foobar(require: $value, /* testReservedKeywordRequire2 */ require: $value); + +/* testReservedKeywordRequire_once1 */ +foobar(require_once: $value, /* testReservedKeywordRequire_once2 */ require_once: $value); + +/* testReservedKeywordReturn1 */ +foobar(return: $value, /* testReservedKeywordReturn2 */ return: $value); + +/* testReservedKeywordStatic1 */ +foobar(static: $value, /* testReservedKeywordStatic2 */ static: $value); + +/* testReservedKeywordSwitch1 */ +foobar(switch: $value, /* testReservedKeywordSwitch2 */ switch: $value); + +/* testReservedKeywordThrow1 */ +foobar(throw: $value, /* testReservedKeywordThrow2 */ throw: $value); + +/* testReservedKeywordTrait1 */ +foobar(trait: $value, /* testReservedKeywordTrait2 */ trait: $value); + +/* testReservedKeywordTry1 */ +foobar(try: $value, /* testReservedKeywordTry2 */ try: $value); + +/* testReservedKeywordUnset1 */ +foobar(unset: $value, /* testReservedKeywordUnset2 */ unset: $value); + +/* testReservedKeywordUse1 */ +foobar(use: $value, /* testReservedKeywordUse2 */ use: $value); + +/* testReservedKeywordVar1 */ +foobar(var: $value, /* testReservedKeywordVar2 */ var: $value); + +/* testReservedKeywordWhile1 */ +foobar(while: $value, /* testReservedKeywordWhile2 */ while: $value); + +/* testReservedKeywordXor1 */ +foobar(xor: $value, /* testReservedKeywordXor2 */ xor: $value); + +/* testReservedKeywordYield1 */ +foobar(yield: $value, /* testReservedKeywordYield2 */ yield: $value); + +/* testReservedKeywordInt1 */ +foobar(int: $value, /* testReservedKeywordInt2 */ int: $value); + +/* testReservedKeywordFloat1 */ +foobar(float: $value, /* testReservedKeywordFloat2 */ float: $value); + +/* testReservedKeywordBool1 */ +foobar(bool: $value, /* testReservedKeywordBool2 */ bool: $value); + +/* testReservedKeywordString1 */ +foobar(string: $value, /* testReservedKeywordString2 */ string: $value); + +/* testReservedKeywordTrue1 */ +foobar(true: $value, /* testReservedKeywordTrue2 */ true: $value); + +/* testReservedKeywordFalse1 */ +foobar(false: $value, /* testReservedKeywordFalse2 */ false: $value); + +/* testReservedKeywordNull1 */ +foobar(null: $value, /* testReservedKeywordNull2 */ null: $value); + +/* testReservedKeywordVoid1 */ +foobar(void: $value, /* testReservedKeywordVoid2 */ void: $value); + +/* testReservedKeywordIterable1 */ +foobar(iterable: $value, /* testReservedKeywordIterable2 */ iterable: $value); + +/* testReservedKeywordObject1 */ +foobar(object: $value, /* testReservedKeywordObject2 */ object: $value); + +/* testReservedKeywordResource1 */ +foobar(resource: $value, /* testReservedKeywordResource2 */ resource: $value); + +/* testReservedKeywordMixed1 */ +foobar(mixed: $value, /* testReservedKeywordMixed2 */ mixed: $value); + +/* testReservedKeywordNumeric1 */ +foobar(numeric: $value, /* testReservedKeywordNumeric2 */ numeric: $value); + +/* testReservedKeywordParent1 */ +foobar(parent: $value, /* testReservedKeywordParent2 */ parent: $value); + +/* testReservedKeywordSelf1 */ +foobar(self: $value, /* testReservedKeywordSelf2 */ self: $value); + +/* testReservedKeywordNever1 */ +foobar(never: $value, /* testReservedKeywordNever2 */ never: $value); diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.php new file mode 100644 index 00000000..cc57637c --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.php @@ -0,0 +1,885 @@ + + * @copyright 2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; +use PHP_CodeSniffer\Util\Tokens; + +class NamedFunctionCallArgumentsTest extends AbstractMethodUnitTest +{ + + + /** + * Verify that parameter labels are tokenized as T_PARAM_NAME and that + * the colon after it is tokenized as a T_COLON. + * + * @param string $testMarker The comment prefacing the target token. + * @param array $parameters The token content for each parameter label to look for. + * + * @dataProvider dataNamedFunctionCallArguments + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testNamedFunctionCallArguments($testMarker, $parameters) + { + $tokens = self::$phpcsFile->getTokens(); + + foreach ($parameters as $content) { + $label = $this->getTargetToken($testMarker, [T_STRING, T_PARAM_NAME], $content); + + $this->assertSame( + T_PARAM_NAME, + $tokens[$label]['code'], + 'Token tokenized as '.$tokens[$label]['type'].', not T_PARAM_NAME (code)' + ); + $this->assertSame( + 'T_PARAM_NAME', + $tokens[$label]['type'], + 'Token tokenized as '.$tokens[$label]['type'].', not T_PARAM_NAME (type)' + ); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true); + + $this->assertSame( + ':', + $tokens[$colon]['content'], + 'Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content'] + ); + $this->assertSame( + T_COLON, + $tokens[$colon]['code'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)' + ); + $this->assertSame( + 'T_COLON', + $tokens[$colon]['type'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)' + ); + }//end foreach + + }//end testNamedFunctionCallArguments() + + + /** + * Data provider. + * + * @see testNamedFunctionCallArguments() + * + * @return array + */ + public function dataNamedFunctionCallArguments() + { + return [ + [ + '/* testNamedArgs */', + [ + 'start_index', + 'count', + 'value', + ], + ], + [ + '/* testNamedArgsMultiline */', + [ + 'start_index', + 'count', + 'value', + ], + ], + [ + '/* testNamedArgsWithWhitespaceAndComments */', + [ + 'start_index', + 'count', + 'value', + ], + ], + [ + '/* testMixedPositionalAndNamedArgs */', + ['double_encode'], + ], + [ + '/* testNestedFunctionCallOuter */', + [ + 'start_index', + 'count', + 'value', + ], + ], + [ + '/* testNestedFunctionCallInner1 */', + ['skip'], + ], + [ + '/* testNestedFunctionCallInner2 */', + ['array_or_countable'], + ], + [ + '/* testNamespaceOperatorFunction */', + [ + 'label', + 'more', + ], + ], + [ + '/* testNamespaceRelativeFunction */', + [ + 'label', + 'more', + ], + ], + [ + '/* testNamespacedFQNFunction */', + [ + 'label', + 'more', + ], + ], + [ + '/* testVariableFunction */', + [ + 'label', + 'more', + ], + ], + [ + '/* testVariableVariableFunction */', + [ + 'label', + 'more', + ], + ], + [ + '/* testMethodCall */', + [ + 'label', + 'more', + ], + ], + [ + '/* testVariableMethodCall */', + [ + 'label', + 'more', + ], + ], + [ + '/* testClassInstantiation */', + [ + 'label', + 'more', + ], + ], + [ + '/* testClassInstantiationSelf */', + [ + 'label', + 'more', + ], + ], + [ + '/* testClassInstantiationStatic */', + [ + 'label', + 'more', + ], + ], + [ + '/* testAnonClass */', + [ + 'label', + 'more', + ], + ], + [ + '/* testNonAsciiNames */', + [ + '💩💩💩', + 'Пасха', + '_valid', + ], + ], + + // Coding errors which should still be handled. + [ + '/* testCompileErrorNamedBeforePositional */', + ['param'], + ], + [ + '/* testDuplicateName1 */', + ['param'], + ], + [ + '/* testDuplicateName2 */', + ['param'], + ], + [ + '/* testIncorrectOrderWithVariadic */', + ['start_index'], + ], + [ + '/* testCompileErrorIncorrectOrderWithVariadic */', + ['param'], + ], + [ + '/* testParseErrorNoValue */', + [ + 'param1', + 'param2', + ], + ], + [ + '/* testParseErrorExit */', + ['status'], + ], + [ + '/* testParseErrorEmpty */', + ['variable'], + ], + [ + '/* testParseErrorEval */', + ['code'], + ], + [ + '/* testParseErrorArbitraryParentheses */', + ['something'], + ], + ]; + + }//end dataNamedFunctionCallArguments() + + + /** + * Verify that other T_STRING tokens within a function call are still tokenized as T_STRING. + * + * @param string $testMarker The comment prefacing the target token. + * @param string $content The token content to look for. + * + * @dataProvider dataOtherTstringInFunctionCall + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testOtherTstringInFunctionCall($testMarker, $content) + { + $tokens = self::$phpcsFile->getTokens(); + + $label = $this->getTargetToken($testMarker, [T_STRING, T_PARAM_NAME], $content); + + $this->assertSame( + T_STRING, + $tokens[$label]['code'], + 'Token tokenized as '.$tokens[$label]['type'].', not T_STRING (code)' + ); + $this->assertSame( + 'T_STRING', + $tokens[$label]['type'], + 'Token tokenized as '.$tokens[$label]['type'].', not T_STRING (type)' + ); + + }//end testOtherTstringInFunctionCall() + + + /** + * Data provider. + * + * @see testOtherTstringInFunctionCall() + * + * @return array + */ + public function dataOtherTstringInFunctionCall() + { + return [ + [ + '/* testPositionalArgs */', + 'START_INDEX', + ], + [ + '/* testPositionalArgs */', + 'COUNT', + ], + [ + '/* testPositionalArgs */', + 'VALUE', + ], + [ + '/* testNestedFunctionCallInner2 */', + 'count', + ], + ]; + + }//end dataOtherTstringInFunctionCall() + + + /** + * Verify whether the colons are tokenized correctly when a ternary is used in a mixed + * positional and named arguments function call. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testMixedPositionalAndNamedArgsWithTernary() + { + $tokens = self::$phpcsFile->getTokens(); + + $true = $this->getTargetToken('/* testMixedPositionalAndNamedArgsWithTernary */', T_TRUE); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($true + 1), null, true); + + $this->assertSame( + T_INLINE_ELSE, + $tokens[$colon]['code'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (code)' + ); + $this->assertSame( + 'T_INLINE_ELSE', + $tokens[$colon]['type'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (type)' + ); + + $label = $this->getTargetToken('/* testMixedPositionalAndNamedArgsWithTernary */', T_PARAM_NAME, 'name'); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true); + + $this->assertSame( + ':', + $tokens[$colon]['content'], + 'Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content'] + ); + $this->assertSame( + T_COLON, + $tokens[$colon]['code'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)' + ); + $this->assertSame( + 'T_COLON', + $tokens[$colon]['type'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)' + ); + + }//end testMixedPositionalAndNamedArgsWithTernary() + + + /** + * Verify whether the colons are tokenized correctly when a ternary is used + * in a named arguments function call. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testNamedArgWithTernary() + { + $tokens = self::$phpcsFile->getTokens(); + + /* + * First argument. + */ + + $label = $this->getTargetToken('/* testNamedArgWithTernary */', T_PARAM_NAME, 'label'); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true); + + $this->assertSame( + ':', + $tokens[$colon]['content'], + 'First arg: Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content'] + ); + $this->assertSame( + T_COLON, + $tokens[$colon]['code'], + 'First arg: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)' + ); + $this->assertSame( + 'T_COLON', + $tokens[$colon]['type'], + 'First arg: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)' + ); + + $true = $this->getTargetToken('/* testNamedArgWithTernary */', T_TRUE); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($true + 1), null, true); + + $this->assertSame( + T_INLINE_ELSE, + $tokens[$colon]['code'], + 'First arg ternary: Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (code)' + ); + $this->assertSame( + 'T_INLINE_ELSE', + $tokens[$colon]['type'], + 'First arg ternary: Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (type)' + ); + + /* + * Second argument. + */ + + $label = $this->getTargetToken('/* testNamedArgWithTernary */', T_PARAM_NAME, 'more'); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true); + + $this->assertSame( + ':', + $tokens[$colon]['content'], + 'Second arg: Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content'] + ); + $this->assertSame( + T_COLON, + $tokens[$colon]['code'], + 'Second arg: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)' + ); + $this->assertSame( + 'T_COLON', + $tokens[$colon]['type'], + 'Second arg: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)' + ); + + $true = $this->getTargetToken('/* testNamedArgWithTernary */', T_STRING, 'CONSTANT_A'); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($true + 1), null, true); + + $this->assertSame( + T_INLINE_ELSE, + $tokens[$colon]['code'], + 'Second arg ternary: Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (code)' + ); + $this->assertSame( + 'T_INLINE_ELSE', + $tokens[$colon]['type'], + 'Second arg ternary: Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (type)' + ); + + }//end testNamedArgWithTernary() + + + /** + * Verify whether the colons are tokenized correctly when named arguments + * function calls are used in a ternary. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testTernaryWithFunctionCallsInThenElse() + { + $tokens = self::$phpcsFile->getTokens(); + + /* + * Then. + */ + + $label = $this->getTargetToken('/* testTernaryWithFunctionCallsInThenElse */', T_PARAM_NAME, 'label'); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true); + + $this->assertSame( + ':', + $tokens[$colon]['content'], + 'Function in then: Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content'] + ); + $this->assertSame( + T_COLON, + $tokens[$colon]['code'], + 'Function in then: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)' + ); + $this->assertSame( + 'T_COLON', + $tokens[$colon]['type'], + 'Function in then: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)' + ); + + $closeParens = $this->getTargetToken('/* testTernaryWithFunctionCallsInThenElse */', T_CLOSE_PARENTHESIS); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($closeParens + 1), null, true); + + $this->assertSame( + T_INLINE_ELSE, + $tokens[$colon]['code'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (code)' + ); + $this->assertSame( + 'T_INLINE_ELSE', + $tokens[$colon]['type'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (type)' + ); + + /* + * Else. + */ + + $label = $this->getTargetToken('/* testTernaryWithFunctionCallsInThenElse */', T_PARAM_NAME, 'more'); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true); + + $this->assertSame( + ':', + $tokens[$colon]['content'], + 'Function in else: Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content'] + ); + $this->assertSame( + T_COLON, + $tokens[$colon]['code'], + 'Function in else: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)' + ); + $this->assertSame( + 'T_COLON', + $tokens[$colon]['type'], + 'Function in else: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)' + ); + + }//end testTernaryWithFunctionCallsInThenElse() + + + /** + * Verify whether the colons are tokenized correctly when constants are used in a ternary. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testTernaryWithConstantsInThenElse() + { + $tokens = self::$phpcsFile->getTokens(); + + $constant = $this->getTargetToken('/* testTernaryWithConstantsInThenElse */', T_STRING, 'CONSTANT_NAME'); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($constant + 1), null, true); + + $this->assertSame( + T_INLINE_ELSE, + $tokens[$colon]['code'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (code)' + ); + $this->assertSame( + 'T_INLINE_ELSE', + $tokens[$colon]['type'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (type)' + ); + + }//end testTernaryWithConstantsInThenElse() + + + /** + * Verify whether the colons are tokenized correctly in a switch statement. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testSwitchStatement() + { + $tokens = self::$phpcsFile->getTokens(); + + $label = $this->getTargetToken('/* testSwitchCaseWithConstant */', T_STRING, 'MY_CONSTANT'); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true); + + $this->assertSame( + T_COLON, + $tokens[$colon]['code'], + 'First case: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)' + ); + $this->assertSame( + 'T_COLON', + $tokens[$colon]['type'], + 'First case: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)' + ); + + $label = $this->getTargetToken('/* testSwitchCaseWithClassProperty */', T_STRING, 'property'); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true); + + $this->assertSame( + T_COLON, + $tokens[$colon]['code'], + 'Second case: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)' + ); + $this->assertSame( + 'T_COLON', + $tokens[$colon]['type'], + 'Second case: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)' + ); + + $default = $this->getTargetToken('/* testSwitchDefault */', T_DEFAULT); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($default + 1), null, true); + + $this->assertSame( + T_COLON, + $tokens[$colon]['code'], + 'Default case: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)' + ); + $this->assertSame( + 'T_COLON', + $tokens[$colon]['type'], + 'Default case: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)' + ); + + }//end testSwitchStatement() + + + /** + * Verify that a variable parameter label (parse error) is still tokenized as T_VARIABLE. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testParseErrorVariableLabel() + { + $tokens = self::$phpcsFile->getTokens(); + + $label = $this->getTargetToken('/* testParseErrorDynamicName */', [T_VARIABLE, T_PARAM_NAME], '$variableStoringParamName'); + + $this->assertSame( + T_VARIABLE, + $tokens[$label]['code'], + 'Token tokenized as '.$tokens[$label]['type'].', not T_VARIABLE (code)' + ); + $this->assertSame( + 'T_VARIABLE', + $tokens[$label]['type'], + 'Token tokenized as '.$tokens[$label]['type'].', not T_VARIABLE (type)' + ); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true); + + $this->assertSame( + ':', + $tokens[$colon]['content'], + 'Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content'] + ); + $this->assertSame( + T_COLON, + $tokens[$colon]['code'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)' + ); + $this->assertSame( + 'T_COLON', + $tokens[$colon]['type'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)' + ); + + }//end testParseErrorVariableLabel() + + + /** + * Verify that reserved keywords used as a parameter label are tokenized as T_PARAM_NAME + * and that the colon after it is tokenized as a T_COLON. + * + * @param string $testMarker The comment prefacing the target token. + * @param array $tokenTypes The token codes to look for. + * @param string $tokenContent The token content to look for. + * + * @dataProvider dataReservedKeywordsAsName + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testReservedKeywordsAsName($testMarker, $tokenTypes, $tokenContent) + { + $tokens = self::$phpcsFile->getTokens(); + $label = $this->getTargetToken($testMarker, $tokenTypes, $tokenContent); + + $this->assertSame( + T_PARAM_NAME, + $tokens[$label]['code'], + 'Token tokenized as '.$tokens[$label]['type'].', not T_PARAM_NAME (code)' + ); + $this->assertSame( + 'T_PARAM_NAME', + $tokens[$label]['type'], + 'Token tokenized as '.$tokens[$label]['type'].', not T_PARAM_NAME (type)' + ); + + // Get the next non-empty token. + $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true); + + $this->assertSame( + ':', + $tokens[$colon]['content'], + 'Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content'] + ); + $this->assertSame( + T_COLON, + $tokens[$colon]['code'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)' + ); + $this->assertSame( + 'T_COLON', + $tokens[$colon]['type'], + 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)' + ); + + }//end testReservedKeywordsAsName() + + + /** + * Data provider. + * + * @see testReservedKeywordsAsName() + * + * @return array + */ + public function dataReservedKeywordsAsName() + { + $reservedKeywords = [ + // '__halt_compiler', NOT TESTABLE + 'abstract', + 'and', + 'array', + 'as', + 'break', + 'callable', + 'case', + 'catch', + 'class', + 'clone', + 'const', + 'continue', + 'declare', + 'default', + 'die', + 'do', + 'echo', + 'else', + 'elseif', + 'empty', + 'enddeclare', + 'endfor', + 'endforeach', + 'endif', + 'endswitch', + 'endwhile', + 'enum', + 'eval', + 'exit', + 'extends', + 'final', + 'finally', + 'fn', + 'for', + 'foreach', + 'function', + 'global', + 'goto', + 'if', + 'implements', + 'include', + 'include_once', + 'instanceof', + 'insteadof', + 'interface', + 'isset', + 'list', + 'match', + 'namespace', + 'new', + 'or', + 'print', + 'private', + 'protected', + 'public', + 'readonly', + 'require', + 'require_once', + 'return', + 'static', + 'switch', + 'throw', + 'trait', + 'try', + 'unset', + 'use', + 'var', + 'while', + 'xor', + 'yield', + 'int', + 'float', + 'bool', + 'string', + 'true', + 'false', + 'null', + 'void', + 'iterable', + 'object', + 'resource', + 'mixed', + 'numeric', + 'never', + + // Not reserved keyword, but do have their own token in PHPCS. + 'parent', + 'self', + ]; + + $data = []; + + foreach ($reservedKeywords as $keyword) { + $tokensTypes = [ + T_PARAM_NAME, + T_STRING, + T_GOTO_LABEL, + ]; + $tokenName = 'T_'.strtoupper($keyword); + + if ($keyword === 'and') { + $tokensTypes[] = T_LOGICAL_AND; + } else if ($keyword === 'die') { + $tokensTypes[] = T_EXIT; + } else if ($keyword === 'or') { + $tokensTypes[] = T_LOGICAL_OR; + } else if ($keyword === 'xor') { + $tokensTypes[] = T_LOGICAL_XOR; + } else if ($keyword === '__halt_compiler') { + $tokensTypes[] = T_HALT_COMPILER; + } else if (defined($tokenName) === true) { + $tokensTypes[] = constant($tokenName); + } + + $data[$keyword.'FirstParam'] = [ + '/* testReservedKeyword'.ucfirst($keyword).'1 */', + $tokensTypes, + $keyword, + ]; + + $data[$keyword.'SecondParam'] = [ + '/* testReservedKeyword'.ucfirst($keyword).'2 */', + $tokensTypes, + $keyword, + ]; + }//end foreach + + return $data; + + }//end dataReservedKeywordsAsName() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NullsafeObjectOperatorTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NullsafeObjectOperatorTest.inc new file mode 100644 index 00000000..982841ea --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NullsafeObjectOperatorTest.inc @@ -0,0 +1,29 @@ +foo; + +/* testNullsafeObjectOperator */ +echo $obj?->foo; + +/* testNullsafeObjectOperatorWriteContext */ +// Intentional parse error, but not the concern of the tokenizer. +$foo?->bar->baz = 'baz'; + +/* testTernaryThen */ +echo $obj ? $obj->prop : $other->prop; + +/* testParseErrorWhitespaceNotAllowed */ +echo $obj ? + -> foo; + +/* testParseErrorCommentNotAllowed */ +echo $obj ?/*comment*/-> foo; + +/* testLiveCoding */ +// Intentional parse error. This has to be the last test in the file. +echo $obj? diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NullsafeObjectOperatorTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NullsafeObjectOperatorTest.php new file mode 100644 index 00000000..8e465a3b --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/NullsafeObjectOperatorTest.php @@ -0,0 +1,140 @@ += 8.0 nullsafe object operator. + * + * @author Juliette Reinders Folmer + * @copyright 2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; +use PHP_CodeSniffer\Util\Tokens; + +class NullsafeObjectOperatorTest extends AbstractMethodUnitTest +{ + + /** + * Tokens to search for. + * + * @var array + */ + protected $find = [ + T_NULLSAFE_OBJECT_OPERATOR, + T_OBJECT_OPERATOR, + T_INLINE_THEN, + ]; + + + /** + * Test that a normal object operator is still tokenized as such. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testObjectOperator() + { + $tokens = self::$phpcsFile->getTokens(); + + $operator = $this->getTargetToken('/* testObjectOperator */', $this->find); + $this->assertSame(T_OBJECT_OPERATOR, $tokens[$operator]['code'], 'Failed asserting code is object operator'); + $this->assertSame('T_OBJECT_OPERATOR', $tokens[$operator]['type'], 'Failed asserting type is object operator'); + + }//end testObjectOperator() + + + /** + * Test that a nullsafe object operator is tokenized as such. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataNullsafeObjectOperator + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testNullsafeObjectOperator($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $operator = $this->getTargetToken($testMarker, $this->find); + $this->assertSame(T_NULLSAFE_OBJECT_OPERATOR, $tokens[$operator]['code'], 'Failed asserting code is nullsafe object operator'); + $this->assertSame('T_NULLSAFE_OBJECT_OPERATOR', $tokens[$operator]['type'], 'Failed asserting type is nullsafe object operator'); + + }//end testNullsafeObjectOperator() + + + /** + * Data provider. + * + * @see testNullsafeObjectOperator() + * + * @return array + */ + public function dataNullsafeObjectOperator() + { + return [ + ['/* testNullsafeObjectOperator */'], + ['/* testNullsafeObjectOperatorWriteContext */'], + ]; + + }//end dataNullsafeObjectOperator() + + + /** + * Test that a question mark not followed by an object operator is tokenized as T_TERNARY_THEN. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param bool $testObjectOperator Whether to test for the next non-empty token being tokenized + * as an object operator. + * + * @dataProvider dataTernaryThen + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testTernaryThen($testMarker, $testObjectOperator=false) + { + $tokens = self::$phpcsFile->getTokens(); + + $operator = $this->getTargetToken($testMarker, $this->find); + $this->assertSame(T_INLINE_THEN, $tokens[$operator]['code'], 'Failed asserting code is inline then'); + $this->assertSame('T_INLINE_THEN', $tokens[$operator]['type'], 'Failed asserting type is inline then'); + + if ($testObjectOperator === true) { + $next = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($operator + 1), null, true); + $this->assertSame(T_OBJECT_OPERATOR, $tokens[$next]['code'], 'Failed asserting code is object operator'); + $this->assertSame('T_OBJECT_OPERATOR', $tokens[$next]['type'], 'Failed asserting type is object operator'); + } + + }//end testTernaryThen() + + + /** + * Data provider. + * + * @see testTernaryThen() + * + * @return array + */ + public function dataTernaryThen() + { + return [ + ['/* testTernaryThen */'], + [ + '/* testParseErrorWhitespaceNotAllowed */', + true, + ], + [ + '/* testParseErrorCommentNotAllowed */', + true, + ], + ['/* testLiveCoding */'], + ]; + + }//end dataTernaryThen() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ScopeSettingWithNamespaceOperatorTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ScopeSettingWithNamespaceOperatorTest.inc new file mode 100644 index 00000000..38e5a47d --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ScopeSettingWithNamespaceOperatorTest.inc @@ -0,0 +1,19 @@ + new namespace\Baz; diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ScopeSettingWithNamespaceOperatorTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ScopeSettingWithNamespaceOperatorTest.php new file mode 100644 index 00000000..23cbd987 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ScopeSettingWithNamespaceOperatorTest.php @@ -0,0 +1,98 @@ + + * @copyright 2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class ScopeSettingWithNamespaceOperatorTest extends AbstractMethodUnitTest +{ + + + /** + * Test that the scope opener/closers are set correctly when the namespace keyword is encountered as an operator. + * + * @param string $testMarker The comment which prefaces the target tokens in the test file. + * @param int|string[] $tokenTypes The token type to search for. + * @param int|string[] $open Optional. The token type for the scope opener. + * @param int|string[] $close Optional. The token type for the scope closer. + * + * @dataProvider dataScopeSetting + * @covers PHP_CodeSniffer\Tokenizers\Tokenizer::recurseScopeMap + * + * @return void + */ + public function testScopeSetting($testMarker, $tokenTypes, $open=T_OPEN_CURLY_BRACKET, $close=T_CLOSE_CURLY_BRACKET) + { + $tokens = self::$phpcsFile->getTokens(); + + $target = $this->getTargetToken($testMarker, $tokenTypes); + $opener = $this->getTargetToken($testMarker, $open); + $closer = $this->getTargetToken($testMarker, $close); + + $this->assertArrayHasKey('scope_opener', $tokens[$target], 'Scope opener missing'); + $this->assertArrayHasKey('scope_closer', $tokens[$target], 'Scope closer missing'); + $this->assertSame($opener, $tokens[$target]['scope_opener'], 'Scope opener not same'); + $this->assertSame($closer, $tokens[$target]['scope_closer'], 'Scope closer not same'); + + $this->assertArrayHasKey('scope_opener', $tokens[$opener], 'Scope opener missing for open curly'); + $this->assertArrayHasKey('scope_closer', $tokens[$opener], 'Scope closer missing for open curly'); + $this->assertSame($opener, $tokens[$opener]['scope_opener'], 'Scope opener not same for open curly'); + $this->assertSame($closer, $tokens[$opener]['scope_closer'], 'Scope closer not same for open curly'); + + $this->assertArrayHasKey('scope_opener', $tokens[$closer], 'Scope opener missing for close curly'); + $this->assertArrayHasKey('scope_closer', $tokens[$closer], 'Scope closer missing for close curly'); + $this->assertSame($opener, $tokens[$closer]['scope_opener'], 'Scope opener not same for close curly'); + $this->assertSame($closer, $tokens[$closer]['scope_closer'], 'Scope closer not same for close curly'); + + }//end testScopeSetting() + + + /** + * Data provider. + * + * @see testScopeSetting() + * + * @return array + */ + public function dataScopeSetting() + { + return [ + [ + '/* testClassExtends */', + [T_CLASS], + ], + [ + '/* testClassImplements */', + [T_ANON_CLASS], + ], + [ + '/* testInterfaceExtends */', + [T_INTERFACE], + ], + [ + '/* testFunctionReturnType */', + [T_FUNCTION], + ], + [ + '/* testClosureReturnType */', + [T_CLOSURE], + ], + [ + '/* testArrowFunctionReturnType */', + [T_FN], + [T_FN_ARROW], + [T_SEMICOLON], + ], + ]; + + }//end dataScopeSetting() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ShortArrayTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ShortArrayTest.inc new file mode 100644 index 00000000..60b23a51 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ShortArrayTest.inc @@ -0,0 +1,111 @@ +function_call()[$x]; + +/* testStaticMethodCallDereferencing */ +$var = ClassName::function_call()[$x]; + +/* testPropertyDereferencing */ +$var = $obj->property[2]; + +/* testPropertyDereferencingWithInaccessibleName */ +$var = $ref->{'ref-type'}[1]; + +/* testStaticPropertyDereferencing */ +$var ClassName::$property[2]; + +/* testStringDereferencing */ +$var = 'PHP'[1]; + +/* testStringDereferencingDoubleQuoted */ +$var = "PHP"[$y]; + +/* testConstantDereferencing */ +$var = MY_CONSTANT[1]; + +/* testClassConstantDereferencing */ +$var ClassName::CONSTANT_NAME[2]; + +/* testMagicConstantDereferencing */ +$var = __FILE__[0]; + +/* testArrayAccessCurlyBraces */ +$var = $array{'key'}['key']; + +/* testArrayLiteralDereferencing */ +echo array(1, 2, 3)[0]; + +echo [1, 2, 3]/* testShortArrayLiteralDereferencing */[0]; + +/* testClassMemberDereferencingOnInstantiation1 */ +(new foo)[0]; + +/* testClassMemberDereferencingOnInstantiation2 */ +$a = (new Foo( array(1, array(4, 5), 3) ))[1][0]; + +/* testClassMemberDereferencingOnClone */ +echo (clone $iterable)[20]; + +/* testNullsafeMethodCallDereferencing */ +$var = $obj?->function_call()[$x]; + +/* testInterpolatedStringDereferencing */ +$var = "PHP{$rocks}"[1]; + +/* + * Short array brackets. + */ + +/* testShortArrayDeclarationEmpty */ +$array = []; + +/* testShortArrayDeclarationWithOneValue */ +$array = [1]; + +/* testShortArrayDeclarationWithMultipleValues */ +$array = [1, 2, 3]; + +/* testShortArrayDeclarationWithDereferencing */ +echo [1, 2, 3][0]; + +/* testShortListDeclaration */ +[ $a, $b ] = $array; + +[ $a, $b, /* testNestedListDeclaration */, [$c, $d]] = $array; + +/* testArrayWithinFunctionCall */ +$var = functionCall([$x, $y]); + +if ( true ) { + /* testShortListDeclarationAfterBracedControlStructure */ + [ $a ] = [ 'hi' ]; +} + +if ( true ) + /* testShortListDeclarationAfterNonBracedControlStructure */ + [ $a ] = [ 'hi' ]; + +if ( true ) : + /* testShortListDeclarationAfterAlternativeControlStructure */ + [ $a ] = [ 'hi' ]; +endif; + +/* testLiveCoding */ +// Intentional parse error. This has to be the last test in the file. +$array = [ diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ShortArrayTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ShortArrayTest.php new file mode 100644 index 00000000..1d97894f --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/ShortArrayTest.php @@ -0,0 +1,135 @@ + + * @copyright 2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class ShortArrayTest extends AbstractMethodUnitTest +{ + + + /** + * Test that real square brackets are still tokenized as square brackets. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataSquareBrackets + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testSquareBrackets($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $opener = $this->getTargetToken($testMarker, [T_OPEN_SQUARE_BRACKET, T_OPEN_SHORT_ARRAY]); + $this->assertSame(T_OPEN_SQUARE_BRACKET, $tokens[$opener]['code']); + $this->assertSame('T_OPEN_SQUARE_BRACKET', $tokens[$opener]['type']); + + if (isset($tokens[$opener]['bracket_closer']) === true) { + $closer = $tokens[$opener]['bracket_closer']; + $this->assertSame(T_CLOSE_SQUARE_BRACKET, $tokens[$closer]['code']); + $this->assertSame('T_CLOSE_SQUARE_BRACKET', $tokens[$closer]['type']); + } + + }//end testSquareBrackets() + + + /** + * Data provider. + * + * @see testSquareBrackets() + * + * @return array + */ + public function dataSquareBrackets() + { + return [ + 'array access 1' => ['/* testArrayAccess1 */'], + 'array access 2' => ['/* testArrayAccess2 */'], + 'array assignment' => ['/* testArrayAssignment */'], + 'function call dereferencing' => ['/* testFunctionCallDereferencing */'], + 'method call dereferencing' => ['/* testMethodCallDereferencing */'], + 'static method call dereferencing' => ['/* testStaticMethodCallDereferencing */'], + 'property dereferencing' => ['/* testPropertyDereferencing */'], + 'property dereferencing with inaccessable name' => ['/* testPropertyDereferencingWithInaccessibleName */'], + 'static property dereferencing' => ['/* testStaticPropertyDereferencing */'], + 'string dereferencing single quotes' => ['/* testStringDereferencing */'], + 'string dereferencing double quotes' => ['/* testStringDereferencingDoubleQuoted */'], + 'global constant dereferencing' => ['/* testConstantDereferencing */'], + 'class constant dereferencing' => ['/* testClassConstantDereferencing */'], + 'magic constant dereferencing' => ['/* testMagicConstantDereferencing */'], + 'array access with curly braces' => ['/* testArrayAccessCurlyBraces */'], + 'array literal dereferencing' => ['/* testArrayLiteralDereferencing */'], + 'short array literal dereferencing' => ['/* testShortArrayLiteralDereferencing */'], + 'class member dereferencing on instantiation 1' => ['/* testClassMemberDereferencingOnInstantiation1 */'], + 'class member dereferencing on instantiation 2' => ['/* testClassMemberDereferencingOnInstantiation2 */'], + 'class member dereferencing on clone' => ['/* testClassMemberDereferencingOnClone */'], + 'nullsafe method call dereferencing' => ['/* testNullsafeMethodCallDereferencing */'], + 'interpolated string dereferencing' => ['/* testInterpolatedStringDereferencing */'], + 'live coding' => ['/* testLiveCoding */'], + ]; + + }//end dataSquareBrackets() + + + /** + * Test that short arrays and short lists are still tokenized as short arrays. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataShortArrays + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testShortArrays($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $opener = $this->getTargetToken($testMarker, [T_OPEN_SQUARE_BRACKET, T_OPEN_SHORT_ARRAY]); + $this->assertSame(T_OPEN_SHORT_ARRAY, $tokens[$opener]['code']); + $this->assertSame('T_OPEN_SHORT_ARRAY', $tokens[$opener]['type']); + + if (isset($tokens[$opener]['bracket_closer']) === true) { + $closer = $tokens[$opener]['bracket_closer']; + $this->assertSame(T_CLOSE_SHORT_ARRAY, $tokens[$closer]['code']); + $this->assertSame('T_CLOSE_SHORT_ARRAY', $tokens[$closer]['type']); + } + + }//end testShortArrays() + + + /** + * Data provider. + * + * @see testShortArrays() + * + * @return array + */ + public function dataShortArrays() + { + return [ + 'short array empty' => ['/* testShortArrayDeclarationEmpty */'], + 'short array with value' => ['/* testShortArrayDeclarationWithOneValue */'], + 'short array with values' => ['/* testShortArrayDeclarationWithMultipleValues */'], + 'short array with dereferencing' => ['/* testShortArrayDeclarationWithDereferencing */'], + 'short list' => ['/* testShortListDeclaration */'], + 'short list nested' => ['/* testNestedListDeclaration */'], + 'short array within function call' => ['/* testArrayWithinFunctionCall */'], + 'short list after braced control structure' => ['/* testShortListDeclarationAfterBracedControlStructure */'], + 'short list after non-braced control structure' => ['/* testShortListDeclarationAfterNonBracedControlStructure */'], + 'short list after alternative control structure' => ['/* testShortListDeclarationAfterAlternativeControlStructure */'], + ]; + + }//end dataShortArrays() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/StableCommentWhitespaceTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/StableCommentWhitespaceTest.inc new file mode 100644 index 00000000..3bf013c6 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/StableCommentWhitespaceTest.inc @@ -0,0 +1,139 @@ + + + + * @copyright 2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; +use PHP_CodeSniffer\Util\Tokens; + +class StableCommentWhitespaceTest extends AbstractMethodUnitTest +{ + + + /** + * Test that comment tokenization with new lines at the end of the comment is stable. + * + * @param string $testMarker The comment prefacing the test. + * @param array $expectedTokens The tokenization expected. + * + * @dataProvider dataCommentTokenization + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testCommentTokenization($testMarker, $expectedTokens) + { + $tokens = self::$phpcsFile->getTokens(); + $comment = $this->getTargetToken($testMarker, Tokens::$commentTokens); + + foreach ($expectedTokens as $key => $tokenInfo) { + $this->assertSame(constant($tokenInfo['type']), $tokens[$comment]['code']); + $this->assertSame($tokenInfo['type'], $tokens[$comment]['type']); + $this->assertSame($tokenInfo['content'], $tokens[$comment]['content']); + + ++$comment; + } + + }//end testCommentTokenization() + + + /** + * Data provider. + * + * @see testCommentTokenization() + * + * @return array + */ + public function dataCommentTokenization() + { + return [ + [ + '/* testSingleLineSlashComment */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineSlashCommentTrailing */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineSlashAnnotation */', + [ + [ + 'type' => 'T_PHPCS_DISABLE', + 'content' => '// phpcs:disable Stnd.Cat +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineSlashComment */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment1 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment2 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment3 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineSlashCommentWithIndent */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment1 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment2 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment3 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineSlashCommentWithAnnotationStart */', + [ + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => '// phpcs:ignore Stnd.Cat +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment2 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment3 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineSlashCommentWithAnnotationMiddle */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment1 +', + ], + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => '// @phpcs:ignore Stnd.Cat +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment3 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineSlashCommentWithAnnotationEnd */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment1 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment2 +', + ], + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => '// phpcs:ignore Stnd.Cat +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineStarComment */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '/* Single line star comment */', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineStarCommentTrailing */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '/* Comment */', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineStarAnnotation */', + [ + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => '/* phpcs:ignore Stnd.Cat */', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineStarComment */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '/* Comment1 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => ' * Comment2 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => ' * Comment3 */', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineStarCommentWithIndent */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '/* Comment1 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => ' * Comment2 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => ' * Comment3 */', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineStarCommentWithAnnotationStart */', + [ + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => '/* @phpcs:ignore Stnd.Cat +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => ' * Comment2 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => ' * Comment3 */', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineStarCommentWithAnnotationMiddle */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '/* Comment1 +', + ], + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => ' * phpcs:ignore Stnd.Cat +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => ' * Comment3 */', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineStarCommentWithAnnotationEnd */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '/* Comment1 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => ' * Comment2 +', + ], + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => ' * phpcs:ignore Stnd.Cat */', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + + [ + '/* testSingleLineDocblockComment */', + [ + [ + 'type' => 'T_DOC_COMMENT_OPEN_TAG', + 'content' => '/**', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STRING', + 'content' => 'Comment ', + ], + [ + 'type' => 'T_DOC_COMMENT_CLOSE_TAG', + 'content' => '*/', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineDocblockCommentTrailing */', + [ + [ + 'type' => 'T_DOC_COMMENT_OPEN_TAG', + 'content' => '/**', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STRING', + 'content' => 'Comment ', + ], + [ + 'type' => 'T_DOC_COMMENT_CLOSE_TAG', + 'content' => '*/', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineDocblockAnnotation */', + [ + [ + 'type' => 'T_DOC_COMMENT_OPEN_TAG', + 'content' => '/**', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => 'phpcs:ignore Stnd.Cat.Sniff ', + ], + [ + 'type' => 'T_DOC_COMMENT_CLOSE_TAG', + 'content' => '*/', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + + [ + '/* testMultiLineDocblockComment */', + [ + [ + 'type' => 'T_DOC_COMMENT_OPEN_TAG', + 'content' => '/**', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STRING', + 'content' => 'Comment1', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STRING', + 'content' => 'Comment2', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_TAG', + 'content' => '@tag', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STRING', + 'content' => 'Comment', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_CLOSE_TAG', + 'content' => '*/', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineDocblockCommentWithIndent */', + [ + [ + 'type' => 'T_DOC_COMMENT_OPEN_TAG', + 'content' => '/**', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STRING', + 'content' => 'Comment1', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STRING', + 'content' => 'Comment2', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_TAG', + 'content' => '@tag', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STRING', + 'content' => 'Comment', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_CLOSE_TAG', + 'content' => '*/', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineDocblockCommentWithAnnotation */', + [ + [ + 'type' => 'T_DOC_COMMENT_OPEN_TAG', + 'content' => '/**', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STRING', + 'content' => 'Comment', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => 'phpcs:ignore Stnd.Cat', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_TAG', + 'content' => '@tag', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STRING', + 'content' => 'Comment', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_CLOSE_TAG', + 'content' => '*/', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineDocblockCommentWithTagAnnotation */', + [ + [ + 'type' => 'T_DOC_COMMENT_OPEN_TAG', + 'content' => '/**', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STRING', + 'content' => 'Comment', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => '@phpcs:ignore Stnd.Cat', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STAR', + 'content' => '*', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_TAG', + 'content' => '@tag', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_STRING', + 'content' => 'Comment', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_DOC_COMMENT_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_DOC_COMMENT_CLOSE_TAG', + 'content' => '*/', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineHashComment */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineHashCommentTrailing */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineHashComment */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment1 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment2 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment3 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineHashCommentWithIndent */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment1 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment2 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment3 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineSlashCommentNoNewLineAtEnd */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Slash ', + ], + [ + 'type' => 'T_CLOSE_TAG', + 'content' => '?> +', + ], + ], + ], + [ + '/* testSingleLineHashCommentNoNewLineAtEnd */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '# Hash ', + ], + [ + 'type' => 'T_CLOSE_TAG', + 'content' => '?> +', + ], + ], + ], + [ + '/* testCommentAtEndOfFile */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '/* Comment', + ], + ], + ], + ]; + + }//end dataCommentTokenization() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/StableCommentWhitespaceWinTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/StableCommentWhitespaceWinTest.inc new file mode 100644 index 00000000..dc98eb01 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/StableCommentWhitespaceWinTest.inc @@ -0,0 +1,63 @@ + + + + * @copyright 2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; +use PHP_CodeSniffer\Util\Tokens; + +class StableCommentWhitespaceWinTest extends AbstractMethodUnitTest +{ + + + /** + * Test that comment tokenization with new lines at the end of the comment is stable. + * + * @param string $testMarker The comment prefacing the test. + * @param array $expectedTokens The tokenization expected. + * + * @dataProvider dataCommentTokenization + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testCommentTokenization($testMarker, $expectedTokens) + { + $tokens = self::$phpcsFile->getTokens(); + $comment = $this->getTargetToken($testMarker, Tokens::$commentTokens); + + foreach ($expectedTokens as $key => $tokenInfo) { + $this->assertSame(constant($tokenInfo['type']), $tokens[$comment]['code']); + $this->assertSame($tokenInfo['type'], $tokens[$comment]['type']); + $this->assertSame($tokenInfo['content'], $tokens[$comment]['content']); + + ++$comment; + } + + }//end testCommentTokenization() + + + /** + * Data provider. + * + * @see testCommentTokenization() + * + * @return array + */ + public function dataCommentTokenization() + { + return [ + [ + '/* testSingleLineSlashComment */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineSlashCommentTrailing */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineSlashAnnotation */', + [ + [ + 'type' => 'T_PHPCS_DISABLE', + 'content' => '// phpcs:disable Stnd.Cat +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineSlashComment */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment1 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment2 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment3 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineSlashCommentWithIndent */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment1 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment2 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment3 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineSlashCommentWithAnnotationStart */', + [ + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => '// phpcs:ignore Stnd.Cat +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment2 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment3 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineSlashCommentWithAnnotationMiddle */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment1 +', + ], + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => '// @phpcs:ignore Stnd.Cat +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment3 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineSlashCommentWithAnnotationEnd */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment1 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '// Comment2 +', + ], + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => '// phpcs:ignore Stnd.Cat +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineSlashCommentNoNewLineAtEnd */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '// Slash ', + ], + [ + 'type' => 'T_CLOSE_TAG', + 'content' => '?> +', + ], + ], + ], + [ + '/* testSingleLineHashComment */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineHashCommentTrailing */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineHashComment */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment1 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment2 +', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment3 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testMultiLineHashCommentWithIndent */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment1 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment2 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '# Comment3 +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testSingleLineHashCommentNoNewLineAtEnd */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '# Hash ', + ], + [ + 'type' => 'T_CLOSE_TAG', + 'content' => '?> +', + ], + ], + ], + [ + '/* testCommentAtEndOfFile */', + [ + [ + 'type' => 'T_COMMENT', + 'content' => '/* Comment', + ], + ], + ], + ]; + + }//end dataCommentTokenization() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/TypeIntersectionTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/TypeIntersectionTest.inc new file mode 100644 index 00000000..abf9b85b --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/TypeIntersectionTest.inc @@ -0,0 +1,120 @@ + $param & $int; + +/* testTypeIntersectionArrowReturnType */ +$arrowWithReturnType = fn ($param) : Foo&Bar => $param * 10; + +/* testBitwiseAndInArrayKey */ +$array = array( + A & B => /* testBitwiseAndInArrayValue */ B & C +); + +/* testBitwiseAndInShortArrayKey */ +$array = [ + A & B => /* testBitwiseAndInShortArrayValue */ B & C +]; + +/* testBitwiseAndNonArrowFnFunctionCall */ +$obj->fn($something & $else); + +/* testBitwiseAnd6 */ +function &fn(/* testTypeIntersectionNonArrowFunctionDeclaration */ Foo&Bar $something) {} + +/* testTypeIntersectionWithInvalidTypes */ +function (int&string $var) {}; + +/* testLiveCoding */ +// Intentional parse error. This has to be the last test in the file. +return function( Foo& diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/TypeIntersectionTest.php b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/TypeIntersectionTest.php new file mode 100644 index 00000000..21700219 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/TypeIntersectionTest.php @@ -0,0 +1,138 @@ + + * @author Jaroslav Hanslík + * @copyright 2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class TypeIntersectionTest extends AbstractMethodUnitTest +{ + + + /** + * Test that non-intersection type bitwise and tokens are still tokenized as bitwise and. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataBitwiseAnd + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testBitwiseAnd($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $opener = $this->getTargetToken($testMarker, [T_BITWISE_AND, T_TYPE_INTERSECTION]); + $this->assertSame(T_BITWISE_AND, $tokens[$opener]['code']); + $this->assertSame('T_BITWISE_AND', $tokens[$opener]['type']); + + }//end testBitwiseAnd() + + + /** + * Data provider. + * + * @see testBitwiseAnd() + * + * @return array + */ + public function dataBitwiseAnd() + { + return [ + ['/* testBitwiseAnd1 */'], + ['/* testBitwiseAnd2 */'], + ['/* testBitwiseAndPropertyDefaultValue */'], + ['/* testBitwiseAndParamDefaultValue */'], + ['/* testBitwiseAnd3 */'], + ['/* testBitwiseAnd4 */'], + ['/* testBitwiseAnd5 */'], + ['/* testBitwiseAndClosureParamDefault */'], + ['/* testBitwiseAndArrowParamDefault */'], + ['/* testBitwiseAndArrowExpression */'], + ['/* testBitwiseAndInArrayKey */'], + ['/* testBitwiseAndInArrayValue */'], + ['/* testBitwiseAndInShortArrayKey */'], + ['/* testBitwiseAndInShortArrayValue */'], + ['/* testBitwiseAndNonArrowFnFunctionCall */'], + ['/* testBitwiseAnd6 */'], + ['/* testLiveCoding */'], + ]; + + }//end dataBitwiseAnd() + + + /** + * Test that bitwise and tokens when used as part of a intersection type are tokenized as `T_TYPE_INTERSECTION`. + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @dataProvider dataTypeIntersection + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testTypeIntersection($testMarker) + { + $tokens = self::$phpcsFile->getTokens(); + + $opener = $this->getTargetToken($testMarker, [T_BITWISE_AND, T_TYPE_INTERSECTION]); + $this->assertSame(T_TYPE_INTERSECTION, $tokens[$opener]['code']); + $this->assertSame('T_TYPE_INTERSECTION', $tokens[$opener]['type']); + + }//end testTypeIntersection() + + + /** + * Data provider. + * + * @see testTypeIntersection() + * + * @return array + */ + public function dataTypeIntersection() + { + return [ + ['/* testTypeIntersectionPropertySimple */'], + ['/* testTypeIntersectionPropertyReverseModifierOrder */'], + ['/* testTypeIntersectionPropertyMulti1 */'], + ['/* testTypeIntersectionPropertyMulti2 */'], + ['/* testTypeIntersectionPropertyMulti3 */'], + ['/* testTypeIntersectionPropertyNamespaceRelative */'], + ['/* testTypeIntersectionPropertyPartiallyQualified */'], + ['/* testTypeIntersectionPropertyFullyQualified */'], + ['/* testTypeIntersectionPropertyWithReadOnlyKeyword */'], + ['/* testTypeIntersectionParam1 */'], + ['/* testTypeIntersectionParam2 */'], + ['/* testTypeIntersectionParam3 */'], + ['/* testTypeIntersectionParamNamespaceRelative */'], + ['/* testTypeIntersectionParamPartiallyQualified */'], + ['/* testTypeIntersectionParamFullyQualified */'], + ['/* testTypeIntersectionReturnType */'], + ['/* testTypeIntersectionConstructorPropertyPromotion */'], + ['/* testTypeIntersectionAbstractMethodReturnType1 */'], + ['/* testTypeIntersectionAbstractMethodReturnType2 */'], + ['/* testTypeIntersectionReturnTypeNamespaceRelative */'], + ['/* testTypeIntersectionReturnPartiallyQualified */'], + ['/* testTypeIntersectionReturnFullyQualified */'], + ['/* testTypeIntersectionClosureParamIllegalNullable */'], + ['/* testTypeIntersectionWithReference */'], + ['/* testTypeIntersectionWithSpreadOperator */'], + ['/* testTypeIntersectionClosureReturn */'], + ['/* testTypeIntersectionArrowParam */'], + ['/* testTypeIntersectionArrowReturnType */'], + ['/* testTypeIntersectionNonArrowFunctionDeclaration */'], + ['/* testTypeIntersectionWithInvalidTypes */'], + ]; + + }//end dataTypeIntersection() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/UndoNamespacedNameSingleTokenTest.inc b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/UndoNamespacedNameSingleTokenTest.inc new file mode 100644 index 00000000..540f72c9 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/UndoNamespacedNameSingleTokenTest.inc @@ -0,0 +1,147 @@ + + * @copyright 2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class UndoNamespacedNameSingleTokenTest extends AbstractMethodUnitTest +{ + + + /** + * Test that identifier names are tokenized the same across PHP versions, based on the PHP 5/7 tokenization. + * + * @param string $testMarker The comment prefacing the test. + * @param array $expectedTokens The tokenization expected. + * + * @dataProvider dataIdentifierTokenization + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testIdentifierTokenization($testMarker, $expectedTokens) + { + $tokens = self::$phpcsFile->getTokens(); + $identifier = $this->getTargetToken($testMarker, constant($expectedTokens[0]['type'])); + + foreach ($expectedTokens as $key => $tokenInfo) { + $this->assertSame(constant($tokenInfo['type']), $tokens[$identifier]['code']); + $this->assertSame($tokenInfo['type'], $tokens[$identifier]['type']); + $this->assertSame($tokenInfo['content'], $tokens[$identifier]['content']); + + ++$identifier; + } + + }//end testIdentifierTokenization() + + + /** + * Data provider. + * + * @see testIdentifierTokenization() + * + * @return array + */ + public function dataIdentifierTokenization() + { + return [ + [ + '/* testNamespaceDeclaration */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Package', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testNamespaceDeclarationWithLevels */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'SubLevel', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Domain', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testUseStatement */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testUseStatementWithLevels */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Domain', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testFunctionUseStatement */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'function', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testFunctionUseStatementWithLevels */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'function', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_in_ns', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testConstantUseStatement */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'const', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'CONSTANT_NAME', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testConstantUseStatementWithLevels */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'const', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'OTHER_CONSTANT', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testMultiUseUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'UnqualifiedClassName', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + ], + ], + [ + '/* testMultiUsePartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Sublevel', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'PartiallyClassName', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testGroupUseStatement */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_OPEN_USE_GROUP', + 'content' => '{', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'AnotherDomain', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_grouped', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'const', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'CONSTANT_GROUPED', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Sub', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'YetAnotherDomain', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'SubLevelA', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_grouped_too', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'const', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'SubLevelB', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'CONSTANT_GROUPED_TOO', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_CLOSE_USE_GROUP', + 'content' => '}', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testClassName */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'MyClass', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testExtendedFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'FQN', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testImplementsRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + ], + ], + [ + '/* testImplementsFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Fully', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Qualified', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + ], + ], + [ + '/* testImplementsUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Unqualified', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + ], + ], + [ + '/* testImplementsPartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Sub', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testFunctionName */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testTypeDeclarationRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + [ + 'type' => 'T_TYPE_UNION', + 'content' => '|', + ], + [ + 'type' => 'T_STRING', + 'content' => 'object', + ], + ], + ], + [ + '/* testTypeDeclarationFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Fully', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Qualified', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testTypeDeclarationUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Unqualified', + ], + [ + 'type' => 'T_TYPE_UNION', + 'content' => '|', + ], + [ + 'type' => 'T_FALSE', + 'content' => 'false', + ], + ], + ], + [ + '/* testTypeDeclarationPartiallyQualified */', + [ + [ + 'type' => 'T_NULLABLE', + 'content' => '?', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Sublevel', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testReturnTypeFQN */', + [ + [ + 'type' => 'T_NULLABLE', + 'content' => '?', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testFunctionCallRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'NameSpace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testFunctionCallFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Package', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testFunctionCallUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testFunctionPartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testCatchRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'SubLevel', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Exception', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testCatchFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Exception', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testCatchUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Exception', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testCatchPartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Exception', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testNewRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testNewFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testNewUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testNewPartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testDoubleColonRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_DOUBLE_COLON', + 'content' => '::', + ], + ], + ], + [ + '/* testDoubleColonFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_DOUBLE_COLON', + 'content' => '::', + ], + ], + ], + [ + '/* testDoubleColonUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_DOUBLE_COLON', + 'content' => '::', + ], + ], + ], + [ + '/* testDoubleColonPartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_DOUBLE_COLON', + 'content' => '::', + ], + ], + ], + [ + '/* testInstanceOfRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testInstanceOfFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Full', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_CLOSE_PARENTHESIS', + 'content' => ')', + ], + ], + ], + [ + '/* testInstanceOfUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testInstanceOfPartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Partially', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testInvalidInPHP8Whitespace */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Sublevel', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testInvalidInPHP8Comments */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Fully', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => '// phpcs:ignore Stnd.Cat.Sniff -- for reasons +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Qualified', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '/* comment */', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + ]; + + }//end dataIdentifierTokenization() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/FileList.php b/vendor/squizlabs/php_codesniffer/tests/FileList.php new file mode 100644 index 00000000..8ef57b7a --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/FileList.php @@ -0,0 +1,94 @@ + + * @copyright 2019 Juliette Reinders Folmer. All rights reserved. + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests; + +class FileList +{ + + /** + * The path to the project root directory. + * + * @var string + */ + protected $rootPath; + + /** + * Recursive directory iterator. + * + * @var \DirectoryIterator + */ + public $fileIterator; + + /** + * Base regex to use if no filter regex is provided. + * + * Matches based on: + * - File path starts with the project root (replacement done in constructor). + * - Don't match .git/ files. + * - Don't match dot files, i.e. "." or "..". + * - Don't match backup files. + * - Match everything else in a case-insensitive manner. + * + * @var string + */ + private $baseRegex = '`^%s(?!\.git/)(?!(.*/)?\.+$)(?!.*\.(bak|orig)).*$`Dix'; + + + /** + * Constructor. + * + * @param string $directory The directory to examine. + * @param string $rootPath Path to the project root. + * @param string $filter PCRE regular expression to filter the file list with. + */ + public function __construct($directory, $rootPath='', $filter='') + { + $this->rootPath = $rootPath; + + $directory = new \RecursiveDirectoryIterator( + $directory, + \RecursiveDirectoryIterator::UNIX_PATHS + ); + $flattened = new \RecursiveIteratorIterator( + $directory, + \RecursiveIteratorIterator::LEAVES_ONLY, + \RecursiveIteratorIterator::CATCH_GET_CHILD + ); + + if ($filter === '') { + $filter = sprintf($this->baseRegex, preg_quote($this->rootPath)); + } + + $this->fileIterator = new \RegexIterator($flattened, $filter); + + return $this; + + }//end __construct() + + + /** + * Retrieve the filtered file list as an array. + * + * @return array + */ + public function getList() + { + $fileList = []; + + foreach ($this->fileIterator as $file) { + $fileList[] = str_replace($this->rootPath, '', $file); + } + + return $fileList; + + }//end getList() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Standards/AbstractSniffUnitTest.php b/vendor/squizlabs/php_codesniffer/tests/Standards/AbstractSniffUnitTest.php new file mode 100644 index 00000000..c050a0c2 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Standards/AbstractSniffUnitTest.php @@ -0,0 +1,461 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Standards; + +use PHP_CodeSniffer\Config; +use PHP_CodeSniffer\Exceptions\RuntimeException; +use PHP_CodeSniffer\Ruleset; +use PHP_CodeSniffer\Files\LocalFile; +use PHP_CodeSniffer\Util\Common; +use PHPUnit\Framework\TestCase; + +abstract class AbstractSniffUnitTest extends TestCase +{ + + /** + * Enable or disable the backup and restoration of the $GLOBALS array. + * Overwrite this attribute in a child class of TestCase. + * Setting this attribute in setUp() has no effect! + * + * @var boolean + */ + protected $backupGlobals = false; + + /** + * The path to the standard's main directory. + * + * @var string + */ + public $standardsDir = null; + + /** + * The path to the standard's test directory. + * + * @var string + */ + public $testsDir = null; + + + /** + * Sets up this unit test. + * + * @return void + */ + protected function setUp() + { + $class = get_class($this); + $this->standardsDir = $GLOBALS['PHP_CODESNIFFER_STANDARD_DIRS'][$class]; + $this->testsDir = $GLOBALS['PHP_CODESNIFFER_TEST_DIRS'][$class]; + + }//end setUp() + + + /** + * Get a list of all test files to check. + * + * These will have the same base as the sniff name but different extensions. + * We ignore the .php file as it is the class. + * + * @param string $testFileBase The base path that the unit tests files will have. + * + * @return string[] + */ + protected function getTestFiles($testFileBase) + { + $testFiles = []; + + $dir = substr($testFileBase, 0, strrpos($testFileBase, DIRECTORY_SEPARATOR)); + $di = new \DirectoryIterator($dir); + + foreach ($di as $file) { + $path = $file->getPathname(); + if (substr($path, 0, strlen($testFileBase)) === $testFileBase) { + if ($path !== $testFileBase.'php' && substr($path, -5) !== 'fixed' && substr($path, -4) !== '.bak') { + $testFiles[] = $path; + } + } + } + + // Put them in order. + sort($testFiles); + + return $testFiles; + + }//end getTestFiles() + + + /** + * Should this test be skipped for some reason. + * + * @return boolean + */ + protected function shouldSkipTest() + { + return false; + + }//end shouldSkipTest() + + + /** + * Tests the extending classes Sniff class. + * + * @return void + * @throws \PHPUnit\Framework\Exception + */ + final public function testSniff() + { + // Skip this test if we can't run in this environment. + if ($this->shouldSkipTest() === true) { + $this->markTestSkipped(); + } + + $sniffCode = Common::getSniffCode(get_class($this)); + list($standardName, $categoryName, $sniffName) = explode('.', $sniffCode); + + $testFileBase = $this->testsDir.$categoryName.DIRECTORY_SEPARATOR.$sniffName.'UnitTest.'; + + // Get a list of all test files to check. + $testFiles = $this->getTestFiles($testFileBase); + $GLOBALS['PHP_CODESNIFFER_SNIFF_CASE_FILES'][] = $testFiles; + + if (isset($GLOBALS['PHP_CODESNIFFER_CONFIG']) === true) { + $config = $GLOBALS['PHP_CODESNIFFER_CONFIG']; + } else { + $config = new Config(); + $config->cache = false; + $GLOBALS['PHP_CODESNIFFER_CONFIG'] = $config; + } + + $config->standards = [$standardName]; + $config->sniffs = [$sniffCode]; + $config->ignored = []; + + if (isset($GLOBALS['PHP_CODESNIFFER_RULESETS']) === false) { + $GLOBALS['PHP_CODESNIFFER_RULESETS'] = []; + } + + if (isset($GLOBALS['PHP_CODESNIFFER_RULESETS'][$standardName]) === false) { + $ruleset = new Ruleset($config); + $GLOBALS['PHP_CODESNIFFER_RULESETS'][$standardName] = $ruleset; + } + + $ruleset = $GLOBALS['PHP_CODESNIFFER_RULESETS'][$standardName]; + + $sniffFile = $this->standardsDir.DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR.$categoryName.DIRECTORY_SEPARATOR.$sniffName.'Sniff.php'; + + $sniffClassName = substr(get_class($this), 0, -8).'Sniff'; + $sniffClassName = str_replace('\Tests\\', '\Sniffs\\', $sniffClassName); + $sniffClassName = Common::cleanSniffClass($sniffClassName); + + $restrictions = [strtolower($sniffClassName) => true]; + $ruleset->registerSniffs([$sniffFile], $restrictions, []); + $ruleset->populateTokenListeners(); + + $failureMessages = []; + foreach ($testFiles as $testFile) { + $filename = basename($testFile); + $oldConfig = $config->getSettings(); + + try { + $this->setCliValues($filename, $config); + $phpcsFile = new LocalFile($testFile, $ruleset, $config); + $phpcsFile->process(); + } catch (RuntimeException $e) { + $this->fail('An unexpected exception has been caught: '.$e->getMessage()); + } + + $failures = $this->generateFailureMessages($phpcsFile); + $failureMessages = array_merge($failureMessages, $failures); + + if ($phpcsFile->getFixableCount() > 0) { + // Attempt to fix the errors. + $phpcsFile->fixer->fixFile(); + $fixable = $phpcsFile->getFixableCount(); + if ($fixable > 0) { + $failureMessages[] = "Failed to fix $fixable fixable violations in $filename"; + } + + // Check for a .fixed file to check for accuracy of fixes. + $fixedFile = $testFile.'.fixed'; + if (file_exists($fixedFile) === true) { + $diff = $phpcsFile->fixer->generateDiff($fixedFile); + if (trim($diff) !== '') { + $filename = basename($testFile); + $fixedFilename = basename($fixedFile); + $failureMessages[] = "Fixed version of $filename does not match expected version in $fixedFilename; the diff is\n$diff"; + } + } + } + + // Restore the config. + $config->setSettings($oldConfig); + }//end foreach + + if (empty($failureMessages) === false) { + $this->fail(implode(PHP_EOL, $failureMessages)); + } + + }//end testSniff() + + + /** + * Generate a list of test failures for a given sniffed file. + * + * @param \PHP_CodeSniffer\Files\LocalFile $file The file being tested. + * + * @return array + * @throws \PHP_CodeSniffer\Exceptions\RuntimeException + */ + public function generateFailureMessages(LocalFile $file) + { + $testFile = $file->getFilename(); + + $foundErrors = $file->getErrors(); + $foundWarnings = $file->getWarnings(); + $expectedErrors = $this->getErrorList(basename($testFile)); + $expectedWarnings = $this->getWarningList(basename($testFile)); + + if (is_array($expectedErrors) === false) { + throw new RuntimeException('getErrorList() must return an array'); + } + + if (is_array($expectedWarnings) === false) { + throw new RuntimeException('getWarningList() must return an array'); + } + + /* + We merge errors and warnings together to make it easier + to iterate over them and produce the errors string. In this way, + we can report on errors and warnings in the same line even though + it's not really structured to allow that. + */ + + $allProblems = []; + $failureMessages = []; + + foreach ($foundErrors as $line => $lineErrors) { + foreach ($lineErrors as $column => $errors) { + if (isset($allProblems[$line]) === false) { + $allProblems[$line] = [ + 'expected_errors' => 0, + 'expected_warnings' => 0, + 'found_errors' => [], + 'found_warnings' => [], + ]; + } + + $foundErrorsTemp = []; + foreach ($allProblems[$line]['found_errors'] as $foundError) { + $foundErrorsTemp[] = $foundError; + } + + $errorsTemp = []; + foreach ($errors as $foundError) { + $errorsTemp[] = $foundError['message'].' ('.$foundError['source'].')'; + + $source = $foundError['source']; + if (in_array($source, $GLOBALS['PHP_CODESNIFFER_SNIFF_CODES'], true) === false) { + $GLOBALS['PHP_CODESNIFFER_SNIFF_CODES'][] = $source; + } + + if ($foundError['fixable'] === true + && in_array($source, $GLOBALS['PHP_CODESNIFFER_FIXABLE_CODES'], true) === false + ) { + $GLOBALS['PHP_CODESNIFFER_FIXABLE_CODES'][] = $source; + } + } + + $allProblems[$line]['found_errors'] = array_merge($foundErrorsTemp, $errorsTemp); + }//end foreach + + if (isset($expectedErrors[$line]) === true) { + $allProblems[$line]['expected_errors'] = $expectedErrors[$line]; + } else { + $allProblems[$line]['expected_errors'] = 0; + } + + unset($expectedErrors[$line]); + }//end foreach + + foreach ($expectedErrors as $line => $numErrors) { + if (isset($allProblems[$line]) === false) { + $allProblems[$line] = [ + 'expected_errors' => 0, + 'expected_warnings' => 0, + 'found_errors' => [], + 'found_warnings' => [], + ]; + } + + $allProblems[$line]['expected_errors'] = $numErrors; + } + + foreach ($foundWarnings as $line => $lineWarnings) { + foreach ($lineWarnings as $column => $warnings) { + if (isset($allProblems[$line]) === false) { + $allProblems[$line] = [ + 'expected_errors' => 0, + 'expected_warnings' => 0, + 'found_errors' => [], + 'found_warnings' => [], + ]; + } + + $foundWarningsTemp = []; + foreach ($allProblems[$line]['found_warnings'] as $foundWarning) { + $foundWarningsTemp[] = $foundWarning; + } + + $warningsTemp = []; + foreach ($warnings as $warning) { + $warningsTemp[] = $warning['message'].' ('.$warning['source'].')'; + + $source = $warning['source']; + if (in_array($source, $GLOBALS['PHP_CODESNIFFER_SNIFF_CODES'], true) === false) { + $GLOBALS['PHP_CODESNIFFER_SNIFF_CODES'][] = $source; + } + + if ($warning['fixable'] === true + && in_array($source, $GLOBALS['PHP_CODESNIFFER_FIXABLE_CODES'], true) === false + ) { + $GLOBALS['PHP_CODESNIFFER_FIXABLE_CODES'][] = $source; + } + } + + $allProblems[$line]['found_warnings'] = array_merge($foundWarningsTemp, $warningsTemp); + }//end foreach + + if (isset($expectedWarnings[$line]) === true) { + $allProblems[$line]['expected_warnings'] = $expectedWarnings[$line]; + } else { + $allProblems[$line]['expected_warnings'] = 0; + } + + unset($expectedWarnings[$line]); + }//end foreach + + foreach ($expectedWarnings as $line => $numWarnings) { + if (isset($allProblems[$line]) === false) { + $allProblems[$line] = [ + 'expected_errors' => 0, + 'expected_warnings' => 0, + 'found_errors' => [], + 'found_warnings' => [], + ]; + } + + $allProblems[$line]['expected_warnings'] = $numWarnings; + } + + // Order the messages by line number. + ksort($allProblems); + + foreach ($allProblems as $line => $problems) { + $numErrors = count($problems['found_errors']); + $numWarnings = count($problems['found_warnings']); + $expectedErrors = $problems['expected_errors']; + $expectedWarnings = $problems['expected_warnings']; + + $errors = ''; + $foundString = ''; + + if ($expectedErrors !== $numErrors || $expectedWarnings !== $numWarnings) { + $lineMessage = "[LINE $line]"; + $expectedMessage = 'Expected '; + $foundMessage = 'in '.basename($testFile).' but found '; + + if ($expectedErrors !== $numErrors) { + $expectedMessage .= "$expectedErrors error(s)"; + $foundMessage .= "$numErrors error(s)"; + if ($numErrors !== 0) { + $foundString .= 'error(s)'; + $errors .= implode(PHP_EOL.' -> ', $problems['found_errors']); + } + + if ($expectedWarnings !== $numWarnings) { + $expectedMessage .= ' and '; + $foundMessage .= ' and '; + if ($numWarnings !== 0) { + if ($foundString !== '') { + $foundString .= ' and '; + } + } + } + } + + if ($expectedWarnings !== $numWarnings) { + $expectedMessage .= "$expectedWarnings warning(s)"; + $foundMessage .= "$numWarnings warning(s)"; + if ($numWarnings !== 0) { + $foundString .= 'warning(s)'; + if (empty($errors) === false) { + $errors .= PHP_EOL.' -> '; + } + + $errors .= implode(PHP_EOL.' -> ', $problems['found_warnings']); + } + } + + $fullMessage = "$lineMessage $expectedMessage $foundMessage."; + if ($errors !== '') { + $fullMessage .= " The $foundString found were:".PHP_EOL." -> $errors"; + } + + $failureMessages[] = $fullMessage; + }//end if + }//end foreach + + return $failureMessages; + + }//end generateFailureMessages() + + + /** + * Get a list of CLI values to set before the file is tested. + * + * @param string $filename The name of the file being tested. + * @param \PHP_CodeSniffer\Config $config The config data for the run. + * + * @return void + */ + public function setCliValues($filename, $config) + { + return; + + }//end setCliValues() + + + /** + * Returns the lines where errors should occur. + * + * The key of the array should represent the line number and the value + * should represent the number of errors that should occur on that line. + * + * @return array + */ + abstract protected function getErrorList(); + + + /** + * Returns the lines where warnings should occur. + * + * The key of the array should represent the line number and the value + * should represent the number of warnings that should occur on that line. + * + * @return array + */ + abstract protected function getWarningList(); + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/Standards/AllSniffs.php b/vendor/squizlabs/php_codesniffer/tests/Standards/AllSniffs.php new file mode 100644 index 00000000..24527dd5 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/Standards/AllSniffs.php @@ -0,0 +1,123 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Standards; + +use PHP_CodeSniffer\Util\Standards; +use PHP_CodeSniffer\Autoload; +use PHPUnit\TextUI\TestRunner; +use PHPUnit\Framework\TestSuite; + +class AllSniffs +{ + + + /** + * Prepare the test runner. + * + * @return void + */ + public static function main() + { + TestRunner::run(self::suite()); + + }//end main() + + + /** + * Add all sniff unit tests into a test suite. + * + * Sniff unit tests are found by recursing through the 'Tests' directory + * of each installed coding standard. + * + * @return \PHPUnit\Framework\TestSuite + */ + public static function suite() + { + $GLOBALS['PHP_CODESNIFFER_SNIFF_CODES'] = []; + $GLOBALS['PHP_CODESNIFFER_FIXABLE_CODES'] = []; + $GLOBALS['PHP_CODESNIFFER_SNIFF_CASE_FILES'] = []; + + $suite = new TestSuite('PHP CodeSniffer Standards'); + + $isInstalled = !is_file(__DIR__.'/../../autoload.php'); + + // Optionally allow for ignoring the tests for one or more standards. + $ignoreTestsForStandards = getenv('PHPCS_IGNORE_TESTS'); + if ($ignoreTestsForStandards === false) { + $ignoreTestsForStandards = []; + } else { + $ignoreTestsForStandards = explode(',', $ignoreTestsForStandards); + } + + $installedStandards = self::getInstalledStandardDetails(); + + foreach ($installedStandards as $standard => $details) { + Autoload::addSearchPath($details['path'], $details['namespace']); + + // If the test is running PEAR installed, the built-in standards + // are split into different directories; one for the sniffs and + // a different file system location for tests. + if ($isInstalled === true && is_dir(dirname($details['path']).DIRECTORY_SEPARATOR.'Generic') === true) { + $testPath = realpath(__DIR__.'/../../src/Standards/'.$standard); + } else { + $testPath = $details['path']; + } + + if (in_array($standard, $ignoreTestsForStandards, true) === true) { + continue; + } + + $testsDir = $testPath.DIRECTORY_SEPARATOR.'Tests'.DIRECTORY_SEPARATOR; + if (is_dir($testsDir) === false) { + // No tests for this standard. + continue; + } + + $di = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($testsDir)); + + foreach ($di as $file) { + // Skip hidden files. + if (substr($file->getFilename(), 0, 1) === '.') { + continue; + } + + // Tests must have the extension 'php'. + $parts = explode('.', $file); + $ext = array_pop($parts); + if ($ext !== 'php') { + continue; + } + + $className = Autoload::loadFile($file->getPathname()); + $GLOBALS['PHP_CODESNIFFER_STANDARD_DIRS'][$className] = $details['path']; + $GLOBALS['PHP_CODESNIFFER_TEST_DIRS'][$className] = $testsDir; + $suite->addTestSuite($className); + } + }//end foreach + + return $suite; + + }//end suite() + + + /** + * Get the details of all coding standards installed. + * + * @return array + * @see Standards::getInstalledStandardDetails() + */ + protected static function getInstalledStandardDetails() + { + return Standards::getInstalledStandardDetails(true); + + }//end getInstalledStandardDetails() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/TestSuite.php b/vendor/squizlabs/php_codesniffer/tests/TestSuite.php new file mode 100644 index 00000000..9eb269f8 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/TestSuite.php @@ -0,0 +1,35 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests; + +use PHPUnit\Framework\TestSuite as PHPUnit_TestSuite; +use PHPUnit\Framework\TestResult; + +class TestSuite extends PHPUnit_TestSuite +{ + + + /** + * Runs the tests and collects their result in a TestResult. + * + * @param \PHPUnit\Framework\TestResult $result A test result. + * + * @return \PHPUnit\Framework\TestResult + */ + public function run(TestResult $result=null) + { + $result = parent::run($result); + printPHPCodeSnifferTestOutput(); + return $result; + + }//end run() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/TestSuite7.php b/vendor/squizlabs/php_codesniffer/tests/TestSuite7.php new file mode 100644 index 00000000..43db293d --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/TestSuite7.php @@ -0,0 +1,35 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests; + +use PHPUnit\Framework\TestSuite as PHPUnit_TestSuite; +use PHPUnit\Framework\TestResult; + +class TestSuite extends PHPUnit_TestSuite +{ + + + /** + * Runs the tests and collects their result in a TestResult. + * + * @param \PHPUnit\Framework\TestResult $result A test result. + * + * @return \PHPUnit\Framework\TestResult + */ + public function run(TestResult $result=null): TestResult + { + $result = parent::run($result); + printPHPCodeSnifferTestOutput(); + return $result; + + }//end run() + + +}//end class diff --git a/vendor/squizlabs/php_codesniffer/tests/bootstrap.php b/vendor/squizlabs/php_codesniffer/tests/bootstrap.php new file mode 100644 index 00000000..47084d11 --- /dev/null +++ b/vendor/squizlabs/php_codesniffer/tests/bootstrap.php @@ -0,0 +1,91 @@ + + * @copyright 2006-2017 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +if (defined('PHP_CODESNIFFER_IN_TESTS') === false) { + define('PHP_CODESNIFFER_IN_TESTS', true); +} + +if (defined('PHP_CODESNIFFER_CBF') === false) { + define('PHP_CODESNIFFER_CBF', false); +} + +if (defined('PHP_CODESNIFFER_VERBOSITY') === false) { + define('PHP_CODESNIFFER_VERBOSITY', 0); +} + +if (is_file(__DIR__.'/../autoload.php') === true) { + include_once __DIR__.'/../autoload.php'; +} else { + include_once 'PHP/CodeSniffer/autoload.php'; +} + +$tokens = new \PHP_CodeSniffer\Util\Tokens(); + +// Compatibility for PHPUnit < 6 and PHPUnit 6+. +if (class_exists('PHPUnit_Framework_TestSuite') === true && class_exists('PHPUnit\Framework\TestSuite') === false) { + class_alias('PHPUnit_Framework_TestSuite', 'PHPUnit'.'\Framework\TestSuite'); +} + +if (class_exists('PHPUnit_Framework_TestCase') === true && class_exists('PHPUnit\Framework\TestCase') === false) { + class_alias('PHPUnit_Framework_TestCase', 'PHPUnit'.'\Framework\TestCase'); +} + +if (class_exists('PHPUnit_TextUI_TestRunner') === true && class_exists('PHPUnit\TextUI\TestRunner') === false) { + class_alias('PHPUnit_TextUI_TestRunner', 'PHPUnit'.'\TextUI\TestRunner'); +} + +if (class_exists('PHPUnit_Framework_TestResult') === true && class_exists('PHPUnit\Framework\TestResult') === false) { + class_alias('PHPUnit_Framework_TestResult', 'PHPUnit'.'\Framework\TestResult'); +} + +// Determine whether this is a PEAR install or not. +$GLOBALS['PHP_CODESNIFFER_PEAR'] = false; + +if (is_file(__DIR__.'/../autoload.php') === false) { + $GLOBALS['PHP_CODESNIFFER_PEAR'] = true; +} + + +/** + * A global util function to help print unit test fixing data. + * + * @return void + */ +function printPHPCodeSnifferTestOutput() +{ + echo PHP_EOL.PHP_EOL; + + $output = 'The test files'; + $data = []; + + $codeCount = count($GLOBALS['PHP_CODESNIFFER_SNIFF_CODES']); + if (empty($GLOBALS['PHP_CODESNIFFER_SNIFF_CASE_FILES']) === false) { + $files = call_user_func_array('array_merge', $GLOBALS['PHP_CODESNIFFER_SNIFF_CASE_FILES']); + $files = array_unique($files); + $fileCount = count($files); + + $output = '%d sniff test files'; + $data[] = $fileCount; + } + + $output .= ' generated %d unique error codes'; + $data[] = $codeCount; + + if ($codeCount > 0) { + $fixes = count($GLOBALS['PHP_CODESNIFFER_FIXABLE_CODES']); + $percent = round(($fixes / $codeCount * 100), 2); + + $output .= '; %d were fixable (%d%%)'; + $data[] = $fixes; + $data[] = $percent; + } + + vprintf($output, $data); + +}//end printPHPCodeSnifferTestOutput() diff --git a/vendor/symfony/deprecation-contracts/CHANGELOG.md b/vendor/symfony/deprecation-contracts/CHANGELOG.md new file mode 100644 index 00000000..7932e261 --- /dev/null +++ b/vendor/symfony/deprecation-contracts/CHANGELOG.md @@ -0,0 +1,5 @@ +CHANGELOG +========= + +The changelog is maintained for all Symfony contracts at the following URL: +https://github.com/symfony/contracts/blob/main/CHANGELOG.md diff --git a/vendor/symfony/deprecation-contracts/LICENSE b/vendor/symfony/deprecation-contracts/LICENSE new file mode 100644 index 00000000..406242ff --- /dev/null +++ b/vendor/symfony/deprecation-contracts/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020-2022 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/deprecation-contracts/composer.json b/vendor/symfony/deprecation-contracts/composer.json new file mode 100644 index 00000000..1c1b4ba0 --- /dev/null +++ b/vendor/symfony/deprecation-contracts/composer.json @@ -0,0 +1,35 @@ +{ + "name": "symfony/deprecation-contracts", + "type": "library", + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=8.0.2" + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + } +} diff --git a/vendor/symfony/deprecation-contracts/function.php b/vendor/symfony/deprecation-contracts/function.php new file mode 100644 index 00000000..2d56512b --- /dev/null +++ b/vendor/symfony/deprecation-contracts/function.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (!function_exists('trigger_deprecation')) { + /** + * Triggers a silenced deprecation notice. + * + * @param string $package The name of the Composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message The message of the deprecation + * @param mixed ...$args Values to insert in the message using printf() formatting + * + * @author Nicolas Grekas + */ + function trigger_deprecation(string $package, string $version, string $message, mixed ...$args): void + { + @trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED); + } +} diff --git a/vendor/symfony/polyfill-php80/LICENSE b/vendor/symfony/polyfill-php80/LICENSE new file mode 100644 index 00000000..5593b1d8 --- /dev/null +++ b/vendor/symfony/polyfill-php80/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/polyfill-php80/Php80.php b/vendor/symfony/polyfill-php80/Php80.php new file mode 100644 index 00000000..362dd1a9 --- /dev/null +++ b/vendor/symfony/polyfill-php80/Php80.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php80; + +/** + * @author Ion Bazan + * @author Nico Oelgart + * @author Nicolas Grekas + * + * @internal + */ +final class Php80 +{ + public static function fdiv(float $dividend, float $divisor): float + { + return @($dividend / $divisor); + } + + public static function get_debug_type($value): string + { + switch (true) { + case null === $value: return 'null'; + case \is_bool($value): return 'bool'; + case \is_string($value): return 'string'; + case \is_array($value): return 'array'; + case \is_int($value): return 'int'; + case \is_float($value): return 'float'; + case \is_object($value): break; + case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class'; + default: + if (null === $type = @get_resource_type($value)) { + return 'unknown'; + } + + if ('Unknown' === $type) { + $type = 'closed'; + } + + return "resource ($type)"; + } + + $class = \get_class($value); + + if (false === strpos($class, '@')) { + return $class; + } + + return (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous'; + } + + public static function get_resource_id($res): int + { + if (!\is_resource($res) && null === @get_resource_type($res)) { + throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res))); + } + + return (int) $res; + } + + public static function preg_last_error_msg(): string + { + switch (preg_last_error()) { + case \PREG_INTERNAL_ERROR: + return 'Internal error'; + case \PREG_BAD_UTF8_ERROR: + return 'Malformed UTF-8 characters, possibly incorrectly encoded'; + case \PREG_BAD_UTF8_OFFSET_ERROR: + return 'The offset did not correspond to the beginning of a valid UTF-8 code point'; + case \PREG_BACKTRACK_LIMIT_ERROR: + return 'Backtrack limit exhausted'; + case \PREG_RECURSION_LIMIT_ERROR: + return 'Recursion limit exhausted'; + case \PREG_JIT_STACKLIMIT_ERROR: + return 'JIT stack limit exhausted'; + case \PREG_NO_ERROR: + return 'No error'; + default: + return 'Unknown error'; + } + } + + public static function str_contains(string $haystack, string $needle): bool + { + return '' === $needle || false !== strpos($haystack, $needle); + } + + public static function str_starts_with(string $haystack, string $needle): bool + { + return 0 === strncmp($haystack, $needle, \strlen($needle)); + } + + public static function str_ends_with(string $haystack, string $needle): bool + { + if ('' === $needle || $needle === $haystack) { + return true; + } + + if ('' === $haystack) { + return false; + } + + $needleLength = \strlen($needle); + + return $needleLength <= \strlen($haystack) && 0 === substr_compare($haystack, $needle, -$needleLength); + } +} diff --git a/vendor/symfony/polyfill-php80/PhpToken.php b/vendor/symfony/polyfill-php80/PhpToken.php new file mode 100644 index 00000000..fe6e6910 --- /dev/null +++ b/vendor/symfony/polyfill-php80/PhpToken.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php80; + +/** + * @author Fedonyuk Anton + * + * @internal + */ +class PhpToken implements \Stringable +{ + /** + * @var int + */ + public $id; + + /** + * @var string + */ + public $text; + + /** + * @var int + */ + public $line; + + /** + * @var int + */ + public $pos; + + public function __construct(int $id, string $text, int $line = -1, int $position = -1) + { + $this->id = $id; + $this->text = $text; + $this->line = $line; + $this->pos = $position; + } + + public function getTokenName(): ?string + { + if ('UNKNOWN' === $name = token_name($this->id)) { + $name = \strlen($this->text) > 1 || \ord($this->text) < 32 ? null : $this->text; + } + + return $name; + } + + /** + * @param int|string|array $kind + */ + public function is($kind): bool + { + foreach ((array) $kind as $value) { + if (\in_array($value, [$this->id, $this->text], true)) { + return true; + } + } + + return false; + } + + public function isIgnorable(): bool + { + return \in_array($this->id, [\T_WHITESPACE, \T_COMMENT, \T_DOC_COMMENT, \T_OPEN_TAG], true); + } + + public function __toString(): string + { + return (string) $this->text; + } + + /** + * @return static[] + */ + public static function tokenize(string $code, int $flags = 0): array + { + $line = 1; + $position = 0; + $tokens = token_get_all($code, $flags); + foreach ($tokens as $index => $token) { + if (\is_string($token)) { + $id = \ord($token); + $text = $token; + } else { + [$id, $text, $line] = $token; + } + $tokens[$index] = new static($id, $text, $line, $position); + $position += \strlen($text); + } + + return $tokens; + } +} diff --git a/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php b/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php new file mode 100644 index 00000000..2b955423 --- /dev/null +++ b/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#[Attribute(Attribute::TARGET_CLASS)] +final class Attribute +{ + public const TARGET_CLASS = 1; + public const TARGET_FUNCTION = 2; + public const TARGET_METHOD = 4; + public const TARGET_PROPERTY = 8; + public const TARGET_CLASS_CONSTANT = 16; + public const TARGET_PARAMETER = 32; + public const TARGET_ALL = 63; + public const IS_REPEATABLE = 64; + + /** @var int */ + public $flags; + + public function __construct(int $flags = self::TARGET_ALL) + { + $this->flags = $flags; + } +} diff --git a/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php b/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php new file mode 100644 index 00000000..bd1212f6 --- /dev/null +++ b/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000 && extension_loaded('tokenizer')) { + class PhpToken extends Symfony\Polyfill\Php80\PhpToken + { + } +} diff --git a/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php b/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php new file mode 100644 index 00000000..7c62d750 --- /dev/null +++ b/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000) { + interface Stringable + { + /** + * @return string + */ + public function __toString(); + } +} diff --git a/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php b/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php new file mode 100644 index 00000000..01c6c6c8 --- /dev/null +++ b/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000) { + class UnhandledMatchError extends Error + { + } +} diff --git a/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php b/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php new file mode 100644 index 00000000..783dbc28 --- /dev/null +++ b/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 80000) { + class ValueError extends Error + { + } +} diff --git a/vendor/symfony/polyfill-php80/bootstrap.php b/vendor/symfony/polyfill-php80/bootstrap.php new file mode 100644 index 00000000..e5f7dbc1 --- /dev/null +++ b/vendor/symfony/polyfill-php80/bootstrap.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php80 as p; + +if (\PHP_VERSION_ID >= 80000) { + return; +} + +if (!defined('FILTER_VALIDATE_BOOL') && defined('FILTER_VALIDATE_BOOLEAN')) { + define('FILTER_VALIDATE_BOOL', \FILTER_VALIDATE_BOOLEAN); +} + +if (!function_exists('fdiv')) { + function fdiv(float $num1, float $num2): float { return p\Php80::fdiv($num1, $num2); } +} +if (!function_exists('preg_last_error_msg')) { + function preg_last_error_msg(): string { return p\Php80::preg_last_error_msg(); } +} +if (!function_exists('str_contains')) { + function str_contains(?string $haystack, ?string $needle): bool { return p\Php80::str_contains($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('str_starts_with')) { + function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('str_ends_with')) { + function str_ends_with(?string $haystack, ?string $needle): bool { return p\Php80::str_ends_with($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('get_debug_type')) { + function get_debug_type($value): string { return p\Php80::get_debug_type($value); } +} +if (!function_exists('get_resource_id')) { + function get_resource_id($resource): int { return p\Php80::get_resource_id($resource); } +} diff --git a/vendor/symfony/polyfill-php80/composer.json b/vendor/symfony/polyfill-php80/composer.json new file mode 100644 index 00000000..bd9a3262 --- /dev/null +++ b/vendor/symfony/polyfill-php80/composer.json @@ -0,0 +1,40 @@ +{ + "name": "symfony/polyfill-php80", + "type": "library", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "keywords": ["polyfill", "shim", "compatibility", "portable"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Php80\\": "" }, + "files": [ "bootstrap.php" ], + "classmap": [ "Resources/stubs" ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/vendor/wp-coding-standards/wpcs/.gitattributes b/vendor/wp-coding-standards/wpcs/.gitattributes new file mode 100644 index 00000000..c12f94cf --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/.gitattributes @@ -0,0 +1,25 @@ +# +# Exclude these files from release archives. +# This will also make them unavailable when using Composer with `--prefer-dist`. +# If you develop for WPCS using Composer, use `--prefer-source`. +# https://blog.madewithlove.be/post/gitattributes/ +# +/.travis.yml export-ignore +/.phpcs.xml.dist export-ignore +/phpunit.xml.dist export-ignore +/.github export-ignore +/bin export-ignore +/WordPress/Tests export-ignore + +# +# Auto detect text files and perform LF normalization +# http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/ +# +* text=auto + +# +# The above will handle all files NOT found below +# +*.md text +*.php text +*.inc text diff --git a/vendor/wp-coding-standards/wpcs/CHANGELOG.md b/vendor/wp-coding-standards/wpcs/CHANGELOG.md new file mode 100644 index 00000000..f8c8c987 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/CHANGELOG.md @@ -0,0 +1,1208 @@ +# Change Log for WordPress Coding Standards + +All notable changes to this project will be documented in this file. + +This projects adheres to [Semantic Versioning](https://semver.org/) and [Keep a CHANGELOG](https://keepachangelog.com/). + +## [Unreleased] + +_No documentation available about unreleased changes as of yet._ + +## [2.3.0] - 2020-05-14 + +### Added +- The `WordPress.WP.I18n` sniff contains a new check for translatable text strings which are wrapped in HTML tags, like `

    Translate me

    `. Those tags should be moved out of the translatable string. + Note: Translatable strings wrapped in `
    ` tags where the URL is intended to be localized will not trigger this check. + +### Changed +- The default value for `minimum_supported_wp_version`, as used by a [number of sniffs detecting usage of deprecated WP features](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#minimum-wp-version-to-check-for-usage-of-deprecated-functions-classes-and-function-parameters), has been updated to `5.1`. +- The `WordPress.WP.DeprecatedFunctions` sniff will now detect functions deprecated in WP 5.4. +- Improved grammar of an error message in the `WordPress.WP.DiscouragedFunctions` sniff. +- CI: The codebase is now - preliminary - being tested against the PHPCS 4.x development branch. + +### Fixed +- All function call detection sniffs: fixed a bug where constants with the same name as one of the targeted functions could inadvertently be recognized as if they were a called function. +- `WordPress.DB.PreparedSQL`: fixed a bug where the sniff would trigger on the namespace separator character `\\`. +- `WordPress.Security.EscapeOutput`: fixed a bug with the variable replacement in one of the error messages. + + +## [2.2.1] - 2020-02-04 + +### Added +- Metrics to the `WordPress.Arrays.CommaAfterArrayItem` sniff. These can be displayed using `--report=info`. +- The `sanitize_hex_color()` and the `sanitize_hex_color_no_hash()` functions to the `escapingFunctions` list used by the `WordPress.Security.EscapeOutput` sniff. + +### Changed +- The recommended version of the suggested DealerDirect PHPCS Composer plugin is now `^0.6`. + +### Fixed +- `WordPress.PHP.NoSilencedErrors`: depending on the custom properties set, the metrics would be different. +- `WordPress.WhiteSpace.ControlStructureSpacing`: fixed undefined index notice for closures with `use`. +- `WordPress.WP.GlobalVariablesOverride`: fixed undefined offset notice when the `treat_files_as_scoped` property would be set to `true`. +- `WordPress.WP.I18n`: fixed a _Trying to access array offset on value of type null_ error when the sniff was run on PHP 7.4 and would encounter a translation function expecting singular and plural texts for which one of these arguments was missing. + +## [2.2.0] - 2019-11-11 + +Note: The repository has moved. The new URL is https://github.com/WordPress/WordPress-Coding-Standards. +The move does not affect the package name for Packagist. This remains the same: `wp-coding-standards/wpcs`. + +### Added +- New `WordPress.DateTime.CurrentTimeTimestamp` sniff to the `WordPress-Core` ruleset, which checks against the use of the WP native `current_time()` function to retrieve a timestamp as this won't be a _real_ timestamp. Includes an auto-fixer. +- New `WordPress.DateTime.RestrictedFunctions` sniff to the `WordPress-Core` ruleset, which checks for the use of certain date/time related functions. Initially this sniff forbids the use of the PHP native `date_default_timezone_set()` and `date()` functions. +- New `WordPress.PHP.DisallowShortTernary` sniff to the `WordPress-Core` ruleset, which, as the name implies, disallows the use of short ternaries. +- New `WordPress.CodeAnalysis.EscapedNotTranslated` sniff to the `WordPress-Extra` ruleset which will warn when a text string is escaped for output, but not being translated, while the arguments passed to the function call give the impression that translation is intended. +- New `WordPress.NamingConventions.ValidPostTypeSlug` sniff to the `WordPress-Extra` ruleset which will examine calls to `register_post_type()` and throw errors when an invalid post type slug is used. +- `Generic.Arrays.DisallowShortArraySyntax` to the `WordPress-Core` ruleset. +- `WordPress.NamingConventions.PrefixAllGlobals`: the `PHP` prefix has been added to the prefix blacklist as it is reserved by PHP itself. +- The `wp_sanitize_redirect()` function to the `sanitizingFunctions` list used by the `WordPress.Security.NonceVerification`, `WordPress.Security.ValidatedSanitizedInput` and `WordPress.Security.EscapeOutput` sniffs. +- The `sanitize_key()` and the `highlight_string()` functions to the `escapingFunctions` list used by the `WordPress.Security.EscapeOutput` sniff. +- The `RECOVERY_MODE_COOKIE` constant to the list of WP Core constants which may be defined by plugins and themes and therefore don't need to be prefixed (`WordPress.NamingConventions.PrefixAllGlobals`). +- `$content_width`, `$plugin`, `$mu_plugin` and `$network_plugin` to the list of WP globals which is used by both the `WordPress.Variables.GlobalVariables` and the `WordPress.NamingConventions.PrefixAllGlobals` sniffs. +- `Sniff::is_short_list()` utility method to determine whether a _short array_ open/close token actually represents a PHP 7.1+ short list. +- `Sniff::find_list_open_close()` utility method to find the opener and closer for `list()` constructs, including short lists. +- `Sniff::get_list_variables()` utility method which will retrieve an array with the token pointers to the variables which are being assigned to in a `list()` construct. Includes support for short lists. +- `Sniff::is_function_deprecated()` static utility method to determine whether a declared function has been marked as deprecated in the function DocBlock. +- End-user documentation to the following existing sniffs: `WordPress.Arrays.ArrayIndentation`, `WordPress.Arrays.ArrayKeySpacingRestrictions`, `WordPress.Arrays.MultipleStatementAlignment`, `WordPress.Classes.ClassInstantiation`, `WordPress.NamingConventions.ValidHookName`, `WordPress.PHP.IniSet`, `WordPress.Security.SafeRedirect`, `WordPress.WhiteSpace.CastStructureSpacing`, `WordPress.WhiteSpace.DisallowInlineTabs`, `WordPress.WhiteSpace.PrecisionAlignment`, `WordPress.WP.CronInterval`, `WordPress.WP.DeprecatedClasses`, `WordPress.WP.DeprecatedFunctions`, `WordPress.WP.DeprecatedParameters`, `WordPress.WP.DeprecatedParameterValues`, `WordPress.WP.EnqueuedResources`, `WordPress.WP.PostsPerPage`. + This documentation can be exposed via the [`PHP_CodeSniffer` `--generator=...` command-line argument](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Usage). + +### Changed +- The default value for `minimum_supported_wp_version`, as used by a [number of sniffs detecting usage of deprecated WP features](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#minimum-wp-version-to-check-for-usage-of-deprecated-functions-classes-and-function-parameters), has been updated to `5.0`. +- The `WordPress.Arrays.ArrayKeySpacingRestrictions` sniff has two new error codes: `TooMuchSpaceBeforeKey` and `TooMuchSpaceAfterKey`. Both auto-fixable. + The sniff will now check that there is _exactly_ one space on the inside of the square brackets around the array key for non-string, non-numeric array keys. Previously, it only checked that there was whitespace, not how much whitespace. +- `WordPress.Arrays.ArrayKeySpacingRestrictions`: the fixers have been made more efficient and less fixer-conflict prone. +- `WordPress.NamingConventions.PrefixAllGlobals`: plugin/theme prefixes should be at least three characters long. A new `ShortPrefixPassed` error has been added for when the prefix passed does not comply with this rule. +- `WordPress.WhiteSpace.CastStructureSpacing` now allows for no whitespace before a cast when the cast is preceded by the spread `...` operator. This pre-empts a fixer conflict for when the spacing around the spread operator will start to get checked. +- The `WordPress.WP.DeprecatedClasses` sniff will now detect classes deprecated in WP 4.9 and WP 5.3. +- The `WordPress.WP.DeprecatedFunctions` sniff will now detect functions deprecated in WP 5.3. +- `WordPress.NamingConventions.ValidHookName` now has "cleaner" error messages and higher precision for the line on which an error is thrown. +- `WordPress.Security.EscapeOutput`: if an error refers to array access via a variable, the array index key will now be included in the error message. +- The processing of the `WordPress` ruleset by `PHP_CodeSniffer` will now be faster. +- Various minor code tweaks and clean up. +- Various minor documentation fixes. +- Documentation: updated the repo URL in all relevant places. + +### Deprecated +- The `WordPress.WP.TimezoneChange` sniff. Use the `WordPress.DateTime.RestrictedFunctions` instead. + The deprecated sniff will be removed in WPCS 3.0.0. + +### Fixed +- All sniffs in the `WordPress.Arrays` category will no longer treat _short lists_ as if they were a short array. +- The `WordPress.NamingConventions.ValidFunctionName` and the `WordPress.NamingConventions.PrefixAllGlobals` sniff will now ignore functions marked as `@deprecated`. +- Both the `WordPress.NamingConventions.PrefixAllGlobals` sniff as well as the `WordPress.WP.GlobalVariablesOverride` sniff have been updated to recognize variables being declared via (long/short) `list()` constructs and handle them correctly. +- Both the `WordPress.NamingConventions.PrefixAllGlobals` sniff as well as the `WordPress.WP.GlobalVariablesOverride` sniff will now take a limited list of WP global variables _which are intended to be overwritten by plugins/themes_ into account. + Initially this list contains the `$content_width` and the `$wp_cockneyreplace` variables. +- `WordPress.NamingConventions.ValidHookName`: will no longer examine a string array access index key as if it were a part of the hook name. +- `WordPress.Security.EscapeOutput`: will no longer trigger on the typical `basename( __FILE__ )` pattern if found as the first parameter passed to a call to `_deprecated_file()`. +- `WordPress.WP.CapitalPDangit`: now allows for the `.test` TLD in URLs. +- WPCS is now fully compatible with PHP 7.4. + Note: `PHP_CodeSniffer` itself is only compatible with PHP 7.4 from PHPCS 3.5.0 onwards. + + +## [2.1.1] - 2019-05-21 + +### Changed +- The `WordPress.WP.CapitalPDangit` will now ignore misspelled instances of `WordPress` within constant declarations. + This covers both constants declared using `defined()` as well as constants declared using the `const` keyword. +- The default value for `minimum_supported_wp_version`, as used by a [number of sniffs detecting usage of deprecated WP features](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#minimum-wp-version-to-check-for-usage-of-deprecated-functions-classes-and-function-parameters), has been updated to `4.9`. + +### Removed +- `paginate_comments_links()` from the list of auto-escaped functions `Sniff::$autoEscapedFunctions`. + This affects the `WordPress.Security.EscapeOutput` sniff. + +### Fixed +- The `$current_blog` and `$tag_ID` variables have been added to the list of WordPress global variables. + This fixes some false positives from the `WordPress.NamingConventions.PrefixAllGlobals` and the `WordPress.WP.GlobalVariablesOverride` sniffs. +- The generic `TestCase` class name has been added to the `$test_class_whitelist`. + This fixes some false positives from the `WordPress.NamingConventions.FileName`, `WordPress.NamingConventions.PrefixAllGlobals` and the `WordPress.WP.GlobalVariablesOverride` sniffs. +- The `WordPress.NamingConventions.ValidVariableName` sniff will now correctly recognize `$tag_ID` as a WordPress native, mixed-case variable. +- The `WordPress.Security.NonceVerification` sniff will now correctly recognize nonce verification within a nested closure or anonymous class. + + +## [2.1.0] - 2019-04-08 + +### Added +- New `WordPress.PHP.IniSet` sniff to the `WordPress-Extra` ruleset. + This sniff will detect calls to `ini_set()` and `ini_alter()` and warn against their use as changing configuration values at runtime leads to an unpredictable runtime environment, which can result in conflicts between core/plugins/themes. + - The sniff will not throw notices about a very limited set of "safe" ini directives. + - For a number of ini directives for which there are alternative, non-conflicting ways to achieve the same available, the sniff will throw an `error` and advise using the alternative. +- `doubleval()`, `count()` and `sizeof()` to `Sniff::$unslashingSanitizingFunctions` property. + While `count()` and its alias `sizeof()`, don't actually unslash or sanitize, the output of these functions is safe to use without unslashing or sanitizing. + This affects the `WordPress.Security.ValidatedSanitizedInput` and the `WordPress.Security.NonceVerification` sniffs. +- The new WP 5.1 `WP_UnitTestCase_Base` class to the `Sniff::$test_class_whitelist` property. +- New `Sniff::get_array_access_keys()` utility method to retrieve all array keys for a variable using multi-level array access. +- New `Sniff::is_class_object_call()`, `Sniff::is_token_namespaced()` utility methods. + These should help make the checking of whether or not a function call is a global function, method call or a namespaced function call more consistent. + This also implements allowing for the [namespace keyword being used as an operator](https://www.php.net/manual/en/language.namespaces.nsconstants.php#example-258). +- New `Sniff::is_in_function_call()` utility method to facilitate checking whether a token is (part of) a parameter passed to a specific (set of) function(s). +- New `Sniff::is_in_type_test()` utility method to determine if a variable is being type tested, along with a `Sniff::$typeTestFunctions` property containing the names of the functions this applies to. +- New `Sniff::is_in_array_comparison()` utility method to determine if a variable is (part of) a parameter in an array-value comparison, along with a `Sniff::$arrayCompareFunctions` property containing the names of the relevant functions. +- New `Sniff::$arrayWalkingFunctions` property containing the names of array functions which apply a callback to the array, but don't change the array by reference. +- New `Sniff::$unslashingFunctions` property containing the names of functions which unslash data passed to them and return the unslashed result. + +### Changed +- Moved the `WordPress.PHP.StrictComparisons`, `WordPress.PHP.StrictInArray` and the `WordPress.CodeAnalysis.AssignmentInCondition` sniff from the `WordPress-Extra` to the `WordPress-Core` ruleset. +- The `Squiz.Commenting.InlineComment.SpacingAfter` error is no longer included in the `WordPress-Docs` ruleset. +- The default value for `minimum_supported_wp_version`, as used by a [number of sniffs detecting usage of deprecated WP features](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#minimum-wp-version-to-check-for-usage-of-deprecated-functions-classes-and-function-parameters), has been updated to `4.8`. +- The `WordPress.WP.DeprecatedFunctions` sniff will now detect functions deprecated in WP 5.1. +- The `WordPress.Security.NonceVerification` sniff now allows for variable type testing, comparisons, unslashing and sanitization before the nonce check. A nonce check within the same scope, however, is still required. +- The `WordPress.Security.ValidatedSanitizedInput` sniff now allows for using a superglobal in an array-value comparison without sanitization, same as when the superglobal is used in a scalar value comparison. +- `WordPress.NamingConventions.PrefixAllGlobals`: some of the error messages have been made more explicit. +- The error messages for the `WordPress.Security.ValidatedSanitizedInput` sniff will now contain information on the index keys accessed. +- The error message for the `WordPress.Security.ValidatedSanitizedInput.InputNotValidated` has been reworded to make it more obvious what the actual issue being reported is. +- The error message for the `WordPress.Security.ValidatedSanitizedInput.MissingUnslash` has been reworded. +- The `Sniff::is_comparison()` method now has a new `$include_coalesce` parameter to allow for toggling whether the null coalesce operator should be seen as a comparison operator. Defaults to `true`. +- All sniffs are now also being tested against PHP 7.4 (unstable) for consistent sniff results. +- The recommended version of the suggested DealerDirect PHPCS Composer plugin is now `^0.5.0`. +- Various minor code tweaks and clean up. + +### Removed +- `ini_set` and `ini_alter` from the list of functions detected by the `WordPress.PHP.DiscouragedFunctions` sniff. + These are now covered via the new `WordPress.PHP.IniSet` sniff. +- `in_array()` and `array_key_exists()` from the list of `Sniff::$sanitizingFunctions`. These are now handled differently. + +### Fixed +- The `WordPress.NamingConventions.PrefixAllGlobals` sniff would underreport when global functions would be autoloaded via a Composer autoload `files` configuration. +- The `WordPress.Security.EscapeOutput` sniff will now recognize `map_deep()` for escaping the values in an array via a callback to an output escaping function. This should prevent false positives. +- The `WordPress.Security.NonceVerification` sniff will no longer inadvertently allow for a variable to be sanitized without a nonce check within the same scope. +- The `WordPress.Security.ValidatedSanitizedInput` sniff will no longer throw errors when a variable is only being type tested. +- The `WordPress.Security.ValidatedSanitizedInput` sniff will now correctly recognize the null coalesce (PHP 7.0) and null coalesce equal (PHP 7.4) operators and will now throw errors for missing unslashing and sanitization where relevant. +- The `WordPress.WP.AlternativeFunctions` sniff will no longer recommend using the WP_FileSystem when PHP native input streams, like `php://input`, or the PHP input stream constants are being read or written to. +- The `WordPress.WP.AlternativeFunctions` sniff will no longer report on usage of the `curl_version()` function. +- The `WordPress.WP.CronInterval` sniff now has improved function recognition which should lower the chance of false positives. +- The `WordPress.WP.EnqueuedResources` sniff will no longer throw false positives for inline jQuery code trying to access a stylesheet link tag. +- Various bugfixes for the `Sniff::has_nonce_check()` method: + - The method will no longer incorrectly identify methods/namespaced functions mirroring the name of WP native nonce verification functions as if they were the global functions. + This will prevent some false negatives. + - The method will now skip over nested closed scopes, such as closures and anonymous classes. This should prevent some false negatives for nonce verification being done while not in the correct scope. + + These fixes affect the `WordPress.Security.NonceVerification` sniff. +- The `Sniff::is_in_isset_or_empty()` method now also checks for usage of `array_key_exist()` and `key_exists()` and will regard these as correct ways to validate a variable. + This should prevent false positives for the `WordPress.Security.ValidatedSanitizedInput` and the `WordPress.Security.NonceVerification` sniffs. +- Various bugfixes for the `Sniff::is_sanitized()` method: + - The method presumed the WordPress coding style regarding code layout, which could lead to false positives. + - The method will no longer incorrectly identify methods/namespaced functions mirroring the name of WP/PHP native unslashing/sanitization functions as if they were the global functions. + This will prevent some false negatives. + - The method will now recognize `map_deep()` for sanitizing an array via a callback to a sanitization function. This should prevent false positives. + - The method will now recognize `stripslashes_deep()` and `stripslashes_from_strings_only()` as valid unslashing functions. This should prevent false positives. + All these fixes affect both the `WordPress.Security.ValidatedSanitizedInput` and the `WordPress.Security.NonceVerification` sniff. +- Various bugfixes for the `Sniff::is_validated()` method: + - The method did not verify correctly whether a variable being validated was the same variable as later used which could lead to false negatives. + - The method did not verify correctly whether a variable being validated had the same array index keys as the variable as later used which could lead to both false negatives as well as false positives. + - The method now also checks for usage of `array_key_exist()` and `key_exists()` and will regard these as correct ways to validate a variable. This should prevent some false positives. + - The methods will now recognize the null coalesce and the null coalesce equal operators as ways to validate a variable. This prevents some false positives. + The results from the `WordPress.Security.ValidatedSanitizedInput` sniff should be more accurate because of these fixes. +- A potential "Undefined index" notice from the `Sniff::is_assignment()` method. + + +## [2.0.0] - 2019-01-16 + +### Important information about this release: + +WordPressCS 2.0.0 contains breaking changes, both for people using custom rulesets as well as for sniff developers who maintain a custom PHPCS standard based on WordPressCS. + +Support for `PHP_CodeSniffer` 2.x has been dropped, the new minimum `PHP_CodeSniffer` version is 3.3.1. +Also, all previously deprecated sniffs, properties and methods have been removed. + +Please read the complete changelog carefully before you upgrade. + +If you are a maintainer of an external standard based on WordPressCS and any of your custom sniffs are based on or extend WPCS sniffs, please read the [Developers Upgrade Guide to WordPressCS 2.0.0](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Upgrade-Guide-to-WordPressCS-2.0.0-for-Developers-of-external-standards). + +### Changes since 2.0.0-RC1 + +#### Fixed + +- `WordPress-Extra`: Reverted back to including the `Squiz.WhiteSpace.LanguageConstructSpacing` sniff instead of the new `Generic.WhiteSpace.LanguageConstructSpacing` sniff as the new sniff is not (yet) available when the PEAR install of PHPCS is used. + +### Changes since 1.2.1 +For a full list of changes from the 1.2.1 version, please review the following changelog: +* https://github.com/WordPress/WordPress-Coding-Standards/releases/tag/2.0.0-RC1 + + +## [2.0.0-RC1] - 2018-12-31 + +### Important information about this release: + +This is the first release candidate for WordPressCS 2.0.0. +WordPressCS 2.0.0 contains breaking changes, both for people using custom rulesets as well as for sniff developers who maintain a custom PHPCS standard based on WordPressCS. + +Support for `PHP_CodeSniffer` 2.x has been dropped, the new minimum `PHP_CodeSniffer` version is 3.3.1. +Also, all previously deprecated sniffs, properties and methods have been removed. + +Please read the complete changelog carefully before you upgrade. + +If you are a maintainer of an external standard based on WordPressCS and any of your custom sniffs are based on or extend WPCS sniffs, please read the [Developers Upgrade Guide to WordPressCS 2.0.0](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Upgrade-Guide-to-WordPressCS-2.0.0-for-Developers-of-external-standards). + +### Added +- `Generic.PHP.DiscourageGoto`, `Generic.PHP.LowerCaseType`, `Generic.WhiteSpace.ArbitraryParenthesesSpacing` and `PSR12.Keywords.ShortFormTypeKeywords` to the `WordPress-Core` ruleset. +- Checking the spacing around the `instanceof` operator to the `WordPress.WhiteSpace.OperatorSpacing` sniff. + +### Changed +- The minimum required `PHP_CodeSniffer` version to 3.3.1 (was 2.9.0). +- The namespace used by WordPressCS has been changed from `WordPress` to `WordPressCS\WordPress`. + This was not possible while `PHP_CodeSniffer` 2.x was still supported, but WordPressCS, as a good Open Source citizen, does not want to occupy the `WordPress` namespace and is releasing its use of it now this is viable. +- The `WordPress.DB.PreparedSQL` sniff used the same error code for two different errors. + The `NotPrepared` error code remains, however an additional `InterpolatedNotPrepared` error code has been added for the second error. + If you are referencing the old error code in a ruleset XML file or in inline annotations, you may need to update it. +- The `WordPress.NamingConventions.PrefixAllGlobals` sniff used the same error code for some errors as well as warnings. + The `NonPrefixedConstantFound` error code remains for the related error, but the warning will now use the new `VariableConstantNameFound` error code. + The `NonPrefixedHooknameFound` error code remains for the related error, but the warning will now use the new `DynamicHooknameFound` error code. + If you are referencing the old error codes in a ruleset XML file or in inline annotations, you may need to update these to use the new codes instead. +- `WordPress.NamingConventions.ValidVariableName`: the error messages and error codes used by this sniff have been changed for improved usability and consistency. + - The error messages will now show a suggestion for a valid alternative name for the variable. + - The `NotSnakeCaseMemberVar` error code has been renamed to `UsedPropertyNotSnakeCase`. + - The `NotSnakeCase` error code has been renamed to `VariableNotSnakeCase`. + - The `MemberNotSnakeCase` error code has been renamed to `PropertyNotSnakeCase`. + - The `StringNotSnakeCase` error code has been renamed to `InterpolatedVariableNotSnakeCase`. + If you are referencing the old error codes in a ruleset XML file or in inline annotations, you may need to update these to use the new codes instead. +- The `WordPress.Security.NonceVerification` sniff used the same error code for both an error as well as a warning. + The old error code `NoNonceVerification` is no longer used. + The `error` now uses the `Missing` error code, while the `warning` now uses the `Recommended` error code. + If you are referencing the old error code in a ruleset XML file or in inline annotations, please update these to use the new codes instead. +- The `WordPress.WP.DiscouragedConstants` sniff used to have two error codes `UsageFound` and `DeclarationFound`. + These error codes will now be prefixed by the name of the constant found to allow for more fine-grained excluding/ignoring of warnings generated by this sniff. + If you are referencing the old error codes in a ruleset XML file or in inline annotations, you may need to update these to use the new codes instead. +- The `WordPress.WP.GlobalVariablesOverride.OverrideProhibited` error code has been replaced by the `WordPress.WP.GlobalVariablesOverride.Prohibited` error code. + If you are referencing the old error code in a ruleset XML file or in inline annotations, you may need to update it. +- `WordPress-Extra`: Replaced the inclusion of the `Generic.Files.OneClassPerFile`, `Generic.Files.OneInterfacePerFile` and the `Generic.Files.OneTraitPerFile` sniffs with the new `Generic.Files.OneObjectStructurePerFile` sniff. +- `WordPress-Extra`: Replaced the inclusion of the `Squiz.WhiteSpace.LanguageConstructSpacing` sniff with the new `Generic.WhiteSpace.LanguageConstructSpacing` sniff. +- `WordPress-Extra`: Replaced the inclusion of the `Squiz.Scope.MemberVarScope` sniff with the more comprehensive `PSR2.Classes.PropertyDeclaration` sniff. +- `WordPress.NamingConventions.ValidFunctionName`: Added a unit test confirming support for interfaces extending multiple interfaces. +- `WordPress.NamingConventions.ValidVariableName`: Added unit tests confirming support for multi-variable/property declarations. +- The `get_name_suggestion()` method has been moved from the `WordPress.NamingConventions.ValidFunctionName` sniff to the base `Sniff` class, renamed to `get_snake_case_name_suggestion()` and made static. +- The rulesets are now validated against the `PHP_CodeSniffer` XSD schema. +- Updated the [custom ruleset example](https://github.com/WordPress/WordPress-Coding-Standards/blob/develop/phpcs.xml.dist.sample) to use the recommended ruleset syntax for `PHP_CodeSniffer` 3.3.1+, including using the new [array property format](https://github.com/squizlabs/PHP_CodeSniffer/releases/tag/3.3.0) which is now supported. +- Dev: The command to run the unit tests has changed. Please see the updated instructions in the [CONTRIBUTING.md](https://github.com/WordPress/WordPress-Coding-Standards/blob/develop/.github/CONTRIBUTING.md) file. + The `bin/pre-commit` example git hook has been updated to match. Additionally a `run-tests` script has been added to the `composer.json` file for your convenience. + To facilitate this, PHPUnit has been added to `require-dev`, even though it is strictly speaking a dependency of PHPCS, not of WPCS. +- Dev: The DealerDirect PHPCS Composer plugin has been added to `require-dev`. +- Various code tweaks and clean up. +- User facing documentation, including the wiki, as well as inline documentation has been updated for all the changes contained in WordPressCS 2.0 and other recommended best practices for `PHP_CodeSniffer` 3.3.1+. + +### Deprecated +- The use of the [WordPressCS native whitelist comments](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Whitelisting-code-which-flags-errors), which were introduced in WPCS 0.4.0, have been deprecated and support will be removed in WPCS 3.0.0. + The WordPressCS native whitelist comments will continue to work for now, but a deprecation warning will be thrown when they are encountered. + You are encouraged to upgrade our whitelist comment to use the [PHPCS native selective ignore annotations](https://github.com/squizlabs/PHP_CodeSniffer/releases/tag/3.2.0) as introduced in `PHP_CodeSniffer` 3.2.0, as soon as possible. + +### Removed +- Support for PHP 5.3. PHP 5.4 is the minimum requirement for `PHP_CodeSniffer` 3.x. + Includes removing any and all workarounds which were in place to still support PHP 5.3. +- Support for `PHP_CodeSniffer` < 3.3.1. + Includes removing any and all workarounds which were in place for supporting older `PHP_CodeSniffer` versions. +- The `WordPress-VIP` standard which was deprecated since WordPressCS 1.0.0. + For checking a theme/plugin for hosting on the WordPress.com VIP platform, please use the [Automattic VIP coding standards](https://github.com/Automattic/VIP-Coding-Standards) instead. +- Support for array properties set in a custom ruleset without the `type="array"` attribute. + Support for this was deprecated in WPCS 1.0.0. + If in doubt about how properties should be set in your custom ruleset, please refer to the [Customizable sniff properties](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties) wiki page which contains XML code examples for setting each and every WPCS native sniff property. + As the minimum `PHP_CodeSniffer` version is now 3.3.1, you can now also use the [new format for setting array properties](https://github.com/squizlabs/PHP_CodeSniffer/releases/tag/3.3.0), so this would be a great moment to review and update your custom ruleset. + Note: the ability to set select properties from the command-line as comma-delimited strings is _not_ affected by this change. +- The following sniffs have been removed outright without deprecation. + If you are referencing these sniffs in a ruleset XML file or in inline annotations, please update these to reference the replacement sniffs instead. + - `WordPress.Functions.FunctionCallSignatureNoParams` - superseded by a bug fix in the upstream `PEAR.Functions.FunctionCallSignature` sniff. + - `WordPress.PHP.DiscourageGoto` - replaced by the same sniff which is now available upstream: `Generic.PHP.DiscourageGoto`. + - `WordPress.WhiteSpace.SemicolonSpacing` - superseded by a bug fix in the upstream `Squiz.WhiteSpace.SemicolonSpacing` sniff. + - `WordPress.WhiteSpace.ArbitraryParenthesesSpacing` - replaced by the same sniff which is now available upstream: `Generic.WhiteSpace.ArbitraryParenthesesSpacing`. +- The following "base" sniffs which were previously already deprecated and turned into abstract base classes, have been removed: + - `WordPress.Arrays.ArrayAssignmentRestrictions` - use the `AbstractArrayAssignmentRestrictionsSniff` class instead. + - `WordPress.Functions.FunctionRestrictions` - use the `AbstractFunctionRestrictionsSniff` class instead. + - `WordPress.Variables.VariableRestrictions` without replacement. +- The following sniffs which were previously deprecated, have been removed: + - `WordPress.Arrays.ArrayDeclaration` - use the other sniffs in the `WordPress.Arrays` category instead. + - `WordPress.CSRF.NonceVerification` - use `WordPress.Security.NonceVerification` instead. + - `WordPress.Functions.DontExtract` - use `WordPress.PHP.DontExtract` instead. + - `WordPress.Variables.GlobalVariables` - use `WordPress.WP.GlobalVariablesOverride` instead. + - `WordPress.VIP.CronInterval` - use `WordPress.WP.CronInterval` instead. + - `WordPress.VIP.DirectDatabaseQuery` - use `WordPress.DB.DirectDatabaseQuery` instead. + - `WordPress.VIP.PluginMenuSlug` - use `WordPress.Security.PluginMenuSlug` instead. + - `WordPress.VIP.SlowDBQuery` - use `WordPress.DB.SlowDBQuery` instead. + - `WordPress.VIP.TimezoneChange` - use `WordPress.WP.TimezoneChange` instead. + - `WordPress.VIP.ValidatedSanitizedInput` - use `WordPress.Security.ValidatedSanitizedInput` instead. + - `WordPress.WP.PreparedSQL` - use `WordPress.DB.PreparedSQL` instead. + - `WordPress.XSS.EscapeOutput` - use `WordPress.Security.EscapeOutput` instead. + - `WordPress.PHP.DiscouragedFunctions` without direct replacement. + The checks previously contained in this sniff were moved to separate sniffs in WPCS 0.11.0. + - `WordPress.Variables.VariableRestrictions` without replacement. + - `WordPress.VIP.AdminBarRemoval` without replacement. + - `WordPress.VIP.FileSystemWritesDisallow` without replacement. + - `WordPress.VIP.OrderByRand` without replacement. + - `WordPress.VIP.PostsPerPage` without replacement. + Part of the previous functionality was split off in WPCS 1.0.0 to the `WordPress.WP.PostsPerPage` sniff. + - `WordPress.VIP.RestrictedFunctions` without replacement. + - `WordPress.VIP.RestrictedVariables` without replacement. + - `WordPress.VIP.SessionFunctionsUsage` without replacement. + - `WordPress.VIP.SessionVariableUsage` without replacement. + - `WordPress.VIP.SuperGlobalInputUsage` without replacement. +- The `WordPress.DB.SlowDBQuery.DeprecatedWhitelistFlagFound` error code which is superseded by the blanket deprecation warning for using the now deprecated WPCS native whitelist comments. +- The `WordPress.PHP.TypeCasts.NonLowercaseFound` error code which has been replaced by the upstream `Generic.PHP.LowerCaseType` sniff. +- The `WordPress.PHP.TypeCasts.LongBoolFound` and `WordPress.PHP.TypeCasts.LongIntFound` error codes which has been replaced by the new upstream `PSR12.Keywords.ShortFormTypeKeywords` sniff. +- The `WordPress.Security.EscapeOutput.OutputNotEscapedShortEcho` error code which was only ever used if WPCS was run on PHP 5.3 with the `short_open_tag` ini directive set to `off`. +- The following sniff categories which were previously deprecated, have been removed, though select categories may be reinstated in the future: + - `CSRF` + - `Functions` + - `Variables` + - `VIP` + - `XSS` +- `WordPress.NamingConventions.ValidVariableName`: The `customVariableWhitelist` property, which had been deprecated since WordPressCS 0.11.0. Use the `customPropertiesWhitelist` property instead. +- `WordPress.Security.EscapeOutput`: The `customSanitizingFunctions` property, which had been deprecated since WordPressCS 0.5.0. Use the `customEscapingFunctions` property instead. +- `WordPress.Security.NonceVerification`: The `errorForSuperGlobals` and `warnForSuperGlobals` properties, which had been deprecated since WordPressCS 0.12.0. +- The `vip_powered_wpcom` function from the `Sniff::$autoEscapedFunctions` list which is used by the `WordPress.Security.EscapeOutput` sniff. +- The `AbstractVariableRestrictionsSniff` class, which was deprecated since WordPressCS 1.0.0. +- The `Sniff::has_html_open_tag()` utility method, which was deprecated since WordPressCS 1.0.0. +- The internal `$php_reserved_vars` property from the `WordPress.NamingConventions.ValidVariableName` sniff in favour of using a PHPCS native property which is now available. +- The class aliases and WPCS native autoloader used for PHPCS cross-version support. +- The unit test framework workarounds for PHPCS cross-version unit testing. +- Support for the `@codingStandardsChangeSetting` annotation, which is generally only used in unit tests. +- The old generic GitHub issue template which was replaced by more specific issue templates in WPCS 1.2.0. + +### Fixed +- Support for PHP 7.3. + `PHP_CodeSniffer` < 3.3.1 was not fully compatible with PHP 7.3. Now the minimum required PHPCS has been upped to `PHP_CodeSniffer` 3.3.1, WordPressCS will run on PHP 7.3 without issue. +- `WordPress.Arrays.ArrayDeclarationSpacing`: improved fixing of the placement of array items following an array item with a trailing multi-line comment. +- `WordPress.NamingConventions.ValidFunctionName`: the sniff will no longer throw false positives nor duplicate errors for methods declared in nested anonymous classes. + The error message has also been improved for methods in anonymous classes. +- `WordPress.NamingConventions.ValidFunctionName`: the sniff will no longer throw false positives for PHP 4-style class constructors/destructors where the name of the constructor/destructor method did not use the same case as the class name. + + +## [1.2.1] - 2018-12-18 + +Note: This will be the last release supporting PHP_CodeSniffer 2.x. + +### Changed +- The default value for `minimum_supported_wp_version`, as used by a [number of sniffs detecting usage of deprecated WP features](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#minimum-wp-version-to-check-for-usage-of-deprecated-functions-classes-and-function-parameters), has been updated to `4.7`. +- The `WordPress.NamingConventions.PrefixAllGlobals` sniff will now report the error for hook names and constant names declared with `define()` on the line containing the parameter for the hook/constant name. Previously, it would report the error on the line containing the function call. +- Various minor housekeeping fixes to inline documentation, rulesets, code. + +### Removed +- `comment_author_email_link()`, `comment_author_email()`, `comment_author_IP()`, `comment_author_link()`, `comment_author_rss()`, `comment_author_url_link()`, `comment_author_url()`, `comment_author()`, `comment_date()`, `comment_excerpt()`, `comment_form_title()`, `comment_form()`, `comment_id_fields()`, `comment_ID()`, `comment_reply_link()`, `comment_text_rss()`, `comment_text()`, `comment_time()`, `comment_type()`, `comments_link()`, `comments_number()`, `comments_popup_link()`, `comments_popup_script()`, `comments_rss_link()`, `delete_get_calendar_cache()`, `edit_bookmark_link()`, `edit_comment_link()`, `edit_post_link()`, `edit_tag_link()`, `get_footer()`, `get_header()`, `get_sidebar()`, `get_the_title()`, `next_comments_link()`, `next_image_link()`, `next_post_link()`, `next_posts_link()`, `permalink_anchor()`, `posts_nav_link()`, `previous_comments_link()`, `previous_image_link()`, `previous_post_link()`, `previous_posts_link()`, `sticky_class()`, `the_attachment_link()`, `the_author_link()`, `the_author_meta()`, `the_author_posts_link()`, `the_author_posts()`, `the_category_rss()`, `the_category()`, `the_content_rss()`, `the_content()`, `the_date_xml()`, `the_excerpt_rss()`, `the_excerpt()`, `the_feed_link()`, `the_ID()`, `the_meta()`, `the_modified_author()`, `the_modified_date()`, `the_modified_time()`, `the_permalink()`, `the_post_thumbnail()`, `the_search_query()`, `the_shortlink()`, `the_tags()`, `the_taxonomies()`, `the_terms()`, `the_time()`, `the_title_rss()`, `the_title()`, `wp_enqueue_script()`, `wp_meta()`, `wp_shortlink_header()` and `wp_shortlink_wp_head()` from the list of auto-escaped functions `Sniff::$autoEscapedFunctions`. This affects the `WordPress.Security.EscapeOutput` sniff. + +### Fixed +- The `WordPress.WhiteSpace.PrecisionAlignment` sniff would loose the value of a custom set `ignoreAlignmentTokens` property when scanning more than one file. + + +## [1.2.0] - 2018-11-12 + +### Added +- New `WordPress.PHP.TypeCasts` sniff to the `WordPress-Core` ruleset. + This new sniff checks that PHP type casts are: + * lowercase; + * short form, i.e. `(bool)` not `(boolean)`; + * normalized, i.e. `(float)` not `(real)`. + Additionally, the new sniff discourages the use of the `(unset)` and `(binary)` type casts. +- New `WordPress.Utils.I18nTextDomainFixer` sniff which can compehensively replace/add `text-domain`s in a plugin or theme. + Important notes: + - This sniff is disabled by default and intended as a utility tool. + - The sniff will fix the text domains in all I18n function calls as well as in a plugin/theme `Text Domain:` header. + - Passing the following properties will activate the sniff: + - `old_text_domain`: an array with one or more (old) text domains which need to be replaced; + - `new_text_domain`: the correct (new) text domain as a string. +- The `WordPress.NamingConventions.PrefixAllGlobals` sniff will now also verify that namespace names use a valid prefix. + * The sniff allows for underscores and (other) non-word characters in a passed prefix to be converted to namespace separators when used in a namespace name. + In other words, if a prefix of `my_plugin` is passed as a value to the `prefixes` property, a namespace name of both `My\Plugin` as well as `My_Plugin\\`, will be accepted automatically. + * Passing a prefix property value containing namespace separators will now also be allowed and will no longer trigger a warning. +- `WordPress` to the prefix blacklist for the `WordPress.NamingConventions.PrefixAllGlobals` sniff. + While the prefix cannot be `WordPress`, a prefix can still _start with_ or _contain_ `WordPress`. +- Additional unit tests covering a change in the tokenizer which will be included in the upcoming `PHP_CodeSniffer` 3.4.0 release. +- A variety of issue templates for use on GitHub. + +### Changed +- The `Sniff::valid_direct_scope()` method will now return the `$stackPtr` to the valid scope if a valid direct scope has been detected. Previously, it would return `true`. +- Minor hardening and efficiency improvements to the `WordPress.NamingConventions.PrefixAllGlobals` sniff. +- The inline documentation of the `WordPress-Core` ruleset has been updated to be in line again with [the handbook](https://make.wordpress.org/core/handbook/best-practices/coding-standards/php/). +- The inline links to documentation about the VIP requirements have been updated. +- Updated the [custom ruleset example](https://github.com/WordPress/WordPress-Coding-Standards/blob/develop/phpcs.xml.dist.sample) to recommend using `PHPCompatibilityWP` rather than `PHPCompatibility`. +- All sniffs are now also being tested against PHP 7.3 for consistent sniff results. + Note: PHP 7.3 is only supported in combination with PHPCS 3.3.1 or higher as `PHP_CodeSniffer` itself has an incompatibility in earlier versions. +- Minor grammar fixes in text strings and documentation. +- Minor consistency improvement for the unit test case files. +- Minor tweaks to the `composer.json` file. +- Updated the PHPCompatibility `dev` dependency. + +### Removed +- The `WordPress.WhiteSpace.CastStructureSpacing.NoSpaceAfterCloseParenthesis` error code as an error for the same issue was already being thrown by an included upstream sniff. + +### Fixed +- The `WordPress.CodeAnalysis.EmptyStatement` would throw a false positive for an empty condition in a `for()` statement. +- The `Sniff::is_class_property()` method could, in certain circumstances, incorrectly recognize parameters in a method declaration as class properties. It would also, incorrectly, fail to recognize class properties when the object they are declared in, was nested in parentheses. + This affected, amongst others, the `GlobalVariablesOverride` sniff. +- The `Sniff::get_declared_namespace_name()` method could get confused over whitespace and comments within a namespace name, which could lead to incorrect results (mostly underreporting). + This affected, amongst others, the `GlobalVariablesOverride` sniff. + The return value of the method will now no longer contain any whitespace or comments encountered. +- The `Sniff::has_whitelist_comment()` method would sometimes incorrectly regard `// phpcs:set` comments as whitelist comments. + +## [1.1.0] - 2018-09-10 + +### Added +- New `WordPress.PHP.NoSilencedErrors` sniff. This sniff replaces the `Generic.PHP.NoSilencedErrors` sniff which was previously used and included in the `WordPress-Core` ruleset. + The WordPress specific version of the sniff differs from the PHPCS version in that it: + * Allows the error control operator `@` if it preceeds a function call to a limited list of PHP functions for which no amount of error checking can prevent a PHP warning from being thrown. + * Allows for a used-defined list of (additional) function names to be passed to the sniff via the `custom_whitelist` property in a custom ruleset, for which - if the error control operator is detected in front of a function call to one of the functions in this whitelist - no warnings will be thrown. + * Displays a brief snippet of code in the `warning` message text to show the context in which the error control operator is being used. The length of the snippet (in tokens) can be customized via the `context_length` property. + * Contains a public `use_default_whitelist` property which can be set from a custom ruleset which regulates whether or not the standard whitelist of PHP functions should be used by the sniff. + The user-defined whitelist will always be respected. + By default, this property is set to `true` for the `WordPress-Core` ruleset and to `false` for the `WordPress-Extra` ruleset (which is stricter regarding these kind of best practices). +- Metrics to the `WordPress.NamingConventions.PrefixAllGlobals` sniff to aid people in determining the most commonly used prefix in a legacy project. + For an example of how to use this feature, please see the detailed explanation in the [pull request](https://github.com/WordPress/WordPress-Coding-Standards/pull/1437). + +### Changed +- The `PEAR.Functions.FunctionCallSignature` sniff, which is part of the `WordPress-Core` ruleset, used to allow multiple function call parameters per line in multi-line function calls. This will no longer be allowed. + As of this release, if a function call is multi-line, each parameter should start on a new line and an `error` will be thrown if the code being analysed does not comply with that rule. + The sniff behaviour for single-line function calls is not affected by this change. +- Moved the `WordPress.CodeAnalysis.EmptyStatement` sniff from the `WordPress-Extra` to the `WordPress-Core` ruleset. +- Moved the `Squiz.PHP.CommentedOutCode` sniff from the `WordPress-Docs` to the `WordPress-Extra` ruleset and lowered the threshold for determining whether or not a comment is commented out code from 45% to 40%. +- The `WordPress.NamingConventions.PrefixAllGlobals` sniff now has improved support for recognizing whether or not (non-prefixed) globals are declared in the context of unit tests. +- The `is_foreach_as()` method has been moved from the `GlobalVariablesOverrideSniff` class to the WordPress `Sniff` base class. +- The `Sniff::is_token_in_test_method()` utility method now has improved support for recognizing test methods in anonymous classes. +- Minor efficiency improvement to the `Sniff::is_safe_casted()` method. +- CI: Minor tweaks to the Travis script. +- CI: Improved Composer scripts for use by WPCS developers. +- Dev: Removed IDE specific files from `.gitignore`. +- Readme: Improved the documentation about the project history and the badge display. + +### Fixed +- The `WordPress.Security.ValidatedSanitizedInput` sniff will now recognize array keys in superglobals independently of the string quote-style used for the array key. +- The `WordPress.WhiteSpace.PrecisionAlignment` sniff will no longer throw false positives for DocBlocks for JavaScript functions within inline HTML. +- `WordPress.WP.DeprecatedClasses`: The error codes for this sniff were unstable as they were based on the code being analysed instead of on fixed values. +- Various bugfixes for the `WordPress.WP.GlobalVariablesOverride` sniff: + - Previously, the sniff only checked variables in the global namespace when a `global` statement would be encountered. As of now, all variable assignments in the global namespace will be checked. + - Nested functions/closures/classes which don't import the global variable will now be skipped over when encountered within another function, preventing false positives. + - Parameters in function declarations will no longer throw false positives. + - The error message for assignments to a subkey of the `$GLOBALS` superglobal has been improved. + - Various efficiency improvements. +- The `Sniff::is_in_isset_or_empty()` method presumed the WordPress coding style regarding code layout, which could lead to incorrect results (mostly underreporting). + This affected, amongst others, the `WordPress.Security.ValidatedSanitizedInput` sniff. +- Broken links in the inline developer documentation. + + +## [1.0.0] - 2018-07-25 + +### Important information about this release: + +If you use the WordPress Coding Standards with a custom ruleset, please be aware that a number of sniffs have been moved between categories and that the old sniff names have been deprecated. +If you selectively include any of these sniffs in your custom ruleset or set custom property values for these sniffs, your custom ruleset will need to be updated. + +The `WordPress-VIP` ruleset has also been deprecated. If you used that ruleset to check your theme/plugin for hosting on the WordPress.com VIP platform, please use the [Automattic VIP coding standards](https://github.com/Automattic/VIP-Coding-Standards) instead. +If you used that ruleset for any other reason, you should probably use the `WordPress-Extra` or `WordPress` ruleset instead. + +These and some related changes have been annotated in detail in the `Deprecated` section of this changelog. + +Please read the complete changelog carefully before you upgrade. + +If you are a maintainer of an external standard based on WPCS and any of your custom sniffs are based on or extend WPCS sniffs, the same applies. + +### Added +- `WordPress.PHP.PregQuoteDelimiter` sniff to the `WordPress-Extra` ruleset to warn about calls to `preg_quote()` which don't pass the `$delimiter` parameter. +- `WordPress.Security.SafeRedirect` sniff to the `WordPress-Extra` ruleset to warn about potential open redirect vulnerabilities. +- `WordPress.WP.DeprecatedParameterValues` sniff to the `WordPress-Extra` ruleset to detect deprecated parameter values being passed to select functions. +- `WordPress.WP.EnqueuedResourceParameters` sniff to the `WordPress-Extra` ruleset to detect: + - Calls to the script/style register/enqueue functions which don't pass a `$version` for the script/style, which can cause issues with browser caching; and/or + - Calls to the register/enqueue script functions which don't pass the `$in_footer` parameter, which causes scripts - by default - to be loaded in the HTML header in a layout rendering blocking manner. +- Detection of calls to `strip_tags()` and various PHP native `..rand()` functions to the `WordPress.WP.AlternativeFunctions` sniff. +- `readonly()` to the list of auto-escaped functions `Sniff::$autoEscapedFunctions`. This affects the `WordPress.Security.EscapeOutput` sniff. +- The `WordPress.Security.PluginMenuSlug`, `WordPress.WP.CronInterval`, `WordPress.WP.PostsPerPage` and `WordPress.WP.TimezoneChange` sniffs are now included in the `WordPress-Extra` ruleset. Previously, they were already included in the `WordPress` and `WordPress-VIP` rulesets. +- New utility method `Sniff::is_use_of_global_constant()`. +- A rationale to the package suggestion made via `composer.json`. +- CI: Validation of the `composer.json` file on each build. +- A wiki page with instructions on how to [set up WPCS to run with Eclipse on XAMPP](https://github.com/WordPress/WordPress-Coding-Standards/wiki/How-to-use-WPCS-with-Eclipse-and-XAMPP). +- Readme: A link to an external resource with more examples for setting up PHPCS for CI. +- Readme: A badge-based quick overview of the project. + +### Changed +- The `WordPress` ruleset no longer includes the `WordPress-VIP` ruleset, nor does it include any of the (deprecated) `VIP` sniffs anymore. +- The following sniffs have been moved to a new category: + - `CronInterval` from the `VIP` category to the `WP` category. + - `DirectDatabaseQuery` from the `VIP` category to the `DB` category. + - `DontExtract` from the `Functions` category to the `PHP` category. + - `EscapeOutput` from the `XSS` category to the `Security` category. + - `GlobalVariables` from the `Variables` category to the `WP` category. + - `NonceVerification` from the `CSRF` category to the `Security` category. + - `PluginMenuSlug` from the `VIP` category to the `Security` category. + - `PreparedSQL` from the `WP` category to the `DB` category. + - `SlowDBQuery` from the `VIP` category to the `DB` category. + - `TimezoneChange` from the `VIP` category to the `WP` category. + - `ValidatedSanitizedInput` from the `VIP` category to the `Security` category. +- The `WordPress.VIP.PostsPerPage` sniff has been split into two distinct sniffs: + - `WordPress.WP.PostsPerPage` which will check for the use of a high pagination limit and will throw a `warning` when this is encountered. For the `VIP` ruleset, the error level remains `error`. + - `WordPress.VIP.PostsPerPage` wich will check for disabling of pagination. +- The default value for `minimum_supported_wp_version`, as used by a [number of sniffs detecting usage of deprecated WP features](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#minimum-wp-version-to-check-for-usage-of-deprecated-functions-classes-and-function-parameters), has been updated to `4.6`. +- The `WordPress.WP.AlternativeFunctions` sniff will now only throw a warning if/when the recommended alternative function is available in the minimum supported WP version of a project. + In addition to this, certain alternatives are only valid alternatives in certain circumstances, like when the WP version only supports the first parameter of the PHP function it is trying to replace. + This will now be taken into account for: + - `wp_strip_all_tags()` is only a valid alternative for the PHP native `strip_tags()` when the second parameter `$allowed_tags` has not been passed. + - `wp_parse_url()` only added support for the second parameter `$component` of the PHP native `parse_url()` function in WP 4.7.0. +- The `WordPress.WP.DeprecatedFunctions` sniff will now detect functions deprecated in WP 4.9. +- The `WordPress.WP.GlobalVariablesOverride` sniff will now display the name of the variable being overridden in the error message. +- The `WordPress.WP.I18n` sniff now extends the `AbstractFunctionRestrictionSniff`. +- Assignments in conditions in ternaries as detected by the `WordPress.CodeAnalysis.AssignmentInCondition` sniff will now be reported under a separate error code `FoundInTernaryCondition`. +- The default error level for the notices from the `WordPress.DB.DirectDatabaseQuery` sniff has been lowered from `error` to `warning`. For the `VIP` ruleset, the error level remains `error`. +- The default error level for the notices from the `WordPress.Security.PluginMenuSlug` sniff has been lowered from `error` to `warning`. For the `VIP` ruleset, the error level remains `error`. +- The default error level for the notices from the `WordPress.WP.CronInterval` sniff has been lowered from `error` to `warning`. For the `VIP` ruleset, the error level remains `error`. +- The `Sniff::get_function_call_parameters()` utility method now has improved handling of closures when passed as function call parameters. +- Rulesets: a number of error codes were previously silenced by explicitly `exclude`-ing them. Now, they will be silenced by setting the `severity` to `0` which makes it more easily discoverable for maintainers of custom rulesets how to enable these error codes again. +- Various performance optimizations which should most notably make a difference when running WPCS on PHP 7. +- References to the WordPress.com VIP platform have been clarified. +- Unit Tests: custom properties set in unit test files are reset after use. +- Various improvements to the ruleset used by the WPCS project itself and minor code clean up related to this. +- CI: Each change will now also be tested against the lowest supported PHPCS 3 version. +- CI: Each change will now also be checked for PHP cross-version compatibility. +- CI: The rulesets will now also be tested on each change to ensure no unexpected messages are thrown. +- CI: Minor changes to the script to make the build testing faster. +- Updated the [custom ruleset example](https://github.com/WordPress/WordPress-Coding-Standards/blob/develop/phpcs.xml.dist.sample) for the changes contained in this release and to reflect current best practices regarding the PHPCompatibility standard. +- The instructions on how to set up WPCS for various IDEs have been moved from the `README` to the [wiki](https://github.com/WordPress/WordPress-Coding-Standards/wiki). +- Updated output examples in `README.md` and `CONTRIBUTING.md` and other minor changes to these files. +- Updated references to the PHPCompatibility standard to reflect its new location and recommend using PHPCompatibilityWP. + +### Deprecated +- The `WordPress-VIP` ruleset has been deprecated. + For checking a theme/plugin for hosting on the WordPress.com VIP platform, please use the [Automattic VIP coding standards](https://github.com/Automattic/VIP-Coding-Standards) instead. + If you used the `WordPress-VIP` ruleset for any other reason, you should probably use the `WordPress-Extra` or `WordPress` ruleset instead. +- The following sniffs have been deprecated and will be removed in WPCS 2.0.0: + - `WordPress.CSRF.NonceVerification` - use `WordPress.Security.NonceVerification` instead. + - `WordPress.Functions.DontExtract` - use `WordPress.PHP.DontExtract` instead. + - `WordPress.Variables.GlobalVariables` - use `WordPress.WP.GlobalVariablesOverride` instead. + - `WordPress.VIP.CronInterval` - use `WordPress.WP.CronInterval` instead. + - `WordPress.VIP.DirectDatabaseQuery` - use `WordPress.DB.DirectDatabaseQuery` instead. + - `WordPress.VIP.PluginMenuSlug` - use `WordPress.Security.PluginMenuSlug` instead. + - `WordPress.VIP.SlowDBQuery` - use `WordPress.DB.SlowDBQuery` instead. + - `WordPress.VIP.TimezoneChange` - use `WordPress.WP.TimezoneChange` instead. + - `WordPress.VIP.ValidatedSanitizedInput` - use `WordPress.Security.ValidatedSanitizedInput` instead. + - `WordPress.WP.PreparedSQL` - use `WordPress.DB.PreparedSQL` instead. + - `WordPress.XSS.EscapeOutput` - use `WordPress.Security.EscapeOutput` instead. + - `WordPress.VIP.AdminBarRemoval` without replacement. + - `WordPress.VIP.FileSystemWritesDisallow` without replacement. + - `WordPress.VIP.OrderByRand` without replacement. + - `WordPress.VIP.RestrictedFunctions` without replacement. + - `WordPress.VIP.RestrictedVariables` without replacement. + - `WordPress.VIP.SessionFunctionsUsage` without replacement. + - `WordPress.VIP.SessionVariableUsage` without replacement. + - `WordPress.VIP.SuperGlobalInputUsage` without replacement. +- The following sniff categories have been deprecated and will be removed in WPCS 2.0.0: + - `CSRF` + - `Variables` + - `XSS` +- The `posts_per_page` property in the `WordPress.VIP.PostsPerPage` sniff has been deprecated as the related functionality has been moved to the `WordPress.WP.PostsPerPage` sniff. + See [WP PostsPerPage: post limit](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#wp-postsperpage-post-limit) for more information about this property. +- The `exclude` property which is available to most sniffs which extend the `AbstractArrayAssignmentRestrictions`, `AbstractFunctionRestrictions` and `AbstractVariableRestrictions` classes or any of their children, used to be a `string` property and expected a comma-delimited list of groups to exclude. + The type of the property has now been changed to `array`. Custom rulesets which pass this property need to be adjusted to reflect this change. + Support for passing the property as a comma-delimited string has been deprecated and will be removed in WPCS 2.0.0. + See [Excluding a group of checks](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#excluding-a-group-of-checks) for more information about the sniffs affected by this change. +- The `AbstractVariableRestrictionsSniff` class has been deprecated as all sniffs depending on this class have been deprecated. Unless a new sniff is created in the near future which uses this class, the abstract class will be removed in WPCS 2.0.0. +- The `Sniff::has_html_open_tag()` utility method has been deprecated as it is now only used by deprecated sniffs. The method will be removed in WPCS 2.0.0. + +### Removed +- `cancel_comment_reply_link()`, `get_bookmark()`, `get_comment_date()`, `get_comment_time()`, `get_template_part()`, `has_post_thumbnail()`, `is_attachement()`, `post_password_required()` and `wp_attachment_is_image()` from the list of auto-escaped functions `Sniff::$autoEscapedFunctions`. This affects the `WordPress.Security.EscapeOutput` sniff. +- WPCS no longer explicitly supports HHVM and builds are no longer tested against HHVM. + For now, running WPCS on HHVM to test PHP code may still work for a little while, but HHVM has announced they are [dropping PHP support](https://hhvm.com/blog/2017/09/18/the-future-of-hhvm.html). + +### Fixed +- Compatibility with PHP 7.3. A change in PHP 7.3 was causing the `WordPress.DB.RestrictedClasses`, `WordPress.DB.RestrictedFunctions` and the `WordPress.WP.AlternativeFunctions` sniffs to fail to correctly detect issues. +- Compatibility with the latest releases from [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer). + PHPCS 3.2.0 introduced new annotations which can be used inline to selectively disable/ignore certain sniffs. + **Note**: The initial implementation of the new annotations was buggy. If you intend to start using these new style annotations, you are strongly advised to use PHPCS 3.3.0 or higher. + For more information about these annotations, please refer to the [PHPCS Wiki](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Advanced-Usage#ignoring-parts-of-a-file). + - The [WPCS native whitelist comments](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Whitelisting-code-which-flags-errors) can now be combined with the new style PHPCS whitelist annotations in the `-- for reasons` part of the annotation. + - `WordPress.Arrays.ArrayDeclarationSpacing`: the fixer will now handle the new style annotations correctly. + - `WordPress.Arrays.CommaAfterArrayItem`: prevent a fixer loop when new style annotations are encountered. + - `WordPress.Files.FileName`: respect the new style annotations if these would selectively disable this sniff. + - `WordPress.WhiteSpace.ControlStructureSpacing`: handle the new style annotations correctly for the "blank line at the start/end of control structure" checks and prevent a fixer conflict when the new style annotations are encountered. + - `WordPress.WhiteSpace.PrecisionAlignment`: allow for checking of for precision alignment on lines containing new style annotations when `phpcs` is run with `--ignore-annotations`. +- The `Sniff::is_test_class()` method now has improved recognition of namespaced test classes. + This positively affects the `WordPress.Files.FileName`, `WordPress.NamingConventions.PrefixAllGlobals` and `WordPress.WP.GlobalVariablesOverride` sniffs, which each allow for test classes to (partially) not comply with the rules these sniffs check for. + This fixes the following bugs: + - Namespaced classes where the classname was one of the whitelisted global classes would incorrectly be recognized as a test class, even though they were not the same class. + This also happened if a namespaced class `extend`ed one of the whitelisted global classes. + - A namespaced custom test class where the name was split between the namespace declaration and the extended class declaration was not correctly recognized as the whitelisted test class. + - A namespaced test class which extended another class using a FQCN prefixed with a `\\` would not be correctly recognized. + - The `custom_test_class_whitelist` property which is available for each of these sniffs expects to be passed a Fully Qualified Class Name. FQCNs prefixed with a global namespace indicator will now be correctly handled. +- The determination of whether a `T_STRING` is a function call or not has been improved in the `AbstractFunctionRestrictions` class. This improvement benefits all sniffs which extend this abstract and any of its children (> 10 sniffs) and fixes the following false positives: + - Class declarations will no longer be confused with function calls. + - Use statement alias declarations will no longer be confused with function calls. +- Various bugs in the `WordPress.Arrays.ArrayIndentation` sniff: + - The sniff will no longer throw false positives or try to fix multi-line text strings where the closing quote is on a line by itself. + - The sniff would go into a fixer loop when it encountered a multi-line trailing comment after an array item. +- The `WordPress.CodeAnalysis.AssignmentInCondition` was throwing false positives for ternaries in nested, but unrelated, parentheses. +- The `WordPress.CodeAnalysis.EmptyStatement` and `WordPress.Files.FileName` sniffs underreported as they did not take PHP short open echo tags into account. +- Various bugs in the `WordPress.NamingConventions.PrefixAllGlobals` sniff: + - Parameters in a closure declaration were incorrectly being regarded as global variables. + - Non-prefixed variables created by a `foreach()` construct in the global namespace were previously not detected. + - Non-prefixed globals found in namespaced test classes should be ignored by the sniff, but were not. + - Definition of non-prefixed global WP constants which are intended to be overruled, should not trigger an error from this sniff. + - The sniff presumed the WP naming conventions for PHP constructs, while it should check for the construct being prefixed regardless of whether camelCase, PascalCase, snake_case or other naming conventions are used. + - The sniff presumed the WP naming conventions for prefixes used in hook names. The sniff will now be more tolerant when non-conventional word separators are used in prefixes for hooks. +- The `WordPress.NamingConventions.ValidFunctionName` sniff no longer "hides" one message behind another. The sniff will now correctly throw a message about function names not being in `snake_case`, even when the `FunctionDoubleUnderscore` or `MethodDoubleUnderscore` error codes have been excluded. +- The `WordPress.PHP.StrictInArray` sniff will no longer throw an error when `in_array`, `array_search` or `array_keys` are used in a file `use` statement. +- Various bugs in the `WordPress.Security.EscapeOutput` sniff: + - A limited list of native PHP constants which are safe to use, such as `PHP_EOL`, has been added. When any of these constants are encountered, the sniff will no longer demand output escaping for them. + - The sniff was underreporting issues with variables passed to `trigger_error()`. + - While reporting an issue, sometimes the wrong error message was used. The sniff logic has been adjusted to prevent this. + - The sniff will now correctly ignore the open and close brackets of short arrays. + - The sniff would throw false positives when `echo`, `print`, `exit` or `die` were encountered as constants, function or class names. While it may not be a good idea to use PHP keywords in such a way, it is allowed, so the sniff should handle this correctly. +- The `WordPress.WhiteSpace.ControlStructureSpacing` sniff would inadvertently throw an error for the spacing around the colon for a return type in a function declaration. +- The `WordPress.WP.AlternativeFunctions` sniff used to flag all function calls to `file_get_contents()` twice, suggesting to use `wp_remote_get()` - which is only applicable for remote URLs - and the `WP_FileSystem` API - which is not needed when just _reading_ local files. These messages contradicted each other. + The sniff will now try to determine whether the file requested is local or remote and will only throw a `warning` suggesting to use `wp_remote_get()`, if a remote URL is being requested or when it could not be determined if the requested file is local or remote. +- The expected default value for `wp_upload_bits()` in the `WordPress.WP.DeprecatedParameters` sniff. +- The `WordPress.WP.GlobalVariablesOverride` sniff previously did not detect variables created by a `foreach()` construct which would override WP global variables. +- Various bugs in the `WordPress.WP.I18n` sniff: + - The sniff will no longer throw false positives for calls to methods carrying the same name as any of the global WP functions being targeted and has improved handling of parse errors and live coding. + - A numeric `0` would throw a false positive for "no translatable content found". +- The fixer in the `WordPress.WhiteSpace.ControlStructureSpacing` sniff will no longer inadvertently remove return type declarations. +- Various bugs in the `WordPress.WhiteSpace.PrecisionAlignment` sniff: + - Inline HTML before the first PHP open tag was not being examined. + - Files which only contained short open echo tags for PHP were not being examined. + - The last line of inline HTML in a file was not being examined. +- Some best practice sniffs presumed the WordPress coding style regarding code layout, which could lead to incorrect results (mostly underreporting). + The following sniffs have received fixes related to this: + - `WordPress.DB.PreparedSQL` + - `WordPress.NamingConventions.ValidVariableName` + - `WordPress.WP.CronInterval` + - `WordPress.WP.I18n` +- Various minor fixes based on visual inspection and Scrutinizer analysis feedback. +- Typo in the instructions contained in `CONTRIBUTING.md`. +- Broken link in the `README.md` file. + + +## [0.14.1] - 2018-02-15 + +### Fixed +- The `WordPress.NamingConventions.PrefixAllGlobals` sniff contained a bug which could inadvertently trigger class autoloading of the project being sniffed and by extension could cause fatal errors during the PHPCS run. + +## [0.14.0] - 2017-11-01 + +### Added +- `WordPress.Arrays.MultipleStatementAlignment` sniff to the `WordPress-Core` ruleset which will align the array assignment operator for multi-item, multi-line associative arrays. + This new sniff offers four custom properties to customize its behaviour: [`ignoreNewlines`](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#array-alignment-allow-for-new-lines), [`exact`](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#array-alignment-allow-non-exact-alignment), [`maxColumn`](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#array-alignment-maximum-column) and [`alignMultilineItems`](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#array-alignment-dealing-with-multi-line-items). +- `WordPress.DB.PreparedSQLPlaceholders` sniff to the `WordPress-Core` ruleset which will analyse the placeholders passed to `$wpdb->prepare()` for their validity, check whether queries using `IN ()` and `LIKE` statements are created correctly and will check whether a correct number of replacements are passed. + This sniff should help detect queries which are impacted by the security fixes to `$wpdb->prepare()` which shipped with WP 4.8.2 and 4.8.3. + The sniff also adds a new ["PreparedSQLPlaceholders replacement count" whitelist comment](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Whitelisting-code-which-flags-errors#preparedsql-placeholders-vs-replacements) for pertinent replacement count vs placeholder mismatches. Please consider carefully whether something could be a bug when you are tempted to use the whitelist comment and if so, [report it](https://github.com/WordPress/WordPress-Coding-Standards/issues/new). +- `WordPress.PHP.DiscourageGoto` sniff to the `WordPress-Core` ruleset. +- `WordPress.PHP.RestrictedFunctions` sniff to the `WordPress-Core` ruleset which initially forbids the use of `create_function()`. + This was previous only discouraged under certain circumstances. +- `WordPress.WhiteSpace.ArbitraryParenthesesSpacing` sniff to the `WordPress-Core` ruleset which checks the spacing on the inside of arbitrary parentheses. +- `WordPress.WhiteSpace.PrecisionAlignment` sniff to the `WordPress-Core` ruleset which will throw a warning when precision alignment is detected in PHP, JS and CSS files. +- `WordPress.WhiteSpace.SemicolonSpacing` sniff to the `WordPress-Core` ruleset which will throw a (fixable) error when whitespace is found before a semi-colon, except for when the semi-colon denotes an empty `for()` condition. +- `WordPress.CodeAnalysis.AssignmentInCondition` sniff to the `WordPress-Extra` ruleset. +- `WordPress.WP.DiscouragedConstants` sniff to the `WordPress-Extra` and `WordPress-VIP` rulesets to detect usage of deprecated WordPress constants, such as `STYLESHEETPATH` and `HEADER_IMAGE`. +- Ability to pass the `minimum_supported_version` to use for the `DeprecatedFunctions`, `DeprecatedClasses` and `DeprecatedParameters` sniff in one go. You can pass a `minimum_supported_wp_version` runtime variable for this [from the command line or pass it using a `config` directive in a custom ruleset](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#setting-minimum-supported-wp-version-for-all-sniffs-in-one-go-wpcs-0140). +- `Generic.Formatting.MultipleStatementAlignment` - customized to have a `maxPadding` of `40` -, `Generic.Functions.FunctionCallArgumentSpacing` and `Squiz.WhiteSpace.ObjectOperatorSpacing` to the `WordPress-Core` ruleset. +- `Squiz.Scope.MethodScope`, `Squiz.Scope.MemberVarScope`, `Squiz.WhiteSpace.ScopeKeywordSpacing`, `PSR2.Methods.MethodDeclaration`, `Generic.Files.OneClassPerFile`, `Generic.Files.OneInterfacePerFile`, `Generic.Files.OneTraitPerFile`, `PEAR.Files.IncludingFile`, `Squiz.WhiteSpace.LanguageConstructSpacing`, `PSR2.Namespaces.NamespaceDeclaration` to the `WordPress-Extra` ruleset. +- The `is_class_constant()`, `is_class_property` and `valid_direct_scope()` utility methods to the `WordPress\Sniff` class. + +### Changed +- When passing an array property via a custom ruleset to PHP_CodeSniffer, spaces around the key/value are taken as intentional and parsed as part of the array key/value. In practice, this leads to confusion and WPCS does not expect any values which could be preceded/followed by a space, so for the WordPress Coding Standard native array properties, like `customAutoEscapedFunction`, `text_domain`, `prefixes`, WPCS will now trim whitespace from the keys/values received before use. +- The WPCS native whitelist comments used to only work when they were put on the _end of the line_ of the code they applied to. As of now, they will also be recognized when they are be put at the _end of the statement_ they apply to. +- The `WordPress.Arrays.ArrayDeclarationSpacing` sniff used to enforce all associative arrays to be multi-line. The handbook has been updated to only require this for multi-item associative arrays and the sniff has been updated accordingly. + [The original behaviour can still be enforced](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#arrays-forcing-single-item-associative-arrays-to-be-multi-line) by setting the new `allow_single_item_single_line_associative_arrays` property to `false` in a custom ruleset. +- The `WordPress.NamingConventions.PrefixAllGlobals` sniff will now allow for a limited list of WP core hooks which are intended to be called by plugins and themes. +- The `WordPress.PHP.DiscouragedFunctions` sniff used to include `create_function`. This check has been moved to the new `WordPress.PHP.RestrictedFunctions` sniff. +- The `WordPress.PHP.StrictInArray` sniff now has a separate error code `FoundNonStrictFalse` for when the `$strict` parameter has been set to `false`. This allows for excluding the warnings for that particular situation, which will normally be intentional, via a custom ruleset. +- The `WordPress.VIP.CronInterval` sniff now allows for customizing the minimum allowed cron interval by [setting a property in a custom ruleset](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#vip-croninterval-minimum-interval). +- The `WordPress.VIP.RestrictedFunctions` sniff used to prohibit the use of certain WP native functions, recommending the use of `wpcom_vip_get_term_link()`, `wpcom_vip_get_term_by()` and `wpcom_vip_get_category_by_slug()` instead, as the WP native functions were not being cached. As the results of the relevant WP native functions are cached as of WP 4.8, the advice has now been reversed i.e. use the WP native functions instead of `wpcom...` functions. +- The `WordPress.VIP.PostsPerPage` sniff now allows for customizing the `post_per_page` limit for which the sniff will trigger by [setting a property in a custom ruleset](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#vip-postsperpage-post-limit). +- The `WordPress.WP.I18n` sniff will now allow and actively encourage omitting the text domain in I18n function calls if the text domain passed via the `text_domain` property is `default`, i.e. the domain used by Core. + When `default` is one of several text domains passed via the `text_domain` property, the error thrown when the domain is missing has been downgraded to a `warning`. +- The `WordPress.XSS.EscapeOutput` sniff now has a separate error code `OutputNotEscapedShortEcho` and the error message texts have been updated. +- Moved `Squiz.PHP.Eval` from the `WordPress-Extra` and `WordPress-VIP` to the `WordPress-Core` ruleset. +- Removed two sniffs from the `WordPress-VIP` ruleset which were already included via the `WordPress-Core` ruleset. +- The unit test suite is now compatible with PHPCS 3.1.0+ and PHPUnit 6.x. +- Some tidying up of the unit test case files. +- All sniffs are now also being tested against PHP 7.2 for consistent sniff results. +- An attempt is made to detect potential fixer conflicts early via a special build test. +- Various minor documentation fixes. +- Improved the Atom setup instructions in the Readme. +- Updated the unit testing information in Contributing. +- Updated the [custom ruleset example](https://github.com/WordPress/WordPress-Coding-Standards/blob/develop/phpcs.xml.dist.sample) for the changes contained in this release and to make it more explicit what is recommended versus example code. +- The minimum recommended version for the suggested `DealerDirect/phpcodesniffer-composer-installer` Composer plugin has gone up to `0.4.3`. This patch version fixes support for PHP 5.3. + +### Fixed +- The `WordPress.Arrays.ArrayIndentation` sniff did not correctly handle array items with multi-line strings as a value. +- The `WordPress.Arrays.ArrayIndentation` sniff did not correctly handle array items directly after an array item with a trailing comment. +- The `WordPress.Classes.ClassInstantiation` sniff will now correctly handle detection when using `new $array['key']` or `new $array[0]`. +- The `WordPress.NamingConventions.PrefixAllGlobals` sniff did not allow for arbitrary word separators in hook names. +- The `WordPress.NamingConventions.PrefixAllGlobals` sniff did not correctly recognize namespaced constants as prefixed. +- The `WordPress.PHP.StrictInArray` sniff would erronously trigger if the `true` for `$strict` was passed in uppercase. +- The `WordPress.PHP.YodaConditions` sniff could get confused over complex ternaries containing assignments. This has been remedied. +- The `WordPress.WP.PreparedSQL` sniff would erronously throw errors about comments found within a DB function call. +- The `WordPress.WP.PreparedSQL` sniff would erronously throw errors about `(int)`, `(float)` and `(bool)` casts and would also flag the subsequent variable which had been safe casted. +- The `WordPress.XSS.EscapeOutput` sniff would erronously trigger when using a fully qualified function call - including the global namespace `\` indicator - to one of the escaping functions. +- The lists of WP global variables and WP mixed case variables have been synchronized, which fixes some false positives. + + +## [0.13.1] - 2017-08-07 + +### Fixed +- Fatal error when using PHPCS 3.x with the `installed_paths` config variable set via the ruleset. + +## [0.13.0] - 2017-08-03 + +### Added +- Support for PHP_CodeSniffer 3.0.2+. The minimum required PHPCS version (2.9.0) stays the same. +- Support for the PHPCS 3 `--ignore-annotations` command line option. If you pass this option, both PHPCS native `@ignore ...` annotations as well as the WPCS specific [whitelist flags](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Whitelisting-code-which-flags-errors) will be ignored. + +### Changed +- The minimum required PHP version is now 5.3 when used in combination with PHPCS 2.x and PHP 5.4 when used in combination with PHPCS 3.x. +- The way the unit tests can be run is now slightly different for PHPCS 2.x versus 3.x. For more details, please refer to the updated information in the [Contributing Guidelines](CONTRIBUTING.md). +- Release archives will no longer contain the unit tests and other typical development files. You can still get these by using Composer with `--prefer-source` or by checking out a git clone of the repository. +- Various textual improvements to the Readme. +- Various textual improvements to the Contributing Guidelines. +- Minor internal changes. + +### Removed +- The `WordPress.Arrays.ArrayDeclaration` sniff has been deprecated. The last remaining checks this sniff contained have been moved to the `WordPress.Arrays.ArrayDeclarationSpacing` sniff. +- Work-arounds which were in place to support PHP 5.2. + +### Fixed +- A minor bug where the auto-fixer could accidentally remove a comment near an array opener. + + +## [0.12.0] - 2017-07-21 + +### Added +- A default file encoding setting to the `WordPress-Core` ruleset. All files sniffed will now be regarded as `utf-8` by default. +- `WordPress.Arrays.ArrayIndentation` sniff to the `WordPress-Core` ruleset to verify - and auto-fix - the indentation of array items and the array closer for multi-line arrays. This replaces the (partial) indentation fixing contained within the `WordPress.Array.ArrayDeclarationSpacing` sniff. +- `WordPress.Arrays.CommaAfterArrayItem` sniff to the `WordPress-Core` ruleset to enforce that each array item is followed by a comma - except for the last item in a single-line array - and checks the spacing around the comma. This replaces (and improves) the checks which were previously included in the `WordPress.Arrays.ArrayDeclaration` sniff which were causing incorrect fixes and fixer conflicts. +- `WordPress.Functions.FunctionCallSignatureNoParams` sniff to the `WordPress-Core` ruleset to verify that function calls without parameters do not have any whitespace between the parentheses. +- `WordPress.WhiteSpace.DisallowInlineTabs` to the `WordPress-Core` ruleset to verify - and auto-fix - that spaces are used for mid-line alignment. +- `WordPress.WP.CapitalPDangit` sniff to the `WordPress-Core` ruleset to - where relevant - verify that `WordPress` is spelled correctly. For misspellings in text strings and comment text, the sniff can auto-fix violations. +- `Squiz.Classes.SelfMemberReference` whitespace related checks to the `WordPress-Core` ruleset and the additional check for using `self` rather than a FQN to the `WordPress-Extra` ruleset. +- `Squiz.PHP.EmbeddedPhp` sniff to the `WordPress-Core` ruleset to check PHP code embedded within HTML blocks. +- `PSR2.ControlStructures.SwitchDeclaration` to the `WordPress-Core` ruleset to check for the correct layout of `switch` control structures. +- `WordPress.Classes.ClassInstantion` sniff to the `WordPress-Extra` ruleset to detect - and auto-fix - missing parentheses on object instantiation and superfluous whitespace in PHP and JS files. The sniff will also detect `new` being assigned by reference. +- `WordPress.CodeAnalysis.EmptyStatement` sniff to the `WordPress-Extra` ruleset to detect - and auto-fix - superfluous semi-colons and empty PHP open-close tag combinations. +- `WordPress.NamingConventions.PrefixAllGlobals` sniff to the `WordPress-Extra` ruleset to verify that all functions, classes, interfaces, traits, variables, constants and hook names which are declared/defined in the global namespace are prefixed with one of the prefixes provided via a custom property or via the command line. + To activate this sniff, [one or more allowed prefixes should be provided to the sniff](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#naming-conventions-prefix-everything-in-the-global-namespace). This can be done using a custom ruleset or via the command line. + PHP superglobals and WP global variables are exempt from variable name prefixing. Deprecated hook names will also be disregarded when non-prefixed. Back-fills for known native PHP functionality is also accounted for. + For verified exceptions, [unprefixed code can be whitelisted](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Whitelisting-code-which-flags-errors#non-prefixed-functionclassvariableconstant-in-the-global-namespace). + Code in unit test files is automatically exempt from this sniff. +- `WordPress.WP.DeprecatedClasses` sniff to the `WordPress-Extra` ruleset to detect usage of deprecated WordPress classes. +- `WordPress.WP.DeprecatedParameters` sniff to the `WordPress-Extra` ruleset to detect deprecated parameters being passed to WordPress functions with a value other than the expected default. +- The `sanitize_textarea_field()` function to the `sanitizingFunctions` list used by the `WordPress.CSRF.NonceVerification`, `WordPress.VIP.ValidatedSanitizedInput` and `WordPress.XSS.EscapeOutput` sniffs. +- The `find_array_open_closer()` utility method to the `WordPress_Sniff` class. +- Information about setting `installed_paths` using a custom ruleset to the Readme. +- Additional support links to the `composer.json` file. +- Support for Composer PHPCS plugins which sort out the `installed_paths` setting. +- Linting and code-style check of the XML ruleset files provided by WPCS. + +### Changed +- The minimum required PHP_CodeSniffer version to 2.9.0 (was 2.8.1). **Take note**: PHPCS 3.x is not (yet) supported. The next release is expected to fix that. +- Improved support for detecting issues in code using heredoc and/or nowdoc syntax. +- Improved sniff efficiency, precision and performance for a number of sniffs. +- Updated a few sniffs to take advantage of new features and fixes which are included in PHP_CodeSniffer 2.9.0. +- `WordPress.Files.Filename`: The "file name mirrors the class name prefixed with 'class'" check for PHP files containing a class will no longer be applied to typical unit test classes, i.e. for classes which extend `WP_UnitTestCase`, `PHPUnit_Framework_TestCase` and `PHPUnit\Framework\TestCase`. Additional test case base classes can be passed to the sniff using the new [`custom_test_class_whitelist` property](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#custom-unit-test-classes). +- The `WordPress.Files.FileName` sniff allows now for more theme-specific template hierarchy based file name exceptions. +- The whitelist flag for the `WordPress.VIP.SlowQuery` sniff was `tax_query` which was unintuitive. This has now been changed to `slow query` to be in line with other whitelist flags. +- The `WordPress.WhiteSpace.OperatorSpacing` sniff will now ignore operator spacing within `declare()` statements. +- The `WordPress.WhiteSpace.OperatorSpacing` sniff now extends the upstream `Squiz.WhiteSpace.OperatorSpacing` sniff for improved results and will now also examine the spacing around ternary operators and logical (`&&`, `||`) operators. +- The `WordPress.WP.DeprecatedFunctions` sniff will now detect functions deprecated in WP 4.7 and 4.8. Additionally, a number of other deprecated functions which were previously not being detected have been added to the sniff and for a number of functions the "alternative" for the deprecated function has been added/improved. +- The `WordPress.XSS.EscapeOutput` sniff will now also detect unescaped output when the short open echo tags `` PHP open tags. +- `WordPress.Classes.ClassOpeningStatement` sniff to the `WordPress-Core` ruleset to flag - and fix - class opening brace placement. +- `WordPress.NamingConventions.ValidHookName` sniff to the `WordPress-Core` ruleset to flag filter and action hooks which don't comply with the guideline of lowercase letters and underscores. For maintaining backward-compatibility of hook names an `additionalWordDelimiters` property can be added via a custom ruleset. +- `WordPress.Functions.DontExtract` sniff to the `WordPress-Core` ruleset to flag usage of the `extract()` function. +- `WordPress.PHP.POSIXFunctions` sniff to the `WordPress-Core` ruleset to flag usage of regex functions from the POSIX PHP extension which was deprecated since PHP 5.3 and removed in PHP 7. +- `WordPress.DB.RestrictedFunctions` and `WordPress.DB.RestrictedClasses` sniffs to the `WordPress-Core` ruleset to flag usage of direct database calls using PHP functions and classes rather than the WP functions for the same. +- Abstract `AbstractClassRestrictions` parent class to allow for easier sniffing for usage of specific classes. +- `Squiz.Strings.ConcatenationSpacing`, `PSR2.ControlStructures.ElseIfDeclaration`, `PSR2.Files.ClosingTag`, `Generic.NamingConventions.UpperCaseConstantName` to the `WordPress-Core` ruleset. +- Ability to add arbitrary variables to the whitelist via a custom ruleset property for the `WordPress.NamingConventions.ValidVariableName` sniff. +- Ability to use a whitelist comment for tax queries for the `WordPress.VIP.SlowDBQuery` sniff. +- Instructions on how to use WPCS with Atom and SublimeLinter to the Readme. +- Reference to the [wiki](https://github.com/WordPress/WordPress-Coding-Standards/wiki) to the Readme. +- Recommendation to also use the [PHPCompatibility](https://github.com/PHPCompatibility/PHPCompatibility) ruleset to the Readme. + +### Changed +- The minimum required PHP_CodeSniffer version to 2.6.0. +- Moved the `WordPress.WP.PreparedSQL` sniff from `WordPress-Extra` to `WordPress-Core`. +- `WordPress.PHP.StrictInArray` will now also flag non-strict usage of `array_keys()` and `array_search()`. +- Added `_deprecated_constructor()` and `_deprecated_hook()` to the list of printing functions. +- Added numerous additional functions to sniff for to the `WordPress.VIP.RestrictedFunctions` sniff as per the VIP guidelines. +- Upped the `posts_per_page` limit from 50 to 100 in `WordPress.VIP.PostsPerPage` sniff as per the VIP guidelines. +- Added `cat_ID` to the whitelisted exceptions for the `WordPress.NamingConventions.ValidVariableName` sniff. +- Added `__debugInfo` to the magic method whitelist for class methods starting with double underscore in the `WordPress.NamingConventions.ValidFunctionName` sniff. +- An error will now also be thrown for non-magic _functions_ using a double underscore prefix - `WordPress.NamingConventions.ValidFunctionName` sniff. +- The `WordPress.Arrays.ArrayAssignmentRestrictions`, `WordPress.Functions.FunctionRestrictions`, `WordPress.Variables.VariableRestrictions` sniffs weren't in actual fact sniffs, but parent classes for child sniffs. These have now all been turned into proper abstract parent classes and moved to the main `WordPress` directory. +- The array provided to `AbstractFunctionRestrictions` can now take a `whitelist` key to whitelist select functions when blocking a group of functions by function prefix. +- Updated installation instructions in the readme. +- The `WordPress-Core` ruleset is now ordered according to the handbook +- The WPCS code base itself now complies with the WordPress-Core, -Extra and -Docs coding standards. +- Various other code quality and code consistency improvements under the hood. + +### Removed +- `Squiz.Functions.FunctionDeclarationArgumentSpacing.SpacingBeforeClose` from the `WordPress-Core` standard (was causing duplicate messages for the same issue). +- `Squiz.Commenting.FunctionComment.ScalarTypeHintMissing`, `Squiz.Commenting.InlineComment.NotCapital` from the `WordPress-Docs` standard. +- Removed the sniffing for `get_pages()` from the `WordPress.VIP.RestrictedFunctions` sniff as per the VIP guidelines. +- Removed the sniffing for `extract()` from the `WordPress.VIP.RestrictedFunctions` sniff as it's now covered in a separate sniff. +- Removed the sniffing for the POSIX functions from the `WordPress.PHP.DiscouragedFunctions` sniff as it's now covered in a separate sniff. + +### Fixed +- Error message precision for the `WordPress.NamingConventions.ValidVariableName` sniff. +- Bug in the `WordPress.WhiteSpace.ControlStructureSpacing.BlankLineAfterEnd` sniff which was incorrectly being triggered on last method of class. +- Function name sniffs based on the `AbstractFunctionRestrictions` parent class will now do a case-insensitive function name comparison. +- Function name sniffs in the `WordPress.PHP.DiscouragedFunctions` sniff will now do a case-insensitive function name comparison. +- Whitelist comments directly followed by a PHP closing tag were not being recognized. +- Some PHP Magic constants were not recognized by the `WordPress.XSS.EscapeOutput` sniff. +- An error message suggesting camel caps rather than the intended snake case format in the `WordPress.NamingConventions.ValidFunctionName` sniff. +- `WordPress.WhiteSpace.ControlStructureSpacing` should no longer throw error notices during live code review. +- Errors will be no longer be thrown for methods not complying with the naming conventions when the class extends a parent class or implements an interface - `WordPress.NamingConventions.ValidFunctionName` sniff. + + +## [0.9.0] - 2016-02-01 + +### Added +- `count()` to the list of auto-escaped functions. +- `Squiz.PHP.CommentedOutCode` sniff to `WordPress-VIP` ruleset. +- Support for PHP 5.2. +- `attachment_url_to_postid()` and `parse_url()` to the restricted functions for `WordPress-VIP`. +- `WordPress.VIP.OrderByRand` sniff. +- `WordPress.PHP.StrictInArray` sniff for `WordPress-VIP` and `WordPress-Extra`. +- `get_tag_link()`, `get_category_link()`, `get_cat_ID()`, `url_to_post_id()`, `attachment_url_to_postid()` +`get_posts()`, `wp_get_recent_posts()`, `get_pages()`, `get_children()`, `wp_get_post_terms()` +`wp_get_post_categories()`, `wp_get_post_tags()`, `wp_get_object_terms()`, `term_exists()`, +`count_user_posts()`, `wp_old_slug_redirect()`, `get_adjacent_post()`, `get_previous_post()`, +`get_next_post()` to uncached functions in `WordPress.VIP.RestrictedFunctions` sniff. +- `wp_handle_upload()` and `array_key_exists()` to the list of sanitizing functions. +- Checking for object properties in `WordPress.PHP.YodaConditions` sniff. +- `WordPress.NamingConventions.ValidVariableName` sniff. +- Flagging of function calls incorporated into database queries in `WordPress.WP.PreparedSQL`. +- Recognition of escaping and auto-escaped functions in `WordPress.WP.PreparedSQL`. +- `true`, `false`, and `null` to the tokens ignored in `WordPress.XSS.EscapeOutput`. + +### Fixed +- Incorrect ternary detection in `WordPress.XSS.EscapeOutput` sniff. +- False positives when detecting variables interpolated into strings in the +`WordPress.WP.PreparedSQL` and `WordPress.VIP.ValidatedSanitizedInput` sniffs. +- False positives in `WordPress.PHP.YodaConditions` when the variable is being casted. +- `$wpdb` properties being flagged in `WordPress.WP.PreparedSQL` sniff. +- False positive in `WordPress.PHP.YodaConditions` when the a string is on the left side of the +comparison. + +## [0.8.0] - 2015-10-02 + +### Added +- `implode()` and `join()` to the list of formatting functions in the `WordPress.XSS.EscapeOutput` +sniff. This is useful when you need to have HTML in the `$glue` parameter. +- Support in the `WordPress.XSS.EscapeOutput` sniff for escaping an array of values +using `array_map()`. (Otherwise the support for `implode()` isn't of much use :) +- Docs for running WPCS in Sublime Text. +- `nl2br()` to the list of formatting functions. +- `wp_dropdown_pages()` to the list of printing functions. +- Error codes to all error/warning messages. +- `WordPress.WP.PreparedSQL` sniff for flagging unprepared SQL queries. + +### Removed +- Sniffing for the number of spaces before a closure's opening parenthesis from the +default configuration of the `WordPress.WhiteSpace.ControlStructureSpacing` sniff. It +can be re-enabled per-project as desired. + +### Fixed +- The `WordPress.XSS.EscapeOutput` sniff giving error messages with the closing +parenthesis in them instead of the offending function's name. + +## [0.7.1] - 2015-08-31 + +### Changed +- The default number of spaces before a closure's opening parenthesis from 1 to 0. + +## [0.7.0] - 2015-08-30 + +### Added +- Automatic error fixing to the `WordPress.Arrays.ArrayKeySpacingRestrictions` sniff. +- Functions and closures to the control structures checked by the `WordPress.WhiteSpace.ControlStructureSpacing` +sniff. +- Sniffing and fixing for extra spacing in the `WordPress.WhiteSpace.ControlStructureSpacing` +sniff. (Previously it only checked for insufficient spacing.) +- `.twig` files to the default ignored files. +- `esc_url_raw()` and `hash_equals()` to the list of sanitizing functions. +- `intval()` and `boolval()` to list of unslashing functions. +- `do_shortcode()` to the list of auto-escaped functions. + +### Removed +- `WordPress.Functions.FunctionDeclarationArgumentSpacing` in favor of the upstream +sniff `Squiz.Functions.FunctionDeclarationArgumentSpacing`. + +### Fixed +- Reference to incorrect issue in the inline docs of the `WordPress.VIP.SessionVariableUsage` +sniff. +- `WordPress.XSS.EscapeOutput` sniff incorrectly handling ternary conditions in +`echo` statements without parentheses in some cases. + +## [0.6.0] - 2015-06-30 + +### Added +- Support for `wp_cache_add()` and `wp_cache_delete()`, as well as custom cache +functions,in the `WordPress.VIP.DirectDatabaseQuery` sniff. + +### Removed +- `WordPress.Functions.FunctionRestrictions` and `WordPress.Variables.VariableRestrictions` +from the `WordPress-VIP` standard, since they are just parents for other sniffs. + +## [0.5.0] - 2015-06-01 + +### Added +- `WordPress.CSRF.NonceVerification` sniff to flag form processing without nonce verification. +- `in_array()` and `is_array()` to the list of sanitizing functions. +- Support for automatic error fixing to the `WordPress.Arrays.ArrayDeclaration` sniff. +- `WordPress.PHP.StrictComparisions` to the `WordPress-VIP` and `WordPress-Extra` rulesets. +- `WordPress-Docs` ruleset to sniff for proper commenting. +- `Generic.PHP.LowerCaseKeyword`, `Generic.Files.EndFileNewline`, `Generic.Files.LowercasedFilename`, +`Generic.Formatting.SpaceAfterCast`, and `Generic.Functions.OpeningFunctionBraceKernighanRitchie` to the `WordPress-Core` ruleset. +- `Generic.PHP.DeprecatedFunctions`, `Generic.PHP.ForbiddenFunctions`, `Generic.Functions.CallTimePassByReference`, +`Generic.Formatting.DisallowMultipleStatements`, `Generic.CodeAnalysis.EmptyStatement`, +`Generic.CodeAnalysis.ForLoopShouldBeWhileLoop`, `Generic.CodeAnalysis.ForLoopWithTestFunctionCall`, +`Generic.CodeAnalysis.JumbledIncrementer`, `Generic.CodeAnalysis.UnconditionalIfStatement`, +`Generic.CodeAnalysis.UnnecessaryFinalModifier`, `Generic.CodeAnalysis.UselessOverridingMethod`, +`Generic.Classes.DuplicateClassName`, and `Generic.Strings.UnnecessaryStringConcat` to the `WordPress-Extra` ruleset. +- Error for missing use of `wp_unslash()` on superglobal data to the `WordPress.VIP.ValidatedSanitizedInput` sniff. + +### Changed +- The `WordPress.VIP.ValidatedSanitizedInput` sniff to require sanitization of input even when it is being directly escaped and output. +- The minimum required PHP_CodeSniffer version to 2.2.0. +- The `WordPress.VIP.ValidatedSanitizedInput` and `WordPress.XSS.EscapeOutput` sniffs: +the list of escaping functions was split from the list of sanitizing functions. The `customSanitizingFunctions` +property has been moved to the `ValidatedSanitizedInput` sniff, and the `customEscapingFunctions` +property should now be used instead for the `EscapeOutput` sniff. +- The `WordPress.Arrays.ArrayDeclaration` sniff to give errors for `NoSpaceAfterOpenParenthesis`, `SpaceAfterArrayOpener`, and `SpaceAfterArrayCloser`, instead of warnings. +- The `WordPress.NamingConventions.ValidFunctionName` sniff to allow camelCase method names in classes that implement interfaces. + +### Fixed +- The `WordPress.VIP.ValidatedSanitizedInput` sniff not reporting missing validation when reporting missing sanitization. +- The `WordPress.VIP.ValidatedSanitizedInput` sniff flagging superglobals as needing sanitization when they were only being used in a comparison using `if` or `switch`, etc. + +## [0.4.0] - 2015-05-01 + +### Added +- Change log file. +- Handling for string-interpolated input variables in the `WordPress.VIP.ValidatedSanitizedInput` sniff. +- Errors for using uncached functions when cached equivalents exist. +- `space_before_colon` setting for the `WordPress.WhiteSpace.ControlStructureSpacing` sniff, for control structures using alternative syntax. Possible values: `'required'`, `'optional'`, `'forbidden'`. +- Support for `sanitization` whitelisting comments for the `WordPress.VIP.ValidatedSanitizedInput` sniff. +- Granular error/warning names for all errors and warnings. +- Handling for ternary conditions in the `WordPress.XSS.EscapeOutput` sniff. +- `die`, `exit`, `printf`, `vprintf`, `wp_die`, `_deprecated_argument`, `_deprecated_function`, `_deprecated_file`, `_doing_it_wrong`, `trigger_error`, and `user_error` to the list of printing functions in the `WordPress.XSS.EscapeOutput` sniff. +- `customPrintingFunctions` setting for the `WordPress.XSS.EscapeOutput` sniff. +- `rawurlencode()` and `wp_parse_id_list()` to the list of "sanitizing" functions in the `WordPress.XSS.EscapeOutput` sniff. +- `json_encode()` to the list of discouraged functions in the `WordPress.PHP.DiscouragedFunctions` sniff, in favor of `wp_json_encode()`. +- `vip_powered_wpcom()` to the list of auto-escaped functions in the `WordPress.XSS.EscapeOutput` sniff. +- `debug_print_backtrace()` and `var_export()` to the list of discouraged functions in the `WordPress.PHP.DiscouragedFunctions` sniff. +- Smart handling for formatting functions (`sprintf()` and `wp_sprintf()`) in the `WordPress.XSS.EscapeOutput` sniff. +- `WordPress.PHP.StrictComparisons` sniff. +- Correct handling of `array_map()` in the `WordPress.VIP.ValidatedSanitizedInput` sniff. +- `$_COOKIE` and `$_FILE` to the list of superglobals flagged by the `WordPress.VIP.ValidatedSanitizedInput` and `WordPress.VIP.SuperGlobalInputUsage` sniffs. +- `$_SERVER` to the list of superglobals flagged by the `WordPress.VIP.SuperGlobalInputUsage` sniff. +- `Squiz.ControlStructures.ControlSignature` sniff to the rulesets. + +### Changed +- `WordPress.Arrays.ArrayKeySpacingRestrictions` sniff to give errors for `NoSpacesAroundArrayKeys` and `SpacesAroundArrayKeys` instead of just warnings. +- `WordPress.NamingConventions.ValidFunctionName` sniff to allow for camel caps method names in child classes. +- `WordPress.XSS.EscapeOutput` sniff to allow for integers (e.g. `echo 5` and `print( -1 )`). + +### Removed +- Errors for mixed key/keyless array elements in the `WordPress.Arrays.ArrayDeclaration` sniff. +- BOM from `WordPress.WhiteSpace.OperatorSpacing` sniff file. +- `$content_width` from the list of non-overwritable globals in the `WordPress.Variables.GlobalVariables` sniff. +- `WordPress.Arrays.ArrayAssignmentRestrictions` sniff from the `WordPress-VIP` ruleset. + +### Fixed +- Incorrect errors for `else` statements using alternative syntax. +- `WordPress.VIP.ValidatedSanitizedInput` sniff not always treating casting as sanitization. +- `WordPress.XSS.EscapeOutput` sniff flagging comments as needing to be escaped. +- `WordPress.XSS.EscapeOutput` sniff not sniffing comma-delimited `echo` arguments after encountering the first escaping function in the statement. +- `WordPress.PHP.YodaConditions` sniff not flagging comparisons to constants or function calls. +- `WordPress.Arrays.ArrayDeclaration` sniff not ignoring doc comments. +- Link to phpStorm instructions in `README.md`. +- Poor performance of the `WordPress.Arrays.ArrayAssignmentRestrictions` sniff. +- Poor performance of the `WordPress.Files.FileName` sniff. + +## [0.3.0] - 2014-12-11 + +See the comparison for full list. + +### Changed +- Use semantic version tags for releases. + +## [2013-10-06] + +See the comparison for full list. + +## 2013-06-11 + +Initial tagged release. + +[Unreleased]: https://github.com/WordPress/WordPress-Coding-Standards/compare/master...HEAD +[2.2.1]: https://github.com/WordPress/WordPress-Coding-Standards/compare/2.2.0...2.2.1 +[2.2.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/2.1.1...2.2.0 +[2.1.1]: https://github.com/WordPress/WordPress-Coding-Standards/compare/2.1.0...2.1.1 +[2.1.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/2.0.0...2.1.0 +[2.0.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/2.0.0-RC1...2.0.0 +[2.0.0-RC1]: https://github.com/WordPress/WordPress-Coding-Standards/compare/1.2.1...2.0.0-RC1 +[1.2.1]: https://github.com/WordPress/WordPress-Coding-Standards/compare/1.2.0...1.2.1 +[1.2.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/1.1.0...1.2.0 +[1.1.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/1.0.0...1.1.0 +[1.0.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.14.1...1.0.0 +[0.14.1]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.14.0...0.14.1 +[0.14.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.13.1...0.14.0 +[0.13.1]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.13.0...0.13.1 +[0.13.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.12.0...0.13.0 +[0.12.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.11.0...0.12.0 +[0.11.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.10.0...0.11.0 +[0.10.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.9.0...0.10.0 +[0.9.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.8.0...0.9.0 +[0.8.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.7.1...0.8.0 +[0.7.1]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.7.0...0.7.1 +[0.7.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.6.0...0.7.0 +[0.6.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.5.0...0.6.0 +[0.5.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.4.0...0.5.0 +[0.4.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/0.3.0...0.4.0 +[0.3.0]: https://github.com/WordPress/WordPress-Coding-Standards/compare/2013-10-06...0.3.0 +[2013-10-06]: https://github.com/WordPress/WordPress-Coding-Standards/compare/2013-06-11...2013-10-06 diff --git a/vendor/wp-coding-standards/wpcs/LICENSE b/vendor/wp-coding-standards/wpcs/LICENSE new file mode 100644 index 00000000..359e1a72 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2009 John Godley and contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/wp-coding-standards/wpcs/WordPress-Core/ruleset.xml b/vendor/wp-coding-standards/wpcs/WordPress-Core/ruleset.xml new file mode 100644 index 00000000..0bff5a0d --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress-Core/ruleset.xml @@ -0,0 +1,529 @@ + + + + Non-controversial generally-agreed upon WordPress Coding Standards + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + error + The "goto" language construct should not be used. + + + + + error + eval() is a security risk so not allowed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress-Docs/ruleset.xml b/vendor/wp-coding-standards/wpcs/WordPress-Docs/ruleset.xml new file mode 100644 index 00000000..e16001ed --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress-Docs/ruleset.xml @@ -0,0 +1,109 @@ + + + + WordPress Coding Standards for Inline Documentation and Comments + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress-Extra/ruleset.xml b/vendor/wp-coding-standards/wpcs/WordPress-Extra/ruleset.xml new file mode 100644 index 00000000..4a2f9599 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress-Extra/ruleset.xml @@ -0,0 +1,187 @@ + + + + Best practices beyond core WordPress Coding Standards + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + warning + + + warning + + + warning + + + + + + + + + + + + warning + Best practice suggestion: Declare only one class/interface/trait in a file. + + + + + + + + + + + + 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + error + + + error + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/AbstractArrayAssignmentRestrictionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/AbstractArrayAssignmentRestrictionsSniff.php new file mode 100644 index 00000000..7c662cd0 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/AbstractArrayAssignmentRestrictionsSniff.php @@ -0,0 +1,240 @@ +setup_groups() ) { + return array(); + } + + return array( + \T_DOUBLE_ARROW, + \T_CLOSE_SQUARE_BRACKET, + \T_CONSTANT_ENCAPSED_STRING, + \T_DOUBLE_QUOTED_STRING, + ); + } + + /** + * Groups of variables to restrict. + * + * This method should be overridden in extending classes. + * + * Example: groups => array( + * 'groupname' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Dont use this one please!', + * 'keys' => array( 'key1', 'another_key' ), + * 'callback' => array( 'class', 'method' ), // Optional. + * ) + * ) + * + * @return array + */ + abstract public function getGroups(); + + /** + * Cache the groups. + * + * @since 0.13.0 + * + * @return bool True if the groups were setup. False if not. + */ + protected function setup_groups() { + $this->groups_cache = $this->getGroups(); + + if ( empty( $this->groups_cache ) && empty( self::$groups ) ) { + return false; + } + + // Allow for adding extra unit tests. + if ( ! empty( self::$groups ) ) { + $this->groups_cache = array_merge( $this->groups_cache, self::$groups ); + } + + return true; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + public function process_token( $stackPtr ) { + + $this->excluded_groups = $this->merge_custom_array( $this->exclude ); + if ( array_diff_key( $this->groups_cache, $this->excluded_groups ) === array() ) { + // All groups have been excluded. + // Don't remove the listener as the exclude property can be changed inline. + return; + } + + $token = $this->tokens[ $stackPtr ]; + + if ( \T_CLOSE_SQUARE_BRACKET === $token['code'] ) { + $equal = $this->phpcsFile->findNext( \T_WHITESPACE, ( $stackPtr + 1 ), null, true ); + if ( \T_EQUAL !== $this->tokens[ $equal ]['code'] ) { + return; // This is not an assignment! + } + } + + // Instances: Multi-dimensional array, keyed by line. + $inst = array(); + + /* + * Covers: + * $foo = array( 'bar' => 'taz' ); + * $foo['bar'] = $taz; + */ + if ( \in_array( $token['code'], array( \T_CLOSE_SQUARE_BRACKET, \T_DOUBLE_ARROW ), true ) ) { + $operator = $stackPtr; // T_DOUBLE_ARROW. + if ( \T_CLOSE_SQUARE_BRACKET === $token['code'] ) { + $operator = $this->phpcsFile->findNext( \T_EQUAL, ( $stackPtr + 1 ) ); + } + + $keyIdx = $this->phpcsFile->findPrevious( array( \T_WHITESPACE, \T_CLOSE_SQUARE_BRACKET ), ( $operator - 1 ), null, true ); + if ( ! is_numeric( $this->tokens[ $keyIdx ]['content'] ) ) { + $key = $this->strip_quotes( $this->tokens[ $keyIdx ]['content'] ); + $valStart = $this->phpcsFile->findNext( array( \T_WHITESPACE ), ( $operator + 1 ), null, true ); + $valEnd = $this->phpcsFile->findNext( array( \T_COMMA, \T_SEMICOLON ), ( $valStart + 1 ), null, false, null, true ); + $val = $this->phpcsFile->getTokensAsString( $valStart, ( $valEnd - $valStart ) ); + $val = $this->strip_quotes( $val ); + $inst[ $key ][] = array( $val, $token['line'] ); + } + } elseif ( \in_array( $token['code'], array( \T_CONSTANT_ENCAPSED_STRING, \T_DOUBLE_QUOTED_STRING ), true ) ) { + // $foo = 'bar=taz&other=thing'; + if ( preg_match_all( '#(?:^|&)([a-z_]+)=([^&]*)#i', $this->strip_quotes( $token['content'] ), $matches ) <= 0 ) { + return; // No assignments here, nothing to check. + } + foreach ( $matches[1] as $i => $_k ) { + $inst[ $_k ][] = array( $matches[2][ $i ], $token['line'] ); + } + } + + if ( empty( $inst ) ) { + return; + } + + foreach ( $this->groups_cache as $groupName => $group ) { + + if ( isset( $this->excluded_groups[ $groupName ] ) ) { + continue; + } + + $callback = ( isset( $group['callback'] ) && is_callable( $group['callback'] ) ) ? $group['callback'] : array( $this, 'callback' ); + + foreach ( $inst as $key => $assignments ) { + foreach ( $assignments as $occurance ) { + list( $val, $line ) = $occurance; + + if ( ! \in_array( $key, $group['keys'], true ) ) { + continue; + } + + $output = \call_user_func( $callback, $key, $val, $line, $group ); + + if ( ! isset( $output ) || false === $output ) { + continue; + } elseif ( true === $output ) { + $message = $group['message']; + } else { + $message = $output; + } + + $this->addMessage( + $message, + $stackPtr, + ( 'error' === $group['type'] ), + $this->string_to_errorcode( $groupName . '_' . $key ), + array( $key, $val ) + ); + } + } + } + } + + /** + * Callback to process each confirmed key, to check value. + * + * This method must be extended to add the logic to check assignment value. + * + * @param string $key Array index / key. + * @param mixed $val Assigned value. + * @param int $line Token line. + * @param array $group Group definition. + * @return mixed FALSE if no match, TRUE if matches, STRING if matches + * with custom error message passed to ->process(). + */ + abstract public function callback( $key, $val, $line, $group ); + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/AbstractClassRestrictionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/AbstractClassRestrictionsSniff.php new file mode 100644 index 00000000..9374622e --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/AbstractClassRestrictionsSniff.php @@ -0,0 +1,245 @@ + array( + * 'lambda' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Avoid direct calls to the database.', + * 'classes' => array( 'PDO', '\Namespace\Classname' ), + * ) + * ) + * + * You can use * wildcards to target a group of (namespaced) classes. + * Aliased namespaces (use ..) are currently not supported. + * + * Documented here for clarity. Not (re)defined as it is already defined in the parent class. + * + * @return array + * + abstract public function getGroups(); + */ + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + // Prepare the function group regular expressions only once. + if ( false === $this->setup_groups( 'classes' ) ) { + return array(); + } + + return array( + \T_DOUBLE_COLON, + \T_NEW, + \T_EXTENDS, + \T_IMPLEMENTS, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * {@internal Unlike in the `AbstractFunctionRestrictionsSniff`, + * we can't do a preliminary check on classes as at this point + * we don't know the class name yet.}} + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + // Reset the temporary storage before processing the token. + unset( $this->classname ); + + $this->excluded_groups = $this->merge_custom_array( $this->exclude ); + if ( array_diff_key( $this->groups, $this->excluded_groups ) === array() ) { + // All groups have been excluded. + // Don't remove the listener as the exclude property can be changed inline. + return; + } + + if ( true === $this->is_targetted_token( $stackPtr ) ) { + return $this->check_for_matches( $stackPtr ); + } + } + + /** + * Determine if we have a valid classname for the target token. + * + * @since 0.11.0 This logic was originally contained in the `process()` method. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return bool + */ + public function is_targetted_token( $stackPtr ) { + + $token = $this->tokens[ $stackPtr ]; + $classname = ''; + + if ( \in_array( $token['code'], array( \T_NEW, \T_EXTENDS, \T_IMPLEMENTS ), true ) ) { + if ( \T_NEW === $token['code'] ) { + $nameEnd = ( $this->phpcsFile->findNext( array( \T_OPEN_PARENTHESIS, \T_WHITESPACE, \T_SEMICOLON, \T_OBJECT_OPERATOR ), ( $stackPtr + 2 ) ) - 1 ); + } else { + $nameEnd = ( $this->phpcsFile->findNext( array( \T_CLOSE_CURLY_BRACKET, \T_WHITESPACE ), ( $stackPtr + 2 ) ) - 1 ); + } + + $length = ( $nameEnd - ( $stackPtr + 1 ) ); + $classname = $this->phpcsFile->getTokensAsString( ( $stackPtr + 2 ), $length ); + + if ( \T_NS_SEPARATOR !== $this->tokens[ ( $stackPtr + 2 ) ]['code'] ) { + $classname = $this->get_namespaced_classname( $classname, ( $stackPtr - 1 ) ); + } + } + + if ( \T_DOUBLE_COLON === $token['code'] ) { + $nameEnd = $this->phpcsFile->findPrevious( \T_STRING, ( $stackPtr - 1 ) ); + $nameStart = ( $this->phpcsFile->findPrevious( array( \T_STRING, \T_NS_SEPARATOR, \T_NAMESPACE ), ( $nameEnd - 1 ), null, true, null, true ) + 1 ); + $length = ( $nameEnd - ( $nameStart - 1 ) ); + $classname = $this->phpcsFile->getTokensAsString( $nameStart, $length ); + + if ( \T_NS_SEPARATOR !== $this->tokens[ $nameStart ]['code'] ) { + $classname = $this->get_namespaced_classname( $classname, ( $nameStart - 1 ) ); + } + } + + // Stop if we couldn't determine a classname. + if ( empty( $classname ) ) { + return false; + } + + // Nothing to do if 'parent', 'self' or 'static'. + if ( \in_array( $classname, array( 'parent', 'self', 'static' ), true ) ) { + return false; + } + + $this->classname = $classname; + return true; + } + + /** + * Verify if the current token is one of the targetted classes. + * + * @since 0.11.0 Split out from the `process()` method. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function check_for_matches( $stackPtr ) { + $skip_to = array(); + + foreach ( $this->groups as $groupName => $group ) { + + if ( isset( $this->excluded_groups[ $groupName ] ) ) { + continue; + } + + if ( preg_match( $group['regex'], $this->classname ) === 1 ) { + $skip_to[] = $this->process_matched_token( $stackPtr, $groupName, $this->classname ); + } + } + + if ( empty( $skip_to ) || min( $skip_to ) === 0 ) { + return; + } + + return min( $skip_to ); + } + + /** + * Prepare the class name for use in a regular expression. + * + * The getGroups() method allows for providing class names with a wildcard * to target + * a group of classes within a namespace. It also allows for providing class names as + * 'ordinary' names or prefixed with one or more namespaces. + * This prepare routine takes that into account while still safely escaping the + * class name for use in a regular expression. + * + * @param string $classname Class name, potentially prefixed with namespaces. + * @return string Regex escaped class name. + */ + protected function prepare_name_for_regex( $classname ) { + $classname = trim( $classname, '\\' ); // Make sure all classnames have a \ prefix, but only one. + return parent::prepare_name_for_regex( $classname ); + } + + /** + * See if the classname was found in a namespaced file and if so, add the namespace to the classname. + * + * @param string $classname The full classname as found. + * @param int $search_from The token position to search up from. + * @return string Classname, potentially prefixed with the namespace. + */ + protected function get_namespaced_classname( $classname, $search_from ) { + // Don't do anything if this is already a fully qualified classname. + if ( empty( $classname ) || '\\' === $classname[0] ) { + return $classname; + } + + // Remove the namespace keyword if used. + if ( 0 === strpos( $classname, 'namespace\\' ) ) { + $classname = substr( $classname, 10 ); + } + + $namespace_keyword = $this->phpcsFile->findPrevious( \T_NAMESPACE, $search_from ); + if ( false === $namespace_keyword ) { + // No namespace keyword found at all, so global namespace. + $classname = '\\' . $classname; + } else { + $namespace = $this->determine_namespace( $search_from ); + + if ( ! empty( $namespace ) ) { + $classname = '\\' . $namespace . '\\' . $classname; + } else { + // No actual namespace found, so global namespace. + $classname = '\\' . $classname; + } + } + + return $classname; + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/AbstractFunctionParameterSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/AbstractFunctionParameterSniff.php new file mode 100644 index 00000000..ab68a1dc --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/AbstractFunctionParameterSniff.php @@ -0,0 +1,112 @@ +target_functions ) ) { + return array(); + } + + return array( + $this->group_name => array( + 'functions' => array_keys( $this->target_functions ), + ), + ); + } + + /** + * Process a matched token. + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_matched_token( $stackPtr, $group_name, $matched_content ) { + + $parameters = $this->get_function_call_parameters( $stackPtr ); + + if ( empty( $parameters ) ) { + return $this->process_no_parameters( $stackPtr, $group_name, $matched_content ); + } else { + return $this->process_parameters( $stackPtr, $group_name, $matched_content, $parameters ); + } + } + + /** + * Process the parameters of a matched function. + * + * This method has to be made concrete in child classes. + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + abstract public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ); + + /** + * Process the function if no parameters were found. + * + * Defaults to doing nothing. Can be overloaded in child classes to handle functions + * were parameters are expected, but none found. + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_no_parameters( $stackPtr, $group_name, $matched_content ) { + return; + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/AbstractFunctionRestrictionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/AbstractFunctionRestrictionsSniff.php new file mode 100644 index 00000000..53ea9a25 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/AbstractFunctionRestrictionsSniff.php @@ -0,0 +1,342 @@ + array( + * 'lambda' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Use anonymous functions instead please!', + * 'functions' => array( 'file_get_contents', 'create_function', 'mysql_*' ), + * // Only useful when using wildcards: + * 'whitelist' => array( 'mysql_to_rfc3339' => true, ), + * ) + * ) + * + * You can use * wildcards to target a group of functions. + * When you use * wildcards, you may inadvertently restrict too many + * functions. In that case you can add the `whitelist` key to + * whitelist individual functions to prevent false positives. + * + * @return array + */ + abstract public function getGroups(); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + // Prepare the function group regular expressions only once. + if ( false === $this->setup_groups( 'functions' ) ) { + return array(); + } + + return array( + \T_STRING, + ); + } + + /** + * Set up the regular expressions for each group. + * + * @since 0.10.0 + * + * @param string $key The group array index key where the input for the regular expression can be found. + * @return bool True if the groups were setup. False if not. + */ + protected function setup_groups( $key ) { + // Prepare the function group regular expressions only once. + $this->groups = $this->getGroups(); + + if ( empty( $this->groups ) && empty( self::$unittest_groups ) ) { + return false; + } + + // Allow for adding extra unit tests. + if ( ! empty( self::$unittest_groups ) ) { + $this->groups = array_merge( $this->groups, self::$unittest_groups ); + } + + $all_items = array(); + foreach ( $this->groups as $groupName => $group ) { + if ( empty( $group[ $key ] ) ) { + unset( $this->groups[ $groupName ] ); + } else { + $items = array_map( array( $this, 'prepare_name_for_regex' ), $group[ $key ] ); + $all_items[] = $items; + $items = implode( '|', $items ); + + $this->groups[ $groupName ]['regex'] = sprintf( $this->regex_pattern, $items ); + } + } + + if ( empty( $this->groups ) ) { + return false; + } + + // Create one "super-regex" to allow for initial filtering. + $all_items = \call_user_func_array( 'array_merge', $all_items ); + $all_items = implode( '|', array_unique( $all_items ) ); + $this->prelim_check_regex = sprintf( $this->regex_pattern, $all_items ); + + return true; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + + $this->excluded_groups = $this->merge_custom_array( $this->exclude ); + if ( array_diff_key( $this->groups, $this->excluded_groups ) === array() ) { + // All groups have been excluded. + // Don't remove the listener as the exclude property can be changed inline. + return; + } + + // Preliminary check. If the content of the T_STRING is not one of the functions we're + // looking for, we can bow out before doing the heavy lifting of checking whether + // this is a function call. + if ( preg_match( $this->prelim_check_regex, $this->tokens[ $stackPtr ]['content'] ) !== 1 ) { + return; + } + + if ( true === $this->is_targetted_token( $stackPtr ) ) { + return $this->check_for_matches( $stackPtr ); + } + } + + /** + * Verify is the current token is a function call. + * + * @since 0.11.0 Split out from the `process()` method. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return bool + */ + public function is_targetted_token( $stackPtr ) { + + if ( \T_STRING !== $this->tokens[ $stackPtr ]['code'] ) { + return false; + } + + // Exclude function definitions, class methods, and namespaced calls. + if ( $this->is_class_object_call( $stackPtr ) === true ) { + return false; + } + + if ( $this->is_token_namespaced( $stackPtr ) === true ) { + return false; + } + + $prev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true ); + if ( false !== $prev ) { + // Skip sniffing on function, class definitions or for function aliases in use statements. + $skipped = array( + \T_FUNCTION => \T_FUNCTION, + \T_CLASS => \T_CLASS, + \T_AS => \T_AS, // Use declaration alias. + ); + + if ( isset( $skipped[ $this->tokens[ $prev ]['code'] ] ) ) { + return false; + } + } + + // Check if this could even be a function call. + $next = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + if ( false === $next ) { + return false; + } + + // Check for `use function ... (as|;)`. + if ( ( \T_STRING === $this->tokens[ $prev ]['code'] && 'function' === $this->tokens[ $prev ]['content'] ) + && ( \T_AS === $this->tokens[ $next ]['code'] || \T_SEMICOLON === $this->tokens[ $next ]['code'] ) + ) { + return true; + } + + // If it's not a `use` statement, there should be parenthesis. + if ( \T_OPEN_PARENTHESIS !== $this->tokens[ $next ]['code'] ) { + return false; + } + + return true; + } + + /** + * Verify if the current token is one of the targetted functions. + * + * @since 0.11.0 Split out from the `process()` method. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function check_for_matches( $stackPtr ) { + $token_content = strtolower( $this->tokens[ $stackPtr ]['content'] ); + $skip_to = array(); + + foreach ( $this->groups as $groupName => $group ) { + + if ( isset( $this->excluded_groups[ $groupName ] ) ) { + continue; + } + + if ( isset( $group['whitelist'][ $token_content ] ) ) { + continue; + } + + if ( preg_match( $group['regex'], $token_content ) === 1 ) { + $skip_to[] = $this->process_matched_token( $stackPtr, $groupName, $token_content ); + } + } + + if ( empty( $skip_to ) || min( $skip_to ) === 0 ) { + return; + } + + return min( $skip_to ); + } + + /** + * Process a matched token. + * + * @since 0.11.0 Split out from the `process()` method. + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_matched_token( $stackPtr, $group_name, $matched_content ) { + + $this->addMessage( + $this->groups[ $group_name ]['message'], + $stackPtr, + ( 'error' === $this->groups[ $group_name ]['type'] ), + $this->string_to_errorcode( $group_name . '_' . $matched_content ), + array( $matched_content ) + ); + } + + /** + * Prepare the function name for use in a regular expression. + * + * The getGroups() method allows for providing function names with a wildcard * to target + * a group of functions. This prepare routine takes that into account while still safely + * escaping the function name for use in a regular expression. + * + * @since 0.10.0 + * + * @param string $function Function name. + * @return string Regex escaped function name. + */ + protected function prepare_name_for_regex( $function ) { + $function = str_replace( array( '.*', '*' ), '@@', $function ); // Replace wildcards with placeholder. + $function = preg_quote( $function, '`' ); + $function = str_replace( '@@', '.*', $function ); // Replace placeholder with regex wildcard. + + return $function; + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/Arrays/ArrayIndentationStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/Arrays/ArrayIndentationStandard.xml new file mode 100644 index 00000000..5b9a1dee --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/Arrays/ArrayIndentationStandard.xml @@ -0,0 +1,112 @@ + + + + + + + 22, +); + ]]> + + + 22, + ); + ]]> + + + + + + + + 22, + 'comment_count' => array( + 'value' => 25, + 'compare' => '>=', + ), + 'post_type' => array( + 'post', + 'page', + ), +); + ]]> + + + 22, + 'comment_count' => array( + 'value' => 25, + 'compare' => '>=', + ), + 'post_type' => array( + 'post', + 'page', + ), +); + ]]> + + + + + + + + 'start of phrase' + . 'concatented additional phrase' + . 'more text', +); + ]]> + + + 'start of phrase' +. 'concatented additional phrase' +. 'more text', +); + ]]> + + + + + + + + << + start of phrase + concatented additional phrase + more text +EOD +, +); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/Arrays/ArrayKeySpacingRestrictionsStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/Arrays/ArrayKeySpacingRestrictionsStandard.xml new file mode 100644 index 00000000..c1e27a13 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/Arrays/ArrayKeySpacingRestrictionsStandard.xml @@ -0,0 +1,27 @@ + + + + + + + [ $post_id ]; +$post_title = $post[ 'concatenated' . $title ]; +$post = $posts[ HOME_PAGE ]; +$post = $posts[123]; +$post_title = $post['post_title']; + ]]> + + + [$post_id]; +$post_title = $post['concatenated' . $title ]; +$post = $posts[HOME_PAGE]; +$post = $posts[ 123 ]; +$post_title = $post[ 'post_title' ]; + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/Arrays/MultipleStatementAlignmentStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/Arrays/MultipleStatementAlignmentStandard.xml new file mode 100644 index 00000000..b3f87cbb --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/Arrays/MultipleStatementAlignmentStandard.xml @@ -0,0 +1,46 @@ + + + + + + + => 22 ); +$bar = array( 'year' => $current_year ); + ]]> + + + =>22 ); +$bar = array( 'year'=> $current_year ); + ]]> + + + + + + + + => 22, + 'year' => $current_year, + 'monthnum' => $current_month, +); + ]]> + + + => 22, + 'year' => $current_year, + 'monthnum' => $current_month, +); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/Classes/ClassInstantiationStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/Classes/ClassInstantiationStandard.xml new file mode 100644 index 00000000..cdd749ec --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/Classes/ClassInstantiationStandard.xml @@ -0,0 +1,53 @@ + + + + + + + (); + ]]> + + + + + + + + + + + + + + (); + ]]> + + + + + + + + new Foobar(); + ]]> + + + & new Foobar(); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/CodeAnalysis/EscapedNotTranslatedStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/CodeAnalysis/EscapedNotTranslatedStandard.xml new file mode 100644 index 00000000..3755a3c9 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/CodeAnalysis/EscapedNotTranslatedStandard.xml @@ -0,0 +1,20 @@ + + + + + + + esc_html__( 'text', 'domain' ); + ]]> + + + esc_html( 'text', 'domain' ); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/DateTime/CurrentTimeTimestampStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/DateTime/CurrentTimeTimestampStandard.xml new file mode 100644 index 00000000..add8ed23 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/DateTime/CurrentTimeTimestampStandard.xml @@ -0,0 +1,31 @@ + + + + + + + time(); + ]]> + + + current_time( 'timestamp', true ); + ]]> + + + + + 'Y-m-d' ); + ]]> + + + current_time( 'U', false ); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/NamingConventions/ValidHookNameStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/NamingConventions/ValidHookNameStandard.xml new file mode 100644 index 00000000..479dbd9b --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/NamingConventions/ValidHookNameStandard.xml @@ -0,0 +1,32 @@ + + + + + + + + 'prefix_hook_name', $var ); + ]]> + + + 'Prefix_Hook_NAME', $var ); + ]]> + + + + + 'prefix_hook_name', $var ); + ]]> + + + 'prefix\hook-name', $var ); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/NamingConventions/ValidPostTypeSlugStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/NamingConventions/ValidPostTypeSlugStandard.xml new file mode 100644 index 00000000..86be1bbe --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/NamingConventions/ValidPostTypeSlugStandard.xml @@ -0,0 +1,117 @@ + + + + + + + 'my_short_slug', + array() +); + ]]> + + + 'my_own_post_type_too_long', + array() +); + ]]> + + + + + + + + 'my_post_type_slug', + array() +); + ]]> + + + 'my/post/type/slug', + array() +); + ]]> + + + + + + + + 'my_post_active', + array() +); + ]]> + + + "my_post_{$status}", + array() +); + ]]> + + + + + + + + 'prefixed_author', + array() +); + ]]> + + + 'author', + array() +); + ]]> + + + + + + + + 'prefixed_author', + array() +); + ]]> + + + 'wp_author', + array() +); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/PHP/DisallowShortTernaryStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/PHP/DisallowShortTernaryStandard.xml new file mode 100644 index 00000000..c81b2fd8 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/PHP/DisallowShortTernaryStandard.xml @@ -0,0 +1,20 @@ + + + + + + + ? + $data['height'] : 0; + ]]> + + + ? : 0; + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/PHP/IniSetStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/PHP/IniSetStandard.xml new file mode 100644 index 00000000..dfc74e91 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/PHP/IniSetStandard.xml @@ -0,0 +1,36 @@ + + + + + + + + + + 'short_open_tag', 'off' ); + ]]> + + + + + + + + wp_raise_memory_limit(); + ]]> + + + 'memory_limit', '256M' ); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/Security/SafeRedirectStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/Security/SafeRedirectStandard.xml new file mode 100644 index 00000000..c2bcf5b8 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/Security/SafeRedirectStandard.xml @@ -0,0 +1,19 @@ + + + + + + + wp_safe_redirect( $location ); + ]]> + + + wp_redirect( $location ); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/CronIntervalStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/CronIntervalStandard.xml new file mode 100644 index 00000000..55a9037d --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/CronIntervalStandard.xml @@ -0,0 +1,41 @@ + + + + + + + HOUR_IN_SECONDS, + 'display' => __( 'Every hour' ) + ); + return $schedules; +} + +add_filter( + 'cron_schedules', + 'adjust_schedules' +); + ]]> + + + 9 * 60, + 'display' => __( 'Every 9 minutes' ) + ); + return $schedules; +} + +add_filter( + 'cron_schedules', + 'adjust_schedules' +); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedClassesStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedClassesStandard.xml new file mode 100644 index 00000000..4d06f6e3 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedClassesStandard.xml @@ -0,0 +1,19 @@ + + + + + + + WP_User_Query(); + ]]> + + + WP_User_Search(); // Deprecated WP 3.1. + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedFunctionsStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedFunctionsStandard.xml new file mode 100644 index 00000000..7fcc9c4e --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedFunctionsStandard.xml @@ -0,0 +1,19 @@ + + + + + + + get_sites(); + ]]> + + + wp_get_sites(); // Deprecated WP 4.6. + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedParameterValuesStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedParameterValuesStandard.xml new file mode 100644 index 00000000..0348fdff --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedParameterValuesStandard.xml @@ -0,0 +1,19 @@ + + + + + + + 'url' ); + ]]> + + + 'home' ); // Deprecated WP 2.2.0. + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedParametersStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedParametersStandard.xml new file mode 100644 index 00000000..6399ca62 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/DeprecatedParametersStandard.xml @@ -0,0 +1,36 @@ + + + after the deprecated parameter, only ever pass the default value. + ]]> + + + + + + + $string ); + ]]> + + + + + '', 'yes' ); + ]]> + + + 'oops', 'yes' ); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/EnqueuedResourcesStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/EnqueuedResourcesStandard.xml new file mode 100644 index 00000000..3857df3d --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/EnqueuedResourcesStandard.xml @@ -0,0 +1,53 @@ + + + + + + + wp_enqueue_script( + 'someScript-js', + $path_to_file, + array( 'jquery' ), + '1.0.0', + true +); + ]]> + + + ', + esc_url( $path_to_file ) +); + ]]> + + + + + + + + wp_enqueue_style( + 'style-name', + $path_to_file, + array(), + '1.0.0' +); + ]]> + + + ', + esc_url( $path_to_file ) +); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/PostsPerPageStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/PostsPerPageStandard.xml new file mode 100644 index 00000000..aa24f740 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WP/PostsPerPageStandard.xml @@ -0,0 +1,69 @@ + + + + + + + -1, +); +$args = array( + 'posts_per_page' => 100, +); +$args = array( + 'posts_per_page' => '10', +); + +$query_args['posts_per_page'] = 100; + +_query_posts( 'nopaging=1&posts_per_page=50' ); + ]]> + + + 101, +); + +$query_args['posts_per_page'] = 200; + +_query_posts( 'nopaging=1&posts_per_page=999' ); + ]]> + + + + + -1, +); +$args = array( + 'numberposts' => 100, +); +$args = array( + 'numberposts' => '10', +); + +$query_args['numberposts'] = '-1'; + +_query_posts( 'numberposts=50' ); + ]]> + + + 101, +); + +$query_args['numberposts'] = '200'; + +_query_posts( 'numberposts=999' ); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/WhiteSpace/CastStructureSpacingStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WhiteSpace/CastStructureSpacingStandard.xml new file mode 100644 index 00000000..8539325b --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WhiteSpace/CastStructureSpacingStandard.xml @@ -0,0 +1,23 @@ + + + + + + + (int) '420'; + +// No space between spread operator and cast. +$a = function_call( ...(array) $mixed ); + ]]> + + + =(int) '420'; + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/WhiteSpace/DisallowInlineTabsStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WhiteSpace/DisallowInlineTabsStandard.xml new file mode 100644 index 00000000..16f1da2f --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WhiteSpace/DisallowInlineTabsStandard.xml @@ -0,0 +1,25 @@ + + + + + + + [space]=> 'lor', + 'b'[space][space][space]=> 'em', +); + ]]> + + + [tab]=> 'lor', + 'b'[tab]=> 'em', +); + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Docs/WhiteSpace/PrecisionAlignmentStandard.xml b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WhiteSpace/PrecisionAlignmentStandard.xml new file mode 100644 index 00000000..ca819ffd --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Docs/WhiteSpace/PrecisionAlignmentStandard.xml @@ -0,0 +1,31 @@ + + + + + + + [tab]$var = true; + ]]> + + + [space][space]$var = true; + ]]> + + + + + [tab][space][space][space][space]$var = true; + ]]> + + + [tab][space][space][space]$var = true; + ]]> + + + diff --git a/vendor/wp-coding-standards/wpcs/WordPress/PHPCSHelper.php b/vendor/wp-coding-standards/wpcs/WordPress/PHPCSHelper.php new file mode 100644 index 00000000..1599a97a --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/PHPCSHelper.php @@ -0,0 +1,109 @@ +config->tabWidth ) && $phpcsFile->config->tabWidth > 0 ) { + $tab_width = $phpcsFile->config->tabWidth; + } + + return $tab_width; + } + + /** + * Check whether the `--ignore-annotations` option has been used. + * + * @since 0.13.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile Optional. The current file being processed. + * + * @return bool True if annotations should be ignored, false otherwise. + */ + public static function ignore_annotations( File $phpcsFile = null ) { + if ( isset( $phpcsFile, $phpcsFile->config->annotations ) ) { + return ! $phpcsFile->config->annotations; + } else { + $annotations = Config::getConfigData( 'annotations' ); + if ( isset( $annotations ) ) { + return ! $annotations; + } + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniff.php new file mode 100644 index 00000000..55ca6e74 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniff.php @@ -0,0 +1,3424 @@ + true`, i.e. the array item is set as the array key. + * This allows for sniffs to verify whether something is in one of these + * lists using `isset()` rather than `in_array()` which is a much more + * efficient (faster) check to execute and therefore improves the + * performance of the sniffs. + * The `true` value in those cases is used as a placeholder and has no + * meaning in and of itself. + * In the rare few cases where the array values *do* have meaning, this + * is documented in the property documentation.}} + */ +abstract class Sniff implements PHPCS_Sniff { + + /** + * Regex to get complex variables from T_DOUBLE_QUOTED_STRING or T_HEREDOC. + * + * @since 0.14.0 + * + * @var string + */ + const REGEX_COMPLEX_VARS = '`(?:(\{)?(?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)(?:->\$?(?P>varname)|\[[^\]]+\]|::\$?(?P>varname)|\([^\)]*\))*(?(3)\}|)(?(2)\}|)(?(1)\}|)`'; + + /** + * Minimum supported WordPress version. + * + * Currently used by the `WordPress.WP.AlternativeFunctions`, + * `WordPress.WP.DeprecatedClasses`, `WordPress.WP.DeprecatedFunctions` + * and the `WordPress.WP.DeprecatedParameter` sniff. + * + * These sniffs will throw an error when usage of a deprecated class/function/parameter + * is detected if the class/function/parameter was deprecated before the minimum + * supported WP version; a warning otherwise. + * By default, it is set to presume that a project will support the current + * WP version and up to three releases before. + * + * This property allows changing the minimum supported WP version used by + * these sniffs by setting a property in a custom phpcs.xml ruleset. + * This property will need to be set for each sniff which uses it. + * + * Example usage: + * + * + * + * + * + * + * Alternatively, the value can be passed in one go for all sniff using it via + * the command line or by setting a `` value in a custom phpcs.xml ruleset. + * Note: the `_wp_` in the command line property name! + * + * CL: `phpcs --runtime-set minimum_supported_wp_version 4.5` + * Ruleset: `` + * + * @since 0.14.0 Previously the individual sniffs each contained this property. + * + * @internal When the value of this property is changed, it will also need + * to be changed in the `WP/AlternativeFunctionsUnitTest.inc` file. + * + * @var string WordPress version. + */ + public $minimum_supported_version = '5.1'; + + /** + * Custom list of classes which test classes can extend. + * + * This property allows end-users to add to the $test_class_whitelist via their ruleset. + * This property will need to be set for each sniff which uses the + * `is_test_class()` method. + * Currently the method is used by the `WordPress.WP.GlobalVariablesOverride`, + * `WordPress.NamingConventions.PrefixAllGlobals` and the `WordPress.Files.Filename` sniffs. + * + * Example usage: + * + * + * + * + * + * + * + * + * + * @since 0.11.0 + * + * @var string|string[] + */ + public $custom_test_class_whitelist = array(); + + /** + * List of the functions which verify nonces. + * + * @since 0.5.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array + */ + protected $nonceVerificationFunctions = array( + 'wp_verify_nonce' => true, + 'check_admin_referer' => true, + 'check_ajax_referer' => true, + ); + + /** + * Functions that escape values for display. + * + * @since 0.5.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array + */ + protected $escapingFunctions = array( + 'absint' => true, + 'esc_attr__' => true, + 'esc_attr_e' => true, + 'esc_attr_x' => true, + 'esc_attr' => true, + 'esc_html__' => true, + 'esc_html_e' => true, + 'esc_html_x' => true, + 'esc_html' => true, + 'esc_js' => true, + 'esc_sql' => true, + 'esc_textarea' => true, + 'esc_url_raw' => true, + 'esc_url' => true, + 'filter_input' => true, + 'filter_var' => true, + 'floatval' => true, + 'highlight_string' => true, + 'intval' => true, + 'json_encode' => true, + 'like_escape' => true, + 'number_format' => true, + 'rawurlencode' => true, + 'sanitize_hex_color' => true, + 'sanitize_hex_color_no_hash' => true, + 'sanitize_html_class' => true, + 'sanitize_key' => true, + 'sanitize_user_field' => true, + 'tag_escape' => true, + 'urlencode_deep' => true, + 'urlencode' => true, + 'wp_json_encode' => true, + 'wp_kses_allowed_html' => true, + 'wp_kses_data' => true, + 'wp_kses_post' => true, + 'wp_kses' => true, + ); + + /** + * Functions whose output is automatically escaped for display. + * + * @since 0.5.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array + */ + protected $autoEscapedFunctions = array( + 'allowed_tags' => true, + 'bloginfo' => true, + 'body_class' => true, + 'calendar_week_mod' => true, + 'category_description' => true, + 'checked' => true, + 'comment_class' => true, + 'count' => true, + 'disabled' => true, + 'do_shortcode' => true, + 'do_shortcode_tag' => true, + 'get_archives_link' => true, + 'get_attachment_link' => true, + 'get_avatar' => true, + 'get_bookmark_field' => true, + 'get_calendar' => true, + 'get_comment_author_link' => true, + 'get_current_blog_id' => true, + 'get_delete_post_link' => true, + 'get_search_form' => true, + 'get_search_query' => true, + 'get_the_author_link' => true, + 'get_the_author' => true, + 'get_the_date' => true, + 'get_the_ID' => true, + 'get_the_post_thumbnail' => true, + 'get_the_term_list' => true, + 'post_type_archive_title' => true, + 'readonly' => true, + 'selected' => true, + 'single_cat_title' => true, + 'single_month_title' => true, + 'single_post_title' => true, + 'single_tag_title' => true, + 'single_term_title' => true, + 'tag_description' => true, + 'term_description' => true, + 'the_author' => true, + 'the_date' => true, + 'the_title_attribute' => true, + 'walk_nav_menu_tree' => true, + 'wp_dropdown_categories' => true, + 'wp_dropdown_users' => true, + 'wp_generate_tag_cloud' => true, + 'wp_get_archives' => true, + 'wp_get_attachment_image' => true, + 'wp_get_attachment_link' => true, + 'wp_link_pages' => true, + 'wp_list_authors' => true, + 'wp_list_bookmarks' => true, + 'wp_list_categories' => true, + 'wp_list_comments' => true, + 'wp_login_form' => true, + 'wp_loginout' => true, + 'wp_nav_menu' => true, + 'wp_register' => true, + 'wp_tag_cloud' => true, + 'wp_title' => true, + ); + + /** + * Functions that sanitize values. + * + * This list is complementary to the `$unslashingSanitizingFunctions` + * list. + * Sanitizing functions should be added to this list if they do *not* + * implicitely unslash data and to the `$unslashingsanitizingFunctions` + * list if they do. + * + * @since 0.5.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array + */ + protected $sanitizingFunctions = array( + '_wp_handle_upload' => true, + 'esc_url_raw' => true, + 'filter_input' => true, + 'filter_var' => true, + 'hash_equals' => true, + 'is_email' => true, + 'number_format' => true, + 'sanitize_bookmark_field' => true, + 'sanitize_bookmark' => true, + 'sanitize_email' => true, + 'sanitize_file_name' => true, + 'sanitize_hex_color_no_hash' => true, + 'sanitize_hex_color' => true, + 'sanitize_html_class' => true, + 'sanitize_meta' => true, + 'sanitize_mime_type' => true, + 'sanitize_option' => true, + 'sanitize_sql_orderby' => true, + 'sanitize_term_field' => true, + 'sanitize_term' => true, + 'sanitize_text_field' => true, + 'sanitize_textarea_field' => true, + 'sanitize_title_for_query' => true, + 'sanitize_title_with_dashes' => true, + 'sanitize_title' => true, + 'sanitize_user_field' => true, + 'sanitize_user' => true, + 'validate_file' => true, + 'wp_handle_sideload' => true, + 'wp_handle_upload' => true, + 'wp_kses_allowed_html' => true, + 'wp_kses_data' => true, + 'wp_kses_post' => true, + 'wp_kses' => true, + 'wp_parse_id_list' => true, + 'wp_redirect' => true, + 'wp_safe_redirect' => true, + 'wp_sanitize_redirect' => true, + 'wp_strip_all_tags' => true, + ); + + /** + * Sanitizing functions that implicitly unslash the data passed to them. + * + * This list is complementary to the `$sanitizingFunctions` list. + * Sanitizing functions should be added to this list if they also + * implicitely unslash data and to the `$sanitizingFunctions` list + * if they don't. + * + * @since 0.5.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array + */ + protected $unslashingSanitizingFunctions = array( + 'absint' => true, + 'boolval' => true, + 'count' => true, + 'doubleval' => true, + 'floatval' => true, + 'intval' => true, + 'sanitize_key' => true, + 'sizeof' => true, + ); + + /** + * Functions which unslash the data passed to them. + * + * @since 2.1.0 + * + * @var array + */ + protected $unslashingFunctions = array( + 'stripslashes_deep' => true, + 'stripslashes_from_strings_only' => true, + 'wp_unslash' => true, + ); + + /** + * List of PHP native functions to test the type of a variable. + * + * Using these functions is safe in combination with superglobals without + * unslashing or sanitization. + * + * They should, however, not be regarded as unslashing or sanitization functions. + * + * @since 2.1.0 + * + * @var array + */ + protected $typeTestFunctions = array( + 'is_array' => true, + 'is_bool' => true, + 'is_callable' => true, + 'is_countable' => true, + 'is_double' => true, + 'is_float' => true, + 'is_int' => true, + 'is_integer' => true, + 'is_iterable' => true, + 'is_long' => true, + 'is_null' => true, + 'is_numeric' => true, + 'is_object' => true, + 'is_real' => true, + 'is_resource' => true, + 'is_scalar' => true, + 'is_string' => true, + ); + + /** + * Token which when they preceed code indicate the value is safely casted. + * + * @since 1.1.0 + * + * @var array + */ + protected $safe_casts = array( + \T_INT_CAST => true, + \T_DOUBLE_CAST => true, + \T_BOOL_CAST => true, + \T_UNSET_CAST => true, + ); + + /** + * List of array functions which apply a callback to the array. + * + * These are often used for sanitization/escaping an array variable. + * + * Note: functions which alter the array by reference are not listed here on purpose. + * These cannot easily be used for sanitization as they can't be combined with unslashing. + * Similarly, they cannot be used for late escaping as the return value is a boolean, not + * the altered array. + * + * @since 2.1.0 + * + * @var array => + */ + protected $arrayWalkingFunctions = array( + 'array_map' => 1, + 'map_deep' => 2, + ); + + /** + * Array functions to compare a $needle to a predefined set of values. + * + * If the value is set to an integer, the function needs to have at least that + * many parameters for it to be considered as a comparison. + * + * @since 2.1.0 + * + * @var array => + */ + protected $arrayCompareFunctions = array( + 'in_array' => true, + 'array_search' => true, + 'array_keys' => 2, + ); + + /** + * Functions that format strings. + * + * These functions are often used for formatting values just before output, and + * it is common practice to escape the individual parameters passed to them as + * needed instead of escaping the entire result. This is especially true when the + * string being formatted contains HTML, which makes escaping the full result + * more difficult. + * + * @since 0.5.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array + */ + protected $formattingFunctions = array( + 'array_fill' => true, + 'ent2ncr' => true, + 'implode' => true, + 'join' => true, + 'nl2br' => true, + 'sprintf' => true, + 'vsprintf' => true, + 'wp_sprintf' => true, + ); + + /** + * Functions which print output incorporating the values passed to them. + * + * @since 0.5.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array + */ + protected $printingFunctions = array( + '_deprecated_argument' => true, + '_deprecated_constructor' => true, + '_deprecated_file' => true, + '_deprecated_function' => true, + '_deprecated_hook' => true, + '_doing_it_wrong' => true, + '_e' => true, + '_ex' => true, + 'printf' => true, + 'trigger_error' => true, + 'user_error' => true, + 'vprintf' => true, + 'wp_die' => true, + 'wp_dropdown_pages' => true, + ); + + /** + * Functions that escape values for use in SQL queries. + * + * @since 0.9.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array + */ + protected $SQLEscapingFunctions = array( + 'absint' => true, + 'esc_sql' => true, + 'floatval' => true, + 'intval' => true, + 'like_escape' => true, + ); + + /** + * Functions whose output is automatically escaped for use in SQL queries. + * + * @since 0.9.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array + */ + protected $SQLAutoEscapedFunctions = array( + 'count' => true, + ); + + /** + * A list of functions that get data from the cache. + * + * @since 0.6.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array + */ + protected $cacheGetFunctions = array( + 'wp_cache_get' => true, + ); + + /** + * A list of functions that set data in the cache. + * + * @since 0.6.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array + */ + protected $cacheSetFunctions = array( + 'wp_cache_set' => true, + 'wp_cache_add' => true, + ); + + /** + * A list of functions that delete data from the cache. + * + * @since 0.6.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array + */ + protected $cacheDeleteFunctions = array( + 'wp_cache_delete' => true, + 'clean_attachment_cache' => true, + 'clean_blog_cache' => true, + 'clean_bookmark_cache' => true, + 'clean_category_cache' => true, + 'clean_comment_cache' => true, + 'clean_network_cache' => true, + 'clean_object_term_cache' => true, + 'clean_page_cache' => true, + 'clean_post_cache' => true, + 'clean_term_cache' => true, + 'clean_user_cache' => true, + ); + + /** + * A list of functions that invoke WP hooks (filters/actions). + * + * @since 0.10.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array + */ + protected $hookInvokeFunctions = array( + 'do_action' => true, + 'do_action_ref_array' => true, + 'do_action_deprecated' => true, + 'apply_filters' => true, + 'apply_filters_ref_array' => true, + 'apply_filters_deprecated' => true, + ); + + /** + * A list of functions that are used to interact with the WP plugins API. + * + * @since 0.10.0 + * @since 0.11.0 Changed from public static to protected non-static. + * + * @var array => + */ + protected $hookFunctions = array( + 'has_filter' => 1, + 'add_filter' => 1, + 'remove_filter' => 1, + 'remove_all_filters' => 1, + 'doing_filter' => 1, // Hook name optional. + 'has_action' => 1, + 'add_action' => 1, + 'doing_action' => 1, // Hook name optional. + 'did_action' => 1, + 'remove_action' => 1, + 'remove_all_actions' => 1, + 'current_filter' => 0, // No hook name argument. + ); + + /** + * List of global WP variables. + * + * @since 0.3.0 + * @since 0.11.0 Changed visibility from public to protected. + * @since 0.12.0 Renamed from `$globals` to `$wp_globals` to be more descriptive. + * @since 0.12.0 Moved here from the WordPress.Variables.GlobalVariables sniff. + * + * @var array + */ + protected $wp_globals = array( + '_links_add_base' => true, + '_links_add_target' => true, + '_menu_item_sort_prop' => true, + '_nav_menu_placeholder' => true, + '_new_bundled_files' => true, + '_old_files' => true, + '_parent_pages' => true, + '_registered_pages' => true, + '_updated_user_settings' => true, + '_wp_additional_image_sizes' => true, + '_wp_admin_css_colors' => true, + '_wp_default_headers' => true, + '_wp_deprecated_widgets_callbacks' => true, + '_wp_last_object_menu' => true, + '_wp_last_utility_menu' => true, + '_wp_menu_nopriv' => true, + '_wp_nav_menu_max_depth' => true, + '_wp_post_type_features' => true, + '_wp_real_parent_file' => true, + '_wp_registered_nav_menus' => true, + '_wp_sidebars_widgets' => true, + '_wp_submenu_nopriv' => true, + '_wp_suspend_cache_invalidation' => true, + '_wp_theme_features' => true, + '_wp_using_ext_object_cache' => true, + 'action' => true, + 'active_signup' => true, + 'admin_body_class' => true, + 'admin_page_hooks' => true, + 'all_links' => true, + 'allowedentitynames' => true, + 'allowedposttags' => true, + 'allowedtags' => true, + 'auth_secure_cookie' => true, + 'authordata' => true, + 'avail_post_mime_types' => true, + 'avail_post_stati' => true, + 'blog_id' => true, + 'blog_title' => true, + 'blogname' => true, + 'cat' => true, + 'cat_id' => true, + 'charset_collate' => true, + 'comment' => true, + 'comment_alt' => true, + 'comment_depth' => true, + 'comment_status' => true, + 'comment_thread_alt' => true, + 'comment_type' => true, + 'comments' => true, + 'compress_css' => true, + 'compress_scripts' => true, + 'concatenate_scripts' => true, + 'content_width' => true, + 'current_blog' => true, + 'current_screen' => true, + 'current_site' => true, + 'current_user' => true, + 'currentcat' => true, + 'currentday' => true, + 'currentmonth' => true, + 'custom_background' => true, + 'custom_image_header' => true, + 'default_menu_order' => true, + 'descriptions' => true, + 'domain' => true, + 'editor_styles' => true, + 'error' => true, + 'errors' => true, + 'EZSQL_ERROR' => true, + 'feeds' => true, + 'GETID3_ERRORARRAY' => true, + 'hook_suffix' => true, + 'HTTP_RAW_POST_DATA' => true, + 'id' => true, + 'in_comment_loop' => true, + 'interim_login' => true, + 'is_apache' => true, + 'is_chrome' => true, + 'is_gecko' => true, + 'is_IE' => true, + 'is_IIS' => true, + 'is_iis7' => true, + 'is_macIE' => true, + 'is_NS4' => true, + 'is_opera' => true, + 'is_safari' => true, + 'is_winIE' => true, + 'l10n' => true, + 'link' => true, + 'link_id' => true, + 'locale' => true, + 'locked_post_status' => true, + 'lost' => true, + 'm' => true, + 'map' => true, + 'menu' => true, + 'menu_order' => true, + 'merged_filters' => true, + 'mode' => true, + 'monthnum' => true, + 'more' => true, + 'mu_plugin' => true, + 'multipage' => true, + 'names' => true, + 'nav_menu_selected_id' => true, + 'network_plugin' => true, + 'new_whitelist_options' => true, + 'numpages' => true, + 'one_theme_location_no_menus' => true, + 'opml' => true, + 'order' => true, + 'orderby' => true, + 'overridden_cpage' => true, + 'page' => true, + 'paged' => true, + 'pagenow' => true, + 'pages' => true, + 'parent_file' => true, + 'pass_allowed_html' => true, + 'pass_allowed_protocols' => true, + 'path' => true, + 'per_page' => true, + 'PHP_SELF' => true, + 'phpmailer' => true, + 'plugin_page' => true, + 'plugin' => true, + 'plugins' => true, + 'post' => true, + 'post_default_category' => true, + 'post_default_title' => true, + 'post_ID' => true, + 'post_id' => true, + 'post_mime_types' => true, + 'post_type' => true, + 'post_type_object' => true, + 'posts' => true, + 'preview' => true, + 'previouscat' => true, + 'previousday' => true, + 'previousweekday' => true, + 'redir_tab' => true, + 'required_mysql_version' => true, + 'required_php_version' => true, + 'rnd_value' => true, + 'role' => true, + 's' => true, + 'search' => true, + 'self' => true, + 'shortcode_tags' => true, + 'show_admin_bar' => true, + 'sidebars_widgets' => true, + 'status' => true, + 'submenu' => true, + 'submenu_file' => true, + 'super_admins' => true, + 'tab' => true, + 'table_prefix' => true, + 'tabs' => true, + 'tag' => true, + 'tag_ID' => true, + 'targets' => true, + 'tax' => true, + 'taxnow' => true, + 'taxonomy' => true, + 'term' => true, + 'text_direction' => true, + 'theme_field_defaults' => true, + 'themes_allowedtags' => true, + 'timeend' => true, + 'timestart' => true, + 'tinymce_version' => true, + 'title' => true, + 'totals' => true, + 'type' => true, + 'typenow' => true, + 'updated_timestamp' => true, + 'upgrading' => true, + 'urls' => true, + 'user_email' => true, + 'user_ID' => true, + 'user_identity' => true, + 'user_level' => true, + 'user_login' => true, + 'user_url' => true, + 'userdata' => true, + 'usersearch' => true, + 'whitelist_options' => true, + 'withcomments' => true, + 'wp' => true, + 'wp_actions' => true, + 'wp_admin_bar' => true, + 'wp_cockneyreplace' => true, + 'wp_current_db_version' => true, + 'wp_current_filter' => true, + 'wp_customize' => true, + 'wp_dashboard_control_callbacks' => true, + 'wp_db_version' => true, + 'wp_did_header' => true, + 'wp_embed' => true, + 'wp_file_descriptions' => true, + 'wp_filesystem' => true, + 'wp_filter' => true, + 'wp_hasher' => true, + 'wp_header_to_desc' => true, + 'wp_importers' => true, + 'wp_json' => true, + 'wp_list_table' => true, + 'wp_local_package' => true, + 'wp_locale' => true, + 'wp_meta_boxes' => true, + 'wp_object_cache' => true, + 'wp_plugin_paths' => true, + 'wp_post_statuses' => true, + 'wp_post_types' => true, + 'wp_queries' => true, + 'wp_query' => true, + 'wp_registered_sidebars' => true, + 'wp_registered_widget_controls' => true, + 'wp_registered_widget_updates' => true, + 'wp_registered_widgets' => true, + 'wp_rewrite' => true, + 'wp_rich_edit' => true, + 'wp_rich_edit_exists' => true, + 'wp_roles' => true, + 'wp_scripts' => true, + 'wp_settings_errors' => true, + 'wp_settings_fields' => true, + 'wp_settings_sections' => true, + 'wp_smiliessearch' => true, + 'wp_styles' => true, + 'wp_taxonomies' => true, + 'wp_the_query' => true, + 'wp_theme_directories' => true, + 'wp_themes' => true, + 'wp_user_roles' => true, + 'wp_version' => true, + 'wp_widget_factory' => true, + 'wp_xmlrpc_server' => true, + 'wpcommentsjavascript' => true, + 'wpcommentspopupfile' => true, + 'wpdb' => true, + 'wpsmiliestrans' => true, + 'year' => true, + ); + + /** + * A list of superglobals that incorporate user input. + * + * @since 0.5.0 + * @since 0.11.0 Changed from static to non-static. + * + * @var string[] + */ + protected $input_superglobals = array( + '$_COOKIE', + '$_GET', + '$_FILES', + '$_POST', + '$_REQUEST', + '$_SERVER', + ); + + /** + * Whitelist of classes which test classes can extend. + * + * @since 0.11.0 + * + * @var string[] + */ + protected $test_class_whitelist = array( + 'WP_UnitTestCase_Base' => true, + 'WP_UnitTestCase' => true, + 'WP_Ajax_UnitTestCase' => true, + 'WP_Canonical_UnitTestCase' => true, + 'WP_Test_REST_TestCase' => true, + 'WP_Test_REST_Controller_Testcase' => true, + 'WP_Test_REST_Post_Type_Controller_Testcase' => true, + 'WP_XMLRPC_UnitTestCase' => true, + 'PHPUnit_Framework_TestCase' => true, + 'PHPUnit\Framework\TestCase' => true, + // PHPUnit native TestCase class when imported via use statement. + 'TestCase' => true, + ); + + /** + * The current file being sniffed. + * + * @since 0.4.0 + * + * @var \PHP_CodeSniffer\Files\File + */ + protected $phpcsFile; + + /** + * The list of tokens in the current file being sniffed. + * + * @since 0.4.0 + * + * @var array + */ + protected $tokens; + + /** + * Set sniff properties and hand off to child class for processing of the token. + * + * @since 0.11.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 int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process( File $phpcsFile, $stackPtr ) { + $this->init( $phpcsFile ); + return $this->process_token( $stackPtr ); + } + + /** + * Processes a sniff when one of its tokens is encountered. + * + * @since 0.11.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + abstract public function process_token( $stackPtr ); + + /** + * Initialize the class for the current process. + * + * This method must be called by child classes before using many of the methods + * below. + * + * @since 0.4.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file currently being processed. + */ + protected function init( File $phpcsFile ) { + $this->phpcsFile = $phpcsFile; + $this->tokens = $phpcsFile->getTokens(); + } + + /** + * Strip quotes surrounding an arbitrary string. + * + * Intended for use with the contents of a T_CONSTANT_ENCAPSED_STRING / T_DOUBLE_QUOTED_STRING. + * + * @since 0.11.0 + * + * @param string $string The raw string. + * @return string String without quotes around it. + */ + public function strip_quotes( $string ) { + return preg_replace( '`^([\'"])(.*)\1$`Ds', '$2', $string ); + } + + /** + * Add a PHPCS message to the output stack as either a warning or an error. + * + * @since 0.11.0 + * + * @param string $message The message. + * @param int $stackPtr The position of the token the message relates to. + * @param bool $is_error Optional. Whether to report the message as an 'error' or 'warning'. + * Defaults to true (error). + * @param string $code Optional error code for the message. Defaults to 'Found'. + * @param array $data Optional input for the data replacements. + * @param int $severity Optional. Severity level. Defaults to 0 which will translate to + * the PHPCS default severity level. + * @return bool + */ + protected function addMessage( $message, $stackPtr, $is_error = true, $code = 'Found', $data = array(), $severity = 0 ) { + return $this->throwMessage( $message, $stackPtr, $is_error, $code, $data, $severity, false ); + } + + /** + * Add a fixable PHPCS message to the output stack as either a warning or an error. + * + * @since 0.11.0 + * + * @param string $message The message. + * @param int $stackPtr The position of the token the message relates to. + * @param bool $is_error Optional. Whether to report the message as an 'error' or 'warning'. + * Defaults to true (error). + * @param string $code Optional error code for the message. Defaults to 'Found'. + * @param array $data Optional input for the data replacements. + * @param int $severity Optional. Severity level. Defaults to 0 which will translate to + * the PHPCS default severity level. + * @return bool + */ + protected function addFixableMessage( $message, $stackPtr, $is_error = true, $code = 'Found', $data = array(), $severity = 0 ) { + return $this->throwMessage( $message, $stackPtr, $is_error, $code, $data, $severity, true ); + } + + /** + * Add a PHPCS message to the output stack as either a warning or an error. + * + * @since 0.11.0 + * + * @param string $message The message. + * @param int $stackPtr The position of the token the message relates to. + * @param bool $is_error Optional. Whether to report the message as an 'error' or 'warning'. + * Defaults to true (error). + * @param string $code Optional error code for the message. Defaults to 'Found'. + * @param array $data Optional input for the data replacements. + * @param int $severity Optional. Severity level. Defaults to 0 which will translate to + * the PHPCS default severity level. + * @param bool $fixable Optional. Whether this is a fixable error. Defaults to false. + * @return bool + */ + private function throwMessage( $message, $stackPtr, $is_error = true, $code = 'Found', $data = array(), $severity = 0, $fixable = false ) { + + $method = 'add'; + if ( true === $fixable ) { + $method .= 'Fixable'; + } + + if ( true === $is_error ) { + $method .= 'Error'; + } else { + $method .= 'Warning'; + } + + return \call_user_func( array( $this->phpcsFile, $method ), $message, $stackPtr, $code, $data, $severity ); + } + + /** + * Convert an arbitrary string to an alphanumeric string with underscores. + * + * Pre-empt issues with arbitrary strings being used as error codes in XML and PHP. + * + * @since 0.11.0 + * + * @param string $base_string Arbitrary string. + * + * @return string + */ + protected function string_to_errorcode( $base_string ) { + return preg_replace( '`[^a-z0-9_]`i', '_', $base_string ); + } + + /** + * Transform the name of a PHP construct (function, variable etc) to one in snake_case. + * + * @since 2.0.0 Moved from the `WordPress.NamingConventions.ValidFunctionName` sniff + * to this class, renamed from `get_name_suggestion` and made static + * so it can also be used by classes which don't extend this class. + * + * @param string $name The construct name. + * + * @return string + */ + public static function get_snake_case_name_suggestion( $name ) { + $suggested = preg_replace( '`([A-Z])`', '_$1', $name ); + $suggested = strtolower( $suggested ); + $suggested = str_replace( '__', '_', $suggested ); + $suggested = trim( $suggested, '_' ); + return $suggested; + } + + /** + * Merge a pre-set array with a ruleset provided array. + * + * - By default flips custom lists to allow for using `isset()` instead + * of `in_array()`. + * - When `$flip` is true: + * * Presumes the base array is in a `'value' => true` format. + * * Any custom items will be given the value `false` to be able to + * distinguish them from pre-set (base array) values. + * * Will filter previously added custom items out from the base array + * before merging/returning to allow for resetting to the base array. + * + * {@internal Function is static as it doesn't use any of the properties or others + * methods anyway and this way the `WordPress.NamingConventions.ValidVariableName` sniff + * which extends an upstream sniff can also use it.}} + * + * @since 0.11.0 + * @since 2.0.0 No longer supports custom array properties which were incorrectly + * passed as a string. + * + * @param array $custom Custom list as provided via a ruleset. + * @param array $base Optional. Base list. Defaults to an empty array. + * Expects `value => true` format when `$flip` is true. + * @param bool $flip Optional. Whether or not to flip the custom list. + * Defaults to true. + * @return array + */ + public static function merge_custom_array( $custom, $base = array(), $flip = true ) { + if ( true === $flip ) { + $base = array_filter( $base ); + } + + if ( empty( $custom ) || ! \is_array( $custom ) ) { + return $base; + } + + if ( true === $flip ) { + $custom = array_fill_keys( $custom, false ); + } + + if ( empty( $base ) ) { + return $custom; + } + + return array_merge( $base, $custom ); + } + + /** + * Get the last pointer in a line. + * + * @since 0.4.0 + * + * @param integer $stackPtr The position of the current token in the stack passed + * in $tokens. + * + * @return integer Position of the last pointer on that line. + */ + protected function get_last_ptr_on_line( $stackPtr ) { + + $tokens = $this->tokens; + $currentLine = $tokens[ $stackPtr ]['line']; + $nextPtr = ( $stackPtr + 1 ); + + while ( isset( $tokens[ $nextPtr ] ) && $tokens[ $nextPtr ]['line'] === $currentLine ) { + $nextPtr++; + // Do nothing, we just want the last token of the line. + } + + // We've made it to the next line, back up one to the last in the previous line. + // We do this for micro-optimization of the above loop. + $lastPtr = ( $nextPtr - 1 ); + + return $lastPtr; + } + + /** + * Overrule the minimum supported WordPress version with a command-line/config value. + * + * Handle setting the minimum supported WP version in one go for all sniffs which + * expect it via the command line or via a `` variable in a ruleset. + * The config variable overrules the default `$minimum_supported_version` and/or a + * `$minimum_supported_version` set for individual sniffs through the ruleset. + * + * @since 0.14.0 + */ + protected function get_wp_version_from_cl() { + $cl_supported_version = trim( PHPCSHelper::get_config_data( 'minimum_supported_wp_version' ) ); + if ( ! empty( $cl_supported_version ) + && filter_var( $cl_supported_version, \FILTER_VALIDATE_FLOAT ) !== false + ) { + $this->minimum_supported_version = $cl_supported_version; + } + } + + /** + * Find whitelisting comment. + * + * Comment must be at the end of the line or at the end of the statement + * and must use // format. + * It can be prefixed or suffixed with anything e.g. "foobar" will match: + * ... // foobar okay + * ... // WPCS: foobar whitelist. + * + * There is an exception, and that is when PHP is being interspersed with HTML. + * In that case, the comment should always come at the end of the statement (right + * before the closing tag, ?>). For example: + * + * + * + * @since 0.4.0 + * @since 0.14.0 Whitelist comments at the end of the statement are now also accepted. + * + * @deprecated 2.0.0 Use the PHPCS native `phpcs:ignore` annotations instead. + * + * @param string $comment Comment to find. + * @param integer $stackPtr The position of the current token in the stack passed + * in $tokens. + * + * @return boolean True if whitelisting comment was found, false otherwise. + */ + protected function has_whitelist_comment( $comment, $stackPtr ) { + + // Respect the PHPCS 3.x --ignore-annotations setting. + if ( true === PHPCSHelper::ignore_annotations( $this->phpcsFile ) ) { + return false; + } + + static $thrown_notices = array(); + + $deprecation_notice = 'Using the WPCS native whitelist comments is deprecated. Please use the PHPCS native "phpcs:ignore Standard.Category.SniffName.ErrorCode" annotations instead. Found: %s'; + $deprecation_code = 'DeprecatedWhitelistCommentFound'; + $filename = $this->phpcsFile->getFileName(); + + $regex = '#\b' . preg_quote( $comment, '#' ) . '\b#i'; + + // There is a findEndOfStatement() method, but it considers more tokens than + // we need to consider here. + $end_of_statement = $this->phpcsFile->findNext( array( \T_CLOSE_TAG, \T_SEMICOLON ), $stackPtr ); + + if ( false !== $end_of_statement ) { + // If the statement was ended by a semicolon, check if there is a whitelist comment directly after it. + if ( \T_SEMICOLON === $this->tokens[ $end_of_statement ]['code'] ) { + $lastPtr = $this->phpcsFile->findNext( \T_WHITESPACE, ( $end_of_statement + 1 ), null, true ); + } elseif ( \T_CLOSE_TAG === $this->tokens[ $end_of_statement ]['code'] ) { + // If the semicolon was left out and it was terminated by an ending tag, we need to look backwards. + $lastPtr = $this->phpcsFile->findPrevious( \T_WHITESPACE, ( $end_of_statement - 1 ), null, true ); + } + + if ( ( \T_COMMENT === $this->tokens[ $lastPtr ]['code'] + || ( isset( Tokens::$phpcsCommentTokens[ $this->tokens[ $lastPtr ]['code'] ] ) + && \T_PHPCS_SET !== $this->tokens[ $lastPtr ]['code'] ) ) + && $this->tokens[ $lastPtr ]['line'] === $this->tokens[ $end_of_statement ]['line'] + && preg_match( $regex, $this->tokens[ $lastPtr ]['content'] ) === 1 + ) { + if ( isset( $thrown_notices[ $filename ][ $lastPtr ] ) === false + && isset( Tokens::$phpcsCommentTokens[ $this->tokens[ $lastPtr ]['code'] ] ) === false + ) { + $this->phpcsFile->addWarning( + $deprecation_notice, + $lastPtr, + $deprecation_code, + array( $this->tokens[ $lastPtr ]['content'] ) + ); + + $thrown_notices[ $filename ][ $lastPtr ] = true; + } + + return true; + } + } + + // No whitelist comment found so far. Check at the end of the stackPtr line. + // Note: a T_COMMENT includes the new line character, so may be the last token on the line! + $end_of_line = $this->get_last_ptr_on_line( $stackPtr ); + $lastPtr = $this->phpcsFile->findPrevious( \T_WHITESPACE, $end_of_line, null, true ); + + if ( ( \T_COMMENT === $this->tokens[ $lastPtr ]['code'] + || ( isset( Tokens::$phpcsCommentTokens[ $this->tokens[ $lastPtr ]['code'] ] ) + && \T_PHPCS_SET !== $this->tokens[ $lastPtr ]['code'] ) ) + && $this->tokens[ $lastPtr ]['line'] === $this->tokens[ $stackPtr ]['line'] + && preg_match( $regex, $this->tokens[ $lastPtr ]['content'] ) === 1 + ) { + if ( isset( $thrown_notices[ $filename ][ $lastPtr ] ) === false + && isset( Tokens::$phpcsCommentTokens[ $this->tokens[ $lastPtr ]['code'] ] ) === false + ) { + $this->phpcsFile->addWarning( + $deprecation_notice, + $lastPtr, + $deprecation_code, + array( $this->tokens[ $lastPtr ]['content'] ) + ); + + $thrown_notices[ $filename ][ $lastPtr ] = true; + } + + return true; + } + + return false; + } + + /** + * Check if a token is used within a unit test. + * + * Unit test methods are identified as such: + * - Method is within a known unit test class; + * - or Method is within a class/trait which extends a known unit test class. + * + * @since 0.11.0 + * @since 1.1.0 Supports anonymous test classes and improved handling of nested scopes. + * + * @param int $stackPtr The position of the token to be examined. + * + * @return bool True if the token is within a unit test, false otherwise. + */ + protected function is_token_in_test_method( $stackPtr ) { + // Is the token inside of a function definition ? + $functionToken = $this->phpcsFile->getCondition( $stackPtr, \T_FUNCTION ); + if ( false === $functionToken ) { + // No conditions or no function condition. + return false; + } + + $conditions = $this->tokens[ $stackPtr ]['conditions']; + foreach ( $conditions as $token => $condition ) { + if ( $token === $functionToken ) { + // Only examine the conditions the function is nested in, not those nested within the function. + break; + } + + if ( isset( Tokens::$ooScopeTokens[ $condition ] ) ) { + $is_test_class = $this->is_test_class( $token ); + if ( true === $is_test_class ) { + return true; + } + } + } + + return false; + } + + /** + * Check if a class token is part of a unit test suite. + * + * Unit test classes are identified as such: + * - Class which either extends WP_UnitTestCase or PHPUnit_Framework_TestCase + * or a custom whitelisted unit test class. + * + * @since 0.12.0 Split off from the `is_token_in_test_method()` method. + * @since 1.0.0 Improved recognition of namespaced class names. + * + * @param int $stackPtr The position of the token to be examined. + * This should be a class, anonymous class or trait token. + * + * @return bool True if the class is a unit test class, false otherwise. + */ + protected function is_test_class( $stackPtr ) { + + if ( isset( $this->tokens[ $stackPtr ], Tokens::$ooScopeTokens[ $this->tokens[ $stackPtr ]['code'] ] ) === false ) { + return false; + } + + // Add any potentially whitelisted custom test classes to the whitelist. + $whitelist = $this->merge_custom_array( + $this->custom_test_class_whitelist, + $this->test_class_whitelist + ); + + /* + * Show some tolerance for user input. + * The custom test class names should be passed as FQN without a prefixing `\`. + */ + foreach ( $whitelist as $k => $v ) { + $whitelist[ $k ] = ltrim( $v, '\\' ); + } + + // Is the class/trait one of the whitelisted test classes ? + $namespace = $this->determine_namespace( $stackPtr ); + $className = $this->phpcsFile->getDeclarationName( $stackPtr ); + if ( '' !== $namespace ) { + if ( isset( $whitelist[ $namespace . '\\' . $className ] ) ) { + return true; + } + } elseif ( isset( $whitelist[ $className ] ) ) { + return true; + } + + // Does the class/trait extend one of the whitelisted test classes ? + $extendedClassName = $this->phpcsFile->findExtendedClassName( $stackPtr ); + if ( false === $extendedClassName ) { + return false; + } + + if ( '\\' === $extendedClassName[0] ) { + if ( isset( $whitelist[ substr( $extendedClassName, 1 ) ] ) ) { + return true; + } + } elseif ( '' !== $namespace ) { + if ( isset( $whitelist[ $namespace . '\\' . $extendedClassName ] ) ) { + return true; + } + } elseif ( isset( $whitelist[ $extendedClassName ] ) ) { + return true; + } + + /* + * Not examining imported classes via `use` statements as with the variety of syntaxes, + * this would get very complicated. + * After all, users can add an `` for a particular sniff to their + * custom ruleset to selectively exclude the test directory. + */ + + return false; + } + + /** + * Check if this variable is being assigned a value. + * + * E.g., $var = 'foo'; + * + * Also handles array assignments to arbitrary depth: + * + * $array['key'][ $foo ][ something() ] = $bar; + * + * @since 0.5.0 + * + * @param int $stackPtr The index of the token in the stack. This must point to + * either a T_VARIABLE or T_CLOSE_SQUARE_BRACKET token. + * + * @return bool Whether the token is a variable being assigned a value. + */ + protected function is_assignment( $stackPtr ) { + + static $valid = array( + \T_VARIABLE => true, + \T_CLOSE_SQUARE_BRACKET => true, + ); + + // Must be a variable, constant or closing square bracket (see below). + if ( ! isset( $valid[ $this->tokens[ $stackPtr ]['code'] ] ) ) { + return false; + } + + $next_non_empty = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + ( $stackPtr + 1 ), + null, + true, + null, + true + ); + + // No token found. + if ( false === $next_non_empty ) { + return false; + } + + // If the next token is an assignment, that's all we need to know. + if ( isset( Tokens::$assignmentTokens[ $this->tokens[ $next_non_empty ]['code'] ] ) ) { + return true; + } + + // Check if this is an array assignment, e.g., `$var['key'] = 'val';` . + if ( \T_OPEN_SQUARE_BRACKET === $this->tokens[ $next_non_empty ]['code'] + && isset( $this->tokens[ $next_non_empty ]['bracket_closer'] ) + ) { + return $this->is_assignment( $this->tokens[ $next_non_empty ]['bracket_closer'] ); + } + + return false; + } + + /** + * Check if this token has an associated nonce check. + * + * @since 0.5.0 + * + * @param int $stackPtr The position of the current token in the stack of tokens. + * + * @return bool + */ + protected function has_nonce_check( $stackPtr ) { + + /** + * A cache of the scope that we last checked for nonce verification in. + * + * @var array { + * @var string $file The name of the file. + * @var int $start The index of the token where the scope started. + * @var int $end The index of the token where the scope ended. + * @var bool|int $nonce_check The index of the token where an nonce check + * was found, or false if none was found. + * } + */ + static $last; + + $start = 0; + $end = $stackPtr; + + $tokens = $this->phpcsFile->getTokens(); + + // If we're in a function, only look inside of it. + // Once PHPCS 3.5.0 comes out this should be changed to the new Conditions::GetLastCondition() method. + if ( isset( $tokens[ $stackPtr ]['conditions'] ) === true ) { + $conditions = $tokens[ $stackPtr ]['conditions']; + $conditions = array_reverse( $conditions, true ); + foreach ( $conditions as $tokenPtr => $condition ) { + if ( \T_FUNCTION === $condition || \T_CLOSURE === $condition ) { + $start = $tokens[ $tokenPtr ]['scope_opener']; + break; + } + } + } + + $allow_nonce_after = false; + if ( $this->is_in_isset_or_empty( $stackPtr ) + || $this->is_in_type_test( $stackPtr ) + || $this->is_comparison( $stackPtr ) + || $this->is_in_array_comparison( $stackPtr ) + || $this->is_in_function_call( $stackPtr, $this->unslashingFunctions ) !== false + || $this->is_only_sanitized( $stackPtr ) + ) { + $allow_nonce_after = true; + } + + // We allow for certain actions, such as an isset() check to come before the nonce check. + // If this superglobal is inside such a check, look for the nonce after it as well, + // all the way to the end of the scope. + if ( true === $allow_nonce_after ) { + $end = ( 0 === $start ) ? $this->phpcsFile->numTokens : $tokens[ $start ]['scope_closer']; + } + + // Check if we've looked here before. + $filename = $this->phpcsFile->getFilename(); + + if ( is_array( $last ) + && $filename === $last['file'] + && $start === $last['start'] + ) { + + if ( false !== $last['nonce_check'] ) { + // If we have already found an nonce check in this scope, we just + // need to check whether it comes before this token. It is OK if the + // check is after the token though, if this was only a isset() check. + return ( true === $allow_nonce_after || $last['nonce_check'] < $stackPtr ); + } elseif ( $end <= $last['end'] ) { + // If not, we can still go ahead and return false if we've already + // checked to the end of the search area. + return false; + } + + // We haven't checked this far yet, but we can still save work by + // skipping over the part we've already checked. + $start = $last['end']; + } else { + $last = array( + 'file' => $filename, + 'start' => $start, + 'end' => $end, + ); + } + + // Loop through the tokens looking for nonce verification functions. + for ( $i = $start; $i < $end; $i++ ) { + // Skip over nested closed scope constructs. + if ( \T_FUNCTION === $tokens[ $i ]['code'] + || \T_CLOSURE === $tokens[ $i ]['code'] + || isset( Tokens::$ooScopeTokens[ $tokens[ $i ]['code'] ] ) + ) { + if ( isset( $tokens[ $i ]['scope_closer'] ) ) { + $i = $tokens[ $i ]['scope_closer']; + } + continue; + } + + // If this isn't a function name, skip it. + if ( \T_STRING !== $tokens[ $i ]['code'] ) { + continue; + } + + // If this is one of the nonce verification functions, we can bail out. + if ( isset( $this->nonceVerificationFunctions[ $tokens[ $i ]['content'] ] ) ) { + /* + * Now, make sure it is a call to a global function. + */ + if ( $this->is_class_object_call( $i ) === true ) { + continue; + } + + if ( $this->is_token_namespaced( $i ) === true ) { + continue; + } + + $last['nonce_check'] = $i; + return true; + } + } + + // We're still here, so no luck. + $last['nonce_check'] = false; + + return false; + } + + /** + * Check if a token is inside of an isset(), empty() or array_key_exists() statement. + * + * @since 0.5.0 + * @since 2.1.0 Now checks for the token being used as the array parameter + * in function calls to array_key_exists() and key_exists() as well. + * + * @param int $stackPtr The index of the token in the stack. + * + * @return bool Whether the token is inside an isset() or empty() statement. + */ + protected function is_in_isset_or_empty( $stackPtr ) { + + if ( ! isset( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) ) { + return false; + } + + $nested_parenthesis = $this->tokens[ $stackPtr ]['nested_parenthesis']; + + end( $nested_parenthesis ); + $open_parenthesis = key( $nested_parenthesis ); + + $previous_non_empty = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $open_parenthesis - 1 ), null, true, null, true ); + if ( false === $previous_non_empty ) { + return false; + } + + $previous_code = $this->tokens[ $previous_non_empty ]['code']; + if ( \T_ISSET === $previous_code || \T_EMPTY === $previous_code ) { + return true; + } + + $valid_functions = array( + 'array_key_exists' => true, + 'key_exists' => true, // Alias. + ); + + $functionPtr = $this->is_in_function_call( $stackPtr, $valid_functions ); + if ( false !== $functionPtr ) { + $second_param = $this->get_function_call_parameter( $functionPtr, 2 ); + if ( $stackPtr >= $second_param['start'] && $stackPtr <= $second_param['end'] ) { + return true; + } + } + + return false; + } + + /** + * Check if a particular token is a (static or non-static) call to a class method or property. + * + * @internal Note: this may still mistake a namespaced function imported via a `use` statement for + * a global function! + * + * @since 2.1.0 + * + * @param int $stackPtr The index of the token in the stack. + * + * @return bool + */ + protected function is_class_object_call( $stackPtr ) { + $before = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true, null, true ); + + if ( false === $before ) { + return false; + } + + if ( \T_OBJECT_OPERATOR !== $this->tokens[ $before ]['code'] + && \T_DOUBLE_COLON !== $this->tokens[ $before ]['code'] + ) { + return false; + } + + return true; + } + + /** + * Check if a particular token is prefixed with a namespace. + * + * @internal This will give a false positive if the file is not namespaced and the token is prefixed + * with `namespace\`. + * + * @since 2.1.0 + * + * @param int $stackPtr The index of the token in the stack. + * + * @return bool + */ + protected function is_token_namespaced( $stackPtr ) { + $prev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true, null, true ); + + if ( false === $prev ) { + return false; + } + + if ( \T_NS_SEPARATOR !== $this->tokens[ $prev ]['code'] ) { + return false; + } + + $before_prev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $prev - 1 ), null, true, null, true ); + if ( false === $before_prev ) { + return false; + } + + if ( \T_STRING !== $this->tokens[ $before_prev ]['code'] + && \T_NAMESPACE !== $this->tokens[ $before_prev ]['code'] + ) { + return false; + } + + return true; + } + + /** + * Check if a token is (part of) a parameter for a function call to a select list of functions. + * + * This is useful, for instance, when trying to determine the context a variable is used in. + * + * For example: this function could be used to determine if the variable `$foo` is used + * in a global function call to the function `is_foo()`. + * In that case, a call to this function would return the stackPtr to the T_STRING `is_foo` + * for code like: `is_foo( $foo, 'some_other_param' )`, while it would return `false` for + * the following code `is_bar( $foo, 'some_other_param' )`. + * + * @since 2.1.0 + * + * @param int $stackPtr The index of the token in the stack. + * @param array $valid_functions List of valid function names. + * Note: The keys to this array should be the function names + * in lowercase. Values are irrelevant. + * @param bool $global Optional. Whether to make sure that the function call is + * to a global function. If `false`, calls to methods, be it static + * `Class::method()` or via an object `$obj->method()`, and + * namespaced function calls, like `MyNS\function_name()` will + * also be accepted. + * Defaults to `true`. + * @param bool $allow_nested Optional. Whether to allow for nested function calls within the + * call to this function. + * I.e. when checking whether a token is within a function call + * to `strtolower()`, whether to accept `strtolower( trim( $var ) )` + * or only `strtolower( $var )`. + * Defaults to `false`. + * + * @return int|bool Stack pointer to the function call T_STRING token or false otherwise. + */ + protected function is_in_function_call( $stackPtr, $valid_functions, $global = true, $allow_nested = false ) { + if ( ! isset( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) ) { + return false; + } + + $nested_parenthesis = $this->tokens[ $stackPtr ]['nested_parenthesis']; + if ( false === $allow_nested ) { + $nested_parenthesis = array_reverse( $nested_parenthesis, true ); + } + + foreach ( $nested_parenthesis as $open => $close ) { + + $prev_non_empty = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $open - 1 ), null, true, null, true ); + if ( false === $prev_non_empty || \T_STRING !== $this->tokens[ $prev_non_empty ]['code'] ) { + continue; + } + + if ( isset( $valid_functions[ strtolower( $this->tokens[ $prev_non_empty ]['content'] ) ] ) === false ) { + if ( false === $allow_nested ) { + // Function call encountered, but not to one of the allowed functions. + return false; + } + + continue; + } + + if ( false === $global ) { + return $prev_non_empty; + } + + /* + * Now, make sure it is a global function. + */ + if ( $this->is_class_object_call( $prev_non_empty ) === true ) { + continue; + } + + if ( $this->is_token_namespaced( $prev_non_empty ) === true ) { + continue; + } + + return $prev_non_empty; + } + + return false; + } + + /** + * Check if a token is inside of an is_...() statement. + * + * @since 2.1.0 + * + * @param int $stackPtr The index of the token in the stack. + * + * @return bool Whether the token is being type tested. + */ + protected function is_in_type_test( $stackPtr ) { + /* + * Casting the potential integer stack pointer return value to boolean here is fine. + * The return can never be `0` as there will always be a PHP open tag before the + * function call. + */ + return (bool) $this->is_in_function_call( $stackPtr, $this->typeTestFunctions ); + } + + /** + * Check if something is only being sanitized. + * + * @since 0.5.0 + * + * @param int $stackPtr The index of the token in the stack. + * + * @return bool Whether the token is only within a sanitization. + */ + protected function is_only_sanitized( $stackPtr ) { + + // If it isn't being sanitized at all. + if ( ! $this->is_sanitized( $stackPtr ) ) { + return false; + } + + // If this isn't set, we know the value must have only been casted, because + // is_sanitized() would have returned false otherwise. + if ( ! isset( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) ) { + return true; + } + + // At this point we're expecting the value to have not been casted. If it + // was, it wasn't *only* casted, because it's also in a function. + if ( $this->is_safe_casted( $stackPtr ) ) { + return false; + } + + // The only parentheses should belong to the sanitizing function. If there's + // more than one set, this isn't *only* sanitization. + return ( \count( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) === 1 ); + } + + /** + * Check if something is being casted to a safe value. + * + * @since 0.5.0 + * + * @param int $stackPtr The index of the token in the stack. + * + * @return bool Whether the token being casted. + */ + protected function is_safe_casted( $stackPtr ) { + + // Get the last non-empty token. + $prev = $this->phpcsFile->findPrevious( + Tokens::$emptyTokens, + ( $stackPtr - 1 ), + null, + true + ); + + if ( false === $prev ) { + return false; + } + + // Check if it is a safe cast. + return isset( $this->safe_casts[ $this->tokens[ $prev ]['code'] ] ); + } + + /** + * Check if something is being sanitized. + * + * @since 0.5.0 + * + * @param int $stackPtr The index of the token in the stack. + * @param bool $require_unslash Whether to give an error if no unslashing function + * is used on the variable before sanitization. + * + * @return bool Whether the token being sanitized. + */ + protected function is_sanitized( $stackPtr, $require_unslash = false ) { + + // First we check if it is being casted to a safe value. + if ( $this->is_safe_casted( $stackPtr ) ) { + return true; + } + + // If this isn't within a function call, we know already that it's not safe. + if ( ! isset( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) ) { + if ( $require_unslash ) { + $this->add_unslash_error( $stackPtr ); + } + + return false; + } + + // Get the function that it's in. + $nested_parenthesis = $this->tokens[ $stackPtr ]['nested_parenthesis']; + $nested_openers = array_keys( $nested_parenthesis ); + $function_opener = array_pop( $nested_openers ); + $functionPtr = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $function_opener - 1 ), null, true, null, true ); + + // If it is just being unset, the value isn't used at all, so it's safe. + if ( \T_UNSET === $this->tokens[ $functionPtr ]['code'] ) { + return true; + } + + $valid_functions = $this->sanitizingFunctions; + $valid_functions += $this->unslashingSanitizingFunctions; + $valid_functions += $this->unslashingFunctions; + $valid_functions += $this->arrayWalkingFunctions; + + $functionPtr = $this->is_in_function_call( $stackPtr, $valid_functions ); + + // If this isn't a call to one of the valid functions, it sure isn't a sanitizing function. + if ( false === $functionPtr ) { + if ( true === $require_unslash ) { + $this->add_unslash_error( $stackPtr ); + } + + return false; + } + + $functionName = $this->tokens[ $functionPtr ]['content']; + + // Check if an unslashing function is being used. + if ( isset( $this->unslashingFunctions[ $functionName ] ) ) { + + $is_unslashed = true; + + // Remove the unslashing functions. + $valid_functions = array_diff_key( $valid_functions, $this->unslashingFunctions ); + + // Check is any of the remaining (sanitizing) functions is used. + $higherFunctionPtr = $this->is_in_function_call( $functionPtr, $valid_functions ); + + // If there is no other valid function being used, this value is unsanitized. + if ( false === $higherFunctionPtr ) { + return false; + } + + $functionPtr = $higherFunctionPtr; + $functionName = $this->tokens[ $functionPtr ]['content']; + + } else { + $is_unslashed = false; + } + + // Arrays might be sanitized via an array walking function using a callback. + if ( isset( $this->arrayWalkingFunctions[ $functionName ] ) ) { + + // Get the callback parameter. + $callback = $this->get_function_call_parameter( $functionPtr, $this->arrayWalkingFunctions[ $functionName ] ); + + if ( ! empty( $callback ) ) { + /* + * If this is a function callback (not a method callback array) and we're able + * to resolve the function name, do so. + */ + $first_non_empty = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + $callback['start'], + ( $callback['end'] + 1 ), + true + ); + + if ( false !== $first_non_empty && \T_CONSTANT_ENCAPSED_STRING === $this->tokens[ $first_non_empty ]['code'] ) { + $functionName = $this->strip_quotes( $this->tokens[ $first_non_empty ]['content'] ); + } + } + } + + // If slashing is required, give an error. + if ( ! $is_unslashed && $require_unslash && ! isset( $this->unslashingSanitizingFunctions[ $functionName ] ) ) { + $this->add_unslash_error( $stackPtr ); + } + + // Check if this is a sanitizing function. + if ( isset( $this->sanitizingFunctions[ $functionName ] ) || isset( $this->unslashingSanitizingFunctions[ $functionName ] ) ) { + return true; + } + + return false; + } + + /** + * Add an error for missing use of unslashing. + * + * @since 0.5.0 + * + * @param int $stackPtr The index of the token in the stack. + */ + public function add_unslash_error( $stackPtr ) { + + $this->phpcsFile->addError( + '%s data not unslashed before sanitization. Use wp_unslash() or similar', + $stackPtr, + 'MissingUnslash', + array( $this->tokens[ $stackPtr ]['content'] ) + ); + } + + /** + * Get the index keys of an array variable. + * + * E.g., "bar" and "baz" in $foo['bar']['baz']. + * + * @since 2.1.0 + * + * @param int $stackPtr The index of the variable token in the stack. + * @param bool $all Whether to get all keys or only the first. + * Defaults to `true`(= all). + * + * @return array An array of index keys whose value is being accessed. + * or an empty array if this is not array access. + */ + protected function get_array_access_keys( $stackPtr, $all = true ) { + + $keys = array(); + + if ( \T_VARIABLE !== $this->tokens[ $stackPtr ]['code'] ) { + return $keys; + } + + $current = $stackPtr; + + do { + // Find the next non-empty token. + $open_bracket = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + ( $current + 1 ), + null, + true + ); + + // If it isn't a bracket, this isn't an array-access. + if ( false === $open_bracket + || \T_OPEN_SQUARE_BRACKET !== $this->tokens[ $open_bracket ]['code'] + || ! isset( $this->tokens[ $open_bracket ]['bracket_closer'] ) + ) { + break; + } + + $key = $this->phpcsFile->getTokensAsString( + ( $open_bracket + 1 ), + ( $this->tokens[ $open_bracket ]['bracket_closer'] - $open_bracket - 1 ) + ); + + $keys[] = trim( $key ); + $current = $this->tokens[ $open_bracket ]['bracket_closer']; + } while ( isset( $this->tokens[ $current ] ) && true === $all ); + + return $keys; + } + + /** + * Get the index key of an array variable. + * + * E.g., "bar" in $foo['bar']. + * + * @since 0.5.0 + * @since 2.1.0 Now uses get_array_access_keys() under the hood. + * + * @param int $stackPtr The index of the token in the stack. + * + * @return string|false The array index key whose value is being accessed. + */ + protected function get_array_access_key( $stackPtr ) { + + $keys = $this->get_array_access_keys( $stackPtr, false ); + + if ( isset( $keys[0] ) ) { + return $keys[0]; + } + + return false; + } + + /** + * Check if the existence of a variable is validated with isset(), empty(), array_key_exists() + * or key_exists(). + * + * When $in_condition_only is false, (which is the default), this is considered + * valid: + * + * ```php + * if ( isset( $var ) ) { + * // Do stuff, like maybe return or exit (but could be anything) + * } + * + * foo( $var ); + * ``` + * + * When it is true, that would be invalid, the use of the variable must be within + * the scope of the validating condition, like this: + * + * ```php + * if ( isset( $var ) ) { + * foo( $var ); + * } + * ``` + * + * @since 0.5.0 + * @since 2.1.0 Now recognizes array_key_exists() and key_exists() as validation functions. + * @since 2.1.0 Stricter check on whether the correct variable and the correct + * array keys are being validated. + * + * @param int $stackPtr The index of this token in the stack. + * @param array|string $array_keys An array key to check for ("bar" in $foo['bar']) + * or an array of keys for multi-level array access. + * @param bool $in_condition_only Whether to require that this use of the + * variable occur within the scope of the + * validating condition, or just in the same + * scope as it (default). + * + * @return bool Whether the var is validated. + */ + protected function is_validated( $stackPtr, $array_keys = array(), $in_condition_only = false ) { + + if ( $in_condition_only ) { + /* + * This is a stricter check, requiring the variable to be used only + * within the validation condition. + */ + + // If there are no conditions, there's no validation. + if ( empty( $this->tokens[ $stackPtr ]['conditions'] ) ) { + return false; + } + + $conditions = $this->tokens[ $stackPtr ]['conditions']; + end( $conditions ); // Get closest condition. + $conditionPtr = key( $conditions ); + $condition = $this->tokens[ $conditionPtr ]; + + if ( ! isset( $condition['parenthesis_opener'] ) ) { + // Live coding or parse error. + return false; + } + + $scope_start = $condition['parenthesis_opener']; + $scope_end = $condition['parenthesis_closer']; + + } else { + /* + * We are are more loose, requiring only that the variable be validated + * in the same function/file scope as it is used. + */ + + $scope_start = 0; + + // Check if we are in a function. + $function = $this->phpcsFile->getCondition( $stackPtr, \T_FUNCTION ); + + // If so, we check only within the function, otherwise the whole file. + if ( false !== $function ) { + $scope_start = $this->tokens[ $function ]['scope_opener']; + } else { + // Check if we are in a closure. + $closure = $this->phpcsFile->getCondition( $stackPtr, \T_CLOSURE ); + + // If so, we check only within the closure. + if ( false !== $closure ) { + $scope_start = $this->tokens[ $closure ]['scope_opener']; + } + } + + $scope_end = $stackPtr; + } + + if ( ! empty( $array_keys ) && ! is_array( $array_keys ) ) { + $array_keys = (array) $array_keys; + } + + $bare_array_keys = array_map( array( $this, 'strip_quotes' ), $array_keys ); + $targets = array( + \T_ISSET => 'construct', + \T_EMPTY => 'construct', + \T_UNSET => 'construct', + \T_STRING => 'function_call', + \T_COALESCE => 'coalesce', + \T_COALESCE_EQUAL => 'coalesce', + ); + + // phpcs:ignore Generic.CodeAnalysis.JumbledIncrementer.Found -- On purpose, see below. + for ( $i = ( $scope_start + 1 ); $i < $scope_end; $i++ ) { + + if ( isset( $targets[ $this->tokens[ $i ]['code'] ] ) === false ) { + continue; + } + + switch ( $targets[ $this->tokens[ $i ]['code'] ] ) { + case 'construct': + $issetOpener = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $i + 1 ), null, true, null, true ); + if ( false === $issetOpener || \T_OPEN_PARENTHESIS !== $this->tokens[ $issetOpener ]['code'] ) { + // Parse error or live coding. + continue 2; + } + + $issetCloser = $this->tokens[ $issetOpener ]['parenthesis_closer']; + + // Look for this variable. We purposely stomp $i from the parent loop. + for ( $i = ( $issetOpener + 1 ); $i < $issetCloser; $i++ ) { + + if ( \T_VARIABLE !== $this->tokens[ $i ]['code'] ) { + continue; + } + + if ( $this->tokens[ $stackPtr ]['content'] !== $this->tokens[ $i ]['content'] ) { + continue; + } + + // If we're checking for specific array keys (ex: 'hello' in + // $_POST['hello']), that must match too. Quote-style, however, doesn't matter. + if ( ! empty( $bare_array_keys ) ) { + $found_keys = $this->get_array_access_keys( $i ); + $found_keys = array_map( array( $this, 'strip_quotes' ), $found_keys ); + $diff = array_diff_assoc( $bare_array_keys, $found_keys ); + if ( ! empty( $diff ) ) { + continue; + } + } + + return true; + } + + break; + + case 'function_call': + // Only check calls to array_key_exists() and key_exists(). + if ( 'array_key_exists' !== $this->tokens[ $i ]['content'] + && 'key_exists' !== $this->tokens[ $i ]['content'] + ) { + continue 2; + } + + $next_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $i + 1 ), null, true, null, true ); + if ( false === $next_non_empty || \T_OPEN_PARENTHESIS !== $this->tokens[ $next_non_empty ]['code'] ) { + // Not a function call. + continue 2; + } + + if ( $this->is_class_object_call( $i ) === true ) { + // Method call. + continue 2; + } + + if ( $this->is_token_namespaced( $i ) === true ) { + // Namespaced function call. + continue 2; + } + + $params = $this->get_function_call_parameters( $i ); + if ( count( $params ) < 2 ) { + continue 2; + } + + $param2_first_token = $this->phpcsFile->findNext( Tokens::$emptyTokens, $params[2]['start'], ( $params[2]['end'] + 1 ), true ); + if ( false === $param2_first_token + || \T_VARIABLE !== $this->tokens[ $param2_first_token ]['code'] + || $this->tokens[ $param2_first_token ]['content'] !== $this->tokens[ $stackPtr ]['content'] + ) { + continue 2; + } + + if ( ! empty( $bare_array_keys ) ) { + // Prevent the original array from being altered. + $bare_keys = $bare_array_keys; + $last_key = array_pop( $bare_keys ); + + /* + * For multi-level array access, the complete set of keys could be split between + * the first and the second parameter, but could also be completely in the second + * parameter, so we need to check both options. + */ + + $found_keys = $this->get_array_access_keys( $param2_first_token ); + $found_keys = array_map( array( $this, 'strip_quotes' ), $found_keys ); + + // First try matching the complete set against the second parameter. + $diff = array_diff_assoc( $bare_array_keys, $found_keys ); + if ( empty( $diff ) ) { + return true; + } + + // If that failed, try getting an exact match for the subset against the + // second parameter and the last key against the first. + if ( $bare_keys === $found_keys && $this->strip_quotes( $params[1]['raw'] ) === $last_key ) { + return true; + } + + // Didn't find the correct array keys. + continue 2; + } + + return true; + + case 'coalesce': + $prev = $i; + do { + $prev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $prev - 1 ), null, true, null, true ); + // Skip over array keys, like $_GET['key']['subkey']. + if ( \T_CLOSE_SQUARE_BRACKET === $this->tokens[ $prev ]['code'] ) { + $prev = $this->tokens[ $prev ]['bracket_opener']; + continue; + } + + break; + } while ( $prev >= ( $scope_start + 1 ) ); + + // We should now have reached the variable. + if ( \T_VARIABLE !== $this->tokens[ $prev ]['code'] ) { + continue 2; + } + + if ( $this->tokens[ $prev ]['content'] !== $this->tokens[ $stackPtr ]['content'] ) { + continue 2; + } + + if ( ! empty( $bare_array_keys ) ) { + $found_keys = $this->get_array_access_keys( $prev ); + $found_keys = array_map( array( $this, 'strip_quotes' ), $found_keys ); + $diff = array_diff_assoc( $bare_array_keys, $found_keys ); + if ( ! empty( $diff ) ) { + continue 2; + } + } + + // Right variable, correct key. + return true; + } + } + + return false; + } + + /** + * Check whether a variable is being compared to another value. + * + * E.g., $var === 'foo', 1 <= $var, etc. + * + * Also recognizes `switch ( $var )`. + * + * @since 0.5.0 + * @since 2.1.0 Added the $include_coalesce parameter. + * + * @param int $stackPtr The index of this token in the stack. + * @param bool $include_coalesce Optional. Whether or not to regard the null + * coalesce operator - ?? - as a comparison operator. + * Defaults to true. + * Null coalesce is a special comparison operator in this + * sense as it doesn't compare a variable to whatever is + * on the other side of the comparison operator. + * + * @return bool Whether this is a comparison. + */ + protected function is_comparison( $stackPtr, $include_coalesce = true ) { + + $comparisonTokens = Tokens::$comparisonTokens; + if ( false === $include_coalesce ) { + unset( $comparisonTokens[ \T_COALESCE ] ); + } + + // We first check if this is a switch statement (switch ( $var )). + if ( isset( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) ) { + $nested_parenthesis = $this->tokens[ $stackPtr ]['nested_parenthesis']; + $close_parenthesis = end( $nested_parenthesis ); + + if ( + isset( $this->tokens[ $close_parenthesis ]['parenthesis_owner'] ) + && \T_SWITCH === $this->tokens[ $this->tokens[ $close_parenthesis ]['parenthesis_owner'] ]['code'] + ) { + return true; + } + } + + // Find the previous non-empty token. We check before the var first because + // yoda conditions are usually expected. + $previous_token = $this->phpcsFile->findPrevious( + Tokens::$emptyTokens, + ( $stackPtr - 1 ), + null, + true + ); + + if ( isset( $comparisonTokens[ $this->tokens[ $previous_token ]['code'] ] ) ) { + return true; + } + + // Maybe the comparison operator is after this. + $next_token = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + ( $stackPtr + 1 ), + null, + true + ); + + // This might be an opening square bracket in the case of arrays ($var['a']). + while ( false !== $next_token && \T_OPEN_SQUARE_BRACKET === $this->tokens[ $next_token ]['code'] ) { + + $next_token = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + ( $this->tokens[ $next_token ]['bracket_closer'] + 1 ), + null, + true + ); + } + + if ( false !== $next_token && isset( $comparisonTokens[ $this->tokens[ $next_token ]['code'] ] ) ) { + return true; + } + + return false; + } + + /** + * Check if a token is inside of an array-value comparison function. + * + * @since 2.1.0 + * + * @param int $stackPtr The index of the token in the stack. + * + * @return bool Whether the token is (part of) a parameter to an + * array-value comparison function. + */ + protected function is_in_array_comparison( $stackPtr ) { + $function_ptr = $this->is_in_function_call( $stackPtr, $this->arrayCompareFunctions, true, true ); + if ( false === $function_ptr ) { + return false; + } + + $function_name = $this->tokens[ $function_ptr ]['content']; + if ( true === $this->arrayCompareFunctions[ $function_name ] ) { + return true; + } + + if ( $this->get_function_call_parameter_count( $function_ptr ) >= $this->arrayCompareFunctions[ $function_name ] ) { + return true; + } + + return false; + } + + /** + * Check what type of 'use' statement a token is part of. + * + * The T_USE token has multiple different uses: + * + * 1. In a closure: function () use ( $var ) {} + * 2. In a class, to import a trait: use Trait_Name + * 3. In a namespace, to import a class: use Some\Class; + * + * This function will check the token and return 'closure', 'trait', or 'class', + * based on which of these uses the use is being used for. + * + * @since 0.7.0 + * + * @param int $stackPtr The position of the token to check. + * + * @return string The type of use. + */ + protected function get_use_type( $stackPtr ) { + + // USE keywords inside closures. + $next = $this->phpcsFile->findNext( \T_WHITESPACE, ( $stackPtr + 1 ), null, true ); + + if ( \T_OPEN_PARENTHESIS === $this->tokens[ $next ]['code'] ) { + return 'closure'; + } + + // USE keywords for traits. + $valid_scopes = array( + 'T_CLASS' => true, + 'T_ANON_CLASS' => true, + 'T_TRAIT' => true, + ); + if ( false !== $this->valid_direct_scope( $stackPtr, $valid_scopes ) ) { + return 'trait'; + } + + // USE keywords for classes to import to a namespace. + return 'class'; + } + + /** + * Get the interpolated variable names from a string. + * + * Check if '$' is followed by a valid variable name, and that it is not preceded by an escape sequence. + * + * @since 0.9.0 + * + * @param string $string The contents of a T_DOUBLE_QUOTED_STRING or T_HEREDOC token. + * + * @return array Variable names (without '$' sigil). + */ + protected function get_interpolated_variables( $string ) { + $variables = array(); + if ( preg_match_all( '/(?P\\\\*)\$(?P\w+)/', $string, $match_sets, \PREG_SET_ORDER ) ) { + foreach ( $match_sets as $matches ) { + if ( ! isset( $matches['backslashes'] ) || ( \strlen( $matches['backslashes'] ) % 2 ) === 0 ) { + $variables[] = $matches['symbol']; + } + } + } + return $variables; + } + + /** + * Strip variables from an arbitrary double quoted/heredoc string. + * + * Intended for use with the contents of a T_DOUBLE_QUOTED_STRING or T_HEREDOC token. + * + * @since 0.14.0 + * + * @param string $string The raw string. + * + * @return string String without variables in it. + */ + public function strip_interpolated_variables( $string ) { + if ( strpos( $string, '$' ) === false ) { + return $string; + } + + return preg_replace( self::REGEX_COMPLEX_VARS, '', $string ); + } + + /** + * Checks if a function call has parameters. + * + * Expects to be passed the T_STRING stack pointer for the function call. + * If passed a T_STRING which is *not* a function call, the behaviour is unreliable. + * + * Extra feature: If passed an T_ARRAY or T_OPEN_SHORT_ARRAY stack pointer, it + * will detect whether the array has values or is empty. + * + * @link https://github.com/PHPCompatibility/PHPCompatibility/issues/120 + * @link https://github.com/PHPCompatibility/PHPCompatibility/issues/152 + * + * @since 0.11.0 + * + * @param int $stackPtr The position of the function call token. + * + * @return bool + */ + public function does_function_call_have_parameters( $stackPtr ) { + + // Check for the existence of the token. + if ( false === isset( $this->tokens[ $stackPtr ] ) ) { + return false; + } + + // Is this one of the tokens this function handles ? + if ( false === \in_array( $this->tokens[ $stackPtr ]['code'], array( \T_STRING, \T_ARRAY, \T_OPEN_SHORT_ARRAY ), true ) ) { + return false; + } + + $next_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true, null, true ); + + // Deal with short array syntax. + if ( 'T_OPEN_SHORT_ARRAY' === $this->tokens[ $stackPtr ]['type'] ) { + if ( false === isset( $this->tokens[ $stackPtr ]['bracket_closer'] ) ) { + return false; + } + + if ( $next_non_empty === $this->tokens[ $stackPtr ]['bracket_closer'] ) { + // No parameters. + return false; + } else { + return true; + } + } + + // Deal with function calls & long arrays. + // Next non-empty token should be the open parenthesis. + if ( false === $next_non_empty && \T_OPEN_PARENTHESIS !== $this->tokens[ $next_non_empty ]['code'] ) { + return false; + } + + if ( false === isset( $this->tokens[ $next_non_empty ]['parenthesis_closer'] ) ) { + return false; + } + + $close_parenthesis = $this->tokens[ $next_non_empty ]['parenthesis_closer']; + $next_next_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $next_non_empty + 1 ), ( $close_parenthesis + 1 ), true ); + + if ( $next_next_non_empty === $close_parenthesis ) { + // No parameters. + return false; + } + + return true; + } + + /** + * Count the number of parameters a function call has been passed. + * + * Expects to be passed the T_STRING stack pointer for the function call. + * If passed a T_STRING which is *not* a function call, the behaviour is unreliable. + * + * Extra feature: If passed an T_ARRAY or T_OPEN_SHORT_ARRAY stack pointer, + * it will return the number of values in the array. + * + * @link https://github.com/PHPCompatibility/PHPCompatibility/issues/111 + * @link https://github.com/PHPCompatibility/PHPCompatibility/issues/114 + * @link https://github.com/PHPCompatibility/PHPCompatibility/issues/151 + * + * @since 0.11.0 + * + * @param int $stackPtr The position of the function call token. + * + * @return int + */ + public function get_function_call_parameter_count( $stackPtr ) { + if ( false === $this->does_function_call_have_parameters( $stackPtr ) ) { + return 0; + } + + return \count( $this->get_function_call_parameters( $stackPtr ) ); + } + + /** + * Get information on all parameters passed to a function call. + * + * Expects to be passed the T_STRING stack pointer for the function call. + * If passed a T_STRING which is *not* a function call, the behaviour is unreliable. + * + * Extra feature: If passed an T_ARRAY or T_OPEN_SHORT_ARRAY stack pointer, + * it will tokenize the values / key/value pairs contained in the array call. + * + * @since 0.11.0 + * + * @param int $stackPtr The position of the function call token. + * + * @return array Multi-dimentional array with parameter details or + * empty array if no parameters are found. + * + * @type int $position 1-based index position of the parameter. { + * @type int $start Stack pointer for the start of the parameter. + * @type int $end Stack pointer for the end of parameter. + * @type int $raw Trimmed raw parameter content. + * } + */ + public function get_function_call_parameters( $stackPtr ) { + if ( false === $this->does_function_call_have_parameters( $stackPtr ) ) { + return array(); + } + + /* + * Ok, we know we have a T_STRING, T_ARRAY or T_OPEN_SHORT_ARRAY with parameters + * and valid open & close brackets/parenthesis. + */ + + // Mark the beginning and end tokens. + if ( 'T_OPEN_SHORT_ARRAY' === $this->tokens[ $stackPtr ]['type'] ) { + $opener = $stackPtr; + $closer = $this->tokens[ $stackPtr ]['bracket_closer']; + + $nestedParenthesisCount = 0; + } else { + $opener = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true, null, true ); + $closer = $this->tokens[ $opener ]['parenthesis_closer']; + + $nestedParenthesisCount = 1; + } + + // Which nesting level is the one we are interested in ? + if ( isset( $this->tokens[ $opener ]['nested_parenthesis'] ) ) { + $nestedParenthesisCount += \count( $this->tokens[ $opener ]['nested_parenthesis'] ); + } + + $parameters = array(); + $next_comma = $opener; + $param_start = ( $opener + 1 ); + $cnt = 1; + while ( $next_comma = $this->phpcsFile->findNext( array( \T_COMMA, $this->tokens[ $closer ]['code'], \T_OPEN_SHORT_ARRAY, \T_CLOSURE ), ( $next_comma + 1 ), ( $closer + 1 ) ) ) { + // Ignore anything within short array definition brackets. + if ( 'T_OPEN_SHORT_ARRAY' === $this->tokens[ $next_comma ]['type'] + && ( isset( $this->tokens[ $next_comma ]['bracket_opener'] ) + && $this->tokens[ $next_comma ]['bracket_opener'] === $next_comma ) + && isset( $this->tokens[ $next_comma ]['bracket_closer'] ) + ) { + // Skip forward to the end of the short array definition. + $next_comma = $this->tokens[ $next_comma ]['bracket_closer']; + continue; + } + + // Skip past closures passed as function parameters. + if ( 'T_CLOSURE' === $this->tokens[ $next_comma ]['type'] + && ( isset( $this->tokens[ $next_comma ]['scope_condition'] ) + && $this->tokens[ $next_comma ]['scope_condition'] === $next_comma ) + && isset( $this->tokens[ $next_comma ]['scope_closer'] ) + ) { + // Skip forward to the end of the closure declaration. + $next_comma = $this->tokens[ $next_comma ]['scope_closer']; + continue; + } + + // Ignore comma's at a lower nesting level. + if ( \T_COMMA === $this->tokens[ $next_comma ]['code'] + && isset( $this->tokens[ $next_comma ]['nested_parenthesis'] ) + && \count( $this->tokens[ $next_comma ]['nested_parenthesis'] ) !== $nestedParenthesisCount + ) { + continue; + } + + // Ignore closing parenthesis/bracket if not 'ours'. + if ( $this->tokens[ $next_comma ]['type'] === $this->tokens[ $closer ]['type'] && $next_comma !== $closer ) { + continue; + } + + // Ok, we've reached the end of the parameter. + $parameters[ $cnt ]['start'] = $param_start; + $parameters[ $cnt ]['end'] = ( $next_comma - 1 ); + $parameters[ $cnt ]['raw'] = trim( $this->phpcsFile->getTokensAsString( $param_start, ( $next_comma - $param_start ) ) ); + + /* + * Check if there are more tokens before the closing parenthesis. + * Prevents code like the following from setting a third parameter: + * functionCall( $param1, $param2, ); + */ + $has_next_param = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $next_comma + 1 ), $closer, true, null, true ); + if ( false === $has_next_param ) { + break; + } + + // Prepare for the next parameter. + $param_start = ( $next_comma + 1 ); + $cnt++; + } + + return $parameters; + } + + /** + * Get information on a specific parameter passed to a function call. + * + * Expects to be passed the T_STRING stack pointer for the function call. + * If passed a T_STRING which is *not* a function call, the behaviour is unreliable. + * + * Will return a array with the start token pointer, end token pointer and the raw value + * of the parameter at a specific offset. + * If the specified parameter is not found, will return false. + * + * @since 0.11.0 + * + * @param int $stackPtr The position of the function call token. + * @param int $param_offset The 1-based index position of the parameter to retrieve. + * + * @return array|false + */ + public function get_function_call_parameter( $stackPtr, $param_offset ) { + $parameters = $this->get_function_call_parameters( $stackPtr ); + + if ( false === isset( $parameters[ $param_offset ] ) ) { + return false; + } + + return $parameters[ $param_offset ]; + } + + /** + * Find the array opener & closer based on a T_ARRAY or T_OPEN_SHORT_ARRAY token. + * + * @since 0.12.0 + * + * @param int $stackPtr The stack pointer to the array token. + * + * @return array|bool Array with two keys `opener`, `closer` or false if + * either or these could not be determined. + */ + protected function find_array_open_close( $stackPtr ) { + /* + * Determine the array opener & closer. + */ + if ( \T_ARRAY === $this->tokens[ $stackPtr ]['code'] ) { + if ( isset( $this->tokens[ $stackPtr ]['parenthesis_opener'] ) ) { + $opener = $this->tokens[ $stackPtr ]['parenthesis_opener']; + + if ( isset( $this->tokens[ $opener ]['parenthesis_closer'] ) ) { + $closer = $this->tokens[ $opener ]['parenthesis_closer']; + } + } + } else { + // Short array syntax. + $opener = $stackPtr; + $closer = $this->tokens[ $stackPtr ]['bracket_closer']; + } + + if ( isset( $opener, $closer ) ) { + return array( + 'opener' => $opener, + 'closer' => $closer, + ); + } + + return false; + } + + /** + * Find the list opener & closer based on a T_LIST or T_OPEN_SHORT_ARRAY token. + * + * @since 2.2.0 + * + * @param int $stackPtr The stack pointer to the array token. + * + * @return array|bool Array with two keys `opener`, `closer` or false if + * not a (short) list token or if either or these + * could not be determined. + */ + protected function find_list_open_close( $stackPtr ) { + /* + * Determine the list opener & closer. + */ + if ( \T_LIST === $this->tokens[ $stackPtr ]['code'] ) { + // PHPCS 3.5.0. + if ( isset( $this->tokens[ $stackPtr ]['parenthesis_opener'] ) ) { + $opener = $this->tokens[ $stackPtr ]['parenthesis_opener']; + + } else { + // PHPCS < 3.5.0. + $next_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + if ( false !== $next_non_empty + && \T_OPEN_PARENTHESIS === $this->tokens[ $next_non_empty ]['code'] + ) { + $opener = $next_non_empty; + } + } + + if ( isset( $opener, $this->tokens[ $opener ]['parenthesis_closer'] ) ) { + $closer = $this->tokens[ $opener ]['parenthesis_closer']; + } + } + + if ( \T_OPEN_SHORT_ARRAY === $this->tokens[ $stackPtr ]['code'] + && $this->is_short_list( $stackPtr ) === true + ) { + $opener = $stackPtr; + $closer = $this->tokens[ $stackPtr ]['bracket_closer']; + } + + if ( isset( $opener, $closer ) ) { + return array( + 'opener' => $opener, + 'closer' => $closer, + ); + } + + return false; + } + + /** + * Determine the namespace name an arbitrary token lives in. + * + * @since 0.10.0 + * @since 0.12.0 Moved from the `AbstractClassRestrictionsSniff` to this class. + * + * @param int $stackPtr The token position for which to determine the namespace. + * + * @return string Namespace name or empty string if it couldn't be determined or no namespace applies. + */ + public function determine_namespace( $stackPtr ) { + + // Check for the existence of the token. + if ( ! isset( $this->tokens[ $stackPtr ] ) ) { + return ''; + } + + // Check for scoped namespace {}. + if ( ! empty( $this->tokens[ $stackPtr ]['conditions'] ) ) { + $namespacePtr = $this->phpcsFile->getCondition( $stackPtr, \T_NAMESPACE ); + if ( false !== $namespacePtr ) { + $namespace = $this->get_declared_namespace_name( $namespacePtr ); + if ( false !== $namespace ) { + return $namespace; + } + + // We are in a scoped namespace, but couldn't determine the name. + // Searching for a global namespace is futile. + return ''; + } + } + + /* + * Not in a scoped namespace, so let's see if we can find a non-scoped namespace instead. + * Keeping in mind that: + * - there can be multiple non-scoped namespaces in a file (bad practice, but it happens). + * - the namespace keyword can also be used as part of a function/method call and such. + * - that a non-named namespace resolves to the global namespace. + */ + $previousNSToken = $stackPtr; + $namespace = false; + do { + $previousNSToken = $this->phpcsFile->findPrevious( \T_NAMESPACE, ( $previousNSToken - 1 ) ); + + // Stop if we encounter a scoped namespace declaration as we already know we're not in one. + if ( ! empty( $this->tokens[ $previousNSToken ]['scope_condition'] ) + && $this->tokens[ $previousNSToken ]['scope_condition'] === $previousNSToken + ) { + break; + } + + $namespace = $this->get_declared_namespace_name( $previousNSToken ); + + } while ( false === $namespace && false !== $previousNSToken ); + + // If we still haven't got a namespace, return an empty string. + if ( false === $namespace ) { + return ''; + } + + return $namespace; + } + + /** + * Get the complete namespace name for a namespace declaration. + * + * For hierarchical namespaces, the name will be composed of several tokens, + * i.e. MyProject\Sub\Level which will be returned together as one string. + * + * @since 0.12.0 A lesser variant of this method previously existed in the + * `AbstractClassRestrictionsSniff` class. + * + * @param int|bool $stackPtr The position of a T_NAMESPACE token. + * + * @return string|false Namespace name or false if not a namespace declaration. + * Namespace name can be an empty string for global namespace declaration. + */ + public function get_declared_namespace_name( $stackPtr ) { + + // Check for the existence of the token. + if ( false === $stackPtr || ! isset( $this->tokens[ $stackPtr ] ) ) { + return false; + } + + if ( \T_NAMESPACE !== $this->tokens[ $stackPtr ]['code'] ) { + return false; + } + + $nextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true, null, true ); + if ( \T_NS_SEPARATOR === $this->tokens[ $nextToken ]['code'] ) { + // Not a namespace declaration, but use of, i.e. `namespace\someFunction();`. + return false; + } + + if ( \T_OPEN_CURLY_BRACKET === $this->tokens[ $nextToken ]['code'] ) { + // Declaration for global namespace when using multiple namespaces in a file. + // I.e.: `namespace {}`. + return ''; + } + + // Ok, this should be a namespace declaration, so get all the parts together. + $acceptedTokens = array( + \T_STRING => true, + \T_NS_SEPARATOR => true, + ); + $validTokens = $acceptedTokens + Tokens::$emptyTokens; + + $namespaceName = ''; + while ( isset( $validTokens[ $this->tokens[ $nextToken ]['code'] ] ) ) { + if ( isset( $acceptedTokens[ $this->tokens[ $nextToken ]['code'] ] ) ) { + $namespaceName .= trim( $this->tokens[ $nextToken ]['content'] ); + } + ++$nextToken; + } + + return $namespaceName; + } + + /** + * Check whether a T_CONST token is a class constant declaration. + * + * @since 0.14.0 + * + * @param int $stackPtr The position in the stack of the T_CONST token to verify. + * + * @return bool + */ + public function is_class_constant( $stackPtr ) { + if ( ! isset( $this->tokens[ $stackPtr ] ) || \T_CONST !== $this->tokens[ $stackPtr ]['code'] ) { + return false; + } + + // Note: traits can not declare constants. + $valid_scopes = array( + 'T_CLASS' => true, + 'T_ANON_CLASS' => true, + 'T_INTERFACE' => true, + ); + + return is_int( $this->valid_direct_scope( $stackPtr, $valid_scopes ) ); + } + + /** + * Check whether a T_VARIABLE token is a class property declaration. + * + * @since 0.14.0 + * + * @param int $stackPtr The position in the stack of the T_VARIABLE token to verify. + * + * @return bool + */ + public function is_class_property( $stackPtr ) { + if ( ! isset( $this->tokens[ $stackPtr ] ) || \T_VARIABLE !== $this->tokens[ $stackPtr ]['code'] ) { + return false; + } + + // Note: interfaces can not declare properties. + $valid_scopes = array( + 'T_CLASS' => true, + 'T_ANON_CLASS' => true, + 'T_TRAIT' => true, + ); + + $scopePtr = $this->valid_direct_scope( $stackPtr, $valid_scopes ); + if ( false !== $scopePtr ) { + // Make sure it's not a method parameter. + if ( empty( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) ) { + return true; + } else { + $parenthesis = array_keys( $this->tokens[ $stackPtr ]['nested_parenthesis'] ); + $deepest_open = array_pop( $parenthesis ); + if ( $deepest_open < $scopePtr + || isset( $this->tokens[ $deepest_open ]['parenthesis_owner'] ) === false + || \T_FUNCTION !== $this->tokens[ $this->tokens[ $deepest_open ]['parenthesis_owner'] ]['code'] + ) { + return true; + } + } + } + + return false; + } + + /** + * Check whether the direct wrapping scope of a token is within a limited set of + * acceptable tokens. + * + * Used to check, for instance, if a T_CONST is a class constant. + * + * @since 0.14.0 + * + * @param int $stackPtr The position in the stack of the token to verify. + * @param array $valid_scopes Array of token types. + * Keys should be the token types in string format + * to allow for newer token types. + * Value is irrelevant. + * + * @return int|bool StackPtr to the scope if valid, false otherwise. + */ + protected function valid_direct_scope( $stackPtr, array $valid_scopes ) { + if ( empty( $this->tokens[ $stackPtr ]['conditions'] ) ) { + return false; + } + + /* + * Check only the direct wrapping scope of the token. + */ + $conditions = array_keys( $this->tokens[ $stackPtr ]['conditions'] ); + $ptr = array_pop( $conditions ); + + if ( ! isset( $this->tokens[ $ptr ] ) ) { + return false; + } + + if ( isset( $valid_scopes[ $this->tokens[ $ptr ]['type'] ] ) ) { + return $ptr; + } + + return false; + } + + /** + * Checks whether this is a call to a $wpdb method that we want to sniff. + * + * If available in the child class, the $methodPtr, $i and $end properties are + * automatically set to correspond to the start and end of the method call. + * The $i property is also set if this is not a method call but rather the + * use of a $wpdb property. + * + * @since 0.8.0 + * @since 0.9.0 The return value is now always boolean. The $end and $i member + * vars are automatically updated. + * @since 0.14.0 Moved this method from the `PreparedSQL` sniff to the base WP sniff. + * + * {@internal This method should probably be refactored.}} + * + * @param int $stackPtr The index of the $wpdb variable. + * @param array $target_methods Array of methods. Key(s) should be method name. + * + * @return bool Whether this is a $wpdb method call. + */ + protected function is_wpdb_method_call( $stackPtr, $target_methods ) { + + // Check for wpdb. + if ( ( \T_VARIABLE === $this->tokens[ $stackPtr ]['code'] && '$wpdb' !== $this->tokens[ $stackPtr ]['content'] ) + || ( \T_STRING === $this->tokens[ $stackPtr ]['code'] && 'wpdb' !== $this->tokens[ $stackPtr ]['content'] ) + ) { + return false; + } + + // Check that this is a method call. + $is_object_call = $this->phpcsFile->findNext( + array( \T_OBJECT_OPERATOR, \T_DOUBLE_COLON ), + ( $stackPtr + 1 ), + null, + false, + null, + true + ); + if ( false === $is_object_call ) { + return false; + } + + $methodPtr = $this->phpcsFile->findNext( \T_WHITESPACE, ( $is_object_call + 1 ), null, true, null, true ); + if ( false === $methodPtr ) { + return false; + } + + if ( \T_STRING === $this->tokens[ $methodPtr ]['code'] && property_exists( $this, 'methodPtr' ) ) { + $this->methodPtr = $methodPtr; + } + + // Find the opening parenthesis. + $opening_paren = $this->phpcsFile->findNext( \T_WHITESPACE, ( $methodPtr + 1 ), null, true, null, true ); + + if ( false === $opening_paren ) { + return false; + } + + if ( property_exists( $this, 'i' ) ) { + $this->i = $opening_paren; + } + + if ( \T_OPEN_PARENTHESIS !== $this->tokens[ $opening_paren ]['code'] + || ! isset( $this->tokens[ $opening_paren ]['parenthesis_closer'] ) + ) { + return false; + } + + // Check that this is one of the methods that we are interested in. + if ( ! isset( $target_methods[ $this->tokens[ $methodPtr ]['content'] ] ) ) { + return false; + } + + // Find the end of the first parameter. + $end = $this->phpcsFile->findEndOfStatement( $opening_paren + 1 ); + + if ( \T_COMMA !== $this->tokens[ $end ]['code'] ) { + ++$end; + } + + if ( property_exists( $this, 'end' ) ) { + $this->end = $end; + } + + return true; + } + + /** + * Determine whether an arbitrary T_STRING token is the use of a global constant. + * + * @since 1.0.0 + * + * @param int $stackPtr The position of the function call token. + * + * @return bool + */ + public function is_use_of_global_constant( $stackPtr ) { + // Check for the existence of the token. + if ( ! isset( $this->tokens[ $stackPtr ] ) ) { + return false; + } + + // Is this one of the tokens this function handles ? + if ( \T_STRING !== $this->tokens[ $stackPtr ]['code'] ) { + return false; + } + + $next = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + if ( false !== $next + && ( \T_OPEN_PARENTHESIS === $this->tokens[ $next ]['code'] + || \T_DOUBLE_COLON === $this->tokens[ $next ]['code'] ) + ) { + // Function call or declaration. + return false; + } + + // Array of tokens which if found preceding the $stackPtr indicate that a T_STRING is not a global constant. + $tokens_to_ignore = array( + 'T_NAMESPACE' => true, + 'T_USE' => true, + 'T_CLASS' => true, + 'T_TRAIT' => true, + 'T_INTERFACE' => true, + 'T_EXTENDS' => true, + 'T_IMPLEMENTS' => true, + 'T_NEW' => true, + 'T_FUNCTION' => true, + 'T_DOUBLE_COLON' => true, + 'T_OBJECT_OPERATOR' => true, + 'T_INSTANCEOF' => true, + 'T_INSTEADOF' => true, + 'T_GOTO' => true, + 'T_AS' => true, + 'T_PUBLIC' => true, + 'T_PROTECTED' => true, + 'T_PRIVATE' => true, + ); + + $prev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true ); + if ( false !== $prev + && isset( $tokens_to_ignore[ $this->tokens[ $prev ]['type'] ] ) + ) { + // Not the use of a constant. + return false; + } + + if ( $this->is_token_namespaced( $stackPtr ) === true ) { + // Namespaced constant of the same name. + return false; + } + + if ( false !== $prev + && \T_CONST === $this->tokens[ $prev ]['code'] + && $this->is_class_constant( $prev ) + ) { + // Class constant declaration of the same name. + return false; + } + + /* + * Deal with a number of variations of use statements. + */ + for ( $i = $stackPtr; $i > 0; $i-- ) { + if ( $this->tokens[ $i ]['line'] !== $this->tokens[ $stackPtr ]['line'] ) { + break; + } + } + + $firstOnLine = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $i + 1 ), null, true ); + if ( false !== $firstOnLine && \T_USE === $this->tokens[ $firstOnLine ]['code'] ) { + $nextOnLine = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $firstOnLine + 1 ), null, true ); + if ( false !== $nextOnLine ) { + if ( \T_STRING === $this->tokens[ $nextOnLine ]['code'] + && 'const' === $this->tokens[ $nextOnLine ]['content'] + ) { + $hasNsSep = $this->phpcsFile->findNext( \T_NS_SEPARATOR, ( $nextOnLine + 1 ), $stackPtr ); + if ( false !== $hasNsSep ) { + // Namespaced const (group) use statement. + return false; + } + } else { + // Not a const use statement. + return false; + } + } + } + + return true; + } + + /** + * Determine if a variable is in the `as $key => $value` part of a foreach condition. + * + * @since 1.0.0 + * @since 1.1.0 Moved from the PrefixAllGlobals sniff to the Sniff base class. + * + * @param int $stackPtr Pointer to the variable. + * + * @return bool True if it is. False otherwise. + */ + protected function is_foreach_as( $stackPtr ) { + if ( ! isset( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) ) { + return false; + } + + $nested_parenthesis = $this->tokens[ $stackPtr ]['nested_parenthesis']; + $close_parenthesis = end( $nested_parenthesis ); + $open_parenthesis = key( $nested_parenthesis ); + if ( ! isset( $this->tokens[ $close_parenthesis ]['parenthesis_owner'] ) ) { + return false; + } + + if ( \T_FOREACH !== $this->tokens[ $this->tokens[ $close_parenthesis ]['parenthesis_owner'] ]['code'] ) { + return false; + } + + $as_ptr = $this->phpcsFile->findNext( \T_AS, ( $open_parenthesis + 1 ), $close_parenthesis ); + if ( false === $as_ptr ) { + // Should never happen. + return false; + } + + return ( $stackPtr > $as_ptr ); + } + + /** + * Determine whether a T_OPEN/CLOSE_SHORT_ARRAY token is a short list() construct. + * + * @internal This function will be introduced in PHPCS upstream in version 3.5.0 + * and can be removed from WPCS once WPCS raises the minimum version. + * + * @since 2.2.0 + * + * @param int $stackPtr The position of the array bracket token. + * + * @return bool True if the token passed is the open/close bracket of a short list. + * False if the token is a short array bracket or not + * a T_OPEN/CLOSE_SHORT_ARRAY token. + */ + protected function is_short_list( $stackPtr ) { + // Is this one of the tokens this function handles ? + if ( \T_OPEN_SHORT_ARRAY !== $this->tokens[ $stackPtr ]['code'] + && \T_CLOSE_SHORT_ARRAY !== $this->tokens[ $stackPtr ]['code'] + ) { + return false; + } + + switch ( $this->tokens[ $stackPtr ]['code'] ) { + case \T_OPEN_SHORT_ARRAY: + $opener = $stackPtr; + $closer = $this->tokens[ $stackPtr ]['bracket_closer']; + break; + + case \T_CLOSE_SHORT_ARRAY: + $opener = $this->tokens[ $stackPtr ]['bracket_opener']; + $closer = $stackPtr; + break; + } + + $nextNonEmpty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $closer + 1 ), null, true, null, true ); + if ( false !== $nextNonEmpty && \T_EQUAL === $this->tokens[ $nextNonEmpty ]['code'] ) { + return true; + } + + // Check for short list in foreach, i.e. `foreach($array as [$a, $b])`. + if ( $this->is_foreach_as( $stackPtr ) === true ) { + return true; + } + + // Maybe this is a short list syntax nested inside another short list syntax ? + $parentOpen = $opener; + do { + $parentOpen = $this->phpcsFile->findPrevious( + \T_OPEN_SHORT_ARRAY, + ( $parentOpen - 1 ), + null, + false, + null, + true + ); + + if ( false === $parentOpen ) { + return false; + } + } while ( $this->tokens[ $parentOpen ]['bracket_closer'] < $opener ); + + return $this->is_short_list( $parentOpen ); + } + + /** + * Get a list of the token pointers to the variables being assigned to in a list statement. + * + * @internal No need to take special measures for nested lists. Nested or not, + * each list part can only contain one variable being written to. + * + * @since 2.2.0 + * + * @param int $stackPtr The position of the T_LIST or T_OPEN_SHORT_ARRAY + * token in the stack. + * @param array $list_open_close Optional. Array containing the token pointers to + * the list opener and closer. + * + * @return array Array with the stack pointers to the variables or an empty + * array when not a (short) list. + */ + protected function get_list_variables( $stackPtr, $list_open_close = array() ) { + if ( \T_LIST !== $this->tokens[ $stackPtr ]['code'] + && \T_OPEN_SHORT_ARRAY !== $this->tokens[ $stackPtr ]['code'] + ) { + return array(); + } + + if ( empty( $list_open_close ) ) { + $list_open_close = $this->find_list_open_close( $stackPtr ); + if ( false === $list_open_close ) { + // Not a (short) list. + return array(); + } + } + + $var_pointers = array(); + $current = $list_open_close['opener']; + $closer = $list_open_close['closer']; + $last = false; + do { + ++$current; + $next_comma = $this->phpcsFile->findNext( \T_COMMA, $current, $closer ); + if ( false === $next_comma ) { + $next_comma = $closer; + $last = true; + } + + // Skip over the "key" part in keyed lists. + $arrow = $this->phpcsFile->findNext( \T_DOUBLE_ARROW, $current, $next_comma ); + if ( false !== $arrow ) { + $current = ( $arrow + 1 ); + } + + /* + * Each list item can only have one variable to which an assignment is being made. + * This can be an array with a (variable) index, but that doesn't matter, we're only + * concerned with the actual variable. + */ + $var = $this->phpcsFile->findNext( \T_VARIABLE, $current, $next_comma ); + if ( false !== $var ) { + // Not an empty list item. + $var_pointers[] = $var; + } + + $current = $next_comma; + + } while ( false === $last ); + + return $var_pointers; + } + + /** + * Check whether a function has been marked as deprecated via a @deprecated tag + * in the function docblock. + * + * {@internal This method is static to allow the ValidFunctionName class to use it.}} + * + * @since 2.2.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of a T_FUNCTION + * token in the stack. + * + * @return bool + */ + public static function is_function_deprecated( File $phpcsFile, $stackPtr ) { + $tokens = $phpcsFile->getTokens(); + $find = Tokens::$methodPrefixes; + $find[] = \T_WHITESPACE; + + $comment_end = $phpcsFile->findPrevious( $find, ( $stackPtr - 1 ), null, true ); + if ( \T_DOC_COMMENT_CLOSE_TAG !== $tokens[ $comment_end ]['code'] ) { + // Function doesn't have a doc comment or is using the wrong type of comment. + return false; + } + + $comment_start = $tokens[ $comment_end ]['comment_opener']; + foreach ( $tokens[ $comment_start ]['comment_tags'] as $tag ) { + if ( '@deprecated' === $tokens[ $tag ]['content'] ) { + return true; + } + } + + return false; + } +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/ArrayDeclarationSpacingSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/ArrayDeclarationSpacingSniff.php new file mode 100644 index 00000000..5488bfd3 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/ArrayDeclarationSpacingSniff.php @@ -0,0 +1,468 @@ + \T_ARRAY, + \T_OPEN_SHORT_ARRAY => \T_OPEN_SHORT_ARRAY, + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 0.12.0 + * + * @return array + */ + public function register() { + return $this->targets; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 0.12.0 The actual checks contained in this method used to + * be in the `processSingleLineArray()` method. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + public function process_token( $stackPtr ) { + + if ( \T_OPEN_SHORT_ARRAY === $this->tokens[ $stackPtr ]['code'] + && $this->is_short_list( $stackPtr ) + ) { + // Short list, not short array. + return; + } + + /* + * Determine the array opener & closer. + */ + $array_open_close = $this->find_array_open_close( $stackPtr ); + if ( false === $array_open_close ) { + // Array open/close could not be determined. + return; + } + + $opener = $array_open_close['opener']; + $closer = $array_open_close['closer']; + unset( $array_open_close ); + + /* + * Long arrays only: Check for space between the array keyword and the open parenthesis. + */ + if ( \T_ARRAY === $this->tokens[ $stackPtr ]['code'] ) { + + if ( ( $stackPtr + 1 ) !== $opener ) { + $error = 'There must be no space between the "array" keyword and the opening parenthesis'; + $error_code = 'SpaceAfterKeyword'; + + $nextNonWhitespace = $this->phpcsFile->findNext( \T_WHITESPACE, ( $stackPtr + 1 ), ( $opener + 1 ), true ); + if ( $nextNonWhitespace !== $opener ) { + // Don't auto-fix: Something other than whitespace found between keyword and open parenthesis. + $this->phpcsFile->addError( $error, $stackPtr, $error_code ); + } else { + + $fix = $this->phpcsFile->addFixableError( $error, $stackPtr, $error_code ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + for ( $i = ( $stackPtr + 1 ); $i < $opener; $i++ ) { + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + $this->phpcsFile->fixer->endChangeset(); + unset( $i ); + } + } + unset( $error, $error_code, $nextNonWhitespace, $fix ); + } + } + + /* + * Check for empty arrays. + */ + $nextNonWhitespace = $this->phpcsFile->findNext( \T_WHITESPACE, ( $opener + 1 ), ( $closer + 1 ), true ); + if ( $nextNonWhitespace === $closer ) { + + if ( ( $opener + 1 ) !== $closer ) { + $fix = $this->phpcsFile->addFixableError( + 'Empty array declaration must have no space between the parentheses', + $stackPtr, + 'SpaceInEmptyArray' + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + for ( $i = ( $opener + 1 ); $i < $closer; $i++ ) { + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + $this->phpcsFile->fixer->endChangeset(); + unset( $i ); + } + } + + // This array is empty, so the below checks aren't necessary. + return; + } + unset( $nextNonWhitespace ); + + // Pass off to either the single line or multi-line array analysis. + if ( $this->tokens[ $opener ]['line'] === $this->tokens[ $closer ]['line'] ) { + $this->process_single_line_array( $stackPtr, $opener, $closer ); + } else { + $this->process_multi_line_array( $stackPtr, $opener, $closer ); + } + } + + /** + * Process a single-line array. + * + * @since 0.13.0 The actual checks contained in this method used to + * be in the `process()` method. + * + * @param int $stackPtr The position of the current token in the stack. + * @param int $opener The position of the array opener. + * @param int $closer The position of the array closer. + * + * @return void + */ + protected function process_single_line_array( $stackPtr, $opener, $closer ) { + /* + * Check that associative arrays are always multi-line. + */ + $array_has_keys = $this->phpcsFile->findNext( \T_DOUBLE_ARROW, $opener, $closer ); + if ( false !== $array_has_keys ) { + + $array_items = $this->get_function_call_parameters( $stackPtr ); + + if ( ( false === $this->allow_single_item_single_line_associative_arrays + && ! empty( $array_items ) ) + || ( true === $this->allow_single_item_single_line_associative_arrays + && \count( $array_items ) > 1 ) + ) { + /* + * Make sure the double arrow is for *this* array, not for a nested one. + */ + $array_has_keys = false; // Reset before doing more detailed check. + foreach ( $array_items as $item ) { + for ( $ptr = $item['start']; $ptr <= $item['end']; $ptr++ ) { + if ( \T_DOUBLE_ARROW === $this->tokens[ $ptr ]['code'] ) { + $array_has_keys = true; + break 2; + } + + // Skip passed any nested arrays. + if ( isset( $this->targets[ $this->tokens[ $ptr ]['code'] ] ) ) { + $nested_array_open_close = $this->find_array_open_close( $ptr ); + if ( false === $nested_array_open_close ) { + // Nested array open/close could not be determined. + continue; + } + + $ptr = $nested_array_open_close['closer']; + } + } + } + + if ( true === $array_has_keys ) { + + $phrase = 'an'; + if ( true === $this->allow_single_item_single_line_associative_arrays ) { + $phrase = 'a multi-item'; + } + $fix = $this->phpcsFile->addFixableError( + 'When %s array uses associative keys, each value should start on a new line.', + $closer, + 'AssociativeArrayFound', + array( $phrase ) + ); + + if ( true === $fix ) { + + $this->phpcsFile->fixer->beginChangeset(); + + foreach ( $array_items as $item ) { + /* + * Add a line break before the first non-empty token in the array item. + * Prevents extraneous whitespace at the start of the line which could be + * interpreted as alignment whitespace. + */ + $first_non_empty = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + $item['start'], + ( $item['end'] + 1 ), + true + ); + if ( false === $first_non_empty ) { + continue; + } + + if ( $item['start'] <= ( $first_non_empty - 1 ) + && \T_WHITESPACE === $this->tokens[ ( $first_non_empty - 1 ) ]['code'] + ) { + // Remove whitespace which would otherwise becoming trailing + // (as it gives problems with the fixed file). + $this->phpcsFile->fixer->replaceToken( ( $first_non_empty - 1 ), '' ); + } + + $this->phpcsFile->fixer->addNewlineBefore( $first_non_empty ); + } + + $this->phpcsFile->fixer->endChangeset(); + } + + // No need to check for spacing around opener/closer as this array should be multi-line. + return; + } + } + } + + /* + * Check that there is a single space after the array opener and before the array closer. + */ + if ( \T_WHITESPACE !== $this->tokens[ ( $opener + 1 ) ]['code'] ) { + + $fix = $this->phpcsFile->addFixableError( + 'Missing space after array opener.', + $opener, + 'NoSpaceAfterArrayOpener' + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->addContent( $opener, ' ' ); + } + } elseif ( ' ' !== $this->tokens[ ( $opener + 1 ) ]['content'] ) { + + $fix = $this->phpcsFile->addFixableError( + 'Expected 1 space after array opener, found %s.', + $opener, + 'SpaceAfterArrayOpener', + array( \strlen( $this->tokens[ ( $opener + 1 ) ]['content'] ) ) + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->replaceToken( ( $opener + 1 ), ' ' ); + } + } + + if ( \T_WHITESPACE !== $this->tokens[ ( $closer - 1 ) ]['code'] ) { + + $fix = $this->phpcsFile->addFixableError( + 'Missing space before array closer.', + $closer, + 'NoSpaceBeforeArrayCloser' + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->addContentBefore( $closer, ' ' ); + } + } elseif ( ' ' !== $this->tokens[ ( $closer - 1 ) ]['content'] ) { + + $fix = $this->phpcsFile->addFixableError( + 'Expected 1 space before array closer, found %s.', + $closer, + 'SpaceBeforeArrayCloser', + array( \strlen( $this->tokens[ ( $closer - 1 ) ]['content'] ) ) + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->replaceToken( ( $closer - 1 ), ' ' ); + } + } + } + + /** + * Process a multi-line array. + * + * @since 0.13.0 The actual checks contained in this method used to + * be in the `ArrayDeclaration` sniff. + * + * @param int $stackPtr The position of the current token in the stack. + * @param int $opener The position of the array opener. + * @param int $closer The position of the array closer. + * + * @return void + */ + protected function process_multi_line_array( $stackPtr, $opener, $closer ) { + /* + * Check that the closing bracket is on a new line. + */ + $last_content = $this->phpcsFile->findPrevious( \T_WHITESPACE, ( $closer - 1 ), $opener, true ); + if ( false !== $last_content + && $this->tokens[ $last_content ]['line'] === $this->tokens[ $closer ]['line'] + ) { + $fix = $this->phpcsFile->addFixableError( + 'Closing parenthesis of array declaration must be on a new line', + $closer, + 'CloseBraceNewLine' + ); + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + + if ( $last_content < ( $closer - 1 ) + && \T_WHITESPACE === $this->tokens[ ( $closer - 1 ) ]['code'] + ) { + // Remove whitespace which would otherwise becoming trailing + // (as it gives problems with the fixed file). + $this->phpcsFile->fixer->replaceToken( ( $closer - 1 ), '' ); + } + + $this->phpcsFile->fixer->addNewlineBefore( $closer ); + $this->phpcsFile->fixer->endChangeset(); + } + } + + /* + * Check that each array item starts on a new line. + */ + $array_items = $this->get_function_call_parameters( $stackPtr ); + $end_of_last_item = $opener; + + foreach ( $array_items as $item ) { + $end_of_this_item = ( $item['end'] + 1 ); + + // Find the line on which the item starts. + $first_content = $this->phpcsFile->findNext( + array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE ), + $item['start'], + $end_of_this_item, + true + ); + + // Ignore comments after array items if the next real content starts on a new line. + if ( $this->tokens[ $first_content ]['line'] === $this->tokens[ $end_of_last_item ]['line'] + && ( \T_COMMENT === $this->tokens[ $first_content ]['code'] + || isset( Tokens::$phpcsCommentTokens[ $this->tokens[ $first_content ]['code'] ] ) ) + ) { + $end_of_comment = $first_content; + + // Find the end of (multi-line) /* */- style trailing comments. + if ( substr( ltrim( $this->tokens[ $end_of_comment ]['content'] ), 0, 2 ) === '/*' ) { + while ( ( \T_COMMENT === $this->tokens[ $end_of_comment ]['code'] + || isset( Tokens::$phpcsCommentTokens[ $this->tokens[ $end_of_comment ]['code'] ] ) ) + && substr( rtrim( $this->tokens[ $end_of_comment ]['content'] ), -2 ) !== '*/' + && ( $end_of_comment + 1 ) < $end_of_this_item + ) { + $end_of_comment++; + } + + if ( $this->tokens[ $end_of_comment ]['line'] !== $this->tokens[ $end_of_last_item ]['line'] ) { + // Multi-line trailing comment. + $end_of_last_item = $end_of_comment; + } + } + + $next = $this->phpcsFile->findNext( + array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE ), + ( $end_of_comment + 1 ), + $end_of_this_item, + true + ); + + if ( false === $next ) { + // Shouldn't happen, but just in case. + $end_of_last_item = $end_of_this_item; + continue; + } + + if ( $this->tokens[ $next ]['line'] !== $this->tokens[ $first_content ]['line'] ) { + $first_content = $next; + } + } + + if ( false === $first_content ) { + // Shouldn't happen, but just in case. + $end_of_last_item = $end_of_this_item; + continue; + } + + if ( $this->tokens[ $end_of_last_item ]['line'] === $this->tokens[ $first_content ]['line'] ) { + + $fix = $this->phpcsFile->addFixableError( + 'Each item in a multi-line array must be on a new line', + $first_content, + 'ArrayItemNoNewLine' + ); + + if ( true === $fix ) { + + $this->phpcsFile->fixer->beginChangeset(); + + if ( ( $end_of_last_item + 1 ) <= ( $first_content - 1 ) + && \T_WHITESPACE === $this->tokens[ ( $first_content - 1 ) ]['code'] + ) { + // Remove whitespace which would otherwise becoming trailing + // (as it gives problems with the fixed file). + $this->phpcsFile->fixer->replaceToken( ( $first_content - 1 ), '' ); + } + + $this->phpcsFile->fixer->addNewlineBefore( $first_content ); + $this->phpcsFile->fixer->endChangeset(); + } + } + + $end_of_last_item = $end_of_this_item; + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/ArrayIndentationSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/ArrayIndentationSniff.php new file mode 100644 index 00000000..c0b5dcf5 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/ArrayIndentationSniff.php @@ -0,0 +1,542 @@ +ignore_tokens = Tokens::$heredocTokens; + unset( $this->ignore_tokens[ \T_START_HEREDOC ], $this->ignore_tokens[ \T_START_NOWDOC ] ); + $this->ignore_tokens[ \T_INLINE_HTML ] = \T_INLINE_HTML; + + return array( + \T_ARRAY, + \T_OPEN_SHORT_ARRAY, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + if ( ! isset( $this->tab_width ) ) { + $this->tab_width = PHPCSHelper::get_tab_width( $this->phpcsFile ); + } + + if ( \T_OPEN_SHORT_ARRAY === $this->tokens[ $stackPtr ]['code'] + && $this->is_short_list( $stackPtr ) + ) { + // Short list, not short array. + return; + } + + /* + * Determine the array opener & closer. + */ + $array_open_close = $this->find_array_open_close( $stackPtr ); + if ( false === $array_open_close ) { + // Array open/close could not be determined. + return; + } + + $opener = $array_open_close['opener']; + $closer = $array_open_close['closer']; + + if ( $this->tokens[ $opener ]['line'] === $this->tokens[ $closer ]['line'] ) { + // Not interested in single line arrays. + return; + } + + /* + * Check the closing bracket is lined up with the start of the content on the line + * containing the array opener. + */ + $opener_line_spaces = $this->get_indentation_size( $opener ); + $closer_line_spaces = ( $this->tokens[ $closer ]['column'] - 1 ); + + if ( $closer_line_spaces !== $opener_line_spaces ) { + $error = 'Array closer not aligned correctly; expected %s space(s) but found %s'; + $error_code = 'CloseBraceNotAligned'; + + /* + * Report & fix the issue if the close brace is on its own line with + * nothing or only indentation whitespace before it. + */ + if ( 0 === $closer_line_spaces + || ( \T_WHITESPACE === $this->tokens[ ( $closer - 1 ) ]['code'] + && 1 === $this->tokens[ ( $closer - 1 ) ]['column'] ) + ) { + $this->add_array_alignment_error( + $closer, + $error, + $error_code, + $opener_line_spaces, + $closer_line_spaces, + $this->get_indentation_string( $opener_line_spaces ) + ); + } else { + /* + * Otherwise, only report the error, don't try and fix it (yet). + * + * It will get corrected in a future loop of the fixer once the closer + * has been moved to its own line by the `ArrayDeclarationSpacing` sniff. + */ + $this->phpcsFile->addError( + $error, + $closer, + $error_code, + array( $opener_line_spaces, $closer_line_spaces ) + ); + } + + unset( $error, $error_code ); + } + + /* + * Verify & correct the array item indentation. + */ + $array_items = $this->get_function_call_parameters( $stackPtr ); + if ( empty( $array_items ) ) { + // Strange, no array items found. + return; + } + + $expected_spaces = ( $opener_line_spaces + $this->tab_width ); + $expected_indent = $this->get_indentation_string( $expected_spaces ); + $end_of_previous_item = $opener; + + foreach ( $array_items as $item ) { + $end_of_this_item = ( $item['end'] + 1 ); + + // Find the line on which the item starts. + $first_content = $this->phpcsFile->findNext( + array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE ), + $item['start'], + $end_of_this_item, + true + ); + + // Deal with trailing comments. + if ( false !== $first_content + && \T_COMMENT === $this->tokens[ $first_content ]['code'] + && $this->tokens[ $first_content ]['line'] === $this->tokens[ $end_of_previous_item ]['line'] + ) { + $first_content = $this->phpcsFile->findNext( + array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE, \T_COMMENT ), + ( $first_content + 1 ), + $end_of_this_item, + true + ); + } + + if ( false === $first_content ) { + $end_of_previous_item = $end_of_this_item; + continue; + } + + // Bow out from reporting and fixing mixed multi-line/single-line arrays. + // That is handled by the ArrayDeclarationSpacingSniff. + if ( $this->tokens[ $first_content ]['line'] === $this->tokens[ $end_of_previous_item ]['line'] + || ( 1 !== $this->tokens[ $first_content ]['column'] + && \T_WHITESPACE !== $this->tokens[ ( $first_content - 1 ) ]['code'] ) + ) { + return $closer; + } + + $found_spaces = ( $this->tokens[ $first_content ]['column'] - 1 ); + + if ( $found_spaces !== $expected_spaces ) { + $this->add_array_alignment_error( + $first_content, + 'Array item not aligned correctly; expected %s spaces but found %s', + 'ItemNotAligned', + $expected_spaces, + $found_spaces, + $expected_indent + ); + } + + // No need for further checking if this is a one-line array item. + if ( $this->tokens[ $first_content ]['line'] === $this->tokens[ $item['end'] ]['line'] ) { + $end_of_previous_item = $end_of_this_item; + continue; + } + + /* + * Multi-line array items. + * + * Verify & if needed, correct the indentation of subsequent lines. + * Subsequent lines may be indented more or less than the mimimum expected indent, + * but the "first line after" should be indented - at least - as much as the very first line + * of the array item. + * Indentation correction for subsequent lines will be based on that diff. + */ + + // Find first token on second line of the array item. + // If the second line is a heredoc/nowdoc, continue on until we find a line with a different token. + // Same for the second line of a multi-line text string. + for ( $ptr = ( $first_content + 1 ); $ptr <= $item['end']; $ptr++ ) { + if ( $this->tokens[ $first_content ]['line'] !== $this->tokens[ $ptr ]['line'] + && 1 === $this->tokens[ $ptr ]['column'] + && false === $this->ignore_token( $ptr ) + ) { + break; + } + } + + $first_content_on_line2 = $this->phpcsFile->findNext( + array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE ), + $ptr, + $end_of_this_item, + true + ); + + if ( false === $first_content_on_line2 ) { + /* + * Apparently there were only tokens in the ignore list on subsequent lines. + * + * In that case, the comma after the array item might be on a line by itself, + * so check its placement. + */ + if ( $this->tokens[ $item['end'] ]['line'] !== $this->tokens[ $end_of_this_item ]['line'] + && \T_COMMA === $this->tokens[ $end_of_this_item ]['code'] + && ( $this->tokens[ $end_of_this_item ]['column'] - 1 ) !== $expected_spaces + ) { + $this->add_array_alignment_error( + $end_of_this_item, + 'Comma after multi-line array item not aligned correctly; expected %s spaces, but found %s', + 'MultiLineArrayItemCommaNotAligned', + $expected_spaces, + ( $this->tokens[ $end_of_this_item ]['column'] - 1 ), + $expected_indent + ); + } + + $end_of_previous_item = $end_of_this_item; + continue; + } + + $found_spaces_on_line2 = $this->get_indentation_size( $first_content_on_line2 ); + $expected_spaces_on_line2 = $expected_spaces; + + if ( $found_spaces < $found_spaces_on_line2 ) { + $expected_spaces_on_line2 += ( $found_spaces_on_line2 - $found_spaces ); + } + + if ( $found_spaces_on_line2 !== $expected_spaces_on_line2 ) { + + $fix = $this->phpcsFile->addFixableError( + 'Multi-line array item not aligned correctly; expected %s spaces, but found %s', + $first_content_on_line2, + 'MultiLineArrayItemNotAligned', + array( + $expected_spaces_on_line2, + $found_spaces_on_line2, + ) + ); + + if ( true === $fix ) { + $expected_indent_on_line2 = $this->get_indentation_string( $expected_spaces_on_line2 ); + + $this->phpcsFile->fixer->beginChangeset(); + + // Fix second line for the array item. + if ( 1 === $this->tokens[ $first_content_on_line2 ]['column'] + && \T_COMMENT === $this->tokens[ $first_content_on_line2 ]['code'] + ) { + $actual_comment = ltrim( $this->tokens[ $first_content_on_line2 ]['content'] ); + $replacement = $expected_indent_on_line2 . $actual_comment; + + $this->phpcsFile->fixer->replaceToken( $first_content_on_line2, $replacement ); + + } else { + $this->fix_alignment_error( $first_content_on_line2, $expected_indent_on_line2 ); + } + + // Fix subsequent lines. + for ( $i = ( $first_content_on_line2 + 1 ); $i <= $item['end']; $i++ ) { + // We're only interested in the first token on each line. + if ( 1 !== $this->tokens[ $i ]['column'] ) { + if ( $this->tokens[ $i ]['line'] === $this->tokens[ $item['end'] ]['line'] ) { + // We might as well quit if we're past the first token on the last line. + break; + } + continue; + } + + $first_content_on_line = $this->phpcsFile->findNext( + array( \T_WHITESPACE, \T_DOC_COMMENT_WHITESPACE ), + $i, + $end_of_this_item, + true + ); + + if ( false === $first_content_on_line ) { + break; + } + + // Ignore lines with heredoc and nowdoc tokens and subsequent lines in multi-line strings. + if ( true === $this->ignore_token( $first_content_on_line ) ) { + $i = $first_content_on_line; + continue; + } + + $found_spaces_on_line = $this->get_indentation_size( $first_content_on_line ); + $expected_spaces_on_line = ( $expected_spaces_on_line2 + ( $found_spaces_on_line - $found_spaces_on_line2 ) ); + $expected_spaces_on_line = max( $expected_spaces_on_line, 0 ); // Can't be below 0. + $expected_indent_on_line = $this->get_indentation_string( $expected_spaces_on_line ); + + if ( $found_spaces_on_line !== $expected_spaces_on_line ) { + if ( 1 === $this->tokens[ $first_content_on_line ]['column'] + && \T_COMMENT === $this->tokens[ $first_content_on_line ]['code'] + ) { + $actual_comment = ltrim( $this->tokens[ $first_content_on_line ]['content'] ); + $replacement = $expected_indent_on_line . $actual_comment; + + $this->phpcsFile->fixer->replaceToken( $first_content_on_line, $replacement ); + } else { + $this->fix_alignment_error( $first_content_on_line, $expected_indent_on_line ); + } + } + + // Move past any potential empty lines between the previous non-empty line and this one. + // No need to do the fixes twice. + $i = $first_content_on_line; + } + + /* + * Check the placement of the comma after the array item as it might be on a line by itself. + */ + if ( $this->tokens[ $item['end'] ]['line'] !== $this->tokens[ $end_of_this_item ]['line'] + && \T_COMMA === $this->tokens[ $end_of_this_item ]['code'] + && ( $this->tokens[ $end_of_this_item ]['column'] - 1 ) !== $expected_spaces + ) { + $this->add_array_alignment_error( + $end_of_this_item, + 'Comma after array item not aligned correctly; expected %s spaces, but found %s', + 'MultiLineArrayItemCommaNotAligned', + $expected_spaces, + ( $this->tokens[ $end_of_this_item ]['column'] - 1 ), + $expected_indent + ); + } + + $this->phpcsFile->fixer->endChangeset(); + } + } + + $end_of_previous_item = $end_of_this_item; + } + } + + /** + * Should the token be ignored ? + * + * This method is only intended to be used with the first token on a line + * for subsequent lines in an multi-line array item. + * + * @param int $ptr Stack pointer to the first token on a line. + * + * @return bool + */ + protected function ignore_token( $ptr ) { + $token_code = $this->tokens[ $ptr ]['code']; + + if ( isset( $this->ignore_tokens[ $token_code ] ) ) { + return true; + } + + /* + * If it's a subsequent line of a multi-line sting, it will not start with a quote + * character, nor just *be* a quote character. + */ + if ( \T_CONSTANT_ENCAPSED_STRING === $token_code + || \T_DOUBLE_QUOTED_STRING === $token_code + ) { + // Deal with closing quote of a multi-line string being on its own line. + if ( "'" === $this->tokens[ $ptr ]['content'] + || '"' === $this->tokens[ $ptr ]['content'] + ) { + return true; + } + + // Deal with subsequent lines of a multi-line string where the token is broken up per line. + if ( "'" !== $this->tokens[ $ptr ]['content'][0] + && '"' !== $this->tokens[ $ptr ]['content'][0] + ) { + return true; + } + } + + return false; + } + + /** + * Determine the line indentation whitespace. + * + * @param int $ptr Stack pointer to an arbitrary token on a line. + * + * @return int Nr of spaces found. Where necessary, tabs are translated to spaces. + */ + protected function get_indentation_size( $ptr ) { + + // Find the first token on the line. + for ( ; $ptr >= 0; $ptr-- ) { + if ( 1 === $this->tokens[ $ptr ]['column'] ) { + break; + } + } + + $whitespace = ''; + + if ( \T_WHITESPACE === $this->tokens[ $ptr ]['code'] + || \T_DOC_COMMENT_WHITESPACE === $this->tokens[ $ptr ]['code'] + ) { + return $this->tokens[ $ptr ]['length']; + } + + /* + * Special case for multi-line, non-docblock comments. + * Only applicable for subsequent lines in an array item. + * + * First/Single line is tokenized as T_WHITESPACE + T_COMMENT + * Subsequent lines are tokenized as T_COMMENT including the indentation whitespace. + */ + if ( \T_COMMENT === $this->tokens[ $ptr ]['code'] ) { + $content = $this->tokens[ $ptr ]['content']; + $actual_comment = ltrim( $content ); + $whitespace = str_replace( $actual_comment, '', $content ); + } + + return \strlen( $whitespace ); + } + + /** + * Create an indentation string. + * + * @param int $nr Number of spaces the indentation should be. + * + * @return string + */ + protected function get_indentation_string( $nr ) { + if ( 0 >= $nr ) { + return ''; + } + + // Space-based indentation. + if ( false === $this->tabIndent ) { + return str_repeat( ' ', $nr ); + } + + // Tab-based indentation. + $num_tabs = (int) floor( $nr / $this->tab_width ); + $remaining = ( $nr % $this->tab_width ); + $tab_indent = str_repeat( "\t", $num_tabs ); + $tab_indent .= str_repeat( ' ', $remaining ); + + return $tab_indent; + } + + /** + * Throw an error and fix incorrect array alignment. + * + * @param int $ptr Stack pointer to the first content on the line. + * @param string $error Error message. + * @param string $error_code Error code. + * @param int $expected Expected nr of spaces (tabs translated to space value). + * @param int $found Found nr of spaces (tabs translated to space value). + * @param string $new_indent Whitespace indent replacement content. + */ + protected function add_array_alignment_error( $ptr, $error, $error_code, $expected, $found, $new_indent ) { + + $fix = $this->phpcsFile->addFixableError( $error, $ptr, $error_code, array( $expected, $found ) ); + if ( true === $fix ) { + $this->fix_alignment_error( $ptr, $new_indent ); + } + } + + /** + * Fix incorrect array alignment. + * + * @param int $ptr Stack pointer to the first content on the line. + * @param string $new_indent Whitespace indent replacement content. + */ + protected function fix_alignment_error( $ptr, $new_indent ) { + if ( 1 === $this->tokens[ $ptr ]['column'] ) { + $this->phpcsFile->fixer->addContentBefore( $ptr, $new_indent ); + } else { + $this->phpcsFile->fixer->replaceToken( ( $ptr - 1 ), $new_indent ); + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/ArrayKeySpacingRestrictionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/ArrayKeySpacingRestrictionsSniff.php new file mode 100644 index 00000000..6b3203cd --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/ArrayKeySpacingRestrictionsSniff.php @@ -0,0 +1,192 @@ +tokens[ $stackPtr ]; + if ( ! isset( $token['bracket_closer'] ) ) { + $this->phpcsFile->addWarning( 'Missing bracket closer.', $stackPtr, 'MissingBracketCloser' ); + return; + } + + $need_spaces = $this->phpcsFile->findNext( + array( \T_CONSTANT_ENCAPSED_STRING, \T_LNUMBER, \T_WHITESPACE, \T_MINUS ), + ( $stackPtr + 1 ), + $token['bracket_closer'], + true + ); + + $spaced1 = ( \T_WHITESPACE === $this->tokens[ ( $stackPtr + 1 ) ]['code'] ); + $spaced2 = ( \T_WHITESPACE === $this->tokens[ ( $token['bracket_closer'] - 1 ) ]['code'] ); + + // It should have spaces unless if it only has strings or numbers as the key. + if ( false !== $need_spaces + && ( false === $spaced1 || false === $spaced2 ) + ) { + $error = 'Array keys must be surrounded by spaces unless they contain a string or an integer.'; + $fix = $this->phpcsFile->addFixableError( $error, $stackPtr, 'NoSpacesAroundArrayKeys' ); + if ( true === $fix ) { + if ( ! $spaced1 ) { + $this->phpcsFile->fixer->addContentBefore( ( $stackPtr + 1 ), ' ' ); + } + if ( ! $spaced2 ) { + $this->phpcsFile->fixer->addContentBefore( $token['bracket_closer'], ' ' ); + } + } + } elseif ( false === $need_spaces && ( $spaced1 || $spaced2 ) ) { + $error = 'Array keys must NOT be surrounded by spaces if they only contain a string or an integer.'; + $fix = $this->phpcsFile->addFixableError( $error, $stackPtr, 'SpacesAroundArrayKeys' ); + if ( true === $fix ) { + if ( $spaced1 ) { + $this->phpcsFile->fixer->beginChangeset(); + $this->phpcsFile->fixer->replaceToken( ( $stackPtr + 1 ), '' ); + + for ( $i = ( $stackPtr + 2 ); $i < $token['bracket_closer']; $i++ ) { + if ( \T_WHITESPACE !== $this->tokens[ $i ]['code'] ) { + break; + } + + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + + $this->phpcsFile->fixer->endChangeset(); + } + if ( $spaced2 ) { + $this->phpcsFile->fixer->beginChangeset(); + $this->phpcsFile->fixer->replaceToken( ( $token['bracket_closer'] - 1 ), '' ); + + for ( $i = ( $token['bracket_closer'] - 2 ); $i > $stackPtr; $i-- ) { + if ( \T_WHITESPACE !== $this->tokens[ $i ]['code'] ) { + break; + } + + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + + $this->phpcsFile->fixer->endChangeset(); + } + } + } + + // If spaces are needed, check that there is only one space. + if ( false !== $need_spaces && ( $spaced1 || $spaced2 ) ) { + if ( $spaced1 ) { + $ptr = ( $stackPtr + 1 ); + $length = 0; + if ( $this->tokens[ $ptr ]['line'] !== $this->tokens[ ( $ptr + 1 ) ]['line'] ) { + $length = 'newline'; + } else { + $length = $this->tokens[ $ptr ]['length']; + } + + if ( 1 !== $length ) { + $error = 'There should be exactly one space before the array key. Found: %s'; + $data = array( $length ); + $fix = $this->phpcsFile->addFixableError( + $error, + $ptr, + 'TooMuchSpaceBeforeKey', + $data + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + $this->phpcsFile->fixer->replaceToken( $ptr, ' ' ); + + for ( $i = ( $ptr + 1 ); $i < $token['bracket_closer']; $i++ ) { + if ( \T_WHITESPACE !== $this->tokens[ $i ]['code'] ) { + break; + } + + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + + $this->phpcsFile->fixer->endChangeset(); + } + } + } + + if ( $spaced2 ) { + $prev_non_empty = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $token['bracket_closer'] - 1 ), null, true ); + $ptr = ( $prev_non_empty + 1 ); + $length = 0; + if ( $this->tokens[ $ptr ]['line'] !== $this->tokens[ $token['bracket_closer'] ]['line'] ) { + $length = 'newline'; + } else { + $length = $this->tokens[ $ptr ]['length']; + } + + if ( 1 !== $length ) { + $error = 'There should be exactly one space after the array key. Found: %s'; + $data = array( $length ); + $fix = $this->phpcsFile->addFixableError( + $error, + $ptr, + 'TooMuchSpaceAfterKey', + $data + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + $this->phpcsFile->fixer->replaceToken( $ptr, ' ' ); + + for ( $i = ( $ptr + 1 ); $i < $token['bracket_closer']; $i++ ) { + if ( \T_WHITESPACE !== $this->tokens[ $i ]['code'] ) { + break; + } + + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + + $this->phpcsFile->fixer->endChangeset(); + } + } + } + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/CommaAfterArrayItemSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/CommaAfterArrayItemSniff.php new file mode 100644 index 00000000..902bc670 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/CommaAfterArrayItemSniff.php @@ -0,0 +1,313 @@ +tokens[ $stackPtr ]['code'] + && $this->is_short_list( $stackPtr ) + ) { + // Short list, not short array. + return; + } + + /* + * Determine the array opener & closer. + */ + $array_open_close = $this->find_array_open_close( $stackPtr ); + if ( false === $array_open_close ) { + // Array open/close could not be determined. + return; + } + + $opener = $array_open_close['opener']; + $closer = $array_open_close['closer']; + unset( $array_open_close ); + + // This array is empty, so the below checks aren't necessary. + if ( ( $opener + 1 ) === $closer ) { + return; + } + + $single_line = true; + if ( $this->tokens[ $opener ]['line'] !== $this->tokens[ $closer ]['line'] ) { + $single_line = false; + } + + $array_items = $this->get_function_call_parameters( $stackPtr ); + if ( empty( $array_items ) ) { + // Strange, no array items found. + return; + } + + $array_item_count = \count( $array_items ); + + // Note: $item_index is 1-based and the array items are split on the commas! + foreach ( $array_items as $item_index => $item ) { + $maybe_comma = ( $item['end'] + 1 ); + $is_comma = false; + if ( isset( $this->tokens[ $maybe_comma ] ) && \T_COMMA === $this->tokens[ $maybe_comma ]['code'] ) { + $is_comma = true; + } + + /* + * Check if this is a comma at the end of the last item in a single line array. + */ + if ( true === $single_line && $item_index === $array_item_count ) { + + $this->phpcsFile->recordMetric( + $stackPtr, + 'Single line array - comma after last item', + ( true === $is_comma ? 'yes' : 'no' ) + ); + + if ( true === $is_comma ) { + $fix = $this->phpcsFile->addFixableError( + 'Comma not allowed after last value in single-line array declaration', + $maybe_comma, + 'CommaAfterLast' + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->replaceToken( $maybe_comma, '' ); + } + } + + /* + * No need to do the spacing checks for the last item in a single line array. + * This is handled by another sniff checking the spacing before the array closer. + */ + continue; + } + + $last_content = $this->phpcsFile->findPrevious( + Tokens::$emptyTokens, + $item['end'], + $item['start'], + true + ); + + if ( false === $last_content ) { + // Shouldn't be able to happen, but just in case, ignore this array item. + continue; + } + + /** + * Make sure every item in a multi-line array has a comma at the end. + * + * Should in reality only be triggered by the last item in a multi-line array + * as otherwise we'd have a parse error already. + */ + if ( false === $is_comma && false === $single_line ) { + + $fix = $this->phpcsFile->addFixableError( + 'Each array item in a multi-line array declaration must end in a comma', + $last_content, + 'NoComma' + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->addContent( $last_content, ',' ); + } + } + + if ( false === $single_line && $item_index === $array_item_count ) { + $this->phpcsFile->recordMetric( + $stackPtr, + 'Multi-line array - comma after last item', + ( true === $is_comma ? 'yes' : 'no' ) + ); + } + + if ( false === $is_comma ) { + // Can't check spacing around the comma if there is no comma. + continue; + } + + /* + * Check for whitespace at the end of the array item. + */ + if ( $last_content !== $item['end'] + // Ignore whitespace at the end of a multi-line item if it is the end of a heredoc/nowdoc. + && ( true === $single_line + || ! isset( Tokens::$heredocTokens[ $this->tokens[ $last_content ]['code'] ] ) ) + ) { + $newlines = 0; + $spaces = 0; + for ( $i = $item['end']; $i > $last_content; $i-- ) { + + if ( \T_WHITESPACE === $this->tokens[ $i ]['code'] ) { + if ( $this->tokens[ $i ]['content'] === $this->phpcsFile->eolChar ) { + $newlines++; + } else { + $spaces += $this->tokens[ $i ]['length']; + } + } elseif ( \T_COMMENT === $this->tokens[ $i ]['code'] + || isset( Tokens::$phpcsCommentTokens[ $this->tokens[ $i ]['code'] ] ) + ) { + break; + } + } + + $space_phrases = array(); + if ( $spaces > 0 ) { + $space_phrases[] = $spaces . ' spaces'; + } + if ( $newlines > 0 ) { + $space_phrases[] = $newlines . ' newlines'; + } + unset( $newlines, $spaces ); + + $fix = $this->phpcsFile->addFixableError( + 'Expected 0 spaces between "%s" and comma; %s found', + $maybe_comma, + 'SpaceBeforeComma', + array( + $this->tokens[ $last_content ]['content'], + implode( ' and ', $space_phrases ), + ) + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + for ( $i = $item['end']; $i > $last_content; $i-- ) { + + if ( \T_WHITESPACE === $this->tokens[ $i ]['code'] ) { + $this->phpcsFile->fixer->replaceToken( $i, '' ); + + } elseif ( \T_COMMENT === $this->tokens[ $i ]['code'] + || isset( Tokens::$phpcsCommentTokens[ $this->tokens[ $i ]['code'] ] ) + ) { + // We need to move the comma to before the comment. + $this->phpcsFile->fixer->addContent( $last_content, ',' ); + $this->phpcsFile->fixer->replaceToken( $maybe_comma, '' ); + + /* + * No need to worry about removing too much whitespace in + * combination with a `//` comment as in that case, the newline + * is part of the comment, so we're good. + */ + + break; + } + } + $this->phpcsFile->fixer->endChangeset(); + } + } + + if ( ! isset( $this->tokens[ ( $maybe_comma + 1 ) ] ) ) { + // Shouldn't be able to happen, but just in case. + continue; + } + + /* + * Check whitespace after the comma. + */ + $next_token = $this->tokens[ ( $maybe_comma + 1 ) ]; + + if ( \T_WHITESPACE === $next_token['code'] ) { + + if ( false === $single_line && $this->phpcsFile->eolChar === $next_token['content'] ) { + continue; + } + + $next_non_whitespace = $this->phpcsFile->findNext( + \T_WHITESPACE, + ( $maybe_comma + 1 ), + $closer, + true + ); + + if ( false === $next_non_whitespace + || ( false === $single_line + && $this->tokens[ $next_non_whitespace ]['line'] === $this->tokens[ $maybe_comma ]['line'] + && ( \T_COMMENT === $this->tokens[ $next_non_whitespace ]['code'] + || isset( Tokens::$phpcsCommentTokens[ $this->tokens[ $next_non_whitespace ]['code'] ] ) ) ) + ) { + continue; + } + + $space_length = $next_token['length']; + if ( 1 === $space_length ) { + continue; + } + + $fix = $this->phpcsFile->addFixableError( + 'Expected 1 space between comma and "%s"; %s found', + $maybe_comma, + 'SpaceAfterComma', + array( + $this->tokens[ $next_non_whitespace ]['content'], + $space_length, + ) + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->replaceToken( ( $maybe_comma + 1 ), ' ' ); + } + } else { + // This is either a comment or a mixed single/multi-line array. + // Just add a space and let other sniffs sort out the array layout. + $fix = $this->phpcsFile->addFixableError( + 'Expected 1 space between comma and "%s"; 0 found', + $maybe_comma, + 'NoSpaceAfterComma', + array( $next_token['content'] ) + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->addContent( $maybe_comma, ' ' ); + } + } + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/MultipleStatementAlignmentSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/MultipleStatementAlignmentSniff.php new file mode 100644 index 00000000..dc68e4f9 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Arrays/MultipleStatementAlignmentSniff.php @@ -0,0 +1,617 @@ += 60, align at column 60. + * - for the outliers, i.e. the array indexes where the end position + * goes past column 60, it will not align the arrow, the sniff will + * just make sure there is only one space between the end of the + * array index and the double arrow. + * + * The column value is regarded as a hard value, i.e. includes indentation, + * so setting it very low is not a good idea. + * + * @since 0.14.0 + * + * @var int + */ + public $maxColumn = 1000; + + /** + * Whether or not to align the arrow operator for multi-line array items. + * + * Whether or not an item is regarded as multi-line is based on the **value** + * of the item, not the key. + * + * Valid values are: + * - 'always': Default. Align all arrays items regardless of single/multi-line. + * - 'never': Never align array items which span multiple lines. + * This will enforce one space between the array index and the + * double arrow operator for multi-line array items, independently + * of the alignment of the rest of the array items. + * Multi-line items where the arrow is already aligned with the + * "expected" alignment, however, will be left alone. + * - operator : Only align the operator for multi-line arrays items if the + * + number percentage of multi-line items passes the comparison. + * - As it is a percentage, the number has to be between 0 and 100. + * - Supported operators: <, <=, >, >=, ==, =, !=, <> + * - The percentage is calculated against all array items + * (with and without assignment operator). + * - The (new) expected alignment will be calculated based only + * on the items being aligned. + * - Multi-line items where the arrow is already aligned with the + * (new) "expected" alignment, however, will be left alone. + * Examples: + * * Setting this to `!=100` or `<100` means that alignment will + * be enforced, unless *all* array items are multi-line. + * This is probably the most commonly desired situation. + * * Setting this to `=100` means that alignment will only + * be enforced, if *all* array items are multi-line. + * * Setting this to `<50` means that the majority of array items + * need to be single line before alignment is enforced for + * multi-line items in the array. + * * Setting this to `=0` is useless as in that case there are + * no multi-line items in the array anyway. + * + * This setting will respect the `ignoreNewlines` and `maxColumnn` settings. + * + * @since 0.14.0 + * + * @var string|int + */ + public $alignMultilineItems = 'always'; + + /** + * Storage for parsed $alignMultilineItems operator part. + * + * @since 0.14.0 + * + * @var string + */ + private $operator; + + /** + * Storage for parsed $alignMultilineItems numeric part. + * + * Stored as a string as the comparison will be done string based. + * + * @since 0.14.0 + * + * @var string + */ + private $number; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 0.14.0 + * + * @return array + */ + public function register() { + return array( + \T_ARRAY, + \T_OPEN_SHORT_ARRAY, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 0.14.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + + if ( \T_OPEN_SHORT_ARRAY === $this->tokens[ $stackPtr ]['code'] + && $this->is_short_list( $stackPtr ) + ) { + // Short list, not short array. + return; + } + + /* + * Determine the array opener & closer. + */ + $array_open_close = $this->find_array_open_close( $stackPtr ); + if ( false === $array_open_close ) { + // Array open/close could not be determined. + return; + } + + $opener = $array_open_close['opener']; + $closer = $array_open_close['closer']; + + $array_items = $this->get_function_call_parameters( $stackPtr ); + if ( empty( $array_items ) ) { + return; + } + + // Pass off to either the single line or multi-line array analysis. + if ( $this->tokens[ $opener ]['line'] === $this->tokens[ $closer ]['line'] ) { + return $this->process_single_line_array( $stackPtr, $array_items, $opener, $closer ); + } else { + return $this->process_multi_line_array( $stackPtr, $array_items, $opener, $closer ); + } + } + + /** + * Process a single-line array. + * + * While the WP standard does not allow single line multi-item associative arrays, + * this sniff should function independently of that. + * + * The `WordPress.WhiteSpace.OperatorSpacing` sniff already covers checking that + * there is a space between the array key and the double arrow, but doesn't + * enforce it to be exactly one space for single line arrays. + * That is what this method covers. + * + * @since 0.14.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param array $items Info array containing information on each array item. + * @param int $opener The position of the array opener. + * @param int $closer The position of the array closer. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + protected function process_single_line_array( $stackPtr, $items, $opener, $closer ) { + /* + * For single line arrays, we don't care about what level the arrow is from. + * Just find and fix them all. + */ + $next_arrow = $this->phpcsFile->findNext( + \T_DOUBLE_ARROW, + ( $opener + 1 ), + $closer + ); + + while ( false !== $next_arrow ) { + if ( \T_WHITESPACE === $this->tokens[ ( $next_arrow - 1 ) ]['code'] ) { + $space_length = $this->tokens[ ( $next_arrow - 1 ) ]['length']; + if ( 1 !== $space_length ) { + $error = 'Expected 1 space between "%s" and double arrow; %s found'; + $data = array( + $this->tokens[ ( $next_arrow - 2 ) ]['content'], + $space_length, + ); + + $fix = $this->phpcsFile->addFixableWarning( $error, $next_arrow, 'SpaceBeforeDoubleArrow', $data ); + if ( true === $fix ) { + $this->phpcsFile->fixer->replaceToken( ( $next_arrow - 1 ), ' ' ); + } + } + } + + // Find the position of the next double arrow. + $next_arrow = $this->phpcsFile->findNext( + \T_DOUBLE_ARROW, + ( $next_arrow + 1 ), + $closer + ); + } + + // Ignore any child-arrays as the double arrows in these will already have been handled. + return ( $closer + 1 ); + } + + /** + * Process a multi-line array. + * + * @since 0.14.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param array $items Info array containing information on each array item. + * @param int $opener The position of the array opener. + * @param int $closer The position of the array closer. + * + * @return void + */ + protected function process_multi_line_array( $stackPtr, $items, $opener, $closer ) { + + $this->maxColumn = (int) $this->maxColumn; + $this->validate_align_multiline_items(); + + /* + * Determine what the spacing before the arrow should be. + * + * Will unset any array items without double arrow and with new line whitespace + * if newlines are to be ignored, so the second foreach loop only has to deal + * with items which need attention. + * + * This sniff does not take incorrect indentation of array keys into account. + * That's for the `WordPress.Arrays.ArrayIndentation` sniff to fix. + * If that would affect the alignment, a second (or third) loop of the fixer + * will correct it (again) after the indentation has been fixed. + */ + $index_end_cols = array(); // Keep track of the end column position of index keys. + $double_arrow_cols = array(); // Keep track of arrow column position and count. + $multi_line_count = 0; + $total_items = \count( $items ); + + foreach ( $items as $key => $item ) { + if ( strpos( $item['raw'], '=>' ) === false ) { + // Ignore items without assignment operators. + unset( $items[ $key ] ); + continue; + } + + // Find the position of the first double arrow. + $double_arrow = $this->phpcsFile->findNext( + \T_DOUBLE_ARROW, + $item['start'], + ( $item['end'] + 1 ) + ); + + if ( false === $double_arrow ) { + // Shouldn't happen, just in case. + unset( $items[ $key ] ); + continue; + } + + // Make sure the arrow is for this item and not for a nested array value assignment. + $has_array_opener = $this->phpcsFile->findNext( + $this->register(), + $item['start'], + $double_arrow + ); + + if ( false !== $has_array_opener ) { + // Double arrow is for a nested array. + unset( $items[ $key ] ); + continue; + } + + // Find the end of the array key. + $last_index_token = $this->phpcsFile->findPrevious( + \T_WHITESPACE, + ( $double_arrow - 1 ), + $item['start'], + true + ); + + if ( false === $last_index_token ) { + // Shouldn't happen, but just in case. + unset( $items[ $key ] ); + continue; + } + + if ( true === $this->ignoreNewlines + && $this->tokens[ $last_index_token ]['line'] !== $this->tokens[ $double_arrow ]['line'] + ) { + // Ignore this item as it has a new line between the item key and the double arrow. + unset( $items[ $key ] ); + continue; + } + + $index_end_position = ( $this->tokens[ $last_index_token ]['column'] + ( $this->tokens[ $last_index_token ]['length'] - 1 ) ); + $items[ $key ]['operatorPtr'] = $double_arrow; + $items[ $key ]['last_index_token'] = $last_index_token; + $items[ $key ]['last_index_col'] = $index_end_position; + + if ( $this->tokens[ $last_index_token ]['line'] === $this->tokens[ $item['end'] ]['line'] ) { + $items[ $key ]['single_line'] = true; + } else { + $items[ $key ]['single_line'] = false; + $multi_line_count++; + } + + if ( ( $index_end_position + 2 ) <= $this->maxColumn ) { + $index_end_cols[] = $index_end_position; + } + + if ( ! isset( $double_arrow_cols[ $this->tokens[ $double_arrow ]['column'] ] ) ) { + $double_arrow_cols[ $this->tokens[ $double_arrow ]['column'] ] = 1; + } else { + $double_arrow_cols[ $this->tokens[ $double_arrow ]['column'] ]++; + } + } + unset( $key, $item, $double_arrow, $has_array_opener, $last_index_token ); + + if ( empty( $items ) || empty( $index_end_cols ) ) { + // No actionable array items found. + return; + } + + /* + * Determine whether the operators for multi-line items should be aligned. + */ + if ( 'always' === $this->alignMultilineItems ) { + $alignMultilineItems = true; + } elseif ( 'never' === $this->alignMultilineItems ) { + $alignMultilineItems = false; + } else { + $percentage = (string) round( ( $multi_line_count / $total_items ) * 100, 0 ); + + // Bit hacky, but this is the only comparison function in PHP which allows to + // pass the comparison operator. And hey, it works ;-). + $alignMultilineItems = version_compare( $percentage, $this->number, $this->operator ); + } + + /* + * If necessary, rebuild the $index_end_cols and $double_arrow_cols arrays + * excluding multi-line items. + */ + if ( false === $alignMultilineItems ) { + $select_index_end_cols = array(); + $double_arrow_cols = array(); + + foreach ( $items as $item ) { + if ( false === $item['single_line'] ) { + continue; + } + + if ( ( $item['last_index_col'] + 2 ) <= $this->maxColumn ) { + $select_index_end_cols[] = $item['last_index_col']; + } + + if ( ! isset( $double_arrow_cols[ $this->tokens[ $item['operatorPtr'] ]['column'] ] ) ) { + $double_arrow_cols[ $this->tokens[ $item['operatorPtr'] ]['column'] ] = 1; + } else { + $double_arrow_cols[ $this->tokens[ $item['operatorPtr'] ]['column'] ]++; + } + } + } + + /* + * Determine the expected position of the double arrows. + */ + if ( ! empty( $select_index_end_cols ) ) { + $max_index_width = max( $select_index_end_cols ); + } else { + $max_index_width = max( $index_end_cols ); + } + + $expected_col = ( $max_index_width + 2 ); + + if ( false === $this->exact && ! empty( $double_arrow_cols ) ) { + /* + * If the alignment does not have to be exact, see if a majority + * group of the arrows is already at an acceptable position. + */ + arsort( $double_arrow_cols, \SORT_NUMERIC ); + reset( $double_arrow_cols ); + $count = current( $double_arrow_cols ); + + if ( $count > 1 || ( 1 === $count && \count( $items ) === 1 ) ) { + // Allow for several groups of arrows having the same $count. + $filtered_double_arrow_cols = array_keys( $double_arrow_cols, $count, true ); + + foreach ( $filtered_double_arrow_cols as $col ) { + if ( $col > $expected_col && $col <= $this->maxColumn ) { + $expected_col = $col; + break; + } + } + } + } + unset( $max_index_width, $count, $filtered_double_arrow_cols, $col ); + + /* + * Verify and correct the spacing around the double arrows. + */ + foreach ( $items as $item ) { + if ( $this->tokens[ $item['operatorPtr'] ]['column'] === $expected_col + && $this->tokens[ $item['operatorPtr'] ]['line'] === $this->tokens[ $item['last_index_token'] ]['line'] + ) { + // Already correctly aligned. + continue; + } + + if ( \T_WHITESPACE !== $this->tokens[ ( $item['operatorPtr'] - 1 ) ]['code'] ) { + $before = 0; + } else { + if ( $this->tokens[ $item['last_index_token'] ]['line'] !== $this->tokens[ $item['operatorPtr'] ]['line'] ) { + $before = 'newline'; + } else { + $before = $this->tokens[ ( $item['operatorPtr'] - 1 ) ]['length']; + } + } + + /* + * Deal with index sizes larger than maxColumn and with multi-line + * array items which should not be aligned. + */ + if ( ( $item['last_index_col'] + 2 ) > $this->maxColumn + || ( false === $alignMultilineItems && false === $item['single_line'] ) + ) { + + if ( ( $item['last_index_col'] + 2 ) === $this->tokens[ $item['operatorPtr'] ]['column'] + && $this->tokens[ $item['operatorPtr'] ]['line'] === $this->tokens[ $item['last_index_token'] ]['line'] + ) { + // MaxColumn/Multi-line item exception, already correctly aligned. + continue; + } + + $prefix = 'LongIndex'; + if ( false === $alignMultilineItems && false === $item['single_line'] ) { + $prefix = 'MultilineItem'; + } + + $error_code = $prefix . 'SpaceBeforeDoubleArrow'; + if ( 0 === $before ) { + $error_code = $prefix . 'NoSpaceBeforeDoubleArrow'; + } + + $fix = $this->phpcsFile->addFixableWarning( + 'Expected 1 space between "%s" and double arrow; %s found.', + $item['operatorPtr'], + $error_code, + array( + $this->tokens[ $item['last_index_token'] ]['content'], + $before, + ) + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + + // Remove whitespace tokens between the end of the index and the arrow, if any. + for ( $i = ( $item['last_index_token'] + 1 ); $i < $item['operatorPtr']; $i++ ) { + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + + // Add the correct whitespace. + $this->phpcsFile->fixer->addContent( $item['last_index_token'], ' ' ); + + $this->phpcsFile->fixer->endChangeset(); + } + continue; + } + + /* + * Deal with the space before double arrows in all other cases. + */ + $expected_whitespace = $expected_col - ( $this->tokens[ $item['last_index_token'] ]['column'] + $this->tokens[ $item['last_index_token'] ]['length'] ); + + $fix = $this->phpcsFile->addFixableWarning( + 'Array double arrow not aligned correctly; expected %s space(s) between "%s" and double arrow, but found %s.', + $item['operatorPtr'], + 'DoubleArrowNotAligned', + array( + $expected_whitespace, + $this->tokens[ $item['last_index_token'] ]['content'], + $before, + ) + ); + + if ( true === $fix ) { + if ( 0 === $before || 'newline' === $before ) { + $this->phpcsFile->fixer->beginChangeset(); + + // Remove whitespace tokens between the end of the index and the arrow, if any. + for ( $i = ( $item['last_index_token'] + 1 ); $i < $item['operatorPtr']; $i++ ) { + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + + // Add the correct whitespace. + $this->phpcsFile->fixer->addContent( + $item['last_index_token'], + str_repeat( ' ', $expected_whitespace ) + ); + + $this->phpcsFile->fixer->endChangeset(); + } elseif ( $expected_whitespace > $before ) { + // Add to the existing whitespace to prevent replacing tabs with spaces. + // That's the concern of another sniff. + $this->phpcsFile->fixer->addContent( + ( $item['operatorPtr'] - 1 ), + str_repeat( ' ', ( $expected_whitespace - $before ) ) + ); + } else { + // Too much whitespace found. + $this->phpcsFile->fixer->replaceToken( + ( $item['operatorPtr'] - 1 ), + str_repeat( ' ', $expected_whitespace ) + ); + } + } + } + } + + /** + * Validate that a valid value has been received for the alignMultilineItems property. + * + * This message may be thrown more than once if the property is being changed inline in a file. + * + * @since 0.14.0 + */ + protected function validate_align_multiline_items() { + $alignMultilineItems = $this->alignMultilineItems; + + if ( 'always' === $alignMultilineItems || 'never' === $alignMultilineItems ) { + return; + } else { + // Correct for a potentially added % sign. + $alignMultilineItems = rtrim( $alignMultilineItems, '%' ); + + if ( preg_match( '`^([=<>!]{1,2})(100|[0-9]{1,2})$`', $alignMultilineItems, $matches ) > 0 ) { + $operator = $matches[1]; + $number = (int) $matches[2]; + + if ( \in_array( $operator, array( '<', '<=', '>', '>=', '==', '=', '!=', '<>' ), true ) === true + && ( $number >= 0 && $number <= 100 ) + ) { + $this->alignMultilineItems = $alignMultilineItems; + $this->number = (string) $number; + $this->operator = $operator; + return; + } + } + } + + $this->phpcsFile->addError( + 'Invalid property value passed: "%s". The value for the "alignMultilineItems" property for the "WordPress.Arrays.MultipleStatementAlignment" sniff should be either "always", "never" or an comparison operator + a number between 0 and 100.', + 0, + 'InvalidPropertyPassed', + array( $this->alignMultilineItems ) + ); + + // Reset to the default if an invalid value was received. + $this->alignMultilineItems = 'always'; + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Classes/ClassInstantiationSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Classes/ClassInstantiationSniff.php new file mode 100644 index 00000000..3572fbee --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Classes/ClassInstantiationSniff.php @@ -0,0 +1,204 @@ +classname_tokens = Tokens::$emptyTokens; + $this->classname_tokens[ \T_NS_SEPARATOR ] = \T_NS_SEPARATOR; + $this->classname_tokens[ \T_STRING ] = \T_STRING; + $this->classname_tokens[ \T_SELF ] = \T_SELF; + $this->classname_tokens[ \T_STATIC ] = \T_STATIC; + $this->classname_tokens[ \T_PARENT ] = \T_PARENT; + $this->classname_tokens[ \T_ANON_CLASS ] = \T_ANON_CLASS; + + // Classname in a variable. + $this->classname_tokens[ \T_VARIABLE ] = \T_VARIABLE; + $this->classname_tokens[ \T_DOUBLE_COLON ] = \T_DOUBLE_COLON; + $this->classname_tokens[ \T_OBJECT_OPERATOR ] = \T_OBJECT_OPERATOR; + $this->classname_tokens[ \T_OPEN_SQUARE_BRACKET ] = \T_OPEN_SQUARE_BRACKET; + $this->classname_tokens[ \T_CLOSE_SQUARE_BRACKET ] = \T_CLOSE_SQUARE_BRACKET; + $this->classname_tokens[ \T_CONSTANT_ENCAPSED_STRING ] = \T_CONSTANT_ENCAPSED_STRING; + $this->classname_tokens[ \T_LNUMBER ] = \T_LNUMBER; + + return array( + \T_NEW, + \T_STRING, // JS. + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + public function process_token( $stackPtr ) { + // Make sure we have the right token, JS vs PHP. + if ( ( 'PHP' === $this->phpcsFile->tokenizerType && \T_NEW !== $this->tokens[ $stackPtr ]['code'] ) + || ( 'JS' === $this->phpcsFile->tokenizerType + && ( \T_STRING !== $this->tokens[ $stackPtr ]['code'] + || 'new' !== strtolower( $this->tokens[ $stackPtr ]['content'] ) ) ) + ) { + return; + } + + /* + * Check for new by reference used in PHP files. + */ + if ( 'PHP' === $this->phpcsFile->tokenizerType ) { + $prev_non_empty = $this->phpcsFile->findPrevious( + Tokens::$emptyTokens, + ( $stackPtr - 1 ), + null, + true + ); + + if ( false !== $prev_non_empty && 'T_BITWISE_AND' === $this->tokens[ $prev_non_empty ]['type'] ) { + $this->phpcsFile->recordMetric( $stackPtr, 'Assigning new by reference', 'yes' ); + + $this->phpcsFile->addError( + 'Assigning the return value of new by reference is no longer supported by PHP.', + $stackPtr, + 'NewByReferenceFound' + ); + } else { + $this->phpcsFile->recordMetric( $stackPtr, 'Assigning new by reference', 'no' ); + } + } + + /* + * Check for parenthesis & correct placement thereof. + */ + $next_non_empty_after_class_name = $this->phpcsFile->findNext( + $this->classname_tokens, + ( $stackPtr + 1 ), + null, + true, + null, + true + ); + + if ( false === $next_non_empty_after_class_name ) { + // Live coding. + return; + } + + // Walk back to the last part of the class name. + $has_comment = false; + for ( $classname_ptr = ( $next_non_empty_after_class_name - 1 ); $classname_ptr >= $stackPtr; $classname_ptr-- ) { + if ( ! isset( Tokens::$emptyTokens[ $this->tokens[ $classname_ptr ]['code'] ] ) ) { + // Prevent a false positive on variable variables, disregard them for now. + if ( $stackPtr === $classname_ptr ) { + return; + } + + break; + } + + if ( \T_WHITESPACE !== $this->tokens[ $classname_ptr ]['code'] ) { + $has_comment = true; + } + } + + if ( \T_OPEN_PARENTHESIS !== $this->tokens[ $next_non_empty_after_class_name ]['code'] ) { + $this->phpcsFile->recordMetric( $stackPtr, 'Object instantiation with parenthesis', 'no' ); + + $fix = $this->phpcsFile->addFixableError( + 'Parenthesis should always be used when instantiating a new object.', + $classname_ptr, + 'MissingParenthesis' + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->addContent( $classname_ptr, '()' ); + } + } else { + $this->phpcsFile->recordMetric( $stackPtr, 'Object instantiation with parenthesis', 'yes' ); + + if ( ( $next_non_empty_after_class_name - 1 ) !== $classname_ptr ) { + $this->phpcsFile->recordMetric( + $stackPtr, + 'Space between classname and parenthesis', + ( $next_non_empty_after_class_name - $classname_ptr ) + ); + + $error = 'There must be no spaces between the class name and the open parenthesis when instantiating a new object.'; + $error_code = 'SpaceBeforeParenthesis'; + + if ( false === $has_comment ) { + $fix = $this->phpcsFile->addFixableError( $error, $next_non_empty_after_class_name, $error_code ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + for ( $i = ( $next_non_empty_after_class_name - 1 ); $i > $classname_ptr; $i-- ) { + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + $this->phpcsFile->fixer->endChangeset(); + } + } else { + $this->phpcsFile->addError( $error, $next_non_empty_after_class_name, $error_code ); + } + } else { + $this->phpcsFile->recordMetric( $stackPtr, 'Space between classname and parenthesis', 0 ); + } + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/CodeAnalysis/AssignmentInConditionSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/CodeAnalysis/AssignmentInConditionSniff.php new file mode 100644 index 00000000..bf5879b8 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/CodeAnalysis/AssignmentInConditionSniff.php @@ -0,0 +1,235 @@ +assignment_tokens = Tokens::$assignmentTokens; + unset( $this->assignment_tokens[ \T_DOUBLE_ARROW ] ); + + $starters = Tokens::$booleanOperators; + $starters[ \T_SEMICOLON ] = \T_SEMICOLON; + $starters[ \T_OPEN_PARENTHESIS ] = \T_OPEN_PARENTHESIS; + $starters[ \T_INLINE_ELSE ] = \T_INLINE_ELSE; + + $this->condition_start_tokens = $starters; + + return array( + \T_IF, + \T_ELSEIF, + \T_FOR, + \T_SWITCH, + \T_CASE, + \T_WHILE, + \T_INLINE_THEN, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 0.14.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + public function process_token( $stackPtr ) { + + $token = $this->tokens[ $stackPtr ]; + + // Find the condition opener/closer. + if ( \T_FOR === $token['code'] ) { + if ( isset( $token['parenthesis_opener'], $token['parenthesis_closer'] ) === false ) { + return; + } + + $semicolon = $this->phpcsFile->findNext( \T_SEMICOLON, ( $token['parenthesis_opener'] + 1 ), $token['parenthesis_closer'] ); + if ( false === $semicolon ) { + return; + } + + $opener = $semicolon; + $semicolon = $this->phpcsFile->findNext( \T_SEMICOLON, ( $opener + 1 ), $token['parenthesis_closer'] ); + if ( false === $semicolon ) { + return; + } + + $closer = $semicolon; + unset( $semicolon ); + + } elseif ( \T_CASE === $token['code'] ) { + if ( isset( $token['scope_opener'] ) === false ) { + return; + } + + $opener = $stackPtr; + $closer = $token['scope_opener']; + + } elseif ( \T_INLINE_THEN === $token['code'] ) { + // Check if the condition for the ternary is bracketed. + $prev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true ); + if ( false === $prev ) { + // Shouldn't happen, but in that case we don't have anything to examine anyway. + return; + } + + if ( \T_CLOSE_PARENTHESIS === $this->tokens[ $prev ]['code'] ) { + if ( ! isset( $this->tokens[ $prev ]['parenthesis_opener'] ) ) { + return; + } + + $opener = $this->tokens[ $prev ]['parenthesis_opener']; + $closer = $prev; + } elseif ( isset( $token['nested_parenthesis'] ) ) { + $closer = end( $token['nested_parenthesis'] ); + $opener = key( $token['nested_parenthesis'] ); + + $next_statement_closer = $this->phpcsFile->findEndOfStatement( $stackPtr, array( \T_COLON, \T_CLOSE_PARENTHESIS, \T_CLOSE_SQUARE_BRACKET ) ); + if ( false !== $next_statement_closer && $next_statement_closer < $closer ) { + // Parentheses are unrelated to the ternary. + return; + } + + $prev_statement_closer = $this->phpcsFile->findStartOfStatement( $stackPtr, array( \T_COLON, \T_OPEN_PARENTHESIS, \T_OPEN_SQUARE_BRACKET ) ); + if ( false !== $prev_statement_closer && $opener < $prev_statement_closer ) { + // Parentheses are unrelated to the ternary. + return; + } + + if ( $closer > $stackPtr ) { + $closer = $stackPtr; + } + } else { + // No parenthesis found, can't determine where the conditional part of the ternary starts. + return; + } + } else { + if ( isset( $token['parenthesis_opener'], $token['parenthesis_closer'] ) === false ) { + return; + } + + $opener = $token['parenthesis_opener']; + $closer = $token['parenthesis_closer']; + } + + $startPos = $opener; + + do { + $hasAssignment = $this->phpcsFile->findNext( $this->assignment_tokens, ( $startPos + 1 ), $closer ); + if ( false === $hasAssignment ) { + return; + } + + // Examine whether the left side is a variable. + $hasVariable = false; + $conditionStart = $startPos; + $altConditionStart = $this->phpcsFile->findPrevious( + $this->condition_start_tokens, + ( $hasAssignment - 1 ), + $startPos + ); + if ( false !== $altConditionStart ) { + $conditionStart = $altConditionStart; + } + + for ( $i = $hasAssignment; $i > $conditionStart; $i-- ) { + if ( isset( Tokens::$emptyTokens[ $this->tokens[ $i ]['code'] ] ) ) { + continue; + } + + // If this is a variable or array, we've seen all we need to see. + if ( \T_VARIABLE === $this->tokens[ $i ]['code'] + || \T_CLOSE_SQUARE_BRACKET === $this->tokens[ $i ]['code'] + ) { + $hasVariable = true; + break; + } + + // If this is a function call or something, we are OK. + if ( \T_CLOSE_PARENTHESIS === $this->tokens[ $i ]['code'] ) { + break; + } + } + + if ( true === $hasVariable ) { + $errorCode = 'Found'; + if ( \T_WHILE === $token['code'] ) { + $errorCode = 'FoundInWhileCondition'; + } elseif ( \T_INLINE_THEN === $token['code'] ) { + $errorCode = 'FoundInTernaryCondition'; + } + + $this->phpcsFile->addWarning( + 'Variable assignment found within a condition. Did you mean to do a comparison?', + $hasAssignment, + $errorCode + ); + } else { + $this->phpcsFile->addWarning( + 'Assignment found within a condition. Did you mean to do a comparison?', + $hasAssignment, + 'NonVariableAssignmentFound' + ); + } + + $startPos = $hasAssignment; + + } while ( $startPos < $closer ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/CodeAnalysis/EmptyStatementSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/CodeAnalysis/EmptyStatementSniff.php new file mode 100644 index 00000000..21e22014 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/CodeAnalysis/EmptyStatementSniff.php @@ -0,0 +1,161 @@ +tokens[ $stackPtr ]['type'] ) { + /* + * Detect `something();;`. + */ + case 'T_SEMICOLON': + $prevNonEmpty = $this->phpcsFile->findPrevious( + Tokens::$emptyTokens, + ( $stackPtr - 1 ), + null, + true + ); + + if ( false === $prevNonEmpty + || ( \T_SEMICOLON !== $this->tokens[ $prevNonEmpty ]['code'] + && \T_OPEN_TAG !== $this->tokens[ $prevNonEmpty ]['code'] + && \T_OPEN_TAG_WITH_ECHO !== $this->tokens[ $prevNonEmpty ]['code'] ) + ) { + return; + } + + if ( isset( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) ) { + $nested = $this->tokens[ $stackPtr ]['nested_parenthesis']; + $last_closer = array_pop( $nested ); + if ( isset( $this->tokens[ $last_closer ]['parenthesis_owner'] ) + && \T_FOR === $this->tokens[ $this->tokens[ $last_closer ]['parenthesis_owner'] ]['code'] + ) { + // Empty for() condition. + return; + } + } + + $fix = $this->phpcsFile->addFixableWarning( + 'Empty PHP statement detected: superfluous semi-colon.', + $stackPtr, + 'SemicolonWithoutCodeDetected' + ); + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + + if ( \T_OPEN_TAG === $this->tokens[ $prevNonEmpty ]['code'] + || \T_OPEN_TAG_WITH_ECHO === $this->tokens[ $prevNonEmpty ]['code'] + ) { + /* + * Check for superfluous whitespace after the semi-colon which will be + * removed as the `tokens[ ( $stackPtr + 1 ) ]['code'] ) { + $replacement = str_replace( ' ', '', $this->tokens[ ( $stackPtr + 1 ) ]['content'] ); + $this->phpcsFile->fixer->replaceToken( ( $stackPtr + 1 ), $replacement ); + } + } + + for ( $i = $stackPtr; $i > $prevNonEmpty; $i-- ) { + if ( \T_SEMICOLON !== $this->tokens[ $i ]['code'] + && \T_WHITESPACE !== $this->tokens[ $i ]['code'] + ) { + break; + } + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + + $this->phpcsFile->fixer->endChangeset(); + } + break; + + /* + * Detect ``. + */ + case 'T_CLOSE_TAG': + $prevNonEmpty = $this->phpcsFile->findPrevious( + \T_WHITESPACE, + ( $stackPtr - 1 ), + null, + true + ); + + if ( false === $prevNonEmpty + || ( \T_OPEN_TAG !== $this->tokens[ $prevNonEmpty ]['code'] + && \T_OPEN_TAG_WITH_ECHO !== $this->tokens[ $prevNonEmpty ]['code'] ) + ) { + return; + } + + $fix = $this->phpcsFile->addFixableWarning( + 'Empty PHP open/close tag combination detected.', + $prevNonEmpty, + 'EmptyPHPOpenCloseTagsDetected' + ); + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + for ( $i = $prevNonEmpty; $i <= $stackPtr; $i++ ) { + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + $this->phpcsFile->fixer->endChangeset(); + } + break; + + default: + /* Deliberately left empty. */ + break; + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/CodeAnalysis/EscapedNotTranslatedSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/CodeAnalysis/EscapedNotTranslatedSniff.php new file mode 100644 index 00000000..5a7af877 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/CodeAnalysis/EscapedNotTranslatedSniff.php @@ -0,0 +1,90 @@ + => + */ + protected $target_functions = array( + 'esc_html' => 'esc_html__', + 'esc_attr' => 'esc_attr__', + ); + + /** + * Process the parameters of a matched function. + * + * @since 2.2.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + if ( \count( $parameters ) === 1 ) { + return; + } + + /* + * We already know that there will be a valid open+close parenthesis, otherwise the sniff + * would have bowed out long before. + */ + $opener = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + $closer = $this->tokens[ $opener ]['parenthesis_closer']; + + $data = array( + $matched_content, + $this->target_functions[ $matched_content ], + $this->phpcsFile->getTokensAsString( $stackPtr, ( $closer - $stackPtr + 1 ) ), + ); + + $this->phpcsFile->addWarning( + '%s() expects only one parameter. Did you mean to use %s() ? Found: %s', + $stackPtr, + 'Found', + $data + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/DirectDatabaseQuerySniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/DirectDatabaseQuerySniff.php new file mode 100644 index 00000000..583e9d9c --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/DirectDatabaseQuerySniff.php @@ -0,0 +1,265 @@ + array(), + 'cacheset' => array(), + 'cachedelete' => array(), + ); + + /** + * The lists of $wpdb methods. + * + * @since 0.6.0 + * @since 0.11.0 Changed from static to non-static. + * + * @var array[] + */ + protected $methods = array( + 'cachable' => array( + 'delete' => true, + 'get_var' => true, + 'get_col' => true, + 'get_row' => true, + 'get_results' => true, + 'query' => true, + 'replace' => true, + 'update' => true, + ), + 'noncachable' => array( + 'insert' => true, + ), + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + return array( + \T_VARIABLE, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + + // Check for $wpdb variable. + if ( '$wpdb' !== $this->tokens[ $stackPtr ]['content'] ) { + return; + } + + $is_object_call = $this->phpcsFile->findNext( \T_OBJECT_OPERATOR, ( $stackPtr + 1 ), null, false, null, true ); + if ( false === $is_object_call ) { + return; // This is not a call to the wpdb object. + } + + $methodPtr = $this->phpcsFile->findNext( array( \T_WHITESPACE ), ( $is_object_call + 1 ), null, true, null, true ); + $method = $this->tokens[ $methodPtr ]['content']; + + $this->mergeFunctionLists(); + + if ( ! isset( $this->methods['all'][ $method ] ) ) { + return; + } + + $endOfStatement = $this->phpcsFile->findNext( \T_SEMICOLON, ( $stackPtr + 1 ), null, false, null, true ); + $endOfLineComment = ''; + for ( $i = ( $endOfStatement + 1 ); $i < $this->phpcsFile->numTokens; $i++ ) { + + if ( $this->tokens[ $i ]['line'] !== $this->tokens[ $endOfStatement ]['line'] ) { + break; + } + + if ( \T_COMMENT === $this->tokens[ $i ]['code'] ) { + $endOfLineComment .= $this->tokens[ $i ]['content']; + } + } + + $whitelisted_db_call = false; + if ( preg_match( '/db call\W*(?:ok|pass|clear|whitelist)/i', $endOfLineComment ) ) { + $whitelisted_db_call = true; + } + + // Check for Database Schema Changes. + for ( $_pos = ( $stackPtr + 1 ); $_pos < $endOfStatement; $_pos++ ) { + $_pos = $this->phpcsFile->findNext( Tokens::$textStringTokens, $_pos, $endOfStatement, false, null, true ); + if ( false === $_pos ) { + break; + } + + if ( preg_match( '#\b(?:ALTER|CREATE|DROP)\b#i', $this->tokens[ $_pos ]['content'] ) > 0 ) { + $this->phpcsFile->addWarning( 'Attempting a database schema change is discouraged.', $_pos, 'SchemaChange' ); + } + } + + // Flag instance if not whitelisted. + if ( ! $whitelisted_db_call ) { + $this->phpcsFile->addWarning( 'Usage of a direct database call is discouraged.', $stackPtr, 'DirectQuery' ); + } + + if ( ! isset( $this->methods['cachable'][ $method ] ) ) { + return $endOfStatement; + } + + $whitelisted_cache = false; + $cached = false; + $wp_cache_get = false; + if ( preg_match( '/cache\s+(?:ok|pass|clear|whitelist)/i', $endOfLineComment ) ) { + $whitelisted_cache = true; + } + if ( ! $whitelisted_cache && ! empty( $this->tokens[ $stackPtr ]['conditions'] ) ) { + $scope_function = $this->phpcsFile->getCondition( $stackPtr, \T_FUNCTION ); + + if ( false === $scope_function ) { + $scope_function = $this->phpcsFile->getCondition( $stackPtr, \T_CLOSURE ); + } + + if ( false !== $scope_function ) { + $scopeStart = $this->tokens[ $scope_function ]['scope_opener']; + $scopeEnd = $this->tokens[ $scope_function ]['scope_closer']; + + for ( $i = ( $scopeStart + 1 ); $i < $scopeEnd; $i++ ) { + if ( \T_STRING === $this->tokens[ $i ]['code'] ) { + + if ( isset( $this->cacheDeleteFunctions[ $this->tokens[ $i ]['content'] ] ) ) { + + if ( \in_array( $method, array( 'query', 'update', 'replace', 'delete' ), true ) ) { + $cached = true; + break; + } + } elseif ( isset( $this->cacheGetFunctions[ $this->tokens[ $i ]['content'] ] ) ) { + + $wp_cache_get = true; + + } elseif ( isset( $this->cacheSetFunctions[ $this->tokens[ $i ]['content'] ] ) ) { + + if ( $wp_cache_get ) { + $cached = true; + break; + } + } + } + } + } + } + + if ( ! $cached && ! $whitelisted_cache ) { + $message = 'Direct database call without caching detected. Consider using wp_cache_get() / wp_cache_set() or wp_cache_delete().'; + $this->phpcsFile->addWarning( $message, $stackPtr, 'NoCaching' ); + } + + return $endOfStatement; + } + + /** + * Merge custom functions provided via a custom ruleset with the defaults, if we haven't already. + * + * @since 0.11.0 Split out from the `process()` method. + * + * @return void + */ + protected function mergeFunctionLists() { + if ( ! isset( $this->methods['all'] ) ) { + $this->methods['all'] = array_merge( $this->methods['cachable'], $this->methods['noncachable'] ); + } + + if ( $this->customCacheGetFunctions !== $this->addedCustomFunctions['cacheget'] ) { + $this->cacheGetFunctions = $this->merge_custom_array( + $this->customCacheGetFunctions, + $this->cacheGetFunctions + ); + + $this->addedCustomFunctions['cacheget'] = $this->customCacheGetFunctions; + } + + if ( $this->customCacheSetFunctions !== $this->addedCustomFunctions['cacheset'] ) { + $this->cacheSetFunctions = $this->merge_custom_array( + $this->customCacheSetFunctions, + $this->cacheSetFunctions + ); + + $this->addedCustomFunctions['cacheset'] = $this->customCacheSetFunctions; + } + + if ( $this->customCacheDeleteFunctions !== $this->addedCustomFunctions['cachedelete'] ) { + $this->cacheDeleteFunctions = $this->merge_custom_array( + $this->customCacheDeleteFunctions, + $this->cacheDeleteFunctions + ); + + $this->addedCustomFunctions['cachedelete'] = $this->customCacheDeleteFunctions; + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/PreparedSQLPlaceholdersSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/PreparedSQLPlaceholdersSniff.php new file mode 100644 index 00000000..8f184c6b --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/PreparedSQLPlaceholdersSniff.php @@ -0,0 +1,661 @@ +prepare method. + * + * Check the following issues: + * - The only placeholders supported are: %d, %f (%F) and %s and their variations. + * - Literal % signs need to be properly escaped as `%%`. + * - Simple placeholders (%d, %f, %F, %s) should be left unquoted in the query string. + * - Complex placeholders - numbered and formatted variants - will not be quoted + * automagically by $wpdb->prepare(), so if used for values, should be quoted in + * the query string. + * - Either an array of replacements should be passed matching the number of + * placeholders found or individual parameters for each placeholder should + * be passed. + * - Wildcards for LIKE compare values should be passed in via a replacement parameter. + * + * The sniff allows for a specific pattern with a variable number of placeholders + * created using code along the lines of: + * `sprintf( 'query .... IN (%s) ...', implode( ',', array_fill( 0, count( $something ), '%s' ) ) )`. + * + * A "PreparedSQLPlaceholders replacement count" whitelist comment is supported + * specifically to silence the `ReplacementsWrongNumber` and `UnfinishedPrepare` + * error codes. The other error codes are not affected by it. + * + * @link https://developer.wordpress.org/reference/classes/wpdb/prepare/ + * @link https://core.trac.wordpress.org/changeset/41496 + * @link https://core.trac.wordpress.org/changeset/41471 + * + * @package WPCS\WordPressCodingStandards + * + * @since 0.14.0 + */ +class PreparedSQLPlaceholdersSniff extends Sniff { + + /** + * These regexes copied from http://php.net/manual/en/function.sprintf.php#93552 + * and adjusted for limitations in `$wpdb->prepare()`. + * + * Near duplicate of the one used in the WP.I18n sniff, but with fewer types allowed. + * + * Note: The regex delimiters and modifiers are not included to allow this regex to be + * concatenated together with other regex partials. + * + * @since 0.14.0 + * + * @var string + */ + const PREPARE_PLACEHOLDER_REGEX = '(?: + (? true, + ); + + /** + * Storage for the stack pointer to the method call token. + * + * @since 0.14.0 + * + * @var int + */ + protected $methodPtr; + + /** + * Simple regex snippet to recognize and remember quotes. + * + * @since 0.14.0 + * + * @var string + */ + private $regex_quote = '["\']'; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 0.14.0 + * + * @return array + */ + public function register() { + return array( + \T_VARIABLE, + \T_STRING, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 0.14.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + public function process_token( $stackPtr ) { + + if ( ! $this->is_wpdb_method_call( $stackPtr, $this->target_methods ) ) { + return; + } + + $parameters = $this->get_function_call_parameters( $this->methodPtr ); + if ( empty( $parameters ) ) { + return; + } + + $query = $parameters[1]; + $text_string_tokens_found = false; + $variable_found = false; + $sql_wildcard_found = false; + $total_placeholders = 0; + $total_parameters = \count( $parameters ); + $valid_in_clauses = array( + 'uses_in' => 0, + 'implode_fill' => 0, + 'adjustment_count' => 0, + ); + + for ( $i = $query['start']; $i <= $query['end']; $i++ ) { + // Skip over groups of tokens if they are part of an inline function call. + if ( isset( $skip_from, $skip_to ) && $i >= $skip_from && $i < $skip_to ) { + $i = $skip_to; + continue; + } + + if ( ! isset( Tokens::$textStringTokens[ $this->tokens[ $i ]['code'] ] ) ) { + if ( \T_VARIABLE === $this->tokens[ $i ]['code'] ) { + if ( '$wpdb' !== $this->tokens[ $i ]['content'] ) { + $variable_found = true; + } + continue; + } + + // Detect a specific pattern for variable replacements in combination with `IN`. + if ( \T_STRING === $this->tokens[ $i ]['code'] ) { + + if ( 'sprintf' === strtolower( $this->tokens[ $i ]['content'] ) ) { + $sprintf_parameters = $this->get_function_call_parameters( $i ); + + if ( ! empty( $sprintf_parameters ) ) { + $skip_from = ( $sprintf_parameters[1]['end'] + 1 ); + $last_param = end( $sprintf_parameters ); + $skip_to = ( $last_param['end'] + 1 ); + + $valid_in_clauses['implode_fill'] += $this->analyse_sprintf( $sprintf_parameters ); + $valid_in_clauses['adjustment_count'] += ( \count( $sprintf_parameters ) - 1 ); + } + unset( $sprintf_parameters, $last_param ); + + } elseif ( 'implode' === strtolower( $this->tokens[ $i ]['content'] ) ) { + $prev = $this->phpcsFile->findPrevious( + Tokens::$textStringTokens, + ( $i - 1 ), + $query['start'] + ); + + $prev_content = $this->strip_quotes( $this->tokens[ $prev ]['content'] ); + $regex_quote = $this->get_regex_quote_snippet( $prev_content, $this->tokens[ $prev ]['content'] ); + + // Only examine the implode if preceded by an ` IN (`. + if ( preg_match( '`\s+IN\s*\(\s*(' . $regex_quote . ')?$`i', $prev_content, $match ) > 0 ) { + + if ( isset( $match[1] ) && $regex_quote !== $this->regex_quote ) { + $this->phpcsFile->addError( + 'Dynamic placeholder generation should not have surrounding quotes.', + $i, + 'QuotedDynamicPlaceholderGeneration' + ); + } + + if ( $this->analyse_implode( $i ) === true ) { + ++$valid_in_clauses['uses_in']; + ++$valid_in_clauses['implode_fill']; + + $next = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $i + 1 ), null, true ); + if ( \T_OPEN_PARENTHESIS === $this->tokens[ $next ]['code'] + && isset( $this->tokens[ $next ]['parenthesis_closer'] ) + ) { + $skip_from = ( $i + 1 ); + $skip_to = ( $this->tokens[ $next ]['parenthesis_closer'] + 1 ); + } + } + } + unset( $prev, $next, $prev_content, $regex_quote, $match ); + } + } + + continue; + } + + $text_string_tokens_found = true; + $content = $this->tokens[ $i ]['content']; + + $regex_quote = $this->regex_quote; + if ( isset( Tokens::$stringTokens[ $this->tokens[ $i ]['code'] ] ) ) { + $content = $this->strip_quotes( $content ); + $regex_quote = $this->get_regex_quote_snippet( $content, $this->tokens[ $i ]['content'] ); + } + + if ( \T_DOUBLE_QUOTED_STRING === $this->tokens[ $i ]['code'] + || \T_HEREDOC === $this->tokens[ $i ]['code'] + ) { + // Only interested in actual query text, so strip out variables. + $stripped_content = $this->strip_interpolated_variables( $content ); + if ( $stripped_content !== $content ) { + $interpolated_vars = $this->get_interpolated_variables( $content ); + $vars_without_wpdb = array_diff( $interpolated_vars, array( 'wpdb' ) ); + $content = $stripped_content; + + if ( ! empty( $vars_without_wpdb ) ) { + $variable_found = true; + } + } + unset( $stripped_content, $interpolated_vars, $vars_without_wpdb ); + } + + $placeholders = preg_match_all( '`' . self::PREPARE_PLACEHOLDER_REGEX . '`x', $content, $matches ); + if ( $placeholders > 0 ) { + $total_placeholders += $placeholders; + } + + /* + * Analyse the query for incorrect LIKE queries. + * + * - `LIKE %s` is the only correct one. + * - `LIKE '%s'` or `LIKE "%s"` will not be reported here, but in the quote check. + * - Any other `LIKE` statement should be reported, either for using `LIKE` without + * SQL wildcards or for not passing the SQL wildcards via the replacement. + */ + $regex = '`\s+LIKE\s*(?:(' . $regex_quote . ')(?!%s(?:\1|$))(?P.*?)(?:\1|$)|(?:concat\((?![^\)]*%s[^\)]*\))(?P[^\)]*))\))`i'; + if ( preg_match_all( $regex, $content, $matches ) > 0 ) { + $walk = array(); + if ( ! empty( $matches['content'] ) ) { + $matches['content'] = array_filter( $matches['content'] ); + if ( ! empty( $matches['content'] ) ) { + $walk[] = 'content'; + } + } + if ( ! empty( $matches['concat'] ) ) { + $matches['concat'] = array_filter( $matches['concat'] ); + if ( ! empty( $matches['concat'] ) ) { + $walk[] = 'concat'; + } + } + + if ( ! empty( $walk ) ) { + foreach ( $walk as $match_key ) { + foreach ( $matches[ $match_key ] as $index => $match ) { + $data = array( $matches[0][ $index ] ); + + // Both a `%` as well as a `_` are wildcards in SQL. + if ( strpos( $match, '%' ) === false && strpos( $match, '_' ) === false ) { + $this->phpcsFile->addWarning( + 'Unless you are using SQL wildcards, using LIKE is inefficient. Use a straight compare instead. Found: %s.', + $i, + 'LikeWithoutWildcards', + $data + ); + } else { + $sql_wildcard_found = true; + + if ( strpos( $match, '%s' ) === false ) { + $this->phpcsFile->addError( + 'SQL wildcards for a LIKE query should be passed in through a replacement parameter. Found: %s.', + $i, + 'LikeWildcardsInQuery', + $data + ); + } else { + $this->phpcsFile->addError( + 'SQL wildcards for a LIKE query should be passed in through a replacement parameter and the variable part of the replacement should be escaped using "esc_like()". Found: %s.', + $i, + 'LikeWildcardsInQueryWithPlaceholder', + $data + ); + } + } + + /* + * Don't throw `UnescapedLiteral`, `UnsupportedPlaceholder` or `QuotedPlaceholder` + * for this part of the SQL query. + */ + $content = preg_replace( '`' . preg_quote( $match, '`' ) . '`', '', $content, 1 ); + } + } + } + unset( $matches, $index, $match, $data ); + } + + if ( strpos( $content, '%' ) === false ) { + continue; + } + + /* + * Analyse the query for unsupported placeholders. + */ + if ( preg_match_all( self::UNSUPPORTED_PLACEHOLDER_REGEX, $content, $matches ) > 0 ) { + if ( ! empty( $matches[0] ) ) { + foreach ( $matches[0] as $match ) { + if ( '%' === $match ) { + $this->phpcsFile->addError( + 'Found unescaped literal "%%" character.', + $i, + 'UnescapedLiteral', + array( $match ) + ); + } else { + $this->phpcsFile->addError( + 'Unsupported placeholder used in $wpdb->prepare(). Found: "%s".', + $i, + 'UnsupportedPlaceholder', + array( $match ) + ); + } + } + } + unset( $match, $matches ); + } + + /* + * Analyse the query for quoted placeholders. + */ + $regex = '`(' . $regex_quote . ')%[dfFs]\1`'; + if ( preg_match_all( $regex, $content, $matches ) > 0 ) { + if ( ! empty( $matches[0] ) ) { + foreach ( $matches[0] as $match ) { + $this->phpcsFile->addError( + 'Simple placeholders should not be quoted in the query string in $wpdb->prepare(). Found: %s.', + $i, + 'QuotedSimplePlaceholder', + array( $match ) + ); + } + } + unset( $match, $matches ); + } + + /* + * Analyse the query for unquoted complex placeholders. + */ + $regex = '`(? 0 ) { + if ( ! empty( $matches[0] ) ) { + foreach ( $matches[0] as $match ) { + if ( preg_match( '`%[dfFs]`', $match ) !== 1 ) { + $this->phpcsFile->addWarning( + 'Complex placeholders used for values in the query string in $wpdb->prepare() will NOT be quoted automagically. Found: %s.', + $i, + 'UnquotedComplexPlaceholder', + array( $match ) + ); + } + } + } + unset( $match, $matches ); + } + + /* + * Check for an ` IN (%s)` clause. + */ + $found_in = preg_match_all( '`\s+IN\s*\(\s*%s\s*\)`i', $content, $matches ); + if ( $found_in > 0 ) { + $valid_in_clauses['uses_in'] += $found_in; + } + unset( $found_in ); + } + + if ( false === $text_string_tokens_found ) { + // Query string passed in as a variable or function call, nothing to examine. + return; + } + + $count_diff_whitelisted = $this->has_whitelist_comment( + 'PreparedSQLPlaceholders replacement count', + $stackPtr + ); + + if ( 0 === $total_placeholders ) { + if ( 1 === $total_parameters ) { + if ( false === $variable_found && false === $sql_wildcard_found ) { + /* + * Only throw this warning if the PreparedSQL sniff won't throw one about + * variables being found. + * Also don't throw it if we just advised to use a replacement variable to pass a + * string containing an SQL wildcard. + */ + $this->phpcsFile->addWarning( + 'It is not necessary to prepare a query which doesn\'t use variable replacement.', + $i, + 'UnnecessaryPrepare' + ); + } + } elseif ( false === $count_diff_whitelisted && 0 === $valid_in_clauses['uses_in'] ) { + $this->phpcsFile->addWarning( + 'Replacement variables found, but no valid placeholders found in the query.', + $i, + 'UnfinishedPrepare' + ); + } + + return; + } + + if ( 1 === $total_parameters ) { + $this->phpcsFile->addError( + 'Placeholders found in the query passed to $wpdb->prepare(), but no replacements found. Expected %d replacement(s) parameters.', + $stackPtr, + 'MissingReplacements', + array( $total_placeholders ) + ); + return; + } + + if ( true === $count_diff_whitelisted ) { + return; + } + + $replacements = $parameters; + array_shift( $replacements ); // Remove the query. + + // The parameters may have been passed as an array in parameter 2. + if ( isset( $parameters[2] ) && 2 === $total_parameters ) { + $next = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + $parameters[2]['start'], + ( $parameters[2]['end'] + 1 ), + true + ); + + if ( false !== $next + && ( \T_ARRAY === $this->tokens[ $next ]['code'] + || \T_OPEN_SHORT_ARRAY === $this->tokens[ $next ]['code'] ) + ) { + $replacements = $this->get_function_call_parameters( $next ); + } + } + + $total_replacements = \count( $replacements ); + $total_placeholders -= $valid_in_clauses['adjustment_count']; + + // Bow out when `IN` clauses have been used which appear to be correct. + if ( $valid_in_clauses['uses_in'] > 0 + && $valid_in_clauses['uses_in'] === $valid_in_clauses['implode_fill'] + && 1 === $total_replacements + ) { + return; + } + + /* + * Verify that the correct amount of replacements have been passed. + */ + if ( $total_replacements !== $total_placeholders ) { + $this->phpcsFile->addWarning( + 'Incorrect number of replacements passed to $wpdb->prepare(). Found %d replacement parameters, expected %d.', + $stackPtr, + 'ReplacementsWrongNumber', + array( $total_replacements, $total_placeholders ) + ); + } + } + + /** + * Retrieve a regex snippet to recognize and remember quotes based on the quote style + * used in the original string (if any). + * + * This allows for recognizing `"` and `\'` in single quoted strings, + * recognizing `'` and `\"` in double quotes strings and `'` and `"`when the quote + * style is unknown or it is a non-quoted string (heredoc/nowdoc and such). + * + * @since 0.14.0 + * + * @param string $stripped_content Text string content without surrounding quotes. + * @param string $original_content Original content for the same text string. + * + * @return string + */ + protected function get_regex_quote_snippet( $stripped_content, $original_content ) { + $regex_quote = $this->regex_quote; + + if ( $original_content !== $stripped_content ) { + $quote_style = $original_content[0]; + + if ( '"' === $quote_style ) { + $regex_quote = '\\\\"|\''; + } elseif ( "'" === $quote_style ) { + $regex_quote = '"|\\\\\''; + } + } + + return $regex_quote; + } + + /** + * Analyse a sprintf() query wrapper to see if it contains a specific code pattern + * to deal correctly with `IN` queries. + * + * The pattern we are searching for is: + * `sprintf( 'query ....', implode( ',', array_fill( 0, count( $something ), '%s' ) ) )` + * + * @since 0.14.0 + * + * @param array $sprintf_params Parameters details for the sprintf call. + * + * @return int The number of times the pattern was found in the replacements. + */ + protected function analyse_sprintf( $sprintf_params ) { + $found = 0; + + unset( $sprintf_params[1] ); + + foreach ( $sprintf_params as $sprintf_param ) { + if ( strpos( strtolower( $sprintf_param['raw'] ), 'implode' ) === false ) { + continue; + } + + $implode = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + $sprintf_param['start'], + $sprintf_param['end'], + true + ); + if ( \T_STRING === $this->tokens[ $implode ]['code'] + && 'implode' === strtolower( $this->tokens[ $implode ]['content'] ) + ) { + if ( $this->analyse_implode( $implode ) === true ) { + ++$found; + } + } + } + + return $found; + } + + /** + * Analyse an implode() function call to see if it contains a specific code pattern + * to dynamically create placeholders. + * + * The pattern we are searching for is: + * `implode( ',', array_fill( 0, count( $something ), '%s' ) )` + * + * This pattern presumes unquoted placeholders! + * + * @since 0.14.0 + * + * @param int $implode_token The stackPtr to the implode function call. + * + * @return bool True if the pattern is found, false otherwise. + */ + protected function analyse_implode( $implode_token ) { + $implode_params = $this->get_function_call_parameters( $implode_token ); + + if ( empty( $implode_params ) || \count( $implode_params ) !== 2 ) { + return false; + } + + if ( preg_match( '`^(["\']), ?\1$`', $implode_params[1]['raw'] ) !== 1 ) { + return false; + } + + if ( strpos( strtolower( $implode_params[2]['raw'] ), 'array_fill' ) === false ) { + return false; + } + + $array_fill = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + $implode_params[2]['start'], + $implode_params[2]['end'], + true + ); + + if ( \T_STRING !== $this->tokens[ $array_fill ]['code'] + || 'array_fill' !== strtolower( $this->tokens[ $array_fill ]['content'] ) + ) { + return false; + } + + $array_fill_params = $this->get_function_call_parameters( $array_fill ); + + if ( empty( $array_fill_params ) || \count( $array_fill_params ) !== 3 ) { + return false; + } + + return (bool) preg_match( '`^(["\'])%[dfFs]\1$`', $array_fill_params[3]['raw'] ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/PreparedSQLSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/PreparedSQLSniff.php new file mode 100644 index 00000000..1fe2ad96 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/PreparedSQLSniff.php @@ -0,0 +1,210 @@ + true, + 'get_col' => true, + 'get_row' => true, + 'get_results' => true, + 'prepare' => true, + 'query' => true, + ); + + /** + * Tokens that we don't flag when they are found in a $wpdb method call. + * + * @since 0.9.0 + * + * @var array + */ + protected $ignored_tokens = array( + \T_OBJECT_OPERATOR => true, + \T_OPEN_PARENTHESIS => true, + \T_CLOSE_PARENTHESIS => true, + \T_STRING_CONCAT => true, + \T_CONSTANT_ENCAPSED_STRING => true, + \T_OPEN_SQUARE_BRACKET => true, + \T_CLOSE_SQUARE_BRACKET => true, + \T_COMMA => true, + \T_LNUMBER => true, + \T_START_HEREDOC => true, + \T_END_HEREDOC => true, + \T_START_NOWDOC => true, + \T_NOWDOC => true, + \T_END_NOWDOC => true, + \T_INT_CAST => true, + \T_DOUBLE_CAST => true, + \T_BOOL_CAST => true, + \T_NS_SEPARATOR => true, + ); + + /** + * A loop pointer. + * + * It is a property so that we can access it in all of our methods. + * + * @since 0.9.0 + * + * @var int + */ + protected $i; + + /** + * The loop end marker. + * + * It is a property so that we can access it in all of our methods. + * + * @since 0.9.0 + * + * @var int + */ + protected $end; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 0.8.0 + * + * @return array + */ + public function register() { + + $this->ignored_tokens += Tokens::$emptyTokens; + + return array( + \T_VARIABLE, + \T_STRING, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 0.8.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + + if ( ! $this->is_wpdb_method_call( $stackPtr, $this->methods ) ) { + return; + } + + if ( $this->has_whitelist_comment( 'unprepared SQL', $stackPtr ) ) { + return; + } + + for ( $this->i; $this->i < $this->end; $this->i++ ) { + + if ( isset( $this->ignored_tokens[ $this->tokens[ $this->i ]['code'] ] ) ) { + continue; + } + + if ( \T_DOUBLE_QUOTED_STRING === $this->tokens[ $this->i ]['code'] + || \T_HEREDOC === $this->tokens[ $this->i ]['code'] + ) { + + $bad_variables = array_filter( + $this->get_interpolated_variables( $this->tokens[ $this->i ]['content'] ), + function ( $symbol ) { + return ( 'wpdb' !== $symbol ); + } + ); + + foreach ( $bad_variables as $bad_variable ) { + $this->phpcsFile->addError( + 'Use placeholders and $wpdb->prepare(); found interpolated variable $%s at %s', + $this->i, + 'InterpolatedNotPrepared', + array( + $bad_variable, + $this->tokens[ $this->i ]['content'], + ) + ); + } + continue; + } + + if ( \T_VARIABLE === $this->tokens[ $this->i ]['code'] ) { + if ( '$wpdb' === $this->tokens[ $this->i ]['content'] ) { + $this->is_wpdb_method_call( $this->i, $this->methods ); + continue; + } + + if ( $this->is_safe_casted( $this->i ) ) { + continue; + } + } + + if ( \T_STRING === $this->tokens[ $this->i ]['code'] ) { + + if ( + isset( $this->SQLEscapingFunctions[ $this->tokens[ $this->i ]['content'] ] ) + || isset( $this->SQLAutoEscapedFunctions[ $this->tokens[ $this->i ]['content'] ] ) + ) { + + // Find the opening parenthesis. + $opening_paren = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $this->i + 1 ), null, true, null, true ); + + if ( false !== $opening_paren + && \T_OPEN_PARENTHESIS === $this->tokens[ $opening_paren ]['code'] + && isset( $this->tokens[ $opening_paren ]['parenthesis_closer'] ) + ) { + // Skip past the end of the function. + $this->i = $this->tokens[ $opening_paren ]['parenthesis_closer']; + continue; + } + } elseif ( isset( $this->formattingFunctions[ $this->tokens[ $this->i ]['content'] ] ) ) { + continue; + } + } + + $this->phpcsFile->addError( + 'Use placeholders and $wpdb->prepare(); found %s', + $this->i, + 'NotPrepared', + array( $this->tokens[ $this->i ]['content'] ) + ); + } + + return $this->end; + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/RestrictedClassesSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/RestrictedClassesSniff.php new file mode 100644 index 00000000..5ae13ee5 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/RestrictedClassesSniff.php @@ -0,0 +1,60 @@ + array( + * 'lambda' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Avoid direct calls to the database.', + * 'classes' => array( 'PDO', '\Namespace\Classname' ), + * ) + * ) + * + * @return array + */ + public function getGroups() { + return array( + + 'mysql' => array( + 'type' => 'error', + 'message' => 'Accessing the database directly should be avoided. Please use the $wpdb object and associated functions instead. Found: %s.', + 'classes' => array( + 'mysqli', + 'PDO', + 'PDOStatement', + ), + ), + + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/RestrictedFunctionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/RestrictedFunctionsSniff.php new file mode 100644 index 00000000..dbab49fe --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/RestrictedFunctionsSniff.php @@ -0,0 +1,66 @@ + array( + * 'lambda' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Use anonymous functions instead please!', + * 'functions' => array( 'file_get_contents', 'create_function' ), + * ) + * ) + * + * @return array + */ + public function getGroups() { + return array( + + 'mysql' => array( + 'type' => 'error', + 'message' => 'Accessing the database directly should be avoided. Please use the $wpdb object and associated functions instead. Found: %s.', + 'functions' => array( + 'mysql_*', + 'mysqli_*', + 'mysqlnd_ms_*', + 'mysqlnd_qc_*', + 'mysqlnd_uh_*', + 'mysqlnd_memcache_*', + 'maxdb_*', + ), + 'whitelist' => array( + 'mysql_to_rfc3339' => true, + ), + ), + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/SlowDBQuerySniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/SlowDBQuerySniff.php new file mode 100644 index 00000000..4feebdcb --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DB/SlowDBQuerySniff.php @@ -0,0 +1,86 @@ + array( + 'type' => 'warning', + 'message' => 'Detected usage of %s, possible slow query.', + 'keys' => array( + 'tax_query', + 'meta_query', + 'meta_key', + 'meta_value', + ), + ), + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 0.10.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + + if ( $this->has_whitelist_comment( 'slow query', $stackPtr ) ) { + return; + } elseif ( $this->has_whitelist_comment( 'tax_query', $stackPtr ) ) { + return; + } + + return parent::process_token( $stackPtr ); + } + + /** + * Callback to process each confirmed key, to check value. + * This must be extended to add the logic to check assignment value. + * + * @param string $key Array index / key. + * @param mixed $val Assigned value. + * @param int $line Token line. + * @param array $group Group definition. + * @return mixed FALSE if no match, TRUE if matches, STRING if matches + * with custom error message passed to ->process(). + */ + public function callback( $key, $val, $line, $group ) { + return true; + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DateTime/CurrentTimeTimestampSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DateTime/CurrentTimeTimestampSniff.php new file mode 100644 index 00000000..180ae240 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DateTime/CurrentTimeTimestampSniff.php @@ -0,0 +1,174 @@ + => + */ + protected $target_functions = array( + 'current_time' => true, + ); + + /** + * Process the parameters of a matched function. + * + * @since 2.2.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + /* + * We already know there will be valid open & close parentheses as otherwise the parameter + * retrieval function call would have returned an empty array, so no additional checks needed. + */ + $open_parens = $this->phpcsFile->findNext( \T_OPEN_PARENTHESIS, $stackPtr ); + $close_parens = $this->tokens[ $open_parens ]['parenthesis_closer']; + + /* + * Check whether the first parameter is a timestamp format. + */ + for ( $i = $parameters[1]['start']; $i <= $parameters[1]['end']; $i++ ) { + if ( isset( Tokens::$emptyTokens[ $this->tokens[ $i ]['code'] ] ) ) { + continue; + } + + if ( isset( Tokens::$textStringTokens[ $this->tokens[ $i ]['code'] ] ) ) { + $content_first = trim( $this->strip_quotes( $this->tokens[ $i ]['content'] ) ); + if ( 'U' !== $content_first && 'timestamp' !== $content_first ) { + // Most likely valid use of current_time(). + return; + } + + continue; + } + + if ( isset( Tokens::$heredocTokens[ $this->tokens[ $i ]['code'] ] ) ) { + continue; + } + + /* + * If we're still here, we've encountered an unexpected token, like a variable or + * function call. Bow out as we can't determine the runtime value. + */ + return; + } + + $gmt_true = false; + + /* + * Check whether the second parameter, $gmt, is a set to `true` or `1`. + */ + if ( isset( $parameters[2] ) ) { + $content_second = ''; + if ( 'true' === $parameters[2]['raw'] || '1' === $parameters[2]['raw'] ) { + $content_second = $parameters[2]['raw']; + $gmt_true = true; + } else { + // Do a more extensive parameter check. + for ( $i = $parameters[2]['start']; $i <= $parameters[2]['end']; $i++ ) { + if ( isset( Tokens::$emptyTokens[ $this->tokens[ $i ]['code'] ] ) ) { + continue; + } + + $content_second .= $this->tokens[ $i ]['content']; + } + + if ( 'true' === $content_second || '1' === $content_second ) { + $gmt_true = true; + } + } + } + + /* + * Non-UTC timestamp requested. + */ + if ( false === $gmt_true ) { + $this->phpcsFile->addWarning( + 'Calling current_time() with a $type of "timestamp" or "U" is strongly discouraged as it will not return a Unix (UTC) timestamp. Please consider using a non-timestamp format or otherwise refactoring this code.', + $stackPtr, + 'Requested' + ); + + return; + } + + /* + * UTC timestamp requested. Should use time() instead. + */ + $has_comment = $this->phpcsFile->findNext( Tokens::$commentTokens, ( $stackPtr + 1 ), ( $close_parens + 1 ) ); + $error = 'Don\'t use current_time() for retrieving a Unix (UTC) timestamp. Use time() instead. Found: %s'; + $error_code = 'RequestedUTC'; + + $code_snippet = "current_time( '" . $content_first . "'"; + if ( isset( $content_second ) ) { + $code_snippet .= ', ' . $content_second; + } + $code_snippet .= ' )'; + + if ( false !== $has_comment ) { + // If there are comments, we don't auto-fix as it would remove those comments. + $this->phpcsFile->addError( $error, $stackPtr, $error_code, array( $code_snippet ) ); + + return; + } + + $fix = $this->phpcsFile->addFixableError( $error, $stackPtr, $error_code, array( $code_snippet ) ); + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + + for ( $i = ( $stackPtr + 1 ); $i < $close_parens; $i++ ) { + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + + $this->phpcsFile->fixer->replaceToken( $stackPtr, 'time(' ); + $this->phpcsFile->fixer->endChangeset(); + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DateTime/RestrictedFunctionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DateTime/RestrictedFunctionsSniff.php new file mode 100644 index 00000000..0da07019 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/DateTime/RestrictedFunctionsSniff.php @@ -0,0 +1,62 @@ + array( + 'type' => 'error', + 'message' => 'Using %s() and similar isn\'t allowed, instead use WP internal timezone support.', + 'functions' => array( + 'date_default_timezone_set', + ), + ), + + /* + * Use gmdate(), not date(). + * Don't rely on the current PHP time zone as it might have been changed by third party code. + * + * @link https://make.wordpress.org/core/2019/09/23/date-time-improvements-wp-5-3/ + * @link https://core.trac.wordpress.org/ticket/46438 + * @link https://github.com/WordPress/WordPress-Coding-Standards/issues/1713 + */ + 'date' => array( + 'type' => 'error', + 'message' => '%s() is affected by runtime timezone changes which can cause date/time to be incorrectly displayed. Use gmdate() instead.', + 'functions' => array( + 'date', + ), + ), + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Files/FileNameSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Files/FileNameSniff.php new file mode 100644 index 00000000..598bc39b --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Files/FileNameSniff.php @@ -0,0 +1,248 @@ + true, + 'class.wp-scripts.php' => true, + 'class.wp-styles.php' => true, + ); + + /** + * Unit test version of the historical exceptions in WP core. + * + * @since 0.11.0 + * + * @var array + */ + private $unittest_class_exceptions = array( + 'class.wp-dependencies.inc' => true, + 'class.wp-scripts.inc' => true, + 'class.wp-styles.inc' => true, + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + if ( \defined( '\PHP_CODESNIFFER_IN_TESTS' ) ) { + $this->class_exceptions = array_merge( $this->class_exceptions, $this->unittest_class_exceptions ); + } + + return array( + \T_OPEN_TAG, + \T_OPEN_TAG_WITH_ECHO, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + + // Usage of `strip_quotes` is to ensure `stdin_path` passed by IDEs does not include quotes. + $file = $this->strip_quotes( $this->phpcsFile->getFileName() ); + if ( 'STDIN' === $file ) { + return; + } + + // Respect phpcs:disable comments as long as they are not accompanied by an enable (PHPCS 3.2+). + if ( \defined( '\T_PHPCS_DISABLE' ) && \defined( '\T_PHPCS_ENABLE' ) ) { + $i = -1; + while ( $i = $this->phpcsFile->findNext( \T_PHPCS_DISABLE, ( $i + 1 ) ) ) { + if ( empty( $this->tokens[ $i ]['sniffCodes'] ) + || isset( $this->tokens[ $i ]['sniffCodes']['WordPress'] ) + || isset( $this->tokens[ $i ]['sniffCodes']['WordPress.Files'] ) + || isset( $this->tokens[ $i ]['sniffCodes']['WordPress.Files.FileName'] ) + ) { + do { + $i = $this->phpcsFile->findNext( \T_PHPCS_ENABLE, ( $i + 1 ) ); + } while ( false !== $i + && ! empty( $this->tokens[ $i ]['sniffCodes'] ) + && ! isset( $this->tokens[ $i ]['sniffCodes']['WordPress'] ) + && ! isset( $this->tokens[ $i ]['sniffCodes']['WordPress.Files'] ) + && ! isset( $this->tokens[ $i ]['sniffCodes']['WordPress.Files.FileName'] ) ); + + if ( false === $i ) { + // The entire (rest of the) file is disabled. + return; + } + } + } + } + + $fileName = basename( $file ); + $expected = strtolower( str_replace( '_', '-', $fileName ) ); + + /* + * Generic check for lowercase hyphenated file names. + */ + if ( $fileName !== $expected && ( false === $this->is_theme || 1 !== preg_match( self::THEME_EXCEPTIONS_REGEX, $fileName ) ) ) { + $this->phpcsFile->addError( + 'Filenames should be all lowercase with hyphens as word separators. Expected %s, but found %s.', + 0, + 'NotHyphenatedLowercase', + array( $expected, $fileName ) + ); + } + unset( $expected ); + + /* + * Check files containing a class for the "class-" prefix and that the rest of + * the file name reflects the class name. + */ + if ( true === $this->strict_class_file_names ) { + $has_class = $this->phpcsFile->findNext( \T_CLASS, $stackPtr ); + if ( false !== $has_class && false === $this->is_test_class( $has_class ) ) { + $class_name = $this->phpcsFile->getDeclarationName( $has_class ); + $expected = 'class-' . strtolower( str_replace( '_', '-', $class_name ) ); + + if ( substr( $fileName, 0, -4 ) !== $expected && ! isset( $this->class_exceptions[ $fileName ] ) ) { + $this->phpcsFile->addError( + 'Class file names should be based on the class name with "class-" prepended. Expected %s, but found %s.', + 0, + 'InvalidClassFileName', + array( + $expected . '.php', + $fileName, + ) + ); + } + unset( $expected ); + } + } + + /* + * Check non-class files in "wp-includes" with a "@subpackage Template" tag for a "-template" suffix. + */ + if ( false !== strpos( $file, \DIRECTORY_SEPARATOR . 'wp-includes' . \DIRECTORY_SEPARATOR ) ) { + $subpackage_tag = $this->phpcsFile->findNext( \T_DOC_COMMENT_TAG, $stackPtr, null, false, '@subpackage' ); + if ( false !== $subpackage_tag ) { + $subpackage = $this->phpcsFile->findNext( \T_DOC_COMMENT_STRING, $subpackage_tag ); + if ( false !== $subpackage ) { + $fileName_end = substr( $fileName, -13 ); + $has_class = $this->phpcsFile->findNext( \T_CLASS, $stackPtr ); + + if ( ( 'Template' === trim( $this->tokens[ $subpackage ]['content'] ) + && $this->tokens[ $subpackage_tag ]['line'] === $this->tokens[ $subpackage ]['line'] ) + && ( ( ! \defined( '\PHP_CODESNIFFER_IN_TESTS' ) && '-template.php' !== $fileName_end ) + || ( \defined( '\PHP_CODESNIFFER_IN_TESTS' ) && '-template.inc' !== $fileName_end ) ) + && false === $has_class + ) { + $this->phpcsFile->addError( + 'Files containing template tags should have "-template" appended to the end of the file name. Expected %s, but found %s.', + 0, + 'InvalidTemplateTagFileName', + array( + substr( $fileName, 0, -4 ) . '-template.php', + $fileName, + ) + ); + } + } + } + } + + // Only run this sniff once per file, no need to run it again. + return ( $this->phpcsFile->numTokens + 1 ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php new file mode 100644 index 00000000..8374faf6 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php @@ -0,0 +1,1046 @@ + true, + 'wp' => true, + '_' => true, + 'php' => true, // See #1728, the 'php' prefix is reserved by PHP itself. + ); + + /** + * Target prefixes after validation. + * + * All prefixes are lowercased for case-insensitive compare. + * + * @since 0.12.0 + * + * @var string[] + */ + private $validated_prefixes = array(); + + /** + * Target namespace prefixes after validation with regex indicator. + * + * All prefixes are lowercased for case-insensitive compare. + * If the prefix doesn't already contain a namespace separator, but does contain + * non-word characters, these will have been replaced with regex syntax to allow + * for namespace separators and the `is_regex` indicator will have been set to `true`. + * + * @since 1.2.0 + * + * @var array + */ + private $validated_namespace_prefixes = array(); + + /** + * Cache of previously set prefixes. + * + * Prevents having to do the same prefix validation over and over again. + * + * @since 0.12.0 + * + * @var string[] + */ + private $previous_prefixes = array(); + + /** + * A list of all PHP superglobals with the exception of $GLOBALS which is handled separately. + * + * @since 0.12.0 + * + * @var array + */ + protected $superglobals = array( + '_COOKIE' => true, + '_ENV' => true, + '_GET' => true, + '_FILES' => true, + '_POST' => true, + '_REQUEST' => true, + '_SERVER' => true, + '_SESSION' => true, + ); + + /** + * A list of core hooks that are allowed to be called by plugins and themes. + * + * @since 0.14.0 + * + * @var array + */ + protected $whitelisted_core_hooks = array( + 'widget_title' => true, + 'add_meta_boxes' => true, + ); + + /** + * A list of core constants that are allowed to be defined by plugins and themes. + * + * @since 1.0.0 + * + * Source: {@link https://core.trac.wordpress.org/browser/trunk/src/wp-includes/default-constants.php#L0} + * The constants are listed in the order they are found in the source file + * to make life easier for future updates. + * Only overrulable constants are listed, i.e. those defined within core within + * a `if ( ! defined() ) {}` wrapper. + * + * @var array + */ + protected $whitelisted_core_constants = array( + 'WP_MEMORY_LIMIT' => true, + 'WP_MAX_MEMORY_LIMIT' => true, + 'WP_CONTENT_DIR' => true, + 'WP_DEBUG' => true, + 'WP_DEBUG_DISPLAY' => true, + 'WP_DEBUG_LOG' => true, + 'WP_CACHE' => true, + 'SCRIPT_DEBUG' => true, + 'MEDIA_TRASH' => true, + 'SHORTINIT' => true, + 'WP_CONTENT_URL' => true, + 'WP_PLUGIN_DIR' => true, + 'WP_PLUGIN_URL' => true, + 'PLUGINDIR' => true, + 'WPMU_PLUGIN_DIR' => true, + 'WPMU_PLUGIN_URL' => true, + 'MUPLUGINDIR' => true, + 'COOKIEHASH' => true, + 'USER_COOKIE' => true, + 'PASS_COOKIE' => true, + 'AUTH_COOKIE' => true, + 'SECURE_AUTH_COOKIE' => true, + 'LOGGED_IN_COOKIE' => true, + 'TEST_COOKIE' => true, + 'COOKIEPATH' => true, + 'SITECOOKIEPATH' => true, + 'ADMIN_COOKIE_PATH' => true, + 'PLUGINS_COOKIE_PATH' => true, + 'COOKIE_DOMAIN' => true, + 'RECOVERY_MODE_COOKIE' => true, + 'FORCE_SSL_ADMIN' => true, + 'FORCE_SSL_LOGIN' => true, + 'AUTOSAVE_INTERVAL' => true, + 'EMPTY_TRASH_DAYS' => true, + 'WP_POST_REVISIONS' => true, + 'WP_CRON_LOCK_TIMEOUT' => true, + 'WP_DEFAULT_THEME' => true, + ); + + /** + * List of all PHP native functions. + * + * Using this list rather than a call to `function_exists()` prevents + * false negatives from user-defined functions when those would be + * autoloaded via a Composer autoload files directives. + * + * @var array + */ + private $built_in_functions; + + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 0.12.0 + * + * @return array + */ + public function register() { + // Get a list of all PHP native functions. + $all_functions = get_defined_functions(); + $this->built_in_functions = array_flip( $all_functions['internal'] ); + + // Set the sniff targets. + $targets = array( + \T_NAMESPACE => \T_NAMESPACE, + \T_FUNCTION => \T_FUNCTION, + \T_CONST => \T_CONST, + \T_VARIABLE => \T_VARIABLE, + \T_DOLLAR => \T_DOLLAR, // Variable variables. + \T_LIST => \T_LIST, + \T_OPEN_SHORT_ARRAY => \T_OPEN_SHORT_ARRAY, + ); + $targets += Tokens::$ooScopeTokens; // T_ANON_CLASS is only used for skipping over test classes. + + // Add function call target for hook names and constants defined using define(). + $parent = parent::register(); + if ( ! empty( $parent ) ) { + $targets[] = \T_STRING; + } + + return $targets; + } + + /** + * Groups of functions to restrict. + * + * @since 0.12.0 + * + * @return array + */ + public function getGroups() { + $this->target_functions = $this->hookInvokeFunctions; + unset( + $this->target_functions['do_action_deprecated'], + $this->target_functions['apply_filters_deprecated'] + ); + + $this->target_functions['define'] = true; + + return parent::getGroups(); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 0.12.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + /* + * Allow for whitelisting. + * + * Generally speaking a theme/plugin should *only* execute their own hooks, but there may be a + * good reason to execute a core hook. + * + * Similarly, newer PHP or WP functions or constants may need to be emulated for continued support + * of older PHP and WP versions. + */ + if ( $this->has_whitelist_comment( 'prefix', $stackPtr ) ) { + return; + } + + // Allow overruling the prefixes set in a ruleset via the command line. + $cl_prefixes = trim( PHPCSHelper::get_config_data( 'prefixes' ) ); + if ( ! empty( $cl_prefixes ) ) { + $this->prefixes = array_filter( array_map( 'trim', explode( ',', $cl_prefixes ) ) ); + } + + $this->prefixes = $this->merge_custom_array( $this->prefixes, array(), false ); + if ( empty( $this->prefixes ) ) { + // No prefixes passed, nothing to do. + return; + } + + $this->validate_prefixes(); + if ( empty( $this->validated_prefixes ) ) { + // No _valid_ prefixes passed, nothing to do. + return; + } + + // Ignore test classes. + if ( isset( Tokens::$ooScopeTokens[ $this->tokens[ $stackPtr ]['code'] ] ) + && true === $this->is_test_class( $stackPtr ) + ) { + if ( $this->tokens[ $stackPtr ]['scope_condition'] === $stackPtr && isset( $this->tokens[ $stackPtr ]['scope_closer'] ) ) { + // Skip forward to end of test class. + return $this->tokens[ $stackPtr ]['scope_closer']; + } + return; + } + + if ( \T_ANON_CLASS === $this->tokens[ $stackPtr ]['code'] ) { + // Token was only registered to allow skipping over test classes. + return; + } + + if ( \T_STRING === $this->tokens[ $stackPtr ]['code'] ) { + // Disallow excluding function groups for this sniff. + $this->exclude = array(); + + return parent::process_token( $stackPtr ); + + } elseif ( \T_DOLLAR === $this->tokens[ $stackPtr ]['code'] ) { + + return $this->process_variable_variable( $stackPtr ); + + } elseif ( \T_VARIABLE === $this->tokens[ $stackPtr ]['code'] ) { + + return $this->process_variable_assignment( $stackPtr ); + + } elseif ( \T_LIST === $this->tokens[ $stackPtr ]['code'] + || \T_OPEN_SHORT_ARRAY === $this->tokens[ $stackPtr ]['code'] + ) { + return $this->process_list_assignment( $stackPtr ); + + } elseif ( \T_NAMESPACE === $this->tokens[ $stackPtr ]['code'] ) { + $namespace_name = $this->get_declared_namespace_name( $stackPtr ); + + if ( false === $namespace_name || '' === $namespace_name || '\\' === $namespace_name ) { + return; + } + + foreach ( $this->validated_namespace_prefixes as $key => $prefix_info ) { + if ( false === $prefix_info['is_regex'] ) { + if ( stripos( $namespace_name, $prefix_info['prefix'] ) === 0 ) { + $this->phpcsFile->recordMetric( $stackPtr, 'Prefix all globals: allowed prefixes', $key ); + return; + } + } else { + // Ok, so this prefix should be used as a regex. + $regex = '`^' . $prefix_info['prefix'] . '`i'; + if ( preg_match( $regex, $namespace_name ) > 0 ) { + $this->phpcsFile->recordMetric( $stackPtr, 'Prefix all globals: allowed prefixes', $key ); + return; + } + } + } + + // Still here ? In that case, we have a non-prefixed namespace name. + $recorded = $this->phpcsFile->addError( + self::ERROR_MSG, + $stackPtr, + 'NonPrefixedNamespaceFound', + array( + 'Namespaces declared', + $namespace_name, + ) + ); + + if ( true === $recorded ) { + $this->record_potential_prefix_metric( $stackPtr, $namespace_name ); + } + + return; + + } else { + + // Namespaced methods, classes and constants do not need to be prefixed. + $namespace = $this->determine_namespace( $stackPtr ); + if ( '' !== $namespace && '\\' !== $namespace ) { + return; + } + + $item_name = ''; + $error_text = 'Unknown syntax used'; + $error_code = 'NonPrefixedSyntaxFound'; + + switch ( $this->tokens[ $stackPtr ]['type'] ) { + case 'T_FUNCTION': + // Methods in a class do not need to be prefixed. + if ( $this->phpcsFile->hasCondition( $stackPtr, Tokens::$ooScopeTokens ) === true ) { + return; + } + + if ( $this->is_function_deprecated( $this->phpcsFile, $stackPtr ) === true ) { + /* + * Deprecated functions don't have to comply with the naming conventions, + * otherwise functions deprecated in favour of a function with a compliant + * name would still trigger an error. + */ + return; + } + + $item_name = $this->phpcsFile->getDeclarationName( $stackPtr ); + if ( isset( $this->built_in_functions[ $item_name ] ) ) { + // Backfill for PHP native function. + return; + } + + $error_text = 'Functions declared in the global namespace'; + $error_code = 'NonPrefixedFunctionFound'; + break; + + case 'T_CLASS': + case 'T_INTERFACE': + case 'T_TRAIT': + $item_name = $this->phpcsFile->getDeclarationName( $stackPtr ); + $error_text = 'Classes declared'; + $error_code = 'NonPrefixedClassFound'; + + switch ( $this->tokens[ $stackPtr ]['type'] ) { + case 'T_CLASS': + if ( class_exists( '\\' . $item_name, false ) ) { + // Backfill for PHP native class. + return; + } + break; + + case 'T_INTERFACE': + if ( interface_exists( '\\' . $item_name, false ) ) { + // Backfill for PHP native interface. + return; + } + + $error_text = 'Interfaces declared'; + $error_code = 'NonPrefixedInterfaceFound'; + break; + + case 'T_TRAIT': + // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.trait_existsFound + if ( function_exists( '\trait_exists' ) && trait_exists( '\\' . $item_name, false ) ) { + // Backfill for PHP native trait. + return; + } + + $error_text = 'Traits declared'; + $error_code = 'NonPrefixedTraitFound'; + break; + + default: + // Left empty on purpose. + break; + } + + break; + + case 'T_CONST': + // Constants in a class do not need to be prefixed. + if ( true === $this->is_class_constant( $stackPtr ) ) { + return; + } + + $constant_name_ptr = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true, null, true ); + if ( false === $constant_name_ptr ) { + // Live coding. + return; + } + + $item_name = $this->tokens[ $constant_name_ptr ]['content']; + if ( \defined( '\\' . $item_name ) ) { + // Backfill for PHP native constant. + return; + } + + if ( isset( $this->whitelisted_core_constants[ $item_name ] ) ) { + // Defining a WP Core constant intended for overruling. + return; + } + + $error_text = 'Global constants defined'; + $error_code = 'NonPrefixedConstantFound'; + break; + + default: + // Left empty on purpose. + break; + + } + + if ( empty( $item_name ) || $this->is_prefixed( $stackPtr, $item_name ) === true ) { + return; + } + + $recorded = $this->phpcsFile->addError( + self::ERROR_MSG, + $stackPtr, + $error_code, + array( + $error_text, + $item_name, + ) + ); + + if ( true === $recorded ) { + $this->record_potential_prefix_metric( $stackPtr, $item_name ); + } + } + } + + /** + * Handle variable variables defined in the global namespace. + * + * @since 0.12.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + protected function process_variable_variable( $stackPtr ) { + static $indicators = array( + \T_OPEN_CURLY_BRACKET => true, + \T_VARIABLE => true, + ); + + // Is this a variable variable ? + // Not concerned with nested ones as those will be recognized on their own token. + $next_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true, null, true ); + if ( false === $next_non_empty || ! isset( $indicators[ $this->tokens[ $next_non_empty ]['code'] ] ) ) { + return; + } + + if ( \T_OPEN_CURLY_BRACKET === $this->tokens[ $next_non_empty ]['code'] + && isset( $this->tokens[ $next_non_empty ]['bracket_closer'] ) + ) { + // Skip over the variable part. + $next_non_empty = $this->tokens[ $next_non_empty ]['bracket_closer']; + } + + $maybe_assignment = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $next_non_empty + 1 ), null, true, null, true ); + + while ( false !== $maybe_assignment + && \T_OPEN_SQUARE_BRACKET === $this->tokens[ $maybe_assignment ]['code'] + && isset( $this->tokens[ $maybe_assignment ]['bracket_closer'] ) + ) { + $maybe_assignment = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + ( $this->tokens[ $maybe_assignment ]['bracket_closer'] + 1 ), + null, + true, + null, + true + ); + } + + if ( false === $maybe_assignment ) { + return; + } + + if ( ! isset( Tokens::$assignmentTokens[ $this->tokens[ $maybe_assignment ]['code'] ] ) ) { + // Not an assignment. + return; + } + + $error = self::ERROR_MSG; + + /* + * Local variable variables in a function do not need to be prefixed. + * But a variable variable could evaluate to the name of an imported global + * variable. + * Not concerned with imported variable variables (global.. ) as that has been + * forbidden since PHP 7.0. Presuming cross-version code and if not, that + * is for the PHPCompatibility standard to detect. + */ + if ( $this->phpcsFile->hasCondition( $stackPtr, array( \T_FUNCTION, \T_CLOSURE ) ) === true ) { + $condition = $this->phpcsFile->getCondition( $stackPtr, \T_FUNCTION ); + if ( false === $condition ) { + $condition = $this->phpcsFile->getCondition( $stackPtr, \T_CLOSURE ); + } + + $has_global = $this->phpcsFile->findPrevious( \T_GLOBAL, ( $stackPtr - 1 ), $this->tokens[ $condition ]['scope_opener'] ); + if ( false === $has_global ) { + // No variable import happening. + return; + } + + $error = 'Variable variable which could potentially override an imported global variable detected. ' . $error; + } + + $variable_name = $this->phpcsFile->getTokensAsString( $stackPtr, ( ( $next_non_empty - $stackPtr ) + 1 ) ); + + // Still here ? In that case, the variable name should be prefixed. + $recorded = $this->phpcsFile->addWarning( + $error, + $stackPtr, + 'NonPrefixedVariableFound', + array( + 'Global variables defined', + $variable_name, + ) + ); + + if ( true === $recorded ) { + $this->record_potential_prefix_metric( $stackPtr, $variable_name ); + } + + // Skip over the variable part of the variable. + return ( $next_non_empty + 1 ); + } + + /** + * Check that defined global variables are prefixed. + * + * @since 0.12.0 + * @since 2.2.0 Added $in_list parameter. + * + * @param int $stackPtr The position of the current token in the stack. + * @param bool $in_list Whether or not this is a variable in a list assignment. + * Defaults to false. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + protected function process_variable_assignment( $stackPtr, $in_list = false ) { + /* + * We're only concerned with variables which are being defined. + * `is_assigment()` will not recognize property assignments, which is good in this case. + * However it will also not recognize $b in `foreach( $a as $b )` as an assignment, so + * we need a separate check for that. + */ + if ( false === $in_list + && false === $this->is_assignment( $stackPtr ) + && false === $this->is_foreach_as( $stackPtr ) + ) { + return; + } + + $is_error = true; + $variable_name = substr( $this->tokens[ $stackPtr ]['content'], 1 ); // Strip the dollar sign. + + // Bow out early if we know for certain no prefix is needed. + if ( $this->variable_prefixed_or_whitelisted( $stackPtr, $variable_name ) === true ) { + return; + } + + if ( 'GLOBALS' === $variable_name ) { + $array_open = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true, null, true ); + if ( false === $array_open || \T_OPEN_SQUARE_BRACKET !== $this->tokens[ $array_open ]['code'] ) { + // Live coding or something very silly. + return; + } + + $array_key = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $array_open + 1 ), null, true, null, true ); + if ( false === $array_key ) { + // No key found, nothing to do. + return; + } + + $stackPtr = $array_key; + $variable_name = $this->strip_quotes( $this->tokens[ $array_key ]['content'] ); + + // Check whether a prefix is needed. + if ( isset( Tokens::$stringTokens[ $this->tokens[ $array_key ]['code'] ] ) + && $this->variable_prefixed_or_whitelisted( $stackPtr, $variable_name ) === true + ) { + return; + } + + if ( \T_DOUBLE_QUOTED_STRING === $this->tokens[ $array_key ]['code'] ) { + // If the array key is a double quoted string, try again with only + // the part before the first variable (if any). + $exploded = explode( '$', $variable_name ); + $first = rtrim( $exploded[0], '{' ); + if ( '' !== $first ) { + if ( $this->variable_prefixed_or_whitelisted( $array_key, $first ) === true ) { + return; + } + } else { + // If the first part was dynamic, throw a warning. + $is_error = false; + } + } elseif ( ! isset( Tokens::$stringTokens[ $this->tokens[ $array_key ]['code'] ] ) ) { + // Dynamic array key, throw a warning. + $is_error = false; + } + } else { + // Function parameters do not need to be prefixed. + if ( false === $in_list && isset( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) ) { + foreach ( $this->tokens[ $stackPtr ]['nested_parenthesis'] as $opener => $closer ) { + if ( isset( $this->tokens[ $opener ]['parenthesis_owner'] ) + && ( \T_FUNCTION === $this->tokens[ $this->tokens[ $opener ]['parenthesis_owner'] ]['code'] + || \T_CLOSURE === $this->tokens[ $this->tokens[ $opener ]['parenthesis_owner'] ]['code'] ) + ) { + return; + } + } + unset( $opener, $closer ); + } + + // Properties in a class do not need to be prefixed. + if ( false === $in_list && true === $this->is_class_property( $stackPtr ) ) { + return; + } + + // Local variables in a function do not need to be prefixed unless they are being imported. + if ( $this->phpcsFile->hasCondition( $stackPtr, array( \T_FUNCTION, \T_CLOSURE ) ) === true ) { + $condition = $this->phpcsFile->getCondition( $stackPtr, \T_FUNCTION ); + if ( false === $condition ) { + $condition = $this->phpcsFile->getCondition( $stackPtr, \T_CLOSURE ); + } + + $has_global = $this->phpcsFile->findPrevious( \T_GLOBAL, ( $stackPtr - 1 ), $this->tokens[ $condition ]['scope_opener'] ); + if ( false === $has_global ) { + // No variable import happening. + return; + } + + // Ok, this may be an imported global variable. + $end_of_statement = $this->phpcsFile->findNext( \T_SEMICOLON, ( $has_global + 1 ) ); + if ( false === $end_of_statement ) { + // No semi-colon - live coding. + return; + } + + for ( $ptr = ( $has_global + 1 ); $ptr <= $end_of_statement; $ptr++ ) { + // Move the stack pointer to the next variable. + $ptr = $this->phpcsFile->findNext( \T_VARIABLE, $ptr, $end_of_statement, false, null, true ); + + if ( false === $ptr ) { + // Reached the end of the global statement without finding the variable, + // so this must be a local variable. + return; + } + + if ( substr( $this->tokens[ $ptr ]['content'], 1 ) === $variable_name ) { + break; + } + } + + unset( $condition, $has_global, $end_of_statement, $ptr, $imported ); + + } + } + + // Still here ? In that case, the variable name should be prefixed. + $recorded = $this->addMessage( + self::ERROR_MSG, + $stackPtr, + $is_error, + 'NonPrefixedVariableFound', + array( + 'Global variables defined', + '$' . $variable_name, + ) + ); + + if ( true === $recorded ) { + $this->record_potential_prefix_metric( $stackPtr, $variable_name ); + } + } + + /** + * Check that global variables declared via a list construct are prefixed. + * + * @internal No need to take special measures for nested lists. Nested or not, + * each list part can only contain one variable being written to. + * + * @since 2.2.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + protected function process_list_assignment( $stackPtr ) { + $list_open_close = $this->find_list_open_close( $stackPtr ); + if ( false === $list_open_close ) { + // Short array, not short list. + return; + } + + $var_pointers = $this->get_list_variables( $stackPtr, $list_open_close ); + foreach ( $var_pointers as $ptr ) { + $this->process_variable_assignment( $ptr, true ); + } + + // No need to re-examine these variables. + return $list_open_close['closer']; + } + + /** + * Process the parameters of a matched function. + * + * @since 0.12.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + + // No matter whether it is a constant definition or a hook call, both use the first parameter. + if ( ! isset( $parameters[1] ) ) { + return; + } + + $is_error = true; + $raw_content = $this->strip_quotes( $parameters[1]['raw'] ); + + if ( ( 'define' !== $matched_content + && isset( $this->whitelisted_core_hooks[ $raw_content ] ) ) + || ( 'define' === $matched_content + && isset( $this->whitelisted_core_constants[ $raw_content ] ) ) + ) { + return; + } + + if ( $this->is_prefixed( $parameters[1]['start'], $raw_content ) === true ) { + return; + } else { + // This may be a dynamic hook/constant name. + $first_non_empty = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + $parameters[1]['start'], + ( $parameters[1]['end'] + 1 ), + true + ); + + if ( false === $first_non_empty ) { + return; + } + + $first_non_empty_content = $this->strip_quotes( $this->tokens[ $first_non_empty ]['content'] ); + + // Try again with just the first token if it's a text string. + if ( isset( Tokens::$stringTokens[ $this->tokens[ $first_non_empty ]['code'] ] ) + && $this->is_prefixed( $parameters[1]['start'], $first_non_empty_content ) === true + ) { + return; + } + + if ( \T_DOUBLE_QUOTED_STRING === $this->tokens[ $first_non_empty ]['code'] ) { + // If the first part of the parameter is a double quoted string, try again with only + // the part before the first variable (if any). + $exploded = explode( '$', $first_non_empty_content ); + $first = rtrim( $exploded[0], '{' ); + if ( '' !== $first ) { + if ( $this->is_prefixed( $parameters[1]['start'], $first ) === true ) { + return; + } + } else { + // Start of hook/constant name is dynamic, throw a warning. + $is_error = false; + } + } elseif ( ! isset( Tokens::$stringTokens[ $this->tokens[ $first_non_empty ]['code'] ] ) ) { + // Dynamic hook/constant name, throw a warning. + $is_error = false; + } + } + + if ( 'define' === $matched_content ) { + if ( \defined( '\\' . $raw_content ) ) { + // Backfill for PHP native constant. + return; + } + + if ( strpos( $raw_content, '\\' ) !== false ) { + // Namespaced or unreachable constant. + return; + } + + $data = array( 'Global constants defined' ); + $error_code = 'NonPrefixedConstantFound'; + if ( false === $is_error ) { + $error_code = 'VariableConstantNameFound'; + } + } else { + $data = array( 'Hook names invoked' ); + $error_code = 'NonPrefixedHooknameFound'; + if ( false === $is_error ) { + $error_code = 'DynamicHooknameFound'; + } + } + + $data[] = $raw_content; + + $recorded = $this->addMessage( self::ERROR_MSG, $first_non_empty, $is_error, $error_code, $data ); + + if ( true === $recorded ) { + $this->record_potential_prefix_metric( $stackPtr, $raw_content ); + } + } + + /** + * Check if a function/class/constant/variable name is prefixed with one of the expected prefixes. + * + * @since 0.12.0 + * @since 0.14.0 Allows for other non-word characters as well as underscores to better support hook names. + * @since 1.0.0 Does not require a word seperator anymore after a prefix. + * This allows for improved code style independent checking, + * i.e. allows for camelCase naming and the likes. + * @since 1.0.1 - Added $stackPtr parameter. + * - The function now also records metrics about the prefixes encountered. + * + * @param int $stackPtr The position of the token to record the metric against. + * @param string $name Name to check for a prefix. + * + * @return bool True when the name is one of the prefixes or starts with an allowed prefix. + * False otherwise. + */ + private function is_prefixed( $stackPtr, $name ) { + foreach ( $this->validated_prefixes as $prefix ) { + if ( stripos( $name, $prefix ) === 0 ) { + $this->phpcsFile->recordMetric( $stackPtr, 'Prefix all globals: allowed prefixes', $prefix ); + return true; + } + } + + return false; + } + + /** + * Check if a variable name might need a prefix. + * + * Prefix is not needed for: + * - superglobals, + * - WP native globals, + * - variables which are already prefixed. + * + * @since 0.12.0 + * @since 1.0.1 Added $stackPtr parameter. + * + * @param int $stackPtr The position of the token to record the metric against. + * @param string $name Variable name without the dollar sign. + * + * @return bool True if the variable name is whitelisted or already prefixed. + * False otherwise. + */ + private function variable_prefixed_or_whitelisted( $stackPtr, $name ) { + // Ignore superglobals and WP global variables. + if ( isset( $this->superglobals[ $name ] ) || isset( $this->wp_globals[ $name ] ) ) { + return true; + } + + return $this->is_prefixed( $stackPtr, $name ); + } + + /** + * Validate an array of prefixes as passed through a custom property or via the command line. + * + * Checks that the prefix: + * - is not one of the blacklisted ones. + * - complies with the PHP rules for valid function, class, variable, constant names. + * + * @since 0.12.0 + */ + private function validate_prefixes() { + if ( $this->previous_prefixes === $this->prefixes ) { + return; + } + + // Set the cache *before* validation so as to not break the above compare. + $this->previous_prefixes = $this->prefixes; + + // Validate the passed prefix(es). + $prefixes = array(); + $ns_prefixes = array(); + foreach ( $this->prefixes as $key => $prefix ) { + $prefixLC = strtolower( $prefix ); + + if ( isset( $this->prefix_blacklist[ $prefixLC ] ) ) { + $this->phpcsFile->addError( + 'The "%s" prefix is not allowed.', + 0, + 'ForbiddenPrefixPassed', + array( $prefix ) + ); + continue; + } + + $prefix_length = strlen( $prefix ); + if ( function_exists( 'iconv_strlen' ) ) { + $prefix_length = iconv_strlen( $prefix, $this->phpcsFile->config->encoding ); + } + + if ( $prefix_length < self::MIN_PREFIX_LENGTH ) { + $this->phpcsFile->addError( + 'The "%s" prefix is too short. Short prefixes are not unique enough and may cause name collisions with other code.', + 0, + 'ShortPrefixPassed', + array( $prefix ) + ); + continue; + } + + // Validate the prefix against characters allowed for function, class, constant names etc. + if ( preg_match( '`^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff\\\\]*$`', $prefix ) !== 1 ) { + $this->phpcsFile->addWarning( + 'The "%s" prefix is not a valid namespace/function/class/variable/constant prefix in PHP.', + 0, + 'InvalidPrefixPassed', + array( $prefix ) + ); + } + + // Lowercase the prefix to allow for direct compare. + $prefixes[ $key ] = $prefixLC; + + /* + * Replace non-word characters in the prefix with a regex snippet, but only if the + * string doesn't already contain namespace separators. + */ + $is_regex = false; + if ( strpos( $prefix, '\\' ) === false && preg_match( '`[_\W]`', $prefix ) > 0 ) { + $prefix = preg_replace( '`([_\W])`', '[\\\\\\\\$1]', $prefixLC ); + $is_regex = true; + } + + $ns_prefixes[ $prefixLC ] = array( + 'prefix' => $prefix, + 'is_regex' => $is_regex, + ); + } + + // Set the validated prefixes caches. + $this->validated_prefixes = $prefixes; + $this->validated_namespace_prefixes = $ns_prefixes; + } + + /** + * Record the "potential prefix" metric. + * + * @since 1.0.1 + * + * @param int $stackPtr The position of the token to record the metric against. + * @param string $construct_name Name of the global construct to try and distill a potential prefix from. + * + * @return void + */ + private function record_potential_prefix_metric( $stackPtr, $construct_name ) { + if ( preg_match( '`^([A-Z]*[a-z0-9]*+)`', ltrim( $construct_name, '\$_' ), $matches ) > 0 + && isset( $matches[1] ) && '' !== $matches[1] + ) { + $this->phpcsFile->recordMetric( $stackPtr, 'Prefix all globals: potential prefixes - start of non-prefixed construct', strtolower( $matches[1] ) ); + } + } +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidFunctionNameSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidFunctionNameSniff.php new file mode 100644 index 00000000..d8826ce1 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidFunctionNameSniff.php @@ -0,0 +1,187 @@ +getDeclarationName( $stackPtr ); + + if ( ! isset( $functionName ) ) { + // Ignore closures. + return; + } + + if ( '' === ltrim( $functionName, '_' ) ) { + // Ignore special functions, like __(). + return; + } + + $functionNameLc = strtolower( $functionName ); + + // Is this a magic function ? I.e., it is prefixed with "__" ? + // Outside class scope this basically just means __autoload(). + if ( 0 === strpos( $functionName, '__' ) ) { + $magicPart = substr( $functionNameLc, 2 ); + if ( isset( $this->magicFunctions[ $magicPart ] ) ) { + return; + } + + $error = 'Function name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore'; + $errorData = array( $functionName ); + $phpcsFile->addError( $error, $stackPtr, 'FunctionDoubleUnderscore', $errorData ); + } + + if ( $functionNameLc !== $functionName ) { + $error = 'Function name "%s" is not in snake case format, try "%s"'; + $errorData = array( + $functionName, + Sniff::get_snake_case_name_suggestion( $functionName ), + ); + $phpcsFile->addError( $error, $stackPtr, 'FunctionNameInvalid', $errorData ); + } + } + + /** + * Processes the tokens within the scope. + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being processed. + * @param int $stackPtr The position where this token was + * found. + * @param int $currScope The position of the current scope. + * + * @return void + */ + protected function processTokenWithinScope( File $phpcsFile, $stackPtr, $currScope ) { + + $tokens = $phpcsFile->getTokens(); + + // Determine if this is a function which needs to be examined. + $conditions = $tokens[ $stackPtr ]['conditions']; + end( $conditions ); + $deepestScope = key( $conditions ); + if ( $deepestScope !== $currScope ) { + return; + } + + if ( Sniff::is_function_deprecated( $phpcsFile, $stackPtr ) === true ) { + /* + * Deprecated functions don't have to comply with the naming conventions, + * otherwise functions deprecated in favour of a function with a compliant + * name would still trigger an error. + */ + return; + } + + $methodName = $phpcsFile->getDeclarationName( $stackPtr ); + + if ( ! isset( $methodName ) ) { + // Ignore closures. + return; + } + + $className = $phpcsFile->getDeclarationName( $currScope ); + if ( isset( $className ) === false ) { + $className = '[Anonymous Class]'; + } + + $methodNameLc = strtolower( $methodName ); + $classNameLc = strtolower( $className ); + + // Ignore special functions. + if ( '' === ltrim( $methodName, '_' ) ) { + return; + } + + // PHP4 constructors are allowed to break our rules. + if ( $methodNameLc === $classNameLc ) { + return; + } + + // PHP4 destructors are allowed to break our rules. + if ( '_' . $classNameLc === $methodNameLc ) { + return; + } + + $extended = $phpcsFile->findExtendedClassName( $currScope ); + $interfaces = $phpcsFile->findImplementedInterfaceNames( $currScope ); + + // If this is a child class or interface implementation, it may have to use camelCase or double underscores. + if ( ! empty( $extended ) || ! empty( $interfaces ) ) { + return; + } + + // Is this a magic method ? I.e. is it prefixed with "__" ? + if ( 0 === strpos( $methodName, '__' ) ) { + $magicPart = substr( $methodNameLc, 2 ); + if ( isset( $this->magicMethods[ $magicPart ] ) ) { + return; + } + + $error = 'Method name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore'; + $errorData = array( $className . '::' . $methodName ); + $phpcsFile->addError( $error, $stackPtr, 'MethodDoubleUnderscore', $errorData ); + } + + // Check for all lowercase. + if ( $methodNameLc !== $methodName ) { + $error = 'Method name "%s" in class %s is not in snake case format, try "%s"'; + $errorData = array( + $methodName, + $className, + Sniff::get_snake_case_name_suggestion( $methodName ), + ); + $phpcsFile->addError( $error, $stackPtr, 'MethodNameInvalid', $errorData ); + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidHookNameSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidHookNameSniff.php new file mode 100644 index 00000000..da62331d --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidHookNameSniff.php @@ -0,0 +1,289 @@ + + * + * + * + * + * + * Provide several extra delimiters as one string: + * + * + * + * + * + * + * @var string + */ + public $additionalWordDelimiters = ''; + + /** + * Regular expression to test for correct punctuation of a hook name. + * + * The placeholder will be replaced by potentially provided additional + * word delimiters in the `prepare_regex()` method. + * + * @var string + */ + protected $punctuation_regex = '`[^\w%s]`'; + + /** + * Groups of functions to restrict. + * + * @since 0.11.0 + * + * @return array + */ + public function getGroups() { + $this->target_functions = $this->hookInvokeFunctions; + + // No need to examine the names of deprecated hooks. + unset( + $this->target_functions['do_action_deprecated'], + $this->target_functions['apply_filters_deprecated'] + ); + + return parent::getGroups(); + } + + /** + * Process the parameters of a matched function. + * + * @since 0.11.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + + $regex = $this->prepare_regex(); + + $case_errors = 0; + $underscores = 0; + $content = array(); + $expected = array(); + + for ( $i = $parameters[1]['start']; $i <= $parameters[1]['end']; $i++ ) { + // Skip past comment tokens. + if ( isset( Tokens::$commentTokens[ $this->tokens[ $i ]['code'] ] ) !== false ) { + continue; + } + + $content[ $i ] = $this->tokens[ $i ]['content']; + $expected[ $i ] = $this->tokens[ $i ]['content']; + + // Skip past potential variable array access: $var['Key']. + if ( \T_VARIABLE === $this->tokens[ $i ]['code'] ) { + do { + $open_bracket = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $i + 1 ), null, true ); + if ( false === $open_bracket + || \T_OPEN_SQUARE_BRACKET !== $this->tokens[ $open_bracket ]['code'] + || ! isset( $this->tokens[ $open_bracket ]['bracket_closer'] ) + ) { + continue 2; + } + + $i = $this->tokens[ $open_bracket ]['bracket_closer']; + + } while ( isset( $this->tokens[ $i ] ) && $i <= $parameters[1]['end'] ); + + continue; + } + + // Skip past non-string tokens. + if ( isset( Tokens::$stringTokens[ $this->tokens[ $i ]['code'] ] ) === false ) { + continue; + } + + $string = $this->strip_quotes( $this->tokens[ $i ]['content'] ); + + /* + * Here be dragons - a double quoted string can contain extrapolated variables + * which don't have to comply with these rules. + */ + if ( \T_DOUBLE_QUOTED_STRING === $this->tokens[ $i ]['code'] ) { + $transform = $this->transform_complex_string( $string, $regex ); + $case_transform = $this->transform_complex_string( $string, $regex, 'case' ); + $punct_transform = $this->transform_complex_string( $string, $regex, 'punctuation' ); + } else { + $transform = $this->transform( $string, $regex ); + $case_transform = $this->transform( $string, $regex, 'case' ); + $punct_transform = $this->transform( $string, $regex, 'punctuation' ); + } + + if ( $string === $transform ) { + continue; + } + + if ( \T_DOUBLE_QUOTED_STRING === $this->tokens[ $i ]['code'] ) { + $expected[ $i ] = '"' . $transform . '"'; + } else { + $expected[ $i ] = '\'' . $transform . '\''; + } + + if ( $string !== $case_transform ) { + $case_errors++; + } + if ( $string !== $punct_transform ) { + $underscores++; + } + } + + $first_non_empty = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + $parameters[1]['start'], + ( $parameters[1]['end'] + 1 ), + true + ); + + $data = array( + trim( implode( '', $expected ) ), + trim( implode( '', $content ) ), + ); + + if ( $case_errors > 0 ) { + $error = 'Hook names should be lowercase. Expected: %s, but found: %s.'; + $this->phpcsFile->addError( $error, $first_non_empty, 'NotLowercase', $data ); + } + if ( $underscores > 0 ) { + $error = 'Words in hook names should be separated using underscores. Expected: %s, but found: %s.'; + $this->phpcsFile->addWarning( $error, $first_non_empty, 'UseUnderscores', $data ); + } + } + + /** + * Prepare the punctuation regular expression. + * + * Merges the existing regular expression with potentially provided extra word delimiters to allow. + * This is done 'late' and for each found token as otherwise inline `phpcs:set` directives + * would be ignored. + * + * @return string + */ + protected function prepare_regex() { + $extra = ''; + if ( '' !== $this->additionalWordDelimiters && \is_string( $this->additionalWordDelimiters ) ) { + $extra = preg_quote( $this->additionalWordDelimiters, '`' ); + } + + return sprintf( $this->punctuation_regex, $extra ); + } + + /** + * Transform an arbitrary string to lowercase and replace punctuation and spaces with underscores. + * + * @param string $string The target string. + * @param string $regex The punctuation regular expression to use. + * @param string $transform_type Whether to a partial or complete transform. + * Valid values are: 'full', 'case', 'punctuation'. + * @return string + */ + protected function transform( $string, $regex, $transform_type = 'full' ) { + + switch ( $transform_type ) { + case 'case': + return strtolower( $string ); + + case 'punctuation': + return preg_replace( $regex, '_', $string ); + + case 'full': + default: + return preg_replace( $regex, '_', strtolower( $string ) ); + } + } + + /** + * Transform a complex string which may contain variable extrapolation. + * + * @param string $string The target string. + * @param string $regex The punctuation regular expression to use. + * @param string $transform_type Whether to a partial or complete transform. + * Valid values are: 'full', 'case', 'punctuation'. + * @return string + */ + protected function transform_complex_string( $string, $regex, $transform_type = 'full' ) { + $output = preg_split( '`([\{\}\$\[\] ])`', $string, -1, \PREG_SPLIT_DELIM_CAPTURE ); + + $is_variable = false; + $has_braces = false; + $braces = 0; + + foreach ( $output as $i => $part ) { + if ( \in_array( $part, array( '$', '{' ), true ) ) { + $is_variable = true; + if ( '{' === $part ) { + $has_braces = true; + $braces++; + } + continue; + } + + if ( true === $is_variable ) { + if ( '[' === $part ) { + $has_braces = true; + $braces++; + } + if ( \in_array( $part, array( '}', ']' ), true ) ) { + $braces--; + } + if ( false === $has_braces && ' ' === $part ) { + $is_variable = false; + $output[ $i ] = $this->transform( $part, $regex, $transform_type ); + } + + if ( ( true === $has_braces && 0 === $braces ) && false === \in_array( $output[ ( $i + 1 ) ], array( '{', '[' ), true ) ) { + $has_braces = false; + $is_variable = false; + } + continue; + } + + $output[ $i ] = $this->transform( $part, $regex, $transform_type ); + } + + return implode( '', $output ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidPostTypeSlugSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidPostTypeSlugSniff.php new file mode 100644 index 00000000..caedae8b --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidPostTypeSlugSniff.php @@ -0,0 +1,208 @@ + true, + ); + + /** + * Array of reserved post type names which can not be used by themes and plugins. + * + * @since 2.2.0 + * + * @var array + */ + protected $reserved_names = array( + 'post' => true, + 'page' => true, + 'attachment' => true, + 'revision' => true, + 'nav_menu_item' => true, + 'custom_css' => true, + 'customize_changeset' => true, + 'oembed_cache' => true, + 'user_request' => true, + 'wp_block' => true, + 'action' => true, + 'author' => true, + 'order' => true, + 'theme' => true, + ); + + /** + * All valid tokens for in the first parameter of register_post_type(). + * + * Set in `register()`. + * + * @since 2.2.0 + * + * @var string + */ + private $valid_tokens = array(); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 2.2.0 + * + * @return array + */ + public function register() { + $this->valid_tokens = Tokens::$textStringTokens + Tokens::$heredocTokens + Tokens::$emptyTokens; + return parent::register(); + } + + /** + * Process the parameter of a matched function. + * + * Errors on invalid post type names when reserved keywords are used, + * the post type is too long, or contains invalid characters. + * + * @since 2.2.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param array $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + + $string_pos = $this->phpcsFile->findNext( Tokens::$textStringTokens, $parameters[1]['start'], ( $parameters[1]['end'] + 1 ) ); + $has_invalid_tokens = $this->phpcsFile->findNext( $this->valid_tokens, $parameters[1]['start'], ( $parameters[1]['end'] + 1 ), true ); + if ( false !== $has_invalid_tokens || false === $string_pos ) { + // Check for non string based slug parameter (we cannot determine if this is valid). + $this->phpcsFile->addWarning( + 'The post type slug is not a string literal. It is not possible to automatically determine the validity of this slug. Found: %s.', + $stackPtr, + 'NotStringLiteral', + array( + $parameters[1]['raw'], + ), + 3 + ); + return; + } + + $post_type = $this->strip_quotes( $this->tokens[ $string_pos ]['content'] ); + + if ( strlen( $post_type ) === 0 ) { + // Error for using empty slug. + $this->phpcsFile->addError( + 'register_post_type() called without a post type slug. The slug must be a non-empty string.', + $parameters[1]['start'], + 'Empty' + ); + return; + } + + $data = array( + $this->tokens[ $string_pos ]['content'], + ); + + // Warn for dynamic parts in the slug parameter. + if ( 'T_DOUBLE_QUOTED_STRING' === $this->tokens[ $string_pos ]['type'] || ( 'T_HEREDOC' === $this->tokens[ $string_pos ]['type'] && strpos( $this->tokens[ $string_pos ]['content'], '$' ) !== false ) ) { + $this->phpcsFile->addWarning( + 'The post type slug may, or may not, get too long with dynamic contents and could contain invalid characters. Found: %s.', + $string_pos, + 'PartiallyDynamic', + $data + ); + $post_type = $this->strip_interpolated_variables( $post_type ); + } + + if ( preg_match( self::POST_TYPE_CHARACTER_WHITELIST, $post_type ) === 0 ) { + // Error for invalid characters. + $this->phpcsFile->addError( + 'register_post_type() called with invalid post type %s. Post type contains invalid characters. Only lowercase alphanumeric characters, dashes, and underscores are allowed.', + $string_pos, + 'InvalidCharacters', + $data + ); + } + + if ( isset( $this->reserved_names[ $post_type ] ) ) { + // Error for using reserved slug names. + $this->phpcsFile->addError( + 'register_post_type() called with reserved post type %s. Reserved post types should not be used as they interfere with the functioning of WordPress itself.', + $string_pos, + 'Reserved', + $data + ); + } elseif ( stripos( $post_type, 'wp_' ) === 0 ) { + // Error for using reserved slug prefix. + $this->phpcsFile->addError( + 'The post type passed to register_post_type() uses a prefix reserved for WordPress itself. Found: %s.', + $string_pos, + 'ReservedPrefix', + $data + ); + } + + // Error for slugs that are too long. + if ( strlen( $post_type ) > self::POST_TYPE_MAX_LENGTH ) { + $this->phpcsFile->addError( + 'A post type slug must not exceed %d characters. Found: %s (%d characters).', + $string_pos, + 'TooLong', + array( + self::POST_TYPE_MAX_LENGTH, + $this->tokens[ $string_pos ]['content'], + strlen( $post_type ), + ) + ); + } + } +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidVariableNameSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidVariableNameSniff.php new file mode 100644 index 00000000..34314ff1 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/NamingConventions/ValidVariableNameSniff.php @@ -0,0 +1,299 @@ + true, + 'GETID3_ERRORARRAY' => true, + 'is_IE' => true, + 'is_IIS' => true, + 'is_macIE' => true, + 'is_NS4' => true, + 'is_winIE' => true, + 'PHP_SELF' => true, + 'post_ID' => true, + 'tag_ID' => true, + 'user_ID' => true, + ); + + /** + * List of member variables that can have mixed case. + * + * @since 0.9.0 + * @since 0.11.0 Changed from public to protected. + * + * @var array + */ + protected $whitelisted_mixed_case_member_var_names = array( + 'ID' => true, + 'comment_ID' => true, + 'comment_post_ID' => true, + 'post_ID' => true, + 'comment_author_IP' => true, + 'cat_ID' => true, + ); + + /** + * Custom list of properties which can have mixed case. + * + * @since 0.11.0 + * + * @var string|string[] + */ + public $customPropertiesWhitelist = array(); + + /** + * Cache of previously added custom functions. + * + * Prevents having to do the same merges over and over again. + * + * @since 0.10.0 + * @since 0.11.0 - Name changed from $addedCustomVariables. + * - Changed the format from simple bool to array. + * + * @var array + */ + protected $addedCustomProperties = array( + 'properties' => null, + ); + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param \PHP_CodeSniffer\Files\File $phpcs_file The file being scanned. + * @param int $stack_ptr The position of the current token in the + * stack passed in $tokens. + * + * @return void + */ + protected function processVariable( File $phpcs_file, $stack_ptr ) { + + $tokens = $phpcs_file->getTokens(); + $var_name = ltrim( $tokens[ $stack_ptr ]['content'], '$' ); + + // If it's a php reserved var, then its ok. + if ( isset( $this->phpReservedVars[ $var_name ] ) ) { + return; + } + + // Merge any custom variables with the defaults. + $this->mergeWhiteList(); + + // Likewise if it is a mixed-case var used by WordPress core. + if ( isset( $this->wordpress_mixed_case_vars[ $var_name ] ) ) { + return; + } + + $obj_operator = $phpcs_file->findNext( Tokens::$emptyTokens, ( $stack_ptr + 1 ), null, true ); + if ( \T_OBJECT_OPERATOR === $tokens[ $obj_operator ]['code'] ) { + // Check to see if we are using a variable from an object. + $var = $phpcs_file->findNext( Tokens::$emptyTokens, ( $obj_operator + 1 ), null, true ); + if ( \T_STRING === $tokens[ $var ]['code'] ) { + $bracket = $phpcs_file->findNext( Tokens::$emptyTokens, ( $var + 1 ), null, true ); + if ( \T_OPEN_PARENTHESIS !== $tokens[ $bracket ]['code'] ) { + $obj_var_name = $tokens[ $var ]['content']; + + // There is no way for us to know if the var is public or + // private, so we have to ignore a leading underscore if there is + // one and just check the main part of the variable name. + $original_var_name = $obj_var_name; + if ( '_' === substr( $obj_var_name, 0, 1 ) ) { + $obj_var_name = substr( $obj_var_name, 1 ); + } + + if ( ! isset( $this->whitelisted_mixed_case_member_var_names[ $obj_var_name ] ) && self::isSnakeCase( $obj_var_name ) === false ) { + $error = 'Object property "$%s" is not in valid snake_case format, try "$%s"'; + $data = array( + $original_var_name, + Sniff::get_snake_case_name_suggestion( $original_var_name ), + ); + $phpcs_file->addError( $error, $var, 'UsedPropertyNotSnakeCase', $data ); + } + } + } + } + + $in_class = false; + $obj_operator = $phpcs_file->findPrevious( Tokens::$emptyTokens, ( $stack_ptr - 1 ), null, true ); + if ( \T_DOUBLE_COLON === $tokens[ $obj_operator ]['code'] || \T_OBJECT_OPERATOR === $tokens[ $obj_operator ]['code'] ) { + // The variable lives within a class, and is referenced like + // this: MyClass::$_variable or $class->variable. + $in_class = true; + } + + // There is no way for us to know if the var is public or private, + // so we have to ignore a leading underscore if there is one and just + // check the main part of the variable name. + $original_var_name = $var_name; + if ( '_' === substr( $var_name, 0, 1 ) && true === $in_class ) { + $var_name = substr( $var_name, 1 ); + } + + if ( self::isSnakeCase( $var_name ) === false ) { + if ( $in_class && ! isset( $this->whitelisted_mixed_case_member_var_names[ $var_name ] ) ) { + $error = 'Object property "$%s" is not in valid snake_case format, try "$%s"'; + $error_name = 'UsedPropertyNotSnakeCase'; + } elseif ( ! $in_class ) { + $error = 'Variable "$%s" is not in valid snake_case format, try "$%s"'; + $error_name = 'VariableNotSnakeCase'; + } + + if ( isset( $error, $error_name ) ) { + $data = array( + $original_var_name, + Sniff::get_snake_case_name_suggestion( $original_var_name ), + ); + $phpcs_file->addError( $error, $stack_ptr, $error_name, $data ); + } + } + } + + /** + * Processes class member variables. + * + * @param \PHP_CodeSniffer\Files\File $phpcs_file The file being scanned. + * @param int $stack_ptr The position of the current token in the + * stack passed in $tokens. + * + * @return void + */ + protected function processMemberVar( File $phpcs_file, $stack_ptr ) { + + $tokens = $phpcs_file->getTokens(); + + $var_name = ltrim( $tokens[ $stack_ptr ]['content'], '$' ); + $member_props = $phpcs_file->getMemberProperties( $stack_ptr ); + if ( empty( $member_props ) ) { + // Couldn't get any info about this variable, which + // generally means it is invalid or possibly has a parse + // error. Any errors will be reported by the core, so + // we can ignore it. + return; + } + + // Merge any custom variables with the defaults. + $this->mergeWhiteList(); + + if ( ! isset( $this->whitelisted_mixed_case_member_var_names[ $var_name ] ) && false === self::isSnakeCase( $var_name ) ) { + $error = 'Member variable "$%s" is not in valid snake_case format, try "$%s"'; + $data = array( + $var_name, + Sniff::get_snake_case_name_suggestion( $var_name ), + ); + $phpcs_file->addError( $error, $stack_ptr, 'PropertyNotSnakeCase', $data ); + } + } + + /** + * Processes the variable found within a double quoted string. + * + * @param \PHP_CodeSniffer\Files\File $phpcs_file The file being scanned. + * @param int $stack_ptr The position of the double quoted + * string. + * + * @return void + */ + protected function processVariableInString( File $phpcs_file, $stack_ptr ) { + + $tokens = $phpcs_file->getTokens(); + + if ( preg_match_all( '|[^\\\]\${?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)|', $tokens[ $stack_ptr ]['content'], $matches ) > 0 ) { + + // Merge any custom variables with the defaults. + $this->mergeWhiteList(); + + foreach ( $matches[1] as $var_name ) { + // If it's a php reserved var, then its ok. + if ( isset( $this->phpReservedVars[ $var_name ] ) ) { + continue; + } + + // Likewise if it is a mixed-case var used by WordPress core. + if ( isset( $this->wordpress_mixed_case_vars[ $var_name ] ) ) { + return; + } + + if ( false === self::isSnakeCase( $var_name ) ) { + $error = 'Variable "$%s" is not in valid snake_case format, try "$%s"'; + $data = array( + $var_name, + Sniff::get_snake_case_name_suggestion( $var_name ), + ); + $phpcs_file->addError( $error, $stack_ptr, 'InterpolatedVariableNotSnakeCase', $data ); + } + } + } + } + + /** + * Return whether the variable is in snake_case. + * + * @param string $var_name Variable name. + * @return bool + */ + public static function isSnakeCase( $var_name ) { + return (bool) preg_match( '/^[a-z0-9_]+$/', $var_name ); + } + + /** + * Merge a custom whitelist provided via a custom ruleset with the predefined whitelist, + * if we haven't already. + * + * @since 0.10.0 + * @since 2.0.0 Removed unused $phpcs_file parameter. + * + * @return void + */ + protected function mergeWhiteList() { + if ( $this->customPropertiesWhitelist !== $this->addedCustomProperties['properties'] ) { + // Fix property potentially passed as comma-delimited string. + $customProperties = Sniff::merge_custom_array( $this->customPropertiesWhitelist, array(), false ); + + $this->whitelisted_mixed_case_member_var_names = Sniff::merge_custom_array( + $customProperties, + $this->whitelisted_mixed_case_member_var_names + ); + + $this->addedCustomProperties['properties'] = $this->customPropertiesWhitelist; + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DevelopmentFunctionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DevelopmentFunctionsSniff.php new file mode 100644 index 00000000..59e914dc --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DevelopmentFunctionsSniff.php @@ -0,0 +1,66 @@ + array( + * 'lambda' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Use anonymous functions instead please!', + * 'functions' => array( 'file_get_contents', 'create_function' ), + * ) + * ) + * + * @return array + */ + public function getGroups() { + return array( + 'error_log' => array( + 'type' => 'warning', + 'message' => '%s() found. Debug code should not normally be used in production.', + 'functions' => array( + 'error_log', + 'var_dump', + 'var_export', + 'print_r', + 'trigger_error', + 'set_error_handler', + 'debug_backtrace', + 'debug_print_backtrace', + 'wp_debug_backtrace_summary', + ), + ), + + 'prevent_path_disclosure' => array( + 'type' => 'warning', + 'message' => '%s() can lead to full path disclosure.', + 'functions' => array( + 'error_reporting', + 'phpinfo', + ), + ), + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DisallowShortTernarySniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DisallowShortTernarySniff.php new file mode 100644 index 00000000..6c734b8c --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DisallowShortTernarySniff.php @@ -0,0 +1,65 @@ +phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + if ( false === $nextNonEmpty ) { + // Live coding or parse error. + return; + } + + if ( \T_INLINE_ELSE !== $this->tokens[ $nextNonEmpty ]['code'] ) { + return; + } + + $this->phpcsFile->addError( + 'Using short ternaries is not allowed', + $stackPtr, + 'Found' + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DiscouragedPHPFunctionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DiscouragedPHPFunctionsSniff.php new file mode 100644 index 00000000..39f87c09 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DiscouragedPHPFunctionsSniff.php @@ -0,0 +1,103 @@ + array( + * 'lambda' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Use anonymous functions instead please!', + * 'functions' => array( 'file_get_contents', 'create_function' ), + * ) + * ) + * + * @return array + */ + public function getGroups() { + return array( + 'serialize' => array( + 'type' => 'warning', + 'message' => '%s() found. Serialized data has known vulnerability problems with Object Injection. JSON is generally a better approach for serializing data. See https://www.owasp.org/index.php/PHP_Object_Injection', + 'functions' => array( + 'serialize', + 'unserialize', + ), + ), + + 'urlencode' => array( + 'type' => 'warning', + 'message' => '%s() should only be used when dealing with legacy applications rawurlencode() should now be used instead. See http://php.net/manual/en/function.rawurlencode.php and http://www.faqs.org/rfcs/rfc3986.html', + 'functions' => array( + 'urlencode', + ), + ), + + 'runtime_configuration' => array( + 'type' => 'warning', + 'message' => '%s() found. Changing configuration values at runtime is strongly discouraged.', + 'functions' => array( + 'error_reporting', + 'ini_restore', + 'apache_setenv', + 'putenv', + 'set_include_path', + 'restore_include_path', + // This alias was DEPRECATED in PHP 5.3.0, and REMOVED as of PHP 7.0.0. + 'magic_quotes_runtime', + // Warning This function was DEPRECATED in PHP 5.3.0, and REMOVED as of PHP 7.0.0. + 'set_magic_quotes_runtime', + // Warning This function was removed from most SAPIs in PHP 5.3.0, and was removed from PHP-FPM in PHP 7.0.0. + 'dl', + ), + ), + + 'system_calls' => array( + 'type' => 'warning', + 'message' => '%s() found. PHP system calls are often disabled by server admins.', + 'functions' => array( + 'exec', + 'passthru', + 'proc_open', + 'shell_exec', + 'system', + 'popen', + ), + ), + + 'obfuscation' => array( + 'type' => 'warning', + 'message' => '%s() can be used to obfuscate code which is strongly discouraged. Please verify that the function is used for benign reasons.', + 'functions' => array( + 'base64_decode', + 'base64_encode', + 'convert_uudecode', + 'convert_uuencode', + 'str_rot13', + ), + ), + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DontExtractSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DontExtractSniff.php new file mode 100644 index 00000000..af2233bb --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/DontExtractSniff.php @@ -0,0 +1,55 @@ + array( + * 'lambda' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Use anonymous functions instead please!', + * 'functions' => array( 'file_get_contents', 'create_function' ), + * ) + * ) + * + * @return array + */ + public function getGroups() { + return array( + + 'extract' => array( + 'type' => 'error', + 'message' => '%s() usage is highly discouraged, due to the complexity and unintended issues it might cause.', + 'functions' => array( + 'extract', + ), + ), + + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/IniSetSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/IniSetSniff.php new file mode 100644 index 00000000..178386d1 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/IniSetSniff.php @@ -0,0 +1,177 @@ + true, + 'ini_alter' => true, + ); + + /** + * Array of PHP configuration options that are allowed to be manipulated. + * + * @since 2.1.0 + * + * @var array Multidimensional array with parameter details. + * $whitelisted_options = array( + * (string) option name. = array( + * (string[]) 'valid_values' = array() + * ) + * ); + */ + protected $whitelisted_options = array( + 'auto_detect_line_endings' => array(), + 'highlight.bg' => array(), + 'highlight.comment' => array(), + 'highlight.default' => array(), + 'highlight.html' => array(), + 'highlight.keyword' => array(), + 'highlight.string' => array(), + 'short_open_tag' => array( + 'valid_values' => array( 'true', '1', 'on' ), + ), + ); + + /** + * Array of PHP configuration options that are not allowed to be manipulated. + * + * @since 2.1.0 + * + * @var array Multidimensional array with parameter details. + * $blacklisted_options = array( + * (string) option name. = array( + * (string[]) 'invalid_values' = array() + * (string) 'message' + * ) + * ); + */ + protected $blacklisted_options = array( + 'bcmath.scale' => array( + 'message' => 'Use `bcscale()` instead.', + ), + 'display_errors' => array( + 'message' => 'Use `WP_DEBUG_DISPLAY` instead.', + ), + 'error_reporting' => array( + 'message' => 'Use `WP_DEBUG` instead.', + ), + 'filter.default' => array( + 'message' => 'Changing the option value can break other plugins. Use the filter flag constants when calling the Filter functions instead.', + ), + 'filter.default_flags' => array( + 'message' => 'Changing the option value can break other plugins. Use the filter flag constants when calling the Filter functions instead.', + ), + 'iconv.input_encoding' => array( + 'message' => 'PHP < 5.6 only - use `iconv_set_encoding()` instead.', + ), + 'iconv.internal_encoding' => array( + 'message' => 'PHP < 5.6 only - use `iconv_set_encoding()` instead.', + ), + 'iconv.output_encoding' => array( + 'message' => 'PHP < 5.6 only - use `iconv_set_encoding()` instead.', + ), + 'ignore_user_abort' => array( + 'message' => 'Use `ignore_user_abort()` instead.', + ), + 'log_errors' => array( + 'message' => 'Use `WP_DEBUG_LOG` instead.', + ), + 'max_execution_time' => array( + 'message' => 'Use `set_time_limit()` instead.', + ), + 'memory_limit' => array( + 'message' => 'Use `wp_raise_memory_limit()` or hook into the filters in that function.', + ), + 'short_open_tag' => array( + 'invalid_values' => array( 'false', '0', 'off' ), + 'message' => 'Turning off short_open_tag is prohibited as it can break other plugins.', + ), + ); + + /** + * Process the parameter of a matched function. + * + * Errors if an option is found in the blacklist. Warns as + * 'risky' when the option is not found in the whitelist. + * + * @since 2.1.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + $option_name = $this->strip_quotes( $parameters[1]['raw'] ); + $option_value = $this->strip_quotes( $parameters[2]['raw'] ); + if ( isset( $this->whitelisted_options[ $option_name ] ) ) { + $whitelisted_option = $this->whitelisted_options[ $option_name ]; + if ( ! isset( $whitelisted_option['valid_values'] ) || in_array( strtolower( $option_value ), $whitelisted_option['valid_values'], true ) ) { + return; + } + } + + if ( isset( $this->blacklisted_options[ $option_name ] ) ) { + $blacklisted_option = $this->blacklisted_options[ $option_name ]; + if ( ! isset( $blacklisted_option['invalid_values'] ) || in_array( strtolower( $option_value ), $blacklisted_option['invalid_values'], true ) ) { + $this->phpcsFile->addError( + '%s(%s, %s) found. %s', + $stackPtr, + $this->string_to_errorcode( $option_name . '_Blacklisted' ), + array( + $matched_content, + $parameters[1]['raw'], + $parameters[2]['raw'], + $blacklisted_option['message'], + ) + ); + return; + } + } + + $this->phpcsFile->addWarning( + '%s(%s, %s) found. Changing configuration values at runtime is strongly discouraged.', + $stackPtr, + 'Risky', + array( + $matched_content, + $parameters[1]['raw'], + $parameters[2]['raw'], + ) + ); + } +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/NoSilencedErrorsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/NoSilencedErrorsSniff.php new file mode 100644 index 00000000..ee3b3b3a --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/NoSilencedErrorsSniff.php @@ -0,0 +1,239 @@ + => + */ + protected $function_whitelist = array( + // Directory extension. + 'chdir' => true, + 'opendir' => true, + 'scandir' => true, + + // File extension. + 'file_exists' => true, + 'file_get_contents' => true, + 'file' => true, + 'fileatime' => true, + 'filectime' => true, + 'filegroup' => true, + 'fileinode' => true, + 'filemtime' => true, + 'fileowner' => true, + 'fileperms' => true, + 'filesize' => true, + 'filetype' => true, + 'fopen' => true, + 'is_dir' => true, + 'is_executable' => true, + 'is_file' => true, + 'is_link' => true, + 'is_readable' => true, + 'is_writable' => true, + 'is_writeable' => true, + 'lstat' => true, + 'mkdir' => true, + 'move_uploaded_file' => true, + 'readfile' => true, + 'readlink' => true, + 'rename' => true, + 'rmdir' => true, + 'stat' => true, + 'unlink' => true, + + // FTP extension. + 'ftp_chdir' => true, + 'ftp_login' => true, + 'ftp_rename' => true, + + // Stream extension. + 'stream_select' => true, + 'stream_set_chunk_size' => true, + + // Zlib extension. + 'deflate_add' => true, + 'deflate_init' => true, + 'inflate_add' => true, + 'inflate_init' => true, + 'readgzfile' => true, + + // Miscellaneous other functions. + 'imagecreatefromstring' => true, + 'parse_url' => true, // Pre-PHP 5.3.3 an E_WARNING was thrown when URL parsing failed. + 'unserialize' => true, + ); + + /** + * Tokens which are regarded as empty for the purpose of determining + * the name of the called function. + * + * This property is set from within the register() method. + * + * @since 1.1.0 + * + * @var array + */ + private $empty_tokens = array(); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.1.0 + * + * @return array + */ + public function register() { + $this->empty_tokens = Tokens::$emptyTokens; + $this->empty_tokens[ \T_NS_SEPARATOR ] = \T_NS_SEPARATOR; + $this->empty_tokens[ \T_BITWISE_AND ] = \T_BITWISE_AND; + + return array( + \T_ASPERAND, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.1.0 + * + * @param int $stackPtr The position of the current token in the stack. + */ + public function process_token( $stackPtr ) { + // Handle the user-defined custom function whitelist. + $this->custom_whitelist = $this->merge_custom_array( $this->custom_whitelist, array(), false ); + $this->custom_whitelist = array_map( 'strtolower', $this->custom_whitelist ); + + /* + * Check if the error silencing is done for one of the whitelisted functions. + * + * @internal The function call name determination is done even when there is no whitelist active + * to allow the metrics to be more informative. + */ + $next_non_empty = $this->phpcsFile->findNext( $this->empty_tokens, ( $stackPtr + 1 ), null, true, null, true ); + if ( false !== $next_non_empty && \T_STRING === $this->tokens[ $next_non_empty ]['code'] ) { + $has_parenthesis = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $next_non_empty + 1 ), null, true, null, true ); + if ( false !== $has_parenthesis && \T_OPEN_PARENTHESIS === $this->tokens[ $has_parenthesis ]['code'] ) { + $function_name = strtolower( $this->tokens[ $next_non_empty ]['content'] ); + if ( ( true === $this->use_default_whitelist + && isset( $this->function_whitelist[ $function_name ] ) === true ) + || ( ! empty( $this->custom_whitelist ) + && in_array( $function_name, $this->custom_whitelist, true ) === true ) + ) { + $this->phpcsFile->recordMetric( $stackPtr, 'Error silencing', 'whitelisted function call: ' . $function_name ); + return; + } + } + } + + $this->context_length = (int) $this->context_length; + $context_length = $this->context_length; + if ( $this->context_length <= 0 ) { + $context_length = 2; + } + + // Prepare the "Found" string to display. + $end_of_statement = $this->phpcsFile->findEndOfStatement( $stackPtr, \T_COMMA ); + if ( ( $end_of_statement - $stackPtr ) < $context_length ) { + $context_length = ( $end_of_statement - $stackPtr ); + } + $found = $this->phpcsFile->getTokensAsString( $stackPtr, $context_length ); + $found = str_replace( array( "\t", "\n", "\r" ), ' ', $found ) . '...'; + + $error_msg = 'Silencing errors is strongly discouraged. Use proper error checking instead.'; + $data = array(); + if ( $this->context_length > 0 ) { + $error_msg .= ' Found: %s'; + $data[] = $found; + } + + $this->phpcsFile->addWarning( + $error_msg, + $stackPtr, + 'Discouraged', + $data + ); + + if ( isset( $function_name ) ) { + $this->phpcsFile->recordMetric( $stackPtr, 'Error silencing', '@' . $function_name ); + } else { + $this->phpcsFile->recordMetric( $stackPtr, 'Error silencing', $found ); + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/POSIXFunctionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/POSIXFunctionsSniff.php new file mode 100644 index 00000000..9fb1cdcb --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/POSIXFunctionsSniff.php @@ -0,0 +1,76 @@ + array( + * 'lambda' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Use anonymous functions instead please!', + * 'functions' => array( 'file_get_contents', 'create_function' ), + * ) + * ) + * + * @return array + */ + public function getGroups() { + return array( + 'ereg' => array( + 'type' => 'error', + 'message' => '%s() has been deprecated since PHP 5.3 and removed in PHP 7.0, please use preg_match() instead.', + 'functions' => array( + 'ereg', + 'eregi', + 'sql_regcase', + ), + ), + + 'ereg_replace' => array( + 'type' => 'error', + 'message' => '%s() has been deprecated since PHP 5.3 and removed in PHP 7.0, please use preg_replace() instead.', + 'functions' => array( + 'ereg_replace', + 'eregi_replace', + ), + ), + + 'split' => array( + 'type' => 'error', + 'message' => '%s() has been deprecated since PHP 5.3 and removed in PHP 7.0, please use explode(), str_split() or preg_split() instead.', + 'functions' => array( + 'split', + 'spliti', + ), + ), + + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/PregQuoteDelimiterSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/PregQuoteDelimiterSniff.php new file mode 100644 index 00000000..a67e1173 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/PregQuoteDelimiterSniff.php @@ -0,0 +1,69 @@ + => + */ + protected $target_functions = array( + 'preg_quote' => true, + ); + + /** + * Process the parameters of a matched function. + * + * @since 1.0.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + if ( \count( $parameters ) > 1 ) { + return; + } + + $this->phpcsFile->addWarning( + 'Passing the $delimiter as the second parameter to preg_quote() is strongly recommended.', + $stackPtr, + 'Missing' + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/RestrictedPHPFunctionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/RestrictedPHPFunctionsSniff.php new file mode 100644 index 00000000..280c94d9 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/RestrictedPHPFunctionsSniff.php @@ -0,0 +1,48 @@ + array( + * 'lambda' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Use anonymous functions instead please!', + * 'functions' => array( 'file_get_contents', 'create_function' ), + * ) + * ) + * + * @return array + */ + public function getGroups() { + return array( + 'create_function' => array( + 'type' => 'error', + 'message' => '%s() is deprecated as of PHP 7.2, please use full fledged functions or anonymous functions instead.', + 'functions' => array( + 'create_function', + ), + ), + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/StrictComparisonsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/StrictComparisonsSniff.php new file mode 100644 index 00000000..85fe0554 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/StrictComparisonsSniff.php @@ -0,0 +1,56 @@ +has_whitelist_comment( 'loose comparison', $stackPtr ) ) { + $error = 'Found: ' . $this->tokens[ $stackPtr ]['content'] . '. Use strict comparisons (=== or !==).'; + $this->phpcsFile->addWarning( $error, $stackPtr, 'LooseComparison' ); + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/StrictInArraySniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/StrictInArraySniff.php new file mode 100644 index 00000000..4973e9bb --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/StrictInArraySniff.php @@ -0,0 +1,105 @@ + => + */ + protected $target_functions = array( + 'in_array' => true, + 'array_search' => true, + 'array_keys' => false, + ); + + /** + * Process the parameters of a matched function. + * + * @since 0.11.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + // Check if the strict check is actually needed. + if ( false === $this->target_functions[ $matched_content ] ) { + if ( \count( $parameters ) === 1 ) { + return; + } + } + + // We're only interested in the third parameter. + if ( false === isset( $parameters[3] ) || 'true' !== strtolower( $parameters[3]['raw'] ) ) { + $errorcode = 'MissingTrueStrict'; + + /* + * Use a different error code when `false` is found to allow for excluding + * the warning as this will be a conscious choice made by the dev. + */ + if ( isset( $parameters[3] ) && 'false' === strtolower( $parameters[3]['raw'] ) ) { + $errorcode = 'FoundNonStrictFalse'; + } + + $this->phpcsFile->addWarning( + 'Not using strict comparison for %s; supply true for third argument.', + ( isset( $parameters[3]['start'] ) ? $parameters[3]['start'] : $parameters[1]['start'] ), + $errorcode, + array( $matched_content ) + ); + return; + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/TypeCastsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/TypeCastsSniff.php new file mode 100644 index 00000000..7aded55f --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/TypeCastsSniff.php @@ -0,0 +1,99 @@ +tokens[ $stackPtr ]['code']; + $typecast = str_replace( ' ', '', $this->tokens[ $stackPtr ]['content'] ); + $typecast_lc = strtolower( $typecast ); + + switch ( $token_code ) { + case \T_DOUBLE_CAST: + if ( '(float)' !== $typecast_lc ) { + $fix = $this->phpcsFile->addFixableError( + 'Normalized type keywords must be used; expected "(float)" but found "%s"', + $stackPtr, + 'DoubleRealFound', + array( $typecast ) + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->replaceToken( $stackPtr, '(float)' ); + } + } + break; + + case \T_UNSET_CAST: + $this->phpcsFile->addWarning( + 'Using the "(unset)" cast is strongly discouraged. Use the "unset()" language construct or assign "null" as the value to the variable instead.', + $stackPtr, + 'UnsetFound' + ); + break; + + case \T_STRING_CAST: + case \T_BINARY_CAST: + if ( \T_STRING_CAST === $token_code && '(binary)' !== $typecast_lc ) { + break; + } + + $this->phpcsFile->addWarning( + 'Using binary casting is strongly discouraged. Found: "%s"', + $stackPtr, + 'BinaryFound', + array( $typecast ) + ); + break; + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/YodaConditionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/YodaConditionsSniff.php new file mode 100644 index 00000000..9f3f3d10 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/PHP/YodaConditionsSniff.php @@ -0,0 +1,125 @@ +condition_start_tokens = $starters; + + return array( + \T_IS_EQUAL, + \T_IS_NOT_EQUAL, + \T_IS_IDENTICAL, + \T_IS_NOT_IDENTICAL, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + public function process_token( $stackPtr ) { + + $start = $this->phpcsFile->findPrevious( $this->condition_start_tokens, $stackPtr, null, false, null, true ); + + $needs_yoda = false; + + // Note: going backwards! + for ( $i = $stackPtr; $i > $start; $i-- ) { + + // Ignore whitespace. + if ( isset( Tokens::$emptyTokens[ $this->tokens[ $i ]['code'] ] ) ) { + continue; + } + + // If this is a variable or array, we've seen all we need to see. + if ( \T_VARIABLE === $this->tokens[ $i ]['code'] + || \T_CLOSE_SQUARE_BRACKET === $this->tokens[ $i ]['code'] + ) { + $needs_yoda = true; + break; + } + + // If this is a function call or something, we are OK. + if ( \T_CLOSE_PARENTHESIS === $this->tokens[ $i ]['code'] ) { + return; + } + } + + if ( ! $needs_yoda ) { + return; + } + + // Check if this is a var to var comparison, e.g.: if ( $var1 == $var2 ). + $next_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + + if ( isset( Tokens::$castTokens[ $this->tokens[ $next_non_empty ]['code'] ] ) ) { + $next_non_empty = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $next_non_empty + 1 ), null, true ); + } + + if ( \in_array( $this->tokens[ $next_non_empty ]['code'], array( \T_SELF, \T_PARENT, \T_STATIC ), true ) ) { + $next_non_empty = $this->phpcsFile->findNext( + ( Tokens::$emptyTokens + array( \T_DOUBLE_COLON => \T_DOUBLE_COLON ) ), + ( $next_non_empty + 1 ), + null, + true + ); + } + + if ( \T_VARIABLE === $this->tokens[ $next_non_empty ]['code'] ) { + return; + } + + $this->phpcsFile->addError( 'Use Yoda Condition checks, you must.', $stackPtr, 'NotYoda' ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/EscapeOutputSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/EscapeOutputSniff.php new file mode 100644 index 00000000..48a4766b --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/EscapeOutputSniff.php @@ -0,0 +1,505 @@ + 'esc_html_e() or esc_attr_e()', + '_ex' => 'echo esc_html_x() or echo esc_attr_x()', + ); + + /** + * Cache of previously added custom functions. + * + * Prevents having to do the same merges over and over again. + * + * @since 0.4.0 + * @since 0.11.0 - Changed from public static to protected non-static. + * - Changed the format from simple bool to array. + * + * @var array + */ + protected $addedCustomFunctions = array( + 'escape' => array(), + 'autoescape' => array(), + 'sanitize' => array(), + 'print' => array(), + ); + + /** + * List of names of the tokens representing PHP magic constants. + * + * @since 0.10.0 + * + * @var array + */ + private $magic_constant_tokens = array( + 'T_CLASS_C' => true, // __CLASS__ + 'T_DIR' => true, // __DIR__ + 'T_FILE' => true, // __FILE__ + 'T_FUNC_C' => true, // __FUNCTION__ + 'T_LINE' => true, // __LINE__ + 'T_METHOD_C' => true, // __METHOD__ + 'T_NS_C' => true, // __NAMESPACE__ + 'T_TRAIT_C' => true, // __TRAIT__ + ); + + /** + * List of names of the native PHP constants which can be considered safe. + * + * @since 1.0.0 + * + * @var array + */ + private $safe_php_constants = array( + 'PHP_EOL' => true, // String. + 'PHP_VERSION' => true, // Integer. + 'PHP_MAJOR_VERSION' => true, // Integer. + 'PHP_MINOR_VERSION' => true, // Integer. + 'PHP_RELEASE_VERSION' => true, // Integer. + 'PHP_VERSION_ID' => true, // Integer. + 'PHP_EXTRA_VERSION' => true, // String. + 'PHP_DEBUG' => true, // Integer. + ); + + /** + * List of tokens which can be considered as safe when directly part of the output. + * + * @since 0.12.0 + * + * @var array + */ + private $safe_components = array( + 'T_CONSTANT_ENCAPSED_STRING' => true, + 'T_LNUMBER' => true, + 'T_MINUS' => true, + 'T_PLUS' => true, + 'T_MULTIPLY' => true, + 'T_DIVIDE' => true, + 'T_MODULUS' => true, + 'T_TRUE' => true, + 'T_FALSE' => true, + 'T_NULL' => true, + 'T_DNUMBER' => true, + 'T_START_NOWDOC' => true, + 'T_NOWDOC' => true, + 'T_END_NOWDOC' => true, + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + + return array( + \T_ECHO, + \T_PRINT, + \T_EXIT, + \T_STRING, + \T_OPEN_TAG_WITH_ECHO, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + + $this->mergeFunctionLists(); + + $function = $this->tokens[ $stackPtr ]['content']; + + // Find the opening parenthesis (if present; T_ECHO might not have it). + $open_paren = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + + // If function, not T_ECHO nor T_PRINT. + if ( \T_STRING === $this->tokens[ $stackPtr ]['code'] ) { + // Skip if it is a function but is not one of the printing functions. + if ( ! isset( $this->printingFunctions[ $this->tokens[ $stackPtr ]['content'] ] ) ) { + return; + } + + if ( isset( $this->tokens[ $open_paren ]['parenthesis_closer'] ) ) { + $end_of_statement = $this->tokens[ $open_paren ]['parenthesis_closer']; + } + + // These functions only need to have the first argument escaped. + if ( \in_array( $function, array( 'trigger_error', 'user_error' ), true ) ) { + $first_param = $this->get_function_call_parameter( $stackPtr, 1 ); + $end_of_statement = ( $first_param['end'] + 1 ); + unset( $first_param ); + } + + /* + * If the first param to `_deprecated_file()` follows the typical `basename( __FILE__ )` + * pattern, it doesn't need to be escaped. + */ + if ( '_deprecated_file' === $function ) { + $first_param = $this->get_function_call_parameter( $stackPtr, 1 ); + + // Quick check. This disregards comments. + if ( preg_match( '`^basename\s*\(\s*__FILE__\s*\)$`', $first_param['raw'] ) === 1 ) { + $stackPtr = ( $first_param['end'] + 2 ); + } + unset( $first_param ); + } + } + + // Checking for the ignore comment, ex: //xss ok. + if ( $this->has_whitelist_comment( 'xss', $stackPtr ) ) { + return; + } + + if ( isset( $this->unsafePrintingFunctions[ $function ] ) ) { + $error = $this->phpcsFile->addError( + "All output should be run through an escaping function (like %s), found '%s'.", + $stackPtr, + 'UnsafePrintingFunction', + array( $this->unsafePrintingFunctions[ $function ], $function ) + ); + + // If the error was reported, don't bother checking the function's arguments. + if ( $error ) { + return isset( $end_of_statement ) ? $end_of_statement : null; + } + } + + $ternary = false; + + // This is already determined if this is a function and not T_ECHO. + if ( ! isset( $end_of_statement ) ) { + + $end_of_statement = $this->phpcsFile->findNext( array( \T_SEMICOLON, \T_CLOSE_TAG ), $stackPtr ); + $last_token = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $end_of_statement - 1 ), null, true ); + + // Check for the ternary operator. We only need to do this here if this + // echo is lacking parenthesis. Otherwise it will be handled below. + if ( \T_OPEN_PARENTHESIS !== $this->tokens[ $open_paren ]['code'] || \T_CLOSE_PARENTHESIS !== $this->tokens[ $last_token ]['code'] ) { + + $ternary = $this->phpcsFile->findNext( \T_INLINE_THEN, $stackPtr, $end_of_statement ); + + // If there is a ternary skip over the part before the ?. However, if + // the ternary is within parentheses, it will be handled in the loop. + if ( false !== $ternary && empty( $this->tokens[ $ternary ]['nested_parenthesis'] ) ) { + $stackPtr = $ternary; + } + } + } + + // Ignore the function itself. + $stackPtr++; + + $in_cast = false; + + // Looping through echo'd components. + $watch = true; + for ( $i = $stackPtr; $i < $end_of_statement; $i++ ) { + + // Ignore whitespaces and comments. + if ( isset( Tokens::$emptyTokens[ $this->tokens[ $i ]['code'] ] ) ) { + continue; + } + + // Ignore namespace separators. + if ( \T_NS_SEPARATOR === $this->tokens[ $i ]['code'] ) { + continue; + } + + if ( \T_OPEN_PARENTHESIS === $this->tokens[ $i ]['code'] ) { + + if ( ! isset( $this->tokens[ $i ]['parenthesis_closer'] ) ) { + // Live coding or parse error. + break; + } + + if ( $in_cast ) { + + // Skip to the end of a function call if it has been casted to a safe value. + $i = $this->tokens[ $i ]['parenthesis_closer']; + $in_cast = false; + + } else { + + // Skip over the condition part of a ternary (i.e., to after the ?). + $ternary = $this->phpcsFile->findNext( \T_INLINE_THEN, $i, $this->tokens[ $i ]['parenthesis_closer'] ); + + if ( false !== $ternary ) { + + $next_paren = $this->phpcsFile->findNext( \T_OPEN_PARENTHESIS, ( $i + 1 ), $this->tokens[ $i ]['parenthesis_closer'] ); + + // We only do it if the ternary isn't within a subset of parentheses. + if ( false === $next_paren || ( isset( $this->tokens[ $next_paren ]['parenthesis_closer'] ) && $ternary > $this->tokens[ $next_paren ]['parenthesis_closer'] ) ) { + $i = $ternary; + } + } + } + + continue; + } + + // Handle arrays for those functions that accept them. + if ( \T_ARRAY === $this->tokens[ $i ]['code'] ) { + $i++; // Skip the opening parenthesis. + continue; + } + + if ( \T_OPEN_SHORT_ARRAY === $this->tokens[ $i ]['code'] + || \T_CLOSE_SHORT_ARRAY === $this->tokens[ $i ]['code'] + ) { + continue; + } + + if ( \in_array( $this->tokens[ $i ]['code'], array( \T_DOUBLE_ARROW, \T_CLOSE_PARENTHESIS ), true ) ) { + continue; + } + + // Handle magic constants for debug functions. + if ( isset( $this->magic_constant_tokens[ $this->tokens[ $i ]['type'] ] ) ) { + continue; + } + + // Handle safe PHP native constants. + if ( \T_STRING === $this->tokens[ $i ]['code'] + && isset( $this->safe_php_constants[ $this->tokens[ $i ]['content'] ] ) + && $this->is_use_of_global_constant( $i ) + ) { + continue; + } + + // Wake up on concatenation characters, another part to check. + if ( \T_STRING_CONCAT === $this->tokens[ $i ]['code'] ) { + $watch = true; + continue; + } + + // Wake up after a ternary else (:). + if ( false !== $ternary && \T_INLINE_ELSE === $this->tokens[ $i ]['code'] ) { + $watch = true; + continue; + } + + // Wake up for commas. + if ( \T_COMMA === $this->tokens[ $i ]['code'] ) { + $in_cast = false; + $watch = true; + continue; + } + + if ( false === $watch ) { + continue; + } + + // Allow T_CONSTANT_ENCAPSED_STRING eg: echo 'Some String'; + // Also T_LNUMBER, e.g.: echo 45; exit -1; and booleans. + if ( isset( $this->safe_components[ $this->tokens[ $i ]['type'] ] ) ) { + continue; + } + + $watch = false; + + // Allow int/double/bool casted variables. + if ( isset( $this->safe_casts[ $this->tokens[ $i ]['code'] ] ) ) { + $in_cast = true; + continue; + } + + // Now check that next token is a function call. + if ( \T_STRING === $this->tokens[ $i ]['code'] ) { + + $ptr = $i; + $functionName = $this->tokens[ $i ]['content']; + $function_opener = $this->phpcsFile->findNext( \T_OPEN_PARENTHESIS, ( $i + 1 ), null, false, null, true ); + $is_formatting_function = isset( $this->formattingFunctions[ $functionName ] ); + + if ( false !== $function_opener ) { + + if ( isset( $this->arrayWalkingFunctions[ $functionName ] ) ) { + + // Get the callback parameter. + $callback = $this->get_function_call_parameter( + $ptr, + $this->arrayWalkingFunctions[ $functionName ] + ); + + if ( ! empty( $callback ) ) { + /* + * If this is a function callback (not a method callback array) and we're able + * to resolve the function name, do so. + */ + $mapped_function = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + $callback['start'], + ( $callback['end'] + 1 ), + true + ); + + if ( false !== $mapped_function + && \T_CONSTANT_ENCAPSED_STRING === $this->tokens[ $mapped_function ]['code'] + ) { + $functionName = $this->strip_quotes( $this->tokens[ $mapped_function ]['content'] ); + $ptr = $mapped_function; + } + } + } + + // Skip pointer to after the function. + // If this is a formatting function we just skip over the opening + // parenthesis. Otherwise we skip all the way to the closing. + if ( $is_formatting_function ) { + $i = ( $function_opener + 1 ); + $watch = true; + } else { + if ( isset( $this->tokens[ $function_opener ]['parenthesis_closer'] ) ) { + $i = $this->tokens[ $function_opener ]['parenthesis_closer']; + } else { + // Live coding or parse error. + break; + } + } + } + + // If this is a safe function, we don't flag it. + if ( + $is_formatting_function + || isset( $this->autoEscapedFunctions[ $functionName ] ) + || isset( $this->escapingFunctions[ $functionName ] ) + ) { + continue; + } + + $content = $functionName; + + } else { + $content = $this->tokens[ $i ]['content']; + $ptr = $i; + } + + // Make the error message a little more informative for array access variables. + if ( \T_VARIABLE === $this->tokens[ $ptr ]['code'] ) { + $array_keys = $this->get_array_access_keys( $ptr ); + + if ( ! empty( $array_keys ) ) { + $content .= '[' . implode( '][', $array_keys ) . ']'; + } + } + + $this->phpcsFile->addError( + "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found '%s'.", + $ptr, + 'OutputNotEscaped', + array( $content ) + ); + } + + return $end_of_statement; + } + + /** + * Merge custom functions provided via a custom ruleset with the defaults, if we haven't already. + * + * @since 0.11.0 Split out from the `process()` method. + * + * @return void + */ + protected function mergeFunctionLists() { + if ( $this->customEscapingFunctions !== $this->addedCustomFunctions['escape'] ) { + $customEscapeFunctions = $this->merge_custom_array( $this->customEscapingFunctions, array(), false ); + + $this->escapingFunctions = $this->merge_custom_array( + $customEscapeFunctions, + $this->escapingFunctions + ); + + $this->addedCustomFunctions['escape'] = $this->customEscapingFunctions; + } + + if ( $this->customAutoEscapedFunctions !== $this->addedCustomFunctions['autoescape'] ) { + $this->autoEscapedFunctions = $this->merge_custom_array( + $this->customAutoEscapedFunctions, + $this->autoEscapedFunctions + ); + + $this->addedCustomFunctions['autoescape'] = $this->customAutoEscapedFunctions; + } + + if ( $this->customPrintingFunctions !== $this->addedCustomFunctions['print'] ) { + + $this->printingFunctions = $this->merge_custom_array( + $this->customPrintingFunctions, + $this->printingFunctions + ); + + $this->addedCustomFunctions['print'] = $this->customPrintingFunctions; + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/NonceVerificationSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/NonceVerificationSniff.php new file mode 100644 index 00000000..15f4a604 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/NonceVerificationSniff.php @@ -0,0 +1,178 @@ + true, + '$_FILE' => true, + '$_GET' => false, + '$_REQUEST' => false, + ); + + /** + * Custom list of functions which verify nonces. + * + * @since 0.5.0 + * + * @var string|string[] + */ + public $customNonceVerificationFunctions = array(); + + /** + * Custom list of functions that sanitize the values passed to them. + * + * @since 0.11.0 + * + * @var string|string[] + */ + public $customSanitizingFunctions = array(); + + /** + * Custom sanitizing functions that implicitly unslash the values passed to them. + * + * @since 0.11.0 + * + * @var string|string[] + */ + public $customUnslashingSanitizingFunctions = array(); + + /** + * Cache of previously added custom functions. + * + * Prevents having to do the same merges over and over again. + * + * @since 0.5.0 + * @since 0.11.0 - Changed from public static to protected non-static. + * - Changed the format from simple bool to array. + * + * @var array + */ + protected $addedCustomFunctions = array( + 'nonce' => array(), + 'sanitize' => array(), + 'unslashsanitize' => array(), + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + + return array( + \T_VARIABLE, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + public function process_token( $stackPtr ) { + + $instance = $this->tokens[ $stackPtr ]; + + if ( ! isset( $this->superglobals[ $instance['content'] ] ) ) { + return; + } + + if ( $this->has_whitelist_comment( 'CSRF', $stackPtr ) ) { + return; + } + + if ( $this->is_assignment( $stackPtr ) ) { + return; + } + + $this->mergeFunctionLists(); + + if ( $this->has_nonce_check( $stackPtr ) ) { + return; + } + + $error_code = 'Missing'; + if ( false === $this->superglobals[ $instance['content'] ] ) { + $error_code = 'Recommended'; + } + + // If we're still here, no nonce-verification function was found. + $this->addMessage( + 'Processing form data without nonce verification.', + $stackPtr, + $this->superglobals[ $instance['content'] ], + $error_code + ); + } + + /** + * Merge custom functions provided via a custom ruleset with the defaults, if we haven't already. + * + * @since 0.11.0 Split out from the `process()` method. + * + * @return void + */ + protected function mergeFunctionLists() { + if ( $this->customNonceVerificationFunctions !== $this->addedCustomFunctions['nonce'] ) { + $this->nonceVerificationFunctions = $this->merge_custom_array( + $this->customNonceVerificationFunctions, + $this->nonceVerificationFunctions + ); + + $this->addedCustomFunctions['nonce'] = $this->customNonceVerificationFunctions; + } + + if ( $this->customSanitizingFunctions !== $this->addedCustomFunctions['sanitize'] ) { + $this->sanitizingFunctions = $this->merge_custom_array( + $this->customSanitizingFunctions, + $this->sanitizingFunctions + ); + + $this->addedCustomFunctions['sanitize'] = $this->customSanitizingFunctions; + } + + if ( $this->customUnslashingSanitizingFunctions !== $this->addedCustomFunctions['unslashsanitize'] ) { + $this->unslashingSanitizingFunctions = $this->merge_custom_array( + $this->customUnslashingSanitizingFunctions, + $this->unslashingSanitizingFunctions + ); + + $this->addedCustomFunctions['unslashsanitize'] = $this->customUnslashingSanitizingFunctions; + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/PluginMenuSlugSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/PluginMenuSlugSniff.php new file mode 100644 index 00000000..08d8d58e --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/PluginMenuSlugSniff.php @@ -0,0 +1,89 @@ + => + */ + protected $target_functions = array( + 'add_menu_page' => array( 4 ), + 'add_object_page' => array( 4 ), + 'add_utility_page' => array( 4 ), + 'add_submenu_page' => array( 1, 5 ), + 'add_dashboard_page' => array( 4 ), + 'add_posts_page' => array( 4 ), + 'add_media_page' => array( 4 ), + 'add_links_page' => array( 4 ), + 'add_pages_page' => array( 4 ), + 'add_comments_page' => array( 4 ), + 'add_theme_page' => array( 4 ), + 'add_plugins_page' => array( 4 ), + 'add_users_page' => array( 4 ), + 'add_management_page' => array( 4 ), + 'add_options_page' => array( 4 ), + ); + + /** + * Process the parameters of a matched function. + * + * @since 0.11.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + foreach ( $this->target_functions[ $matched_content ] as $position ) { + if ( isset( $parameters[ $position ] ) ) { + $file_constant = $this->phpcsFile->findNext( \T_FILE, $parameters[ $position ]['start'], ( $parameters[ $position ]['end'] + 1 ) ); + + if ( false !== $file_constant ) { + $this->phpcsFile->addWarning( 'Using __FILE__ for menu slugs risks exposing filesystem structure.', $stackPtr, 'Using__FILE__' ); + } + } + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/SafeRedirectSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/SafeRedirectSniff.php new file mode 100644 index 00000000..2f6aa6a6 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/SafeRedirectSniff.php @@ -0,0 +1,48 @@ + array( + * 'lambda' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Use anonymous functions instead please!', + * 'functions' => array( 'file_get_contents', 'create_function' ), + * ) + * ) + * + * @return array + */ + public function getGroups() { + return array( + 'wp_redirect' => array( + 'type' => 'warning', + 'message' => '%s() found. Using wp_safe_redirect(), along with the allowed_redirect_hosts filter if needed, can help avoid any chances of malicious redirects within code. It is also important to remember to call exit() after a redirect so that no other unwanted code is executed.', + 'functions' => array( + 'wp_redirect', + ), + ), + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/ValidatedSanitizedInputSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/ValidatedSanitizedInputSniff.php new file mode 100644 index 00000000..a545c580 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Security/ValidatedSanitizedInputSniff.php @@ -0,0 +1,233 @@ + array(), + 'unslashsanitize' => array(), + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + return array( + \T_VARIABLE, + \T_DOUBLE_QUOTED_STRING, + \T_HEREDOC, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + public function process_token( $stackPtr ) { + + $superglobals = $this->input_superglobals; + + // Handling string interpolation. + if ( \T_DOUBLE_QUOTED_STRING === $this->tokens[ $stackPtr ]['code'] + || \T_HEREDOC === $this->tokens[ $stackPtr ]['code'] + ) { + $interpolated_variables = array_map( + function ( $symbol ) { + return '$' . $symbol; + }, + $this->get_interpolated_variables( $this->tokens[ $stackPtr ]['content'] ) + ); + foreach ( array_intersect( $interpolated_variables, $superglobals ) as $bad_variable ) { + $this->phpcsFile->addError( 'Detected usage of a non-sanitized, non-validated input variable %s: %s', $stackPtr, 'InputNotValidatedNotSanitized', array( $bad_variable, $this->tokens[ $stackPtr ]['content'] ) ); + } + + return; + } + + // Check if this is a superglobal. + if ( ! \in_array( $this->tokens[ $stackPtr ]['content'], $superglobals, true ) ) { + return; + } + + // If we're overriding a superglobal with an assignment, no need to test. + if ( $this->is_assignment( $stackPtr ) ) { + return; + } + + // This superglobal is being validated. + if ( $this->is_in_isset_or_empty( $stackPtr ) ) { + return; + } + + $array_keys = $this->get_array_access_keys( $stackPtr ); + + if ( empty( $array_keys ) ) { + return; + } + + $error_data = array( $this->tokens[ $stackPtr ]['content'] . '[' . implode( '][', $array_keys ) . ']' ); + + /* + * Check for validation first. + */ + $validated = false; + + for ( $i = ( $stackPtr + 1 ); $i < $this->phpcsFile->numTokens; $i++ ) { + if ( isset( Tokens::$emptyTokens[ $this->tokens[ $i ]['code'] ] ) ) { + continue; + } + + if ( \T_OPEN_SQUARE_BRACKET === $this->tokens[ $i ]['code'] + && isset( $this->tokens[ $i ]['bracket_closer'] ) + ) { + // Skip over array keys. + $i = $this->tokens[ $i ]['bracket_closer']; + continue; + } + + if ( \T_COALESCE === $this->tokens[ $i ]['code'] ) { + $validated = true; + } + + // Anything else means this is not a validation coalesce. + break; + } + + if ( false === $validated ) { + $validated = $this->is_validated( $stackPtr, $array_keys, $this->check_validation_in_scope_only ); + } + + if ( false === $validated ) { + $this->phpcsFile->addError( + 'Detected usage of a possibly undefined superglobal array index: %s. Use isset() or empty() to check the index exists before using it', + $stackPtr, + 'InputNotValidated', + $error_data + ); + } + + if ( $this->has_whitelist_comment( 'sanitization', $stackPtr ) ) { + return; + } + + // If this variable is being tested with one of the `is_..()` functions, sanitization isn't needed. + if ( $this->is_in_type_test( $stackPtr ) ) { + return; + } + + // If this is a comparison ('a' == $_POST['foo']), sanitization isn't needed. + if ( $this->is_comparison( $stackPtr, false ) ) { + return; + } + + // If this is a comparison using the array comparison functions, sanitization isn't needed. + if ( $this->is_in_array_comparison( $stackPtr ) ) { + return; + } + + $this->mergeFunctionLists(); + + // Now look for sanitizing functions. + if ( ! $this->is_sanitized( $stackPtr, true ) ) { + $this->phpcsFile->addError( + 'Detected usage of a non-sanitized input variable: %s', + $stackPtr, + 'InputNotSanitized', + $error_data + ); + } + } + + /** + * Merge custom functions provided via a custom ruleset with the defaults, if we haven't already. + * + * @since 0.11.0 Split out from the `process()` method. + * + * @return void + */ + protected function mergeFunctionLists() { + if ( $this->customSanitizingFunctions !== $this->addedCustomFunctions['sanitize'] ) { + $this->sanitizingFunctions = $this->merge_custom_array( + $this->customSanitizingFunctions, + $this->sanitizingFunctions + ); + + $this->addedCustomFunctions['sanitize'] = $this->customSanitizingFunctions; + } + + if ( $this->customUnslashingSanitizingFunctions !== $this->addedCustomFunctions['unslashsanitize'] ) { + $this->unslashingSanitizingFunctions = $this->merge_custom_array( + $this->customUnslashingSanitizingFunctions, + $this->unslashingSanitizingFunctions + ); + + $this->addedCustomFunctions['unslashsanitize'] = $this->customUnslashingSanitizingFunctions; + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Utils/I18nTextDomainFixerSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Utils/I18nTextDomainFixerSniff.php new file mode 100644 index 00000000..e7090b48 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/Utils/I18nTextDomainFixerSniff.php @@ -0,0 +1,731 @@ + => + */ + protected $target_functions = array( + 'load_textdomain' => 1, + 'load_plugin_textdomain' => 1, + 'load_muplugin_textdomain' => 1, + 'load_theme_textdomain' => 1, + 'load_child_theme_textdomain' => 1, + 'unload_textdomain' => 1, + + '__' => 2, + '_e' => 2, + '_x' => 3, + '_ex' => 3, + '_n' => 4, + '_nx' => 5, + '_n_noop' => 3, + '_nx_noop' => 4, + 'translate_nooped_plural' => 3, + '_c' => 2, // Deprecated. + '_nc' => 4, // Deprecated. + '__ngettext' => 4, // Deprecated. + '__ngettext_noop' => 3, // Deprecated. + 'translate_with_context' => 2, // Deprecated. + + 'esc_html__' => 2, + 'esc_html_e' => 2, + 'esc_html_x' => 3, + 'esc_attr__' => 2, + 'esc_attr_e' => 2, + 'esc_attr_x' => 3, + + 'is_textdomain_loaded' => 1, + 'get_translations_for_domain' => 1, + + // Shouldn't be used by plugins/themes. + 'translate' => 2, + 'translate_with_gettext_context' => 3, + + // WP private functions. Shouldn't be used by plugins/themes. + '_load_textdomain_just_in_time' => 1, + '_get_path_to_translation_from_lang_dir' => 1, + '_get_path_to_translation' => 1, + ); + + /** + * Whether a valid new text domain was found. + * + * @since 1.2.0 + * + * @var bool + */ + private $is_valid = false; + + /** + * The new text domain as validated. + * + * @since 1.2.0 + * + * @var string + */ + private $validated_textdomain = ''; + + /** + * Whether the plugin/theme header has been seen and fixed yet. + * + * @since 1.2.0 + * + * @var bool + */ + private $header_found = false; + + /** + * Possible headers for a theme. + * + * @link https://developer.wordpress.org/themes/basics/main-stylesheet-style-css/ + * + * @since 1.2.0 + * + * @var array Array key is the header name, the value indicated whether it is a + * required (true) or optional (false) header. + */ + private $theme_headers = array( + 'Theme Name' => true, + 'Theme URI' => false, + 'Author' => true, + 'Author URI' => false, + 'Description' => true, + 'Version' => true, + 'License' => true, + 'License URI' => true, + 'Tags' => false, + 'Text Domain' => true, + 'Domain Path' => false, + ); + + /** + * Possible headers for a plugin. + * + * @link https://developer.wordpress.org/plugins/the-basics/header-requirements/ + * + * @since 1.2.0 + * + * @var array Array key is the header name, the value indicated whether it is a + * required (true) or optional (false) header. + */ + private $plugin_headers = array( + 'Plugin Name' => true, + 'Plugin URI' => false, + 'Description' => false, + 'Version' => false, + 'Author' => false, + 'Author URI' => false, + 'License' => false, + 'License URI' => false, + 'Text Domain' => false, + 'Domain Path' => false, + 'Network' => false, + ); + + /** + * Regex template to match theme/plugin headers. + * + * @since 1.2.0 + * + * @var string + */ + private $header_regex_template = '`^(?:\s*(?:(?:\*|//)\s*)?)?(%s)\s*:\s*([^\r\n]+)`'; + + /** + * Regex to match theme headers. + * + * Set from within the register() method. + * + * @since 1.2.0 + * + * @var string + */ + private $theme_header_regex; + + /** + * Regex to match plugin headers. + * + * Set from within the register() method. + * + * @since 1.2.0 + * + * @var string + */ + private $plugin_header_regex; + + /** + * The --tab-width CLI value that is being used. + * + * @since 1.2.0 + * + * @var integer + */ + private $tab_width = null; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.2.0 + * + * @return array + */ + public function register() { + $headers = array_map( + 'preg_quote', + array_keys( $this->theme_headers ), + array_fill( 0, \count( $this->theme_headers ), '`' ) + ); + $this->theme_header_regex = sprintf( $this->header_regex_template, implode( '|', $headers ) ); + + $headers = array_map( + 'preg_quote', + array_keys( $this->plugin_headers ), + array_fill( 0, \count( $this->plugin_headers ), '`' ) + ); + $this->plugin_header_regex = sprintf( $this->header_regex_template, implode( '|', $headers ) ); + + $targets = parent::register(); + + $targets[] = \T_DOC_COMMENT_OPEN_TAG; + $targets[] = \T_COMMENT; + + return $targets; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.2.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + // Check if the old/new properties are correctly set. If not, bow out. + if ( ! is_string( $this->new_text_domain ) + || '' === $this->new_text_domain + ) { + return ( $this->phpcsFile->numTokens + 1 ); + } + + if ( isset( $this->old_text_domain ) ) { + $this->old_text_domain = $this->merge_custom_array( $this->old_text_domain, array(), false ); + + if ( ! is_array( $this->old_text_domain ) + || array() === $this->old_text_domain + ) { + return ( $this->phpcsFile->numTokens + 1 ); + } + } + + // Only validate and throw warning about the text domain once. + if ( $this->new_text_domain !== $this->validated_textdomain ) { + $this->is_valid = false; + $this->validated_textdomain = $this->new_text_domain; + $this->header_found = false; + + if ( 'default' === $this->new_text_domain ) { + $this->phpcsFile->addWarning( + 'The "default" text domain is reserved for WordPress core use and should not be used by plugins or themes', + 0, + 'ReservedNewDomain', + array( $this->new_text_domain ) + ); + + return ( $this->phpcsFile->numTokens + 1 ); + } + + if ( preg_match( '`^[a-z0-9-]+$`', $this->new_text_domain ) !== 1 ) { + $this->phpcsFile->addWarning( + 'The text domain should be a simple lowercase text string with words separated by dashes. "%s" appears invalid', + 0, + 'InvalidNewDomain', + array( $this->new_text_domain ) + ); + + return ( $this->phpcsFile->numTokens + 1 ); + } + + // If the text domain passed both validations, it should be considered valid. + $this->is_valid = true; + + } elseif ( false === $this->is_valid ) { + return ( $this->phpcsFile->numTokens + 1 ); + } + + if ( isset( $this->tab_width ) === false ) { + if ( isset( $this->phpcsFile->config->tabWidth ) === false + || 0 === $this->phpcsFile->config->tabWidth + ) { + // We have no idea how wide tabs are, so assume 4 spaces for fixing. + $this->tab_width = 4; + } else { + $this->tab_width = $this->phpcsFile->config->tabWidth; + } + } + + if ( \T_DOC_COMMENT_OPEN_TAG === $this->tokens[ $stackPtr ]['code'] + || \T_COMMENT === $this->tokens[ $stackPtr ]['code'] + ) { + // Examine for plugin/theme file header. + return $this->process_comments( $stackPtr ); + + } elseif ( 'CSS' !== $this->phpcsFile->tokenizerType ) { + // Examine a T_STRING token in a PHP file as a function call. + return parent::process_token( $stackPtr ); + } + } + + + /** + * Process the parameters of a matched function. + * + * @since 1.2.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + $target_param = $this->target_functions[ $matched_content ]; + + if ( isset( $parameters[ $target_param ] ) === false && 1 !== $target_param ) { + $error_msg = 'Missing $domain arg'; + $error_code = 'MissingArgDomain'; + + if ( isset( $parameters[ ( $target_param - 1 ) ] ) ) { + $fix = $this->phpcsFile->addFixableError( $error_msg, $stackPtr, $error_code ); + + if ( true === $fix ) { + $start_previous = $parameters[ ( $target_param - 1 ) ]['start']; + $end_previous = $parameters[ ( $target_param - 1 ) ]['end']; + if ( \T_WHITESPACE === $this->tokens[ $start_previous ]['code'] + && $this->tokens[ $start_previous ]['content'] === $this->phpcsFile->eolChar + ) { + // Replicate the new line + indentation of the previous item. + $replacement = ','; + for ( $i = $start_previous; $i <= $end_previous; $i++ ) { + if ( \T_WHITESPACE !== $this->tokens[ $i ]['code'] ) { + break; + } + + if ( isset( $this->tokens[ $i ]['orig_content'] ) ) { + $replacement .= $this->tokens[ $i ]['orig_content']; + } else { + $replacement .= $this->tokens[ $i ]['content']; + } + } + + $replacement .= "'{$this->new_text_domain}'"; + } else { + $replacement = ", '{$this->new_text_domain}'"; + } + + if ( \T_WHITESPACE === $this->tokens[ $end_previous ]['code'] ) { + $this->phpcsFile->fixer->addContentBefore( $end_previous, $replacement ); + } else { + $this->phpcsFile->fixer->addContent( $end_previous, $replacement ); + } + } + } else { + $error_msg .= ' and preceding argument(s)'; + $error_code = 'MissingArgs'; + + // Expected preceeding param also missing, just throw the warning. + $this->phpcsFile->addWarning( $error_msg, $stackPtr, $error_code ); + } + + return; + } + + // Target parameter found. Let's examine it. + $domain_param_start = $parameters[ $target_param ]['start']; + $domain_param_end = $parameters[ $target_param ]['end']; + $domain_token = null; + + for ( $i = $domain_param_start; $i <= $domain_param_end; $i++ ) { + if ( isset( Tokens::$emptyTokens[ $this->tokens[ $i ]['code'] ] ) ) { + continue; + } + + if ( \T_CONSTANT_ENCAPSED_STRING !== $this->tokens[ $i ]['code'] ) { + // Unexpected token found, not our concern. This is handled by the I18n sniff. + return; + } + + if ( isset( $domain_token ) ) { + // More than one T_CONSTANT_ENCAPSED_STRING found, not our concern. This is handled by the I18n sniff. + return; + } + + $domain_token = $i; + } + + // If we're still here, this means only one T_CONSTANT_ENCAPSED_STRING was found. + $old_domain = $this->strip_quotes( $this->tokens[ $domain_token ]['content'] ); + + if ( ! \in_array( $old_domain, $this->old_text_domain, true ) ) { + // Not a text domain targetted for replacement, ignore. + return; + } + + $fix = $this->phpcsFile->addFixableError( + 'Mismatched text domain. Expected \'%s\' but found \'%s\'', + $domain_token, + 'TextDomainMismatch', + array( $this->new_text_domain, $old_domain ) + ); + + if ( true === $fix ) { + $replacement = str_replace( $old_domain, $this->new_text_domain, $this->tokens[ $domain_token ]['content'] ); + $this->phpcsFile->fixer->replaceToken( $domain_token, $replacement ); + } + } + + /** + * Process the function if no parameters were found. + * + * @since 1.2.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * + * @return void + */ + public function process_no_parameters( $stackPtr, $group_name, $matched_content ) { + + $target_param = $this->target_functions[ $matched_content ]; + + if ( 1 !== $target_param ) { + // Only process the no param case as fixable if the text domain is expected to be the first parameter. + $this->phpcsFile->addWarning( 'Missing $domain arg and preceding argument(s)', $stackPtr, 'MissingArgs' ); + return; + } + + $opener = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + if ( \T_OPEN_PARENTHESIS !== $this->tokens[ $opener ]['code'] + || isset( $this->tokens[ $opener ]['parenthesis_closer'] ) === false + ) { + // Parse error or live coding. + return; + } + + $fix = $this->phpcsFile->addFixableError( 'Missing $domain arg', $stackPtr, 'MissingArgDomain' ); + if ( true === $fix ) { + $closer = $this->tokens[ $opener ]['parenthesis_closer']; + $replacement = " '{$this->new_text_domain}' "; + + if ( $this->tokens[ $opener ]['line'] !== $this->tokens[ $closer ]['line'] ) { + $replacement = trim( $replacement ); + $addBefore = ( $closer - 1 ); + if ( \T_WHITESPACE === $this->tokens[ ( $closer - 1 ) ]['code'] + && $this->tokens[ $closer - 1 ]['line'] === $this->tokens[ $closer ]['line'] + ) { + if ( isset( $this->tokens[ ( $closer - 1 ) ]['orig_content'] ) ) { + $replacement = $this->tokens[ ( $closer - 1 ) ]['orig_content'] + . "\t" + . $replacement; + } else { + $replacement = $this->tokens[ ( $closer - 1 ) ]['content'] + . str_repeat( ' ', $this->tab_width ) + . $replacement; + } + + --$addBefore; + } else { + // We don't know whether the code uses tabs or spaces, so presume WPCS, i.e. tabs. + $replacement = "\t" . $replacement; + } + + $replacement = $this->phpcsFile->eolChar . $replacement; + + $this->phpcsFile->fixer->addContentBefore( $addBefore, $replacement ); + + } elseif ( \T_WHITESPACE === $this->tokens[ ( $closer - 1 ) ]['code'] ) { + $this->phpcsFile->fixer->replaceToken( ( $closer - 1 ), $replacement ); + } else { + $this->phpcsFile->fixer->addContentBefore( $closer, $replacement ); + } + } + } + + + /** + * Process comments to find the plugin/theme headers. + * + * @since 1.2.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_comments( $stackPtr ) { + if ( true === $this->header_found && ! defined( 'PHP_CODESNIFFER_IN_TESTS' ) ) { + return; + } + + $regex = $this->plugin_header_regex; + $headers = $this->plugin_headers; + $type = 'plugin'; + $skip_to = $stackPtr; + + $file = $this->strip_quotes( $this->phpcsFile->getFileName() ); + if ( 'STDIN' === $file ) { + return; + } + + $file_name = basename( $file ); + if ( 'CSS' === $this->phpcsFile->tokenizerType ) { + if ( 'style.css' !== $file_name && ! defined( 'PHP_CODESNIFFER_IN_TESTS' ) ) { + // CSS files only need to be examined for the file header. + return ( $this->phpcsFile->numTokens + 1 ); + } + + $regex = $this->theme_header_regex; + $headers = $this->theme_headers; + $type = 'theme'; + } + + $comment_details = array( + 'required_header_found' => false, + 'headers_found' => 0, + 'text_domain_ptr' => false, + 'text_domain_found' => '', + 'last_header_ptr' => false, + 'last_header_matches' => array(), + ); + + if ( \T_COMMENT === $this->tokens[ $stackPtr ]['code'] ) { + $block_comment = false; + if ( substr( $this->tokens[ $stackPtr ]['content'], 0, 2 ) === '/*' ) { + $block_comment = true; + } + + $current = $stackPtr; + do { + if ( false === $comment_details['text_domain_ptr'] + || false === $comment_details['required_header_found'] + || $comment_details['headers_found'] < 3 + ) { + $comment_details = $this->examine_comment_line( $current, $regex, $headers, $comment_details ); + } + + if ( true === $block_comment && substr( $this->tokens[ $current ]['content'], -2 ) === '*/' ) { + ++$current; + break; + } + + ++$current; + } while ( isset( $this->tokens[ $current ] ) && \T_COMMENT === $this->tokens[ $current ]['code'] ); + + $skip_to = $current; + + } else { + if ( ! isset( $this->tokens[ $stackPtr ]['comment_closer'] ) ) { + return; + } + + $closer = $this->tokens[ $stackPtr ]['comment_closer']; + $current = $stackPtr; + + while ( ( $current = $this->phpcsFile->findNext( \T_DOC_COMMENT_STRING, ( $current + 1 ), $closer ) ) !== false ) { + $comment_details = $this->examine_comment_line( $current, $regex, $headers, $comment_details ); + + if ( false !== $comment_details['text_domain_ptr'] + && true === $comment_details['required_header_found'] + && $comment_details['headers_found'] >= 3 + ) { + // No need to look at the rest of the docblock. + break; + } + } + + $skip_to = $closer; + } + + // So, was this the plugin/theme header ? + if ( true === $comment_details['required_header_found'] + && $comment_details['headers_found'] >= 3 + ) { + $this->header_found = true; + + $text_domain_ptr = $comment_details['text_domain_ptr']; + $text_domain_found = $comment_details['text_domain_found']; + + if ( false !== $text_domain_ptr ) { + if ( $this->new_text_domain !== $text_domain_found + && ( \in_array( $text_domain_found, $this->old_text_domain, true ) ) + ) { + $fix = $this->phpcsFile->addFixableError( + 'Mismatched text domain in %s header. Expected \'%s\' but found \'%s\'', + $text_domain_ptr, + 'TextDomainHeaderMismatch', + array( + $type, + $this->new_text_domain, + $text_domain_found, + ) + ); + + if ( true === $fix ) { + if ( isset( $this->tokens[ $text_domain_ptr ]['orig_content'] ) ) { + $replacement = $this->tokens[ $text_domain_ptr ]['orig_content']; + } else { + $replacement = $this->tokens[ $text_domain_ptr ]['content']; + } + + $replacement = str_replace( $text_domain_found, $this->new_text_domain, $replacement ); + + $this->phpcsFile->fixer->replaceToken( $text_domain_ptr, $replacement ); + } + } + } else { + $last_header_ptr = $comment_details['last_header_ptr']; + $last_header_matches = $comment_details['last_header_matches']; + + $fix = $this->phpcsFile->addFixableError( + 'Missing "Text Domain" in %s header', + $last_header_ptr, + 'MissingTextDomainHeader', + array( $type ) + ); + + if ( true === $fix ) { + if ( isset( $this->tokens[ $last_header_ptr ]['orig_content'] ) ) { + $replacement = $this->tokens[ $last_header_ptr ]['orig_content']; + } else { + $replacement = $this->tokens[ $last_header_ptr ]['content']; + } + + $replacement = str_replace( $last_header_matches[1], 'Text Domain', $replacement ); + $replacement = str_replace( $last_header_matches[2], $this->new_text_domain, $replacement ); + + if ( \T_DOC_COMMENT_OPEN_TAG === $this->tokens[ $stackPtr ]['code'] ) { + for ( $i = ( $last_header_ptr - 1 ); ; $i-- ) { + if ( $this->tokens[ $i ]['line'] !== $this->tokens[ $last_header_ptr ]['line'] ) { + ++$i; + break; + } + } + + $replacement = $this->phpcsFile->eolChar + . $this->phpcsFile->getTokensAsString( $i, ( $last_header_ptr - $i ), true ) + . $replacement; + } + + $this->phpcsFile->fixer->addContent( $comment_details['last_header_ptr'], $replacement ); + } + } + } + + return $skip_to; + } + + /** + * Examine an individual token in a larger comment for plugin/theme headers. + * + * @since 1.2.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $regex The regex to use to examine the comment line. + * @param array $headers Valid headers for a plugin or theme. + * @param array $comment_details The information collected so far. + * + * @return array Adjusted $comment_details array + */ + protected function examine_comment_line( $stackPtr, $regex, $headers, $comment_details ) { + if ( preg_match( $regex, $this->tokens[ $stackPtr ]['content'], $matches ) === 1 ) { + ++$comment_details['headers_found']; + + if ( true === $headers[ $matches[1] ] ) { + $comment_details['required_header_found'] = true; + } + + if ( 'Text Domain' === $matches[1] ) { + $comment_details['text_domain_ptr'] = $stackPtr; + $comment_details['text_domain_found'] = trim( $matches[2] ); + } + + $comment_details['last_header_ptr'] = $stackPtr; + $comment_details['last_header_matches'] = $matches; + } + + return $comment_details; + } +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/AlternativeFunctionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/AlternativeFunctionsSniff.php new file mode 100644 index 00000000..fc6e8fbb --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/AlternativeFunctionsSniff.php @@ -0,0 +1,316 @@ + true, + 'php://output' => true, + 'php://stdin' => true, + 'php://stdout' => true, + 'php://stderr' => true, + ); + + /** + * Local input streams which should not be flagged for the file system function checks if + * the $filename starts with them. + * + * @link http://php.net/manual/en/wrappers.php.php + * + * @var array + */ + protected $allowed_local_stream_partials = array( + 'php://temp/', + 'php://fd/', + ); + + /** + * Local input stream constants which should not be flagged for the file system function checks. + * + * @link http://php.net/manual/en/wrappers.php.php + * + * @var array + */ + protected $allowed_local_stream_constants = array( + 'STDIN' => true, + 'STDOUT' => true, + 'STDERR' => true, + ); + + /** + * Groups of functions to restrict. + * + * Example: groups => array( + * 'lambda' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Use anonymous functions instead please!', + * 'since' => '4.9.0', //=> the WP version in which the alternative became available. + * 'functions' => array( 'file_get_contents', 'create_function' ), + * ) + * ) + * + * @return array + */ + public function getGroups() { + return array( + 'curl' => array( + 'type' => 'warning', + 'message' => 'Using cURL functions is highly discouraged. Use wp_remote_get() instead.', + 'since' => '2.7.0', + 'functions' => array( + 'curl_*', + ), + 'whitelist' => array( + 'curl_version' => true, + ), + ), + + 'parse_url' => array( + 'type' => 'warning', + 'message' => '%s() is discouraged because of inconsistency in the output across PHP versions; use wp_parse_url() instead.', + 'since' => '4.4.0', + 'functions' => array( + 'parse_url', + ), + ), + + 'json_encode' => array( + 'type' => 'warning', + 'message' => '%s() is discouraged. Use wp_json_encode() instead.', + 'since' => '4.1.0', + 'functions' => array( + 'json_encode', + ), + ), + + 'file_get_contents' => array( + 'type' => 'warning', + 'message' => '%s() is discouraged. Use wp_remote_get() for remote URLs instead.', + 'since' => '2.7.0', + 'functions' => array( + 'file_get_contents', + ), + ), + + 'file_system_read' => array( + 'type' => 'warning', + 'message' => 'File operations should use WP_Filesystem methods instead of direct PHP filesystem calls. Found: %s()', + 'since' => '2.5.0', + 'functions' => array( + 'readfile', + 'fclose', + 'fopen', + 'fread', + 'fwrite', + 'file_put_contents', + 'fsockopen', + 'pfsockopen', + ), + ), + + 'strip_tags' => array( + 'type' => 'warning', + 'message' => '%s() is discouraged. Use the more comprehensive wp_strip_all_tags() instead.', + 'since' => '2.9.0', + 'functions' => array( + 'strip_tags', + ), + ), + + 'rand_seeding' => array( + 'type' => 'warning', + 'message' => '%s() is discouraged. Rand seeding is not necessary when using the wp_rand() function (as you should).', + 'since' => '2.6.2', + 'functions' => array( + 'srand', + 'mt_srand', + ), + ), + + 'rand' => array( + 'type' => 'warning', + 'message' => '%s() is discouraged. Use the far less predictable wp_rand() instead.', + 'since' => '2.6.2', + 'functions' => array( + 'rand', + 'mt_rand', + ), + ), + ); + } + + /** + * Process a matched token. + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_matched_token( $stackPtr, $group_name, $matched_content ) { + + $this->get_wp_version_from_cl(); + + /* + * Deal with exceptions. + */ + switch ( $matched_content ) { + case 'strip_tags': + /* + * The function `wp_strip_all_tags()` is only a valid alternative when + * only the first parameter is passed to `strip_tags()`. + */ + if ( $this->get_function_call_parameter_count( $stackPtr ) !== 1 ) { + return; + } + + break; + + case 'wp_parse_url': + /* + * Before WP 4.7.0, the function `wp_parse_url()` was only a valid alternative + * if no second param was passed to `parse_url()`. + * + * @see https://developer.wordpress.org/reference/functions/wp_parse_url/#changelog + */ + if ( $this->get_function_call_parameter_count( $stackPtr ) !== 1 + && version_compare( $this->minimum_supported_version, '4.7.0', '<' ) + ) { + return; + } + + break; + + case 'file_get_contents': + /* + * Using `wp_remote_get()` will only work for remote URLs. + * See if we can determine is this function call is for a local file and if so, bow out. + */ + $params = $this->get_function_call_parameters( $stackPtr ); + + if ( isset( $params[2] ) && 'true' === $params[2]['raw'] ) { + // Setting `$use_include_path` to `true` is only relevant for local files. + return; + } + + if ( isset( $params[1] ) === false ) { + // If the file to get is not set, this is a non-issue anyway. + return; + } + + if ( strpos( $params[1]['raw'], 'http:' ) !== false + || strpos( $params[1]['raw'], 'https:' ) !== false + ) { + // Definitely a URL, throw notice. + break; + } + + if ( preg_match( '`\b(?:ABSPATH|WP_(?:CONTENT|PLUGIN)_DIR|WPMU_PLUGIN_DIR|TEMPLATEPATH|STYLESHEETPATH|(?:MU)?PLUGINDIR)\b`', $params[1]['raw'] ) === 1 ) { + // Using any of the constants matched in this regex is an indicator of a local file. + return; + } + + if ( preg_match( '`(?:get_home_path|plugin_dir_path|get_(?:stylesheet|template)_directory|wp_upload_dir)\s*\(`i', $params[1]['raw'] ) === 1 ) { + // Using any of the functions matched in the regex is an indicator of a local file. + return; + } + + if ( $this->is_local_data_stream( $params[1]['raw'] ) === true ) { + // Local data stream. + return; + } + + unset( $params ); + + break; + + case 'readfile': + case 'fopen': + case 'file_put_contents': + /* + * Allow for handling raw data streams from the request body. + */ + $first_param = $this->get_function_call_parameter( $stackPtr, 1 ); + + if ( false === $first_param ) { + // If the file to work with is not set, local data streams don't come into play. + break; + } + + if ( $this->is_local_data_stream( $first_param['raw'] ) === true ) { + // Local data stream. + return; + } + + unset( $first_param ); + + break; + } + + if ( ! isset( $this->groups[ $group_name ]['since'] ) ) { + return parent::process_matched_token( $stackPtr, $group_name, $matched_content ); + } + + // Verify if the alternative is available in the minimum supported WP version. + if ( version_compare( $this->groups[ $group_name ]['since'], $this->minimum_supported_version, '<=' ) ) { + return parent::process_matched_token( $stackPtr, $group_name, $matched_content ); + } + } + + /** + * Determine based on the "raw" parameter value, whether a file parameter points to + * a local data stream. + * + * @param string $raw_param_value Raw parameter value. + * + * @return bool True if this is a local data stream. False otherwise. + */ + protected function is_local_data_stream( $raw_param_value ) { + + $raw_stripped = $this->strip_quotes( $raw_param_value ); + if ( isset( $this->allowed_local_streams[ $raw_stripped ] ) + || isset( $this->allowed_local_stream_constants[ $raw_param_value ] ) + ) { + return true; + } + + foreach ( $this->allowed_local_stream_partials as $partial ) { + if ( strpos( $raw_stripped, $partial ) === 0 ) { + return true; + } + } + + return false; + } +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/CapitalPDangitSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/CapitalPDangitSniff.php new file mode 100644 index 00000000..bf98233f --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/CapitalPDangitSniff.php @@ -0,0 +1,292 @@ +\'"()]*?\.(?:php|js|css|png|j[e]?pg|gif|pot))#i'; + + /** + * Regex to match a large number or spelling variations of WordPress in class names. + * + * @var string + */ + const WP_CLASSNAME_REGEX = '`(?:^|_)(Word[_]*Pres+)(?:_|$)`i'; + + /** + * String tokens we want to listen for. + * + * @var array + */ + private $text_string_tokens = array( + \T_CONSTANT_ENCAPSED_STRING => \T_CONSTANT_ENCAPSED_STRING, + \T_DOUBLE_QUOTED_STRING => \T_DOUBLE_QUOTED_STRING, + \T_HEREDOC => \T_HEREDOC, + \T_NOWDOC => \T_NOWDOC, + \T_INLINE_HTML => \T_INLINE_HTML, + ); + + /** + * Comment tokens we want to listen for as they contain text strings. + * + * @var array + */ + private $comment_text_tokens = array( + \T_DOC_COMMENT => \T_DOC_COMMENT, + \T_DOC_COMMENT_STRING => \T_DOC_COMMENT_STRING, + \T_COMMENT => \T_COMMENT, + ); + + /** + * Combined text string and comment tokens array. + * + * This property is set in the register() method and used for lookups. + * + * @var array + */ + private $text_and_comment_tokens = array(); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 0.12.0 + * + * @return array + */ + public function register() { + // Union the arrays - keeps the array keys. + $this->text_and_comment_tokens = ( $this->text_string_tokens + $this->comment_text_tokens ); + + $targets = ( $this->text_and_comment_tokens + Tokens::$ooScopeTokens ); + + // Also sniff for array tokens to make skipping anything within those more efficient. + $targets[ \T_ARRAY ] = \T_ARRAY; + $targets[ \T_OPEN_SHORT_ARRAY ] = \T_OPEN_SHORT_ARRAY; + + return $targets; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 0.12.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + + if ( $this->has_whitelist_comment( 'spelling', $stackPtr ) ) { + return; + } + + /* + * Ignore tokens within an array definition as this is a false positive in 80% of all cases. + * + * The return values skip to the end of the array. + * This prevents the sniff "hanging" on very long configuration arrays. + */ + if ( \T_OPEN_SHORT_ARRAY === $this->tokens[ $stackPtr ]['code'] && isset( $this->tokens[ $stackPtr ]['bracket_closer'] ) ) { + return $this->tokens[ $stackPtr ]['bracket_closer']; + } elseif ( \T_ARRAY === $this->tokens[ $stackPtr ]['code'] && isset( $this->tokens[ $stackPtr ]['parenthesis_closer'] ) ) { + return $this->tokens[ $stackPtr ]['parenthesis_closer']; + } + + /* + * Deal with misspellings in class/interface/trait names. + * These are not auto-fixable, but need the attention of a developer. + */ + if ( isset( Tokens::$ooScopeTokens[ $this->tokens[ $stackPtr ]['code'] ] ) ) { + $classname = $this->phpcsFile->getDeclarationName( $stackPtr ); + if ( empty( $classname ) ) { + return; + } + + if ( preg_match_all( self::WP_CLASSNAME_REGEX, $classname, $matches, \PREG_PATTERN_ORDER ) > 0 ) { + $mispelled = $this->retrieve_misspellings( $matches[1] ); + + if ( ! empty( $mispelled ) ) { + $this->phpcsFile->addWarning( + 'Please spell "WordPress" correctly. Found: "%s" as part of the class/interface/trait name.', + $stackPtr, + 'MisspelledClassName', + array( implode( ', ', $mispelled ) ) + ); + } + } + + return; + } + + /* + * Deal with misspellings in text strings and documentation. + */ + + // Ignore content of docblock @link tags. + if ( \T_DOC_COMMENT_STRING === $this->tokens[ $stackPtr ]['code'] + || \T_DOC_COMMENT === $this->tokens[ $stackPtr ]['code'] + ) { + + $comment_start = $this->phpcsFile->findPrevious( \T_DOC_COMMENT_OPEN_TAG, ( $stackPtr - 1 ) ); + if ( false !== $comment_start ) { + $comment_tag = $this->phpcsFile->findPrevious( \T_DOC_COMMENT_TAG, ( $stackPtr - 1 ), $comment_start ); + if ( false !== $comment_tag && '@link' === $this->tokens[ $comment_tag ]['content'] ) { + // @link tag, so ignore. + return; + } + } + } + + // Ignore any text strings which are array keys `$var['key']` as this is a false positive in 80% of all cases. + if ( \T_CONSTANT_ENCAPSED_STRING === $this->tokens[ $stackPtr ]['code'] ) { + $prevToken = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true, null, true ); + if ( false !== $prevToken && \T_OPEN_SQUARE_BRACKET === $this->tokens[ $prevToken ]['code'] ) { + $nextToken = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true, null, true ); + if ( false !== $nextToken && \T_CLOSE_SQUARE_BRACKET === $this->tokens[ $nextToken ]['code'] ) { + return; + } + } + } + + // Ignore constant declarations via define(). + if ( $this->is_in_function_call( $stackPtr, array( 'define' => true ), true, true ) ) { + return; + } + + // Ignore constant declarations using the const keyword. + $stop_points = array( + \T_CONST, + \T_SEMICOLON, + \T_OPEN_TAG, + \T_CLOSE_TAG, + \T_OPEN_CURLY_BRACKET, + ); + $maybe_const = $this->phpcsFile->findPrevious( $stop_points, ( $stackPtr - 1 ) ); + if ( false !== $maybe_const && \T_CONST === $this->tokens[ $maybe_const ]['code'] ) { + return; + } + + $content = $this->tokens[ $stackPtr ]['content']; + + if ( preg_match_all( self::WP_REGEX, $content, $matches, ( \PREG_PATTERN_ORDER | \PREG_OFFSET_CAPTURE ) ) > 0 ) { + /* + * Prevent some typical false positives. + */ + if ( isset( $this->text_and_comment_tokens[ $this->tokens[ $stackPtr ]['code'] ] ) ) { + $offset = 0; + foreach ( $matches[1] as $key => $match_data ) { + $next_offset = ( $match_data[1] + \strlen( $match_data[0] ) ); + + // Prevent matches on part of a URL. + if ( preg_match( '`http[s]?://[^\s<>\'"()]*' . preg_quote( $match_data[0], '`' ) . '`', $content, $discard, 0, $offset ) === 1 ) { + unset( $matches[1][ $key ] ); + } elseif ( preg_match( '`[a-z]+=(["\'])' . preg_quote( $match_data[0], '`' ) . '\1`', $content, $discard, 0, $offset ) === 1 ) { + // Prevent matches on html attributes like: `value="wordpress"`. + unset( $matches[1][ $key ] ); + } elseif ( preg_match( '`\\\\\'' . preg_quote( $match_data[0], '`' ) . '\\\\\'`', $content, $discard, 0, $offset ) === 1 ) { + // Prevent matches on xpath queries and such: `\'wordpress\'`. + unset( $matches[1][ $key ] ); + } elseif ( preg_match( '`(?:\?|&|&)[a-z0-9_]+=' . preg_quote( $match_data[0], '`' ) . '(?:&|$)`', $content, $discard, 0, $offset ) === 1 ) { + // Prevent matches on url query strings: `?something=wordpress`. + unset( $matches[1][ $key ] ); + } + + $offset = $next_offset; + } + + if ( empty( $matches[1] ) ) { + return; + } + } + + $mispelled = $this->retrieve_misspellings( $matches[1] ); + + if ( empty( $mispelled ) ) { + return; + } + + $fix = $this->phpcsFile->addFixableWarning( + 'Please spell "WordPress" correctly. Found %s misspelling(s): %s', + $stackPtr, + 'Misspelled', + array( + \count( $mispelled ), + implode( ', ', $mispelled ), + ) + ); + + if ( true === $fix ) { + // Apply fixes based on offset to ensure we don't replace false positives. + $replacement = $content; + foreach ( $matches[1] as $match ) { + $replacement = substr_replace( $replacement, 'WordPress', $match[1], \strlen( $match[0] ) ); + } + + $this->phpcsFile->fixer->replaceToken( $stackPtr, $replacement ); + } + } + } + + /** + * Retrieve a list of misspellings based on an array of matched variations on the target word. + * + * @param array $match_stack Array of matched variations of the target word. + * @return array Array containing only the misspelled variants. + */ + protected function retrieve_misspellings( $match_stack ) { + $mispelled = array(); + foreach ( $match_stack as $match ) { + // Deal with multi-dimensional arrays when capturing offset. + if ( \is_array( $match ) ) { + $match = $match[0]; + } + + if ( 'WordPress' !== $match ) { + $mispelled[] = $match; + } + } + + return $mispelled; + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/CronIntervalSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/CronIntervalSniff.php new file mode 100644 index 00000000..7cfdddd9 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/CronIntervalSniff.php @@ -0,0 +1,235 @@ + 60, + 'HOUR_IN_SECONDS' => 3600, + 'DAY_IN_SECONDS' => 86400, + 'WEEK_IN_SECONDS' => 604800, + 'MONTH_IN_SECONDS' => 2592000, + 'YEAR_IN_SECONDS' => 31536000, + ); + + /** + * Function within which the hook should be found. + * + * @var array + */ + protected $valid_functions = array( + 'add_filter' => true, + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + return array( + \T_CONSTANT_ENCAPSED_STRING, + \T_DOUBLE_QUOTED_STRING, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + public function process_token( $stackPtr ) { + $token = $this->tokens[ $stackPtr ]; + + if ( 'cron_schedules' !== $this->strip_quotes( $token['content'] ) ) { + return; + } + + // If within add_filter. + $functionPtr = $this->is_in_function_call( $stackPtr, $this->valid_functions ); + if ( false === $functionPtr ) { + return; + } + + $callback = $this->get_function_call_parameter( $functionPtr, 2 ); + if ( false === $callback ) { + return; + } + + if ( $stackPtr >= $callback['start'] ) { + // "cron_schedules" found in the second parameter, not the first. + return; + } + + // Detect callback function name. + $callbackArrayPtr = $this->phpcsFile->findNext( Tokens::$emptyTokens, $callback['start'], ( $callback['end'] + 1 ), true ); + + // If callback is array, get second element. + if ( false !== $callbackArrayPtr + && ( \T_ARRAY === $this->tokens[ $callbackArrayPtr ]['code'] + || \T_OPEN_SHORT_ARRAY === $this->tokens[ $callbackArrayPtr ]['code'] ) + ) { + $callback = $this->get_function_call_parameter( $callbackArrayPtr, 2 ); + + if ( false === $callback ) { + $this->confused( $stackPtr ); + return; + } + } + + unset( $functionPtr ); + + // Search for the function in tokens. + $callbackFunctionPtr = $this->phpcsFile->findNext( array( \T_CONSTANT_ENCAPSED_STRING, \T_DOUBLE_QUOTED_STRING, \T_CLOSURE ), $callback['start'], ( $callback['end'] + 1 ) ); + + if ( false === $callbackFunctionPtr ) { + $this->confused( $stackPtr ); + return; + } + + if ( \T_CLOSURE === $this->tokens[ $callbackFunctionPtr ]['code'] ) { + $functionPtr = $callbackFunctionPtr; + } else { + $functionName = $this->strip_quotes( $this->tokens[ $callbackFunctionPtr ]['content'] ); + + for ( $ptr = 0; $ptr < $this->phpcsFile->numTokens; $ptr++ ) { + if ( \T_FUNCTION === $this->tokens[ $ptr ]['code'] ) { + $foundName = $this->phpcsFile->getDeclarationName( $ptr ); + if ( $foundName === $functionName ) { + $functionPtr = $ptr; + break; + } elseif ( isset( $this->tokens[ $ptr ]['scope_closer'] ) ) { + // Skip to the end of the function definition. + $ptr = $this->tokens[ $ptr ]['scope_closer']; + } + } + } + } + + if ( ! isset( $functionPtr ) ) { + $this->confused( $stackPtr ); + return; + } + + if ( ! isset( $this->tokens[ $functionPtr ]['scope_opener'], $this->tokens[ $functionPtr ]['scope_closer'] ) ) { + return; + } + + $opening = $this->tokens[ $functionPtr ]['scope_opener']; + $closing = $this->tokens[ $functionPtr ]['scope_closer']; + for ( $i = $opening; $i <= $closing; $i++ ) { + + if ( \in_array( $this->tokens[ $i ]['code'], array( \T_CONSTANT_ENCAPSED_STRING, \T_DOUBLE_QUOTED_STRING ), true ) ) { + if ( 'interval' === $this->strip_quotes( $this->tokens[ $i ]['content'] ) ) { + $operator = $this->phpcsFile->findNext( \T_DOUBLE_ARROW, $i, null, false, null, true ); + if ( false === $operator ) { + $this->confused( $stackPtr ); + return; + } + + $valueStart = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $operator + 1 ), null, true, null, true ); + $valueEnd = $this->phpcsFile->findNext( array( \T_COMMA, \T_CLOSE_PARENTHESIS ), ( $valueStart + 1 ) ); + $value = ''; + for ( $j = $valueStart; $j < $valueEnd; $j++ ) { + if ( isset( Tokens::$emptyTokens[ $this->tokens[ $j ]['code'] ] ) ) { + continue; + } + $value .= $this->tokens[ $j ]['content']; + } + + if ( is_numeric( $value ) ) { + $interval = $value; + break; + } + + // Deal correctly with WP time constants. + $value = str_replace( array_keys( $this->wp_time_constants ), array_values( $this->wp_time_constants ), $value ); + + // If all digits and operators, eval! + if ( preg_match( '#^[\s\d+*/-]+$#', $value ) > 0 ) { + $interval = eval( "return ( $value );" ); // phpcs:ignore Squiz.PHP.Eval -- No harm here. + break; + } + + $this->confused( $stackPtr ); + return; + } + } + } + + $this->min_interval = (int) $this->min_interval; + + if ( isset( $interval ) && $interval < $this->min_interval ) { + $minutes = round( ( $this->min_interval / 60 ), 1 ); + $this->phpcsFile->addWarning( + 'Scheduling crons at %s sec ( less than %s minutes ) is discouraged.', + $stackPtr, + 'CronSchedulesInterval', + array( + $interval, + $minutes, + ) + ); + return; + } + } + + /** + * Add warning about unclear cron schedule change. + * + * @param int $stackPtr The position of the current token in the stack. + */ + public function confused( $stackPtr ) { + $this->phpcsFile->addWarning( + 'Detected changing of cron_schedules, but could not detect the interval value.', + $stackPtr, + 'ChangeDetected' + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedClassesSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedClassesSniff.php new file mode 100644 index 00000000..9d1f95bf --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedClassesSniff.php @@ -0,0 +1,120 @@ + value + * in a custom ruleset. + * + * @uses \WordPressCS\WordPress\Sniff::$minimum_supported_version + */ +class DeprecatedClassesSniff extends AbstractClassRestrictionsSniff { + + /** + * List of deprecated classes with alternative when available. + * + * To be updated after every major release. + * + * Version numbers should be fully qualified. + * + * @var array + */ + private $deprecated_classes = array( + + // WP 3.1.0. + 'WP_User_Search' => array( + 'alt' => 'WP_User_Query', + 'version' => '3.1.0', + ), + + // WP 4.9.0. + 'Customize_New_Menu_Section' => array( + 'version' => '4.9.0', + ), + 'WP_Customize_New_Menu_Control' => array( + 'version' => '4.9.0', + ), + + // WP 5.3.0. + 'Services_JSON' => array( + 'alt' => 'The PHP native JSON extension', + 'version' => '5.3.0', + ), + ); + + + /** + * Groups of classes to restrict. + * + * @return array + */ + public function getGroups() { + // Make sure all array keys are lowercase. + $this->deprecated_classes = array_change_key_case( $this->deprecated_classes, CASE_LOWER ); + + return array( + 'deprecated_classes' => array( + 'classes' => array_keys( $this->deprecated_classes ), + ), + ); + } + + /** + * Process a matched token. + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. Will + * always be 'deprecated_classes'. + * @param string $matched_content The token content (class name) which was matched. + * + * @return void + */ + public function process_matched_token( $stackPtr, $group_name, $matched_content ) { + + $this->get_wp_version_from_cl(); + + $class_name = ltrim( strtolower( $matched_content ), '\\' ); + + $message = 'The %s class has been deprecated since WordPress version %s.'; + $data = array( + ltrim( $matched_content, '\\' ), + $this->deprecated_classes[ $class_name ]['version'], + ); + + if ( ! empty( $this->deprecated_classes[ $class_name ]['alt'] ) ) { + $message .= ' Use %s instead.'; + $data[] = $this->deprecated_classes[ $class_name ]['alt']; + } + + $this->addMessage( + $message, + $stackPtr, + ( version_compare( $this->deprecated_classes[ $class_name ]['version'], $this->minimum_supported_version, '<' ) ), + $this->string_to_errorcode( $class_name . 'Found' ), + $data + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php new file mode 100644 index 00000000..df7a4b55 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php @@ -0,0 +1,1422 @@ + value + * in a custom ruleset. + * + * @uses \WordPressCS\WordPress\Sniff::$minimum_supported_version + */ +class DeprecatedFunctionsSniff extends AbstractFunctionRestrictionsSniff { + + /** + * List of deprecated functions with alternative when available. + * + * To be updated after every major release. + * Last updated for WordPress 4.8. + * + * Version numbers should be fully qualified. + * Replacement functions should have parentheses. + * + * To retrieve a function list for comparison, the following tool is available: + * https://github.com/JDGrimes/wp-deprecated-code-scanner + * + * @var array + */ + private $deprecated_functions = array( + + // WP 0.71. + 'the_category_head' => array( + 'alt' => 'get_the_category_by_ID()', + 'version' => '0.71', + ), + 'the_category_ID' => array( + 'alt' => 'get_the_category()', + 'version' => '0.71', + ), + + // WP 1.2.0. + 'permalink_link' => array( + 'alt' => 'the_permalink()', + 'version' => '1.2.0', + ), + + // WP 1.5.0. + 'start_wp' => array( + // Verified correct alternative. + 'alt' => 'the Loop', + 'version' => '1.5.0', + ), + + // WP 1.5.1. + 'get_postdata' => array( + 'alt' => 'get_post()', + 'version' => '1.5.1', + ), + + // WP 2.0.0. + 'create_user' => array( + 'alt' => 'wp_create_user()', + 'version' => '2.0.0', + ), + 'next_post' => array( + 'alt' => 'next_post_link()', + 'version' => '2.0.0', + ), + 'previous_post' => array( + 'alt' => 'previous_post_link()', + 'version' => '2.0.0', + ), + 'user_can_create_draft' => array( + 'alt' => 'current_user_can()', + 'version' => '2.0.0', + ), + 'user_can_create_post' => array( + 'alt' => 'current_user_can()', + 'version' => '2.0.0', + ), + 'user_can_delete_post' => array( + 'alt' => 'current_user_can()', + 'version' => '2.0.0', + ), + 'user_can_delete_post_comments' => array( + 'alt' => 'current_user_can()', + 'version' => '2.0.0', + ), + 'user_can_edit_post' => array( + 'alt' => 'current_user_can()', + 'version' => '2.0.0', + ), + 'user_can_edit_post_comments' => array( + 'alt' => 'current_user_can()', + 'version' => '2.0.0', + ), + 'user_can_edit_post_date' => array( + 'alt' => 'current_user_can()', + 'version' => '2.0.0', + ), + 'user_can_edit_user' => array( + 'alt' => 'current_user_can()', + 'version' => '2.0.0', + ), + 'user_can_set_post_date' => array( + 'alt' => 'current_user_can()', + 'version' => '2.0.0', + ), + + // WP 2.1.0. + 'dropdown_cats' => array( + 'alt' => 'wp_dropdown_categories()', + 'version' => '2.1.0', + ), + 'get_archives' => array( + 'alt' => 'wp_get_archives()', + 'version' => '2.1.0', + ), + 'get_author_link' => array( + 'alt' => 'get_author_posts_url()', + 'version' => '2.1.0', + ), + 'get_autotoggle' => array( + 'alt' => '', + 'version' => '2.1.0', + ), + 'get_link' => array( + 'alt' => 'get_bookmark()', + 'version' => '2.1.0', + ), + 'get_linkcatname' => array( + 'alt' => 'get_category()', + 'version' => '2.1.0', + ), + 'get_linkobjects' => array( + 'alt' => 'get_bookmarks()', + 'version' => '2.1.0', + ), + 'get_linkobjectsbyname' => array( + 'alt' => 'get_bookmarks()', + 'version' => '2.1.0', + ), + 'get_linkrating' => array( + 'alt' => 'sanitize_bookmark_field()', + 'version' => '2.1.0', + ), + 'get_links' => array( + 'alt' => 'get_bookmarks()', + 'version' => '2.1.0', + ), + 'get_links_list' => array( + 'alt' => 'wp_list_bookmarks()', + 'version' => '2.1.0', + ), + 'get_links_withrating' => array( + 'alt' => 'get_bookmarks()', + 'version' => '2.1.0', + ), + 'get_linksbyname' => array( + 'alt' => 'get_bookmarks()', + 'version' => '2.1.0', + ), + 'get_linksbyname_withrating' => array( + 'alt' => 'get_bookmarks()', + 'version' => '2.1.0', + ), + 'get_settings' => array( + 'alt' => 'get_option()', + 'version' => '2.1.0', + ), + 'link_pages' => array( + 'alt' => 'wp_link_pages()', + 'version' => '2.1.0', + ), + 'links_popup_script' => array( + 'alt' => '', + 'version' => '2.1.0', + ), + 'list_authors' => array( + 'alt' => 'wp_list_authors()', + 'version' => '2.1.0', + ), + 'list_cats' => array( + 'alt' => 'wp_list_categories()', + 'version' => '2.1.0', + ), + 'tinymce_include' => array( + 'alt' => 'wp_editor()', + 'version' => '2.1.0', + ), + 'wp_get_links' => array( + 'alt' => 'wp_list_bookmarks()', + 'version' => '2.1.0', + ), + 'wp_get_linksbyname' => array( + 'alt' => 'wp_list_bookmarks()', + 'version' => '2.1.0', + ), + 'wp_get_post_cats' => array( + 'alt' => 'wp_get_post_categories()', + 'version' => '2.1.0', + ), + 'wp_list_cats' => array( + 'alt' => 'wp_list_categories()', + 'version' => '2.1.0', + ), + 'wp_set_post_cats' => array( + 'alt' => 'wp_set_post_categories()', + 'version' => '2.1.0', + ), + + // WP 2.2.0. + 'comments_rss' => array( + 'alt' => 'get_post_comments_feed_link()', + 'version' => '2.2.0', + ), + + // WP 2.3.0. + 'permalink_single_rss' => array( + 'alt' => 'the_permalink_rss()', + 'version' => '2.3.0', + ), + + // WP 2.5.0. + 'comments_rss_link' => array( + 'alt' => 'post_comments_feed_link()', + 'version' => '2.5.0', + ), + 'documentation_link' => array( + 'alt' => '', + 'version' => '2.5.0', + ), + 'get_attachment_icon' => array( + 'alt' => 'wp_get_attachment_image()', + 'version' => '2.5.0', + ), + 'get_attachment_icon_src' => array( + 'alt' => 'wp_get_attachment_image_src()', + 'version' => '2.5.0', + ), + 'get_attachment_innerHTML' => array( + 'alt' => 'wp_get_attachment_image()', + 'version' => '2.5.0', + ), + 'get_author_rss_link' => array( + 'alt' => 'get_author_feed_link()', + 'version' => '2.5.0', + ), + 'get_category_rss_link' => array( + 'alt' => 'get_category_feed_link()', + 'version' => '2.5.0', + ), + 'get_the_attachment_link' => array( + 'alt' => 'wp_get_attachment_link()', + 'version' => '2.5.0', + ), + 'gzip_compression' => array( + 'alt' => '', + 'version' => '2.5.0', + ), + 'wp_clearcookie' => array( + 'alt' => 'wp_clear_auth_cookie()', + 'version' => '2.5.0', + ), + 'wp_get_cookie_login' => array( + 'alt' => '', + 'version' => '2.5.0', + ), + 'wp_login' => array( + 'alt' => 'wp_signon()', + 'version' => '2.5.0', + ), + 'wp_setcookie' => array( + 'alt' => 'wp_set_auth_cookie()', + 'version' => '2.5.0', + ), + + // WP 2.6.0. + 'dropdown_categories' => array( + 'alt' => 'wp_category_checklist()', + 'version' => '2.6.0', + ), + 'dropdown_link_categories' => array( + 'alt' => 'wp_link_category_checklist()', + 'version' => '2.6.0', + ), + + // WP 2.7.0. + 'get_commentdata' => array( + 'alt' => 'get_comment()', + 'version' => '2.7.0', + ), + // This is a method i.e. WP_Filesystem_Base::find_base_dir() See #731. + 'find_base_dir' => array( + 'alt' => 'WP_Filesystem::abspath()', + 'version' => '2.7.0', + ), + // This is a method i.e. WP_Filesystem_Base::get_base_dir() See #731. + 'get_base_dir' => array( + 'alt' => 'WP_Filesystem::abspath()', + 'version' => '2.7.0', + ), + + // WP 2.8.0. + '__ngettext' => array( + 'alt' => '_n()', + 'version' => '2.8.0', + ), + '__ngettext_noop' => array( + 'alt' => '_n_noop()', + 'version' => '2.8.0', + ), + 'attribute_escape' => array( + 'alt' => 'esc_attr()', + 'version' => '2.8.0', + ), + 'get_author_name' => array( + 'alt' => 'get_the_author_meta(\'display_name\')', + 'version' => '2.8.0', + ), + 'get_category_children' => array( + 'alt' => 'get_term_children()', + 'version' => '2.8.0', + ), + 'get_catname' => array( + 'alt' => 'get_cat_name()', + 'version' => '2.8.0', + ), + 'get_the_author_aim' => array( + 'alt' => 'get_the_author_meta(\'aim\')', + 'version' => '2.8.0', + ), + 'get_the_author_description' => array( + 'alt' => 'get_the_author_meta(\'description\')', + 'version' => '2.8.0', + ), + 'get_the_author_email' => array( + 'alt' => 'get_the_author_meta(\'email\')', + 'version' => '2.8.0', + ), + 'get_the_author_firstname' => array( + 'alt' => 'get_the_author_meta(\'first_name\')', + 'version' => '2.8.0', + ), + 'get_the_author_icq' => array( + 'alt' => 'get_the_author_meta(\'icq\')', + 'version' => '2.8.0', + ), + 'get_the_author_ID' => array( + 'alt' => 'get_the_author_meta(\'ID\')', + 'version' => '2.8.0', + ), + 'get_the_author_lastname' => array( + 'alt' => 'get_the_author_meta(\'last_name\')', + 'version' => '2.8.0', + ), + 'get_the_author_login' => array( + 'alt' => 'get_the_author_meta(\'login\')', + 'version' => '2.8.0', + ), + 'get_the_author_msn' => array( + 'alt' => 'get_the_author_meta(\'msn\')', + 'version' => '2.8.0', + ), + 'get_the_author_nickname' => array( + 'alt' => 'get_the_author_meta(\'nickname\')', + 'version' => '2.8.0', + ), + 'get_the_author_url' => array( + 'alt' => 'get_the_author_meta(\'url\')', + 'version' => '2.8.0', + ), + 'get_the_author_yim' => array( + 'alt' => 'get_the_author_meta(\'yim\')', + 'version' => '2.8.0', + ), + 'js_escape' => array( + 'alt' => 'esc_js()', + 'version' => '2.8.0', + ), + 'register_sidebar_widget' => array( + 'alt' => 'wp_register_sidebar_widget()', + 'version' => '2.8.0', + ), + 'register_widget_control' => array( + 'alt' => 'wp_register_widget_control()', + 'version' => '2.8.0', + ), + 'sanitize_url' => array( + 'alt' => 'esc_url_raw()', + 'version' => '2.8.0', + ), + 'the_author_aim' => array( + 'alt' => 'the_author_meta(\'aim\')', + 'version' => '2.8.0', + ), + 'the_author_description' => array( + 'alt' => 'the_author_meta(\'description\')', + 'version' => '2.8.0', + ), + 'the_author_email' => array( + 'alt' => 'the_author_meta(\'email\')', + 'version' => '2.8.0', + ), + 'the_author_firstname' => array( + 'alt' => 'the_author_meta(\'first_name\')', + 'version' => '2.8.0', + ), + 'the_author_icq' => array( + 'alt' => 'the_author_meta(\'icq\')', + 'version' => '2.8.0', + ), + 'the_author_ID' => array( + 'alt' => 'the_author_meta(\'ID\')', + 'version' => '2.8.0', + ), + 'the_author_lastname' => array( + 'alt' => 'the_author_meta(\'last_name\')', + 'version' => '2.8.0', + ), + 'the_author_login' => array( + 'alt' => 'the_author_meta(\'login\')', + 'version' => '2.8.0', + ), + 'the_author_msn' => array( + 'alt' => 'the_author_meta(\'msn\')', + 'version' => '2.8.0', + ), + 'the_author_nickname' => array( + 'alt' => 'the_author_meta(\'nickname\')', + 'version' => '2.8.0', + ), + 'the_author_url' => array( + 'alt' => 'the_author_meta(\'url\')', + 'version' => '2.8.0', + ), + 'the_author_yim' => array( + 'alt' => 'the_author_meta(\'yim\')', + 'version' => '2.8.0', + ), + 'unregister_sidebar_widget' => array( + 'alt' => 'wp_unregister_sidebar_widget()', + 'version' => '2.8.0', + ), + 'unregister_widget_control' => array( + 'alt' => 'wp_unregister_widget_control()', + 'version' => '2.8.0', + ), + 'wp_specialchars' => array( + 'alt' => 'esc_html()', + 'version' => '2.8.0', + ), + + // WP 2.9.0. + '_c' => array( + 'alt' => '_x()', + 'version' => '2.9.0', + ), + '_nc' => array( + 'alt' => '_nx()', + 'version' => '2.9.0', + ), + 'get_real_file_to_edit' => array( + 'alt' => '', + 'version' => '2.9.0', + ), + 'make_url_footnote' => array( + 'alt' => '', + 'version' => '2.9.0', + ), + 'the_content_rss' => array( + 'alt' => 'the_content_feed()', + 'version' => '2.9.0', + ), + 'translate_with_context' => array( + 'alt' => '_x()', + 'version' => '2.9.0', + ), + + // WP 3.0.0. + 'activate_sitewide_plugin' => array( + 'alt' => 'activate_plugin()', + 'version' => '3.0.0', + ), + 'add_option_update_handler' => array( + 'alt' => 'register_setting()', + 'version' => '3.0.0', + ), + 'automatic_feed_links' => array( + 'alt' => 'add_theme_support( \'automatic-feed-links\' )', + 'version' => '3.0.0', + ), + 'clean_url' => array( + 'alt' => 'esc_url()', + 'version' => '3.0.0', + ), + 'clear_global_post_cache' => array( + 'alt' => 'clean_post_cache()', + 'version' => '3.0.0', + ), + 'codepress_footer_js' => array( + 'alt' => '', + 'version' => '3.0.0', + ), + 'codepress_get_lang' => array( + 'alt' => '', + 'version' => '3.0.0', + ), + 'deactivate_sitewide_plugin' => array( + 'alt' => 'deactivate_plugin()', + 'version' => '3.0.0', + ), + 'delete_usermeta' => array( + 'alt' => 'delete_user_meta()', + 'version' => '3.0.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + 'funky_javascript_callback' => array( + 'alt' => '', + 'version' => '3.0.0', + ), + 'funky_javascript_fix' => array( + 'alt' => '', + 'version' => '3.0.0', + ), + 'generate_random_password' => array( + 'alt' => 'wp_generate_password()', + 'version' => '3.0.0', + ), + 'get_alloptions' => array( + 'alt' => 'wp_load_alloptions()', + 'version' => '3.0.0', + ), + 'get_blog_list' => array( + 'alt' => 'wp_get_sites()', + 'version' => '3.0.0', + ), + 'get_most_active_blogs' => array( + 'alt' => '', + 'version' => '3.0.0', + ), + 'get_profile' => array( + 'alt' => 'get_the_author_meta()', + 'version' => '3.0.0', + ), + 'get_user_details' => array( + 'alt' => 'get_user_by()', + 'version' => '3.0.0', + ), + 'get_usermeta' => array( + 'alt' => 'get_user_meta()', + 'version' => '3.0.0', + ), + 'get_usernumposts' => array( + 'alt' => 'count_user_posts()', + 'version' => '3.0.0', + ), + 'graceful_fail' => array( + 'alt' => 'wp_die()', + 'version' => '3.0.0', + ), + // Verified version & alternative. + 'install_blog_defaults' => array( + 'alt' => 'wp_install_defaults', + 'version' => '3.0.0', + ), + 'is_main_blog' => array( + 'alt' => 'is_main_site()', + 'version' => '3.0.0', + ), + 'is_site_admin' => array( + 'alt' => 'is_super_admin()', + 'version' => '3.0.0', + ), + 'is_taxonomy' => array( + 'alt' => 'taxonomy_exists()', + 'version' => '3.0.0', + ), + 'is_term' => array( + 'alt' => 'term_exists()', + 'version' => '3.0.0', + ), + 'is_wpmu_sitewide_plugin' => array( + 'alt' => 'is_network_only_plugin()', + 'version' => '3.0.0', + ), + 'mu_options' => array( + 'alt' => '', + 'version' => '3.0.0', + ), + 'remove_option_update_handler' => array( + 'alt' => 'unregister_setting()', + 'version' => '3.0.0', + ), + 'set_current_user' => array( + 'alt' => 'wp_set_current_user()', + 'version' => '3.0.0', + ), + 'update_usermeta' => array( + 'alt' => 'update_user_meta()', + 'version' => '3.0.0', + ), + 'use_codepress' => array( + 'alt' => '', + 'version' => '3.0.0', + ), + 'validate_email' => array( + 'alt' => 'is_email()', + 'version' => '3.0.0', + ), + 'wp_dropdown_cats' => array( + 'alt' => 'wp_dropdown_categories()', + 'version' => '3.0.0', + ), + 'wp_shrink_dimensions' => array( + 'alt' => 'wp_constrain_dimensions()', + 'version' => '3.0.0', + ), + 'wpmu_checkAvailableSpace' => array( + 'alt' => 'is_upload_space_available()', + 'version' => '3.0.0', + ), + 'wpmu_menu' => array( + 'alt' => '', + 'version' => '3.0.0', + ), + + // WP 3.1.0. + 'get_author_user_ids' => array( + 'alt' => 'get_users()', + 'version' => '3.1.0', + ), + 'get_dashboard_blog' => array( + 'alt' => 'get_site()', + 'version' => '3.1.0', + ), + 'get_editable_authors' => array( + 'alt' => 'get_users()', + 'version' => '3.1.0', + ), + 'get_editable_user_ids' => array( + 'alt' => 'get_users()', + 'version' => '3.1.0', + ), + 'get_nonauthor_user_ids' => array( + 'alt' => 'get_users()', + 'version' => '3.1.0', + ), + 'get_others_drafts' => array( + 'alt' => '', + 'version' => '3.1.0', + ), + 'get_others_pending' => array( + 'alt' => '', + 'version' => '3.1.0', + ), + 'get_others_unpublished_posts' => array( + 'alt' => '', + 'version' => '3.1.0', + ), + 'get_users_of_blog' => array( + 'alt' => 'get_users()', + 'version' => '3.1.0', + ), + 'install_themes_feature_list' => array( + 'alt' => 'get_theme_feature_list()', + 'version' => '3.1.0', + ), + 'is_plugin_page' => array( + // Verified correct alternative. + 'alt' => 'global $plugin_page and/or get_plugin_page_hookname() hooks', + 'version' => '3.1.0', + ), + 'update_category_cache' => array( + 'alt' => '', + 'version' => '3.1.0', + ), + + // WP 3.2.0. + 'favorite_actions' => array( + 'alt' => 'WP_Admin_Bar', + 'version' => '3.2.0', + ), + 'wp_dashboard_quick_press_output' => array( + 'alt' => 'wp_dashboard_quick_press()', + 'version' => '3.2.0', + ), + 'wp_timezone_supported' => array( + 'alt' => '', + 'version' => '3.2.0', + ), + + // WP 3.3.0. + 'add_contextual_help' => array( + 'alt' => 'get_current_screen()->add_help_tab()', + 'version' => '3.3.0', + ), + 'get_boundary_post_rel_link' => array( + 'alt' => '', + 'version' => '3.3.0', + ), + 'get_index_rel_link' => array( + 'alt' => '', + 'version' => '3.3.0', + ), + 'get_parent_post_rel_link' => array( + 'alt' => '', + 'version' => '3.3.0', + ), + 'get_user_by_email' => array( + 'alt' => 'get_user_by(\'email\')', + 'version' => '3.3.0', + ), + 'get_user_metavalues' => array( + 'alt' => '', + 'version' => '3.3.0', + ), + 'get_userdatabylogin' => array( + 'alt' => 'get_user_by(\'login\')', + 'version' => '3.3.0', + ), + 'index_rel_link' => array( + 'alt' => '', + 'version' => '3.3.0', + ), + 'is_blog_user' => array( + 'alt' => 'is_user_member_of_blog()', + 'version' => '3.3.0', + ), + 'media_upload_audio' => array( + 'alt' => 'wp_media_upload_handler()', + 'version' => '3.3.0', + ), + 'media_upload_file' => array( + 'alt' => 'wp_media_upload_handler()', + 'version' => '3.3.0', + ), + 'media_upload_image' => array( + 'alt' => 'wp_media_upload_handler()', + 'version' => '3.3.0', + ), + 'media_upload_video' => array( + 'alt' => 'wp_media_upload_handler()', + 'version' => '3.3.0', + ), + 'parent_post_rel_link' => array( + 'alt' => '', + 'version' => '3.3.0', + ), + 'sanitize_user_object' => array( + 'alt' => '', + 'version' => '3.3.0', + ), + 'screen_layout' => array( + 'alt' => '$current_screen->render_screen_layout()', + 'version' => '3.3.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + 'screen_meta' => array( + 'alt' => '$current_screen->render_screen_meta()', + 'version' => '3.3.0', + ), + 'screen_options' => array( + 'alt' => '$current_screen->render_per_page_options()', + 'version' => '3.3.0', + ), + 'start_post_rel_link' => array( + 'alt' => '', + 'version' => '3.3.0', + ), + 'the_editor' => array( + 'alt' => 'wp_editor()', + 'version' => '3.3.0', + ), + 'type_url_form_audio' => array( + 'alt' => 'wp_media_insert_url_form(\'audio\')', + 'version' => '3.3.0', + ), + 'type_url_form_file' => array( + 'alt' => 'wp_media_insert_url_form(\'file\')', + 'version' => '3.3.0', + ), + 'type_url_form_image' => array( + 'alt' => 'wp_media_insert_url_form(\'image\')', + 'version' => '3.3.0', + ), + 'type_url_form_video' => array( + 'alt' => 'wp_media_insert_url_form(\'video\')', + 'version' => '3.3.0', + ), + 'wp_admin_bar_dashboard_view_site_menu' => array( + 'alt' => '', + 'version' => '3.3.0', + ), + 'wp_preload_dialogs' => array( + 'alt' => 'wp_editor()', + 'version' => '3.3.0', + ), + 'wp_print_editor_js' => array( + 'alt' => 'wp_editor()', + 'version' => '3.3.0', + ), + 'wp_quicktags' => array( + 'alt' => 'wp_editor()', + 'version' => '3.3.0', + ), + 'wp_tiny_mce' => array( + 'alt' => 'wp_editor()', + 'version' => '3.3.0', + ), + 'wpmu_admin_do_redirect' => array( + 'alt' => 'wp_redirect()', + 'version' => '3.3.0', + ), + 'wpmu_admin_redirect_add_updated_param' => array( + 'alt' => 'add_query_arg()', + 'version' => '3.3.0', + ), + + // WP 3.4.0. + 'add_custom_background' => array( + 'alt' => 'add_theme_support( \'custom-background\', $args )', + 'version' => '3.4.0', + ), + 'add_custom_image_header' => array( + 'alt' => 'add_theme_support( \'custom-header\', $args )', + 'version' => '3.4.0', + ), + 'clean_page_cache' => array( + 'alt' => 'clean_post_cache()', + 'version' => '3.4.0', + ), + 'clean_pre' => array( + 'alt' => '', + 'version' => '3.4.0', + ), + 'current_theme_info' => array( + 'alt' => 'wp_get_theme()', + 'version' => '3.4.0', + ), + 'debug_fclose' => array( + 'alt' => 'error_log()', + 'version' => '3.4.0', + ), + 'debug_fopen' => array( + 'alt' => 'error_log()', + 'version' => '3.4.0', + ), + 'debug_fwrite' => array( + 'alt' => 'error_log()', + 'version' => '3.4.0', + ), + 'display_theme' => array( + 'alt' => '', + 'version' => '3.4.0', + ), + 'get_allowed_themes' => array( + 'alt' => 'wp_get_themes( array( \'allowed\' => true ) )', + 'version' => '3.4.0', + ), + 'get_broken_themes' => array( + 'alt' => 'wp_get_themes( array( \'errors\' => true )', + 'version' => '3.4.0', + ), + 'get_current_theme' => array( + 'alt' => 'wp_get_theme()', + 'version' => '3.4.0', + ), + 'get_site_allowed_themes' => array( + 'alt' => 'WP_Theme::get_allowed_on_network()', + 'version' => '3.4.0', + ), + 'get_theme' => array( + 'alt' => 'wp_get_theme( $stylesheet )', + 'version' => '3.4.0', + ), + 'get_theme_data' => array( + 'alt' => 'wp_get_theme()', + 'version' => '3.4.0', + ), + 'get_themes' => array( + 'alt' => 'wp_get_themes()', + 'version' => '3.4.0', + ), + 'logIO' => array( + 'alt' => 'error_log()', + 'version' => '3.4.0', + ), + 'remove_custom_background' => array( + 'alt' => 'remove_theme_support( \'custom-background\' )', + 'version' => '3.4.0', + ), + 'remove_custom_image_header' => array( + 'alt' => 'remove_theme_support( \'custom-header\' )', + 'version' => '3.4.0', + ), + 'update_page_cache' => array( + 'alt' => 'update_post_cache()', + 'version' => '3.4.0', + ), + 'wpmu_get_blog_allowedthemes' => array( + 'alt' => 'WP_Theme::get_allowed_on_site()', + 'version' => '3.4.0', + ), + + // WP 3.4.1. + 'wp_explain_nonce' => array( + 'alt' => 'wp_nonce_ays()', + 'version' => '3.4.1', + ), + + // WP 3.5.0. + '_flip_image_resource' => array( + 'alt' => 'WP_Image_Editor::flip()', + 'version' => '3.5.0', + ), + '_get_post_ancestors' => array( + 'alt' => '', + 'version' => '3.5.0', + ), + '_insert_into_post_button' => array( + 'alt' => '', + 'version' => '3.5.0', + ), + '_media_button' => array( + 'alt' => '', + 'version' => '3.5.0', + ), + '_rotate_image_resource' => array( + 'alt' => 'WP_Image_Editor::rotate()', + 'version' => '3.5.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + '_save_post_hook' => array( + 'alt' => '', + 'version' => '3.5.0', + ), + 'gd_edit_image_support' => array( + 'alt' => 'wp_image_editor_supports()', + 'version' => '3.5.0', + ), + 'get_default_page_to_edit' => array( + 'alt' => 'get_default_post_to_edit( \'page\' )', + 'version' => '3.5.0', + ), + 'get_post_to_edit' => array( + 'alt' => 'get_post()', + 'version' => '3.5.0', + ), + 'get_udims' => array( + 'alt' => 'wp_constrain_dimensions()', + 'version' => '3.5.0', + ), + 'image_resize' => array( + 'alt' => 'wp_get_image_editor()', + 'version' => '3.5.0', + ), + 'sticky_class' => array( + 'alt' => 'post_class()', + 'version' => '3.5.0', + ), + 'user_pass_ok' => array( + 'alt' => 'wp_authenticate()', + 'version' => '3.5.0', + ), + 'wp_cache_reset' => array( + 'alt' => 'WP_Object_Cache::reset()', + 'version' => '3.5.0', + ), + 'wp_create_thumbnail' => array( + 'alt' => 'image_resize()', + 'version' => '3.5.0', + ), + 'wp_get_single_post' => array( + 'alt' => 'get_post()', + 'version' => '3.5.0', + ), + 'wp_load_image' => array( + 'alt' => 'wp_get_image_editor()', + 'version' => '3.5.0', + ), + + // WP 3.6.0. + 'get_user_id_from_string' => array( + 'alt' => 'get_user_by()', + 'version' => '3.6.0', + ), + 'wp_convert_bytes_to_hr' => array( + 'alt' => 'size_format()', + 'version' => '3.6.0', + ), + 'wp_nav_menu_locations_meta_box' => array( + 'alt' => '', + 'version' => '3.6.0', + ), + + // WP 3.7.0. + '_search_terms_tidy' => array( + 'alt' => '', + 'version' => '3.7.0', + ), + 'get_blogaddress_by_domain' => array( + 'alt' => '', + 'version' => '3.7.0', + ), + 'the_attachment_links' => array( + 'alt' => '', + 'version' => '3.7.0', + ), + 'wp_update_core' => array( + 'alt' => 'new Core_Upgrader();', + 'version' => '3.7.0', + ), + 'wp_update_plugin' => array( + 'alt' => 'new Plugin_Upgrader();', + 'version' => '3.7.0', + ), + 'wp_update_theme' => array( + 'alt' => 'new Theme_Upgrader();', + 'version' => '3.7.0', + ), + + // WP 3.8.0. + 'get_screen_icon' => array( + 'alt' => '', + 'version' => '3.8.0', + ), + 'screen_icon' => array( + 'alt' => '', + 'version' => '3.8.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + 'wp_dashboard_incoming_links' => array( + 'alt' => '', + 'version' => '3.8.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + 'wp_dashboard_incoming_links_control' => array( + 'alt' => '', + 'version' => '3.8.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + 'wp_dashboard_incoming_links_output' => array( + 'alt' => '', + 'version' => '3.8.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + 'wp_dashboard_plugins' => array( + 'alt' => '', + 'version' => '3.8.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + 'wp_dashboard_primary_control' => array( + 'alt' => '', + 'version' => '3.8.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + 'wp_dashboard_recent_comments_control' => array( + 'alt' => '', + 'version' => '3.8.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + 'wp_dashboard_secondary' => array( + 'alt' => '', + 'version' => '3.8.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + 'wp_dashboard_secondary_control' => array( + 'alt' => '', + 'version' => '3.8.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + 'wp_dashboard_secondary_output' => array( + 'alt' => '', + 'version' => '3.8.0', + ), + + // WP 3.9.0. + '_relocate_children' => array( + 'alt' => '', + 'version' => '3.9.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + 'default_topic_count_text' => array( + 'alt' => '', + 'version' => '3.9.0', + ), + 'format_to_post' => array( + 'alt' => '', + 'version' => '3.9.0', + ), + 'get_current_site_name' => array( + 'alt' => 'get_current_site()', + 'version' => '3.9.0', + ), + 'rich_edit_exists' => array( + 'alt' => '', + 'version' => '3.9.0', + ), + 'wpmu_current_site' => array( + 'alt' => '', + 'version' => '3.9.0', + ), + + // WP 4.0.0. + 'get_all_category_ids' => array( + 'alt' => 'get_terms()', + 'version' => '4.0.0', + ), + 'like_escape' => array( + 'alt' => 'wpdb::esc_like()', + 'version' => '4.0.0', + ), + 'url_is_accessable_via_ssl' => array( + 'alt' => '', + 'version' => '4.0.0', + ), + + // WP 4.1.0. + // This is a method from the WP_Customize_Image_Control class. See #731. + 'add_tab' => array( + 'alt' => '', + 'version' => '4.1.0', + ), + // This is a method from the WP_Customize_Image_Control class. See #731. + 'prepare_control' => array( + 'alt' => '', + 'version' => '4.1.0', + ), + // This is a method from the WP_Customize_Image_Control class. See #731. + 'print_tab_image' => array( + 'alt' => '', + 'version' => '4.1.0', + ), + // This is a method from the WP_Customize_Image_Control class. See #731. + 'remove_tab' => array( + 'alt' => '', + 'version' => '4.1.0', + ), + + // WP 4.2.0. + // This is a method from the WP_Customize_Widgets class. See #731. + 'prepreview_added_sidebars_widgets' => array( + 'alt' => 'the \'customize_dynamic_setting_args\' filter', + 'version' => '4.2.0', + ), + // This is a method from the WP_Customize_Widgets class. See #731. + 'prepreview_added_widget_instance' => array( + 'alt' => 'the \'customize_dynamic_setting_args\' filter', + 'version' => '4.2.0', + ), + // This is a method from the WP_Customize_Widgets class. See #731. + 'remove_prepreview_filters' => array( + 'alt' => 'the \'customize_dynamic_setting_args\' filter', + 'version' => '4.2.0', + ), + // This is a method from the WP_Customize_Widgets class. See #731. + 'setup_widget_addition_previews' => array( + 'alt' => 'the \'customize_dynamic_setting_args\' filter', + 'version' => '4.2.0', + ), + + // WP 4.3.0. + '_preview_theme_stylesheet_filter' => array( + 'alt' => '', + 'version' => '4.3.0', + ), + '_preview_theme_template_filter' => array( + 'alt' => '', + 'version' => '4.3.0', + ), + 'preview_theme' => array( + 'alt' => '', + 'version' => '4.3.0', + ), + 'preview_theme_ob_filter' => array( + 'alt' => '', + 'version' => '4.3.0', + ), + 'preview_theme_ob_filter_callback' => array( + 'alt' => '', + 'version' => '4.3.0', + ), + // Verified; see https://core.trac.wordpress.org/ticket/41121, patch 3. + 'wp_ajax_wp_fullscreen_save_post' => array( + 'alt' => '', + 'version' => '4.3.0', + ), + 'wp_htmledit_pre' => array( + 'alt' => 'format_for_editor()', + 'version' => '4.3.0', + ), + 'wp_richedit_pre' => array( + 'alt' => 'format_for_editor()', + 'version' => '4.3.0', + ), + + // WP 4.4.0. + 'create_empty_blog' => array( + 'alt' => '', + 'version' => '4.4.0', + ), + 'force_ssl_login' => array( + 'alt' => 'force_ssl_admin()', + 'version' => '4.4.0', + ), + 'get_admin_users_for_domain' => array( + 'alt' => '', + 'version' => '4.4.0', + ), + 'post_permalink' => array( + 'alt' => 'get_permalink()', + 'version' => '4.4.0', + ), + 'wp_get_http' => array( + 'alt' => 'the WP_Http class', + 'version' => '4.4.0', + ), + // This is a method i.e. WP_Widget_Recent_Comments::flush_widget_cache() See #731. + 'flush_widget_cache' => array( + 'alt' => '', + 'version' => '4.4.0', + ), + + // WP 4.5.0. + 'add_object_page' => array( + 'alt' => 'add_menu_page()', + 'version' => '4.5.0', + ), + 'add_utility_page' => array( + 'alt' => 'add_menu_page()', + 'version' => '4.5.0', + ), + 'comments_popup_script' => array( + 'alt' => '', + 'version' => '4.5.0', + ), + 'get_comments_popup_template' => array( + 'alt' => '', + 'version' => '4.5.0', + ), + 'get_currentuserinfo' => array( + 'alt' => 'wp_get_current_user()', + 'version' => '4.5.0', + ), + 'is_comments_popup' => array( + 'alt' => '', + 'version' => '4.5.0', + ), + 'popuplinks' => array( + 'alt' => '', + 'version' => '4.5.0', + ), + + // WP 4.6.0. + 'post_form_autocomplete_off' => array( + 'alt' => '', + 'version' => '4.6.0', + ), + 'wp_embed_handler_googlevideo' => array( + 'alt' => '', + 'version' => '4.6.0', + ), + 'wp_get_sites' => array( + 'alt' => 'get_sites()', + 'version' => '4.6.0', + ), + + // WP 4.7.0. + '_sort_nav_menu_items' => array( + 'alt' => 'wp_list_sort()', + 'version' => '4.7.0', + ), + '_usort_terms_by_ID' => array( + 'alt' => 'wp_list_sort()', + 'version' => '4.7.0', + ), + '_usort_terms_by_name' => array( + 'alt' => 'wp_list_sort()', + 'version' => '4.7.0', + ), + 'get_paged_template' => array( + 'alt' => '', + 'version' => '4.7.0', + ), + 'wp_get_network' => array( + 'alt' => 'get_network()', + 'version' => '4.7.0', + ), + 'wp_kses_js_entities' => array( + 'alt' => '', + 'version' => '4.7.0', + ), + + // WP 4.8.0. + 'wp_dashboard_plugins_output' => array( + 'alt' => '', + 'version' => '4.8.0', + ), + + // WP 4.9.0. + 'get_shortcut_link' => array( + 'alt' => '', + 'version' => '4.9.0', + ), + 'is_user_option_local' => array( + 'alt' => '', + 'version' => '4.9.0', + ), + 'wp_ajax_press_this_add_category' => array( + 'alt' => '', + 'version' => '4.9.0', + ), + 'wp_ajax_press_this_save_post' => array( + 'alt' => '', + 'version' => '4.9.0', + ), + + // WP 5.1.0. + 'insert_blog' => array( + 'alt' => 'wp_insert_site()', + 'version' => '5.1.0', + ), + 'install_blog' => array( + 'alt' => '', + 'version' => '5.1.0', + ), + + // WP 5.3.0. + '_wp_json_prepare_data' => array( + 'alt' => '', + 'version' => '5.3.0', + ), + '_wp_privacy_requests_screen_options' => array( + 'alt' => '', + 'version' => '5.3.0', + ), + 'update_user_status' => array( + 'alt' => 'wp_update_user()', + 'version' => '5.3.0', + ), + + // WP 5.4.0. + 'wp_get_user_request_data' => array( + 'alt' => 'wp_get_user_request()', + 'version' => '5.4.0', + ), + ); + + /** + * Groups of functions to restrict. + * + * @return array + */ + public function getGroups() { + // Make sure all array keys are lowercase. + $this->deprecated_functions = array_change_key_case( $this->deprecated_functions, CASE_LOWER ); + + return array( + 'deprecated_functions' => array( + 'functions' => array_keys( $this->deprecated_functions ), + ), + ); + } + + /** + * Process a matched token. + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. Will + * always be 'deprecated_functions'. + * @param string $matched_content The token content (function name) which was matched. + * + * @return void + */ + public function process_matched_token( $stackPtr, $group_name, $matched_content ) { + + $this->get_wp_version_from_cl(); + + $function_name = strtolower( $matched_content ); + + $message = '%s() has been deprecated since WordPress version %s.'; + $data = array( + $matched_content, + $this->deprecated_functions[ $function_name ]['version'], + ); + + if ( ! empty( $this->deprecated_functions[ $function_name ]['alt'] ) ) { + $message .= ' Use %s instead.'; + $data[] = $this->deprecated_functions[ $function_name ]['alt']; + } + + $this->addMessage( + $message, + $stackPtr, + ( version_compare( $this->deprecated_functions[ $function_name ]['version'], $this->minimum_supported_version, '<' ) ), + $this->string_to_errorcode( $matched_content . 'Found' ), + $data + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedParameterValuesSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedParameterValuesSniff.php new file mode 100644 index 00000000..0758def2 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedParameterValuesSniff.php @@ -0,0 +1,216 @@ + array( + * (int) Target parameter position, 1-based. => array( + * (string) Parameter value. => array( + * 'alt' => (string) Suggested alternative. + * 'version' => (int) The WordPress version when deprecated. + * ) + * ) + * ) + * ); + */ + protected $target_functions = array( + 'add_settings_field' => array( + 4 => array( + 'misc' => array( + 'alt' => 'another settings group', + 'version' => '3.0.0', + ), + 'privacy' => array( + 'alt' => 'another settings group', + 'version' => '3.5.0', + ), + ), + ), + 'add_settings_section' => array( + 4 => array( + 'misc' => array( + 'alt' => 'another settings group', + 'version' => '3.0.0', + ), + 'privacy' => array( + 'alt' => 'another settings group', + 'version' => '3.5.0', + ), + ), + ), + 'bloginfo' => array( + 1 => array( + 'home' => array( + 'alt' => 'the "url" argument', + 'version' => '2.2.0', + ), + 'siteurl' => array( + 'alt' => 'the "url" argument', + 'version' => '2.2.0', + ), + 'text_direction' => array( + 'alt' => 'is_rtl()', + 'version' => '2.2.0', + ), + ), + ), + 'get_bloginfo' => array( + 1 => array( + 'home' => array( + 'alt' => 'the "url" argument', + 'version' => '2.2.0', + ), + 'siteurl' => array( + 'alt' => 'the "url" argument', + 'version' => '2.2.0', + ), + 'text_direction' => array( + 'alt' => 'is_rtl()', + 'version' => '2.2.0', + ), + ), + ), + 'register_setting' => array( + 1 => array( + 'misc' => array( + 'alt' => 'another settings group', + 'version' => '3.0.0', + ), + 'privacy' => array( + 'alt' => 'another settings group', + 'version' => '3.5.0', + ), + ), + ), + 'unregister_setting' => array( + 1 => array( + 'misc' => array( + 'alt' => 'another settings group', + 'version' => '3.0.0', + ), + 'privacy' => array( + 'alt' => 'another settings group', + 'version' => '3.5.0', + ), + ), + ), + ); + + /** + * Process the parameters of a matched function. + * + * @since 1.0.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + $this->get_wp_version_from_cl(); + $param_count = \count( $parameters ); + foreach ( $this->target_functions[ $matched_content ] as $position => $parameter_args ) { + + // Stop if the position is higher then the total number of parameters. + if ( $position > $param_count ) { + break; + } + + $this->process_parameter( $matched_content, $parameters[ $position ], $parameter_args ); + } + } + + /** + * Process the parameter of a matched function. + * + * @since 1.0.0 + * + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameter Array with start and end token positon of the parameter. + * @param array $parameter_args Array with alternative and WordPress deprecation version of the parameter. + * + * @return void + */ + protected function process_parameter( $matched_content, $parameter, $parameter_args ) { + + $parameter_position = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + $parameter['start'], + $parameter['end'] + 1, + true + ); + + if ( false === $parameter_position ) { + return; + } + + $matched_parameter = $this->strip_quotes( $this->tokens[ $parameter_position ]['content'] ); + if ( ! isset( $parameter_args[ $matched_parameter ] ) ) { + return; + } + + $message = 'The parameter value "%s" has been deprecated since WordPress version %s.'; + $data = array( + $matched_parameter, + $parameter_args[ $matched_parameter ]['version'], + ); + + if ( ! empty( $parameter_args[ $matched_parameter ]['alt'] ) ) { + $message .= ' Use %s instead.'; + $data[] = $parameter_args[ $matched_parameter ]['alt']; + } + + $is_error = version_compare( $parameter_args[ $matched_parameter ]['version'], $this->minimum_supported_version, '<' ); + $this->addMessage( + $message, + $parameter_position, + $is_error, + $this->string_to_errorcode( 'Found' ), + $data + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedParametersSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedParametersSniff.php new file mode 100644 index 00000000..3d45bafc --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DeprecatedParametersSniff.php @@ -0,0 +1,338 @@ + value + * in a custom ruleset. + * + * @uses \WordPressCS\WordPress\Sniff::$minimum_supported_version + */ +class DeprecatedParametersSniff extends AbstractFunctionParameterSniff { + + /** + * The group name for this group of functions. + * + * @since 0.12.0 + * + * @var string + */ + protected $group_name = 'wp_deprecated_parameters'; + + /** + * Array of function, argument, and default value for deprecated argument. + * + * The functions are ordered alphabetically. + * Last updated for WordPress 4.8.0. + * + * @since 0.12.0 + * + * @var array Multidimensional array with parameter details. + * $target_functions = array( + * (string) Function name. => array( + * (int) Target parameter position, 1-based. => array( + * 'value' => (mixed) Expected default value for the + * deprecated parameter. Currently the default + * values: true, false, null, empty arrays and + * both empty and non-empty strings can be + * handled correctly by the process_parameters() + * method. When an additional default value is + * added, the relevant code in the + * process_parameters() method will need to be + * adjusted. + * 'version' => (int) The WordPress version when deprecated. + * ) + * ) + * ); + */ + protected $target_functions = array( + + 'add_option' => array( + 3 => array( + 'value' => '', + 'version' => '2.3.0', + ), + ), + 'comments_link' => array( + 1 => array( + 'value' => '', + 'version' => '0.72', + ), + 2 => array( + 'value' => '', + 'version' => '1.3.0', + ), + ), + 'comments_number' => array( + 4 => array( + 'value' => '', + 'version' => '1.3.0', + ), + ), + 'convert_chars' => array( + 2 => array( + 'value' => '', + 'version' => '0.71', + ), + ), + 'discover_pingback_server_uri' => array( + 2 => array( + 'value' => '', + 'version' => '2.7.0', + ), + ), + 'get_category_parents' => array( + 5 => array( + 'value' => array(), + 'version' => '4.8.0', + ), + ), + 'get_delete_post_link' => array( + 2 => array( + 'value' => '', + 'version' => '3.0.0', + ), + ), + 'get_last_updated' => array( + 1 => array( + 'value' => '', + 'version' => '3.0.0', // Was previously part of MU. + ), + ), + 'get_the_author' => array( + 1 => array( + 'value' => '', + 'version' => '2.1.0', + ), + ), + 'get_user_option' => array( + 3 => array( + 'value' => '', + 'version' => '2.3.0', + ), + ), + 'get_wp_title_rss' => array( + 1 => array( + 'value' => '–', + 'version' => '4.4.0', + ), + ), + 'is_email' => array( + 2 => array( + 'value' => false, + 'version' => '3.0.0', + ), + ), + 'load_plugin_textdomain' => array( + 2 => array( + 'value' => false, + 'version' => '2.7.0', + ), + ), + 'safecss_filter_attr' => array( + 2 => array( + 'value' => '', + 'version' => '2.8.1', + ), + ), + 'the_attachment_link' => array( + 3 => array( + 'value' => false, + 'version' => '2.5.0', + ), + ), + 'the_author' => array( + 1 => array( + 'value' => '', + 'version' => '2.1.0', + ), + 2 => array( + 'value' => true, + 'version' => '1.5.0', + ), + ), + 'the_author_posts_link' => array( + 1 => array( + 'value' => '', + 'version' => '2.1.0', + ), + ), + 'trackback_rdf' => array( + 1 => array( + 'value' => '', + 'version' => '2.5.0', + ), + ), + 'trackback_url' => array( + 1 => array( + 'value' => true, + 'version' => '2.5.0', + ), + ), + 'update_blog_option' => array( + 4 => array( + 'value' => null, + 'version' => '3.1.0', + ), + ), + 'update_blog_status' => array( + 4 => array( + 'value' => null, + 'version' => '3.1.0', + ), + ), + 'update_user_status' => array( + 4 => array( + 'value' => null, + 'version' => '3.0.2', + ), + ), + 'unregister_setting' => array( + 4 => array( + 'value' => '', + 'version' => '4.7.0', + ), + ), + 'wp_get_http_headers' => array( + 2 => array( + 'value' => false, + 'version' => '2.7.0', + ), + ), + 'wp_get_sidebars_widgets' => array( + 1 => array( + 'value' => true, + 'version' => '2.8.1', + ), + ), + 'wp_install' => array( + 5 => array( + 'value' => '', + 'version' => '2.6.0', + ), + ), + 'wp_new_user_notification' => array( + 2 => array( + 'value' => null, + 'version' => '4.3.1', + ), + ), + 'wp_notify_postauthor' => array( + 2 => array( + 'value' => null, + 'version' => '3.8.0', + ), + ), + 'wp_title_rss' => array( + 1 => array( + 'value' => '–', + 'version' => '4.4.0', + ), + ), + 'wp_upload_bits' => array( + 2 => array( + 'value' => null, + 'version' => '2.0.0', + ), + ), + 'xfn_check' => array( + 3 => array( + 'value' => '', + 'version' => '2.5.0', + ), + ), + ); + + /** + * Process the parameters of a matched function. + * + * @since 0.12.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + + $this->get_wp_version_from_cl(); + + $paramCount = \count( $parameters ); + foreach ( $this->target_functions[ $matched_content ] as $position => $parameter_args ) { + + // Check that number of parameters defined is not less than the position to check. + if ( $position > $paramCount ) { + break; + } + + // The list will need to updated if the default value is not supported. + switch ( $parameters[ $position ]['raw'] ) { + case 'true': + $matched_parameter = true; + break; + case 'false': + $matched_parameter = false; + break; + case 'null': + $matched_parameter = null; + break; + case 'array()': + case '[]': + $matched_parameter = array(); + break; + default: + $matched_parameter = $this->strip_quotes( $parameters[ $position ]['raw'] ); + break; + } + + if ( $parameter_args['value'] === $matched_parameter ) { + continue; + } + + $message = 'The parameter "%s" at position #%s of %s() has been deprecated since WordPress version %s.'; + $is_error = version_compare( $parameter_args['version'], $this->minimum_supported_version, '<' ); + $code = $this->string_to_errorcode( ucfirst( $matched_content ) . 'Param' . $position . 'Found' ); + + $data = array( + $parameters[ $position ]['raw'], + $position, + $matched_content, + $parameter_args['version'], + ); + + if ( isset( $parameter_args['value'] ) && $position < $paramCount ) { + $message .= ' Use "%s" instead.'; + $data[] = (string) $parameter_args['value']; + } else { + $message .= ' Instead do not pass the parameter.'; + } + + $this->addMessage( $message, $stackPtr, $is_error, $code, $data, 0 ); + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DiscouragedConstantsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DiscouragedConstantsSniff.php new file mode 100644 index 00000000..bcd960c4 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DiscouragedConstantsSniff.php @@ -0,0 +1,217 @@ + 'get_stylesheet_directory()', + 'TEMPLATEPATH' => 'get_template_directory()', + 'PLUGINDIR' => 'WP_PLUGIN_DIR', + 'MUPLUGINDIR' => 'WPMU_PLUGIN_DIR', + 'HEADER_IMAGE' => 'add_theme_support( \'custom-header\' )', + 'NO_HEADER_TEXT' => 'add_theme_support( \'custom-header\' )', + 'HEADER_TEXTCOLOR' => 'add_theme_support( \'custom-header\' )', + 'HEADER_IMAGE_WIDTH' => 'add_theme_support( \'custom-header\' )', + 'HEADER_IMAGE_HEIGHT' => 'add_theme_support( \'custom-header\' )', + 'BACKGROUND_COLOR' => 'add_theme_support( \'custom-background\' )', + 'BACKGROUND_IMAGE' => 'add_theme_support( \'custom-background\' )', + ); + + /** + * Array of functions to check. + * + * @since 0.14.0 + * + * @var array => + */ + protected $target_functions = array( + 'define' => 1, + ); + + /** + * Array of tokens which if found preceding the $stackPtr indicate that a T_STRING is not a constant. + * + * @var array + */ + private $preceding_tokens_to_ignore = array( + \T_NAMESPACE => true, + \T_USE => true, + \T_CLASS => true, + \T_TRAIT => true, + \T_INTERFACE => true, + \T_EXTENDS => true, + \T_IMPLEMENTS => true, + \T_NEW => true, + \T_FUNCTION => true, + \T_DOUBLE_COLON => true, + \T_OBJECT_OPERATOR => true, + \T_INSTANCEOF => true, + \T_GOTO => true, + ); + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 0.14.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + if ( isset( $this->target_functions[ strtolower( $this->tokens[ $stackPtr ]['content'] ) ] ) ) { + // Disallow excluding function groups for this sniff. + $this->exclude = array(); + + return parent::process_token( $stackPtr ); + + } else { + return $this->process_arbitrary_tstring( $stackPtr ); + } + } + + /** + * Process an arbitrary T_STRING token to determine whether it is one of the target constants. + * + * @since 0.14.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + public function process_arbitrary_tstring( $stackPtr ) { + $content = $this->tokens[ $stackPtr ]['content']; + + if ( ! isset( $this->discouraged_constants[ $content ] ) ) { + return; + } + + $next = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + if ( false !== $next && \T_OPEN_PARENTHESIS === $this->tokens[ $next ]['code'] ) { + // Function call or declaration. + return; + } + + $prev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true ); + if ( false !== $prev && isset( $this->preceding_tokens_to_ignore[ $this->tokens[ $prev ]['code'] ] ) ) { + // Not the use of a constant. + return; + } + + if ( $this->is_token_namespaced( $stackPtr ) === true ) { + // Namespaced constant of the same name. + return; + } + + if ( false !== $prev + && \T_CONST === $this->tokens[ $prev ]['code'] + && true === $this->is_class_constant( $prev ) + ) { + // Class constant of the same name. + return; + } + + /* + * Deal with a number of variations of use statements. + */ + for ( $i = $stackPtr; $i > 0; $i-- ) { + if ( $this->tokens[ $i ]['line'] !== $this->tokens[ $stackPtr ]['line'] ) { + break; + } + } + + $first_on_line = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $i + 1 ), null, true ); + if ( false !== $first_on_line && \T_USE === $this->tokens[ $first_on_line ]['code'] ) { + $next_on_line = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $first_on_line + 1 ), null, true ); + if ( false !== $next_on_line ) { + if ( ( \T_STRING === $this->tokens[ $next_on_line ]['code'] + && 'const' === $this->tokens[ $next_on_line ]['content'] ) + || \T_CONST === $this->tokens[ $next_on_line ]['code'] // Happens in some PHPCS versions. + ) { + $has_ns_sep = $this->phpcsFile->findNext( \T_NS_SEPARATOR, ( $next_on_line + 1 ), $stackPtr ); + if ( false !== $has_ns_sep ) { + // Namespaced const (group) use statement. + return; + } + } else { + // Not a const use statement. + return; + } + } + } + + // Ok, this is really one of the discouraged constants. + $this->phpcsFile->addWarning( + 'Found usage of constant "%s". Use %s instead.', + $stackPtr, + $this->string_to_errorcode( $content . 'UsageFound' ), + array( + $content, + $this->discouraged_constants[ $content ], + ) + ); + } + + /** + * Process the parameters of a matched `define` function call. + * + * @since 0.14.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + $function_name = strtolower( $matched_content ); + $target_param = $this->target_functions[ $function_name ]; + + // Was the target parameter passed ? + if ( ! isset( $parameters[ $target_param ] ) ) { + return; + } + + $raw_content = $this->strip_quotes( $parameters[ $target_param ]['raw'] ); + + if ( isset( $this->discouraged_constants[ $raw_content ] ) ) { + $this->phpcsFile->addWarning( + 'Found declaration of constant "%s". Use %s instead.', + $stackPtr, + $this->string_to_errorcode( $raw_content . 'DeclarationFound' ), + array( + $raw_content, + $this->discouraged_constants[ $raw_content ], + ) + ); + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DiscouragedFunctionsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DiscouragedFunctionsSniff.php new file mode 100644 index 00000000..d51504a3 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/DiscouragedFunctionsSniff.php @@ -0,0 +1,57 @@ + array( + * 'lambda' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Use anonymous functions instead please!', + * 'functions' => array( 'file_get_contents', 'create_function' ), + * ) + * ) + * + * @return array + */ + public function getGroups() { + return array( + 'query_posts' => array( + 'type' => 'warning', + 'message' => '%s() is discouraged. Use WP_Query instead.', + 'functions' => array( + 'query_posts', + ), + ), + + 'wp_reset_query' => array( + 'type' => 'warning', + 'message' => '%s() is discouraged. Use wp_reset_postdata() instead.', + 'functions' => array( + 'wp_reset_query', + ), + ), + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/EnqueuedResourceParametersSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/EnqueuedResourceParametersSniff.php new file mode 100644 index 00000000..9c1f5f1e --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/EnqueuedResourceParametersSniff.php @@ -0,0 +1,223 @@ + => + */ + protected $target_functions = array( + 'wp_register_script' => true, + 'wp_enqueue_script' => true, + 'wp_register_style' => true, + 'wp_enqueue_style' => true, + ); + + /** + * False + the empty tokens array. + * + * This array is enriched with the $emptyTokens array in the register() method. + * + * @var array + */ + private $false_tokens = array( + \T_FALSE => \T_FALSE, + ); + + /** + * Token codes which are "safe" to accept to determine whether a version would evaluate to `false`. + * + * This array is enriched with the several of the PHPCS token arrays in the register() method. + * + * @var array + */ + private $safe_tokens = array( + \T_NULL => \T_NULL, + \T_FALSE => \T_FALSE, + \T_TRUE => \T_TRUE, + \T_LNUMBER => \T_LNUMBER, + \T_DNUMBER => \T_DNUMBER, + \T_CONSTANT_ENCAPSED_STRING => \T_CONSTANT_ENCAPSED_STRING, + \T_START_NOWDOC => \T_START_NOWDOC, + \T_NOWDOC => \T_NOWDOC, + \T_END_NOWDOC => \T_END_NOWDOC, + \T_OPEN_PARENTHESIS => \T_OPEN_PARENTHESIS, + \T_CLOSE_PARENTHESIS => \T_CLOSE_PARENTHESIS, + \T_STRING_CONCAT => \T_STRING_CONCAT, + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * Overloads and calls the parent method to allow for adding additional tokens to the $safe_tokens property. + * + * @return array + */ + public function register() { + $this->false_tokens += Tokens::$emptyTokens; + + $this->safe_tokens += Tokens::$emptyTokens; + $this->safe_tokens += Tokens::$assignmentTokens; + $this->safe_tokens += Tokens::$comparisonTokens; + $this->safe_tokens += Tokens::$operators; + $this->safe_tokens += Tokens::$booleanOperators; + $this->safe_tokens += Tokens::$castTokens; + + return parent::register(); + } + + /** + * Process the parameters of a matched function. + * + * @since 1.0.0 + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + + // Check to see if a source ($src) is specified. + if ( ! isset( $parameters[2] ) ) { + return; + } + + /* + * Version Check: Check to make sure the version is set explicitly. + */ + + if ( ! isset( $parameters[4] ) || 'null' === $parameters[4]['raw'] ) { + $type = 'script'; + if ( strpos( $matched_content, '_style' ) !== false ) { + $type = 'style'; + } + + $this->phpcsFile->addError( + 'Resource version not set in call to %s(). This means new versions of the %s will not always be loaded due to browser caching.', + $stackPtr, + 'MissingVersion', + array( $matched_content, $type ) + ); + } else { + // The version argument should have a non-false value. + if ( $this->is_falsy( $parameters[4]['start'], $parameters[4]['end'] ) ) { + $this->phpcsFile->addError( + 'Version parameter is not explicitly set or has been set to an equivalent of "false" for %s; ' . + 'This means that the WordPress core version will be used which is not recommended for plugin or theme development.', + $stackPtr, + 'NoExplicitVersion', + array( $matched_content ) + ); + } + } + + /* + * In footer Check + * + * Check to make sure that $in_footer is set to true. + * It will warn the user to make sure it is intended. + * + * Only wp_register_script and wp_enqueue_script need this check, + * as this parameter is not available to wp_register_style and wp_enqueue_style. + */ + if ( 'wp_register_script' !== $matched_content && 'wp_enqueue_script' !== $matched_content ) { + return; + } + + if ( ! isset( $parameters[5] ) ) { + // If in footer is not set, throw a warning about the default. + $this->phpcsFile->addWarning( + 'In footer ($in_footer) is not set explicitly %s; ' . + 'It is recommended to load scripts in the footer. Please set this value to `true` to load it in the footer, or explicitly `false` if it should be loaded in the header.', + $stackPtr, + 'NotInFooter', + array( $matched_content ) + ); + } + } + + /** + * Determine if a range has a falsy value. + * + * @param int $start The position to start looking from. + * @param int $end The position to stop looking (inclusive). + * + * @return bool True if the parameter is falsy. + * False if the parameter is not falsy or when it + * couldn't be reliably determined. + */ + protected function is_falsy( $start, $end ) { + + // Find anything excluding the false tokens. + $has_non_false = $this->phpcsFile->findNext( $this->false_tokens, $start, ( $end + 1 ), true ); + // If no non-false tokens are found, we are good. + if ( false === $has_non_false ) { + return true; + } + + $code_string = ''; + for ( $i = $start; $i <= $end; $i++ ) { + if ( isset( $this->safe_tokens[ $this->tokens[ $i ]['code'] ] ) === false ) { + // Function call/variable or other token which makes it neigh impossible + // to determine whether the actual value would evaluate to false. + return false; + } + + if ( isset( Tokens::$emptyTokens[ $this->tokens[ $i ]['code'] ] ) === true ) { + continue; + } + + $code_string .= $this->tokens[ $i ]['content']; + } + + if ( '' === $code_string ) { + return false; + } + + // Evaluate the argument to figure out the outcome is false or not. + // phpcs:ignore Squiz.PHP.Eval -- No harm here. + return ( false === eval( "return (bool) $code_string;" ) ); + } +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/EnqueuedResourcesSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/EnqueuedResourcesSniff.php new file mode 100644 index 00000000..27572e95 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/EnqueuedResourcesSniff.php @@ -0,0 +1,64 @@ +tokens[ $stackPtr ]; + + if ( preg_match( '# rel=\\\\?[\'"]?stylesheet\\\\?[\'"]?#', $token['content'] ) > 0 ) { + $this->phpcsFile->addError( + 'Stylesheets must be registered/enqueued via wp_enqueue_style', + $stackPtr, + 'NonEnqueuedStylesheet' + ); + } + + if ( preg_match( '#]*(?<=src=)#', $token['content'] ) > 0 ) { + $this->phpcsFile->addError( + 'Scripts must be registered/enqueued via wp_enqueue_script', + $stackPtr, + 'NonEnqueuedScript' + ); + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/GlobalVariablesOverrideSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/GlobalVariablesOverrideSniff.php new file mode 100644 index 00000000..37bf68b2 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/GlobalVariablesOverrideSniff.php @@ -0,0 +1,466 @@ + true, + 'wp_cockneyreplace' => true, + ); + + /** + * Scoped object and function structures to skip over as + * variables will have a different scope within those. + * + * @since 1.1.0 + * + * @var array + */ + private $skip_over = array( + \T_FUNCTION => true, + \T_CLOSURE => true, + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 0.3.0 + * @since 1.1.0 Added class tokens for improved test classes skipping. + * + * @return array + */ + public function register() { + // Add the OO scope tokens to the $skip_over property. + $this->skip_over += Tokens::$ooScopeTokens; + + $targets = array( + \T_GLOBAL, + \T_VARIABLE, + \T_LIST, + \T_OPEN_SHORT_ARRAY, + ); + + // Only used to skip over test classes. + $targets += Tokens::$ooScopeTokens; + + return $targets; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 0.3.0 + * @since 1.1.0 Split the token specific logic off into separate methods. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_token( $stackPtr ) { + + $token = $this->tokens[ $stackPtr ]; + + // Ignore variable overrides in test classes. + if ( isset( Tokens::$ooScopeTokens[ $token['code'] ] ) ) { + + if ( true === $this->is_test_class( $stackPtr ) + && $token['scope_condition'] === $stackPtr + && isset( $token['scope_closer'] ) + ) { + // Skip forward to end of test class. + return $token['scope_closer']; + } + + // Otherwise ignore the tokens as they were only registered to enable skipping over test classes. + return; + } + + /* + * Examine variables within a function scope based on a `global` statement in the + * function. + * Examine variables not within a function scope, but within a list construct, based + * on that. + * Examine variables not within a function scope and access to the `$GLOBALS` + * variable based on the variable token. + */ + $in_function_scope = $this->phpcsFile->hasCondition( $stackPtr, array( \T_FUNCTION, \T_CLOSURE ) ); + + if ( ( \T_LIST === $token['code'] || \T_OPEN_SHORT_ARRAY === $token['code'] ) + && false === $in_function_scope + && false === $this->treat_files_as_scoped + ) { + return $this->process_list_assignment( $stackPtr ); + } elseif ( \T_VARIABLE === $token['code'] + && ( '$GLOBALS' === $token['content'] + || ( false === $in_function_scope && false === $this->treat_files_as_scoped ) ) + ) { + return $this->process_variable_assignment( $stackPtr ); + } elseif ( \T_GLOBAL === $token['code'] + && ( true === $in_function_scope || true === $this->treat_files_as_scoped ) + ) { + return $this->process_global_statement( $stackPtr, $in_function_scope ); + } + } + + /** + * Check that global variables declared via a list construct are prefixed. + * + * @internal No need to take special measures for nested lists. Nested or not, + * each list part can only contain one variable being written to. + * + * @since 2.2.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + protected function process_list_assignment( $stackPtr ) { + $list_open_close = $this->find_list_open_close( $stackPtr ); + if ( false === $list_open_close ) { + // Short array, not short list. + return; + } + + $var_pointers = $this->get_list_variables( $stackPtr, $list_open_close ); + foreach ( $var_pointers as $ptr ) { + $this->process_variable_assignment( $ptr, true ); + } + + // No need to re-examine these variables. + return $list_open_close['closer']; + } + + /** + * Check that defined global variables are prefixed. + * + * @since 1.1.0 Logic was previously contained in the process_token() method. + * + * @param int $stackPtr The position of the current token in the stack. + * @param bool $in_list Whether or not this is a variable in a list assignment. + * Defaults to false. + * + * @return void + */ + protected function process_variable_assignment( $stackPtr, $in_list = false ) { + + if ( $this->has_whitelist_comment( 'override', $stackPtr ) === true ) { + return; + } + + $token = $this->tokens[ $stackPtr ]; + $var_name = substr( $token['content'], 1 ); // Strip the dollar sign. + $data = array(); + + // Determine the variable name for `$GLOBALS['array_key']`. + if ( 'GLOBALS' === $var_name ) { + $bracketPtr = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + + if ( false === $bracketPtr || \T_OPEN_SQUARE_BRACKET !== $this->tokens[ $bracketPtr ]['code'] || ! isset( $this->tokens[ $bracketPtr ]['bracket_closer'] ) ) { + return; + } + + // Retrieve the array key and avoid getting tripped up by some simple obfuscation. + $var_name = ''; + $start = ( $bracketPtr + 1 ); + for ( $ptr = $start; $ptr < $this->tokens[ $bracketPtr ]['bracket_closer']; $ptr++ ) { + /* + * If the globals array key contains a variable, constant, function call + * or interpolated variable, bow out. + */ + if ( \T_VARIABLE === $this->tokens[ $ptr ]['code'] + || \T_STRING === $this->tokens[ $ptr ]['code'] + || \T_DOUBLE_QUOTED_STRING === $this->tokens[ $ptr ]['code'] + ) { + return; + } + + if ( \T_CONSTANT_ENCAPSED_STRING === $this->tokens[ $ptr ]['code'] ) { + $var_name .= $this->strip_quotes( $this->tokens[ $ptr ]['content'] ); + } + } + + if ( '' === $var_name ) { + // Shouldn't happen, but just in case. + return; + } + + // Set up the data for the error message. + $data[] = '$GLOBALS[\'' . $var_name . '\']'; + } + + /* + * Is this one of the WP global variables ? + */ + if ( isset( $this->wp_globals[ $var_name ] ) === false ) { + return; + } + + /* + * Is this one of the WP global variables which are allowed to be overwritten ? + */ + if ( isset( $this->override_allowed[ $var_name ] ) === true ) { + return; + } + + /* + * Check if the variable value is being changed. + */ + if ( false === $in_list + && false === $this->is_assignment( $stackPtr ) + && false === $this->is_foreach_as( $stackPtr ) + ) { + return; + } + + /* + * Function parameters with the same name as a WP global variable are fine, + * including when they are being assigned a default value. + */ + if ( false === $in_list && isset( $this->tokens[ $stackPtr ]['nested_parenthesis'] ) ) { + foreach ( $this->tokens[ $stackPtr ]['nested_parenthesis'] as $opener => $closer ) { + if ( isset( $this->tokens[ $opener ]['parenthesis_owner'] ) + && ( \T_FUNCTION === $this->tokens[ $this->tokens[ $opener ]['parenthesis_owner'] ]['code'] + || \T_CLOSURE === $this->tokens[ $this->tokens[ $opener ]['parenthesis_owner'] ]['code'] ) + ) { + return; + } + } + unset( $opener, $closer ); + } + + /* + * Class property declarations with the same name as WP global variables are fine. + */ + if ( false === $in_list && true === $this->is_class_property( $stackPtr ) ) { + return; + } + + // Still here ? In that case, the WP global variable is being tampered with. + $this->add_error( $stackPtr, $data ); + } + + /** + * Check that global variables imported into a function scope using a global statement + * are not being overruled. + * + * @since 1.1.0 Logic was previously contained in the process_token() method. + * + * @param int $stackPtr The position of the current token in the stack. + * @param bool $in_function_scope Whether the global statement is within a scoped function/closure. + * + * @return void + */ + protected function process_global_statement( $stackPtr, $in_function_scope ) { + /* + * Collect the variables to watch for. + */ + $search = array(); + $ptr = ( $stackPtr + 1 ); + while ( isset( $this->tokens[ $ptr ] ) ) { + $var = $this->tokens[ $ptr ]; + + // Halt the loop at end of statement. + if ( \T_SEMICOLON === $var['code'] ) { + break; + } + + if ( \T_VARIABLE === $var['code'] ) { + $var_name = substr( $var['content'], 1 ); + if ( isset( $this->wp_globals[ $var_name ] ) + && isset( $this->override_allowed[ $var_name ] ) === false + ) { + $search[] = $var['content']; + } + } + + $ptr++; + } + unset( $var ); + + if ( empty( $search ) ) { + return; + } + + /* + * Search for assignments to the imported global variables within the relevant scope. + */ + $start = $ptr; + if ( true === $in_function_scope ) { + $function_cond = $this->phpcsFile->getCondition( $stackPtr, \T_FUNCTION ); + $closure_cond = $this->phpcsFile->getCondition( $stackPtr, \T_CLOSURE ); + $scope_cond = max( $function_cond, $closure_cond ); // If false, it will evaluate as zero, so this is fine. + if ( isset( $this->tokens[ $scope_cond ]['scope_closer'] ) === false ) { + // Live coding or parse error. + return; + } + $end = $this->tokens[ $scope_cond ]['scope_closer']; + } else { + // Global statement in the global namespace with file is being treated as scoped. + $end = $this->phpcsFile->numTokens; + } + + for ( $ptr = $start; $ptr < $end; $ptr++ ) { + + // Skip over nested functions, classes and the likes. + if ( isset( $this->skip_over[ $this->tokens[ $ptr ]['code'] ] ) ) { + if ( ! isset( $this->tokens[ $ptr ]['scope_closer'] ) ) { + // Live coding or parse error. + break; + } + + $ptr = $this->tokens[ $ptr ]['scope_closer']; + continue; + } + + // Make sure to recognize assignments to variables in a list construct. + if ( \T_LIST === $this->tokens[ $ptr ]['code'] + || \T_OPEN_SHORT_ARRAY === $this->tokens[ $ptr ]['code'] + ) { + $list_open_close = $this->find_list_open_close( $ptr ); + + if ( false === $list_open_close ) { + // Short array, not short list. + continue; + } + + $var_pointers = $this->get_list_variables( $ptr, $list_open_close ); + foreach ( $var_pointers as $ptr ) { + $var_name = $this->tokens[ $ptr ]['content']; + if ( '$GLOBALS' === $var_name ) { + $var_name = '$' . $this->strip_quotes( $this->get_array_access_key( $ptr ) ); + } + + if ( \in_array( $var_name, $search, true ) ) { + $this->process_variable_assignment( $ptr, true ); + } + } + + // No need to re-examine these variables. + $ptr = $list_open_close['closer']; + continue; + } + + if ( \T_VARIABLE !== $this->tokens[ $ptr ]['code'] ) { + continue; + } + + if ( \in_array( $this->tokens[ $ptr ]['content'], $search, true ) === false ) { + // Not one of the variables we're interested in. + continue; + } + + // Don't throw false positives for static class properties. + if ( $this->is_class_object_call( $ptr ) === true ) { + continue; + } + + if ( true === $this->is_assignment( $ptr ) ) { + $this->maybe_add_error( $ptr ); + continue; + } + + // Check if this is a variable assignment within a `foreach()` declaration. + if ( $this->is_foreach_as( $ptr ) === true ) { + $this->maybe_add_error( $ptr ); + } + } + } + + /** + * Add the error if there is no whitelist comment present. + * + * @since 0.11.0 + * @since 1.1.0 - Visibility changed from public to protected. + * - Check for being in a test class moved to the process_token() method. + * + * @param int $stackPtr The position of the token to throw the error for. + * + * @return void + */ + protected function maybe_add_error( $stackPtr ) { + if ( $this->has_whitelist_comment( 'override', $stackPtr ) === false ) { + $this->add_error( $stackPtr ); + } + } + + /** + * Add the error. + * + * @since 1.1.0 + * + * @param int $stackPtr The position of the token to throw the error for. + * @param array $data Optional. Array containing one entry holding the + * name of the variable being overruled. + * Defaults to the 'content' of the $stackPtr token. + * + * @return void + */ + protected function add_error( $stackPtr, $data = array() ) { + if ( empty( $data ) ) { + $data[] = $this->tokens[ $stackPtr ]['content']; + } + + $this->phpcsFile->addError( + 'Overriding WordPress globals is prohibited. Found assignment to %s', + $stackPtr, + 'Prohibited', + $data + ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/I18nSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/I18nSniff.php new file mode 100644 index 00000000..d229cb5e --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/I18nSniff.php @@ -0,0 +1,789 @@ +phpcsFile->getFilename() + * @link https://youtrack.jetbrains.com/issue/WI-17740 + * + * @var string[]|string + */ + public $text_domain; + + /** + * The I18N functions in use in WP. + * + * @since 0.10.0 + * @since 0.11.0 Changed visibility from public to protected. + * + * @var array => + */ + protected $i18n_functions = array( + 'translate' => 'simple', + '__' => 'simple', + 'esc_attr__' => 'simple', + 'esc_html__' => 'simple', + '_e' => 'simple', + 'esc_attr_e' => 'simple', + 'esc_html_e' => 'simple', + 'translate_with_gettext_context' => 'context', + '_x' => 'context', + '_ex' => 'context', + 'esc_attr_x' => 'context', + 'esc_html_x' => 'context', + '_n' => 'number', + '_nx' => 'number_context', + '_n_noop' => 'noopnumber', + '_nx_noop' => 'noopnumber_context', + ); + + /** + * Toggle whether or not to check for translators comments for text string containing placeholders. + * + * Intended to make this part of the sniff unit testable, but can be used by end-users too, + * though they can just as easily disable this via the sniff code. + * + * @since 0.11.0 + * + * @var bool + */ + public $check_translator_comments = true; + + /** + * Whether or not the `default` text domain is one of the allowed text domains. + * + * @since 0.14.0 + * + * @var bool + */ + private $text_domain_contains_default = false; + + /** + * Whether or not the `default` text domain is the only allowed text domain. + * + * @since 0.14.0 + * + * @var bool + */ + private $text_domain_is_default = false; + + /** + * Groups of functions to restrict. + * + * Example: groups => array( + * 'lambda' => array( + * 'type' => 'error' | 'warning', + * 'message' => 'Use anonymous functions instead please!', + * 'functions' => array( 'file_get_contents', 'create_function' ), + * ) + * ) + * + * @return array + */ + public function getGroups() { + return array( + 'i18n' => array( + 'functions' => array_keys( $this->i18n_functions ), + ), + 'typos' => array( + 'functions' => array( + '_', + ), + ), + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.0.0 Defers to the abstractFunctionRestriction sniff for determining + * whether something is a function call. The logic after that has + * been split off to the `process_matched_token()` method. + * + * @param int $stack_ptr The position of the current token in the stack. + * + * @return void + */ + public function process_token( $stack_ptr ) { + + // Reset defaults. + $this->text_domain_contains_default = false; + $this->text_domain_is_default = false; + + // Allow overruling the text_domain set in a ruleset via the command line. + $cl_text_domain = trim( PHPCSHelper::get_config_data( 'text_domain' ) ); + if ( ! empty( $cl_text_domain ) ) { + $this->text_domain = array_filter( array_map( 'trim', explode( ',', $cl_text_domain ) ) ); + } + + $this->text_domain = $this->merge_custom_array( $this->text_domain, array(), false ); + + if ( ! empty( $this->text_domain ) ) { + if ( \in_array( 'default', $this->text_domain, true ) ) { + $this->text_domain_contains_default = true; + if ( \count( $this->text_domain ) === 1 ) { + $this->text_domain_is_default = true; + } + } + } + + // Prevent exclusion of the i18n group. + $this->exclude = array(); + + parent::process_token( $stack_ptr ); + } + + /** + * Process a matched token. + * + * @since 1.0.0 Logic split off from the `process_token()` method. + * + * @param int $stack_ptr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_matched_token( $stack_ptr, $group_name, $matched_content ) { + + $func_open_paren_token = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stack_ptr + 1 ), null, true ); + if ( false === $func_open_paren_token + || \T_OPEN_PARENTHESIS !== $this->tokens[ $func_open_paren_token ]['code'] + || ! isset( $this->tokens[ $func_open_paren_token ]['parenthesis_closer'] ) + ) { + // Live coding, parse error or not a function call. + return; + } + + if ( 'typos' === $group_name && '_' === $matched_content ) { + $this->phpcsFile->addError( 'Found single-underscore "_()" function when double-underscore expected.', $stack_ptr, 'SingleUnderscoreGetTextFunction' ); + return; + } + + if ( \in_array( $matched_content, array( 'translate', 'translate_with_gettext_context' ), true ) ) { + $this->phpcsFile->addWarning( 'Use of the "%s()" function is reserved for low-level API usage.', $stack_ptr, 'LowLevelTranslationFunction', array( $matched_content ) ); + } + + $arguments_tokens = array(); + $argument_tokens = array(); + $tokens = $this->tokens; + + // Look at arguments. + for ( $i = ( $func_open_paren_token + 1 ); $i < $this->tokens[ $func_open_paren_token ]['parenthesis_closer']; $i++ ) { + $this_token = $this->tokens[ $i ]; + $this_token['token_index'] = $i; + if ( isset( Tokens::$emptyTokens[ $this_token['code'] ] ) ) { + continue; + } + if ( \T_COMMA === $this_token['code'] ) { + $arguments_tokens[] = $argument_tokens; + $argument_tokens = array(); + continue; + } + + // Merge consecutive single or double quoted strings (when they span multiple lines). + if ( isset( Tokens::$textStringTokens[ $this_token['code'] ] ) ) { + for ( $j = ( $i + 1 ); $j < $this->tokens[ $func_open_paren_token ]['parenthesis_closer']; $j++ ) { + if ( $this_token['code'] === $this->tokens[ $j ]['code'] ) { + $this_token['content'] .= $this->tokens[ $j ]['content']; + $i = $j; + } else { + break; + } + } + } + $argument_tokens[] = $this_token; + + // Include everything up to and including the parenthesis_closer if this token has one. + if ( ! empty( $this_token['parenthesis_closer'] ) ) { + for ( $j = ( $i + 1 ); $j <= $this_token['parenthesis_closer']; $j++ ) { + $tokens[ $j ]['token_index'] = $j; + $argument_tokens[] = $tokens[ $j ]; + } + $i = $this_token['parenthesis_closer']; + } + } + + if ( ! empty( $argument_tokens ) ) { + $arguments_tokens[] = $argument_tokens; + } + unset( $argument_tokens ); + + $argument_assertions = array(); + if ( 'simple' === $this->i18n_functions[ $matched_content ] ) { + $argument_assertions[] = array( + 'arg_name' => 'text', + 'tokens' => array_shift( $arguments_tokens ), + ); + $argument_assertions[] = array( + 'arg_name' => 'domain', + 'tokens' => array_shift( $arguments_tokens ), + ); + } elseif ( 'context' === $this->i18n_functions[ $matched_content ] ) { + $argument_assertions[] = array( + 'arg_name' => 'text', + 'tokens' => array_shift( $arguments_tokens ), + ); + $argument_assertions[] = array( + 'arg_name' => 'context', + 'tokens' => array_shift( $arguments_tokens ), + ); + $argument_assertions[] = array( + 'arg_name' => 'domain', + 'tokens' => array_shift( $arguments_tokens ), + ); + } elseif ( 'number' === $this->i18n_functions[ $matched_content ] ) { + $argument_assertions[] = array( + 'arg_name' => 'single', + 'tokens' => array_shift( $arguments_tokens ), + ); + $argument_assertions[] = array( + 'arg_name' => 'plural', + 'tokens' => array_shift( $arguments_tokens ), + ); + array_shift( $arguments_tokens ); + $argument_assertions[] = array( + 'arg_name' => 'domain', + 'tokens' => array_shift( $arguments_tokens ), + ); + } elseif ( 'number_context' === $this->i18n_functions[ $matched_content ] ) { + $argument_assertions[] = array( + 'arg_name' => 'single', + 'tokens' => array_shift( $arguments_tokens ), + ); + $argument_assertions[] = array( + 'arg_name' => 'plural', + 'tokens' => array_shift( $arguments_tokens ), + ); + array_shift( $arguments_tokens ); + $argument_assertions[] = array( + 'arg_name' => 'context', + 'tokens' => array_shift( $arguments_tokens ), + ); + $argument_assertions[] = array( + 'arg_name' => 'domain', + 'tokens' => array_shift( $arguments_tokens ), + ); + } elseif ( 'noopnumber' === $this->i18n_functions[ $matched_content ] ) { + $argument_assertions[] = array( + 'arg_name' => 'single', + 'tokens' => array_shift( $arguments_tokens ), + ); + $argument_assertions[] = array( + 'arg_name' => 'plural', + 'tokens' => array_shift( $arguments_tokens ), + ); + $argument_assertions[] = array( + 'arg_name' => 'domain', + 'tokens' => array_shift( $arguments_tokens ), + ); + } elseif ( 'noopnumber_context' === $this->i18n_functions[ $matched_content ] ) { + $argument_assertions[] = array( + 'arg_name' => 'single', + 'tokens' => array_shift( $arguments_tokens ), + ); + $argument_assertions[] = array( + 'arg_name' => 'plural', + 'tokens' => array_shift( $arguments_tokens ), + ); + $argument_assertions[] = array( + 'arg_name' => 'context', + 'tokens' => array_shift( $arguments_tokens ), + ); + $argument_assertions[] = array( + 'arg_name' => 'domain', + 'tokens' => array_shift( $arguments_tokens ), + ); + } + + if ( ! empty( $arguments_tokens ) ) { + $this->phpcsFile->addError( 'Too many arguments for function "%s".', $func_open_paren_token, 'TooManyFunctionArgs', array( $matched_content ) ); + } + + foreach ( $argument_assertions as $argument_assertion_context ) { + if ( empty( $argument_assertion_context['tokens'][0] ) ) { + $argument_assertion_context['stack_ptr'] = $func_open_paren_token; + } else { + $argument_assertion_context['stack_ptr'] = $argument_assertion_context['tokens'][0]['token_index']; + } + $this->check_argument_tokens( $argument_assertion_context ); + } + + /* + * For _n*() calls, compare the singular and plural strings. + * If either of the arguments is missing, empty or has more than 1 token, skip out. + * An error for that will already have been reported via the `check_argument_tokens()` method. + */ + if ( false !== strpos( $this->i18n_functions[ $matched_content ], 'number' ) + && isset( $argument_assertions[0]['tokens'], $argument_assertions[1]['tokens'] ) + && count( $argument_assertions[0]['tokens'] ) === 1 + && count( $argument_assertions[1]['tokens'] ) === 1 + ) { + $single_context = $argument_assertions[0]; + $plural_context = $argument_assertions[1]; + + $this->compare_single_and_plural_arguments( $stack_ptr, $single_context, $plural_context ); + } + + if ( true === $this->check_translator_comments ) { + $this->check_for_translator_comment( $stack_ptr, $argument_assertions ); + } + } + + /** + * Check if supplied tokens represent a translation text string literal. + * + * @param array $context Context (@todo needs better description). + * @return bool + */ + protected function check_argument_tokens( $context ) { + $stack_ptr = $context['stack_ptr']; + $tokens = $context['tokens']; + $arg_name = $context['arg_name']; + $is_error = empty( $context['warning'] ); + $content = isset( $tokens[0] ) ? $tokens[0]['content'] : ''; + + if ( empty( $tokens ) || 0 === \count( $tokens ) ) { + $code = $this->string_to_errorcode( 'MissingArg' . ucfirst( $arg_name ) ); + if ( 'domain' !== $arg_name ) { + $this->addMessage( 'Missing $%s arg.', $stack_ptr, $is_error, $code, array( $arg_name ) ); + return false; + } + + // Ok, we're examining a text domain, now deal correctly with the 'default' text domain. + if ( true === $this->text_domain_is_default ) { + return true; + } + + if ( true === $this->text_domain_contains_default ) { + $this->phpcsFile->addWarning( + 'Missing $%s arg. If this text string is supposed to use a WP Core translation, use the "default" text domain.', + $stack_ptr, + $code . 'Default', + array( $arg_name ) + ); + } elseif ( ! empty( $this->text_domain ) ) { + $this->addMessage( 'Missing $%s arg.', $stack_ptr, $is_error, $code, array( $arg_name ) ); + } + + return false; + } + + if ( \count( $tokens ) > 1 ) { + $contents = ''; + foreach ( $tokens as $token ) { + $contents .= $token['content']; + } + $code = $this->string_to_errorcode( 'NonSingularStringLiteral' . ucfirst( $arg_name ) ); + $this->addMessage( 'The $%s arg must be a single string literal, not "%s".', $stack_ptr, $is_error, $code, array( $arg_name, $contents ) ); + return false; + } + + if ( \in_array( $arg_name, array( 'text', 'single', 'plural' ), true ) ) { + $this->check_text( $context ); + } + + if ( \T_DOUBLE_QUOTED_STRING === $tokens[0]['code'] || \T_HEREDOC === $tokens[0]['code'] ) { + $interpolated_variables = $this->get_interpolated_variables( $content ); + foreach ( $interpolated_variables as $interpolated_variable ) { + $code = $this->string_to_errorcode( 'InterpolatedVariable' . ucfirst( $arg_name ) ); + $this->addMessage( 'The $%s arg must not contain interpolated variables. Found "$%s".', $stack_ptr, $is_error, $code, array( $arg_name, $interpolated_variable ) ); + } + if ( ! empty( $interpolated_variables ) ) { + return false; + } + } + + if ( isset( Tokens::$textStringTokens[ $tokens[0]['code'] ] ) ) { + if ( 'domain' === $arg_name && ! empty( $this->text_domain ) ) { + $stripped_content = $this->strip_quotes( $content ); + + if ( ! \in_array( $stripped_content, $this->text_domain, true ) ) { + $this->addMessage( + 'Mismatched text domain. Expected \'%s\' but got %s.', + $stack_ptr, + $is_error, + 'TextDomainMismatch', + array( implode( "' or '", $this->text_domain ), $content ) + ); + return false; + } + + if ( true === $this->text_domain_is_default && 'default' === $stripped_content ) { + $fixable = false; + $error = 'No need to supply the text domain when the only accepted text domain is "default".'; + $error_code = 'SuperfluousDefaultTextDomain'; + + if ( $tokens[0]['token_index'] === $stack_ptr ) { + $prev = $this->phpcsFile->findPrevious( \T_WHITESPACE, ( $stack_ptr - 1 ), null, true ); + if ( false !== $prev && \T_COMMA === $this->tokens[ $prev ]['code'] ) { + $fixable = true; + } + } + + if ( false === $fixable ) { + $this->phpcsFile->addWarning( $error, $stack_ptr, $error_code ); + return false; + } + + $fix = $this->phpcsFile->addFixableWarning( $error, $stack_ptr, $error_code ); + if ( true === $fix ) { + // Remove preceeding comma, whitespace and the text domain token. + $this->phpcsFile->fixer->beginChangeset(); + for ( $i = $prev; $i <= $stack_ptr; $i++ ) { + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + $this->phpcsFile->fixer->endChangeset(); + } + + return false; + } + } + + return true; + } + + $code = $this->string_to_errorcode( 'NonSingularStringLiteral' . ucfirst( $arg_name ) ); + $this->addMessage( 'The $%s arg must be a single string literal, not "%s".', $stack_ptr, $is_error, $code, array( $arg_name, $content ) ); + return false; + } + + /** + * Check for inconsistencies between single and plural arguments. + * + * @param int $stack_ptr The position of the current token in the stack. + * @param array $single_context Single context (@todo needs better description). + * @param array $plural_context Plural context (@todo needs better description). + * @return void + */ + protected function compare_single_and_plural_arguments( $stack_ptr, $single_context, $plural_context ) { + $single_content = $single_context['tokens'][0]['content']; + $plural_content = $plural_context['tokens'][0]['content']; + + preg_match_all( self::SPRINTF_PLACEHOLDER_REGEX, $single_content, $single_placeholders ); + $single_placeholders = $single_placeholders[0]; + + preg_match_all( self::SPRINTF_PLACEHOLDER_REGEX, $plural_content, $plural_placeholders ); + $plural_placeholders = $plural_placeholders[0]; + + // English conflates "singular" with "only one", described in the codex: + // https://codex.wordpress.org/I18n_for_WordPress_Developers#Plurals . + if ( \count( $single_placeholders ) < \count( $plural_placeholders ) ) { + $error_string = 'Missing singular placeholder, needed for some languages. See https://codex.wordpress.org/I18n_for_WordPress_Developers#Plurals'; + $single_index = $single_context['tokens'][0]['token_index']; + + $this->phpcsFile->addError( $error_string, $single_index, 'MissingSingularPlaceholder' ); + } + + // Reordering is fine, but mismatched placeholders is probably wrong. + sort( $single_placeholders ); + sort( $plural_placeholders ); + + if ( $single_placeholders !== $plural_placeholders ) { + $this->phpcsFile->addWarning( 'Mismatched placeholders is probably an error', $stack_ptr, 'MismatchedPlaceholders' ); + } + } + + /** + * Check the string itself for problems. + * + * @param array $context Context (@todo needs better description). + * @return void + */ + protected function check_text( $context ) { + $stack_ptr = $context['stack_ptr']; + $arg_name = $context['arg_name']; + $content = $context['tokens'][0]['content']; + $is_error = empty( $context['warning'] ); + + // UnorderedPlaceholders: Check for multiple unordered placeholders. + $unordered_matches_count = preg_match_all( self::UNORDERED_SPRINTF_PLACEHOLDER_REGEX, $content, $unordered_matches ); + $unordered_matches = $unordered_matches[0]; + $all_matches_count = preg_match_all( self::SPRINTF_PLACEHOLDER_REGEX, $content, $all_matches ); + + if ( $unordered_matches_count > 0 && $unordered_matches_count !== $all_matches_count && $all_matches_count > 1 ) { + $code = $this->string_to_errorcode( 'MixedOrderedPlaceholders' . ucfirst( $arg_name ) ); + $this->phpcsFile->addError( + 'Multiple placeholders should be ordered. Mix of ordered and non-ordered placeholders found. Found: %s.', + $stack_ptr, + $code, + array( implode( ', ', $all_matches[0] ) ) + ); + + } elseif ( $unordered_matches_count >= 2 ) { + $code = $this->string_to_errorcode( 'UnorderedPlaceholders' . ucfirst( $arg_name ) ); + + $suggestions = array(); + $replace_regexes = array(); + $replacements = array(); + for ( $i = 0; $i < $unordered_matches_count; $i++ ) { + $to_insert = ( $i + 1 ); + $to_insert .= ( '"' !== $content[0] ) ? '$' : '\$'; + $suggestions[ $i ] = substr_replace( $unordered_matches[ $i ], $to_insert, 1, 0 ); + + // Prepare the strings for use a regex. + $replace_regexes[ $i ] = '`\Q' . $unordered_matches[ $i ] . '\E`'; + // Note: the initial \\ is a literal \, the four \ in the replacement translate to also to a literal \. + $replacements[ $i ] = str_replace( '\\', '\\\\', $suggestions[ $i ] ); + // Note: the $ needs escaping to prevent numeric sequences after the $ being interpreted as match replacements. + $replacements[ $i ] = str_replace( '$', '\\$', $replacements[ $i ] ); + } + + $fix = $this->addFixableMessage( + 'Multiple placeholders should be ordered. Expected \'%s\', but got %s.', + $stack_ptr, + $is_error, + $code, + array( implode( ', ', $suggestions ), implode( ', ', $unordered_matches ) ) + ); + + if ( true === $fix ) { + $fixed_str = preg_replace( $replace_regexes, $replacements, $content, 1 ); + + $this->phpcsFile->fixer->replaceToken( $stack_ptr, $fixed_str ); + } + } + + /* + * NoEmptyStrings. + * + * Strip placeholders and surrounding quotes. + */ + $content_without_quotes = trim( $this->strip_quotes( $content ) ); + $non_placeholder_content = preg_replace( self::SPRINTF_PLACEHOLDER_REGEX, '', $content_without_quotes ); + + if ( '' === $non_placeholder_content ) { + $this->phpcsFile->addError( 'Strings should have translatable content', $stack_ptr, 'NoEmptyStrings' ); + return; + } + + /* + * NoHtmlWrappedStrings + * + * Strip surrounding quotes. + */ + $reader = new \XMLReader(); + $reader->XML( $content_without_quotes, 'UTF-8', LIBXML_NOERROR | LIBXML_ERR_NONE | LIBXML_NOWARNING ); + + // Is the first node an HTML element? + if ( ! $reader->read() || \XMLReader::ELEMENT !== $reader->nodeType ) { + return; + } + + // If the opening HTML element includes placeholders in its attributes, we don't warn. + // E.g. ''. + $i = 0; + while ( $attr = $reader->getAttributeNo( $i ) ) { + if ( preg_match( self::SPRINTF_PLACEHOLDER_REGEX, $attr ) === 1 ) { + return; + } + + ++$i; + } + + // We don't flag strings wrapped in `...`, as the link target might actually need localization. + if ( 'a' === $reader->name && $reader->getAttribute( 'href' ) ) { + return; + } + + // Does the entire string only consist of this HTML node? + if ( $reader->readOuterXml() === $content_without_quotes ) { + $this->phpcsFile->addWarning( 'Strings should not be wrapped in HTML', $stack_ptr, 'NoHtmlWrappedStrings' ); + } + } + + /** + * Check for the presence of a translators comment if one of the text strings contains a placeholder. + * + * @param int $stack_ptr The position of the gettext call token in the stack. + * @param array $args The function arguments. + * @return void + */ + protected function check_for_translator_comment( $stack_ptr, $args ) { + foreach ( $args as $arg ) { + if ( false === \in_array( $arg['arg_name'], array( 'text', 'single', 'plural' ), true ) ) { + continue; + } + + if ( empty( $arg['tokens'] ) ) { + continue; + } + + foreach ( $arg['tokens'] as $token ) { + if ( empty( $token['content'] ) ) { + continue; + } + + if ( preg_match( self::SPRINTF_PLACEHOLDER_REGEX, $token['content'], $placeholders ) < 1 ) { + // No placeholders found. + continue; + } + + $previous_comment = $this->phpcsFile->findPrevious( Tokens::$commentTokens, ( $stack_ptr - 1 ) ); + + if ( false !== $previous_comment ) { + /* + * Check that the comment is either on the line before the gettext call or + * if it's not, that there is only whitespace between. + */ + $correctly_placed = false; + + if ( ( $this->tokens[ $previous_comment ]['line'] + 1 ) === $this->tokens[ $stack_ptr ]['line'] ) { + $correctly_placed = true; + } else { + $next_non_whitespace = $this->phpcsFile->findNext( \T_WHITESPACE, ( $previous_comment + 1 ), $stack_ptr, true ); + if ( false === $next_non_whitespace || $this->tokens[ $next_non_whitespace ]['line'] === $this->tokens[ $stack_ptr ]['line'] ) { + // No non-whitespace found or next non-whitespace is on same line as gettext call. + $correctly_placed = true; + } + unset( $next_non_whitespace ); + } + + /* + * Check that the comment starts with 'translators:'. + */ + if ( true === $correctly_placed ) { + + if ( \T_COMMENT === $this->tokens[ $previous_comment ]['code'] ) { + $comment_text = trim( $this->tokens[ $previous_comment ]['content'] ); + + // If it's multi-line /* */ comment, collect all the parts. + if ( '*/' === substr( $comment_text, -2 ) && '/*' !== substr( $comment_text, 0, 2 ) ) { + for ( $i = ( $previous_comment - 1 ); 0 <= $i; $i-- ) { + if ( \T_COMMENT !== $this->tokens[ $i ]['code'] ) { + break; + } + + $comment_text = trim( $this->tokens[ $i ]['content'] ) . $comment_text; + } + } + + if ( true === $this->is_translators_comment( $comment_text ) ) { + // Comment is ok. + return; + } + } elseif ( \T_DOC_COMMENT_CLOSE_TAG === $this->tokens[ $previous_comment ]['code'] ) { + // If it's docblock comment (wrong style) make sure that it's a translators comment. + $db_start = $this->phpcsFile->findPrevious( \T_DOC_COMMENT_OPEN_TAG, ( $previous_comment - 1 ) ); + $db_first_text = $this->phpcsFile->findNext( \T_DOC_COMMENT_STRING, ( $db_start + 1 ), $previous_comment ); + + if ( true === $this->is_translators_comment( $this->tokens[ $db_first_text ]['content'] ) ) { + $this->phpcsFile->addWarning( + 'A "translators:" comment must be a "/* */" style comment. Docblock comments will not be picked up by the tools to generate a ".pot" file.', + $stack_ptr, + 'TranslatorsCommentWrongStyle' + ); + return; + } + } + } + } + + // Found placeholders but no translators comment. + $this->phpcsFile->addWarning( + 'A gettext call containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.', + $stack_ptr, + 'MissingTranslatorsComment' + ); + return; + } + } + } + + /** + * Check if a (collated) comment string starts with 'translators:'. + * + * @since 0.11.0 + * + * @param string $content Comment string content. + * @return bool + */ + private function is_translators_comment( $content ) { + if ( preg_match( '`^(?:(?://|/\*{1,2}) )?translators:`i', $content, $matches ) === 1 ) { + return true; + } + return false; + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/PostsPerPageSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/PostsPerPageSniff.php new file mode 100644 index 00000000..968a0342 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/PostsPerPageSniff.php @@ -0,0 +1,78 @@ + array( + 'type' => 'warning', + 'keys' => array( + 'posts_per_page', + 'numberposts', + ), + ), + ); + } + + /** + * Callback to process each confirmed key, to check value. + * + * @param string $key Array index / key. + * @param mixed $val Assigned value. + * @param int $line Token line. + * @param array $group Group definition. + * @return mixed FALSE if no match, TRUE if matches, STRING if matches + * with custom error message passed to ->process(). + */ + public function callback( $key, $val, $line, $group ) { + $this->posts_per_page = (int) $this->posts_per_page; + + if ( $val > $this->posts_per_page ) { + return 'Detected high pagination limit, `%s` is set to `%s`'; + } + + return false; + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/TimezoneChangeSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/TimezoneChangeSniff.php new file mode 100644 index 00000000..f376c5e3 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/TimezoneChangeSniff.php @@ -0,0 +1,88 @@ + false, + 'FoundPropertyForDeprecatedSniff' => false, + ); + + /** + * Don't use. + * + * @deprecated 2.2.0 + * + * @return array + */ + public function getGroups() { + $groups = parent::getGroups(); + return array( 'timezone_change' => $groups['timezone_change'] ); + } + + /** + * Don't use. + * + * @since 2.2.0 Added to allow for throwing the deprecation notices. + * @deprecated 2.2.0 + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return void|int + */ + public function process_token( $stackPtr ) { + if ( false === $this->thrown['DeprecatedSniff'] ) { + $this->thrown['DeprecatedSniff'] = $this->phpcsFile->addWarning( + 'The "WordPress.WP.TimezoneChange" sniff has been deprecated. Use the "WordPress.DateTime.RestrictedFunctions" sniff instead. Please update your custom ruleset.', + 0, + 'DeprecatedSniff' + ); + } + + if ( ! empty( $this->exclude ) + && false === $this->thrown['FoundPropertyForDeprecatedSniff'] + ) { + $this->thrown['FoundPropertyForDeprecatedSniff'] = $this->phpcsFile->addWarning( + 'The "WordPress.WP.TimezoneChange" sniff has been deprecated. Use the "WordPress.DateTime.RestrictedFunctions" sniff instead. "exclude" property setting found. Please update your custom ruleset.', + 0, + 'FoundPropertyForDeprecatedSniff' + ); + } + + return parent::process_token( $stackPtr ); + } +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/CastStructureSpacingSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/CastStructureSpacingSniff.php new file mode 100755 index 00000000..41494b91 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/CastStructureSpacingSniff.php @@ -0,0 +1,62 @@ +tokens[ ( $stackPtr - 1 ) ]['code'] + && \T_ELLIPSIS !== $this->tokens[ ( $stackPtr - 1 ) ]['code'] + ) { + $error = 'No space before opening casting parenthesis is prohibited'; + $fix = $this->phpcsFile->addFixableError( $error, $stackPtr, 'NoSpaceBeforeOpenParenthesis' ); + if ( true === $fix ) { + $this->phpcsFile->fixer->addContentBefore( $stackPtr, ' ' ); + } + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/ControlStructureSpacingSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/ControlStructureSpacingSniff.php new file mode 100644 index 00000000..5f7b9a3c --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/ControlStructureSpacingSniff.php @@ -0,0 +1,572 @@ + true, + \T_CLOSURE => true, + \T_DO => true, + \T_ELSE => true, + \T_TRY => true, + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + return array( + \T_IF, + \T_WHILE, + \T_FOREACH, + \T_FOR, + \T_SWITCH, + \T_DO, + \T_ELSE, + \T_ELSEIF, + \T_FUNCTION, + \T_CLOSURE, + \T_USE, + \T_TRY, + \T_CATCH, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return void + */ + public function process_token( $stackPtr ) { + $this->spaces_before_closure_open_paren = (int) $this->spaces_before_closure_open_paren; + + if ( isset( $this->tokens[ ( $stackPtr + 1 ) ] ) && \T_WHITESPACE !== $this->tokens[ ( $stackPtr + 1 ) ]['code'] + && ! ( \T_ELSE === $this->tokens[ $stackPtr ]['code'] && \T_COLON === $this->tokens[ ( $stackPtr + 1 ) ]['code'] ) + && ! ( \T_CLOSURE === $this->tokens[ $stackPtr ]['code'] + && 0 >= $this->spaces_before_closure_open_paren ) + ) { + $error = 'Space after opening control structure is required'; + $fix = $this->phpcsFile->addFixableError( $error, $stackPtr, 'NoSpaceAfterStructureOpen' ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->addContent( $stackPtr, ' ' ); + } + } + + if ( ! isset( $this->tokens[ $stackPtr ]['scope_closer'] ) ) { + + if ( \T_USE === $this->tokens[ $stackPtr ]['code'] && 'closure' === $this->get_use_type( $stackPtr ) ) { + $scopeOpener = $this->phpcsFile->findNext( \T_OPEN_CURLY_BRACKET, ( $stackPtr + 1 ) ); + $scopeCloser = $this->tokens[ $scopeOpener ]['scope_closer']; + } elseif ( \T_WHILE !== $this->tokens[ $stackPtr ]['code'] ) { + return; + } + } else { + $scopeOpener = $this->tokens[ $stackPtr ]['scope_opener']; + $scopeCloser = $this->tokens[ $stackPtr ]['scope_closer']; + } + + // Alternative syntax. + if ( isset( $scopeOpener ) && \T_COLON === $this->tokens[ $scopeOpener ]['code'] ) { + + if ( 'required' === $this->space_before_colon ) { + + if ( \T_WHITESPACE !== $this->tokens[ ( $scopeOpener - 1 ) ]['code'] ) { + $error = 'Space between opening control structure and T_COLON is required'; + $fix = $this->phpcsFile->addFixableError( $error, $scopeOpener, 'NoSpaceBetweenStructureColon' ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->addContentBefore( $scopeOpener, ' ' ); + } + } + } elseif ( 'forbidden' === $this->space_before_colon ) { + + if ( \T_WHITESPACE === $this->tokens[ ( $scopeOpener - 1 ) ]['code'] ) { + $error = 'Extra space between opening control structure and T_COLON found'; + $fix = $this->phpcsFile->addFixableError( $error, ( $scopeOpener - 1 ), 'SpaceBetweenStructureColon' ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->replaceToken( ( $scopeOpener - 1 ), '' ); + } + } + } + } + + $parenthesisOpener = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + + // If this is a function declaration. + if ( \T_FUNCTION === $this->tokens[ $stackPtr ]['code'] ) { + + if ( \T_STRING === $this->tokens[ $parenthesisOpener ]['code'] ) { + + $function_name_ptr = $parenthesisOpener; + + } elseif ( \T_BITWISE_AND === $this->tokens[ $parenthesisOpener ]['code'] ) { + + // This function returns by reference (function &function_name() {}). + $parenthesisOpener = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + ( $parenthesisOpener + 1 ), + null, + true + ); + $function_name_ptr = $parenthesisOpener; + } + + if ( isset( $function_name_ptr ) ) { + $parenthesisOpener = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + ( $parenthesisOpener + 1 ), + null, + true + ); + + // Checking this: function my_function[*](...) {}. + if ( ( $function_name_ptr + 1 ) !== $parenthesisOpener ) { + + $error = 'Space between function name and opening parenthesis is prohibited.'; + $fix = $this->phpcsFile->addFixableError( + $error, + $stackPtr, + 'SpaceBeforeFunctionOpenParenthesis', + $this->tokens[ ( $function_name_ptr + 1 ) ]['content'] + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->replaceToken( ( $function_name_ptr + 1 ), '' ); + } + } + } + } elseif ( \T_CLOSURE === $this->tokens[ $stackPtr ]['code'] ) { + + // Check if there is a use () statement. + if ( isset( $this->tokens[ $parenthesisOpener ]['parenthesis_closer'] ) ) { + + $usePtr = $this->phpcsFile->findNext( + Tokens::$emptyTokens, + ( $this->tokens[ $parenthesisOpener ]['parenthesis_closer'] + 1 ), + null, + true, + null, + true + ); + + // If it is, we set that as the "scope opener". + if ( \T_USE === $this->tokens[ $usePtr ]['code'] ) { + $scopeOpener = $usePtr; + } + } + } + + if ( \T_COLON !== $this->tokens[ $parenthesisOpener ]['code'] + && \T_FUNCTION !== $this->tokens[ $stackPtr ]['code'] + ) { + + if ( \T_CLOSURE === $this->tokens[ $stackPtr ]['code'] + && 0 === $this->spaces_before_closure_open_paren + ) { + + if ( ( $stackPtr + 1 ) !== $parenthesisOpener ) { + // Checking this: function[*](...) {}. + $error = 'Space before closure opening parenthesis is prohibited'; + $fix = $this->phpcsFile->addFixableError( $error, $stackPtr, 'SpaceBeforeClosureOpenParenthesis' ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->replaceToken( ( $stackPtr + 1 ), '' ); + } + } + } elseif ( + ( + \T_CLOSURE !== $this->tokens[ $stackPtr ]['code'] + || 1 === $this->spaces_before_closure_open_paren + ) + && ( $stackPtr + 1 ) === $parenthesisOpener + ) { + + // Checking this: if[*](...) {}. + $error = 'No space before opening parenthesis is prohibited'; + $fix = $this->phpcsFile->addFixableError( $error, $stackPtr, 'NoSpaceBeforeOpenParenthesis' ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->addContent( $stackPtr, ' ' ); + } + } + } + + if ( \T_WHITESPACE === $this->tokens[ ( $stackPtr + 1 ) ]['code'] + && ' ' !== $this->tokens[ ( $stackPtr + 1 ) ]['content'] + ) { + // Checking this: if [*](...) {}. + $error = 'Expected exactly one space before opening parenthesis; "%s" found.'; + $fix = $this->phpcsFile->addFixableError( + $error, + $stackPtr, + 'ExtraSpaceBeforeOpenParenthesis', + $this->tokens[ ( $stackPtr + 1 ) ]['content'] + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->replaceToken( ( $stackPtr + 1 ), ' ' ); + } + } + + if ( \T_CLOSE_PARENTHESIS !== $this->tokens[ ( $parenthesisOpener + 1 ) ]['code'] ) { + if ( \T_WHITESPACE !== $this->tokens[ ( $parenthesisOpener + 1 ) ]['code'] ) { + // Checking this: $value = my_function([*]...). + $error = 'No space after opening parenthesis is prohibited'; + $fix = $this->phpcsFile->addFixableError( $error, $stackPtr, 'NoSpaceAfterOpenParenthesis' ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->addContent( $parenthesisOpener, ' ' ); + } + } elseif ( ( ' ' !== $this->tokens[ ( $parenthesisOpener + 1 ) ]['content'] + && "\n" !== $this->tokens[ ( $parenthesisOpener + 1 ) ]['content'] + && "\r\n" !== $this->tokens[ ( $parenthesisOpener + 1 ) ]['content'] ) + && ! isset( $this->ignore_extra_space_after_open_paren[ $this->tokens[ $stackPtr ]['code'] ] ) + ) { + // Checking this: if ([*]...) {}. + $error = 'Expected exactly one space after opening parenthesis; "%s" found.'; + $fix = $this->phpcsFile->addFixableError( + $error, + $stackPtr, + 'ExtraSpaceAfterOpenParenthesis', + $this->tokens[ ( $parenthesisOpener + 1 ) ]['content'] + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->replaceToken( ( $parenthesisOpener + 1 ), ' ' ); + } + } + } + + if ( isset( $this->tokens[ $parenthesisOpener ]['parenthesis_closer'] ) ) { + + $parenthesisCloser = $this->tokens[ $parenthesisOpener ]['parenthesis_closer']; + + if ( \T_CLOSE_PARENTHESIS !== $this->tokens[ ( $parenthesisOpener + 1 ) ]['code'] ) { + + // Checking this: if (...[*]) {}. + if ( \T_WHITESPACE !== $this->tokens[ ( $parenthesisCloser - 1 ) ]['code'] ) { + $error = 'No space before closing parenthesis is prohibited'; + $fix = $this->phpcsFile->addFixableError( $error, $parenthesisCloser, 'NoSpaceBeforeCloseParenthesis' ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->addContentBefore( $parenthesisCloser, ' ' ); + } + } elseif ( ' ' !== $this->tokens[ ( $parenthesisCloser - 1 ) ]['content'] ) { + $prevNonEmpty = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $parenthesisCloser - 1 ), null, true ); + if ( $this->tokens[ ( $parenthesisCloser ) ]['line'] === $this->tokens[ ( $prevNonEmpty + 1 ) ]['line'] ) { + $error = 'Expected exactly one space before closing parenthesis; "%s" found.'; + $fix = $this->phpcsFile->addFixableError( + $error, + $stackPtr, + 'ExtraSpaceBeforeCloseParenthesis', + $this->tokens[ ( $parenthesisCloser - 1 ) ]['content'] + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->replaceToken( ( $parenthesisCloser - 1 ), ' ' ); + } + } + } + + if ( \T_WHITESPACE !== $this->tokens[ ( $parenthesisCloser + 1 ) ]['code'] + && ! ( // Do NOT flag : immediately following ) for return types declarations. + \T_COLON === $this->tokens[ ( $parenthesisCloser + 1 ) ]['code'] + && ( isset( $this->tokens[ $parenthesisCloser ]['parenthesis_owner'] ) === false + || in_array( $this->tokens[ $this->tokens[ $parenthesisCloser ]['parenthesis_owner'] ]['code'], array( \T_FUNCTION, \T_CLOSURE ), true ) ) + ) + && ( isset( $scopeOpener ) && \T_COLON !== $this->tokens[ $scopeOpener ]['code'] ) + ) { + $error = 'Space between opening control structure and closing parenthesis is required'; + $fix = $this->phpcsFile->addFixableError( $error, $scopeOpener, 'NoSpaceAfterCloseParenthesis' ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->addContentBefore( $scopeOpener, ' ' ); + } + } + } + + // Ignore this for function declarations. Handled by the OpeningFunctionBraceKernighanRitchie sniff. + if ( \T_FUNCTION !== $this->tokens[ $stackPtr ]['code'] + && \T_CLOSURE !== $this->tokens[ $stackPtr ]['code'] + && isset( $this->tokens[ $parenthesisOpener ]['parenthesis_owner'] ) + && ( isset( $scopeOpener ) + && $this->tokens[ $parenthesisCloser ]['line'] !== $this->tokens[ $scopeOpener ]['line'] ) + ) { + $error = 'Opening brace should be on the same line as the declaration'; + $fix = $this->phpcsFile->addFixableError( $error, $parenthesisOpener, 'OpenBraceNotSameLine' ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + + for ( $i = ( $parenthesisCloser + 1 ); $i < $scopeOpener; $i++ ) { + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + + $this->phpcsFile->fixer->addContent( $parenthesisCloser, ' ' ); + $this->phpcsFile->fixer->endChangeset(); + } + return; + + } elseif ( \T_WHITESPACE === $this->tokens[ ( $parenthesisCloser + 1 ) ]['code'] + && ' ' !== $this->tokens[ ( $parenthesisCloser + 1 ) ]['content'] + ) { + + // Checking this: if (...) [*]{}. + $error = 'Expected exactly one space between closing parenthesis and opening control structure; "%s" found.'; + $fix = $this->phpcsFile->addFixableError( + $error, + $stackPtr, + 'ExtraSpaceAfterCloseParenthesis', + $this->tokens[ ( $parenthesisCloser + 1 ) ]['content'] + ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->replaceToken( ( $parenthesisCloser + 1 ), ' ' ); + } + } + } + + if ( false !== $this->blank_line_check && isset( $scopeOpener ) ) { + $firstContent = $this->phpcsFile->findNext( \T_WHITESPACE, ( $scopeOpener + 1 ), null, true ); + + // We ignore spacing for some structures that tend to have their own rules. + $ignore = array( + \T_FUNCTION => true, + \T_CLOSURE => true, + \T_DOC_COMMENT_OPEN_TAG => true, + \T_CLOSE_TAG => true, + \T_COMMENT => true, + ); + $ignore += Tokens::$ooScopeTokens; + + if ( ! isset( $ignore[ $this->tokens[ $firstContent ]['code'] ] ) + && $this->tokens[ $firstContent ]['line'] > ( $this->tokens[ $scopeOpener ]['line'] + 1 ) + ) { + $error = 'Blank line found at start of control structure'; + $fix = $this->phpcsFile->addFixableError( $error, $scopeOpener, 'BlankLineAfterStart' ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + + for ( $i = ( $scopeOpener + 1 ); $i < $firstContent; $i++ ) { + if ( $this->tokens[ $i ]['line'] === $this->tokens[ $firstContent ]['line'] ) { + break; + } + $this->phpcsFile->fixer->replaceToken( $i, '' ); + } + + $this->phpcsFile->fixer->addNewline( $scopeOpener ); + $this->phpcsFile->fixer->endChangeset(); + } + } + + if ( $firstContent !== $scopeCloser ) { + $lastContent = $this->phpcsFile->findPrevious( \T_WHITESPACE, ( $scopeCloser - 1 ), null, true ); + + $lastNonEmptyContent = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $scopeCloser - 1 ), null, true ); + + $checkToken = $lastContent; + if ( isset( $this->tokens[ $lastNonEmptyContent ]['scope_condition'] ) ) { + $checkToken = $this->tokens[ $lastNonEmptyContent ]['scope_condition']; + } + + if ( ! isset( $ignore[ $this->tokens[ $checkToken ]['code'] ] ) + && $this->tokens[ $lastContent ]['line'] <= ( $this->tokens[ $scopeCloser ]['line'] - 2 ) + ) { + for ( $i = ( $scopeCloser - 1 ); $i > $lastContent; $i-- ) { + if ( $this->tokens[ $i ]['line'] < $this->tokens[ $scopeCloser ]['line'] + && \T_OPEN_TAG !== $this->tokens[ $firstContent ]['code'] + ) { + // TODO: Reporting error at empty line won't highlight it in IDE. + $error = 'Blank line found at end of control structure'; + $fix = $this->phpcsFile->addFixableError( $error, $i, 'BlankLineBeforeEnd' ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + + for ( $j = ( $lastContent + 1 ); $j < $scopeCloser; $j++ ) { + if ( $this->tokens[ $j ]['line'] === $this->tokens[ $scopeCloser ]['line'] ) { + break; + } + $this->phpcsFile->fixer->replaceToken( $j, '' ); + } + + /* + * PHPCS annotations, like normal inline comments, are tokenized including + * the new line at the end, so don't add any extra as it would cause a fixer + * conflict. + */ + if ( \T_COMMENT !== $this->tokens[ $lastContent ]['code'] + && ! isset( Tokens::$phpcsCommentTokens[ $this->tokens[ $lastContent ]['code'] ] ) ) { + $this->phpcsFile->fixer->addNewlineBefore( $j ); + } + + $this->phpcsFile->fixer->endChangeset(); + } + break; + } + } + } + } + unset( $ignore ); + } + + if ( ! isset( $scopeCloser ) || true !== $this->blank_line_after_check ) { + return; + } + + // {@internal This is just for the blank line check. Only whitespace should be considered, + // not "other" empty tokens.}} + $trailingContent = $this->phpcsFile->findNext( \T_WHITESPACE, ( $scopeCloser + 1 ), null, true ); + if ( false === $trailingContent ) { + return; + } + + if ( \T_COMMENT === $this->tokens[ $trailingContent ]['code'] + || isset( Tokens::$phpcsCommentTokens[ $this->tokens[ $trailingContent ]['code'] ] ) + ) { + // Special exception for code where the comment about + // an ELSE or ELSEIF is written between the control structures. + $nextCode = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $scopeCloser + 1 ), null, true ); + + if ( \T_ELSE === $this->tokens[ $nextCode ]['code'] || \T_ELSEIF === $this->tokens[ $nextCode ]['code'] ) { + $trailingContent = $nextCode; + } + + // Move past end comments. + if ( $this->tokens[ $trailingContent ]['line'] === $this->tokens[ $scopeCloser ]['line'] ) { + if ( preg_match( '`^//[ ]?end`i', $this->tokens[ $trailingContent ]['content'], $matches ) > 0 ) { + $scopeCloser = $trailingContent; + $trailingContent = $this->phpcsFile->findNext( \T_WHITESPACE, ( $trailingContent + 1 ), null, true ); + } + } + } + + if ( \T_ELSE === $this->tokens[ $trailingContent ]['code'] && \T_IF === $this->tokens[ $stackPtr ]['code'] ) { + // IF with ELSE. + return; + } + + if ( \T_WHILE === $this->tokens[ $trailingContent ]['code'] && \T_DO === $this->tokens[ $stackPtr ]['code'] ) { + // DO with WHILE. + return; + } + + if ( \T_CLOSE_TAG === $this->tokens[ $trailingContent ]['code'] ) { + // At the end of the script or embedded code. + return; + } + + if ( isset( $this->tokens[ $trailingContent ]['scope_condition'] ) + && \T_CLOSE_CURLY_BRACKET === $this->tokens[ $trailingContent ]['code'] + ) { + // Another control structure's closing brace. + $owner = $this->tokens[ $trailingContent ]['scope_condition']; + if ( \in_array( $this->tokens[ $owner ]['code'], array( \T_FUNCTION, \T_CLOSURE, \T_CLASS, \T_ANON_CLASS, \T_INTERFACE, \T_TRAIT ), true ) ) { + // The next content is the closing brace of a function, class, interface or trait + // so normal function/class rules apply and we can ignore it. + return; + } + + if ( ( $this->tokens[ $scopeCloser ]['line'] + 1 ) !== $this->tokens[ $trailingContent ]['line'] ) { + // TODO: Won't cover following case: "} echo 'OK';". + $error = 'Blank line found after control structure'; + $fix = $this->phpcsFile->addFixableError( $error, $scopeCloser, 'BlankLineAfterEnd' ); + + if ( true === $fix ) { + $this->phpcsFile->fixer->beginChangeset(); + + $i = ( $scopeCloser + 1 ); + while ( $this->tokens[ $i ]['line'] !== $this->tokens[ $trailingContent ]['line'] ) { + $this->phpcsFile->fixer->replaceToken( $i, '' ); + $i++; + } + + // TODO: Instead a separate error should be triggered when content comes right after closing brace. + if ( \T_COMMENT !== $this->tokens[ $scopeCloser ]['code'] + && isset( Tokens::$phpcsCommentTokens[ $this->tokens[ $scopeCloser ]['code'] ] ) === false + ) { + $this->phpcsFile->fixer->addNewlineBefore( $trailingContent ); + } + $this->phpcsFile->fixer->endChangeset(); + } + } + } + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/DisallowInlineTabsSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/DisallowInlineTabsSniff.php new file mode 100644 index 00000000..c20a4d33 --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/DisallowInlineTabsSniff.php @@ -0,0 +1,104 @@ +tab_width ) ) { + $this->tab_width = PHPCSHelper::get_tab_width( $this->phpcsFile ); + } + + $check_tokens = array( + \T_WHITESPACE => true, + \T_DOC_COMMENT_WHITESPACE => true, + \T_DOC_COMMENT_STRING => true, + ); + + for ( $i = ( $stackPtr + 1 ); $i < $this->phpcsFile->numTokens; $i++ ) { + // Skip all non-whitespace tokens and skip whitespace at the start of a new line. + if ( ! isset( $check_tokens[ $this->tokens[ $i ]['code'] ] ) || 1 === $this->tokens[ $i ]['column'] ) { + continue; + } + + // If tabs are being converted to spaces by the tokenizer, the + // original content should be checked instead of the converted content. + if ( isset( $this->tokens[ $i ]['orig_content'] ) ) { + $content = $this->tokens[ $i ]['orig_content']; + } else { + $content = $this->tokens[ $i ]['content']; + } + + if ( '' === $content || strpos( $content, "\t" ) === false ) { + continue; + } + + $fix = $this->phpcsFile->addFixableError( + 'Spaces must be used for mid-line alignment; tabs are not allowed', + $i, + 'NonIndentTabsUsed' + ); + if ( true === $fix ) { + if ( isset( $this->tokens[ $i ]['orig_content'] ) ) { + // Use the replacement that PHPCS has already done. + $this->phpcsFile->fixer->replaceToken( $i, $this->tokens[ $i ]['content'] ); + } else { + // Replace tabs with spaces, using an indent of $tab_width. + // Other sniffs can then correct the indent if they need to. + $spaces = str_repeat( ' ', $this->tab_width ); + $newContent = str_replace( "\t", $spaces, $this->tokens[ $i ]['content'] ); + $this->phpcsFile->fixer->replaceToken( $i, $newContent ); + } + } + } + + // Ignore the rest of the file. + return ( $this->phpcsFile->numTokens + 1 ); + } + +} diff --git a/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/OperatorSpacingSniff.php b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/OperatorSpacingSniff.php new file mode 100644 index 00000000..17e5090c --- /dev/null +++ b/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WhiteSpace/OperatorSpacingSniff.php @@ -0,0 +1,64 @@ + + * + * + * + * + * + * + * + * + * @var array + */ + public $ignoreAlignmentTokens = array(); + + /** + * The --tab-width CLI value that is being used. + * + * @var int + */ + private $tab_width; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + return array( + \T_OPEN_TAG, + \T_OPEN_TAG_WITH_ECHO, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param int $stackPtr The position of the current token in the stack. + * + * @return int Integer stack pointer to skip the rest of the file. + */ + public function process_token( $stackPtr ) { + if ( ! isset( $this->tab_width ) ) { + $this->tab_width = PHPCSHelper::get_tab_width( $this->phpcsFile ); + } + + // Handle any custom ignore tokens received from a ruleset. + $ignoreAlignmentTokens = $this->merge_custom_array( $this->ignoreAlignmentTokens ); + + $check_tokens = array( + \T_WHITESPACE => true, + \T_INLINE_HTML => true, + \T_DOC_COMMENT_WHITESPACE => true, + \T_COMMENT => true, + ); + $check_tokens += Tokens::$phpcsCommentTokens; + + for ( $i = 0; $i < $this->phpcsFile->numTokens; $i++ ) { + + if ( 1 !== $this->tokens[ $i ]['column'] ) { + continue; + } elseif ( isset( $check_tokens[ $this->tokens[ $i ]['code'] ] ) === false + || ( isset( $this->tokens[ ( $i + 1 ) ] ) + && \T_WHITESPACE === $this->tokens[ ( $i + 1 ) ]['code'] ) + || $this->tokens[ $i ]['content'] === $this->phpcsFile->eolChar + || isset( $ignoreAlignmentTokens[ $this->tokens[ $i ]['type'] ] ) + || ( isset( $this->tokens[ ( $i + 1 ) ] ) + && isset( $ignoreAlignmentTokens[ $this->tokens[ ( $i + 1 ) ]['type'] ] ) ) + ) { + continue; + } + + $spaces = 0; + switch ( $this->tokens[ $i ]['type'] ) { + case 'T_WHITESPACE': + $spaces = ( $this->tokens[ $i ]['length'] % $this->tab_width ); + break; + + case 'T_DOC_COMMENT_WHITESPACE': + $length = $this->tokens[ $i ]['length']; + $spaces = ( $length % $this->tab_width ); + + if ( isset( $this->tokens[ ( $i + 1 ) ] ) + && ( \T_DOC_COMMENT_STAR === $this->tokens[ ( $i + 1 ) ]['code'] + || \T_DOC_COMMENT_CLOSE_TAG === $this->tokens[ ( $i + 1 ) ]['code'] ) + && 0 !== $spaces + ) { + // One alignment space expected before the *. + --$spaces; + } + break; + + case 'T_COMMENT': + case 'T_PHPCS_ENABLE': + case 'T_PHPCS_DISABLE': + case 'T_PHPCS_SET': + case 'T_PHPCS_IGNORE': + case 'T_PHPCS_IGNORE_FILE': + /* + * Indentation whitespace for subsequent lines of multi-line comments + * are tokenized as part of the comment. + */ + $comment = ltrim( $this->tokens[ $i ]['content'] ); + $whitespace = str_replace( $comment, '', $this->tokens[ $i ]['content'] ); + $length = \strlen( $whitespace ); + $spaces = ( $length % $this->tab_width ); + + if ( isset( $comment[0] ) && '*' === $comment[0] && 0 !== $spaces ) { + --$spaces; + } + break; + + case 'T_INLINE_HTML': + if ( $this->tokens[ $i ]['content'] === $this->phpcsFile->eolChar ) { + $spaces = 0; + } else { + /* + * Indentation whitespace for inline HTML is part of the T_INLINE_HTML token. + */ + $content = ltrim( $this->tokens[ $i ]['content'] ); + $whitespace = str_replace( $content, '', $this->tokens[ $i ]['content'] ); + $spaces = ( \strlen( $whitespace ) % $this->tab_width ); + } + + /* + * Prevent triggering on multi-line /*-style inline javascript comments. + * This may cause false negatives as there is no check for being in a + *