serendipity_hq/rector-config
A shared Rector configuration for PHP projects, providing ready-made rule sets to automate code upgrades and refactoring. Drop it into your project to standardize automated code transformations without hand-rolling configs.
Since this is a shared configuration package rather than a runtime library, "using" it means pulling in Serendipity HQ's Rector rule presets and pointing them at your own codebase.
Install as a dev dependency alongside Rector itself:
composer require --dev rector/rector serendipity_hq/rector-config
Look first at the package's own rector.php and src/ directory inside vendor/serendipity_hq/rector-config/. This tells you whether it exposes a Sets constants class, a Config helper (e.g. SerendipityHQ\Rector\Config\Config), or simply a rector.php file intended to be require'd. That shape determines the wiring in step 3.
Create a rector.php at the root of your Laravel project and extend the shared config:
<?php
use Rector\Config\RectorConfig;
use SerendipityHQ\Rector\Config\Config; // adjust to the actual exported symbol
return RectorConfig::configure()
->withPaths([
__DIR__ . '/app',
__DIR__ . '/config',
__DIR__ . '/database',
__DIR__ . '/routes',
__DIR__ . '/tests',
])
->withSets(Config::getSets()); // or withRules(...), depending on what the package exposes
First use case — an audit, not an auto-fix. Run Rector in dry-run mode against a single directory before touching the whole app:
vendor/bin/rector process app --dry-run
Read the diff. On a legacy Laravel app the first run will usually surface dozens of safe modernisations (typed closures, match conversions, readonly properties, str_* helpers, etc.). That diff is the value of the assessment.
Commit the working baseline (a clean rector.php plus a passing --dry-run) before you start applying fixes. Treat this like adopting PHP-CS-Fixer: you want the tool wired in, not just run once.
Layered configuration over the shared set. Use the Serendipity HQ set as the "house rules" floor and layer your project rules on top. In practice:
return RectorConfig::configure()
->withPaths([__DIR__ . '/app', __DIR__ . '/tests'])
->withSets([
...Config::getSets(), // Serendipity HQ presets
\Rector\PHPUnit\Set\PHPUnitSetList::PHPUNIT_100, // project-specific layer
])
->withSkip([
\Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector::class,
]);
Scope by directory, not by whole repo. Laravel projects have very different code styles in app/, tests/, database/migrations/, and config/. Run Rector per-directory in CI rather than a single repo-wide sweep, so a noisy ruleset in database/ does not block a clean app/.
Use it as a gate, not a rewrite tool. The healthy daily workflow is:
vendor/bin/rector process $(git diff --name-only --cached) before committing.vendor/bin/rector process --dry-run and fail the job if any file changes.git bisect still helps.Pair it with a Rector-specific --config file for large upgrades. Copy the shared config into rector-upgrade-php83.php with a restricted rule set. When you do a PHP-version bump, you get an isolated, auditable ruleset whose output you can compare against the Serendipity HQ baseline.
Integrate with static analysis. Run Rector before PHPStan in CI, not after — Rector's rewrites frequently turn a class of PHPStan findings into clean code, and running them in the opposite order double-counts issues.
Combine with rector-laravel. Serendipity HQ is framework-agnostic; add driftingly/rector-laravel explicitly for Laravel-specific rules (facades, helpers, app() → DI, etc.). Don't assume the shared config pulls it in — check the composer.json of the package first.
Per-branch config via --config. For a Laravel upgrade from 10 → 11, keep two config files (rector-l10.php, rector-l11.php) and gate each in its own PR. This keeps the Serendipity HQ base out of the blast radius if a rule misfires.
The package is effectively unmaintained from a consumer's perspective. Zero stars, zero dependents, and it is described by its own README as "used by Serendipity HQ" — that means it is tuned to their code, PHP version, and dependency matrix. Pin the exact version, review every release's diff, and be prepared to vendor it (copy the rector.php / set class into your project) rather than depend on it directly.
withPaths() overwrites, it does not merge. If you call withPaths() after including the shared config (or vice versa), the last call wins. Do the withPaths() in your file and keep path definitions out of the shared config to avoid surprise empty scans.
Rector's set order is additive and can produce conflicting rewrites. Two rules can ping-pong (e.g. one rule extracts a variable, another inlines it). Run --dry-run twice: if the second run still produces diffs on already-processed files, you have a conflicting pair — skip one of them with withSkip([...]).
Laravel magic methods and facades are blind spots. Rules that assume static analysis can see all call sites will happily rewrite Foo::bar() into dependency injection. Use withSkipPath(__DIR__ . '/app/Providers') or a .rector-baseline when you first adopt this on a Laravel app with lots of facades.
Clear the cache between config changes. Rector caches heavily; when a rule you expect to fire does not, run vendor/bin/rector process --clear-cache path/to/dir. This costs far less time than debugging "why didn't the rule run."
Vendor and generated code must be excluded. Laravel's bootstrap/cache/, storage/, and anything under vendor/ should never be in withPaths(). Generated Eloquent models are also a common source of false positives — scope to app/Models carefully.
Run with --debug to see which rule changed a line. The default output only shows file-level changes. --debug shows the rule class, which is exactly what you need to file a withSkip() for a rule that fights your style guide.
Use vendor/bin/rector list-rules to audit what the shared config actually enables. Because the set is opaque, printing the resolved rule list is the only reliable way to know what a composer update of the package will do to your codebase.
Combine with .php-cs-fixer.dist.php order. Rector rewrites code structurally; PHP-CS-Fixer rewrites it stylistically. Always run Rector first, then PHP-CS-Fixer, in local git hooks and CI, or CI will flip-flop on formatting.
Extension point: if the package simply exposes a rector.php, wrap it in your project's own rector.php and return the merged RectorConfig. Do not require it from multiple configs; that bypasses the withSkip mechanism and prevents any targeted exclusions.
How can I help you explore Laravel packages today?