simpleWorkflow
  • Class
  • Tree

Classes

  • SWActiveRecord
  • SWActiveRecordBehavior
  • SWComponent
  • SWEvent
  • SWException
  • SWHelper
  • SWNode
  • SWPhpWorkflowSource
  • SWValidator
  • SWWorkflowSource
  • SWyEdConverter
  • SWyEdConverterDOM
  1 <?php
  2 /**
  3  * This class implements a graph node for the simpleWorkflow extension.
  4  */
  5 class SWNode extends CComponent
  6 {
  7     /**
  8      * @var string workflow identifier
  9      */
 10     private $_workflowId;
 11     /**
 12      * @var string node identifier which must be unique within the workflow
 13      */
 14     private $_id;
 15     /**
 16      * @var string user friendly node name. If not provided at construction, the string
 17      * 'workflowId/nodeId' will be used.
 18      */
 19     private $_label;
 20     /**
 21      * @var string expression evaluated in the context of an CActiveRecord object. It must returns
 22      * a boolean value that is used to allow access to this node.
 23      */
 24     private $_constraint = array();
 25     /**
 26      * @var array
 27      */
 28     private $_metadata = array();
 29     /**
 30      * @var array array of transitions that exist between this node and other nodes
 31      */
 32     private $_tr=array();
 33     
 34     /**
 35      * Creates a workflow node instance.
 36      * If no workflowId is specified in the nodeId, then the $defaultWorkflowId is used.<br/>
 37      * Note that both workflow and node id must begin with a alphabetic character followed by aplha-numeric
 38      * characters : all other characters are not accepted and cause an exception to be thrown (see {@link SWNode::parseNodeId()})
 39      *
 40      * @param mixed $node If a string is passed as argument, it can be both in format workflowId/NodeId
 41      * or simply 'nodeId'. In this last case, argument $defaultWorkflowIs must be provided, otherwise it is
 42      * ignored. <br/>
 43      * The $node argument may also be provided as an associative array, with the following structure :<br/>
 44      * <pre>
 45      *  {
 46      *      'id'         => string,         // mandatory
 47      *      'label'      => string ,        // optional
 48      *      'constraint' => string,         // optional
 49      *      'transition' => array,          // optional
 50      *      'metadata'   => array,          // optional
 51      *  }
 52      * </pre>
 53      * Again, the 'id' value may contain a workflow id (e.g 'workflowId/nodeId') but if it's not the case then
 54      * the second argument $defaultWorkflowId must be provided.
 55      * @param string defaultWorkflowId workflow Id that is used each time a workflow is needed to complete
 56      * a status name.
 57      */
 58     public function __construct($node, $defaultWorkflowId=null)
 59     {
 60         if($node==null || empty($node))
 61             throw new SWException('illegal argument exception : $node cannot be empty', SWException::SW_ERR_CREATE_NODE);
 62         
 63         $st=array();
 64         
 65         if( $node instanceof SWNode )
 66         {
 67             // copy constructor : does not copy transitions, constraints and metadata
 68             
 69             $this->_workflowId = $node->getWorkflowId();
 70             $this->_id         = $node->getId();
 71             $this->_label      = $node->getLabel();
 72             $this->_metadata   = $node->getMetadata();
 73         }
 74         else {
 75             if( is_array($node))
 76             {
 77                 if(!isset($node['id']))
 78                     throw new SWException('missing node id',SWException::SW_ERR_MISSING_NODE_ID);
 79                     
 80                 // set node id -----------------------
 81             
 82                 $st=$this->parseNodeId($node['id'],$defaultWorkflowId);
 83             
 84                 if(isset($node['label'])){
 85                     $this->_label=$node['label'];
 86                 }
 87             
 88                 if(isset($node['constraint'])){
 89                     $this->_constraint=$node['constraint'];
 90                 }
 91             
 92                 if(isset($node['transition'])){
 93                     $this->_loadTransition($node['transition'],$st['workflow']);
 94                 }
 95             
 96                 if(isset($node['metadata'])){
 97                     $this->_metadata = $node['metadata'];
 98                 }
 99             }
100             elseif(is_string($node))
101             {
102                 $st=$this->parseNodeId($node,$defaultWorkflowId);
103             }
104             
105             $this->_workflowId = $st['workflow'];
106             $this->_id         = $st['node'];
107             
108             if(!isset($this->_label))
109                 $this->_label=$this->_id;
110         }
111     }
112     /**
113      * Parse a status name and return it as an array. The string passed as argument
114      * may be a complete status name (e.g workflowId/nodeId) and if no workflowId is
115      * specified, then an exception is thrown. Both workflow and node ids must match
116      * following pattern:
117      * <pre>
118      *     /^[[:alpha:]][[:alnum:]_]*$/
119      * </pre>
120      * For instance :
121      * <ul>
122      *  <li>ready : matches</li>
123      *  <li>to_process : matches</li>
124      *  <li>priority_1 : matches</li>
125      *  <li>2_level : does not match</li>
126      *  <li>to-production : does not match</li>
127      *  <li>enable/disable : does not match</li>
128      *</ul>
129      * @param string status status name (wfId/nodeId or nodeId)
130      * @return array the complete status (e.g array ( [workflow] => 'a' [node] => 'b' ))
131      */
132     public function parseNodeId($status,$workflowId)
133     {
134         $nodeId=$wfId=null;
135 
136         if(strstr($status,'/')){
137             if(preg_match('/^([[:alpha:]][[:alnum:]_]*)\/([[:alpha:]][[:alnum:]_]*)$/',$status,$matches) == 1){
138                 $wfId   = $matches[1];
139                 $nodeId = $matches[2];
140             }
141         }
142         else{
143             if(preg_match('/^[[:alpha:]][[:alnum:]_]*$/',$status) == 1){
144                 $nodeId = $status;
145                 if(preg_match('/^[[:alpha:]][[:alnum:]_]*$/',$workflowId) == 1){
146                     $wfId = $workflowId;
147                 }
148             }
149         }
150     
151         if( $wfId == null || $nodeId == null){
152             throw new SWException('failed to create node from node Id = '.$status.', workflow Id = '.$workflowId, SWException::SW_ERR_CREATE_NODE);
153         }
154         return array('workflow'=>$wfId,'node'=>$nodeId);
155     }
156     /**
157      * Overrides the default magic method defined at the CComponent level in order to
158      * return a metadata value if parent method fails.
159      *
160      * @see CComponent::__get()
161      */
162     public function __get($name)
163     {
164         try{
165             return parent::__get($name);
166         }catch(CException $e){
167             
168             if(isset($this->_metadata[$name])){
169                 return $this->_metadata[$name];
170             }else{
171                 throw new SWException('Property "'.$name.'" is not found.',SWException::SW_ERR_ATTR_NOT_FOUND);
172             }
173         }
174     }
175     /**
176      * Loads the set of transitions passed as argument.
177      *
178      * @param mixed $tr if provided as a string, it is a comma separated list of SWNodes id,
179      * This list can also be provided as an array
180      * @param string $defWfId Default workflow Id if nodes have no workflow id, this value is used
181      * as their workflow id.
182      */
183     private function _loadTransition($tr, $defWfId)
184     {
185         if( is_string($tr))
186         {
187             $trAr=explode(',',$tr);
188             foreach($trAr as $aTr)
189             {
190                 $objNode=new SWNode(trim($aTr),$defWfId);
191                 $this->_tr[$objNode->toString()]=null;
192             }
193         }
194         elseif( is_array($tr))
195         {
196             foreach($tr as $key => $value){
197                 if( is_string($key)){
198                     $objNode=new SWNode(trim($key),$defWfId);
199                     if($value!=null)
200                         $this->_tr[$objNode->toString()]=$value;
201                     else
202                         $this->_tr[$objNode->toString()]=null;
203                 }else {
204                     $objNode=new SWNode(trim($value),$defWfId);
205                     $this->_tr[$objNode->toString()]=null;
206                 }
207             }
208         }else {
209             throw new SWException(__FUNCTION__. 'incorrect arg type : string or array expected');
210         }
211     }
212     
213     //////////////////////////////////////////////////////////////////////////////////////////
214     // accessors
215 
216     public function getWorkflowId()     {return $this->_workflowId;}
217     public function getId()             {return $this->_id;}
218     public function getLabel()          {return $this->_label;}
219     public function getNext()           {return $this->_tr;}
220     public function getConstraint()     {return $this->_constraint;}
221     public function getMetadata()       {return $this->_metadata;}
222     public function getNextNodeIds()    {return array_keys($this->_tr);}
223     /**
224      * @returns String the task for this transition or NULL if no task is defined
225      * @param mixed $endNode SWNode instance or string that will be converted to SWNode instance (e.g 'workflowId/nodeId')
226      * @throws SWException
227      */
228     public function getTransitionTask($endNode){
229         
230         if( ! $endNode instanceof SWNode ){
231             $endNode = new SWNode($endNode, $this->getWorkflowId());
232         }
233         $endNodeId = $endNode->toString();
234                     
235         return ( isset($this->_tr[$endNodeId])
236             ? $this->_tr[$endNodeId]
237             : null
238         );
239     }
240 
241     public function __toString(){
242         return $this->getWorkflowId().'/'.$this->getId();
243     }
244     public function toString(){
245         return $this->__toString();
246     }
247     /**
248      * SWnode comparator method. Note that only the node and the workflow id
249      * members are compared.
250      *
251      * @param mixed SWNode object or string. If a string is provided it is used to create
252      * a new SWNode object.
253      */
254     public function equals($status){
255 
256         if( $status instanceof SWNode )
257         {
258             return  $status->toString() == $this->toString();
259         }
260         else try{
261             $other=new SWNode($status,$this->getWorkflowId());
262             return $other->equals($this);
263         }catch(Exception $e)
264         {
265             throw new SWException('comparaison error - the value passed as argument (value='.$status.') cannot be converted into a SWNode',$e->getCode());
266         }
267     }
268 }
269 ?>
simpleWorkflow API documentation generated by ApiGen 2.8.0