chore: fix code sniffer

This commit is contained in:
grandeljay 2023-09-19 19:26:46 +02:00
parent 78239a69d0
commit a7bf23154c
282 changed files with 52726 additions and 94 deletions

View file

@ -7,5 +7,14 @@
"gettext/translator": "^1.1",
"jaybizzle/crawler-detect": "^1.2",
"slim/psr7": "^1.6"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
"wp-coding-standards/wpcs": "^3.0"
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
}
}

368
composer.lock generated
View file

@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "e9f274cb680a99cb3a744f784d9c166d",
"content-hash": "13979facffb09d73589c505f59cc11d5",
"packages": [
{
"name": "composer/ca-bundle",
"version": "1.3.6",
"version": "1.3.7",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
"reference": "90d087e988ff194065333d16bc5cf649872d9cdb"
"reference": "76e46335014860eec1aa5a724799a00a2e47cc85"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/90d087e988ff194065333d16bc5cf649872d9cdb",
"reference": "90d087e988ff194065333d16bc5cf649872d9cdb",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/76e46335014860eec1aa5a724799a00a2e47cc85",
"reference": "76e46335014860eec1aa5a724799a00a2e47cc85",
"shasum": ""
},
"require": {
@ -64,7 +64,7 @@
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/ca-bundle/issues",
"source": "https://github.com/composer/ca-bundle/tree/1.3.6"
"source": "https://github.com/composer/ca-bundle/tree/1.3.7"
},
"funding": [
{
@ -80,7 +80,7 @@
"type": "tidelift"
}
],
"time": "2023-06-06T12:02:59+00:00"
"time": "2023-08-30T09:31:38+00:00"
},
{
"name": "embed/embed",
@ -1031,16 +1031,16 @@
},
{
"name": "symfony/polyfill-php80",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"shasum": ""
},
"require": {
@ -1049,7 +1049,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -1094,7 +1094,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
},
"funding": [
{
@ -1110,10 +1110,348 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
}
],
"packages-dev": [
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/composer-installer.git",
"reference": "4be43904336affa5c2f70744a348312336afd0da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da",
"reference": "4be43904336affa5c2f70744a348312336afd0da",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0 || ^2.0",
"php": ">=5.4",
"squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
},
"require-dev": {
"composer/composer": "*",
"ext-json": "*",
"ext-zip": "*",
"php-parallel-lint/php-parallel-lint": "^1.3.1",
"phpcompatibility/php-compatibility": "^9.0",
"yoast/phpunit-polyfills": "^1.0"
},
"type": "composer-plugin",
"extra": {
"class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
},
"autoload": {
"psr-4": {
"PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Franck Nijhof",
"email": "franck.nijhof@dealerdirect.com",
"homepage": "http://www.frenck.nl",
"role": "Developer / IT Manager"
},
{
"name": "Contributors",
"homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
}
],
"description": "PHP_CodeSniffer Standards Composer Installer Plugin",
"homepage": "http://www.dealerdirect.com",
"keywords": [
"PHPCodeSniffer",
"PHP_CodeSniffer",
"code quality",
"codesniffer",
"composer",
"installer",
"phpcbf",
"phpcs",
"plugin",
"qa",
"quality",
"standard",
"standards",
"style guide",
"stylecheck",
"tests"
],
"support": {
"issues": "https://github.com/PHPCSStandards/composer-installer/issues",
"source": "https://github.com/PHPCSStandards/composer-installer"
},
"time": "2023-01-05T11:28:13+00:00"
},
{
"name": "phpcsstandards/phpcsextra",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHPCSExtra.git",
"reference": "98bcdbacbda14b1db85f710b1853125726795bbc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/98bcdbacbda14b1db85f710b1853125726795bbc",
"reference": "98bcdbacbda14b1db85f710b1853125726795bbc",
"shasum": ""
},
"require": {
"php": ">=5.4",
"phpcsstandards/phpcsutils": "^1.0.8",
"squizlabs/php_codesniffer": "^3.7.1"
},
"require-dev": {
"php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcsstandards/phpcsdevcs": "^1.1.6",
"phpcsstandards/phpcsdevtools": "^1.2.1",
"phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0"
},
"type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
"dev-stable": "1.x-dev",
"dev-develop": "1.x-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "Juliette Reinders Folmer",
"homepage": "https://github.com/jrfnl",
"role": "lead"
},
{
"name": "Contributors",
"homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors"
}
],
"description": "A collection of sniffs and standards for use with PHP_CodeSniffer.",
"keywords": [
"PHP_CodeSniffer",
"phpcbf",
"phpcodesniffer-standard",
"phpcs",
"standards",
"static analysis"
],
"support": {
"issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues",
"source": "https://github.com/PHPCSStandards/PHPCSExtra"
},
"time": "2023-08-26T04:46:45+00:00"
},
{
"name": "phpcsstandards/phpcsutils",
"version": "1.0.8",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHPCSUtils.git",
"reference": "69465cab9d12454e5e7767b9041af0cd8cd13be7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/69465cab9d12454e5e7767b9041af0cd8cd13be7",
"reference": "69465cab9d12454e5e7767b9041af0cd8cd13be7",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0",
"php": ">=5.4",
"squizlabs/php_codesniffer": "^3.7.1 || 4.0.x-dev@dev"
},
"require-dev": {
"ext-filter": "*",
"php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcsstandards/phpcsdevcs": "^1.1.6",
"yoast/phpunit-polyfills": "^1.0.5 || ^2.0.0"
},
"type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
"dev-stable": "1.x-dev",
"dev-develop": "1.x-dev"
}
},
"autoload": {
"classmap": [
"PHPCSUtils/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "Juliette Reinders Folmer",
"homepage": "https://github.com/jrfnl",
"role": "lead"
},
{
"name": "Contributors",
"homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors"
}
],
"description": "A suite of utility functions for use with PHP_CodeSniffer",
"homepage": "https://phpcsutils.com/",
"keywords": [
"PHP_CodeSniffer",
"phpcbf",
"phpcodesniffer-standard",
"phpcs",
"phpcs3",
"standards",
"static analysis",
"tokens",
"utility"
],
"support": {
"docs": "https://phpcsutils.com/",
"issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues",
"source": "https://github.com/PHPCSStandards/PHPCSUtils"
},
"time": "2023-07-16T21:39:41+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"
},
{
"name": "wp-coding-standards/wpcs",
"version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
"reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/b4caf9689f1a0e4a4c632679a44e638c1c67aff1",
"reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1",
"shasum": ""
},
"require": {
"ext-filter": "*",
"ext-libxml": "*",
"ext-tokenizer": "*",
"ext-xmlreader": "*",
"php": ">=5.4",
"phpcsstandards/phpcsextra": "^1.1.0",
"phpcsstandards/phpcsutils": "^1.0.8",
"squizlabs/php_codesniffer": "^3.7.2"
},
"require-dev": {
"php-parallel-lint/php-console-highlighter": "^1.0.0",
"php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcompatibility/php-compatibility": "^9.0",
"phpcsstandards/phpcsdevtools": "^1.2.0",
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"suggest": {
"ext-iconv": "For improved results",
"ext-mbstring": "For improved results"
},
"type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Contributors",
"homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors"
}
],
"description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
"keywords": [
"phpcs",
"standards",
"static analysis",
"wordpress"
],
"support": {
"issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues",
"source": "https://github.com/WordPress/WordPress-Coding-Standards",
"wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki"
},
"funding": [
{
"url": "https://opencollective.com/thewpcc/contribute/wp-php-63406",
"type": "custom"
}
],
"time": "2023-09-14T07:06:09+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],

View file

@ -8,6 +8,43 @@ $baseDir = dirname($vendorDir);
return array(
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'PHPCSUtils\\AbstractSniffs\\AbstractArrayDeclarationSniff' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/AbstractSniffs/AbstractArrayDeclarationSniff.php',
'PHPCSUtils\\BackCompat\\BCFile' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCFile.php',
'PHPCSUtils\\BackCompat\\BCTokens' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCTokens.php',
'PHPCSUtils\\BackCompat\\Helper' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/Helper.php',
'PHPCSUtils\\Exceptions\\InvalidTokenArray' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/InvalidTokenArray.php',
'PHPCSUtils\\Exceptions\\TestFileNotFound' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestFileNotFound.php',
'PHPCSUtils\\Exceptions\\TestMarkerNotFound' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestMarkerNotFound.php',
'PHPCSUtils\\Exceptions\\TestTargetNotFound' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestTargetNotFound.php',
'PHPCSUtils\\Fixers\\SpacesFixer' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Fixers/SpacesFixer.php',
'PHPCSUtils\\Internal\\Cache' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/Cache.php',
'PHPCSUtils\\Internal\\IsShortArrayOrList' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/IsShortArrayOrList.php',
'PHPCSUtils\\Internal\\IsShortArrayOrListWithCache' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/IsShortArrayOrListWithCache.php',
'PHPCSUtils\\Internal\\NoFileCache' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/NoFileCache.php',
'PHPCSUtils\\Internal\\StableCollections' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/StableCollections.php',
'PHPCSUtils\\TestUtils\\UtilityMethodTestCase' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/TestUtils/UtilityMethodTestCase.php',
'PHPCSUtils\\Tokens\\Collections' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Tokens/Collections.php',
'PHPCSUtils\\Tokens\\TokenHelper' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Tokens/TokenHelper.php',
'PHPCSUtils\\Utils\\Arrays' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Arrays.php',
'PHPCSUtils\\Utils\\Conditions' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Conditions.php',
'PHPCSUtils\\Utils\\Context' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Context.php',
'PHPCSUtils\\Utils\\ControlStructures' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/ControlStructures.php',
'PHPCSUtils\\Utils\\FunctionDeclarations' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/FunctionDeclarations.php',
'PHPCSUtils\\Utils\\GetTokensAsString' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/GetTokensAsString.php',
'PHPCSUtils\\Utils\\Lists' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Lists.php',
'PHPCSUtils\\Utils\\MessageHelper' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/MessageHelper.php',
'PHPCSUtils\\Utils\\Namespaces' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Namespaces.php',
'PHPCSUtils\\Utils\\NamingConventions' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/NamingConventions.php',
'PHPCSUtils\\Utils\\Numbers' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Numbers.php',
'PHPCSUtils\\Utils\\ObjectDeclarations' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/ObjectDeclarations.php',
'PHPCSUtils\\Utils\\Operators' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Operators.php',
'PHPCSUtils\\Utils\\Orthography' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Orthography.php',
'PHPCSUtils\\Utils\\Parentheses' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Parentheses.php',
'PHPCSUtils\\Utils\\PassedParameters' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/PassedParameters.php',
'PHPCSUtils\\Utils\\Scopes' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Scopes.php',
'PHPCSUtils\\Utils\\TextStrings' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/TextStrings.php',
'PHPCSUtils\\Utils\\UseStatements' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/UseStatements.php',
'PHPCSUtils\\Utils\\Variables' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Variables.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',

View file

@ -11,6 +11,7 @@ return array(
'Qferrer\\Mjml\\' => array($vendorDir . '/qferr/mjml-php/src'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
'PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => array($vendorDir . '/dealerdirect/phpcodesniffer-composer-installer/src'),
'ML\\JsonLD\\' => array($vendorDir . '/ml/json-ld'),
'Jaybizzle\\CrawlerDetect\\' => array($vendorDir . '/jaybizzle/crawler-detect/src'),
'HtmlParser\\' => array($vendorDir . '/oscarotero/html-parser/src'),

View file

@ -26,6 +26,7 @@ class ComposerStaticInit5f3db9fc1d0cf1dd6a77a1d84501b4b1
array (
'Psr\\Http\\Message\\' => 17,
'Psr\\Http\\Client\\' => 16,
'PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => 57,
),
'M' =>
array (
@ -81,6 +82,10 @@ class ComposerStaticInit5f3db9fc1d0cf1dd6a77a1d84501b4b1
array (
0 => __DIR__ . '/..' . '/psr/http-client/src',
),
'PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' =>
array (
0 => __DIR__ . '/..' . '/dealerdirect/phpcodesniffer-composer-installer/src',
),
'ML\\JsonLD\\' =>
array (
0 => __DIR__ . '/..' . '/ml/json-ld',
@ -133,6 +138,43 @@ class ComposerStaticInit5f3db9fc1d0cf1dd6a77a1d84501b4b1
public static $classMap = array (
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'PHPCSUtils\\AbstractSniffs\\AbstractArrayDeclarationSniff' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/AbstractSniffs/AbstractArrayDeclarationSniff.php',
'PHPCSUtils\\BackCompat\\BCFile' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCFile.php',
'PHPCSUtils\\BackCompat\\BCTokens' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCTokens.php',
'PHPCSUtils\\BackCompat\\Helper' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/Helper.php',
'PHPCSUtils\\Exceptions\\InvalidTokenArray' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/InvalidTokenArray.php',
'PHPCSUtils\\Exceptions\\TestFileNotFound' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestFileNotFound.php',
'PHPCSUtils\\Exceptions\\TestMarkerNotFound' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestMarkerNotFound.php',
'PHPCSUtils\\Exceptions\\TestTargetNotFound' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestTargetNotFound.php',
'PHPCSUtils\\Fixers\\SpacesFixer' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Fixers/SpacesFixer.php',
'PHPCSUtils\\Internal\\Cache' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/Cache.php',
'PHPCSUtils\\Internal\\IsShortArrayOrList' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/IsShortArrayOrList.php',
'PHPCSUtils\\Internal\\IsShortArrayOrListWithCache' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/IsShortArrayOrListWithCache.php',
'PHPCSUtils\\Internal\\NoFileCache' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/NoFileCache.php',
'PHPCSUtils\\Internal\\StableCollections' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/StableCollections.php',
'PHPCSUtils\\TestUtils\\UtilityMethodTestCase' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/TestUtils/UtilityMethodTestCase.php',
'PHPCSUtils\\Tokens\\Collections' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Tokens/Collections.php',
'PHPCSUtils\\Tokens\\TokenHelper' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Tokens/TokenHelper.php',
'PHPCSUtils\\Utils\\Arrays' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Arrays.php',
'PHPCSUtils\\Utils\\Conditions' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Conditions.php',
'PHPCSUtils\\Utils\\Context' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Context.php',
'PHPCSUtils\\Utils\\ControlStructures' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/ControlStructures.php',
'PHPCSUtils\\Utils\\FunctionDeclarations' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/FunctionDeclarations.php',
'PHPCSUtils\\Utils\\GetTokensAsString' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/GetTokensAsString.php',
'PHPCSUtils\\Utils\\Lists' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Lists.php',
'PHPCSUtils\\Utils\\MessageHelper' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/MessageHelper.php',
'PHPCSUtils\\Utils\\Namespaces' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Namespaces.php',
'PHPCSUtils\\Utils\\NamingConventions' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/NamingConventions.php',
'PHPCSUtils\\Utils\\Numbers' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Numbers.php',
'PHPCSUtils\\Utils\\ObjectDeclarations' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/ObjectDeclarations.php',
'PHPCSUtils\\Utils\\Operators' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Operators.php',
'PHPCSUtils\\Utils\\Orthography' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Orthography.php',
'PHPCSUtils\\Utils\\Parentheses' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Parentheses.php',
'PHPCSUtils\\Utils\\PassedParameters' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/PassedParameters.php',
'PHPCSUtils\\Utils\\Scopes' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Scopes.php',
'PHPCSUtils\\Utils\\TextStrings' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/TextStrings.php',
'PHPCSUtils\\Utils\\UseStatements' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/UseStatements.php',
'PHPCSUtils\\Utils\\Variables' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Variables.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',

View file

@ -1,7 +1,7 @@
##
## Bundle of CA Root Certificates
##
## Certificate data from Mozilla as of: Tue May 30 03:12:04 2023 GMT
## Certificate data from Mozilla as of: Tue Aug 22 03:12:04 2023 GMT
##
## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates
@ -14,7 +14,7 @@
## Just configure this file as the SSLCACertificateFile.
##
## Conversion done with mk-ca-bundle.pl version 1.29.
## SHA256: c47475103fb05bb562bbadff0d1e72346b03236154e1448a6ca191b740f83507
## SHA256: 0ff137babc6a5561a9cfbe9f29558972e5b528202681b7d3803d03a3e82922bd
##
@ -3222,55 +3222,6 @@ AwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozmut6Dacpps6kFtZaSF4fC0urQe87YQVt8
rgIwRt7qy12a7DLCZRawTDBcMPPaTnOGBtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR
-----END CERTIFICATE-----
E-Tugra Global Root CA RSA v3
=============================
-----BEGIN CERTIFICATE-----
MIIF8zCCA9ugAwIBAgIUDU3FzRYilZYIfrgLfxUGNPt5EDQwDQYJKoZIhvcNAQELBQAwgYAxCzAJ
BgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4xHTAb
BgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290
IENBIFJTQSB2MzAeFw0yMDAzMTgwOTA3MTdaFw00NTAzMTIwOTA3MTdaMIGAMQswCQYDVQQGEwJU
UjEPMA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRF
LVR1Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBSU0Eg
djMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCiZvCJt3J77gnJY9LTQ91ew6aEOErx
jYG7FL1H6EAX8z3DeEVypi6Q3po61CBxyryfHUuXCscxuj7X/iWpKo429NEvx7epXTPcMHD4QGxL
sqYxYdE0PD0xesevxKenhOGXpOhL9hd87jwH7eKKV9y2+/hDJVDqJ4GohryPUkqWOmAalrv9c/SF
/YP9f4RtNGx/ardLAQO/rWm31zLZ9Vdq6YaCPqVmMbMWPcLzJmAy01IesGykNz709a/r4d+ABs8q
QedmCeFLl+d3vSFtKbZnwy1+7dZ5ZdHPOrbRsV5WYVB6Ws5OUDGAA5hH5+QYfERaxqSzO8bGwzrw
bMOLyKSRBfP12baqBqG3q+Sx6iEUXIOk/P+2UNOMEiaZdnDpwA+mdPy70Bt4znKS4iicvObpCdg6
04nmvi533wEKb5b25Y08TVJ2Glbhc34XrD2tbKNSEhhw5oBOM/J+JjKsBY04pOZ2PJ8QaQ5tndLB
eSBrW88zjdGUdjXnXVXHt6woq0bM5zshtQoK5EpZ3IE1S0SVEgpnpaH/WwAH0sDM+T/8nzPyAPiM
bIedBi3x7+PmBvrFZhNb/FAHnnGGstpvdDDPk1Po3CLW3iAfYY2jLqN4MpBs3KwytQXk9TwzDdbg
h3cXTJ2w2AmoDVf3RIXwyAS+XF1a4xeOVGNpf0l0ZAWMowIDAQABo2MwYTAPBgNVHRMBAf8EBTAD
AQH/MB8GA1UdIwQYMBaAFLK0ruYt9ybVqnUtdkvAG1Mh0EjvMB0GA1UdDgQWBBSytK7mLfcm1ap1
LXZLwBtTIdBI7zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAImocn+M684uGMQQ
gC0QDP/7FM0E4BQ8Tpr7nym/Ip5XuYJzEmMmtcyQ6dIqKe6cLcwsmb5FJ+Sxce3kOJUxQfJ9emN4
38o2Fi+CiJ+8EUdPdk3ILY7r3y18Tjvarvbj2l0Upq7ohUSdBm6O++96SmotKygY/r+QLHUWnw/q
ln0F7psTpURs+APQ3SPh/QMSEgj0GDSz4DcLdxEBSL9htLX4GdnLTeqjjO/98Aa1bZL0SmFQhO3s
SdPkvmjmLuMxC1QLGpLWgti2omU8ZgT5Vdps+9u1FGZNlIM7zR6mK7L+d0CGq+ffCsn99t2HVhjY
sCxVYJb6CH5SkPVLpi6HfMsg2wY+oF0Dd32iPBMbKaITVaA9FCKvb7jQmhty3QUBjYZgv6Rn7rWl
DdF/5horYmbDB7rnoEgcOMPpRfunf/ztAmgayncSd6YAVSgU7NbHEqIbZULpkejLPoeJVF3Zr52X
nGnnCv8PWniLYypMfUeUP95L6VPQMPHF9p5J3zugkaOj/s1YzOrfr28oO6Bpm4/srK4rVJ2bBLFH
IK+WEj5jlB0E5y67hscMmoi/dkfv97ALl2bSRM9gUgfh1SxKOidhd8rXj+eHDjD/DLsE4mHDosiX
YY60MGo8bcIHX0pzLz/5FooBZu+6kcpSV3uu1OYP3Qt6f4ueJiDPO++BcYNZ
-----END CERTIFICATE-----
E-Tugra Global Root CA ECC v3
=============================
-----BEGIN CERTIFICATE-----
MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMwgYAxCzAJBgNV
BAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4xHTAbBgNV
BAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENB
IEVDQyB2MzAeFw0yMDAzMTgwOTQ2NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEP
MA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1
Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBFQ0MgdjMw
djAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQKczLWYHMjLiSF4mDKpL2
w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YKfWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31
Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQ
zPUwHQYDVR0OBBYEFP+CMXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjO
PQQDAwNpADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/67W4W
Aie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFxvmjkI6TZraE3
-----END CERTIFICATE-----
Security Communication RootCA3
==============================
-----BEGIN CERTIFICATE-----
@ -3361,3 +3312,140 @@ SR9BIgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK++kpRuDCK
W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8g
UXOQwKhbYdDFUDn9hf7B43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w==
-----END CERTIFICATE-----
Sectigo Public Server Authentication Root E46
=============================================
-----BEGIN CERTIFICATE-----
MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQswCQYDVQQGEwJH
QjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBTZXJ2
ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5
WjBfMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0
aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUr
gQQAIgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccCWvkEN/U0
NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+6xnOQ6OjQjBAMB0GA1Ud
DgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
/zAKBggqhkjOPQQDAwNnADBkAjAn7qRaqCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RH
lAFWovgzJQxC36oCMB3q4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21U
SAGKcw==
-----END CERTIFICATE-----
Sectigo Public Server Authentication Root R46
=============================================
-----BEGIN CERTIFICATE-----
MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBfMQswCQYDVQQG
EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT
ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwHhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1
OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T
ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3
DQEBAQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDaef0rty2k
1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnzSDBh+oF8HqcIStw+Kxwf
GExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xfiOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMP
FF1bFOdLvt30yNoDN9HWOaEhUTCDsG3XME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vu
ZDCQOc2TZYEhMbUjUDM3IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5Qaz
Yw6A3OASVYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgESJ/A
wSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu+Zd4KKTIRJLpfSYF
plhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt8uaZFURww3y8nDnAtOFr94MlI1fZ
EoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+LHaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW
6aWWrL3DkJiy4Pmi1KZHQ3xtzwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWI
IUkwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c
mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQYKlJfp/imTYp
E0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52gDY9hAaLMyZlbcp+nv4fjFg4
exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZAFv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M
0ejf5lG5Nkc/kLnHvALcWxxPDkjBJYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI
84HxZmduTILA7rpXDhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9m
pFuiTdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5dHn5Hrwd
Vw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65LvKRRFHQV80MNNVIIb/b
E/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmm
J1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAYQqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL
-----END CERTIFICATE-----
SSL.com TLS RSA Root CA 2022
============================
-----BEGIN CERTIFICATE-----
MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQG
EwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxTU0wuY29tIFRMUyBSU0Eg
Um9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloXDTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMC
VVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJv
b3QgQ0EgMjAyMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u
9nTPL3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OYt6/wNr/y
7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0insS657Lb85/bRi3pZ7Qcac
oOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3PnxEX4MN8/HdIGkWCVDi1FW24IBydm5M
R7d1VVm0U3TZlMZBrViKMWYPHqIbKUBOL9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDG
D6C1vBdOSHtRwvzpXGk3R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEW
TO6Af77wdr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS+YCk
8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYSd66UNHsef8JmAOSq
g+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoGAtUjHBPW6dvbxrB6y3snm/vg1UYk
7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2fgTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1Ud
EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsu
N+7jhHonLs0ZNbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt
hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsMQtfhWsSWTVTN
j8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvfR4iyrT7gJ4eLSYwfqUdYe5by
iB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJDPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjU
o3KUQyxi4U5cMj29TH0ZR6LDSeeWP4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqo
ENjwuSfr98t67wVylrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7Egkaib
MOlqbLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2wAgDHbICi
vRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3qr5nsLFR+jM4uElZI7xc7
P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sjiMho6/4UIyYOf8kpIEFR3N+2ivEC+5BB0
9+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA=
-----END CERTIFICATE-----
SSL.com TLS ECC Root CA 2022
============================
-----BEGIN CERTIFICATE-----
MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQswCQYDVQQGEwJV
UzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxTU0wuY29tIFRMUyBFQ0MgUm9v
dCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMx
GDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3Qg
Q0EgMjAyMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWy
JGYmacCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFNSeR7T5v1
5wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSJjy+j6CugFFR7
81a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NWuCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGG
MAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w
7deedWo1dlJF4AIxAMeNb0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5
Zn6g6g==
-----END CERTIFICATE-----
Atos TrustedRoot Root CA ECC TLS 2021
=====================================
-----BEGIN CERTIFICATE-----
MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4wLAYDVQQDDCVB
dG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0wCwYDVQQKDARBdG9zMQswCQYD
VQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3Mg
VHJ1c3RlZFJvb3QgUm9vdCBDQSBFQ0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYT
AkRFMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6K
DP/XtXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4AjJn8ZQS
b+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2KCXWfeBmmnoJsmo7jjPX
NtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIwW5kp85wxtolrbNa9d+F851F+
uDrNozZffPc8dz7kUK2o59JZDCaOMDtuCCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGY
a3cpetskz2VAv9LcjBHo9H1/IISpQuQo
-----END CERTIFICATE-----
Atos TrustedRoot Root CA RSA TLS 2021
=====================================
-----BEGIN CERTIFICATE-----
MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBMMS4wLAYDVQQD
DCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIxMQ0wCwYDVQQKDARBdG9zMQsw
CQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0
b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNV
BAYTAkRFMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BB
l01Z4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYvYe+W/CBG
vevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZkmGbzSoXfduP9LVq6hdK
ZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDsGY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt
0xU6kGpn8bRrZtkh68rZYnxGEFzedUlnnkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVK
PNe0OwANwI8f4UDErmwh3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMY
sluMWuPD0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzygeBY
Br3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8ANSbhqRAvNncTFd+
rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezBc6eUWsuSZIKmAMFwoW4sKeFYV+xa
fJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lIpw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/
BAUwAwEB/zAdBgNVHQ4EFgQUdEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0G
CSqGSIb3DQEBDAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS
4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPso0UvFJ/1TCpl
Q3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJqM7F78PRreBrAwA0JrRUITWX
AdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuywxfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9G
slA9hGCZcbUztVdF5kJHdWoOsAgMrr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2Vkt
afcxBPTy+av5EzH4AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9q
TFsR0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuYo7Ey7Nmj
1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5dDTedk+SKlOxJTnbPP/l
PqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcEoji2jbDwN/zIIX8/syQbPYtuzE2wFg2W
HYMfRsCbvUOZ58SWLs5fyQ==
-----END CERTIFICATE-----

View file

@ -2,17 +2,17 @@
"packages": [
{
"name": "composer/ca-bundle",
"version": "1.3.6",
"version_normalized": "1.3.6.0",
"version": "1.3.7",
"version_normalized": "1.3.7.0",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
"reference": "90d087e988ff194065333d16bc5cf649872d9cdb"
"reference": "76e46335014860eec1aa5a724799a00a2e47cc85"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/90d087e988ff194065333d16bc5cf649872d9cdb",
"reference": "90d087e988ff194065333d16bc5cf649872d9cdb",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/76e46335014860eec1aa5a724799a00a2e47cc85",
"reference": "76e46335014860eec1aa5a724799a00a2e47cc85",
"shasum": ""
},
"require": {
@ -26,7 +26,7 @@
"symfony/phpunit-bridge": "^4.2 || ^5",
"symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0"
},
"time": "2023-06-06T12:02:59+00:00",
"time": "2023-08-30T09:31:38+00:00",
"type": "library",
"extra": {
"branch-alias": {
@ -61,7 +61,7 @@
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/ca-bundle/issues",
"source": "https://github.com/composer/ca-bundle/tree/1.3.6"
"source": "https://github.com/composer/ca-bundle/tree/1.3.7"
},
"funding": [
{
@ -79,6 +79,87 @@
],
"install-path": "./ca-bundle"
},
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
"version": "v1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/composer-installer.git",
"reference": "4be43904336affa5c2f70744a348312336afd0da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da",
"reference": "4be43904336affa5c2f70744a348312336afd0da",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0 || ^2.0",
"php": ">=5.4",
"squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
},
"require-dev": {
"composer/composer": "*",
"ext-json": "*",
"ext-zip": "*",
"php-parallel-lint/php-parallel-lint": "^1.3.1",
"phpcompatibility/php-compatibility": "^9.0",
"yoast/phpunit-polyfills": "^1.0"
},
"time": "2023-01-05T11:28:13+00:00",
"type": "composer-plugin",
"extra": {
"class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Franck Nijhof",
"email": "franck.nijhof@dealerdirect.com",
"homepage": "http://www.frenck.nl",
"role": "Developer / IT Manager"
},
{
"name": "Contributors",
"homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
}
],
"description": "PHP_CodeSniffer Standards Composer Installer Plugin",
"homepage": "http://www.dealerdirect.com",
"keywords": [
"PHPCodeSniffer",
"PHP_CodeSniffer",
"code quality",
"codesniffer",
"composer",
"installer",
"phpcbf",
"phpcs",
"plugin",
"qa",
"quality",
"standard",
"standards",
"style guide",
"stylecheck",
"tests"
],
"support": {
"issues": "https://github.com/PHPCSStandards/composer-installer/issues",
"source": "https://github.com/PHPCSStandards/composer-installer"
},
"install-path": "../dealerdirect/phpcodesniffer-composer-installer"
},
{
"name": "embed/embed",
"version": "v4.4.8",
@ -725,6 +806,148 @@
},
"install-path": "../oscarotero/html-parser"
},
{
"name": "phpcsstandards/phpcsextra",
"version": "1.1.1",
"version_normalized": "1.1.1.0",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHPCSExtra.git",
"reference": "98bcdbacbda14b1db85f710b1853125726795bbc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/98bcdbacbda14b1db85f710b1853125726795bbc",
"reference": "98bcdbacbda14b1db85f710b1853125726795bbc",
"shasum": ""
},
"require": {
"php": ">=5.4",
"phpcsstandards/phpcsutils": "^1.0.8",
"squizlabs/php_codesniffer": "^3.7.1"
},
"require-dev": {
"php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcsstandards/phpcsdevcs": "^1.1.6",
"phpcsstandards/phpcsdevtools": "^1.2.1",
"phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0"
},
"time": "2023-08-26T04:46:45+00:00",
"type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
"dev-stable": "1.x-dev",
"dev-develop": "1.x-dev"
}
},
"installation-source": "dist",
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "Juliette Reinders Folmer",
"homepage": "https://github.com/jrfnl",
"role": "lead"
},
{
"name": "Contributors",
"homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors"
}
],
"description": "A collection of sniffs and standards for use with PHP_CodeSniffer.",
"keywords": [
"PHP_CodeSniffer",
"phpcbf",
"phpcodesniffer-standard",
"phpcs",
"standards",
"static analysis"
],
"support": {
"issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues",
"source": "https://github.com/PHPCSStandards/PHPCSExtra"
},
"install-path": "../phpcsstandards/phpcsextra"
},
{
"name": "phpcsstandards/phpcsutils",
"version": "1.0.8",
"version_normalized": "1.0.8.0",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHPCSUtils.git",
"reference": "69465cab9d12454e5e7767b9041af0cd8cd13be7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/69465cab9d12454e5e7767b9041af0cd8cd13be7",
"reference": "69465cab9d12454e5e7767b9041af0cd8cd13be7",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0",
"php": ">=5.4",
"squizlabs/php_codesniffer": "^3.7.1 || 4.0.x-dev@dev"
},
"require-dev": {
"ext-filter": "*",
"php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcsstandards/phpcsdevcs": "^1.1.6",
"yoast/phpunit-polyfills": "^1.0.5 || ^2.0.0"
},
"time": "2023-07-16T21:39:41+00:00",
"type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
"dev-stable": "1.x-dev",
"dev-develop": "1.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"classmap": [
"PHPCSUtils/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "Juliette Reinders Folmer",
"homepage": "https://github.com/jrfnl",
"role": "lead"
},
{
"name": "Contributors",
"homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors"
}
],
"description": "A suite of utility functions for use with PHP_CodeSniffer",
"homepage": "https://phpcsutils.com/",
"keywords": [
"PHP_CodeSniffer",
"phpcbf",
"phpcodesniffer-standard",
"phpcs",
"phpcs3",
"standards",
"static analysis",
"tokens",
"utility"
],
"support": {
"docs": "https://phpcsutils.com/",
"issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues",
"source": "https://github.com/PHPCSStandards/PHPCSUtils"
},
"install-path": "../phpcsstandards/phpcsutils"
},
{
"name": "psr/http-client",
"version": "1.0.2",
@ -1075,28 +1298,88 @@
"install-path": "../slim/psr7"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.27.0",
"version_normalized": "1.27.0.0",
"name": "squizlabs/php_codesniffer",
"version": "3.7.2",
"version_normalized": "3.7.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"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/polyfill-php80",
"version": "v1.28.0",
"version_normalized": "1.28.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"time": "2022-11-03T14:55:06+00:00",
"time": "2023-01-26T09:26:14+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -1142,7 +1425,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
},
"funding": [
{
@ -1159,8 +1442,83 @@
}
],
"install-path": "../symfony/polyfill-php80"
},
{
"name": "wp-coding-standards/wpcs",
"version": "3.0.1",
"version_normalized": "3.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
"reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/b4caf9689f1a0e4a4c632679a44e638c1c67aff1",
"reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1",
"shasum": ""
},
"require": {
"ext-filter": "*",
"ext-libxml": "*",
"ext-tokenizer": "*",
"ext-xmlreader": "*",
"php": ">=5.4",
"phpcsstandards/phpcsextra": "^1.1.0",
"phpcsstandards/phpcsutils": "^1.0.8",
"squizlabs/php_codesniffer": "^3.7.2"
},
"require-dev": {
"php-parallel-lint/php-console-highlighter": "^1.0.0",
"php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcompatibility/php-compatibility": "^9.0",
"phpcsstandards/phpcsdevtools": "^1.2.0",
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"suggest": {
"ext-iconv": "For improved results",
"ext-mbstring": "For improved results"
},
"time": "2023-09-14T07:06:09+00:00",
"type": "phpcodesniffer-standard",
"installation-source": "dist",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Contributors",
"homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors"
}
],
"description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
"keywords": [
"phpcs",
"standards",
"static analysis",
"wordpress"
],
"support": {
"issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues",
"source": "https://github.com/WordPress/WordPress-Coding-Standards",
"wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki"
},
"funding": [
{
"url": "https://opencollective.com/thewpcc/contribute/wp-php-63406",
"type": "custom"
}
],
"install-path": "../wp-coding-standards/wpcs"
}
],
"dev": true,
"dev-package-names": []
"dev-package-names": [
"dealerdirect/phpcodesniffer-composer-installer",
"phpcsstandards/phpcsextra",
"phpcsstandards/phpcsutils",
"squizlabs/php_codesniffer",
"wp-coding-standards/wpcs"
]
}

View file

@ -3,7 +3,7 @@
'name' => '__root__',
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '6d20c0b45ad6539a4835855d6a33eab9f232cea1',
'reference' => 'f79071d77a00dcf2cb86e6e7011443c8b50bac7c',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -13,21 +13,30 @@
'__root__' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '6d20c0b45ad6539a4835855d6a33eab9f232cea1',
'reference' => 'f79071d77a00dcf2cb86e6e7011443c8b50bac7c',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'composer/ca-bundle' => array(
'pretty_version' => '1.3.6',
'version' => '1.3.6.0',
'reference' => '90d087e988ff194065333d16bc5cf649872d9cdb',
'pretty_version' => '1.3.7',
'version' => '1.3.7.0',
'reference' => '76e46335014860eec1aa5a724799a00a2e47cc85',
'type' => 'library',
'install_path' => __DIR__ . '/./ca-bundle',
'aliases' => array(),
'dev_requirement' => false,
),
'dealerdirect/phpcodesniffer-composer-installer' => array(
'pretty_version' => 'v1.0.0',
'version' => '1.0.0.0',
'reference' => '4be43904336affa5c2f70744a348312336afd0da',
'type' => 'composer-plugin',
'install_path' => __DIR__ . '/../dealerdirect/phpcodesniffer-composer-installer',
'aliases' => array(),
'dev_requirement' => true,
),
'embed/embed' => array(
'pretty_version' => 'v4.4.8',
'version' => '4.4.8.0',
@ -118,6 +127,24 @@
'aliases' => array(),
'dev_requirement' => false,
),
'phpcsstandards/phpcsextra' => array(
'pretty_version' => '1.1.1',
'version' => '1.1.1.0',
'reference' => '98bcdbacbda14b1db85f710b1853125726795bbc',
'type' => 'phpcodesniffer-standard',
'install_path' => __DIR__ . '/../phpcsstandards/phpcsextra',
'aliases' => array(),
'dev_requirement' => true,
),
'phpcsstandards/phpcsutils' => array(
'pretty_version' => '1.0.8',
'version' => '1.0.8.0',
'reference' => '69465cab9d12454e5e7767b9041af0cd8cd13be7',
'type' => 'phpcodesniffer-standard',
'install_path' => __DIR__ . '/../phpcsstandards/phpcsutils',
'aliases' => array(),
'dev_requirement' => true,
),
'psr/http-client' => array(
'pretty_version' => '1.0.2',
'version' => '1.0.2.0',
@ -184,14 +211,32 @@
'aliases' => array(),
'dev_requirement' => false,
),
'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/polyfill-php80' => array(
'pretty_version' => 'v1.27.0',
'version' => '1.27.0.0',
'reference' => '7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936',
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '6caa57379c4aec19c0a12a38b59b26487dcfe4b5',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),
'dev_requirement' => false,
),
'wp-coding-standards/wpcs' => array(
'pretty_version' => '3.0.1',
'version' => '3.0.1.0',
'reference' => 'b4caf9689f1a0e4a4c632679a44e638c1c67aff1',
'type' => 'phpcodesniffer-standard',
'install_path' => __DIR__ . '/../wp-coding-standards/wpcs',
'aliases' => array(),
'dev_requirement' => true,
),
),
);

View file

@ -0,0 +1,523 @@
# Change Log for the PHPCSExtra standard for PHP CodeSniffer
All notable changes to this project will be documented in this file.
This projects adheres to [Keep a CHANGELOG](http://keepachangelog.com/) and uses [Semantic Versioning](http://semver.org/).
**Legend**:
:wrench: = Includes auto-fixer.
:bar_chart: = Includes metrics.
:books: = Includes CLI documentation.
## [Unreleased]
_Nothing yet._
## [1.1.1] - 2023-08-26
### Changed
#### Modernize
* `Modernize.FunctionCalls.Dirname`: the sniff will now respect a potentially set [`php_version` configuration option][php_version-config] and only report on modernizations which are possible on the configured `php_version`. [#261]
If the `php_version` is not set, the sniff will continue to report on all modernization options.
#### Other
* Various documentation improvements. Props in part to [@szepeviktor].
* Improved defensive coding in select places.
* Various housekeeping.
[#261]: https://github.com/PHPCSStandards/PHPCSExtra/pull/261
## [1.1.0] - 2023-07-19
### Added
#### Universal
* :wrench: :books: New `Universal.CodeAnalysis.NoEchoSprintf` sniff to detect use of the inefficient `echo [v]sprintf(...);` combi and recommends using `[v]printf()` instead. [#242]
* :bar_chart: :books: New `Universal.FunctionDeclarations.NoLongClosures` sniff to detect "long" closures and recommend using a named function instead. [#240]
The sniff offers the following properties to influence its behaviour: `recommendedLines` (defaults to `5`), `maxLines` (defaults to `8`), `ignoreCommentLines` (defaults to `true`) and `ignoreEmptyLines` (defaults to `true`).
* :wrench: :bar_chart: :books: New `Universal.FunctionDeclarations.RequireFinalMethodsInTraits` sniff to enforce non-private, non-abstract methods in traits to be declared as `final`. [#243], [#245]
There is a separate `NonFinalMagicMethodFound` error code for magic methods to allow those to be excluded from the check.
* :wrench: :bar_chart: :books: New `Universal.UseStatements.DisallowMixedGroupUse` sniff to disallow group use statements which import a combination of namespace/OO construct, functions and/or constants in one statement. [#241], [#246]
Note: the fixer will use a semi-standardized format for group use statements. If there are more specific requirements for the formatting of group use statements, the ruleset configurator should ensure that additional sniffs are included in the ruleset to enforce the required format.
* :wrench: :bar_chart: :books: New `Universal.UseStatements.KeywordSpacing` sniff to enforce the use of a single space after the `use`, `function`, `const` keywords and both before and after the `as` keyword in import `use` statements. [#247]
The sniff has modular error codes to allow for disabling individual checks.
* :wrench: :books: New `Universal.UseStatements.NoUselessAliases` sniff to detect useless aliases (aliasing something to its original name) in import use statements. [#244]
Note: as OO and function names in PHP are case-insensitive, aliasing to the same name, using a different case is also considered useless.
* :wrench: :bar_chart: :books: New `Universal.WhiteSpace.CommaSpacing` sniff to enforce that there is no space before a comma and exactly one space, or a new line, after a comma. [#254]
Additionally, the sniff also enforces that the comma should follow the code and not be placed after a trailing comment.
The sniff has modular error codes to allow for disabling individual checks and checks in certain contexts.
The sniff will respect a potentially set [`php_version` configuration option][php_version-config] when deciding how to handle the spacing after a heredoc/nowdoc closer.
### Changed
#### Universal
* Minor performance improvements for the `Universal.Arrays.DuplicateArrayKey` and the `Universal.CodeAnalysis.ConstructorDestructorReturn` sniffs. [#251], [#252]
#### Other
* Composer: The minimum `PHPCSUtils` requirement has been updated to `^1.0.8` (was `^1.0.6`). [#249], [#254]
* Various housekeeping.
[#240]: https://github.com/PHPCSStandards/PHPCSExtra/pull/240
[#241]: https://github.com/PHPCSStandards/PHPCSExtra/pull/241
[#242]: https://github.com/PHPCSStandards/PHPCSExtra/pull/242
[#243]: https://github.com/PHPCSStandards/PHPCSExtra/pull/243
[#244]: https://github.com/PHPCSStandards/PHPCSExtra/pull/244
[#245]: https://github.com/PHPCSStandards/PHPCSExtra/pull/245
[#246]: https://github.com/PHPCSStandards/PHPCSExtra/pull/246
[#247]: https://github.com/PHPCSStandards/PHPCSExtra/pull/247
[#249]: https://github.com/PHPCSStandards/PHPCSExtra/pull/249
[#251]: https://github.com/PHPCSStandards/PHPCSExtra/pull/251
[#252]: https://github.com/PHPCSStandards/PHPCSExtra/pull/252
[#254]: https://github.com/PHPCSStandards/PHPCSExtra/pull/254
## [1.0.4] - 2023-06-18
### Changed
#### Other
* Composer: The minimum `PHPCSUtils` requirement has been updated to `^1.0.6` (was `^1.0.0`). [#237]
* Various housekeeping.
### Fixed
#### Universal
* `Universal.Constants.LowercaseClassResolutionKeyword`: prevent false positives for function calls to methods called `class`. [#226]
[#226]: https://github.com/PHPCSStandards/PHPCSExtra/pull/226
[#237]: https://github.com/PHPCSStandards/PHPCSExtra/pull/237
## [1.0.3] - 2023-03-28
### Changed
#### Universal
* `Universal.WhiteSpace.DisallowInlineTabs`: significant performance improvement. [#216], [#217]
#### Other
* Various housekeeping.
### Fixed
#### Modernize
* `Modernize.FunctionCalls.Dirname`: prevent false positives for attribute classes called `dirname`. [#211], [#213]
[#211]: https://github.com/PHPCSStandards/PHPCSExtra/pull/211
[#213]: https://github.com/PHPCSStandards/PHPCSExtra/pull/213
[#216]: https://github.com/PHPCSStandards/PHPCSExtra/pull/216
[#217]: https://github.com/PHPCSStandards/PHPCSExtra/pull/217
## [1.0.2] - 2023-01-10
### Changed
#### Universal
* `Universal.CodeAnalysis.ConstructorDestructorReturn`: the sniff will now respect a potentially set [`php_version` configuration option][php_version-config] and only report on PHP4-style constructors when the `php_version` is below `'80000'`. Thanks [@anomiex] for reporting! [#207], [#208]
[#207]: https://github.com/PHPCSStandards/PHPCSExtra/issues/207
[#208]: https://github.com/PHPCSStandards/PHPCSExtra/pull/208
## [1.0.1] - 2023-01-05
### Fixed
#### Universal
* `Universal.CodeAnalysis.ConstructorDestructorReturn`: fixed false positive for return statements in nested functions/closures declared within constructor/destructor methods. Thanks [@anomiex] for reporting! [#201], [#202]
[#201]: https://github.com/PHPCSStandards/PHPCSExtra/issues/201
[#202]: https://github.com/PHPCSStandards/PHPCSExtra/pull/202
## [1.0.0] - 2023-01-04
:warning: Important: this package now requires [PHPCSUtils 1.0.0]. Please make sure you use `--with-[all-]dependencies` when running `composer update`. :exclamation:
For the full list of features, please see the changelogs of the alpha/rc releases:
* [1.0.0-rc1](https://github.com/PHPCSStandards/PHPCSExtra/releases/tag/1.0.0-rc1)
* [1.0.0-alpha3](https://github.com/PHPCSStandards/PHPCSExtra/releases/tag/1.0.0-alpha3)
* [1.0.0-alpha2](https://github.com/PHPCSStandards/PHPCSExtra/releases/tag/1.0.0-alpha2)
* [1.0.0-alpha1](https://github.com/PHPCSStandards/PHPCSExtra/releases/tag/1.0.0-alpha1)
### Changed
#### Other
* Updated various sniffs to take advantage of PHPCSUtils 1.0.0(-rc1). [#193], [#194], [#195]
* Minor documentation improvements.
* Various housekeeping.
### Fixed
#### Modernize
* `Modernize.FunctionCalls.Dirname`: the sniff will now correctly recognize magic constants in a case-insensitive manner. [#187]
[PHPCSUtils 1.0.0]: https://github.com/PHPCSStandards/PHPCSUtils/releases/tag/1.0.0
[#187]: https://github.com/PHPCSStandards/PHPCSExtra/pull/187
[#193]: https://github.com/PHPCSStandards/PHPCSExtra/pull/193
[#194]: https://github.com/PHPCSStandards/PHPCSExtra/pull/194
[#195]: https://github.com/PHPCSStandards/PHPCSExtra/pull/195
## [1.0.0-RC1] - 2022-12-07
:warning: Important: this package now requires [PHPCSUtils 1.0.0-alpha4]. Please make sure you use `--with-[all-]dependencies` when running `composer update`. :exclamation:
### Added
#### Modernize
* This is a new standard with one sniff to start with.
* :wrench: :books: New `Modernize.FunctionCalls.Dirname` sniff to detect and auto-fix two typical code modernizations which can be made related to the [`dirname()`][php-manual-dirname] function. [#172]
#### Universal
* :wrench: :bar_chart: :books: New `Universal.Classes.DisallowAnonClassParentheses` sniff to disallow the use of parentheses when declaring an anonymous class without passing parameters. [#76], [#162]
* :wrench: :bar_chart: :books: New `Universal.Classes.RequireAnonClassParentheses` sniff to require the use of parentheses when declaring an anonymous class, whether parameters are passed or not. [#76], [#166]
* :wrench: :bar_chart: :books: New `Universal.Classes.DisallowFinalClass` sniff to disallow classes being declared `final`. [#108], [#114], [#148], [#163]
* :wrench: :bar_chart: :books: New `Universal.Classes.RequireFinalClass` sniff to require all non-`abstract` classes to be declared `final`. [#109], [#148], [#164]
Warning: the auto-fixer for this sniff _may_ have unintended side-effects for applications and should be used with care! This is considered a _risky_ fixer.
* :wrench: :bar_chart: :books: New `Universal.Classes.ModifierKeywordOrder` sniff to standardize the modifier keyword order for class declarations. [#142]
The sniff offers an `order` property to specify the preferred order.
* :wrench: :books: New `Universal.CodeAnalysis.ConstructorDestructorReturn` sniff to verify that class constructor/destructor methods 1) do not have a return type declaration and 2) do not return a value. [#137], [#140], [#146] Inspired by [@derickr].
* :wrench: :books: New `Universal.CodeAnalysis.ForeachUniqueAssignment` sniff to detect `foreach` control structures which use the same variable for both the key as well as the value assignment as this will lead to unexpected - and most likely unintended - behaviour. [#110], [#175]
The fixer will maintain the existing behaviour of the code. Mind: this may not be the _intended_ behaviour.
* :wrench: :books: New `Universal.CodeAnalysis.StaticInFinalClass` sniff to detect using `static` instead of `self` in OO constructs which are `final`. [#116], [#180]
The sniff has modular error codes to allow for making exceptions based on the type of use for `static`.
* :wrench: :bar_chart: :books: New `Universal.Constants.LowercaseClassResolutionKeyword` sniff to enforce that the `class` keyword when used for class name resolution, i.e. `::class`, is in lowercase. [#72]
* :wrench: :bar_chart: :books: New `Universal.Constants.ModifierKeywordOrder` sniff to standardize the modifier keyword order for OO constant declarations. [#143]
The sniff offers an `order` property to specify the preferred order.
* :wrench: :books: New `Universal.ControlStructures.DisallowLonelyIf` sniff to disallow `if` statements as the only statement in an `else` block. [#85], [#168], [#169]
Inspired by the [ESLint "no lonely if"] rule.
Note: This sniff will not fix the indentation of the "inner" code. It is strongly recommended to run this sniff together with the `Generic.WhiteSpace.ScopeIndent` sniff to get the correct indentation.
* :bar_chart: :books: New `Universal.Files.SeparateFunctionsFromOO` sniff to enforce that a file should either declare (global/namespaced) functions or declare OO structures, but not both. [#95], [#170], [#171]
Nested function declarations, i.e. functions declared within a function/method will be disregarded for the purposes of this sniff.
The same goes for anonymous classes, closures and arrow functions.
* :books: New `Universal.NamingConventions.NoReservedKeywordParameterNames` sniff to verify that function parameters do not use reserved keywords as names, as this can quickly become confusing when people use them in function calls using named parameters. [#80], [#81], [#106], [#107], [#173]
The sniff has modular error codes to allow for making exceptions for specific keywords.
* :wrench: :bar_chart: :books: New `Universal.Operators.TypeSeparatorSpacing` sniff to enforce no spaces around union type and intersection type separators. [#117]
* :wrench: :books: New `Universal.PHP.OneStatementInShortEchoTag` sniff to disallow short open echo tags `<?=` containing more than one PHP statement. [#89], [#147], [#165]
* :wrench: :bar_chart: :books: New `Universal.WhiteSpace.AnonClassKeywordSpacing` sniff to standardize the amount of spacing between the `class` keyword and the open parenthesis (if any) for anonymous class declarations. [#120]
The sniff offers a `spacing` property to set the amount of spaces the sniff should check for.
* :wrench: :books: New `Universal.WhiteSpace.PrecisionAlignment` sniff to enforce indentation to always be a multiple of a tabstop, i.e. disallow precision alignment. [#119], [#122], [#123], [#124]
Note:
- This sniff does not concern itself with tabs versus spaces.
It is recommended to use the sniff in combination with the PHPCS native `Generic.WhiteSpace.DisallowTabIndent` or the `Generic.WhiteSpace.DisallowSpaceIndent` sniff.
- When using this sniff with tab-based standards, please ensure that the `tab-width` is set and either don't set the `$indent` property or set it to the tab-width (or a multiple thereof).
- The fixer works based on "best guess" and may not always result in the desired indentation. Combine this sniff with the `Generic.WhiteSpace.ScopeIndent` sniff for more precise indentation fixes.
- The behaviour of the sniff is customizable via the following properties:
- `indent`: the indent used for the codebase.
- `ignoreAlignmentBefore`: allows for providing a list of token names for which (preceding) precision alignment should be ignored.
- `ignoreBlankLines`: whether or not potential trailing whitespace on otherwise blank lines should be examined or ignored.
### Changed
#### Universal
* `Universal.Arrays.DisallowShortArraySyntax`: the sniff will now record metrics about long vs short array usage. [#154]
* `Universal.Arrays.DuplicateArrayKey`: where relevant, the sniff will now make a distinction between keys which will be duplicate in all PHP version and (numeric) keys which will only be a duplicate key in [PHP < 8.0 or PHP >= 8.0][php-rfc-negative_array_index]. [#177], [#178]
If a [`php_version` configuration option][php_version-config] has been passed to PHPCS, it will be respected by the sniff and only report duplicate keys for the configured PHP version.
* `Universal.ControlStructures.DisallowAlternativeSyntax`: the sniff will now also record a metric when single-line (no body) control structures are encountered. [#158]
* `Universal.ControlStructures.DisallowAlternativeSyntax`: the error message thrown by the sniff is now more descriptive. [#159]
* `Universal.ControlStructures.DisallowAlternativeSyntax`: metrics will no longer be recorded for `elseif` and `else` keywords, but only on the `if` keyword as the type of syntax used has to be the same for the whole "chain". [#161]
* `Universal.Lists.DisallowLongListSyntax`: the sniff will no longer record (incomplete) metrics about long vs short list usage. [#155]
* `Universal.Lists.DisallowShortListSyntax`: the sniff will now record (complete) metrics about long vs short list usage. [#155]
* `Universal.OOStructures.AlphabeticExtendsImplements`: documented support for `enum ... implements`. [#150]
* `Universal.UseStatements.DisallowUseClass`: updated error message and metric name to take PHP 8.1 `enum`s into account. [#149]
* `Universal.UseStatements.NoLeadingBackslash`: the sniff will now also flag and auto-fix leading backslashes in group use statements. [#167]
#### Other
* Updated the sniffs for compatibility with PHPCSUtils 1.0.0-alpha4. [#134]
* Updated the sniffs to correctly handle PHP 8.0/8.1/8.2 features whenever relevant.
* Readme: Updated installation instructions for compatibility with Composer 2.2+. [#101]
* Composer: The minimum `PHP_CodeSniffer` requirement has been updated to `^3.7.1` (was `^3.3.1`). [#115], [#130]
* Composer: The package will now identify itself as a static analysis tool. Thanks [@GaryJones]! [#126]
* All non-`abstract` classes in this package are now `final`. [#121]
* All XML documentation now has a schema annotation. [#128]
* Various housekeeping.
### Fixed
The upgrade to PHPCSUtils 1.0.0-alpha4 took care of a number of bugs, which potentially could have affected sniffs in this package.
#### NormalizedArrays
* `NormalizedArrays.Arrays.ArrayBraceSpacing`: the sniff now allows for trailing comments after the array opener in multi-line arrays. [#118]
* `NormalizedArrays.Arrays.ArrayBraceSpacing`: trailing comments at the end of an array, but before the closer, in multi-line arrays will no longer confuse the sniff. [#135]
* `NormalizedArrays.Arrays.CommaAfterLast`: the fixer will now recognize PHP 7.3+ flexible heredoc/nowdocs and in that case, will add the comma on the same line as the heredoc/nowdoc closer. [#144]
#### Universal
* `Universal.Arrays.DisallowShortArraySyntax`: nested short arrays in short lists will now be detected and fixed correctly. [#153]
* `Universal.ControlStructures.DisallowAlternativeSyntax`: the sniff will no longer bow out indiscriminately when the `allowWithInlineHTML` property is set to `true`. [#90], [#161]
* `Universal.ControlStructures.DisallowAlternativeSyntax`: when alternative control structure syntax is allowed in combination with inline HTML (`allowWithInlineHTML` property set to `true`), inline HTML in functions declared within the control structure body will no longer be taken into account for determining whether or not the control structure contains inline HTML. [#160]
* `Universal.Lists.DisallowShortListSyntax`: the sniff will work around a tokenizer bug in PHPCS 3.7.1, which previously could lead to false negatives. [#151].
* `Universal.Lists.DisallowShortListSyntax`: nested short lists in short arrays will now be detected and fixed correctly. [#152]
* `Universal.Operators.DisallowStandalonePostIncrementDecrement`: the sniff will now correctly recognize stand-alone statements which end on a PHP close tag. [#176]
[#72]: https://github.com/PHPCSStandards/PHPCSExtra/pull/72
[#76]: https://github.com/PHPCSStandards/PHPCSExtra/pull/76
[#80]: https://github.com/PHPCSStandards/PHPCSExtra/pull/80
[#81]: https://github.com/PHPCSStandards/PHPCSExtra/pull/81
[#85]: https://github.com/PHPCSStandards/PHPCSExtra/pull/85
[#89]: https://github.com/PHPCSStandards/PHPCSExtra/pull/89
[#90]: https://github.com/PHPCSStandards/PHPCSExtra/pull/90
[#95]: https://github.com/PHPCSStandards/PHPCSExtra/pull/95
[#101]: https://github.com/PHPCSStandards/PHPCSExtra/pull/101
[#106]: https://github.com/PHPCSStandards/PHPCSExtra/pull/106
[#107]: https://github.com/PHPCSStandards/PHPCSExtra/pull/107
[#108]: https://github.com/PHPCSStandards/PHPCSExtra/pull/108
[#109]: https://github.com/PHPCSStandards/PHPCSExtra/pull/109
[#110]: https://github.com/PHPCSStandards/PHPCSExtra/pull/110
[#114]: https://github.com/PHPCSStandards/PHPCSExtra/pull/114
[#115]: https://github.com/PHPCSStandards/PHPCSExtra/pull/115
[#116]: https://github.com/PHPCSStandards/PHPCSExtra/pull/116
[#117]: https://github.com/PHPCSStandards/PHPCSExtra/pull/117
[#118]: https://github.com/PHPCSStandards/PHPCSExtra/pull/118
[#119]: https://github.com/PHPCSStandards/PHPCSExtra/pull/119
[#120]: https://github.com/PHPCSStandards/PHPCSExtra/pull/120
[#121]: https://github.com/PHPCSStandards/PHPCSExtra/pull/121
[#122]: https://github.com/PHPCSStandards/PHPCSExtra/pull/122
[#123]: https://github.com/PHPCSStandards/PHPCSExtra/pull/123
[#124]: https://github.com/PHPCSStandards/PHPCSExtra/pull/124
[#126]: https://github.com/PHPCSStandards/PHPCSExtra/pull/126
[#128]: https://github.com/PHPCSStandards/PHPCSExtra/pull/128
[#130]: https://github.com/PHPCSStandards/PHPCSExtra/pull/130
[#134]: https://github.com/PHPCSStandards/PHPCSExtra/pull/134
[#135]: https://github.com/PHPCSStandards/PHPCSExtra/pull/135
[#137]: https://github.com/PHPCSStandards/PHPCSExtra/pull/137
[#140]: https://github.com/PHPCSStandards/PHPCSExtra/pull/140
[#142]: https://github.com/PHPCSStandards/PHPCSExtra/pull/142
[#143]: https://github.com/PHPCSStandards/PHPCSExtra/pull/143
[#144]: https://github.com/PHPCSStandards/PHPCSExtra/pull/144
[#146]: https://github.com/PHPCSStandards/PHPCSExtra/pull/146
[#147]: https://github.com/PHPCSStandards/PHPCSExtra/pull/147
[#148]: https://github.com/PHPCSStandards/PHPCSExtra/pull/148
[#149]: https://github.com/PHPCSStandards/PHPCSExtra/pull/149
[#150]: https://github.com/PHPCSStandards/PHPCSExtra/pull/150
[#151]: https://github.com/PHPCSStandards/PHPCSExtra/pull/151
[#152]: https://github.com/PHPCSStandards/PHPCSExtra/pull/152
[#153]: https://github.com/PHPCSStandards/PHPCSExtra/pull/153
[#154]: https://github.com/PHPCSStandards/PHPCSExtra/pull/154
[#155]: https://github.com/PHPCSStandards/PHPCSExtra/pull/155
[#158]: https://github.com/PHPCSStandards/PHPCSExtra/pull/158
[#159]: https://github.com/PHPCSStandards/PHPCSExtra/pull/159
[#160]: https://github.com/PHPCSStandards/PHPCSExtra/pull/160
[#161]: https://github.com/PHPCSStandards/PHPCSExtra/pull/161
[#162]: https://github.com/PHPCSStandards/PHPCSExtra/pull/162
[#163]: https://github.com/PHPCSStandards/PHPCSExtra/pull/163
[#164]: https://github.com/PHPCSStandards/PHPCSExtra/pull/164
[#165]: https://github.com/PHPCSStandards/PHPCSExtra/pull/165
[#166]: https://github.com/PHPCSStandards/PHPCSExtra/pull/166
[#167]: https://github.com/PHPCSStandards/PHPCSExtra/pull/167
[#168]: https://github.com/PHPCSStandards/PHPCSExtra/pull/168
[#169]: https://github.com/PHPCSStandards/PHPCSExtra/pull/169
[#170]: https://github.com/PHPCSStandards/PHPCSExtra/pull/170
[#171]: https://github.com/PHPCSStandards/PHPCSExtra/pull/171
[#172]: https://github.com/PHPCSStandards/PHPCSExtra/pull/172
[#173]: https://github.com/PHPCSStandards/PHPCSExtra/pull/173
[#175]: https://github.com/PHPCSStandards/PHPCSExtra/pull/175
[#176]: https://github.com/PHPCSStandards/PHPCSExtra/pull/176
[#177]: https://github.com/PHPCSStandards/PHPCSExtra/pull/177
[#178]: https://github.com/PHPCSStandards/PHPCSExtra/pull/178
[#180]: https://github.com/PHPCSStandards/PHPCSExtra/pull/180
[php-manual-dirname]: https://www.php.net/function.dirname
[php-rfc-negative_array_index]: https://wiki.php.net/rfc/negative_array_index
[ESLint "no lonely if"]: https://eslint.org/docs/rules/no-lonely-if
[PHPCSUtils 1.0.0-alpha4]: https://github.com/PHPCSStandards/PHPCSUtils/releases/tag/1.0.0-alpha4
## [1.0.0-alpha3] - 2020-06-29
### Added
#### Universal
* :wrench: :books: New `Universal.Arrays.DisallowShortArraySyntax` sniff to disallow short array syntax. [#40]
In contrast to the PHPCS native `Generic.Arrays.DisallowShortArraySyntax` sniff, this sniff will ignore short list syntax and not cause parse errors when the fixer is used.
* :wrench: :bar_chart: :books: New `Universal.Constants.UppercaseMagicConstants` sniff to enforce that PHP native magic constants are in uppercase. [#64]
* :bar_chart: :books: New `Universal.Namespaces.DisallowDeclarationWithoutName` sniff to disallow namespace declarations without a namespace name. [#50]
* :bar_chart: :books: New `Universal.Operators.DisallowLogicalAndOr` sniff to enforce the use of the boolean `&&` and `||` operators instead of the logical `and`/`or` operators. [#52]
Note: as the [operator precedence] of the logical operators is significantly lower than the operator precedence of boolean operators, this sniff does not contain an auto-fixer.
* :bar_chart: :books: New `Universal.Operators.DisallowShortTernary` sniff to disallow the use of short ternaries `?:`. [#42]
While short ternaries are useful when used correctly, the principle of them is often misunderstood and they are more often than not used incorrectly, leading to hard to debug issues and/or PHP warnings/notices.
* :wrench: :bar_chart: :books: New `Universal.Operators.DisallowStandalonePostIncrementDecrement` sniff disallow the use of post-in/decrements in stand-alone statements and discourage the use of multiple increment/decrement operators in a stand-alone statement. [#65]
* :wrench: :bar_chart: :books: New `Universal.Operators.StrictComparisons` sniff to enforce the use of strict comparisons. [#48]
Warning: the auto-fixer for this sniff _may_ cause bugs in applications and should be used with care! This is considered a _risky_ fixer.
* :wrench: :bar_chart: :books: New `Universal.OOStructures.AlphabeticExtendsImplements` sniff to verify that the names used in a class "implements" statement or an interface "extends" statement are listed in alphabetic order. [#55]
* This sniff contains a public `orderby` property to determine the sort order to use for the statement.
If all names used are unqualified, the sort order won't make a difference.
However, if one or more of the names are partially or fully qualified, the chosen sort order will determine how the sorting between unqualified, partially and fully qualified names is handled.
The sniff supports two sort order options:
- _'name'_ : sort by the interface name only (default);
- _'full'_ : sort by the full name as used in the statement (without leading backslash).
In both cases, the sorting will be done using natural sort, case-insensitive.
* The sniff has modular error codes to allow for selective inclusion/exclusion:
- `ImplementsWrongOrder` - for "class implements" statements.
- `ImplementsWrongOrderWithComments` - for "class implements" statements interlaced with comments. These will not be auto-fixed.
- `ExtendsWrongOrder` - for "interface extends" statements.
- `ExtendsWrongOrderWithComments` - for "interface extends" statements interlaced with comments. These will not be auto-fixed.
* When fixing, the existing spacing between the names in an `implements`/`extends` statement will not be maintained.
The fixer will separate each name with a comma and one space.
If alternative formatting is desired, a sniff which will check and fix the formatting should be added to the ruleset.
* :wrench: :bar_chart: :books: New `Universal.UseStatements.LowercaseFunctionConst` sniff to enforce that `function` and `const` keywords when used in an import `use` statement are always lowercase. [#58]
* :wrench: :bar_chart: :books: New `Universal.UseStatements.NoLeadingBackslash` sniff to verify that a name being imported in an import `use` statement does not start with a leading backslash. [#46]
Names in import `use` statements should always be fully qualified, so a leading backslash is not needed and it is strongly recommended not to use one.
This sniff handles all types of import use statements supported by PHP, in contrast to other sniffs for the same in, for instance, the PSR12 or the Slevomat standard, which are incomplete.
* :wrench: :books: New `Universal.WhiteSpace.DisallowInlineTabs` sniff to enforce using spaces for mid-line alignment. [#43]
### Changed
#### Other
* The `master` branch has been renamed to `stable`.
* Composer: The version requirements for the [Composer PHPCS plugin] have been widened to allow for version 0.7.0 which supports Composer 2.0.0. [#62]
* Various housekeeping.
[#40]: https://github.com/PHPCSStandards/PHPCSExtra/pull/40
[#42]: https://github.com/PHPCSStandards/PHPCSExtra/pull/42
[#43]: https://github.com/PHPCSStandards/PHPCSExtra/pull/43
[#46]: https://github.com/PHPCSStandards/PHPCSExtra/pull/46
[#48]: https://github.com/PHPCSStandards/PHPCSExtra/pull/48
[#50]: https://github.com/PHPCSStandards/PHPCSExtra/pull/50
[#52]: https://github.com/PHPCSStandards/PHPCSExtra/pull/52
[#55]: https://github.com/PHPCSStandards/PHPCSExtra/pull/55
[#58]: https://github.com/PHPCSStandards/PHPCSExtra/pull/58
[#62]: https://github.com/PHPCSStandards/PHPCSExtra/pull/62
[#64]: https://github.com/PHPCSStandards/PHPCSExtra/pull/64
[#65]: https://github.com/PHPCSStandards/PHPCSExtra/pull/65
[operator precedence]: https://www.php.net/language.operators.precedence
## [1.0.0-alpha2] - 2020-02-18
### Added
#### Universal
* :wrench: :bar_chart: :books: New `Universal.ControlStructures.DisallowAlternativeSyntax` sniff to disallow using the alternative syntax for control structures. [#23]
- This sniff contains a `allowWithInlineHTML` property to allow alternative syntax when inline HTML is used within the control structure. In all other cases, the use of the alternative syntax will still be disallowed.
- The sniff has modular error codes to allow for making exceptions based on specific control structures and/or specific control structures in combination with inline HTML.
* :bar_chart: `Universal.UseStatements.DisallowUseClass/Function/Const`: new, additional metrics about the import source will be shown in the `info` report. [#25]
#### Other
* Readme: installation instructions and sniff list. [#26]
### Changed
#### Universal
* `Universal.Arrays.DuplicateArrayKey`: wording of the error message. [#18]
* `Universal.UseStatements.DisallowUseClass/Function/Const`: the error codes have been made more modular. [#25]
Each of these sniffs now has four additional error codes:
<ul>
<li><code>FoundSameNamespace</code>, <code>FoundSameNamespaceWithAlias</code> for <code>use</code> statements importing from the same namespace;</li>
<li><code>FoundGlobalNamespace</code>, <code>FoundGlobalNamespaceWithAlias</code> for <code>use</code> statements importing from the global namespace, like import statements for PHP native classes, functions and constants.</li>
</ul>
In all other circumstances, the existing error codes <code>FoundWithAlias</code> and <code>FoundWithoutAlias</code> will continue to be used.
#### Other
* Improved formatting of the CLI documentation which can be viewed using `--generator=text`. [#17]
* Various housekeeping.
### Fixed
#### Universal
* `Universal.Arrays.DuplicateArrayKey`: improved handling of parse errors. [#34]
* `Universal.ControlStructures.IfElseDeclaration`: the fixer will now respect tab indentation. [#19]
* `Universal.UseStatements.DisallowUseClass/Function/Const`: the determination of whether a import is aliased in now done in a case-insensitive manner. [#25]
* `Universal.UseStatements.DisallowUseClass/Function/Const`: an import from the global namespace would previously always be seen as non-aliased, even when it was aliased. [#25]
* `Universal.UseStatements.DisallowUseClass/Function/Const`: improved tolerance for `use` import statements with leading backslashes. [#25]
[#17]: https://github.com/PHPCSStandards/PHPCSExtra/pull/17
[#18]: https://github.com/PHPCSStandards/PHPCSExtra/pull/18
[#19]: https://github.com/PHPCSStandards/PHPCSExtra/pull/19
[#23]: https://github.com/PHPCSStandards/PHPCSExtra/pull/23
[#25]: https://github.com/PHPCSStandards/PHPCSExtra/pull/25
[#26]: https://github.com/PHPCSStandards/PHPCSExtra/pull/26
[#34]: https://github.com/PHPCSStandards/PHPCSExtra/pull/34
## 1.0.0-alpha1 - 2020-01-23
Initial alpha release containing:
* A `NormalizedArrays` standard which will contain a full set of sniffs to check the formatting of array declarations.
* A `Universal` standard which will contain a collection of universal sniffs.
DO NOT INCLUDE THIS AS A STANDARD.
`Universal`, like the upstream PHPCS `Generic` standard, contains sniffs which contradict each other.
Include individual sniffs from this standard in a custom project/company ruleset to use them.
This initial alpha release contains the following sniffs:
### NormalizedArrays
* :wrench: :bar_chart: :books: `NormalizedArrays.Arrays.ArrayBraceSpacing`: enforce consistent spacing for the open/close braces of array declarations.
The sniff allows for having different settings for:
- Space between the array keyword and the open parenthesis for long arrays via the `keywordSpacing` property.
Accepted values: (int) number of spaces or `false` to turn this check off. Defaults to `0` spaces.
- Spaces on the inside of the braces for empty arrays via the `spacesWhenEmpty` property.
Accepted values: (string) `newline`, (int) number of spaces or `false` to turn this check off. Defaults to `0` spaces.
- Spaces on the inside of the braces for single-line arrays via the `spacesSingleLine` property;
Accepted values: (int) number of spaces or `false` to turn this check off. Defaults to `0` spaces.
- Spaces on the inside of the braces for multi-line arrays via the `spacesMultiLine` property.
Accepted values: (string) `newline`, (int) number of spaces or `false` to turn this check off. Defaults to `newline`.
Note: if any of the above properties are set to `newline`, it is recommended to also include an array indentation sniff. This sniff will not handle the indentation.
* :wrench: :bar_chart: :books: `NormalizedArrays.Arrays.CommaAfterLast`: enforce/forbid a comma after the last item in an array declaration.
By default, this sniff will:
<ul>
<li>forbid a comma after the last array item for single-line arrays.</li>
<li>enforce a comma after the last array item for multi-line arrays.</li>
</ul>
This can be changed for each type or array individually by setting the <code>singleLine</code> or <code>multiLine</code> properties in a custom ruleset.
The valid values are: <code>enforce</code>, <code>forbid</code> or <code>skip</code> to not check the comma after the last array item for a particular type of array.
### Universal
* :books: `Universal.Arrays.DuplicateArrayKey`: detects duplicate array keys in array declarations.
* :books: `Universal.Arrays.MixedArrayKeyTypes`: best practice sniff: don't use a mix of integer and numeric keys for array items.
* :books: `Universal.Arrays.MixedKeyedUnkeyedArray`: best practice sniff: don't use a mix of keyed and unkeyed array items.
* :wrench: :bar_chart: :books: `Universal.ControlStructures.IfElseDeclaration`: verify that else(if) statements with braces are on a new line.
* :wrench: :bar_chart: :books: `Universal.Lists.DisallowLongListSyntax`: disallow the use of long `list`s.
* :wrench: :bar_chart: :books: `Universal.Lists.DisallowShortListSyntax`: disallow the use of short lists.
* :bar_chart: :books: `Universal.Namespaces.DisallowCurlyBraceSyntax`: disallow the use of the alternative namespace declaration syntax using curly braces.
* :bar_chart: :books: `Universal.Namespaces.EnforceCurlyBraceSyntax`: enforce the use of the alternative namespace syntax using curly braces.
* :books: `Universal.Namespaces.OneDeclarationPerFile`: disallow the use of multiple namespaces within a file.
* :bar_chart: :books: `Universal.UseStatements.DisallowUseClass`: forbid using import use statements for classes/traits/interfaces.
Individual sub-types can be allowed by excluding specific error codes.
* :bar_chart: :books: `Universal.UseStatements.DisallowUseConst`: forbid using import use statements for constants.
Individual sub-types can be allowed by excluding specific error codes.
* :bar_chart: :books: `Universal.UseStatements.DisallowUseFunction`: forbid using import use statements for functions.
Individual sub-types can be allowed by excluding specific error codes.
[Composer PHPCS plugin]: https://github.com/PHPCSStandards/composer-installer
[php_version-config]: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Configuration-Options#setting-the-php-version
[Unreleased]: https://github.com/PHPCSStandards/PHPCSExtra/compare/stable...HEAD
[1.1.1]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.1.0...1.1.1
[1.1.0]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.4...1.1.0
[1.0.4]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.3...1.0.4
[1.0.3]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.2...1.0.3
[1.0.2]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.1...1.0.2
[1.0.1]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.0...1.0.1
[1.0.0]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.0-rc1...1.0.0
[1.0.0-RC1]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.0-alpha3...1.0.0-rc1
[1.0.0-alpha3]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.0-alpha2...1.0.0-alpha3
[1.0.0-alpha2]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.0-alpha1...1.0.0-alpha2
[@anomiex]: https://github.com/anomiex
[@derickr]: https://github.com/derickr
[@GaryJones]: https://github.com/GaryJones
[@szepeviktor]: https://github.com/szepeviktor

165
vendor/phpcsstandards/phpcsextra/LICENSE vendored Normal file
View file

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser 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
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View file

@ -0,0 +1,40 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Function Calls to Dirname"
>
<standard>
<![CDATA[
PHP >= 5.3: Usage of dirname(__FILE__) can be replaced with __DIR__.
]]>
</standard>
<code_comparison>
<code title="Valid: Using __DIR__.">
<![CDATA[
$path = <em>__DIR__</em>;
]]>
</code>
<code title="Invalid: dirname(__FILE__).">
<![CDATA[
$path = <em>dirname(__FILE__)</em>;
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
PHP >= 7.0: Nested calls to dirname() can be replaced by using dirname() with the $levels parameter.
]]>
</standard>
<code_comparison>
<code title="Valid: Using dirname() with the $levels parameter.">
<![CDATA[
$path = <em>dirname($file, 3)</em>;
]]>
</code>
<code title="Invalid: Nested function calls to dirname().">
<![CDATA[
$path = <em>dirname(dirname(dirname($file)))</em>;
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,382 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Modernize\Sniffs\FunctionCalls;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\BackCompat\Helper;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\Context;
use PHPCSUtils\Utils\PassedParameters;
/**
* Detect `dirname(__FILE__)` and nested uses of `dirname()`.
*
* @since 1.0.0
*/
final class DirnameSniff implements Sniff
{
/**
* PHP version as configured or 0 if unknown.
*
* @since 1.1.1
*
* @var int
*/
private $phpVersion;
/**
* Registers the tokens that this sniff wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_STRING];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
if (isset($this->phpVersion) === false || \defined('PHP_CODESNIFFER_IN_TESTS')) {
// Set default value to prevent this code from running every time the sniff is triggered.
$this->phpVersion = 0;
$phpVersion = Helper::getConfigData('php_version');
if ($phpVersion !== null) {
$this->phpVersion = (int) $phpVersion;
}
}
if ($this->phpVersion !== 0 && $this->phpVersion < 50300) {
// PHP version too low, nothing to do.
return;
}
$tokens = $phpcsFile->getTokens();
if (\strtolower($tokens[$stackPtr]['content']) !== 'dirname') {
// Not our target.
return;
}
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($nextNonEmpty === false
|| $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS
|| isset($tokens[$nextNonEmpty]['parenthesis_owner']) === true
) {
// Not our target.
return;
}
if (isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false) {
// Live coding or parse error, ignore.
return;
}
if (Context::inAttribute($phpcsFile, $stackPtr) === true) {
// Class instantiation in attribute, not function call.
return;
}
// Check if it is really a function call to the global function.
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if (isset(Collections::objectOperators()[$tokens[$prevNonEmpty]['code']]) === true
|| $tokens[$prevNonEmpty]['code'] === \T_NEW
) {
// Method call, class instantiation or other "not our target".
return;
}
if ($tokens[$prevNonEmpty]['code'] === \T_NS_SEPARATOR) {
$prevPrevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevNonEmpty - 1), null, true);
if ($tokens[$prevPrevToken]['code'] === \T_STRING
|| $tokens[$prevPrevToken]['code'] === \T_NAMESPACE
) {
// Namespaced function.
return;
}
}
/*
* As of here, we can be pretty sure this is a function call to the global function.
*/
$opener = $nextNonEmpty;
$closer = $tokens[$nextNonEmpty]['parenthesis_closer'];
$parameters = PassedParameters::getParameters($phpcsFile, $stackPtr);
$paramCount = \count($parameters);
if (empty($parameters) || $paramCount > 2) {
// No parameters or too many parameter.
return;
}
$pathParam = PassedParameters::getParameterFromStack($parameters, 1, 'path');
if ($pathParam === false) {
// If the path parameter doesn't exist, there's nothing to do.
return;
}
$levelsParam = PassedParameters::getParameterFromStack($parameters, 2, 'levels');
if ($levelsParam === false && $paramCount === 2) {
// There must be a typo in the param name or an otherwise stray parameter. Ignore.
return;
}
/*
* PHP 5.3+: Detect use of dirname(__FILE__).
*/
if (\strtoupper($pathParam['clean']) === '__FILE__') {
$levelsValue = false;
// Determine if the issue is auto-fixable.
$hasComment = $phpcsFile->findNext(Tokens::$commentTokens, ($opener + 1), $closer);
$fixable = ($hasComment === false);
if ($fixable === true) {
$levelsValue = $this->getLevelsValue($phpcsFile, $levelsParam);
if ($levelsParam !== false && $levelsValue === false) {
// Can't autofix if we don't know the value of the $levels parameter.
$fixable = false;
}
}
$error = 'Use the __DIR__ constant instead of calling dirname(__FILE__) (PHP >= 5.3)';
$code = 'FileConstant';
// Throw the error.
if ($fixable === false) {
$phpcsFile->addError($error, $stackPtr, $code);
return;
}
$fix = $phpcsFile->addFixableError($error, $stackPtr, $code);
if ($fix === true) {
if ($levelsParam === false || $levelsValue === 1) {
// No $levels or $levels set to 1: we can replace the complete function call.
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken($stackPtr, '__DIR__');
for ($i = ($stackPtr + 1); $i <= $closer; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
// Remove potential leading \.
if ($tokens[$prevNonEmpty]['code'] === \T_NS_SEPARATOR) {
$phpcsFile->fixer->replaceToken($prevNonEmpty, '');
}
$phpcsFile->fixer->endChangeset();
} else {
// We can replace the $path parameter and will need to adjust the $levels parameter.
$filePtr = $phpcsFile->findNext(\T_FILE, $pathParam['start'], ($pathParam['end'] + 1));
$levelsPtr = $phpcsFile->findNext(\T_LNUMBER, $levelsParam['start'], ($levelsParam['end'] + 1));
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken($filePtr, '__DIR__');
$phpcsFile->fixer->replaceToken($levelsPtr, ($levelsValue - 1));
$phpcsFile->fixer->endChangeset();
}
}
return;
}
/*
* PHP 7.0+: Detect use of nested calls to dirname().
*/
if ($this->phpVersion !== 0 && $this->phpVersion < 70000) {
// No need to check for this issue if the PHP version would not allow for it anyway.
return;
}
if (\preg_match('`^\s*\\\\?dirname\s*\(`i', $pathParam['clean']) !== 1) {
return;
}
/*
* Check if there is something _behind_ the nested dirname() call within the same parameter.
*
* Note: the findNext() calls are safe and will always match the dirname() function call
* as otherwise the above regex wouldn't have matched.
*/
$innerDirnamePtr = $phpcsFile->findNext(\T_STRING, $pathParam['start'], ($pathParam['end'] + 1));
$innerOpener = $phpcsFile->findNext(\T_OPEN_PARENTHESIS, ($innerDirnamePtr + 1), ($pathParam['end'] + 1));
if (isset($tokens[$innerOpener]['parenthesis_closer']) === false) {
// Shouldn't be possible.
return; // @codeCoverageIgnore
}
$innerCloser = $tokens[$innerOpener]['parenthesis_closer'];
if ($innerCloser !== $pathParam['end']) {
$hasContentAfter = $phpcsFile->findNext(
Tokens::$emptyTokens,
($innerCloser + 1),
($pathParam['end'] + 1),
true
);
if ($hasContentAfter !== false) {
// Matched code like: `dirname(dirname($file) . 'something')`. Ignore.
return;
}
}
/*
* Determine if this is an auto-fixable error.
*/
// Step 1: Are there comments ? If so, not auto-fixable as we don't want to remove comments.
$fixable = true;
$outerLevelsValue = false;
$innerParameters = [];
$innerLevelsParam = false;
$innerLevelsValue = false;
for ($i = ($opener + 1); $i < $closer; $i++) {
if (isset(Tokens::$commentTokens[$tokens[$i]['code']])) {
$fixable = false;
break;
}
if ($tokens[$i]['code'] === \T_OPEN_PARENTHESIS
&& isset($tokens[$i]['parenthesis_closer'])
) {
// Skip over everything within the nested dirname() function call.
$i = $tokens[$i]['parenthesis_closer'];
}
}
// Step 2: Does the `$levels` parameter exist for the outer dirname() call and if so, is it usable ?
if ($fixable === true) {
$outerLevelsValue = $this->getLevelsValue($phpcsFile, $levelsParam);
if ($levelsParam !== false && $outerLevelsValue === false) {
// Can't autofix if we don't know the value of the $levels parameter.
$fixable = false;
}
}
// Step 3: Does the `$levels` parameter exist for the inner dirname() call and if so, is it usable ?
if ($fixable === true) {
$innerParameters = PassedParameters::getParameters($phpcsFile, $innerDirnamePtr);
$innerLevelsParam = PassedParameters::getParameterFromStack($innerParameters, 2, 'levels');
$innerLevelsValue = $this->getLevelsValue($phpcsFile, $innerLevelsParam);
if ($innerLevelsParam !== false && $innerLevelsValue === false) {
// Can't autofix if we don't know the value of the $levels parameter.
$fixable = false;
}
}
/*
* Throw the error.
*/
$error = 'Pass the $levels parameter to the dirname() call instead of using nested dirname() calls';
$error .= ' (PHP >= 7.0)';
$code = 'Nested';
if ($fixable === false) {
$phpcsFile->addError($error, $stackPtr, $code);
return;
}
$fix = $phpcsFile->addFixableError($error, $stackPtr, $code);
if ($fix === false) {
return;
}
/*
* Fix the error.
*/
$phpcsFile->fixer->beginChangeset();
// Remove the info in the _outer_ param call.
for ($i = $opener; $i < $innerOpener; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
for ($i = ($innerCloser + 1); $i <= $closer; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
if ($innerLevelsParam !== false) {
// Inner $levels parameter already exists, just adjust the value.
$innerLevelsPtr = $phpcsFile->findNext(
\T_LNUMBER,
$innerLevelsParam['start'],
($innerLevelsParam['end'] + 1)
);
$phpcsFile->fixer->replaceToken($innerLevelsPtr, ($innerLevelsValue + $outerLevelsValue));
} else {
// Inner $levels parameter does not exist yet. We need to add it.
$content = ', ';
$prevBeforeCloser = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($innerCloser - 1), null, true);
if ($tokens[$prevBeforeCloser]['code'] === \T_COMMA) {
// Trailing comma found, no need to add the comma.
$content = ' ';
}
$innerPathParam = PassedParameters::getParameterFromStack($innerParameters, 1, 'path');
if (isset($innerPathParam['name_token']) === true) {
// Non-named param cannot follow named param, so add param name.
$content .= 'levels: ';
}
$content .= ($innerLevelsValue + $outerLevelsValue);
$phpcsFile->fixer->addContentBefore($innerCloser, $content);
}
$phpcsFile->fixer->endChangeset();
}
/**
* Determine the value of the $levels parameter passed to dirname().
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param array<string, int|string>|false $levelsParam The information about the parameter as retrieved
* via PassedParameters::getParameterFromStack().
*
* @return int|false Integer levels value or FALSE if the levels value couldn't be determined.
*/
private function getLevelsValue($phpcsFile, $levelsParam)
{
if ($levelsParam === false) {
return 1;
}
$ignore = Tokens::$emptyTokens;
$ignore[] = \T_LNUMBER;
$hasNonNumber = $phpcsFile->findNext($ignore, $levelsParam['start'], ($levelsParam['end'] + 1), true);
if ($hasNonNumber !== false) {
return false;
}
return (int) $levelsParam['clean'];
}
}

View file

@ -0,0 +1,5 @@
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Modernize" namespace="PHPCSExtra\Modernize" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/squizlabs/PHP_CodeSniffer/master/phpcs.xsd">
<description>A collection of sniffs to detect code modernization opportunities.</description>
</ruleset>

View file

@ -0,0 +1,94 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Array Brace Spacing"
>
<standard>
<![CDATA[
There should be no space between the "array" keyword and the array open brace.
]]>
</standard>
<code_comparison>
<code title="Valid: No space between the keyword and the open brace.">
<![CDATA[
$args = array(1, 2);
]]>
</code>
<code title="Invalid: Space between the keyword and the open brace.">
<![CDATA[
$args = array<em> </em>(1, 2);
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
There should be no space between the array open brace and the array close brace for an empty array.
]]>
</standard>
<code_comparison>
<code title="Valid: No space between the braces.">
<![CDATA[
$args = array();
$args = [];
]]>
</code>
<code title="Invalid: Space between the braces.">
<![CDATA[
$args = array(<em> </em>);
$args = [<em> </em>];
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
There should be no space after the array open brace and before the array close brace in a single-line array.
]]>
</standard>
<code_comparison>
<code title="Valid: No space on the inside of the braces.">
<![CDATA[
$args = array(1, 2);
$args = [1, 2];
]]>
</code>
<code title="Invalid: Space on the inside of the braces.">
<![CDATA[
$args = array(<em> </em>1, 2<em> </em>);
$args = [<em> </em>1, 2<em> </em>];
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
There should be a new line after the array open brace and before the array close brace in a multi-line array.
]]>
</standard>
<code_comparison>
<code title="Valid: One new line after the open brace and before the close brace.">
<![CDATA[
$args = array(<em>
</em> 1,
2<em>
</em>);
$args = [<em>
</em> 1,
2<em>
</em>];
]]>
</code>
<code title="Invalid: No new lines after the open brace and/or before the close brace.">
<![CDATA[
$args = array(1,
2);
$args = [1,
2];
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,43 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Comma After Last Array Item"
>
<standard>
<![CDATA[
For single-line arrays, there should be <em>no</em> comma after the last array item.
However, for multi-line arrays, there <em>should</em> be a comma after the last array item.
]]>
</standard>
<code_comparison>
<code title="Valid: Single-line array without a comma after the last item">
<![CDATA[
$args = array(1, 2, 3);
]]>
</code>
<code title="Invalid: Single-line array with a comma after the last item">
<![CDATA[
$args = array(1, 2, 3<em>,</em> );
]]>
</code>
</code_comparison>
<code_comparison>
<code title="Valid: Multi-line array with a comma after the last item">
<![CDATA[
$args = [
1 => 'foo',
2 => 'bar'<em>,</em>
];
]]>
</code>
<code title="Invalid: Multi-line array without a comma after the last item">
<![CDATA[
$args = [
1 => 'foo',
2 => 'bar'
];
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,305 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\NormalizedArrays\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\Fixers\SpacesFixer;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\Arrays;
/**
* Enforce consistent spacing for the open/close braces of arrays.
*
* The sniff allows for having different settings for:
* - space between the `array` keyword and the open parenthesis for long arrays;
* - spaces on the inside of the braces for single-line arrays;
* - spaces on the inside of the braces for multi-line arrays;
* - spaces on the inside of the braces for empty arrays.
*
* Note: If 'newline' is expected and _no_ new line is encountered, a new line will be
* added, but no assumptions will be made about the intended indentation of the code.
* This should be handled by a (separate) indentation sniff.
*
* @since 1.0.0
*/
final class ArrayBraceSpacingSniff implements Sniff
{
/**
* Number of spaces which should be between the `array` keyword and the open parenthesis for long arrays.
*
* Accepted values:
* - (int) number of spaces.
* - or `false` to turn this check off.
*
* Defaults to 0 spaces.
*
* @since 1.0.0
*
* @var int|false
*/
public $keywordSpacing = 0;
/**
* Number of spaces to enforce between the braces for an empty array.
*
* Accepted values:
* - (string) 'newline'
* - (int) number of spaces.
* - or `false` to turn this check off.
*
* Defaults to 0 spaces.
*
* @since 1.0.0
*
* @var string|int|false
*/
public $spacesWhenEmpty = 0;
/**
* Number of spaces which should be on the inside of array braces for a single-line array.
*
* Accepted values:
* - (int) number of spaces.
* - or `false` to turn this check off.
*
* Defaults to 0 spaces.
*
* @since 1.0.0
*
* @var int|false
*/
public $spacesSingleLine = 0;
/**
* Number of spaces which should be on the inside of array braces for a multi-line array.
*
* Accepted values:
* - (string) 'newline'
* - (int) number of spaces.
* - or `false` to turn this check off.
*
* Defaults to 'newline'.
*
* @since 1.0.0
*
* @var string|int|false
*/
public $spacesMultiLine = 'newline';
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return Collections::arrayOpenTokensBC();
}
/**
* Processes this test when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token was found.
* @param int $stackPtr The position in the PHP_CodeSniffer
* file's token stack where the token
* was found.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
/*
* Normalize the public settings.
*/
if ($this->keywordSpacing !== false) {
$this->keywordSpacing = \max((int) $this->keywordSpacing, 0);
}
if ($this->spacesSingleLine !== false) {
$this->spacesSingleLine = \max((int) $this->spacesSingleLine, 0);
}
if ($this->spacesMultiLine !== false && $this->spacesMultiLine !== 'newline') {
$this->spacesMultiLine = \max((int) $this->spacesMultiLine, 0);
}
if ($this->spacesWhenEmpty !== false && $this->spacesWhenEmpty !== 'newline') {
$this->spacesWhenEmpty = \max((int) $this->spacesWhenEmpty, 0);
}
if ($this->keywordSpacing === false
&& $this->spacesSingleLine === false
&& $this->spacesMultiLine === false
&& $this->spacesWhenEmpty === false
) {
// Nothing to do. Why was the sniff turned on at all ?
return;
}
$openClose = Arrays::getOpenClose($phpcsFile, $stackPtr);
if ($openClose === false) {
// Live coding, short list or real square brackets.
return;
}
$tokens = $phpcsFile->getTokens();
$opener = $openClose['opener'];
$closer = $openClose['closer'];
/*
* Check the spacing between the array keyword and the open parenthesis for long arrays.
*/
if ($tokens[$stackPtr]['code'] === \T_ARRAY && $this->keywordSpacing !== false) {
$error = 'There should be %s between the "array" keyword and the open parenthesis. Found: %s';
$code = 'SpaceAfterKeyword';
SpacesFixer::checkAndFix(
$phpcsFile,
$stackPtr,
$opener,
$this->keywordSpacing,
$error,
$code,
'error',
0,
'Space between array keyword and open brace'
);
}
/*
* Check for empty arrays.
*/
$nextNonWhiteSpace = $phpcsFile->findNext(\T_WHITESPACE, ($opener + 1), null, true);
if ($nextNonWhiteSpace === $closer) {
if ($this->spacesWhenEmpty === false) {
// Check was turned off.
return;
}
$error = 'There should be %s between the array opener and closer for an empty array. Found: %s';
$code = 'EmptyArraySpacing';
SpacesFixer::checkAndFix(
$phpcsFile,
$opener,
$closer,
$this->spacesWhenEmpty,
$error,
$code,
'error',
0,
'Space between open and close brace for an empty array'
);
return;
}
/*
* Check non-empty arrays.
*/
if ($tokens[$opener]['line'] === $tokens[$closer]['line']) {
// Single line array.
if ($this->spacesSingleLine === false) {
// Check was turned off.
return;
}
$error = 'Expected %s after the array opener in a single line array. Found: %s';
$code = 'SpaceAfterArrayOpenerSingleLine';
SpacesFixer::checkAndFix(
$phpcsFile,
$opener,
$phpcsFile->findNext(\T_WHITESPACE, ($opener + 1), null, true),
$this->spacesSingleLine,
$error,
$code,
'error',
0,
'Space after array opener, single line array'
);
$error = 'Expected %s before the array closer in a single line array. Found: %s';
$code = 'SpaceBeforeArrayCloserSingleLine';
SpacesFixer::checkAndFix(
$phpcsFile,
$closer,
$phpcsFile->findPrevious(\T_WHITESPACE, ($closer - 1), null, true),
$this->spacesSingleLine,
$error,
$code,
'error',
0,
'Space before array closer, single line array'
);
return;
}
// Multi-line array.
if ($this->spacesMultiLine === false) {
// Check was turned off.
return;
}
$error = 'Expected %s after the array opener in a multi line array. Found: %s';
$code = 'SpaceAfterArrayOpenerMultiLine';
$nextNonWhitespace = $phpcsFile->findNext(\T_WHITESPACE, ($opener + 1), null, true);
if ($this->spacesMultiLine === 'newline') {
// Check for a trailing comment after the array opener and allow for it.
if (($tokens[$nextNonWhitespace]['code'] === \T_COMMENT
|| isset(Tokens::$phpcsCommentTokens[$tokens[$nextNonWhitespace]['code']]) === true)
&& $tokens[$nextNonWhitespace]['line'] === $tokens[$opener]['line']
) {
// We found a trailing comment after array opener. Treat that as the opener instead.
$opener = $nextNonWhitespace;
$nextNonWhitespace = $phpcsFile->findNext(\T_WHITESPACE, ($opener + 1), null, true);
}
}
SpacesFixer::checkAndFix(
$phpcsFile,
$opener,
$nextNonWhitespace,
$this->spacesMultiLine,
$error,
$code,
'error',
0,
'Space after array opener, multi-line array'
);
$error = 'Expected %s before the array closer in a multi line array. Found: %s';
$code = 'SpaceBeforeArrayCloserMultiLine';
SpacesFixer::checkAndFix(
$phpcsFile,
$closer,
$phpcsFile->findPrevious(\T_WHITESPACE, ($closer - 1), null, true),
$this->spacesMultiLine,
$error,
$code,
'error',
0,
'Space before array closer, multi-line array'
);
}
}

View file

@ -0,0 +1,219 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\NormalizedArrays\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\Arrays;
/**
* Enforce/forbid a comma after the last item in an array declaration.
*
* Allows for different settings for single-line and multi-line arrays.
*
* @since 1.0.0
*/
final class CommaAfterLastSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = '%s array - comma after last item';
/**
* Whether or not to enforce a comma after the last array item in a single-line array.
*
* Valid values:
* - 'enforce' to always demand a comma after the last item in single-line arrays;
* - 'forbid' to disallow a comma after the last item in single-line arrays;
* - 'skip' to ignore single-line arrays.
*
* Defaults to: 'forbid'.
*
* @since 1.0.0
*
* @var string
*/
public $singleLine = 'forbid';
/**
* Whether or not to enforce a comma after the last array item in a multi-line array.
*
* Valid values:
* - 'enforce' to always demand a comma after the last item in single-line arrays;
* - 'forbid' to disallow a comma after the last item in single-line arrays;
* - 'skip' to ignore multi-line arrays.
*
* Defaults to: 'enforce'.
*
* @since 1.0.0
*
* @var string
*/
public $multiLine = 'enforce';
/**
* The input values to consider as valid for the public properties.
*
* Used for input validation.
*
* @since 1.0.0
*
* @var array<string, true>
*/
private $validValues = [
'enforce' => true,
'forbid' => true,
'skip' => true,
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return Collections::arrayOpenTokensBC();
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
// Validate the property input. Invalid values will result in the check being skipped.
if (isset($this->validValues[$this->singleLine]) === false) {
$this->singleLine = 'skip';
}
if (isset($this->validValues[$this->multiLine]) === false) {
$this->multiLine = 'skip';
}
$openClose = Arrays::getOpenClose($phpcsFile, $stackPtr);
if ($openClose === false) {
// Short list, real square bracket, live coding or parse error.
return;
}
$tokens = $phpcsFile->getTokens();
$opener = $openClose['opener'];
$closer = $openClose['closer'];
$action = $this->singleLine;
$phrase = 'single-line';
$errorCode = 'SingleLine';
if ($tokens[$opener]['line'] !== $tokens[$closer]['line']) {
$action = $this->multiLine;
$phrase = 'multi-line';
$errorCode = 'MultiLine';
}
if ($action === 'skip') {
// Nothing to do.
return;
}
$lastNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($closer - 1), $opener, true);
if ($lastNonEmpty === false || $lastNonEmpty === $opener) {
// Bow out: empty array.
return;
}
$isComma = ($tokens[$lastNonEmpty]['code'] === \T_COMMA);
$phpcsFile->recordMetric(
$stackPtr,
\sprintf(self::METRIC_NAME, \ucfirst($phrase)),
($isComma === true ? 'yes' : 'no')
);
switch ($action) {
case 'enforce':
if ($isComma === true) {
return;
}
$error = 'There should be a comma after the last array item in a %s array.';
$errorCode = 'Missing' . $errorCode;
$data = [$phrase];
$fix = $phpcsFile->addFixableError($error, $lastNonEmpty, $errorCode, $data);
if ($fix === true) {
$extraContent = ',';
if (($tokens[$lastNonEmpty]['code'] === \T_END_HEREDOC
|| $tokens[$lastNonEmpty]['code'] === \T_END_NOWDOC)
// Check for indentation, if indented, it's a PHP 7.3+ heredoc/nowdoc.
&& $tokens[$lastNonEmpty]['content'] === \ltrim($tokens[$lastNonEmpty]['content'])
) {
// Prevent parse errors in PHP < 7.3 which doesn't support flexible heredoc/nowdoc.
$extraContent = $phpcsFile->eolChar . $extraContent;
}
$phpcsFile->fixer->addContent($lastNonEmpty, $extraContent);
}
return;
case 'forbid':
if ($isComma === false) {
return;
}
$error = 'A comma after the last array item in a %s array is not allowed.';
$errorCode = 'Found' . $errorCode;
$data = [$phrase];
$fix = $phpcsFile->addFixableError($error, $lastNonEmpty, $errorCode, $data);
if ($fix === true) {
$start = $lastNonEmpty;
$end = $lastNonEmpty;
// Make sure we're not leaving a superfluous blank line behind.
$prevNonWhitespace = $phpcsFile->findPrevious(\T_WHITESPACE, ($lastNonEmpty - 1), $opener, true);
$nextNonWhitespace = $phpcsFile->findNext(\T_WHITESPACE, ($lastNonEmpty + 1), ($closer + 1), true);
if ($prevNonWhitespace !== false
&& $tokens[$prevNonWhitespace]['line'] < $tokens[$lastNonEmpty]['line']
&& $nextNonWhitespace !== false
&& $tokens[$nextNonWhitespace]['line'] > $tokens[$lastNonEmpty]['line']
) {
$start = ($prevNonWhitespace + 1);
}
$phpcsFile->fixer->beginChangeset();
for ($i = $start; $i <= $end; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}
return;
}
}
}

View file

@ -0,0 +1,5 @@
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="NormalizedArrays" namespace="PHPCSExtra\NormalizedArrays" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/squizlabs/PHP_CodeSniffer/master/phpcs.xsd">
<description>A ruleset for PHP_CodeSniffer to check arrays for normalized format.</description>
</ruleset>

View file

@ -0,0 +1,553 @@
PHPCSExtra
=====================================================
<div aria-hidden="true">
[![Latest Stable Version](https://poser.pugx.org/phpcsstandards/phpcsextra/v/stable)][phpcsextra-packagist]
[![Release Date of the Latest Version](https://img.shields.io/github/release-date/PHPCSStandards/PHPCSExtra.svg?maxAge=1800)](https://github.com/PHPCSStandards/PHPCSExtra/releases)
:construction:
[![Latest Unstable Version](https://img.shields.io/badge/unstable-dev--develop-e68718.svg?maxAge=2419200)](https://packagist.org/packages/phpcsstandards/phpcsextra#dev-develop)
[![Last Commit to Unstable](https://img.shields.io/github/last-commit/PHPCSStandards/PHPCSExtra/develop.svg)](https://github.com/PHPCSStandards/PHPCSExtra/commits/develop)
[![Minimum PHP Version](https://img.shields.io/packagist/php-v/phpcsstandards/phpcsextra.svg?maxAge=3600)][phpcsextra-packagist]
[![CS Build Status](https://github.com/PHPCSStandards/PHPCSExtra/actions/workflows/basics.yml/badge.svg?branch=develop)][gha-qa-results]
[![Test Build Status](https://github.com/PHPCSStandards/PHPCSExtra/actions/workflows/test.yml/badge.svg?branch=develop)][gha-test-results]
[![Tested on PHP 5.4 to 8.2](https://img.shields.io/badge/tested%20on-PHP%205.4%20|%205.5%20|%205.6%20|%207.0%20|%207.1%20|%207.2%20|%207.3%20|%207.4%20|%208.0%20|%208.1%20|%208.2-brightgreen.svg?maxAge=2419200)][gha-test-results]
[![Coverage Status](https://coveralls.io/repos/github/PHPCSStandards/PHPCSExtra/badge.svg)](https://coveralls.io/github/PHPCSStandards/PHPCSExtra)
[![License: LGPLv3](https://poser.pugx.org/phpcsstandards/phpcsextra/license)](https://github.com/PHPCSStandards/PHPCSExtra/blob/stable/LICENSE)
![Awesome](https://img.shields.io/badge/awesome%3F-yes!-brightgreen.svg)
</div>
* [Introduction](#introduction)
* [Minimum Requirements](#minimum-requirements)
* [Installation](#installation)
+ [Composer Project-based Installation](#composer-project-based-installation)
+ [Composer Global Installation](#composer-global-installation)
+ [Updating to a newer version](#updating-to-a-newer-version)
* [Features](#features)
* [Sniffs](#sniffs)
+ [Modernize](#modernize)
+ [NormalizedArrays](#normalizedarrays)
+ [Universal](#universal)
* [Contributing](#contributing)
* [License](#license)
Introduction
-------------------------------------------
PHPCSExtra is a collection of sniffs and standards for use with [PHP_CodeSniffer][phpcs-gh].
Minimum Requirements
-------------------------------------------
* PHP 5.4 or higher.
* [PHP_CodeSniffer][phpcs-gh] version **3.7.1** or higher.
* [PHPCSUtils][phpcsutils-gh] version **1.0.8** or higher.
Installation
-------------------------------------------
Installing via Composer is highly recommended.
[Composer](http://getcomposer.org/) will automatically install the project dependencies and register the rulesets from PHPCSExtra and other external standards with PHP_CodeSniffer using the [Composer PHPCS plugin][composer-installer-gh].
### Composer Project-based Installation
Run the following from the root of your project:
```bash
composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
composer require --dev phpcsstandards/phpcsextra:"^1.1.0"
```
### Composer Global Installation
Alternatively, you may want to install this standard globally:
```bash
composer global config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
composer global require --dev phpcsstandards/phpcsextra:"^1.1.0"
```
### Updating to a newer version
If you installed PHPCSExtra using either of the above commands, you can update to a newer version as follows:
```bash
# Project local install
composer update phpcsstandards/phpcsextra --with-dependencies
# Global install
composer global update phpcsstandards/phpcsextra --with-dependencies
```
> If your project includes `require[-dev]`s for the `squizlabs/php_codesniffer`, `phpcsstandards/phpcsutils` or
> `dealerdirect/phpcodesniffer-composer-installer` packages in its `composer.json` file, you may need to use
> `--with-all-dependencies` instead of `--with-dependencies`.
>
> :bulb: **Pro-tip**: Unless your project is a PHPCS standard which actually uses any of these packages directly,
> it is recommended to remove these packages from your own `composer.json` file, in favour of letting PHPCSExtra
> (and potential other external PHPCS standards you use), manage the version requirements for these packages.
Features
-------------------------------------------
Once this project is installed, you will see three new rulesets in the list of installed standards when you run `vendor/bin/phpcs -i`: `Modernize`, `NormalizedArrays` and `Universal`.
* The `Modernize` ruleset is a standard which checks code for modernization opportunaties.
* The `NormalizedArrays` ruleset is a standard to check the formatting of array declarations.
* The `Universal` ruleset is **NOT** a standard, but a sniff collection.
It should **NOT** be included in custom rulesets as a standard as it contains contradictory rules.
Instead include individual sniffs from this standard in a custom project/company ruleset to use them.
Sniffs
-------------------------------------------
**Legend**:
* :wrench: = Includes auto-fixer.
_Use the `phpcbf` command to run the fixers._
* :bar_chart: = Includes metrics.
_Use `phpcs` with `--report=info` to see the metrics._
* :books: = Includes CLI documentation.
_Use `phpcs` with `--generator=Text` to see the documentation._
### Modernize
#### `Modernize.FunctionCalls.Dirname` :wrench: :books:
This sniff will detect and auto-fix two typical code modernizations which can be made related to the `dirname()` function:
1. Since PHP 5.3, calls to `dirname(__FILE__)` can be replaced by `__DIR__`.
Errorcode: `Modernize.FunctionCalls.Dirname.FileConstant`.
2. Since PHP 7.0, nested function calls to `dirname()` can be changed to use the `$levels` parameter.
Errorcode: `Modernize.FunctionCalls.Dirname.Nested`.
If a [`php_version` configuration option][php_version-config] has been passed to PHPCS using either `--config-set` or `--runtime-set`, it will be respected by the sniff.
In effect, this means that the sniff will only report on modernizations which can be applied for the PHP version as configured.
### NormalizedArrays
#### `NormalizedArrays.Arrays.ArrayBraceSpacing` :wrench: :bar_chart: :books:
Enforce consistent spacing for the open/close braces of array declarations.
The sniff allows for having different settings for:
- Space between the array keyword and the open parenthesis for long arrays via the `keywordSpacing` property.
Accepted values: (int) number of spaces or `false` to turn this check off. Defaults to `0` spaces.
- Spaces on the inside of the braces for empty arrays via the `spacesWhenEmpty` property.
Accepted values: (string) `newline`, (int) number of spaces or `false` to turn this check off. Defaults to `0` spaces.
- Spaces on the inside of the braces for single-line arrays via the `spacesSingleLine` property;
Accepted values: (int) number of spaces or `false` to turn this check off. Defaults to `0` spaces.
- Spaces on the inside of the braces for multi-line arrays via the `spacesMultiLine` property.
Accepted values: (string) `newline`, (int) number of spaces or `false` to turn this check off. Defaults to `newline`.
Note: if any of the above properties are set to `newline`, it is recommended to also include an array indentation sniff. This sniff will not handle the indentation.
#### `NormalizedArrays.Arrays.CommaAfterLast` :wrench: :bar_chart: :books:
Enforce/forbid a comma after the last item in an array declaration.
By default, this sniff will:
* Forbid a comma after the last array item for single-line arrays.
* Enforce a comma after the last array item for multi-line arrays.
This can be changed for each type or array individually by setting the `singleLine` and/or `multiLine` properties in a custom ruleset.
Use any of the following values to change the properties: `enforce`, `forbid` or `skip` to not check the comma after the last array item for a particular type of array.
The default for the `singleLine` property is `forbid`. The default for the `multiLine` property is `enforce`.
### Universal
#### `Universal.Arrays.DisallowShortArraySyntax` :wrench: :bar_chart: :books:
Disallow short array syntax.
In contrast to the PHPCS native `Generic.Arrays.DisallowShortArraySyntax` sniff, this sniff will ignore short list syntax and not cause parse errors when the fixer is used.
#### `Universal.Arrays.DuplicateArrayKey` :books:
Detects duplicate array keys in array declarations.
The sniff will make a distinction between keys which will be duplicate in all PHP version and (numeric) keys which will only be a duplicate key in [PHP < 8.0 or PHP >= 8.0][php-rfc-negative_array_index].
If a [`php_version` configuration option][php_version-config] has been passed to PHPCS using either `--config-set` or `--runtime-set`, it will be respected by the sniff and only report duplicate keys for the configured PHP version.
[php-rfc-negative_array_index]: https://wiki.php.net/rfc/negative_array_index
#### `Universal.Arrays.MixedArrayKeyTypes` :books:
Best practice sniff: don't use a mix of integer and string keys for array items.
#### `Universal.Arrays.MixedKeyedUnkeyedArray` :books:
Best practice sniff: don't use a mix of keyed and unkeyed array items.
#### `Universal.Classes.DisallowAnonClassParentheses` :wrench: :bar_chart: :books:
Disallow the use of parentheses when declaring an anonymous class without passing parameters.
#### `Universal.Classes.RequireAnonClassParentheses` :wrench: :bar_chart: :books:
Require the use of parentheses when declaring an anonymous class, whether parameters are passed or not.
#### `Universal.Classes.DisallowFinalClass` :wrench: :bar_chart: :books:
Disallow classes being declared `final`.
#### `Universal.Classes.RequireFinalClass` :wrench: :bar_chart: :books:
Require all non-`abstract` classes to be declared `final`.
:warning: **Warning**: the auto-fixer for this sniff _may_ have unintended side-effects for applications and should be used with care!
This is considered a **_risky_ fixer**.
#### `Universal.Classes.ModifierKeywordOrder` :wrench: :bar_chart: :books:
Require a consistent modifier keyword order for class declarations.
* This sniff contains an `order` property to specify the preferred order.
Accepted values: (string) `'extendability readonly'`|`'readonly extendability'`. Defaults to `'extendability readonly'`.
#### `Universal.CodeAnalysis.ConstructorDestructorReturn` :wrench: :books:
* Disallows return type declarations on constructor/destructor methods - error code: `ReturnTypeFound`, auto-fixable.
* Discourages constructor/destructor methods returning a value - error code: `ReturnValueFound`.
If a [`php_version` configuration option][php_version-config] has been passed to PHPCS using either `--config-set` or `--runtime-set`, it will be respected by the sniff.
In effect, this means that the sniff will only report on PHP4-style constructors if the configured PHP version is less than 8.0.
#### `Universal.CodeAnalysis.ForeachUniqueAssignment` :wrench: :books:
Detects `foreach` control structures which use the same variable for both the key as well as the value assignment as this will lead to unexpected - and most likely unintended - behaviour.
Note: The fixer will maintain the existing behaviour of the code. This may not be the _intended_ behaviour.
#### `Universal.CodeAnalysis.NoEchoSprintf` :wrench: :books:
Detects use of the inefficient `echo [v]sprintf(...);` combi. Use `[v]printf()` instead.
#### `Universal.CodeAnalysis.StaticInFinalClass` :wrench: :books:
Detects using `static` instead of `self` in OO constructs which are `final`.
* The sniff has modular error codes to allow for making exceptions based on the type of use for `static`.
The available error codes are: `ReturnType`, `InstanceOf`, `NewInstance`, `ScopeResolution`.
#### `Universal.Constants.LowercaseClassResolutionKeyword` :wrench: :bar_chart: :books:
Enforce that the `class` keyword when used for class name resolution, i.e. `::class`, is in lowercase.
#### `Universal.Constants.ModifierKeywordOrder` :wrench: :bar_chart: :books:
Require a consistent modifier keyword order for OO constant declarations.
* This sniff contains an `order` property to specify the preferred order.
Accepted values: (string) `'final visibility'`|`'visibility final'`. Defaults to `'final visibility'`.
#### `Universal.Constants.UppercaseMagicConstants` :wrench: :bar_chart: :books:
Enforce uppercase when using PHP native magic constants, like `__FILE__` et al.
#### `Universal.ControlStructures.DisallowAlternativeSyntax` :wrench: :bar_chart: :books:
Disallow using the alternative syntax for control structures.
* This sniff contains an `allowWithInlineHTML` property to allow alternative syntax when inline HTML is used within the control structure. In all other cases, the use of the alternative syntax will still be disallowed.
Accepted values: (bool) `true`|`false`. Defaults to `false`.
* The sniff has modular error codes to allow for making exceptions based on specific control structures and/or specific control structures in combination with inline HTML.
The error codes follow the following pattern: `Found[ControlStructure][WithInlineHTML]`. Examples: `FoundIf`, `FoundSwitchWithInlineHTML`.
#### `Universal.ControlStructures.DisallowLonelyIf` :wrench: :books:
Disallow `if` statements as the only statement in an `else` block.
Note: This sniff will not fix the indentation of the "inner" code.
It is strongly recommended to run this sniff together with the `Generic.WhiteSpace.ScopeIndent` sniff to get the correct indentation.
#### `Universal.ControlStructures.IfElseDeclaration` :wrench: :bar_chart: :books:
Verify that else(if) statements with braces are on a new line.
#### `Universal.Files.SeparateFunctionsFromOO` :bar_chart: :books:
Enforce for a file to either declare (global/namespaced) functions or declare OO structures, but not both.
* Nested function declarations, i.e. functions declared within a function/method will be disregarded for the purposes of this sniff.
The same goes for anonymous classes, closures and arrow functions.
* Note: This sniff has no opinion on side effects. If you want to sniff for those, use the PHPCS native `PSR1.Files.SideEffects` sniff.
* Also note: This sniff has no opinion on multiple OO structures being declared in one file.
If you want to sniff for that, use the PHPCS native `Generic.Files.OneObjectStructurePerFile` sniff.
#### `Universal.FunctionDeclarations.NoLongClosures` :bar_chart: :books:
Detects "long" closures and recommends using a named function instead.
The sniff is configurable by setting any of the following properties in a custom ruleset:
* `recommendedLines` (int): determines when a warning will be thrown.
Defaults to `5`, meaning a warning with the errorcode `ExceedsRecommended` will be thrown if the closure is more than 5 lines long.
* `maxLines` (int): determines when an error will be thrown.
Defaults to `8`, meaning that an error with the errorcode `ExceedsMaximum` will be thrown if the closure is more than 8 lines long.
* `ignoreCommentLines` (bool): whether or not comment-only lines should be ignored for the lines count.
Defaults to `true`.
* `ignoreEmptyLines` (bool): whether or not blank lines should be ignored for the lines count.
Defaults to `true`.
#### `Universal.FunctionDeclarations.RequireFinalMethodsInTraits` :wrench: :bar_chart: :books:
Enforce non-private, non-abstract methods in traits to be declared as `final`.
The available error codes are: `NonFinalMethodFound` and `NonFinalMagicMethodFound`.
#### `Universal.Lists.DisallowLongListSyntax` :wrench: :books:
Disallow the use of long `list`s.
> For metrics about the use of long lists vs short lists, please use the `Universal.Lists.DisallowShortListSyntax` sniff.
#### `Universal.Lists.DisallowShortListSyntax` :wrench: :bar_chart: :books:
Disallow the use of short lists.
#### `Universal.Namespaces.DisallowDeclarationWithoutName` :bar_chart: :books:
Disallow namespace declarations without a namespace name.
This sniff only applies to namespace declarations using the curly brace syntax.
#### `Universal.Namespaces.DisallowCurlyBraceSyntax` :bar_chart: :books:
Disallow the use of the alternative namespace declaration syntax using curly braces.
#### `Universal.Namespaces.EnforceCurlyBraceSyntax` :bar_chart: :books:
Enforce the use of the alternative namespace syntax using curly braces.
#### `Universal.Namespaces.OneDeclarationPerFile` :books:
Disallow the use of multiple namespaces within a file.
#### `Universal.NamingConventions.NoReservedKeywordParameterNames` :books:
Disallow function parameters using reserved keywords as names, as this can quickly become confusing when people use them in function calls using named parameters
* The sniff has modular error codes to allow for making exceptions for specific keywords.
The error codes follow the following pattern: `[keyword]Found`.
#### `Universal.OOStructures.AlphabeticExtendsImplements` :wrench: :bar_chart: :books:
Enforce that the names used in a class/enum "implements" statement or an interface "extends" statement are listed in alphabetic order.
* This sniff contains a `orderby` property to determine the sort order to use for the statement.
If all names used are unqualified, the sort order won't make a difference.
However, if one or more of the names are partially or fully qualified, the chosen sort order will determine how the sorting between unqualified, partially and fully qualified names is handled.
The sniff supports two sort order options:
- _'name'_ : sort by the interface name only (default);
- _'full'_ : sort by the full name as used in the statement (without leading backslash).
In both cases, the sorting will be done using natural sort, case-insensitive.
* The sniff has modular error codes to allow for selective inclusion/exclusion:
- `ImplementsWrongOrder` - for "class implements" statements.
- `ImplementsWrongOrderWithComments` - for "class implements" statements interlaced with comments. These will not be auto-fixed.
- `ExtendsWrongOrder` - for "interface extends" statements.
- `ExtendsWrongOrderWithComments` - for "interface extends" statements interlaced with comments. These will not be auto-fixed.
* When fixing, the existing spacing between the names in an `implements`/`extends` statement will not be maintained.
The fixer will separate each name with a comma and one space.
If alternative formatting is desired, a sniff which will check and fix the formatting should be added to the ruleset.
#### `Universal.Operators.DisallowLogicalAndOr` :bar_chart: :books:
Enforce the use of the boolean `&&` and `||` operators instead of the logical `and`/`or` operators.
:information_source: Note: as the [operator precedence](https://www.php.net/language.operators.precedence) of the logical operators is significantly lower than the operator precedence of boolean operators, this sniff does not contain an auto-fixer.
#### `Universal.Operators.DisallowShortTernary` :bar_chart: :books:
Disallow the use of short ternaries `?:`.
While short ternaries are useful when used correctly, the principle of them is often misunderstood and they are more often than not used incorrectly, leading to hard to debug issues and/or PHP warnings/notices.
#### `Universal.Operators.DisallowStandalonePostIncrementDecrement` :wrench: :bar_chart: :books:
* Disallow the use of post-in/decrements in stand-alone statements - error codes: `PostDecrementFound` and `PostIncrementFound`.
Using pre-in/decrement is more in line with the principle of least astonishment and prevents bugs when code gets moved around at a later point in time.
* Discourages the use of multiple increment/decrement operators in a stand-alone statement - error code: `MultipleOperatorsFound`.
#### `Universal.Operators.StrictComparisons` :wrench: :bar_chart: :books:
Enforce the use of strict comparisons.
:warning: **Warning**: the auto-fixer for this sniff _may_ cause bugs in applications and should be used with care!
This is considered a **_risky_ fixer**.
#### `Universal.Operators.TypeSeparatorSpacing` :wrench: :bar_chart: :books:
Enforce no spaces around the union type and intersection type operators.
The available error codes are: `UnionTypeSpacesBefore`, `UnionTypeSpacesAfter`, `IntersectionTypeSpacesBefore`, `IntersectionTypeSpacesAfter`.
#### `Universal.PHP.OneStatementInShortEchoTag` :wrench: :books:
Disallow short open echo tags `<?=` containing more than one PHP statement.
#### `Universal.UseStatements.DisallowMixedGroupUse` :wrench: :bar_chart: :books:
Disallow group use statements which import a combination of namespace/OO construct, functions and/or constants in one statement.
Note: the fixer will use a semi-standardized format for group use statements.
If there are more specific requirements for the formatting of group use statements, the ruleset configurator should ensure that additional sniffs are included in the ruleset to enforce the required format.
#### `Universal.UseStatements.DisallowUseClass` :bar_chart: :books:
Forbid using import `use` statements for classes/traits/interfaces/enums.
Individual sub-types - with/without alias, global imports, imports from the same namespace - can be forbidden by including that specific error code and/or allowed including the whole sniff and excluding specific error codes.
The available error codes are: `FoundWithoutAlias`, `FoundWithAlias`, `FromGlobalNamespace`, `FromGlobalNamespaceWithAlias`, `FromSameNamespace` and `FromSameNamespaceWithAlias`.
#### `Universal.UseStatements.DisallowUseConst` :bar_chart: :books:
Forbid using import `use` statements for constants.
See [`Universal.UseStatements.DisallowUseClass`](#universalusestatementsdisallowuseclass-bar_chart-books) for information on the error codes.
#### `Universal.UseStatements.DisallowUseFunction` :bar_chart: :books:
Forbid using import `use` statements for functions.
See [`Universal.UseStatements.DisallowUseClass`](#universalusestatementsdisallowuseclass-bar_chart-books) for information on the error codes.
#### `Universal.UseStatements.KeywordSpacing` :wrench: :bar_chart: :books:
Enforce the use of a single space after the `use`, `function`, `const` keywords and both before and after the `as` keyword in import `use` statements.
Companion sniff to the PHPCS native `Generic.WhiteSpace.LanguageConstructSpacing` sniff which doesn't cover the `function`, `const` and `as` keywords when used in an import `use` statement.
The sniff has modular error codes to allow for disabling individual checks. The error codes are: `SpaceAfterUse`, `SpaceAfterFunction`, `SpaceAfterConst`, `SpaceBeforeAs` and `SpaceAfterAs`.
#### `Universal.UseStatements.LowercaseFunctionConst` :wrench: :bar_chart: :books:
Enforce that `function` and `const` keywords when used in an import `use` statement are always lowercase.
Companion sniff to the PHPCS native `Generic.PHP.LowerCaseKeyword` sniff which doesn't cover these keywords when used in an import `use` statement.
#### `Universal.UseStatements.NoLeadingBackslash` :wrench: :bar_chart: :books:
Verify that a name being imported in an import `use` statement does not start with a leading backslash.
Names in import `use` statements should always be fully qualified, so a leading backslash is not needed and it is strongly recommended not to use one.
This sniff handles all types of import use statements supported by PHP, in contrast to other sniffs for the same in, for instance, the PHPCS native `PSR12` or the Slevomat standard, which are incomplete.
#### `Universal.UseStatements.NoUselessAliases` :wrench: :books:
Detects useless aliases in import use statements.
Aliasing something to the same name as the original construct is considered useless (though allowed in PHP).
Note: as OO and function names in PHP are case-insensitive, aliasing to the same name, using a different case is also considered useless.
#### `Universal.WhiteSpace.AnonClassKeywordSpacing` :wrench: :bar_chart: :books:
Standardize the amount of spacing between the `class` keyword and the open parenthesis (if any) for anonymous class declarations.
* This sniff contains an `spacing` property to set the amount of spaces the sniff should check for.
Accepted values: (int) number of spaces. Defaults to `0` (spaces).
#### `Universal.WhiteSpace.CommaSpacing` :wrench: :bar_chart: :books:
Enforce that there is no space before a comma and exactly one space, or a new line, after a comma.
Additionally, the sniff also enforces that the comma should follow the code and not be placed after a trailing comment.
For the spacing part, the sniff makes the following exceptions:
1. A comma preceded or followed by a parenthesis, curly or square bracket.
These will not be flagged to prevent conflicts with sniffs handling spacing around braces.
2. A comma preceded or followed by another comma, like for skipping items in a list assignment.
These will not be flagged.
* The sniff has a separate error code - `TooMuchSpaceAfterCommaBeforeTrailingComment` - for when a comma is found with more than one space after it, followed by a trailing comment.
Exclude this error code to allow trailing comment alignment.
* The other error codes the sniff uses, `SpaceBefore`, `TooMuchSpaceAfter` and `NoSpaceAfter`, may be suffixed with a context indicator - `*InFunctionDeclaration`, `*InFunctionCall`, `*InClosureUse` or `*InDeclare` -.
This allows for disabling the sniff in any of these contexts by excluding the specific suffixed error codes.
* The sniff will respect a potentially set [`php_version` configuration option][php_version-config] when deciding how to handle the spacing after a heredoc/nowdoc closer.
In effect, this means that the sniff will enforce a new line between the closer and a comma if the configured PHP version is less than 7.3.
When no `php_version` is passed, the sniff will handle the spacing between a heredoc/nowdoc closer and a comma based on whether it is a cross-version compatible heredoc/nowdoc (enforce new line) or a flexible heredoc/nowdoc (enforce no space).
#### `Universal.WhiteSpace.DisallowInlineTabs` :wrench: :books:
Enforce using spaces for mid-line alignment.
While tab versus space based indentation is a question of preference, for mid-line alignment, spaces should always be preferred, as using tabs will result in inconsistent formatting depending on the dev-user's chosen tab width.
> _This sniff is especially useful for tab-indentation based standards which use the `Generic.Whitespace.DisallowSpaceIndent` sniff to enforce this._
>
> **DO** make sure to set the PHPCS native `tab-width` configuration for the best results.
> ```xml
> <arg name="tab-width" value="4"/>
> ```
>
> The PHPCS native `Generic.Whitespace.DisallowTabIndent` sniff (used for space-based standards) oversteps its reach and silently does mid-line tab to space replacements as well.
> However, the sister-sniff `Generic.Whitespace.DisallowSpaceIndent` leaves mid-line tabs/spaces alone.
> This sniff fills that gap.
#### `Universal.WhiteSpace.PrecisionAlignment` :wrench: :books:
Enforce code indentation to always be a multiple of a tabstop, i.e. disallow precision alignment.
Note:
* This sniff does not concern itself with tabs versus spaces.
It is recommended to use the sniff in combination with the PHPCS native `Generic.WhiteSpace.DisallowTabIndent` or the `Generic.WhiteSpace.DisallowSpaceIndent` sniff.
* When using this sniff with tab-based standards, please ensure that the `tab-width` is set and either don't set the `$indent` property or set it to the tab-width (or a multiple thereof).
* The fixer works based on "best guess" and may not always result in the desired indentation. Combine this sniff with the `Generic.WhiteSpace.ScopeIndent` sniff for more precise indentation fixes.
The behaviour of the sniff is customizable via the following properties:
* `indent`: the indent used for the codebase.
Accepted values: (int|null) number of spaces. Defaults to `null`.
If this property is not set, the sniff will look to the `--tab-width` CLI value.
If that also isn't set, the default tab-width of `4` will be used.
* `ignoreAlignmentBefore`: allows for providing a list of token names for which (preceding) precision alignment should be ignored.
Accepted values: (array<string>) token constant names. Defaults to an empty array.
Usage example:
```xml
<rule ref="Universal.WhiteSpace.PrecisionAlignment">
<properties>
<property name="ignoreAlignmentBefore" type="array">
<!-- Ignore precision alignment in inline HTML -->
<element value="T_INLINE_HTML"/>
<!-- Ignore precision alignment in multiline chained method calls. -->
<element value="T_OBJECT_OPERATOR"/>
<element value="T_NULLSAFE_OBJECT_OPERATOR"/>
</property>
</properties>
</rule>
```
* `ignoreBlankLines`: whether or not potential trailing whitespace on otherwise blank lines should be examined or ignored.
It is recommended to only set this to `false` if the standard including this sniff does not include the `Squiz.WhiteSpace.SuperfluousWhitespace` sniff (which is included in most standards).
Accepted values: (bool)`true`|`false`. Defaults to `true`.
Contributing
-------
Contributions to this project are welcome. Clone the repo, branch off from `develop`, make your changes, commit them and send in a pull request.
If unsure whether the changes you are proposing would be welcome, open an issue first to discuss your proposal.
License
-------
This code is released under the [GNU Lesser General Public License (LGPLv3)](LICENSE).
[phpcsextra-packagist]: https://packagist.org/packages/phpcsstandards/phpcsextra
[gha-qa-results]: https://github.com/PHPCSStandards/PHPCSExtra/actions/workflows/basics.yml
[gha-test-results]: https://github.com/PHPCSStandards/PHPCSExtra/actions/workflows/test.yml
[phpcs-gh]: https://github.com/squizlabs/PHP_CodeSniffer
[phpcsutils-gh]: https://github.com/PHPCSStandards/PHPCSUtils
[composer-installer-gh]: https://github.com/PHPCSStandards/composer-installer
[php_version-config]: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Configuration-Options#setting-the-php-version

View file

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Short Array Syntax"
>
<standard>
<![CDATA[
The array keyword must be used to define arrays.
]]>
</standard>
<code_comparison>
<code title="Valid: Using long form array syntax.">
<![CDATA[
$arr = <em>array(</em>
'foo' => 'bar',
<em>)</em>;
]]>
</code>
<code title="Invalid: Using short array syntax.">
<![CDATA[
$arr = <em>[</em>
'foo' => 'bar',
<em>]</em>;
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,44 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Duplicate Array Key"
>
<standard>
<![CDATA[
When a second array item with the same key is declared, it will overwrite the first.
This sniff detects when this is happening in array declarations.
]]>
</standard>
<code_comparison>
<code title="Valid: Arrays with unique keys.">
<![CDATA[
$args = array(
<em>'foo'</em> => 22,
<em>'bar'</em> => 25,
<em>'baz'</em> => 28,
);
$args = array(
22,
25,
2 => 28,
);
]]>
</code>
<code title="Invalid: Array with duplicate keys.">
<![CDATA[
$args = array(
<em>'foo'</em> => 22,
<em>'bar'</em> => 25,
<em>'bar'</em> => 28,
);
$args = array(
22,
25,
1 => 28,
);
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,40 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Mixed Array Key Types"
>
<standard>
<![CDATA[
In an array where the items have keys, all items should either have a numeric key assigned or a string key. A mix of numeric and string keys is not allowed.
]]>
</standard>
<code_comparison>
<code title="Valid: Arrays with either numeric keys or string keys.">
<![CDATA[
$args = array(
<em>'foo'</em> => 22,
<em>'bar'</em> => 25,
);
$args = array(
<em>0</em> => 22,
<em>1</em> => 25,
);
]]>
</code>
<code title="Invalid: Arrays with a mix of numeric and string keys.">
<![CDATA[
$args = array(
'foo' => 22,
25,
);
$args = array(
'foo' => 22,
12 => 25,
);
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,31 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Mixed Keyed Unkeyed Array"
>
<standard>
<![CDATA[
All items in an array should either have an explicit key assigned or none. A mix of keyed and unkeyed items is not allowed.
]]>
</standard>
<code_comparison>
<code title="Valid: Arrays with all items either keyed or unkeyed.">
<![CDATA[
$args = array(
<em>'foo'</em> => 22,
<em>'bar'</em> => 25,
);
$args = array(22, 25);
]]>
</code>
<code title="Invalid: Array with a mix of keyed and unkeyed items.">
<![CDATA[
$args = array(
'foo' => 22,
25,
);
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Anonymous Class Parentheses"
>
<standard>
<![CDATA[
Disallows the use of parentheses when declaring an anonymous class without passing parameters.
]]>
</standard>
<code_comparison>
<code title="Valid: Anonymous class without parentheses or with parameters.">
<![CDATA[
$anon = new class<em></em> {};
$anon = new class<em>($param)</em> {};
]]>
</code>
<code title="Invalid: Anonymous class with parentheses.">
<![CDATA[
$anon = new class<em>()</em> {};
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Final Class"
>
<standard>
<![CDATA[
Disallows the use of the `final` keyword for class declarations.
]]>
</standard>
<code_comparison>
<code title="Valid: Non-final class.">
<![CDATA[
<em>class</em> Foo {}
<em>abstract class</em> Bar implements MyInterface {}
]]>
</code>
<code title="Invalid: Final class.">
<![CDATA[
<em>final class</em> Foo {}
<em>final class</em> Bar extends MyAbstract {}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Class Modifier Keyword Order"
>
<standard>
<![CDATA[
Requires that class modifier keywords consistently use the same keyword order.
By default the expected order is "abstract/final readonly", but this can be changed via the sniff configuration.
]]>
</standard>
<code_comparison>
<code title="Valid: Modifier keywords ordered correctly.">
<![CDATA[
<em>final readonly</em> class Foo {}
<em>abstract readonly</em> class Bar {}
]]>
</code>
<code title="Invalid: Modifier keywords in reverse order.">
<![CDATA[
<em>readonly final</em> class Foo {}
<em>readonly abstract</em> class Bar {}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Require Anonymous Class Parentheses"
>
<standard>
<![CDATA[
Require the use of parentheses when declaring an anonymous class.
]]>
</standard>
<code_comparison>
<code title="Valid: Anonymous class with parentheses.">
<![CDATA[
$anon = new class<em>()</em> {};
]]>
</code>
<code title="Invalid: Anonymous class without parentheses.">
<![CDATA[
$anon = new class<em></em> {};
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Require Final Class"
>
<standard>
<![CDATA[
Requires the use of the `final` keyword for non-abstract class declarations.
]]>
</standard>
<code_comparison>
<code title="Valid: Final class.">
<![CDATA[
<em>final class</em> Foo {}
<em>final class</em> Bar extends MyAbstract {}
]]>
</code>
<code title="Invalid: Non-final class.">
<![CDATA[
<em>class</em> Foo {}
<em>abstract class</em> Bar implements MyInterface {}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,64 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Constructor Destructor Return"
>
<standard>
<![CDATA[
A class constructor/destructor can not have a return type declarations. This would result in a fatal error.
]]>
</standard>
<code_comparison>
<code title="Valid: no return type declaration.">
<![CDATA[
class Foo {
public function __construct() {}
}
]]>
</code>
<code title="Invalid: return type declaration.">
<![CDATA[
class Foo {
public function __construct()<em>: int</em> {}
}
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
A class constructor/destructor should not return anything.
]]>
</standard>
<code_comparison>
<code title="Valid: class constructor/destructor doesn't return anything.">
<![CDATA[
class Foo {
public function __construct() {
// Do something.
}
public function __destruct() {
// Do something.
return;
}
}
]]>
</code>
<code title="Invalid: class constructor/destructor returns a value.">
<![CDATA[
class Foo {
public function __construct() {
// Do something.
return <em>$this</em>;
}
public function __destruct() {
// Do something.
return <em>false</em>;
}
}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Foreach Unique Assignment"
>
<standard>
<![CDATA[
When a foreach control structure uses the same variable for both the $key as well as the $value assignment, the key will be disregarded and be inaccessible and the variable will contain the value.
Mix in reference assignments and the behaviour becomes even more unpredictable.
This is a coding error. Either use unique variables for the key and the value or don't assign the key.
]]>
</standard>
<code_comparison>
<code title="Valid: using unique variables.">
<![CDATA[
foreach ($array as $k => $v ) {}
]]>
</code>
<code title="Invalid: using the same variable for both the key as well as the value.">
<![CDATA[
foreach ($array as $k => $k ) {}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="No Echo Sprintf"
>
<standard>
<![CDATA[
Detects use of `echo [v]sprintf(...);`. Use `[v]printf()` instead.
]]>
</standard>
<code_comparison>
<code title="Valid: using [v]printf() or echo with anything but [v]sprintf().">
<![CDATA[
<em>printf</em>('text %s text', $var);
<em>echo callMe</em>('text %s text', $var);
]]>
</code>
<code title="Invalid: using echo [v]sprintf().">
<![CDATA[
<em>echo sprintf</em>('text %s text', $var);
<em>echo vsprintf</em>('text %s text', [$var]);
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,43 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Static in Final Class"
>
<standard>
<![CDATA[
When a class is declared as final, using the `static` keyword for late static binding is unnecessary and redundant.
This rule also covers using `static` in a comparison with `instanceof`, using `static` for class instantiations or as a return type.
`self` should be used instead.
This applies to final classes, anonymous classes (final by nature) and enums (final by design).
]]>
</standard>
<code_comparison>
<code title="Valid: Using 'self' in a final OO construct.">
<![CDATA[
final class Foo
{
public function myMethod($param) : <em>self</em>
{
$var = <em>self</em>::functionCall();
$var = $obj instanceof <em>self</em>;
$var = new <em>self</em>;
}
}
]]>
</code>
<code title="Invalid: Using 'static' in a final OO construct.">
<![CDATA[
$anon = new class {
public function myMethod(
): int|<em>static</em>|false {
$var = <em>static</em>::$prop;
$var = $obj instanceof <em>static</em>;
$var = new <em>static</em>();
}
};
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Lowercase Class Resolution Keyword"
>
<standard>
<![CDATA[
The "class" keyword when used for class name resolution, i.e. `::class`, must be in lowercase.
]]>
</standard>
<code_comparison>
<code title="Valid: using lowercase.">
<![CDATA[
echo MyClass::class;
]]>
</code>
<code title="Invalid: using uppercase or mixed case.">
<![CDATA[
echo <em>MyClass::CLASS</em>;
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,30 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Constant Modifier Keyword Order"
>
<standard>
<![CDATA[
Requires that constant modifier keywords consistently use the same keyword order.
By default the expected order is "final visibility", but this can be changed via the sniff configuration.
]]>
</standard>
<code_comparison>
<code title="Valid: Modifier keywords ordered correctly.">
<![CDATA[
class CorrectOrder {
<em>final public</em> const FOO = 'foo';
}
]]>
</code>
<code title="Invalid: Modifier keywords in reverse order.">
<![CDATA[
class IncorrectOrder {
#[SomeAttribute]
<em>protected final</em> const BAR = 'foo';
}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Uppercase Magic Constants"
>
<standard>
<![CDATA[
The PHP native `__...__` magic constant should be in uppercase.
]]>
</standard>
<code_comparison>
<code title="Valid: using uppercase.">
<![CDATA[
echo <em>__LINE__</em>;
include <em>__DIR__</em> . '/file.php';
]]>
</code>
<code title="Invalid: using lowercase or mixed case.">
<![CDATA[
echo <em>__NameSpace__</em>;
include dirname(<em>__file__</em>) . '/file.php';
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,35 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Alternative Control Structure Syntax"
>
<standard>
<![CDATA[
The use of the alternative syntax for control structures is not allowed.
]]>
</standard>
<code_comparison>
<code title="Valid: using curly brace syntax for control structures.">
<![CDATA[
if ($foo) <em>{</em>
$var = 1;
<em>}</em>
while (++$i < 10) <em>{</em>
echo $i;
<em>}</em>
]]>
</code>
<code title="Invalid: using the alternative syntax for control structures.">
<![CDATA[
if ($foo) <em>:</em>
$var = 1;
<em>endif;</em>
while (++$i < 10)<em>:</em>
echo $i;
<em>endwhile;</em>
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,49 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Lonely If"
>
<standard>
<![CDATA[
Disallows `if` statements as the only statement in an `else` block.
If an `if` statement is the only statement in the `else` block, use `elseif` instead.
]]>
</standard>
<code_comparison>
<code title="Valid: use of elseif or if followed by another statement.">
<![CDATA[
if ($foo) {
// ...
} <em>elseif ($bar)</em> {
// ...
}
if ($foo) {
// ...
} else {
<em>if ($bar) {
// ...
}
doSomethingElse();
</em>
}
]]>
</code>
<code title="Invalid: lonely if in an else block.">
<![CDATA[
if ($foo) {
// ...
} else {
<em>if ($bar) {
// ...
} else {
// ...
}</em>
}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,37 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="If-else Declarations"
>
<standard>
<![CDATA[
The `else` and `elseif` keywords should be on a new line.
]]>
</standard>
<code_comparison>
<code title="Valid: `elseif` and `else` each on a new line.">
<![CDATA[
if ($foo) {
$var = 1;
}<em>
elseif</em> ($bar) {
$var = 2;
}
else {
$var = 3;
}
]]>
</code>
<code title="Invalid: `elseif` and `else` on the same line as the closing curly of the preceding (else)if.">
<![CDATA[
if ($foo) {
$var = 1;
} <em>elseif</em> ($bar) {
$var = 2;
} <em>else</em> {
$var = 3;
}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,45 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Separate Functions From OO"
>
<standard>
<![CDATA[
A file should either declare (global/namespaced) functions or declare OO structures, but not both.
Nested function declarations, i.e. functions declared within a function/method will be disregarded for the purposes of this sniff.
The same goes for anonymous classes, closures and arrow functions.
]]>
</standard>
<code_comparison>
<code title="Valid: Files containing only functions or only OO.">
<![CDATA[
// Valid1.php
<?php
class Bar {
public function foo() {}
}
// Valid2.php
<?php
function foo() {}
function bar() {}
function baz() {}
]]>
</code>
<code title="Invalid: File containing both OO structure declarations as well as function declarations.">
<![CDATA[
// Invalid.php
<?php
function foo() {}
class Bar {
public function foo() {}
}
function bar() {}
function baz() {}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,42 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="No Long Closures"
>
<standard>
<![CDATA[
Forbids the use of long closures and recommends using named functions instead.
By default a closure is considered "longish" (warning) when it contains more than 5 lines of code and too long (error) when it contains more than 8 lines of code.
Also, by default only code lines are counted and blank lines and comment lines are ignored.
Each of these settings can be changed via the sniff configuration.
]]>
</standard>
<code_comparison>
<code title="Valid: Short closure.">
<![CDATA[
$closure = function() {
line1();
line2();
line3();
};
]]>
</code>
<code title="Invalid: Long closure.">
<![CDATA[
$closure = function() {
line1();
line2();
line3();
line4();
line5();
line6();
line7();
line8();
line9();
line10();
};
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,33 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Require Final Methods in Traits"
>
<standard>
<![CDATA[
Requires the use of the `final` keyword for non-abstract, non-private methods in traits.
]]>
</standard>
<code_comparison>
<code title="Valid: Final methods in a trait.">
<![CDATA[
trait Foo {
<em>final</em> public function bar() {}
<em>final</em> public static function baz() {}
// Also valid (out of scope):
protected abstract function overload() {}
private function okay() {}
}
]]>
</code>
<code title="Invalid: Non-final methods in a trait.">
<![CDATA[
trait Foo {
<em>public function</em> bar() {}
<em>protected static function</em> baz() {}
}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Long List Syntax"
>
<standard>
<![CDATA[
Short list syntax must be used.
]]>
</standard>
<code_comparison>
<code title="Valid: Short form of list.">
<![CDATA[
<em>[</em>$a, $b<em>]</em> = $array;
]]>
</code>
<code title="Invalid: Long form of list.">
<![CDATA[
<em>list(</em>$a, $b<em>)</em> = $array;
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Short List Syntax"
>
<standard>
<![CDATA[
Long list syntax must be used.
]]>
</standard>
<code_comparison>
<code title="Valid: Long form of list.">
<![CDATA[
<em>list(</em>$a, $b<em>)</em> = $array;
]]>
</code>
<code title="Invalid: Short form of list.">
<![CDATA[
<em>[</em>$a, $b<em>]</em> = $array;
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Curly Brace Namespace Syntax"
>
<standard>
<![CDATA[
Namespace declarations using the curly brace syntax are not allowed.
]]>
</standard>
<code_comparison>
<code title="Valid: Namespace declaration without braces.">
<![CDATA[
namespace Vendor\Project\Sub<em>;</em>
// Code
]]>
</code>
<code title="Invalid: Namespace declaration with braces.">
<![CDATA[
namespace Vendor\Project\Scoped <em>{</em>
// Code.
<em>}</em>
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Namespace Declaration Without Name"
>
<standard>
<![CDATA[
Namespace declarations without a namespace name are not allowed.
]]>
</standard>
<code_comparison>
<code title="Valid: Named namespace declaration.">
<![CDATA[
namespace <em>Vendor\Name</em> {
}
]]>
</code>
<code title="Invalid: Namespace declaration without a name (=global namespace).">
<![CDATA[
namespace<em> </em>{
}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Enforce Curly Brace Namespace Syntax"
>
<standard>
<![CDATA[
Namespace declarations without curly braces are not allowed.
]]>
</standard>
<code_comparison>
<code title="Valid: Namespace declaration with braces.">
<![CDATA[
namespace Vendor\Project\Scoped <em>{</em>
// Code.
<em>}</em>
]]>
</code>
<code title="Invalid: Namespace declaration without braces.">
<![CDATA[
namespace Vendor\Project\Sub<em>;</em>
// Code
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="One Namespace Declaration Per File"
>
<standard>
<![CDATA[
There should be only one namespace declaration per file.
]]>
</standard>
<code_comparison>
<code title="Valid: One namespace declaration in a file.">
<![CDATA[
namespace Vendor\Project\Sub;
]]>
</code>
<code title="Invalid: Multiple namespace declarations in a file.">
<![CDATA[
namespace Vendor\Project\Sub\A {
}
<em>namespace Vendor\Project\Sub\B {
}</em>
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="No Reserved Keyword Parameter Names"
>
<standard>
<![CDATA[
It is recommended not to use reserved keywords as parameter names as this can become confusing when people use them in function calls using named parameters.
]]>
</standard>
<code_comparison>
<code title="Valid: parameter names do not use reserved keywords.">
<![CDATA[
function foo( $input, $description ) {}
]]>
</code>
<code title="Invalid: parameter names use reserved keywords.">
<![CDATA[
function foo( $string, $echo = true ) {}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Alphabetic Extends Implements"
>
<standard>
<![CDATA[
The names used in class "implements" or interface "extends" statements should be listed in alphabetic order.
]]>
</standard>
<code_comparison>
<code title="Valid: Names listed alphabetically.">
<![CDATA[
class Baz implements <em>Bar, Foo</em>
{
}
]]>
</code>
<code title="Invalid: Names not listed alphabetically.">
<![CDATA[
class Baz implements <em>Foo, Bar</em>
{
}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,30 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Logical And Or"
>
<standard>
<![CDATA[
Using the logical "and" and "or" operators is not allowed.
The logical "and" and "or" operators have a significantly lower operator precedence than their boolean "&&" and "||" counter-parts, which often leads to unexpected bugs.
The boolean "&&" and "||" operators are nearly always the better choice.
]]>
</standard>
<code_comparison>
<code title="Valid: Using boolean operators.">
<![CDATA[
if (isset($var) <em>&&</em> $var > 10) {}
if (empty($var) <em>||</em> $var < 0) {}
]]>
</code>
<code title="Invalid: Using logical operators.">
<![CDATA[
if (isset($var) <em>and</em> $var > 10) {}
if (empty($var) <em>or</em> $var < 0) {}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Short Ternary"
>
<standard>
<![CDATA[
Using short ternaries is not allowed.
While short ternaries are useful when used correctly, the principle of them is often misunderstood and they are more often than not used incorrectly, leading to hard to debug issues and/or PHP warnings/notices.
]]>
</standard>
<code_comparison>
<code title="Valid: Full ternary.">
<![CDATA[
echo !empty($a) <em>?</em> $a <em>:</em> 'default';
]]>
</code>
<code title="Invalid: Short ternary.">
<![CDATA[
echo !empty($a) <em>?:</em> 'default';
echo $a <em>? :</em> 'default';
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,44 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Standalone Post-Increment/Decrement"
>
<standard>
<![CDATA[
In a stand-alone in/decrement statement, pre-in/decrement should always be favoured over post-in/decrement.
This reduces the chance of bugs when code gets moved around.
]]>
</standard>
<code_comparison>
<code title="Valid: Pre-in/decrement in a stand-alone statement.">
<![CDATA[
<em>++</em>$i;
<em>--</em>$j;
]]>
</code>
<code title="Invalid: Post-in/decrement in a stand-alone statement.">
<![CDATA[
$i<em>++</em>;
$j<em>--</em>;
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
Using multiple increment/decrement operators in a stand-alone statement is strongly discouraged.
]]>
</standard>
<code_comparison>
<code title="Valid: Single in/decrement operator in a stand-alone statement.">
<![CDATA[
<em>++</em>$i;
]]>
</code>
<code title="Invalid: Multiple in/decrement operators in a stand-alone statement.">
<![CDATA[
<em>--</em>$i<em>++</em><em>++</em>;
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,29 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Strict Comparisons"
>
<standard>
<![CDATA[
Using loose comparisons is not allowed.
Loose comparisons will type juggle the values being compared, which often results in bugs.
]]>
</standard>
<code_comparison>
<code title="Valid: Using strict comparisons.">
<![CDATA[
if ($var <em>===</em> 'text') {}
if ($var <em>!==</em> true) {}
]]>
</code>
<code title="Invalid: Using loose comparisons.">
<![CDATA[
if ($var <em>==</em> 'text') {}
if ($var <em>!=</em> true) {}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,33 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Type Separator Spacing"
>
<standard>
<![CDATA[
There should be no spacing around the union type separator or the intersection type separator.
This applies to all locations where type declarations can be used, i.e. property types, parameter types and return types.
]]>
</standard>
<code_comparison>
<code title="Valid: No space around the separators.">
<![CDATA[
function foo(
int<em>|</em>string $paramA,
TypeA<em>&</em>TypeB $paramB
): int<em>|</em>false {}
]]>
</code>
<code title="Invalid: Space around the separators.">
<![CDATA[
function foo(
int<em> | </em>string $paramA,
TypeA<em> & </em>TypeB $paramB
): int<em>
|
</em>false {}
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,41 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="One Statement In Short Echo Tag"
>
<standard>
<![CDATA[
Best practice: Short open echo tags should only ever contain *one* statement.
Use normal "<?php" tags for multi-statement PHP.
]]>
</standard>
<code_comparison>
<code title="Valid: Using short open echo tag with one statement.">
<![CDATA[
div><?= $text ?></div>
]]>
</code>
<code title="Invalid: Using short open echo tag with multiple statements.">
<![CDATA[
<div><em><?=</em> $text; <em>echo $moreText;</em> ?></div>
]]>
</code>
</code_comparison>
<code_comparison>
<code title="Valid: Using normal PHP tag with multiple statements.">
<![CDATA[
<div><em><?php</em>
echo $text;
echo $moreText;
?></div>
]]>
</code>
<code title="Invalid: Using short open echo tag with multiple statements.">
<![CDATA[
<div><em><?=</em> $text;
<em>echo $moreText;</em>
?></div>
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,39 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Mixed Group Use"
>
<standard>
<![CDATA[
Disallow group use statements which combine imports for namespace/OO, functions and/or constants in one statement.
]]>
</standard>
<code_comparison>
<code title="Valid: Single type group use statements.">
<![CDATA[
use Some\NS\ {
ClassName,
AnotherClass,
};
use function Some\NS\ {
SubLevel\functionName,
SubLevel\AnotherFunction,
};
use const Some\NS\ {
Constants\MY_CONSTANT as SOME_CONSTANT,
};
]]>
</code>
<code title="Invalid: Mixed group use statement.">
<![CDATA[
use Some\NS\ {
ClassName,
function SubLevel\functionName,
const MY_CONSTANT as SOME_CONSTANT,
function SubLevel\AnotherName,
AnotherLevel,
};
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Use Class/Trait/Interface"
>
<standard>
<![CDATA[
Disallow the use of `use` import statements for classes, traits and interfaces, with or without alias.
]]>
</standard>
<code_comparison>
<code title="Valid: Other type of use import statements.">
<![CDATA[
use function Vendor\Sub\functionName;
use const Vendor\Sub\CONST;
]]>
</code>
<code title="Invalid: Class/trait/interface use import statement.">
<![CDATA[
use Vendor\Sub\ClassName;
use Vendor\Sub\InterfaceName as Other;
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Use Const"
>
<standard>
<![CDATA[
Disallow the use of `use const` import statements, with or without alias.
]]>
</standard>
<code_comparison>
<code title="Valid: Other type of use import statements.">
<![CDATA[
use Vendor\Sub\ClassName;
use function Vendor\Sub\functionName;
]]>
</code>
<code title="Invalid: use const import statements.">
<![CDATA[
use const Vendor\Sub\CONST;
use const Vendor\Sub\BAR as otherConst;
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Use Function"
>
<standard>
<![CDATA[
Disallow the use of `use function` import statements, with or without alias.
]]>
</standard>
<code_comparison>
<code title="Valid: Other type of use import statements.">
<![CDATA[
use Vendor\Sub\ClassName;
use const Vendor\Sub\CONST;
]]>
</code>
<code title="Invalid: use function import statements.">
<![CDATA[
use function Vendor\Sub\functionName;
use function Vendor\Sub\functionName as other;
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,29 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Import Use Keyword Spacing"
>
<standard>
<![CDATA[
Enforce a single space after the `use`, `function`, `const` keywords and both before and after the `as` keyword in import `use` statements.
]]>
</standard>
<code_comparison>
<code title="Valid: Single space used around keywords.">
<![CDATA[
use<em> </em>function<em> </em>strpos;
use<em> </em>const<em> </em>PHP_EOL<em> </em>as<em> </em>MY_EOL;
]]>
</code>
<code title="Invalid: Incorrect spacing used around keywords.">
<![CDATA[
use<em> </em>function<em> </em>strpos;
use<em>
</em>const<em>
</em>PHP_EOL<em>
</em>as<em>
</em>MY_EOL;
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Lowercase Function Const"
>
<standard>
<![CDATA[
`function` and `const` keywords in import `use` statements should be in lowercase.
]]>
</standard>
<code_comparison>
<code title="Valid: Lowercase keywords.">
<![CDATA[
use <em>function</em> strpos;
use <em>const</em> PHP_EOL;
]]>
</code>
<code title="Invalid: Non-lowercase keywords.">
<![CDATA[
use <em>Function</em> strpos;
use <em>CONST</em> PHP_EOL;
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="No Leading Backslash"
>
<standard>
<![CDATA[
Import `use` statements must never begin with a leading backslash as they should always be fully qualified.
]]>
</standard>
<code_comparison>
<code title="Valid: Import use statement without leading backslash.">
<![CDATA[
<em>use Package</em>\ClassName;
]]>
</code>
<code title="Invalid: Import use statement with leading backslash.">
<![CDATA[
use <em>\</em>Package\ClassName;
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,30 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="No Useless Aliases"
>
<standard>
<![CDATA[
Detects useless aliases for import use statements.
Aliasing something to the same name as the original construct is considered useless.
Note: as OO and function names in PHP are case-insensitive, aliasing to the same name, using a different case is also considered useless.
]]>
</standard>
<code_comparison>
<code title="Valid: Import use statement with an alias to a different name.">
<![CDATA[
use Vendor\Package\ClassName as AnotherName;
use function functionName as my_function;
use const SOME_CONSTANT as MY_CONSTANT;
]]>
</code>
<code title="Invalid: Import use statement with an alias to the same name.">
<![CDATA[
use Vendor\Package\ClassName as ClassName;
use function functionName as FunctionName;
use const SOME_CONSTANT as SOME_CONSTANT;
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,31 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Anonymous Class Keyword Spacing"
>
<standard>
<![CDATA[
Checks the spacing between the "class" keyword and the open parenthesis for anonymous classes with parentheses.
The desired amount of spacing is configurable and defaults to no space.
]]>
</standard>
<code_comparison>
<code title="Valid: No space between the class keyword and the open parenthesis.">
<![CDATA[
$foo = new <em>class(</em>$param)
{
public function __construct($p) {}
};
]]>
</code>
<code title="Invalid: Space between the class keyword and the open parenthesis.">
<![CDATA[
$foo = new <em>class (</em>$param)
{
public function __construct($p) {}
};
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,94 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Comma Spacing"
>
<standard>
<![CDATA[
There should be no space before a comma and exactly one space, or a new line, after a comma.
The sniff makes the following exceptions to this rule:
1. A comma preceded or followed by a parenthesis, curly or square bracket.
These will not be flagged to prevent conflicts with sniffs handling spacing around braces.
2. A comma preceded or followed by another comma, like for skipping items in a list assignment.
These will not be flagged.
3. A comma preceded by a non-indented heredoc/nowdoc closer.
In that case, unless the `php_version` config directive is set to a version higher than PHP 7.3.0,
a new line before will be enforced to prevent parse errors on PHP < 7.3.
]]>
</standard>
<code_comparison>
<code title="Valid: Correct spacing.">
<![CDATA[
isset($param1<em>, </em>$param2<em>, </em>$param3);
function_call(
$param1<em>,</em>
$param2<em>,</em>
$param3
);
$array = array($item1<em>, </em>$item2<em>, </em>$item3);
$array = [
$item1<em>,</em>
$item2<em>,</em>
];
list(, $a<em>, </em>$b<em>,</em>,) = $array;
list(
,
$a<em>,</em>
$b<em>,</em>
) = $array;
]]>
</code>
<code title="Invalid: Incorrect spacing.">
<![CDATA[
unset($param1<em> , </em>$param2<em>,</em>$param3);
function_call(
$a
<em>,</em>$b
<em>,</em>$c
);
$array = array($item1<em>,</em>$item2<em> , </em>$item3);
$array = [
$item1,
$item2<em> ,</em>
];
list( ,$a, $b<em> ,</em>,) = $array;
list(
,
$a,
$b<em> ,</em>
) = $array;
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
A comma should follow the code and not be placed after a trailing comment.
]]>
</standard>
<code_comparison>
<code title="Valid: Comma after the code.">
<![CDATA[
function_call(
$param1<em>,</em> // Comment.
$param2<em>,</em> /* Comment. */
);
]]>
</code>
<code title="Invalid: Comma after a trailing comment.">
<![CDATA[
function_call(
$param1 // Comment.
<em>,</em>
$param2 /* Comment. */<em>,</em>
);
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Disallow Inline Tabs"
>
<standard>
<![CDATA[
Spaces must be used for mid-line alignment.
]]>
</standard>
<code_comparison>
<code title="Valid: Spaces used for alignment.">
<![CDATA[
$title<em>[space]</em>= 'text';
$text<em>[space][space]</em>= 'more text';
]]>
</code>
<code title="Invalid: Tabs used for alignment.">
<![CDATA[
$title<em>[tab]</em>= 'text';
$text<em>[tab]</em>= 'more text';
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,29 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Precision Alignment"
>
<standard>
<![CDATA[
Detects when the indentation is not a multiple of a tab-width, i.e. when precision alignment is used.
]]>
</standard>
<code_comparison>
<code title="Valid: indentation equals (a multiple of) the tab width.">
<![CDATA[
// Code samples presume tab width = 4.
<em>[space][space][space][space]</em>$foo = 'bar';
<em>[tab]</em>$foo = 'bar';
]]>
</code>
<code title="Invalid: precision alignment used, indentation does not equal (a multiple of) the tab width.">
<![CDATA[
// Code samples presume tab width = 4.
<em>[space][space]</em>$foo = 'bar';
<em>[tab][space]</em>$foo = 'bar';
]]>
</code>
</code_comparison>
</documentation>

View file

@ -0,0 +1,60 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Helpers;
use PHP_CodeSniffer\Tokenizers\Tokenizer;
/**
* Dummy tokenizer class to allow for accessing the replaceTabsInToken() method.
*
* @codeCoverageIgnore
*
* @since 1.0.0
*/
final class DummyTokenizer extends Tokenizer
{
/**
* Initialise and (don't) run the tokenizer.
*
* @param string $content The content to tokenize,
* @param \PHP_CodeSniffer\Config | null $config The config data for the run.
* @param string $eolChar The EOL char used in the content.
*
* @return void
*/
public function __construct($content, $config, $eolChar = '\n')
{
$this->eolChar = $eolChar;
$this->config = $config;
}
/**
* Creates an array of tokens when given some content.
*
* @param string $string The string to tokenize.
*
* @return array<int, array<string, mixed>>
*/
protected function tokenize($string)
{
return [];
}
/**
* Performs additional processing after main tokenizing.
*
* @return void
*/
protected function processAdditional()
{
}
}

View file

@ -0,0 +1,89 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\Arrays;
/**
* Disallow the use of the short array syntax.
*
* Improved version of the upstream `Generic.Arrays.DisallowShortArraySyntax` sniff which does
* not account for short lists and because of this can cause parse errors when auto-fixing.
*
* Other related sniffs:
* - `Generic.Arrays.DisallowLongArraySyntax` Forbids the use of the long array syntax.
*
* @since 1.0.0 This sniff is loosely based on and inspired by the upstream
* `Generic.Arrays.DisallowShortArraySyntax` sniff.
*/
final class DisallowShortArraySyntaxSniff implements Sniff
{
/**
* The phrase to use for the metric recorded by this sniff.
*
* @var string
*/
const METRIC_NAME = 'Short array syntax used';
/**
* Registers the tokens that this sniff wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return Collections::arrayOpenTokensBC();
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['code'] === \T_ARRAY) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
return;
}
if (Arrays::isShortArray($phpcsFile, $stackPtr) === false) {
// Square brackets, but not a short array. Probably short list or real square brackets.
return;
}
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
$error = 'Short array syntax is not allowed';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found');
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken($tokens[$stackPtr]['bracket_opener'], 'array(');
$phpcsFile->fixer->replaceToken($tokens[$stackPtr]['bracket_closer'], ')');
$phpcsFile->fixer->endChangeset();
}
}
}

View file

@ -0,0 +1,297 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\AbstractSniffs\AbstractArrayDeclarationSniff;
use PHPCSUtils\BackCompat\Helper;
/**
* Detect duplicate array keys in array declarations.
*
* This sniff will detect duplicate keys with high precision, though any array key
* set via a variable/constant/function call is excluded from the examination.
*
* The sniff will handle the change in how numeric array keys are set
* since PHP 8.0 and will flag keys which would be duplicates cross-version.
* {@link https://wiki.php.net/rfc/negative_array_index}
*
* @since 1.0.0
*/
final class DuplicateArrayKeySniff extends AbstractArrayDeclarationSniff
{
/**
* Keep track of which array keys have been seen already on PHP < 8.0.
*
* @since 1.0.0
*
* @var array<int, array<string, int>>
*/
private $keysSeenLt8 = [];
/**
* Keep track of which array keys have been seen already on PHP >= 8.0.
*
* @since 1.0.0
*
* @var array<int, array<string, int>>
*/
private $keysSeenGt8 = [];
/**
* Keep track of the maximum seen integer key to know what the next value will be for
* array items without a key on PHP < 8.0.
*
* @since 1.0.0
*
* @var int
*/
private $currentMaxIntKeyLt8;
/**
* Keep track of the maximum seen integer key to know what the next value will be for
* array items without a key on PHP >= 8.0.
*
* @since 1.0.0
*
* @var int
*/
private $currentMaxIntKeyGt8;
/**
* PHP version as configured or -1 if unknown.
*
* @since 1.0.0
*
* @var int
*/
private $phpVersion;
/**
* Process every part of the array declaration.
*
* This contains the default logic for the sniff, but can be overloaded in a concrete child class
* if needed.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token was found.
*
* @return void
*/
public function processArray(File $phpcsFile)
{
// Reset properties before processing this array.
$this->keysSeenLt8 = [];
$this->keysSeenGt8 = [];
if (isset($this->phpVersion) === false) {
// Set default value to prevent this code from running every time the sniff is triggered.
$this->phpVersion = -1;
$phpVersion = Helper::getConfigData('php_version');
if ($phpVersion !== null) {
$this->phpVersion = (int) $phpVersion;
}
}
unset($this->currentMaxIntKeyLt8, $this->currentMaxIntKeyGt8);
parent::processArray($phpcsFile);
}
/**
* Process the tokens in an array key.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token was found.
* @param int $startPtr The stack pointer to the first token in the "key" part of
* an array item.
* @param int $endPtr The stack pointer to the last token in the "key" part of
* an array item.
* @param int $itemNr Which item in the array is being handled.
*
* @return void
*/
public function processKey(File $phpcsFile, $startPtr, $endPtr, $itemNr)
{
$key = $this->getActualArrayKey($phpcsFile, $startPtr, $endPtr);
if (isset($key) === false) {
// Key could not be determined.
return;
}
$integerKey = \is_int($key);
$errorMsg = 'Duplicate array key found. The value will be overwritten%s.'
. ' The %s array key "%s" was first seen on line %d';
$errorCode = 'Found';
$errors = [];
$baseData = [
($integerKey === true) ? 'integer' : 'string',
$key,
];
/*
* Check if we've seen the key before.
*/
if (($this->phpVersion === -1 || $this->phpVersion < 80000)
&& isset($this->keysSeenLt8[$key]) === true
) {
$errors['phplt8'] = [
'data_subset' => $baseData,
'error_suffix' => '',
'code_suffix' => '',
];
if ($integerKey === true) {
$errors['phplt8']['error_suffix'] = ' when using PHP < 8.0';
$errors['phplt8']['code_suffix'] = 'ForPHPlt80';
}
$firstSeen = $this->keysSeenLt8[$key];
$firstNonEmptyFirstSeen = $phpcsFile->findNext(Tokens::$emptyTokens, $firstSeen['ptr'], null, true);
$errors['phplt8']['data_subset'][] = $this->tokens[$firstNonEmptyFirstSeen]['line'];
}
if (($this->phpVersion === -1 || $this->phpVersion >= 80000)
&& isset($this->keysSeenGt8[$key]) === true
) {
$errors['phpgt8'] = [
'data_subset' => $baseData,
'error_suffix' => '',
'code_suffix' => '',
];
if ($integerKey === true) {
$errors['phpgt8']['error_suffix'] = ' when using PHP >= 8.0';
$errors['phpgt8']['code_suffix'] = 'ForPHPgte80';
}
$firstSeen = $this->keysSeenGt8[$key];
$firstNonEmptyFirstSeen = $phpcsFile->findNext(Tokens::$emptyTokens, $firstSeen['ptr'], null, true);
$errors['phpgt8']['data_subset'][] = $this->tokens[$firstNonEmptyFirstSeen]['line'];
}
/*
* Throw the error(s).
*
* If no PHP version was passed, throw errors both for PHP < 8.0 and PHP >= 8.0.
* If a PHP version was set, only throw the error appropriate for the selected PHP version.
* If both errors would effectively be the same, only throw one.
*/
if ($errors !== []) {
$firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true);
if (isset($errors['phplt8'], $errors['phpgt8'])
&& $errors['phplt8']['data_subset'] === $errors['phpgt8']['data_subset']
) {
// Only throw the error once if it would be the same for PHP < 8.0 and PHP >= 8.0.
$data = $errors['phplt8']['data_subset'];
\array_unshift($data, '');
$phpcsFile->addError($errorMsg, $firstNonEmpty, $errorCode, $data);
return;
}
if (isset($errors['phplt8'])) {
$code = $errorCode . $errors['phplt8']['code_suffix'];
$data = $errors['phplt8']['data_subset'];
\array_unshift($data, $errors['phplt8']['error_suffix']);
$phpcsFile->addError($errorMsg, $firstNonEmpty, $code, $data);
}
if (isset($errors['phpgt8'])) {
$code = $errorCode . $errors['phpgt8']['code_suffix'];
$data = $errors['phpgt8']['data_subset'];
\array_unshift($data, $errors['phpgt8']['error_suffix']);
$phpcsFile->addError($errorMsg, $firstNonEmpty, $code, $data);
}
return;
}
/*
* Key not seen before. Add to arrays.
*/
$this->keysSeenLt8[$key] = [
'item' => $itemNr,
'ptr' => $startPtr,
];
$this->keysSeenGt8[$key] = [
'item' => $itemNr,
'ptr' => $startPtr,
];
if ($integerKey === true) {
if ((isset($this->currentMaxIntKeyLt8) === false && $key > -1)
|| (isset($this->currentMaxIntKeyLt8) === true && $key > $this->currentMaxIntKeyLt8)
) {
$this->currentMaxIntKeyLt8 = $key;
}
if (isset($this->currentMaxIntKeyGt8) === false
|| $key > $this->currentMaxIntKeyGt8
) {
$this->currentMaxIntKeyGt8 = $key;
}
}
}
/**
* Process an array item without an array key.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token was found.
* @param int $startPtr The stack pointer to the first token in the array item,
* which in this case will be the first token of the array
* value part of the array item.
* @param int $itemNr Which item in the array is being handled.
*
* @return void
*/
public function processNoKey(File $phpcsFile, $startPtr, $itemNr)
{
// Track the key for PHP < 8.0.
if (isset($this->currentMaxIntKeyLt8) === false) {
$this->currentMaxIntKeyLt8 = -1;
}
++$this->currentMaxIntKeyLt8;
$this->keysSeenLt8[$this->currentMaxIntKeyLt8] = [
'item' => $itemNr,
'ptr' => $startPtr,
];
// Track the key for PHP 8.0+.
if (isset($this->currentMaxIntKeyGt8) === false) {
$this->currentMaxIntKeyGt8 = -1;
}
++$this->currentMaxIntKeyGt8;
$this->keysSeenGt8[$this->currentMaxIntKeyGt8] = [
'item' => $itemNr,
'ptr' => $startPtr,
];
}
}

View file

@ -0,0 +1,174 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\AbstractSniffs\AbstractArrayDeclarationSniff;
/**
* Forbid arrays which contain both array items with numeric keys as well as array items with string keys.
*
* @since 1.0.0
*/
final class MixedArrayKeyTypesSniff extends AbstractArrayDeclarationSniff
{
/**
* Whether a string key was encountered.
*
* @var bool
*/
private $seenStringKey = false;
/**
* Whether a numeric key was encountered.
*
* @var bool
*/
private $seenNumericKey = false;
/**
* Process the array declaration.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token was found.
*
* @return void
*/
public function processArray(File $phpcsFile)
{
// Reset properties before processing this array.
$this->seenStringKey = false;
$this->seenNumericKey = false;
parent::processArray($phpcsFile);
}
/**
* Process the tokens in an array key.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token was found.
* @param int $startPtr The stack pointer to the first token in the "key" part of
* an array item.
* @param int $endPtr The stack pointer to the last token in the "key" part of
* an array item.
* @param int $itemNr Which item in the array is being handled.
*
* @return true|void Returning `TRUE` will short-circuit the array item loop and stop processing.
* In effect, this means that the sniff will not examine the double arrow, the array
* value or comma for this array item and will not process any array items after this one.
*/
public function processKey(File $phpcsFile, $startPtr, $endPtr, $itemNr)
{
$key = $this->getActualArrayKey($phpcsFile, $startPtr, $endPtr);
if (isset($key) === false) {
// Key could not be determined.
return;
}
$integerKey = \is_int($key);
// Handle integer key.
if ($integerKey === true) {
if ($this->seenStringKey === false) {
if ($this->seenNumericKey !== false) {
// Already seen a numeric key before.
return;
}
$this->seenNumericKey = true;
return;
}
// Ok, so we've seen a string key before and now see an explicit numeric key.
$firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true);
$phpcsFile->addError(
'Arrays should have either numeric keys or string keys. Explicit numeric key detected,'
. ' while all previous keys in this array were string keys.',
$firstNonEmpty,
'ExplicitNumericKey'
);
// Stop the loop.
return true;
}
// Handle string key.
if ($this->seenNumericKey === false) {
if ($this->seenStringKey !== false) {
// Already seen a string key before.
return;
}
$this->seenStringKey = true;
return;
}
// Ok, so we've seen a numeric key before and now see a string key.
$firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true);
$phpcsFile->addError(
'Arrays should have either numeric keys or string keys. String key detected,'
. ' while all previous keys in this array were integer based keys.',
$firstNonEmpty,
'StringKey'
);
// Stop the loop.
return true;
}
/**
* Process an array item without an array key.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token was found.
* @param int $startPtr The stack pointer to the first token in the array item,
* which in this case will be the first token of the array
* value part of the array item.
* @param int $itemNr Which item in the array is being handled.
*
* @return true|void Returning `TRUE` will short-circuit the array item loop and stop processing.
* In effect, this means that the sniff will not examine the array value or
* comma for this array item and will not process any array items after this one.
*/
public function processNoKey(File $phpcsFile, $startPtr, $itemNr)
{
if ($this->seenStringKey === false) {
if ($this->seenNumericKey !== false) {
// Already seen a numeric key before.
return;
}
$this->seenNumericKey = true;
return;
}
// Ok, so we've seen a string key before and now see an implicit numeric key.
$firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true);
$phpcsFile->addError(
'Arrays should have either numeric keys or string keys. Implicit numeric key detected,'
. ' while all previous keys in this array were string keys.',
$firstNonEmpty,
'ImplicitNumericKey'
);
// Stop the loop.
return true;
}
}

View file

@ -0,0 +1,134 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\AbstractSniffs\AbstractArrayDeclarationSniff;
/**
* Forbid arrays which contain both array items with an explicit key and array items without a key set.
*
* @since 1.0.0
*/
final class MixedKeyedUnkeyedArraySniff extends AbstractArrayDeclarationSniff
{
/**
* Whether or not any array items with a key were encountered in the array.
*
* @since 1.0.0
*
* @var bool
*/
private $hasKeys = false;
/**
* Cache of any array items encountered without keys up to the time an array item _with_ a key is encountered.
*
* @since 1.0.0
*
* @var array<int, int> Key is the item number; value the stack point to the first non empty token in the item.
*/
private $itemsWithoutKey = [];
/**
* Process the array declaration.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token was found.
*
* @return void
*/
public function processArray(File $phpcsFile)
{
// Reset properties before processing this array.
$this->hasKeys = false;
$this->itemsWithoutKey = [];
parent::processArray($phpcsFile);
}
/**
* Process the tokens in an array key.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token was found.
* @param int $startPtr The stack pointer to the first token in the "key" part of
* an array item.
* @param int $endPtr The stack pointer to the last token in the "key" part of
* an array item.
* @param int $itemNr Which item in the array is being handled.
*
* @return void
*/
public function processKey(File $phpcsFile, $startPtr, $endPtr, $itemNr)
{
$this->hasKeys = true;
// Process any previously encountered items without keys.
if (empty($this->itemsWithoutKey) === false) {
foreach ($this->itemsWithoutKey as $itemNr => $stackPtr) {
$phpcsFile->addError(
'Inconsistent array detected. A mix of keyed and unkeyed array items is not allowed.'
. ' The array item in position %d does not have an array key.',
$stackPtr,
'Found',
[$itemNr]
);
}
// No need to do this again.
$this->itemsWithoutKey = [];
}
}
/**
* Process an array item without an array key.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token was found.
* @param int $startPtr The stack pointer to the first token in the array item,
* which in this case will be the first token of the array
* value part of the array item.
* @param int $itemNr Which item in the array is being handled.
*
* @return void
*/
public function processNoKey(File $phpcsFile, $startPtr, $itemNr)
{
$firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true);
if ($firstNonEmpty === false || $this->tokens[$firstNonEmpty]['code'] === \T_COMMA) {
// Shouldn't really be possible, but this must be a parse error (empty array item).
return;
}
// If we already know there are keys in the array, throw an error message straight away.
if ($this->hasKeys === true) {
$phpcsFile->addError(
'Inconsistent array detected. A mix of keyed and unkeyed array items is not allowed.'
. ' The array item in position %d does not have an array key.',
$firstNonEmpty,
'Found',
[$itemNr]
);
} else {
// Save the array item info for later in case we do encounter an array key later on in the array.
$this->itemsWithoutKey[$itemNr] = $firstNonEmpty;
}
}
}

View file

@ -0,0 +1,112 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Classes;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
/**
* Forbid for an anonymous class declaration/instantiation to have parentheses, except when
* parameters are being passed.
*
* @since 1.0.0
*/
final class DisallowAnonClassParenthesesSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Anon class declaration with parenthesis';
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_ANON_CLASS];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
// Note: no need to check for `false` as PHPCS won't retokenize `class` to `T_ANON_CLASS` in that case.
if ($tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) {
// No parentheses found.
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
return;
}
if (isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false) {
/*
* Incomplete set of parentheses. Ignore.
* Shouldn't be possible as PHPCS won't retokenize `class` to `T_ANON_CLASS` in that case.
*/
// @codeCoverageIgnoreStart
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
return;
// @codeCoverageIgnoreEnd
}
$opener = $nextNonEmpty;
$closer = $tokens[$opener]['parenthesis_closer'];
$hasParams = $phpcsFile->findNext(Tokens::$emptyTokens, ($opener + 1), $closer, true);
if ($hasParams !== false) {
// There is something between the parentheses. Ignore.
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes, with parameter(s)');
return;
}
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
$fix = $phpcsFile->addFixableError(
'Parenthesis not allowed when creating a new anonymous class without passing parameters',
$stackPtr,
'Found'
);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($i = $opener; $i <= $closer; $i++) {
if (isset(Tokens::$commentTokens[$tokens[$i]['code']]) === true) {
continue;
}
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}
}
}

View file

@ -0,0 +1,116 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Classes;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\Utils\GetTokensAsString;
use PHPCSUtils\Utils\ObjectDeclarations;
/**
* Forbids classes from being declared as "final".
*
* @since 1.0.0
*/
final class DisallowFinalClassSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Class is abstract or final ?';
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_CLASS];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$classProp = ObjectDeclarations::getClassProperties($phpcsFile, $stackPtr);
if ($classProp['is_final'] === false) {
if ($classProp['is_abstract'] === true) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'abstract');
return;
}
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'not abstract, not final');
return;
}
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'final');
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($nextNonEmpty === false) {
// Live coding or parse error.
return;
}
// No extra safeguards needed, we know the keyword will exist based on the check above.
$finalKeyword = $phpcsFile->findPrevious(\T_FINAL, ($stackPtr - 1));
$snippetEnd = $nextNonEmpty;
$classCloser = '';
$tokens = $phpcsFile->getTokens();
if (isset($tokens[$stackPtr]['scope_opener']) === true) {
$snippetEnd = $tokens[$stackPtr]['scope_opener'];
$classCloser = '}';
}
$snippet = GetTokensAsString::compact($phpcsFile, $finalKeyword, $snippetEnd, true);
$fix = $phpcsFile->addFixableError(
'Declaring a class as final is not allowed. Found: %s%s',
$finalKeyword,
'FinalClassFound',
[$snippet, $classCloser]
);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken($finalKeyword, '');
// Remove redundant whitespace.
for ($i = ($finalKeyword + 1); $i < $stackPtr; $i++) {
if ($tokens[$i]['code'] === \T_WHITESPACE) {
$phpcsFile->fixer->replaceToken($i, '');
continue;
}
break;
}
$phpcsFile->fixer->endChangeset();
}
}
}

View file

@ -0,0 +1,188 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2022 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Classes;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHPCSUtils\Utils\ObjectDeclarations;
/**
* Standardize the modifier keyword order for class declarations.
*
* @since 1.0.0
*/
final class ModifierKeywordOrderSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Class modifier keyword order';
/**
* Order preference: abstract/final readonly.
*
* @since 1.0.0
*
* @var string
*/
const EXTEND_READONLY = 'extendability readonly';
/**
* Order preference: readonly abstract/final.
*
* @since 1.0.0
*
* @var string
*/
const READONLY_EXTEND = 'readonly extendability';
/**
* Preferred order for the modifier keywords.
*
* Accepted values:
* - "extendability readonly".
* - or "readonly extendability".
*
* Defaults to "extendability readonly".
*
* @since 1.0.0
*
* @var string
*/
public $order = self::EXTEND_READONLY;
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_CLASS];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$classProp = ObjectDeclarations::getClassProperties($phpcsFile, $stackPtr);
if ($classProp['readonly_token'] === false
|| ($classProp['final_token'] === false && $classProp['abstract_token'] === false)
) {
/*
* Either no modifier keywords found at all; or only one type of modifier
* keyword (abstract/final or readonly) declared, but not both. No ordering needed.
*/
return;
}
if ($classProp['final_token'] !== false && $classProp['abstract_token'] !== false) {
// Parse error. Ignore.
return;
}
$readonly = $classProp['readonly_token'];
if ($classProp['final_token'] !== false) {
$extendability = $classProp['final_token'];
} else {
$extendability = $classProp['abstract_token'];
}
if ($readonly < $extendability) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, self::READONLY_EXTEND);
} else {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, self::EXTEND_READONLY);
}
$message = 'Class modifier keywords are not in the correct order. Expected: "%s", found: "%s"';
switch ($this->order) {
case self::READONLY_EXTEND:
if ($readonly < $extendability) {
// Order is correct. Nothing to do.
return;
}
$this->handleError($phpcsFile, $extendability, $readonly);
break;
case self::EXTEND_READONLY:
default:
if ($extendability < $readonly) {
// Order is correct. Nothing to do.
return;
}
$this->handleError($phpcsFile, $readonly, $extendability);
break;
}
}
/**
* Throw the error and potentially fix it.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $firstKeyword The position of the first keyword found.
* @param int $secondKeyword The position of the second keyword token.
*
* @return void
*/
private function handleError(File $phpcsFile, $firstKeyword, $secondKeyword)
{
$tokens = $phpcsFile->getTokens();
$message = 'Class modifier keywords are not in the correct order. Expected: "%s", found: "%s"';
$data = [
$tokens[$secondKeyword]['content'] . ' ' . $tokens[$firstKeyword]['content'],
$tokens[$firstKeyword]['content'] . ' ' . $tokens[$secondKeyword]['content'],
];
$fix = $phpcsFile->addFixableError($message, $firstKeyword, 'Incorrect', $data);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken($secondKeyword, '');
// Prevent leaving behind trailing whitespace.
$i = ($secondKeyword + 1);
while ($tokens[$i]['code'] === \T_WHITESPACE) {
$phpcsFile->fixer->replaceToken($i, '');
++$i;
}
// Use the original token content as the case used for keywords is not the concern of this sniff.
$phpcsFile->fixer->addContentBefore($firstKeyword, $tokens[$secondKeyword]['content'] . ' ');
$phpcsFile->fixer->endChangeset();
}
}
}

View file

@ -0,0 +1,81 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Classes;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
/**
* Require that an anonymous class declaration/instantiation has parentheses, i.e. `new class().
*
* @since 1.0.0
*/
final class RequireAnonClassParenthesesSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Anon class declaration with parenthesis';
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_ANON_CLASS];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
// Note: no need to check for `false` as PHPCS won't retokenize `class` to `T_ANON_CLASS` in that case.
if ($tokens[$nextNonEmpty]['code'] === \T_OPEN_PARENTHESIS) {
// Parentheses found.
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
return;
}
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
$fix = $phpcsFile->addFixableError(
'Parenthesis required when creating a new anonymous class.',
$stackPtr,
'Missing'
);
if ($fix === true) {
$phpcsFile->fixer->addContent($stackPtr, '()');
}
}
}

View file

@ -0,0 +1,102 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Classes;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\Utils\GetTokensAsString;
use PHPCSUtils\Utils\ObjectDeclarations;
/**
* Require classes being declared as "final".
*
* @since 1.0.0
*/
final class RequireFinalClassSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Class is abstract or final ?';
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_CLASS];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$classProp = ObjectDeclarations::getClassProperties($phpcsFile, $stackPtr);
if ($classProp['is_final'] === true) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'final');
return;
}
if ($classProp['is_abstract'] === true) {
// Abstract classes can't be final.
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'abstract');
return;
}
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'not abstract, not final');
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($nextNonEmpty === false) {
// Live coding or parse error.
return;
}
$snippetEnd = $nextNonEmpty;
$classCloser = '';
$tokens = $phpcsFile->getTokens();
if (isset($tokens[$stackPtr]['scope_opener']) === true) {
$snippetEnd = $tokens[$stackPtr]['scope_opener'];
$classCloser = '}';
}
$snippet = GetTokensAsString::compact($phpcsFile, $stackPtr, $snippetEnd, true);
$fix = $phpcsFile->addFixableError(
'A non-abstract class should be declared as final. Found: %s%s',
$stackPtr,
'NonFinalClassFound',
[$snippet, $classCloser]
);
if ($fix === true) {
$phpcsFile->fixer->addContentBefore($stackPtr, 'final ');
}
}
}

View file

@ -0,0 +1,199 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\BackCompat\BCFile;
use PHPCSUtils\BackCompat\Helper;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\FunctionDeclarations;
use PHPCSUtils\Utils\GetTokensAsString;
use PHPCSUtils\Utils\NamingConventions;
use PHPCSUtils\Utils\ObjectDeclarations;
use PHPCSUtils\Utils\Scopes;
/**
* Verify that a class constructor/destructor does not return anything, nor has a
* return type declaration (fatal error).
*
* @since 1.0.0
*/
final class ConstructorDestructorReturnSniff implements Sniff
{
/**
* PHP version as configured or 0 if unknown.
*
* @since 1.1.0
*
* @var int
*/
private $phpVersion;
/**
* Registers the tokens that this sniff wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_FUNCTION];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
if (isset($this->phpVersion) === false || \defined('PHP_CODESNIFFER_IN_TESTS')) {
// Set default value to prevent this code from running every time the sniff is triggered.
$this->phpVersion = 0;
$phpVersion = Helper::getConfigData('php_version');
if ($phpVersion !== null) {
$this->phpVersion = (int) $phpVersion;
}
}
$scopePtr = Scopes::validDirectScope($phpcsFile, $stackPtr, Tokens::$ooScopeTokens);
if ($scopePtr === false) {
// Not an OO method.
return;
}
$functionName = FunctionDeclarations::getName($phpcsFile, $stackPtr);
$functionNameLC = \strtolower($functionName);
if ($functionNameLC === '__construct' || $functionNameLC === '__destruct') {
$functionType = \sprintf('A "%s()" magic method', $functionNameLC);
} else {
// If the PHP version is explicitly set to PHP 8.0 or higher, ignore PHP 4-style constructors.
if ($this->phpVersion >= 80000) {
return;
}
// This may be a PHP 4-style constructor which should be handled.
$OOName = ObjectDeclarations::getName($phpcsFile, $scopePtr);
if (empty($OOName) === true) {
// Anonymous class or parse error. The function can't be a PHP 4-style constructor.
return;
}
if (NamingConventions::isEqual($functionName, $OOName) === false) {
// Class and function name not the same, so not a PHP 4-style constructor.
return;
}
$functionType = 'A PHP 4-style constructor';
}
/*
* OK, so now we know for sure that this is a constructor/destructor method.
*/
// Check for a return type.
$tokens = $phpcsFile->getTokens();
$properties = FunctionDeclarations::getProperties($phpcsFile, $stackPtr);
if ($properties['return_type'] !== '' && $properties['return_type_token'] !== false) {
$data = [
$functionType,
$properties['return_type'],
];
$fix = $phpcsFile->addFixableError(
'%s can not declare a return type. Found: %s',
$properties['return_type_token'],
'ReturnTypeFound',
$data
);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
$parensCloser = $tokens[$stackPtr]['parenthesis_closer'];
for ($i = ($parensCloser + 1); $i <= $properties['return_type_end_token']; $i++) {
if (isset(Tokens::$commentTokens[$tokens[$i]['code']])) {
// Ignore comments and leave them be.
continue;
}
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}
}
if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
// Abstract/interface method, live coding or parse error.
return;
}
// Check for a value being returned.
$current = $tokens[$stackPtr]['scope_opener'];
$end = $tokens[$stackPtr]['scope_closer'];
// Not searching for arrow functions as those have an implicit return, so no
$search = Collections::functionDeclarationTokens();
$search[\T_RETURN] = \T_RETURN;
do {
$current = $phpcsFile->findNext($search, ($current + 1), $end);
if ($current === false) {
break;
}
if (isset(Collections::functionDeclarationTokens()[$tokens[$current]['code']])
&& isset($tokens[$current]['scope_closer'])
) {
// Skip over nested function/closure declarations.
$current = $tokens[$current]['scope_closer'];
continue;
}
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($current + 1), $end, true);
if ($next === false
|| $tokens[$next]['code'] === \T_SEMICOLON
|| $tokens[$next]['code'] === \T_CLOSE_TAG
) {
// Return statement without value.
continue;
}
$endOfStatement = BCFile::findEndOfStatement($phpcsFile, $next);
$data = [
$functionType,
GetTokensAsString::compact($phpcsFile, $current, $endOfStatement, true),
];
$phpcsFile->addWarning(
'%s can not return a value. Found: "%s"',
$current,
'ReturnValueFound',
$data
);
} while ($current < $end);
}
}

View file

@ -0,0 +1,153 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\GetTokensAsString;
use PHPCSUtils\Utils\Lists;
/**
* Detects using the same variable for both the key as well as the value in a foreach assignment.
*
* @link https://twitter.com/exakat/status/1509103728934203397
* @link https://3v4l.org/DdddX
*
* @since 1.0.0
*/
final class ForeachUniqueAssignmentSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_FOREACH];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
// Parse error or live coding, not our concern.
return;
}
$opener = $tokens[$stackPtr]['parenthesis_opener'];
$closer = $tokens[$stackPtr]['parenthesis_closer'];
$asPtr = $phpcsFile->findNext(\T_AS, ($opener + 1), $closer);
if ($asPtr === false) {
// Parse error or live coding, not our concern.
return;
}
// Real target.
$find = [\T_DOUBLE_ARROW];
// Prevent matching on double arrows within a list assignment.
$find += Collections::listTokens();
$doubleArrowPtr = $phpcsFile->findNext($find, ($asPtr + 1), $closer);
if ($doubleArrowPtr === false
|| $tokens[$doubleArrowPtr]['code'] !== \T_DOUBLE_ARROW
) {
// No key assignment.
return;
}
$isListAssignment = $phpcsFile->findNext(Tokens::$emptyTokens, ($doubleArrowPtr + 1), $closer, true);
if ($isListAssignment === false) {
// Parse error or live coding, not our concern.
}
$keyAsString = \ltrim(GetTokensAsString::noEmpties($phpcsFile, ($asPtr + 1), ($doubleArrowPtr - 1)), '&');
$valueAssignments = [];
if (isset(Collections::listTokens()[$tokens[$isListAssignment]['code']]) === false) {
// Single value assignment.
$valueAssignments[] = GetTokensAsString::noEmpties($phpcsFile, ($doubleArrowPtr + 1), ($closer - 1));
} else {
// List assignment.
$assignments = Lists::getAssignments($phpcsFile, $isListAssignment);
foreach ($assignments as $listItem) {
if ($listItem['assignment'] === '') {
// Ignore empty list assignments.
continue;
}
// Note: this doesn't take nested lists into account (yet).
$valueAssignments[] = $listItem['assignment'];
}
}
if (empty($valueAssignments)) {
// No assignments found.
return;
}
foreach ($valueAssignments as $valueAsString) {
$valueAsString = \ltrim($valueAsString, '&');
if ($keyAsString !== $valueAsString) {
// Key and value not the same.
continue;
}
$error = 'The variables used for the key and the value in a foreach assignment should be unique.';
$error .= 'Both the key and the value will currently be assigned to: "%s"';
$fix = $phpcsFile->addFixableError($error, $doubleArrowPtr, 'NotUnique', [$valueAsString]);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
// Remove the key.
for ($i = ($asPtr + 1); $i < ($doubleArrowPtr + 1); $i++) {
if ($tokens[$i]['code'] === \T_WHITESPACE
&& isset(Tokens::$commentTokens[$tokens[($i + 1)]['code']])
) {
// Don't remove whitespace when followed directly by a comment.
continue;
}
if (isset(Tokens::$commentTokens[$tokens[$i]['code']])) {
// Don't remove comments.
continue;
}
// Remove everything else.
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}
break;
}
}
}

View file

@ -0,0 +1,131 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2023 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
/**
* Detects use of `echo [v]sprintf();.
*
* @link https://www.php.net/manual/en/function.printf.php
* @link https://www.php.net/manual/en/function.sprintf.php
* @link https://www.php.net/manual/en/function.vprintf.php
* @link https://www.php.net/manual/en/function.vsprintf.php
*
* @since 1.1.0
*/
final class NoEchoSprintfSniff implements Sniff
{
/**
* Functions to look for with their replacements.
*
* @since 1.1.0
*
* @var array<string, string>
*/
private $targetFunctions = [
'sprintf' => 'printf',
'vsprintf' => 'vprintf',
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.1.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_ECHO];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.1.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$skip = Tokens::$emptyTokens;
$skip[] = \T_NS_SEPARATOR;
$next = $phpcsFile->findNext($skip, ($stackPtr + 1), null, true);
if ($next === false
|| $tokens[$next]['code'] !== \T_STRING
|| isset($this->targetFunctions[\strtolower($tokens[$next]['content'])]) === false
) {
// Not our target.
return;
}
$detectedFunction = \strtolower($tokens[$next]['content']);
$openParens = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
if ($openParens === false
|| $tokens[$openParens]['code'] !== \T_OPEN_PARENTHESIS
|| isset($tokens[$openParens]['parenthesis_closer']) === false
) {
// Live coding/parse error.
return;
}
$closeParens = $tokens[$openParens]['parenthesis_closer'];
$afterFunctionCall = $phpcsFile->findNext(Tokens::$emptyTokens, ($closeParens + 1), null, true);
if ($afterFunctionCall === false
|| ($tokens[$afterFunctionCall]['code'] !== \T_SEMICOLON
&& $tokens[$afterFunctionCall]['code'] !== \T_CLOSE_TAG)
) {
// Live coding/parse error or compound echo statement.
return;
}
$fix = $phpcsFile->addFixableError(
'Unnecessary "echo %s(...)" found. Use "%s(...)" instead.',
$next,
'Found',
[
$tokens[$next]['content'],
$this->targetFunctions[$detectedFunction],
]
);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
// Remove echo and whitespace.
$phpcsFile->fixer->replaceToken($stackPtr, '');
for ($i = ($stackPtr + 1); $i < $next; $i++) {
if ($tokens[$i]['code'] !== \T_WHITESPACE) {
break;
}
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->replaceToken($next, $this->targetFunctions[$detectedFunction]);
$phpcsFile->fixer->endChangeset();
}
}
}

View file

@ -0,0 +1,216 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\Utils\Conditions;
use PHPCSUtils\Utils\FunctionDeclarations;
use PHPCSUtils\Utils\GetTokensAsString;
use PHPCSUtils\Utils\ObjectDeclarations;
use PHPCSUtils\Utils\Scopes;
/**
* Forbid the use of the `static` keyword for late static binding in OO constructs which are final.
*
* @since 1.0.0
*/
final class StaticInFinalClassSniff implements Sniff
{
/**
* OO Scopes in which late static binding is useless.
*
* @var array<int|string>
*/
private $validOOScopes = [
\T_CLASS, // Only if final.
\T_ANON_CLASS, // Final by nature.
\T_ENUM, // Final by design.
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [
// These tokens are used to retrieve return types reliably.
\T_FUNCTION,
\T_FN,
// While this is our "real" target.
\T_STATIC,
// But we also need this as after "instanceof", `static` is tokenized as `T_STRING in PHPCS < 4.0.0.
// See: https://github.com/squizlabs/PHP_CodeSniffer/pull/3121
\T_STRING,
];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return int|void Integer stack pointer to skip forward or void to continue
* normal file processing.
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['code'] === \T_STRING
&& \strtolower($tokens[$stackPtr]['content']) !== 'static'
) {
return;
}
if ($tokens[$stackPtr]['code'] === \T_FUNCTION
|| $tokens[$stackPtr]['code'] === \T_FN
) {
/*
* Check return types for methods in final classes, anon classes and enums.
*
* Will return the scope opener of the function to prevent potential duplicate notifications.
*/
$scopeOpener = $stackPtr;
if (isset($tokens[$stackPtr]['scope_opener']) === true) {
$scopeOpener = $tokens[$stackPtr]['scope_opener'];
}
if ($tokens[$stackPtr]['code'] === \T_FUNCTION) {
$ooPtr = Scopes::validDirectScope($phpcsFile, $stackPtr, $this->validOOScopes);
if ($ooPtr === false) {
// Method in a trait (not known where it is used), interface (never final) or not in an OO scope.
return $scopeOpener;
}
} else {
$ooPtr = Conditions::getLastCondition($phpcsFile, $stackPtr, $this->validOOScopes);
if ($ooPtr === false) {
// Arrow function outside of OO.
return $scopeOpener;
}
}
if ($tokens[$ooPtr]['code'] === \T_CLASS) {
$classProps = ObjectDeclarations::getClassProperties($phpcsFile, $ooPtr);
if ($classProps['is_final'] === false) {
// Method in a non-final class.
return $scopeOpener;
}
}
$functionProps = FunctionDeclarations::getProperties($phpcsFile, $stackPtr);
if ($functionProps['return_type'] === '') {
return $scopeOpener;
}
$staticPtr = $phpcsFile->findNext(
\T_STATIC,
$functionProps['return_type_token'],
($functionProps['return_type_end_token'] + 1)
);
if ($staticPtr === false) {
return $scopeOpener;
}
// Found a return type containing the `static` type.
$this->handleError($phpcsFile, $staticPtr, 'ReturnType', '"static" return type');
return $scopeOpener;
}
/*
* Check other uses of static.
*/
$functionPtr = Conditions::getLastCondition($phpcsFile, $stackPtr, [\T_FUNCTION, \T_CLOSURE]);
if ($functionPtr === false || $tokens[$functionPtr]['code'] === \T_CLOSURE) {
/*
* When `false`, this code is absolutely invalid, but not something to be addressed via this sniff.
* When a closure, we're not interested in it. The closure class is final, but closures
* can be bound to other classes. This needs further research and should maybe get its own sniff.
*/
return;
}
$ooPtr = Scopes::validDirectScope($phpcsFile, $functionPtr, $this->validOOScopes);
if ($ooPtr === false) {
// Not in an OO context.
return;
}
if ($tokens[$ooPtr]['code'] === \T_CLASS) {
$classProps = ObjectDeclarations::getClassProperties($phpcsFile, $ooPtr);
if ($classProps['is_final'] === false) {
// Token in a non-final class.
return;
}
}
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($prevNonEmpty !== false) {
if ($tokens[$prevNonEmpty]['code'] === \T_INSTANCEOF) {
$prevPrevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevNonEmpty - 1), null, true);
$extraMsg = GetTokensAsString::compact($phpcsFile, $prevPrevNonEmpty, $stackPtr, true);
$this->handleError($phpcsFile, $stackPtr, 'InstanceOf', '"' . $extraMsg . '"');
return;
}
if ($tokens[$prevNonEmpty]['code'] === \T_NEW) {
$this->handleError($phpcsFile, $stackPtr, 'NewInstance', '"new static"');
return;
}
}
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['code'] === \T_DOUBLE_COLON) {
$nextNextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true);
$extraMsg = GetTokensAsString::compact($phpcsFile, $stackPtr, $nextNextNonEmpty, true);
$this->handleError($phpcsFile, $stackPtr, 'ScopeResolution', '"' . $extraMsg . '"');
return;
}
}
/**
* Throw and potentially fix the error.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of erroneous `T_STATIC` token.
* @param string $errorCode The error code for the message.
* @param string $extraMsg Addition to the error message.
*
* @return void
*/
private function handleError($phpcsFile, $stackPtr, $errorCode, $extraMsg)
{
$fix = $phpcsFile->addFixableError(
'Use "self" instead of "static" when using late static binding in a final OO construct. Found: %s',
$stackPtr,
$errorCode,
[$extraMsg]
);
if ($fix === true) {
$phpcsFile->fixer->replaceToken($stackPtr, 'self');
}
}
}

View file

@ -0,0 +1,106 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Constants;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
/**
* Verifies that the "::class" keyword when used for class name resolution is in lowercase.
*
* @link https://www.php.net/manual/en/language.constants.predefined.php
* @link https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class.class
*
* @since 1.0.0
*/
final class LowercaseClassResolutionKeywordSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Magic ::class constant case';
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_STRING];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$content = $tokens[$stackPtr]['content'];
$contentLC = \strtolower($content);
if ($contentLC !== 'class') {
return;
}
$nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($nextToken !== false && $tokens[$nextToken]['code'] === \T_OPEN_PARENTHESIS) {
// Function call or declaration for a function called "class".
return;
}
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($prevToken === false || $tokens[$prevToken]['code'] !== \T_DOUBLE_COLON) {
return;
}
if ($contentLC === $content) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'lowercase');
return;
}
$error = "The ::class keyword for class name resolution must be in lowercase. Expected: '::%s'; found: '::%s'";
$data = [
$contentLC,
$content,
];
$errorCode = '';
if (\strtoupper($content) === $content) {
$errorCode = 'Uppercase';
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'uppercase');
} else {
$errorCode = 'Mixedcase';
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'mixed case');
}
$fix = $phpcsFile->addFixableError($error, $stackPtr, $errorCode, $data);
if ($fix === true) {
$phpcsFile->fixer->replaceToken($stackPtr, $contentLC);
}
}
}

View file

@ -0,0 +1,199 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2022 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Constants;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\Scopes;
/**
* Standardize the modifier keyword order for OO constant declarations.
*
* @since 1.0.0
*/
final class ModifierKeywordOrderSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'OO constant modifier keyword order';
/**
* Order preference: final visibility.
*
* @since 1.0.0
*
* @var string
*/
const FINAL_VISIBILITY = 'final visibility';
/**
* Order preference: visibility final.
*
* @since 1.0.0
*
* @var string
*/
const VISIBILITY_FINAL = 'visibility final';
/**
* Preferred order for the modifier keywords.
*
* Accepted values:
* - "final visibility".
* - or "visibility final".
*
* Defaults to "final visibility".
*
* @since 1.0.0
*
* @var string
*/
public $order = self::FINAL_VISIBILITY;
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_CONST];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
if (Scopes::isOOConstant($phpcsFile, $stackPtr) === false) {
return;
}
$tokens = $phpcsFile->getTokens();
$valid = Collections::constantModifierKeywords() + Tokens::$emptyTokens;
$finalPtr = false;
$visibilityPtr = false;
for ($i = ($stackPtr - 1); $i > 0; $i--) {
if (isset($valid[$tokens[$i]['code']]) === false) {
break;
}
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
continue;
}
if ($tokens[$i]['code'] === \T_FINAL) {
$finalPtr = $i;
} else {
$visibilityPtr = $i;
}
}
if ($finalPtr === false || $visibilityPtr === false) {
/*
* Either no modifier keywords found at all; or only one type of modifier
* keyword (final or visibility) declared, but not both. No ordering needed.
*/
return;
}
if ($visibilityPtr < $finalPtr) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, self::VISIBILITY_FINAL);
} else {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, self::FINAL_VISIBILITY);
}
$message = 'OO constant modifier keywords are not in the correct order. Expected: "%s", found: "%s"';
switch ($this->order) {
case self::VISIBILITY_FINAL:
if ($visibilityPtr < $finalPtr) {
// Order is correct. Nothing to do.
return;
}
$this->handleError($phpcsFile, $finalPtr, $visibilityPtr);
break;
case self::FINAL_VISIBILITY:
default:
if ($finalPtr < $visibilityPtr) {
// Order is correct. Nothing to do.
return;
}
$this->handleError($phpcsFile, $visibilityPtr, $finalPtr);
break;
}
}
/**
* Throw the error and potentially fix it.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $firstKeyword The position of the first keyword found.
* @param int $secondKeyword The position of the second keyword token.
*
* @return void
*/
private function handleError(File $phpcsFile, $firstKeyword, $secondKeyword)
{
$tokens = $phpcsFile->getTokens();
$message = 'Constant modifier keywords are not in the correct order. Expected: "%s", found: "%s"';
$data = [
$tokens[$secondKeyword]['content'] . ' ' . $tokens[$firstKeyword]['content'],
$tokens[$firstKeyword]['content'] . ' ' . $tokens[$secondKeyword]['content'],
];
$fix = $phpcsFile->addFixableError($message, $firstKeyword, 'Incorrect', $data);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken($secondKeyword, '');
// Prevent leaving behind trailing whitespace.
$i = ($secondKeyword + 1);
while ($tokens[$i]['code'] === \T_WHITESPACE) {
$phpcsFile->fixer->replaceToken($i, '');
++$i;
}
// Use the original token content as the case used for keywords is not the concern of this sniff.
$phpcsFile->fixer->addContentBefore($firstKeyword, $tokens[$secondKeyword]['content'] . ' ');
$phpcsFile->fixer->endChangeset();
}
}
}

View file

@ -0,0 +1,89 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Constants;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
/**
* Verifies that PHP native `__...__` magic constants are in uppercase when used.
*
* @link https://www.php.net/manual/en/language.constants.predefined.php
*
* @since 1.0.0
*/
final class UppercaseMagicConstantsSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Magic constant case';
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return Tokens::$magicConstants;
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$content = $tokens[$stackPtr]['content'];
$contentUC = \strtoupper($content);
if ($contentUC === $content) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'uppercase');
return;
}
$error = 'Magic constants should be in uppercase. Expected: %s; found: %s';
$errorCode = '';
$data = [
$contentUC,
$content,
];
if (\strtolower($content) === $content) {
$errorCode = 'Lowercase';
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'lowercase');
} else {
$errorCode = 'Mixedcase';
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'mixed case');
}
$fix = $phpcsFile->addFixableError($error, $stackPtr, $errorCode, $data);
if ($fix === true) {
$phpcsFile->fixer->replaceToken($stackPtr, $contentUC);
}
}
}

View file

@ -0,0 +1,216 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\ControlStructures;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\ControlStructures;
/**
* Forbid the use of the alternative syntax for control structures.
*
* @since 1.0.0
*/
final class DisallowAlternativeSyntaxSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Control Structure Style';
/**
* Whether to allow the alternative syntax when it is wrapped around
* inline HTML, as is often seen in views.
*
* Note: inline HTML within "closed scopes" - like function declarations -,
* within the control structure body will not be taken into account.
*
* @since 1.0.0
*
* @var bool
*/
public $allowWithInlineHTML = false;
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
$targets = Collections::alternativeControlStructureSyntaxes();
// Don't look for elseif/else as they need to be dealt with in one go with the if.
unset($targets[\T_ELSEIF], $targets[\T_ELSE]);
return $targets;
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
/*
* Ignore control structures without body (i.e. single line control structures).
* This doesn't ignore _empty_ bodies.
*/
if (ControlStructures::hasBody($phpcsFile, $stackPtr, true) === false) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'single line (without body)');
return;
}
$tokens = $phpcsFile->getTokens();
/*
* Check if the control structure uses alternative syntax.
*/
if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
// No scope opener found: inline control structure or parse error.
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'inline');
return;
}
$opener = $tokens[$stackPtr]['scope_opener'];
$closer = $tokens[$stackPtr]['scope_closer'];
if ($tokens[$opener]['code'] !== \T_COLON) {
// Curly brace syntax (not our concern).
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'curly braces');
return;
}
/*
* As of here, we *know* the control structure must be using alternative syntax and
* must have all scope openers/closers set as, in case of parse errors, PHPCS wouldn't
* have set the scope opener, even for the first `if`.
*
* Also note that alternative syntax cannot be used with `else if`, so we don't need to take that
* into account.
*/
/*
* Determine whether there is inline HTML.
*
* For "chained" control structures (if - elseif - else), the complete control structure
* needs to be examined in one go as these cannot be changed individually, only as a complete group.
*/
$closedScopes = Collections::closedScopes();
$find = $closedScopes;
$find[\T_INLINE_HTML] = \T_INLINE_HTML;
$chainedIssues = [];
$hasInlineHTML = false;
$currentPtr = $stackPtr;
do {
$opener = $tokens[$currentPtr]['scope_opener'];
$closer = $tokens[$currentPtr]['scope_closer'];
$chainedIssues[$opener] = $closer;
if ($hasInlineHTML === true) {
// No need to search the contents, we already know there is inline HTML.
$currentPtr = $closer;
continue;
}
$inlineHTMLPtr = $opener;
do {
$inlineHTMLPtr = $phpcsFile->findNext($find, ($inlineHTMLPtr + 1), $closer);
if ($tokens[$inlineHTMLPtr]['code'] === \T_INLINE_HTML) {
$hasInlineHTML = true;
break;
}
if (isset($closedScopes[$tokens[$inlineHTMLPtr]['code']], $tokens[$inlineHTMLPtr]['scope_closer'])) {
$inlineHTMLPtr = $tokens[$inlineHTMLPtr]['scope_closer'];
}
} while ($inlineHTMLPtr !== false && $inlineHTMLPtr < $closer);
$currentPtr = $closer;
} while (isset(Collections::alternativeControlStructureSyntaxes()[$tokens[$closer]['code']]) === true);
if ($hasInlineHTML === true) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'alternative syntax with inline HTML');
} else {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'alternative syntax');
}
if ($hasInlineHTML === true && $this->allowWithInlineHTML === true) {
return;
}
$error = 'Using control structures with the alternative syntax is not allowed';
if ($this->allowWithInlineHTML === true) {
$error .= ' unless the control structure contains inline HTML';
}
$error .= '. Found: %1$s(): ... end%1$s;';
$code = 'Found' . \ucfirst($tokens[$stackPtr]['content']);
if ($hasInlineHTML === true) {
$code .= 'WithInlineHTML';
}
$data = [$tokens[$stackPtr]['content']];
foreach ($chainedIssues as $opener => $closer) {
$fix = $phpcsFile->addFixableError($error, $opener, $code, $data);
}
if ($fix === false) {
return;
}
/*
* Fix all issues for this chain in one go to diminish the chance of conflicts.
*/
$phpcsFile->fixer->beginChangeset();
foreach ($chainedIssues as $opener => $closer) {
$phpcsFile->fixer->replaceToken($opener, '{');
if (isset(Collections::alternativeControlStructureSyntaxClosers()[$tokens[$closer]['code']]) === true) {
$phpcsFile->fixer->replaceToken($closer, '}');
$semicolon = $phpcsFile->findNext(Tokens::$emptyTokens, ($closer + 1), null, true);
if ($semicolon !== false && $tokens[$semicolon]['code'] === \T_SEMICOLON) {
$phpcsFile->fixer->replaceToken($semicolon, '');
}
} else {
/*
* This must be an if/else using alternative syntax.
* The closer will be the next control structure keyword.
*/
$phpcsFile->fixer->addContentBefore($closer, '} ');
}
}
$phpcsFile->fixer->endChangeset();
}
}

View file

@ -0,0 +1,348 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\ControlStructures;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\Utils\ControlStructures;
use PHPCSUtils\Utils\GetTokensAsString;
/**
* Disallow `if` statements as the only statement in an `else` block.
*
* Note: This sniff will not fix the indentation of the "inner" code. It is strongly recommended to run
* this sniff together with the `Generic.WhiteSpace.ScopeIndent` sniff to get the correct indentation.
*
* Inspired by the {@link https://eslint.org/docs/rules/no-lonely-if ESLint "no lonely if"} rule
* in response to upstream {@link https://github.com/squizlabs/PHP_CodeSniffer/issues/3206 PHPCS issue 3206}.
*
* @since 1.0.0
*/
final class DisallowLonelyIfSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_ELSE];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
/*
* Deal with `else if`.
*/
if (ControlStructures::isElseIf($phpcsFile, $stackPtr) === true) {
// Ignore, not our real target.
return;
}
if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
// Either an else without curly braces or a parse error. Ignore.
return;
}
$outerScopeOpener = $tokens[$stackPtr]['scope_opener'];
$outerScopeCloser = $tokens[$stackPtr]['scope_closer'];
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($outerScopeOpener + 1), $outerScopeCloser, true);
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_IF) {
// Definitely not a lonely if statement.
return;
}
if (isset($tokens[$nextNonEmpty]['scope_closer']) === false) {
// Either a control structure without curly braces or a parse error. Ignore.
return;
}
/*
* Find the end of an if - else chain.
*/
$innerIfPtr = $nextNonEmpty;
$innerIfToken = $tokens[$innerIfPtr];
$autoFixable = true;
$innerScopeCloser = $innerIfToken['scope_closer'];
// For alternative syntax fixer only.
// Remember the individual inner scope opener and closers so the fixer doesn't need
// to do the same walking over the if/else chain again.
$innerScopes = [
$innerIfToken['scope_opener'] => $innerScopeCloser,
];
do {
/*
* Handle control structures using alternative syntax.
*/
if ($tokens[$innerScopeCloser]['code'] !== \T_CLOSE_CURLY_BRACKET) {
if ($tokens[$innerScopeCloser]['code'] === \T_ENDIF) {
$nextAfter = $phpcsFile->findNext(
Tokens::$emptyTokens,
($innerScopeCloser + 1),
$outerScopeCloser,
true
);
if ($tokens[$nextAfter]['code'] === \T_CLOSE_TAG) {
// Not "lonely" as at the very least there must be a PHP open tag before the outer closer.
return;
}
if ($tokens[$nextAfter]['code'] === \T_SEMICOLON) {
$innerScopeCloser = $nextAfter;
} else {
// Missing semi-colon. Report, but don't auto-fix.
$autoFixable = false;
}
} else {
// This must be an else[if].
--$innerScopeCloser;
}
}
$innerNextNonEmpty = $phpcsFile->findNext(
Tokens::$emptyTokens,
($innerScopeCloser + 1),
$outerScopeCloser,
true
);
if ($innerNextNonEmpty === false) {
// This was the last closer.
break;
}
if ($tokens[$innerNextNonEmpty]['code'] !== \T_ELSE
&& $tokens[$innerNextNonEmpty]['code'] !== \T_ELSEIF
) {
// Found another statement after the control structure. The "if" is not lonely.
return;
}
if (isset($tokens[$innerNextNonEmpty]['scope_closer']) === false) {
// This may still be an "else if"...
$nextAfter = $phpcsFile->findNext(
Tokens::$emptyTokens,
($innerNextNonEmpty + 1),
$outerScopeCloser,
true
);
if ($nextAfter === false
|| $tokens[$nextAfter]['code'] !== \T_IF
|| isset($tokens[$nextAfter]['scope_closer']) === false
) {
// Defense in depth. Either a control structure without curly braces or a parse error. Ignore.
return;
}
$innerNextNonEmpty = $nextAfter;
}
$innerScopeCloser = $tokens[$innerNextNonEmpty]['scope_closer'];
$innerScopes[$tokens[$innerNextNonEmpty]['scope_opener']] = $innerScopeCloser;
} while (true);
/*
* As of now, we know we have an error. Check if it can be auto-fixed.
*/
if ($phpcsFile->findNext(\T_WHITESPACE, ($innerScopeCloser + 1), $outerScopeCloser, true) !== false) {
// Comment between the inner and outer closers.
$autoFixable = false;
}
if ($tokens[$innerScopeCloser]['code'] === \T_SEMICOLON) {
$hasComment = $phpcsFile->findPrevious(\T_WHITESPACE, ($innerScopeCloser - 1), null, true);
if ($tokens[$hasComment]['code'] !== \T_ENDIF) {
// Comment between the "endif" and the semi-colon.
$autoFixable = false;
}
}
if ($tokens[$outerScopeOpener]['line'] !== $innerIfToken['line']) {
for ($startOfNextLine = ($outerScopeOpener + 1); $startOfNextLine < $innerIfPtr; $startOfNextLine++) {
if ($tokens[$outerScopeOpener]['line'] !== $tokens[$startOfNextLine]['line']) {
break;
}
}
if ($phpcsFile->findNext(\T_WHITESPACE, $startOfNextLine, $innerIfPtr, true) !== false) {
// Comment between the inner and outer openers.
$autoFixable = false;
}
}
if (isset($innerIfToken['parenthesis_opener'], $innerIfToken['parenthesis_closer']) === false) {
// Start/end of the condition of the if unclear. Most likely a parse error.
$autoFixable = false;
}
/*
* Throw the error and potentially fix it.
*/
$error = 'If control structure block found as the only statement within an "else" block. Use elseif instead.';
$code = 'Found';
if ($autoFixable === false) {
$phpcsFile->addError($error, $stackPtr, $code);
return;
}
$fix = $phpcsFile->addFixableError($error, $stackPtr, $code);
if ($fix === false) {
return;
}
/*
* Fix it.
*/
$outerInnerSameType = false;
if (($tokens[$outerScopeCloser]['code'] === \T_CLOSE_CURLY_BRACKET
&& $tokens[$innerScopeCloser]['code'] === \T_CLOSE_CURLY_BRACKET)
|| ($tokens[$outerScopeCloser]['code'] === \T_ENDIF
&& $tokens[$innerScopeCloser]['code'] === \T_SEMICOLON)
) {
$outerInnerSameType = true;
}
$targetIsCurly = ($tokens[$outerScopeCloser]['code'] === \T_CLOSE_CURLY_BRACKET);
$innerScopeCount = \count($innerScopes);
$condition = GetTokensAsString::origContent($phpcsFile, ($innerIfPtr + 1), ($innerIfToken['scope_opener'] - 1));
if ($targetIsCurly === true) {
$condition = \rtrim($condition) . ' ';
}
$phpcsFile->fixer->beginChangeset();
// Remove the inner if + condition up to and including the scope opener.
for ($i = $innerIfPtr; $i <= $innerIfToken['scope_opener']; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
// Potentially remove trailing whitespace/new line if there is no comment after the inner condition.
while ($tokens[$i]['line'] === $innerIfToken['line']
&& $tokens[$i]['code'] === \T_WHITESPACE
) {
$phpcsFile->fixer->replaceToken($i, '');
++$i;
}
// Remove any potential indentation whitespace for the inner if.
if ($tokens[$outerScopeOpener]['line'] !== $innerIfToken['line']
&& $tokens[$i]['line'] !== $innerIfToken['line']
) {
$i = ($nextNonEmpty - 1);
while ($tokens[$i]['line'] === $innerIfToken['line']
&& $tokens[$i]['code'] === \T_WHITESPACE
) {
$phpcsFile->fixer->replaceToken($i, '');
--$i;
}
}
// Remove the inner scope closer.
$phpcsFile->fixer->replaceToken($innerScopeCloser, '');
$i = ($innerScopeCloser - 1);
// Handle alternative syntax for the closer.
if ($tokens[$innerScopeCloser]['code'] === \T_SEMICOLON) {
// Remove potential whitespace between the "endif" and the semicolon.
while ($tokens[$i]['code'] === \T_WHITESPACE) {
$phpcsFile->fixer->replaceToken($i, '');
--$i;
}
// Remove the "endif".
$phpcsFile->fixer->replaceToken($i, '');
--$i;
}
// Remove superfluous whitespace before the inner scope closer.
while ($tokens[$i]['code'] === \T_WHITESPACE) {
$phpcsFile->fixer->replaceToken($i, '');
--$i;
}
// Replace the else.
$phpcsFile->fixer->replaceToken($stackPtr, 'elseif' . $condition);
// Remove potential superfluous whitespace between the new condition and the scope opener.
$i = ($stackPtr + 1);
while ($tokens[$i]['line'] === $tokens[$stackPtr]['line']
&& $tokens[$i]['code'] === \T_WHITESPACE
) {
$phpcsFile->fixer->replaceToken($i, '');
++$i;
}
if ($outerInnerSameType === false
&& $innerScopeCount > 1
) {
$loop = 1;
foreach ($innerScopes as $opener => $closer) {
if ($targetIsCurly === true) {
if ($loop !== 1) {
// Only handle the opener when it's not the first of the chain as that's already handled above.
$phpcsFile->fixer->replaceToken($opener, ' {');
}
if ($loop !== $innerScopeCount) {
// Only handle the closer when it's not the last of the chain as that's already handled above.
$phpcsFile->fixer->addContentBefore($closer, '} ');
}
} else {
if ($loop !== 1) {
// Only handle the opener when it's not the first of the chain as that's already handled above.
$phpcsFile->fixer->replaceToken($opener, ':');
}
if ($loop !== $innerScopeCount) {
// Only handle the closer when it's not the last of the chain as that's already handled above.
$phpcsFile->fixer->replaceToken($closer, '');
$j = ($closer + 1);
while ($tokens[$j]['code'] === \T_WHITESPACE) {
$phpcsFile->fixer->replaceToken($j, '');
++$j;
}
}
}
++$loop;
}
}
$phpcsFile->fixer->endChangeset();
}
}

View file

@ -0,0 +1,164 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\ControlStructures;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
/**
* Verifies that `else(if)` statements with braces are on a new line.
*
* Sister-sniff to the following two PHPCS native sniffs which each demand that `else[]if` is on the
* same line as the closing curly of the preceding `(else)if`:
* - `PEAR.ControlStructures.ControlSignature[.Found]`
* - `Squiz.ControlStructures.ControlSignature.SpaceAfterCloseBrace`
*
* Other related sniffs:
* - `Squiz.ControlStructures.ElseIfDeclaration` Forbids the use of "elseif", demands "else if".
* - `PSR2.ControlStructures.ElseIfDeclaration` Forbids the use of "else if", demands "elseif".
*
* @since 1.0.0
*/
final class IfElseDeclarationSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Else(if) on a new line';
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [
\T_ELSE,
\T_ELSEIF,
];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
/*
* Check for control structures without braces and alternative syntax.
*/
$scopePtr = $stackPtr;
if (isset($tokens[$stackPtr]['scope_opener']) === false) {
// Deal with "else if".
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($tokens[$next]['code'] === \T_IF) {
$scopePtr = $next;
}
}
if (isset($tokens[$scopePtr]['scope_opener']) === false
|| $tokens[$tokens[$scopePtr]['scope_opener']]['code'] === \T_COLON
) {
// No scope opener found or alternative syntax (not our concern).
return;
}
/*
* Check whether the else(if) is on a new line.
*/
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($prevNonEmpty === false || $tokens[$prevNonEmpty]['code'] !== \T_CLOSE_CURLY_BRACKET) {
// Parse error or mixing braced and non-braced. Not our concern.
return;
}
if ($tokens[$prevNonEmpty]['line'] !== $tokens[$stackPtr]['line']) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
return;
}
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
$errorBase = \strtoupper($tokens[$stackPtr]['content']);
$error = $errorBase . ' statement must be on a new line.';
$prevNonWhitespace = $phpcsFile->findPrevious(\T_WHITESPACE, ($stackPtr - 1), null, true);
if ($prevNonWhitespace !== $prevNonEmpty) {
// Comment found between previous scope closer and the keyword.
$fix = $phpcsFile->addError($error, $stackPtr, 'NoNewLine');
return;
}
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoNewLine');
if ($fix === false) {
return;
}
/*
* Fix it.
*/
// Figure out the indentation for the else(if).
$indentBase = $prevNonEmpty;
if (isset($tokens[$prevNonEmpty]['scope_condition']) === true
&& ($tokens[$tokens[$prevNonEmpty]['scope_condition']]['column'] === 1
|| ($tokens[($tokens[$prevNonEmpty]['scope_condition'] - 1)]['code'] === \T_WHITESPACE
&& $tokens[($tokens[$prevNonEmpty]['scope_condition'] - 1)]['column'] === 1))
) {
// Base the indentation off the previous if/elseif if on a line by itself.
$indentBase = $tokens[$prevNonEmpty]['scope_condition'];
}
$indent = '';
$firstOnIndentLine = $indentBase;
if ($tokens[$firstOnIndentLine]['column'] !== 1) {
while (isset($tokens[($firstOnIndentLine - 1)]) && $tokens[--$firstOnIndentLine]['column'] !== 1);
if ($tokens[$firstOnIndentLine]['code'] === \T_WHITESPACE) {
$indent = $tokens[$firstOnIndentLine]['content'];
// If tabs were replaced, use the original content.
if (isset($tokens[$firstOnIndentLine]['orig_content']) === true) {
$indent = $tokens[$firstOnIndentLine]['orig_content'];
}
}
}
$phpcsFile->fixer->beginChangeset();
// Remove any whitespace between the previous scope closer and the else(if).
for ($i = ($prevNonEmpty + 1); $i < $stackPtr; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->addContent($prevNonEmpty, $phpcsFile->eolChar . $indent);
$phpcsFile->fixer->endChangeset();
}
}

View file

@ -0,0 +1,190 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Files;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\Tokens\Collections;
/**
* A file should either declare (global/namespaced) functions or declare OO structures, but not both.
*
* Nested function declarations, i.e. functions declared within a function/method will be disregarded
* for the purposes of this sniff.
* The same goes for anonymous classes, closures and arrow functions.
*
* Notes:
* - This sniff has no opinion on side effects. If you want to sniff for those, use the PHPCS
* native `PSR1.Files.SideEffects` sniff.
* - This sniff has no opinion on multiple OO structures being declared in one file.
* If you want to sniff for that, use the PHPCS native `Generic.Files.OneObjectStructurePerFile` sniff.
*
* @since 1.0.0
*/
final class SeparateFunctionsFromOOSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Functions or OO declarations ?';
/**
* Tokens this sniff searches for.
*
* Enhanced from within the register() methods.
*
* @since 1.0.0
*
* @var array<int|string>
*/
private $search = [
// Some tokens to help skip over structures we're not interested in.
\T_START_HEREDOC => \T_START_HEREDOC,
\T_START_NOWDOC => \T_START_NOWDOC,
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
$this->search += Tokens::$ooScopeTokens;
$this->search += Collections::functionDeclarationTokens();
return Collections::phpOpenTags();
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return int|void Integer stack pointer to skip forward or void to continue
* normal file processing.
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$firstOO = null;
$firstFunction = null;
$functionCount = 0;
$OOCount = 0;
for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
// Ignore anything within square brackets.
if ($tokens[$i]['code'] !== \T_OPEN_CURLY_BRACKET
&& isset($tokens[$i]['bracket_opener'], $tokens[$i]['bracket_closer'])
&& $i === $tokens[$i]['bracket_opener']
) {
$i = $tokens[$i]['bracket_closer'];
continue;
}
// Skip past nested arrays, function calls and arbitrary groupings.
if ($tokens[$i]['code'] === \T_OPEN_PARENTHESIS
&& isset($tokens[$i]['parenthesis_closer'])
) {
$i = $tokens[$i]['parenthesis_closer'];
continue;
}
// Skip over potentially large docblocks.
if ($tokens[$i]['code'] === \T_DOC_COMMENT_OPEN_TAG
&& isset($tokens[$i]['comment_closer'])
) {
$i = $tokens[$i]['comment_closer'];
continue;
}
// Ignore everything else we're not interested in.
if (isset($this->search[$tokens[$i]['code']]) === false) {
continue;
}
// Skip over structures which won't contain anything we're interested in.
if (($tokens[$i]['code'] === \T_START_HEREDOC
|| $tokens[$i]['code'] === \T_START_NOWDOC
|| $tokens[$i]['code'] === \T_ANON_CLASS
|| $tokens[$i]['code'] === \T_CLOSURE
|| $tokens[$i]['code'] === \T_FN)
&& isset($tokens[$i]['scope_condition'], $tokens[$i]['scope_closer'])
&& $tokens[$i]['scope_condition'] === $i
) {
$i = $tokens[$i]['scope_closer'];
continue;
}
// This will be either a function declaration or an OO declaration token.
if ($tokens[$i]['code'] === \T_FUNCTION) {
if (isset($firstFunction) === false) {
$firstFunction = $i;
}
++$functionCount;
} else {
if (isset($firstOO) === false) {
$firstOO = $i;
}
++$OOCount;
}
if (isset($tokens[$i]['scope_closer']) === true) {
$i = $tokens[$i]['scope_closer'];
}
}
if ($functionCount > 0 && $OOCount > 0) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'Both function and OO declarations');
$reportToken = \max($firstFunction, $firstOO);
$phpcsFile->addError(
'A file should either contain function declarations or OO structure declarations, but not both.'
. ' Found %d function declaration(s) and %d OO structure declaration(s).'
. ' The first function declaration was found on line %d;'
. ' the first OO declaration was found on line %d',
$reportToken,
'Mixed',
[
$functionCount,
$OOCount,
$tokens[$firstFunction]['line'],
$tokens[$firstOO]['line'],
]
);
} elseif ($functionCount > 0) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'Only function(s)');
} elseif ($OOCount > 0) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'Only OO structure(s)');
} else {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'Neither');
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}
}

View file

@ -0,0 +1,233 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\FunctionDeclarations;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
/**
* Forbids long closures.
*
* @since 1.1.0
*/
final class NoLongClosuresSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.1.0
*
* @var string
*/
const METRIC_NAME_CODE = 'Closure length (code only)';
/**
* Name of the metric.
*
* @since 1.1.0
*
* @var string
*/
const METRIC_NAME_COMMENTS = 'Closure length (code + comments)';
/**
* Name of the metric.
*
* @since 1.1.0
*
* @var string
*/
const METRIC_NAME_ALL = 'Closure length (code + comments + blank lines)';
/**
* Maximum number of lines allowed before a closure is considered a "long" closure.
*
* Defaults to 5 lines, i.e. when a closure contains 6 lines, a warning will be thrown.
*
* @since 1.1.0
*
* @var int
*/
public $recommendedLines = 5;
/**
* Maximum number of lines allowed before a closure is considered a "long" closure.
*
* Defaults to 8 lines, i.e. when a closure contains 9 lines, an error will be thrown.
*
* @since 1.1.0
*
* @var int
*/
public $maxLines = 8;
/**
* Whether or not to exclude lines which only contain documentation in the line count.
*
* Defaults to `true`.
*
* @since 1.1.0
*
* @var bool
*/
public $ignoreCommentLines = true;
/**
* Whether or not to exclude empty lines from the line count.
*
* Defaults to `true`.
*
* @since 1.1.0
*
* @var bool
*/
public $ignoreEmptyLines = true;
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.1.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_CLOSURE];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.1.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$this->recommendedLines = (int) $this->recommendedLines;
$this->maxLines = (int) $this->maxLines;
$tokens = $phpcsFile->getTokens();
if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
// Live coding/parse error. Shouldn't be possible as in that case tokenizer won't retokenize to T_CLOSURE.
return; // @codeCoverageIgnore
}
$opener = $tokens[$stackPtr]['scope_opener'];
$closer = $tokens[$stackPtr]['scope_closer'];
$currentLine = $tokens[$opener]['line'];
$closerLine = $tokens[$closer]['line'];
$codeLines = 0;
$commentLines = 0;
$blankLines = 0;
// Check whether the line of the scope opener needs to be counted, but ignore trailing comments on that line.
$firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($opener + 1), $closer, true);
if ($firstNonEmpty !== false && $tokens[$firstNonEmpty]['line'] === $currentLine) {
++$codeLines;
}
// Check whether the line of the scope closer needs to be counted.
if ($closerLine !== $currentLine) {
$hasCommentTokens = false;
$hasCodeTokens = false;
for ($i = ($closer - 1); $tokens[$i]['line'] === $closerLine && $i > $opener; $i--) {
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === false) {
$hasCodeTokens = true;
} elseif (isset(Tokens::$commentTokens[$tokens[$i]['code']]) === true) {
$hasCommentTokens = true;
}
}
if ($hasCodeTokens === true) {
++$codeLines;
} elseif ($hasCommentTokens === true) {
++$commentLines;
}
}
// We've already examined the opener line, so move to the next line.
for ($i = ($opener + 1); $tokens[$i]['line'] === $currentLine && $i < $closer; $i++);
$currentLine = $tokens[$i]['line'];
// Walk tokens.
while ($currentLine !== $closerLine) {
$hasCommentTokens = false;
$hasCodeTokens = false;
while ($tokens[$i]['line'] === $currentLine) {
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === false) {
$hasCodeTokens = true;
} elseif (isset(Tokens::$commentTokens[$tokens[$i]['code']]) === true) {
$hasCommentTokens = true;
}
++$i;
}
if ($hasCodeTokens === true) {
++$codeLines;
} elseif ($hasCommentTokens === true) {
++$commentLines;
} else {
// Only option left is that this is an empty line.
++$blankLines;
}
$currentLine = $tokens[$i]['line'];
}
$nonBlankLines = ($codeLines + $commentLines);
$totalLines = ($codeLines + $commentLines + $blankLines);
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME_CODE, $codeLines . ' lines');
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME_COMMENTS, $nonBlankLines . ' lines');
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME_ALL, $totalLines . ' lines');
$lines = $codeLines;
if ($this->ignoreCommentLines === false) {
$lines += $commentLines;
}
if ($this->ignoreEmptyLines === false) {
$lines += $blankLines;
}
$errorSuffix = ' Declare a named function instead. Found closure containing %s lines';
if ($lines > $this->maxLines) {
$phpcsFile->addError(
'Closures which are longer than %s lines are forbidden.' . $errorSuffix,
$stackPtr,
'ExceedsMaximum',
[$this->maxLines, $lines]
);
return;
}
if ($lines > $this->recommendedLines) {
$phpcsFile->addWarning(
'It is recommended for closures to contain %s lines or less.' . $errorSuffix,
$stackPtr,
'ExceedsRecommended',
[$this->recommendedLines, $lines]
);
}
}
}

View file

@ -0,0 +1,120 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2023 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\FunctionDeclarations;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHPCSUtils\Utils\FunctionDeclarations;
use PHPCSUtils\Utils\ObjectDeclarations;
use PHPCSUtils\Utils\Scopes;
/**
* Require non-abstract, non-private methods in traits to be declared as "final".
*
* @since 1.1.0
*/
final class RequireFinalMethodsInTraitsSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.1.0
*
* @var string
*/
const METRIC_NAME = 'Non-private method in trait is abstract or final ?';
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.1.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_FUNCTION];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.1.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if (isset($tokens[$stackPtr]['parenthesis_opener']) === false) {
// Parse error/live coding.
return;
}
$scopePtr = Scopes::validDirectScope($phpcsFile, $stackPtr, \T_TRAIT);
if ($scopePtr === false) {
// Not a trait method.
return;
}
$methodProps = FunctionDeclarations::getProperties($phpcsFile, $stackPtr);
if ($methodProps['scope'] === 'private') {
// Private methods can't be final.
return;
}
if ($methodProps['is_final'] === true) {
// Already final, nothing to do.
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'final');
return;
}
if ($methodProps['is_abstract'] === true) {
// Abstract classes can't be final.
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'abstract');
return;
}
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'not abstract, not final');
$methodName = FunctionDeclarations::getName($phpcsFile, $stackPtr);
$magic = '';
$code = 'NonFinalMethodFound';
if (FunctionDeclarations::isMagicMethodName($methodName) === true) {
// Use separate error code for magic methods.
$magic = 'magic ';
$code = 'NonFinalMagicMethodFound';
}
$data = [
$methodProps['scope'],
$magic,
$methodName,
ObjectDeclarations::getName($phpcsFile, $scopePtr),
];
$fix = $phpcsFile->addFixableError(
'The non-abstract, %s %smethod "%s()" in trait %s should be declared as final.',
$stackPtr,
$code,
$data
);
if ($fix === true) {
$phpcsFile->fixer->addContentBefore($stackPtr, 'final ');
}
}
}

View file

@ -0,0 +1,71 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Lists;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHPCSUtils\Utils\Lists;
/**
* Bans the use of the PHP long list syntax.
*
* @since 1.0.0
*/
final class DisallowLongListSyntaxSniff implements Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_LIST];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$openClose = Lists::getOpenClose($phpcsFile, $stackPtr);
if ($openClose === false) {
// Live coding or parse error.
return;
}
$fix = $phpcsFile->addFixableError('Long list syntax is not allowed', $stackPtr, 'Found');
if ($fix === true) {
$opener = $openClose['opener'];
$closer = $openClose['closer'];
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken($stackPtr, '');
$phpcsFile->fixer->replaceToken($opener, '[');
$phpcsFile->fixer->replaceToken($closer, ']');
$phpcsFile->fixer->endChangeset();
}
}
}

View file

@ -0,0 +1,86 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Lists;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\Lists;
/**
* Bans the use of the PHP short list syntax.
*
* @since 1.0.0
*/
final class DisallowShortListSyntaxSniff implements Sniff
{
/**
* The phrase to use for the metric recorded by this sniff.
*
* @var string
*/
const METRIC_NAME = 'Short list syntax used';
/**
* Registers the tokens that this sniff wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return Collections::listOpenTokensBC();
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['code'] === \T_LIST) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
return;
}
$openClose = Lists::getOpenClose($phpcsFile, $stackPtr);
if ($openClose === false) {
// Not a short list, live coding or parse error.
return;
}
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
$fix = $phpcsFile->addFixableError('Short list syntax is not allowed', $stackPtr, 'Found');
if ($fix === true) {
$opener = $openClose['opener'];
$closer = $openClose['closer'];
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken($opener, 'list(');
$phpcsFile->fixer->replaceToken($closer, ')');
$phpcsFile->fixer->endChangeset();
}
}
}

View file

@ -0,0 +1,81 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Namespaces;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHPCSUtils\Utils\Namespaces;
/**
* Disallow the use of namespace declarations using the curly brace syntax.
*
* @since 1.0.0
*/
final class DisallowCurlyBraceSyntaxSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Namespace declaration using curly brace syntax';
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_NAMESPACE];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
if (Namespaces::isDeclaration($phpcsFile, $stackPtr) === false) {
// Namespace operator, not a declaration; or live coding/parse error.
return;
}
$tokens = $phpcsFile->getTokens();
if (isset($tokens[$stackPtr]['scope_condition']) === false
|| $tokens[$stackPtr]['scope_condition'] !== $stackPtr
) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
return;
}
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
$phpcsFile->addError(
'Namespace declarations using the curly brace syntax are not allowed.',
$stackPtr,
'Forbidden'
);
}
}

View file

@ -0,0 +1,80 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Namespaces;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHPCSUtils\Utils\Namespaces;
/**
* Forbids the use of namespace declarations without a namespace name.
*
* @since 1.0.0
*/
final class DisallowDeclarationWithoutNameSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Namespace declaration declares a name';
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_NAMESPACE];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$name = Namespaces::getDeclaredName($phpcsFile, $stackPtr);
if ($name === false) {
// Use of the namespace keyword as an operator or live coding/parse error.
return;
}
if ($name !== '') {
// Named namespace.
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
return;
}
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
// Namespace declaration without namespace name (= global namespace).
$phpcsFile->addError(
'Namespace declarations without a namespace name are not allowed.',
$stackPtr,
'Forbidden'
);
}
}

View file

@ -0,0 +1,81 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Namespaces;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHPCSUtils\Utils\Namespaces;
/**
* Enforce the use of namespace declarations using the curly brace syntax.
*
* @since 1.0.0
*/
final class EnforceCurlyBraceSyntaxSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Namespace declaration using curly brace syntax';
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_NAMESPACE];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
if (Namespaces::isDeclaration($phpcsFile, $stackPtr) === false) {
// Namespace operator, not a declaration; or live coding/parse error.
return;
}
$tokens = $phpcsFile->getTokens();
if (isset($tokens[$stackPtr]['scope_condition']) === true
&& $tokens[$stackPtr]['scope_condition'] === $stackPtr
) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
return;
}
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
$phpcsFile->addError(
'Namespace declarations without curly braces are not allowed.',
$stackPtr,
'Forbidden'
);
}
}

View file

@ -0,0 +1,96 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Namespaces;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHPCSUtils\Utils\Namespaces;
/**
* Disallow having more than one namespace declaration in a file.
*
* @since 1.0.0
*/
final class OneDeclarationPerFileSniff implements Sniff
{
/**
* Current file being scanned.
*
* @since 1.0.0
*
* @var string
*/
private $currentFile;
/**
* Stack pointer to the first namespace declaration seen in the file.
*
* @since 1.0.0
*
* @var int|false
*/
private $declarationSeen = false;
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_NAMESPACE];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$fileName = $phpcsFile->getFilename();
if ($this->currentFile !== $fileName) {
// Reset the properties for each new file.
$this->currentFile = $fileName;
$this->declarationSeen = false;
}
if (Namespaces::isDeclaration($phpcsFile, $stackPtr) === false) {
// Namespace operator, not a declaration; or live coding/parse error.
return;
}
if ($this->declarationSeen === false) {
// This is the first namespace declaration in the file.
$this->declarationSeen = $stackPtr;
return;
}
$tokens = $phpcsFile->getTokens();
// OK, so this is a file with multiple namespace declarations.
$phpcsFile->addError(
'There should be only one namespace declaration per file. The first declaration was found on line %d',
$stackPtr,
'MultipleFound',
[$tokens[$this->declarationSeen]['line']]
);
}
}

View file

@ -0,0 +1,190 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\NamingConventions;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\FunctionDeclarations;
/**
* Verifies that parameters in function declarations do not use PHP reserved keywords
* as this can lead to confusing code when using PHP 8.0+ named parameters in function calls.
*
* Note: while parameters (variables) are case-sensitive in PHP, keywords are not,
* so this sniff checks for the keywords used in parameter names in a
* case-insensitive manner to make this sniff independent of code style rules
* regarding the case for parameter names.
*
* @link https://www.php.net/manual/en/reserved.keywords.php
*
* @since 1.0.0
*/
final class NoReservedKeywordParameterNamesSniff implements Sniff
{
/**
* A list of PHP reserved keywords.
*
* @since 1.0.0
*
* @var array<string, string> Key is the lowercased keyword, value the "proper" cased keyword.
*/
private $reservedNames = [
'abstract' => 'abstract',
'and' => 'and',
'array' => 'array',
'as' => 'as',
'break' => 'break',
'callable' => 'callable',
'case' => 'case',
'catch' => 'catch',
'class' => 'class',
'clone' => 'clone',
'const' => 'const',
'continue' => 'continue',
'declare' => 'declare',
'default' => 'default',
'die' => 'die',
'do' => 'do',
'echo' => 'echo',
'else' => 'else',
'elseif' => 'elseif',
'empty' => 'empty',
'enddeclare' => 'enddeclare',
'endfor' => 'endfor',
'endforeach' => 'endforeach',
'endif' => 'endif',
'endswitch' => 'endswitch',
'endwhile' => 'endwhile',
'enum' => 'enum',
'eval' => 'eval',
'exit' => 'exit',
'extends' => 'extends',
'final' => 'final',
'finally' => 'finally',
'fn' => 'fn',
'for' => 'for',
'foreach' => 'foreach',
'function' => 'function',
'global' => 'global',
'goto' => 'goto',
'if' => 'if',
'implements' => 'implements',
'include' => 'include',
'include_once' => 'include_once',
'instanceof' => 'instanceof',
'insteadof' => 'insteadof',
'interface' => 'interface',
'isset' => 'isset',
'list' => 'list',
'match' => 'match',
'namespace' => 'namespace',
'new' => 'new',
'or' => 'or',
'print' => 'print',
'private' => 'private',
'protected' => 'protected',
'public' => 'public',
'readonly' => 'readonly',
'require' => 'require',
'require_once' => 'require_once',
'return' => 'return',
'static' => 'static',
'switch' => 'switch',
'throw' => 'throw',
'trait' => 'trait',
'try' => 'try',
'unset' => 'unset',
'use' => 'use',
'var' => 'var',
'while' => 'while',
'xor' => 'xor',
'yield' => 'yield',
'__class__' => '__CLASS__',
'__dir__' => '__DIR__',
'__file__' => '__FILE__',
'__function__' => '__FUNCTION__',
'__line__' => '__LINE__',
'__method__' => '__METHOD__',
'__namespace__' => '__NAMESPACE__',
'__trait__' => '__TRAIT__',
'int' => 'int',
'float' => 'float',
'bool' => 'bool',
'string' => 'string',
'true' => 'true',
'false' => 'false',
'null' => 'null',
'void' => 'void',
'iterable' => 'iterable',
'object' => 'object',
'resource' => 'resource',
'mixed' => 'mixed',
'numeric' => 'numeric',
'never' => 'never',
/*
* Not reserved keywords, but equally confusing when used in the context of function calls
* with named parameters.
*/
'parent' => 'parent',
'self' => 'self',
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return Collections::functionDeclarationTokens();
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
// Get all parameters from method signature.
$parameters = FunctionDeclarations::getParameters($phpcsFile, $stackPtr);
if (empty($parameters)) {
return;
}
$message = 'It is recommended not to use reserved keyword "%s" as function parameter name. Found: %s';
foreach ($parameters as $param) {
$name = \ltrim($param['name'], '$');
$nameLC = \strtolower($name);
if (isset($this->reservedNames[$nameLC]) === true) {
$errorCode = $nameLC . 'Found';
$data = [
$this->reservedNames[$nameLC],
$param['name'],
];
$phpcsFile->addWarning($message, $param['token'], $errorCode, $data);
}
}
}
}

View file

@ -0,0 +1,275 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\OOStructures;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use PHPCSUtils\Tokens\Collections;
use PHPCSUtils\Utils\ObjectDeclarations;
/**
* Verifies that the interface names used in a class/enum "implements" statement or an interface "extends" statement,
* are listed in alphabetic order.
*
* @since 1.0.0
*/
final class AlphabeticExtendsImplementsSniff implements Sniff
{
/**
* Name of the "Alphabetically ordered" metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME_ALPHA = 'Interface names in implements/extends ordered alphabetically (%s)';
/**
* Name of the "interface count" metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME_COUNT = 'Number of interfaces being implemented/extended';
/**
* The sort order to use for the statement.
*
* If all names used are unqualified, the sort order won't make a difference.
* However, if one or more of the names are partially or fully qualified, the chosen
* sort order will determine how the sorting between unqualified, partially and
* fully qualified names is handled.
*
* The sniff supports two sort order options:
* - 'name' : sort by the interface name only (default);
* - 'full' : sort by the full name as used in the statement (without leading backslash).
*
* In both cases, the sorting will be done using natural sort, case-insensitive.
*
* Example:
* <code>
* class Foo implements \Vendor\DiffIterator, My\Count, DateTimeInterface {}
* </code>
*
* If sorted using the "name" sort-order, the sniff looks just at the interface name, i.e.
* `DiffIterator`, `Count` and `DateTimeInterface`, which for this example would mean
* the correct order would be `My\Count, DateTimeInterface, \Vendor\DiffIterator`.
*
* If sorted using the "full" sort-order, the sniff will look at the full name as used
* in the `implements` statement, without leading backslashes.
* For the example above, this would mean that the correct order would be:
* `DateTimeInterface, My\Count, \Vendor\DiffIterator`.
*
* @since 1.0.0
*
* @var string
*/
public $orderby = 'name';
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return (Collections::ooCanExtend() + Collections::ooCanImplement());
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token in
* the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
/*
* Validate the setting.
*/
if ($this->orderby !== 'full') {
// Use the default.
$this->orderby = 'name';
}
$metricNameAlpha = \sprintf(self::METRIC_NAME_ALPHA, $this->orderby);
$tokens = $phpcsFile->getTokens();
if (isset($tokens[$stackPtr]['scope_opener']) === false) {
// Parse error or live coding. Ignore.
return;
}
$scopeOpener = $tokens[$stackPtr]['scope_opener'];
/*
* Get the names.
*/
if (isset(Collections::ooCanImplement()[$tokens[$stackPtr]['code']]) === true) {
$names = ObjectDeclarations::findImplementedInterfaceNames($phpcsFile, $stackPtr);
} else {
$names = ObjectDeclarations::findExtendedInterfaceNames($phpcsFile, $stackPtr);
}
if (\is_array($names) === false) {
// Class/interface/enum doesn't extend or implement.
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME_COUNT, 0);
$phpcsFile->recordMetric($stackPtr, $metricNameAlpha, 'n/a');
return;
}
$count = \count($names);
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME_COUNT, $count);
if ($count < 2) {
// Nothing to sort.
$phpcsFile->recordMetric($stackPtr, $metricNameAlpha, 'n/a');
return;
}
/*
* Check the order.
*/
if ($this->orderby === 'name') {
$sorted = $this->sortByName($names);
} else {
$sorted = $this->sortByFull($names);
}
if ($sorted === $names) {
// Order is already correct.
$phpcsFile->recordMetric($stackPtr, $metricNameAlpha, 'yes');
return;
}
$phpcsFile->recordMetric($stackPtr, $metricNameAlpha, 'no');
/*
* Throw the error.
*/
$keyword = \T_IMPLEMENTS;
if (isset(Collections::ooCanImplement()[$tokens[$stackPtr]['code']]) === false) {
$keyword = \T_EXTENDS;
}
$fixable = true;
$keywordPtr = $phpcsFile->findNext($keyword, ($stackPtr + 1), $scopeOpener);
$hasComment = $phpcsFile->findNext(Tokens::$commentTokens, ($keywordPtr + 1), $scopeOpener);
if ($hasComment !== false) {
$fixable = false;
}
$error = "The interface names in a \"%s %s\" statement should be ordered alphabetically.\n";
$error .= 'Expected: %s; Found: %s';
$code = \ucfirst(\strtolower($tokens[$keywordPtr]['content'])) . 'WrongOrder';
$data = [
$tokens[$stackPtr]['content'],
$tokens[$keywordPtr]['content'],
\implode(', ', $names),
\implode(', ', $sorted),
];
if ($fixable === false) {
$code .= 'WithComments';
$phpcsFile->addError($error, $keywordPtr, $code, $data);
return;
}
// OK, so we appear to have a fixable error.
$fix = $phpcsFile->addFixableError($error, $keywordPtr, $code, $data);
if ($fix === false) {
return;
}
$phpcsFile->fixer->beginChangeset();
// Remove the complete previous extends/implements part.
for ($i = ($keywordPtr + 1); $i < $scopeOpener; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->addContent($keywordPtr, ' ' . \implode(', ', $sorted) . ' ');
$phpcsFile->fixer->endChangeset();
}
/**
* Sort an array of potentially mixed qualified and unqualified names by the interface name.
*
* @since 1.0.0
*
* @param string[] $names Interface names, potentially mixed qualified and unqualified.
*
* @return string[]
*/
protected function sortByName(array $names)
{
$getLastName = function ($name) {
$last = \strrchr($name, '\\');
if ($last === false) {
$last = $name;
} else {
$last = \substr($last, 1);
}
return $last;
};
return $this->sortNames($names, $getLastName);
}
/**
* Sort an array of potentially mixed qualified and unqualified names by the full name.
*
* @since 1.0.0
*
* @param string[] $names Interface names, potentially mixed qualified and unqualified.
*
* @return string[]
*/
protected function sortByFull(array $names)
{
$trimLeadingBackslash = function ($name) {
return \ltrim($name, '\\');
};
return $this->sortNames($names, $trimLeadingBackslash);
}
/**
* Sort an array of names.
*
* @since 1.0.0
*
* @param string[] $names Interface names, potentially mixed qualified and unqualified.
* @param callable $prepareNames Function to call to prepare the names before sorting.
*
* @return string[]
*/
private function sortNames(array $names, callable $prepareNames)
{
$preppedNames = \array_map($prepareNames, $names);
$names = \array_combine($names, $preppedNames);
\natcasesort($names);
return \array_keys($names);
}
}

View file

@ -0,0 +1,112 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Operators;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
/**
* Enforce the use of the boolean `&&` and `||` operators instead of the logical `and`/`or` operators.
*
* Note: as the {@link https://www.php.net/manual/en/language.operators.precedence.php operator precedence}
* of the logical operators is significantly lower than the operator precedence of boolean operators,
* this sniff does not contain an auto-fixer.
*
* @since 1.0.0
*/
final class DisallowLogicalAndOrSniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Type of and/or operator used';
/**
* The tokens this sniff records metrics for.
*
* @since 1.0.0
*
* @var array<int|string, string>
*/
private $metricType = [
\T_LOGICAL_AND => 'logical (and/or)',
\T_LOGICAL_OR => 'logical (and/or)',
\T_BOOLEAN_AND => 'boolean (&&/||)',
\T_BOOLEAN_OR => 'boolean (&&/||)',
];
/**
* The tokens this sniff targets with error code and replacements.
*
* @since 1.0.0
*
* @var array<int|string, array<string, string>>
*/
private $targetTokenInfo = [
\T_LOGICAL_AND => [
'error_code' => 'LogicalAnd',
'replacement' => '&&',
],
\T_LOGICAL_OR => [
'error_code' => 'LogicalOr',
'replacement' => '||',
],
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return \array_keys($this->metricType);
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$tokenCode = $tokens[$stackPtr]['code'];
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, $this->metricType[$tokenCode]);
if (isset($this->targetTokenInfo[$tokenCode]) === false) {
// Already using boolean operator.
return;
}
$error = 'Using logical operators is not allowed. Expected: "%s"; Found: "%s"';
$data = [
$this->targetTokenInfo[$tokenCode]['replacement'],
$tokens[ $stackPtr ]['content'],
];
$phpcsFile->addError($error, $stackPtr, $this->targetTokenInfo[$tokenCode]['error_code'], $data);
}
}

View file

@ -0,0 +1,76 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/
namespace PHPCSExtra\Universal\Sniffs\Operators;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHPCSUtils\Utils\Operators;
/**
* Disallow the use of short ternaries.
*
* While short ternaries are useful when used correctly, the principle of them is
* often misunderstood and they are more often than not used incorrectly, leading to
* hard to debug issues and/or PHP warnings/notices.
*
* @since 1.0.0
*/
final class DisallowShortTernarySniff implements Sniff
{
/**
* Name of the metric.
*
* @since 1.0.0
*
* @var string
*/
const METRIC_NAME = 'Ternary usage';
/**
* Registers the tokens that this sniff wants to listen for.
*
* @since 1.0.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_INLINE_THEN];
}
/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
if (Operators::isShortTernary($phpcsFile, $stackPtr) === false) {
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'long');
return;
}
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'short');
$phpcsFile->addError(
'Using short ternaries is not allowed as they are rarely used correctly',
$stackPtr,
'Found'
);
}
}

Some files were not shown because too many files have changed in this diff Show more