Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

withBody + getBody results in an empty body #150

Closed
rbaarsma opened this issue Apr 29, 2020 · 2 comments
Closed

withBody + getBody results in an empty body #150

rbaarsma opened this issue Apr 29, 2020 · 2 comments

Comments

@rbaarsma
Copy link

rbaarsma commented Apr 29, 2020

I have the following library code, which is implemented in another Symfony application with httplug and auto discovery.

// Both `$requestFactory` and `$streamFactory` are the same instance of `Nyholm\Psr7\Factory\HttplugFactory` (through auto discovery).

$request = $requestFactory->createRequest($method, $url);

$body = json_encode(['test' => 'test']);
$request->withBody($streamFactory->createStream($body));

echo (string)$request->getBody(); // empty string "" (!)

I've traced this problem to the MessageTrait, the withBody method (line 126), does this:

        $new = clone $this;
        $new->stream = $body;  // Note: `stream` is a PRIVATE property and that's probably why this does nothing.

If I change the code to this, it works as expected:

        $this->stream = $body;
        $new = clone $this;

What I don't understand is that nobody else runs into this problem ;)

My version of nyholm/psr7 is 1.2.1

@rbaarsma
Copy link
Author

I'm baffled, but when I change my code to do this, it works

$request = $this->requestFactory->createRequest($method, $this->host.$url)
    ->withBody($this->streamFactory->createStream($body));

echo (string)$request->getBody(); // now returns the $body as expected!

I really did not expect php to see direct chaining differently than using a local variable in between.

In any case, I stlil think this is very werid behavior and my first code should work too or otherwise give some kind of comprehensible exception why I shouldn't do that..

I still don't understand why the two statements are different

@Zegnat
Copy link
Collaborator

Zegnat commented May 1, 2020

Every time you call a method on an object, it actually returns a completely new object rather than updating the existing one. So when you call $request->withBody(…) this call returns a new instance of a Request object while the original object $request is unchanged.

By making sure you redefine the variable $request you can make sure you always have the latest created object ready to use. You can do this by either chaining the method calls (because the final output returned gets assigned to your variable) or by redeclaring the variable like so:

$request = $requestFactory->createRequest($method, $url);

$body = json_encode(['test' => 'test']);
$request = $request->withBody($streamFactory->createStream($body));

echo (string)$request->getBody(); // empty string "" (!)

Note how I have updated line 4 of your example code to start with $request = .

This is because PSR-7 objects are defined to be immutable. Meaning (in short) they cannot be changed after being created. To quote from the PSR-7 definition of a Request object:

 * Requests are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.

You can read more about this on the PHP-FIG’s website for PSR-7, under “Why value objects?”.

I am closing this issue as there is nothing here for this library to address. But if you have any follow-up questions, feel free to post them as I am happy to answer them :)

@Zegnat Zegnat closed this as completed May 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants