PACKAGE PHILOSOPHY
This whole package strives to follow the open/closed principle, making it
open for extension (adding your own logic through subclassing) but closed
for modification. For that purpose, we provide Loader interfaces, Builders
and Persistence Adapters for the backend implementation. States and
Transitions can also be subclassed to store data and provide functionality.
Following the same philosophy: if you want more functionality in the
statemachine, you can use the provided methods and override them in your
subclass. multiple hooks are provided. Most functionality should be provided by
using the diverse ways of interacting with the statemachine: callables can be injected,
commands can be injected, event handlers can be defined and hooks can be overriden.
We have provided multiple persistance backends to function as a data store to store all
relevant information for a statemachine including configuration, transition history and current states.
- relational databases: postgresql, mysql, sqlite
- nosql key/value: redis
- nosql document based: mongodb
Memory and session backend adapters can be used to temporarily store the state information.
yaml, json and xml loaders can be used to load configuration data from files containing those
data formats.
Examples are provided in the 'examples' folder and serve to highlight some of the
features and the way to work with the package. The unittests can serve as examples too,
including interaction with databases.
ENVIRONMENTS OF USAGE:
- 1: a one time process:
-- on webpages where there are page refreshes in between (use session/pdo adapter)
see 'examples/session'
-- an api where succesive calls are made (use pdo adapter)
-- cron jobs (pdo adapter)
- 2: a longer running process:
-- a php daemon that runs as a background process (use memory adapter)
-- an interactive shell environment (use memory adapter)
see 'examples/interactive'
DESCRIPTION OF THE 4 MAIN MODELS OF USAGE:
- 1: DELEGATION: Use an existing domain model. You can use a subclass of the
AbstractFactory to get a StateMachine, since that will put the creation of
all the relevant Contextual classes and Transitions in a reusable model.
usage: This is the most formal, least invasive and powerful model of usage, but the most complex.
Use Rules and Commands to interact with your domain model without altering a domain model
to work with the statemachine.
see 'examples/trafficlight'
- 2: INHERITANCE: Subclass a statemachine. Build the full Context and all transitions in
the constructor of your subclass (which could be a domain model itself) and
call the parent constructor. use the hooks to provide functionality (optionally a ModelBuilder and callbacks).
usage: This is the most flexible model of usage, but mixes your domain logic with the statemachine.
see 'examples/inheritance'
- 3: COMPOSITION: Use object composition. Instantiate and build a statemachine in a domain
model and build the full Context, Transitions and statemachine there. Use a ModelBuilder and callbacks
to drive functionality.
usage: This is a good mixture between encapsulating statemachine logic and flexibility/formal usage.
see 'examples/composition'
- 4: STANDALONE: Use the statemachine as is, without any domain models. Your application will use it and inspect the
statemachine to act on it. Use callables to provide functionality
usage: This is the easiest model of usage, but the least powerful.
see 'examples/interactive'
MECHANISMS FOR GUARD AND TRANSITION LOGIC
1. rules and commands: they are fully qualified class names of Rule and Command (sub)classes that can be injected
in Transition and State instances. they accept the domain model provided via an EntityBuilder
in their constructor.
2. hooks: the methods in this class that start with an underscore and can be overriden in a subclass.
these can be used to provide a subclass of this machine specifically tailored to your application domain.