ICanBoogie is a high-performance micro-framework for PHP 5.5+. It is written with speed, flexibility and lightness in mind. ICanBoogie doesn't try to be an all-in-one do-it-all solution but provides the essential features to quickly and easily build web applications. It is easily extensible, and a variety of packages are available to complement its features with rendering, views, routing, operations, internationalization, translation, ORM, facets, mailer…
Together with Brickrouge and Patron, ICanBoogie is one of the components that make the CMS Icybee. You might want to check these projects too.
micro means that the core features of ICanBoogie are kept to the essential, the core is simple but greatly extensible. For instance, ICanBoogie won't force an ORM on you, although its ActiveRecord implementation is pretty nice. In the same fashion, its routing mechanisms are quite agnostic and let you use your very own dispatcher if you want to.
ICanBoogie and its components are usually very configurable and come with sensible defaults and a
few conventions. Configurations are usually located in "config" folders, while locale messages are
usually located in "locale" folders. Components configure themselves thanks to ICanBoogie's
Autoconfig feature, and won't require much of you other than a line in your
composer.json
file.
MooTools, Ruby on Rails, Yii, and of course Bacara.
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.
Magic properties are used in favor of getter and setter methods (e.g. getXxx()
or setXxx()
).
For instance, DateTime instances provide a minute
magic property instead of getMinute()
and setMinute()
methods:
<?php
use ICanBoogie\DateTime;
$time = new DateTime('2013-05-17 12:30:45', 'utc');
echo $time; // 2013-05-17T12:30:45Z
echo $time->minute; // 30
$time->minute += 120;
echo $time; // 2013-05-17T14:30:45Z
The getter/setter feature provided by icanboogie/accessor, and extended by icanboogie/prototype, allows you to create read-only or write-only properties, façades properties, fallbacks to generate default values. Getters are setters are also often using to control types, lazy load resources, and inversion of control.
icanboogie/prototype allows methods to be defined at runtime, and since getters and setters are methods as well, this feature in often used as a mean to reverse control to provide dependencies. What's great about it is that dependencies are only provided when they are required, not when to instance is created.
The following example demonstrates how a shared database connection is obtained through a db
property:
<?php
use ICanBoogie\ActiveRecord\Connection;
use ICanBoogie\Prototype;
use ICanBoogie\PrototypeTrait;
/**
* @property-read Connection $db
*/
class A
{
use PrototypeTrait;
public function truncate()
{
$this->db("TRUNCATE my_table");
}
}
Prototype::from(A::class)['get_db'] = function(A $a) {
static $db;
return $db ?: $db = new Connection("sqlite::memory:");
};
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\Core */
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
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\Core */
$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\Core */
$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 of objects always provide an array interface, whether they are records in the database, database connections, models, modules, header fields…
<?php
/* @var $app ICanBoogie\Core */
$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';
Most classes provide a from()
static method that create instances from various data types.
This is especially true for sub-classes of the Prototyped class, which can create instances
from arrays of properties. 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'
]
]);
Thanks to its Autoconfig system and a few conventions, running your application only requires three lines:
<?php
require 'vendor/autoload.php';
$app = ICanBoogie\boot();
$app();
1. The first line is pretty common for applications using Composer, it creates and runs its autoloader.
2. On the second line the Core instance is created with the autoconfig, its boot()
method is invoked, and the ICanBoogie\Core::boot
event is fired. At this point ICanBoogie and
low-level components are configured and booted. Your application is ready to process requests.
3. On the third line the application is run, which implies the following:
3.1. The HTTP response code is set to 500, so that if a fatal error occurs the error message won't be sent with the HTTP code 200 (Ok).
3.2. The initial request is obtained and the ICanBoogie\Core::run
event is fired with it.
3.3. The request is executed to obtain a response.
3.4. The response is executed to respond to the request. It should set the HTTP code to the appropriate value.
3.5. The ICanBoogie\Core::terminate
event is fired at which point the application should be
terminated.
ICanBoogie has built-in multi-site support and can be configured for different domains or environments. Even if you are dealing with only one domain, this feature can be used to provide different configuration for the "dev", "stage", and "production" versions of a same application.
The intended location for your custom application code is in a separate "protected" directory, but
another directory can be defined with the app-root
autoconfig directive. The directory is
relative to the root
directive.
ICanBoogie searches for "config" directories to add to the autoconfig. Packages may alter the autoconfig as well. For instance, icanboogie/module searches for "modules" directories.
The instance name of the application is used to resolve the application paths. It is usually
defined by the ICANBOOGIE_INSTANCE
environment variable but can be retrieved from PHP_SAPI
or
$_SERVER
.
If the variable is not defined, the instance name defaults as follows:
- The application runs from the CLI (e.g. PHP_SAPI == "cli"), "cli" is used as instance name.
$_SERVER['SERVER_NAME']
is defined, it is used as instance name.
Consider an application root directory with the following directories:
all
cli
default
dev
icanboogie.org
org
The directory "all" contains resources that are common to all instances. It is always added if present. The directory "default" is only added if there no directory matches the instance name.
To resolve the matching directory, the instance name is first broken into parts and the most
specific ones are removed until a corresponding directory is found. For instance, given the
server name www.icanboogie.dev
, the following directories are tried:
www.icanboogie.dev
, icanboogie.dev
, and finally dev
.
If the instance name cannot be resolved into a directory, "default" is used instead.
The Autoconfig feature automatically generates a configuration file from the available low-level components participating in the Autoconfig process. Currently, it is used to define configuration constructors, paths to component configurations, paths to locale message catalogs, and paths to modules.
To participate in the autoconfig process, packages need to define their autoconfig fragment
in the extra/icanboogie
section of their "composer.json" file. The file must match the
composer-schema.json schema. The following example
demonstrates how an application can specify the path to its configuration and locale messages.
{
"extra": {
"icanboogie": {
"config-path": "path/to/config",
"locale-path": "path/to/locale"
}
}
}
Note: Packages can also define their autoconfig fragment in a stand-alone "icanboogie.json" file, beside their "composer.json" file, but using the "composer.json" file is recommended. The file must match the icanboogie-schema.json schema.
The autoconfig file is generated after the autoloader is dumped, during the
post-autoload-dump
event emitted by Composer.
Thus, in order for the autoconfig feature to work, a script for the event
is required in the root package of the application:
{
"scripts": {
"post-autoload-dump": "ICanBoogie\\Autoconfig\\Hooks::on_autoload_dump"
}
}
The autoconfig is obtained using the ICanBoogie\get_autoconfig()
function, and can be
used as is to instantiate the Core instance. The function also updates the app-root
and
app-paths
values with the resolved application root and and resolved application paths
respectively.
<?php
namespace ICanBoogie;
$app = new Core(get_autoconfig());
Additionally, the ICanBoogie\AUTOCONFIG_PATHNAME
constant defines the absolute pathname to the
autoconfig file.
Note: A fatal error is triggered if the autoconfig file does not exists, which might happen if the user forgot to add the
post-autoload-dump
hook in its "composer.json" file.
Filters defined with the autoconfig-filters
key are invoked to alter the autoconfig before
the get_autoconfig()
function returns it. For instance, ICanBoogie uses this feature to add
"config" directories found in the application paths (using the multi-site support).
{
"extra": {
"icanboogie": {
"autoconfig-filters": [ "ICanBoogie\\Autoconfig\\Hooks::filter_autoconfig" ]
}
}
}
The Core instance is configured with core configuration fragments. The fragment used by your
application is usually located in the /protected/all/config/core.php
file.
The following example demonstrates how to enable configs caching and how to specify the name of the session and its scope.
<?php
// protected/all/config/core.php
return [
'cache configs' => true,
'session' => [
'name' => "ICanBoogie",
'domain' => ".example.org"
]
];
Check ICanBoogie's "config/core.php" for a list of available options and their default values.
A storage engine for synthesized configurations may be specified with the
storage_for_configs
option. You may specify a class name or a callable that would return a
Storage instance.
The default implementation returns a FileStorage instance, or if APC is available a StorageCollection made of an APCStorage instance and a FileStorage instance.
A storage engine for variables may be specified with the
storage_for_variables
option. You may specify a class name or a callable that would return a
Storage instance.
The default implementation returns a FileStorage instance, or if APC is available a StorageCollection made of an APCStorage instance and a FileStorage instance.
The ICanBoogie\Core::configure
event of class ConfigureEvent is fired once the application
is configured. Event hooks may use this event to alter the application configuration or
configure components.
The ICanBoogie\Core::boot
event of class BootEvent is fired once the application has booted.
Event hooks may use this event to bootstrap components before the application is ran.
The ICanBoogie\Core::run
event of class RunEvent is fired when the application is running.
Event hooks may use this event to alter various states of the application, starting with the
initial request.
The ICanBoogie\Core::terminate
event of class TerminateEvent is fired after the response to
the initial request was sent and the application is about to be terminated. Event hooks may
use this event to cleanup loose ends.
The clear_cache
event of class ClearCacheEvent is fired when the various caches of the
application must be cleared. Event hooks may use this event to clear their own cache. For instance,
ICanBoogie clears its configurations cache when this event is fired.
The app
magic property of Prototyped instances returns the instance of the
application. The property is read-only and is only available after the Core instance
has been created.
The PrototypedBindings trait may be used to type hint instances.
<?php
namespace ICanBoogie;
use ICanBoogie\Binding\PrototypedBindings;
/* @var $o Prototyped|PrototypedBindings */
$o = new Prototyped;
$o->app;
// throw ICanBoogie\PropertyNotDefined;
$app = boot();
$app === $o->app;
// true
The package provides a controller for the /api/ping
route, which may be used to renew a session,
if one existed in the first place. When the timer
query parameter is present, the controller
gives timing information as well.
<?php
use ICanBoogie\HTTP\Request;
$request = Request::from('/api/ping?timer');
echo $request()->body;
// pong, in 4.875 ms (ready in 3.172 ms)
The following helper functions are defined:
app()
: Returns the Core instance, or throws CoreNotInstantiated if it hasn't been instantiated yet.boot()
: Instantiates a Core instance with the autoconfig and boots it.log()
: Logs a debug message.log_success()
: Logs a success message.log_error()
: Logs an error message.log_info()
: Logs an info message.log_time()
: Logs a debug message associated with a timing information.
The minimum requirement is PHP 5.5.
The recommended way to install this package is through Composer:
$ composer require icanboogie/icanboogie
Don't forget to modify the script section of your "composer.json" file if you want to benefit from the autoconfig feature:
{
"scripts": {
"post-autoload-dump": "ICanBoogie\\Autoconfig\\Hooks::on_autoload_dump"
}
}
The following packages are required, you might want to check them out:
- icanboogie/common
- icanboogie/inflector
- icanboogie/datetime
- icanboogie/prototype
- icanboogie/event
- icanboogie/http
- icanboogie/routing
The following packages can also be installed for additional features:
- icanboogie/render: A rendering API.
- icanboogie/view: Adds views to controllers.
- icanboogie/activerecord: ActiveRecord Object-relational mapping.
- icanboogie/cldr: Provides internationalization for your application.
- icanboogie/i18n: Provides localization for your application and additional internationalization helpers.
- icanboogie/image: Provides image resizing, filling, and color resolving.
- icanboogie/module: Provides framework extensibility using modules.
- icanboogie/operation: Operation oriented controllers API.
The following bindings are available to help in integrating components:
The package is available on GitHub, its repository can be cloned with the following command line:
$ git clone https://github.com/ICanBoogie/ICanBoogie.git
The documentation for the package and its dependencies can be generated with the make doc
command. The documentation is generated in the docs
directory using ApiGen.
The package directory can later by cleaned with the make clean
command.
The documentation for the complete framework is also available online: http://icanboogie.org/docs/
The test suite is ran with the make test
command. Composer is
automatically installed as well as all dependencies required to run the suite. You can later
clean the directory with the make clean
command.
The package is continuously tested by Travis CI.
ICanBoogie is licensed under the New BSD License - See the LICENSE file for details.