From c1257dade8a68da532560387304beaa97925b7cc Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Fri, 6 Mar 2026 13:14:36 +0100 Subject: [PATCH 01/12] feat: add PHP-CS-Fixer Config and Rules classes for shared configuration Provides a proper API for sharing php-cs-fixer config across projects, replacing the hacky vendor path require approach. --- IxDFCodingStandard/PhpCsFixer/Config.php | 58 +++++++++++++++ IxDFCodingStandard/PhpCsFixer/Rules.php | 20 +++++ README.md | 93 +++++++----------------- composer.json | 4 +- 4 files changed, 108 insertions(+), 67 deletions(-) create mode 100644 IxDFCodingStandard/PhpCsFixer/Config.php create mode 100644 IxDFCodingStandard/PhpCsFixer/Rules.php diff --git a/IxDFCodingStandard/PhpCsFixer/Config.php b/IxDFCodingStandard/PhpCsFixer/Config.php new file mode 100644 index 0000000..0f38370 --- /dev/null +++ b/IxDFCodingStandard/PhpCsFixer/Config.php @@ -0,0 +1,58 @@ + $ruleOverrides Rules to merge on top of the shared ruleset + */ + public static function create( + string $projectDir, + array $ruleOverrides = [], + ?Finder $finder = null, + ): BaseConfig { + $finder ??= self::defaultFinder($projectDir); + + return (new BaseConfig()) + ->setParallelConfig(ParallelConfigFactory::detect()) + ->setUsingCache(true) + ->setCacheFile($projectDir.'/.cache/.php-cs-fixer.cache') + ->setRiskyAllowed(true) + ->setIndent(' ') + ->setLineEnding("\n") + ->setRules(array_merge(Rules::get(), $ruleOverrides)) + ->setFinder($finder); + } + + private static function defaultFinder(string $projectDir): Finder + { + return Finder::create() + ->in($projectDir) + ->exclude([ + '.cache', + '.docker', + 'bootstrap/cache', + 'node_modules', + 'public', + 'storage', + 'vendor', + ]) + ->name('*.php') + ->notName('*.blade.php') + ->notName('_ide_helper.php') + ->notName('.phpstorm.meta.php') + ->ignoreDotFiles(false) + ->ignoreVCS(true) + ->ignoreVCSIgnored(true); + } +} diff --git a/IxDFCodingStandard/PhpCsFixer/Rules.php b/IxDFCodingStandard/PhpCsFixer/Rules.php new file mode 100644 index 0000000..07d4d82 --- /dev/null +++ b/IxDFCodingStandard/PhpCsFixer/Rules.php @@ -0,0 +1,20 @@ +|null */ + private static ?array $rules = null; + + /** @return array */ + public static function get(): array + { + return self::$rules ??= require dirname(__DIR__, 2).'/.php-cs-fixer-rules.php'; + } +} diff --git a/README.md b/README.md index 8f9c93a..d3d7d95 100644 --- a/README.md +++ b/README.md @@ -3,107 +3,70 @@ [![PHP Psalm Level](https://shepherd.dev/github/InteractionDesignFoundation/coding-standard/level.svg)](https://shepherd.dev/github/InteractionDesignFoundation/coding-standard) [![PHP Psalm Type Coverage](https://shepherd.dev/github/InteractionDesignFoundation/coding-standard/coverage.svg)](https://shepherd.dev/github/InteractionDesignFoundation/coding-standard) -# IxDF Coding Standard for Laravel +# IxDF Coding Standard -An opinionated ruleset focused on strict types. -Suitable for both applications and packages. +An opinionated coding standard for PHP/Laravel projects. Provides **PHP_CodeSniffer** rules and a shared **PHP-CS-Fixer** configuration. ## Installation -1. Install the package via composer by running: ```shell composer require --dev interaction-design-foundation/coding-standard ``` -2. Add composer scripts into your `composer.json`: -```json -"scripts": { - "cs:check": "phpcs -p -s --colors --report-full --report-summary", - "cs:fix": "phpcbf -p --colors" -} -``` +## PHP_CodeSniffer -3. Create file `phpcs.xml` on the base path of your repository with content +Create `phpcs.xml` in your project root: ```xml - - - app config database - lang routes tests ``` -## Usage +## PHP-CS-Fixer -- To run checks only: +Create `.php-cs-fixer.php` in your project root: -```shell -composer cs:check -``` +```php +send(); -// phpcs:enable +return Config::create(__DIR__, ruleOverrides: [ + 'final_public_method_for_abstract_class' => false, +]); ``` -Disable a specific rule: +With a custom Finder: ```php -// phpcs:disable Generic.Commenting.Todo.Found -$xmlPackage = new XMLPackage; -$xmlPackage['error_code'] = get_default_error_code_value(); -// TODO: Add an error message here. -$xmlPackage->send(); -// phpcs:enable -``` +use IxDFCodingStandard\PhpCsFixer\Config; +use PhpCsFixer\Finder; -Ignore a specific violation: +$finder = Finder::create()->in(__DIR__)->name('*.php'); -```php -$xmlPackage = new XMLPackage; -$xmlPackage['error_code'] = get_default_error_code_value(); -// phpcs:ignore Generic.Commenting.Todo.Found -// TODO: Add an error message here. -$xmlPackage->send(); +return Config::create(__DIR__, finder: $finder); ``` -## Development - -### Versioning -> **New rules or Sniffs may not be introduced in minor or bugfix releases and should always be based on the develop -branch and queued for the next major release, unless considered a bugfix for existing rules.** - - -## Reference +If you only need the rules array: +```php +$rules = \IxDFCodingStandard\PhpCsFixer\Rules::get(); +``` -Rules can be added, excluded or tweaked locally, depending on your preferences. -More information on how to do this can be found here: +## Usage -- [Coding Standard Tutorial](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Coding-Standard-Tutorial) -- [Configuration Options](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Configuration-Options) -- [Selectively Applying Rules](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-Ruleset#selectively-applying-rules) -- [Customisable Sniff Properties](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Customisable-Sniff-Properties) -- Other coding standards (inspiring us): - - [Slevomat coding standard](https://github.com/slevomat/coding-standard) - - [Doctrine coding standard](https://github.com/doctrine/coding-standard) - - [Laminas coding standard](https://github.com/laminas/laminas-coding-standard) +```shell +composer cs:check # check only +composer cs:fix # auto-fix +``` diff --git a/composer.json b/composer.json index 8cfb74f..41c5f00 100644 --- a/composer.json +++ b/composer.json @@ -1,16 +1,16 @@ { "name": "interaction-design-foundation/coding-standard", - "description": "IxDF Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "description": "IxDF coding standard: PHP_CodeSniffer rules and shared PHP-CS-Fixer configuration.", "license": "MIT", "type": "phpcodesniffer-standard", "require": { "php": "^8.3", "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "friendsofphp/php-cs-fixer": "^3.89", "slevomat/coding-standard": "^8.25", "squizlabs/php_codesniffer": "^4.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.89", "phpunit/phpunit": "^12.4", "vimeo/psalm": "^6.13" }, From b5df10899e7096a408e648e3cfe8dd5dc63e3f91 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 6 Mar 2026 12:14:58 +0000 Subject: [PATCH 02/12] =?UTF-8?q?#15000=20=F0=9F=AA=84=EF=B8=8F=20Apply=20?= =?UTF-8?q?coding=20style=20fixes=20to=20PHP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IxDFCodingStandard/PhpCsFixer/Config.php | 12 +++--------- IxDFCodingStandard/PhpCsFixer/Rules.php | 1 - 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/IxDFCodingStandard/PhpCsFixer/Config.php b/IxDFCodingStandard/PhpCsFixer/Config.php index 0f38370..db339c2 100644 --- a/IxDFCodingStandard/PhpCsFixer/Config.php +++ b/IxDFCodingStandard/PhpCsFixer/Config.php @@ -8,19 +8,13 @@ /** * Pre-configured PHP-CS-Fixer config factory for IxDF projects. - * * @see README.md for usage examples */ final class Config { - /** - * @param array $ruleOverrides Rules to merge on top of the shared ruleset - */ - public static function create( - string $projectDir, - array $ruleOverrides = [], - ?Finder $finder = null, - ): BaseConfig { + /** @param array $ruleOverrides Rules to merge on top of the shared ruleset */ + public static function create(string $projectDir, array $ruleOverrides = [], ?Finder $finder = null): BaseConfig + { $finder ??= self::defaultFinder($projectDir); return (new BaseConfig()) diff --git a/IxDFCodingStandard/PhpCsFixer/Rules.php b/IxDFCodingStandard/PhpCsFixer/Rules.php index 07d4d82..58f0880 100644 --- a/IxDFCodingStandard/PhpCsFixer/Rules.php +++ b/IxDFCodingStandard/PhpCsFixer/Rules.php @@ -4,7 +4,6 @@ /** * Shared PHP-CS-Fixer rules for IxDF projects. - * * @see https://mlocati.github.io/php-cs-fixer-configurator/ */ final class Rules From b57ba269943bee4388f66990f6f0ea354b0fb80f Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Fri, 6 Mar 2026 13:40:34 +0100 Subject: [PATCH 03/12] feat: add new rules from phpyh/coding-standard - blank_line_before_statement: blank lines before return, throw, try, etc. - multiline_whitespace_before_semicolons: no_multi_line strategy - no_superfluous_phpdoc_tags: remove redundant @inheritDoc - nullable_type_declaration_for_default_null_value: explicit nullability - numeric_literal_separator: 1_000_000 instead of 1000000 - ordered_imports: class, function, const order --- .php-cs-fixer-rules.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.php-cs-fixer-rules.php b/.php-cs-fixer-rules.php index 5794174..237d5cd 100644 --- a/.php-cs-fixer-rules.php +++ b/.php-cs-fixer-rules.php @@ -21,6 +21,7 @@ 'array_indentation' => true, 'assign_null_coalescing_to_coalesce_equal' => true, 'binary_operator_spaces' => ['default' => 'single_space'], + 'blank_line_before_statement' => ['statements' => ['continue', 'declare', 'return', 'throw', 'try']], 'cast_spaces' => ['space' => 'single'], 'class_attributes_separation' => ['elements' => ['method' => 'one']], 'declare_strict_types' => true, @@ -44,8 +45,10 @@ ], 'modernize_types_casting' => true, 'mb_str_functions' => true, + 'multiline_whitespace_before_semicolons' => ['strategy' => 'no_multi_line'], 'no_alias_functions' => true, 'no_binary_string' => true, + 'no_superfluous_phpdoc_tags' => ['remove_inheritdoc' => true], 'no_empty_comment' => true, 'no_empty_phpdoc' => true, 'no_empty_statement' => true, @@ -69,7 +72,10 @@ 'no_whitespace_before_comma_in_array' => true, 'normalize_index_brace' => true, 'nullable_type_declaration' => ['syntax' => 'question_mark'], + 'nullable_type_declaration_for_default_null_value' => true, + 'numeric_literal_separator' => ['strategy' => 'use_separator', 'override_existing' => true], 'object_operator_without_whitespace' => true, + 'ordered_imports' => ['imports_order' => ['class', 'function', 'const']], /* * @see https://github.com/slevomat/coding-standard/issues/1620#issuecomment-1758006718 * 'ordered_class_elements' => [ From 2f2db2ae93221f2f9b1313fe59a67ca79f83eec5 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Fri, 6 Mar 2026 13:43:08 +0100 Subject: [PATCH 04/12] docs: clarify that PHPCS and PHP-CS-Fixer can be used independently --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d3d7d95..241fe8f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,10 @@ # IxDF Coding Standard -An opinionated coding standard for PHP/Laravel projects. Provides **PHP_CodeSniffer** rules and a shared **PHP-CS-Fixer** configuration. +An opinionated coding standard for PHP/Laravel projects. Provides two independent tools — use either one or both together: + +- **PHP_CodeSniffer** — custom sniffs for strict types and Laravel conventions +- **PHP-CS-Fixer** — shared config with 80+ rules based on PER-CS 3.0 ## Installation From e5d43abc89b55ab8fb8a017dd7652d4b380627bb Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Fri, 6 Mar 2026 13:43:41 +0100 Subject: [PATCH 05/12] docs: add composer scripts examples to Usage section --- README.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 241fe8f..cb26a2c 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,22 @@ $rules = \IxDFCodingStandard\PhpCsFixer\Rules::get(); ## Usage +Add composer scripts to your `composer.json`: + +```json +"scripts": { + "cs:check": "phpcs -p -s --colors --report-full --report-summary", + "cs:fix": "phpcbf -p --colors", + "cs-fixer:check": "php-cs-fixer fix --dry-run --diff", + "cs-fixer:fix": "php-cs-fixer fix" +} +``` + +Then run: + ```shell -composer cs:check # check only -composer cs:fix # auto-fix +composer cs:check # PHP_CodeSniffer: check only +composer cs:fix # PHP_CodeSniffer: auto-fix +composer cs-fixer:check # PHP-CS-Fixer: check only +composer cs-fixer:fix # PHP-CS-Fixer: auto-fix ``` From 9732cd6c94b63e45dfb6525a2dba2fd9a386cd0a Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Fri, 6 Mar 2026 13:45:14 +0100 Subject: [PATCH 06/12] docs: update Usage with combined cs:check/cs:fix scripts --- README.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index cb26a2c..5fc081a 100644 --- a/README.md +++ b/README.md @@ -73,18 +73,21 @@ Add composer scripts to your `composer.json`: ```json "scripts": { - "cs:check": "phpcs -p -s --colors --report-full --report-summary", - "cs:fix": "phpcbf -p --colors", - "cs-fixer:check": "php-cs-fixer fix --dry-run --diff", - "cs-fixer:fix": "php-cs-fixer fix" + "cs": "@cs:fix", + "cs:check": ["@php-cs-fixer:dry", "@phpcs"], + "cs:fix": ["@php-cs-fixer", "@phpcbf"], + "phpcs": "phpcs -p -s --colors --report-full --report-summary", + "phpcbf": "phpcbf -p --colors", + "php-cs-fixer": "php-cs-fixer fix --no-interaction --ansi --quiet", + "php-cs-fixer:dry": "php-cs-fixer fix --no-interaction --ansi --verbose --dry-run" } ``` Then run: ```shell -composer cs:check # PHP_CodeSniffer: check only -composer cs:fix # PHP_CodeSniffer: auto-fix -composer cs-fixer:check # PHP-CS-Fixer: check only -composer cs-fixer:fix # PHP-CS-Fixer: auto-fix +composer cs:check # run both tools in check mode +composer cs:fix # run both tools in fix mode +composer phpcs # PHP_CodeSniffer only +composer php-cs-fixer # PHP-CS-Fixer only ``` From 9e1ced9514891e442577ed45a70f70f92b807ff8 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Fri, 6 Mar 2026 13:46:11 +0100 Subject: [PATCH 07/12] docs: add direct vendor/bin usage before composer scripts --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5fc081a..3f188c2 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,16 @@ $rules = \IxDFCodingStandard\PhpCsFixer\Rules::get(); ## Usage -Add composer scripts to your `composer.json`: +```shell +vendor/bin/phpcs # check with PHP_CodeSniffer +vendor/bin/phpcbf # fix with PHP_CodeSniffer +vendor/bin/php-cs-fixer fix --dry-run --diff # check with PHP-CS-Fixer +vendor/bin/php-cs-fixer fix # fix with PHP-CS-Fixer +``` + +### Composer scripts (recommended) + +Add to your `composer.json`: ```json "scripts": { From fa637712311dd707bbde77741c4533edb9fa85bd Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Fri, 6 Mar 2026 13:54:06 +0100 Subject: [PATCH 08/12] revert: remove no_superfluous_phpdoc_tags rule It removes @inheritDoc which is needed by PHPStan. --- .php-cs-fixer-rules.php | 1 - 1 file changed, 1 deletion(-) diff --git a/.php-cs-fixer-rules.php b/.php-cs-fixer-rules.php index 237d5cd..dc11ed5 100644 --- a/.php-cs-fixer-rules.php +++ b/.php-cs-fixer-rules.php @@ -48,7 +48,6 @@ 'multiline_whitespace_before_semicolons' => ['strategy' => 'no_multi_line'], 'no_alias_functions' => true, 'no_binary_string' => true, - 'no_superfluous_phpdoc_tags' => ['remove_inheritdoc' => true], 'no_empty_comment' => true, 'no_empty_phpdoc' => true, 'no_empty_statement' => true, From d757a331ae8cb851792799eb98539e402e133181 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Fri, 6 Mar 2026 13:56:16 +0100 Subject: [PATCH 09/12] revert: remove numeric_literal_separator rule --- .php-cs-fixer-rules.php | 1 - 1 file changed, 1 deletion(-) diff --git a/.php-cs-fixer-rules.php b/.php-cs-fixer-rules.php index dc11ed5..e10bfeb 100644 --- a/.php-cs-fixer-rules.php +++ b/.php-cs-fixer-rules.php @@ -72,7 +72,6 @@ 'normalize_index_brace' => true, 'nullable_type_declaration' => ['syntax' => 'question_mark'], 'nullable_type_declaration_for_default_null_value' => true, - 'numeric_literal_separator' => ['strategy' => 'use_separator', 'override_existing' => true], 'object_operator_without_whitespace' => true, 'ordered_imports' => ['imports_order' => ['class', 'function', 'const']], /* From 76019f2ed1d4ac02bea9fcd2ede216e45431f272 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Fri, 6 Mar 2026 13:58:39 +0100 Subject: [PATCH 10/12] fix: remove return/throw from blank_line_before_statement These add noise in short methods where the blank line is unnecessary. --- .php-cs-fixer-rules.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer-rules.php b/.php-cs-fixer-rules.php index e10bfeb..20878f1 100644 --- a/.php-cs-fixer-rules.php +++ b/.php-cs-fixer-rules.php @@ -21,7 +21,7 @@ 'array_indentation' => true, 'assign_null_coalescing_to_coalesce_equal' => true, 'binary_operator_spaces' => ['default' => 'single_space'], - 'blank_line_before_statement' => ['statements' => ['continue', 'declare', 'return', 'throw', 'try']], + 'blank_line_before_statement' => ['statements' => ['continue', 'declare', 'try']], 'cast_spaces' => ['space' => 'single'], 'class_attributes_separation' => ['elements' => ['method' => 'one']], 'declare_strict_types' => true, From bac37cded06b2fab188863dfbbd0431c1b5b1998 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Fri, 6 Mar 2026 13:59:08 +0100 Subject: [PATCH 11/12] revert: remove blank_line_before_statement rule --- .php-cs-fixer-rules.php | 1 - 1 file changed, 1 deletion(-) diff --git a/.php-cs-fixer-rules.php b/.php-cs-fixer-rules.php index 20878f1..10e6ccb 100644 --- a/.php-cs-fixer-rules.php +++ b/.php-cs-fixer-rules.php @@ -21,7 +21,6 @@ 'array_indentation' => true, 'assign_null_coalescing_to_coalesce_equal' => true, 'binary_operator_spaces' => ['default' => 'single_space'], - 'blank_line_before_statement' => ['statements' => ['continue', 'declare', 'try']], 'cast_spaces' => ['space' => 'single'], 'class_attributes_separation' => ['elements' => ['method' => 'one']], 'declare_strict_types' => true, From aa40b19fe40fd2667b0fb33482d48bf0c904dfc2 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Fri, 6 Mar 2026 14:03:03 +0100 Subject: [PATCH 12/12] fix: suppress mixed type hint warnings in PhpCsFixer classes --- IxDFCodingStandard/PhpCsFixer/Config.php | 3 ++- IxDFCodingStandard/PhpCsFixer/Rules.php | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/IxDFCodingStandard/PhpCsFixer/Config.php b/IxDFCodingStandard/PhpCsFixer/Config.php index db339c2..941d95c 100644 --- a/IxDFCodingStandard/PhpCsFixer/Config.php +++ b/IxDFCodingStandard/PhpCsFixer/Config.php @@ -12,7 +12,8 @@ */ final class Config { - /** @param array $ruleOverrides Rules to merge on top of the shared ruleset */ + // phpcs:ignore SlevomatCodingStandard.TypeHints.DisallowMixedTypeHint.DisallowedMixedTypeHint + /** @param array|bool> $ruleOverrides Rules to merge on top of the shared ruleset */ public static function create(string $projectDir, array $ruleOverrides = [], ?Finder $finder = null): BaseConfig { $finder ??= self::defaultFinder($projectDir); diff --git a/IxDFCodingStandard/PhpCsFixer/Rules.php b/IxDFCodingStandard/PhpCsFixer/Rules.php index 58f0880..f398324 100644 --- a/IxDFCodingStandard/PhpCsFixer/Rules.php +++ b/IxDFCodingStandard/PhpCsFixer/Rules.php @@ -8,10 +8,12 @@ */ final class Rules { - /** @var array|null */ + // phpcs:ignore SlevomatCodingStandard.TypeHints.DisallowMixedTypeHint.DisallowedMixedTypeHint + /** @var array|bool>|null */ private static ?array $rules = null; - /** @return array */ + // phpcs:ignore SlevomatCodingStandard.TypeHints.DisallowMixedTypeHint.DisallowedMixedTypeHint + /** @return array|bool> */ public static function get(): array { return self::$rules ??= require dirname(__DIR__, 2).'/.php-cs-fixer-rules.php';