/protected/components/ezcomponents/Workflow/src/interfaces/node.php

https://github.com/kamarulismail/kamarul-playground · PHP · 657 lines · 264 code · 68 blank · 325 comment · 27 complexity · 4c589943632659661481478457d0cdeb MD5 · raw file

  1. <?php
  2. /**
  3. * File containing the ezcWorkflowNode class.
  4. *
  5. * This class provides basic facilities for workflow nodes
  6. * such as constraints for in and output nodes and methods
  7. * for identifying and execution of the node.
  8. *
  9. * @package Workflow
  10. * @version 1.4.1
  11. * @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved.
  12. * @license http://ez.no/licenses/new_bsd New BSD License
  13. */
  14. /**
  15. * Abstract base class for workflow nodes.
  16. *
  17. * All workflow nodes must extend this class.
  18. *
  19. * @package Workflow
  20. * @version 1.4.1
  21. * @mainclass
  22. */
  23. abstract class ezcWorkflowNode implements ezcWorkflowVisitable
  24. {
  25. /**
  26. * The node is waiting to be activated.
  27. */
  28. const WAITING_FOR_ACTIVATION = 0;
  29. /**
  30. * The node is activated and waiting to be executed.
  31. */
  32. const WAITING_FOR_EXECUTION = 1;
  33. /**
  34. * Unique ID of this node.
  35. *
  36. * Only available when the workflow this node belongs to has been loaded
  37. * from or saved to the data storage.
  38. *
  39. * @var integer
  40. */
  41. protected $id = false;
  42. /**
  43. * The incoming nodes of this node.
  44. *
  45. * @var array( int => ezcWorkflowNode )
  46. */
  47. protected $inNodes = array();
  48. /**
  49. * The outgoing nodes of this node.
  50. *
  51. * @var array( int => ezcWorkflowNode )
  52. */
  53. protected $outNodes = array();
  54. /**
  55. * Constraint: The minimum number of incoming nodes this node has to have
  56. * to be valid. Set to false to disable this constraint.
  57. *
  58. * @var integer
  59. */
  60. protected $minInNodes = 1;
  61. /**
  62. * Constraint: The maximum number of incoming nodes this node has to have
  63. * to be valid. Set to false to disable this constraint.
  64. *
  65. * @var integer
  66. */
  67. protected $maxInNodes = 1;
  68. /**
  69. * Constraint: The minimum number of outgoing nodes this node has to have
  70. * to be valid. Set to false to disable this constraint.
  71. *
  72. * @var integer
  73. */
  74. protected $minOutNodes = 1;
  75. /**
  76. * Constraint: The maximum number of outgoing nodes this node has to have
  77. * to be valid. Set to false to disable this constraint.
  78. *
  79. * @var integer
  80. */
  81. protected $maxOutNodes = 1;
  82. /**
  83. * The number of incoming nodes.
  84. *
  85. * @var integer
  86. */
  87. protected $numInNodes = 0;
  88. /**
  89. * The number of outgoing nodes.
  90. *
  91. * @var integer
  92. */
  93. protected $numOutNodes = 0;
  94. /**
  95. * The configuration of this node.
  96. *
  97. * The configuration is a structured (hash) array with the
  98. * various options of the implemented node.
  99. *
  100. * This functionality is implemented as an array to make it possible
  101. * to have the storage engines unaware of the node classes.
  102. *
  103. * @var array( config key => config value )
  104. */
  105. protected $configuration;
  106. /**
  107. * The state of this node.
  108. *
  109. * @var integer
  110. */
  111. protected $activationState;
  112. /**
  113. * The node(s) that activated this node.
  114. *
  115. * @var ezcWorkflowNode[]
  116. */
  117. protected $activatedFrom = array();
  118. /**
  119. * The state of this node.
  120. *
  121. * @var mixed
  122. */
  123. protected $state = null;
  124. /**
  125. * The id of the thread this node is executing in.
  126. *
  127. * @var integer
  128. */
  129. protected $threadId = null;
  130. /**
  131. * Flag that indicates whether an add*Node() or remove*Node()
  132. * call is internal. This is necessary to avoid unlimited loops.
  133. *
  134. * @var boolean
  135. */
  136. protected static $internalCall = false;
  137. /**
  138. * Constructs a new node with the configuration $configuration.
  139. *
  140. * The configuration is a structured (hash) array. Implementations
  141. * must pass their complete configuration on to this object. We have
  142. * chosen to use structured arrays for the configuration since it
  143. * simplifies the process of creating new node types and storing workflows.
  144. *
  145. * @param mixed $configuration
  146. */
  147. public function __construct( $configuration = null )
  148. {
  149. if ( $configuration !== null )
  150. {
  151. $this->configuration = $configuration;
  152. }
  153. $this->initState();
  154. }
  155. /**
  156. * Adds a node to the incoming nodes of this node.
  157. *
  158. * Automatically adds $node to the workflow and adds
  159. * this node as an out node of $node.
  160. *
  161. * @param ezcWorkflowNode $node The node that is to be added as incoming node.
  162. * @throws ezcWorkflowInvalidWorkflowException if the operation violates the constraints of the nodes involved.
  163. * @return ezcWorkflowNode
  164. */
  165. public function addInNode( ezcWorkflowNode $node )
  166. {
  167. // Check whether the node is already an incoming node of this node.
  168. if ( ezcWorkflowUtil::findObject( $this->inNodes, $node ) === false )
  169. {
  170. // Add this node as an outgoing node to the other node.
  171. if ( !self::$internalCall )
  172. {
  173. self::$internalCall = true;
  174. $node->addOutNode( $this );
  175. }
  176. else
  177. {
  178. self::$internalCall = false;
  179. }
  180. // Add the other node as an incoming node to this node.
  181. $this->inNodes[] = $node;
  182. $this->numInNodes++;
  183. }
  184. return $this;
  185. }
  186. /**
  187. * Removes a node from the incoming nodes of this node.
  188. *
  189. * Automatically removes $this as an out node of $node.
  190. *
  191. * @param ezcWorkflowNode $node The node that is to be removed as incoming node.
  192. * @throws ezcWorkflowInvalidWorkflowException if the operation violates the constraints of the nodes involved.
  193. * @return boolean
  194. */
  195. public function removeInNode( ezcWorkflowNode $node )
  196. {
  197. $index = ezcWorkflowUtil::findObject( $this->inNodes, $node );
  198. if ( $index !== false )
  199. {
  200. // Remove this node as an outgoing node from the other node.
  201. if ( !self::$internalCall )
  202. {
  203. self::$internalCall = true;
  204. $node->removeOutNode( $this );
  205. }
  206. else
  207. {
  208. self::$internalCall = false;
  209. }
  210. unset( $this->inNodes[$index] );
  211. $this->numInNodes--;
  212. return true;
  213. }
  214. return false;
  215. }
  216. /**
  217. * Adds a node to the outgoing nodes of this node.
  218. *
  219. * Automatically adds $node to the workflow and adds
  220. * this node as an in node of $node.
  221. *
  222. * @param ezcWorkflowNode $node The node that is to be added as outgoing node.
  223. * @throws ezcWorkflowInvalidWorkflowException if the operation violates the constraints of the nodes involved.
  224. * @return ezcWorkflowNode
  225. */
  226. public function addOutNode( ezcWorkflowNode $node )
  227. {
  228. // Check whether the other node is already an outgoing node of this node.
  229. if ( ezcWorkflowUtil::findObject( $this->outNodes, $node ) === false )
  230. {
  231. // Add this node as an incoming node to the other node.
  232. if ( !self::$internalCall )
  233. {
  234. self::$internalCall = true;
  235. $node->addInNode( $this );
  236. }
  237. else
  238. {
  239. self::$internalCall = false;
  240. }
  241. // Add the other node as an outgoing node to this node.
  242. $this->outNodes[] = $node;
  243. $this->numOutNodes++;
  244. }
  245. return $this;
  246. }
  247. /**
  248. * Removes a node from the outgoing nodes of this node.
  249. *
  250. * Automatically removes $this as an in node of $node.
  251. *
  252. * @param ezcWorkflowNode $node The node that is to be removed as outgoing node.
  253. * @throws ezcWorkflowInvalidWorkflowException if the operation violates the constraints of the nodes involved.
  254. * @return boolean
  255. */
  256. public function removeOutNode( ezcWorkflowNode $node )
  257. {
  258. $index = ezcWorkflowUtil::findObject( $this->outNodes, $node );
  259. if ( $index !== false )
  260. {
  261. // Remove this node as an incoming node from the other node.
  262. if ( !self::$internalCall )
  263. {
  264. self::$internalCall = true;
  265. $node->removeInNode( $this );
  266. }
  267. else
  268. {
  269. self::$internalCall = false;
  270. }
  271. unset( $this->outNodes[$index] );
  272. $this->numOutNodes--;
  273. return true;
  274. }
  275. return false;
  276. }
  277. /**
  278. * Returns the Id of this node.
  279. *
  280. * @return integer
  281. * @ignore
  282. */
  283. public function getId()
  284. {
  285. return $this->id;
  286. }
  287. /**
  288. * Sets the Id of this node.
  289. *
  290. * @param int $id
  291. * @ignore
  292. */
  293. public function setId( $id )
  294. {
  295. $this->id = $id;
  296. }
  297. /**
  298. * Sets the activation state for this node.
  299. *
  300. * One of ezcWorkflowNode::WAITING_FOR_ACTIVATION or
  301. * ezcWorkflowNode::WAITING_FOR_EXECUTION.
  302. *
  303. * @param int $activationState
  304. * @ignore
  305. */
  306. public function setActivationState( $activationState )
  307. {
  308. if ( $activationState == self::WAITING_FOR_ACTIVATION ||
  309. $activationState == self::WAITING_FOR_EXECUTION )
  310. {
  311. $this->activationState = $activationState;
  312. }
  313. }
  314. /**
  315. * Returns the incoming nodes of this node.
  316. *
  317. * @return ezcWorkflowNode[]
  318. */
  319. public function getInNodes()
  320. {
  321. return $this->inNodes;
  322. }
  323. /**
  324. * Returns the outgoing nodes of this node.
  325. *
  326. * @return ezcWorkflowNode[]
  327. */
  328. public function getOutNodes()
  329. {
  330. return $this->outNodes;
  331. }
  332. /**
  333. * Returns the configuration of this node.
  334. *
  335. * @return mixed
  336. */
  337. public function getConfiguration()
  338. {
  339. return $this->configuration;
  340. }
  341. /**
  342. * Returns the state of this node.
  343. *
  344. * @return mixed
  345. * @ignore
  346. */
  347. public function getState()
  348. {
  349. return $this->state;
  350. }
  351. /**
  352. * Sets the state of this node.
  353. *
  354. * @param mixed $state
  355. * @ignore
  356. */
  357. public function setState( $state )
  358. {
  359. $this->state = $state;
  360. }
  361. /**
  362. * Returns the node(s) that activated this node.
  363. *
  364. * @return array
  365. * @ignore
  366. */
  367. public function getActivatedFrom()
  368. {
  369. return $this->activatedFrom;
  370. }
  371. /**
  372. * Sets the node(s) that activated this node.
  373. *
  374. * @param array $activatedFrom
  375. * @ignore
  376. */
  377. public function setActivatedFrom( array $activatedFrom )
  378. {
  379. $this->activatedFrom = $activatedFrom;
  380. }
  381. /**
  382. * Returns the id of the thread this node is executing in.
  383. *
  384. * @return integer
  385. * @ignore
  386. */
  387. public function getThreadId()
  388. {
  389. return $this->threadId;
  390. }
  391. /**
  392. * Sets the id of the thread this node is executing in.
  393. *
  394. * @param int $threadId
  395. * @ignore
  396. */
  397. public function setThreadId( $threadId )
  398. {
  399. $this->threadId = $threadId;
  400. }
  401. /**
  402. * Checks this node's constraints.
  403. *
  404. * The constraints checked are the minimum in nodes
  405. * maximum in nodes, minimum out nodes and maximum
  406. * out nodes.
  407. *
  408. * @throws ezcWorkflowInvalidWorkflowException if the constraints of this node are not met.
  409. */
  410. public function verify()
  411. {
  412. $type = str_replace( 'ezcWorkflowNode', '', get_class( $this ) );
  413. if ( $this->minInNodes !== false && $this->numInNodes < $this->minInNodes )
  414. {
  415. throw new ezcWorkflowInvalidWorkflowException(
  416. sprintf(
  417. 'Node of type "%s" has less incoming nodes than required.',
  418. $type
  419. )
  420. );
  421. }
  422. if ( $this->maxInNodes !== false && $this->numInNodes > $this->maxInNodes )
  423. {
  424. throw new ezcWorkflowInvalidWorkflowException(
  425. sprintf(
  426. 'Node of type "%s" has more incoming nodes than allowed.',
  427. $type
  428. )
  429. );
  430. }
  431. if ( $this->minOutNodes !== false && $this->numOutNodes < $this->minOutNodes )
  432. {
  433. throw new ezcWorkflowInvalidWorkflowException(
  434. sprintf(
  435. 'Node of type "%s" has less outgoing nodes than required.',
  436. $type
  437. )
  438. );
  439. }
  440. if ( $this->maxOutNodes !== false && $this->numOutNodes > $this->maxOutNodes )
  441. {
  442. throw new ezcWorkflowInvalidWorkflowException(
  443. sprintf(
  444. 'Node of type "%s" has more outgoing nodes than allowed.',
  445. $type
  446. )
  447. );
  448. }
  449. }
  450. /**
  451. * Reimplementation of accept() calls accept on all out nodes.
  452. *
  453. * @param ezcWorkflowVisitor $visitor
  454. */
  455. public function accept( ezcWorkflowVisitor $visitor )
  456. {
  457. if ( $visitor->visit( $this ) )
  458. {
  459. foreach ( $this->outNodes as $outNode )
  460. {
  461. $outNode->accept( $visitor );
  462. }
  463. }
  464. }
  465. /**
  466. * Activate this node in the execution environment $execution.
  467. *
  468. * $activatedFrom is the node that activated this node and $threadId is
  469. * threadId of the thread the node should be activated in.
  470. *
  471. * This method is called by other nodes and/or the execution environment
  472. * depending on the workflow.
  473. *
  474. * @param ezcWorkflowExecution $execution
  475. * @param ezcWorkflowNode $activatedFrom
  476. * @param int $threadId
  477. * @ignore
  478. */
  479. public function activate( ezcWorkflowExecution $execution, ezcWorkflowNode $activatedFrom = null, $threadId = 0 )
  480. {
  481. if ( $this->activationState === self::WAITING_FOR_ACTIVATION )
  482. {
  483. $this->activationState = self::WAITING_FOR_EXECUTION;
  484. $this->setThreadId( $threadId );
  485. if ( $activatedFrom !== null )
  486. {
  487. $this->activatedFrom[] = get_class( $activatedFrom );
  488. }
  489. $execution->activate( $this );
  490. }
  491. }
  492. /**
  493. * Convenience method for activating an (outgoing) node.
  494. *
  495. * @param ezcWorkflowExecution $execution
  496. * @param ezcWorkflowNode $node
  497. */
  498. protected function activateNode( ezcWorkflowExecution $execution, ezcWorkflowNode $node )
  499. {
  500. $node->activate( $execution, $this, $this->getThreadId() );
  501. }
  502. /**
  503. * Returns true if this node is ready for execution
  504. * and false if it is not.
  505. *
  506. * @return boolean
  507. * @ignore
  508. */
  509. public function isExecutable()
  510. {
  511. return $this->activationState === self::WAITING_FOR_EXECUTION;
  512. }
  513. /**
  514. * Executes and performs the workflow duties of this node
  515. * and returns true if the node completed execution.
  516. *
  517. * Implementations of ezcWorkflowNode should reimplement this method.
  518. *
  519. * This method is called automatically by the workflow execution
  520. * environment and should not be called directly.
  521. *
  522. * The default implementation resets the activation state of the
  523. * node.
  524. *
  525. * @param ezcWorkflowExecution $execution
  526. * @return boolean true when the node finished execution,
  527. * and false otherwise
  528. * @ignore
  529. */
  530. public function execute( ezcWorkflowExecution $execution )
  531. {
  532. $this->activationState = self::WAITING_FOR_ACTIVATION;
  533. $this->activatedFrom = array();
  534. $this->threadId = null;
  535. return true;
  536. }
  537. /**
  538. * Generate node configuration from XML representation.
  539. *
  540. * @param DOMElement $element
  541. * @ignore
  542. */
  543. public static function configurationFromXML( DOMElement $element )
  544. {
  545. }
  546. /**
  547. * Generate XML representation of this node's configuration.
  548. *
  549. * @param DOMElement $element
  550. * @ignore
  551. */
  552. public function configurationToXML( DOMElement $element )
  553. {
  554. }
  555. /**
  556. * Returns a textual representation of this node.
  557. *
  558. * @return string
  559. * @ignore
  560. */
  561. public function __toString()
  562. {
  563. $type = str_replace( 'ezcWorkflowNode', '', get_class( $this ) );
  564. $max = strlen( $type );
  565. $string = '';
  566. for ( $i = 0; $i < $max; $i++ )
  567. {
  568. if ( $i > 0 && ord( $type[$i] ) >= 65 && ord( $type[$i] ) <= 90 )
  569. {
  570. $string .= ' ';
  571. }
  572. $string .= $type[$i];
  573. }
  574. return $string;
  575. }
  576. /**
  577. * Initializes the state of this node.
  578. *
  579. * @ignore
  580. */
  581. public function initState()
  582. {
  583. $this->activatedFrom = array();
  584. $this->state = null;
  585. $this->threadId = null;
  586. $this->setActivationState( self::WAITING_FOR_ACTIVATION );
  587. }
  588. }
  589. ?>