REST with the Recess! Framework

Some people might immediately dismiss this post based on its title. The immediate response of others is likely to be a groan immediately followed by, "Not another framework!" Were this yet another general purpose PHP framework for web applications, I would agree. It's already obvious we have more than enough of those, whether they're aimed at general web development or content management.

But this framework has a different focus, at least partly outside of those two areas. REST is an set of architectural principles and concepts often applied (though not limited) to web service implementations. The focus of REST is resources (things) and a simple set of operations (similar to those included in CRUD) that can be performed on resources.

Ben Ramsey has presented and written about REST a great deal in the past. The April 2008 issue of php|architect magazine has an article on the subject; if you don't have that issue, you can look forward to another article that Ben has written for the upcoming February 2009 issue. You can also find a few articles on the C7Y PHP Community web site. So given all this background information on REST that I'd seen thus far, I decided to dig a little deeper into the Recess! Framework after coming across mention of it across several blog posts.

There were a few things that troubled me about the project going in. First, while the main project web site has a development blog and discussion forums, and the author has written several blog posts about the framework, there are no official tutorials or documentation to speak of at the time of this writing. Thankfully, the README file bundled with the preview release indicates that these are top priorities for the next version. A tool or library may indeed be the best thing since sliced bread, but prospective adopters will only put forth so much time and effort to figure out how to use it before moving onto an alternative. It's in the best interest of the project to make it as simple as possible (but no simpler) for people to pick it up.

Second is that the components included in the framework indicate a scattered focus. An MVC stack, a PDO-based DAL, an ORM system, a scaffolding system, multiple caching backends -- these are all things that plenty of existing frameworks, RAD or not, have already. While its routing mechanisms have been the subject of some examination, they're not overly dissimilar from routing mechanisms in other frameworks. Same with wrapper classes for requests and responses.

Smarty is bundled in the preview release, but the landing page on the project web site states that both Smarty and native PHP views are supported. It would seem more natural to not to bundle Smarty and allow the developer to include if it it's being used. Same with SimpleTest: if I want to run the unit tests, I'll download it and point the test runner to it myself. I also have to question why Smarty would be useful as a view layer for this particular application domain. Most use cases I see for Smarty involve either a team of web designers already familiar with it or systems in which users can manipulate templates (for security reasons), but not a REST API.

Third is the organization of the code itself. Multiple adapters for things like caching and views are bundled into single files or included whether they're needed or not. While I've said I don't entirely agree with views that statics are evil, the Recess! code makes rather liberal use of them. Tickets #51 and #54, which I filed, make me wonder if the development team uses an E_STRICT level of error reporting (which I very much hope you're doing in your own code). Given that tickets haven't been updated in a month or so, mine may have been filed in vain.

The post probably sounds like a rant at this point. Really, the major frameworks already have most of the components that a REST application would use and just need a little augmentation to be more REST-friendly. Here are a few points to that end.

  • Modification of the application response to indicate to the client that the response data can be cached and for how long. This reduces demands on the server because the client can used the cached response for a time rather than having to request it from the server again. Server-side caching has its place but the less traffic that the server has to handle, the better.
  • Support for multiple representations of each resource. Ideally, all resources should have a unique canonical URL from which they can always be accessed and their representation format determined using content negotiation. This includes the content format (HTML, XML, JSON, Atom, etc.), language (English, French, Spanish, etc.), and character set (ISO-8859-1, UTF-8, etc.). So long as this is maintained, it is acceptable to also offer other URLs specific to particular representations of a given resource. (Twitter does this.)
  • Declaration of which methods (GET, HEAD, POST, PUT, DELETE) each offered resource supports, as not all resources may support all methods, and a mechanism to return an appropriate HTTP response (405 Method Not Allowed) if a resource is accessed using an unsupported method. Responses should include an Allow header indicating supported methods, especially in 405 responses but ideally for HEAD requests as well.
  • Support for documentation generation from REST service source code. Ideally, this would come in the form of a service browser that offered full explanations of each resource including available representations and methods, possibly with WSDL 2.0 support.
  • Content encoding (gzip, deflate) should be supported in both directions. That is, a client should be able to submit data that is encoded and the server should be able to encode content when the client requests it.

In the end, if your application is carefully thought out and well-designed, adding a REST API should be borderline trivial because only the resource representation should change to be more appropriate for automated agents to consume it. I hope to see more developments toward REST-friendless in the major frameworks in their future versions.

Appreciate the Thorough Investigation

Matt, Hi, I'm Kris Jordan, creator of the Recess Framework. First of all thank-you for all the issues you encountered and submitted to the Lighthouse repository! As this is the first release of Recess my feeling these tickets are extremely helpful for identifying sore spots. Regarding the points you picked up on: 1. Documentation, or lack thereof. I hear you. Still pounding away here. Somehow I manage to be slower at writing tutorials and docs than code. Improving with time, though! 2. Scattered focus. A fundamental goal of Recess is to be a full-stack, RESTful framework with interchangeable parts. The ORM for example is not coupled with the Views and Controllers. Though we generate some scaffolding code based on the Recess ORM you could just quite easily use Doctrine instead. As time wears on we hope to make it easier to interchange other parts as well. The top priority in initial releases is to be able to provide a great experience and development environment without having to patch together the solution. 2.b. Inclusion of 3rd party libraries like Smarty and SimpleTest. This may very well change in the future. Like most frameworks Recess was spawned from the needs of real project. Recess' just so happened to be one where Smarty was important and why it is in now. I definitely see your case for Smarty moving out of the base distribution, though, and will do so in a future iteration. The plans for SimpleTest are to incorporate it into the 'Tools' backend so that running all tests / some tests has a UI and tooling support. 3. Organization of code. The purpose of Cache in Recess is more nuanced than the general type of caching most PHP applications use at the top of the stack. Caching is fundamental to the architecture of Recess when running in production mode. In a layered diagram of the Recess architecture you would find the Cache class sitting below all else, even its Library class. This is an extremely intentional design decision which allows us to cache the important (and expensive to compute!) data structures within the framework (like the routing tree, ORM data, and class meta-data). The reason for breaking the 'single class per file' mold with Cache is to avoid the cost of touching additional files on every request. In production mode a request to Recess is handled requiring/including under 10 PHP files. Performance is a top priority of the framework and the combination of caching important data structures and minimizing the number PHP files required in production mode is how we're able to make the performance of Recess fly. I really appreciate your inspection of the code and locating improvements which can be made. This young framework has an exciting road ahead and the more intelligent people who get their fingers dirty with it and identify areas for improvement the sooner Recess will be a first tier framework. Finally - great job infusing your article with insightful, relevant links across the web. Might be worth adding a link back to the PHP REST framework itself. It's: http://www.recessframework.org. Thanks again Matt, keep up the insightful blogging, Kris Jordan Recess Framework, Creator New Media Campaigns, Partner

Nice to meet you

Nice to make your acquaintance, Kris.

1) I hear you: it does tend to be difficult to keep up with documentation, especially on larger projects. Glad to know you're working on that, though.

2) Good to know that you're aiming for loose coupling, though I'm still admittedly unconvinced that development of these components is necessary given the current offerings available to fulfill their intended purposes. It seems like your time would be better spent working on the parts of the framework that aren't already present elsewhere rather than reinventing the wheel.

3) Caching is fundamental, yes, but see my response to #2. There are caching backends in other frameworks as well as independent components in libraries like PEAR. As far as breaking the one class per file convention, see point #3 in this blog post and the Appendix section it inspired in the ZF documentation. Your attempt seems to be in the vein of premature optimization to me, but if you think it's a valid concern, you may want to investigate this technique.

I do hope that Recess goes on to become as well known as the other frameworks I've linked to in my original post, though I'm skeptical as to whether it will do so unless it introduces value that isn't present elsewhere. Good luck and best wishes in your endeavors to bring that to the PHP community.

Regards,

Matt

Good overview

Hey Matthew, Interesting post. I have to say, it's sad to see so much development effort wasted on yet another framework for PHP, especially when Cake does most of this already (and most of that stuff is even pretty easy :-)). That said, we still have a bit of a ways to go in being more HTTP-friendly in certain areas of the framework, and taking some of the extra work out of serving up content in XML or JSON format. Btw, I thought OPTIONS was for returning the methods available on a resource. - nate

Re: OPTIONS

Nate,

You're technically correct about OPTIONS. To quote RFC 2616 Section 9.2, "A 200 response SHOULD include any header fields that indicate optional features implemented by the server and applicable to that resource (e.g., Allow)." However, the RFC also mentions a few other things that are pertinent.

  1. "Responses to this method are not cacheable."
  2. "If the OPTIONS request includes an entity-body (as indicated by the presence of Content-Length or Transfer-Encoding), then the media type MUST be indicated by a Content-Type field. Although this specification does not define any use for such a body, future extensions to HTTP might use the OPTIONS body to make more detailed queries on the server. A server that does not support such an extension MAY discard the request body."

If a Max-Forwards header is included in the request, it's possible that it will only go so far as to reach a proxy server, rather than the origin server hosting the originally requested resource, and that the returned response will reflect information about that proxy.

If the request does return the resource, the presence and format of the response body is not defined by the RFC. It might be the resource content depending on the server implementation, which is not ideal if it's large and/or not needed by the client. That's why I recommended a HEAD request, because its response is cacheable and it never returns a response body.

Hope that clarifies my explanation. Thanks for your comments.

Regards,

Matt