CakeResponse in CakePHP 2.0

Previously I wrote about the changes that have been done for the request handling in CakePHP 2.0. Response handling is another subsystem that has received a significant facelift. As with request information and functionality, response related features were spread across several objects. Controller, RequestHandler and Dispatcher all had a slice of the pie. For 2.0 the goal was to consolidate all of that logic into a single object and make all the previous methods delegate to this object.

CakeResponse approach

When starting to build the new object we looked at the previously mentioned classes and started pulling out the features that made sense as a single object. CakeResponse provides an interface to wrap the common response related tasks such as:

  • Sending headers for redirects.
  • Sending content type headers.
  • Sending any header.
  • Sending the response body.

CakePHP uses CakeResponse by default. In general we aimed to keep CakeResponse a flexible and transparent to use class. But if you need to replace it with an application specific class, you can override and replace CakeResponse with your own class.

Show Plain Text
  1. App::import('Lib', 'CustomResponse');
  2.  
  3. class AppController extends Controller {
  4.     protected $_responseClass = 'CustomResponse';
  5. }

This will make all the controllers in your application use CustomResponse instead of CakeResponse. Overriding the response class is useful when testing, as it allows you to stub out the methods that interact with header().

Using CakeResponse

CakeResponse offers a number of useful methods for interacting with the response you are sending to a client. You can manipulate the headers with several methods.

  • header() Allows you to directly set one or many headers to be sent with the response.
  • charset() Sets the charset that will be used in the response.
  • type() Sets the content type for the response. You can either use a known content type alias or the full content type name.
  • cache() Allows you to set caching headers in the response.
  • disableCache() Sets the headers to disable client caching for the response.
  • compress() Turns on gzip compression for the request.
  • download() Allows you to send the response as an attachment and set the filename.
  • statusCode() Allows you to set the status code for the response.
  • body() Set the content body for the response.

Setting headers is done with the header() method. It can be called with a few different parameter configurations:

Show Plain Text
  1. // Set a single header
  2. $this->response->header('Location', 'http://example.com');
  3.  
  4. // Set multiple headers
  5. $this->response->header(array('Location' => 'http://example.com', 'X-Extra' => 'My header'));
  6. $this->response->header(array('WWW-Authenticate: Negotiate', 'Content-type: application/pdf'));

Setting the same header multiple times will result in overwriting the previous values, just like regular header calls. Headers are not sent when header() is called either. They are just buffered until the response is actually sent.

In addition to various setter methods that manipulate the response that will be made to the client, you also can augment the content types CakeResponse knows about with type().

Show Plain Text
  1. // Add a vCard type
  2. $this->response->type(array('vcf' => 'text/v-card'));
  3.  
  4. // Set the response Content-Type to vcard.
  5. $this->response->type('vcf');

This makes adding new content types a snap. When you are finally finished with a response and want to send it to the client you can call $this->response->send(); to output all the headers and body. By default this is done automatically by Dispatcher.

CakeResponse and testing

Probably one of the biggest wins from CakeResponse comes from how it makes testing controllers and components easier. Instead of methods spread across several objects, you only have a single object to mock as controllers and components delegate to CakeResponse. This helps you get closer to a ‘unit’ test and makes testing controllers easier.

Show Plain Text
  1. function testSomething() {
  2.     $this->controller->response = $this->getMock('CakeResponse');
  3.     $this->controller->response->expects($this->once())->method('header');
  4.     ...
  5. }

Additionally you can more easily run tests from the command line, as you can use mocks to avoid the ‘headers sent’ errors that can come up from trying to set headers in CLI.

CakeResponse and RequestHandler

As with CakeRequest many of the features CakeResponse has came from RequestHandlerComponent and Controller. For 2.0, RequestHandler will be more of a sugar layer, providing the same content type magic it has in the past. But now instead of being directly responsible for setting headers, it delegates out to the response object. This makes for cleaner code with less repetition, and hopefully a more extensible and modular framework.

Comments

Slightly offtopic, but is there a cakephp 2.0 cookbook that’s been started somewhere? I’d like to start the process of converting a decent sized application to cakephp (from plain, unorganized php). However, with so many new and different changes in 2.0, it hardly seems worth doing the conversion to 1.3 first.

mscdex on 9/19/10

This looks extremely promising. I have never been a fan of the RequestHandler as it does to much auto magic for you. These new classes should give us more control.

One thing I don’t like is putting both a getter and setter in the same method, type().

Miles Johnson on 9/19/10

mscdex: There is no cookbook for 2.0 yet as its not ready for production use. Attempting to migrate to pre-pre-alpha code is usually a bad idea.

Miles: Combining the getter and setter into a single function is a pattern used in more than a few places inside Cake already. So its mostly more of the same.

mark story on 9/19/10

Awesome, Mark! One thing I’m really excited about is the improved testing in 2.0 and this makes it (specifically those pesky controllers) a breeze, like you said. Keep up the good work.

Jeremy Harris on 9/29/10

Dear Mark, one of my controller’s actions is calling 3rd party SMS API using cURL. Is it possible to test it without calling 3rd-party API? Thanks in advance. :)

Zaw Zaw on 4/12/12

So that section on CakeResponse and Testing touches on how you would verifying that the appropriate response is declared.

But can you use the CakeResponse object to check what the resulting status code of the controller method would be, particularly if it wasn’t explicitly set? How would you do this?

Brad Koch on 10/5/12

If setting (and sending) the Content-Type through $this->response->header(…);, $this->response->type(…); or even header(…); try $this->response->send(); before outputting large strings.

Christian Hartmann on 1/20/13

These years, you may find respective financial the Lumia 900, one motivation only look to some other critically acclaimed, yet interminably stalled abroad signification: Kiley Minogue.
pounds 2 pocket Hardware If you’re looking at for high-end clients is besides prohibited.

www.pocketpaydaypounds.co.uk on 5/4/13

Cake PHP seems to be more flexible then I previous thought. PHP being a schizophrenic language kind of takes the cake when it comes to this framework.

Web Developer on 7/9/13

Comments are not open at this time.