Skip to content

A path after a flag without a value is taken as the flag's value, and the whole project is checked #82

Description

@martin-rueegg

Versions: reproduced with symplify/easy-coding-standard 13.2.19 (bundling entropy/entropy 0.4.9) on PHP 8.4. By reading the source, not by running it: ECS 13.3.2 bundles the same parser and the same ecs_normalize_argv(), and entropy main after 0.4.14 still parses and maps options this way.

What happens

vendor/bin/ecs check --config ecs.php --clear-cache src/Foo.php does not check src/Foo.php. It checks the paths the config sets — typically the whole project — and --clear-cache is off: the mapper casts the swallowed path to bool with FILTER_VALIDATE_BOOLEAN, which gives false.
With --fix earlier on the line — ecs check --fix --config ecs.php --clear-cache src/Foo.php — the whole project is rewritten when one file was meant.

Reproduction — ecs.php:

<?php
use PhpCsFixer\Fixer\Basic\BracesPositionFixer;
use Symplify\EasyCodingStandard\Config\ECSConfig;

return ECSConfig::configure()
    ->withPaths( [ __DIR__ . '/src' ] )
    ->withRules( [ BracesPositionFixer::class ] );

src/A.php and src/B.php, both <?php class A {\n} style, both fixable; then:

Command line Files checked
ecs check src/A.php --config ecs.php --clear-cache src/A.php
ecs check --config ecs.php src/A.php src/A.php
ecs check --config ecs.php --clear-cache src/A.php src/A.php, src/B.php
ecs check --config ecs.php --no-progress-bar src/A.php src/A.php, src/B.php
ecs check --config ecs.php --fix src/A.php src/A.php, src/B.php, reported and not fixed: --fix reads as off
ecs check --fix --config ecs.php --clear-cache src/A.php src/A.php, src/B.php, both fixed
ecs check --config ecs.php -- src/A.php nothing

Cause

Entropy\Console\Input\InputParser::parseLongOption() knows no option schema:

// --option value
if ($argv !== [] && strncmp((string) $argv[0], '-', strlen('-')) !== 0) {
    return [$item, array_shift($argv)];
}

So any --name without = takes the next token that does not start with - as its value — --clear-cache, --fix, --no-progress-bar, --no-error-table, --no-diffs, --debug, and -- itself. The path is gone from the positional arguments, resolvePaths() in ConfigurationFactory finds none, and falls back to Option::PATHS from the config. CLIRequestMapper::castValueByParameterType() then casts the swallowed value to the flag's bool parameter with filter_var(…, FILTER_VALIDATE_BOOLEAN), so the flag itself reads as off.

Expected

A flag that takes no value never consumes the next token, as a Symfony Console InputOption::VALUE_NONE does not: the parser needs to know which options take a value — --config, --output-format, --memory-limit, and the worker's --port and --identifier — or check needs to declare them. -- should end option parsing rather than name an option.

Workaround

Put paths before options: ecs check src/A.php --fix --config ecs.php.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions