1<?php 2 3namespace ComboStrap; 4 5/** 6 * A class to modeling a tree 7 */ 8abstract class TreeNode 9{ 10 11 /** 12 * @var TreeNode[] 13 */ 14 private array $children = []; 15 16 private TreeNode $parentNode; 17 18 /** 19 * @var int the child number when added 20 * used to get a unique identifier in the tree 21 */ 22 private int $levelChildIdentifier; 23 24 25 /** 26 * @throws ExceptionBadState - if the node has already been added and is not the same than in the tree 27 */ 28 public function appendChild(TreeNode $treeNode): TreeNode 29 { 30 try { 31 $levelChildIdentifier = $treeNode->getLevelChildIdentifier(); 32 } catch (ExceptionNotFound $e) { 33 // no level child identifier 34 $childCount = count($this->children); 35 $levelChildIdentifier = $childCount + 1; 36 $treeNode->setLevelChildIdentifier($levelChildIdentifier); 37 $treeNode->setParent($this); 38 $this->children[$levelChildIdentifier] = $treeNode; 39 return $this; 40 } 41 $actualTreeNode = $this->children[$levelChildIdentifier]; 42 if ($actualTreeNode !== $treeNode) { 43 throw new ExceptionBadState("The node ($treeNode) was already added but not on this level"); 44 } 45 return $this; 46 } 47 48 public function getLocalId() 49 { 50 51 } 52 53 54 public 55 function __toString() 56 { 57 return $this->getTreeIdentifier(); 58 } 59 60 61 public 62 function getChildCount(): int 63 { 64 return sizeof($this->children); 65 } 66 67 /** 68 * @return TreeNode[] - empty array for a leaf 69 */ 70 public function getChildren(): array 71 { 72 return $this->children; 73 } 74 75 76 public function hasChildren(): bool 77 { 78 return sizeof($this->children) > 0; 79 } 80 81 public function hasParent(): bool 82 { 83 return isset($this->parentNode); 84 } 85 86 /** 87 * @return TreeNode 88 * @throws ExceptionNotFound 89 */ 90 public function getFirstChild(): TreeNode 91 { 92 $childrenKeys = array_keys($this->children); 93 if (sizeof($childrenKeys) === 0) { 94 throw new ExceptionNotFound("This node has no child"); 95 } 96 return $this->children[$childrenKeys[0]]; 97 } 98 99 /** 100 * A hierarchical tree identifier composed 101 * of the node id of each level separated by a point 102 * @return string 103 */ 104 function getTreeIdentifier(): string 105 { 106 107 $treeIdentifier = $this->getLevelChildIdentifier(); 108 $parent = $this; 109 while (true) { 110 try { 111 $parent = $parent->getParent(); 112 $parentLevelNodeIdentifier = $parent->getLevelChildIdentifier(); 113 if ($parentLevelNodeIdentifier !== "") { 114 $treeIdentifier = "{$parentLevelNodeIdentifier}.$treeIdentifier"; 115 } else { 116 $treeIdentifier = "$treeIdentifier"; 117 } 118 } catch (ExceptionNotFound $e) { 119 // no parent anymore 120 break; 121 } 122 123 } 124 return $treeIdentifier; 125 } 126 127 128 private function setParent(TreeNode $parent) 129 { 130 $this->parentNode = $parent; 131 } 132 133 /** 134 * @throws ExceptionNotFound 135 */ 136 public function getParent(): TreeNode 137 { 138 if (!isset($this->parentNode)) { 139 throw new ExceptionNotFound("No parent node"); 140 } 141 return $this->parentNode; 142 } 143 144 private function setLevelChildIdentifier(int $levelIdentifier) 145 { 146 $this->levelChildIdentifier = $levelIdentifier; 147 } 148 149 public function detachBeforeAppend(){ 150 151 unset($this->parentNode->children[$this->levelChildIdentifier]); 152 unset($this->levelChildIdentifier); 153 unset($this->parentNode); 154 155 } 156 157 /** 158 * @throws ExceptionNotFound 159 */ 160 private function getLevelChildIdentifier(): int 161 { 162 if (!isset($this->levelChildIdentifier)) { 163 throw new ExceptionNotFound("No child identifier level found"); 164 } 165 return $this->levelChildIdentifier; 166 } 167 168 169} 170