From 5e9a14fe47f1dd80c81293314a357aae7b792311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 15:16:14 -0300 Subject: [PATCH 01/17] feat: bootstrap standalone PHP style and syntax checks (#1) --- .changelog/initial-standards.md | 8 + .github/actions/check/action.yml | 32 + .github/workflows/code-standards-check.yml | 64 + .github/workflows/code-standards.yml | 26 + .github/workflows/repository-changelog.yml | 26 + .github/workflows/repository-ci.yml | 32 + .gitignore | 3 + AGENTS.md | 62 + CHANGELOG.md | 3 + LICENSE | 21 + README.md | 81 + bin/code-standards | 12 + composer.json | 27 + composer.lock | 1855 +++++++++++++++++ config/ecs.php | 12 + docs/automation.md | 58 + docs/instruction-map.md | 14 + docs/policy.md | 43 + phpunit.xml | 4 + .../fast-forward-code-standards/SKILL.md | 32 + resources/workflows/code-standards.yml | 12 + src/Config/ECSConfig.php | 40 + src/Console/Application.php | 76 + src/Filesystem/PhpFiles.php | 55 + src/Git/FileSelection.php | 67 + src/Process/NativeProcessRunner.php | 34 + src/Process/ProcessRunner.php | 15 + src/Standards/StandardsRunner.php | 47 + tests/Automation/WorkflowTest.php | 25 + tests/Integration/StandardsTest.php | 230 ++ tests/Standards/StandardsRunnerTest.php | 65 + 31 files changed, 3081 insertions(+) create mode 100644 .changelog/initial-standards.md create mode 100644 .github/actions/check/action.yml create mode 100644 .github/workflows/code-standards-check.yml create mode 100644 .github/workflows/code-standards.yml create mode 100644 .github/workflows/repository-changelog.yml create mode 100644 .github/workflows/repository-ci.yml create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 bin/code-standards create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 config/ecs.php create mode 100644 docs/automation.md create mode 100644 docs/instruction-map.md create mode 100644 docs/policy.md create mode 100644 phpunit.xml create mode 100644 resources/skills/fast-forward-code-standards/SKILL.md create mode 100644 resources/workflows/code-standards.yml create mode 100644 src/Config/ECSConfig.php create mode 100644 src/Console/Application.php create mode 100644 src/Filesystem/PhpFiles.php create mode 100644 src/Git/FileSelection.php create mode 100644 src/Process/NativeProcessRunner.php create mode 100644 src/Process/ProcessRunner.php create mode 100644 src/Standards/StandardsRunner.php create mode 100644 tests/Automation/WorkflowTest.php create mode 100644 tests/Integration/StandardsTest.php create mode 100644 tests/Standards/StandardsRunnerTest.php diff --git a/.changelog/initial-standards.md b/.changelog/initial-standards.md new file mode 100644 index 0000000..2f33c39 --- /dev/null +++ b/.changelog/initial-standards.md @@ -0,0 +1,8 @@ +--- +category: added +type: minor +--- + +Introduces standalone non-risky ECS style configuration, explicit check/fix CLI, +PHP syntax validation and exact-head changed-file checking with a portable +consumer skill and reusable workflow examples. diff --git a/.github/actions/check/action.yml b/.github/actions/check/action.yml new file mode 100644 index 0000000..90a6fe9 --- /dev/null +++ b/.github/actions/check/action.yml @@ -0,0 +1,32 @@ +name: Check PHP standards +description: Invoke the installed tool without writes to consumer sources. +inputs: + directory: + required: true + description: Absolute consumer checkout directory. + base-sha: + required: true + description: Exact PR base commit. + head-sha: + required: true + description: Exact PR head commit. + config: + required: false + default: '' + description: Optional consumer-relative ECS config; executable PHP in an unprivileged job only. +runs: + using: composite + steps: + - shell: bash + env: + STANDARDS_ROOT: ${{ github.action_path }}/../../.. + STANDARDS_DIRECTORY: ${{ inputs.directory }} + STANDARDS_BASE: ${{ inputs.base-sha }} + STANDARDS_HEAD: ${{ inputs.head-sha }} + STANDARDS_CONFIG: ${{ inputs.config }} + run: | + arguments=(check "--directory=$STANDARDS_DIRECTORY" "--base=$STANDARDS_BASE" "--head=$STANDARDS_HEAD") + if [[ -n "$STANDARDS_CONFIG" ]]; then + arguments+=("--config=$STANDARDS_CONFIG") + fi + php "$STANDARDS_ROOT/bin/code-standards" "${arguments[@]}" diff --git a/.github/workflows/code-standards-check.yml b/.github/workflows/code-standards-check.yml new file mode 100644 index 0000000..b7ea527 --- /dev/null +++ b/.github/workflows/code-standards-check.yml @@ -0,0 +1,64 @@ +name: 'Internal: Check Changed PHP' +on: + workflow_call: + inputs: + runtime-ref: + required: true + type: string + php-version: + default: '8.3' + type: string + config: + default: '' + type: string +permissions: + contents: read +jobs: + check: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Reject privileged events and invalid refs + env: + STANDARDS_EVENT: ${{ github.event_name }} + STANDARDS_RUNTIME: ${{ inputs.runtime-ref }} + STANDARDS_BASE: ${{ github.event.pull_request.base.sha }} + STANDARDS_HEAD: ${{ github.event.pull_request.head.sha }} + run: | + [[ "$STANDARDS_EVENT" == pull_request ]] || exit 1 + [[ "$STANDARDS_RUNTIME" =~ ^[a-fA-F0-9]{40}$ ]] || exit 1 + [[ "$STANDARDS_BASE" =~ ^[a-fA-F0-9]{40}$ ]] || exit 1 + [[ "$STANDARDS_HEAD" =~ ^[a-fA-F0-9]{40}$ ]] || exit 1 + - name: Check out reviewed tool runtime + uses: actions/checkout@v7.0.1 + with: + repository: php-fast-forward/code-standards + ref: ${{ inputs.runtime-ref }} + path: tool + persist-credentials: false + - name: Set up PHP + uses: shivammathur/setup-php@2.37.1 + with: + php-version: ${{ inputs.php-version }} + coverage: none + tools: composer:v2 + - name: Install only the reviewed tool + working-directory: tool + env: + COMPOSER_HOME: ${{ runner.temp }}/standards-composer-home + COMPOSER_CACHE_DIR: ${{ runner.temp }}/standards-composer-cache + run: composer install --no-dev --no-plugins --no-scripts --no-interaction --prefer-dist --no-progress + - name: Check out exact consumer head + uses: actions/checkout@v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha }} + path: consumer + fetch-depth: 0 + persist-credentials: false + - name: Check changed PHP + uses: ./tool/.github/actions/check + with: + directory: ${{ github.workspace }}/consumer + base-sha: ${{ github.event.pull_request.base.sha }} + head-sha: ${{ github.event.pull_request.head.sha }} + config: ${{ inputs.config }} diff --git a/.github/workflows/code-standards.yml b/.github/workflows/code-standards.yml new file mode 100644 index 0000000..9a6d7c6 --- /dev/null +++ b/.github/workflows/code-standards.yml @@ -0,0 +1,26 @@ +name: 'Reusable: Code Standards' +on: + workflow_call: + inputs: + runtime-ref: + description: Reviewed full commit SHA matching this workflow ref. + required: true + type: string + php-version: + description: Tool runtime PHP, at least 8.3; also used for syntax parsing. + default: '8.3' + type: string + config: + description: Optional consumer-relative ECS config. + default: '' + type: string +permissions: {} +jobs: + check: + permissions: + contents: read + uses: ./.github/workflows/code-standards-check.yml + with: + runtime-ref: ${{ inputs.runtime-ref }} + php-version: ${{ inputs.php-version }} + config: ${{ inputs.config }} diff --git a/.github/workflows/repository-changelog.yml b/.github/workflows/repository-changelog.yml new file mode 100644 index 0000000..70c3360 --- /dev/null +++ b/.github/workflows/repository-changelog.yml @@ -0,0 +1,26 @@ +name: 'Repository: Changelog' +on: + pull_request: + branches: [main] + types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] + pull_request_target: + branches: [main] + types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled, closed] + push: + branches: [main] + workflow_dispatch: +permissions: {} +jobs: + changelog: + permissions: + contents: write + pull-requests: write + issues: read + statuses: write + uses: php-fast-forward/changelog/.github/workflows/changelog.yml@85474de5a16bb08f83c9f30e0ed72f331a210f07 # v0.3.0 + with: + runs-on: '["ubuntu-latest"]' + base-branch: main + source: tags + prepare-release: true + publish: true diff --git a/.github/workflows/repository-ci.yml b/.github/workflows/repository-ci.yml new file mode 100644 index 0000000..5d19f1a --- /dev/null +++ b/.github/workflows/repository-ci.yml @@ -0,0 +1,32 @@ +name: 'Repository: Quality' +on: + pull_request: + push: + branches: [main] + workflow_dispatch: +permissions: + contents: read +jobs: + tests: + runs-on: ubuntu-latest + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + php: ['8.3', '8.4', '8.5'] + steps: + - uses: actions/checkout@v7.0.1 + with: + persist-credentials: false + - uses: shivammathur/setup-php@2.37.1 + with: + php-version: ${{ matrix.php }} + coverage: none + tools: composer:v2 + - run: composer install --no-plugins --no-scripts --no-interaction --prefer-dist --no-progress + env: + COMPOSER_HOME: ${{ runner.temp }}/standards-composer-home + COMPOSER_CACHE_DIR: ${{ runner.temp }}/standards-composer-cache + - run: composer validate --strict + - run: composer check + - run: git diff --exit-code diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a0cf97 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/vendor/ +/.build/ +/.phpunit.cache/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..95c45f3 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,62 @@ +# Fast Forward Code Standards + +## Purpose + +Own focused PHP style rules, syntax validation, CLI and changed-file automation. +Application tests, coverage, reports, dependency updates and documentation +rendering belong to other domains. This first increment does not claim PHPStan +type-analysis or Rector migration parity with DevTools. + +## Ownership + +`src/` owns reusable behavior; the CLI and Actions orchestrate the same services. +The sole distributable skill is under `resources/skills/fast-forward-code-standards`. +Consumer examples live under `resources`, executable Actions under `.github/actions`, +reusables under `.github/workflows/code-standards*.yml`, own event workflows use +`repository-`. Ordinary folders share this root contract. + +## Local Contracts + +- English for code and documentation. PHP runtime ^8.3; no DevTools dependency. +- `check` MUST NOT pass `--fix`, update dependencies, execute application tests, + publish or change source. Tool caches may live in the system temporary directory. +- `fix` is explicit, local and potentially partial on failure; inspect its diff. +- No implicit risky migration, PHP-version migration or semantic refactor profile. + Future semantic work requires an explicit consumer target PHP and opt-in. +- PR checks use `pull_request`, exact head/base, merge-base comparison and Git's + NUL-delimited protocol. Never run untrusted PHP on privileged target events or + self-hosted runners. No secrets are accepted by the checking reusable. +- Never copy credentials, change branch protections or make a status required. +- Use `codex/` branches, independent review and exact-SHA evidence before push. + Create/deduplicate the initial issues first. Each PR including a version PR + requires real successful checks AND Codex AND CodeRabbit reviews on its + current HEAD with no pending findings before merge. Unavailable, skipped, + requested-only or stale reviews do not count. Do not expand permissions to + install those integrations. Technical review replies are authorized here. +- During 0.x, compatible corrections use patch and functionality/breaking + development changes explicitly use minor. No major release is authorized. + Changelog automation owns version PRs/tags; never create manual bootstrap tags. + +## Work Guidance + +Run commands through RTK on this host. Preserve unrelated changes. Add regression +fixtures and unique `.changelog/*.md` fragments; never consolidate history in +ordinary work. Select a verified Changelog release before wiring its lifecycle. +Repository creation, authentication and distribution are separate from local tests. + +## Verification + +`composer install --no-plugins --no-scripts`, `composer test`, +`composer validate --strict`, `php bin/code-standards check --all`, +`actionlint .github/workflows/*.yml`, and `git diff --check`. +Tests use disposable repositories and synthetic files, never consumers' worktrees. +No self-hosted CI dispatch. Record actual commands/results and independent review +for the exact local base/head before publication; do not claim remote CI evidence. + +## Child DOX Index + +- [README.md](README.md): library/CLI adoption, explicit fix and tool scope. +- [Policy comparison](docs/policy.md): evidence, rule differences and extraction gates. +- [Automation](docs/automation.md): trust, ref/bootstrap contracts and consumer adoption. +- [Instruction map](docs/instruction-map.md): single root scope and skill ownership. +- [Consumer skill](resources/skills/fast-forward-code-standards/SKILL.md): portable consumption procedure; no automatic installation or rollout. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6361e43 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# Changelog + +All notable changes to this project will be documented in this file. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f320900 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Felipe Sayao Lobato Abreu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f6b6b0 --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# Fast Forward Code Standards + +Focused PHP style and static syntax checks, independent of DevTools. The library +provides an extendable ECS PSR-12 preset; the CLI checks tracked PHP files or the files +changed by an exact PR head. This initial increment validates PHP syntax and +style, not type correctness, application behavior or coverage. + +## Local development and first adoption + +Requires PHP 8.3+ and Git. Composer resolves the tool runtime independently from +the consumer application's PHP minimum; syntax is checked using the executing +PHP binary. No PHP migration rules are enabled. Review compatibility with the +application's minimum PHP before adopting a preset. + +```sh +composer install --no-plugins --no-scripts +php bin/code-standards help +php bin/code-standards check --all +php bin/code-standards check --base=BASE_COMMIT_SHA --head=HEAD_COMMIT_SHA +php bin/code-standards fix --all +composer test +``` + +`check` reports violations with a nonzero status and does not change source. +`fix` explicitly applies ECS fixes. It does not commit, push or update dependencies. +Recheck and inspect the diff afterward; failures can leave already applied fixes. +No selected PHP files is a successful no-op. Invalid options, configuration, Git +history or head mismatch fail closed with exit 2. Otherwise tool exit status is +propagated. Changed mode requires a clean tracked checkout of the requested head. +`--all` includes tracked files and permits inspecting local tracked edits. + +### Library and local configuration + +Once a reviewed version/ref is available, install `fast-forward/code-standards` +as development tooling. No release of this package is assumed by these examples. +To trial before publication, use a Composer path repository pointing at this +checkout, require `*@dev`, and disable plugins/scripts. Production adoption must +select the reviewed Composer version and workflow/runtime refs together. + +```php +withConfiguredRule( + \PhpCsFixer\Fixer\Phpdoc\PhpdocAlignFixer::class, + ['align' => 'left'], + ); +}); +``` + +Select it explicitly with `--config=ecs.php`. PHP configuration is executable +code and belongs in an unprivileged environment. The CLI defaults to its own +preset, does not discover or overwrite consumer configuration, and never runs +both ECS and a competing PHP-CS-Fixer pass. Standalone ECS users may import the +same library preset and set their own paths. CLI selection uses the scope below; +ECS's configured paths do not expand the CLI's explicit selected file list. + +## File scope and guarantees + +- Tracked `*.php` and PHP entrypoints under `bin/` are included. +- `vendor`, `.git`, `.build`, `.dev-tools`, `backup`, `cache`, `public`, + `resources`, `tmp`, `node_modules` directory components are excluded. +- Symlink paths fail closed. Spaces, tabs, newlines and leading dashes remain + individual subprocess arguments; deleted files are omitted, renames use destinations. +- Changed mode uses `base...head`, so unrelated base-branch changes are excluded. + Composer, ECS and EditorConfig changes escalate to a complete tracked scan. +- Tools run in bounded batches and stop on failure. Syntax checks use `php -n -l`, + so selected application sources are parsed without executing them. + +See [policy and differences](docs/policy.md), [workflow/ref adoption](docs/automation.md) +and the [consumer skill](resources/skills/fast-forward-code-standards/SKILL.md). +Rector/custom PHPDoc transformations and deeper static analysis are separate, +unimplemented increments; this package does not assert extraction parity yet. diff --git a/bin/code-standards b/bin/code-standards new file mode 100644 index 0000000..901ef6a --- /dev/null +++ b/bin/code-standards @@ -0,0 +1,12 @@ +#!/usr/bin/env php +run(array_slice($argv, 1))); diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a1370cc --- /dev/null +++ b/composer.json @@ -0,0 +1,27 @@ +{ + "name": "fast-forward/code-standards", + "description": "Focused PHP style presets, syntax checks and changed-file automation.", + "type": "library", + "license": "MIT", + "require": { + "php": "^8.3", + "symfony/process": "^7.4 || ^8.0", + "symplify/easy-coding-standard": "^13.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.5" + }, + "autoload": { + "psr-4": {"FastForward\\CodeStandards\\": "src/"} + }, + "autoload-dev": { + "psr-4": {"FastForward\\CodeStandards\\Tests\\": "tests/"} + }, + "bin": ["bin/code-standards"], + "scripts": { + "test": "phpunit", + "check": ["@test", "@php bin/code-standards check --all"], + "fix": "@php bin/code-standards fix --all" + }, + "config": {"sort-packages": true, "allow-plugins": false, "platform": {"php": "8.3.0"}} +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..e279246 --- /dev/null +++ b/composer.lock @@ -0,0 +1,1855 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "6ffc99c1cfd2f99007a91b578e86cf3d", + "packages": [ + { + "name": "symfony/process", + "version": "v7.4.21", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "e5247f4a5e0797d232ad8e8e6803aeb341843c83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/e5247f4a5e0797d232ad8e8e6803aeb341843c83", + "reference": "e5247f4a5e0797d232ad8e8e6803aeb341843c83", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.4.21" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-10-08T16:56:12+00:00" + }, + { + "name": "symplify/easy-coding-standard", + "version": "13.3.8", + "source": { + "type": "git", + "url": "https://github.com/ecsphp/ecs.git", + "reference": "0564c0b9b807f1740bf0f2cc965ab51551f4dc3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ecsphp/ecs/zipball/0564c0b9b807f1740bf0f2cc965ab51551f4dc3b", + "reference": "0564c0b9b807f1740bf0f2cc965ab51551f4dc3b", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "conflict": { + "friendsofphp/php-cs-fixer": "<3.95.20", + "phpcsstandards/php_codesniffer": "<4.0.4", + "symplify/coding-standard": "<13.0" + }, + "bin": [ + "bin/ecs" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Use Coding Standard with 0-knowledge of PHP-CS-Fixer and PHP_CodeSniffer", + "keywords": [ + "Code style", + "automation", + "fixer", + "static analysis" + ], + "support": { + "issues": "https://github.com/ecsphp/ecs/issues", + "source": "https://github.com/ecsphp/ecs/tree/13.3.8" + }, + "time": "2026-10-20T21:02:34+00:00" + } + ], + "packages-dev": [ + { + "name": "myclabs/deep-copy", + "version": "1.14.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.14.0" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + } + ], + "time": "2026-08-11T10:17:44+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.9.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "9e33da9553fe7786f0962b35f4e4ecf01be89def" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/9e33da9553fe7786f0962b35f4e4ecf01be89def", + "reference": "9e33da9553fe7786f0962b35f4e4ecf01be89def", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.9.0" + }, + "time": "2026-09-13T18:51:52+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "12.5.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7440368d94738d54f2c1de24dbd3871793bbb0dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7440368d94738d54f2c1de24dbd3871793bbb0dd", + "reference": "7440368d94738d54f2c1de24dbd3871793bbb0dd", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.9.0", + "php": ">=8.3", + "phpunit/php-text-template": "^5.0.1", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.1.2", + "sebastian/lines-of-code": "^4.0.1", + "sebastian/version": "^6.0", + "theseer/tokenizer": "^2.0.1" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.37" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.5.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2026-10-03T14:03:50+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a248d1640ab059b075f53a2ef0f9856e864e06b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a248d1640ab059b075f53a2ef0f9856e864e06b5", + "reference": "a248d1640ab059b075f53a2ef0f9856e864e06b5", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.33" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" + } + ], + "time": "2026-08-25T14:40:53+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:58+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "babab73b7ef9fae406f2eb3ee11323a61caa63d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/babab73b7ef9fae406f2eb3ee11323a61caa63d5", + "reference": "babab73b7ef9fae406f2eb3ee11323a61caa63d5", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.37" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-text-template", + "type": "tidelift" + } + ], + "time": "2026-10-03T04:51:29+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "8.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:38+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "12.5.40", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "76bd3198f16aeb375a4d5f3626471445aed0966e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/76bd3198f16aeb375a4d5f3626471445aed0966e", + "reference": "76bd3198f16aeb375a4d5f3626471445aed0966e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-filter": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.14.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.5.8", + "phpunit/php-file-iterator": "^6.0.2", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.1", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.2.1", + "sebastian/comparator": "^7.1.8", + "sebastian/diff": "^7.0.1", + "sebastian/environment": "^8.1.2", + "sebastian/exporter": "^7.0.3", + "sebastian/global-state": "^8.0.3", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/recursion-context": "^7.0.1", + "sebastian/type": "^6.0.4", + "sebastian/version": "^6.0.1", + "staabm/side-effects-detector": "^1.0.5" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.40" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsoring.html", + "type": "other" + } + ], + "time": "2026-10-16T05:55:07+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "4.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "7d05781b13f7dec9043a629a21d086ed74582a15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/7d05781b13f7dec9043a629a21d086ed74582a15", + "reference": "7d05781b13f7dec9043a629a21d086ed74582a15", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" + } + ], + "time": "2026-05-17T05:29:34+00:00" + }, + { + "name": "sebastian/comparator", + "version": "7.1.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "7c65c1e79836812819705b473a90c12399542485" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/7c65c1e79836812819705b473a90c12399542485", + "reference": "7c65c1e79836812819705b473a90c12399542485", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.25" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-05-21T04:45:25+00:00" + }, + { + "name": "sebastian/complexity", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:25+00:00" + }, + { + "name": "sebastian/diff", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "cd4cabe39f8a4e8ee6818ba99f10a05561ea4ad6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/cd4cabe39f8a4e8ee6818ba99f10a05561ea4ad6", + "reference": "cd4cabe39f8a4e8ee6818ba99f10a05561ea4ad6", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.33", + "symfony/process": "^7.4.17" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/diff", + "type": "tidelift" + } + ], + "time": "2026-08-25T15:35:54+00:00" + }, + { + "name": "sebastian/environment", + "version": "8.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "9d32c685773823b1983e256ae4ecd48a10d6e439" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/9d32c685773823b1983e256ae4ecd48a10d6e439", + "reference": "9d32c685773823b1983e256ae4ecd48a10d6e439", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.26" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/8.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2026-05-25T13:40:20+00:00" + }, + { + "name": "sebastian/exporter", + "version": "7.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23", + "reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/recursion-context": "^7.0.1" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2026-05-20T04:37:17+00:00" + }, + { + "name": "sebastian/global-state", + "version": "8.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "b164d3274d6537ab462591c5755f76a8f5b1aae9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b164d3274d6537ab462591c5755f76a8f5b1aae9", + "reference": "b164d3274d6537ab462591c5755f76a8f5b1aae9", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0.1" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^12.5.28" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" + } + ], + "time": "2026-06-01T15:10:33+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d543b8ef219dcd8da262cbb958639a96bedba10e", + "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.7.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/lines-of-code", + "type": "tidelift" + } + ], + "time": "2026-05-19T16:22:07+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:48+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:17+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-13T04:44:59+00:00" + }, + { + "name": "sebastian/type", + "version": "6.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "82ff822c2edc46724be9f7411d3163021f602773" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/82ff822c2edc46724be9f7411d3163021f602773", + "reference": "82ff822c2edc46724be9f7411d3163021f602773", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2026-05-20T06:45:45+00:00" + }, + { + "name": "sebastian/version", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "9e935a16187e14f39b0e79910dabd614972cdb26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/9e935a16187e14f39b0e79910dabd614972cdb26", + "reference": "9e935a16187e14f39b0e79910dabd614972cdb26", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/version", + "type": "tidelift" + } + ], + "time": "2026-10-04T05:50:08+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^8.1" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-12-08T11:19:18+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^8.3" + }, + "platform-dev": {}, + "platform-overrides": { + "php": "8.3.0" + }, + "plugin-api-version": "2.9.0" +} diff --git a/config/ecs.php b/config/ecs.php new file mode 100644 index 0000000..7665f68 --- /dev/null +++ b/config/ecs.php @@ -0,0 +1,12 @@ +` +and `runtime-ref: `. GitHub's reusable context identifies the caller, +so the workflow must not derive its tool source ref from `github.workflow_sha`. +Review both refs together. The runtime input is validated before checkout. +An explicit user-selected config remains executable code; only use it in this +unprivileged sandbox. GitHub Actions private-repository sharing policies must +already permit the caller. This project never broadens organization access. + +Before a first release, use the reviewed, pushed implementation commit for both +pins. The resource wrapper contains `REPLACE_WITH_REVIEWED_40_CHARACTER_SHA` and +is intentionally inactive until that replacement. It does not reference an +imaginary `v0.1.0`. For a local trial use a path Composer repository or run the +checkout's CLI with `--directory` targeting a disposable consumer. Check archives +and fresh Composer installations before claiming distribution readiness. + +A technical empty baseline commit permits an initial functional PR when a remote +repository has no history. All functional content remains on its PR branch. +The initial `.changelog` fragment is explicitly `minor`; Changelog's supported +baseline `0.0.0` yields `0.1.0` without a manual tag or engine change. Before wiring +publication, verify the selected published Changelog tag, source workflow inputs, +release transaction and token availability. The caller selects the verified +published `v0.3.0` commit `85474de5a16bb08f83c9f30e0ed72f331a210f07`. +Its documented no-tag baseline and minor fragment support initial `0.1.0`. +It uses the existing GitHub Actions token; if repository policy prevents PR +creation, report the gate without changing that setting or creating credentials. +0.x policy requires reviewing every prepared version PR; reject any plan +whose next major exceeds zero. There is no claimed generic maximum-major input. +Both Codex and CodeRabbit must actually approve the current HEAD, including the +version PR. An unavailable integration blocks merge and publication. Local review +does not substitute for these remote gates. The privileged Changelog target +event only invokes its pinned trusted lifecycle, never this PHP checking job. + +Checking can fail the workflow. Making it a required status is a separate GitHub +protection decision. `fix` has no automatic push workflow and never runs during +PR checks. Full tracked scans occur for Composer/ECS/EditorConfig changes; after +other policy imports change, run an explicit full check in addition to PR checks. diff --git a/docs/instruction-map.md b/docs/instruction-map.md new file mode 100644 index 0000000..096a778 --- /dev/null +++ b/docs/instruction-map.md @@ -0,0 +1,14 @@ +# Instruction map + +The initial directory was empty and had no repository contracts. The shared host +bootstrap and Harness were read before implementation. This repository uses one +root `AGENTS.md`, following Changelog's single-root model: ordinary source, tests, +docs, Actions and workflow folders have no different maintenance authority. + +`resources/skills/fast-forward-code-standards/SKILL.md` owns only the portable +consumer procedure. It is not copied to `.agents/skills` and needs no host alias. +Read root instructions, then only the linked domain guide or skill needed by +the task. The root Child DOX Index provides the reading route. + +No previous instruction tree is migrated. Rollback restores the root contract, +its index and this map together through Git; it changes no host configuration. diff --git a/docs/policy.md b/docs/policy.md new file mode 100644 index 0000000..e698e31 --- /dev/null +++ b/docs/policy.md @@ -0,0 +1,43 @@ +# Policy boundary and comparison + +Source evidence read for this extraction: + +| Repository | Inspected local commit | Relevant paths | +| --- | --- | --- | +| php-fast-forward/dev-tools | `35b5f78ac63813ec07aa3fb3eda08751541e28e1` | `src/Config/ECSConfig.php`, `src/Config/RectorConfig.php`, `.php-cs-fixer.dist.php`, `src/Path/WorkingProjectPathResolver.php` | +| php-fast-forward/changelog | `e75a9be67114b13e593049ea3a37921259f295f7` | `AGENTS.md`, `docs/automation-map.md`, workflows and resource caller | +| coisa/php-cs-fixer | `fdee71bc63d4846463d7715afbc515d63ef2c537` | `src/Config.php`, `Finder.php`, `HeaderExtractor.php`, `RuleSet/DefaultSet.php`, `check-php.yml` | +| coisa/error-handler | `d5aa900b186b273d5ece8fe3a2f5b1c416a2d23b` | `.github/workflows/php-style.yml` | +| mentordosnerds/cli | `ad7651c26ecc6966b6a407c4c301ebedeccc11d1` | `.github/workflows/php-style.yml` | + +The design reference is [DevTools #379](https://github.com/php-fast-forward/dev-tools/issues/379). +These are inspected local snapshots, not claims that local branches equal remote +main. Unrelated edits in source repositories were preserved. No implementation +from blocked PR #376 or composer-installers #15 was executed or changed. + +| Policy | DevTools | CoiSA | Initial Code Standards | +| --- | --- | --- | --- | +| Engine | ECS plus separate PHPDoc fixer and Rector | PHP-CS-Fixer | ECS only; no competing pass | +| Named presets | Symfony + risky + auto + risky; prepared PSR12/common/Symplify/cleanCode | PhpCsFixer + risky + auto + risky PHP migration | Prepared PSR12 only, plus left PHPDoc alignment | +| Runtime | PHP 8.3+ contract | PHP ^8.3 | PHP ^8.3 | +| Consumer PHP target | Rector resolves Composer minimum | auto rules resolve minimum | No version-dependent migration sets | +| PHPDoc preservation | skips conversion, empty/redundant tags removal, global imports, general annotation removal | removes author; preserves empty return/package | Same six ECS skips as DevTools; left alignment | +| Extra risky policy | PHPUnit static calls self; missing typed params; refactoring sets | immutable date, private visibility, simplified null returns and migration | Deliberately absent pending separate profile/fixtures | +| Headers | `.docheader`, current year, after strict declaration | opening header extracted without executing config, updated year, after open | No automatic header rewrite | +| Scope | source resolver excludes generated/resources/public/vendor | consumer Finder, hidden files, VCS ignores, path exclusions | Tracked PHP/bin scope, same generated exclusions plus node_modules; symlinks rejected | +| Overrides | customize callback last | extraRules last | customize callback last; explicit config selection | +| Check | combined orchestration has independent mutation risks | dry-run fixer | syntax parse then ECS check, no source writes | +| Semantic transformations | Rector seven sets, PHP migration, Safe migration, Faker and custom PHPDoc | mixed into risky fixer | Not implemented; requires explicit target, opt-in and independent tests | + +The explicit rule deltas above are intentional. Transitive upstream preset +membership is dependency-version specific and fixed for contributor builds by +`composer.lock`. This is not a rule-by-rule equivalence proof for the complete +legacy ECS + Rector + PHPDoc stack. No consumers may be automatically migrated +on the strength of this comparison. Before migration, expand resolved rule lists, +exercise headers and overrides in golden fixtures and decide which semantic +transformations belong in an opt-in profile. Optional Twig/magic-value policy, +Composer metadata normalization and existing public DevTools APIs stay untouched. + +Static quality in this first increment means parseable PHP and formatting +conformance. Type analysis, dead-code analysis and semantic refactors are not +claimed. Application tests, coverage and complete reports remain outside this domain. diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..041b153 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,4 @@ + + + tests + diff --git a/resources/skills/fast-forward-code-standards/SKILL.md b/resources/skills/fast-forward-code-standards/SKILL.md new file mode 100644 index 0000000..4de8a33 --- /dev/null +++ b/resources/skills/fast-forward-code-standards/SKILL.md @@ -0,0 +1,32 @@ +--- +name: fast-forward-code-standards +description: Check or explicitly fix PHP formatting and syntax using Fast Forward Code Standards, including changed-file PR workflow adoption and review of local ECS overrides. +license: MIT +metadata: + repository: https://github.com/php-fast-forward/code-standards +--- + +# Fast Forward Code Standards + +1. Read consumer AGENTS, Git status, PHP requirements and existing style config. + Preserve local edits. Do not replace another preset or migrate consumers + without an explicit request and a fixture-backed policy comparison. +2. Inspect the selected reviewed release/ref's README, workflow and CLI `help`. + Before the first release use a reviewed commit or local path trial, never an + invented version. Keep workflow and tool runtime pinned to the same commit. +3. Use `code-standards check --all` for a full tracked scan. For a clean PR head, + use `check --base= --head=`. Check means syntax + and ECS style only; it does not prove type safety, tests or coverage. +4. Select a local ECS configuration explicitly with `--config=ecs.php` and keep + imports/overrides. Configuration is executable PHP: never load it with a + privileged token or on a privileged target event. No incidental dependency update. +5. Only when fixing is requested run `code-standards fix --all`, inspect the diff + and rerun check. Do not commit/push automatically. Semantic migrations need a + separate reviewed procedure; none are enabled by this initial package. +6. For requested CI adoption copy `resources/workflows/code-standards.yml` from + the selected ref, replace both pin placeholders and use `pull_request` with + read-only permissions and a hosted runner. No secrets/inherit, write token, + target event or automatic fix. Required-check settings remain separately owned. + +This directory is the sole source payload. Installation/host activation is explicit; +copying this skill does not install the runtime or authorize consumer rollout. diff --git a/resources/workflows/code-standards.yml b/resources/workflows/code-standards.yml new file mode 100644 index 0000000..84dd389 --- /dev/null +++ b/resources/workflows/code-standards.yml @@ -0,0 +1,12 @@ +# Copy to .github/workflows/code-standards.yml and replace BOTH SHA placeholders. +name: PHP standards +on: + pull_request: +permissions: + contents: read +jobs: + standards: + uses: php-fast-forward/code-standards/.github/workflows/code-standards.yml@REPLACE_WITH_REVIEWED_40_CHARACTER_SHA + with: + runtime-ref: REPLACE_WITH_REVIEWED_40_CHARACTER_SHA + php-version: '8.3' diff --git a/src/Config/ECSConfig.php b/src/Config/ECSConfig.php new file mode 100644 index 0000000..db2d7fd --- /dev/null +++ b/src/Config/ECSConfig.php @@ -0,0 +1,40 @@ +withPreparedSets(psr12: true) + ->withSkip([ + PhpdocToCommentFixer::class, + NoSuperfluousPhpdocTagsFixer::class, + NoEmptyPhpdocFixer::class, + PhpdocNoEmptyReturnFixer::class, + GlobalNamespaceImportFixer::class, + GeneralPhpdocAnnotationRemoveFixer::class, + ]) + ->withConfiguredRule(PhpdocAlignFixer::class, ['align' => 'left']) + ->withoutParallel(); + if (null !== $customize) { + $customize($config); + } + + return $config; + } +} diff --git a/src/Console/Application.php b/src/Console/Application.php new file mode 100644 index 0000000..167ecdd --- /dev/null +++ b/src/Console/Application.php @@ -0,0 +1,76 @@ + $arguments Returns tool status, or 2 for invalid invocation. */ + public function run(array $arguments): int + { + try { + $operation = array_shift($arguments); + if (null === $operation || in_array($operation, ['help', '--help'], true)) { + echo "code-standards check|fix (--all | --base=SHA --head=SHA) [--directory=PATH] [--config=PATH]\n"; + echo "check: PHP syntax and ECS dry check; fix: explicitly apply ECS fixes.\n"; + + return 0; + } + if (!in_array($operation, ['check', 'fix'], true)) { + throw new \InvalidArgumentException('Unknown operation. Use help.'); + } + $options = []; + foreach ($arguments as $argument) { + if ('--all' === $argument) { + $key = 'all'; + $value = true; + } elseif (1 === preg_match('/\A--(base|head|directory|config)=(.+)\z/sD', $argument, $match)) { + [, $key, $value] = $match; + } else { + throw new \InvalidArgumentException('Unsupported argument. Use help.'); + } + if (array_key_exists($key, $options)) { + throw new \InvalidArgumentException('Duplicate option: ' . $key); + } + $options[$key] = $value; + } + $all = isset($options['all']); + if ($all === (isset($options['base']) || isset($options['head']))) { + throw new \InvalidArgumentException('Select exactly --all or both --base and --head.'); + } + $directory = realpath($options['directory'] ?? getcwd()); + if (false === $directory || !is_dir($directory)) { + throw new \InvalidArgumentException('Invalid project directory.'); + } + $config = $options['config'] ?? dirname(__DIR__, 2) . '/config/ecs.php'; + if (!str_starts_with($config, '/')) { + $config = $directory . '/' . $config; + } + if (!is_file($config)) { + throw new \InvalidArgumentException('Configuration file does not exist.'); + } + $process = new NativeProcessRunner(); + $paths = (new FileSelection($process))->select($directory, $options['base'] ?? null, $options['head'] ?? null, realpath($config)); + $files = (new PhpFiles())->filter($directory, $paths); + printf("Selected %d PHP file(s).\n", count($files)); + + return (new StandardsRunner($process, $this->ecs))->run($directory, $files, $config, 'fix' === $operation); + } catch (\Throwable $exception) { + fwrite(STDERR, $exception->getMessage() . "\n"); + + return 2; + } + } +} diff --git a/src/Filesystem/PhpFiles.php b/src/Filesystem/PhpFiles.php new file mode 100644 index 0000000..ce37cbb --- /dev/null +++ b/src/Filesystem/PhpFiles.php @@ -0,0 +1,55 @@ + $paths + * @return list + */ + public function filter(string $directory, array $paths): array + { + $root = realpath($directory); + if (false === $root) { + throw new \InvalidArgumentException('Project directory does not exist.'); + } + $files = []; + foreach ($paths as $path) { + if (str_starts_with($path, '/') || str_contains($path, "\0") || in_array('..', explode('/', $path), true)) { + throw new \InvalidArgumentException('File paths must remain within the project.'); + } + $segments = explode('/', $path); + if ([] !== array_intersect($segments, self::EXCLUDED)) { + continue; + } + $current = $root; + foreach ($segments as $segment) { + $current .= '/' . $segment; + if (is_link($current)) { + throw new \InvalidArgumentException('Symlink paths are outside the supported PHP scope.'); + } + } + if (!is_file($current)) { + continue; + } + $resolved = realpath($current); + if (false === $resolved || !str_starts_with($resolved, $root . DIRECTORY_SEPARATOR)) { + throw new \InvalidArgumentException('File resolves outside the project.'); + } + if (str_ends_with($path, '.php') || (str_starts_with($path, 'bin/') && str_contains((string) file_get_contents($current, length: 256), ' + */ + public function select(string $directory, ?string $base, ?string $head, ?string $config = null): array + { + $top = trim($this->process->capture(['git', 'rev-parse', '--show-toplevel'], $directory)); + if (realpath($top) !== realpath($directory)) { + throw new \InvalidArgumentException('Project directory must be the Git checkout root.'); + } + if (null === $base && null === $head) { + return $this->paths($this->process->capture(['git', 'ls-files', '-z'], $directory)); + } + + foreach ([$base, $head] as $sha) { + if (null === $sha || 1 !== preg_match('/\A[a-fA-F0-9]{40}\z/D', $sha)) { + throw new \InvalidArgumentException('Changed mode requires immutable 40-character base and head SHAs.'); + } + $this->process->capture(['git', 'cat-file', '-e', $sha . '^{commit}'], $directory); + } + + if (trim($this->process->capture(['git', 'rev-parse', 'HEAD'], $directory)) !== strtolower($head)) { + throw new \RuntimeException('Checkout HEAD differs from the requested PR head.'); + } + if ('' !== $this->process->capture(['git', 'status', '--porcelain', '--untracked-files=no'], $directory)) { + throw new \RuntimeException('Changed mode requires a clean tracked checkout of the requested head.'); + } + + // No shell, newline parsing or rename-source parsing. Deleted paths are never passed to tools. + $allChanges = $this->paths($this->process->capture([ + 'git', 'diff', '--name-only', '-z', $base . '...' . $head, '--', + ], $directory)); + foreach ($allChanges as $path) { + if ((null !== $config && $directory . '/' . $path === $config) + || 1 === preg_match('~(^|/)(composer\.(json|lock)|ecs[^/]*\.php|\.editorconfig)$|^src/Config/|^config/~', $path)) { + return $this->paths($this->process->capture(['git', 'ls-files', '-z'], $directory)); + } + } + + return $this->paths($this->process->capture([ + 'git', 'diff', '--name-only', '--diff-filter=ACMRT', '--find-renames', '-z', $base . '...' . $head, '--', + ], $directory)); + } + + /** @return list Decodes Git's binary path protocol without trimming file names. */ + private function paths(string $output): array + { + return array_values(array_filter(explode("\0", $output), static fn (string $path): bool => '' !== $path)); + } +} diff --git a/src/Process/NativeProcessRunner.php b/src/Process/NativeProcessRunner.php new file mode 100644 index 0000000..a1de40a --- /dev/null +++ b/src/Process/NativeProcessRunner.php @@ -0,0 +1,34 @@ +mustRun(); + + return $process->getOutput(); + } + + /** Runs one tool, streaming its output without changing its failure status. */ + public function run(array $arguments, string $directory): int + { + $process = new Process($arguments, $directory, [ + 'GH_TOKEN' => false, + 'GITHUB_TOKEN' => false, + 'COMPOSER_AUTH' => false, + ], timeout: 300); + + return $process->run(static function (string $type, string $output): void { + fwrite(Process::ERR === $type ? STDERR : STDOUT, $output); + }); + } +} diff --git a/src/Process/ProcessRunner.php b/src/Process/ProcessRunner.php new file mode 100644 index 0000000..698523b --- /dev/null +++ b/src/Process/ProcessRunner.php @@ -0,0 +1,15 @@ + $arguments Returns stdout or throws on failure. */ + public function capture(array $arguments, string $directory): string; + + /** @param list $arguments Streams diagnostics and returns the actual exit code. */ + public function run(array $arguments, string $directory): int; +} diff --git a/src/Standards/StandardsRunner.php b/src/Standards/StandardsRunner.php new file mode 100644 index 0000000..67d6975 --- /dev/null +++ b/src/Standards/StandardsRunner.php @@ -0,0 +1,47 @@ + $files Validated absolute PHP paths. + */ + public function run(string $directory, array $files, string $config, bool $fix): int + { + if ([] === $files) { + return 0; + } + foreach ($files as $file) { + if (0 !== $status = $this->process->run([PHP_BINARY, '-n', '-l', $file], $directory)) { + return $status; + } + } + + // Bounded batches avoid ARG_MAX. Stop at the first failure; never report partial success. + foreach (array_chunk($files, 50) as $batch) { + $command = [PHP_BINARY, $this->ecs, 'check', '--config=' . $config, '--no-progress-bar', '--no-interaction']; + if ($fix) { + $command[] = '--fix'; + } + $status = $this->process->run([...$command, '--', ...$batch], $directory); + if (0 !== $status) { + return $status; + } + } + + return 0; + } +} diff --git a/tests/Automation/WorkflowTest.php b/tests/Automation/WorkflowTest.php new file mode 100644 index 0000000..4a94454 --- /dev/null +++ b/tests/Automation/WorkflowTest.php @@ -0,0 +1,25 @@ +directory = realpath(sys_get_temp_dir()) . '/standards-' . bin2hex(random_bytes(8)); + mkdir($this->directory); + $this->git(['init', '-b', 'main']); + $this->git(['config', 'user.name', 'Fixture']); + $this->git(['config', 'user.email', 'fixture@example.invalid']); + $this->git(['config', 'commit.gpgsign', 'false']); + $this->commit(); + } + + protected function tearDown(): void + { + $files = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST, + ); + foreach ($files as $file) { + if ($file->isDir() && !$file->isLink()) { + rmdir($file->getPathname()); + } else { + unlink($file->getPathname()); + } + } + rmdir($this->directory); + } + + public function testChangedFilesUseMergeBaseDestinationsAndNulSafePaths(): void + { + $this->put('old name.php', "put('deleted.php', 'put('unchanged.php', 'commit(); + $this->git(['switch', '-c', 'feature']); + $this->git(['mv', 'old name.php', "renamed\tfile.php"]); + $this->git(['rm', 'deleted.php']); + $this->put("new\nfile.php", 'put('-leading.php', 'put('bin/a command', "#!/usr/bin/env php\nput('vendor/ignored.php', 'commit(); + $this->git(['switch', 'main']); + $this->put('base only.php', 'commit(); + $this->git(['switch', 'feature']); + $paths = (new FileSelection(new NativeProcessRunner()))->select($this->directory, $advancedBase, $head); + self::assertContains("renamed\tfile.php", $paths); + self::assertContains("new\nfile.php", $paths); + self::assertNotContains('deleted.php', $paths); + self::assertNotContains('old name.php', $paths); + self::assertNotContains('base only.php', $paths); + self::assertNotSame($base, $advancedBase); + $filtered = (new PhpFiles())->filter($this->directory, $paths); + self::assertCount(4, $filtered); + self::assertContains($this->directory . '/-leading.php', $filtered); + } + + public function testDeletedConfigurationStillTriggersFullScan(): void + { + $this->put('unchanged.php', 'put('ecs.php', 'commit(); + $this->git(['rm', 'ecs.php']); + $head = $this->commit(); + self::assertSame(['unchanged.php'], (new FileSelection(new NativeProcessRunner()))->select($this->directory, $base, $head)); + } + + public function testInvalidHeadAndDirtyCheckoutFailClosed(): void + { + $this->put('a.php', 'commit(); + $this->put('a.php', 'commit(); + $mismatch = $this->cli(['check', '--base=' . $base, '--head=' . $base]); + self::assertSame(2, $mismatch->getExitCode()); + self::assertStringContainsString('HEAD differs', $mismatch->getErrorOutput()); + $this->put('a.php', 'cli(['check', '--base=' . $base, '--head=' . $head]); + self::assertSame(2, $dirty->getExitCode()); + self::assertStringContainsString('clean tracked checkout', $dirty->getErrorOutput()); + } + + public function testStyleCheckDoesNotWriteAndExplicitFixIsIdempotent(): void + { + $path = "some source.php"; + $bad = "put($path, $bad); + $this->commit(); + $check = $this->cli(['check', '--all']); + self::assertNotSame(0, $check->getExitCode(), $check->getOutput() . $check->getErrorOutput()); + self::assertSame($bad, file_get_contents($this->directory . '/' . $path)); + self::assertSame([$path], $this->trackedAndUntrackedSources()); + $fix = $this->cli(['fix', '--all']); + self::assertSame(0, $fix->getExitCode(), $fix->getOutput() . $fix->getErrorOutput()); + $fixed = file_get_contents($this->directory . '/' . $path); + self::assertNotSame($bad, $fixed); + self::assertSame(0, $this->cli(['check', '--all'])->getExitCode()); + self::assertSame(0, $this->cli(['fix', '--all'])->getExitCode()); + self::assertSame($fixed, file_get_contents($this->directory . '/' . $path)); + } + + public function testSyntaxIsParsedWithoutExecutingSource(): void + { + $this->put('a.php', "commit(); + $this->cli(['check', '--all']); + self::assertFileDoesNotExist($this->directory . '/executed'); + $this->put('a.php', 'directory . '/a.php'); + self::assertNotSame(0, $this->cli(['check', '--all'])->getExitCode()); + self::assertSame($bad, file_get_contents($this->directory . '/a.php')); + } + + public function testEmptyPhpSelectionSucceedsWithoutEcs(): void + { + $this->put('README.md', 'No PHP'); + $base = $this->commit(); + $this->put('README.md', 'Still no PHP'); + $head = $this->commit(); + $check = $this->cli(['check', '--base=' . $base, '--head=' . $head]); + self::assertSame(0, $check->getExitCode()); + self::assertStringContainsString('Selected 0 PHP', $check->getOutput()); + } + + public function testExplicitCustomConfigChangesTriggerFullScanAndSubdirectoryIsRejected(): void + { + $this->put('unchanged.php', 'put('style-policy.php', 'commit(); + $this->put('style-policy.php', 'commit(); + $paths = (new FileSelection(new NativeProcessRunner()))->select( + $this->directory, + $base, + $head, + $this->directory . '/style-policy.php', + ); + self::assertContains('unchanged.php', $paths); + mkdir($this->directory . '/nested'); + $check = $this->cli(['check', '--all', '--directory=' . $this->directory . '/nested']); + self::assertSame(2, $check->getExitCode()); + self::assertStringContainsString('Git checkout root', $check->getErrorOutput()); + } + + public function testSymlinksAndTraversalAreRejected(): void + { + $this->put('a.php', 'directory . '/a.php', $this->directory . '/linked.php'); + try { + (new PhpFiles())->filter($this->directory, ['linked.php']); + self::fail('Symlink should be rejected.'); + } catch (\InvalidArgumentException $exception) { + self::assertStringContainsString('Symlink', $exception->getMessage()); + } + $this->expectException(\InvalidArgumentException::class); + (new PhpFiles())->filter($this->directory, ['../a.php']); + } + + public function testMalformedArgumentsConfigurationAndHistoryFail(): void + { + foreach ([ + ['check'], + ['check', '--all', '--head=' . str_repeat('a', 40)], + ['check', '--base=main', '--head=' . str_repeat('a', 40)], + ['check', '--base=' . str_repeat('a', 40), '--head=' . str_repeat('a', 40)], + ['check', '--all', '--all'], + ['check', '--all', '--fix'], + ['check', '--all', '--config=missing.php'], + ['unknown'], + ] as $arguments) { + self::assertSame(2, $this->cli($arguments)->getExitCode(), json_encode($arguments)); + } + $this->put('a.php', 'put('ecs.php', 'commit(); + self::assertNotSame(0, $this->cli(['check', '--all', '--config=ecs.php'])->getExitCode()); + } + + private function put(string $path, string $content): void + { + $parent = dirname($this->directory . '/' . $path); + if (!is_dir($parent)) { + mkdir($parent, recursive: true); + } + file_put_contents($this->directory . '/' . $path, $content); + } + + private function git(array $arguments): string + { + return (new NativeProcessRunner())->capture(['git', ...$arguments], $this->directory); + } + + private function commit(): string + { + $this->git(['add', '--all']); + $this->git(['-c', 'core.hooksPath=/dev/null', 'commit', '--allow-empty', '-m', 'fixture']); + + return trim($this->git(['rev-parse', 'HEAD'])); + } + + private function cli(array $arguments): Process + { + $process = new Process([PHP_BINARY, dirname(__DIR__, 2) . '/bin/code-standards', ...$arguments], $this->directory, timeout: 60); + $process->run(); + + return $process; + } + + private function trackedAndUntrackedSources(): array + { + return array_values(array_filter(explode("\0", $this->git(['ls-files', '-z', '--cached', '--others', '--exclude-standard'])))); + } +} diff --git a/tests/Standards/StandardsRunnerTest.php b/tests/Standards/StandardsRunnerTest.php new file mode 100644 index 0000000..79985ba --- /dev/null +++ b/tests/Standards/StandardsRunnerTest.php @@ -0,0 +1,65 @@ +run('/repo', $files, '/tools/ecs.php', $fix)); + $command = $process->commands[3]; + self::assertSame($fix, in_array('--fix', $command, true)); + self::assertSame($files, array_slice($command, -3)); + self::assertSame([PHP_BINARY, '-n', '-l', $files[0]], $process->commands[0]); + } + } + + public function testBatchesAreBoundedAndEcsFailureIsNotSwallowed(): void + { + $process = new RecordingProcess(23); + $files = array_map(static fn (int $i): string => '/repo/' . $i . '.php', range(1, 101)); + self::assertSame(23, (new StandardsRunner($process, '/tools/ecs'))->run('/repo', $files, '/config', false)); + self::assertCount(102, $process->commands); + self::assertCount(57, $process->commands[101]); + } + + public function testSyntaxFailurePreventsFixAndNoFilesExecutesNothing(): void + { + $process = new RecordingProcess(0, 17); + $runner = new StandardsRunner($process, '/tools/ecs'); + self::assertSame(0, $runner->run('/repo', [], '/config', true)); + self::assertSame([], $process->commands); + self::assertSame(17, $runner->run('/repo', ['/repo/a.php'], '/config', true)); + self::assertCount(1, $process->commands); + } +} + +final class RecordingProcess implements ProcessRunner +{ + public array $commands = []; + + public function __construct(private readonly int $ecsStatus = 0, private readonly int $syntaxStatus = 0) + { + } + + public function capture(array $arguments, string $directory): string + { + throw new \LogicException('Not expected.'); + } + + public function run(array $arguments, string $directory): int + { + $this->commands[] = $arguments; + + return in_array('-l', $arguments, true) ? $this->syntaxStatus : $this->ecsStatus; + } +} From b456daffbac1ccf2cabffaa6d791363cbf2916e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 15:19:14 -0300 Subject: [PATCH 02/17] fix: preserve changelog contribution gate and clean archives --- CHANGELOG.md | 3 --- composer.json | 3 +++ docs/automation.md | 3 +++ docs/policy.md | 7 ++++++- 4 files changed, 12 insertions(+), 4 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 6361e43..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. diff --git a/composer.json b/composer.json index a1370cc..be15d11 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,9 @@ "psr-4": {"FastForward\\CodeStandards\\Tests\\": "tests/"} }, "bin": ["bin/code-standards"], + "archive": { + "exclude": ["/vendor", "/.build", "/.phpunit.cache", "/.git", "/packaging-consumer"] + }, "scripts": { "test": "phpunit", "check": ["@test", "@php bin/code-standards check --all"], diff --git a/docs/automation.md b/docs/automation.md index 1955e99..4f7fa28 100644 --- a/docs/automation.md +++ b/docs/automation.md @@ -37,6 +37,9 @@ and fresh Composer installations before claiming distribution readiness. A technical empty baseline commit permits an initial functional PR when a remote repository has no history. All functional content remains on its PR branch. +The contribution deliberately does not create `CHANGELOG.md`: the normal +contribution check forbids changes to consolidated history. The managed version +PR creates it while consuming the initial fragment. The initial `.changelog` fragment is explicitly `minor`; Changelog's supported baseline `0.0.0` yields `0.1.0` without a manual tag or engine change. Before wiring publication, verify the selected published Changelog tag, source workflow inputs, diff --git a/docs/policy.md b/docs/policy.md index e698e31..e7aa4b7 100644 --- a/docs/policy.md +++ b/docs/policy.md @@ -31,7 +31,12 @@ from blocked PR #376 or composer-installers #15 was executed or changed. The explicit rule deltas above are intentional. Transitive upstream preset membership is dependency-version specific and fixed for contributor builds by -`composer.lock`. This is not a rule-by-rule equivalence proof for the complete +`composer.lock`. +ECS 13.3.8 deprecates `withPhpCsFixerSets()` as a no-op. The initial implementation +therefore uses the supported prepared PSR-12 set and proves violations with a +real fixture instead of inheriting the legacy dynamic-set call blindly. + +This is not a rule-by-rule equivalence proof for the complete legacy ECS + Rector + PHPDoc stack. No consumers may be automatically migrated on the strength of this comparison. Before migration, expand resolved rule lists, exercise headers and overrides in golden fixtures and decide which semantic From 464179f310dccbd3d8824d18f29e67c9fad79610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 15:19:50 -0300 Subject: [PATCH 03/17] docs: define library tool boundaries and bootstrap --- AGENTS.md | 1 + README.md | 2 ++ docs/architecture.md | 59 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 docs/architecture.md diff --git a/AGENTS.md b/AGENTS.md index 95c45f3..a3925af 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -57,6 +57,7 @@ for the exact local base/head before publication; do not claim remote CI evidenc - [README.md](README.md): library/CLI adoption, explicit fix and tool scope. - [Policy comparison](docs/policy.md): evidence, rule differences and extraction gates. +- [Architecture decision](docs/architecture.md): library/tool dependencies, CPX/autoload limits and bootstrap without circular prerequisites. - [Automation](docs/automation.md): trust, ref/bootstrap contracts and consumer adoption. - [Instruction map](docs/instruction-map.md): single root scope and skill ownership. - [Consumer skill](resources/skills/fast-forward-code-standards/SKILL.md): portable consumption procedure; no automatic installation or rollout. diff --git a/README.md b/README.md index 0f6b6b0..8c014e4 100644 --- a/README.md +++ b/README.md @@ -77,5 +77,7 @@ ECS's configured paths do not expand the CLI's explicit selected file list. See [policy and differences](docs/policy.md), [workflow/ref adoption](docs/automation.md) and the [consumer skill](resources/skills/fast-forward-code-standards/SKILL.md). +The [architecture decision](docs/architecture.md) defines library/tool dependency +directions and CPX/autoload limits. Rector/custom PHPDoc transformations and deeper static analysis are separate, unimplemented increments; this package does not assert extraction parity yet. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..89b53ed --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,59 @@ +# Architecture decision: libraries, tools and bootstrap + +Status: accepted direction for the initial Code Standards extraction. + +## Context + +DevTools combines independently reusable libraries and developer tools. Extracting +that combination must not introduce a dependency cycle or require all tools to +exist before the first tool can be tested. See +[DevTools #379](https://github.com/php-fast-forward/dev-tools/issues/379) and the +[initial backlog](https://github.com/php-fast-forward/code-standards/issues). + +## Decision + +Libraries expose reusable runtime behavior: Reflection, Console, Iterators and +Enum are examples of library responsibilities. Tools own executable development +workflows: Changelog, Code Standards and Testing are tool responsibilities. +These are dependency roles, not a request to create Reflection or Console now. + +Tools may depend on libraries at runtime. Libraries may consume tools as +development dependencies. A library must not require a development tool for its +normal runtime behavior. Tools must not depend on DevTools in production or +create a circular tool/bootstrap prerequisite. Code Standards provides its +focused preset as a library API for tool consumers while remaining a development +tool; that API does not make application tests/reports part of its domain. + +CPX separates installation and dependency resolution. It does not isolate PHP +namespaces when PHPUnit, Rector or executable configuration loads the consumer +and tool in the same process. Version conflicts and global state still require +explicit compatibility tests or a real process boundary. A separate Composer +graph alone must never be described as runtime isolation. + +## Bootstrap and compatibility + +The first Code Standards increment uses PHPUnit directly and deterministic local +checks, without waiting for a future Testing tool or other extracted libraries. +Its subprocess transport uses an existing library. Its actual ECS check/fix and +syntax checks run as bounded child processes; `php -n -l` parses source without +executing it. ECS configuration is executable PHP inside that tool's process. + +Own source checks can use the source checkout during bootstrap. Cross-tool +consumption should select compatible published versions once those versions +exist; no dependency or example may point to an invented release. Workflow and +runtime commits must match. Distinguish tool runtime requirements, Composer +resolution and the consumer application's supported PHP language/API minimum. +No automatic PHP migrations derive authority from the host tool's PHP version. + +## Consequences and next gates + +This small slice validates syntax and PSR-12 style. Type analysis and semantic +refactoring remain separate scoped issues with explicit opt-in and compatibility +evidence. Changelog owns version PR/tag/release transactions; Code Standards does +not duplicate its engine. Application tests, coverage and full reports remain +outside this domain. No consumer migration follows implicitly from extraction. + +Backlog transfers require a confirmed destination issue with context and links +before commenting/closing the DevTools original. Mixed issues retain their +DevTools scope. Await the coordinated inventory before creating migrated +duplicates; current DevTools bug PRs stay in that repository. From 657d4a08bc93f3eb4df97c0a3dab0e13d1202e59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 15:28:04 -0300 Subject: [PATCH 04/17] docs: record dependency and migrated backlog boundaries --- docs/architecture.md | 32 +++++++++++++++++-- .../fast-forward-code-standards/SKILL.md | 2 ++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index 89b53ed..7850036 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -23,12 +23,18 @@ normal runtime behavior. Tools must not depend on DevTools in production or create a circular tool/bootstrap prerequisite. Code Standards provides its focused preset as a library API for tool consumers while remaining a development tool; that API does not make application tests/reports part of its domain. +`require-dev` dependencies are not transitively installed for library consumers: +an application explicitly selects the development tools it actually uses. CPX separates installation and dependency resolution. It does not isolate PHP namespaces when PHPUnit, Rector or executable configuration loads the consumer and tool in the same process. Version conflicts and global state still require explicit compatibility tests or a real process boundary. A separate Composer graph alone must never be described as runtime isolation. +Analyzer/autoload fixtures must prove that the code under analysis comes from +the consumer checkout, rather than another copy installed inside the tool. +This initial syntax parser executes no application autoload; future PHPUnit, +Rector and type-analysis adapters must add those compatibility tests. ## Bootstrap and compatibility @@ -38,7 +44,9 @@ Its subprocess transport uses an existing library. Its actual ECS check/fix and syntax checks run as bounded child processes; `php -n -l` parses source without executing it. ECS configuration is executable PHP inside that tool's process. -Own source checks can use the source checkout during bootstrap. Cross-tool +Own source checks can use the source checkout during bootstrap. Once published, +self-consumption selects a compatible previous published tool version rather +than depending on the version currently being built. Cross-tool consumption should select compatible published versions once those versions exist; no dependency or example may point to an invented release. Workflow and runtime commits must match. Distinguish tool runtime requirements, Composer @@ -53,7 +61,27 @@ evidence. Changelog owns version PR/tag/release transactions; Code Standards doe not duplicate its engine. Application tests, coverage and full reports remain outside this domain. No consumer migration follows implicitly from extraction. +## Coordinated backlog boundaries + +Code Standards #1 is the destination epic for DevTools #379. Optional magic-value +and Twig evaluations move to #8/#9 and do not block 0.1.0. Relevant skill +procedures from DevTools #195 and ECS/Rector structured-result criteria from +#248 belong here, while both mixed source issues remain open. LLM Skills owns +discovery/install/update; this tool owns its canonical skill payload and does +not introduce another installer or sync system. + +DevTools #14/#265/#266/#267 remain conditional analysis candidates. Ownership +between a Code Standards module and another analysis domain remains undecided; +Code Standards #3 is a bounded investigation, not an implicit transfer or engine +choice. Validate #267's actual engine before treating its proposed PHPCS/PHPStan +relationship as fact. None is a prerequisite for initial 0.1.0. + +Doctor #142, distribution #197, published refs #238, orchestration #240/#384 and +Actions governance #347–349 remain with DevTools. Current bugs and correction +PRs remain there. Historical extensibility/options/config/path contracts are +criteria, not automatically reopened tasks. + Backlog transfers require a confirmed destination issue with context and links before commenting/closing the DevTools original. Mixed issues retain their -DevTools scope. Await the coordinated inventory before creating migrated +DevTools scope. Use the coordinated inventory and existing destinations to avoid duplicates; current DevTools bug PRs stay in that repository. diff --git a/resources/skills/fast-forward-code-standards/SKILL.md b/resources/skills/fast-forward-code-standards/SKILL.md index 4de8a33..eef851b 100644 --- a/resources/skills/fast-forward-code-standards/SKILL.md +++ b/resources/skills/fast-forward-code-standards/SKILL.md @@ -30,3 +30,5 @@ metadata: This directory is the sole source payload. Installation/host activation is explicit; copying this skill does not install the runtime or authorize consumer rollout. +LLM Skills owns discovery/install/update; follow the selected ref's distribution +contract rather than adding another copy/symlink installer in this tool. From fe794f90296fbd500e88cee10b8d5f23450ef4ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 15:35:29 -0300 Subject: [PATCH 05/17] fix: apply reviewed PHP scope and immutable Action pins --- .changelog/review-safe-scope.md | 8 ++++++ .github/workflows/code-standards-check.yml | 6 ++-- .github/workflows/repository-ci.yml | 4 +-- README.md | 3 +- src/Filesystem/PhpFiles.php | 3 ++ tests/Automation/WorkflowTest.php | 14 ++++++++++ tests/Integration/StandardsTest.php | 32 ++++++++++++++++++++++ tests/Standards/StandardsRunnerTest.php | 6 ++++ 8 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 .changelog/review-safe-scope.md diff --git a/.changelog/review-safe-scope.md b/.changelog/review-safe-scope.md new file mode 100644 index 0000000..6c766c5 --- /dev/null +++ b/.changelog/review-safe-scope.md @@ -0,0 +1,8 @@ +--- +category: fixed +type: patch +--- + +Skips non-PHP documentation symlinks before source-path validation while retaining +symlink rejection for PHP and bin candidates, and pins workflow Actions to reviewed +immutable commits. diff --git a/.github/workflows/code-standards-check.yml b/.github/workflows/code-standards-check.yml index b7ea527..00eb94f 100644 --- a/.github/workflows/code-standards-check.yml +++ b/.github/workflows/code-standards-check.yml @@ -30,14 +30,14 @@ jobs: [[ "$STANDARDS_BASE" =~ ^[a-fA-F0-9]{40}$ ]] || exit 1 [[ "$STANDARDS_HEAD" =~ ^[a-fA-F0-9]{40}$ ]] || exit 1 - name: Check out reviewed tool runtime - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: php-fast-forward/code-standards ref: ${{ inputs.runtime-ref }} path: tool persist-credentials: false - name: Set up PHP - uses: shivammathur/setup-php@2.37.1 + uses: shivammathur/setup-php@7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc # 2.37.1 with: php-version: ${{ inputs.php-version }} coverage: none @@ -49,7 +49,7 @@ jobs: COMPOSER_CACHE_DIR: ${{ runner.temp }}/standards-composer-cache run: composer install --no-dev --no-plugins --no-scripts --no-interaction --prefer-dist --no-progress - name: Check out exact consumer head - uses: actions/checkout@v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event.pull_request.head.sha }} path: consumer diff --git a/.github/workflows/repository-ci.yml b/.github/workflows/repository-ci.yml index 5d19f1a..ae6971c 100644 --- a/.github/workflows/repository-ci.yml +++ b/.github/workflows/repository-ci.yml @@ -15,10 +15,10 @@ jobs: matrix: php: ['8.3', '8.4', '8.5'] steps: - - uses: actions/checkout@v7.0.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: shivammathur/setup-php@2.37.1 + - uses: shivammathur/setup-php@7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc # 2.37.1 with: php-version: ${{ matrix.php }} coverage: none diff --git a/README.md b/README.md index 8c014e4..41be594 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,8 @@ ECS's configured paths do not expand the CLI's explicit selected file list. - Tracked `*.php` and PHP entrypoints under `bin/` are included. - `vendor`, `.git`, `.build`, `.dev-tools`, `backup`, `cache`, `public`, `resources`, `tmp`, `node_modules` directory components are excluded. -- Symlink paths fail closed. Spaces, tabs, newlines and leading dashes remain +- Candidate PHP/bin symlink paths fail closed; unrelated non-PHP aliases are ignored. + Spaces, tabs, newlines and leading dashes remain individual subprocess arguments; deleted files are omitted, renames use destinations. - Changed mode uses `base...head`, so unrelated base-branch changes are excluded. Composer, ECS and EditorConfig changes escalate to a complete tracked scan. diff --git a/src/Filesystem/PhpFiles.php b/src/Filesystem/PhpFiles.php index ce37cbb..049deed 100644 --- a/src/Filesystem/PhpFiles.php +++ b/src/Filesystem/PhpFiles.php @@ -30,6 +30,9 @@ public function filter(string $directory, array $paths): array if ([] !== array_intersect($segments, self::EXCLUDED)) { continue; } + if (!str_ends_with($path, '.php') && !str_starts_with($path, 'bin/')) { + continue; + } $current = $root; foreach ($segments as $segment) { $current .= '/' . $segment; diff --git a/tests/Automation/WorkflowTest.php b/tests/Automation/WorkflowTest.php index 4a94454..9a5588c 100644 --- a/tests/Automation/WorkflowTest.php +++ b/tests/Automation/WorkflowTest.php @@ -9,6 +9,20 @@ /** Contract assertions complement actionlint and real Git/CLI regressions. */ final class WorkflowTest extends TestCase { + /** Verifies supply-chain pins for every external Action in executable repository workflows. */ + public function testExternalActionsUseReviewedImmutableCommits(): void + { + foreach (glob(dirname(__DIR__, 2) . '/.github/workflows/*.yml') as $file) { + preg_match_all('~(?:^|\n)\s*(?:-\s*)?uses:\s*([^\s#]+)~', file_get_contents($file), $matches); + foreach ($matches[1] as $reference) { + if (!str_starts_with($reference, './')) { + self::assertMatchesRegularExpression('/@[a-f0-9]{40}$/', $reference, $file); + } + } + } + } + + /** Keeps the checking adapter bound to unprivileged PR events and disposable hosted runners. */ public function testPrWorkflowHasNoPrivilegedEventSecretOrSelfHostedRunner(): void { $workflow = file_get_contents(dirname(__DIR__, 2) . '/.github/workflows/code-standards-check.yml'); diff --git a/tests/Integration/StandardsTest.php b/tests/Integration/StandardsTest.php index a6e2c38..ce2e8f6 100644 --- a/tests/Integration/StandardsTest.php +++ b/tests/Integration/StandardsTest.php @@ -15,6 +15,7 @@ final class StandardsTest extends TestCase { private string $directory; + /** Creates a disposable repository with synthetic author and no inherited signing requirement. */ protected function setUp(): void { $this->directory = realpath(sys_get_temp_dir()) . '/standards-' . bin2hex(random_bytes(8)); @@ -26,6 +27,7 @@ protected function setUp(): void $this->commit(); } + /** Removes only this test's disposable tree, unlinking symlinks without following them. */ protected function tearDown(): void { $files = new \RecursiveIteratorIterator( @@ -42,6 +44,7 @@ protected function tearDown(): void rmdir($this->directory); } + /** Exercises divergence, rename destinations, deleted sources and binary-safe path arguments. */ public function testChangedFilesUseMergeBaseDestinationsAndNulSafePaths(): void { $this->put('old name.php', "directory . '/-leading.php', $filtered); } + /** Ensures removing a policy file cannot limit checking to an empty changed-file set. */ public function testDeletedConfigurationStillTriggersFullScan(): void { $this->put('unchanged.php', 'select($this->directory, $base, $head)); } + /** Prevents checks from reporting on a different commit or dirty tracked source snapshot. */ public function testInvalidHeadAndDirtyCheckoutFailClosed(): void { $this->put('a.php', 'getErrorOutput()); } + /** Uses real ECS to prove violation status, source preservation and second-pass fix stability. */ public function testStyleCheckDoesNotWriteAndExplicitFixIsIdempotent(): void { $path = "some source.php"; @@ -116,6 +122,7 @@ public function testStyleCheckDoesNotWriteAndExplicitFixIsIdempotent(): void self::assertSame($fixed, file_get_contents($this->directory . '/' . $path)); } + /** Proves parsing never executes selected source and syntax failures leave its bytes intact. */ public function testSyntaxIsParsedWithoutExecutingSource(): void { $this->put('a.php', "directory . '/a.php')); } + /** Allows documentation-only PRs to complete without invoking the style tool. */ public function testEmptyPhpSelectionSucceedsWithoutEcs(): void { $this->put('README.md', 'No PHP'); @@ -139,6 +147,7 @@ public function testEmptyPhpSelectionSucceedsWithoutEcs(): void self::assertStringContainsString('Selected 0 PHP', $check->getOutput()); } + /** Preserves complete policy-change checks and rejects ambiguous nested-project Git paths. */ public function testExplicitCustomConfigChangesTriggerFullScanAndSubdirectoryIsRejected(): void { $this->put('unchanged.php', 'getErrorOutput()); } + /** Keeps PHP symlinks and parent-directory paths outside the permitted source boundary. */ public function testSymlinksAndTraversalAreRejected(): void { $this->put('a.php', 'filter($this->directory, ['../a.php']); } + /** Checks that documentation aliases cannot block PHP checks or admit executable symlinks. */ + public function testNonPhpSymlinksAreIgnoredButBinSymlinksAreRejected(): void + { + $this->put('AGENTS.md', 'Repository instructions.'); + $this->put('source.php', "directory . '/CLAUDE.md'); + $this->commit(); + $check = $this->cli(['check', '--all']); + self::assertSame(0, $check->getExitCode(), $check->getErrorOutput()); + self::assertStringContainsString('Selected 1 PHP', $check->getOutput()); + mkdir($this->directory . '/bin'); + symlink('../source.php', $this->directory . '/bin/tool'); + $this->expectException(\InvalidArgumentException::class); + (new PhpFiles())->filter($this->directory, ['bin/tool']); + } + + /** Checks invalid invocations, unavailable history and executable config failures return errors. */ public function testMalformedArgumentsConfigurationAndHistoryFail(): void { foreach ([ @@ -193,6 +220,7 @@ public function testMalformedArgumentsConfigurationAndHistoryFail(): void self::assertNotSame(0, $this->cli(['check', '--all', '--config=ecs.php'])->getExitCode()); } + /** Writes a synthetic fixture beneath the disposable test root, creating its parents. */ private function put(string $path, string $content): void { $parent = dirname($this->directory . '/' . $path); @@ -202,11 +230,13 @@ private function put(string $path, string $content): void file_put_contents($this->directory . '/' . $path, $content); } + /** Captures Git fixture output through the production shell-free transport. */ private function git(array $arguments): string { return (new NativeProcessRunner())->capture(['git', ...$arguments], $this->directory); } + /** Commits fixture changes without hooks and returns the exact resulting commit identity. */ private function commit(): string { $this->git(['add', '--all']); @@ -215,6 +245,7 @@ private function commit(): string return trim($this->git(['rev-parse', 'HEAD'])); } + /** Runs the real packaged binary in the fixture with a bounded timeout and captured diagnostics. */ private function cli(array $arguments): Process { $process = new Process([PHP_BINARY, dirname(__DIR__, 2) . '/bin/code-standards', ...$arguments], $this->directory, timeout: 60); @@ -223,6 +254,7 @@ private function cli(array $arguments): Process return $process; } + /** Lists both tracked files and unexpected new files to detect incidental check writes. */ private function trackedAndUntrackedSources(): array { return array_values(array_filter(explode("\0", $this->git(['ls-files', '-z', '--cached', '--others', '--exclude-standard'])))); diff --git a/tests/Standards/StandardsRunnerTest.php b/tests/Standards/StandardsRunnerTest.php index 79985ba..760ef22 100644 --- a/tests/Standards/StandardsRunnerTest.php +++ b/tests/Standards/StandardsRunnerTest.php @@ -10,6 +10,7 @@ final class StandardsRunnerTest extends TestCase { + /** Proves write authority comes only from explicit fix and paths remain separate arguments. */ public function testOnlyExplicitFixAddsWriteFlagAndFilesRemainIndividualArguments(): void { foreach ([false, true] as $fix) { @@ -23,6 +24,7 @@ public function testOnlyExplicitFixAddsWriteFlagAndFilesRemainIndividualArgument } } + /** Verifies batch bounds and immediate propagation of a failed style subprocess. */ public function testBatchesAreBoundedAndEcsFailureIsNotSwallowed(): void { $process = new RecordingProcess(23); @@ -32,6 +34,7 @@ public function testBatchesAreBoundedAndEcsFailureIsNotSwallowed(): void self::assertCount(57, $process->commands[101]); } + /** Rejects malformed source before writes and makes empty selection a subprocess-free no-op. */ public function testSyntaxFailurePreventsFixAndNoFilesExecutesNothing(): void { $process = new RecordingProcess(0, 17); @@ -47,15 +50,18 @@ final class RecordingProcess implements ProcessRunner { public array $commands = []; + /** Configures deterministic transport outcomes without executing real tools. */ public function __construct(private readonly int $ecsStatus = 0, private readonly int $syntaxStatus = 0) { } + /** Fails if standards orchestration unexpectedly performs a capture operation. */ public function capture(array $arguments, string $directory): string { throw new \LogicException('Not expected.'); } + /** Records argument boundaries and returns the selected synthetic tool status. */ public function run(array $arguments, string $directory): int { $this->commands[] = $arguments; From 1f548c8ce024c81aabf5162e23c04a5dec1a8b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 15:56:30 -0300 Subject: [PATCH 06/17] refactor: compose CLI with Symfony Console and validated scope --- AGENTS.md | 4 + README.md | 3 + bin/code-standards | 21 +- composer.json | 1 + composer.lock | 737 ++++++++++++++++++++++- docs/architecture.md | 41 ++ src/Config/ECSConfig.php | 7 +- src/Console/Application.php | 88 +-- src/Console/Command/StandardsCommand.php | 81 +++ src/Process/NativeProcessRunner.php | 9 +- src/Project/ProjectScope.php | 25 + src/Project/ProjectScopeResolver.php | 59 ++ tests/Integration/StandardsTest.php | 41 +- 13 files changed, 1050 insertions(+), 67 deletions(-) create mode 100644 src/Console/Command/StandardsCommand.php create mode 100644 src/Project/ProjectScope.php create mode 100644 src/Project/ProjectScopeResolver.php diff --git a/AGENTS.md b/AGENTS.md index a3925af..88037c4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,6 +18,10 @@ reusables under `.github/workflows/code-standards*.yml`, own event workflows use ## Local Contracts - English for code and documentation. PHP runtime ^8.3; no DevTools dependency. +- Keep methods focused, use early returns and inject execution/output boundaries. + Prefer established library APIs, native ECS configuration and useful PHPDoc. + Separate logical blocks with whitespace; avoid speculative abstractions, custom + parsers/DSLs and silent input coercions. See the architecture quality contract. - `check` MUST NOT pass `--fix`, update dependencies, execute application tests, publish or change source. Tool caches may live in the system temporary directory. - `fix` is explicit, local and potentially partial on failure; inspect its diff. diff --git a/README.md b/README.md index 41be594..945c3a2 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,9 @@ No selected PHP files is a successful no-op. Invalid options, configuration, Git history or head mismatch fail closed with exit 2. Otherwise tool exit status is propagated. Changed mode requires a clean tracked checkout of the requested head. `--all` includes tracked files and permits inspecting local tracked edits. +Symfony Console provides `help check`, standard global flags and both +`--option=value` and `--option value`. Error diagnostics use stderr. Empty paths, +conflicting scan modes and unsupported arguments are rejected explicitly. ### Library and local configuration diff --git a/bin/code-standards b/bin/code-standards index 901ef6a..4e5bfa8 100644 --- a/bin/code-standards +++ b/bin/code-standards @@ -4,9 +4,28 @@ declare(strict_types=1); use FastForward\CodeStandards\Console\Application; +use FastForward\CodeStandards\Console\Command\StandardsCommand; +use FastForward\CodeStandards\Filesystem\PhpFiles; +use FastForward\CodeStandards\Git\FileSelection; +use FastForward\CodeStandards\Process\NativeProcessRunner; +use FastForward\CodeStandards\Project\ProjectScopeResolver; +use FastForward\CodeStandards\Standards\StandardsRunner; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Process\Process; $autoload = $_composer_autoload_path ?? __DIR__ . '/../vendor/autoload.php'; require $autoload; $vendor = dirname($autoload); -exit((new Application($vendor . '/symplify/easy-coding-standard/bin/ecs'))->run(array_slice($argv, 1))); +$output = new ConsoleOutput(); +$process = new NativeProcessRunner(static function (string $type, string $message) use ($output): void { + $stream = Process::ERR === $type ? $output->getErrorOutput() : $output; + $stream->write($message); +}); +$projects = new ProjectScopeResolver(dirname(__DIR__) . '/config/ecs.php'); +$selection = new FileSelection($process); +$files = new PhpFiles(); +$standards = new StandardsRunner($process, $vendor . '/symplify/easy-coding-standard/bin/ecs'); +$command = static fn (bool $fix): StandardsCommand => new StandardsCommand($fix, $projects, $selection, $files, $standards); + +exit((new Application($command(false), $command(true)))->run(output: $output)); diff --git a/composer.json b/composer.json index be15d11..58711ac 100644 --- a/composer.json +++ b/composer.json @@ -5,6 +5,7 @@ "license": "MIT", "require": { "php": "^8.3", + "symfony/console": "^7.4 || ^8.0", "symfony/process": "^7.4 || ^8.0", "symplify/easy-coding-standard": "^13.0" }, diff --git a/composer.lock b/composer.lock index e279246..f42db87 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,565 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6ffc99c1cfd2f99007a91b578e86cf3d", + "content-hash": "dad04c1e4b1938500b5b659cd9bd9c29", "packages": [ + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "symfony/console", + "version": "v7.4.21", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "21f1bc2296f7ff6043241a8dcbc09559b3732820" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/21f1bc2296f7ff6043241a8dcbc09559b3732820", + "reference": "21f1bc2296f7ff6043241a8dcbc09559b3732820", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2|^8.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.4.21" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-10-12T18:45:38+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-05T06:23:12+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.43.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "5cac8f4db97208823aa353f59a01faad2da52c75" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5cac8f4db97208823aa353f59a01faad2da52c75", + "reference": "5cac8f4db97208823aa353f59a01faad2da52c75", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.43.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-09-25T15:46:47+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.43.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/ebd57ccc7edb93d632e184efbe7693d2517bab3b", + "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.43.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-09-23T22:02:09+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.43.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "4d5d331bf731e0aedbc6b2f5e61135b7b7996a72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/4d5d331bf731e0aedbc6b2f5e61135b7b7996a72", + "reference": "4d5d331bf731e0aedbc6b2f5e61135b7b7996a72", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.43.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-09-24T15:19:04+00:00" + }, { "name": "symfony/process", "version": "v7.4.21", @@ -71,6 +628,184 @@ ], "time": "2026-10-08T16:56:12+00:00" }, + { + "name": "symfony/service-contracts", + "version": "v3.7.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "15e6a07ec2a2c75ceb1b21dd98105ee8456d2257" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/15e6a07ec2a2c75ceb1b21dd98105ee8456d2257", + "reference": "15e6a07ec2a2c75ceb1b21dd98105ee8456d2257", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.7.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-27T15:39:01+00:00" + }, + { + "name": "symfony/string", + "version": "v7.4.21", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "9bc35d074ad16a58c21cd52b18c5cacc4f4c3981" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/9bc35d074ad16a58c21cd52b18c5cacc4f4c3981", + "reference": "9bc35d074ad16a58c21cd52b18c5cacc4f4c3981", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.33", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.4.21" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-10-03T15:15:30+00:00" + }, { "name": "symplify/easy-coding-standard", "version": "13.3.8", diff --git a/docs/architecture.md b/docs/architecture.md index 7850036..1f13d2a 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -53,6 +53,47 @@ runtime commits must match. Distinguish tool runtime requirements, Composer resolution and the consumer application's supported PHP language/API minimum. No automatic PHP migrations derive authority from the host tool's PHP version. +## CLI composition and configuration contract + +Changelog's console separates commands, input definitions and injected services. +Code Standards follows that separation with Symfony Console 7.4-compatible APIs, +matching this project's PHP 8.3 minimum. Symfony owns argument binding, help and +global options. Two inexpensive commands are composed directly in the binary; +there is no custom parser, service container or speculative lazy-command layer. +Project-scope validation, Git selection, PHP filtering and tool execution have +separate responsibilities. The process transport receives its output sink; +reusable services do not write to global standard-output handles. + +The supported configuration extension is the native ECS builder. A small PSR-4 +preset class returns that builder and applies an optional callback last. An array +would require a second schema/translator; a global helper function would add an +autoload convention without simplifying native ECS consumption. Neither is +needed. Internal CLI classes are implementation details, not a promise of API +compatibility with DevTools. No custom DSL or automatic configuration discovery +is introduced. + +No configuration is needed for the default preset. A consumer selects an existing +readable PHP file with `--config`; relative paths resolve from the project root. +That file must return the configuration supported by the installed ECS version. +Native builder methods define rule imports, skips and overrides. Invalid paths, +unsupported options and conflicting scan modes fail with an objective error; +empty paths are rejected rather than coerced to the current directory. ECS +configuration errors propagate as failures. Standard Symfony flag semantics +apply, including idempotent repeated boolean flags and both `--option=value` +and `--option value`. Behavioral tests exercise the default preset, real rule +overrides, help outside a checkout and invalid invocations. + +## Implementation quality + +Keep methods focused on one responsibility and use early returns for rejected +inputs. Inject collaborators and execution/output boundaries explicitly. Reuse +established library mechanisms when they solve an actual need; introduce no +framework, duplicate configuration schema or abstraction for hypothetical work. +Separate logical blocks with whitespace and add PHPDoc that explains contracts, +failure conditions and meaningful collection/callback types. Avoid comments +that merely repeat syntax. Regression tests assert observable behavior, errors, +exit statuses and source preservation rather than private implementation shape. + ## Consequences and next gates This small slice validates syntax and PSR-12 style. Type analysis and semantic diff --git a/src/Config/ECSConfig.php b/src/Config/ECSConfig.php index db2d7fd..6be6150 100644 --- a/src/Config/ECSConfig.php +++ b/src/Config/ECSConfig.php @@ -16,7 +16,12 @@ /** A deliberately non-risky PSR-12 starting point for extraction. */ final class ECSConfig { - /** Consumer callbacks run last and may import local rules explicitly. */ + /** + * Returns native ECS defaults with explicit consumer overrides applied last. + * + * @param callable(ECSConfigBuilder):void|null $customize Native rule imports, skips or overrides; null uses the preset unchanged. + * @return ECSConfigBuilder The native builder, ready to return from an explicit ECS configuration file. + */ public static function configure(?callable $customize = null): ECSConfigBuilder { $config = (new ECSConfigBuilder()) diff --git a/src/Console/Application.php b/src/Console/Application.php index 167ecdd..cb036c4 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -4,73 +4,41 @@ namespace FastForward\CodeStandards\Console; -use FastForward\CodeStandards\Filesystem\PhpFiles; -use FastForward\CodeStandards\Git\FileSelection; -use FastForward\CodeStandards\Process\NativeProcessRunner; -use FastForward\CodeStandards\Standards\StandardsRunner; - -/** Thin CLI adapter; services own file selection and checking. */ -final class Application +use FastForward\CodeStandards\Console\Command\StandardsCommand; +use Symfony\Component\Console\Application as SymfonyApplication; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** Native Symfony application; standard binding errors retain the CLI's invalid-input status. */ +final class Application extends SymfonyApplication { - /** Resolves ECS from the same Composer graph as the packaged binary. */ - public function __construct(private readonly string $ecs) + /** Registers the two explicitly composed operations without resolving project state or executing tools. */ + public function __construct(StandardsCommand $check, StandardsCommand $fix) { + parent::__construct('Fast Forward Code Standards'); + + $this->add($check); + $this->add($fix); + $this->setAutoExit(false); } - /** @param list $arguments Returns tool status, or 2 for invalid invocation. */ - public function run(array $arguments): int + /** + * Delegates parsing/help to Symfony and renders controlled binding failures on the error stream. + * + * @return int Command status, or INVALID when Symfony rejects a command, argument or option. + */ + public function doRun(InputInterface $input, OutputInterface $output): int { try { - $operation = array_shift($arguments); - if (null === $operation || in_array($operation, ['help', '--help'], true)) { - echo "code-standards check|fix (--all | --base=SHA --head=SHA) [--directory=PATH] [--config=PATH]\n"; - echo "check: PHP syntax and ECS dry check; fix: explicitly apply ECS fixes.\n"; - - return 0; - } - if (!in_array($operation, ['check', 'fix'], true)) { - throw new \InvalidArgumentException('Unknown operation. Use help.'); - } - $options = []; - foreach ($arguments as $argument) { - if ('--all' === $argument) { - $key = 'all'; - $value = true; - } elseif (1 === preg_match('/\A--(base|head|directory|config)=(.+)\z/sD', $argument, $match)) { - [, $key, $value] = $match; - } else { - throw new \InvalidArgumentException('Unsupported argument. Use help.'); - } - if (array_key_exists($key, $options)) { - throw new \InvalidArgumentException('Duplicate option: ' . $key); - } - $options[$key] = $value; - } - $all = isset($options['all']); - if ($all === (isset($options['base']) || isset($options['head']))) { - throw new \InvalidArgumentException('Select exactly --all or both --base and --head.'); - } - $directory = realpath($options['directory'] ?? getcwd()); - if (false === $directory || !is_dir($directory)) { - throw new \InvalidArgumentException('Invalid project directory.'); - } - $config = $options['config'] ?? dirname(__DIR__, 2) . '/config/ecs.php'; - if (!str_starts_with($config, '/')) { - $config = $directory . '/' . $config; - } - if (!is_file($config)) { - throw new \InvalidArgumentException('Configuration file does not exist.'); - } - $process = new NativeProcessRunner(); - $paths = (new FileSelection($process))->select($directory, $options['base'] ?? null, $options['head'] ?? null, realpath($config)); - $files = (new PhpFiles())->filter($directory, $paths); - printf("Selected %d PHP file(s).\n", count($files)); - - return (new StandardsRunner($process, $this->ecs))->run($directory, $files, $config, 'fix' === $operation); - } catch (\Throwable $exception) { - fwrite(STDERR, $exception->getMessage() . "\n"); + return parent::doRun($input, $output); + } catch (ExceptionInterface $exception) { + $errorOutput = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; + $this->renderThrowable($exception, $errorOutput); - return 2; + return Command::INVALID; } } } diff --git a/src/Console/Command/StandardsCommand.php b/src/Console/Command/StandardsCommand.php new file mode 100644 index 0000000..e7ed2ea --- /dev/null +++ b/src/Console/Command/StandardsCommand.php @@ -0,0 +1,81 @@ +setDescription($this->fix ? 'Explicitly apply ECS style fixes after syntax validation.' : 'Check PHP syntax and style without changing source.') + ->addOption('all', null, InputOption::VALUE_NONE, 'Check all tracked PHP files.') + ->addOption('base', null, InputOption::VALUE_REQUIRED, 'Immutable base commit for changed-file selection.') + ->addOption('head', null, InputOption::VALUE_REQUIRED, 'Immutable head commit matching the checkout.') + ->addOption('directory', 'C', InputOption::VALUE_REQUIRED, 'Project Git checkout root; defaults to the current directory.') + ->addOption('config', null, InputOption::VALUE_REQUIRED, 'Explicit ECS config file; defaults to the packaged preset.'); + } + + /** + * Presents selection and controlled errors while domain services own validation, paths and execution. + * + * @return int Exact tool status, success for no selected files, or INVALID for controlled input/operation errors. + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + try { + $scope = $this->projects->resolve( + $input->getOption('all'), + $input->getOption('base'), + $input->getOption('head'), + $input->getOption('directory'), + $input->getOption('config'), + ); + + $paths = $this->selection->select($scope->directory, $scope->base, $scope->head, $scope->config); + $files = $this->phpFiles->filter($scope->directory, $paths); + $output->writeln(sprintf('Selected %d PHP file(s).', count($files))); + + return $this->standards->run($scope->directory, $files, $scope->config, $this->fix); + } catch (InvalidArgumentException|RuntimeException $exception) { + $errors = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; + (new SymfonyStyle($input, $errors))->error($exception->getMessage()); + + return self::INVALID; + } + } +} diff --git a/src/Process/NativeProcessRunner.php b/src/Process/NativeProcessRunner.php index a1de40a..2090c9c 100644 --- a/src/Process/NativeProcessRunner.php +++ b/src/Process/NativeProcessRunner.php @@ -9,6 +9,11 @@ /** Native transport; callers own selection and operation policy. */ final class NativeProcessRunner implements ProcessRunner { + /** @param \Closure(string,string):void|null $write Explicit output sink; null keeps this library transport silent. */ + public function __construct(private readonly ?\Closure $write = null) + { + } + /** Captures Git output as bytes, including NUL-delimited paths. */ public function capture(array $arguments, string $directory): string { @@ -27,8 +32,6 @@ public function run(array $arguments, string $directory): int 'COMPOSER_AUTH' => false, ], timeout: 300); - return $process->run(static function (string $type, string $output): void { - fwrite(Process::ERR === $type ? STDERR : STDOUT, $output); - }); + return $process->run($this->write); } } diff --git a/src/Project/ProjectScope.php b/src/Project/ProjectScope.php new file mode 100644 index 0000000..2270be1 --- /dev/null +++ b/src/Project/ProjectScope.php @@ -0,0 +1,25 @@ +configuration($root, $config); + + return new ProjectScope($root, $configuration, $base, $head); + } + + /** + * Resolves a selected file relative to the project or uses the injected default. + * + * @throws InvalidArgumentException When the selected configuration cannot be a readable regular file. + */ + private function configuration(string $directory, ?string $config): string + { + $selected = $config ?? $this->defaultConfig; + if ('' === $selected) { + throw new InvalidArgumentException('Configuration path must not be empty.'); + } + + $path = str_starts_with($selected, '/') ? $selected : $directory . '/' . $selected; + $resolved = realpath($path); + if (false === $resolved || !is_file($resolved) || !is_readable($resolved)) { + throw new InvalidArgumentException('Configuration file does not exist or is not readable.'); + } + + return $resolved; + } +} diff --git a/tests/Integration/StandardsTest.php b/tests/Integration/StandardsTest.php index ce2e8f6..5b43b8d 100644 --- a/tests/Integration/StandardsTest.php +++ b/tests/Integration/StandardsTest.php @@ -207,9 +207,13 @@ public function testMalformedArgumentsConfigurationAndHistoryFail(): void ['check', '--all', '--head=' . str_repeat('a', 40)], ['check', '--base=main', '--head=' . str_repeat('a', 40)], ['check', '--base=' . str_repeat('a', 40), '--head=' . str_repeat('a', 40)], - ['check', '--all', '--all'], ['check', '--all', '--fix'], ['check', '--all', '--config=missing.php'], + ['check', '--all', '--directory='], + ['check', '--all', '--config='], + ['check', '--all', '--unknown'], + ['check', '--all', 'unexpected'], + ['check', '--all', '--config'], ['unknown'], ] as $arguments) { self::assertSame(2, $this->cli($arguments)->getExitCode(), json_encode($arguments)); @@ -218,6 +222,41 @@ public function testMalformedArgumentsConfigurationAndHistoryFail(): void $this->put('ecs.php', 'commit(); self::assertNotSame(0, $this->cli(['check', '--all', '--config=ecs.php'])->getExitCode()); + + $this->put('ecs.php', 'cli(['check', '--all', '--config=ecs.php'])->getExitCode()); + } + + /** Native help needs no checkout, and standard option forms share the same checked behavior. */ + public function testNativeConsoleHelpAndSeparateOptionValues(): void + { + $this->put('source.php', "commit(); + $check = $this->cli(['check', '--all', '--directory', $this->directory, '--no-interaction']); + self::assertSame(0, $check->getExitCode(), $check->getErrorOutput()); + self::assertStringContainsString('Selected 1 PHP', $check->getOutput()); + + $help = new Process([PHP_BINARY, dirname(__DIR__, 2) . '/bin/code-standards', 'help', 'check'], sys_get_temp_dir(), timeout: 10); + self::assertSame(0, $help->run()); + self::assertStringContainsString('Usage:', $help->getOutput()); + self::assertStringContainsString('--base', $help->getOutput()); + } + + /** A real native-builder override is applied last without replacing the default preset or source bytes. */ + public function testExplicitPresetOverrideUsesNativeEcsConfiguration(): void + { + $bad = "put('source.php', $bad); + $this->commit(); + self::assertNotSame(0, $this->cli(['check', '--all'])->getExitCode()); + + $autoload = var_export(dirname(__DIR__, 2) . '/vendor/autoload.php', true); + $this->put('local-policy.php', 'withSkip([\\PhpCsFixer\\Fixer\\Operator\\BinaryOperatorSpacesFixer::class]); });'); + $check = $this->cli(['check', '--all', '--config', 'local-policy.php']); + self::assertSame(0, $check->getExitCode(), $check->getOutput() . $check->getErrorOutput()); + self::assertSame($bad, file_get_contents($this->directory . '/source.php')); } /** Writes a synthetic fixture beneath the disposable test root, creating its parents. */ From c252d2409fb3634419cc018de83db1db0b9a3c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 16:08:04 -0300 Subject: [PATCH 07/17] test: lock preset policy and golden preservation contracts (#5) --- .changelog/preset-contract-regressions.md | 7 + AGENTS.md | 1 + docs/policy.md | 5 + docs/resolved-preset.md | 86 +++++++ tests/Config/PresetTest.php | 48 ++++ tests/Fixtures/preservation.after.php.inc | 33 +++ tests/Fixtures/preservation.before.php.inc | 33 +++ tests/Fixtures/preset-rules.json | 276 +++++++++++++++++++++ tests/Fixtures/style.after.php.inc | 12 + tests/Fixtures/style.before.php.inc | 6 + tests/Integration/StandardsTest.php | 34 +++ 11 files changed, 541 insertions(+) create mode 100644 .changelog/preset-contract-regressions.md create mode 100644 docs/resolved-preset.md create mode 100644 tests/Config/PresetTest.php create mode 100644 tests/Fixtures/preservation.after.php.inc create mode 100644 tests/Fixtures/preservation.before.php.inc create mode 100644 tests/Fixtures/preset-rules.json create mode 100644 tests/Fixtures/style.after.php.inc create mode 100644 tests/Fixtures/style.before.php.inc diff --git a/.changelog/preset-contract-regressions.md b/.changelog/preset-contract-regressions.md new file mode 100644 index 0000000..1f75477 --- /dev/null +++ b/.changelog/preset-contract-regressions.md @@ -0,0 +1,7 @@ +--- +category: changed +type: patch +--- + +Verifies the compiled non-risky ECS preset and representative golden style, +PHPDoc preservation and explicit fix idempotence contracts. diff --git a/AGENTS.md b/AGENTS.md index 88037c4..b04ddfa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,6 +61,7 @@ for the exact local base/head before publication; do not claim remote CI evidenc - [README.md](README.md): library/CLI adoption, explicit fix and tool scope. - [Policy comparison](docs/policy.md): evidence, rule differences and extraction gates. +- [Resolved preset](docs/resolved-preset.md): compiled rules, golden contracts and dependency-update review. - [Architecture decision](docs/architecture.md): library/tool dependencies, CPX/autoload limits and bootstrap without circular prerequisites. - [Automation](docs/automation.md): trust, ref/bootstrap contracts and consumer adoption. - [Instruction map](docs/instruction-map.md): single root scope and skill ownership. diff --git a/docs/policy.md b/docs/policy.md index e7aa4b7..3233e4b 100644 --- a/docs/policy.md +++ b/docs/policy.md @@ -36,6 +36,11 @@ ECS 13.3.8 deprecates `withPhpCsFixerSets()` as a no-op. The initial implementat therefore uses the supported prepared PSR-12 set and proves violations with a real fixture instead of inheriting the legacy dynamic-set call blindly. +The [resolved initial preset](resolved-preset.md) records the compiled ECS rule +list and effective options for the locked version. Golden fixtures make style, +documentation/semantic preservation and second-pass stability reviewable. This +is a first bounded part of #5, not equivalence with the legacy engines. + This is not a rule-by-rule equivalence proof for the complete legacy ECS + Rector + PHPDoc stack. No consumers may be automatically migrated on the strength of this comparison. Before migration, expand resolved rule lists, diff --git a/docs/resolved-preset.md b/docs/resolved-preset.md new file mode 100644 index 0000000..fce3612 --- /dev/null +++ b/docs/resolved-preset.md @@ -0,0 +1,86 @@ +# Resolved initial preset + +Snapshot: ECS 13.3.8 from this repository’s Composer lock. The compiled container +contains 48 distinct PHP-CS-Fixer rules, all reporting `isRisky() === false`. +This is the actual Code Standards preset, not a resolved DevTools or CoiSA preset. + +The fixture `tests/Fixtures/preset-rules.json` records the installed ECS version +and exact reference, fully qualified classes, explicit configuration and risky +classification after container boot/skip passes. Unspecified options retain the +fixer's defaults at that exact version; the table does not expand those defaults. +`tests/Config/PresetTest.php` rebuilds that configuration and compares it exactly. +It reads this installation's Composer metadata and verifies every fixer's source +path inside that exact ECS package. This avoids querying the global +`InstalledVersions` registry after the bundled ECS autoloader has registered its +own graph: locally that changed the reported ECS version from `13.3.8` to +`dev-main`. Installation separation alone does not isolate that global registry. +Dependency updates must review any resulting rule/configuration change before +updating the fixture; do not regenerate it merely to silence a failure. + +| Rule | Explicit effective configuration | +| --- | --- | +| `binary_operator_spaces` | `default` | +| `blank_line_after_namespace` | `default` | +| `blank_line_after_opening_tag` | `default` | +| `braces_position` | `{"allow_single_line_empty_anonymous_classes":true}` | +| `class_definition` | `default` | +| `concat_space` | `{"spacing":"one"}` | +| `constant_case` | `default` | +| `control_structure_braces` | `default` | +| `control_structure_continuation_position` | `default` | +| `declare_equal_normalize` | `{"space":"none"}` | +| `declare_parentheses` | `default` | +| `elseif` | `default` | +| `encoding` | `default` | +| `full_opening_tag` | `default` | +| `function_declaration` | `default` | +| `indentation_type` | `default` | +| `line_ending` | `default` | +| `lowercase_cast` | `default` | +| `lowercase_keywords` | `default` | +| `method_argument_space` | `{"on_multiline":"ensure_fully_multiline"}` | +| `new_with_parentheses` | `default` | +| `no_blank_lines_after_class_opening` | `default` | +| `no_break_comment` | `default` | +| `no_closing_tag` | `default` | +| `no_extra_blank_lines` | `{"tokens":["curly_brace_block"]}` | +| `no_leading_import_slash` | `default` | +| `no_multiple_statements_per_line` | `default` | +| `no_singleline_whitespace_before_semicolons` | `default` | +| `no_spaces_after_function_name` | `default` | +| `no_trailing_whitespace` | `default` | +| `no_trailing_whitespace_in_comment` | `default` | +| `no_whitespace_before_comma_in_array` | `default` | +| `ordered_imports` | `{"imports_order":["class","function","const"]}` | +| `phpdoc_align` | `{"align":"left"}` | +| `return_type_declaration` | `default` | +| `short_scalar_cast` | `default` | +| `single_blank_line_at_eof` | `default` | +| `single_class_element_per_statement` | `{"elements":["property"]}` | +| `single_line_after_imports` | `default` | +| `single_space_around_construct` | `default` | +| `spaces_inside_parentheses` | `default` | +| `statement_indentation` | `default` | +| `switch_case_semicolon_to_colon` | `default` | +| `switch_case_space` | `default` | +| `ternary_operator_spaces` | `default` | +| `unary_operator_spaces` | `default` | +| `visibility_required` | `{"elements":["const","method","property"]}` | +| `whitespace_after_comma_in_array` | `default` | + +The six preservation skips and callback-last precedence remain declared in the +preset. Some skipped rules are absent from this initial PSR-12 set; their absence +must not be presented as proof that broader profiles are safe. Native ECS builder +`withSkip()` replaces the builder’s skip list, while configured rules of the same +class use the last configuration. Consumers own their explicit overrides. + +Golden fixtures prove formatting, check-without-writes, exact fixed bytes and +second-pass idempotence. Preservation covers author/package/typed-parameter/ +void-return documentation, property visibility, mutable DateTime construction +and the existing copyright/header position/year. No fixture executes source. + +Remaining #5 work: resolve DevTools ECS/Rector/PHPDoc and CoiSA against explicit +compatible dependency graphs, compare all rules and header/Finder behavior, and +decide separately authorized opt-in profiles. The CoiSA source snapshot does not +track a Composer lock; an ignored local lock is not a reproducible published +dependency graph. This initial evidence cannot authorize consumer migration. diff --git a/tests/Config/PresetTest.php b/tests/Config/PresetTest.php new file mode 100644 index 0000000..3bc8a24 --- /dev/null +++ b/tests/Config/PresetTest.php @@ -0,0 +1,48 @@ + 'symplify/easy-coding-standard' === $entry['name']))[0]; + + require_once $packageRoot . '/vendor/squizlabs/php_codesniffer/autoload.php'; + + $container = (new ServiceContainerFactory())->create([dirname(__DIR__, 2) . '/config/ecs.php']); + $container->boot(); + $configuration = $container->getCheckerConfiguration(); + $rules = []; + + foreach ($container->findByContract(FixerInterface::class) as $fixer) { + $rules[$fixer->getName()] = [ + 'class' => $fixer::class, + 'configuration' => $configuration[$fixer::class], + 'risky' => $fixer->isRisky(), + ]; + self::assertFalse($fixer->isRisky(), $fixer->getName() . ' must not become implicitly risky.'); + self::assertStringStartsWith($packageRoot . '/', (new \ReflectionClass($fixer))->getFileName()); + } + + ksort($rules); + $expected = json_decode(file_get_contents(__DIR__ . '/../Fixtures/preset-rules.json'), true, flags: JSON_THROW_ON_ERROR); + self::assertSame($expected, [ + 'ecs_version' => $package['version'], + 'ecs_reference' => $package['source']['reference'], + 'rules' => $rules, + ]); + self::assertCount(count($container->getCheckerClasses()), $rules, 'Every active checker must appear in this snapshot.'); + } +} diff --git a/tests/Fixtures/preservation.after.php.inc b/tests/Fixtures/preservation.after.php.inc new file mode 100644 index 0000000..f4a6661 --- /dev/null +++ b/tests/Fixtures/preservation.after.php.inc @@ -0,0 +1,33 @@ +directory . '/source.php')); } + /** Golden files expose style drift, preserved public documentation and explicit second-pass stability. */ + public function testGoldenPresetIsReadOnlyUntilFixAndPreservesContracts(): void + { + $fixtures = dirname(__DIR__) . '/Fixtures/'; + $original = []; + + foreach (['style', 'preservation'] as $name) { + $original[$name] = file_get_contents($fixtures . $name . '.before.php.inc'); + $this->put($name . '.php', $original[$name]); + } + + $this->commit(); + self::assertNotSame(0, $this->cli(['check', '--all'])->getExitCode()); + foreach ($original as $name => $bytes) { + self::assertSame($bytes, file_get_contents($this->directory . '/' . $name . '.php')); + } + + $fixed = $this->cli(['fix', '--all']); + self::assertSame(0, $fixed->getExitCode(), $fixed->getOutput() . $fixed->getErrorOutput()); + foreach (array_keys($original) as $name) { + self::assertSame( + file_get_contents($fixtures . $name . '.after.php.inc'), + file_get_contents($this->directory . '/' . $name . '.php'), + $name, + ); + } + + self::assertSame(0, $this->cli(['check', '--all'])->getExitCode()); + self::assertSame(0, $this->cli(['fix', '--all'])->getExitCode()); + foreach (array_keys($original) as $name) { + self::assertSame(file_get_contents($fixtures . $name . '.after.php.inc'), file_get_contents($this->directory . '/' . $name . '.php')); + } + } + /** Writes a synthetic fixture beneath the disposable test root, creating its parents. */ private function put(string $path, string $content): void { From b5d963b8efc0cf941744041136b99ece6e502023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 16:14:30 -0300 Subject: [PATCH 08/17] refactor: expose distinct attributed invocable check and fix commands --- bin/code-standards | 8 ++- docs/architecture.md | 13 +++- src/Config/ECSConfig.php | 2 + src/Console/Application.php | 9 +-- src/Console/Command/CheckCommand.php | 34 ++++++++++ src/Console/Command/FixCommand.php | 34 ++++++++++ src/Console/Command/StandardsCommand.php | 81 ------------------------ src/Console/Input/ScanInput.php | 26 ++++++++ src/Console/StandardsOperation.php | 52 +++++++++++++++ tests/Integration/StandardsTest.php | 39 ++++++++++++ 10 files changed, 208 insertions(+), 90 deletions(-) create mode 100644 src/Console/Command/CheckCommand.php create mode 100644 src/Console/Command/FixCommand.php delete mode 100644 src/Console/Command/StandardsCommand.php create mode 100644 src/Console/Input/ScanInput.php create mode 100644 src/Console/StandardsOperation.php diff --git a/bin/code-standards b/bin/code-standards index 4e5bfa8..1baaa3c 100644 --- a/bin/code-standards +++ b/bin/code-standards @@ -4,7 +4,9 @@ declare(strict_types=1); use FastForward\CodeStandards\Console\Application; -use FastForward\CodeStandards\Console\Command\StandardsCommand; +use FastForward\CodeStandards\Console\Command\CheckCommand; +use FastForward\CodeStandards\Console\Command\FixCommand; +use FastForward\CodeStandards\Console\StandardsOperation; use FastForward\CodeStandards\Filesystem\PhpFiles; use FastForward\CodeStandards\Git\FileSelection; use FastForward\CodeStandards\Process\NativeProcessRunner; @@ -26,6 +28,6 @@ $projects = new ProjectScopeResolver(dirname(__DIR__) . '/config/ecs.php'); $selection = new FileSelection($process); $files = new PhpFiles(); $standards = new StandardsRunner($process, $vendor . '/symplify/easy-coding-standard/bin/ecs'); -$command = static fn (bool $fix): StandardsCommand => new StandardsCommand($fix, $projects, $selection, $files, $standards); +$operation = new StandardsOperation($projects, $selection, $files, $standards); -exit((new Application($command(false), $command(true)))->run(output: $output)); +exit((new Application(new CheckCommand($operation), new FixCommand($operation)))->run(output: $output)); diff --git a/docs/architecture.md b/docs/architecture.md index 1f13d2a..383c36a 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -58,8 +58,10 @@ No automatic PHP migrations derive authority from the host tool's PHP version. Changelog's console separates commands, input definitions and injected services. Code Standards follows that separation with Symfony Console 7.4-compatible APIs, matching this project's PHP 8.3 minimum. Symfony owns argument binding, help and -global options. Two inexpensive commands are composed directly in the binary; -there is no custom parser, service container or speculative lazy-command layer. +global options. Commands are plain invocable PHP objects with `AsCommand` and +mapped `Option` attributes, adapted by native `Application::addCommand()`; they +do not extend Symfony Command. These APIs are present in published Console7.4. +There is no custom parser or speculative lazy-command layer. Project-scope validation, Git selection, PHP filtering and tool execution have separate responsibilities. The process transport receives its output sink; reusable services do not write to global standard-output handles. @@ -93,6 +95,13 @@ Separate logical blocks with whitespace and add PHPDoc that explains contracts, failure conditions and meaningful collection/callback types. Avoid comments that merely repeat syntax. Regression tests assert observable behavior, errors, exit statuses and source preservation rather than private implementation shape. +Follow Changelog's attribute layout: multiline command metadata, an attribute +immediately above its target, and a blank line between parameter groups with +attributes and the preceding group. Keep logical blocks visibly separated. +The formatter regression verifies both check and fix retain that spacing. +The initial preset overrides `method_argument_space.on_multiline` to `ignore`: +the upstream `ensure_fully_multiline` mode collapses these deliberate blank +lines. Standard comma spacing still applies; no custom fixer is introduced. ## Consequences and next gates diff --git a/src/Config/ECSConfig.php b/src/Config/ECSConfig.php index 6be6150..b4c0f80 100644 --- a/src/Config/ECSConfig.php +++ b/src/Config/ECSConfig.php @@ -4,6 +4,7 @@ namespace FastForward\CodeStandards\Config; +use PhpCsFixer\Fixer\FunctionNotation\MethodArgumentSpaceFixer; use PhpCsFixer\Fixer\Import\GlobalNamespaceImportFixer; use PhpCsFixer\Fixer\Phpdoc\GeneralPhpdocAnnotationRemoveFixer; use PhpCsFixer\Fixer\Phpdoc\NoEmptyPhpdocFixer; @@ -34,6 +35,7 @@ public static function configure(?callable $customize = null): ECSConfigBuilder GlobalNamespaceImportFixer::class, GeneralPhpdocAnnotationRemoveFixer::class, ]) + ->withConfiguredRule(MethodArgumentSpaceFixer::class, ['on_multiline' => 'ignore']) ->withConfiguredRule(PhpdocAlignFixer::class, ['align' => 'left']) ->withoutParallel(); if (null !== $customize) { diff --git a/src/Console/Application.php b/src/Console/Application.php index cb036c4..031fef0 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -4,7 +4,8 @@ namespace FastForward\CodeStandards\Console; -use FastForward\CodeStandards\Console\Command\StandardsCommand; +use FastForward\CodeStandards\Console\Command\CheckCommand; +use FastForward\CodeStandards\Console\Command\FixCommand; use Symfony\Component\Console\Application as SymfonyApplication; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Exception\ExceptionInterface; @@ -16,12 +17,12 @@ final class Application extends SymfonyApplication { /** Registers the two explicitly composed operations without resolving project state or executing tools. */ - public function __construct(StandardsCommand $check, StandardsCommand $fix) + public function __construct(CheckCommand $check, FixCommand $fix) { parent::__construct('Fast Forward Code Standards'); - $this->add($check); - $this->add($fix); + $this->addCommand($check); + $this->addCommand($fix); $this->setAutoExit(false); } diff --git a/src/Console/Command/CheckCommand.php b/src/Console/Command/CheckCommand.php new file mode 100644 index 0000000..0962aa7 --- /dev/null +++ b/src/Console/Command/CheckCommand.php @@ -0,0 +1,34 @@ +operation->run($settings, $io, false); + } +} diff --git a/src/Console/Command/FixCommand.php b/src/Console/Command/FixCommand.php new file mode 100644 index 0000000..efeea1c --- /dev/null +++ b/src/Console/Command/FixCommand.php @@ -0,0 +1,34 @@ +operation->run($settings, $io, true); + } +} diff --git a/src/Console/Command/StandardsCommand.php b/src/Console/Command/StandardsCommand.php deleted file mode 100644 index e7ed2ea..0000000 --- a/src/Console/Command/StandardsCommand.php +++ /dev/null @@ -1,81 +0,0 @@ -setDescription($this->fix ? 'Explicitly apply ECS style fixes after syntax validation.' : 'Check PHP syntax and style without changing source.') - ->addOption('all', null, InputOption::VALUE_NONE, 'Check all tracked PHP files.') - ->addOption('base', null, InputOption::VALUE_REQUIRED, 'Immutable base commit for changed-file selection.') - ->addOption('head', null, InputOption::VALUE_REQUIRED, 'Immutable head commit matching the checkout.') - ->addOption('directory', 'C', InputOption::VALUE_REQUIRED, 'Project Git checkout root; defaults to the current directory.') - ->addOption('config', null, InputOption::VALUE_REQUIRED, 'Explicit ECS config file; defaults to the packaged preset.'); - } - - /** - * Presents selection and controlled errors while domain services own validation, paths and execution. - * - * @return int Exact tool status, success for no selected files, or INVALID for controlled input/operation errors. - */ - protected function execute(InputInterface $input, OutputInterface $output): int - { - try { - $scope = $this->projects->resolve( - $input->getOption('all'), - $input->getOption('base'), - $input->getOption('head'), - $input->getOption('directory'), - $input->getOption('config'), - ); - - $paths = $this->selection->select($scope->directory, $scope->base, $scope->head, $scope->config); - $files = $this->phpFiles->filter($scope->directory, $paths); - $output->writeln(sprintf('Selected %d PHP file(s).', count($files))); - - return $this->standards->run($scope->directory, $files, $scope->config, $this->fix); - } catch (InvalidArgumentException|RuntimeException $exception) { - $errors = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; - (new SymfonyStyle($input, $errors))->error($exception->getMessage()); - - return self::INVALID; - } - } -} diff --git a/src/Console/Input/ScanInput.php b/src/Console/Input/ScanInput.php new file mode 100644 index 0000000..dbdce82 --- /dev/null +++ b/src/Console/Input/ScanInput.php @@ -0,0 +1,26 @@ +projects->resolve($settings->all, $settings->base, $settings->head, $settings->directory, $settings->config); + $paths = $this->selection->select($scope->directory, $scope->base, $scope->head, $scope->config); + $files = $this->phpFiles->filter($scope->directory, $paths); + $io->writeln(sprintf('Selected %d PHP file(s).', count($files))); + + return $this->standards->run($scope->directory, $files, $scope->config, $fix); + } catch (InvalidArgumentException|RuntimeException $exception) { + $io->getErrorStyle()->error($exception->getMessage()); + + return Command::INVALID; + } + } +} diff --git a/tests/Integration/StandardsTest.php b/tests/Integration/StandardsTest.php index 5b43b8d..09f4e13 100644 --- a/tests/Integration/StandardsTest.php +++ b/tests/Integration/StandardsTest.php @@ -259,6 +259,45 @@ public function testExplicitPresetOverrideUsesNativeEcsConfiguration(): void self::assertSame($bad, file_get_contents($this->directory . '/source.php')); } + /** Both formatter modes retain the owner's spacing between attributed command parameter groups. */ + public function testFormatterPreservesAttributedParameterSpacing(): void + { + $source = <<<'PHP' +put('example.php', $source); + $this->commit(); + + $check = $this->cli(['check', '--all']); + self::assertSame(0, $check->getExitCode(), $check->getOutput() . $check->getErrorOutput()); + self::assertSame($source, file_get_contents($this->directory . '/example.php')); + self::assertSame(0, $this->cli(['fix', '--all'])->getExitCode()); + self::assertSame($source, file_get_contents($this->directory . '/example.php')); + } + /** Writes a synthetic fixture beneath the disposable test root, creating its parents. */ private function put(string $path, string $content): void { From bcf538756c1c9da73e27148346d6f7c0dfaa97b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 16:19:28 -0300 Subject: [PATCH 09/17] feat: autowire tooling through published Fast Forward Container (#14) --- .changelog/framework-composition.md | 7 + bin/code-standards | 24 +- composer.json | 1 + composer.lock | 583 ++++++++++++++++-- docs/architecture.md | 31 + .../StandardsServiceProvider.php | 64 ++ tests/DependencyInjection/CompositionTest.php | 125 ++++ 7 files changed, 757 insertions(+), 78 deletions(-) create mode 100644 .changelog/framework-composition.md create mode 100644 src/DependencyInjection/StandardsServiceProvider.php create mode 100644 tests/DependencyInjection/CompositionTest.php diff --git a/.changelog/framework-composition.md b/.changelog/framework-composition.md new file mode 100644 index 0000000..d188797 --- /dev/null +++ b/.changelog/framework-composition.md @@ -0,0 +1,7 @@ +--- +category: changed +type: minor +--- + +Composes the attributed invocable commands through the published Fast Forward +Container autowiring API, with focused provider bindings and explicit overrides. diff --git a/bin/code-standards b/bin/code-standards index 1baaa3c..aa90dc1 100644 --- a/bin/code-standards +++ b/bin/code-standards @@ -4,30 +4,14 @@ declare(strict_types=1); use FastForward\CodeStandards\Console\Application; -use FastForward\CodeStandards\Console\Command\CheckCommand; -use FastForward\CodeStandards\Console\Command\FixCommand; -use FastForward\CodeStandards\Console\StandardsOperation; -use FastForward\CodeStandards\Filesystem\PhpFiles; -use FastForward\CodeStandards\Git\FileSelection; -use FastForward\CodeStandards\Process\NativeProcessRunner; -use FastForward\CodeStandards\Project\ProjectScopeResolver; -use FastForward\CodeStandards\Standards\StandardsRunner; +use FastForward\CodeStandards\DependencyInjection\StandardsServiceProvider; +use FastForward\Container\ContainerFactory; use Symfony\Component\Console\Output\ConsoleOutput; -use Symfony\Component\Process\Process; $autoload = $_composer_autoload_path ?? __DIR__ . '/../vendor/autoload.php'; require $autoload; -$vendor = dirname($autoload); $output = new ConsoleOutput(); -$process = new NativeProcessRunner(static function (string $type, string $message) use ($output): void { - $stream = Process::ERR === $type ? $output->getErrorOutput() : $output; - $stream->write($message); -}); -$projects = new ProjectScopeResolver(dirname(__DIR__) . '/config/ecs.php'); -$selection = new FileSelection($process); -$files = new PhpFiles(); -$standards = new StandardsRunner($process, $vendor . '/symplify/easy-coding-standard/bin/ecs'); -$operation = new StandardsOperation($projects, $selection, $files, $standards); +$container = ContainerFactory::create(new StandardsServiceProvider(dirname($autoload), $output)); -exit((new Application(new CheckCommand($operation), new FixCommand($operation)))->run(output: $output)); +exit($container->get(Application::class)->run(output: $output)); diff --git a/composer.json b/composer.json index 58711ac..6d7f276 100644 --- a/composer.json +++ b/composer.json @@ -5,6 +5,7 @@ "license": "MIT", "require": { "php": "^8.3", + "fast-forward/container": "~1.10.1", "symfony/console": "^7.4 || ^8.0", "symfony/process": "^7.4 || ^8.0", "symplify/easy-coding-standard": "^13.0" diff --git a/composer.lock b/composer.lock index f42db87..084cd1f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,422 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dad04c1e4b1938500b5b659cd9bd9c29", + "content-hash": "53fa33641f1aadb904734255031fbd70", "packages": [ + { + "name": "brick/varexporter", + "version": "0.7.0", + "source": { + "type": "git", + "url": "https://github.com/brick/varexporter.git", + "reference": "b3a50b8f630a9ed5015ea3e1f00479af261ed80d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/varexporter/zipball/b3a50b8f630a9ed5015ea3e1f00479af261ed80d", + "reference": "b3a50b8f630a9ed5015ea3e1f00479af261ed80d", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": "^8.2" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^11.0", + "vimeo/psalm": "6.14.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\VarExporter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A powerful alternative to var_export(), which can export closures and objects without __set_state()", + "keywords": [ + "var_export" + ], + "support": { + "issues": "https://github.com/brick/varexporter/issues", + "source": "https://github.com/brick/varexporter/tree/0.7.0" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2026-01-06T22:56:00+00:00" + }, + { + "name": "container-interop/service-provider", + "version": "v0.4.1", + "source": { + "type": "git", + "url": "https://github.com/container-interop/service-provider.git", + "reference": "e04441ca21ef03e10dce70b0af29269281eec6dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/container-interop/service-provider/zipball/e04441ca21ef03e10dce70b0af29269281eec6dc", + "reference": "e04441ca21ef03e10dce70b0af29269281eec6dc", + "shasum": "" + }, + "require": { + "psr/container": "^1.0 || ^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Interop\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Promoting container interoperability through standard service providers", + "homepage": "https://github.com/container-interop/service-provider", + "support": { + "issues": "https://github.com/container-interop/service-provider/issues", + "source": "https://github.com/container-interop/service-provider/tree/v0.4.1" + }, + "time": "2023-12-14T14:50:12+00:00" + }, + { + "name": "fast-forward/config", + "version": "v1.7.3", + "source": { + "type": "git", + "url": "https://github.com/php-fast-forward/config.git", + "reference": "2b5ade2cc6a7141c0e7906a555978b9bfafa1edd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fast-forward/config/zipball/2b5ade2cc6a7141c0e7906a555978b9bfafa1edd", + "reference": "2b5ade2cc6a7141c0e7906a555978b9bfafa1edd", + "shasum": "" + }, + "require": { + "brick/varexporter": "^0.5 || ^0.6 || ^0.7", + "laminas/laminas-config-aggregator": "^1.13", + "php": "^8.3", + "psr/container": "^1.0 || ^2.0", + "psr/simple-cache": "^3.0" + }, + "require-dev": { + "fast-forward/dev-tools": "dev-main", + "symfony/cache": "^7.4" + }, + "suggest": { + "llm/skills": "Optionally synchronize the bundled agent skill in consumer projects." + }, + "type": "library", + "extra": { + "skills": { + "source": "resources/skills" + }, + "grumphp": { + "config-default-path": "vendor/fast-forward/dev-tools/grumphp.yml" + }, + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "FastForward\\Config\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Felipe Sayão Lobato Abreu", + "email": "github@mentordosnerds.com" + } + ], + "description": "Fast Forward Config utility classes", + "homepage": "https://php-fast-forward.github.io/config/", + "support": { + "issues": "https://github.com/php-fast-forward/config/issues", + "source": "https://github.com/php-fast-forward/config" + }, + "funding": [ + { + "url": "https://www.paypal.com/donate/?business=JLDAF45XZ8D84", + "type": "custom" + }, + { + "url": "https://github.com/sponsors/php-fast-forward", + "type": "github" + } + ], + "time": "2026-10-20T15:00:07+00:00" + }, + { + "name": "fast-forward/container", + "version": "v1.10.1", + "source": { + "type": "git", + "url": "https://github.com/php-fast-forward/container.git", + "reference": "a75dda2c8a9bd9cb2ef5bb25abc1a6b09de8a298" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fast-forward/container/zipball/a75dda2c8a9bd9cb2ef5bb25abc1a6b09de8a298", + "reference": "a75dda2c8a9bd9cb2ef5bb25abc1a6b09de8a298", + "shasum": "" + }, + "require": { + "container-interop/service-provider": "^0.4.1", + "fast-forward/config": "^1.6", + "php": "^8.3", + "psr/container": "^1.1 || ^2.0" + }, + "require-dev": { + "fast-forward/dev-tools": "dev-main" + }, + "suggest": { + "llm/skills": "Automatically synchronize the bundled agent skill in consumer projects." + }, + "type": "library", + "extra": { + "skills": { + "source": "resources/skills" + }, + "grumphp": { + "config-default-path": "vendor/fast-forward/dev-tools/grumphp.yml" + }, + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "FastForward\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Felipe Sayão Lobato Abreu", + "email": "github@mentordosnerds.com" + } + ], + "description": "Fast Forward PSR-11 Container utility classes", + "homepage": "https://php-fast-forward.github.io/container/", + "support": { + "issues": "https://github.com/php-fast-forward/container/issues", + "source": "https://github.com/php-fast-forward/container" + }, + "funding": [ + { + "url": "https://github.com/php-fast-forward", + "type": "github" + } + ], + "time": "2026-10-18T08:00:17+00:00" + }, + { + "name": "laminas/laminas-config-aggregator", + "version": "1.20.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-config-aggregator.git", + "reference": "75331b17a9ba72c5fa8679988ef08a588ddd86af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-config-aggregator/zipball/75331b17a9ba72c5fa8679988ef08a588ddd86af", + "reference": "75331b17a9ba72c5fa8679988ef08a588ddd86af", + "shasum": "" + }, + "require": { + "brick/varexporter": "^0.5.0 || ^0.4.0 || ^0.6.0 || ^0.7.0", + "laminas/laminas-stdlib": "^3.18.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "webimpress/safe-writer": "^2.2.0" + }, + "conflict": { + "nikic/php-parser": "<4.12", + "zendframework/zend-config-aggregator": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.1.0", + "laminas/laminas-config": "^3.10.1", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "laminas/laminas-config": "Allows loading configuration from XML, INI, YAML, and JSON files", + "laminas/laminas-config-aggregator-modulemanager": "Allows loading configuration from laminas-mvc Module classes", + "laminas/laminas-config-aggregator-parameters": "Allows usage of templated parameters within your configuration" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\ConfigAggregator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Lightweight library for collecting and merging configuration from different sources", + "homepage": "https://laminas.dev", + "keywords": [ + "config-aggregator", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-config-aggregator/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-config-aggregator/issues", + "rss": "https://github.com/laminas/laminas-config-aggregator/releases.atom", + "source": "https://github.com/laminas/laminas-config-aggregator" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2026-06-01T15:37:55+00:00" + }, + { + "name": "laminas/laminas-stdlib", + "version": "3.21.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-stdlib.git", + "reference": "b1c81514cfe158aadf724c42b34d3d0a8164c096" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/b1c81514cfe158aadf724c42b34d3d0a8164c096", + "reference": "b1c81514cfe158aadf724c42b34d3d0a8164c096", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "zendframework/zend-stdlib": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "^3.1.0", + "phpbench/phpbench": "^1.4.1", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Stdlib\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "SPL extensions, array utilities, error handlers, and more", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "stdlib" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-stdlib/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-stdlib/issues", + "rss": "https://github.com/laminas/laminas-stdlib/releases.atom", + "source": "https://github.com/laminas/laminas-stdlib" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-11T18:13:12+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.9.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "9e33da9553fe7786f0962b35f4e4ecf01be89def" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/9e33da9553fe7786f0962b35f4e4ecf01be89def", + "reference": "9e33da9553fe7786f0962b35f4e4ecf01be89def", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.9.0" + }, + "time": "2026-09-13T18:51:52+00:00" + }, { "name": "psr/container", "version": "2.0.2", @@ -59,6 +473,57 @@ }, "time": "2021-11-05T16:47:00+00:00" }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, { "name": "symfony/console", "version": "v7.4.21", @@ -853,6 +1318,65 @@ "source": "https://github.com/ecsphp/ecs/tree/13.3.8" }, "time": "2026-10-20T21:02:34+00:00" + }, + { + "name": "webimpress/safe-writer", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/webimpress/safe-writer.git", + "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webimpress/safe-writer/zipball/9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", + "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.4", + "vimeo/psalm": "^4.7", + "webimpress/coding-standard": "^1.2.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev", + "dev-develop": "2.3.x-dev", + "dev-release-1.0": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Webimpress\\SafeWriter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "description": "Tool to write files safely, to avoid race conditions", + "keywords": [ + "concurrent write", + "file writer", + "race condition", + "safe writer", + "webimpress" + ], + "support": { + "issues": "https://github.com/webimpress/safe-writer/issues", + "source": "https://github.com/webimpress/safe-writer/tree/2.2.0" + }, + "funding": [ + { + "url": "https://github.com/michalbundyra", + "type": "github" + } + ], + "time": "2021-04-19T16:34:45+00:00" } ], "packages-dev": [ @@ -916,63 +1440,6 @@ ], "time": "2026-08-11T10:17:44+00:00" }, - { - "name": "nikic/php-parser", - "version": "v5.9.0", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "9e33da9553fe7786f0962b35f4e4ecf01be89def" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/9e33da9553fe7786f0962b35f4e4ecf01be89def", - "reference": "9e33da9553fe7786f0962b35f4e4ecf01be89def", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-tokenizer": "*", - "php": ">=7.4" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.9.0" - }, - "time": "2026-09-13T18:51:52+00:00" - }, { "name": "phar-io/manifest", "version": "2.0.4", diff --git a/docs/architecture.md b/docs/architecture.md index 383c36a..7173ffd 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -62,6 +62,12 @@ global options. Commands are plain invocable PHP objects with `AsCommand` and mapped `Option` attributes, adapted by native `Application::addCommand()`; they do not extend Symfony Command. These APIs are present in published Console7.4. There is no custom parser or speculative lazy-command layer. +Fast Forward Container autowires the concrete application, commands, scan +orchestration, file selector and PHP filter. The module's service provider binds +only the process/output interfaces and services needing installation/config +paths. The binary supplies the actual vendor directory and terminal output; +there are no hidden construction defaults or service lookups inside domain +services. The container is used only at this composition boundary. Project-scope validation, Git selection, PHP filtering and tool execution have separate responsibilities. The process transport receives its output sink; reusable services do not write to global standard-output handles. @@ -103,6 +109,31 @@ The initial preset overrides `method_argument_space.on_multiline` to `ignore`: the upstream `ensure_fully_multiline` mode collapses these deliberate blank lines. Standard comma spacing still applies; no custom fixer is introduced. +## Published framework composition + +This increment consumes `fast-forward/container` 1.10.1 (release commit +`a75dda2c8a9bd9cb2ef5bb25abc1a6b09de8a298`), `fast-forward/config` 1.7.3 and +`container-interop/service-provider` 0.4.1 through the actual Composer graph. +These are runtime library dependencies of the tool. Their development tools +are not transitively installed. No unpublished branch/PR or future Container +PR49 behavior is required. The provider API is experimental0.4 and distinct +from PSR-11's lookup contract; inspect compatibility when updating dependencies. + +Use the published `ContainerFactory::create()` for composition. Consumers needing +explicit bindings may pass one native `AggregateServiceProvider` containing the +module provider first and their `ArrayServiceProvider` last. Later factories +replace earlier factories before services are resolved. Keep replacements typed +to the declared interface and apply them before first lookup. Tests exercise +lazy construction, shared application identity, nested transport/output +overrides, native command binding and exact failure status with the published +adapter. No module-specific container, provider registry or override DSL exists. + +The default provider defines no extensions. This tool does not claim additional +cross-provider decoration or alias guarantees beyond the published adapter. +CPX installation isolation does not alter these in-process runtime/autoload +boundaries. Command configuration does not double as dependency injection: +check/fix are separate invocable entrypoints and transport authority is explicit. + ## Consequences and next gates This small slice validates syntax and PSR-12 style. Type analysis and semantic diff --git a/src/DependencyInjection/StandardsServiceProvider.php b/src/DependencyInjection/StandardsServiceProvider.php new file mode 100644 index 0000000..4496e98 --- /dev/null +++ b/src/DependencyInjection/StandardsServiceProvider.php @@ -0,0 +1,64 @@ + Lazy bindings; later aggregate-provider factories may replace them. */ + public function getFactories(): array + { + return [ + OutputInterface::class => fn (): OutputInterface => $this->output, + ProcessRunner::class => fn (ContainerInterface $container): ProcessRunner => $this->process($container->get(OutputInterface::class)), + ProjectScopeResolver::class => static fn (): ProjectScopeResolver => new ProjectScopeResolver(dirname(__DIR__, 2) . '/config/ecs.php'), + StandardsRunner::class => fn (ContainerInterface $container): StandardsRunner => new StandardsRunner( + $container->get(ProcessRunner::class), + $this->vendorDirectory . '/symplify/easy-coding-standard/bin/ecs', + ), + ]; + } + + /** @return array No default decorators; consumer extensions use the published provider contract. */ + public function getExtensions(): array + { + return []; + } + + /** Binds raw subprocess bytes to the supplied output while retaining stderr when supported. */ + private function process(OutputInterface $output): ProcessRunner + { + return new NativeProcessRunner(static function (string $type, string $message) use ($output): void { + $stream = Process::ERR === $type && $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; + $stream->write($message, options: OutputInterface::OUTPUT_RAW); + }); + } +} diff --git a/tests/DependencyInjection/CompositionTest.php b/tests/DependencyInjection/CompositionTest.php new file mode 100644 index 0000000..d46a2c3 --- /dev/null +++ b/tests/DependencyInjection/CompositionTest.php @@ -0,0 +1,125 @@ + false, 'fix' => true] as $name => $write) { + $process = new RecordingTransport(); + $constructions = 0; + $override = new ArrayServiceProvider([ + ProcessRunner::class => static function () use ($process, &$constructions): ProcessRunner { + ++$constructions; + + return $process; + }, + ]); + $container = ContainerFactory::create(new AggregateServiceProvider( + new StandardsServiceProvider(dirname(__DIR__, 2) . '/vendor', new BufferedOutput()), + $override, + )); + self::assertTrue($container->has(Application::class)); + self::assertSame(0, $constructions); + + $application = $container->get(Application::class); + self::assertSame($application, $container->get(Application::class)); + self::assertSame(1, $constructions); + self::assertInstanceOf(CheckCommand::class, $container->get(CheckCommand::class)); + self::assertInstanceOf(FixCommand::class, $container->get(FixCommand::class)); + self::assertFalse(is_subclass_of(CheckCommand::class, Command::class)); + self::assertFalse(is_subclass_of(FixCommand::class, Command::class)); + + $tester = new ApplicationTester($application); + self::assertSame(23, $tester->run(['command' => $name, '--all' => true, '--directory' => dirname(__DIR__, 2)])); + self::assertSame($write, in_array('--fix', $process->commands[array_key_last($process->commands)], true)); + self::assertStringContainsString('Selected ', $tester->getDisplay()); + } + } + + /** A later output binding is used by the real native transport without interpreting subprocess markup. */ + public function testOutputOverridePreservesRawProcessBytes(): void + { + $default = new BufferedOutput(); + $replacement = new BufferedOutput(); + $container = ContainerFactory::create(new AggregateServiceProvider( + new StandardsServiceProvider(dirname(__DIR__, 2) . '/vendor', $default), + new ArrayServiceProvider([OutputInterface::class => static fn (): OutputInterface => $replacement]), + )); + + $status = $container->get(ProcessRunner::class)->run([PHP_BINARY, '-r', 'echo "literal";'], sys_get_temp_dir()); + self::assertSame(0, $status); + self::assertSame('', $default->fetch()); + self::assertSame('literal', $replacement->fetch()); + } + + /** A failed explicit dependency remains a construction error with its original cause. */ + public function testFailedBindingPreservesItsCause(): void + { + $cause = new \RuntimeException('Synthetic transport construction failure.'); + $container = ContainerFactory::create(new AggregateServiceProvider( + new StandardsServiceProvider(dirname(__DIR__, 2) . '/vendor', new BufferedOutput()), + new ArrayServiceProvider([ProcessRunner::class => static fn (): ProcessRunner => throw $cause]), + )); + + try { + $container->get(Application::class); + self::fail('Failed binding must not be replaced by an implicit default.'); + } catch (\Psr\Container\ContainerExceptionInterface $failure) { + while (null !== $failure->getPrevious()) { + $failure = $failure->getPrevious(); + } + + self::assertSame($cause, $failure); + } + } + + /** Invalid installation paths fail before building any service graph. */ + public function testInvalidVendorDirectoryHasAnObjectiveError(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('readable installed ECS entrypoint'); + new StandardsServiceProvider('', new BufferedOutput()); + } +} + +/** Captures tool authority/status while using real read-only Git selection in this checkout. */ +final class RecordingTransport implements ProcessRunner +{ + /** @var list> Tool arguments observed without executing syntax/style processes. */ + public array $commands = []; + + /** Reads only Git metadata through the production shell-free transport. */ + public function capture(array $arguments, string $directory): string + { + return (new NativeProcessRunner())->capture($arguments, $directory); + } + + /** Returns a synthetic style failure to verify end-to-end exit-code propagation. */ + public function run(array $arguments, string $directory): int + { + $this->commands[] = $arguments; + + return in_array('-l', $arguments, true) ? 0 : 23; + } +} From 9666d997c864e4e74b7ca0ca8cbce849091353a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 16:26:07 -0300 Subject: [PATCH 10/17] fix: load symlinked presets through ECS installation autoload --- .changelog/symlinked-preset-autoload.md | 7 +++++++ config/ecs.php | 6 +----- tests/Integration/StandardsTest.php | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 .changelog/symlinked-preset-autoload.md diff --git a/.changelog/symlinked-preset-autoload.md b/.changelog/symlinked-preset-autoload.md new file mode 100644 index 0000000..14c18ea --- /dev/null +++ b/.changelog/symlinked-preset-autoload.md @@ -0,0 +1,7 @@ +--- +category: fixed +type: patch +--- + +Loads the default ECS preset through symlinked Composer path installations +without requiring a separate vendor directory beside the source checkout. diff --git a/config/ecs.php b/config/ecs.php index 7665f68..8f458eb 100644 --- a/config/ecs.php +++ b/config/ecs.php @@ -4,9 +4,5 @@ use FastForward\CodeStandards\Config\ECSConfig; -$autoload = is_file(__DIR__ . '/../vendor/autoload.php') - ? __DIR__ . '/../vendor/autoload.php' - : __DIR__ . '/../../../autoload.php'; -require_once $autoload; - +// ECS bootstraps its actual installation autoloader before loading this preset. return ECSConfig::configure(); diff --git a/tests/Integration/StandardsTest.php b/tests/Integration/StandardsTest.php index 09f4e13..eb981cd 100644 --- a/tests/Integration/StandardsTest.php +++ b/tests/Integration/StandardsTest.php @@ -298,6 +298,23 @@ public function __invoke( self::assertSame($source, file_get_contents($this->directory . '/example.php')); } + /** ECS already loads the installation autoloader; a symlinked preset needs no vendor beside its real source. */ + public function testPresetCanLoadThroughSymlinkWithoutAnAdjacentVendor(): void + { + $this->put('source.php', "put('package-source/config/ecs.php', file_get_contents(dirname(__DIR__, 2) . '/config/ecs.php')); + mkdir($this->directory . '/linked'); + symlink('../package-source', $this->directory . '/linked/package'); + $this->commit(); + + foreach (['check', 'fix'] as $command) { + $result = $this->cli([$command, '--all', '--config=linked/package/config/ecs.php']); + self::assertSame(0, $result->getExitCode(), $result->getOutput() . $result->getErrorOutput()); + } + self::assertFileDoesNotExist($this->directory . '/package-source/vendor'); + self::assertSame("directory . '/source.php')); + } + /** Writes a synthetic fixture beneath the disposable test root, creating its parents. */ private function put(string $path, string $content): void { From e380812cf73079f2d2e1acf506daf6ee83e264bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 16:46:18 -0300 Subject: [PATCH 11/17] docs: distinguish input errors from propagated ECS failures --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 945c3a2..97eb656 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,10 @@ composer test `check` reports violations with a nonzero status and does not change source. `fix` explicitly applies ECS fixes. It does not commit, push or update dependencies. Recheck and inspect the diff afterward; failures can leave already applied fixes. -No selected PHP files is a successful no-op. Invalid options, configuration, Git -history or head mismatch fail closed with exit 2. Otherwise tool exit status is -propagated. Changed mode requires a clean tracked checkout of the requested head. +No selected PHP files is a successful no-op. Invalid options, configuration paths, +Git history or head mismatch fail closed with exit 2. Syntax/style tool failures, +including executable configuration errors inside ECS, retain their tool status. +Changed mode requires a clean tracked checkout of the requested head. `--all` includes tracked files and permits inspecting local tracked edits. Symfony Console provides `help check`, standard global flags and both `--option=value` and `--option value`. Error diagnostics use stderr. Empty paths, From 6695eef757e1a25cfd03a6ed6ffaaceca962abc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 17:02:51 -0300 Subject: [PATCH 12/17] docs: resolve legacy fixer policies and loaded-source provenance --- .changelog/legacy-fixer-comparison.md | 6 + AGENTS.md | 1 + docs/legacy-fixer-comparison.md | 80 + docs/policy.md | 7 +- docs/reference/legacy-fixer-inventory.json | 5658 ++++++++++++++++++++ 5 files changed, 5751 insertions(+), 1 deletion(-) create mode 100644 .changelog/legacy-fixer-comparison.md create mode 100644 docs/legacy-fixer-comparison.md create mode 100644 docs/reference/legacy-fixer-inventory.json diff --git a/.changelog/legacy-fixer-comparison.md b/.changelog/legacy-fixer-comparison.md new file mode 100644 index 0000000..d2bca83 --- /dev/null +++ b/.changelog/legacy-fixer-comparison.md @@ -0,0 +1,6 @@ +--- +category: changed +type: patch +--- + +Record resolved legacy fixer rules, options and loaded-source provenance without broadening the initial default policy. diff --git a/AGENTS.md b/AGENTS.md index b04ddfa..1cdb87d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -62,6 +62,7 @@ for the exact local base/head before publication; do not claim remote CI evidenc - [README.md](README.md): library/CLI adoption, explicit fix and tool scope. - [Policy comparison](docs/policy.md): evidence, rule differences and extraction gates. - [Resolved preset](docs/resolved-preset.md): compiled rules, golden contracts and dependency-update review. +- [Legacy fixer comparison](docs/legacy-fixer-comparison.md): fixed-source inventory, loaded dependencies and remaining profile decisions. - [Architecture decision](docs/architecture.md): library/tool dependencies, CPX/autoload limits and bootstrap without circular prerequisites. - [Automation](docs/automation.md): trust, ref/bootstrap contracts and consumer adoption. - [Instruction map](docs/instruction-map.md): single root scope and skill ownership. diff --git a/docs/legacy-fixer-comparison.md b/docs/legacy-fixer-comparison.md new file mode 100644 index 0000000..b451d8e --- /dev/null +++ b/docs/legacy-fixer-comparison.md @@ -0,0 +1,80 @@ +# Resolved legacy fixer comparison + +This reference extends issue #5 with actual fixer instances at fixed source and +package references. It does not select a broader default profile or claim parity +with the DevTools Rector/PHPDoc pipeline. The initial non-risky preset stays the +default. No consumer migration follows from these results. + +The [machine-readable inventory](reference/legacy-fixer-inventory.json) contains +every active fixer, fully resolved configuration including upstream defaults, +priority, reported risky flag, package reference and source-file SHA-256. Paths +are relative to source packages; no developer paths or credentials are included. +This is recorded evidence, not executable configuration or a consumer DSL. + +## Reproduction boundaries + +Three separate PHP 8.5.11 processes used one disposable Composer project declaring +PHP `^8.3`. No process loaded another tool's autoloader. Reflection verified each +loaded fixer against its own installation. Source commits are in the inventory. + +CoiSA expanded `DefaultSet('')->getRules()` with native RuleSet/FixerFactory; +no header was supplied. DevTools loaded its actual ECS builder, booting the +ECS 13.0.4 LazyContainerFactory and compiler passes. Only cache location and +parallelism were redirected. Its additional Generic GitMergeConflictSniff is +outside the fixer counts. Code Standards booted its ECS 13.3.8 container/preset. +Configured fixer state was inspected after native normalization; null means +that fixer has no stored configuration property. + +Both legacy checkouts have untracked local Composer locks. These local installs +are evidence, not published lockfiles or fresh-install reproducibility promises. +Reproduce the recorded package references in separate installations before +updating the inventory; do not combine dependency graphs. Verify source-file +hashes as well as package metadata. + +The active DevTools fixer is **bundled PHP-CS-Fixer 3.92.4**, not its separately +installed root PHP-CS-Fixer 3.95.1. The active reference is +`9e7488b19403423e02e8403cc1eb596baf4673b0`. CoiSA and Code Standards load +PHP-CS-Fixer 3.95.27 (`1d0c68ee5f37446d8ad5970b82384cd1bb0a3317`). DevTools +also loads bundled Symplify Coding Standard 13.0.0 +(`bd7c36af1e96eecd08cfcc0a772a19767aa02300`). Each rule records its real origin; +root metadata must not silently stand in for the loaded code. + +## Observed deltas + +| Profile | Active fixers | Reported risky | +| --- | ---: | ---: | +| CoiSA | 230 | 54 | +| DevTools ECS | 216 | 43 | +| Initial Code Standards | 48 | 0 | + +The legacy profiles share 194 names; CoiSA has 36 additional rules and DevTools 22. +Seventeen shared names have different normalized configurations. All 48 initial +rules occur in DevTools; CoiSA lacks `visibility_required`. Membership does not +establish behavior equivalence across package versions. + +Differences include PHPDoc alignment (CoiSA vertical, DevTools/Code Standards +left), typed-parameter annotation generation, strict types, empty loop layout, +PHPDoc ordering and trailing-comma elements. Both legacy profiles normalize +multiline arguments; Code Standards preserves blank lines between attributed +parameters with `on_multiline=ignore`, covered by its existing golden regression. + +CoiSA enables author annotation removal, immutable DateTime conversion and +protected-to-private conversion. DevTools preserves author annotations and +mutable DateTime at this ECS stage, but still enables protected-to-private. +The initial preset preserves all three, as its goldens demonstrate. Upstream +`isRisky()` is metadata, not a guarantee of unchanged public API: +`protected_to_private` reports false despite changing visibility. + +No header is active in the CoiSA snapshot because its header argument is empty. +A supplied header adds the PHPDoc `after_open` writer and year/range normalization. +DevTools' separate `.docheader`/PHPDoc pass has another placement contract. +Neither is part of the initial Code Standards default. + +## Remaining decision + +Keep #5 open for behavioral comparisons of the complete legacy pipeline, +header/year and Finder fixtures, actual override precedence and target-version +variations, plus a separately authorized choice of any opt-in profile. Rector +sets/custom rules are semantic work; optional Twig/magic-value and type-analysis +ownership remain independent. Do not broaden the default or regenerate this +inventory merely to silence a dependency-update failure. diff --git a/docs/policy.md b/docs/policy.md index 3233e4b..9bfaed6 100644 --- a/docs/policy.md +++ b/docs/policy.md @@ -22,7 +22,7 @@ from blocked PR #376 or composer-installers #15 was executed or changed. | Runtime | PHP 8.3+ contract | PHP ^8.3 | PHP ^8.3 | | Consumer PHP target | Rector resolves Composer minimum | auto rules resolve minimum | No version-dependent migration sets | | PHPDoc preservation | skips conversion, empty/redundant tags removal, global imports, general annotation removal | removes author; preserves empty return/package | Same six ECS skips as DevTools; left alignment | -| Extra risky policy | PHPUnit static calls self; missing typed params; refactoring sets | immutable date, private visibility, simplified null returns and migration | Deliberately absent pending separate profile/fixtures | +| Additional behavior | PHPUnit static calls self; missing typed params; refactoring sets | immutable date, private visibility, simplified null returns and migration | Deliberately absent pending separate profile/fixtures | | Headers | `.docheader`, current year, after strict declaration | opening header extracted without executing config, updated year, after open | No automatic header rewrite | | Scope | source resolver excludes generated/resources/public/vendor | consumer Finder, hidden files, VCS ignores, path exclusions | Tracked PHP/bin scope, same generated exclusions plus node_modules; symlinks rejected | | Overrides | customize callback last | extraRules last | customize callback last; explicit config selection | @@ -41,6 +41,11 @@ list and effective options for the locked version. Golden fixtures make style, documentation/semantic preservation and second-pass stability reviewable. This is a first bounded part of #5, not equivalence with the legacy engines. +The [resolved legacy fixer comparison](legacy-fixer-comparison.md) expands all +three actual fixer profiles with normalized options, reported risk, priority +and source hashes. It distinguishes bundled code from root Composer metadata +and keeps the remaining full-pipeline decisions explicit. + This is not a rule-by-rule equivalence proof for the complete legacy ECS + Rector + PHPDoc stack. No consumers may be automatically migrated on the strength of this comparison. Before migration, expand resolved rule lists, diff --git a/docs/reference/legacy-fixer-inventory.json b/docs/reference/legacy-fixer-inventory.json new file mode 100644 index 0000000..5b601a6 --- /dev/null +++ b/docs/reference/legacy-fixer-inventory.json @@ -0,0 +1,5658 @@ +{ + "runtime_php": "8.5.11", + "consumer_php_constraint": "^8.3", + "scope": "PHP-CS-Fixer rules only; no Rector/PHPDoc pipeline equivalence claim", + "profiles": { + "coisa": { + "source_commit": "fdee71bc63d4846463d7715afbc515d63ef2c537", + "tool_packages": {}, + "packages": { + "friendsofphp/php-cs-fixer": { + "version": "v3.95.27", + "reference": "1d0c68ee5f37446d8ad5970b82384cd1bb0a3317" + } + }, + "rules": { + "align_multiline_comment": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\AlignMultilineCommentFixer", + "configuration": { + "comment_type": "phpdocs_only" + }, + "risky": false, + "priority": 27, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/AlignMultilineCommentFixer.php", + "source_sha256": "fabb552de03de370add21a98252bdf0281a678b7c2c33658f737198a78535602" + }, + "array_indentation": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\ArrayIndentationFixer", + "configuration": null, + "risky": false, + "priority": 29, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/ArrayIndentationFixer.php", + "source_sha256": "14c2f613191493943c18b756ba06ecc42451e1fa974be720d497e0ad1e3acb7d" + }, + "array_push": { + "class": "PhpCsFixer\\Fixer\\Alias\\ArrayPushFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/ArrayPushFixer.php", + "source_sha256": "9125b08bb5104850ef7da09acb1cce2446fb089de5797ffcce04f43d20aae8ce" + }, + "array_syntax": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\ArraySyntaxFixer", + "configuration": { + "syntax": "short" + }, + "risky": false, + "priority": 37, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/ArraySyntaxFixer.php", + "source_sha256": "6dcb80261c2bd2c1ca592b5257e98e56c4369d6b4611d0903b21e57888b53a53" + }, + "assign_null_coalescing_to_coalesce_equal": { + "class": "PhpCsFixer\\Fixer\\Operator\\AssignNullCoalescingToCoalesceEqualFixer", + "configuration": null, + "risky": false, + "priority": -1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/AssignNullCoalescingToCoalesceEqualFixer.php", + "source_sha256": "1bbcddb6bebceb1c8013f587365cffdad64c85935686df05ce10589844e445a0" + }, + "attribute_block_no_spaces": { + "class": "PhpCsFixer\\Fixer\\AttributeNotation\\AttributeBlockNoSpacesFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/AttributeNotation/AttributeBlockNoSpacesFixer.php", + "source_sha256": "cc3d3580b7ef82058617e0baecbf79fcaa0e5cdc8974786f6454c5a19c984747" + }, + "backtick_to_shell_exec": { + "class": "PhpCsFixer\\Fixer\\Alias\\BacktickToShellExecFixer", + "configuration": null, + "risky": false, + "priority": 17, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/BacktickToShellExecFixer.php", + "source_sha256": "8c1c7a743cd8f71291629530d04249cb823bfe6e6bd2468d06e06df134364b3e" + }, + "binary_operator_spaces": { + "class": "PhpCsFixer\\Fixer\\Operator\\BinaryOperatorSpacesFixer", + "configuration": { + "default": "at_least_single_space", + "operators": [] + }, + "risky": false, + "priority": -32, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/BinaryOperatorSpacesFixer.php", + "source_sha256": "77ab72f53c6c3c8a0fd193af4fb67575a49f49daef0cc94ac4b494852ae7e9c2" + }, + "blank_line_after_namespace": { + "class": "PhpCsFixer\\Fixer\\NamespaceNotation\\BlankLineAfterNamespaceFixer", + "configuration": null, + "risky": false, + "priority": -20, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/NamespaceNotation/BlankLineAfterNamespaceFixer.php", + "source_sha256": "b38fb4aaea094e812c69599ff8c5cf30e0f5d02abb16c5ccb2db98be7cd46b00" + }, + "blank_line_after_opening_tag": { + "class": "PhpCsFixer\\Fixer\\PhpTag\\BlankLineAfterOpeningTagFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpTag/BlankLineAfterOpeningTagFixer.php", + "source_sha256": "766bae0ca162a9bb4b51e8a2df9aa01fbd89f231d0fd68ecacdfa7a8241ea0d0" + }, + "blank_line_before_statement": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\BlankLineBeforeStatementFixer", + "configuration": { + "statements": [ + "break", + "case", + "continue", + "declare", + "default", + "exit", + "goto", + "include", + "include_once", + "phpdoc", + "require", + "require_once", + "return", + "switch", + "throw", + "try", + "yield", + "yield_from" + ] + }, + "risky": false, + "priority": -21, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/BlankLineBeforeStatementFixer.php", + "source_sha256": "b6882e6fb73088c718bf1361b0185c7b4bc068c4669bd03e844c3d59aceeafe0" + }, + "blank_line_between_import_groups": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\BlankLineBetweenImportGroupsFixer", + "configuration": null, + "risky": false, + "priority": -40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/BlankLineBetweenImportGroupsFixer.php", + "source_sha256": "7efe0b68f0376f99c3d0ec4de71e0008c2fd6df283c8e5ae27288d7ac1bc4273" + }, + "blank_lines_before_namespace": { + "class": "PhpCsFixer\\Fixer\\NamespaceNotation\\BlankLinesBeforeNamespaceFixer", + "configuration": { + "min_line_breaks": 2, + "max_line_breaks": 2 + }, + "risky": false, + "priority": -31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/NamespaceNotation/BlankLinesBeforeNamespaceFixer.php", + "source_sha256": "06af880839a88c8e76ea2826b1f908f433b38faa52944a767cb31de5c529c8b8" + }, + "braces_position": { + "class": "PhpCsFixer\\Fixer\\Basic\\BracesPositionFixer", + "configuration": { + "allow_single_line_anonymous_functions": false, + "allow_single_line_empty_anonymous_classes": true, + "anonymous_classes_opening_brace": "same_line", + "anonymous_functions_opening_brace": "same_line", + "classes_opening_brace": "next_line_unless_newline_at_signature_end", + "control_structures_opening_brace": "same_line", + "functions_opening_brace": "next_line_unless_newline_at_signature_end" + }, + "risky": false, + "priority": -2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/BracesPositionFixer.php", + "source_sha256": "950e38f4a7d74e57dba9c9017c44c04d3c3897eb71d109588c935b59fac17b51" + }, + "cast_spaces": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\CastSpacesFixer", + "configuration": { + "space": "single" + }, + "risky": false, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/CastSpacesFixer.php", + "source_sha256": "cf69171f6b38f14f6b36fb1b32c1995d92cdd807b74cde8f4dc059fb2f704f08" + }, + "class_attributes_separation": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\ClassAttributesSeparationFixer", + "configuration": { + "elements": { + "method": "one" + } + }, + "risky": false, + "priority": 55, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/ClassAttributesSeparationFixer.php", + "source_sha256": "88bcea90e65f6fd0605f94c976897c2e11fef54b9dce03d225cf1b3faca105ac" + }, + "class_definition": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\ClassDefinitionFixer", + "configuration": { + "inline_constructor_arguments": false, + "multi_line_extends_each_single_line": false, + "single_item_single_line": false, + "single_line": false, + "space_before_parenthesis": true + }, + "risky": false, + "priority": 36, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/ClassDefinitionFixer.php", + "source_sha256": "4d22e89bb047807c2d6227dee52a106ede39636b09ea7dc2ea69ad0e1f58b7b8" + }, + "class_reference_name_casing": { + "class": "PhpCsFixer\\Fixer\\Casing\\ClassReferenceNameCasingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/ClassReferenceNameCasingFixer.php", + "source_sha256": "367fa2fc7b5f6f29159575f017d62d496b8f6f5d81023f43c42e6b4b618ccfa0" + }, + "clean_namespace": { + "class": "PhpCsFixer\\Fixer\\NamespaceNotation\\CleanNamespaceFixer", + "configuration": null, + "risky": false, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/NamespaceNotation/CleanNamespaceFixer.php", + "source_sha256": "d4b36323de54827de6a65403c3f18066440c0dd149d7e439b4697ce3828d03e2" + }, + "combine_consecutive_issets": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\CombineConsecutiveIssetsFixer", + "configuration": null, + "risky": false, + "priority": 4, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/CombineConsecutiveIssetsFixer.php", + "source_sha256": "97d82f7ca05987f003587755ae7069ac63fbb2e609dc8dd41d4077983465fb1d" + }, + "combine_consecutive_unsets": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\CombineConsecutiveUnsetsFixer", + "configuration": null, + "risky": false, + "priority": 24, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/CombineConsecutiveUnsetsFixer.php", + "source_sha256": "3d6cee83a8c97ecb58b50082b722336bc06444a0ef88839b623cd7e5f7c2a97d" + }, + "combine_nested_dirname": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\CombineNestedDirnameFixer", + "configuration": null, + "risky": true, + "priority": 35, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/CombineNestedDirnameFixer.php", + "source_sha256": "6a6522123949d719da48d7c758c0d7d8f1b8893849df4064e3927219de3b40b6" + }, + "comment_to_phpdoc": { + "class": "PhpCsFixer\\Fixer\\Comment\\CommentToPhpdocFixer", + "configuration": { + "ignored_tags": [] + }, + "risky": true, + "priority": 26, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Comment/CommentToPhpdocFixer.php", + "source_sha256": "abcb50b5067eba28737d96289273ce887b53a9941d2270f832a702486ffe598b" + }, + "compact_nullable_type_declaration": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\CompactNullableTypeDeclarationFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/CompactNullableTypeDeclarationFixer.php", + "source_sha256": "576b7be655f425b679e653299f240f3b704a7deb47f7903d13a065e7e5e5627a" + }, + "concat_space": { + "class": "PhpCsFixer\\Fixer\\Operator\\ConcatSpaceFixer", + "configuration": { + "spacing": "one" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/ConcatSpaceFixer.php", + "source_sha256": "6156c7e8d1cc8662896c5137b27785b7f1201662cf88624d83bd82ce611d5a74" + }, + "constant_case": { + "class": "PhpCsFixer\\Fixer\\Casing\\ConstantCaseFixer", + "configuration": { + "case": "lower" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/ConstantCaseFixer.php", + "source_sha256": "d7f96e6ee2f911f167d7a0e252b522dc7ee12230b1c4e106b8d449558cbe93a2" + }, + "control_structure_braces": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\ControlStructureBracesFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/ControlStructureBracesFixer.php", + "source_sha256": "99b8b53cfaa8c686ed2bc8d4a4e6f4b93117083a1f580b45221feb39d9cc18a6" + }, + "control_structure_continuation_position": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\ControlStructureContinuationPositionFixer", + "configuration": { + "position": "same_line" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/ControlStructureContinuationPositionFixer.php", + "source_sha256": "8d7beaa51db5b4106f6ee688a016218181c6560ea8a2fbf5915539aa6e5a2b5c" + }, + "date_time_immutable": { + "class": "PhpCsFixer\\Fixer\\ClassUsage\\DateTimeImmutableFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassUsage/DateTimeImmutableFixer.php", + "source_sha256": "8de5e80a4d84294c7a3d772120f4d27cc5dd738be69e6caa00dc9726d8b31f01" + }, + "declare_equal_normalize": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\DeclareEqualNormalizeFixer", + "configuration": { + "space": "none" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/DeclareEqualNormalizeFixer.php", + "source_sha256": "993ed796f68f190846f567ac0f8784d296911a67317704af89dd55ac42481383" + }, + "declare_parentheses": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\DeclareParenthesesFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/DeclareParenthesesFixer.php", + "source_sha256": "641d9121450bb0c3d10db2ad7b529ee2115007065dd54edd811658314b355f00" + }, + "declare_strict_types": { + "class": "PhpCsFixer\\Fixer\\Strict\\DeclareStrictTypesFixer", + "configuration": { + "strategy": "enforce" + }, + "risky": true, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Strict/DeclareStrictTypesFixer.php", + "source_sha256": "25a5fb8fd8ba64ec48399f2e8c3c82d03fbc6f7b0b4921d7d63a9007f3aa4f93" + }, + "dir_constant": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\DirConstantFixer", + "configuration": null, + "risky": true, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/DirConstantFixer.php", + "source_sha256": "da14cb6ae1da8c62cec31f783471707f0470c969389f164a0c7f5605919da361" + }, + "echo_tag_syntax": { + "class": "PhpCsFixer\\Fixer\\PhpTag\\EchoTagSyntaxFixer", + "configuration": { + "format": "long", + "long_function": "echo", + "shorten_simple_statements_only": true + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpTag/EchoTagSyntaxFixer.php", + "source_sha256": "5239d026771ef0f8a7fb087c87f89339e24a58aaa2606c492af723463afc4126" + }, + "elseif": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\ElseifFixer", + "configuration": null, + "risky": false, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/ElseifFixer.php", + "source_sha256": "cf771ae1c98a220647d15355839e5ae2dfe836e56b10497b1159cfa97273e4e5" + }, + "empty_loop_body": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\EmptyLoopBodyFixer", + "configuration": { + "style": "semicolon" + }, + "risky": false, + "priority": 39, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/EmptyLoopBodyFixer.php", + "source_sha256": "3e93bdf8b96a3fe3ad8bd42917cf1f931ab7691a8408de51427a7f9dd6e5f5d1" + }, + "empty_loop_condition": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\EmptyLoopConditionFixer", + "configuration": { + "style": "while" + }, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/EmptyLoopConditionFixer.php", + "source_sha256": "86833d8a330d7c4df5f670ac87814f7951fc2c0d66fa41d61844be584e66e991" + }, + "encoding": { + "class": "PhpCsFixer\\Fixer\\Basic\\EncodingFixer", + "configuration": null, + "risky": false, + "priority": 100, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/EncodingFixer.php", + "source_sha256": "bd5cf2f4545e04281a9b014d5a1c84ac51bdb58393b24d2a3dffe27ff05228a0" + }, + "ereg_to_preg": { + "class": "PhpCsFixer\\Fixer\\Alias\\EregToPregFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/EregToPregFixer.php", + "source_sha256": "798b25b4e51e246013404506709a43c8bb33bada4a396a2a995d7884016c2c22" + }, + "error_suppression": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\ErrorSuppressionFixer", + "configuration": { + "mute_deprecation_error": true, + "noise_remaining_usages": false, + "noise_remaining_usages_exclude": [] + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/ErrorSuppressionFixer.php", + "source_sha256": "22011e249b91110737ffe82f9ad058d6ac61d62a3a452d50d598364e83052083" + }, + "explicit_indirect_variable": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\ExplicitIndirectVariableFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/ExplicitIndirectVariableFixer.php", + "source_sha256": "faf3c8bce1e51082aa8328d9ca7df278c15f033579fcebc996ade354ba882ccf" + }, + "explicit_string_variable": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\ExplicitStringVariableFixer", + "configuration": null, + "risky": false, + "priority": 6, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/ExplicitStringVariableFixer.php", + "source_sha256": "5a16a0cced253604c37fd027c224b5841fda51858f99855146fb4a2810c1fc1a" + }, + "final_internal_class": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\FinalInternalClassFixer", + "configuration": { + "consider_absent_docblock_as_internal_class": false, + "exclude": { + "final": true, + "entity": true, + "orm\\entity": true, + "orm\\mapping\\entity": true, + "mapping\\entity": true, + "document": true, + "odm\\document": true + }, + "include": { + "internal": true + } + }, + "risky": true, + "priority": 67, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/FinalInternalClassFixer.php", + "source_sha256": "1476381cb1d2bbc34ed920236beb3abce1ae7a6a534c4bd25f3d817e03280bc2" + }, + "fopen_flag_order": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\FopenFlagOrderFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/FopenFlagOrderFixer.php", + "source_sha256": "40edb659696413706249efa7eea8d88254959f4806b49c9a20fa408db218b92d" + }, + "fopen_flags": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\FopenFlagsFixer", + "configuration": { + "b_mode": false + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/FopenFlagsFixer.php", + "source_sha256": "40fa85393f0d7f43c563d4ebe05b461cb079bb34cffb59dddbdcc3ab1f3dc3f4" + }, + "full_opening_tag": { + "class": "PhpCsFixer\\Fixer\\PhpTag\\FullOpeningTagFixer", + "configuration": null, + "risky": false, + "priority": 98, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpTag/FullOpeningTagFixer.php", + "source_sha256": "bf81cdde5a197918ff6f83ed4d23485c5b55cb94f0e8377f34fa565194282bf3" + }, + "fully_qualified_strict_types": { + "class": "PhpCsFixer\\Fixer\\Import\\FullyQualifiedStrictTypesFixer", + "configuration": { + "import_symbols": true, + "leading_backslash_in_global_namespace": false, + "phpdoc_tags": [ + "param", + "phpstan-param", + "phpstan-property", + "phpstan-property-read", + "phpstan-property-write", + "phpstan-return", + "phpstan-var", + "property", + "property-read", + "property-write", + "psalm-param", + "psalm-property", + "psalm-property-read", + "psalm-property-write", + "psalm-return", + "psalm-var", + "return", + "see", + "throws", + "var" + ] + }, + "risky": false, + "priority": 7, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/FullyQualifiedStrictTypesFixer.php", + "source_sha256": "19a2fc390d4f7c395ff0e563d0d3c6c42911ac89bd7b57da3918e6e743922143" + }, + "function_declaration": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\FunctionDeclarationFixer", + "configuration": { + "closure_fn_spacing": "none", + "closure_function_spacing": "one", + "trailing_comma_single_line": false + }, + "risky": false, + "priority": 31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/FunctionDeclarationFixer.php", + "source_sha256": "21ac94a88be0502675439a68e9a19abf52c4b98a840d7dd476e430a8f1e1fa93" + }, + "function_to_constant": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\FunctionToConstantFixer", + "configuration": { + "functions": [ + "get_called_class", + "get_class", + "get_class_this", + "php_sapi_name", + "phpversion", + "pi" + ] + }, + "risky": true, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/FunctionToConstantFixer.php", + "source_sha256": "7bb6f716df2c709f5787abd1092312fc0ed9979259c82c2190c4c36594282d60" + }, + "general_phpdoc_annotation_remove": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\GeneralPhpdocAnnotationRemoveFixer", + "configuration": { + "annotations": [ + "author" + ], + "case_sensitive": true + }, + "risky": false, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/GeneralPhpdocAnnotationRemoveFixer.php", + "source_sha256": "3bee7cd4f09f4daaa9da1af5665755cacbe9177ea3b76d578b059362324090e3" + }, + "general_phpdoc_tag_rename": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\GeneralPhpdocTagRenameFixer", + "configuration": { + "case_sensitive": false, + "fix_annotation": true, + "fix_inline": true, + "replacements": { + "inheritdocs": "inheritDoc" + } + }, + "risky": false, + "priority": 11, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/GeneralPhpdocTagRenameFixer.php", + "source_sha256": "70ce083478972b8f14d79f1bc32a8add2feb5316f95320d16ba19d650e0caab6" + }, + "get_class_to_class_keyword": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\GetClassToClassKeywordFixer", + "configuration": null, + "risky": true, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/GetClassToClassKeywordFixer.php", + "source_sha256": "fa4c27e0ec56386d392244e7f289365cdafb6f1baa4d5d33b88f7b008fad992d" + }, + "global_namespace_import": { + "class": "PhpCsFixer\\Fixer\\Import\\GlobalNamespaceImportFixer", + "configuration": { + "import_classes": false, + "import_constants": false, + "import_functions": false + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/GlobalNamespaceImportFixer.php", + "source_sha256": "d24965c38eb43dd6fa8eea46c25d56389023a57fdbdc53e03074b4b778f7fc73" + }, + "heredoc_indentation": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\HeredocIndentationFixer", + "configuration": { + "indentation": "start_plus_one" + }, + "risky": false, + "priority": -26, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/HeredocIndentationFixer.php", + "source_sha256": "f8e0a26ae0d0d963d1e8957d9a4e0a78206d5ed1f0174bd0f0eeb190198e83d9" + }, + "heredoc_to_nowdoc": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\HeredocToNowdocFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/HeredocToNowdocFixer.php", + "source_sha256": "001807cce53fc7c6481836a59c87659f76647420085e7adfe5ad955378c2a740" + }, + "implode_call": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\ImplodeCallFixer", + "configuration": null, + "risky": true, + "priority": 37, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/ImplodeCallFixer.php", + "source_sha256": "6021e4338c580a2f7ca126a29375fc10cd420d96e5409f4c76f1fa05a043c7be" + }, + "include": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\IncludeFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/IncludeFixer.php", + "source_sha256": "5ffc88cd1bcd2070de7262ec10be2b312217f8a9f67c128cd7a04b1f3cf71017" + }, + "increment_style": { + "class": "PhpCsFixer\\Fixer\\Operator\\IncrementStyleFixer", + "configuration": { + "style": "pre" + }, + "risky": false, + "priority": 15, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/IncrementStyleFixer.php", + "source_sha256": "bc55cf78eb7d8a651c4a7a6dd67a10f8d64ecd84066f6a4e9f842aad75bdf95e" + }, + "indentation_type": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\IndentationTypeFixer", + "configuration": null, + "risky": false, + "priority": 50, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/IndentationTypeFixer.php", + "source_sha256": "4b3d9a968552eaf1ca68efb11ae3eed620f44156acfc3e7db2ba505bd1a570b0" + }, + "integer_literal_case": { + "class": "PhpCsFixer\\Fixer\\Casing\\IntegerLiteralCaseFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/IntegerLiteralCaseFixer.php", + "source_sha256": "1efaf1977a8b59010aaac1effedbda8bd2edfce62b16a384a4f1cf9580033c79" + }, + "is_null": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\IsNullFixer", + "configuration": null, + "risky": true, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/IsNullFixer.php", + "source_sha256": "1702c56c64fc6aeb648387c44a888ec2dfca49725e405b614d6e2727681ead44" + }, + "lambda_not_used_import": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\LambdaNotUsedImportFixer", + "configuration": null, + "risky": false, + "priority": 31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/LambdaNotUsedImportFixer.php", + "source_sha256": "5291c1c7e9afe288063a3a1c6489aabaff34f7a2a42cd5ea30fe35ac865ec2e2" + }, + "line_ending": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\LineEndingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/LineEndingFixer.php", + "source_sha256": "ff6c1a63e234eb63beffb1aa6172db6275b6bd32044e0aa7567f2c35b3a61f09" + }, + "linebreak_after_opening_tag": { + "class": "PhpCsFixer\\Fixer\\PhpTag\\LinebreakAfterOpeningTagFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpTag/LinebreakAfterOpeningTagFixer.php", + "source_sha256": "faacfbacc3bb909526075b602b2a17d417164873f44c7edf2d9c0106eb123511" + }, + "list_syntax": { + "class": "PhpCsFixer\\Fixer\\ListNotation\\ListSyntaxFixer", + "configuration": { + "syntax": "short" + }, + "risky": false, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ListNotation/ListSyntaxFixer.php", + "source_sha256": "49b93699b9cd740f460529afc7a378c48a770b33c05d61ab36f5efc856cebc29" + }, + "logical_operators": { + "class": "PhpCsFixer\\Fixer\\Operator\\LogicalOperatorsFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/LogicalOperatorsFixer.php", + "source_sha256": "6995e905077983ad429c5df2aa89ad049769840742c6cdb63ef1ff1683d922c1" + }, + "long_to_shorthand_operator": { + "class": "PhpCsFixer\\Fixer\\Operator\\LongToShorthandOperatorFixer", + "configuration": null, + "risky": true, + "priority": 17, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/LongToShorthandOperatorFixer.php", + "source_sha256": "2919c05e914e4c32a45ada8c0ff5dc467eeb570b8f5ff565cebf13ec6b0e7cfe" + }, + "lowercase_cast": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\LowercaseCastFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/LowercaseCastFixer.php", + "source_sha256": "72ad3b69f87b1ef14785772c4d318043645424642c476d1839af1704f9e48463" + }, + "lowercase_keywords": { + "class": "PhpCsFixer\\Fixer\\Casing\\LowercaseKeywordsFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/LowercaseKeywordsFixer.php", + "source_sha256": "f2da2a2ea1fc87a6dfac3a5cadc11a5eed5602a521269775b3bcea9ef0e0dfbd" + }, + "lowercase_static_reference": { + "class": "PhpCsFixer\\Fixer\\Casing\\LowercaseStaticReferenceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/LowercaseStaticReferenceFixer.php", + "source_sha256": "4441f3f395d4e859079af96d743ea7098be1d78239a94973deec4e3be42a4382" + }, + "magic_constant_casing": { + "class": "PhpCsFixer\\Fixer\\Casing\\MagicConstantCasingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/MagicConstantCasingFixer.php", + "source_sha256": "b416a55ab8876a4f194f4a836c89d226cd23e8879e6d3a02c307442aeb35ed35" + }, + "magic_method_casing": { + "class": "PhpCsFixer\\Fixer\\Casing\\MagicMethodCasingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/MagicMethodCasingFixer.php", + "source_sha256": "76271dc31ea6028ced47263306c57bffce0d9064fd46704170b44a81a4a8f520" + }, + "method_argument_space": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\MethodArgumentSpaceFixer", + "configuration": { + "after_heredoc": true, + "attribute_placement": "standalone", + "keep_multiple_spaces_after_comma": false, + "on_multiline": "ensure_fully_multiline" + }, + "risky": false, + "priority": 30, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/MethodArgumentSpaceFixer.php", + "source_sha256": "d0a6ad6c80c3334c46a4d4490a4478594f24bbeb35e15d7005052779eee96c38" + }, + "method_chaining_indentation": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\MethodChainingIndentationFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/MethodChainingIndentationFixer.php", + "source_sha256": "83a87fb2b10ef102849e33ce023f92d614da30c1c1bad7c83dcc9dc45578f5c9" + }, + "modern_serialization_methods": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\ModernSerializationMethodsFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/ModernSerializationMethodsFixer.php", + "source_sha256": "9a9d6213096fd9c1ed3242fdbe337eb65f233e49b3056d358a83eaadc6c949ed" + }, + "modernize_strpos": { + "class": "PhpCsFixer\\Fixer\\Alias\\ModernizeStrposFixer", + "configuration": { + "modernize_stripos": false + }, + "risky": true, + "priority": 37, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/ModernizeStrposFixer.php", + "source_sha256": "d8c5baac9b837671a2e279cd3b029d710db2945e2c541b002dd32cf2e6346121" + }, + "modernize_types_casting": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\ModernizeTypesCastingFixer", + "configuration": null, + "risky": true, + "priority": 31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/ModernizeTypesCastingFixer.php", + "source_sha256": "93b1268bfa4b5c4372ef27d709d729850bdeacd151c7a905d3792426bee27d21" + }, + "modifier_keywords": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\ModifierKeywordsFixer", + "configuration": { + "elements": [ + "const", + "method", + "property" + ] + }, + "risky": false, + "priority": 56, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/ModifierKeywordsFixer.php", + "source_sha256": "a11ebceee8d0b368b8c7968c4e951383909e6015825c40a2623858dfbeb8245a" + }, + "multiline_comment_opening_closing": { + "class": "PhpCsFixer\\Fixer\\Comment\\MultilineCommentOpeningClosingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Comment/MultilineCommentOpeningClosingFixer.php", + "source_sha256": "794b0e743353b5855bf6e5d4b7d509bcad4b68310eb122ec7cd915c7b73d6d5a" + }, + "multiline_whitespace_before_semicolons": { + "class": "PhpCsFixer\\Fixer\\Semicolon\\MultilineWhitespaceBeforeSemicolonsFixer", + "configuration": { + "strategy": "new_line_for_chained_calls" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Semicolon/MultilineWhitespaceBeforeSemicolonsFixer.php", + "source_sha256": "f7dff6668bfe8a0256c8e424f429fccac7a9265773d9437e6dc8dbc353de0f9a" + }, + "native_constant_invocation": { + "class": "PhpCsFixer\\Fixer\\ConstantNotation\\NativeConstantInvocationFixer", + "configuration": { + "exclude": [ + "null", + "false", + "true" + ], + "fix_built_in": false, + "include": [ + "DIRECTORY_SEPARATOR", + "PHP_INT_SIZE", + "PHP_SAPI", + "PHP_VERSION_ID" + ], + "scope": "namespaced", + "strict": true + }, + "risky": true, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ConstantNotation/NativeConstantInvocationFixer.php", + "source_sha256": "2854ca71d02494a4761e0cc00b5858f0c1f023361c9337794b35590680bea48d" + }, + "native_function_casing": { + "class": "PhpCsFixer\\Fixer\\Casing\\NativeFunctionCasingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/NativeFunctionCasingFixer.php", + "source_sha256": "bcf7b1544645aa1aa12b46554496eda339738b41617dbf9bf8cda6de65ee7edb" + }, + "native_function_invocation": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\NativeFunctionInvocationFixer", + "configuration": { + "exclude": [], + "include": [ + "@compiler_optimized" + ], + "scope": "namespaced", + "strict": true + }, + "risky": true, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/NativeFunctionInvocationFixer.php", + "source_sha256": "f7523e97fed6ce8dd19192fe0d5cb96fa131522a3a52c42353b230af1779c146" + }, + "native_type_declaration_casing": { + "class": "PhpCsFixer\\Fixer\\Casing\\NativeTypeDeclarationCasingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/NativeTypeDeclarationCasingFixer.php", + "source_sha256": "afce6977130e67ca18af3fab360189643ee4b34e72be70042fb59062f147a0c1" + }, + "new_with_parentheses": { + "class": "PhpCsFixer\\Fixer\\Operator\\NewWithParenthesesFixer", + "configuration": { + "anonymous_class": false, + "named_class": true + }, + "risky": false, + "priority": 38, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/NewWithParenthesesFixer.php", + "source_sha256": "83f80a987cd2af29c8d88f68b5cccffa32675675bb4bf1d61cbad8f68bc3a44b" + }, + "no_alias_functions": { + "class": "PhpCsFixer\\Fixer\\Alias\\NoAliasFunctionsFixer", + "configuration": { + "sets": [ + "@all" + ] + }, + "risky": true, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/NoAliasFunctionsFixer.php", + "source_sha256": "7ea3c6bf672946ba7b1213d3b91fa55e05fbaa9887b93f7d03c54482ca4fd6d7" + }, + "no_alias_language_construct_call": { + "class": "PhpCsFixer\\Fixer\\Alias\\NoAliasLanguageConstructCallFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/NoAliasLanguageConstructCallFixer.php", + "source_sha256": "fabfa3e59a7dd9138772a90c655a86daaa7ccd960745fbe157fc1d182c76bb49" + }, + "no_alternative_syntax": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\NoAlternativeSyntaxFixer", + "configuration": { + "fix_non_monolithic_code": true + }, + "risky": false, + "priority": 42, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/NoAlternativeSyntaxFixer.php", + "source_sha256": "08328fcba29a0b9d8b3eb22a01b5ecb39d5c9b687c99fcac90c0ddfd4adea14f" + }, + "no_binary_string": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\NoBinaryStringFixer", + "configuration": null, + "risky": false, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/NoBinaryStringFixer.php", + "source_sha256": "3b1ec39e21de2a789c29c74408064fee3656d05fd639a617c10591cb0828c0fc" + }, + "no_blank_lines_after_class_opening": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\NoBlankLinesAfterClassOpeningFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/NoBlankLinesAfterClassOpeningFixer.php", + "source_sha256": "a9ba2281eeebd463ae94f1eac9f042980d8152e3d10e1c6b0f07ad2c095f3385" + }, + "no_blank_lines_after_phpdoc": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\NoBlankLinesAfterPhpdocFixer", + "configuration": null, + "risky": false, + "priority": -20, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/NoBlankLinesAfterPhpdocFixer.php", + "source_sha256": "b767570edf9d616e82426aa84e302f2ddb9114dec1830f7bfea3081676fac547" + }, + "no_break_comment": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\NoBreakCommentFixer", + "configuration": { + "comment_text": "no break" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/NoBreakCommentFixer.php", + "source_sha256": "4af3153b185ca339035f1eed391da229d81977504f409ecfb9d5cbf25bfdb370" + }, + "no_closing_tag": { + "class": "PhpCsFixer\\Fixer\\PhpTag\\NoClosingTagFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpTag/NoClosingTagFixer.php", + "source_sha256": "65aa0cefbf11c18cc173e9fd456e1ea73815995b65b6e4513d4ba117d68154e9" + }, + "no_empty_comment": { + "class": "PhpCsFixer\\Fixer\\Comment\\NoEmptyCommentFixer", + "configuration": null, + "risky": false, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Comment/NoEmptyCommentFixer.php", + "source_sha256": "9f6fb8e2bc0c25d2dfae909567805a76674125d5334959324029fe1d142694b5" + }, + "no_empty_phpdoc": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\NoEmptyPhpdocFixer", + "configuration": null, + "risky": false, + "priority": 3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/NoEmptyPhpdocFixer.php", + "source_sha256": "0cf0a4b4f6463dd18ee19d17f2ba65ddb1e34578be23906a0cc1d3c4b1f13892" + }, + "no_empty_statement": { + "class": "PhpCsFixer\\Fixer\\Semicolon\\NoEmptyStatementFixer", + "configuration": null, + "risky": false, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Semicolon/NoEmptyStatementFixer.php", + "source_sha256": "3b309a505c612d67a36bd13bc5213fccc49f43a9058fe210eafb313b77f24591" + }, + "no_extra_blank_lines": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\NoExtraBlankLinesFixer", + "configuration": { + "tokens": [ + "use" + ] + }, + "risky": false, + "priority": -20, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/NoExtraBlankLinesFixer.php", + "source_sha256": "94ed59e89d682ffc4ada39c8cc82c7f747c720b907d9bca73efe094a2ee4a158" + }, + "no_homoglyph_names": { + "class": "PhpCsFixer\\Fixer\\Naming\\NoHomoglyphNamesFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Naming/NoHomoglyphNamesFixer.php", + "source_sha256": "8d21ec9cbb6ca3884f79a19f7324a753072cde066fcbded6e49524763e90fbc8" + }, + "no_leading_import_slash": { + "class": "PhpCsFixer\\Fixer\\Import\\NoLeadingImportSlashFixer", + "configuration": null, + "risky": false, + "priority": -20, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/NoLeadingImportSlashFixer.php", + "source_sha256": "9e4198081a246f42365844a8fd5d1aa95ea4e59b9d5e56a335b15843b5f19182" + }, + "no_leading_namespace_whitespace": { + "class": "PhpCsFixer\\Fixer\\NamespaceNotation\\NoLeadingNamespaceWhitespaceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/NamespaceNotation/NoLeadingNamespaceWhitespaceFixer.php", + "source_sha256": "9be990f7d7c08510ac6d22d1577815581c004ae1d8e3be6eadf03848ef6fd3cb" + }, + "no_mixed_echo_print": { + "class": "PhpCsFixer\\Fixer\\Alias\\NoMixedEchoPrintFixer", + "configuration": { + "use": "echo" + }, + "risky": false, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/NoMixedEchoPrintFixer.php", + "source_sha256": "dd50b6eb4e71838f0dd39924c9011cf19a9d0b2c8bc8157fc4a390f5418c6b54" + }, + "no_multiline_whitespace_around_double_arrow": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\NoMultilineWhitespaceAroundDoubleArrowFixer", + "configuration": null, + "risky": false, + "priority": 31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/NoMultilineWhitespaceAroundDoubleArrowFixer.php", + "source_sha256": "edbfa1cf58acec84fbd9f2af59752011c3bdb885bb07e5cb9c5a01170f2b7087" + }, + "no_multiple_statements_per_line": { + "class": "PhpCsFixer\\Fixer\\Basic\\NoMultipleStatementsPerLineFixer", + "configuration": null, + "risky": false, + "priority": -1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/NoMultipleStatementsPerLineFixer.php", + "source_sha256": "c09c53612cb0823f7043d7357ccd1c4a026f737d5c2d5285b327c1639d1dad9d" + }, + "no_null_property_initialization": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\NoNullPropertyInitializationFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/NoNullPropertyInitializationFixer.php", + "source_sha256": "2b8b481d21a95f6ccfc561304604c56fa7c20c89edb94a08f3113f2f8b4303a5" + }, + "no_php4_constructor": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\NoPhp4ConstructorFixer", + "configuration": null, + "risky": true, + "priority": 75, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/NoPhp4ConstructorFixer.php", + "source_sha256": "74554cd50701424e7f1162a74d2d6002de81c38abfce3a656767b5b8718dea80" + }, + "no_short_bool_cast": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\NoShortBoolCastFixer", + "configuration": null, + "risky": false, + "priority": -9, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/NoShortBoolCastFixer.php", + "source_sha256": "201f81ee29cb5a7b93faac37e5bbe3e58ee647122cf89a56aa47e6a2d0fe99d2" + }, + "no_singleline_whitespace_before_semicolons": { + "class": "PhpCsFixer\\Fixer\\Semicolon\\NoSinglelineWhitespaceBeforeSemicolonsFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Semicolon/NoSinglelineWhitespaceBeforeSemicolonsFixer.php", + "source_sha256": "f9ff9a406a8e6a10da7779eaf7357b85ab585401f92df13b9fec4ce59241eb77" + }, + "no_space_around_double_colon": { + "class": "PhpCsFixer\\Fixer\\Operator\\NoSpaceAroundDoubleColonFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/NoSpaceAroundDoubleColonFixer.php", + "source_sha256": "d2575d8b2c6f8f49afc0593cd685eb6747e89611e32430f8b533eef71171dd31" + }, + "no_spaces_after_function_name": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\NoSpacesAfterFunctionNameFixer", + "configuration": null, + "risky": false, + "priority": 3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/NoSpacesAfterFunctionNameFixer.php", + "source_sha256": "b24c4fc7d466c54f5bfba325bd206efa2fcb91ef79fe483d53299f2927151e46" + }, + "no_spaces_around_offset": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\NoSpacesAroundOffsetFixer", + "configuration": { + "positions": [ + "inside", + "outside" + ] + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/NoSpacesAroundOffsetFixer.php", + "source_sha256": "12e2b7ecbd51c2434bcc7610196218072abc74c1a1d2a663a581b86666afb31f" + }, + "no_superfluous_elseif": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\NoSuperfluousElseifFixer", + "configuration": null, + "risky": false, + "priority": 39, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/NoSuperfluousElseifFixer.php", + "source_sha256": "b68a4017e08ba9385326ae28baf0b968c458dffa90b04dcbefae89bdb257b22d" + }, + "no_superfluous_phpdoc_tags": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\NoSuperfluousPhpdocTagsFixer", + "configuration": { + "allow_hidden_params": true, + "allow_mixed": true, + "allow_unused_params": false, + "remove_inheritdoc": true + }, + "risky": false, + "priority": 6, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/NoSuperfluousPhpdocTagsFixer.php", + "source_sha256": "fa86d22cb3e46634bb6fe138707f640d5fafada69ce0ac4e515b7fad31d27eef" + }, + "no_trailing_comma_in_singleline": { + "class": "PhpCsFixer\\Fixer\\Basic\\NoTrailingCommaInSinglelineFixer", + "configuration": { + "elements": [ + "arguments", + "array", + "array_destructuring", + "group_import" + ] + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/NoTrailingCommaInSinglelineFixer.php", + "source_sha256": "05001627238fd1576c2d518161e6df92c089fe8fde74ce7b107bfe7a2d93d095" + }, + "no_trailing_whitespace": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\NoTrailingWhitespaceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/NoTrailingWhitespaceFixer.php", + "source_sha256": "2e56f2fa38fb308b8cf91c32352466d25a9ca614acfcf01a43bc6ec5ee362253" + }, + "no_trailing_whitespace_in_comment": { + "class": "PhpCsFixer\\Fixer\\Comment\\NoTrailingWhitespaceInCommentFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Comment/NoTrailingWhitespaceInCommentFixer.php", + "source_sha256": "140875eb77176c75e31cbc0d3aedfc9bec4e26125358ee8aee210767d287bbbd" + }, + "no_unneeded_braces": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\NoUnneededBracesFixer", + "configuration": { + "namespaces": false + }, + "risky": false, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/NoUnneededBracesFixer.php", + "source_sha256": "20d7498c3e9d88a0741a3ca5b0abf9f8979bf64dda6f1d39edb78482f4ef2bee" + }, + "no_unneeded_control_parentheses": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\NoUnneededControlParenthesesFixer", + "configuration": { + "statements": [ + "break", + "continue", + "echo_print", + "negative_instanceof", + "others", + "return", + "yield", + "yield_from" + ] + }, + "risky": false, + "priority": 30, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/NoUnneededControlParenthesesFixer.php", + "source_sha256": "999e84dc5c465b055f24126d9bfb826dc6f0e9c13799d6909dddc9844df2cfa0" + }, + "no_unneeded_final_method": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\NoUnneededFinalMethodFixer", + "configuration": { + "private_methods": true + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/NoUnneededFinalMethodFixer.php", + "source_sha256": "64f70bce1536b76696b4686aa91c8f114a750806e05956e20fcc57aec9a1fada" + }, + "no_unneeded_import_alias": { + "class": "PhpCsFixer\\Fixer\\Import\\NoUnneededImportAliasFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/NoUnneededImportAliasFixer.php", + "source_sha256": "67112cd996e9c69b77ade3058e42525b1614adaf695522a04afec3ed646f6876" + }, + "no_unreachable_default_argument_value": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\NoUnreachableDefaultArgumentValueFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/NoUnreachableDefaultArgumentValueFixer.php", + "source_sha256": "b395022a75232afab195401d1f74c19feaac05a91ff849ff8e676c7333750b40" + }, + "no_unset_cast": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\NoUnsetCastFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/NoUnsetCastFixer.php", + "source_sha256": "82fba230f873685ac38561883cc08f39b10c9e13fc338220de90fcfba0262909" + }, + "no_unused_imports": { + "class": "PhpCsFixer\\Fixer\\Import\\NoUnusedImportsFixer", + "configuration": null, + "risky": false, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/NoUnusedImportsFixer.php", + "source_sha256": "7acb96344183ef51fd1e0a1e1343df67f15bb77c1a857828968e4778ed241e0a" + }, + "no_useless_concat_operator": { + "class": "PhpCsFixer\\Fixer\\Operator\\NoUselessConcatOperatorFixer", + "configuration": { + "juggle_simple_strings": false + }, + "risky": false, + "priority": 5, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/NoUselessConcatOperatorFixer.php", + "source_sha256": "7c8d601d5405a55f1dd404a90e2db3f87fce16fec25cbec6df57c0ec9c6b14a0" + }, + "no_useless_else": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\NoUselessElseFixer", + "configuration": null, + "risky": false, + "priority": 39, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/NoUselessElseFixer.php", + "source_sha256": "3e037b19365f39697443baeb767c8e2187557a8a0ab80ad49cf0801c4854c18c" + }, + "no_useless_nullsafe_operator": { + "class": "PhpCsFixer\\Fixer\\Operator\\NoUselessNullsafeOperatorFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/NoUselessNullsafeOperatorFixer.php", + "source_sha256": "7acc733796ea4639befe57c541d5b47692dba1d2351d6466602bf14a95c64360" + }, + "no_useless_return": { + "class": "PhpCsFixer\\Fixer\\ReturnNotation\\NoUselessReturnFixer", + "configuration": null, + "risky": false, + "priority": -18, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ReturnNotation/NoUselessReturnFixer.php", + "source_sha256": "fc7cd117d60370dbe9d4ba9665c315adbb2ef42630da0e7f8e7e7a4f9c979703" + }, + "no_useless_sprintf": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\NoUselessSprintfFixer", + "configuration": null, + "risky": true, + "priority": 42, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/NoUselessSprintfFixer.php", + "source_sha256": "5bf2594b465acbf395a28d463a8254754fcb8c5dae9d11ee3abed09f0ac11331" + }, + "no_whitespace_before_comma_in_array": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\NoWhitespaceBeforeCommaInArrayFixer", + "configuration": { + "after_heredoc": true + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/NoWhitespaceBeforeCommaInArrayFixer.php", + "source_sha256": "69594fbdee537f6f0b9447e5d407f4a638d6a8d1d61e9b2aac0123773d6a736f" + }, + "no_whitespace_in_blank_line": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\NoWhitespaceInBlankLineFixer", + "configuration": null, + "risky": false, + "priority": -99, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/NoWhitespaceInBlankLineFixer.php", + "source_sha256": "fb6ddefd794703f237940ad6cccf71d029825395350e0f305a9f9d8074276970" + }, + "no_whitespace_in_empty_array": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\NoWhitespaceInEmptyArrayFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/NoWhitespaceInEmptyArrayFixer.php", + "source_sha256": "ec8b7af01743ffc40047671af7aee8ce3f263e84d16cd307973f3ffb5595169e" + }, + "non_printable_character": { + "class": "PhpCsFixer\\Fixer\\Basic\\NonPrintableCharacterFixer", + "configuration": { + "use_escape_sequences_in_strings": true + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/NonPrintableCharacterFixer.php", + "source_sha256": "9d4008b99b4411fbe0236bfd9d70a4944572a44bc4380dcca319a2e5cb897616" + }, + "normalize_index_brace": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\NormalizeIndexBraceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/NormalizeIndexBraceFixer.php", + "source_sha256": "ca6b45b0225ba405ac3679d9b9dc448f494d9dc9ae5321978ebda18b1b84e408" + }, + "nullable_type_declaration": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\NullableTypeDeclarationFixer", + "configuration": { + "syntax": "question_mark" + }, + "risky": false, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/NullableTypeDeclarationFixer.php", + "source_sha256": "c006c6c72588ec3679a00c518053fa5caebc3fa1b7834f3088f33f69a6fb0246" + }, + "nullable_type_declaration_for_default_null_value": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\NullableTypeDeclarationForDefaultNullValueFixer", + "configuration": { + "use_nullable_type_declaration": true + }, + "risky": false, + "priority": 3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixer.php", + "source_sha256": "301c3d8cbf0cb2cf1b5f1d633e3a1f661d89c99db3666ed44171640b5e94bdc1" + }, + "object_operator_without_whitespace": { + "class": "PhpCsFixer\\Fixer\\Operator\\ObjectOperatorWithoutWhitespaceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/ObjectOperatorWithoutWhitespaceFixer.php", + "source_sha256": "3ca54566e900aae92a790f515880618fd641d06a3a6916ed01d6d845b687fe48" + }, + "octal_notation": { + "class": "PhpCsFixer\\Fixer\\Basic\\OctalNotationFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/OctalNotationFixer.php", + "source_sha256": "f935ca541be32c60d739fd4d0f7acef63ac9c6e04e34a1145d70776be3ecae60" + }, + "operator_linebreak": { + "class": "PhpCsFixer\\Fixer\\Operator\\OperatorLinebreakFixer", + "configuration": { + "only_booleans": false, + "position": "beginning" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/OperatorLinebreakFixer.php", + "source_sha256": "5371b76174bff88cae0bfb045e3e903e12207832c41a1bf2a815bfd5a55e01cd" + }, + "ordered_class_elements": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\OrderedClassElementsFixer", + "configuration": { + "case_sensitive": false, + "order": [ + "use_trait" + ], + "sort_algorithm": "none" + }, + "risky": false, + "priority": 65, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/OrderedClassElementsFixer.php", + "source_sha256": "516b2637620f6a5efbf6fd13ac933ca0cc11e4e8414b519d88d57345e8680d0c" + }, + "ordered_imports": { + "class": "PhpCsFixer\\Fixer\\Import\\OrderedImportsFixer", + "configuration": { + "case_sensitive": false, + "imports_order": [ + "class", + "function", + "const" + ], + "sort_algorithm": "none" + }, + "risky": false, + "priority": -30, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/OrderedImportsFixer.php", + "source_sha256": "31c7b5636db54ad3ce4022c0060ccb15e0835656fa0c4ec0413bac484a19139c" + }, + "ordered_traits": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\OrderedTraitsFixer", + "configuration": { + "case_sensitive": false + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/OrderedTraitsFixer.php", + "source_sha256": "a1fe7fbff20cdf0ca77218147966726274017ca10a0a75ddc4bdbdd8bc5867ac" + }, + "ordered_types": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\OrderedTypesFixer", + "configuration": { + "case_sensitive": false, + "null_adjustment": "always_last", + "sort_algorithm": "none" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/OrderedTypesFixer.php", + "source_sha256": "a8d4bd3e6c695d5c65b101982f0566733e94b8283a0b44c6428507fc4e7c8e61" + }, + "php_unit_construct": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitConstructFixer", + "configuration": { + "assertions": [ + "assertEquals", + "assertNotEquals", + "assertNotSame", + "assertSame" + ] + }, + "risky": true, + "priority": -8, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitConstructFixer.php", + "source_sha256": "8e572a7ede8e95caf13144cd99d51b686de25d4fb1860786f9acb097e871cea6" + }, + "php_unit_data_provider_method_order": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitDataProviderMethodOrderFixer", + "configuration": { + "placement": "after" + }, + "risky": false, + "priority": 64, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitDataProviderMethodOrderFixer.php", + "source_sha256": "38ac618e446fd4097ad6beef3b73b17d32371d3cebc49b68fd167c5eff0e4881" + }, + "php_unit_data_provider_name": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitDataProviderNameFixer", + "configuration": { + "prefix": "provide", + "suffix": "Cases" + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitDataProviderNameFixer.php", + "source_sha256": "941b95582c69d7e7868d0364befcfd564a13378ce309e4f739210d3d2c43b0d7" + }, + "php_unit_data_provider_return_type": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitDataProviderReturnTypeFixer", + "configuration": null, + "risky": true, + "priority": 9, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitDataProviderReturnTypeFixer.php", + "source_sha256": "bb43ca5720bfe847fa2447359389606fa338a6b5654f86ecd6e11ce6d0e1fdd4" + }, + "php_unit_data_provider_static": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitDataProviderStaticFixer", + "configuration": { + "force": true + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitDataProviderStaticFixer.php", + "source_sha256": "1bbd7b16ff976b1211902f51da9c8101633aea9e5055b7492399b8d0480c5fe0" + }, + "php_unit_fqcn_annotation": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitFqcnAnnotationFixer", + "configuration": null, + "risky": false, + "priority": -9, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitFqcnAnnotationFixer.php", + "source_sha256": "1c88e1c5a78e327edf0cd7aa4d3a6c8ea9608ff6e05ee2a429ae90b5e026181a" + }, + "php_unit_internal_class": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitInternalClassFixer", + "configuration": { + "types": [ + "normal", + "final" + ] + }, + "risky": false, + "priority": 68, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitInternalClassFixer.php", + "source_sha256": "24406ffdb03d989be7c8980d59819930a29b6039bbad73f55324d74a80ebc92c" + }, + "php_unit_method_casing": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitMethodCasingFixer", + "configuration": { + "case": "camel_case" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitMethodCasingFixer.php", + "source_sha256": "b5bc66bf3049257754fc0cd537e6f6e7354f64b43c4051ebbe51201e36e621ed" + }, + "php_unit_mock_short_will_return": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitMockShortWillReturnFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitMockShortWillReturnFixer.php", + "source_sha256": "231b9f48e11e26015f4f1f474d1d47bd3d7ddd120e9a489802498f204996e1f5" + }, + "php_unit_set_up_tear_down_visibility": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitSetUpTearDownVisibilityFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitSetUpTearDownVisibilityFixer.php", + "source_sha256": "ab8737e1fc65a57d0492ed536f1ff5868a1e5d0b7f1dd969403d5702fa2f4fa1" + }, + "php_unit_strict": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitStrictFixer", + "configuration": { + "assertions": [ + "assertAttributeEquals", + "assertAttributeNotEquals", + "assertEquals", + "assertNotEquals" + ] + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitStrictFixer.php", + "source_sha256": "046be85cd5a19bc7ce4492e17683aff0219cad2e3f75eb16f3b087d1c9fe5d3d" + }, + "php_unit_test_annotation": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitTestAnnotationFixer", + "configuration": { + "style": "prefix" + }, + "risky": true, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitTestAnnotationFixer.php", + "source_sha256": "a25ded631c5e41b1d0b613b67431cf81fecbc51474aa01687a1a90d395d3dcfb" + }, + "php_unit_test_case_static_method_calls": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitTestCaseStaticMethodCallsFixer", + "configuration": { + "call_type": "self", + "methods": [], + "target": "10.0" + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixer.php", + "source_sha256": "c5206c72cbfe490c1e6113e5d883d529fc37f10716a5644aa53697d041504e02" + }, + "php_unit_test_class_requires_covers": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitTestClassRequiresCoversFixer", + "configuration": null, + "risky": false, + "priority": 9, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitTestClassRequiresCoversFixer.php", + "source_sha256": "190d02ffff63c5d41a632be8b1d4bce553761f25bd925efb97e01f0964544977" + }, + "phpdoc_add_missing_param_annotation": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocAddMissingParamAnnotationFixer", + "configuration": { + "only_untyped": true + }, + "risky": false, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocAddMissingParamAnnotationFixer.php", + "source_sha256": "f3910ee8d4b9c9aa7d04af70c9dfd4147beeac444f5696c6cd76c9d7dd48d819" + }, + "phpdoc_align": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocAlignFixer", + "configuration": { + "align": "vertical", + "spacing": 1, + "tags": [ + "method", + "param", + "property", + "return", + "throws", + "type", + "var" + ] + }, + "risky": false, + "priority": -42, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocAlignFixer.php", + "source_sha256": "084038c82e24f3faf9412f88efcb15c1fb99d14638b7d4564282e9990cdf2648" + }, + "phpdoc_annotation_without_dot": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocAnnotationWithoutDotFixer", + "configuration": null, + "risky": false, + "priority": 17, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocAnnotationWithoutDotFixer.php", + "source_sha256": "9f7b2eb9cfe6675a0c6fc77ca49a44a8bc0eea57f4e727f17283591c0a2c922b" + }, + "phpdoc_indent": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocIndentFixer", + "configuration": null, + "risky": false, + "priority": 20, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocIndentFixer.php", + "source_sha256": "43dba73eb60be672d8fcb759811aa52710719d656becba11495e2fdc1fb4a1ed" + }, + "phpdoc_inline_tag_normalizer": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocInlineTagNormalizerFixer", + "configuration": { + "tags": [ + "example", + "id", + "internal", + "inheritdoc", + "inheritdocs", + "link", + "source", + "toc", + "tutorial" + ] + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocInlineTagNormalizerFixer.php", + "source_sha256": "b9caa31d0601890285279d1489622a736931d024b5f9da68c7da42131670353b" + }, + "phpdoc_no_access": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoAccessFixer", + "configuration": null, + "risky": false, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocNoAccessFixer.php", + "source_sha256": "eddf1cfeb3de6a83f6567d2890fd476148b721a55a621ea989929099376050fb" + }, + "phpdoc_no_alias_tag": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoAliasTagFixer", + "configuration": { + "replacements": { + "const": "var", + "link": "see", + "property-read": "property", + "property-write": "property", + "type": "var" + } + }, + "risky": false, + "priority": 11, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocNoAliasTagFixer.php", + "source_sha256": "8bdb5463faa488352467b8ff608ae775a077bb54d3df6fb2b6dd1758d46389f1" + }, + "phpdoc_no_duplicate_types": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoDuplicateTypesFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocNoDuplicateTypesFixer.php", + "source_sha256": "e845142ff5165da33b996260912cafe4d41d80ec3aa9deb565c622e2bb3aff2b" + }, + "phpdoc_no_useless_inheritdoc": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoUselessInheritdocFixer", + "configuration": null, + "risky": false, + "priority": 6, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocNoUselessInheritdocFixer.php", + "source_sha256": "13d30aad1d89dfdf5ea33d63cfa907712e3f6053a5dcf798c911cd1ea1425908" + }, + "phpdoc_order": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocOrderFixer", + "configuration": { + "order": [ + "param", + "return", + "throws" + ] + }, + "risky": false, + "priority": -2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocOrderFixer.php", + "source_sha256": "b47cdbb5be2a5d3a5b6741bc1b321d006eee078470a3843d16f2efac694551fe" + }, + "phpdoc_order_by_value": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocOrderByValueFixer", + "configuration": { + "annotations": { + "covers": "covers" + } + }, + "risky": false, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocOrderByValueFixer.php", + "source_sha256": "27032a29f00cf83159c47ec3172236fdf48f839e2e50f6fb33d2a725261cd71f" + }, + "phpdoc_readonly_class_comment_to_keyword": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\PhpdocReadonlyClassCommentToKeywordFixer", + "configuration": null, + "risky": true, + "priority": 4, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/PhpdocReadonlyClassCommentToKeywordFixer.php", + "source_sha256": "cc9a3139f17b6e3f74cb46e193b17a50056e0f27f7c3e10f5814137fff04f5c5" + }, + "phpdoc_return_self_reference": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocReturnSelfReferenceFixer", + "configuration": { + "replacements": { + "this": "$this", + "@this": "$this", + "$self": "self", + "@self": "self", + "$static": "static", + "@static": "static" + } + }, + "risky": false, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocReturnSelfReferenceFixer.php", + "source_sha256": "35ca01bef922ec143c3de6fa90afdea855be5033b1b2ff34d3658bfc1c0f83f0" + }, + "phpdoc_scalar": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocScalarFixer", + "configuration": { + "types": [ + "boolean", + "callback", + "double", + "integer", + "never-return", + "never-returns", + "no-return", + "real", + "str" + ] + }, + "risky": false, + "priority": 15, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocScalarFixer.php", + "source_sha256": "f00c0b1b47cdabaecdb60cdb896cc58afda60829db69bd2b0ae4d87b8dc9ddd2" + }, + "phpdoc_separation": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocSeparationFixer", + "configuration": { + "groups": [ + [ + "Annotation", + "NamedArgumentConstructor", + "Target" + ], + [ + "author", + "copyright", + "license" + ], + [ + "category", + "package", + "subpackage" + ], + [ + "property", + "property-read", + "property-write" + ], + [ + "deprecated", + "link", + "see", + "since" + ] + ], + "skip_unlisted_annotations": false + }, + "risky": false, + "priority": -3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocSeparationFixer.php", + "source_sha256": "6bad41c3a0bd843d6d107b8d53704afe3dc9fd202ba13d9fb293f4ad48903f79" + }, + "phpdoc_single_line_var_spacing": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocSingleLineVarSpacingFixer", + "configuration": null, + "risky": false, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocSingleLineVarSpacingFixer.php", + "source_sha256": "c7ddf9d99660eee58c24210feaf5f2f3f48cfbbe2cd9057c7aacf3fd09ea2c92" + }, + "phpdoc_summary": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocSummaryFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocSummaryFixer.php", + "source_sha256": "106f4047e772cf7f5df6d2706ab694449c5df2123076f46d8ae5b502cc5b5ded" + }, + "phpdoc_tag_type": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTagTypeFixer", + "configuration": { + "tags": { + "inheritdoc": "inline" + } + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocTagTypeFixer.php", + "source_sha256": "eb24ab58b51d7d12583122763749bd717b83dbb54eb9527aa5d188b4b936eb34" + }, + "phpdoc_to_comment": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocToCommentFixer", + "configuration": { + "allow_before_return_statement": false, + "ignored_tags": [] + }, + "risky": false, + "priority": 25, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocToCommentFixer.php", + "source_sha256": "781e33973c5e942a0d09553d72755cfacf20ed7b9b44004e122ef33d1efdf5cc" + }, + "phpdoc_trim": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTrimFixer", + "configuration": null, + "risky": false, + "priority": -5, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocTrimFixer.php", + "source_sha256": "37e858d1f0b84f4babcfd84d5a1a682fbcbb6e61f9d5183154a322b482088dae" + }, + "phpdoc_trim_consecutive_blank_line_separation": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTrimConsecutiveBlankLineSeparationFixer", + "configuration": null, + "risky": false, + "priority": -41, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocTrimConsecutiveBlankLineSeparationFixer.php", + "source_sha256": "ca990e8bfba980a89034b89d64aff45eb5b09668fe4bb3adf402a56d8d542f96" + }, + "phpdoc_types": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTypesFixer", + "configuration": { + "exclude": [], + "groups": [ + "alias", + "meta", + "simple" + ] + }, + "risky": false, + "priority": 16, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocTypesFixer.php", + "source_sha256": "b1e2907a077333b0d35d7d99dfb1bd4d9d16633a00257fbbeda0a03f3706e808" + }, + "phpdoc_types_order": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTypesOrderFixer", + "configuration": { + "case_sensitive": false, + "null_adjustment": "always_first", + "sort_algorithm": "alpha" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocTypesOrderFixer.php", + "source_sha256": "8a04df99eab6f5b0e1766e2dcb49930f7994845c920c8903e85b376c23afd231" + }, + "phpdoc_var_annotation_correct_order": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocVarAnnotationCorrectOrderFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocVarAnnotationCorrectOrderFixer.php", + "source_sha256": "90ae78fbdf67680adb4f7a5a1af69264ebec37c0ec7a70cd2ef09b8ba326b0e1" + }, + "phpdoc_var_without_name": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocVarWithoutNameFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocVarWithoutNameFixer.php", + "source_sha256": "31c94ce97b6687d4b569a0027ee7f443bcb1db17004925ce7bef4eeb90ce752b" + }, + "pow_to_exponentiation": { + "class": "PhpCsFixer\\Fixer\\Alias\\PowToExponentiationFixer", + "configuration": null, + "risky": true, + "priority": 32, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/PowToExponentiationFixer.php", + "source_sha256": "d6b86c3547eafe3527bed13c109049550c842f37562cbc4309577fabcbaea323" + }, + "protected_to_private": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\ProtectedToPrivateFixer", + "configuration": null, + "risky": false, + "priority": 66, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/ProtectedToPrivateFixer.php", + "source_sha256": "c5ba24c556aac52642693954888fb54c75d0922f5ad7eb9ad61666194f2d13c4" + }, + "psr_autoloading": { + "class": "PhpCsFixer\\Fixer\\Basic\\PsrAutoloadingFixer", + "configuration": { + "dir": null + }, + "risky": true, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/PsrAutoloadingFixer.php", + "source_sha256": "034bfeb349fe3ef9d90b2bb515cf070dcbe6ffc9ad913ad90987e06280a8a041" + }, + "random_api_migration": { + "class": "PhpCsFixer\\Fixer\\Alias\\RandomApiMigrationFixer", + "configuration": { + "replacements": { + "mt_getrandmax": "getrandmax", + "mt_rand": "random_int", + "mt_srand": "srand", + "rand": "random_int" + } + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/RandomApiMigrationFixer.php", + "source_sha256": "30670bfaa3e81402230e917484f05bd5bd55577ed242532d47be393c1e8e2a8b" + }, + "return_assignment": { + "class": "PhpCsFixer\\Fixer\\ReturnNotation\\ReturnAssignmentFixer", + "configuration": { + "skip_named_var_tags": false + }, + "risky": false, + "priority": -15, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ReturnNotation/ReturnAssignmentFixer.php", + "source_sha256": "74cc96e4d7ca64b3b302cb995cb40e9f4bea338be6c06ec96b8195cbe22de753" + }, + "return_type_declaration": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\ReturnTypeDeclarationFixer", + "configuration": { + "space_before": "none" + }, + "risky": false, + "priority": -17, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/ReturnTypeDeclarationFixer.php", + "source_sha256": "398907c6f086a8c4849cab75f137eca5408557a03f5c44d78b2040b8438272a0" + }, + "self_accessor": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\SelfAccessorFixer", + "configuration": null, + "risky": true, + "priority": -11, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/SelfAccessorFixer.php", + "source_sha256": "42daa21843b883bfc76707960f996040b6519a9ce3c5fb22c54af2bd3d36c6c6" + }, + "self_static_accessor": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\SelfStaticAccessorFixer", + "configuration": null, + "risky": false, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/SelfStaticAccessorFixer.php", + "source_sha256": "a619c43a49d8af3c46ec3bf333ee1a1ea5e142beba609089715d9273399d0da2" + }, + "semicolon_after_instruction": { + "class": "PhpCsFixer\\Fixer\\Semicolon\\SemicolonAfterInstructionFixer", + "configuration": null, + "risky": false, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Semicolon/SemicolonAfterInstructionFixer.php", + "source_sha256": "c760e704ac6986fd4f95ab80748166a5796e337ad5b9544f81b37306ee3d694b" + }, + "set_type_to_cast": { + "class": "PhpCsFixer\\Fixer\\Alias\\SetTypeToCastFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/SetTypeToCastFixer.php", + "source_sha256": "bcb7f2087aa329e69c260ed7f97870d85af67f095adb6db39be296e430a761d4" + }, + "short_scalar_cast": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\ShortScalarCastFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/ShortScalarCastFixer.php", + "source_sha256": "f171df0e6724d4ef53ad73d4d1185dff999246239d1d366fbcf493d197d74d69" + }, + "simple_to_complex_string_variable": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\SimpleToComplexStringVariableFixer", + "configuration": null, + "risky": false, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/SimpleToComplexStringVariableFixer.php", + "source_sha256": "5c69f05ed999e1de1392e722ab2cb0681464376013f6ea6054998b1ccd959b93" + }, + "simplified_null_return": { + "class": "PhpCsFixer\\Fixer\\ReturnNotation\\SimplifiedNullReturnFixer", + "configuration": null, + "risky": false, + "priority": 16, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ReturnNotation/SimplifiedNullReturnFixer.php", + "source_sha256": "dad65e790ef75e7ec93d11b579a0697c4d762b725a06ef97b3b9a80788c5ada2" + }, + "single_blank_line_at_eof": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\SingleBlankLineAtEofFixer", + "configuration": null, + "risky": false, + "priority": -100, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/SingleBlankLineAtEofFixer.php", + "source_sha256": "35092651b2909e0f2c5a2b7b03ddb41d47cedb9cf5ca592058e1dffa4e98fcba" + }, + "single_class_element_per_statement": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\SingleClassElementPerStatementFixer", + "configuration": { + "elements": [ + "const", + "property" + ] + }, + "risky": false, + "priority": 56, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/SingleClassElementPerStatementFixer.php", + "source_sha256": "dd3a5d6a4186a90dc7148f4a60a4b937b63bde24f8cde2c7bc71f59bb91467e1" + }, + "single_import_per_statement": { + "class": "PhpCsFixer\\Fixer\\Import\\SingleImportPerStatementFixer", + "configuration": { + "group_to_single_imports": false + }, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/SingleImportPerStatementFixer.php", + "source_sha256": "a4f3268fb9aa150f2bc16c96033174d54489743d153228a063add3fdf53bced4" + }, + "single_line_after_imports": { + "class": "PhpCsFixer\\Fixer\\Import\\SingleLineAfterImportsFixer", + "configuration": null, + "risky": false, + "priority": -11, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/SingleLineAfterImportsFixer.php", + "source_sha256": "44fd8fc6d2f8f109ee12350683014b89d5c801a36b9933a8bd448fae3020379d" + }, + "single_line_comment_spacing": { + "class": "PhpCsFixer\\Fixer\\Comment\\SingleLineCommentSpacingFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Comment/SingleLineCommentSpacingFixer.php", + "source_sha256": "85aebe4a0346fbc6a3060631d90f673e5beb881620a5021eb5fed37990fcc918" + }, + "single_line_comment_style": { + "class": "PhpCsFixer\\Fixer\\Comment\\SingleLineCommentStyleFixer", + "configuration": { + "comment_types": [ + "asterisk", + "hash" + ] + }, + "risky": false, + "priority": -31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Comment/SingleLineCommentStyleFixer.php", + "source_sha256": "20bae86ed682f28812b6db237eb0f1669486aee00fa4a63701d1168a5c7140c3" + }, + "single_line_empty_body": { + "class": "PhpCsFixer\\Fixer\\Basic\\SingleLineEmptyBodyFixer", + "configuration": null, + "risky": false, + "priority": -19, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/SingleLineEmptyBodyFixer.php", + "source_sha256": "d9e9edbe766dd72ade13c104070a3d4325435e97d7fdc5aebdbb7c23046a5d60" + }, + "single_quote": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\SingleQuoteFixer", + "configuration": { + "strings_containing_single_quote_chars": false + }, + "risky": false, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/SingleQuoteFixer.php", + "source_sha256": "ee65f33fa163658e71127349a0e91a5281941f6f2e67b5181a28b687089d5eaa" + }, + "single_space_around_construct": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\SingleSpaceAroundConstructFixer", + "configuration": { + "constructs_contain_a_single_space": [ + "yield_from" + ], + "constructs_followed_by_a_single_space": [ + "abstract", + "as", + "case", + "catch", + "class", + "const", + "const_import", + "do", + "else", + "elseif", + "enum", + "final", + "finally", + "for", + "foreach", + "function", + "function_import", + "if", + "insteadof", + "interface", + "match", + "named_argument", + "namespace", + "new", + "private", + "protected", + "public", + "readonly", + "static", + "switch", + "trait", + "try", + "type_colon", + "use", + "use_lambda", + "while" + ], + "constructs_preceded_by_a_single_space": [ + "as", + "else", + "elseif", + "use_lambda" + ] + }, + "risky": false, + "priority": 36, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/SingleSpaceAroundConstructFixer.php", + "source_sha256": "0307e9b05b73ff875ed65f956dceb45bbb3921c7f81af2befc3e24dbadf0c648" + }, + "single_trait_insert_per_statement": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\SingleTraitInsertPerStatementFixer", + "configuration": null, + "risky": false, + "priority": 36, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/SingleTraitInsertPerStatementFixer.php", + "source_sha256": "bf9eaeb64bf14d62a41d15946e46598eabef586eb231f3d75b5fe96e5620a4f2" + }, + "space_after_semicolon": { + "class": "PhpCsFixer\\Fixer\\Semicolon\\SpaceAfterSemicolonFixer", + "configuration": { + "remove_in_empty_for_expressions": true + }, + "risky": false, + "priority": -1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Semicolon/SpaceAfterSemicolonFixer.php", + "source_sha256": "b971e5bc4e3e6503de5b369e7ce0d296f1839c5b8ee8b96f0bbb58341debb797" + }, + "spaces_inside_parentheses": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\SpacesInsideParenthesesFixer", + "configuration": { + "space": "none" + }, + "risky": false, + "priority": 3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/SpacesInsideParenthesesFixer.php", + "source_sha256": "8aaf001aaa3673f89d5388c678a616a4e9b59d401fd965bfe8753b9a8d988ce9" + }, + "standardize_increment": { + "class": "PhpCsFixer\\Fixer\\Operator\\StandardizeIncrementFixer", + "configuration": null, + "risky": false, + "priority": 16, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/StandardizeIncrementFixer.php", + "source_sha256": "c0d3f77f2b69586bc9989d25de0eb2b412e5de4c4810a673ce9fc4909cd59f8a" + }, + "standardize_not_equals": { + "class": "PhpCsFixer\\Fixer\\Operator\\StandardizeNotEqualsFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/StandardizeNotEqualsFixer.php", + "source_sha256": "f2db807edd6806998b55881b288a0eaa01242174a7a9106ec6636ea93125ca6c" + }, + "statement_indentation": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\StatementIndentationFixer", + "configuration": { + "stick_comment_to_next_continuous_control_statement": false + }, + "risky": false, + "priority": -3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/StatementIndentationFixer.php", + "source_sha256": "b3ecad00771b44a58f12467cc8a25fbe9b36b9e719137005183d89dfaf4982cb" + }, + "static_lambda": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\StaticLambdaFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/StaticLambdaFixer.php", + "source_sha256": "3466406dbfc922096b7265a48add4359e47495503ec1bdb51f5ebefaf7cc9e95" + }, + "strict_comparison": { + "class": "PhpCsFixer\\Fixer\\Strict\\StrictComparisonFixer", + "configuration": null, + "risky": true, + "priority": 38, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Strict/StrictComparisonFixer.php", + "source_sha256": "c05a9a3061171c89f835c51d6b4b6a357a0f3d35eb1f98244d3cbad2d7a4c1b3" + }, + "strict_param": { + "class": "PhpCsFixer\\Fixer\\Strict\\StrictParamFixer", + "configuration": null, + "risky": true, + "priority": 31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Strict/StrictParamFixer.php", + "source_sha256": "43e8a1e75e38c70a40479ff502287c04d4c7faa8a20e3127f6d1d633e19f54b3" + }, + "string_implicit_backslashes": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\StringImplicitBackslashesFixer", + "configuration": { + "double_quoted": "escape", + "heredoc": "escape", + "single_quoted": "unescape" + }, + "risky": false, + "priority": 15, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/StringImplicitBackslashesFixer.php", + "source_sha256": "d69057c2ad9a41355beecc859691a54e9e6a6b42794bff7295808de09e7b3b3c" + }, + "string_length_to_empty": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\StringLengthToEmptyFixer", + "configuration": null, + "risky": true, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/StringLengthToEmptyFixer.php", + "source_sha256": "b11b386a2110183d96393a021d01c990720806953d3e0d3951829e41e952d459" + }, + "string_line_ending": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\StringLineEndingFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/StringLineEndingFixer.php", + "source_sha256": "c81457b562f589a942e3c371b1ec5724f00ba34f29d2c68aa1b30721de293457" + }, + "switch_case_semicolon_to_colon": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\SwitchCaseSemicolonToColonFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixer.php", + "source_sha256": "85d401224f4b9bdccebf73bd5508be5ecfc9c6584c8efca1db048c63e742cbe4" + }, + "switch_case_space": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\SwitchCaseSpaceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/SwitchCaseSpaceFixer.php", + "source_sha256": "8c0d8f6605ff64587696b2b23974b027093c043f5648ecd3539387155664a0d4" + }, + "switch_continue_to_break": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\SwitchContinueToBreakFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/SwitchContinueToBreakFixer.php", + "source_sha256": "59a726ad60d476549f44053932c7ca7e9ec296c6f242f45c8daee79191d41bf0" + }, + "ternary_operator_spaces": { + "class": "PhpCsFixer\\Fixer\\Operator\\TernaryOperatorSpacesFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/TernaryOperatorSpacesFixer.php", + "source_sha256": "ce8500170a055fe5664f387475b2d9c8c126f8ee4ca9ec613404faeb8ab3e778" + }, + "ternary_to_elvis_operator": { + "class": "PhpCsFixer\\Fixer\\Operator\\TernaryToElvisOperatorFixer", + "configuration": null, + "risky": true, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/TernaryToElvisOperatorFixer.php", + "source_sha256": "d72cb548b760ab058d36a5c86eb2c1b2a7944a1cdafefa255aa0835068fe4b57" + }, + "ternary_to_null_coalescing": { + "class": "PhpCsFixer\\Fixer\\Operator\\TernaryToNullCoalescingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/TernaryToNullCoalescingFixer.php", + "source_sha256": "6a3e2a7b7753c42661a4809873b1754f532dd990c72a91caae847baec8feec03" + }, + "trailing_comma_in_multiline": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\TrailingCommaInMultilineFixer", + "configuration": { + "after_heredoc": true, + "elements": [ + "arguments", + "array_destructuring", + "arrays", + "match", + "parameters" + ] + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/TrailingCommaInMultilineFixer.php", + "source_sha256": "603c7cdb5794e783ea4108df75655bcbe9c388356446cd55599f30c8407f17b2" + }, + "trim_array_spaces": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\TrimArraySpacesFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/TrimArraySpacesFixer.php", + "source_sha256": "b65d89672665847044bd538dbe97a80c78dbd582faa1d4bd37fd88df46e8dc4b" + }, + "type_declaration_spaces": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\TypeDeclarationSpacesFixer", + "configuration": { + "elements": [ + "function", + "property" + ] + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/TypeDeclarationSpacesFixer.php", + "source_sha256": "12db8aa8fc2da6285b2a19f73cd7a00162132d144ef406bea5527c3d3ac480f2" + }, + "types_spaces": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\TypesSpacesFixer", + "configuration": { + "space": "none", + "space_multiple_catch": "none" + }, + "risky": false, + "priority": -1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/TypesSpacesFixer.php", + "source_sha256": "e77dbe08843f2689c9464a1cc33e4f1879a8ead63f26f5c1f8c4b6667b60f877" + }, + "unary_operator_spaces": { + "class": "PhpCsFixer\\Fixer\\Operator\\UnaryOperatorSpacesFixer", + "configuration": { + "only_dec_inc": true + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/UnaryOperatorSpacesFixer.php", + "source_sha256": "9f06d15ed1442c2197fafae468f94eaa017613697fde4f03a9ea5ba59a0d49f0" + }, + "use_arrow_functions": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\UseArrowFunctionsFixer", + "configuration": null, + "risky": true, + "priority": 32, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/UseArrowFunctionsFixer.php", + "source_sha256": "197ae5777f9aeeab7d60fae08ddc3034f9c85de41b82af08474fb09fc46146ba" + }, + "void_return": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\VoidReturnFixer", + "configuration": { + "fix_lambda": true + }, + "risky": true, + "priority": 5, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/VoidReturnFixer.php", + "source_sha256": "939acddaea9a8ee927a0c98759c459c8ba2c5a3a01578e0307b1a8e59388f162" + }, + "whitespace_after_comma_in_array": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\WhitespaceAfterCommaInArrayFixer", + "configuration": { + "ensure_single_space": true + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/WhitespaceAfterCommaInArrayFixer.php", + "source_sha256": "0a76d3dd79cf17f9904a7ad09972c5c6a5fb86527d0c13f7e0f4663fd23cccba" + }, + "yield_from_array_to_yields": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\YieldFromArrayToYieldsFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/YieldFromArrayToYieldsFixer.php", + "source_sha256": "921c2bbad4d92c0d4224e3e545efc9c71199ef0b56c5480adc67e61dc1350c9a" + }, + "yoda_style": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\YodaStyleFixer", + "configuration": { + "always_move_variable": false, + "equal": true, + "identical": true, + "less_and_greater": null + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/YodaStyleFixer.php", + "source_sha256": "46023d9cea0183221239c7fe70abdb7c4b37eb1890704a4600bb06163b64e6b2" + } + }, + "non_fixer_sniffs": [] + }, + "devtools": { + "source_commit": "35b5f78ac63813ec07aa3fb3eda08751541e28e1", + "tool_packages": { + "symplify/easy-coding-standard": { + "version": "13.0.4", + "reference": "5c7e7a07e5d6a98b9dd2e6fc0a9155efb7c166c8" + } + }, + "packages": { + "symplify/coding-standard": { + "version": "13.0.0", + "reference": "bd7c36af1e96eecd08cfcc0a772a19767aa02300" + }, + "friendsofphp/php-cs-fixer": { + "version": "v3.92.4", + "reference": "9e7488b19403423e02e8403cc1eb596baf4673b0" + }, + "squizlabs/php_codesniffer": { + "version": "4.0.1", + "reference": "0525c73950de35ded110cffafb9892946d7771b5" + } + }, + "rules": { + "Symplify\\CodingStandard\\Fixer\\Annotation\\RemoveMethodNameDuplicateDescriptionFixer": { + "class": "Symplify\\CodingStandard\\Fixer\\Annotation\\RemoveMethodNameDuplicateDescriptionFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "symplify/coding-standard", + "source_file": "src/Fixer/Annotation/RemoveMethodNameDuplicateDescriptionFixer.php", + "source_sha256": "b7321c66c0df11326e9e54f9a05ae858d89e5833286723cfa126fef74edb53d2" + }, + "Symplify\\CodingStandard\\Fixer\\Annotation\\RemovePHPStormAnnotationFixer": { + "class": "Symplify\\CodingStandard\\Fixer\\Annotation\\RemovePHPStormAnnotationFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "symplify/coding-standard", + "source_file": "src/Fixer/Annotation/RemovePHPStormAnnotationFixer.php", + "source_sha256": "835867d8dfab69c2ca9d8e7b426b872e939477430713d7eafe691035daa15bda" + }, + "Symplify\\CodingStandard\\Fixer\\Annotation\\RemovePropertyVariableNameDescriptionFixer": { + "class": "Symplify\\CodingStandard\\Fixer\\Annotation\\RemovePropertyVariableNameDescriptionFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "symplify/coding-standard", + "source_file": "src/Fixer/Annotation/RemovePropertyVariableNameDescriptionFixer.php", + "source_sha256": "21bc77c3356ead4933116b2d45f498cb1eabd1b697b5c06c275abe7e0e9ecff7" + }, + "Symplify\\CodingStandard\\Fixer\\ArrayNotation\\ArrayListItemNewlineFixer": { + "class": "Symplify\\CodingStandard\\Fixer\\ArrayNotation\\ArrayListItemNewlineFixer", + "configuration": null, + "risky": false, + "priority": 40, + "source_package": "symplify/coding-standard", + "source_file": "src/Fixer/ArrayNotation/ArrayListItemNewlineFixer.php", + "source_sha256": "b9a4ff1a4f0e6e84f3d532338e9e2b2e90b2fba29348f0739cac9eef48f3b530" + }, + "Symplify\\CodingStandard\\Fixer\\ArrayNotation\\ArrayOpenerAndCloserNewlineFixer": { + "class": "Symplify\\CodingStandard\\Fixer\\ArrayNotation\\ArrayOpenerAndCloserNewlineFixer", + "configuration": null, + "risky": false, + "priority": 34, + "source_package": "symplify/coding-standard", + "source_file": "src/Fixer/ArrayNotation/ArrayOpenerAndCloserNewlineFixer.php", + "source_sha256": "183f546f5c650a615714c5e2c794da6496ca21a285be941064c6504730abc636" + }, + "Symplify\\CodingStandard\\Fixer\\ArrayNotation\\StandaloneLineInMultilineArrayFixer": { + "class": "Symplify\\CodingStandard\\Fixer\\ArrayNotation\\StandaloneLineInMultilineArrayFixer", + "configuration": null, + "risky": false, + "priority": 5, + "source_package": "symplify/coding-standard", + "source_file": "src/Fixer/ArrayNotation/StandaloneLineInMultilineArrayFixer.php", + "source_sha256": "f95f5766b44f8c5e1ebffa05852544124baeef8acaa7f512bd4eeab080ae1eb2" + }, + "Symplify\\CodingStandard\\Fixer\\Commenting\\ParamReturnAndVarTagMalformsFixer": { + "class": "Symplify\\CodingStandard\\Fixer\\Commenting\\ParamReturnAndVarTagMalformsFixer", + "configuration": null, + "risky": false, + "priority": -37, + "source_package": "symplify/coding-standard", + "source_file": "src/Fixer/Commenting/ParamReturnAndVarTagMalformsFixer.php", + "source_sha256": "b336438d31b60e2e2e041ca41bf77bf1f0bcb210dc87d232635c298ca090c767" + }, + "Symplify\\CodingStandard\\Fixer\\Commenting\\RemoveUselessDefaultCommentFixer": { + "class": "Symplify\\CodingStandard\\Fixer\\Commenting\\RemoveUselessDefaultCommentFixer", + "configuration": null, + "risky": false, + "priority": 40, + "source_package": "symplify/coding-standard", + "source_file": "src/Fixer/Commenting/RemoveUselessDefaultCommentFixer.php", + "source_sha256": "ac1e8098a18b2440bb314e057d872ea97c5646a2ce9c75d8f02404ea5698c126" + }, + "Symplify\\CodingStandard\\Fixer\\LineLength\\LineLengthFixer": { + "class": "Symplify\\CodingStandard\\Fixer\\LineLength\\LineLengthFixer", + "configuration": { + "line_length": 120, + "break_long_lines": true, + "inline_short_lines": true + }, + "risky": false, + "priority": 5, + "source_package": "symplify/coding-standard", + "source_file": "src/Fixer/LineLength/LineLengthFixer.php", + "source_sha256": "279e82bef5b6af6dccb457af89817128d99137234c02542128bc782e7cf2788a" + }, + "Symplify\\CodingStandard\\Fixer\\Spacing\\MethodChainingNewlineFixer": { + "class": "Symplify\\CodingStandard\\Fixer\\Spacing\\MethodChainingNewlineFixer", + "configuration": null, + "risky": false, + "priority": 39, + "source_package": "symplify/coding-standard", + "source_file": "src/Fixer/Spacing/MethodChainingNewlineFixer.php", + "source_sha256": "7a97a9dd1261368427a18e557f88df4f34160508190bec6896f2962470403868" + }, + "Symplify\\CodingStandard\\Fixer\\Spacing\\SpaceAfterCommaHereNowDocFixer": { + "class": "Symplify\\CodingStandard\\Fixer\\Spacing\\SpaceAfterCommaHereNowDocFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "symplify/coding-standard", + "source_file": "src/Fixer/Spacing/SpaceAfterCommaHereNowDocFixer.php", + "source_sha256": "d464316b508377232e99f64f9712103ddbd03bcfae41cdb71a3a78f331293a8e" + }, + "Symplify\\CodingStandard\\Fixer\\Spacing\\StandaloneLinePromotedPropertyFixer": { + "class": "Symplify\\CodingStandard\\Fixer\\Spacing\\StandaloneLinePromotedPropertyFixer", + "configuration": null, + "risky": false, + "priority": 40, + "source_package": "symplify/coding-standard", + "source_file": "src/Fixer/Spacing/StandaloneLinePromotedPropertyFixer.php", + "source_sha256": "e42b2810ac983f116b91414d5727e652568d8262ecb7fdfc583d0de4cc3e43da" + }, + "Symplify\\CodingStandard\\Fixer\\Strict\\BlankLineAfterStrictTypesFixer": { + "class": "Symplify\\CodingStandard\\Fixer\\Strict\\BlankLineAfterStrictTypesFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "symplify/coding-standard", + "source_file": "src/Fixer/Strict/BlankLineAfterStrictTypesFixer.php", + "source_sha256": "ceb7c15c0c575bd1f20464545ed1f2ab15c20602f60fd2a33d6785d26301a189" + }, + "align_multiline_comment": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\AlignMultilineCommentFixer", + "configuration": { + "comment_type": "phpdocs_only" + }, + "risky": false, + "priority": 27, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/AlignMultilineCommentFixer.php", + "source_sha256": "c1126f2620d4443b20f203c37a97f41c340a1a2eb524b90579c7636bf9321a18" + }, + "array_indentation": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\ArrayIndentationFixer", + "configuration": null, + "risky": false, + "priority": 29, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/ArrayIndentationFixer.php", + "source_sha256": "83ac064407a2f4e5a3a80ad0d7107bfd30f036b59d7c6e925b8ed17b18f0f73d" + }, + "array_push": { + "class": "PhpCsFixer\\Fixer\\Alias\\ArrayPushFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/ArrayPushFixer.php", + "source_sha256": "55bb4129c9a77fc8594a8d683b4f0d82733800041cdfc49c91fca17f59e7f2bf" + }, + "array_syntax": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\ArraySyntaxFixer", + "configuration": { + "syntax": "short" + }, + "risky": false, + "priority": 37, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/ArraySyntaxFixer.php", + "source_sha256": "c6dd00c5b60b6821e812f995eb174974aa135e3367380fb1d779af67a09000d5" + }, + "assign_null_coalescing_to_coalesce_equal": { + "class": "PhpCsFixer\\Fixer\\Operator\\AssignNullCoalescingToCoalesceEqualFixer", + "configuration": null, + "risky": false, + "priority": -1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/AssignNullCoalescingToCoalesceEqualFixer.php", + "source_sha256": "7ce801f37c5900487893af0cb00cc61288592e3965afacc7ec57c81e9a77436c" + }, + "backtick_to_shell_exec": { + "class": "PhpCsFixer\\Fixer\\Alias\\BacktickToShellExecFixer", + "configuration": null, + "risky": false, + "priority": 17, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/BacktickToShellExecFixer.php", + "source_sha256": "8f1f15a919eb83c58ee0a45034d1b0b615c71f1ffc92c3478f25fc1d5e0b6571" + }, + "binary_operator_spaces": { + "class": "PhpCsFixer\\Fixer\\Operator\\BinaryOperatorSpacesFixer", + "configuration": { + "default": "at_least_single_space", + "operators": [] + }, + "risky": false, + "priority": -32, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/BinaryOperatorSpacesFixer.php", + "source_sha256": "6111de034d3a9daaa63ef3da844d515ceaf88cfc2265d9fc837637663de73a3b" + }, + "blank_line_after_namespace": { + "class": "PhpCsFixer\\Fixer\\NamespaceNotation\\BlankLineAfterNamespaceFixer", + "configuration": null, + "risky": false, + "priority": -20, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/NamespaceNotation/BlankLineAfterNamespaceFixer.php", + "source_sha256": "ef9629a90bc7849d2f8ed659c98b5090e19f08471f47deab64dfecb70c4b624c" + }, + "blank_line_after_opening_tag": { + "class": "PhpCsFixer\\Fixer\\PhpTag\\BlankLineAfterOpeningTagFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpTag/BlankLineAfterOpeningTagFixer.php", + "source_sha256": "048b81b9ef979856b95d4d4e2902a22d0769c192c88238b88a57517effcefba5" + }, + "blank_line_before_statement": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\BlankLineBeforeStatementFixer", + "configuration": { + "statements": [ + "return" + ] + }, + "risky": false, + "priority": -21, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/BlankLineBeforeStatementFixer.php", + "source_sha256": "d4a7fb68d237ae9012cfa48f49851cd637551262e20810ab44d5018c03e50b45" + }, + "blank_line_between_import_groups": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\BlankLineBetweenImportGroupsFixer", + "configuration": null, + "risky": false, + "priority": -40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/BlankLineBetweenImportGroupsFixer.php", + "source_sha256": "f6f672482b00510833e7748368dc1c04016f3ac8b2421c909d8548aae7ac613e" + }, + "blank_lines_before_namespace": { + "class": "PhpCsFixer\\Fixer\\NamespaceNotation\\BlankLinesBeforeNamespaceFixer", + "configuration": { + "min_line_breaks": 2, + "max_line_breaks": 2 + }, + "risky": false, + "priority": -31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/NamespaceNotation/BlankLinesBeforeNamespaceFixer.php", + "source_sha256": "ecac5ade915a30d1e11cf7529ed073dec9eac7e3a02330a6d2ca393994a284d8" + }, + "braces_position": { + "class": "PhpCsFixer\\Fixer\\Basic\\BracesPositionFixer", + "configuration": { + "allow_single_line_anonymous_functions": true, + "allow_single_line_empty_anonymous_classes": true, + "anonymous_classes_opening_brace": "same_line", + "anonymous_functions_opening_brace": "same_line", + "classes_opening_brace": "next_line_unless_newline_at_signature_end", + "control_structures_opening_brace": "same_line", + "functions_opening_brace": "next_line_unless_newline_at_signature_end" + }, + "risky": false, + "priority": -2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/BracesPositionFixer.php", + "source_sha256": "8637bf98dbd77d8ae97c67eb598ddb7a90600ddb1165e79ab3dcc443fc85b274" + }, + "cast_spaces": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\CastSpacesFixer", + "configuration": { + "space": "single" + }, + "risky": false, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/CastSpacesFixer.php", + "source_sha256": "088052845f1e03e6e24670b8d236b11d1a1310c255307ce7d05970539395ca57" + }, + "class_attributes_separation": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\ClassAttributesSeparationFixer", + "configuration": { + "elements": { + "method": "one" + } + }, + "risky": false, + "priority": 55, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/ClassAttributesSeparationFixer.php", + "source_sha256": "33b4ad2f7feb263478697e115dbf8b8373dc5cd75b8c104faa5cac515c5d71fe" + }, + "class_definition": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\ClassDefinitionFixer", + "configuration": { + "inline_constructor_arguments": false, + "multi_line_extends_each_single_line": false, + "single_item_single_line": false, + "single_line": false, + "space_before_parenthesis": true + }, + "risky": false, + "priority": 36, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/ClassDefinitionFixer.php", + "source_sha256": "b4a48483f82fdf64d0a9cb10d2cce3bc176f1a06cfe1e345fd16b65474ca2a86" + }, + "class_reference_name_casing": { + "class": "PhpCsFixer\\Fixer\\Casing\\ClassReferenceNameCasingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/ClassReferenceNameCasingFixer.php", + "source_sha256": "05ef89e2e4f3dc75d1f64fbcf864bef6c4b85406785d3552f4fffb3e2c8953a7" + }, + "clean_namespace": { + "class": "PhpCsFixer\\Fixer\\NamespaceNotation\\CleanNamespaceFixer", + "configuration": null, + "risky": false, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/NamespaceNotation/CleanNamespaceFixer.php", + "source_sha256": "eda8fa4a06906faadfb6a499e3499258272b992484971d171245b5f5413efca3" + }, + "combine_nested_dirname": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\CombineNestedDirnameFixer", + "configuration": null, + "risky": true, + "priority": 35, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/CombineNestedDirnameFixer.php", + "source_sha256": "eca065e4dfa32c599527cea1bdb87995d56a1a78bf27b08b75b0e2d34b5fc81c" + }, + "compact_nullable_type_declaration": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\CompactNullableTypeDeclarationFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/CompactNullableTypeDeclarationFixer.php", + "source_sha256": "1e082cda4375c5c1d7c99c71f923bf46f2f7e1f955afb355cc20765bf48ea1b7" + }, + "concat_space": { + "class": "PhpCsFixer\\Fixer\\Operator\\ConcatSpaceFixer", + "configuration": { + "spacing": "one" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/ConcatSpaceFixer.php", + "source_sha256": "2c4c376ae2854f88f3d4255a9ef9070066e0fff52831eb3f091881771192cd35" + }, + "constant_case": { + "class": "PhpCsFixer\\Fixer\\Casing\\ConstantCaseFixer", + "configuration": { + "case": "lower" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/ConstantCaseFixer.php", + "source_sha256": "86bb1ff16b28881f51462e851a61e2a8a8fd5592ed5af22e529987d4bf684bb6" + }, + "control_structure_braces": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\ControlStructureBracesFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/ControlStructureBracesFixer.php", + "source_sha256": "8542b6a2bfca2de2ac4d147226a12c9cf594355ff1af668a23a2930cdd1523d3" + }, + "control_structure_continuation_position": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\ControlStructureContinuationPositionFixer", + "configuration": { + "position": "same_line" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/ControlStructureContinuationPositionFixer.php", + "source_sha256": "413d258a48f505a579072d79ca83520e2e4a99913dffbe610ea6c46983b53ed9" + }, + "declare_equal_normalize": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\DeclareEqualNormalizeFixer", + "configuration": { + "space": "none" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/DeclareEqualNormalizeFixer.php", + "source_sha256": "b959b00cebcd6e341c9d6dc263e88e68a8697c02fdf3404e2d3ea652cc21d0cb" + }, + "declare_parentheses": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\DeclareParenthesesFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/DeclareParenthesesFixer.php", + "source_sha256": "c7a81d8cbb31f19c9bd71745506c33ae6072b28d0ffa1f950b653e24c776e6fc" + }, + "declare_strict_types": { + "class": "PhpCsFixer\\Fixer\\Strict\\DeclareStrictTypesFixer", + "configuration": { + "preserve_existing_declaration": false + }, + "risky": true, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Strict/DeclareStrictTypesFixer.php", + "source_sha256": "45cc04e507fa3ec9a9d49597eee2f0b64077855ded86158c2258874acf5ef64c" + }, + "dir_constant": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\DirConstantFixer", + "configuration": null, + "risky": true, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/DirConstantFixer.php", + "source_sha256": "156b2cc4dcee3ac808fe1ea3a4429920ab0bdab50e1dad8e1fb3110042102181" + }, + "echo_tag_syntax": { + "class": "PhpCsFixer\\Fixer\\PhpTag\\EchoTagSyntaxFixer", + "configuration": { + "format": "long", + "long_function": "echo", + "shorten_simple_statements_only": true + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpTag/EchoTagSyntaxFixer.php", + "source_sha256": "3015a063eff225fedf0ffa0510957435870c1013fc6880af34ced89d39dc8322" + }, + "elseif": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\ElseifFixer", + "configuration": null, + "risky": false, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/ElseifFixer.php", + "source_sha256": "73267fd024cb500073410674370565d1f43cc600fa961818c0215003b1b9dea3" + }, + "empty_loop_body": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\EmptyLoopBodyFixer", + "configuration": { + "style": "braces" + }, + "risky": false, + "priority": 39, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/EmptyLoopBodyFixer.php", + "source_sha256": "2702378e2327245e2f751d00d233d0bf84ca3381022f45fbc234ae5b62102b8a" + }, + "empty_loop_condition": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\EmptyLoopConditionFixer", + "configuration": { + "style": "while" + }, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/EmptyLoopConditionFixer.php", + "source_sha256": "db5791ba1272635fa46a13f3e39a8395392c5c6e8f35b8ba5ff125732d6a685d" + }, + "encoding": { + "class": "PhpCsFixer\\Fixer\\Basic\\EncodingFixer", + "configuration": null, + "risky": false, + "priority": 100, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/EncodingFixer.php", + "source_sha256": "cbeaf1e480f14ff307d6c8e54b37f571bd3978d01d96799e9ffc21999811fedb" + }, + "ereg_to_preg": { + "class": "PhpCsFixer\\Fixer\\Alias\\EregToPregFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/EregToPregFixer.php", + "source_sha256": "1a38ca24919fdec8dcb991aaf19d6e3c83cf470697297d7cad858b4f000725c1" + }, + "error_suppression": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\ErrorSuppressionFixer", + "configuration": { + "mute_deprecation_error": true, + "noise_remaining_usages": false, + "noise_remaining_usages_exclude": [] + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/ErrorSuppressionFixer.php", + "source_sha256": "d2fce854ced7bbfa92679ed615cf1de66237ffcdbfab326c96acc3ff9ddb28d1" + }, + "explicit_indirect_variable": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\ExplicitIndirectVariableFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/ExplicitIndirectVariableFixer.php", + "source_sha256": "3107e38f1430564b4445aedbe2bb4b37984690b7aa2fe32d4760c34fa17e137a" + }, + "explicit_string_variable": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\ExplicitStringVariableFixer", + "configuration": null, + "risky": false, + "priority": 6, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/ExplicitStringVariableFixer.php", + "source_sha256": "b8aa8b323c85ba9b8e2969d2146114fc88bc8eb8837485f700ff0936b6deeecf" + }, + "fopen_flag_order": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\FopenFlagOrderFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/FopenFlagOrderFixer.php", + "source_sha256": "934ca72f6504d66e70786643d4779afa053a83ca57b7b0eb9216a36436bb5b97" + }, + "fopen_flags": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\FopenFlagsFixer", + "configuration": { + "b_mode": false + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/FopenFlagsFixer.php", + "source_sha256": "b489608a00ce07cced2b9a562fe18454be850ebf1924d249447bd0e08548afb8" + }, + "full_opening_tag": { + "class": "PhpCsFixer\\Fixer\\PhpTag\\FullOpeningTagFixer", + "configuration": null, + "risky": false, + "priority": 98, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpTag/FullOpeningTagFixer.php", + "source_sha256": "696260f55aee88b30184e0c12c5ba514ee71f6d48e2db89038c8473639ea5d0a" + }, + "fully_qualified_strict_types": { + "class": "PhpCsFixer\\Fixer\\Import\\FullyQualifiedStrictTypesFixer", + "configuration": { + "import_symbols": false, + "leading_backslash_in_global_namespace": false, + "phpdoc_tags": [ + "param", + "phpstan-param", + "phpstan-property", + "phpstan-property-read", + "phpstan-property-write", + "phpstan-return", + "phpstan-var", + "property", + "property-read", + "property-write", + "psalm-param", + "psalm-property", + "psalm-property-read", + "psalm-property-write", + "psalm-return", + "psalm-var", + "return", + "see", + "throws", + "var" + ] + }, + "risky": false, + "priority": 7, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/FullyQualifiedStrictTypesFixer.php", + "source_sha256": "da39e174a04017ebb72bb0129921bb888ffb30e51861afa137a2ea87803a81b8" + }, + "function_declaration": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\FunctionDeclarationFixer", + "configuration": { + "closure_fn_spacing": "none", + "closure_function_spacing": "one", + "trailing_comma_single_line": false + }, + "risky": false, + "priority": 31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/FunctionDeclarationFixer.php", + "source_sha256": "26b5670ebf7065badcd731103b723f71899f7751208df3ab4d9c62d4568aad5c" + }, + "function_to_constant": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\FunctionToConstantFixer", + "configuration": { + "functions": [ + "get_called_class", + "get_class", + "get_class_this", + "php_sapi_name", + "phpversion", + "pi" + ] + }, + "risky": true, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/FunctionToConstantFixer.php", + "source_sha256": "9512dca51d05b8327a546f21845241b613763770725eda7b19702835c0848269" + }, + "general_phpdoc_tag_rename": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\GeneralPhpdocTagRenameFixer", + "configuration": { + "case_sensitive": false, + "fix_annotation": true, + "fix_inline": true, + "replacements": { + "inheritdocs": "inheritDoc" + } + }, + "risky": false, + "priority": 11, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/GeneralPhpdocTagRenameFixer.php", + "source_sha256": "ce3558347829f815022bef95912dec4810d549a379588cebc09b9e176e537c33" + }, + "get_class_to_class_keyword": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\GetClassToClassKeywordFixer", + "configuration": null, + "risky": true, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/GetClassToClassKeywordFixer.php", + "source_sha256": "0fc0c98199735d22f879fa0f1e14c0e2bef2596eade494314201f98408c624ed" + }, + "heredoc_indentation": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\HeredocIndentationFixer", + "configuration": { + "indentation": "start_plus_one" + }, + "risky": false, + "priority": -26, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/HeredocIndentationFixer.php", + "source_sha256": "7d954df536cd43b462377974b565a7ef0ba753e5ff4318ce69c6d56a9caba205" + }, + "implode_call": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\ImplodeCallFixer", + "configuration": null, + "risky": true, + "priority": 37, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/ImplodeCallFixer.php", + "source_sha256": "2616c76bb6c8eee7bdf8800030324d990d37cc6082061be0f3df52e67e415f45" + }, + "include": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\IncludeFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/IncludeFixer.php", + "source_sha256": "aee22a2acaebedd4fc56597dfa1d3b7e73098b55fdea65b86981d813d55d732a" + }, + "increment_style": { + "class": "PhpCsFixer\\Fixer\\Operator\\IncrementStyleFixer", + "configuration": { + "style": "pre" + }, + "risky": false, + "priority": 15, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/IncrementStyleFixer.php", + "source_sha256": "7f39acc06dd54bcdb0c3422017fd7e91509d6ca12378b539cccebb133dfe2ea0" + }, + "indentation_type": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\IndentationTypeFixer", + "configuration": null, + "risky": false, + "priority": 50, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/IndentationTypeFixer.php", + "source_sha256": "7422537b53f83bfa9d4360dc86bc41a4d0c9feb109c648f5aa07fc1d15cf8fee" + }, + "integer_literal_case": { + "class": "PhpCsFixer\\Fixer\\Casing\\IntegerLiteralCaseFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/IntegerLiteralCaseFixer.php", + "source_sha256": "ca71ac52338d5574a71e31249105a2768ae9d267d20f7cefcd8f5158087646b2" + }, + "is_null": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\IsNullFixer", + "configuration": null, + "risky": true, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/IsNullFixer.php", + "source_sha256": "a6705f009bcb08904716d70528db31eb00f7e362f985aeaf6f88a48bb257f41b" + }, + "lambda_not_used_import": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\LambdaNotUsedImportFixer", + "configuration": null, + "risky": false, + "priority": 31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/LambdaNotUsedImportFixer.php", + "source_sha256": "a00892e825ef9ec7dc32ac65140f34320b4df924e2295e55b79d4dff763ec165" + }, + "line_ending": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\LineEndingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/LineEndingFixer.php", + "source_sha256": "6feccf31edd221ff9027bd775438506394584f123f11ce4bb9894924bb5f97df" + }, + "linebreak_after_opening_tag": { + "class": "PhpCsFixer\\Fixer\\PhpTag\\LinebreakAfterOpeningTagFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpTag/LinebreakAfterOpeningTagFixer.php", + "source_sha256": "3ee7e9ca73577d05433c544cc9a3305abffef72deaaec58ad8f8772e6c76af49" + }, + "list_syntax": { + "class": "PhpCsFixer\\Fixer\\ListNotation\\ListSyntaxFixer", + "configuration": { + "syntax": "short" + }, + "risky": false, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ListNotation/ListSyntaxFixer.php", + "source_sha256": "316c9487862841e912d649b80b1748b0954ecc27683799795f167b61a917ae6e" + }, + "logical_operators": { + "class": "PhpCsFixer\\Fixer\\Operator\\LogicalOperatorsFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/LogicalOperatorsFixer.php", + "source_sha256": "7cc9f60e92fe01dc2b371756a332d990eecf7e1ada7e983a7ee08fbcf7b2a2a1" + }, + "long_to_shorthand_operator": { + "class": "PhpCsFixer\\Fixer\\Operator\\LongToShorthandOperatorFixer", + "configuration": null, + "risky": true, + "priority": 17, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/LongToShorthandOperatorFixer.php", + "source_sha256": "321fdcae17a8660f66c62ea8cafeda57ba16157ec53dbabccf1e34b8ebc49f84" + }, + "lowercase_cast": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\LowercaseCastFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/LowercaseCastFixer.php", + "source_sha256": "296a726ea05706b052805bb96881bb2b1d150ab9cfd7eec6466911de6e0cd46e" + }, + "lowercase_keywords": { + "class": "PhpCsFixer\\Fixer\\Casing\\LowercaseKeywordsFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/LowercaseKeywordsFixer.php", + "source_sha256": "7480cab1ec57ca3eeb1fb6807e11efcefa054d1b33aa40434f9ccf748e11398b" + }, + "lowercase_static_reference": { + "class": "PhpCsFixer\\Fixer\\Casing\\LowercaseStaticReferenceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/LowercaseStaticReferenceFixer.php", + "source_sha256": "9681573e761b61a23998abb57567aab0a498ffb1780d887aa7c98bb5d2d642dc" + }, + "magic_constant_casing": { + "class": "PhpCsFixer\\Fixer\\Casing\\MagicConstantCasingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/MagicConstantCasingFixer.php", + "source_sha256": "2db45725266b66a7ea2a831dfa8d8e38fa35b24f16a9b802301d4344db0d1e66" + }, + "magic_method_casing": { + "class": "PhpCsFixer\\Fixer\\Casing\\MagicMethodCasingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/MagicMethodCasingFixer.php", + "source_sha256": "e7a5f2b9102715e767fbe4c45a3b513925e63e17d34ff3dccd4328a4cac28297" + }, + "method_argument_space": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\MethodArgumentSpaceFixer", + "configuration": { + "after_heredoc": true, + "attribute_placement": "standalone", + "keep_multiple_spaces_after_comma": false, + "on_multiline": "ensure_fully_multiline" + }, + "risky": false, + "priority": 30, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/MethodArgumentSpaceFixer.php", + "source_sha256": "03d7c51596b97317d76e81811e98637104e91d025c9308ac2ab3f1c457a74f0a" + }, + "method_chaining_indentation": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\MethodChainingIndentationFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/MethodChainingIndentationFixer.php", + "source_sha256": "1c78b141b5c3472597320f674a4cb29c795974f23d81fab0ea61778d8d4ab77e" + }, + "modernize_strpos": { + "class": "PhpCsFixer\\Fixer\\Alias\\ModernizeStrposFixer", + "configuration": { + "modernize_stripos": false + }, + "risky": true, + "priority": 37, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/ModernizeStrposFixer.php", + "source_sha256": "ea5b618425da0730ce0c4bcb38cc5a5b14864f87ab73b57fed7d9d9e37bcf181" + }, + "modernize_types_casting": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\ModernizeTypesCastingFixer", + "configuration": null, + "risky": true, + "priority": 31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/ModernizeTypesCastingFixer.php", + "source_sha256": "514221b2ec1c53b3e630edf8abf1cff462fa94e3f6a9badb9b74568ebbe9e632" + }, + "modifier_keywords": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\ModifierKeywordsFixer", + "configuration": { + "elements": [ + "const", + "method", + "property" + ] + }, + "risky": false, + "priority": 56, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/ModifierKeywordsFixer.php", + "source_sha256": "4d187851f120d431e59b1f534e2e4e8189efbd659d1cff184bdaccc7e4ef20b8" + }, + "native_constant_invocation": { + "class": "PhpCsFixer\\Fixer\\ConstantNotation\\NativeConstantInvocationFixer", + "configuration": { + "exclude": [ + "null", + "false", + "true" + ], + "fix_built_in": true, + "include": [], + "scope": "all", + "strict": false + }, + "risky": true, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ConstantNotation/NativeConstantInvocationFixer.php", + "source_sha256": "95a86978edae1dee4feb3024083d9feb6cb66e51fb887c684b5b4973af3ec1e9" + }, + "native_function_casing": { + "class": "PhpCsFixer\\Fixer\\Casing\\NativeFunctionCasingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/NativeFunctionCasingFixer.php", + "source_sha256": "57698722d47b46fa87cccb8318af3f2a7063cbcce9746807e4f4ab0a75613d05" + }, + "native_function_invocation": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\NativeFunctionInvocationFixer", + "configuration": { + "exclude": [], + "include": [ + "@compiler_optimized" + ], + "scope": "namespaced", + "strict": true + }, + "risky": true, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/NativeFunctionInvocationFixer.php", + "source_sha256": "2b9fd219ce602ad258e9757ad430d51bf97651231b30e02d1acc970729d7566f" + }, + "native_type_declaration_casing": { + "class": "PhpCsFixer\\Fixer\\Casing\\NativeTypeDeclarationCasingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/NativeTypeDeclarationCasingFixer.php", + "source_sha256": "51962589e3f313723ea2a45103a6e5b3ced88ac60b1a2f19110d662b1b1824cb" + }, + "new_with_braces": { + "class": "PhpCsFixer\\Fixer\\Operator\\NewWithBracesFixer", + "configuration": { + "anonymous_class": true, + "named_class": true + }, + "risky": false, + "priority": 38, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/NewWithBracesFixer.php", + "source_sha256": "6ea57d2d096d2efca653e55369269eff1e524b58a52c9ef214a28df5a764bbcb" + }, + "new_with_parentheses": { + "class": "PhpCsFixer\\Fixer\\Operator\\NewWithParenthesesFixer", + "configuration": { + "anonymous_class": false, + "named_class": true + }, + "risky": false, + "priority": 38, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/NewWithParenthesesFixer.php", + "source_sha256": "8c2fa1853a9daac4accd917c85809190a8c370efa3596d4b057bcec4826f8ca4" + }, + "no_alias_functions": { + "class": "PhpCsFixer\\Fixer\\Alias\\NoAliasFunctionsFixer", + "configuration": { + "sets": [ + "@all" + ] + }, + "risky": true, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/NoAliasFunctionsFixer.php", + "source_sha256": "c69538b5594493f2b8d8730a4f49a27174e07e5c6784e42c16c7018388f50514" + }, + "no_alias_language_construct_call": { + "class": "PhpCsFixer\\Fixer\\Alias\\NoAliasLanguageConstructCallFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/NoAliasLanguageConstructCallFixer.php", + "source_sha256": "30a47c7d1a1b3285e1a88be74197953f2cf75e2a07687ce157b25956e0939b9f" + }, + "no_alternative_syntax": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\NoAlternativeSyntaxFixer", + "configuration": { + "fix_non_monolithic_code": true + }, + "risky": false, + "priority": 42, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/NoAlternativeSyntaxFixer.php", + "source_sha256": "906b7004ed40eb62a1b65bf2f1a3785cf94200569154a9f518a9edd3c9cc6368" + }, + "no_binary_string": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\NoBinaryStringFixer", + "configuration": null, + "risky": false, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/NoBinaryStringFixer.php", + "source_sha256": "09831eccbd6fd31eaea4839391c67ee930aa735f75469a1d7b0cac7016bc3f38" + }, + "no_blank_lines_after_class_opening": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\NoBlankLinesAfterClassOpeningFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/NoBlankLinesAfterClassOpeningFixer.php", + "source_sha256": "333c25c59506031c8cc014871d4ca2d491f1019163d1e45d47487fa4bc5c0fbf" + }, + "no_blank_lines_after_phpdoc": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\NoBlankLinesAfterPhpdocFixer", + "configuration": null, + "risky": false, + "priority": -20, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/NoBlankLinesAfterPhpdocFixer.php", + "source_sha256": "0c4fd05a2d50885db0c9ca436a17b29aa418a3630e16be1718bc246f1bc2e53e" + }, + "no_break_comment": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\NoBreakCommentFixer", + "configuration": { + "comment_text": "no break" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/NoBreakCommentFixer.php", + "source_sha256": "fc766ba1723cfc3891a95d91d4cbd1e1c76e1b522b306ca7309b903450126b19" + }, + "no_closing_tag": { + "class": "PhpCsFixer\\Fixer\\PhpTag\\NoClosingTagFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpTag/NoClosingTagFixer.php", + "source_sha256": "48894b8bda74d1b8f899948aca379d38456ab50485f300a3440447c509fc7426" + }, + "no_empty_comment": { + "class": "PhpCsFixer\\Fixer\\Comment\\NoEmptyCommentFixer", + "configuration": null, + "risky": false, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Comment/NoEmptyCommentFixer.php", + "source_sha256": "e23648dae41de91066bb16e9b00ecb5a26dbdb5eba1cb09456913dc6db52d5b5" + }, + "no_empty_statement": { + "class": "PhpCsFixer\\Fixer\\Semicolon\\NoEmptyStatementFixer", + "configuration": null, + "risky": false, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Semicolon/NoEmptyStatementFixer.php", + "source_sha256": "6c0dcf797a758b8ae46a44a556e014cb0c4c81b5ca4c0f8fde61fd46881f3d66" + }, + "no_extra_blank_lines": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\NoExtraBlankLinesFixer", + "configuration": { + "tokens": [ + "use" + ] + }, + "risky": false, + "priority": -20, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/NoExtraBlankLinesFixer.php", + "source_sha256": "0c9b0b892ece727a882d9ffbdc5a081e8c5e294fbac05ceedadf522139047c33" + }, + "no_homoglyph_names": { + "class": "PhpCsFixer\\Fixer\\Naming\\NoHomoglyphNamesFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Naming/NoHomoglyphNamesFixer.php", + "source_sha256": "81a6a955d9e2890621ae5e22258f9915e1e05e9802de5e6c566bca99427f99f6" + }, + "no_leading_import_slash": { + "class": "PhpCsFixer\\Fixer\\Import\\NoLeadingImportSlashFixer", + "configuration": null, + "risky": false, + "priority": -20, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/NoLeadingImportSlashFixer.php", + "source_sha256": "2f63070bd5ffc50c8413d813ed859222a24812c7c2a87b036202c65c1656be9c" + }, + "no_leading_namespace_whitespace": { + "class": "PhpCsFixer\\Fixer\\NamespaceNotation\\NoLeadingNamespaceWhitespaceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/NamespaceNotation/NoLeadingNamespaceWhitespaceFixer.php", + "source_sha256": "30aca1c71f2b64416639c0fdf8cbb409f72e727cf38ac470f66ee9a5efc7eb5f" + }, + "no_mixed_echo_print": { + "class": "PhpCsFixer\\Fixer\\Alias\\NoMixedEchoPrintFixer", + "configuration": { + "use": "echo" + }, + "risky": false, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/NoMixedEchoPrintFixer.php", + "source_sha256": "eeb3161e38e6bd50e88eade092ae4993fd288acd4efc5ddd9c2a82729c02b76f" + }, + "no_multiline_whitespace_around_double_arrow": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\NoMultilineWhitespaceAroundDoubleArrowFixer", + "configuration": null, + "risky": false, + "priority": 31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/NoMultilineWhitespaceAroundDoubleArrowFixer.php", + "source_sha256": "b243aa2b352e0044c8c75cbec5e031a7c6296c1535a2fccaede341f1d63e7e95" + }, + "no_multiple_statements_per_line": { + "class": "PhpCsFixer\\Fixer\\Basic\\NoMultipleStatementsPerLineFixer", + "configuration": null, + "risky": false, + "priority": -1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/NoMultipleStatementsPerLineFixer.php", + "source_sha256": "92b943e631cf4e976caa6d886e4d21a09a4e249b2cc55ba8dd3faeadeb94237d" + }, + "no_null_property_initialization": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\NoNullPropertyInitializationFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/NoNullPropertyInitializationFixer.php", + "source_sha256": "e3ca83c4c84fa276aa1e853d3da00a5c626c5eddc0bd1b929db4f1a94a27b020" + }, + "no_php4_constructor": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\NoPhp4ConstructorFixer", + "configuration": null, + "risky": true, + "priority": 75, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/NoPhp4ConstructorFixer.php", + "source_sha256": "4a4a7ed102605c5b6826fc9685ca85b657c3ea266c853301c7dbf4ec35e0b148" + }, + "no_short_bool_cast": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\NoShortBoolCastFixer", + "configuration": null, + "risky": false, + "priority": -9, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/NoShortBoolCastFixer.php", + "source_sha256": "022deeb03903050ef2dfc3005e94a761e63b00fe5ada73a09108de688459a79e" + }, + "no_singleline_whitespace_before_semicolons": { + "class": "PhpCsFixer\\Fixer\\Semicolon\\NoSinglelineWhitespaceBeforeSemicolonsFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Semicolon/NoSinglelineWhitespaceBeforeSemicolonsFixer.php", + "source_sha256": "d305d084a47f1f885bc5b90ba84d95cd565cd6a63c36cd81b5747aac854da076" + }, + "no_space_around_double_colon": { + "class": "PhpCsFixer\\Fixer\\Operator\\NoSpaceAroundDoubleColonFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/NoSpaceAroundDoubleColonFixer.php", + "source_sha256": "9a703a7cdfd5930f48716d997814a188ae1dbd5e16534e5fb66d7f842ff834a1" + }, + "no_spaces_after_function_name": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\NoSpacesAfterFunctionNameFixer", + "configuration": null, + "risky": false, + "priority": 3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/NoSpacesAfterFunctionNameFixer.php", + "source_sha256": "b64a9e3c63b4469262315bf6bfee166155b52c90487c5eaf73e1a5c0502c6c4f" + }, + "no_spaces_around_offset": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\NoSpacesAroundOffsetFixer", + "configuration": { + "positions": [ + "inside", + "outside" + ] + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/NoSpacesAroundOffsetFixer.php", + "source_sha256": "9d869d05b990b1eefc9c9b33cb9602cf3c70d9c05200e6ce9e3615a71adf92b4" + }, + "no_trailing_comma_in_singleline": { + "class": "PhpCsFixer\\Fixer\\Basic\\NoTrailingCommaInSinglelineFixer", + "configuration": { + "elements": [ + "arguments", + "array", + "array_destructuring", + "group_import" + ] + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/NoTrailingCommaInSinglelineFixer.php", + "source_sha256": "34e04780de5239f1808bee99966e7cd6e10b098016dc67b174fc8f91f35c8709" + }, + "no_trailing_whitespace": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\NoTrailingWhitespaceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/NoTrailingWhitespaceFixer.php", + "source_sha256": "334bd154503de5624ad52a80b00125b6c2aa4938c789b7b7564d09c974491377" + }, + "no_trailing_whitespace_in_comment": { + "class": "PhpCsFixer\\Fixer\\Comment\\NoTrailingWhitespaceInCommentFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Comment/NoTrailingWhitespaceInCommentFixer.php", + "source_sha256": "dcbca151fed6f4111d775c9aa39cf2373e52fb19b2c3c89d75f2271ff81e13f4" + }, + "no_trailing_whitespace_in_string": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\NoTrailingWhitespaceInStringFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/NoTrailingWhitespaceInStringFixer.php", + "source_sha256": "51f6482d72b53683340632ff6a875df58b4a419409cad63b013c8e2593324aac" + }, + "no_unneeded_braces": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\NoUnneededBracesFixer", + "configuration": { + "namespaces": true + }, + "risky": false, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/NoUnneededBracesFixer.php", + "source_sha256": "e617422bba3ac1076d2b02cbddfea82be978204e6713ae645e550a4c22001e4e" + }, + "no_unneeded_control_parentheses": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\NoUnneededControlParenthesesFixer", + "configuration": { + "statements": [ + "break", + "clone", + "continue", + "echo_print", + "others", + "return", + "switch_case", + "yield", + "yield_from" + ] + }, + "risky": false, + "priority": 30, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/NoUnneededControlParenthesesFixer.php", + "source_sha256": "0fb6e0267822fda3d2a99dbc65cd5500c7342d60bc4ac58c7842c41f33041fb3" + }, + "no_unneeded_curly_braces": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\NoUnneededCurlyBracesFixer", + "configuration": { + "namespaces": false + }, + "risky": false, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/NoUnneededCurlyBracesFixer.php", + "source_sha256": "be90d51fa086e94cb6d170eee848ac1f5c0af9dae27aec342b04bb5c0a9ebc38" + }, + "no_unneeded_final_method": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\NoUnneededFinalMethodFixer", + "configuration": { + "private_methods": true + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/NoUnneededFinalMethodFixer.php", + "source_sha256": "1219ee7296741cc3b5f122428979167284cdc1e078329bafd87ffd6ffced632c" + }, + "no_unneeded_import_alias": { + "class": "PhpCsFixer\\Fixer\\Import\\NoUnneededImportAliasFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/NoUnneededImportAliasFixer.php", + "source_sha256": "44aecfb7d5e9d8e5a94568f3ab515ef944629d3e7f71a31653e4bcf210185df8" + }, + "no_unreachable_default_argument_value": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\NoUnreachableDefaultArgumentValueFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/NoUnreachableDefaultArgumentValueFixer.php", + "source_sha256": "41f2c2a6f45ae712cdaf4bb2ee63700643e039d472f24b0d3da10ce56bc59b4c" + }, + "no_unset_cast": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\NoUnsetCastFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/NoUnsetCastFixer.php", + "source_sha256": "b638c800517eb6bbadf441316173a97927b08993553b36fefdb46d6c67c29431" + }, + "no_unused_imports": { + "class": "PhpCsFixer\\Fixer\\Import\\NoUnusedImportsFixer", + "configuration": null, + "risky": false, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/NoUnusedImportsFixer.php", + "source_sha256": "cf47ef5dfa61e618a65eb11a41e090fb9deacf5cc03c490e73613ed605a5b320" + }, + "no_useless_concat_operator": { + "class": "PhpCsFixer\\Fixer\\Operator\\NoUselessConcatOperatorFixer", + "configuration": { + "juggle_simple_strings": false + }, + "risky": false, + "priority": 5, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/NoUselessConcatOperatorFixer.php", + "source_sha256": "5706bffee3c42dfea924e4fb425e4d7bf05d8ab86faead2d085f51f147311b41" + }, + "no_useless_else": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\NoUselessElseFixer", + "configuration": null, + "risky": false, + "priority": 39, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/NoUselessElseFixer.php", + "source_sha256": "ccd83e13a2311140d54a589c04be8de6632f9f83674f14753cf1a72c500fc9da" + }, + "no_useless_nullsafe_operator": { + "class": "PhpCsFixer\\Fixer\\Operator\\NoUselessNullsafeOperatorFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/NoUselessNullsafeOperatorFixer.php", + "source_sha256": "0e5c29c1514db1758c53e589d2fc9a03b87d9ff0c3fe7deb7c0b86432da68482" + }, + "no_useless_sprintf": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\NoUselessSprintfFixer", + "configuration": null, + "risky": true, + "priority": 42, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/NoUselessSprintfFixer.php", + "source_sha256": "3721546ef6a621bfcab15357844066e610a1dc89b9d9e82870ee569aac22315d" + }, + "no_whitespace_before_comma_in_array": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\NoWhitespaceBeforeCommaInArrayFixer", + "configuration": { + "after_heredoc": true + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/NoWhitespaceBeforeCommaInArrayFixer.php", + "source_sha256": "a1efbabca9488034a38aa2b63cd18c66661608629bd5b6363550794a037d5ae8" + }, + "no_whitespace_in_blank_line": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\NoWhitespaceInBlankLineFixer", + "configuration": null, + "risky": false, + "priority": -99, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/NoWhitespaceInBlankLineFixer.php", + "source_sha256": "8c3654c59b6516bef0aa04874473b6f54b1100b99ab0b292c5bcdc7d155cf0e9" + }, + "non_printable_character": { + "class": "PhpCsFixer\\Fixer\\Basic\\NonPrintableCharacterFixer", + "configuration": { + "use_escape_sequences_in_strings": true + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/NonPrintableCharacterFixer.php", + "source_sha256": "d3cde7fe1086f565e32097d2fe13ce7656fbb1a55990479984271a0c5fc52ffc" + }, + "normalize_index_brace": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\NormalizeIndexBraceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/NormalizeIndexBraceFixer.php", + "source_sha256": "8223b4651522b9ff8137d032110bd691797c414e84a81303ca95150c271c0aa2" + }, + "not_operator_with_successor_space": { + "class": "PhpCsFixer\\Fixer\\Operator\\NotOperatorWithSuccessorSpaceFixer", + "configuration": null, + "risky": false, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/NotOperatorWithSuccessorSpaceFixer.php", + "source_sha256": "7ed4bcad59ec26d3f6cb49a8811461a3d98271c1ff2114031c997044d8ffd539" + }, + "nullable_type_declaration": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\NullableTypeDeclarationFixer", + "configuration": { + "syntax": "question_mark" + }, + "risky": false, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/NullableTypeDeclarationFixer.php", + "source_sha256": "6cef17ffc46ff26dda8c52d57836a5f3ee48050f98e3ba5761a4026acd415db0" + }, + "nullable_type_declaration_for_default_null_value": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\NullableTypeDeclarationForDefaultNullValueFixer", + "configuration": { + "use_nullable_type_declaration": true + }, + "risky": false, + "priority": 3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixer.php", + "source_sha256": "a1bf6159292ad2fe0823d763b5ca1bfd6f9cea858e1fac47854a88fb829ca263" + }, + "object_operator_without_whitespace": { + "class": "PhpCsFixer\\Fixer\\Operator\\ObjectOperatorWithoutWhitespaceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/ObjectOperatorWithoutWhitespaceFixer.php", + "source_sha256": "c6d1c133a779c433bb65cc7b543e5afe87fbd54b7e20862c118bed7f5090dd9c" + }, + "octal_notation": { + "class": "PhpCsFixer\\Fixer\\Basic\\OctalNotationFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/OctalNotationFixer.php", + "source_sha256": "7fc7b8b627e61a54cdc0496c6ec1662f5034c853eea3bc96ce83c1be01e717e2" + }, + "operator_linebreak": { + "class": "PhpCsFixer\\Fixer\\Operator\\OperatorLinebreakFixer", + "configuration": { + "only_booleans": false, + "position": "beginning" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/OperatorLinebreakFixer.php", + "source_sha256": "555498c830e8fcbabcf772b243dd573892b89d5ad00d051a11ef709ed897ffa7" + }, + "ordered_class_elements": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\OrderedClassElementsFixer", + "configuration": { + "case_sensitive": false, + "order": [ + "use_trait" + ], + "sort_algorithm": "none" + }, + "risky": false, + "priority": 65, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/OrderedClassElementsFixer.php", + "source_sha256": "9293aab40d2ef6e404c4ba619ef65777ee86cdfa552b2f9aaa53622df37201a3" + }, + "ordered_imports": { + "class": "PhpCsFixer\\Fixer\\Import\\OrderedImportsFixer", + "configuration": { + "case_sensitive": false, + "imports_order": [ + "class", + "function", + "const" + ], + "sort_algorithm": "none" + }, + "risky": false, + "priority": -30, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/OrderedImportsFixer.php", + "source_sha256": "6a40c4d858dc7a75a862019ceafdad667efa24d19f6b907968b91fa59683c69f" + }, + "ordered_traits": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\OrderedTraitsFixer", + "configuration": { + "case_sensitive": false + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/OrderedTraitsFixer.php", + "source_sha256": "229539d5a73107d56538173f588e21530dc1912fbebf5840dc186521eba64916" + }, + "ordered_types": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\OrderedTypesFixer", + "configuration": { + "case_sensitive": false, + "null_adjustment": "always_last", + "sort_algorithm": "none" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/OrderedTypesFixer.php", + "source_sha256": "c74836d5a8295edf7852a974cb1060f9090b1203d57e145b152cbb623ca3996c" + }, + "php_unit_construct": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitConstructFixer", + "configuration": { + "assertions": [ + "assertEquals", + "assertNotEquals", + "assertNotSame", + "assertSame" + ] + }, + "risky": true, + "priority": -8, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitConstructFixer.php", + "source_sha256": "ae98f321c7939f2028001d6aa6b7495b4b183613c83bd24c63889d1728843703" + }, + "php_unit_fqcn_annotation": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitFqcnAnnotationFixer", + "configuration": null, + "risky": false, + "priority": -9, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitFqcnAnnotationFixer.php", + "source_sha256": "74ef0303cd6b1fc85fe03e43179f6a0cde2fac43289a6332035279c96331e4c1" + }, + "php_unit_method_casing": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitMethodCasingFixer", + "configuration": { + "case": "camel_case" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitMethodCasingFixer.php", + "source_sha256": "45e576493d667faeaee9393b689a8fcbbcb38e20ce6669dbad19574f99ae543a" + }, + "php_unit_mock_short_will_return": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitMockShortWillReturnFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitMockShortWillReturnFixer.php", + "source_sha256": "3c107e4136cebf834136363a0d44c400c9d972ab4f557abccbcee382fe94a665" + }, + "php_unit_set_up_tear_down_visibility": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitSetUpTearDownVisibilityFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitSetUpTearDownVisibilityFixer.php", + "source_sha256": "a690e9ad203e55c952e827f1724137bfd2e44b7f4001fe9fd18b0e329170454d" + }, + "php_unit_test_annotation": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitTestAnnotationFixer", + "configuration": { + "style": "prefix" + }, + "risky": true, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitTestAnnotationFixer.php", + "source_sha256": "1c6a5ecbfae3a0ea957b0972d3d642ab93051637cdcd83f4daa3d6591668d869" + }, + "php_unit_test_case_static_method_calls": { + "class": "PhpCsFixer\\Fixer\\PhpUnit\\PhpUnitTestCaseStaticMethodCallsFixer", + "configuration": { + "call_type": "self", + "methods": [], + "target": "10.0" + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixer.php", + "source_sha256": "8f3465afc4e8a45f6efd964f70047da85043bb9db06837e8aba105960720adb8" + }, + "phpdoc_add_missing_param_annotation": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocAddMissingParamAnnotationFixer", + "configuration": { + "only_untyped": false + }, + "risky": false, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocAddMissingParamAnnotationFixer.php", + "source_sha256": "467f6bdeeda54b93635be0c647cd4c7b3d8ea4f3242b8eda7f3b21357ecc2750" + }, + "phpdoc_align": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocAlignFixer", + "configuration": { + "align": "left", + "spacing": 1, + "tags": [ + "method", + "param", + "property", + "return", + "throws", + "type", + "var" + ] + }, + "risky": false, + "priority": -42, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocAlignFixer.php", + "source_sha256": "f1e68114e42c5c585651b2a1a1973e40e13912854dd4c2e8f794f9c1e9d924fe" + }, + "phpdoc_annotation_without_dot": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocAnnotationWithoutDotFixer", + "configuration": null, + "risky": false, + "priority": 17, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocAnnotationWithoutDotFixer.php", + "source_sha256": "a5de82c551a90529c65122b9a4932832616da2732c3491f2b446deb529839614" + }, + "phpdoc_indent": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocIndentFixer", + "configuration": null, + "risky": false, + "priority": 20, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocIndentFixer.php", + "source_sha256": "26f1c8f0737c1bd0caaa06765b9488c89803fb4afab308fdc1fe72ba68e082f6" + }, + "phpdoc_inline_tag_normalizer": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocInlineTagNormalizerFixer", + "configuration": { + "tags": [ + "example", + "id", + "internal", + "inheritdoc", + "inheritdocs", + "link", + "source", + "toc", + "tutorial" + ] + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocInlineTagNormalizerFixer.php", + "source_sha256": "c24a75d83818c5788d74b5308f97c53f0598ad6c8446ac093b0136cca7aabd6c" + }, + "phpdoc_line_span": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocLineSpanFixer", + "configuration": { + "const": "multi", + "method": "multi", + "property": "multi" + }, + "risky": false, + "priority": 7, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocLineSpanFixer.php", + "source_sha256": "eb7203916549da642f2c00b2f8c6f102973b061fa1f8575eba1b88a5fca7f9a2" + }, + "phpdoc_no_access": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoAccessFixer", + "configuration": null, + "risky": false, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocNoAccessFixer.php", + "source_sha256": "8febed858d2ac341bdbbb8940ac3bd0d3b47c24ea4ef99474d43959f485d206e" + }, + "phpdoc_no_alias_tag": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoAliasTagFixer", + "configuration": { + "replacements": { + "const": "var", + "link": "see", + "property-read": "property", + "property-write": "property", + "type": "var" + } + }, + "risky": false, + "priority": 11, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocNoAliasTagFixer.php", + "source_sha256": "1010823c98e5052961208b0de222b8be6fba2191ed7254af4f565ce6817c9054" + }, + "phpdoc_no_package": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoPackageFixer", + "configuration": null, + "risky": false, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocNoPackageFixer.php", + "source_sha256": "0651b59ad67d64ba1aafbabd37c607ceb11787a13758bdf4dcb65fcf341e22cc" + }, + "phpdoc_no_useless_inheritdoc": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocNoUselessInheritdocFixer", + "configuration": null, + "risky": false, + "priority": 6, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocNoUselessInheritdocFixer.php", + "source_sha256": "5f930374979380b7d3a747b70bf1bf8a8f11a151c8ed055d719a928c310505ad" + }, + "phpdoc_order": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocOrderFixer", + "configuration": { + "order": [ + "param", + "return", + "throws" + ] + }, + "risky": false, + "priority": -2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocOrderFixer.php", + "source_sha256": "6109ecc50877c9fe976c0dd1d74d6a22e43c6d6145f98c05fe7186b7006821b3" + }, + "phpdoc_readonly_class_comment_to_keyword": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\PhpdocReadonlyClassCommentToKeywordFixer", + "configuration": null, + "risky": true, + "priority": 4, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/PhpdocReadonlyClassCommentToKeywordFixer.php", + "source_sha256": "dd0e91dd50e812c3003acf96fab3488875a7a5dce712a30a0219147c67b38bc5" + }, + "phpdoc_return_self_reference": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocReturnSelfReferenceFixer", + "configuration": { + "replacements": { + "this": "$this", + "@this": "$this", + "$self": "self", + "@self": "self", + "$static": "static", + "@static": "static" + } + }, + "risky": false, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocReturnSelfReferenceFixer.php", + "source_sha256": "c7106a552553d324803be2f9b63f2ad42fad2138a0df96d316bf29df61e81888" + }, + "phpdoc_scalar": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocScalarFixer", + "configuration": { + "types": [ + "boolean", + "callback", + "double", + "integer", + "never-return", + "never-returns", + "no-return", + "real", + "str" + ] + }, + "risky": false, + "priority": 15, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocScalarFixer.php", + "source_sha256": "4ea3ba10cf6fab4c0c7f8efdfdf66549d3426b209ce8811ab974e6bd862264f2" + }, + "phpdoc_separation": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocSeparationFixer", + "configuration": { + "groups": [ + [ + "Annotation", + "NamedArgumentConstructor", + "Target" + ], + [ + "author", + "copyright", + "license" + ], + [ + "category", + "package", + "subpackage" + ], + [ + "property", + "property-read", + "property-write" + ], + [ + "deprecated", + "link", + "see", + "since" + ] + ], + "skip_unlisted_annotations": false + }, + "risky": false, + "priority": -3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocSeparationFixer.php", + "source_sha256": "c5aeabe25e08d46ff0427e4ce12f3c87476c78ac2b9203f2b2245a807a822556" + }, + "phpdoc_single_line_var_spacing": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocSingleLineVarSpacingFixer", + "configuration": null, + "risky": false, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocSingleLineVarSpacingFixer.php", + "source_sha256": "4ea9dfba014f371c4cd36cadac4ea7da169338e493cf34dea4e3cde2c2d812af" + }, + "phpdoc_summary": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocSummaryFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocSummaryFixer.php", + "source_sha256": "aaefae6a776a8fd5c78d3a6e895be3a9aafe0d7f6f2b1f2f8f60b26aaa719fe4" + }, + "phpdoc_tag_type": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTagTypeFixer", + "configuration": { + "tags": { + "inheritdoc": "inline" + } + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocTagTypeFixer.php", + "source_sha256": "dfc94b2b8652943d2689db98a87118fecb70ac2546f16fd082528a79bf4da72f" + }, + "phpdoc_trim": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTrimFixer", + "configuration": null, + "risky": false, + "priority": -5, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocTrimFixer.php", + "source_sha256": "99d1d631f97a7b152166945ed9efb487488688582b0a5361787b20d42c705361" + }, + "phpdoc_trim_consecutive_blank_line_separation": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTrimConsecutiveBlankLineSeparationFixer", + "configuration": null, + "risky": false, + "priority": -41, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocTrimConsecutiveBlankLineSeparationFixer.php", + "source_sha256": "6210246fbb8cabdb27563be745816cf9da406f07f870c662948de76e1a76405f" + }, + "phpdoc_types": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTypesFixer", + "configuration": { + "groups": [ + "alias", + "meta", + "simple" + ] + }, + "risky": false, + "priority": 16, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocTypesFixer.php", + "source_sha256": "29533fe65a9a0c77b24ca59f904a45431481ed3f6747ac4066b5e18b9a7f9d74" + }, + "phpdoc_types_order": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocTypesOrderFixer", + "configuration": { + "case_sensitive": false, + "null_adjustment": "always_last", + "sort_algorithm": "none" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocTypesOrderFixer.php", + "source_sha256": "528d07cedf171db88f8b66f5f34253eb8aaa88a886e1be28fbd963521cb632db" + }, + "phpdoc_var_annotation_correct_order": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocVarAnnotationCorrectOrderFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocVarAnnotationCorrectOrderFixer.php", + "source_sha256": "a912fc56fd3d48aa79bd686c3d6e369d54956c9aceb3083a6db8e8b03318c182" + }, + "phpdoc_var_without_name": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocVarWithoutNameFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocVarWithoutNameFixer.php", + "source_sha256": "f2aa48f9da61e922c34c2e20c0be84e30b7b30674b16a82961217120cd272657" + }, + "pow_to_exponentiation": { + "class": "PhpCsFixer\\Fixer\\Alias\\PowToExponentiationFixer", + "configuration": null, + "risky": true, + "priority": 32, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/PowToExponentiationFixer.php", + "source_sha256": "41f17abd4e427915f80482307effd75106bdf04d172bee92a25c2d771293d11c" + }, + "protected_to_private": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\ProtectedToPrivateFixer", + "configuration": null, + "risky": false, + "priority": 66, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/ProtectedToPrivateFixer.php", + "source_sha256": "44aa5008acd2ad50c7047dfe3ae51b2db31c9c2f79fffd2ecd3b373a557e467b" + }, + "psr_autoloading": { + "class": "PhpCsFixer\\Fixer\\Basic\\PsrAutoloadingFixer", + "configuration": { + "dir": null + }, + "risky": true, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/PsrAutoloadingFixer.php", + "source_sha256": "d0bb9c4f29bda80b79f752d7fccfb8233b6d711a1183f9227914f05fc6d9e65a" + }, + "random_api_migration": { + "class": "PhpCsFixer\\Fixer\\Alias\\RandomApiMigrationFixer", + "configuration": { + "replacements": { + "mt_rand": "random_int", + "rand": "random_int" + } + }, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/RandomApiMigrationFixer.php", + "source_sha256": "fefb85baa0672e0b68af3bcbcfd4a27c79ce0e572ec28ead844eaff4d8576fc3" + }, + "return_type_declaration": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\ReturnTypeDeclarationFixer", + "configuration": { + "space_before": "none" + }, + "risky": false, + "priority": -17, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/ReturnTypeDeclarationFixer.php", + "source_sha256": "e81dd34dfe5afbdc289d8103036ca5a03879198faab4f6adf0b10442a4313c04" + }, + "self_accessor": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\SelfAccessorFixer", + "configuration": null, + "risky": true, + "priority": -11, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/SelfAccessorFixer.php", + "source_sha256": "5dc470dd4700fad3ae2cc397eaaf35a4f250ccf781eeedf433d0956a100ff76e" + }, + "semicolon_after_instruction": { + "class": "PhpCsFixer\\Fixer\\Semicolon\\SemicolonAfterInstructionFixer", + "configuration": null, + "risky": false, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Semicolon/SemicolonAfterInstructionFixer.php", + "source_sha256": "aaaf1143441c7e5d18e51efed1b3bf26ac985ed686591021eb684e510782ee2b" + }, + "set_type_to_cast": { + "class": "PhpCsFixer\\Fixer\\Alias\\SetTypeToCastFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Alias/SetTypeToCastFixer.php", + "source_sha256": "93736d86147359c75b885e33c46043c397d014ac45ce3a65577fcf991363e939" + }, + "short_scalar_cast": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\ShortScalarCastFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/ShortScalarCastFixer.php", + "source_sha256": "7f1ffd1b64754f1800423554e27b35762e2b7bdfb55473dcabd3df28863085b9" + }, + "simple_to_complex_string_variable": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\SimpleToComplexStringVariableFixer", + "configuration": null, + "risky": false, + "priority": -10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/SimpleToComplexStringVariableFixer.php", + "source_sha256": "efdcefd7b1133f1e6ea1b6187971c7930241d0bde5a9daeb50540711f162efc7" + }, + "single_blank_line_at_eof": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\SingleBlankLineAtEofFixer", + "configuration": null, + "risky": false, + "priority": -100, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/SingleBlankLineAtEofFixer.php", + "source_sha256": "0a01c2d76a9c69d147d74f994fc4eea6ae8a5626b810e71d54066d424dbd6622" + }, + "single_blank_line_before_namespace": { + "class": "PhpCsFixer\\Fixer\\NamespaceNotation\\SingleBlankLineBeforeNamespaceFixer", + "configuration": null, + "risky": false, + "priority": -31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/NamespaceNotation/SingleBlankLineBeforeNamespaceFixer.php", + "source_sha256": "05433df0789798ace73411de446140a59c5adb78c065e4ae8b7950ab085feacb" + }, + "single_class_element_per_statement": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\SingleClassElementPerStatementFixer", + "configuration": { + "elements": [ + "const", + "property" + ] + }, + "risky": false, + "priority": 56, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/SingleClassElementPerStatementFixer.php", + "source_sha256": "078d0046895585c6d0dd5afa04d4e2ddba6ebedc3f42fb2c2cdbb82f8218afa9" + }, + "single_line_after_imports": { + "class": "PhpCsFixer\\Fixer\\Import\\SingleLineAfterImportsFixer", + "configuration": null, + "risky": false, + "priority": -11, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/SingleLineAfterImportsFixer.php", + "source_sha256": "07c2cfb6cf4c01a85ded48331235df7fc5b78fa060248c008580b924f070b9c9" + }, + "single_line_comment_spacing": { + "class": "PhpCsFixer\\Fixer\\Comment\\SingleLineCommentSpacingFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Comment/SingleLineCommentSpacingFixer.php", + "source_sha256": "7ed5a0bcc423c328346b20b8429ced786a2154b955bb1a86f322e5a5364aba5b" + }, + "single_line_comment_style": { + "class": "PhpCsFixer\\Fixer\\Comment\\SingleLineCommentStyleFixer", + "configuration": { + "comment_types": [ + "hash" + ] + }, + "risky": false, + "priority": -31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Comment/SingleLineCommentStyleFixer.php", + "source_sha256": "e8a008089081d27ee96ce118d45416e96f8be60569be56cf4fcaebd7680ce148" + }, + "single_line_empty_body": { + "class": "PhpCsFixer\\Fixer\\Basic\\SingleLineEmptyBodyFixer", + "configuration": null, + "risky": false, + "priority": -19, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/SingleLineEmptyBodyFixer.php", + "source_sha256": "45badb0ec953383fe28973f5fa30f81d133a62f6f309e9841ebb3df3bcad12f2" + }, + "single_line_throw": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\SingleLineThrowFixer", + "configuration": null, + "risky": false, + "priority": 36, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/SingleLineThrowFixer.php", + "source_sha256": "f4a3b872742912801212cfa66576c1b7eeb8e0b549d54942baac98b5cfdeb952" + }, + "single_quote": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\SingleQuoteFixer", + "configuration": { + "strings_containing_single_quote_chars": false + }, + "risky": false, + "priority": 10, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/SingleQuoteFixer.php", + "source_sha256": "79f1cc261920ea1d1954cd59dbb0655441045816e235fdee0cb035248324c81f" + }, + "single_space_around_construct": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\SingleSpaceAroundConstructFixer", + "configuration": { + "constructs_contain_a_single_space": [ + "yield_from" + ], + "constructs_followed_by_a_single_space": [ + "abstract", + "as", + "case", + "catch", + "class", + "const", + "const_import", + "do", + "else", + "elseif", + "enum", + "final", + "finally", + "for", + "foreach", + "function", + "function_import", + "if", + "insteadof", + "interface", + "match", + "named_argument", + "namespace", + "new", + "private", + "protected", + "public", + "readonly", + "static", + "switch", + "trait", + "try", + "type_colon", + "use", + "use_lambda", + "while" + ], + "constructs_preceded_by_a_single_space": [ + "as", + "else", + "elseif", + "use_lambda" + ] + }, + "risky": false, + "priority": 36, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/SingleSpaceAroundConstructFixer.php", + "source_sha256": "573dc6173f63a02653ea2efeb7b384431d9ce0acd0f69c38c820d89186a89824" + }, + "single_trait_insert_per_statement": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\SingleTraitInsertPerStatementFixer", + "configuration": null, + "risky": false, + "priority": 36, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/SingleTraitInsertPerStatementFixer.php", + "source_sha256": "74f1e59bb47faeb06b3058832e6bb372b9bcc0633b7fa7e4b1087b522a3bcbde" + }, + "space_after_semicolon": { + "class": "PhpCsFixer\\Fixer\\Semicolon\\SpaceAfterSemicolonFixer", + "configuration": { + "remove_in_empty_for_expressions": true + }, + "risky": false, + "priority": -1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Semicolon/SpaceAfterSemicolonFixer.php", + "source_sha256": "89c45242812d153ef1a77151d8389f4706b5b98675ba50f9296e06cf666f28f0" + }, + "spaces_inside_parentheses": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\SpacesInsideParenthesesFixer", + "configuration": { + "space": "none" + }, + "risky": false, + "priority": 3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/SpacesInsideParenthesesFixer.php", + "source_sha256": "f06fc4f4b9c390ca4429873cb1764022f96978cd7845674fdcf994c2f7782072" + }, + "standardize_increment": { + "class": "PhpCsFixer\\Fixer\\Operator\\StandardizeIncrementFixer", + "configuration": null, + "risky": false, + "priority": 16, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/StandardizeIncrementFixer.php", + "source_sha256": "effdd496ca1141a207063f67037c5a593792c39804f125978813b058f47cc57e" + }, + "standardize_not_equals": { + "class": "PhpCsFixer\\Fixer\\Operator\\StandardizeNotEqualsFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/StandardizeNotEqualsFixer.php", + "source_sha256": "0620d756686dc7804d1517819b5fc965cee8f4671c0dc9d0711af30e1b24a9d6" + }, + "statement_indentation": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\StatementIndentationFixer", + "configuration": { + "stick_comment_to_next_continuous_control_statement": false + }, + "risky": false, + "priority": -3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/StatementIndentationFixer.php", + "source_sha256": "240c3b141067c261c7376a17f78f9eb49215b70e6c0a46e87f6286354a9e25fd" + }, + "string_length_to_empty": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\StringLengthToEmptyFixer", + "configuration": null, + "risky": true, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/StringLengthToEmptyFixer.php", + "source_sha256": "699276e6bb37bb3c0143fdc1f1af29b94978f14f185296bed335987687926edc" + }, + "string_line_ending": { + "class": "PhpCsFixer\\Fixer\\StringNotation\\StringLineEndingFixer", + "configuration": null, + "risky": true, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/StringNotation/StringLineEndingFixer.php", + "source_sha256": "24f3de261b440bfe98371e7c85a04ff4e37970201295856b36a67d287a74b5a4" + }, + "switch_case_semicolon_to_colon": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\SwitchCaseSemicolonToColonFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixer.php", + "source_sha256": "cb68f2bc219a5a0310b50fc9f4b7822e6edcd7cb21c1e0e076e6203c09103e67" + }, + "switch_case_space": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\SwitchCaseSpaceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/SwitchCaseSpaceFixer.php", + "source_sha256": "54301f67edee670bd819a483fef4f4ffdafad0f0ed0b211ddf5f4e4eee7babf0" + }, + "switch_continue_to_break": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\SwitchContinueToBreakFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/SwitchContinueToBreakFixer.php", + "source_sha256": "f904b428d3e99e9bb29cd27493955d7ab086343fe14db8a35bf405961d102cf9" + }, + "ternary_operator_spaces": { + "class": "PhpCsFixer\\Fixer\\Operator\\TernaryOperatorSpacesFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/TernaryOperatorSpacesFixer.php", + "source_sha256": "9711d7a84c93a3e695d41c238f134f046f3edd56b88b0e129fd8fd25d3ce2740" + }, + "ternary_to_elvis_operator": { + "class": "PhpCsFixer\\Fixer\\Operator\\TernaryToElvisOperatorFixer", + "configuration": null, + "risky": true, + "priority": 2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/TernaryToElvisOperatorFixer.php", + "source_sha256": "960338df80695a41d4bafe7839cc08276b978342a8d001e99fad0d94ff18cc94" + }, + "ternary_to_null_coalescing": { + "class": "PhpCsFixer\\Fixer\\Operator\\TernaryToNullCoalescingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/TernaryToNullCoalescingFixer.php", + "source_sha256": "8328708d1424aa91aa2d46fb757de66122b37700174ad070e8e00ec809821513" + }, + "trailing_comma_in_multiline": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\TrailingCommaInMultilineFixer", + "configuration": { + "after_heredoc": true, + "elements": [ + "arrays" + ] + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/TrailingCommaInMultilineFixer.php", + "source_sha256": "cdec2305cca1b693205024ee4a31391ef3d9dbd9a343b060eebe01fa79dbe284" + }, + "trim_array_spaces": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\TrimArraySpacesFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/TrimArraySpacesFixer.php", + "source_sha256": "8f61f7d5f34471fc56e0ed7f2b88b7018d9ec177e4bb705acf09ea7cbd899bea" + }, + "type_declaration_spaces": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\TypeDeclarationSpacesFixer", + "configuration": { + "elements": [ + "function", + "property" + ] + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/TypeDeclarationSpacesFixer.php", + "source_sha256": "5322b200e33b20b7f9ae05d6c4132e1d4a37c58bf0116694dbc8743de8e9f372" + }, + "types_spaces": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\TypesSpacesFixer", + "configuration": { + "space": "none", + "space_multiple_catch": "none" + }, + "risky": false, + "priority": -1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/TypesSpacesFixer.php", + "source_sha256": "b59c1c7a3367222a94539bc3ea8fd1ea17248781fc967da8381710c274ff27bd" + }, + "unary_operator_spaces": { + "class": "PhpCsFixer\\Fixer\\Operator\\UnaryOperatorSpacesFixer", + "configuration": { + "only_dec_inc": true + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/UnaryOperatorSpacesFixer.php", + "source_sha256": "51796d2d05bc9405f218484f61e8f320943260fa7db4f008620590a161199394" + }, + "use_arrow_functions": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\UseArrowFunctionsFixer", + "configuration": null, + "risky": true, + "priority": 32, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/UseArrowFunctionsFixer.php", + "source_sha256": "78a81d2d7e370a0d2ed210b906239fe297b70498c2b5672c4311249dea908856" + }, + "visibility_required": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\VisibilityRequiredFixer", + "configuration": { + "elements": [ + "const", + "method", + "property" + ] + }, + "risky": false, + "priority": 56, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/VisibilityRequiredFixer.php", + "source_sha256": "498bd408f38b850f27966f350f28919c5527a8ede65ce0aa720d1d3b8866d475" + }, + "void_return": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\VoidReturnFixer", + "configuration": null, + "risky": true, + "priority": 5, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/VoidReturnFixer.php", + "source_sha256": "7a7787f2d8990402568bd9bdefa98882733ed4cea6f3e1d13c005d60b3ebcb98" + }, + "whitespace_after_comma_in_array": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\WhitespaceAfterCommaInArrayFixer", + "configuration": { + "ensure_single_space": false + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/WhitespaceAfterCommaInArrayFixer.php", + "source_sha256": "1105cd4853d10eac522f65df4130ecd3df2714ba00c2d8ce7dc1a44d7ebafe9d" + }, + "yoda_style": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\YodaStyleFixer", + "configuration": { + "always_move_variable": false, + "equal": true, + "identical": true, + "less_and_greater": null + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/YodaStyleFixer.php", + "source_sha256": "7927be5eab9d9d280c27e02aaa1c932893f88a61cbd1c92e9a427cc2bfea504d" + } + }, + "non_fixer_sniffs": [ + { + "class": "PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\VersionControl\\GitMergeConflictSniff", + "source_package": "squizlabs/php_codesniffer", + "source_file": "src/Standards/Generic/Sniffs/VersionControl/GitMergeConflictSniff.php", + "source_sha256": "86fa85cef2320f73f9d7d974335b17754eea8664b30d3eb645eafd89da66da96" + } + ] + }, + "standards": { + "source_commit": "9666d997c864e4e74b7ca0ca8cbce849091353a7", + "tool_packages": { + "symplify/easy-coding-standard": { + "version": "13.3.8", + "reference": "0564c0b9b807f1740bf0f2cc965ab51551f4dc3b" + } + }, + "packages": { + "friendsofphp/php-cs-fixer": { + "version": "v3.95.27", + "reference": "1d0c68ee5f37446d8ad5970b82384cd1bb0a3317" + } + }, + "rules": { + "binary_operator_spaces": { + "class": "PhpCsFixer\\Fixer\\Operator\\BinaryOperatorSpacesFixer", + "configuration": { + "default": "single_space", + "operators": [] + }, + "risky": false, + "priority": -32, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/BinaryOperatorSpacesFixer.php", + "source_sha256": "c66fba269691385a2d48ae8802bdb2c61b4d9f07ec5202de61687920fa229e94" + }, + "blank_line_after_namespace": { + "class": "PhpCsFixer\\Fixer\\NamespaceNotation\\BlankLineAfterNamespaceFixer", + "configuration": null, + "risky": false, + "priority": -20, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/NamespaceNotation/BlankLineAfterNamespaceFixer.php", + "source_sha256": "36568b2e5573d914026e2441d6db03c35074e85a7e731407c46196a6c22e48f5" + }, + "blank_line_after_opening_tag": { + "class": "PhpCsFixer\\Fixer\\PhpTag\\BlankLineAfterOpeningTagFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpTag/BlankLineAfterOpeningTagFixer.php", + "source_sha256": "6c4dfffc0bd4f20f2a82122778d0b84ee4506a28a455f17be56f4cc42ba398dd" + }, + "braces_position": { + "class": "PhpCsFixer\\Fixer\\Basic\\BracesPositionFixer", + "configuration": { + "allow_single_line_anonymous_functions": true, + "allow_single_line_empty_anonymous_classes": true, + "anonymous_classes_opening_brace": "same_line", + "anonymous_functions_opening_brace": "same_line", + "classes_opening_brace": "next_line_unless_newline_at_signature_end", + "control_structures_opening_brace": "same_line", + "functions_opening_brace": "next_line_unless_newline_at_signature_end" + }, + "risky": false, + "priority": -2, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/BracesPositionFixer.php", + "source_sha256": "b64f54041b08d310e5066a8782e43a0b87f6097c7c265c863a2417dce0d65473" + }, + "class_definition": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\ClassDefinitionFixer", + "configuration": { + "inline_constructor_arguments": true, + "multi_line_extends_each_single_line": false, + "single_item_single_line": false, + "single_line": false, + "space_before_parenthesis": false + }, + "risky": false, + "priority": 36, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/ClassDefinitionFixer.php", + "source_sha256": "78ecb1c633612c32b2d7dfd7c13256ca5950ff0b38dfd5140f31a4b9d98784f7" + }, + "concat_space": { + "class": "PhpCsFixer\\Fixer\\Operator\\ConcatSpaceFixer", + "configuration": { + "spacing": "one" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/ConcatSpaceFixer.php", + "source_sha256": "5c6b1a26ffffed6a22504b3f713397b9e081235a7bec4b6e960f637830028d6d" + }, + "constant_case": { + "class": "PhpCsFixer\\Fixer\\Casing\\ConstantCaseFixer", + "configuration": { + "case": "lower" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/ConstantCaseFixer.php", + "source_sha256": "552741fbd7e67b3b5f08890224335214c844f01fedca6db02a52895bc35da3b2" + }, + "control_structure_braces": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\ControlStructureBracesFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/ControlStructureBracesFixer.php", + "source_sha256": "72bf6c506161517caa42fd5d1673f5a7374ef884bdf73b8e4289619f56852493" + }, + "control_structure_continuation_position": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\ControlStructureContinuationPositionFixer", + "configuration": { + "position": "same_line" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/ControlStructureContinuationPositionFixer.php", + "source_sha256": "f659b3079bdea382dccc60f73dc69f55104ba5e31b7760b11312a755a1cbbbcf" + }, + "declare_equal_normalize": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\DeclareEqualNormalizeFixer", + "configuration": { + "space": "none" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/DeclareEqualNormalizeFixer.php", + "source_sha256": "9739fa47323e1e93a6b552c51d029013477154e94cae5d5532ad6dfffa866ac5" + }, + "declare_parentheses": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\DeclareParenthesesFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/DeclareParenthesesFixer.php", + "source_sha256": "c8365f538071518a1eac604e556ec33c698dc1ee2d5bf37640a80c42d372223e" + }, + "elseif": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\ElseifFixer", + "configuration": null, + "risky": false, + "priority": 40, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/ElseifFixer.php", + "source_sha256": "d5403740d0d230d2b9fb68bab71104ccf025dcafe6b88267949a5eb77fcfabfa" + }, + "encoding": { + "class": "PhpCsFixer\\Fixer\\Basic\\EncodingFixer", + "configuration": null, + "risky": false, + "priority": 100, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/EncodingFixer.php", + "source_sha256": "e1baeb52fb79cb606c3edda213b14993460eeea30b4f12573e5b9af6aa9bf26c" + }, + "full_opening_tag": { + "class": "PhpCsFixer\\Fixer\\PhpTag\\FullOpeningTagFixer", + "configuration": null, + "risky": false, + "priority": 98, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpTag/FullOpeningTagFixer.php", + "source_sha256": "866f27065893c15e4169eaa520d2e5bdd1cd7841527d1494426e422179c2c86c" + }, + "function_declaration": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\FunctionDeclarationFixer", + "configuration": { + "closure_fn_spacing": "one", + "closure_function_spacing": "one", + "trailing_comma_single_line": false + }, + "risky": false, + "priority": 31, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/FunctionDeclarationFixer.php", + "source_sha256": "01c32728a267c86e5f1f7f901d40ad09cb4904d7c93bb022489618d6b4f6c04f" + }, + "indentation_type": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\IndentationTypeFixer", + "configuration": null, + "risky": false, + "priority": 50, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/IndentationTypeFixer.php", + "source_sha256": "b34108c007caa230fe7903f65ab2231afaaaad8b6bfd57743d070b9eafebdd38" + }, + "line_ending": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\LineEndingFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/LineEndingFixer.php", + "source_sha256": "4815f5ea0302a00c494f1b5dde71bc2068424dab07a4b7331d103a7d294a0920" + }, + "lowercase_cast": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\LowercaseCastFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/LowercaseCastFixer.php", + "source_sha256": "c0f0b3a210ec4fd05070c27087893ff54ac175c26f27646c35be3c69e742ee5b" + }, + "lowercase_keywords": { + "class": "PhpCsFixer\\Fixer\\Casing\\LowercaseKeywordsFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Casing/LowercaseKeywordsFixer.php", + "source_sha256": "a4cf74db36d7b86f79121be058ad43feddaf01a66db6cf215c8972118011a976" + }, + "method_argument_space": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\MethodArgumentSpaceFixer", + "configuration": { + "after_heredoc": false, + "attribute_placement": "standalone", + "keep_multiple_spaces_after_comma": false, + "on_multiline": "ignore" + }, + "risky": false, + "priority": 30, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/MethodArgumentSpaceFixer.php", + "source_sha256": "ae0e4a7dea35fc3fbdb78fbc6bd4cf2d8d36ce1e0882d8f83684009a6f8af547" + }, + "new_with_parentheses": { + "class": "PhpCsFixer\\Fixer\\Operator\\NewWithParenthesesFixer", + "configuration": { + "anonymous_class": true, + "named_class": true + }, + "risky": false, + "priority": 38, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/NewWithParenthesesFixer.php", + "source_sha256": "c650e536a52f28d3a9c8092ec56671ce2b4cf5860f91dc36a3562d873da20f0f" + }, + "no_blank_lines_after_class_opening": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\NoBlankLinesAfterClassOpeningFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/NoBlankLinesAfterClassOpeningFixer.php", + "source_sha256": "04fe64d3a0c3b3e53605cead533b4e45a25f5ab693b75db030a4b044abb1e994" + }, + "no_break_comment": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\NoBreakCommentFixer", + "configuration": { + "comment_text": "no break" + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/NoBreakCommentFixer.php", + "source_sha256": "d82a048de5cfe482b6f118e1b19b240300ea951ec2ca48b51e9423de0f4142f3" + }, + "no_closing_tag": { + "class": "PhpCsFixer\\Fixer\\PhpTag\\NoClosingTagFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/PhpTag/NoClosingTagFixer.php", + "source_sha256": "255f766df468a23c550812d29b1070a9a1fa2aca3c5b06a140af93680a196c9c" + }, + "no_extra_blank_lines": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\NoExtraBlankLinesFixer", + "configuration": { + "tokens": [ + "curly_brace_block" + ] + }, + "risky": false, + "priority": -20, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/NoExtraBlankLinesFixer.php", + "source_sha256": "fea05b231893b2b6b67770314e3c6ae4001b97183fbc4486ae4606899061012a" + }, + "no_leading_import_slash": { + "class": "PhpCsFixer\\Fixer\\Import\\NoLeadingImportSlashFixer", + "configuration": null, + "risky": false, + "priority": -20, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/NoLeadingImportSlashFixer.php", + "source_sha256": "c17a9f49e2d0363dcf8f9bf50ed0901288b72cbed9c55765b4cd6175f30cdb0b" + }, + "no_multiple_statements_per_line": { + "class": "PhpCsFixer\\Fixer\\Basic\\NoMultipleStatementsPerLineFixer", + "configuration": null, + "risky": false, + "priority": -1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Basic/NoMultipleStatementsPerLineFixer.php", + "source_sha256": "572ff11d4c76233669e6abd2fe79986b19c4ea0f67ceee1d70865abd78db018c" + }, + "no_singleline_whitespace_before_semicolons": { + "class": "PhpCsFixer\\Fixer\\Semicolon\\NoSinglelineWhitespaceBeforeSemicolonsFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Semicolon/NoSinglelineWhitespaceBeforeSemicolonsFixer.php", + "source_sha256": "b271b195ac981fe715cba150bb0fd72faf19d42848727b63145b191c80dc1f1f" + }, + "no_spaces_after_function_name": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\NoSpacesAfterFunctionNameFixer", + "configuration": null, + "risky": false, + "priority": 3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/NoSpacesAfterFunctionNameFixer.php", + "source_sha256": "8ce69fe2acb3aaf8277f054b2cb281ad2d5aa0f69871a809c5afade36578289e" + }, + "no_trailing_whitespace": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\NoTrailingWhitespaceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/NoTrailingWhitespaceFixer.php", + "source_sha256": "e1e5e926343546dab15ee2b6b6fd6b282752628f45268995566de2c5af9ac8bd" + }, + "no_trailing_whitespace_in_comment": { + "class": "PhpCsFixer\\Fixer\\Comment\\NoTrailingWhitespaceInCommentFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Comment/NoTrailingWhitespaceInCommentFixer.php", + "source_sha256": "61df40776445c0ff4a2dcbc28ec243ff8e8d6a27f5bf841cee2a61d05375b949" + }, + "no_whitespace_before_comma_in_array": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\NoWhitespaceBeforeCommaInArrayFixer", + "configuration": { + "after_heredoc": false + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/NoWhitespaceBeforeCommaInArrayFixer.php", + "source_sha256": "9125783c0b837accae72118b614c7b01924406b54891883dc94103efba1e94a5" + }, + "ordered_imports": { + "class": "PhpCsFixer\\Fixer\\Import\\OrderedImportsFixer", + "configuration": { + "case_sensitive": false, + "imports_order": [ + "class", + "function", + "const" + ], + "sort_algorithm": "alpha" + }, + "risky": false, + "priority": -30, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/OrderedImportsFixer.php", + "source_sha256": "7d160e62f1ac6982da4534d5692508ae32db60f45606334917d70401132e4bc0" + }, + "phpdoc_align": { + "class": "PhpCsFixer\\Fixer\\Phpdoc\\PhpdocAlignFixer", + "configuration": { + "align": "left", + "spacing": 1, + "tags": [ + "method", + "param", + "property", + "return", + "throws", + "type", + "var" + ] + }, + "risky": false, + "priority": -42, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Phpdoc/PhpdocAlignFixer.php", + "source_sha256": "0c591c0be890849ee505443db9fed40e6bcb058402821910eed8144d2a91b9a5" + }, + "return_type_declaration": { + "class": "PhpCsFixer\\Fixer\\FunctionNotation\\ReturnTypeDeclarationFixer", + "configuration": { + "space_before": "none" + }, + "risky": false, + "priority": -17, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/FunctionNotation/ReturnTypeDeclarationFixer.php", + "source_sha256": "d523008bd35654ef49ffbf3562bb0ae57e4922e29024dd8989cabc1dd670adb3" + }, + "short_scalar_cast": { + "class": "PhpCsFixer\\Fixer\\CastNotation\\ShortScalarCastFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/CastNotation/ShortScalarCastFixer.php", + "source_sha256": "fbde3e3a82e975cf4bd6cd33b738f7bd0803de1ce12c1aacb9d2ce5a4819ada7" + }, + "single_blank_line_at_eof": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\SingleBlankLineAtEofFixer", + "configuration": null, + "risky": false, + "priority": -100, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/SingleBlankLineAtEofFixer.php", + "source_sha256": "317a5837f98406bd97a28728419f790a7cc5c69ebf987db85d801a13e44e857b" + }, + "single_class_element_per_statement": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\SingleClassElementPerStatementFixer", + "configuration": { + "elements": [ + "property" + ] + }, + "risky": false, + "priority": 56, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/SingleClassElementPerStatementFixer.php", + "source_sha256": "98f35844c2e04955f227010cb9be6c851a01ca262ddd3665b25e9eb4294b113a" + }, + "single_line_after_imports": { + "class": "PhpCsFixer\\Fixer\\Import\\SingleLineAfterImportsFixer", + "configuration": null, + "risky": false, + "priority": -11, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Import/SingleLineAfterImportsFixer.php", + "source_sha256": "eadb27866ffe7edb5d86c42b5f612b98ea0dc0c715dc3591eb94b181c11b14b5" + }, + "single_space_around_construct": { + "class": "PhpCsFixer\\Fixer\\LanguageConstruct\\SingleSpaceAroundConstructFixer", + "configuration": { + "constructs_contain_a_single_space": [ + "yield_from" + ], + "constructs_followed_by_a_single_space": [ + "abstract", + "as", + "attribute", + "break", + "case", + "catch", + "class", + "clone", + "comment", + "const", + "const_import", + "continue", + "do", + "echo", + "else", + "elseif", + "enum", + "extends", + "final", + "finally", + "for", + "foreach", + "function", + "function_import", + "global", + "goto", + "if", + "implements", + "include", + "include_once", + "instanceof", + "insteadof", + "interface", + "match", + "named_argument", + "namespace", + "new", + "open_tag_with_echo", + "php_doc", + "php_open", + "print", + "private", + "private_set", + "protected", + "protected_set", + "public", + "public_set", + "readonly", + "require", + "require_once", + "return", + "static", + "switch", + "throw", + "trait", + "try", + "type_colon", + "use", + "use_lambda", + "use_trait", + "var", + "while", + "yield", + "yield_from" + ], + "constructs_preceded_by_a_single_space": [ + "as", + "use_lambda" + ] + }, + "risky": false, + "priority": 36, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/LanguageConstruct/SingleSpaceAroundConstructFixer.php", + "source_sha256": "0509d92d4d33746c25ce076f56c9a7cd0ecb6ce4d03b54d5911a889b5804bf10" + }, + "spaces_inside_parentheses": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\SpacesInsideParenthesesFixer", + "configuration": { + "space": "none" + }, + "risky": false, + "priority": 3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/SpacesInsideParenthesesFixer.php", + "source_sha256": "b96e4d25b727b156c45d33de773e1a6ae563cd0adf0352a903b063c409204ccb" + }, + "statement_indentation": { + "class": "PhpCsFixer\\Fixer\\Whitespace\\StatementIndentationFixer", + "configuration": { + "stick_comment_to_next_continuous_control_statement": false + }, + "risky": false, + "priority": -3, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Whitespace/StatementIndentationFixer.php", + "source_sha256": "f35771a2be31aa9df716d5dbc21b7c827cd1a14fa99303a08ec6dfdd429823e3" + }, + "switch_case_semicolon_to_colon": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\SwitchCaseSemicolonToColonFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixer.php", + "source_sha256": "1372653f3df1d0cbf386e648aee917de8776534e5c8a6f528fab29ee70c5fbd0" + }, + "switch_case_space": { + "class": "PhpCsFixer\\Fixer\\ControlStructure\\SwitchCaseSpaceFixer", + "configuration": null, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ControlStructure/SwitchCaseSpaceFixer.php", + "source_sha256": "ceec144cec79f010541155f38234c2f98e417967a4360abf7b44e9fe798799fd" + }, + "ternary_operator_spaces": { + "class": "PhpCsFixer\\Fixer\\Operator\\TernaryOperatorSpacesFixer", + "configuration": null, + "risky": false, + "priority": 1, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/TernaryOperatorSpacesFixer.php", + "source_sha256": "2b592dd9d839959c19c6ed6c08064240d2eb74fa8441af129be7862f991692c8" + }, + "unary_operator_spaces": { + "class": "PhpCsFixer\\Fixer\\Operator\\UnaryOperatorSpacesFixer", + "configuration": { + "only_dec_inc": false + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/Operator/UnaryOperatorSpacesFixer.php", + "source_sha256": "8582e23a8ec2038dc60af1335261130b7fec021f2196b8fa42789024a0eea8da" + }, + "visibility_required": { + "class": "PhpCsFixer\\Fixer\\ClassNotation\\VisibilityRequiredFixer", + "configuration": { + "elements": [ + "const", + "method", + "property" + ] + }, + "risky": false, + "priority": 56, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ClassNotation/VisibilityRequiredFixer.php", + "source_sha256": "89159d11b41cf0c3360d0e15ac92c8ce07f5353b9d74fc0fe4672e7be046fb90" + }, + "whitespace_after_comma_in_array": { + "class": "PhpCsFixer\\Fixer\\ArrayNotation\\WhitespaceAfterCommaInArrayFixer", + "configuration": { + "ensure_single_space": false + }, + "risky": false, + "priority": 0, + "source_package": "friendsofphp/php-cs-fixer", + "source_file": "src/Fixer/ArrayNotation/WhitespaceAfterCommaInArrayFixer.php", + "source_sha256": "c93db0857bc1f8017add8e4c0860784152d00ac841ffe9017e6f0695c1901237" + } + }, + "non_fixer_sniffs": [] + } + } +} From 6e99a549e356cb302bcfc1078a6ba9d3215624b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 17:10:39 -0300 Subject: [PATCH 13/17] fix: preserve checkout root whitespace during Git selection --- .changelog/git-root-whitespace.md | 6 ++++++ src/Git/FileSelection.php | 5 +++-- tests/Integration/StandardsTest.php | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 .changelog/git-root-whitespace.md diff --git a/.changelog/git-root-whitespace.md b/.changelog/git-root-whitespace.md new file mode 100644 index 0000000..2b95829 --- /dev/null +++ b/.changelog/git-root-whitespace.md @@ -0,0 +1,6 @@ +--- +category: fixed +type: patch +--- + +Preserve whitespace and control bytes in checkout root names while still rejecting nested project directories. diff --git a/src/Git/FileSelection.php b/src/Git/FileSelection.php index b80d72c..b57ce88 100644 --- a/src/Git/FileSelection.php +++ b/src/Git/FileSelection.php @@ -21,8 +21,9 @@ public function __construct(private readonly ProcessRunner $process) */ public function select(string $directory, ?string $base, ?string $head, ?string $config = null): array { - $top = trim($this->process->capture(['git', 'rev-parse', '--show-toplevel'], $directory)); - if (realpath($top) !== realpath($directory)) { + // A nested prefix always ends in '/'; no absolute pathname needs trimming or unquoting. + $prefix = rtrim($this->process->capture(['git', 'rev-parse', '--show-prefix'], $directory), "\r\n"); + if ('' !== $prefix) { throw new \InvalidArgumentException('Project directory must be the Git checkout root.'); } if (null === $base && null === $head) { diff --git a/tests/Integration/StandardsTest.php b/tests/Integration/StandardsTest.php index eb981cd..c1d52b3 100644 --- a/tests/Integration/StandardsTest.php +++ b/tests/Integration/StandardsTest.php @@ -75,6 +75,25 @@ public function testChangedFilesUseMergeBaseDestinationsAndNulSafePaths(): void self::assertContains($this->directory . '/-leading.php', $filtered); } + /** Checks trailing root-path spaces/control bytes without weakening nested-checkout rejection. */ + public function testCheckoutRootWhitespaceIsPreservedInAllAndChangedModes(): void + { + $renamed = $this->directory . " \t\n"; + rename($this->directory, $renamed); + $this->directory = $renamed; + + $base = $this->commit(); + $source = "put('source.php', $source); + $head = $this->commit(); + $selection = new FileSelection(new NativeProcessRunner()); + + self::assertSame(['source.php'], $selection->select($this->directory, null, null)); + self::assertSame(['source.php'], $selection->select($this->directory, $base, $head)); + self::assertSame(0, $this->cli(['check', '--all'])->getExitCode()); + self::assertSame($source, file_get_contents($this->directory . '/source.php')); + } + /** Ensures removing a policy file cannot limit checking to an empty changed-file set. */ public function testDeletedConfigurationStillTriggersFullScan(): void { From accfc7deb58aee8ca9cdd4da5ddfb55851ed07af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 17:30:56 -0300 Subject: [PATCH 14/17] docs: clarify successful empty PHP selection --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 97eb656..aa98c3c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ composer test `check` reports violations with a nonzero status and does not change source. `fix` explicitly applies ECS fixes. It does not commit, push or update dependencies. Recheck and inspect the diff afterward; failures can leave already applied fixes. -No selected PHP files is a successful no-op. Invalid options, configuration paths, +An empty PHP selection is a successful no-op. Invalid options, configuration paths, Git history or head mismatch fail closed with exit 2. Syntax/style tool failures, including executable configuration errors inside ECS, retain their tool status. Changed mode requires a clean tracked checkout of the requested head. From 054a66913bd8f3726ae285688c4bcd5a3ae5ab0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 17:41:45 -0300 Subject: [PATCH 15/17] fix: reject Git metadata and bare roots explicitly --- .changelog/git-root-whitespace.md | 2 +- src/Git/FileSelection.php | 22 +++++++++++++++++----- tests/Integration/StandardsTest.php | 13 +++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/.changelog/git-root-whitespace.md b/.changelog/git-root-whitespace.md index 2b95829..81a9277 100644 --- a/.changelog/git-root-whitespace.md +++ b/.changelog/git-root-whitespace.md @@ -3,4 +3,4 @@ category: fixed type: patch --- -Preserve whitespace and control bytes in checkout root names while still rejecting nested project directories. +Preserve whitespace and control bytes in checkout root names while rejecting nested, metadata and bare repository directories. diff --git a/src/Git/FileSelection.php b/src/Git/FileSelection.php index b57ce88..d91dd73 100644 --- a/src/Git/FileSelection.php +++ b/src/Git/FileSelection.php @@ -21,11 +21,7 @@ public function __construct(private readonly ProcessRunner $process) */ public function select(string $directory, ?string $base, ?string $head, ?string $config = null): array { - // A nested prefix always ends in '/'; no absolute pathname needs trimming or unquoting. - $prefix = rtrim($this->process->capture(['git', 'rev-parse', '--show-prefix'], $directory), "\r\n"); - if ('' !== $prefix) { - throw new \InvalidArgumentException('Project directory must be the Git checkout root.'); - } + $this->assertCheckoutRoot($directory); if (null === $base && null === $head) { return $this->paths($this->process->capture(['git', 'ls-files', '-z'], $directory)); } @@ -60,6 +56,22 @@ public function select(string $directory, ?string $base, ?string $head, ?string ], $directory)); } + /** + * Validates Git's scope metadata without interpreting the absolute root pathname. + * + * @throws \InvalidArgumentException When the directory is nested, bare or inside Git metadata. + */ + private function assertCheckoutRoot(string $directory): void + { + $inside = trim($this->process->capture(['git', 'rev-parse', '--is-inside-work-tree'], $directory)); + // A nested prefix always ends in '/'; removing line terminators cannot turn it into a root. + $prefix = rtrim($this->process->capture(['git', 'rev-parse', '--show-prefix'], $directory), "\r\n"); + + if ('true' !== $inside || '' !== $prefix) { + throw new \InvalidArgumentException('Project directory must be the Git checkout root.'); + } + } + /** @return list Decodes Git's binary path protocol without trimming file names. */ private function paths(string $output): array { diff --git a/tests/Integration/StandardsTest.php b/tests/Integration/StandardsTest.php index c1d52b3..9d350a1 100644 --- a/tests/Integration/StandardsTest.php +++ b/tests/Integration/StandardsTest.php @@ -94,6 +94,19 @@ public function testCheckoutRootWhitespaceIsPreservedInAllAndChangedModes(): voi self::assertSame($source, file_get_contents($this->directory . '/source.php')); } + /** Rejects metadata/bare repositories instead of silently checking an empty source selection. */ + public function testGitMetadataAndBareRepositoriesAreNotSourceCheckoutRoots(): void + { + $this->git(['init', '--bare', '--initial-branch=main', 'bare.git']); + + foreach (['.git', 'bare.git'] as $directory) { + $check = $this->cli(['check', '--all', '--directory=' . $this->directory . '/' . $directory]); + + self::assertSame(2, $check->getExitCode(), $directory); + self::assertStringContainsString('Git checkout root', $check->getErrorOutput(), $directory); + } + } + /** Ensures removing a policy file cannot limit checking to an empty changed-file set. */ public function testDeletedConfigurationStillTriggersFullScan(): void { From 9d4b708c8555beb8f6362285a51b2161fc94d65e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Wed, 21 Oct 2026 18:38:06 -0300 Subject: [PATCH 16/17] docs: bound PHPDoc and analyzer extraction policies --- .changelog/source-analysis-boundaries.md | 7 +++ AGENTS.md | 2 + docs/phpdoc-comparison.md | 55 ++++++++++++++++ docs/policy.md | 5 ++ docs/static-analysis-evaluation.md | 80 ++++++++++++++++++++++++ 5 files changed, 149 insertions(+) create mode 100644 .changelog/source-analysis-boundaries.md create mode 100644 docs/phpdoc-comparison.md create mode 100644 docs/static-analysis-evaluation.md diff --git a/.changelog/source-analysis-boundaries.md b/.changelog/source-analysis-boundaries.md new file mode 100644 index 0000000..1740aab --- /dev/null +++ b/.changelog/source-analysis-boundaries.md @@ -0,0 +1,7 @@ +--- +category: changed +type: patch +--- + +Documents the legacy PHPDoc rule activation and future analyzer boundaries, +including native whole-project PHPStan scope, without enabling new policies. diff --git a/AGENTS.md b/AGENTS.md index 88037c4..2fd31bc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,6 +61,8 @@ for the exact local base/head before publication; do not claim remote CI evidenc - [README.md](README.md): library/CLI adoption, explicit fix and tool scope. - [Policy comparison](docs/policy.md): evidence, rule differences and extraction gates. +- [PHPDoc comparison](docs/phpdoc-comparison.md): actual custom-rule registration, source contracts and remaining golden decisions. +- [Static-analysis evaluation](docs/static-analysis-evaluation.md): candidate engines, whole-project analysis and unresolved domain ownership. - [Architecture decision](docs/architecture.md): library/tool dependencies, CPX/autoload limits and bootstrap without circular prerequisites. - [Automation](docs/automation.md): trust, ref/bootstrap contracts and consumer adoption. - [Instruction map](docs/instruction-map.md): single root scope and skill ownership. diff --git a/docs/phpdoc-comparison.md b/docs/phpdoc-comparison.md new file mode 100644 index 0000000..5a9b0e9 --- /dev/null +++ b/docs/phpdoc-comparison.md @@ -0,0 +1,55 @@ +# Source PHPDoc: legacy rules and extraction boundary + +Fixed DevTools source `35b5f78ac63813ec07aa3fb3eda08751541e28e1`; source inventory +for extraction planning. These observations do not establish runtime parity. +Potential edge cases below are future fixture requirements, not reproduced bugs +or an adopted rule profile. + +## Registration matters + +There are three custom classes, but they are not all enabled by the inspected +default. `Config/RectorConfig::DEFAULT_RULES` registers +AddMissingMethodPhpDocRector and RemoveEmptyDocBlockRector, plus external Faker +GeneratorPropertyFetchToMethodCallRector. AddMissingClassPhpDocRector exists but +is absent from that default list. Do not equate class presence with activation. + +The config also enables broad DEAD_CODE/CODE_QUALITY/CODING_STYLE/ +TYPE_DECLARATION/PRIVATIZATION/INSTANCEOF/EARLY_RETURN sets, derives PHP migration +sets from consumer Composer, enables name import/removal, and conditionally loads +Safe migrations. These effects must not come along implicitly with a PHPDoc +extraction. Customization runs last in the native Rector callback. + +## Observed class contracts + +| Class | Inspected behavior | Required future goldens before selecting policy | +| --- | --- | --- | +| AddMissingClassPhpDocRector | Targets Class_ only, skips any existing Doc; generates class-name summary and namespace @package when available | Named/anonymous/namespaced classes; preserve useful existing prose/tags; choose whether generated boilerplate adds useful human information | +| AddMissingMethodPhpDocRector | Targets ClassMethod; skips the entire method when any Doc exists; creates @param from native types or mixed, @return except constructors, and @throws from literal new named exceptions found recursively | Existing partial docs vs absent docs, typed/untyped/nullable/union/intersection/DNF, variadic/reference parameters, constructors, abstract methods, nested closures and exception-name imports | +| RemoveEmptyDocBlockRector | Targets Class_/ClassMethod only; removes whitespace/star-only Doc; preserves other comment objects and clears Rector/AST doc attributes | Empty vs meaningful prose/tags, other comments, declaration docs on interfaces/traits/enums and docs on properties/functions outside this target set, methods inside interfaces/traits/enums still targeted as ClassMethod, repeated passes and native Rector cache/API compatibility | + +These observations derive from the exact source, not generated example output. +In particular, the method rule does not fill missing tags in an existing partial +docblock, despite a broad reading of its description. Recursive throw traversal +and type rendering require dedicated fixture decisions; no unsupported claim of +precise escaping-exception inference or all-syntax correctness is made here. + +## Extraction boundary + +The initial Code Standards preset preserves existing PHPDoc and does not promise +useful documentation generation by restating syntax. Select human-useful policy +before copying any algorithm. Keep any later semantic operation explicit, +opt-in and target-PHP aware, separate from default check/fix. Reuse native APIs +and focused services rather than importing legacy broad configuration or a DSL. + +Future comparative goldens must isolate each selected rule first, then test the +selected combined ordering against ECS. Assert source-readonly check, explicit +write/second-pass idempotence, exact exits, comments/attribute spacing and config +override precedence. Do not execute automatic Safe/Faker/PHP migrations as part +of that PHPDoc comparison. Detailed engine compatibility and behavior remain +unverified; this inventory does not close issue5 or issue4. + +Immutable source links: +- https://github.com/php-fast-forward/dev-tools/blob/35b5f78ac63813ec07aa3fb3eda08751541e28e1/src/Config/RectorConfig.php +- https://github.com/php-fast-forward/dev-tools/blob/35b5f78ac63813ec07aa3fb3eda08751541e28e1/src/Rector/AddMissingClassPhpDocRector.php +- https://github.com/php-fast-forward/dev-tools/blob/35b5f78ac63813ec07aa3fb3eda08751541e28e1/src/Rector/AddMissingMethodPhpDocRector.php +- https://github.com/php-fast-forward/dev-tools/blob/35b5f78ac63813ec07aa3fb3eda08751541e28e1/src/Rector/RemoveEmptyDocBlockRector.php diff --git a/docs/policy.md b/docs/policy.md index e7aa4b7..846fbc3 100644 --- a/docs/policy.md +++ b/docs/policy.md @@ -46,3 +46,8 @@ Composer metadata normalization and existing public DevTools APIs stay untouched Static quality in this first increment means parseable PHP and formatting conformance. Type analysis, dead-code analysis and semantic refactors are not claimed. Application tests, coverage and complete reports remain outside this domain. + +The [PHPDoc comparison](phpdoc-comparison.md) records actual custom Rector +activation and the fixtures required before adopting a documentation policy. +The [static-analysis evaluation](static-analysis-evaluation.md) distinguishes +candidate engines and their analysis scopes without deciding domain ownership. diff --git a/docs/static-analysis-evaluation.md b/docs/static-analysis-evaluation.md new file mode 100644 index 0000000..1d924d6 --- /dev/null +++ b/docs/static-analysis-evaluation.md @@ -0,0 +1,80 @@ +# Static analysis: candidates and ownership boundary + +This source comparison records candidates without deciding ownership or +implementing an analyzer. DevTools issues +[#14](https://github.com/php-fast-forward/dev-tools/issues/14), +[#265](https://github.com/php-fast-forward/dev-tools/issues/265), +[#266](https://github.com/php-fast-forward/dev-tools/issues/266) and +[#267](https://github.com/php-fast-forward/dev-tools/issues/267) remain open. +These decisions are not prerequisites for the initial 0.1.0 style/syntax profile. + +## Existing source + +Inspected DevTools `35b5f78ac63813ec07aa3fb3eda08751541e28e1`. Bounded searches of +composer.json, src/, resources/, .github/ and README found no declarations of +PHPStan/Psalm/PHPat/php-assumptions/TypePerfect/security-audit engines or a +dedicated command for these candidate source analyzers. Existing Dependencies +and Metrics commands serve other contracts. PHPStan/Psalm filenames occur in +GitAttributes CandidateProvider only. +This is evidence of the searched surfaces, not a proof about every historical +branch or generated dependency. No source file was changed or scan executed. + +## Actual candidate engines + +Moving upstream default-branch manifests read via the authorized GitHub connector +on 2026-10-21 at 21:12 UTC, with exact file blob hashes below. These are not selected published +package versions or verified runtime compatibility claims. + +| Source issue | Candidate and real upstream | Observed contract | Consequence | +| --- | --- | --- | --- | +| DevTools14 | phpstan/phpstan | Dedicated analyzer/PHAR; native analyse/configuration/level/error-format/memory-limit | Prefer native settings and exits; no generic multi-engine abstraction before a second real requirement | +| DevTools265 | rskuipers/php-assumptions | Standalone bin/phpa; php-parser4/5 + league/climate; README calls it a proof of concept | Not a PHPStan extension or runtime assertion framework on this evidence; identify intended package/version and test actual exits/target syntax before integration | +| DevTools266 | carlosas/phpat, package phpat/phpat | type phpstan-extension; PHP^8.1, PHPStan^2.1; extension.neon registered in extra | Real PHPStan extension; its development Psalm dependency is not a consumer runtime engine | +| DevTools267 | FloeDesignTechnologies/phpcs-security-audit, package pheromone/phpcs-security-audit | type phpcodesniffer-standard; PHP_CodeSniffer^3.1.0 + DealerDirect installer plugin; manifest declares GPL-3.0-or-later | Premise that this is a PHPStan security extension is incorrect; assess separately, without automatic plugin activation, mandatory adoption or inferred compatibility with modern PHPCS | + +Manifest blob hashes: PHPStan `a267d4c50d8cd59cd63866d4c8b04a25f0a24627`; +assumptions `6037a1c9b39737581f74577f513d18333295354f`; +PHPat `055245974b55aa70c3a59954d373dc95c938b8e9`; +security-audit `a2a72a7b98d94d0e65e151f470dcc5715d9306a0`. +Security-audit README blob `63be29bacf78614110c556dfad68eafda4b140c4` +explicitly describes PHPCS invocation and significant noise; no scanner was run. + +Primary manifests: +[PHPStan](https://github.com/phpstan/phpstan/blob/2.3.x/composer.json), +[assumptions](https://github.com/rskuipers/php-assumptions/blob/master/composer.json), +[PHPat](https://github.com/carlosas/phpat/blob/main/composer.json), +[security-audit](https://github.com/FloeDesignTechnologies/phpcs-security-audit/blob/master/composer.json). + +## Changed-file semantics need a distinct decision + +PHPStan explicitly recommends a stable whole-project analysed path set and uses +its native result cache/dependency tree to revisit dependents of changed symbols. +Therefore the ECS changed-file list must not be presented as equivalent type +analysis. A changed method signature can invalidate unchanged callers; checking +only the changed source would not establish those callers' validity. This last +example is an architectural inference, not an executed regression result. +If a restricted mode is retained later, name its limits explicitly and require +cross-file dependency regressions; native whole-project analysis is the proposed +default for an eventual PHPStan slice. No new CLI option was implemented. +[Official result-cache contract](https://phpstan.org/user-guide/result-cache). + +PHPStan resolves symbols from analysed paths and the consumer Composer graph; +custom bootstrap/autoload PHP actually executes. A separately installed tool +does not remove that execution or namespace boundary. Preserve consumer NEON, +imports and baselines; use explicit target-PHP/config selection, unprivileged +processes, isolated tool cache and no incidental Composer scripts/plugins or +baseline writes. Test that checkout symbols win over an installed library with +the same namespace before adoption. +[Symbol discovery](https://phpstan.org/user-guide/discovering-symbols), +[native CLI](https://phpstan.org/user-guide/command-line-usage). + +## Remaining gates + +- Decide Code Standards ownership versus another tool before closing or moving + the four DevTools candidates. Do not implement every optional engine at once. +- Select actual published versions, inspect licenses/compatibility and prove + execution/format/exit/read-only behavior with synthetic cross-file fixtures. +- Keep TypePerfect semantic changes opt-in and independently scoped; its actual + package/groups remain to verify rather than copying an issue's assumption. +- Preserve current first-release boundary: syntax + non-risky ECS style only; + tests/coverage/full reporting remain other domains. No consumer migration. From 8b46c13481d35a39e6c01ab13647509a15a274aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Say=C3=A3o=20Lobato=20Abreu?= Date: Sat, 24 Oct 2026 09:11:45 -0300 Subject: [PATCH 17/17] fix: isolate ECS caches between checkouts --- .changelog/checkout-cache-isolation.md | 7 ++++ README.md | 5 +++ src/Config/ECSConfig.php | 14 +++++++ tests/Integration/StandardsTest.php | 51 ++++++++++++++++++++++++-- 4 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 .changelog/checkout-cache-isolation.md diff --git a/.changelog/checkout-cache-isolation.md b/.changelog/checkout-cache-isolation.md new file mode 100644 index 0000000..4dfc797 --- /dev/null +++ b/.changelog/checkout-cache-isolation.md @@ -0,0 +1,7 @@ +--- +category: fixed +type: patch +--- + +Isolate the default ECS cache by executing checkout so another project's +configuration invalidation cannot erase its cache, preserving explicit overrides. diff --git a/README.md b/README.md index aa98c3c..cff409d 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,11 @@ composer test ``` `check` reports violations with a nonzero status and does not change source. +The default preset stores its ECS cache beneath the system temporary directory, +in `fast-forward-code-standards/`. +Different checkouts cannot invalidate each other's cache. Native +`withCache()` overrides remain supported and apply last; share cache directories +only when their invalidation and concurrent use are intentionally coordinated. `fix` explicitly applies ECS fixes. It does not commit, push or update dependencies. Recheck and inspect the diff afterward; failures can leave already applied fixes. An empty PHP selection is a successful no-op. Invalid options, configuration paths, diff --git a/src/Config/ECSConfig.php b/src/Config/ECSConfig.php index b4c0f80..e557406 100644 --- a/src/Config/ECSConfig.php +++ b/src/Config/ECSConfig.php @@ -12,6 +12,7 @@ use PhpCsFixer\Fixer\Phpdoc\PhpdocAlignFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocNoEmptyReturnFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocToCommentFixer; +use RuntimeException; use Symplify\EasyCodingStandard\Configuration\ECSConfigBuilder; /** A deliberately non-risky PSR-12 starting point for extraction. */ @@ -22,6 +23,7 @@ final class ECSConfig * * @param callable(ECSConfigBuilder):void|null $customize Native rule imports, skips or overrides; null uses the preset unchanged. * @return ECSConfigBuilder The native builder, ready to return from an explicit ECS configuration file. + * @throws RuntimeException When the executing checkout cannot be identified for cache isolation. */ public static function configure(?callable $customize = null): ECSConfigBuilder { @@ -37,6 +39,7 @@ public static function configure(?callable $customize = null): ECSConfigBuilder ]) ->withConfiguredRule(MethodArgumentSpaceFixer::class, ['on_multiline' => 'ignore']) ->withConfiguredRule(PhpdocAlignFixer::class, ['align' => 'left']) + ->withCache(self::cacheDirectory()) ->withoutParallel(); if (null !== $customize) { $customize($config); @@ -44,4 +47,15 @@ public static function configure(?callable $customize = null): ECSConfigBuilder return $config; } + + /** Keeps ECS invalidation within the executing checkout's temporary cache; native overrides still run last. */ + private static function cacheDirectory(): string + { + $directory = getcwd(); + if (false === $directory) { + throw new RuntimeException('Cannot determine the checkout directory for the ECS cache.'); + } + + return sys_get_temp_dir() . '/fast-forward-code-standards/' . hash('sha256', $directory); + } } diff --git a/tests/Integration/StandardsTest.php b/tests/Integration/StandardsTest.php index 9d350a1..ae70f87 100644 --- a/tests/Integration/StandardsTest.php +++ b/tests/Integration/StandardsTest.php @@ -347,6 +347,46 @@ public function testPresetCanLoadThroughSymlinkWithoutAnAdjacentVendor(): void self::assertSame("directory . '/source.php')); } + /** Runs the real default preset across two checkouts without erasing the first checkout's cache. */ + public function testDefaultCachesDoNotInvalidateAnotherCheckout(): void + { + $source = "put('source.php', $source); + $this->commit(); + $this->put('.build/project-b/source.php', $source); + $second = $this->directory . '/.build/project-b'; + $temporary = $this->directory . '/.build/private-tmp'; + mkdir($temporary); + $this->git(['init', '-b', 'main', $second]); + $this->git(['-C', $second, 'add', 'source.php']); + $environment = ['TMPDIR' => $temporary]; + $cache = $temporary . '/fast-forward-code-standards/' . hash('sha256', $this->directory); + + $first = $this->cli(['check', '--all'], $environment); + self::assertSame(0, $first->getExitCode(), $first->getOutput() . $first->getErrorOutput()); + self::assertDirectoryExists($cache); + file_put_contents($cache . '/owned-marker', 'project A cache'); + + foreach (['check', 'fix', 'check', 'fix'] as $command) { + $result = $this->cli([$command, '--all', '--directory=' . $second], $environment); + + self::assertSame(0, $result->getExitCode(), $result->getOutput() . $result->getErrorOutput()); + self::assertSame('project A cache', file_get_contents($cache . '/owned-marker')); + self::assertSame($source, file_get_contents($second . '/source.php')); + self::assertSame($source, file_get_contents($this->directory . '/source.php')); + self::assertFileDoesNotExist($second . '/composer.lock'); + } + + $this->put('.build/explicit-ecs.php', 'withCache(__DIR__ . "/explicit-cache", "consumer");});'); + $override = $this->cli(['check', '--all', '--config=.build/explicit-ecs.php'], $environment); + + self::assertSame(0, $override->getExitCode(), $override->getOutput() . $override->getErrorOutput()); + self::assertDirectoryExists($this->directory . '/.build/explicit-cache'); + self::assertSame('project A cache', file_get_contents($cache . '/owned-marker')); + } + /** Writes a synthetic fixture beneath the disposable test root, creating its parents. */ private function put(string $path, string $content): void { @@ -372,10 +412,15 @@ private function commit(): string return trim($this->git(['rev-parse', 'HEAD'])); } - /** Runs the real packaged binary in the fixture with a bounded timeout and captured diagnostics. */ - private function cli(array $arguments): Process + /** + * Runs the real packaged binary in the fixture with bounded timeout and captured diagnostics. + * + * @param list $arguments Explicit CLI arguments. + * @param array $environment Invocation-local overrides, including isolated test temp directories. + */ + private function cli(array $arguments, array $environment = []): Process { - $process = new Process([PHP_BINARY, dirname(__DIR__, 2) . '/bin/code-standards', ...$arguments], $this->directory, timeout: 60); + $process = new Process([PHP_BINARY, dirname(__DIR__, 2) . '/bin/code-standards', ...$arguments], $this->directory, $environment, timeout: 60); $process->run(); return $process;