Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Rector Downgrade Php Laravel Package

rector/rector-downgrade-php

rector/rector-downgrade-php provides Rector rules for downgrading PHP code, automatically rewriting modern syntax and features into older, compatible versions. Useful for backporting projects or libraries to legacy PHP runtimes with repeatable transformations.

View on GitHub
Deep Wiki
Context7

Getting Started

What it is: a Rector ruleset that rewrites newer PHP syntax/semantics into older-PHP-compatible code. Typical use case: you want to develop using PHP 8.0+ features (named args, match, nullsafe, constructor promotion, enums, readonly, first-class callables) but still ship code that runs on PHP 7.4 (a library with a wide support matrix, a plugin targeting legacy hosts, a package distributed as source). It operates on your source, not vendor/.

Minimal install:

composer require --dev rector/rector rector/rector-downgrade-php

First config (rector-downgrade.php) — keep this separate from your normal refactoring config:

<?php
use Rector\Config\RectorConfig;

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/src'])
    ->withDowngradeSets(php74: true);   // target PHP 7.4

Older Rector API uses set-list classes directly (these live in this package):

$rectorConfig->sets([
    \Rector\DowngradePhp74\Set\DowngradePhp74SetList::class,
    \Rector\DowngradePhp80\Set\DowngradePhp80SetList::class,
]);

First use case: downgrade one file and diff it before committing to the approach:

vendor/bin/rector process src/Foo.php --config rector-downgrade.php --dry-run
vendor/bin/rector process src/Foo.php --config rector-downgrade.php

Where to look first: the per-version Set\DowngradePhp*SetList classes — they enumerate exactly which rules run, which tells you what is and isn't covered. Then browse the rule classes themselves (DowngradeMatchToSwitchRector, DowngradeNullsafeToTernaryOperatorRector, DowngradeNamedArgumentRector, DowngradeConstructorRector, …) to see the transformation each performs. Confirm you're running a Rector version aligned with the set lists you're using.

Implementation Patterns

Two-config workflow (recommended). Keep modern code in src/, keep a rector.php for ordinary refactoring, and a rector-downgrade.php used only at release time or in a dedicated CI job. Never merge downgrade sets into your everyday config, or you'll silently lose the modern syntax you're writing against.

Build-artifact downgrade, not source downgrade. If you develop in 8.0+ and ship a 7.4 tarball, run downgrade against a copy of the tree, then package:

rsync -a --exclude vendor ./ ./build/
vendor/bin/rector process build/src --config rector-downgrade.php

This keeps your working tree readable and makes the downgrade step auditable and repeatable. Commit the generated output only if the artifact is what you publish (e.g. a library distributed without a build step).

CI gate in two modes:

# 1. fail the build if any un-downgradable modern syntax slipped in
vendor/bin/rector process src --config rector-downgrade.php --dry-run
# 2. or apply and assert the diff is empty (downgrade is idempotent)
vendor/bin/rector process src --config rector-downgrade.php
git diff --exit-code

The --dry-run gate is the cheapest early warning that a new language feature in your source isn't covered by a rule.

Granularity when a set is too aggressive. Register individual rules instead of a whole set list:

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/src'])
    ->withRules([
        \Rector\DowngradePhp80\Rector\Match_\DowngradeMatchToSwitchRector::class,
        \Rector\DowngradePhp80\Rector\NullsafeMethodCall\DowngradeNullsafeToTernaryOperatorRector::class,
    ]);

Useful when you only need one construct lowered, or when one rule in a set misbehaves on your codebase.

Scope control. Always constrain withPaths() to your own code (src/, app/, lib/). Downgrading vendor/ will not make a PHP 8–only dependency run on 7.4, and it wastes iterations. Pair with withSkip() to exclude generated files, stubs, and fixtures that intentionally contain modern syntax.

Stacked targets. For a multi-version support matrix, downgrade in order (8.2 → 8.1 → 8.0 → 7.4) and run each stage separately; each stage's test suite should pass on its target interpreter before moving to the next.

Verification pattern that actually catches problems: run the downgraded output's test suite on the target PHP version (via a matrix job or Docker), not just the dry-run diff. Rector changing syntax is necessary but not sufficient — semantics can drift (see below).

Gotchas and Tips

  • Downgrading is best-effort and sometimes lossy. Some features have no faithful 7.4 equivalent: enums become class constants + objects with caveats; readonly becomes convention-only; first-class callable syntax and fibers are constrained; attributes have no runtime representation in 7.4. Treat the output as "compiles and mostly works," then verify with tests.
  • match → switch changes comparison semantics. match uses strict ===; switch uses loose ==. If your arms rely on strict typing (e.g. 0 vs '0' vs false), review the generated switch or add explicit casts/if chains. This is the single most common silent behavior change.
  • Nullsafe → ternaries introduces intermediate variables. Multiple chained calls ($a?->b()?->c()) expand into nested temporaries; check that the generated variables don't collide and that side-effecting calls still execute once.
  • Named arguments require reordering to positional. This only works when the rule can resolve the signature; calls into variadics, ...$args, or dynamically-named parameters may be left untouched or reordered incorrectly. Inspect DowngradeNamedArgumentRector output on factory/DI code especially.
  • Constructor promotion → explicit properties is usually safe, but property visibility, readonly, and reflection-based tooling (serializers, ORMs, ReflectionProperty) can behave differently. Re-run integration tests for anything that introspects objects.
  • Not idempotent in edge cases. Running downgrade twice on already-downgraded code occasionally produces noise. Prefer a clean build directory, and rely on the git diff --exit-code CI pattern rather than repeated in-place runs.
  • Cache confusion. Rector caches analysis; after changing set lists or Rector versions, clear it (--clear-cache) if you see stale or missing changes. When in doubt, -vvv --debug shows which rules fired per file.
  • Version pinning matters here. With 44 stars and few dependents, this package is a thin companion to the main rector/rector repo — pin both to compatible versions (composer require --dev rector/rector:^X rector/rector-downgrade-php:^X) and upgrade deliberately. Rules and set-list namespaces shift between majors, and functionality may migrate into the core package.
  • Attributes are the sneaky one. #[Attribute], #[\Override], DI attributes, and route attributes get commented out or dropped in the downgrade. If your framework relies on attributes at runtime on 8.0+ but you're also shipping a 7.4 build, ensure the 7.4 code path doesn't depend on them (annotations, config arrays, or conditional loading).
  • Extension point for gaps: if a construct isn't covered, write a custom RectorInterface rule (or extend the abstract rule base) and register it with ->withRules([...]). This is the intended escape hatch — check the existing rule naming/structure in this package before writing one, since many are small and easy to model on.
  • Don't confuse "downgraded" with "backwards compatible." This tool changes syntax, not behavior guarantees. Runtime deprecations, library API differences between PHP versions, and type-juggling changes (e.g. 8.0's stricter string/number comparisons) are outside its scope and will still bite you.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource