Remove the Magic with Functional PHP - Part III

This is the final part of a three part post based on a presentation I plan to give at some meetups or conferences this year. The slides for this presentation can be seen here: http://slides.com/davidcorona/remove-magic-functional-php/live

Part I: http://www.lovesmesomecode.com/remove-the-magic-with-functional-php-part-i

Part II: http://www.lovesmesomecode.com/remove-the-magic-with-functional-php-part-ii


In Part II, we went over some basic functional programming concepts that we will be using in this final post of the series. We are going to build a simple todo application using the ideas we discussed in the last two posts. Although this is a simple example application, it has several opportunities for us to practice using our new tools.

Todo App

Here is the complete source code for our sample application.

What if we could build this application in a way that every class, every object and every dependency was bootstrapped up front in one file right at the beginning of our application?

So, if we wanted to know how a certain piece of code was being handled, or how a certain request traveled through our application, all we had to do was go look at this one file and we can know what was going on. If we were trying to ramp someone up in our project, we could sit them down and open up this file. Doesn't that sounds pretty sweet? Well, let’s see how we can do this.

Index.php

First, let’s create an index file to house our bootstrap code.

// index.php

require 'vendor/autoload.php';
require 'src/TodoApp.php';
require 'src/Mustache.php';

$handlers = []; // [1]

$mustache = Mustache::Render(new Mustache_Engine(), function($tpl) {
    return file_get_contents(__DIR__ . "/views/$tpl.mustache");
}); // [2]

$klein = new \Klein\Klein(); // [3]

$todoApp = new TodoApp($klein, $mustache, $handlers); // [4]
$todoApp->setRoutes();
$todoApp->start();

This is the starting point of our main bootstrap code. [1] We create an array which will house our route handler functions. We will get to this shortly. [2] Then we set up a Mustache template renderer. [3] We are using an open source micro framework called Klein which will handle the basic routing for our application. [4] Then we instantiate an instance of our TodoApp class and pass in our $klien object as well as the $mustache renderer and our $handlers array. Now we can call our setRoutes() function which will setup the routes and call start().

TodoApp.php

// TodoApp.php

class TodoApp {
  private $app, $handlers, $mustache;

  public function __construct(Klein $app, $mustache, $handlers) {
    $this->app = $app;
    $this->handlers = $handlers;
    $this->mustache = $mustache;
  }

  public function start() {
    $this->app->dispatch(); // [1]
  }

  public function setRoutes() { ... } // [2]

  private function handle($view, $handlers) { ... } // [3]
}

Here is the TodoApp class. It has a simple constructor. [1] The start function, which we called from index.php. It just calls the dispatch method on the Klien app. [2] We also have a setRoutes function and a [3] handle function. I will show the code for this shortly.

Notice that we are receiving our Klien app instance from our index.php file. We instantiated it in our bootstrap code and passed it in. We will basically be doing this sort of injection everywhere. We want to pass our dependencies into our classes. It will make things so much easier for maintenance and testing. We don't need a dependency injection framework if we can inject them ourselves!

Routes

// TodoApp.php

public function setRoutes() {
  $h = $this->handlers;

  $this->app->respond('/', $this->handle('index', $h['index'])); // [1]
}

private function handle($view, array $handlers) { // [2]
  return function(Request $request) use ($view, $handlers) { // [3]
    $params = $request->params();

    $model = array_reduce($handlers, function($model, $handler) { // [4]
          return $handler($model, $params);
        }, []);

    $this->app->response()->body($this->mustache($view, $model)); // [5]
    $this->app->response()->send();
  };
}

Here we have the code for the setRoutes and handle functions. The setRoutes function is simple. [1] We just setup our routes via the Klien app's interface. This is where things get a little tricky if you are not used to working with anonymous functions. We call our handle function as the route handler that the Klien router accepts. [2] The handle function takes a $view string and a $handler. We get the handler from our $handlers array which we create in our bootstrap code.

[3] Now the tricky part. Our handle function does not have the correct signature that the Klien app expects. So, the handle function returns a NEW function that meets the Klein app's route handler signature. We also use to use keyword to close over the $view and $handlers arguments into the closure. This function is returned, so it will be called by the Klien router.

[4] Now some more trickiness! The $handlers variable that we receive is just a simple array of functions. The handle method runs this array through an array_reduce. The ultimate outcome of the reduce will be a $model which will contain the data necessary to render our view template. [5] We can then pass this $model to mustache and send the response to the browser.

These two functions are very important, as they are the backbone of our entire application. All our code will be run through these two functions. One cool thing about how we are setting this application up is that these two files will contain all of our framework and Klien interfacing code. The rest of our business logic code will be completely isolated.

IndexHandler.php

Now that we have all the complex part of the application our of the way, let’s write the handler for our index route that will return a list of todo items to render.

// IndexHandler.php

class IndexHandler {
  public static function Handle($dataSource) { // [1]
    return function($params) use ($dataSource) { // [2]
      $todos = $dataSource('SELECT id, todo, completed
          FROM todos WHERE deleted IS NULL ORDER BY created'); // [3]

      $count = array_reduce($todos, function($carry, $todo) { // [4]
        return $carry + (($todo['completed'] === null) ? 1 : 0);
      }, 0);

      return ['title'=>'Todo App', 'todos'=>$todos, 'total'=>$count]; // [5]
    };
  }
}

This IndexHandler class shows the common interface that all of our business logic classes will follow for our application. [1] It has one function called Handle. This function takes a $dataSource function, which as you can tell by the name, is our source of data, or database access class. [2] We then immediately return a new function. This is the function that will be called by the array_reduce in the TodoApp's handle function above. It receives a $params array which will contain any relevant request information that our business logic would need, such as query string parameters or url arguments, etc.

[3] We call our dataSource function with a SQL query which returns a list of todo items. [4] Then we can run an array_reduce over this list to get a count of un-completed todos in a $count variable, and [5] return an array.

The next thing we need to do is setup this handler function in our bootstrap code. Remember that empty $handlers array we created in our first code sample above?

// index.php

$handlers = [
  'index' => [ Handlers\IndexHandler::Handle($dataSource) ]
];

This adds a new index key to our $handlers array which contains an array of a single function. It calls the Handle function we just wrote above and passes it a $dataSource function. I am not going to show the bootstrap code which creates this $dataSource function. You can look at the source code to see how this is created.

Update and Add Handlers

Now, we need to define handlers for our add and update routes. These will let our app add new todo items and update existing ones, or delete them, etc. Here is the bootstrap code.

// index.php

$handlers = [
  'index'  => [ Handlers\IndexHandler::Handle($dataSource) ],
  'add'    => [ Handlers\AddTodoHandler::Handle($dataSource) ],
  'update' => [ Handlers\UpdateTodoHandler::Handle($dataSource) ]
];

I am adding two new handlers for the add and update route. I defined these the same as the index handler, but with code that will add new todos or update existing todos.

Can you start to see the pattern here? We have dumped our dependency injection framework because we are manually wiring up our dependencies. There is no configuration or convention magic going on here.

There is one problem so far though. We want to create pure functions as much as we can, and so far all of our functions have been impure. Our main framework, rendering and database functions are going to be impure by nature because they deal with IO. There is no way around that. Our IndexHandler function however could be turned into a pure function if we moved the database call out of it to somewhere else.

IndexQuery.php

Let’s make a query class that will do the query for us. It will be an impure function because it needs to do database IO. If you forgot, it is not considered pure because if we called it multiple times, there is a chance it could return different data as our database may change. A pure function should always return the same thing everytime when passed the same input.

// IndexQuery.php

class IndexQuery {
  public static function Query($dataSource) {
    return function($model, $params) use ($dataSource) {
      $todos = $dataSource('SELECT id, todo, completed
            FROM todos WHERE deleted IS NULL ORDER BY created');

      return [ 'todos' => $todos ];
    };
  }
}

This is fairly simple. It has the same basic structure as the handler classes, but all it does is query our database and return the raw results of that query. With this in place, we can now remove the database query from our IndexHandler and we have a nice pure function.

// IndexHandler.php

class IndexHandler {
  public static function Handle() {
    return function($model, $params) {
      $count = array_reduce($model['todos'], function($carry, $todo) {
        return $carry + (($todo['completed'] === null) ? 1 : 0);
      }, 0);

      return [ 'title'=>'Todo App',
               'todos'=>$model['todos'],
               'total'=>$count ];
    };
  }
}

Much better! Now it is only using the data it receives to produce a new model which we can use to render. Notice how the function it returns now accepts a new $model argument. This will be the $model that our handler classes will operate on and build up. Let’s wire this up in our bootstrap code.

// index.php

$handlers = [
  'index' => [
    Queries\IndexQuery::Query($dataSource), // [1]
    Handlers\IndexHandler::Handle()
  ],
  ...
];

[1] All we needed to do was add this new query class as a new item in our handler array for the index key. We put it first, so it will run before the IndexHandler and we have a nice separation of database IO to business logic. Notice how I removed the $dataSource dependency from the IndexHandler class. It does not need this anymore, so no need to pass it in.

I also changed the update and add handlers to something similar, but I won’t show that here. You can look at the full code on Github, and if you do, make sure you look at the tests as well.

Summary

So, thats it. Our application is done. We have a single index.php file that wires up the entire application, and we can see exactly which classes are depending on other classes and what order the code is executing in. Can you start to see what is going on here?

Now, you might be thinking. Well, that’s a lot of typing. My index.php file is going to be a thousand lines because all of my classes and dependency hierarchies. Well, that’s kind of the point. I want you to have to type out all of those levels of dependencies. Maybe when your hand starts cramping up you will realize how complex your application has become. Hopefully it will make you step back and think more about how your application is architected and you can start to find better ways to keep things simple. Personally, I’ve already built a few applications in this way, and I can’t see myself building them any other way anymore.


Now, I’m not trying to tell you not to use that library or framework. I don’t have any problems with them, and I still use them sometimes. All I want is for you to ask yourself two questions. The next time you find yourself reaching for that library or framework to add into your application ask yourself.

  • What problem am I having that this library is going to solve?
  • Can I change my problem around so I don’t need it?

Our problem in this Todo application was that we did not want to use a complex dependency injection framework. So, we changed the way our application is architected so we could manually wire up our dependencies. We did not want to use a large magical ORM framework, so we wrote our queries manually.

I hope this will get you thinking of new ways to keep your applications simple and you future code is cleaner and more maintainable.