An introduction to ICanBoogie

ICanBoogie is a high-performance micro-framework for PHP that provides essential features to build web applications. "micro" means that the core features of ICanBoogie are kept to the essential, its core is simple but greatly extensible and a variety of components can be used to complement its features with routing, controllers, views, message bus, modules, validation, mailer caching, ActiveRecord, internationalization, and many more… These components can be used on their own, they are integrated into the framework using special packages called bindings. Foreing packages, such as Symfony's dependency injection component, are bound in the same fashion.

Working with ICanBoogie

ICanBoogie tries to leverage the magic features of PHP as much as possible: getters/setters, invokable objects, array access, stringifiable objects, closures… to create a coherent framework which requires less typing and most of all less guessing. As a result, applications created with ICanBoogie have readable concise code and fluid flow.

Conventions and configuration

ICanBoogie comes with a few conventions: configurations are usually located in config directories, locale messages are usually located in locale directories, cache and private/public files are stored in a repository directory, and an Application class must be defined by the application.

Components configure themselves thanks to ICanBoogie's Autoconfig feature and come with sensible defaults. They won't require much from you other than a line in your composer.json file.

Events

Events are everywhere in ICanBoogie, when the application boots, before a view is rendered, when a controller returns the result of an action, when an exception needs rescuing… It's really easy to hook into the even system to be notified when certain events occur inside the application and take action.

Getters and setters

Throughout ICanBoogie, magic properties are used in favor of explicit getters and setters. For instance, DateTime instances provide a minute property instead of getMinute() and setMinute() methods.

Using a combination of getters, setters, properties, and property visibilities, you can create read-only properties, write-only properties, virtual properties; but also provide a default value until a property is set, control types and guard, and lazy load values.

Dependency injection, inversion of control

Some classes of ICanBoogie allow their methods to be defined at runtime, and since getters and setters are methods, this feature in often used to reverse control or provide dependencies. A more traditionnal approach is also available and is often used to instantiate message handlers and application services.

Objects as strings

If a string represents a serialized set of data ICanBoogie usually provides a class to make its manipulation easy. Instances can be created from strings, and in turn they can be used as strings. This applies to dates and times, time zones, time zone locations, HTTP headers, HTTP responses, database queries, and many more.

<?php

use ICanBoogie\DateTime;

$time = new DateTime('2013-05-17 12:30:45', 'Europe/Paris');

echo $time;                           // 2013-05-17T12:30:45+0200
echo $time->minute;                   // 30
echo $time->zone;                     // Europe/Paris
echo $time->zone->offset;             // 7200
echo $time->zone->location;           // FR,48.86667,2.3333348
echo $time->zone->location->latitude; // 48.86667

use ICanBoogie\HTTP\Headers;

$headers = new Headers;
$headers['Cache-Control'] = 'no-cache';
echo $headers['Cache-Control'];       // no-cache

$headers['Cache-Control']->cacheable = 'public';
$headers['Cache-Control']->no_transform = true;
$headers['Cache-Control']->must_revalidate = false;
$headers['Cache-Control']->max_age = 3600;
echo $headers['Cache-Control'];       // public, max-age=3600, no-transform

use ICanBoogie\HTTP\Response;

$response = new Response('ok', 200);
echo $response;                       // HTTP/1.0 200 OK\r\nDate: Fri, 17 May 2013 15:08:21 GMT\r\n\r\nok

/* @var $app ICanBoogie\Application */

echo $app->models['pages']->own->visible->filter_by_nid(12)->order('created_on DESC')->limit(5);
// SELECT * FROM `pages` `page` INNER JOIN `nodes` `node` USING(`nid`) WHERE (`constructor` = ?) AND (`is_online` = ?) AND (site_id = 0 OR site_id = ?) AND (language = "" OR language = ?) AND (`nid` = ?) ORDER BY created_on DESC LIMIT 5

Invokable objects

Objects performing a main action are simply invoked to perform that action. For instance, a prepared database statement is invoked to perform a command:

<?php

# DB statements

/* @var $app ICanBoogie\Application */

$statement = $app->models['nodes']->prepare('UPDATE {self} SET title = ? WHERE nid = ?');
$statement("Title 1", 1);
$statement("Title 2", 2);
$statement("Title 3", 3);

This applies to database connections, models, requests, responses, translators… and many more.

<?php

/* @var $app ICanBoogie\Application */

$pages = $app->models['pages'];
$pages('SELECT * FROM {self_and_related} WHERE YEAR(created_on) = 2013')->all;

# HTTP

use ICanBoogie\HTTP\Request;

$request = Request::from($_SERVER);
$response = $request();
$response();

# I18n translator

use ICanBoogie\I18n\Locale;

$translator = Locale::from('fr')->translator;
echo $translator('I can Boogie'); // Je sais danser le Boogie

Collections as arrays

Collections of objects always provide an array interface, whether they are records in the database, database connections, models, modules, header fields…

<?php

/* @var $app ICanBoogie\Application */

$app->models['nodes'][123];   // fetch record with key 123 in nodes
$app->modules['nodes'];       // obtain the Nodes module
$app->connections['primary']; // obtain the primary database connection

/* @var $request ICanBoogie\HTTP\Request */

$request['param1'];            // fetch param of the request named `param1`, returns `null` if it doesn't exists

/* @var $response ICanBoogie\HTTP\Response */

$response->headers['Cache-Control'] = 'no-cache';
$response->headers['Content-Type'] = 'text/html; charset=utf-8';

Creating instances from data

Most classes provide a from() static method that creates instances from various data types. This is especially true for sub-classes of the Prototyped class, which can create instances from arrays of properties, and ActiveRecords are a perfect example of this feature:

<?php

use Icybee\Modules\Nodes\Node;

$node = new Node;
$node->uid = 1;
$node->title = "Title";

# or

$node = Node::from([ 'uid' => 1, 'title' => "Title" ]);

Some classes don't even provide a public constructor and rely solely on the from() method. For instance, Request instances can only by created using the from() method:

<?php

use ICanBoogie\HTTP\Request;

# Creating the initial request from the $_SERVER array

$initial_request = Request::from($_SERVER);

# Creating a local XHR post request with some parameters

$custom_request = Request::from([

    Request::OPTION_URI => '/path/to/controller',
    Request::OPTION_IS_POST => true,
    Request::OPTION_IS_LOCAL => true,
    Request::OPTION_IS_XHR => true,
    Request::OPTION_REQUEST_PARAMS => [

        'param1' => 'value1',
        'param2' => 'value2'

    ]

]);