diff --git a/.php-cs-fixer-rules.php b/.php-cs-fixer-rules.php index 5794174..10e6ccb 100644 --- a/.php-cs-fixer-rules.php +++ b/.php-cs-fixer-rules.php @@ -44,6 +44,7 @@ ], '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_empty_comment' => true, @@ -69,7 +70,9 @@ '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, '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' => [ diff --git a/IxDFCodingStandard/PhpCsFixer/Config.php b/IxDFCodingStandard/PhpCsFixer/Config.php new file mode 100644 index 0000000..941d95c --- /dev/null +++ b/IxDFCodingStandard/PhpCsFixer/Config.php @@ -0,0 +1,53 @@ +|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); + + 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..f398324 --- /dev/null +++ b/IxDFCodingStandard/PhpCsFixer/Rules.php @@ -0,0 +1,21 @@ +|bool>|null */ + private static ?array $rules = null; + + // 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'; + } +} diff --git a/README.md b/README.md index 8f9c93a..3f188c2 100644 --- a/README.md +++ b/README.md @@ -3,107 +3,100 @@ [![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 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 -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'); +return Config::create(__DIR__, finder: $finder); +``` + +If you only need the rules array: ```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(); +$rules = \IxDFCodingStandard\PhpCsFixer\Rules::get(); ``` -## Development +## Usage + +```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 +``` -### 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.** +### Composer scripts (recommended) +Add to your `composer.json`: -## Reference +```json +"scripts": { + "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" +} +``` -Rules can be added, excluded or tweaked locally, depending on your preferences. -More information on how to do this can be found here: +Then run: -- [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 # 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 +``` 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" },