Evolving Framework, Step 2: Make PHP understand the clean URL

So why am I bothering with “clean” URLs? For several reasons really. For one thing, they look nicer than un-clean URLs. I would much rather get rid of something like, this:

http://www.example.com/forumDisplay.php?fid=17

and instead use something like this:

http://www.example.com/forum/general

But aesthetics aside, why should I bother? My reason is inspired by Roy Fielding’s REST. I won’t try to get into the guts of it here, partly because there’s alot to it, and partly because my grasp of it is far from complete. Regardless, what I’ve taken from my brief introduction to it is a resolution to build applications from a resource-centric point of view, as opposed to a functional, process-centric point of view. Instead of building a collection of scripts that expect data to be passed in so that they can spit data out, I want to try to build a system of resources that can be manipulated.

In order to manipulate a resource, three essential pieces of information are required:

  1. The location of the resource in question (the URL)
  2. The type of manipulation to be performed on the resource (HTTP method; PUT, GET, POST, DELETE)
  3. The data needed to perform the manipulation (request parameters)

One thing I noticed at this point was that the request method could essentially take the place of the action parameter I’ve seen being passed around in so many frameworks (and which I’m guilty of having used in the past). With any resource in mind (e.g., user object, news article, forum thread, photo album, etc), PUT, GET, POST, and DELETE can do pretty much anything you need, or at least imply your intention to do pretty much anything.

Also, those four major request methods map pretty conveniently to CRUD:

  • PUT == CREATE
  • GET == READ
  • POST == UPDATE
  • DELETE == DELETE

I figured a good place to start trying to implement something like this would be structuring the URLs in such a way that they look like nouns. So an old school login form may have looked like:

<form action="/login.php" method="post">
  Username <input type="text" name="username" /><br />
  Password <input type="password" name="password" /><br /> 
  <input type="submit" value="Login..." />
</form>

The problem with that code (RESTfully clean URLs in mind) is the action and the method. The form is POSTing the username and password to login.php. But is login.php really the resource that I want to POST to? If I replace the word POST with the word UPDATE, it makes a little bit less sense. I’m not updating login.php itself. That script doesn’t represent a resource of any kind; it’s just a procedure, a machine that takes data in one side and spits data out the other.

So I took a step back and thought about what I was actually trying to accomplish. I wanted to authenticate a user; check their username and password against the database, and if it matched a valid user record then stick their information in the session for use throughout their visit.

In other words, my goal was to create an authenticated user session. Since “create” maps to the PUT method, I decided to change the form around to look like this:

<form action="/session" method="put">
  Username <input type="text" name="username" /><br />
  Password <input type="password" name="password" /><br /> 
  <input type="submit" value="Login..." /> 
</form>

My intention was to provide the following information to the web server:

  • Location of the resource: /session
  • Operation to be performed on the resource: PUT (i.e., create)
  • Additional information needed to operate on the resource: username and password
    • It seemed a good bit more logical this way. Instead of telling the web server, “here’s a username and password that I want the login script to authenticate“, I’m telling it, “I want to create a new session based on this username and password“.

      The tricky part was actually writing the code to interpret the request in such a way that this actually happened like I wanted. Step 3 is coming up next. In it I’ll explain my initial approach to solving my RESTlessness, and some unexpected problems I ran into along the way.