php-vcr/vcr-bundle
Symfony integration for PHP-VCR that records real HTTP interactions and replays them in your test suite. Configure cassettes, hooks and fixtures to keep functional tests fast, deterministic and offline without changing your HTTP client code.
Architecture fit. Unchanged. This is still a Symfony bundle, not a Laravel package: it wires php-vcr/php-vcr into Symfony’s container and web profiler so HTTP interactions are recorded once and replayed in tests. For a Symfony shop the fit remains clean and thin — configuration, service registration, profiler panel. For a Laravel shop the bundle is still not usable; the leverage remains the underlying php-vcr core, bootstrapped manually or wrapped in a small test helper, and only where Http::fake() cannot cover Guzzle/cURL/SOAP or legacy clients. Treat it as test infrastructure, not platform architecture. The 3.0.0 release does not change the adoption picture (still a small-star, zero-dependent convenience layer that is cheap to replace), but it does change the support picture: the project is demonstrably active, with CI coverage added for PHP 8.3/8.4/8.5 and a Symfony 6.4 LTS target. That upgrades it from "possibly abandoned wrapper" to "small but maintained wrapper" — still not strategic, but no longer a likely upgrade blocker.
Integration feasibility. Feasible in a short spike, and now with a clearer target matrix. 3.0.0 is a dependency-and-CI release with no API, configuration-key, or service-name changes, so the integration surface is identical to 2.x: enable in dev/test, point at a cassette directory, use in functional/integration tests. The constraint check is now prescriptive rather than investigative — PHP ^8.1, Symfony ^5.4|^6.4, php-vcr/php-vcr ^1.6, PHPUnit ^9.6|^10.5. The PHPUnit 10 work is relevant beyond the bundle itself: VCRTestCaseTrait moved to name()/dataName() with a fallback for PHPUnit 9, so teams mid-migration to PHPUnit 10 have one fewer blocker. The php-vcr floor bump from ^1.5 to ^1.6 is the most substantive item — 1.5.x validates the curl handle with is_resource(), which always returns false on PHP 8 where curl_init() returns a CurlHandle object. Any team that pinned 1.5 on a PHP 8 runtime had a degraded or broken record/replay path; 3.0.0 closes that.
Technical risk. Runtime blast radius stays low — the bundle should never load in production, and 3.0.0 explicitly makes no runtime behaviour changes. The real risks remain test integrity and information leakage: cassettes routinely capture auth headers, tokens, and customer payloads, and the profiler panel can surface recorded traffic in dev. The new risk introduced by this release is estate skew: PHP 7.x and Symfony 4 (and Symfony 5.0–5.3) shops are now cut off and must stay on the 2.x line, which has no stated support window. A repo split across 2.x and 3.x will diverge on php-vcr semantics (1.5 vs 1.6) and PHPUnit version, which is precisely where cassette-format and trait-behaviour drift shows up as confusing test flakes. The earlier provenance concern is partly answered — the support matrix is current, CI is real, and the release notes are specific about root causes — but the project is still single-maintainer-scale and should be assessed as replaceable in an afternoon.
Key questions. Is there a published support/EOL window for the 2.x line, and will it receive security fixes for PHP 7/Symfony 4 estates? What is the plan and timeline for Symfony 7 (and PHPUnit 11+) support, and does the maintainer treat the Symfony LTS as the cadence driver? Does php-vcr 1.6 change cassette format or matching behaviour versus 1.5, and are existing cassettes forward-compatible? Does the bundle expose redaction hooks for headers and bodies, or only raw capture — and what exactly does the profiler panel display? How does it behave under parallel test execution (paratest, per-worker cassette isolation)? Who is the maintainer of record, and what is the bus factor? What is the fallback if upstream stalls — direct php-vcr 1.6 bootstrap, or native framework fakes?
Stack fit. If the estate is Symfony 5.4 or 6.4 on PHP 8.1+ with PHPUnit 9.6/10.5 and Guzzle-based clients, 3.0.0 is the version to adopt — it is a strict improvement on 2.x with no code changes. If the estate is Laravel, still do not introduce the bundle; evaluate php-vcr 1.6 directly for HTTP paths native fakes cannot cover. Mixed PHP estates should standardise on php-vcr 1.6 semantics as the portability layer and treat the Symfony bundle as optional sugar, so a future Symfony exit costs almost nothing.
Migration path. For teams already on 2.x and already on PHP 8.1+/Symfony 5.4+, the upgrade is composer require php-vcr/vcr-bundle:^3.0 and nothing else — verified against the stated no-API-change guarantee, so a single CI run should confirm it. Teams still on PHP 7.x or Symfony 4/5.0–5.3 have a sequencing dependency: they must land the PHP 8.1 and Symfony 5.4 platform upgrade first, or stay on 2.x and accept an unsupported line. Teams bootstrapping php-vcr by hand can replace bespoke code with bundle config, but only if they are already on the 3.0 matrix. There is no production migration path; the dependency stays out of production autoloading.
Compatibility. Confirm the target platform meets PHP ^8.1 and Symfony ^5.4|^6.4, and that php-vcr/php-vcr resolves to ^1.6 — check for transitive pins holding it at 1.5, which would silently reintroduce the PHP 8 curl-handle defect. Verify PHPUnit is ^9.6 or ^10.5 and that the trait’s name()/dataName() path is the one being exercised in data-provider-driven tests. Confirm that enabling the bundle in test does not leak into prod container compilation. PHP 8.5 is continue-on-error in CI, so do not treat it as supported for planning purposes.
Sequencing. Run a one-repo spike on the current Symfony LTS covering a single third-party API. Pin ^3.0, define cassette naming and storage before scaling, and add a redaction step for headers and bodies. Then wire CI artifact caching and per-worker cassette directories. Roll out to one team, measure flake reduction and suite runtime, and only then broaden. Keep the exit plan explicit: a small internal bootstrap wrapper over php-vcr 1.6 that can replace the bundle if upstream stalls or a Symfony 7 gap persists.
Maintenance. Low ongoing code maintenance; non-zero dependency maintenance. Pin ^3.0, review upstream quarterly, and track Symfony 7 readiness against your LTS upgrade date. Because it is dev-only, there is no production on-call burden. Note the version-split overhead: if any repo remains on 2.x while others move to 3.0, you are maintaining two dependency policies and two php-vcr minors.
Support. Community-supported only, but the 3.0.0 release — widened constraints, real CI matrix, fixes with named root causes — is a positive maintenance signal and reduces (not eliminates) abandonment risk. Assign an internal owner and a short runbook covering cassette refresh, redaction checks, record-mode policy, and the 2.x-to-3.x constraint boundary. Budget modest effort for breakage during framework upgrades, particularly Symfony 7 and PHPUnit 11.
Scaling. Scaling concerns remain cassette volume and CI behaviour, not runtime throughput. Cassettes accumulate, cause merge conflicts, and bloat repositories; recorded payloads can contain PII. Organise cassettes per test suite, avoid committing large binary payloads, and isolate cassette directories per CI worker to prevent parallel collisions. The PHPUnit 10 trait change means test names may appear differently in cassette naming, so re-baseline naming conventions before a broad rollout.
Failure modes. Stale cassettes let tests pass while the real API contract drifts — record/replay validates the client against a frozen contract, not the live dependency, and that false confidence is still the most important failure mode. Recorded secrets leak into source control or the profiler. Cassette mismatches produce confusing failures on benign API changes. Parallel runs collide on shared cassette files. Estate skew between 2.x (php-vcr 1.5) and 3.x (php-vcr 1.6) causes divergent replay behaviour and version-dependent bugs — and the 1.5 curl-handle defect on PHP 8 is a concrete example of a pin that looks fine and is not. Upstream abandonment remains a slower-moving risk now than before this release.
Ramp-up. Roughly half a day for a PHP developer familiar with HTTP testing to learn record/replay, matchers, and record modes; add a short policy session on redaction and cassette ownership. The release adds negligible learning cost for new adopters, but teams upgrading across the PHP 8.1/PHPUnit 10 boundary should budget the platform upgrade separately — that work is theirs, not the bundle’s. Learn the underlying php-vcr 1.6 concepts rather than the bundle’s wiring so the tool stays replaceable.
How can I help you explore Laravel packages today?