Posted by Dave Redfern (Writer), Documentation on 24 Apr 2009 @ 02:27
For 0.2.0 major improvements have been made across the framework, but none are more evident than in the MVC and CLI systems. When migrating from the previous versions to 0.2.0 you will likely encounter several issues. This article will attempt to highlight them and offer advice on how to handle updating.
The migration of CLI apps should be relatively straight forward. Most of the interfaces are still the same with few changes to the structure of the controllers and views. There are several points to note though:
Starting with 0.2.0, mvcRequest::getInstance is being avoided in all controllers, models and views. Access in the view is via the controller so instead of directly calling the mvcRequest, it should be accessed by calling: getController()->getRequest(). This should help make writing test cases much easier.
mvcRequest has inherited some of the methods that were originally in the distributor. These were moved here as they form part of the request and are not dependent on the dispatcher. This was the file type system that determines the output format for the current request.
mvcDistributorBase and the base/index.php launcher in the websites folder have seen many changes and refactorings. The biggest of these is the removal of much of the initialisation logic into plugins that can be fired during initialisation (e.g. session management, log setup, locale and device detection), pre-dispatch and post-dispatch. These plugins can be manually assigned to the distributor by instantiating and attaching them directly, or by using the distributorPlugins config section in the website config.xml file. Examples are available in the base/config.xml and baseAdminSite/config.xml.
Plugins are started by giving the full name of the plugin class and setting the value to "true". It is important to realise that plugins "stack" i.e. if they are defined in the base/config.xml they will be inherited through the site path. With that said, the current site always has priority, so any active plugin can be disabled on a site by site basis. Simply set the value to 0. Please note that ONLY 0 (the number zero) will disable a plugin.
These mvcDistributorPlugins can be added to by extending the base plugin and placing the necessary logic in the appropriate method. To load the plugin, add the class name and file location to the autoload classes section, again in the site config.xml file.
Several plugins are included with the framework, replicating the previous functionality that was hard-coded into the primary dispatch() method of the distributorDispatcher class. These plugins are:
| Plugin Class Name | Description |
|---|---|
| mvcDistributorPluginLog |
Creates log files based on the site name and sets individual log levels for each site. |
| mvcDistributorPluginSession | Creates and maintains sessions for the MVC system |
| mvcDistributorPluginLocale | Provides locale support for the MVC system |
| mvcDistributorPluginDetectDevice | Handles mobile device detection and content-negotiation (requires session and the WURFL system be configured and a current WURFL file parsed and ready for usage) |
All of these plugins can be loaded by simply setting the name in the distributorPlugins section of the config file.
Moving to the plugin system allows only those plugins that you need to be used. Note that there can (are) dependencies between plugins.
The final change in the distributor is that the mvcRequest is passed during instantiation - it is NOT created internally.
What do these changes mean for usage? If you have overridden, or implemented your own Distributor you should check the default in /base/index.php for the base implementation and then either update or use this. If you have added specific code to support your site, either try to re-implement it as a plugin or if not possible, again extend the mvcDistributorBase and add custom methods that contain only your changes. You are strongly urged to use the plugin system as a future version will move mvcDistributorBase into a concrete class.
mvcSessionBase has been extended to include pre and postInitialise methods to allow for easier overriding of functionality.
mvcControllerBase has seen a few changes the most prominent the addition of get/set Request for passing the mvcRequest around instead of needing to use the static mvcRequest::getInstance. There is still one call to this static method though, in the constructor. It was decided not to remove it just yet as this caused issues downstream in many controllers that used the constructor to setup various options.
The prefered method is to use the initialise() method to do this setup and this is what should be done as in a future release the constructor may be marked as "final" to prevent this from happening. However you can still overload it, but you must rememeber to call parent::__construct() BEFORE applying any logic.
The MVC autoloader has picked up a caching system to help improve performance. This caches the FULL path to the various classes that make up the site. By default this is enabled and set to auto-save on shutdown. This can improve performance quite substantially so understanding the changes is important. In particular you must pay attention to the fact that the ENTIRE filepath is stored in the cache file. Therefore when creating static caches (i.e. you disable autoload-class files and auto-save) you must pre-generate the cache file with the files in the exact same locations as they are on the production system. This means that you cannot create cache files on Windows and deploy to Linux (or vice versa). A command has been added to the scorpio CLI tool to automatically build the cache for any registered site. When deploying to production, you should re-run this after any updates.
Additionally: as the full path is stored you must re-build or delete the cache if you move any files, or create a new override - otherwise the already cached location will be used which will either error, cause inconsistent behaviour or hard to track errors.
The cache system can be completely disabled be setting autoloadCacheEnabled to 0 HOWEVER you MUST enable alwaysPreloadSiteClasses otherwise required files will not be loaded and your application will fail to start.
The most difficult challenge will be migrating CLI applications or tools. The CLI system has been completely re-developed to be based on a pseudo Front Controller system. This allows for a single entry point for any command and means you no longer have to code for each parameters, switch or argument you want to support. You immediately get access to them via the cliRequest object. The downside to this, is that CLI apps have a very prescriptive design.
Now when creating a cliApplication you no longer extend the cliApplication class. Instead you use this and add your commands to the command stack. A command generally performs a single function and can re-route to additional commands upon error, completion etc. Error handling has been centralised and all commands should through the cliApplicationCommandException class which always carriers an instance of the command that triggered the error.
Other changes include revised signal handling, help system and a response system where cliApp responses can be aggregated to a single cliResponse object, or flushed as the command sees fit.
There are many built-in commands for handling common tasks and providing support to other commands, some of the more important and useful ones are:
| Command Class | Location | Description |
|---|---|---|
| cliCommandChain | /libraries/cli/commands | Used internal to hold other command objects and allow iteration over them |
| cliCommandNull | /libraries/cli/commands | A 'null' command, it has no execute body allowing it to act as a place-holder during app building, or to provide help messages |
| cliCommandSwitch | /libraries/cli/commands | Creates a 'switch' command and allows for aliases to it. Switch commands are always processed before arguments or parameters |
| cliCommandHelp | /libraries/cli/commands | Provides a help system that formats command options, names and messages into a friendly format. Also adds the -h switch. |
| cliCommandLog | /libraries/cli/commands | Adds verbose and very verbose log options as switches. |
| cliCommandLogToConsole | /libraries/cli/commands | Outputs log messages to the console as well as a log file |
| cliCommandConfig | /libraries/cli/commands | Adds support for --config to allow other config files to be loaded before commands are processed |
| systemCommandExtract | /libraries/system/command | Provides a container for aggregating extraction tools e.g. the i18n and autoload commands. |
| dbUpdateCommandUpdate | /libraries/db/update/command | Provides a CLI interface to the dbUpdate system allowing dbUpdate class files to be executed. |
There are other commands including pre-built command chains for new, delete and list commands.
In cliApplication the order of the commands is important. All switches (anything starting with a single hyphen, that is one character long) are processed before any other command. Therefore, switches can occur anywhere in the arguments.
Once all the switches have been processed, the arguments are then worked on. An argument can be either a --param=value pair, or a chain such as: new dao MyObject. The cliApplication will work through the arguments IN THE ORDER THEY ARE SET and dispatch each one to the first matching command. Commands must therefore have unique signatures and should not be repeated.
A basic cliApplication can look like:
require_once(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'libraries'.DIRECTORY_SEPARATOR.'system.inc');
$oApp = new cliApplication('cliUtils', 'A simple script file that has some useful functions without the whole application framework behind it.');
$oRequest = cliRequest::getInstance()->setApplication($oApp);
$oApp->getCommandChain()
->addCommand(new cliCommandHelp($oRequest))
->addCommand(new cliCommandPassword($oRequest))
->addCommand(new cliCommandSerialise($oRequest));
$oApp->getListeners()->attachListener(new cliApplicationListenerLog());
$oApp->execute($oRequest);
This is the cliUtils.php script from /tools. It allows for help, hashing passwords and serialising data. It is very simple to put together and each command can be re-used by any other cliApplication.
The final change to the cliApplication system is the addition of cliApplicationListener objects. This system allows various listeners to be attached to the application and events notified to them. Currently only a logging listener is complete, however anything can be added so long as it implements the interface.
Listeners take a cliApplicationEvent object. This records the event information and can include additional information depending on where the event was fired. Some parameters are reserved for the logging system (e.g. log.source, app.command, app.name) but anything can be registered, including additional objects, though you should code to handle if they are not present.
cliApplications can be very advanced, for an example look at the scorpio.php tool in /tools. This has many sub-commands, uses the listener system as well as command redirection.
Finally, cliDaemons use the revised system, however these are still extended into your own daemon. They now use the method execute() to needs to be overloaded with an implementation - the default is to throw an exception. Custom terminate is still supported, and now listeners can be configured to receive events from cliDaemon termination.
The last component is the test system. This has been overhauled to make it easier to work with and to try and provide a level of abstraction from the test library. The test system has been moved to a dedicated /test folder in libraries and several additional classes have been added.
All tests should now inherit from testCaseScorpio. This abstract class provides some basic utility methods including a skip() for SimpleTest is production mode is set. This prevents test cases from running on a production system.
The additional classes include objects for locating test cases and returning object sets and a HTML reporter - test cases can now be run via the admin system or a web server with relative ease.
These changes along with a much larger simpletest autoload file make it far more reliable to run tests and allows for the test case management tools to be used by both the CLI and Web systems.
The changes required are relatively minor, simply change the inheritance to testCaseScorpio instead of the previous MyUnitTestCase. This class still exists, but it will be removed in later releases of the Scorpio framework.
That covers the major changes that are likely to cause issues. CLI Applications are the major issue. Both test cases and MVC require very little in the way of modification, in fact in some cases it is more of just updating config files than actual code changes.
Of course any updates should be thoroughly tested in a development and then a staging environment before being placed into production.
This release of Scorpio is a big leap forward both in quality and stability, moving forward this will continue to be improved.