Fork me on GitHub

Functional programming in PHP

Thu 14 January 2010 | tags: php

Was recently brave (or foolhardy) enough to attempt to present how functional programming constructs could be used even in a language such as PHP. Now I am fully aware that one of the bigger reasons for functional programming ie. concurrency is simply not applicable as yet for a language like PHP, but I thought it might still be useful to reduce global variable proliferation, and improve both composability and testability.

The following sample PHP code was what I presented. The associated Twig template is not shown since it is not particularly relevant to the context. Note that the following is simply a sample code - any attempts to use it are at your own risk - I am simply not a good enough PHP programmer to even know if the code is good or has many smells that are entirely invisible (or unodorous) to me.

The code simply retrieves data from a table, filters it and shows it on screen.

<?php 
 require_once 'Twig/Autoloader.php';
 Twig_Autoloader::register();

##### Global constants #####
$base='\/funcblog';

function initialise()
{
    ##### Environment #####
    $loader = new Twig_Loader_Filesystem('templates');
    $twig = new Twig_Environment($loader, array(
      'debug' => true,
      'cache' => 'cache',
    ));


    #### Database initialisation #####

    $link = mysql_connect('localhost', 'funcblog', 'funcblog')
        or die('Could not connect: ' . mysql_error());
    mysql_select_db('funcblog',$link);
    return array('twig' => $twig, 'link' => $link);
}

##### Application configuration #####
$routes = array(
    array(path=>'posts',handler=>'get_posts'),
    array(path=>'categories',handler => 'get_categories')
    );

// Initialise the route map based on the routes configuration
$route_map = array_reduce(
        $routes,
        function ($x,$y) { $x[$y['path']] = $y['handler']; return $x;}, 
        array());

##### Functions #####

// Extract path and request components from URI
function compose_request($request_uri,$base)
{
    preg_match_all('/('. $base . ')(?P<path>(\/\w+)+)(\?(?P<query>(\w+=[^\&]+\&?)*))?/', $request_uri, $words, PREG_SET_ORDER);
    return array($words[0]['path'], $words[0]['query']);
}

// process the uri based on path and query options
function process_uri($context, $route_map, $path,$query)
{
    $handler_name = $route_map[ltrim($path,'/')];
    return $handler_name($context, $query);
}

// Display the template with the associated data
function display_template($context,$template,$data)
{
    $context['twig']->loadTemplate($template)->display($data);
}

$context = initialise();

// Closure to extract rows from table
$context['get_rows'] = function ($query) use($context)
{
    $result = mysql_query($query,$context['link']);
    $rows = array();
    while ($row = mysql_fetch_assoc($result)) 
    {
        $rows[] = $row;
    }
    return $rows;
};

// From the request URI, extract the path and the query parameters
list($path, $query) = compose_request($_SERVER['REQUEST_URI'],$base);

// Dispatch to the appropriate function based on the uri to routes mapping
list($template, $data) = process_uri($context, $route_map,$path,$query);

// Display the template
display_template($context,$template,$data);

##### Custom Application Logic #####

function get_posts($context,$query)
{
    # NOTE : An Array filter function, filters a collection using a
    #        filter function. It returns a subset of that collection
    #        for which the filter function (predicate) evaluates to true
    $posts = array_filter(
        $context['get_rows']('select * from posts'),
        function($row) { 
            return ($row['author'] == 'dhananjay') ; 
        }
    );
    return array('posts.html', array('rows'=> $posts)); 
}
?> 

Comments !

social