Category: Routing

Routing in Recess! Screencast

Posted by Kris Jordan | Comments ( 13 ) | Filed in: Routing | | | | |


In the first ever Recess Framework screencast we will be walking through the exciting Routing features of the framework.

What is routing?

Routing is the machinery that takes a requested URL path like “/product/23” and ‘routes’ or dispatches control to some other place your application. In most frameworks, including Recess, this is to a method in a controller. Using a framework that has fast, flexible, RESTful routing is important because URLs are fundamental to how people, search engines, and web services interact with you web application.

Simple Routing Techniques (Screencast 0:20)

Let’s dive right into some code and take a look at how we can set up a route to a method in a controller:

class TestController extends Controller {

    /** !Route GET, /hello/world */
    function aMethod() {
         echo 'Hello PHP Community!'; exit;
    }

}

What’s that funny stuff above the function? It’s a Recess Route annotation.  Recess annotations may look a bit strange but they’re really simple. They are written inside of doccomments, a language construct in PHP which begins with a forward slash and two asterisks. Recess annotations are banging. Literally, they start with an exclamation point, or, BANG! (as opposed to the @-symbol if you’re used to Java style annotations). The Route annotation has two parameters. The first is the HTTP method such as GET, POST, PUT, or DELETE and the second is the URL path.

Parametric Routes

When a part of the route is preceded with a dollar sign it becomes a method parameter. Here is an example:

class TestController extends Controller {

    /** !Route GET, /hello/$first/$last */
    function aMethod($first, $last) {
         echo "Hello $first $last!"; exit;
    }

}

Now if we browse to /hello/PHP/Community the browser will print “Hello PHP Community”. Parametric routes are often used with ID or primary key columns in a database. For example, if I were building a store in Recess I might have a Product Details page that used a route like: !Route GET, /product/$id

Multiple Routes per Method

Controller methods can have multiple routes. For example, we can combine the previous two methods into one:

class TestController extends Controller {

    /** 
     * !Route GET, /hello/world
     * !Route GET, /hello/$first/$last
     * */
    function aMethod($first = "PHP", $last = "Community") {
         echo "Hello $first $last!"; exit;
    }

}


If you accidentally add a route that conflicts with another somewhere else in your app Recess will tell you where the conflict occurred. The Recess Diagnostics error screen shows you where in your code the conflict occurred.

Keeping it DRY

If you’re familiar with Cake or Rails you may be wondering what is the upside to specifying routes in-line with my methods? The long and short of it is, it is more DRY. With a separate routes file you must duplicate the name of a controller and method which a route maps to. So if you refactor your controller code you must remember to go and update your routes file as well. By keeping the two together it’s never a mystery what URL will take you to the controller method you’re working on.

Advanced Routing Techniques (Screencast 3:00)

HTTP METHOD Aware Routes for RESTful Routing

Routing in Recess is HTTP method-aware. To demonstrate this we can have two controller methods mapped to the same URL but differing HTTP METHODs:

class TestController extends Controller {

    /** !Route GET, /same/url */
    function comingFromGet() {
         echo '
'; echo '
'; exit; } /** !Route POST, /same/url */ function comingFromPost() { echo 'POSTed!'; exit; } }

The first method will handle a GET to the url “/same/url” and the second a POST to “/same/url”. For an actual demonstration of this running check out the screen cast at minute 7:00! Having a routing system aware of HTTP methods is one way in which Recess helps simplify RESTful application develop in PHP.

Relative Routes

In Recess, Routes can be relative to their context. What does that even mean? There are three logical levels of organization for the purposes of Routing. The most general level is the application. Recess allows multiple applications to be installed at once a la Django. Within an application there may be multiple controllers and a controller can have many methods.

Implicit routes are different from explicit routes in that they do not begin with a forward slash. Check out the !Route annotation for the world method below. By adding a !RoutesPrefix annotation to a controller we will prepend any relative route in the controller with "hello/" so we can now reach the world method using "hello/world/".

/** !RoutesPrefix hello/ */
class TestController extends Controller {

    /** !Route GET, world */
    function world() {
         echo 'Hello World!'; exit;
    }

}

Implicit Routes

If you’re familiar with Rails or Cake you’re probably wondering why I needed specify routes for the world and universe methods. In many frameworks these routes would be implicit. In Recess they can be implicit too. We can delete the route annotations and still access the methods in the same way. The important difference between the way implicit routes work in Recess and other frameworks is that Recess does not rely on the name of a controller to determine the route, but rather on the routing prefixes of the application and the controller.

/** !RoutesPrefix hello/ */
class TestController extends Controller {

    function world() {
         echo 'Hello World!'; exit;
    }

}

Because we've set up a route prefix for the Tests controller we can reach the world method by using the URL: "hello/world". Implicit routes can have parameters too.

/** !RoutesPrefix hello/ */
class TestController extends Controller {

    function world($first, $last) {
         echo "Hello $first $last!"; exit;
    }

}

Now, "hello/world/Michael/Scott" will rest in the world method being called and printing "Hello Michael Scott!"

Tools (Screencast 8:00)

With routes being sprinkled throughout controllers don’t you lose the ability to look in a single place and get a sense of all of the routes in your application?

Recess! Tools is first class Recess! application that runs in the browser and is designed to help you along in development mode writing apps. With Tools we can see all of the routes explicit and implicit, relative and absolute, for any given application. The table lists the HTTP method and route corresponding which map to a controller class and method. With Recess Tools you can get a global picture of Routes in your application.

Performance (Screencast 9:00)

How in the world can you expect to get any kind of performance out of an app when you have to reflect over every single controller in order to know all routes?

This is a great question.  Routing changes as shown in the screencast take immediate effect. This is because the screencast was taken while Recess was in development mode. By switching to deployment mode the routing computation would only have to happen once because the routes won’t change. On the first request in deployment mode Recess will build up the routing data structure, a tree, and cache it either to disk or memory depending on what your server has available.  Subsequent requests simply unserialize the routing tree and Recess is off to the races. Perf has been a top priority while developing Recess and this technique enables great performance while allowing your code to stay simple, nimble, and DRY.

Wrap-up

So this has been a quick look at routing in Recess! I hope you’ll sign up to be notified when the alpha bits go public and try routing out for yourself. For more info on Recess! and other screen casts subscribe to the RSS feed and/or follow me on Twitter: KrisJordan.  Would love to hear your reactions, thoughts, and questions in the comments!

Towards RESTful PHP - 5 Basic Tips

Posted by Kris Jordan | Comments ( 1 ) | Filed in: REST, Routing | | | | |

What is REST?
REST is an architectural style, or set of conventions, for web applications and services that centers itself around resource manipulation and the HTTP spec. Web apps have traditionally ignored the HTTP spec and moved forward using a subset of the protocol: GET and POST, 200 OKs and 404 NOT FOUNDs. As we entered a programmable web of applications with APIs the decision to ignore HTTP gave us problems we’re still dealing with today. We have an internet full of applications with different interfaces (GET /user/1/delete vs. POST /user/delete {id=1}). With REST we can say /user/1 is a resource and use the HTTP DELETE verb to delete it. For more detail on REST check out wikipedia and “quick pitch“.

Tip #1: Using PUT and DELETE methods

In PHP you can determine which HTTP method was used with: $_SERVER['REQUEST_METHOD']; From web browsers this will be either GET or POST. For RESTful clients applications need to support PUT and DELETE (and ideally OPTIONS, etc.) as well. Unfortunately PHP doesn’t have $_PUT and $_DELETE variables like it does $_POST and $_GET. Here’s how to access the content of a PUT request in PHP:

$_PUT  = array();
if($_SERVER['REQUEST_METHOD'] == 'PUT') {
    parse_str(file_get_contents('php://input'), $_PUT);
}

Tip #2: Send Custom HTTP/1.1 Headers

PHP’s header function allows custom HTTP headers to be sent to the client. The HTTP/1.x header contains the response code from the server. PHP will, by default, send back a 200 OK status code which suggests that the request has succeeded even if it has die()’ed or a new resource has been created. There are two ways to change the status code of your response:

header('HTTP/1.1 404 Not Found');
/* OR */
header('Location: http://www.foo.com/bar', true, 201); // 201 CREATED

The first line is a generic way of setting the response status code. If your response requires another header, like the Location header to the resource of a ‘201 Created’ or ‘301 Moved Permanently’, placing the integer status code in the third parameter of header is a shortcut. It is the logical equivalent of the following example, which is easier to read at the cost of being an extra line of code.

header('HTTP/1.1 201 Created');
header('Location: http://www.foo.com/bar');

Tip #3: Send Meaningful HTTP Headers

Policy for deciding when it is appropriate to send each HTTP status code is a full post on its own and the HTTP spec leaves room for ambiguity. There are many other resources on the net which provide insights so I’ll just touch on a few.

201 Created is used when a new resource has been created. It should include a Location header which specifies the URL for the resource (i.e. books/1). The inclusion of a location header does not automatically forward the client to the resource, rather, 201 Created responses should include an entity (message body) which lists the location of the resource.

202 Accepted allows the server to tell the client “yeah, we heard your order, we’ll get to it soon.” Think the Twitter API on a busy day. Where 201 Created implies the resource has been created before a response returns, 202 Accepted implies the request is ok and in a queue somewhere.

304 Not Modified in conjunction with caching and conditional GET requests (requests with If-Modified-Since / If-None-Match headers) allows web applications to say “the content hasn’t changed, continue using the cached version” without having to re-render and send the cached content down the pipe.

401 Unauthorized should be used when attempting to access a resource which requires authentication credentials the request does not carry. This is used in conjunction with www-authentication.

500 Internal Server Error is better than OK when your PHP script dies or reaches an exception.

In the Recess! Framework I use this StatusCodes class to provide named constants for all HTTP/1.1 status codes. Example usage:

header(StatusCodes::httpHeaderFor(StatusCodes::HTTP_NOT_FOUND));

Tip #4: Don’t Use $_SESSION

A truly RESTful PHP application should be entirely stateless- all requests should contain enough information to be handled without additional server side state. In practice this means storing authentication information in a cookie with a timestamp and a checksum. Additional data can also be stored in a cookie. In the event you need more than a cookie’s worth of data fall back to storing it in a central database with the authentication still in the cookie. This is how Flickr approaches statelessness.

Tip #5: Test with cURL or rest-client

cURL makes it easy to execute any HTTP METHOD on a resource URL. You can pass request parameters and headers as well as inspect response headers and data. The command line tool ‘curl’ is standard on many *nix distros. Windows users should check out MinGW/MSYS which supports cURL. Even PHP has cURL functions which are enabled on most hosts (tp://us2.php.net/manual/en/curl.setup.php">php/curl install page).

cURL Example Usage & Common Parameters:

# curl -X PUT http://www.foo.com/bar/1 -d "some=var" -d "other=var2" -H "Accept: text/json" -I

-X [METHOD] Specify the HTTP method.
-d “name=value” Set a POST/PUT field name and value.
-H [HEADER] Set a header.
-I Only display response’s headers.

Alternatively, a free GUI to test REST interfaces is Java/Swing based rest-client. rest-client is scriptable and has support for JSON/XML.

Tip #6 - Use a RESTful PHP Framework

Frankly, developers shouldn’t have to worry about many of these low-level details of REST when writing PHP apps. REST is based on conventions and conventions, by nature, involve a lot of boilerplate. This was one of the motivating reasons for Recess, a REST PHP Framework. Recess has RESTful routing that makes creating friendly URLs that respond to PUT, POST, DELETE, & GET with ease. Check out this REST routing screencast for a preview of how it works or download Recess and have some fun!