A sequencer executes a list of tasks and watches the tasks as they execute. This allows the sequencer to respond to unfolding events — including making changes to the task list. Once the app has been started up it can re-write itself from the pool of available task classes. The current action can be refined in some way or the app can change track altogether and try something else.
Sequencing might be used when there are a series of loosely-coupled tasks to be performed or you need some kind of self-assembling intelligence. That probably fits php cli apps better than web pages — the sequencing code was originally developed for Rephactor.
Include the sequencing file...
<?php
require_once('aperiplus/lib/control/sequencing.php');
...and define a sequence:
<?php
$sequence = FromPhemto(array('ThisTask', 'ThatTask'));
(#!! pass in a Phemto..?)
The above example uses Phemto to instantiate the given tasks. You can also use a hand-written factory.
<?php
$sequence = new FromFactory(
array('ThisTask', 'ThatTask'),
new YourFactory);
The factory class should have a getter for each Task object it's supposed to provide. Note that you must use the naming convention "getClassName":
<?php
class YourFactory {
function getThisTask() {
}
function getThatTask() {
}
}
Tasks should implement:
<?php
interface Task {
function execute();
}
In Sequencer, successfully executed tasks will be passed to a namesake method, if implemented, or to _defaultSuccess() if not.
Failed tasks must throw an exception of some kind to signify failure and the exception will similarly be passed to a namesake method, if implemented, or _defaultFailure if not.
Method names must be class names prefixed with an underscore.
Although Sequencer will work as-is, you will probably want to extend it with your own custom sequencer class in order to define how the app will respond to events of interest.
Handler methods can do anything they like including altering the task sequence. You can add tasks to the front or the end of the sequence or reset it altogether. Here I've got a custom handler for each of Foo, Bar, and Busted — the names of task classes or exceptions thrown by them. The task (or exception) object is passed to the handler method in case you want to query it.
<?php
class MySequencer extends Sequencer {
function _Foo($task) {
$this->_sequence->addToFront(
array('AnotherTask')); // array of class names
}
function _Bar($task) {
$this->_sequence->addToBack(
array('YetAnother', 'AndAnother'));
}
function _Busted($exception) {
$this->_sequence = new FromPhemto(
array('Change', 'Direction')); // reset
}
Finally, instantiate and run the sequencer:
<?php
$sequencer = new MySequencer($sequence);
$sequencer->execute();
This is a bit of an experiment. I'm not sure how easy it would be to follow a complicated app with a lot of re-sequencing rules. At least they are all in one place (MySequencer).
Although it would be possible to allow a Sequencer instance as an item in a tasks list, that might again make things very hard to follow?