Zeta Workflow 11: Sniffer Plugin

The available methods are documented here: http://ezcomponents.org/docs/api/latest/Workflow/ezcWorkflowExecutionPlugin.html. For now let’s keep this simple and only implement afterNodeExecuted().

When called, we want to do the following.

  • Notify the Execution State Registry that this execution is, as of this moment, the current execution.
  • Obtain the relevant Execution State object from the Execution State Registry.
  • Have the relevant Execution State object capture and retain this execution’s current execution state.

Our unit test, therefore, can be frightfully simple. We can pass in mocks of the execution and node, and condition mocks of the Execution State Registry and Execution State object to ensure they are called as intended.

To be sure, we may have some frightfully complex scenarios to explore, such as synchronized parallel executions with paused sub workflows. But we’ll get to those later!

Test Outcomes

Having now built and tested the Execution State Registry, it’s clear that we only need to call the registry’s setCurrentExecution() method. This means we’ll need to pass the registry object in to the plugin’s constructor.

A check of the Zeta Workflow source code http://ezcomponents.org/docs/api/latest/__filesource/fsource_Workflow—Workflow—1.4—src—interfaces—execution_plugin.php.html shows that we’ll be extending the abstract base class, and that it does not define a constructor. So we’re safe defining our constructor to include the registry object.

For our only test, we will condition a mock registry object to require that its setCurrentExecution() method is called with our mock execution object.

Here is the test.

namespace StarTribune\Workflow\Tests\Sniffer;
use Mockery as m;
class snifferPluginTest extends \TestCase {
    public $execution;
    public $registry;
    public $fixture;
    public $node;

    public function setUp() {
        $this->registry  = m::mock('\StarTribune\Workflow\Sniffer\ExecutionStateRegistry');
        $this->execution = m::mock('\ezcWorkflowExecution');
        $this->node      = m::mock('\ezcWorkflowNode');
        $this->fixture   = new \StarTribune\Workflow\Sniffer\Plugin($this->registry);

    public function testMockCorrectClass() {
        $this->assertTrue($this->registry  instanceof \StarTribune\Workflow\Sniffer\ExecutionStateRegistry);
        $this->assertTrue($this->execution instanceof \ezcWorkflowExecution);
        $this->assertTrue($this->node      instanceof \ezcWorkflowNode);

    public function testFixtureCorrectClass() {
        $this->assertTrue($this->fixture instanceof \StarTribune\Workflow\Sniffer\Plugin);

    public function testSetCurrentExecution() {
        $this->fixture->afterNodeExecuted($this->execution, $this->node);


Here is the production code. As the starting point, I copied the method docblock and signature from the abstract parent class.

namespace StarTribune\Workflow\Sniffer;

class Plugin extends \ezcWorkflowExecutionPlugin {
    protected $registry;

    public function __construct(\StarTribune\Workflow\Sniffer\ExecutionStateRegistry $registry) {
        $this->registry = $registry;

     * Called after a node has been executed.
     * @param ezcWorkflowExecution $execution
     * @param ezcWorkflowNode      $node
    public function afterNodeExecuted( \ezcWorkflowExecution $execution, \ezcWorkflowNode $node )
        $ignore = $this->registry->setCurrentExecution($execution);

Once again we have a small class with a single responsibility.

Next up: The Sniffer Class.