php-vcr is a free, open source api development & testing project written in PHP and released under MIT. It has 1,212 GitHub stars, 215 forks and 24 open issues, and was last pushed 5 days ago. On this registry it ranks #87 of 103 tracked projects in API Development & Testing, with 5 head-to-head comparisons available. It gained 2 stars over the last 3 tracked days.

What is php-vcr?

php-vcr is an MIT-licensed PHP library that records a test suite's HTTP interactions into cassette files and replays them on later runs, giving PHPUnit-style projects fast, deterministic tests that do not touch the live network.

What it is

php-vcr is a PHP port of the Ruby VCR library, distributed through Composer as php-vcr/php-vcr and documented at php-vcr.github.io/php-vcr. It hooks into the HTTP layer of a PHP process so that outgoing requests are captured to disk on the first run and served from those recordings afterwards. The captured data is a "cassette": a file holding the recorded request and response pairs, serialized in YAML or JSON, which the test suite can commit alongside its source.

The concrete problem it solves is the live network dependency inside automated tests. A test that calls a remote REST endpoint, a SOAP service, or any URL through cURL is slow, fragile against rate limits and outages, and non-deterministic when the upstream data changes. php-vcr replaces the live HTTP call with a replay of a previously recorded exchange, so a test that fetched http://example.com once can assert on the same bytes on every later run. It also closes the failure mode where a forgotten cassette silently reaches the network: when no cassette is inserted, php-vcr throws a BadMethodCallException with the message "Invalid http request. No cassette inserted. Please make sure to insert a cassette in your unit test using VCR::insertCassette('name');" instead of making the request.

Key capabilities

  • Records and replays HTTP and HTTPS interactions with minimal setup, using VCR::turnOn(), VCR::insertCassette('example'), VCR::eject(), and VCR::turnOff().
  • Intercepts three library hooks by default when VCR::turnOn() is called: the stream_wrapper hook (covering functions such as file_get_contents), the curl hook, and the soap hook for SoapClient.
  • Blocks every HTTP request that is not explicitly allowed, using the configured record mode.
  • Supports the record modes new_episodes (default), once, none, and all, which control how a cassette behaves while it is inserted.
  • Matches requests configurably on HTTP method, URI, host, path, body, and headers, and allows a custom request matcher to be written for other needs.
  • Stores recorded requests and responses on disk in a serialization format of choice, with YAML and JSON built in.
  • Lets the same request return different responses in different tests by inserting different cassettes.

Who uses it and how

  • PHPUnit test suites that exercise code paths reaching external HTTP services, where the test case extends TestCase and drives the VCR static API in setUp or in the individual test method.
  • Teams testing REST and SOAP integrations, including Guzzle-based clients, that need the same assertions to pass offline and inside CI without depending on a third-party sandbox being up.
  • Projects that call curl_* functions or instantiate SoapClient, where VCR must be turned on immediately after Composer's autoloader and before any code loading those entry points, otherwise the interception is not registered.
  • Bootstraps that want hooks live only for part of the run: the pattern is to call VCR::turnOn() then VCR::turnOff() in the bootstrap file, and let each test cheaply call turnOn() again when it actually wants a cassette.
  • Repositories that commit cassettes to version control so recorded exchange data is reviewable and travels with the code that depends on it.

Getting started

Install with Composer as a development dependency: composer require --dev php-vcr/php-vcr. Full guides, including library hooks and request matching, live in the project documentation and in the raw Markdown under docs/.

How it compares

php-vcr is a port of the Ruby VCR library, so it brings the same cassette record-and-replay model into PHP, which does not support the monkey patching that makes the Ruby original straightforward. Where PHP HTTP test doubles are often bound to a single client, php-vcr intercepts the stream_wrapper, curl, and soap hooks together and additionally works with Guzzle-based services named in its topics. The licence is MIT, and the recordings live in files the project itself owns rather than in an external hosted service.

When to use it — and when not to

Choose php-vcr when a PHP test suite reaches real HTTP endpoints and the team wants those calls recorded once, committed as YAML or JSON cassettes, and replayed without network access. Be aware of what a self-hoster must operate: this is a library, not a service, so there is no database or mail server to run, but the cassettes themselves become artifacts that must be stored, curated, and regenerated when upstream responses change, and the hook ordering requirement means VCR has to be turned on before any code that loads curl_* or SoapClient. It is a poor fit for tests that genuinely need to validate live upstream behaviour, and the project's own disclaimer notes that interception in PHP is harder than in languages supporting monkey patching, so anyone expecting the Ruby VCR's transparency should check the library hooks documentation before committing to it.

project readme (upstream, from github) — read inline

PHP-VCR

Continuous Integration Code Coverage Latest Version PHP Version License Context7

This is a port of the VCR Ruby library to PHP.

Record your test suite's HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests. Full documentation lives at php-vcr.github.io/php-vcr — or browse the raw Markdown in docs/.

Disclaimer: Doing this in PHP is not as easy as in programming languages which support monkey patching (I'm looking at you, Ruby)

Features

  • Automatically records and replays your HTTP(s) interactions with minimal setup/configuration code.
  • Supports common http functions and extensions — see Supported HTTP libraries below
  • The same request can receive different responses in different tests -- just use different cassettes.
  • Disables all HTTP requests that you don't explicitly allow by setting the record mode
  • Request matching is configurable based on HTTP method, URI, host, path, body and headers, or you can easily implement a custom request matcher to handle any need.
  • The recorded requests and responses are stored on disk in a serialization format of your choice (currently YAML and JSON are built in)

Usage example

⚠️ Turn VCR on as soon as possible — right after Composer's autoloader, before any code that calls curl_* or uses SoapClient gets loaded. That call is what registers the interception; turn it back off right afterwards (VCR::turnOn(); VCR::turnOff(); in your bootstrap file) if you don't want hooks live for the whole run — each test can cheaply call turnOn() again only when it actually wants a cassette. Details: How VCR works.

Using static method calls:

class VCRTest extends TestCase
{
    public function testShouldInterceptStreamWrapper()
    {
        // After turning on the VCR will intercept all requests
        \VCR\VCR::turnOn();

        // Record requests and responses in cassette file 'example'
        \VCR\VCR::insertCassette('example');

        // Following request will be recorded once and replayed in future test runs
        $result = file_get_contents('http://example.com');
        $this->assertNotEmpty($result);

        // To stop recording requests, eject the cassette
        \VCR\VCR::eject();

        // Turn off VCR to stop intercepting requests
        \VCR\VCR::turnOff();
    }
}

Forgetting to insert a cassette throws immediately, instead of silently hitting the network:

public function testShouldThrowExceptionIfNoCasettePresent()
{
    $this->expectException(\BadMethodCallException::class);
    $this->expectExceptionMessage(
        "Invalid http request. No cassette inserted. Please make sure to insert "
        . "a cassette in your unit test using VCR::insertCassette('name');"
    );
    \VCR\VCR::turnOn();
    file_get_contents('http://example.com');
}

Supported HTTP libraries

All three hooks (stream_wrapper, curl, soap) are enabled by default when you call VCR::turnOn(). Full interception details and how to enable only specific hooks: Library Hooks.

Record modes

The record mode controls how VCR behaves when a cassette is inserted: new_episodes (default), once, none, all. Full behaviour per mode: Record Modes.

Recording identical requests

By default php-vcr records identical requests separately and replays them in the same order they were made. Details and how to change this: Cassettes → identical requests.

Installation

Simply run the following command:

composer require --dev php-vcr/php-vcr

Dependencies

PHP-VCR depends on PHP 8 and the curl extension, plus a few Composer packages Composer installs for you. Full requirements and the tested HTTP library matrix: Requirements.

Documentation

Full documentation — searchable, versioned, dark mode — lives at php-vcr.github.io/php-vcr. Or browse the raw Markdown directly in docs/:

Contributing

Bug reports, feature requests and pull requests are welcome — see CONTRIBUTING.md for how this repository is set up, the pre-push checks, and why documentation is part of every change.

Run tests

In order to run all tests you need to get development dependencies using composer:

composer install
composer test

Changelog

The changelog has moved to the PHP-VCR releases page.

Old changelog entries

Copyright

Copyright (c) 2013-2026 Adrian Philipp. Released under the terms of the MIT license. See LICENSE for details. Contributors

Frequently asked questions

Is php-vcr free to use?

php-vcr is open source under the MIT licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does php-vcr do?

Record your test suite's HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests.

What is php-vcr written in?

php-vcr is primarily written in PHP. Its source is publicly available at https://github.com/php-vcr/php-vcr, and it has 1,212 GitHub stars.