Code Monkey home page Code Monkey logo

php-vcr's Introduction

PHP-VCR

Continuous Integration Code Coverage Scrutinizer Quality Score

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. A bit of documentation can be found on the php-vcr website.

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
    • everything using streamWrapper: fopen(), fread(), file_get_contents(), ... without any modification (except $http_response_header see #96)
    • SoapClient by adding \VCR\VCR::turnOn(); in your tests/bootstrap.php
    • curl(), by adding \VCR\VCR::turnOn(); in your tests/bootstrap.php
  • 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, and you can easily implement your own custom serializer)
  • Supports PHPUnit annotations.

Usage example

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();
    }

    public function testShouldThrowExceptionIfNoCasettePresent()
    {
        $this->setExpectedException(
            'BadMethodCallException',
            "Invalid http request. No cassette inserted. Please make sure to insert "
            . "a cassette in your unit test using VCR::insertCassette('name');"
        );
        \VCR\VCR::turnOn();
        // If there is no cassette inserted, a request throws an exception
        file_get_contents('http://example.com');
    }
}

You can use annotations in PHPUnit by using phpunit-testlistener-vcr:

class VCRTest extends TestCase
{
    /**
     * @vcr unittest_annotation_test
     */
    public function testInterceptsWithAnnotations()
    {
        // Requests are intercepted and stored into  tests/fixtures/unittest_annotation_test.
        $result = file_get_contents('http://google.com');

        $this->assertEquals('This is a annotation test dummy.', $result, 'Call was not intercepted (using annotations).');

        // VCR is automatically turned on and off.
    }
}

Installation

Simply run the following command:

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

Dependencies

PHP-VCR depends on:

Composer installs all dependencies except extensions like curl.

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-2023 Adrian Philipp. Released under the terms of the MIT license. See LICENSE for details. Contributors

php-vcr's People

Contributors

aaa2000 avatar adri avatar alnorth avatar benzittlau avatar bwood avatar chregu avatar dbu avatar endorama avatar higidi avatar jamiecuthill avatar janvernieuwe avatar jeroenvanoort avatar johnmadrak avatar justincy avatar k-phoen avatar kasperg avatar kornrunner avatar laland avatar lapistano avatar moufmouf avatar mrrio avatar nicodemuz avatar oranges13 avatar pauladams8 avatar pvgnd avatar renatomefi avatar specialtactics avatar webspectyler avatar yateric avatar yethee avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

php-vcr's Issues

Curl Helper passes all headers as a block if CURLOPT_HEADERFUNCTION is set

In CurlHelper line 59, $response->getRawHeaders() returns for me a string containing newlines:
"HTTP/1.1 200 OK
Server: spray-can/1.3.1b
Date: Wed, 21 May 2014 07:52:49 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 375
"
call_user_func on line 60 invokes Guzzle\Http\Curl\RequestMediator->receiveResponseHeader() - (note, a singular method) but this method only parses a single header - indeed it finds the HTTP at the start, removes all newlines and concatenates the headers together - leading to a Response object with no headers set.

Is this a problem in CurlHelper->handleOutput() - should it iterate through the lines of the raw headers? Or is it a $curlOptions mis-configuration? Or a problem in guzzle3 ?

SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from...

I'm trying to use this with:

  • phpunit-testlistener-vcr
  • Laravel 4.1.29
  • PHP 5.5.6 (soap is turned on, have checked in php -i)
  • Windows

I have a test which calls a SOAP WSDL with SoapClient. It works fine, until I put this before it:

/**
* @vcr unittest_foobar
*/

This creates an empty fixture but now the test fails with this:
SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from 'https://api.e-conomic.com/secure/api1/EconomicWebservice.asmx?W
SDL' : failed to load external entity "https://api.e-conomic.com/secure/api1/EconomicWebservice.asmx?WSDL"

However, I can change the test to instead of a SOAP call just do:

$testfile = file_get_contents('http://www.google.com');

Now it all works fine and the fixture is created as it should be. Any ideas?

Can't install with composer

Attempting to install with composer I'm getting the following from composer

Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for adri/php-vcr dev-master -> satisfiable by adri/php-vcr[dev-master].
    - adri/php-vcr dev-master requires beberlei/assert dev-master -> no matching package found.

Potential causes:
 - A typo in the package name
 - The package is not available in a stable-enough version according to your minimum-stability setting
   see <https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion> for more details.

Read <http://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.

I'm going to investigate further to see if I can resolve the problem, but any insight would be welcome.

BeforePlaybackEvent does not happen before playback

Kudos to @aaa2000 who added hooks on #86.

The BeforePlaybackEvent does not actually happen before playback. Look at Videorecorder.php:

public function handleRequest(Request $request)
{
    ...
    if ($this->cassette->hasResponse($request)) {
        $this->dispatch(VCREvents::VCR_BEFORE_PLAYBACK, new BeforePlaybackEvent($request, $this->cassette));
        $response = $this->cassette->playback($request);
        $this->dispatch(VCREvents::VCR_AFTER_PLAYBACK, new AfterPlaybackEvent($request, $response, $this->cassette));

hasResponse just calls playback. Thus, the code should look more like this:

$this->dispatch(VCREvents::VCR_BEFORE_PLAYBACK, new BeforePlaybackEvent($request, $this->cassette));
if ($this->cassette->hasResponse($request)) {
    $response = $this->cassette->playback($request);
    $this->dispatch(VCREvents::VCR_AFTER_PLAYBACK, new AfterPlaybackEvent($request, $response, $this->cassette));
    ...

This feels gross, but is more correct.

So, how to fix the grossness of dispatching the event before the if? Some ideas:

  • Dispatch the VCR_BEFORE_PLAYBACK event in the actual playback method. This means the event would be dispatched each time playback happens. That might be what's wanted. If not, we could keep track if the event has already been dispatched.
  • Why is playback responsible for finding a response? Doesn't that make more sense for hasResponse to do? Or really, I think the code that does the work of finding find the response should be moved from playback into a getResponse method, which is called both from hasResponse and playback.

I'm happy to do any of this work. Just asking for input first.

Error when checking for casette file is_writable in vagrant

we get the following exception:

VCR\VCRException: Specified path 'tests/fixtures/default' must be writeable.

/var/www/vendor/beberlei/assert/lib/Assert/Assertion.php:205
/var/www/vendor/beberlei/assert/lib/Assert/Assertion.php:1177
/var/www/vendor/php-vcr/php-vcr/src/VCR/Storage/AbstractStorage.php:68
/var/www/vendor/php-vcr/php-vcr/src/VCR/Storage/Yaml.php:36
/var/www/vendor/php-vcr/php-vcr/src/VCR/VCRFactory.php:53
/var/www/vendor/php-vcr/php-vcr/src/VCR/VCRFactory.php:119
/var/www/vendor/php-vcr/php-vcr/src/VCR/VCRFactory.php:98
/var/www/vendor/php-vcr/php-vcr/src/VCR/Videorecorder.php:177
/var/www/vendor/php-vcr/php-vcr/src/VCR/VCR.php:20
/var/www/src/Acme/Tests/MyTestCase.php:42

the file is created with 664, and when typing ls -l the system reports the file as belonging to user 1000 (which is my user id on the host system). inside vagrant, the vagrant user has id 501. but somehow the linux filesystem can still write the file. and the is_writable function is true when the stream wrapper is not registered. as soon as its registered, files are considered not writable.

i managed to confirm the problem by changing the vagrant user id inside the box to user 1000. ls -l reports the file as owned by the "vagrant" user instead of numeric now. with this, the file is considered writable even with the stream wrapper.

i could live with the workaround except for our continuous integration that is seeing the same problem.

Always CurlException When Using Guzzle

Apologies if I'm showing my naivety here. I started using this package today, and everything works great -- except when I hook into Guzzle. Here's an example:

    public function test_intercept()
    {
        VCR::turnOn();

        VCR::insertCassette('test');

        $c = new Guzzle\Http\Client;
        $rq = $c->get('http://google.com');
        $rq->send();

        VCR::eject();
        VCR::turnOff();
    }

When I run phpunit, I get:

1) ExampleTest::test_intercept
Guzzle\Http\Exception\CurlException: [curl] 6: Couldn't resolve host 'google.com, google.com' [url] http://google.com, google.com

...and the fixtures/test file is created, but it's blank. If I comment out all the VCR method calls, the Guzzle HTTP request goes through, per usual.

I've tested PHP-VCR with file_get_contents() and the basic curl extension, and everything works great. I'm just struggling with Guzzle usage...

I'm using:

  • php-vcr dev-master
  • Guzzle 3.7

Any ideas? Is there some extension or override that I'm missing? As an aside, the docs mention installing the runkit extension, but I couldn't tell if that's required to intercept curl requests.

Guzzle request not intercepted when called in a PHPUnit test case

Hi,

When I only use guzzle all works fine:

public function test_GuzzleFail()
{
    $client = new Guzzle\Http\Client('https://www.google.com');
    $response = $client->get('/')->send();
}

But the time I use vcr, guzzle will get a bad response and the fixture remains empty:

/**
 * @vcr guzzletest.yml
 */
public function test_GuzzleFail()
{
    $client = new Guzzle\Http\Client('https://www.google.com');
    $response = $client->get('/')->send();
}
Guzzle\Http\Exception\ClientErrorResponseException : Client error response
[status code] 404
[reason phrase] Not Found
[url] https://www.google.com/

Wrong JSON stored in cassette

I have a test which uses file "testSearch.json" as cassette.

If the file does not already exist before running the test, the JSON stored in it is correct:

[{ .... }]

If the file already exists and is blank, will store a wrong JSON:

{ ... }]

We can see that the leading square bracket is missing

Casset recorded but is not used in the next request

Hello I have VCR properly configured and it records the cassette, but when I run the test again it makes the HTTP request again without using the cassette.

Question, the HTTP response code is changed after the first request, could that be the reason or is other issue?

Add more information when things go wrong

If a invalid HTTP request is detected it would be nice to have additional information like the url, parameters (...) about the invalid request.

The current exception message is:

BadMethodCallException: Invalid http request. No cassette inserted. Please make sure to insert a cassette in your unit test using VCR::insertCassette('name');

Non GET HTTP requests

Is this framework meant to work with non GET requests such as POST because at current I've found that any requests made using POST break my tests and the results inside the yml fixtures show that they're GET requests instead of POST requests.

I'm using Guzzle and therefore Curl in my library that I am testing.

If non GET requests aren't supported it should really be mentioned clearly on the Documentation pages, if not I assume it's a bug?

[solved] Cassette path '/tmp/vcr' doesn't exist.

edit

I figured it out. The init system that I use (systemd) has a notion if "private tmp", which makes the process see an isolated /tmp directory, which is not the same as the /tmp on the disk. Sorry bout that.

I know this is one of the most basic things, but I'm 100% sure that the directory exists, and I can't figure out what's wrong. I'm trying to make php-vcr save the responses to a file:

$config = VCR::configure();
$config->enableLibraryHooks('curl_rewrite');
$config->setCassettePath('/tmp/vcr');
VCR::turnOn();
VCR::insertCassette('1234');
$client = new \Guzzle\Http\Client();
$rq = $client->post($nlpUrl);
$result = $rq->send();
VCR::eject();
VCR::turnOff();

In the shell:

$ mkdir /tmp/vcr
$ chmod 777 /tmp/vcr

Yet, when I run the script, I get:

Cassette path '/tmp/vcr' doesn't exist.

If I stop execution with a debugger, and issue a "scandir('/tmp')", it lists '.', and '..', so it really does not see anything else in /tmp. Does php-vcr somehow hook into into file checks, even though I have specified I only want it to hook into curl?

This is on PHP 5.5.5, with OPCache disabled.

Guzzle Request Posts are Null

Sorry to bring up another issue but in 1.1.1 it works for post requests but in the fixtures generated from the requests it's not saving the request body meaning that tests which use the same cassette and url will collide. I'm not sure if this is intentional or not?

An example can be seen in running the tests from my library, in tests/fixtures/CreateInvoice_Working there's no post body when the API is creating one for that request.

https://github.com/peterfox/BitPayClient/tree/master

Does PHP-VCR work with stream_context_create?

I'm using the expert-sender-api library. The request in the cassette that ends up recorded is almost blank:

    request:
        method: GET
        url: [URL HERE]
        headers:
            Host: [HOST HERE]
            Content-Length: '0'

There should be a request body, and the content-length should not be zero (it's also odd it's a string, no?). Also, the method there is GET, but it should be POST. If I disable PHP-VCR, and perform a live web service call, the request goes out as I expect and then I get a proper response. So something is not right or fully handled by PHP-VCR here.

If I enabled PHP-VCR and let it send the blank request, it captures the response. As you might expect, it's a response to the blank request, but in my case, the response contains 9 headers and an XML body that I'd expect. So that part works.

The expert-sender-api library's query() method here makes use of PHP's stream_context_create I'm wondering if that has anything to do with it. I suppose not, because that's part of HTTP wrapper, which PHP-VCR explicitly states it supports.

If I manually create the cassette request, there's still another problem. Weirdly (this is directly from the php manual):

When using the HTTP wrapper, $http_response_header will be populated with the HTTP response headers. $http_response_header will be created in the local scope.

The fatal error I get with PHP-VCR is:

Undefined variable: http_response_header

That variable is from line 32. Is it possible for PHP-VCR to handle this? I know essentially how PHP-VCR works, and I can't imagine it dealing with that. Searching the source code for http_response_header yields nothing.

Matching URL seems to not be working with curl requests

I noticed that my cassettes were being recorded, but the recorder couldn't resolve cassettes (even ones that were just created). I tracked it down to the matchUrl method in the RequestMatcher class:

public static function matchUrl(Request $first, Request $second)
{
    if (null !== $first->getPath()) {
        $path = str_replace('#', '\\#', $first->getPath());

        if (!preg_match('#'.$path.'#', rawurldecode($second->getPath()))) {
            return false;
        }
    }

    return true;
}

Here are the contents of the cassette:

-
    request:
        method: GET
        url: 'https://api.balancedpayments.com/marketplaces%3Flimit%3D2%26offset%3D0'
        headers:
            Host: api.balancedpayments.com
            Content-Type: ''
            User-Agent: balanced-php/1.1.2
            Accept: 'application/vnd.balancedpayments+json; version=1.1, application/vnd.api+json'
            Content-Length: '0'
    response:
        status: 200
        headers:
            Content-Type: application/json
            Date: 'Sun, 27 Jul 2014 02:23:01 GMT'
            Server: ngx_openresty/1.2.6.3
            Content-Length: '1487'
            Connection: keep-alive
        body: "{removed for privacy}"

Now the problem seems to be in how it's comparing the URLs. The value of the $path variable is:

string(38) "/marketplaces%3Flimit%3D2%26offset%3D0"

And the result of $second->getPath() is:

string(38) "/marketplaces%3Flimit%3D2%26offset%3D0"

But when you run rawurldecode on it, it becomes:

string(30) "/marketplaces?limit=2&offset=0"

So then the preg_match is:

preg_match('#/marketplaces%3Flimit%3D2%26offset%3D0#', '/marketplaces?limit=2&offset=0')

Which doesn't seem to want to match. When I replace it with:

if ($second->getPath() != $first->getPath()) {

...all is then well in the universe. I suspect there's a reason why you do it this way, though, so I'm curious what you think of this.

Logging

Some logging would be nice :-)

[SOAP] WSDL request not intercepted yet

Although the actual soap method call is intercepted correctly there are actually two request when a SOAP action is performed:

  • Receiving the WSDL, for example http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL
  • POSTing the http://wsf.cdyne.com/WeatherWS/Weather.asmx with <?xml version="1.0...

The first part is not done yet.

Implement fsockopen() interception?

fsockopen communicates directly using the created socket connect. Overwriting fsockopen() calls with a StreamProcessor and creating an intercepting implementation should be possible. Any need for that?

Curl interception: Alternative to runkit extension

Idea: Overwrite curl_* functions by intercepting require/include calls and rewriting included source code on the fly.

First draft:

class SourceTransform
{
    public $context;
    public $position = 0;
    public $bodyData;

    public static function transformCode($code)
    {
        return str_replace('curl_init', '\VCR\VCR::curl_init', $code);
    }

    /**
     * Wraps all calls to this stream wrapper restore.
     */
    public function __call($method, $args)
    {
        $localMethod = str_replace('stream_', '', $method);
        if (!method_exists($this, $localMethod)) {
            throw new \BadMethodCall("Method '{$method}' not found.");
        }

        stream_wrapper_restore('file');
        $returnValue = null;
        try {
            $returnValue = call_user_func_array(array($this, $localMethod), $args);
        } catch (Exception $e) {
            stream_wrapper_unregister('file');
            stream_wrapper_register('file', __CLASS__);
            throw $e;
        }

        stream_wrapper_unregister('file');
        stream_wrapper_register('file', __CLASS__);


        return $returnValue;
    }

    protected function open($path, $mode, $options, &$opened_path) {
        $this->bodyData = self::transformCode(file_get_contents($path));
        return true;
    }

    protected function read($count) {
        $this->position += strlen($this->bodyData);
        if ($this->position > strlen($this->bodyData)) {
            return false;
        }
        return $this->bodyData;
    }

    protected function eof() {
        return $this->position >= strlen($this->bodyData);
    }

    protected function stat() {
        return array('wrapper_data' => array('test'));
    }

    protected function tell() {
        return $this->position;
    }
}

stream_wrapper_unregister('file');
stream_wrapper_register('file', 'SourceTransform');

require 'phpinfo.php';

Segmentation fault when using \VCR\VCR::turnOn() in bootstrap.php

I'm trying to intercept curl request. But when I add VCR in my bootstrap like so:
require_once DIR . '/../vendor/autoload.php';
\VCR\VCR::turnOn();

then run:
$ ./vendor/bin/phpunit -c tests/phpunit.xml

I get:

Segmentation fault (core dumped)

My Info:
I am using a phpunit.xml with a bootstrap.php file.
PHP 5.4.16 (cli) (built: Jul 29 2013 09:45:31)
curl 7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Protocols: tftp ftp telnet dict ldap ldaps http file https ftps scp sftp

Using YML cassette results in "Segmentation fault: 11"

The memory limit set in php.ini on my machine is: memory_limit = 1024M

In the cassette we have a response which contains 163750 characters on a single line.
When we use JSON format the tests are working as expected, but when YML format is used we end up with "Segmentation fault: 11".

I suspect there is an issue with YmlParser (which is not a component developed by you), so I was wondering why YML is default option, not JSON?

Unit test errors when using \LOCK_EX with \file_put_contents.

It seems that PHP-VCR is causing an issue when you try to acquire an exclusive lock on a file with file_put_contents. I have been able to narrow it down to this line: \file_put_contents( $pFileName, $pContent, \LOCK_EX );

I'm using Windows 7, 64-bit with:

PHP 5.6.0RC3 (cli) (built: Jul 30 2014 11:15:07)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.6.0-dev, Copyright (c) 1998-2014 Zend Technologies
    with Xdebug v2.2.4, Copyright (c) 2002-2014, by Derick Rethans

If I do NOT remove the LOCK_EX parameter, I get the following error if PHP-VCR is turned on in my bootstrap file:

There was 1 error:

1) Kshabazz\Tests\Test\VcrCurlTest::test_saveFile_function
flock(): Illegal operation argument

E:\Khalifah\Projects\test\vendor\php-vcr\php-vcr\src\VCR\Util\StreamProcessor.php:461
E:\Khalifah\Projects\test\src\functions.php:22
E:\Khalifah\Projects\test\tests\VcrCurlTest.php:37
C:\Users\Khalifah\vendor\phpunit\phpunit\src\TextUI\Command.php:186
C:\Users\Khalifah\vendor\phpunit\phpunit\src\TextUI\Command.php:138

FAILURES!
Tests: 2, Assertions: 1, Errors: 1.

I have a function defined as follows:

function saveFile( $pFileName, $pContent )
{
    $directory = \dirname( $pFileName );
    if ( !is_dir($directory) )
    {
        $madeDir = \mkdir( $directory, 0755, TRUE );
        if ( $madeDir === FALSE )
        {
            throw new \Exception( "mkdir: Unable make directory '{$directory}'." );
        }
    }
    // Save data to a file.
    $fileSaved = \file_put_contents( $pFileName, $pContent, \LOCK_EX );
    if ( $fileSaved === FALSE )
    {
        throw new \Exception( "file_put_contents: Unable to save file '{$pFileName}'." );
    }

    return $fileSaved;
}

I test it with this code:

    /**
     * Test saveFile.
     */
    public function test_saveFile_function()
    {
        // todo: change to temp space.
        $tempDir = $this->fixtures . '/bunker';
        $file = $tempDir . '/save-test-file.txt';
        $saved = \Kshabazz\Test\saveFile( $file, 'test' );
        $this->assertEquals( 4, $saved, 'Unable to save file.' );
        $this->assertFileExists( $file, 'Unexpected data loaded from empty JSON test file.' );
        \unlink( $file );
        \rmdir( $tempDir );
    }

Curl response not working with Balanced Payments

So I've created a test marketplace for this issue so that you can try this. Install the latest balanced-php from composer and run the following:

VCR::configure()->setCassettePath(__DIR__ . '/../cassettes')
                ->enableLibraryHooks(['curl']);

Httpful\Bootstrap::init();
RESTful\Bootstrap::init();
Balanced\Bootstrap::init();

Balanced\Settings::$api_key = "ak-test-2c3ugp5QD8xIMr2JsJIojehKl9b6JTDNT";


VCR::turnOn();
VCR::insertCassette('foo');

$card = Balanced\Card::get("/cards/CC6zkcT0FHN4O32BWMBxXJUC");

Without fail, I get this error:

Guzzle\Http\Exception\CurlException: [curl] 23: Failed writing body (0 != 17) [url] https://api.balancedpayments.com/cards/CC6zkcT0FHN4O32BWMBxXJUC

C:\Web\repo\vendor\guzzle\guzzle\src\Guzzle\Http\Curl\CurlMulti.php:342
C:\Web\repo\vendor\guzzle\guzzle\src\Guzzle\Http\Curl\CurlMulti.php:283
C:\Web\repo\vendor\guzzle\guzzle\src\Guzzle\Http\Curl\CurlMulti.php:248
C:\Web\repo\vendor\guzzle\guzzle\src\Guzzle\Http\Curl\CurlMulti.php:231
C:\Web\repo\vendor\guzzle\guzzle\src\Guzzle\Http\Curl\CurlMulti.php:215
C:\Web\repo\vendor\guzzle\guzzle\src\Guzzle\Http\Curl\CurlMulti.php:109
C:\Web\repo\vendor\guzzle\guzzle\src\Guzzle\Http\Curl\CurlMultiProxy.php:94
C:\Web\repo\vendor\guzzle\guzzle\src\Guzzle\Http\Client.php:284
C:\Web\repo\vendor\php-vcr\php-vcr\src\VCR\Util\HttpClient.php:39
C:\Web\repo\vendor\php-vcr\php-vcr\src\VCR\Videorecorder.php:176
C:\Web\repo\vendor\php-vcr\php-vcr\src\VCR\Videorecorder.php:214
C:\Web\repo\vendor\php-vcr\php-vcr\src\VCR\LibraryHooks\CurlHook.php:187
C:\Web\repo\vendor\php-vcr\php-vcr\src\VCR\LibraryHooks\CurlHook.php:154
C:\Web\repo\vendor\nategood\httpful\src\Httpful\Request.php:195
C:\Web\repo\vendor\nategood\httpful\src\Httpful\Request.php:195
C:\Web\repo\vendor\nategood\httpful\src\Httpful\Request.php:212
C:\Web\repo\vendor\matthewfl\restful\src\RESTful\Client.php:67
C:\Web\repo\vendor\matthewfl\restful\src\RESTful\Client.php:24
C:\Web\repo\vendor\matthewfl\restful\src\RESTful\Resource.php:191
C:\Web\repo\workbench\api\tests\integration\Payment\Payment.php:120

I tried setting up a simple test that exactly replicates the curl options set by doing this:

VCR::turnOn();
VCR::insertCassette('foo');
$uri = "https://api.balancedpayments.com/cards/CC6zkcT0FHN4O32BWMBxXJUC/";
$ch = curl_init($uri);

curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_USERPWD, 'ak-test-2c3ugp5QD8xIMr2JsJIojehKl9b6JTDNT:');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$headers = array();
// https://github.com/nategood/httpful/issues/37
// Except header removes any HTTP 1.1 Continue from response headers
$headers[] = 'Expect:';
$headers[] = "Content-Type: ";
$headers[] = "User-Agent: balanced-php/1.0";
$headers[] = "Accept: application/vnd.balancedpayments+json; version=1.1, application/vnd.api+json";

curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_HEADER, 1);

$result = curl_exec($ch);

...but when I do this, I can't even get php-vcr to intercept the request at all. I think it catches the curl_init and then nothing afterward. I'm a bit lost on how to proceed.

It's worth mentioning that the first request works when it's not being intercepted. The second request works every time because it's not being intercepted.

Curl malformed url

Hello,
I'm trying to use php-vcr which will improve a lot the testability of our system, but I have a problem.
I get this error which I cannot understand:
Guzzle\Http\Exception\CurlException: [curl] 3: <url> malformed [url]
I'm testing an interaction with Amazon SQS in a Yii/PHPunit test suite.

I'm starting php-vcr in the bootstrap.php file like so:

\VCR\VCR::configure()->setCassettePath('fixtures');
\VCR\VCR::turnOn();

and then the setUpBeforeClass is already failing with the error I've said in the beginning.

public static function setUpBeforeClass() {
    \VCR\VCR::insertCassette('example');
    self::$helper = new AmazonSQSHelper( 'send-access-stk' );
    parent::setUpBeforeClass();
}

I tried to replace the Amazon call with this standard curl code:

$ch = curl_init("http://www.example.com/");
$fp = fopen("example_homepage.txt", "w");

curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);

curl_exec($ch);
curl_close($ch);
fclose($fp);

and it works fine. Do you have any suggestion on how to understand more about the problem?
Thanks a lot.

Compatibility with HHVM

Issue to track compatibility issues with HHVM

  • [HHVM] constructor for php_user_filter requires three internal parameters

Empty cassette file after request

I'm not sure if I'm using the library incorrectly, but I can't seem to get it to save requests sent with Guzzle:

$config = VCR::configure();
$config->enableLibraryHooks('curl_rewrite');
$dir = '/srv/http/project/vcr';
$config->setCassettePath($dir);
VCR::turnOn();
VCR::insertCassette('1234');
$client = new \Guzzle\Http\Client();
$rq = $client->post($nlpUrl);
$result = $rq->send();
VCR::eject();
VCR::turnOff();

After running this code, a file does appear in in the cassette directory, but it's totally empty.

Am I using the library incorrectly?

Add request and response body formatter for more readable recordings.

For easier debugging and general readability it would be nice to have post_fields and body formatted in the yaml file.

For example, when calling a elastic search server the recording of a http interaction looks like this at the moment:

-
    request:
        method: GET
        url: 'http://localhost:9200/website/_search'
        headers:
            host: 'localhost:9200'
            content-type: 'application/x-www-form-urlencoded; charset=utf-8'
        post_fields:
            '{"sort":': { '{"_geo_distance":{"geopoint":"48.782300,9.177020","order":"asc","unit":"km"}}': '' }
    response:
        status: 200
        headers:
            Content-Type: 'application/json; charset=UTF-8'
            Content-Length: '8349'
        body: '{"took":8,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":100,"max_score":1.0,"hits":[{"_index":"website","_type":"location","_id":"4","_score":1.0, "_source" : {"id":4,"name":"Edwardtown","description":"Eos omnis inventore perferendis voluptatem nisi. Consequatur ullam voluptas et tempora. Corporis excepturi sint dolores quaerat odit quia nisi accusantium.","directions":"Ut explicabo corporis eligendi ut. Sapiente ut qui quidem explicabo optio amet velit aut. Iure sed alias asperiores perspiciatis deserunt omnis inventore mollitia.","is_private_access":false,"photos":[],"tags":[{"id":37,"tag":"aliquam","created_at":"2013-08-01T21:07:20+02:00","updated_at":"2013-08-01T21:07:20+02:00"}],"geopoint":{"lat":82.420116,"lon":33.733625},"created_at":"2009-08-20T12:08:06+02:00","updated_at":"2013-08-01T21:07:20+02:00"}}, .... }'

I suggest formatters for JSON and XML.

StreamProcessor: Stat missing file causes exception

I have a project which autoloads classes for a namespace from multiple directories.

After integrating PHP-VCR an exception is thrown when the Composer autoloader first tries to include the class from the first directory (where the file is not located) before trying in the second directory. This causes my tests to fail.

As far as I can tell the problem seems to be related to StreamProcessor->url_stat() but here errors caused by the stat call should be suppressed.

Notes:

Any idea what could cause this?

Fail running test

Hi I'm running php-vcr tests with Guzzle 3.7.

And I get the error
PHP Fatal error: Cannot use object of type Guzzle\Http\Message\Header as array in /opt/app/framework/vendor/adri/php-vcr/src/VCR/Response.php on line 33

When the test testShouldInterceptStreamWrapper() is runned.

Then I modified the line
https://github.com/adri/php-vcr/blob/master/src/VCR/Response.php#L32
for something like

            $value=$value->toArray();
            $headers[$key] = $value[0];

after the change I get
Segmentation fault (core dumped)

Let me know if you have any ideas how to solve this.

php-vcr unit test fail on Windows 7

Trying to run unit test on latest master branch fails. Could it be that PHP-VCR is overwriting its own class?

On windows 7, with PHP 5.6 RC3

Khalifah@KHALIFAH-PC /e/Khalifah/Projects/php-vcr (master)
$ phpunit
PHPUnit 3.7.37 by Sebastian Bergmann.

Configuration read from E:\Khalifah\Projects\php-vcr\phpunit.xml

...............................................................  63 / 198 ( 31%)
...............
Fatal error: Class 'VCR\Util\SoapClient' not found in E:\Khalifah\Projects\php-vcr\src\VCR\Util\SoapClient.php on line 12

Call Stack:
    0.0000     124248   1. {main}() C:\Users\Khalifah\vendor\phpunit\phpunit\phpunit:0
    0.0040     326056   2. PHPUnit_TextUI_Command::main() C:\Users\Khalifah\vendor\phpunit\phpunit\phpunit:54
    0.0040     326384   3. PHPUnit_TextUI_Command->run() C:\Users\Khalifah\vendor\phpunit\phpunit\src\TextUI\Command.php:138
    0.7470    3746792   4. PHPUnit_TextUI_TestRunner->doRun() C:\Users\Khalifah\vendor\phpunit\phpunit\src\TextUI\Command.php:186
    0.8020    3926400   5. PHPUnit_Framework_TestSuite->run() E:\Khalifah\Projects\php-vcr\vendor\phpunit\phpunit\PHPUnit\TextUI\TestRunner.php:350
    1.7681    6218960   6. PHPUnit_Framework_TestSuite->run() E:\Khalifah\Projects\php-vcr\vendor\phpunit\phpunit\PHPUnit\Framework\TestSuite.php:709
    1.7681    6219440   7. PHPUnit_Framework_TestSuite->runTest() E:\Khalifah\Projects\php-vcr\vendor\phpunit\phpunit\PHPUnit\Framework\TestSuite.php:
749
    1.7681    6219792   8. PHPUnit_Framework_TestCase->run() E:\Khalifah\Projects\php-vcr\vendor\phpunit\phpunit\PHPUnit\Framework\TestSuite.php:779
    1.7691    6220040   9. PHPUnit_Framework_TestResult->run() E:\Khalifah\Projects\php-vcr\vendor\phpunit\phpunit\PHPUnit\Framework\TestCase.php:783
    1.7691    6221264  10. PHPUnit_Framework_TestCase->runBare() E:\Khalifah\Projects\php-vcr\vendor\phpunit\phpunit\PHPUnit\Framework\TestResult.php:
648
    1.7691    6255192  11. PHPUnit_Framework_TestCase->runTest() E:\Khalifah\Projects\php-vcr\vendor\phpunit\phpunit\PHPUnit\Framework\TestCase.php:83
8
    1.7691    6255720  12. ReflectionMethod->invokeArgs() E:\Khalifah\Projects\php-vcr\vendor\phpunit\phpunit\PHPUnit\Framework\TestCase.php:988
    1.7691    6255856  13. VCR\LibraryHooks\SoapHookTest->testShouldInterceptCallWhenEnabled() E:\Khalifah\Projects\php-vcr\vendor\phpunit\phpunit\PHP
Unit\Framework\TestCase.php:988
    1.7701    6256616  14. spl_autoload_call() E:\Khalifah\Projects\php-vcr\vendor\phpunit\phpunit\PHPUnit\Framework\TestCase.php:34
    1.7701    6256648  15. Composer\Autoload\ClassLoader->loadClass() E:\Khalifah\Projects\php-vcr\vendor\phpunit\phpunit\PHPUnit\Framework\TestCase.p
hp:0
    1.7701    6256984  16. Composer\Autoload\includeFile() C:\Users\Khalifah\vendor\composer\ClassLoader.php:274
    1.7731    6265192  17. include('E:\Khalifah\Projects\php-vcr\src\VCR\Util\SoapClient.php') C:\Users\Khalifah\vendor\composer\ClassLoader.php:382

CurlHelper::handleOutput() must be an instance of VCR\Response, null given

I'm getting this error while trying to use vcr and I can't find the solution to the problem:

1) DataFetcherTest::test_fetch_should_return_from_api
Argument 1 passed to VCR\Util\CurlHelper::handleOutput() must be an instance of
VCR\Response, null given, called in G:\PHP\EveFetcher\workbench\beijer\eve-fetch
er\vendor\php-vcr\php-vcr\src\VCR\LibraryHooks\CurlHook.php on line 193 and defi
ned

G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\vendor\php-vcr\php-vcr\src\VCR\Ut
il\CurlHelper.php:52
G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\vendor\php-vcr\php-vcr\src\VCR\Li
braryHooks\CurlHook.php:193
G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\vendor\php-vcr\php-vcr\src\VCR\Li
braryHooks\CurlHook.php:245
G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\vendor\php-vcr\php-vcr\src\VCR\Li
braryHooks\CurlHook.php:150
G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\vendor\guzzle\guzzle\src\Guzzle\H
ttp\Curl\CurlMulti.php:225
G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\vendor\guzzle\guzzle\src\Guzzle\H
ttp\Curl\CurlMulti.php:225
G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\vendor\guzzle\guzzle\src\Guzzle\H
ttp\Curl\CurlMulti.php:211
G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\vendor\guzzle\guzzle\src\Guzzle\H
ttp\Curl\CurlMulti.php:105
G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\vendor\guzzle\guzzle\src\Guzzle\H
ttp\Curl\CurlMultiProxy.php:91
G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\vendor\guzzle\guzzle\src\Guzzle\H
ttp\Client.php:282
G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\vendor\guzzle\guzzle\src\Guzzle\H
ttp\Message\Request.php:198
G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\src\Beijer\EveFetcher\Core\DataFe
tcher.php:45
G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\src\Beijer\EveFetcher\Core\DataFe
tcher.php:28
G:\PHP\EveFetcher\workbench\beijer\eve-fetcher\tests\DataFetcherTest.php:16

The fixture is getting the info it should recieve:

-
    request:
        method: GET
        url: 'https://api.eveonline.com/server/ServerStatus.xml.aspx'
        headers:
            host: api.eveonline.com
            user-agent: 'Guzzle/3.8.1 curl/7.30.0 PHP/5.5.2'
            content-length: 0
    response:
        status: 200
        headers:
            Transfer-Encoding: chunked
            Content-Type: 'application/xml; charset=utf-8'
            Date: 'Sun, 06 Apr 2014 15:42:06 GMT'
        body: "<?xml version='1.0' encoding='UTF-8'?>\r\n<eveapi version=\"2\">\r\n  <currentTime>2014-04-06 15:42:07</currentTime>\r\n  <result>\r\n    <serverOpen>True</serverOpen>\r\n    <onlinePlayers>40065</onlinePlayers>\r\n  </result>\r\n  <cachedUntil>2014-04-06 15:42:19</cachedUntil>\r\n</eveapi>"
-
    request:
        method: GET
        url: 'https://api.eveonline.com/server/ServerStatus.xml.aspx'
        headers:
            host: api.eveonline.com
            user-agent: 'Guzzle/3.8.1 curl/7.30.0 PHP/5.5.2'
            content-length: 0
    response:
        status: 200
        headers:
            Transfer-Encoding: chunked
            Content-Type: 'application/xml; charset=utf-8'
            Date: 'Sun, 06 Apr 2014 15:42:20 GMT'
        body: "<?xml version='1.0' encoding='UTF-8'?>\r\n<eveapi version=\"2\">\r\n  <currentTime>2014-04-06 15:42:20</currentTime>\r\n  <result>\r\n    <serverOpen>True</serverOpen>\r\n    <onlinePlayers>40171</onlinePlayers>\r\n  </result>\r\n  <cachedUntil>2014-04-06 15:45:19</cachedUntil>\r\n</eveapi>"

Update guzzle dependency

Is it possible to update the dependency of guzzle to 3.8?
I'm using guzzle in a project where I need 3.8 but it will collide with php-vcr.

Prefix any curl method with "\" causes a parse error

Problem: When I prefix any curl method with "" I get parse error:

Parse error: syntax error, unexpected '' (T_NS_SEPARATOR), expecting identifier
(T_STRING) in E:\Khalifah\Projects\slib\src\Kshabazz\Slib\HttpRequester.php on
line 111

Work-a-round: Removed the backslash from curl methods.

Background: I prefix global functions with a backslash to refer to the global namespace, I don't have to, but I tend to out of habit. Plus I believe it keeps PHP from looking in the current namespace for the function (I have not verified this),

Modifying length of file can break VCR integration

Tl;dr: Changing the length of your file by adding/removing code can break PHP VCR.

How The Code Works

PHP VCR modifies files when they're being read from the filesystem through the use of PHP's php_user_filter (http://php.net/manual/en/class.php-user-filter.php).

When a file is read from the filesystem, it goes through this function in PHP VCR

<?php
public function filter($in, $out, &$consumed, $closing)
{
        while ($bucket = stream_bucket_make_writeable($in)) {
            $bucket->data = $this->transformCode($bucket->data);
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }

        return PSFS_PASS_ON;
    }
}

which calls transformCode($code) in each code transformer

<?php

class CurlCodeTransform extends AbstractCodeTransform
{

    private static $patterns = array(
        '/(?<!::|->)\\\?curl_init\s*\(/i' => '\VCR\LibraryHooks\CurlHook::curl_init('
        ...
    );

    protected function transformCode($code)
    {
        return preg_replace(array_keys(self::$patterns), array_values(self::$patterns), $code);
    }
}

The transformCode($code) function does a preg_replace and replaces the original code with code that calls PHP VCR.

The Problem

You may have noticed that the filter function operates in a while loop calling stream_bucket_make_writeable. The bug is that this function can split up the stream, i.e. the file that is being read.

If it splits it between a line of code that we're trying to search and replace, then the preg_replace will fail and we won't modify this file.

Example

The class we're rewriting:

<?php
class TransformMe {
    public function test() {
        $curl = curl_init();
    }
}

Ideally, this will be re-written to

<?php
class TransformMe {
    public function test() {
        $curl = \VCR\LibraryHooks\CurlHook::curl_init();
    }
}

However if the file is split like this:

<?php
class TransformMe {
    public function test() {
        $curl = curl_
init();
    }
}

Then the transformation will never happen.

This was fun to debug :)

Solutions?

I'd say the best thing to do would be to read the entire file at once, and then do the search and replace, rather than relying on PHP's streams and bucketing.

Refactor static helper classes/methods to instances

Refactor helper classes as they don't play nice with the open closed and dependency inversion principle and make mocking during tests hard/impossible.

  • CurlHelper and StreamHelper are Mappers (from curl options and stream context to Request objects)
  • Find out what to do with TextUtil and HttpUtil

Instances of the new classes can be injected in the VCRFactory.

Feature request: indicate why cassette does not have necessary response

I got this message:

LogicException: Invalid http request. The cassette inserted did not have the necessary response. If you want to send a request anyway, make sure your mode is set to new_episodes.

Another developer had modified our code, such that the body that was used for an http request was different. He did not modify the cassette, which he should have.

My issue is with the message. How about something like "The request body does not match a previous request"? Or, as I was digging through the code, the failure was not actually on the body, but the Content-Length. Same difference... I think a message saying "The request header ("Content-Length: 1567") did not match an existing request" would be more helpful.

This took me an hour to figure out myself, because I had to dig through the PHP-VCR source code. A more helpful message would've helped me understand the issue immediately.

PHP-VCR with behat

Hi

Thanks for this library.
I cannot seem to find too much on the subject of using VCR with Behat.

I can get the following to execute and write the recording in Behat:

public function iSubmitFeedbackWithValidData()
{
    VCR::turnOn();
    VCR::insertCassette('successful_contact_submit');

    file_get_contents('http://www.google.com');

    VCR::eject();
    VCR::turnOff();
}

But this doesn't work:

public function iSubmitFeedbackWithValidData()
{
    VCR::turnOn();
    VCR::insertCassette('successful_contact_submit');

    $this->fillInContactForm();
    $this->page()->pressButton('Submit');

    VCR::eject();
    VCR::turnOff();
}

Is this a reasonable thing to try and do with php-vcr?

[Util/Streamprocessor] PHP file detection

Currently the Steamprocessor does not recognize files other than those using »php« as file extension as a PHP file and therefore will not process its' contents probably to be replaced in order to make it possible to intercept a request and/or response.

Common extensions for a PHP file might also be:
*.inc
*.php(\d?) //like *.php4 or *.php5
*.phtml
*.cgi

  • ...

Improve performance of StreamProcessor

Because stream_wrapper_register('file', __CLASS__); creates a new instance of StreamProcessor on every intercept() call, which we do once every include. Maybe we can avoid this?

Exception "The YAML value does not appear to be valid UTF-8"

If response body was compressed gzcompress function, we have next exception:

PHP Fatal error: Uncaught exception 'Symfony\Component\Yaml\Exception\ParseException' with message 'The YAML value does not appear to be valid UTF-8.' in /var/www/site1.com/vendor/symfony/yaml/Symfony/Component/Yaml/Parser.php:58\nStack trace:\n#0 /var/www/site1.com/vendor/php-vcr/php-vcr/src/VCR/Storage/Yaml.php(57): Symfony\Component\Yaml\Parser->parse('-? request:?...')\n#1 /var/www/site1.com/vendor/php-vcr/php-vcr/src/VCR/Storage/Yaml.php(107): VCR\Storage\Yaml->next()\n#2 /var/www/site1.com/vendor/php-vcr/php-vcr/src/VCR/Cassette.php(36): VCR\Storage\Yaml->valid()\n#3 /var/www/site1.com/vendor/php-vcr/php-vcr/src/VCR/Cassette.php(25): VCR\Cassette->playback(Object(VCR\Request))\n#4 /var/www/site1.com/vendor/php-vcr/php-vcr/src/VCR/Videorecorder.php(97): VCR\Cassette->hasResponse(Object(VCR\Request))\n#5 /var/www/site1.com/vendor/php-vcr/php-vcr/src/VCR/Videorecorder.php(122): VCR\Videorecorder->handleRequest(Object(VCR\Request))\n#6 /var/www/site1.com/vendor/php-vcr/php-vcr/src/VCR/LibraryHooks/CurlRewrite.php(138): VCR\Video in /var/www/site1.com/vendor/symfony/yaml/Symfony/Component/Yaml/Parser.php on line 58

Example:

request:
method: POST
url: 'http://77777777/serv/index.php'
headers:
host: 77777777
content-type: 'application/x-www-form-urlencoded; charset=utf-8'
post_fields:
request: YTox...XNzIjtpOjE7fQ==
t: b
response:
status: 200
headers:
Date: 'Sun, 29 Dec 2013 13:02:33 GMT'
Server: 'Apache/2.2.15 (CentOS)'
X-Powered-By: PHP/5.3.3
Content-Length: '2391'
Connection: close
Content-Type: 'text/html; charset=UTF-8'
body: 'x���v�8��_ɗ8�,f���3m1v�o;_:�/���`?�T�7$$=m;������BH�O�K����m9���y����,�$E���>Ҝғ�U��빤>���[\mEbo׶;���<�=k�i��)M��M��&;�S$y�����.���oo����y�u��k�����7xO0�ݷ�ޞ.ߓ=��
�>�ŵ�œ� >������?@���I~y����u����r�Y�m���5��������v�K�2� �qa���s���u����G1.��5�k�{Ch۲��@]?*��)�6���c0u�dJ^�W�%���6��\ٲ\u_���i��@vv����s]F

Curl post body is converted into form fields

When setting request body using CURLOPT_POSTFIELDS to a json string (or anything for that matter), php-vcr converts the body into post fields which mangles the request.

Example curl:

<?php

class TestCurl
{
    public function go()
    {
        $content = json_encode(array('foo' => 'bar'));

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, 'http://requestb.in/rkwk87rk');
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
        curl_setopt($ch, CURLOPT_POSTFIELDS, $content);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');

        ob_start();
        curl_exec($ch);
        return ob_get_clean();
    }
}
VCR::turnOn();
VCR::insertCassette('test');

$test = new TestCurl();
$test->go();

VCR::eject();
VCR::turnOff();

Here's the resulting cassette:

{
  "request": {
    "method": "PUT",
    "url": "http://requestb.in/rkwk87rk",
    "headers": {
      "host": "requestb.in",
      "content-type": "application/x-www-form-urlencoded; charset=utf-8"
    },
    "post_fields": {
      "{\"foo\":\"bar\"}": ""
    }
  },
  "response": {
    "status": 200,
    "headers": {
      "Content-Type": "text/html; charset=utf-8",
      "Date": "Wed, 12 Mar 2014 02:37:51 GMT",
      "Server": "gunicorn/18.0",
      "Sponsored-By": "https://www.runscope.com",
      "Content-Length": "3",
      "Connection": "keep-alive"
    },
    "body": "ok\n"
  }
}

Here's the request from requestbin:

HEADERS

X-Request-Id: 1e96392b-a22f-476a-aa5e-05b7847560aa
Host: requestb.in
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Connection: close
Content-Length: 28

RAW BODY

%7B%22foo%22%3A%22bar%22%7D=

"Disables all HTTP requests that you don't explicitly allow"

I see this written in several places of documentation: "Disables all HTTP requests that you don't explicitly allow", but it doesn't seem true for me. I am using a cassette, and the http request details must have changed, because it's telling me:

Guzzle\Http\Exception\CurlException: [curl] 6: Couldn't resolve host 'fakeurl' [url] http://fakeurl/?...

Thank goodness I used fakeurl, because otherwise it would be contacting the live system (unfortunately I don't have a sandbox test system).

Looking at the code (src/VCR/Videorecorder.php):

    public function handleRequest(Request $request)
    {
        if ($this->cassette === null) {
            throw new \BadMethodCallException(
                "Invalid http request. No cassette inserted. "
                . "Please make sure to insert a cassette in your unit test using "
                . "VCR::insertCassette('name');"
            );
        }

        if (!$this->cassette->hasResponse($request)) {
            $this->disableLibraryHooks();
            $response = $this->client->send($request);
            $this->cassette->record($request, $response);
            $this->enableLibraryHooks();
        }

        return $this->cassette->playback($request);
    }

That to me looks like it's always calling out via HTTP (or whichever hook). What am I missing? How can I instead have VCR issue an error if it tries to invoke a live HTTP request?

Symfony 2 Profiler integration

The idea would be to provide a HTTP interaction tab to the symfony profiler during development to inspect all HTTP requests which have been issued. This is not the use case PHP-VCR was developed for but it would be cool.

Any interest in that?

Guzzle v3 causing issues with other packages requiring Guzzle v4

I'm trying to use VCR with another package which uses "guzzlehttp/guzzle": "4.2.2" but because VCR is using "guzzle/http": "3.*" I'm getting an error:

To set a CURLOPT_READFUNCTION, CURLOPT_INFILESIZE must be set.

I've spoken to the maintainer of the other package, omniphx/forrest, in some depth about it in issue 17 on his repository and full details of the errors we've managed to reproduce are available there.

Do you have any plans to upgrade to Guzzle 4 or 5, or is there a workaround you could suggest to solve the issue we're having?

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.