5

The majority of responses in my application are either views or JSON. I can't figure out how to put them in objects that implement ResponseInterface in PSR-7.

Here is what I currently do:

// Views
header('Content-Type: text/html; charset=utf-8');
header('Content-Language: en-CA');
echo $twig->render('foo.html.twig', array(
    'param' => 'value'
    /* ... */
));

// JSON
header('Content-Type: application/json; charset=utf-8');
echo json_encode($foo);

Here is what I am attempting to do with PSR-7:

// Views
$response = new Http\Response(200, array(
    'Content-Type' => 'text/html; charset=utf-8',
    'Content-Language' => 'en-CA'
));

// what to do here to put the Twig output in the response??

foreach ($response->getHeaders() as $k => $values) {
    foreach ($values as $v) {
        header(sprintf('%s: %s', $k, $v), false);
    }
}
echo (string) $response->getBody();

And I suppose it would be similar for the JSON response just with different headers. As I understand the message body is a StreamInterface and it works when I try to output a file resource created with fopen but how do I do it with strings?

Update

Http\Response in my code is actually my own implementation of the ResponseInterface in PSR-7. I have implemented all of the interfaces as I am currently stuck with PHP 5.3 and I couldn't find any implementations that were compatible with PHP < 5.4. Here is the constructor of Http\Response:

public function __construct($code = 200, array $headers = array()) {
    if (!in_array($code, static::$validCodes, true)) {
        throw new \InvalidArgumentException('Invalid HTTP status code');
    }

    parent::__construct($headers);
    $this->code = $code;
}

I can modify my implementation to accept the output as a constructor argument, alternatively I can use the withBody method of the MessageInterface implementation. Regardless of how I do it, the issue is how to get a string into a stream.

rink.attendant.6
  • 44,500
  • 61
  • 101
  • 156

1 Answers1

2

ResponseInterface extends MessageInterface, which provides the getBody() getter you've found. PSR-7 expects the object implementing ResponseInterface to be immutable, which you will not be able to achieve without modifying your constructor.

As you are running PHP < 5.4 (and can't type-hint effectively), modify it as follows:

public function __construct($code = 200, array $headers = array(), $content='') {
  if (!in_array($code, static::$validCodes, true)) {
    throw new \InvalidArgumentException('Invalid HTTP status code');
  }

  parent::__construct($headers);
  $this->code = $code;
  $this->content = (string) $content;
}

Define a private member $content as follows:

private $content = '';

And a getter:

public function getBody() {
  return $this->content;
}

And you're good to go!

Sébastien Renauld
  • 19,203
  • 2
  • 46
  • 66
  • I'm not using Slim, and `Http\Response` is actually my own implementation of `ResponseInterface` (the constructor takes two parameters). I've implemented the `withBody` method in the `MessageInterface`, but that takes a `StreamInterface`as a parameter so the same issue still remains with creating a stream of a string. I can modify my implementation of `ResponseInterface` to accept a string `$body` but I wouldn't know how to implement it... – rink.attendant.6 Nov 22 '15 at 11:26
  • @rink.attendant.6: Then, obviously, you need to implement a setter for the body or a constructor argument. As, by PSR-7, all response objects are supposed to be immutable, it'll have to be a constructor argument. Can you post your code for the `Response` object? I'll edit my answer afterwards. – Sébastien Renauld Nov 22 '15 at 11:43
  • I've updated the question with the constructor of the `Response` object. Apart from a method to check if the code is valid, it has the bare minimum to implement the three methods in the interface. – rink.attendant.6 Nov 22 '15 at 11:46
  • 1
    That works... but aren't I supposed to return a `StreamInterface` with `getBody()`? At least I would expect a subclass that overrides a method to return an object compatible with that interface – rink.attendant.6 Nov 22 '15 at 12:09
  • @rink.attendant.6: If you want to go that far, get the `StreamInterface` interface. This won't fit in a comment so I'll edit again. However, for most use cases, major frameworks currently only really use `__toString()`. – Sébastien Renauld Nov 22 '15 at 12:15
  • First you have to get response body `$body = $response->getBody();` write anything you need to to the message body `$body->write('only string content here');`. Do not forget to add the stream back to the response `$response->withBody($body);`. – David Cery Jan 06 '17 at 08:19