Complex classes like NodalFlow often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use NodalFlow, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 24 | class NodalFlow implements FlowInterface |
||
| 25 | { |
||
| 26 | /** |
||
| 27 | * Flow steps triggering callbacks |
||
| 28 | */ |
||
| 29 | const FLOW_START = 'start'; |
||
| 30 | const FLOW_PROGRESS = 'progress'; |
||
| 31 | const FLOW_SUCCESS = 'success'; |
||
| 32 | const FLOW_FAIL = 'fail'; |
||
| 33 | |||
| 34 | /** |
||
| 35 | * The parent Flow, only set when branched |
||
| 36 | * |
||
| 37 | * @var FlowInterface |
||
| 38 | */ |
||
| 39 | public $parent; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * This Flow id |
||
| 43 | * |
||
| 44 | * @var string |
||
| 45 | */ |
||
| 46 | protected $id; |
||
| 47 | |||
| 48 | /** |
||
| 49 | * The underlying node structure |
||
| 50 | * |
||
| 51 | * @var NodeInterface[] |
||
| 52 | */ |
||
| 53 | protected $nodes = []; |
||
| 54 | |||
| 55 | /** |
||
| 56 | * The current Node index |
||
| 57 | * |
||
| 58 | * @var int |
||
| 59 | */ |
||
| 60 | protected $nodeIdx = 0; |
||
| 61 | |||
| 62 | /** |
||
| 63 | * The last index value |
||
| 64 | * |
||
| 65 | * @var int |
||
| 66 | */ |
||
| 67 | protected $lastIdx = 0; |
||
| 68 | |||
| 69 | /** |
||
| 70 | * The number of Node in this Flow |
||
| 71 | * |
||
| 72 | * @var int |
||
| 73 | */ |
||
| 74 | protected $nodeCount = 0; |
||
| 75 | |||
| 76 | /** |
||
| 77 | * The number of iteration within this Flow |
||
| 78 | * |
||
| 79 | * @var int |
||
| 80 | */ |
||
| 81 | protected $numIterate = 0; |
||
| 82 | |||
| 83 | /** |
||
| 84 | * The number of break within this Flow |
||
| 85 | * |
||
| 86 | * @var int |
||
| 87 | */ |
||
| 88 | protected $numBreak = 0; |
||
| 89 | |||
| 90 | /** |
||
| 91 | * The number of continue within this Flow |
||
| 92 | * |
||
| 93 | * @var int |
||
| 94 | */ |
||
| 95 | protected $numContinue = 0; |
||
| 96 | |||
| 97 | /** |
||
| 98 | * The current registered Callback class if any |
||
| 99 | * |
||
| 100 | * @var CallbackInterface|null |
||
| 101 | */ |
||
| 102 | protected $callBack; |
||
| 103 | |||
| 104 | /** |
||
| 105 | * Progress modulo to apply |
||
| 106 | * Set to x if you want to trigger |
||
| 107 | * progress every x iterations in flow |
||
| 108 | * |
||
| 109 | * @var int |
||
| 110 | */ |
||
| 111 | protected $progressMod = 1024; |
||
| 112 | |||
| 113 | /** |
||
| 114 | * The default Node Map values |
||
| 115 | * |
||
| 116 | * @var array |
||
| 117 | */ |
||
| 118 | protected $nodeMapDefault = [ |
||
| 119 | 'class' => null, |
||
| 120 | 'branchId' => null, |
||
| 121 | 'hash' => null, |
||
| 122 | 'index' => 0, |
||
| 123 | 'num_exec' => 0, |
||
| 124 | 'num_iterate' => 0, |
||
| 125 | 'num_break' => 0, |
||
| 126 | 'num_continue' => 0, |
||
| 127 | ]; |
||
| 128 | |||
| 129 | /** |
||
| 130 | * The default Node stats values |
||
| 131 | * |
||
| 132 | * @var array |
||
| 133 | */ |
||
| 134 | protected $nodeStatsDefault = [ |
||
| 135 | 'num_exec' => 0, |
||
| 136 | 'num_iterate' => 0, |
||
| 137 | 'num_break' => 0, |
||
| 138 | 'num_continue' => 0, |
||
| 139 | ]; |
||
| 140 | |||
| 141 | /** |
||
| 142 | * Node stats values |
||
| 143 | * |
||
| 144 | * @var array |
||
| 145 | */ |
||
| 146 | protected $nodeStats = []; |
||
| 147 | |||
| 148 | /** |
||
| 149 | * The object map, used to enforce object unicity within the Flow |
||
| 150 | * |
||
| 151 | * @var array |
||
| 152 | */ |
||
| 153 | protected $objectMap = []; |
||
| 154 | |||
| 155 | /** |
||
| 156 | * The Node Map |
||
| 157 | * |
||
| 158 | * @var array |
||
| 159 | */ |
||
| 160 | protected $nodeMap = []; |
||
| 161 | |||
| 162 | /** |
||
| 163 | * The Flow stats default values |
||
| 164 | * |
||
| 165 | * @var array |
||
| 166 | */ |
||
| 167 | protected $statsDefault = [ |
||
| 168 | 'start' => null, |
||
| 169 | 'end' => null, |
||
| 170 | 'duration' => null, |
||
| 171 | 'mib' => null, |
||
| 172 | ]; |
||
| 173 | |||
| 174 | /** |
||
| 175 | * The Flow Stats |
||
| 176 | * |
||
| 177 | * @var array |
||
| 178 | */ |
||
| 179 | protected $stats = [ |
||
| 180 | 'invocations' => [], |
||
| 181 | ]; |
||
| 182 | |||
| 183 | /** |
||
| 184 | * Number of exec calls in this Flow |
||
| 185 | * |
||
| 186 | * @var int |
||
| 187 | */ |
||
| 188 | protected $numExec = 0; |
||
| 189 | |||
| 190 | /** |
||
| 191 | * Continue flag |
||
| 192 | * |
||
| 193 | * @var bool |
||
| 194 | */ |
||
| 195 | protected $continue = false; |
||
| 196 | |||
| 197 | /** |
||
| 198 | * Break Flag |
||
| 199 | * |
||
| 200 | * @var bool |
||
| 201 | */ |
||
| 202 | protected $break = false; |
||
| 203 | |||
| 204 | /** |
||
| 205 | * Current Flow Status |
||
| 206 | * |
||
| 207 | * @var FlowStatusInterface |
||
| 208 | */ |
||
| 209 | protected $flowStatus; |
||
| 210 | |||
| 211 | /** |
||
| 212 | * @var string |
||
| 213 | */ |
||
| 214 | protected $interruptNodeId; |
||
| 215 | |||
| 216 | /** |
||
| 217 | * Current nonce |
||
| 218 | * |
||
| 219 | * @var int |
||
| 220 | */ |
||
| 221 | private static $nonce = 0; |
||
| 222 | |||
| 223 | /** |
||
| 224 | * Instantiate a Flow |
||
| 225 | */ |
||
| 226 | public function __construct() |
||
| 231 | |||
| 232 | /** |
||
| 233 | * Adds a Node to the flow |
||
| 234 | * |
||
| 235 | * @param NodeInterface $node |
||
| 236 | * |
||
| 237 | * @throws NodalFlowException |
||
| 238 | * |
||
| 239 | * @return $this |
||
| 240 | */ |
||
| 241 | public function add(NodeInterface $node) |
||
| 278 | |||
| 279 | /** |
||
| 280 | * Adds a Payload Node to the Flow |
||
| 281 | * |
||
| 282 | * @param callable $payload |
||
| 283 | * @param mixed $isAReturningVal |
||
| 284 | * @param mixed $isATraversable |
||
| 285 | * |
||
| 286 | * @return $this |
||
| 287 | */ |
||
| 288 | public function addPayload(callable $payload, $isAReturningVal, $isATraversable = false) |
||
| 296 | |||
| 297 | /** |
||
| 298 | * Register callback class |
||
| 299 | * |
||
| 300 | * @param CallbackInterface $callBack |
||
| 301 | * |
||
| 302 | * @return $this |
||
| 303 | */ |
||
| 304 | public function setCallBack(CallbackInterface $callBack) |
||
| 310 | |||
| 311 | /** |
||
| 312 | * Used to set the eventual Node Target of an Interrupt signal |
||
| 313 | * set to : |
||
| 314 | * - A node hash to target |
||
| 315 | * - true to interrupt every upstream nodes |
||
| 316 | * in this Flow |
||
| 317 | * - false to only interrupt up to the first |
||
| 318 | * upstream Traversable in this Flow |
||
| 319 | * |
||
| 320 | * @param string|bool $interruptNodeId |
||
| 321 | * |
||
| 322 | * @return $this |
||
| 323 | */ |
||
| 324 | public function setInterruptNodeId($interruptNodeId) |
||
| 330 | |||
| 331 | /** |
||
| 332 | * Set parent Flow, happens only when branched |
||
| 333 | * |
||
| 334 | * @param FlowInterface $flow |
||
| 335 | * |
||
| 336 | * @return $this |
||
| 337 | */ |
||
| 338 | public function setParent(FlowInterface $flow) |
||
| 344 | |||
| 345 | /** |
||
| 346 | * Get eventual parent Flow |
||
| 347 | * |
||
| 348 | * @return FlowInterface |
||
| 349 | */ |
||
| 350 | public function getParent() |
||
| 354 | |||
| 355 | /** |
||
| 356 | * Tells if this flow has a parent |
||
| 357 | * |
||
| 358 | * @return bool |
||
| 359 | */ |
||
| 360 | public function hasParent() |
||
| 364 | |||
| 365 | /** |
||
| 366 | * Generates a truly unique id for the Flow context |
||
| 367 | * |
||
| 368 | * @return string |
||
| 369 | */ |
||
| 370 | public function uniqId() |
||
| 376 | |||
| 377 | /** |
||
| 378 | * Execute the flow |
||
| 379 | * |
||
| 380 | * @param null|mixed $param The eventual init argument to the first node |
||
| 381 | * or, in case of a branch, the last relevant |
||
| 382 | * argument from upstream Flow |
||
| 383 | * |
||
| 384 | * @throws NodalFlowException |
||
| 385 | * |
||
| 386 | * @return mixed the last result of the |
||
| 387 | * last returning value node |
||
| 388 | */ |
||
| 389 | public function exec($param = null) |
||
| 417 | |||
| 418 | /** |
||
| 419 | * Computes a human readable duration string from floating seconds |
||
| 420 | * |
||
| 421 | * @param float $seconds |
||
| 422 | * |
||
| 423 | * @return array |
||
| 424 | */ |
||
| 425 | public function duration($seconds) |
||
| 445 | |||
| 446 | /** |
||
| 447 | * Resets Nodes stats, can be used prior to Flow's re-exec |
||
| 448 | * |
||
| 449 | * @return $this |
||
| 450 | */ |
||
| 451 | public function resetNodeStats() |
||
| 459 | |||
| 460 | /** |
||
| 461 | * Get the stats array with latest Node stats |
||
| 462 | * |
||
| 463 | * @return array |
||
| 464 | */ |
||
| 465 | public function getStats() |
||
| 475 | |||
| 476 | /** |
||
| 477 | * Return the Flow id as set during instantiation |
||
| 478 | * |
||
| 479 | * @return string |
||
| 480 | */ |
||
| 481 | public function getId() |
||
| 485 | |||
| 486 | /** |
||
| 487 | * getId() alias for backward compatibility |
||
| 488 | * |
||
| 489 | * @deprecated |
||
| 490 | * |
||
| 491 | * @return string |
||
| 492 | */ |
||
| 493 | public function getFlowId() |
||
| 497 | |||
| 498 | /** |
||
| 499 | * Get the Node array |
||
| 500 | * |
||
| 501 | * @return NodeInterface[] |
||
| 502 | */ |
||
| 503 | public function getNodes() |
||
| 507 | |||
| 508 | /** |
||
| 509 | * Generate Node Map |
||
| 510 | * |
||
| 511 | * @return array |
||
| 512 | */ |
||
| 513 | public function getNodeMap() |
||
| 534 | |||
| 535 | /** |
||
| 536 | * Get the Node stats |
||
| 537 | * |
||
| 538 | * @return array |
||
| 539 | */ |
||
| 540 | public function getNodeStats() |
||
| 553 | |||
| 554 | /** |
||
| 555 | * Rewinds the Flow |
||
| 556 | * |
||
| 557 | * @return $this |
||
| 558 | */ |
||
| 559 | public function rewind() |
||
| 570 | |||
| 571 | /** |
||
| 572 | * Define the progress modulo, Progress Callback will be |
||
| 573 | * triggered upon each iteration in the flow modulo $progressMod |
||
| 574 | * |
||
| 575 | * @param int $progressMod |
||
| 576 | * |
||
| 577 | * @return $this |
||
| 578 | */ |
||
| 579 | public function setProgressMod($progressMod) |
||
| 585 | |||
| 586 | /** |
||
| 587 | * Get current $progressMod |
||
| 588 | * |
||
| 589 | * @return int |
||
| 590 | */ |
||
| 591 | public function getProgressMod() |
||
| 595 | |||
| 596 | /** |
||
| 597 | * The Flow status can either indicate be: |
||
| 598 | * - clean (isClean()): everything went well |
||
| 599 | * - dirty (isDirty()): one Node broke the flow |
||
| 600 | * - exception (isException()): an exception was raised during the flow |
||
| 601 | * |
||
| 602 | * @return FlowStatusInterface |
||
| 603 | */ |
||
| 604 | public function getFlowStatus() |
||
| 608 | |||
| 609 | /** |
||
| 610 | * Break the flow's execution, conceptually similar to breaking |
||
| 611 | * a regular loop |
||
| 612 | * |
||
| 613 | * @param InterrupterInterface|null $flowInterrupt |
||
| 614 | * |
||
| 615 | * @return $this |
||
| 616 | */ |
||
| 617 | public function breakFlow(InterrupterInterface $flowInterrupt = null) |
||
| 621 | |||
| 622 | /** |
||
| 623 | * Continue the flow's execution, conceptually similar to continuing |
||
| 624 | * a regular loop |
||
| 625 | * |
||
| 626 | * @param InterrupterInterface|null $flowInterrupt |
||
| 627 | * |
||
| 628 | * @return $this |
||
| 629 | */ |
||
| 630 | public function continueFlow(InterrupterInterface $flowInterrupt = null) |
||
| 634 | |||
| 635 | /** |
||
| 636 | * @param string $interruptType |
||
| 637 | * @param InterrupterInterface|null $flowInterrupt |
||
| 638 | * |
||
| 639 | * @throws NodalFlowException |
||
| 640 | * |
||
| 641 | * @return $this |
||
| 642 | */ |
||
| 643 | public function interruptFlow($interruptType, InterrupterInterface $flowInterrupt = null) |
||
| 665 | |||
| 666 | /** |
||
| 667 | * @param NodeInterface|null $node |
||
| 668 | * |
||
| 669 | * @return bool |
||
| 670 | */ |
||
| 671 | protected function interruptNode(NodeInterface $node = null) |
||
| 677 | |||
| 678 | /** |
||
| 679 | * Triggered just before the flow starts |
||
| 680 | * |
||
| 681 | * @return $this |
||
| 682 | */ |
||
| 683 | protected function flowStart() |
||
| 698 | |||
| 699 | /** |
||
| 700 | * Triggered right after the flow stops |
||
| 701 | * |
||
| 702 | * @return $this |
||
| 703 | */ |
||
| 704 | protected function flowEnd() |
||
| 722 | |||
| 723 | /** |
||
| 724 | * Return a simple nonce, fully valid within any flow |
||
| 725 | * |
||
| 726 | * @return int |
||
| 727 | */ |
||
| 728 | protected function getNonce() |
||
| 732 | |||
| 733 | /** |
||
| 734 | * Recurse over nodes which may as well be Flows and |
||
| 735 | * Traversable ... |
||
| 736 | * Welcome to the abysses of recursion or iter-recursion ^^ |
||
| 737 | * |
||
| 738 | * `recurse` perform kind of an hybrid recursion as the |
||
| 739 | * Flow is effectively iterating and recurring over its |
||
| 740 | * Nodes, which may as well be seen as over itself |
||
| 741 | * |
||
| 742 | * Iterating tends to limit the amount of recursion levels: |
||
| 743 | * recursion is only triggered when executing a Traversable |
||
| 744 | * Node's downstream Nodes while every consecutive exec |
||
| 745 | * Nodes are executed within a while loop. |
||
| 746 | * And recursion keeps the size of the recursion context |
||
| 747 | * to a minimum as pretty much everything is done by the |
||
| 748 | * iterating instance |
||
| 749 | * |
||
| 750 | * @param mixed $param |
||
| 751 | * @param int $nodeIdx |
||
| 752 | * |
||
| 753 | * @return mixed the last value returned by the last |
||
| 754 | * returning value Node in the flow |
||
| 755 | */ |
||
| 756 | protected function recurse($param = null, $nodeIdx = 0) |
||
| 834 | |||
| 835 | /** |
||
| 836 | * KISS helper to trigger Callback slots |
||
| 837 | * |
||
| 838 | * @param string $which |
||
| 839 | * @param null|NodeInterface $node |
||
| 840 | * |
||
| 841 | * @return $this |
||
| 842 | */ |
||
| 843 | protected function triggerCallback($which, NodeInterface $node = null) |
||
| 851 | } |
||
| 852 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountIdthat can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theidproperty of an instance of theAccountclass. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.