vlucas / bulletphp

A resource-oriented micro PHP framework
http://bulletphp.com
BSD 3-Clause "New" or "Revised" License
418 stars 50 forks source link

Raw Request Not Always Available #46

Closed jcbwlkr closed 7 years ago

jcbwlkr commented 10 years ago

Depending on how your app is ran, the raw body of a request might not be available inside your routes. The problem is that \Bullet\App instantiates a new \Bullet\Request which consumes the contents of php://input which, as is mentioned in the comments, can only be read once.

Here's the setup. Given that I have this app:

<?php

require __DIR__ . "/vendor/autoload.php";

$app = new \Bullet\App();

$app->path("/test", function($request) use($app) {
    $app->put(function($request) use($app) {
        return "You sent me: " . $request->raw();
    });
});

echo $app->run(new \Bullet\Request());

And my client code looks like this:

<?php

$ch = curl_init("http://127.0.0.1/test");

$body = json_encode(array("foo" => "bar"));

curl_setopt($ch, CURLOPT_POSTFIELDS,    $body);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");

curl_exec($ch);

print PHP_EOL;

I should get the following output

You sent me: {"foo":"bar"}

But instead I get

You sent me: 

Digging in to the issue it seems that the problem is that when I call $app->path to define my route, the app internally calls $this->request()->isHHVM() which defines a new \Bullet\Request. That instance of request consumes php://input so the instance I define in echo $app->run(new \Bullet\Request()); doesn't see the raw request.

If I instantiate a \Bullet\Request before I define my app then it gets the raw body. Or if I change the last line of my server code to echo $app->run($app->request()); I can use the instance that has the raw body.

Honestly, I'm not sure how to test this. Is it possible to inject up in to php://input or mock it somehow?

vlucas commented 10 years ago

Ah, yes - this is indeed related to how Bullet gets/sets the current Request object. I will have to take a deeper look at this. As far as mocking php://input - I am not sure, but you may be able to register a custom stream handler for php (if you are allowed to override the built-ins).

jcbwlkr commented 10 years ago

That's worth looking in to. Another idea would be to have some class whose sole purpose is to provide the contents of php://input. You could mock that and expect that it's only called once. I'm not sure where that would be injected so there might need to be some refactoring.

vlucas commented 10 years ago

I switched this back to a raw defined check in my fix for #43. It might also fix this issue. The permanent solution here is a breaking one - to force the Bullet\Request to be injected into Bullet\App as a dependency so it never creates it's own request object on demand at all.

netom commented 7 years ago

This issue no longer seems to present, closing it.