回首页

php 有限状态机使用说明

winzou/state-machine

$config = array(
    'graph'         => 'myGraphA', // Name of the current graph - there can be many of them attached to the same object
    'property_path' => 'stateA',  // Property path of the object actually holding the state
    'states'        => array(
        'checkout',
        'pending',
        'confirmed',
        'cancelled'
    ),
    'transitions' => array(
        'create' => array(
            'from' => array('checkout'),
            'to'   => 'pending'
        ),
        'confirm' => array(
            'from' => array('checkout', 'pending'),
            'to'   => 'confirmed'
        ),
        'cancel' => array(
            'from' => array('confirmed'),
            'to'   => 'cancelled'
        )
    ),
    'callbacks' => array(
        'guard' => array(
            'guard-cancel' => array(
                'to' => array('cancelled'), // Will be called only for transitions going to this state
                'do' => function() { var_dump('guarding to cancelled state'); return false; }
            )
        ),
        'before' => array(
            'from-checkout' => array(
                'from' => array('checkout'), // Will be called only for transitions coming from this state
                'do'   => function() { var_dump('from checkout transition'); }
            )
        ),
        'after' => array(
            'on-confirm' => array(
                'on' => array('confirm'), // Will be called only on this transition
                'do' => function() { var_dump('on confirm transition'); }
            ),
            'to-cancelled' => array(
                'to' => array('cancelled'), // Will be called only for transitions going to this state
                'do' => function() { var_dump('to cancel transition'); }
            ),
            'cancel-date' => array(
                'to' => array('cancelled'),
                'do' => array('object', 'setCancelled'),
            ),
        )
    )
);

各个属性说明

1'property_path' => 'stateA'

需要在 DomainObject对象中定义,如:

class DomainObject
{
    private $stateA = 'checkout';//初始状态

    public function getStateA()
    {
        return $this->stateA;
    }
    public function setStateA($state)
    {
        $this->stateA = $state;
    }
}

2 states

定义所有可能的状态

'states' => array(
        'checkout',
        'pending',
        'confirmed',
        'cancelled'
    ),

3 transitions

定义了执行动作后的状态变化

'transitions' => array(
        'create' => array(
            'from' => array('checkout'),
            'to'   => 'pending'
        ),
        'confirm' => array(
            'from' => array('checkout', 'pending'),
            'to'   => 'confirmed'
        ),
        'cancel' => array(
            'from' => array('confirmed'),
            'to'   => 'cancelled'
        )
    ),

4 callbacks

定义了什么时候执行相对应的回调

'callbacks' => array(
        'guard' => array(
            'guard-cancel' => array(
                'to' => array('cancelled'), // Will be called only for transitions going to this state
                'do' => function() { var_dump('guarding to cancelled state'); return false; }
            )
        ),
        'before' => array(
            'from-checkout' => array(
                'from' => array('checkout'), // Will be called only for transitions coming from this state
                'do'   => function() { var_dump('from checkout transition'); }
            )
        ),
        'after' => array(
            'on-confirm' => array(
                'on' => array('confirm'), // Will be called only on this transition
                'do' => function() { var_dump('on confirm transition'); }
            ),
            'to-cancelled' => array(
                'to' => array('cancelled'), // Will be called only for transitions going to this state
                'do' => function() { var_dump('to cancel transition'); }
            ),
            'cancel-date' => array(
                'to' => array('cancelled'),
                'do' => array('object', 'setCancelled'),
            ),
        )
    )

下面这个定义了是否可以更改到当前状态,guard-cancel修饰作用没有实际意义,执行的时候会扫描整个数组,当to的指向状态是cancelled时执行do回调,当回调返回true的时候可以更改到该状态,类似于中间件

'guard' => array(
            'guard-cancel' => array(
                'to' => array('cancelled'), // Will be called only for transitions going to this state
                'do' => function() { var_dump('guarding to cancelled state'); return false; }
            )
        ),

通过guard之后会先执行before数组的key其修饰作用,执行的时候会循环这个数组,当from的状态是checkout会执行do回调,

 'before' => array(
            'from-checkout' => array(
                'from' => array('checkout'), // Will be called only for transitions coming from this state
                'do'   => function() { var_dump('from checkout transition'); }
            )
        ),

before执行完之后,接下来开始扫描after数组,其key起装饰作用。有两种类型一种是on do,另一种是 to doon 对应的是transitions的key值,匹配到此key值就执行do回调。to对应的是要转换到的状态,匹配到此状态就执行do回调

 'after' => array(
            'on-confirm' => array(
                'on' => array('confirm'), // Will be called only on this transition
                'do' => function() { var_dump('on confirm transition'); }
            ),
            'to-cancelled' => array(
                'to' => array('cancelled'), // Will be called only for transitions going to this state
                'do' => function() { var_dump('to cancel transition'); }
            ),
            'cancel-date' => array(
                'to' => array('cancelled'),
                'do' => array('object', 'setCancelled'),
            ),
        )

do回调有两种方式一种是回调,另一种是DomainObject的方法

'do' => array('object', 'setCancelled'),

对应

class DomainObject
{
    private $stateA = 'checkout';//初始状态

    public function getStateA()
    {
        return $this->stateA;
    }
    public function setStateA($state)
    {
        $this->stateA = $state;
    }
     public function setConfirmedNow()
    {
        var_dump('I (the object) am set confirmed at '.date('Y-m-d').'.');
    }
}

setConfirmedNow方法


有三个事件

    const PRE_TRANSITION  = 'winzou.state_machine.pre_transition';
    const POST_TRANSITION = 'winzou.state_machine.post_transition';
    const TEST_TRANSITION = 'winzou.state_machine.test_transition';

可参考symfony/event-dispatcher

定义完图后,设置一个初始状态,例如把订单的checkout状态设置进去DomainObject中,然后操作执行相对应的方法(\$stateMachine->apply('create')),可能执行成功($stateMachine->getState(),设置状态进入订单表),也可能执行不成功(错误处理),