Evolving Framework, Step 3: Cleaning up the server-side

Once I had my virtual host set up to route all requests into a single script, I then had to figure out what to do with each request. I knew that the essential request information was available in the $_SERVER autoglobal, but I wanted a cleaner way of dealing with the request. So I put together a very simple Request class, which I saved in the document root as Request.php:

<?php 
class Request 
{ 
    public $method; 
    public $url; 
    public $parameters = array();  
    public function __construct() 
    { 
        $this->method = $_SERVER['REQUEST_METHOD']; 
        $this->url = $_SERVER['SCRIPT_URI']; 
        $this->parameters = $_REQUEST; 
    } 
} 


I then altered index.php to contain the following:

<?php
    include 'Request.php';
 
    $req = new Request();   
?>
Method: <?=$req->method?><br />
URL: <?=$req->url?><br />
Parameters: <code><?=print_r($req->parameters, true)?></code>
 

To give it a test run, I pointed my browser to http://localhost/some/fake/path?name=drew&gender=male and it showed me:

Method: GET
Path: /some/fake/path
Params:  
Array  
(  
    [name] => drew  
    [gender] => male 
)

This is exactly what I was expecting. So I decided to try it with my RESTful login form from Step 2. I saved that HTML code:

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

into the document root as login.html, but then I remembered that Apache wasn’t going to serve that file properly because of my RewriteRules. I had a rule in the vhost container that let images, css, and js files get served up as-is, so I just added “html” as another file extension. In other words, this:

RewriteRule /\w+\.(css|js|gif|png|jpe?g)$ - [NC,L]

Became this:

RewriteRule /\w+\.(html|css|js|gif|png|jpe?g)$ - [NC,L]

I made the change and restarted Apache, then I hit http://localhost/login.html and got the little login form as expected. When I tried submitting the form, however, I noticed a slight problem. The page displayed everything properly except for the request method:

Method: GET
Path: /session  
Params:  
Array  
(  
    [username] => drew  
    [password] => mypass  
)

Where did that GET come from? I specified “put” as the method in my form element, so how did that get translated into a “get”? I decided to try a few other tests; if PUT became GET, what would DELETE become? I changed login.html to use method=”delete” in the form element, but I got the exact same result when I submitted the form. What about method=”omgwtf”? No good, it displayed as GET again. What about just leaving the method attribute out of the form element altogether? You guessed it: GET. The only way I could make the thing show up any different is if I put method=”post” in the form. So I came to the conclusion that my browser only knows how to send GET and POST requests. After a bit of googling, I also came to the conclusion that I am probably one of the last web developers to realize this little limitation.

GET and PUT are only the “RU” of CRUD though, and I want the whole thing. So I had to figure out some way to force my request method through. So I rigged up the form with the most obvious solution:

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

Basically the idea is to wrap any PUT or DELETE requests inside a POST request by specifying a hidden “method” parameter that effectively overrides the form’s “method” parameter. I just had to modify the Request class to do that overriding inside the constructor. After a few minutes I came up with:

<?php
class Request
{  
    public $method;  
    public $url;  
    public $parameters = array();    
 
    public function __construct()  
    {  
        $this->url        = $_SERVER['SCRIPT_URI'];  
        $this->parameters = $_REQUEST;  
        $method = strtoupper($_SERVER['REQUEST_METHOD']);  
 
        if ($method == 'POST' && isset($this->parameters['method']))  
        {  
            $tunneledMethod = strtoupper($this->parameters['method']);  
            if (strtoupper($tunneledMethod == 'DELETE' || $tunneledMethod != 'PUT')  
            {  
                $method = $tunneledMethod;  
            }  
        }  
        else if ($method != 'PUT' && $method != 'DELETE')  
        {  
            $method = 'GET';  
        }  
        $this->method = $method;  
    }  
}  

I then went back and refreshed my new, slightly hacked login.html, and upon submitting it I was relieved to be presented with:

Method: PUT
Path: /session
Params:  
Array  
(  
    [username] => drew  
    [password] => mypass  
)

I had successfully tunneled my intended request method inside of a POST request, and while the constructor for my little Request class became a good bit uglier, I couldn’t care much less. That’s a small price to pay for what will hopefully be a cleaner way to build my PHP apps.

Of course, the hard part is deciding how to actually use those three crucial pieces of information about the request to do anything meaningful. I know that I want to create a new session with the supplied username and password, but I’d be insane to put that exact application logic directly in index.php. Instead, I need a generic way to look at the request’s path and determine what resource is being requested. Is a user trying to login? Does an article need to be deleted? Does a blog entry need to be updated? Does a list of books need to be displayed?

In Step 4, I’ll introduce the RequestHandler class and explain how it looks at the guts of the incoming request and decides what to do accordingly.