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