diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index ca39002..5a38af2 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -3,15 +3,33 @@ name: checks on: - push +env: + COMPOSER_NO_INTERACTION: "1" + jobs: + format: + name: Code style + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + coverage: none + + - run: composer install --ansi --no-progress --prefer-dist + + - name: Run PHP_Codesniffer + run: vendor/bin/phpcs + static_analysis: name: Static analysis runs-on: ubuntu-latest - env: - COMPOSER_NO_INTERACTION: "1" - steps: - uses: actions/checkout@v4 @@ -44,9 +62,6 @@ jobs: runs-on: ${{ matrix.os }} - env: - COMPOSER_NO_INTERACTION: "1" - steps: - uses: actions/checkout@v4 diff --git a/composer.json b/composer.json index 3d7f02a..b58ee0c 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,9 @@ } }, "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": false + }, "sort-packages": true }, "description": "Library for tokenization, abstract syntax tree parsing & interpretation", @@ -37,7 +40,8 @@ "phpstan/phpstan": "^2.1.12", "phpstan/phpstan-strict-rules": "^2.0.4", "spaze/phpstan-disallowed-calls": "^4.5.0", - "tracy/tracy": "^2.10.9" + "tracy/tracy": "^2.10.9", + "vojtech-dobes/php-codestyle": "~0.2.0" }, "scripts": { "phpstan": "phpstan analyse", diff --git a/composer.lock b/composer.lock index 587c714..e857d4b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,9 +4,87 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e5b5acae37b59710218e5b85a6e9bd7b", + "content-hash": "c65143e080501c4c57e88f70ff673e90", "packages": [], "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": "nette/tester", "version": "v2.5.4", @@ -82,6 +160,53 @@ }, "time": "2024-10-23T23:57:10+00:00" }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0" + }, + "time": "2025-02-19T13:28:12+00:00" + }, { "name": "phpstan/phpstan", "version": "2.1.12", @@ -188,6 +313,71 @@ }, "time": "2025-03-18T11:42:40+00:00" }, + { + "name": "slevomat/coding-standard", + "version": "8.19.0", + "source": { + "type": "git", + "url": "https://github.com/slevomat/coding-standard.git", + "reference": "9cc50509c3912d465a9f6898069f64cf2abf4e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/9cc50509c3912d465a9f6898069f64cf2abf4e8a", + "reference": "9cc50509c3912d465a9f6898069f64cf2abf4e8a", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", + "php": "^7.4 || ^8.0", + "phpstan/phpdoc-parser": "^2.1.0", + "squizlabs/php_codesniffer": "^3.13.0" + }, + "require-dev": { + "phing/phing": "3.0.1", + "php-parallel-lint/php-parallel-lint": "1.4.0", + "phpstan/phpstan": "2.1.17", + "phpstan/phpstan-deprecation-rules": "2.0.3", + "phpstan/phpstan-phpunit": "2.0.6", + "phpstan/phpstan-strict-rules": "2.0.4", + "phpunit/phpunit": "9.6.8|10.5.45|11.4.4|11.5.21|12.1.3" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "SlevomatCodingStandard\\": "SlevomatCodingStandard/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "keywords": [ + "dev", + "phpcs" + ], + "support": { + "issues": "https://github.com/slevomat/coding-standard/issues", + "source": "https://github.com/slevomat/coding-standard/tree/8.19.0" + }, + "funding": [ + { + "url": "https://github.com/kukulich", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard", + "type": "tidelift" + } + ], + "time": "2025-06-07T15:02:20+00:00" + }, { "name": "spaze/phpstan-disallowed-calls", "version": "v4.5.0", @@ -255,6 +445,90 @@ ], "time": "2025-04-10T19:01:43+00:00" }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.13.0", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "65ff2489553b83b4597e89c3b8b721487011d186" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/65ff2489553b83b4597e89c3b8b721487011d186", + "reference": "65ff2489553b83b4597e89c3b8b721487011d186", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "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": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-05-11T03:36:00+00:00" + }, { "name": "tracy/tracy", "version": "v2.10.9", @@ -329,6 +603,41 @@ "source": "https://github.com/nette/tracy/tree/v2.10.9" }, "time": "2024-11-07T14:48:00+00:00" + }, + { + "name": "vojtech-dobes/php-codestyle", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/vojtech-dobes/php-codestyle.git", + "reference": "d59431f2d4cc36987b9eefba40b6bd8fddd4a440" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vojtech-dobes/php-codestyle/zipball/d59431f2d4cc36987b9eefba40b6bd8fddd4a440", + "reference": "d59431f2d4cc36987b9eefba40b6bd8fddd4a440", + "shasum": "" + }, + "require": { + "slevomat/coding-standard": "~8.19.0" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "keywords": [ + "code-style", + "codesniffer", + "coding-standard", + "dev", + "phpcs" + ], + "support": { + "issues": "https://github.com/vojtech-dobes/php-codestyle/issues", + "source": "https://github.com/vojtech-dobes/php-codestyle/tree/0.2.0" + }, + "time": "2025-06-09T07:30:13+00:00" } ], "aliases": [], diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..98cb514 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,9 @@ + + + + + src + tests + + + diff --git a/src/GrammarProcessing/AbstractSyntaxTree.php b/src/GrammarProcessing/AbstractSyntaxTree.php index dc77ba0..d9a96e4 100644 --- a/src/GrammarProcessing/AbstractSyntaxTree.php +++ b/src/GrammarProcessing/AbstractSyntaxTree.php @@ -24,11 +24,11 @@ public function interpret(Interpretation $interpretation): mixed $nodeInterpretations = $interpretation->nodeInterpretations; $generatorStack = []; - $currentNodeValue = NULL; + $currentNodeValue = null; $currentGenerator = $nodeInterpretations[$this->name]->interpret($this->value); - while (TRUE) { + while (true) { if ($currentGenerator->valid()) { $subnode = $currentGenerator->current(); @@ -36,8 +36,8 @@ public function interpret(Interpretation $interpretation): mixed $subnode = $subnode->value; } - if ($subnode === NULL) { - $currentGenerator->send(NULL); + if ($subnode === null) { + $currentGenerator->send(null); } elseif ($subnode instanceof Token) { $currentGenerator->send($subnode->value); } elseif ($subnode instanceof TokenNode) { diff --git a/src/GrammarProcessing/Ebnf/Nodes/Grammar.php b/src/GrammarProcessing/Ebnf/Nodes/Grammar.php index 7cbd9b5..ad4b5c8 100644 --- a/src/GrammarProcessing/Ebnf/Nodes/Grammar.php +++ b/src/GrammarProcessing/Ebnf/Nodes/Grammar.php @@ -72,7 +72,7 @@ private function ensureOnlyNonoptionalSyntaxTokenSymbols(array $symbols): array return $symbols; } - $replace = function ( + $replace = static function ( GrammarProcessing\Vocabulary\Symbol $symbol, ) use ( $optionalSyntaxTokenSymbols, diff --git a/src/GrammarProcessing/Error.php b/src/GrammarProcessing/Error.php index e2163f1..4faddcf 100644 --- a/src/GrammarProcessing/Error.php +++ b/src/GrammarProcessing/Error.php @@ -18,7 +18,7 @@ public function setError(?CannotConsumeTokenException $error): void throw new LogicException("Most specific error can't be unset"); } - if ($this->error === NULL || $this->error->tokenPosition <= $error->tokenPosition) { + if ($this->error === null || $this->error->tokenPosition <= $error->tokenPosition) { $this->error = $error; } } diff --git a/src/GrammarProcessing/LexicalGrammar.php b/src/GrammarProcessing/LexicalGrammar.php index 1159ef2..f3c7e67 100644 --- a/src/GrammarProcessing/LexicalGrammar.php +++ b/src/GrammarProcessing/LexicalGrammar.php @@ -2,8 +2,6 @@ namespace Vojtechdobes\GrammarProcessing; -use LogicException; - /** * @template-covariant TSymbol of string @@ -74,7 +72,7 @@ public function parseSource(string $source): TokenStream $matches = array_values( array_filter( $matches, - static fn ($match) => $match[1][0] === NULL, + static fn ($match) => $match[1][0] === null, ), ); } else { @@ -102,10 +100,10 @@ public function parseSource(string $source): TokenStream return new TokenStream( array_map( function (array $match) use ($iMax, $iMin): Token { - $type = NULL; + $type = null; for ($i = $iMin; $i < $iMax; $i++) { - if ($match[$i][0] !== NULL) { + if ($match[$i][0] !== null) { $type = $this->syntaxTokenSymbols[$i - $iMin]; } } diff --git a/src/GrammarProcessing/ListNode.php b/src/GrammarProcessing/ListNode.php index e615b40..b931bc7 100644 --- a/src/GrammarProcessing/ListNode.php +++ b/src/GrammarProcessing/ListNode.php @@ -5,18 +5,19 @@ use LogicException; +// phpcs:ignore SlevomatCodingStandard.Classes.DisallowMultiPropertyDefinition.DisallowedMultiPropertyDefinition -- until hooks are properly supported final class ListNode implements Node { - public string $name { + public string $name { // phpcs:disable SlevomatCodingStandard.Classes.DisallowMultiPropertyDefinition.DisallowedMultiPropertyDefinition get { throw new LogicException( - self::class . " must be parsed manually", + self::class . ' must be parsed manually', ); } - } + } // phpcs:enable SlevomatCodingStandard.Classes.DisallowMultiPropertyDefinition.DisallowedMultiPropertyDefinition diff --git a/src/GrammarProcessing/SelectedNode.php b/src/GrammarProcessing/SelectedNode.php index cfcff88..df8429b 100644 --- a/src/GrammarProcessing/SelectedNode.php +++ b/src/GrammarProcessing/SelectedNode.php @@ -5,18 +5,19 @@ use LogicException; +// phpcs:ignore SlevomatCodingStandard.Classes.DisallowMultiPropertyDefinition.DisallowedMultiPropertyDefinition -- until hooks are properly supported final class SelectedNode implements Node { - public string $name { + public string $name { // phpcs:disable SlevomatCodingStandard.Classes.DisallowMultiPropertyDefinition.DisallowedMultiPropertyDefinition get { throw new LogicException( - self::class . " must be parsed manually", + self::class . ' must be parsed manually', ); } - } + } // phpcs:enable SlevomatCodingStandard.Classes.DisallowMultiPropertyDefinition.DisallowedMultiPropertyDefinition public mixed $value { diff --git a/src/GrammarProcessing/TokenStream.php b/src/GrammarProcessing/TokenStream.php index 2203bc9..5ab1ad2 100644 --- a/src/GrammarProcessing/TokenStream.php +++ b/src/GrammarProcessing/TokenStream.php @@ -77,11 +77,11 @@ public function consumeEndOfStream(): void */ public function consumeTokenWithType(string $type): Token { - if (isset($this->tokens[$this->currentToken]) === FALSE) { + if (isset($this->tokens[$this->currentToken]) === false) { throw new CannotConsumeTokenException( "Expected token with type '{$type}', got end of stream", $this->currentToken, - NULL, + null, ); } @@ -107,11 +107,11 @@ public function consumeTokenWithType(string $type): Token */ public function consumeTokenWithValue(string $value): Token { - if (isset($this->tokens[$this->currentToken]) === FALSE) { + if (isset($this->tokens[$this->currentToken]) === false) { throw new CannotConsumeTokenException( "Expected token '{$value}', got end of stream", $this->currentToken, - NULL, + null, ); } diff --git a/src/GrammarProcessing/UnexpectedTokenException.php b/src/GrammarProcessing/UnexpectedTokenException.php index 2665067..30b8e01 100644 --- a/src/GrammarProcessing/UnexpectedTokenException.php +++ b/src/GrammarProcessing/UnexpectedTokenException.php @@ -13,9 +13,9 @@ public function __construct( public readonly ?Location $location, ) { - if ($this->location !== NULL) { + if ($this->location !== null) { $message = sprintf( - "%s (line %s, col %s)", + '%s (line %s, col %s)', $message, $this->location->line, $this->location->column, diff --git a/src/GrammarProcessing/Vocabulary/Repeat.php b/src/GrammarProcessing/Vocabulary/Repeat.php index db9d32c..26fb4d2 100644 --- a/src/GrammarProcessing/Vocabulary/Repeat.php +++ b/src/GrammarProcessing/Vocabulary/Repeat.php @@ -46,8 +46,8 @@ public function acceptNode( $tokenStream->advanceTo($tokenStreamCopy); } - while (TRUE) { - if ($this->max !== NULL && count($result) >= $this->max) { + while (true) { + if ($this->max !== null && count($result) >= $this->max) { break; } diff --git a/src/GrammarProcessing/Vocabulary/Subtract.php b/src/GrammarProcessing/Vocabulary/Subtract.php index ddd2b93..efc0414 100644 --- a/src/GrammarProcessing/Vocabulary/Subtract.php +++ b/src/GrammarProcessing/Vocabulary/Subtract.php @@ -43,11 +43,11 @@ public function acceptNode( $nonterminals, ); } catch (GrammarProcessing\CannotConsumeTokenException) { - $pass = TRUE; + $pass = true; } - if (isset($pass) === FALSE) { - throw new GrammarProcessing\CannotConsumeTokenException("", $tokenStream->getCurrentPosition(), NULL); + if (isset($pass) === false) { + throw new GrammarProcessing\CannotConsumeTokenException('', $tokenStream->getCurrentPosition(), null); } $tokenStream->advanceTo($tokenStreamCopy);