coff/state-machine-framework

用于设计状态机的PHP组件状态机框架

v2.0.0-alpha 2018-12-30 23:32 UTC

This package is auto-updated.

Last update: 2024-09-29 05:02:15 UTC


README

可能状态机不是你通常用PHP做的事情,但当你这样做的时候...就使用这个。这个简单而强大的框架将保持你的状态机在其期望的状态转换周期内。你还可以在运行时修改它们的行为,或者只是动态配置它们并启动。你可以根据匿名函数或类方法定义你想要的状态转换条件。使用你最喜欢的事件调度器在每个状态转换时触发事件。

安装

最简单的方法是使用composer

$ composer require coff/state-machine-framework

或者直接克隆仓库

$ git clone https://github.com/coff/php-state-machine-framework.git

然后检出特定标签

$ git checkout tags/v0.3

使用示例

状态字典

为了清晰,每个状态机都应该定义自己的状态字典。

    
    /**
     * @method static DRAFT()
     * @method static SENT()
     * ... // this should make your IDE believe these methods exist
     *
     */
    class PetitionEnum extends StateEnum {
        const __default     = self::DRAFT,
              DRAFT         = 'draft',
              SENT          = 'sent',
              VOTED         = 'voted',
              ACCEPTED      = 'accepted',
              REJECTED      = 'rejected',
              CANCELED      = 'canceled';
              
              // States names should be defined lowercase without spaces and special characters due to 
              // automatically determined assertion method names when in use with DefaultCallbackAssertion
              // @todo replace DefaultCallbackAssertion behavior in this matter 
              
    }

机器类

    class Petition extends Machine {
        
        protected $votesYes, $votesNo;
    
        public function init() {
            $this->setInitState(PetitionEnum::DRAFT());
            
            // defines machine's allowed behavior
            $this
                // prevents changing state upon assertion when AlwaysFalseAssertion is given
                ->allowTransition(PetitionEnum::DRAFT(), PetitionEnum::SENT(), new AlwaysFalseAssertion())
                ->allowTransition(PetitionEnum::DRAFT(), PetitionEnum::CANCELED(), new AlwaysFalseAssertion())
                
                // when no Assertion is given uses DefaultCallbackAssertion which calls assertXToY methods
                ->allowTransition(PetitionEnum::SENT(), PetitionEnum::VOTED())
                ->allowTransition(PetitionEnum::VOTED(), PetitionEnum::ACCEPTED())
                ->allowTransition(PetitionEnum::VOTED(), PetitionEnum::REJECTED())
                ;
            
        }
        
        public function send() 
        {
            // shall throw an exception if current state is not DRAFT because it wasn't allowed transition
            $this->setMachineState(PetitionEnum::SENT());
        }
        
        public function cancel() 
        {
            // shall throw an exception if current state is not DRAFT because it wasn't allowed transition
            $this->setMachineState(PetitionEnum::CANCELED());
        }

        
        public function setVotes($ya, $nay) 
        {
            $this->votesYes = $ya;
            $this->votesNo = $nay;
        }
        
        public function assertSentToVoted() 
        {
            // Method name used here is based upon DefaultCallbackAssertion. This can be changed though.
            return (null !== $this->votesYes && null !== $this->votesNo) ? true : false;
        }
        
        
        public function assertVotedToAccepted()
        {
            // condition for transition from state VOTED to ACCEPTED
            return $this->votesYes > $this->votesNo ? true : false;
        }
        
        public function assertVotedToRejected() 
        {
            // condition for transition from state VOTED to REJECTED
            return $this->votesYes <= $this->votesNo ? true : false;
        }
        
        public function onTransition(Transition $transition) 
        {
            // for purpose of this example we only echo this transition but you can easily dispatch an event from here 
            echo 'State changed from ' . $transition->getFromState() . ' to ' . $transition->getToState() . PHP_EOL;
        }
    }

正在使用的机器

    $p = new Petition();
    $p->init();
    
    $p->run();
    // <nothing happens>
    
    $p->send();
    // State changed from draft to sent
    
    $p->run();
    // <nothing happens>
    
    $p->setVotes(5,1);
    
    $p->run();
    // State changed from sent to voted
    // State changed from voted to accepted
    

转换对象

每个转换对象应附加一个或多个断言对象。

备注:默认情况下(如果没有提供断言对象作为参数),Machine::allowTransition()方法附加DefaultCallbackAssertion

断言行为

AlwaysTrueAssertion

在机器启动时导致自动转换。

注意

    $machine
        ->allowTransition(MachineEnum::ONE(), MachineEnum::TWO(), new AlwaysTrueAssertion)
        ->allowTransition(MachineEnum::TWO(), MachineEnum::ONE(), new AlwaysTrueAssertion)
        
    $machine->run();
    // this will result in endless loop of state changes

AlwaysFalseAssertion

在机器启动时导致没有转换。当机器状态应该在调用setMachineState()方法时改变时,使用这种断言。

DefaultCallbackAssertion

在机器对象上调用assertXToY方法(或指定其他对象),并根据其返回值做出转换决策。

CommonCallbackAssertion

在机器对象上调用assertTransition方法(或指定其他对象),并根据其返回值做出转换决策。

CallbackAssertion

调用用户指定的方法以断言是否应进行状态转换。