NodalFlow implements a series of events through symfony/event-dispatcher
. You can easily register any existing dispatcher implementing Symfony's EventDispatcherInterface
.
NodalFlow is compatible and tested with symfony/event-dispatcher versions 2.8.*
, 3.4.*
and 4.0.*
(php > 7.1).
NodalFlow provides each dispatch()
call with a FlowEvent
instance, extending Symfony Event
and implementing FlowEventInterface
. Each FlowEvent instance carries the dispatcher's Flow instance, and eventually a Node instance, when the event is tied to a specific Node.
To increase performance, a hash map of active event is build when the Flow is about to start, and event dispatch calls are wrapped with a costless isset
call on this tiny hash map.
In addition, the same event instance is used for all dispatch, only the Node gets set to either null
or current Node instance at runtime.
In order to make it simple to use any kind of EventDispatcherInterface
implementation, NodalFlow does not instantiate the default Symfony implementation until you actually call $flow->getDispatcher()
or register a Callback (the old way).
This means that you can set your own dispatcher before you use it to register NodalFlow events (or just set it already setup) :
$flow->setDispatcher(new CustomDispatcher);
or :
$flow->setDispatcher($alreadySetupDispatcher);
But you can also just let NodalFlow handle instantiation :
$flow->getDispatcher()->addListener('flow.event.name', function(FlowEventInterface $event) {
// always set
$flow = $event->getFlow();
// not always set
$node = $event->getNode();
// do stuff ...
});
or even :
$flow->getDispatcher()->addSubscriber(new EventSubscriberInterfaceImplementation());
It is important to note that each Flow instance carries its own dispatcher instance. In most cases, it is ok to just register events on the Root Flow as it is the one controlling the executions of all its eventual children. You still get the big picture with Root Flow events, such as start, end and exceptions, but you do not have access to children iteration events (the FlowEvent::FLOW_PROGRESS
event).
If you need more granularity, you will need to register events in each Flow you want to observe.
Triggered when the Flow starts, the event only carries the flow instance.
$flow->getDispatcher()->addListener(FlowEvent::FLOW_START, function(FlowEventInterface $event) {
$flow = $event->getFlow();
// do stuff ...
});
Triggered when node iterates in the Flow, the event carries the flow and the iterating node instances.
$flow->getDispatcher()->addListener(FlowEvent::FLOW_PROGRESS, function(FlowEventInterface $event) {
$flow = $event->getFlow();
$node = $event->getNode();
// do stuff ...
});
As this is the most called event, a modulo is implemented to only fire it once every $progressMod
iteration. The default is 1024, you can set it directly on the Flow :
// increase to 100k
$flow->setProgressMod(100000);
// or for full granularity
$flow->setProgressMod(1);
Since the $progressMod
modulo is applied to each iterating node iteration count, each iterating node will have an opportunity to fire the event.
For example, using a $progressMod
of 10 and extracting 10 categories chained to another extractor extracting items in these categories, the FlowEvent::FLOW_PROGRESS
event will be fired once with the first extractor and each 10 items found in each categories from the second extractor.
Triggered when a node triggers a continue
on the Flow, the event carries the flow and the node instance triggering the continue
.
$flow->getDispatcher()->addListener(FlowEvent::FLOW_CONTINUE, function(FlowEventInterface $event) {
$flow = $event->getFlow();
$node = $event->getNode();
// do stuff ...
});
Triggered when a node triggers a break
on the Flow, the event carries the flow and the node instance triggering the break
.
$flow->getDispatcher()->addListener(FlowEvent::FLOW_BREAK, function(FlowEventInterface $event) {
$flow = $event->getFlow();
$node = $event->getNode();
// do stuff ...
});
Triggered when the Flow completes successfully (eg with no exceptions), the event only carries the flow instance.
$flow->getDispatcher()->addListener(FlowEvent::FLOW_SUCCESS, function(FlowEventInterface $event) {
$flow = $event->getFlow();
// do stuff ...
});
Triggered when an exception is raised during Flow execution, the event carries the flow instance, and the node current Node instance in the Flow when the exception was thrown.
$flow->getDispatcher()->addListener(FlowEvent::FLOW_FAIL, function(FlowEventInterface $event) {
$flow = $event->getFlow();
$node = $event->getNode();
// if you need to inspect the exeption
$exception = $flow->getFlowStatus()->getException();
// do stuff ...
});
The original exception is re-thrown by NodalFlow after the execution of FlowEvent::FLOW_FAIL events.
The event implementation is fully compatible with the old Callback strategy. Although deprecated, you do not have to do anything to continue using your CallbackInterface
implementations.
Under the hood, a CallbackWrapper
implementation of Symfony EventSubscriberInterface
is used as a proxy to the CallbackInterface
implementation.