1<?php 2 3namespace dokuwiki\plugin\struct\meta; 4 5use dokuwiki\Utf8\Sort; 6 7/** 8 * Object to create a tree of values 9 * 10 * You should not create these yourself, but use the NestedResult class instead 11 */ 12class NestedValue 13{ 14 15 /** @var Value */ 16 protected $value; 17 18 /** @var NestedValue[] */ 19 protected $children = []; 20 21 /** @var Value[][] */ 22 protected $resultRows = []; 23 24 /** @var int the nesting depth */ 25 protected $depth; 26 27 /** 28 * Create a nested version of the given value 29 * 30 * @param Value|null $value The value to store, null for root node 31 * @param int $depth The depth of this node (avoids collision where the same values are selected on multiple levels) 32 */ 33 public function __construct(?Value $value, $depth = 0) 34 { 35 $this->value = $value; 36 $this->depth = $depth; 37 } 38 39 /** 40 * @return int 41 */ 42 public function getDepth() 43 { 44 return $this->depth; 45 } 46 47 /** 48 * @param int $depth 49 */ 50 public function setDepth($depth) 51 { 52 $this->depth = $depth; 53 foreach ($this->children as $child) { 54 $child->setDepth($depth + 1); 55 } 56 } 57 58 /** 59 * Access the stored value 60 * 61 * @return Value|null the value stored in this node, null for root node 62 */ 63 public function getValueObject() 64 { 65 return $this->value; 66 } 67 68 /** 69 * Add a child node 70 * 71 * Nodes with the same key (__toString()) will be overwritten 72 * 73 * @param NestedValue $child 74 * @return void 75 */ 76 public function addChild(NestedValue $child) 77 { 78 $this->children[(string)$child] = $child; // ensures uniqueness 79 } 80 81 /** 82 * Get all child nodes 83 * 84 * @return NestedValue[] 85 */ 86 public function getChildren() 87 { 88 $children = $this->children; 89 usort($children, [$this, 'sortChildren']); 90 return $children; 91 } 92 93 /** 94 * Add a result row to this node 95 * 96 * Only unique rows will be stored, duplicates are detected by hashing the row values' toString result 97 * 98 * @param Value[] $row 99 * @return void 100 */ 101 public function addResultRow($row) 102 { 103 // only add unique rows 104 $ident = md5(array_reduce($row, function ($carry, $value) { 105 return $carry . $value; 106 }, '')); 107 108 $this->resultRows[$ident] = $row; 109 } 110 111 /** 112 * Get all result rows stored in this node 113 * 114 * @return Value[][] 115 */ 116 public function getResultRows() 117 { 118 return array_values($this->resultRows); 119 } 120 121 /** 122 * Get a unique key for this node 123 * 124 * @return string 125 */ 126 public function __toString() 127 { 128 if ($this->value === null) return ''; // root node 129 return $this->value->__toString() . '-' . $this->depth; 130 } 131 132 /** 133 * Custom comparator to sort the children of this node 134 * 135 * @param NestedValue $a 136 * @param NestedValue $b 137 * @return int 138 */ 139 public function sortChildren(NestedValue $a, NestedValue $b) 140 { 141 // note: the way NestedResults build the NestedValues, the value object should 142 // always contain a single value only. But since the associated column is still 143 // a multi-value column, getCompareValue() will still return an array. 144 // So here we treat all returns as array and join them with a dash (even though 145 // there should never be more than one value in there) 146 return Sort::strcmp( 147 join('-', (array)$a->getValueObject()->getCompareValue()), 148 join('-', (array)$b->getValueObject()->getCompareValue()) 149 ); 150 } 151 152 /** 153 * print the tree for debugging 154 * 155 * @return string 156 */ 157 public function dump() 158 { 159 $return = ''; 160 161 if ($this->value) { 162 $return .= str_pad('', $this->getDepth() * 4, ' '); 163 $return .= join(', ', (array)$this->value->getDisplayValue()); 164 $return .= "\n"; 165 } else { 166 $return .= "*\n"; 167 } 168 169 foreach ($this->getResultRows() as $row) { 170 $return .= str_pad('', $this->getDepth() * 4, ' '); 171 foreach ($row as $value) { 172 $return .= ' ' . join(', ', (array)$value->getDisplayValue()); 173 } 174 $return .= "\n"; 175 } 176 177 foreach ($this->getChildren() as $child) { 178 $return .= $child->dump(); 179 } 180 181 return $return; 182 } 183} 184