Posted on Aug 3, 2012
First, even when the title of this post mention the framework word, I like to think that what we're going to do is what I call an application boilerplate, that means, we are going to create a simple application that you can extend and use however you want without being tied to a certain framework rules.
This application will have covered the following topics:
This application will not cover the following topics:
Even when I think that the not-covered points here are very important, almost every PHP programmer I have talked to loose the interest in what I'm saying when I start talking about those features, so, if you need those, you will have to implement them by yourself which is the whole point of this tutorial, give you some base you can extend and use whatever you want.
Since there's a lot to talk about, let's start right away.
The first thing you need to do in order to see what we do in your browser is
create a VirtualHost with the DocumentRoot pointing at the Public folder of
our application (you can see the project directory structure below) or, if your
using PHP 5.4, you can just start a development server in that directory.
We are going to start by building our controllers in a way that they are more HTTP oriented, instead of having this big controller with a bunch of (often unrelated) methods or actions, we will have URL Handlers, each of these handlers will be associated with only one URL and will have 4 main methods:
For the first example, let's start by creating the following folder structure:
App/ Public/ index.php
Also, make sure you have all the display erros directives turned on and showing the errors in the standard output (that means the browser), we'll see how to dynamically manage these configurations in next posts.
Inside the index.php file write the following:
<?php class HomeHandler { /* * GET / */ public function get() { return "hello world"; } }
Even though that chunk of code will not work you can as is, we can start having a feeling of what we are trying to build and how the code will look like, this is very important when you want to create beautiful APIs and I have found that by focusing first in the interface of what I'm trying to build, it reduces the rest of the process to just trying to fit the already coded pieces. The rest of the components we need will be created organically as we are going through.
Now, the next thing we need is something to map our handler to a certain URL,
in this case we're talking about the / url and that's why the handler is
called HomeHandler, this thing we need is what we are going to refer to as an
UrlMatcher and all it's going to do is map urls to handlers, that simple.
As we did before with our handler, let's append the following to index.php:
<?php // previous code not displayed $urls = new UrlMatcher(array( '/^$/' => 'HomeHandler' )); $currentUrl = $_SERVER['REQUEST_URI']; $handlerName = $urls->match($currentUrl);
URLMatcher will receive a dictionary (or associative array) where the keys
are regular expressions that will describe the url we are trying to match, and
the values (by the moment) are the name of the handler we want to attach to
that url.
The only method this class will have is the match method and is going to
receive the current accessed url (that we can access through
$_SERVER['REQUEST_URI']) and will return the name of the associated handler.
Update your project structure to match the following:
App/ Urls/ UrlMatcher.php Tests/ App/ Unit/ UrlMatcherTest.php Public/ index.php
Inside App/Urls/UrlMatcher.php put the code:
<?php class UrlMatcher { protected $urls; public function __construct($urls = null) { if (null === $urls) { $urls = array(); } $this->urls = $urls; } public function match($path) { if ($path[0] == '/') { $path = substr($path, 1); // strip first slash / } foreach ($this->urls as $regexp => $target) { if (preg_match($regexp, $path)) { return $target; } } return null; } }
And inside App/Tests/App/Unit/UrlMatcherTest.php put the code:
<?php require_once('Urls/UrlMatcher.php'); class UrlMatcherTest extends PHPUnit_Framework_TestCase { public function setUp() { $urls = array( '/^$/' => 'url1', ); $this->matcher = new UrlMatcher($urls); } public function testRootPath() { $path = '/'; $match = $this->matcher->match($path); $this->assertEquals('url1', $match); } public function testNoMatch() { $path = '/path/'; $match = $this->matcher->match($path); $this->assertNull($match); } }
Now, let's configure things so we are able to start testing our code.
First, download and install PHPUnit, you can follow the instructions in their website: Installing PHPUnit. Also you will need to install Pear, see the instructions in Installing Pear. Remember to configure Pear correctly and set the include path in your php.ini file, see Checking if Pear works
Second, update the project structure including the phpunit.xml and the
Tests/bootstrap.php files.
App/ Urls/ UrlMatcher.php phpunit.xml Tests/ bootstrap.php App/ Unit/ UrlMatcherTest.php Public/ index.php
Then put the following in phpunit.xml:
<?xml version="1.0" encoding="UTF-8"?> <phpunit backupGlobals="false" backupStaticAttributes="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" syntaxCheck="true" bootstrap="Tests/bootstrap.php" verbose="false" strict="true" > </phpunit>
Everytime you run your tests using phpunit command line program from inside
your App directory, it will look for this configuration file. The most
important thing to note of that configuration file is the
boostrap="Tests/bootstrap.php part which is telling PHPUnit to load that file
before running any test and it will serve us as a hook to put some
configuration.
Now, inside Tests/bootstrap.php put the following:
<?php set_include_path( implode(DIRECTORY_SEPARATOR, array( realpath(__DIR__.'/../'), get_include_path() )) );
Here we're just setting the include path so that includes our App root folder,
this way we can do relative requires starting at that directory, for example,
like we did in Tests/App/Unit/UrlMatcherTest.php with the line:
<?php require_once('Urls/UrlMatcher.php');
Next, if you run the tests you should see that everything is working and the tests pass:

If we have everything working we can keep going with the next piece in our puzzle: the Dispatcher
So far we have a handler and an url matcher that will map that handler with the
base url / but we're lacking a cornerstone component: the Dispatcher.
The Dispatcher is the one that will take our url mapper, find which handler is
attached to the current url (if any) and will invoke the method of that handler
that match the current HTTP Verb.
Update the project structure to include the Dispatcher:
App/ Core/ Dispatcher.php Urls/ UrlMatcher.php phpunit.xml Tests/ bootstrap.php App/ Unit/ UrlMatcherTest.php DispatcherTest.php Public/ index.php
Update the App/Core/Dispatcher.php:
<?php class Dispatcher { protected $urlMatcher; public function __construct($urlMatcher, $handlerInvoker) { $this->urlMatcher = $urlMatcher; $this->handlerInvoker = $handlerInvoker; } public function dispatch($env) { $path = $env['REQUEST_URI']; $method = $env['REQUEST_METHOD']; $handler = $this->urlMatcher->match($path); if ($handler) { return $this->handlerInvoker->invoke($handler, $method); } } }
And create the corresponding tests in App/Tests/App/Unit/DispatchTest.php:
<?php require_once('Core/Dispatcher.php'); require_once('Urls/UrlMatcher.php'); class FakeInvoker { public function invoke($handler, $method) { return 'Hello world'; } } class DispatcherTest extends PHPUnit_Framework_TestCase { public function setUp() { $urls = array( '/^$/' => 'url1', ); $this->matcher = new UrlMatcher($urls); } public function testRootPath() { $env = array('REQUEST_URI' => '/', 'REQUEST_METHOD' => 'GET'); $handlerInvoker = new FakeInvoker(); $dispatcher = new Dispatcher($this->matcher, $handlerInvoker); $response = $dispatcher->dispatch($env); $this->assertEquals('Hello world', $response); } public function testNoMatch() { $env = array('REQUEST_URI' => '/path/', 'REQUEST_METHOD' => 'GET'); $handlerInvoker = new FakeInvoker(); $dispatcher = new Dispatcher($this->matcher, $handlerInvoker); $response = $dispatcher->dispatch($env); $this->assertNull($response); } }
The idea is that our Dispatcher is constructed by passing the mapping between urls and handlers, and a HandlerInvoker that is just an object we're going to create that is responsible of creating a handler, invoking the specified method and returning a response. (here we are using the Dependency Injection pattern, you can learn more about it in the post what is dependency injection.
Run the tests and make sure everything is working:

So far so good, the last thing we need to do is put everything together to make the first alpha version of our app.
Update App/Public/index.php:
<?php set_include_path( implode(DIRECTORY_SEPARATOR, array( realpath(__DIR__.'/../'), get_include_path() )) ); require_once('Core/Dispatcher.php'); require_once('Urls/UrlMatcher.php'); class HomeHandler { public function get() { return "hello world"; } } class FixedHandlerInvoker { public function invoke($handlerName, $method) { $handler = new HomeHandler(); return $handler->get(); } } $urls = new URLMatcher(array( '/^$/' => 'HomeHandler' )); $handlerInvoker = new FixedHandlerInvoker(); $dispatcher = new Dispatcher($urls, $handlerInvoker); $env = $_SERVER; echo $dispatcher->dispatch($env);
If we did everything right, you should see a "hello world" in your browser.
We have updated the file with some code that set the include path just as we
did in App/Tests/bootstrap.php. then we create a pretty basic Handler
Invoker that all it does (by the moment) is create an instance of the
HomeHandler and call its get method, finally we put together all the pieces
and we invoke the dispatcher's dispatch method that will return the response
returned by the handler.
In the next post we are going to see how to create an invoker that dynamically
creates a handler by using the $handlerName and $method variables.
Also we're going to see how encapsulate the php superglobal variables
$_SERVER, $_POSt and $_GET in a HttpRequest object that will help us to
test better our application.