adachsoft/composer-json-fixer

Validate and automatically fix composer.json files for PHP libraries.

Installs: 1

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Forks: 0

pkg:composer/adachsoft/composer-json-fixer

v0.2.1 2026-02-27 17:39 UTC

This package is not auto-updated.

Last update: 2026-02-27 17:00:17 UTC


README

A small PHP library and CLI tool for validating and automatically fixing composer.json files, with a focus on library packages.

The tool runs a set of opinionated rules against your composer.json, reports issues, and can generate patches or write fixes back to disk while preserving indentation style.

Requirements

  • PHP >= 8.2
  • composer.json size up to 5 MB (hard limit enforced by file reader)

Runtime dependencies are installed automatically via Composer (notably adachsoft/console-io and adachsoft/collection).

Installation

Install via Composer:

composer require --dev adachsoft/composer-json-fixer

The package is intended primarily as a dev-tool, hence --dev in the example. You can also install it as a regular dependency if you need to ship it with your application.

After installation, Composer exposes the binary as:

./vendor/bin/composer-json-fixer

CLI Usage

Basic commands

Validate a composer.json file:

./vendor/bin/composer-json-fixer validate --path=path/to/composer.json

Fix a composer.json file and show a patch (dry-run):

./vendor/bin/composer-json-fixer fix --path=path/to/composer.json --dry-run

Fix a composer.json file in place:

./vendor/bin/composer-json-fixer fix --path=path/to/composer.json

You can optionally provide a JSON configuration file with rule settings:

./vendor/bin/composer-json-fixer validate --path=path/to/composer.json --config=path/to/config.json
./vendor/bin/composer-json-fixer fix --path=path/to/composer.json --config=path/to/config.json

Exit codes

  • 0 – success, no issues (or all issues fixed and file is valid)
  • 1 – validation failed, at least one issue (error or warning) remains before/after fix, or invalid/broken file

Note: the CLI does not distinguish severity for exit codes – any reported issue (even a warning) results in a non‑zero exit code.

CLI output

The CLI uses adachsoft/console-io for colored and icon-enhanced output. Example flows:

  • validate with no issues:
    • prints a success message and exits with code 0
  • validate with issues:
    • prints all issues (severity, message, JSON pointer) and exits with code 1
  • fix --dry-run:
    • prints a unified diff with proposed changes but does not modify the file
  • fix (write mode):
    • prints a diff if there are changes
    • writes the updated composer.json
    • validates the result and prints final status

Indentation is preserved (2 spaces, 4 spaces or tabs) based on the original file.

Library Usage (Public API)

The main entry point is AdachSoft\ComposerJsonFixer\PublicApi\ComposerJsonFixerInterface.

In most cases you should use the ComposerJsonFixerBuilder from the Public API, which wires Core dependencies and rules for you.

Construction via builder

<?php

use AdachSoft\ComposerJsonFixer\PublicApi\ComposerJsonFixerBuilder;
use AdachSoft\ComposerJsonFixer\PublicApi\ComposerJsonFixerInterface;

$builder = new ComposerJsonFixerBuilder();

// Without explicit configuration – uses default rule set
$fixer = $builder->build();

// Or, using a configuration file (see section below)
$fixerFromConfig = $builder->buildFromConfigFile(__DIR__ . '/composer-json-fixer.config.json');

assert($fixer instanceof ComposerJsonFixerInterface);

You normally do not need to instantiate Core classes (ComposerJsonFileReader, ComposerJsonFileWriter, DefaultRuleProvider, UnifiedDiffGenerator) by hand – this is handled inside the builder.

Configuration file (rules)

The builder supports a JSON configuration file passed on the CLI (--config) or directly to buildFromConfigFile().

The configuration controls:

  • which rules are enabled/disabled,
  • rule-specific parameters (e.g. packages for RequireVersionNormalizationRule).

Example configuration file (snake_case JSON):

{
  "rules": [
    {
      "rule_id": "require_version_normalization",
      "enabled": true,
      "params": {
        "packages": [
          "phpunit/phpunit",
          "phpstan/phpstan"
        ]
      }
    },
    {
      "rule_id": "config_must_be_object",
      "enabled": true,
      "params": {}
    }
  ]
}

Validation API

use AdachSoft\ComposerJsonFixer\PublicApi\Dto\ValidateComposerJsonRequestDto;

$request = new ValidateComposerJsonRequestDto('/path/to/composer.json');
$response = $fixer->validate($request);

if ($response->isValid) {
    // no issues
} else {
    foreach ($response->issues as $issue) {
        // $issue is AdachSoft\ComposerJsonFixer\Core\ComposerJsonIssue
        // $issue->severity (IssueSeverityEnum::Error|Warning)
        // $issue->message (string)
        // $issue->jsonPointer (?string)
    }
}

Fix API

use AdachSoft\ComposerJsonFixer\PublicApi\Dto\FixComposerJsonRequestDto;
use AdachSoft\ComposerJsonFixer\PublicApi\Enum\FixModeEnum;

$request = new FixComposerJsonRequestDto(
    '/path/to/composer.json',
    FixModeEnum::DryRun, // or FixModeEnum::Write
);

$response = $fixer->fix($request);

// $response is AdachSoft\ComposerJsonFixer\PublicApi\Dto\FixComposerJsonResponseDto

if ($response->unifiedDiff !== null) {
    echo $response->unifiedDiff; // unified diff between original and fixed JSON
}

if ($response->wasWritten) {
    // file was updated on disk
}

if (!$response->isValidAfterFix) {
    // some issues remain after automatic fix
}

Exceptions

The following public exceptions can be thrown by the reader/fixer facade:

  • AdachSoft\ComposerJsonFixer\PublicApi\Exception\ComposerJsonNotFoundException – file does not exist
  • AdachSoft\ComposerJsonFixer\PublicApi\Exception\ComposerJsonInvalidException – file is too large, not readable, or contains invalid JSON

Rules

The library ships with a set of built-in rules, all implementing:

AdachSoft\ComposerJsonFixer\Core\Rule\ComposerJsonRuleInterface

Each rule provides:

  • getRuleId(): string
  • validate(ComposerJsonDocument $document): ComposerJsonIssueCollection
  • fix(array $data): ?array – returns modified decoded JSON or null when no change is applied

MissingRequiredFieldsRule

Checks that the following fields are present at the root level:

  • name
  • description
  • type
  • license
  • authors
  • keywords

Severity: error for each missing field.

This rule does not auto-fill missing values during fix() (no guessing of metadata).

Psr4NamespaceConsistencyRule

For packages with:

  • type: "library"
  • valid non-empty name (vendor/package-name)

The rule computes an expected PSR-4 namespace from the package name using ExpectedPsr4Namespace, e.g.:

  • vendor/package-nameVendor\PackageName\\

Behavior:

  • validate() compares the first autoload.psr-4 key with the expected namespace and reports a warning if they differ.
    • Internally, the rule compares the normalized namespace (multiple backslashes collapsed) so that DoubleBackslashInNamespaceRule and this rule work together.
  • fix() rewrites only the first autoload.psr-4 key to the expected namespace, preserving:
    • the original path for that entry
    • all other psr-4 entries
    • all other autoload sections (classmap, files, etc.).

DoubleBackslashInNamespaceRule

Normalizes namespace keys in autoload.psr-4 by collapsing multiple backslashes:

  • AdachSoft\\\\Foo\\AdachSoft\Foo\\

Reports warnings for affected entries and updates keys during fix().

ConfigMustBeObjectRule

Ensures that the root config section is a JSON object, not a JSON array.

Examples:

  • "config": [] (JSON array) → issue + fix to "config": {} (empty object, represented as stdClass in PHP)
  • "config": {} (already an object) → no issue

The rule uses both the decoded data and the raw JSON to distinguish between [] and {} at the root level.

Severity: warning.

RequireVersionNormalizationRule

Normalizes overly specific versions for selected packages in require, e.g.:

  • "phpunit/phpunit": "9.6.5""9.6"
  • "adachsoft/command-executor-lib": "2.0.1""2.0"

Behavior:

  • validate() emits warnings for versions matching x.y.z (full major.minor.patch) for a configured list of packages.
  • fix() rewrites them to x.y (i.e. drops the patch segment) for configured packages, such as:
    • phpunit/phpunit
    • phpstan/phpstan
    • rector/rector
    • adachsoft/command-executor-lib

After a successful fix, a subsequent validate should no longer report normalization warnings for these packages.

Indentation & Formatting

The tool tries hard not to destroy your formatting:

  • The reader inspects the original file and detects the indentation style (2 spaces, 4 spaces, or tab).
  • The fixer uses json_encode(..., JSON_PRETTY_PRINT) and then rewrites the leading spaces on each line to match the detected indent.
  • If the file is fully minified (single line), the fixer will pretty-print it using a default indent (4 spaces).

Note: key ordering may still change according to json_encode behavior; the primary focus is on logical structure and indent style, not on preserving exact byte-for-byte formatting.

Development

Running tests

composer install

# Unit and functional tests
./vendor/bin/phpunit tests

Static analysis

./vendor/bin/phpstan analyse src tests

Code style

This project follows coding standards provided by adachsoft/php-code-style.

./vendor/bin/php-cs-fixer fix

Limitations

  • Only JSON files up to 5 MB are supported.
  • Automatic fixes are intentionally conservative – some rules only validate and do not attempt to guess missing metadata.
  • The ruleset is opinionated and biased towards typical library packages; custom requirements may need additional rules.

Contributions in the form of new rules or improvements to existing ones are welcome, as long as they keep the behavior predictable and safe for CI usage.