o.php

O-Lite

A one file PHP 5.2.x web development framework built on the o.php libraries and using the JSON Template engine. The complete system weighs in at under 50KB and gives you the tools to make simple sites quickly.

require('oLite.php');
oLite::route('GET /', 'home');
    function home() {
        echo 'Hello, world!';
    }
oLite::run();

Get the latest version from svn now: oLite.php

Why?

O-Lite was originally inspired by Fat-Free, and by adding under 100 lines of code to the then o.php libraries most of its functionality became available to PHP 5.2.x.

Getting Started

The example above is really all there is for Hello, world!. You add a route, give it a function and your done. As a testament of its simplicity this documentation site has just two functions, home() and menu(), plus a markdown library.

Lets look at the example in more detail. The following script would be found in an index.php file hosted in an Apache directory. Its first line is a normal require('oLite.php') statement which loads the O-Lite code from the same directory.

The next line, oLite::route('GET /', 'home'), lets the program know there is data to show at the relative URL /, and when that URL is accessed the function home() will be called.

<?php
require('oLite.php');
oLite::route('GET /''home');
    function 
home() {
        echo 
'Hello, world!';
    }
oLite::run();
?>

Next we have a standard global function named home(). It has one line which echo's the string Hello, world!. On the last line we have the business end of the program, oLite::run(), this brings O-Lite to life so it can do its stuff.

Before you can use your Hello, world! script you need to tell Apache that it should forward all requests to the index.php file. You can do this with a .htaccess file placed in the same directory as your index.php file.

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php [L,QSA]

The .htaccess file should contain the above statements. Now you're ready for action. Point your browser at the directory where you saved index.php and you should see the text Hello, world!.

Routers & Controllers

In O-Lite, a route is a HTTP method paired with a URL matching pattern. Each route is associated with a function or class method and routes are matched in the order they are defined. The first route that matches the request is the one called.

<?php
oLite
::route('GET /''home');
oLite::route('POST /''home_save');
oLite::route('GET|POST /poll.html''poll');
?>

Above the relative path / is associated with the method home() when the request is a GET. If a request for the relative path is a POST, then the function save_home() is called. Finally if the relative path is /poll.html and the request type is either a GET or a POST then the function poll() is called.

<?php
oLite
::route('GET /simple.html''simple');
    function 
simple(){
        echo 
'This is so simple it rocks!';
    }
?>

In its simplest incarnation the O-Lite web page you return is controlled by what you decide to echo in your function. For example going to the relative URL /simple.html would produce the following page.

This is so simple it rocks!

If for some reason you want to decommission a URL you can use the oLite::redirect() method. This lets you forward the requester to to a different URL with a 301 redirect.

<?php
oLite
::route('GET /simple.html''simple');
    function 
simple(){
        
oLite::redirect('/');
    }
?>

Now, what if we want some arguments from the URL? Luckily O-Lite has some helpers for you. To extract parameters from the URL you simply supply the parameter name prefixed by a :, for example :num. These parameters can appear anywhere in the URL and should be separated by slashes.

Parameters defined in the URL are accessed via an array which must be the first argument in your function. The array is keyed with the parameter names defined in the URL.

<?php
oLite
::route('GET /factorial/:num/''factorial');
    function 
factorial($args){
        
$num $args['num']; $fac 1;
        foreach(
range(2,$num+1) as $i$fac $fac*$i;
        echo 
"The factorial of {$num} is {$fac}.";
    }
?>

Above we accessed the :num parameter with the Key num, $args['num'], from the $args array. You can also define fixed hidden parameters as a URL encoded string of key/values.

Below we see the oLite::route() method with a last argument of name=tom&color=green. When this route is matched the array provided to the function factorial() has these fixed hidden parameters append to it.

<?php
oLite
::route('GET /factorial/:num/''factorial''name=tom&color=green');
?>

http://localhost/factorial/3/ "The factorial of 3 is 24."

php> Array
(
    [num] => 3
    [name] => tom
    [color] => green
)

To guarantee that the value we get from the :num parameter is a number we can provide some extra help. The next argument you can pass to oLite::route() is an array of key/values where the Key is the URL parameter name and the Value is a regular expression to validate that parameter.

<?php
oLite
::route(
    
'GET /factorial/:num/',
    
'factorial',
    
'name=tom&color=green',
    array(
'num'=>'[0-9]+'));
?>

http://localhost/factorial/3/ "The factorial of 3 is 24."

http://localhost/factorial/a/ "404"

The example above shows the result of adding the regular expression [0-9]+ to check that the parameter :num is made of only numbers. If it is not, as in the last example, the route is not matched and a 404 is returned.

Up until now we have just used echo to output the web pages. But we can enforce our separation of concerns in a much better way than that using templates.

If you return an array from your function you invoke the O-Lite templating system (detailed in the next section). However, you don't have to provide a template to make use of it. There is a built template called 'json' which converts an array to a JSON string.

To use this template you add the string 'json' as the fifth argument of oLite::route().

<?php
require('oLite.php');
oLite::route'GET /ajax/''ajax'nullnull'json' );
  function 
ajax(){
    
$url  'http://query.yahooapis.com/v1/public/yql?q=select%20url%2Ctitle%2Cabstract%20from%20search.web%20where%20query%3D%22php%22%20limit%203&format=json';
    
$json oCurl::create()->get($url'json')->call();
    return 
$json['query']['results']['result'];
  }
oLite::run();
?>

The above is a complete example that calls YQL, a service from Yahoo, and then outputs an array of search results for the query term PHP. O-Lite then converts this to JSON and outputs it as the request result.

[
    {
        "abstract": "Free server-side scripting.",
        "title": "<b>PHP</b>: Hypertext Preprocessor",
        "url":"http://www.php.net/"
    },
    ....
]

This is a useful short cut when creating JavaScript applications that require data from the sever. When used with JSONView for Firefox it becomes a very useful debug tool.

So far we have just called functions, but if like me you don't like random global functions scattered about your code you can just as easily call methods on Classes.

<?php
class MyClass{
  public function 
home(){
    echo 
"Class: Hello, world!";
  }
}
?>

With the class defined you can then provide it in place of the function argument in the oLite::route() method. Calling class methods works just the same as calling functions so all the things we have discussed so far can be applied to them.

<?php
oLite
::route('GET /class/''MyClass:home');
?>

You specify it as Class:method with a single : as the separator. This may look similar to calling a static method but in the background it creates a new instance of the class and then calls the named method.

This last piece of functionality allows you to encapsulate your code into classes. Its useful for grouping together collections of related tasks when you have lots of pages to manage.

Route Helpers

O-Lite provides a URL builder to help with managing your routes.

<?php
oLite
::route('GET /shop/:cat/index.html''category');

oLite::url('?cat=toys&_call=category'true);
?>

http://www.example.com/shop/toys/index.html

More to be written...

Templates

JSON Template is used as the default O-Lite templating language. It is a simple and powerful language built around the JSON data structure. The premise is that there is a one-to-one mapping between your data and the holes in the template where that data will be shown.

<?php
$template 
'Hello, {name}!';
$data['name'] = 'world'// As JSON: {'name': 'world'}
oLite::render(template$data); // Hello, world!
?>

In the example above the string {name} in the variable $template can be substituted because it is surrounded with curly braces. The $data variable has a Key called name, it is the value of this Key that will be substituted with the {name} placeholder when the template is rendered.

The language can also perform for loops and if statements, its documentation says the following on the subject.

In place of for loops and if statements, the basic idea is that the template declares sections and repeated sections. The sections are named after keys in the JSON data dictionary. The presence or absence of the keys in the JSON dictionary implicitly determines if and how many times a section is expanded.

We will look at these one at a time stating with the for loop. Firstly we will make an array of links to loop over. The $data array below has three items in it, each item having a link and text value.

<?php
$data 
= array();
$data[0]['link'] = 'http://www.olite.com';
$data[0]['text'] = 'o.php';
$data[1]['link'] = 'http://www.json.com';
$data[1]['text'] = 'JSON';
$data[2]['link'] = 'http://www.template.com';
$data[2]['text'] = 'JSON Template';
?>

Now we will make a HTML template for the data. The main lesson in this template is the {.repeated section @} line. This line tells the template engine to loop over each item in the current array, $data in our case.

<ol>
    {.repeated section @}
    <li><a href="{link}">{text}</a></li>
    {.end}
</ol>

For each item the engine substitutes {link} and {text} for their $data array counterparts. The result of this operation is three <li/> nodes with the values from the $data array inserted in place of the JSON.

<ol>
    <li><a href="http://www.olite.com">o.php</a></li>
    <li><a href="http://www.json.com">JSON</a></li>
    <li><a href="http://www.template.com">JSON Template</a></li>
</ol>

Now lets look at the if statement. What if our $data array was empty? We would want to show a message to the user so they don't think the page is broken.

{.section @}
<ol>
    {.repeated section @}
    <li><a href="{link}">{text}</a></li>
    {.end}
</ol>
{.or}
    <p>(No links to show)</p>
{.end}

The first line gives us what we want, {.section @} tells the engine to check if the $data array has any items in it. Should the check result in no items then we jump to {.or} and show what is between that and {.end}.

<p>(No links to show)</p>

In this case the text No links to show would is printed.

This was a quick look at what the JSON Template language can do. For more complete examples and advanced features go to the projects wiki page.

Route Templates

So far we have just looked at calling the method oLite::render() to use our templates. However O-Lite has a built-in helper for using templates. If you return an array like we did in the JSON example above (but do not state it should be json) we trigger the built-in template mapper.

<?php
oLite
::route('GET /template/''mytemplate');
    function 
mytemplate(){
        return array( 
"body"=>"Hello, world!" );
    }
?>

Above we have created a standard route and pointed it at the function mytemplate() which returns an array. In the directory where your index.php script is located you need to create a new directory named /tmpl/. This is where all your JSON Template files will live.

We will now create a template to work with the mytemplate() function. We do this by adding a new file to the directory you just created and give it the same name as the function (in lower case), mytemplate.xhtml. Then we add the following to that new file;

My function template prints "{body}"

You have just written your template. The {body} text in the template will be substituted with the content of the value of the body key in the array the function returns. The result of accessing this route will be;

My function template prints "Hello, world!"

As you can see it is very easy to automatically map a function to a JSON Template. The important thing to remember is that the function and template must have the same name, and the function must return an array.

This same idea can be used with the Class:method route option. The difference is that all the method templates live in a directory named the same as the containing class name. For example if the class was named MyClass and it had a method MyMethod the corresponding template would be found in /tmpl/myclass/mymethod.xhtml.

Command Line Access

O-Lite allows you to access all your routes via the command line.

/usr/bin/php index.php "/simple.html"

More to be written...

Global Variables

O-Lite uses the following two methods for managing all its global variables.

<?php
oLite
::set('key''value');
oLite::get('key''default');
?>

More to be written...

SQL Handler

Yea right, get with the times and use a key/value store. If you are expecting some MySql CRUD wrapper here you've got me mixed up with someone who doesn't care about scale.

On a more helpful note a framework is not about its Active Record implementation or its Create, Read, Update and Delete abstractions. If you're not ready to use a key/value store, find a Database library you like that can be moved between frameworks.

You can touch it, smell it, taste it so sweet - Mike Patton