Migration Guide (1.x to 2.x)
Between CSF v1 and v2 there have been a lot of conceptual and syntactic changes to using both the core functionality and the libraries.
Basic Concepts
In my own opinion, and judging by the feedback from others, the concepts and roles of libraries, helpers and modules were unclear in CSF v1. In CSF v2, this has been resolved so that now there are only two concepts.
Libraries are chunks of functionality, in the same way as for the rest of the field of software development. Examples are things like a template engine, an authentication library, a collection of template helper functions. “Helpers” was a useless concept in CSF v1, so everything that was previously under “helpers” should really be a library.
One of the main things CSF does is store a collection of objects, giving each one a unique name, for the purpose of accessing them globally. In CSF v2, “module” either refers to one of these registered objects, or a PHP file which is responsible only for creating and registering such an object.
Configuration
The CSF configuration mechanism hasn't really changed, but some CSF options are in new places now (documented later). A refresher on the use of configuration:
$conf['foo']['bar'] = 'baz'; $conf['foo']['baz'] = 'xyz'; CSF::init($conf); // Returns 'baz' $a = CSF::config('foo.bar'); // Returns array('bar' => 'baz', 'baz' => 'xyz') $b = CSF::config('foo'); // Throws an exception $c = CSF::config('nothing'); // Returns 'default' $d = CSF::config('nothing', 'default');
Libraries
Loading libraries manually hasn't changed:
CSF::load_library('mylib'); // Load a library from a subdirectory of a library path CSF::load_library('subdir/mylib');
Libraries can be automatically loaded by supplying an array of libraries to load in the autoload.libraries configuration item. They will be loaded in the order they are defined.
$conf['autoload']['libraries'] = array( 'lib1', 'lib2', );
Library paths should be supplied as an array of paths in the core.library_paths configuration item, and when loading a library they will be searched in reverse order (with CSF's libraries directory being the last to be tried). This means libraries with the same name existing in directories further down the list will replace those in earlier directories. Library paths can also be added manually:
CSF::add_library_path('path/to/more/libraries');
Modules
Previously, CSF did too much magic when it came to loading modules, which made it difficult to implement auto-loading and near-impossible to load anything other than a purpose-built class. This interface has been refined and separated.
Module registration and accessing are distinct operations:
$obj = new MyClass(); CSF::register('mymod', $obj); // ... CSF::get('mymod')->do_something(); // Or slightly shorter... CSF('mymod')->do_something(); // Or another way... $csf = CSF(); $csf->mymod->do_something();
Since CSF_Controller now extends CSF_Module, you can also get to modules easily from within all controllers:
$this->somemodule->dosomething();
Typically, a module file consists only of loading any necessary libraries, creating an object and registering it with CSF (but it could do pretty much anything you want). For example, modules/dispatch.php:
CSF::load_library('csf_dispatch'); CSF::register($MODULE_NAME, new CSF_Dispatch($MODULE_CONF));
When loading a module, CSF will expose the $MODULE_NAME and $MODULE_CONF variables (the name to register under and the configuration array for the module) and include the module file. The mechanism for specifying and searching paths for module files is identical to that for libraries.
Manually loading modules is done with the load_module function:
CSF::load_module($name, $config);
$nameis either of the formarray('alias', 'name'), where “alias” is the name the module will be registered as and “name” is the name of the module used in the path to the module file, or just a single string to be used as both the name and the alias.
// Include mymodule.php with $MODULE_NAME = 'mymodule' CSF::load_module('mymodule'); // Include mymodule.php with $MODULE_NAME = 'foo' CSF::load_module(array('foo', 'mymodule'));
$configis the configuration array exposed to the module file as$MODULE_CONFIG. This can be omitted, in which case the configuration itemmodules.alias(where “alias” has the same meaning as for$name) will be used, defaulting to an empty array.
// Load mymodule with $MODULE_NAME = 'mymodule' and $MODULE_CONF = CSF::config('modules.mymodule') CSF::load_module('mymodule'); // Load mymodule with $MODULE_NAME = 'mymodule' and $MODULE_CONF = array('foo' => 'bar') CSF::load_module('mymodule' array('foo' => 'bar')); // Load mymodule with $MODULE_NAME = 'foo' and $MODULE_CONF = CSF::config('modules.foo') CSF::load_module(array('foo', 'mymodule'));
Modules can be autoloaded—the autoload.modules configuration item is an array of module names as understood by CSF::load_module (and therefore accepts the array('alias', 'module') notation. For example:
$conf['modules']['ystvdb'] = array('dsn' => 'sqlite:ystv.db'); $conf['autoload']['modules'] = array( 'session', array('ystvdb', 'db'), );
Templating
Previously, templating was provided by the View library, which maintained the context for rendering the template (i.e. the variables and values to expose) and provided the rendering mechanism. The role of the template engine has been refined so that it handles only template rendering, and the context is a single object passed directly through to the template.
In the controller
Previously, controller code would look like this:
// ... do something ... $csf->view->title = 'My wonderful web page of awesome'; $csf->view->content = 'This is the content'; echo $csf->view->render('thetemplate.php');
The new code would look like this (for minimal modification of logic):
$context = array(); $context['title'] = 'My wonderful web page of awesome'; $context['content'] = 'This is the content'; return new BasicResponse($csf->template->render('thetemplate', $context));
Things to note here:
- No
.phpextension—the file extension is now a configuration option - Context is built separate from the template module
- The context can be any variable
- The context is passed as part of the call to
render - Use of “response objects”—see Dispatch
Further exploiting the idea of response objects, the code might look more like this (with $response→send() rendering the template):
$response = new TemplateResponse('thetemplate'); $response->title = 'My wonderful web page of awesome'; $response->content = 'This is the content'; return $response;
In the template
Previously, template code might look like:
<? Template::inherit('master.php'); ?> <? Template::block('maincontent'); ?> <?=$content?> <? Template::endblock(); ?>
Assuming the simple example above, where the context object is an array, the new template would look like this:
<? $TPL->extend('master'); ?> <? $TPL->block('maincontent'); ?> <?=$C['content']?> <? $TPL->endblock(); ?>
Things to note here:
- Yet again, the lack of
.phpextension in the call toextend Template::replaced with$TPL->inheritrenamed toextend(a more meaningful verb, I feel)$Cis the context variable passed torender
Dispatch/Controllers
A lot of the code for dispatch and the supplied controller base class has been changed, but it still works the same, and defining routes is the same as before.
The only significant change is that controllers should return their output rather than echoing directly. For the YSTV website, we will be using a nice abstraction of this, where controllers must return an object that has a send() method (which should do any echoing, sending of headers, etc.), and index.php will operate like this:
$response = CSF('dispatch')->dispatch_uri(CSF('request')->get_uri()); $response->send();
CSF_Request
The Request library was a bit half-baked before, but now it's better: Documented here.