node.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <?php
  2. /**
  3. * Tree data structure, used for rendering purposes.
  4. *
  5. * @author Taddeus Kroes
  6. * @version 1.0
  7. * @date 13-07-2012
  8. */
  9. namespace BasicWeb;
  10. require_once 'base.php';
  11. /**
  12. * Tree node.
  13. *
  14. * Each tree node has a (non-unique) name, a list of variables, and zero or
  15. * more children.
  16. *
  17. * @package BasicWeb
  18. */
  19. class Node extends Base {
  20. /**
  21. * The number of Node instances, used to create unique node id's.
  22. *
  23. * @var int
  24. */
  25. private static $count = 0;
  26. /**
  27. * The unique id of this Bloc.
  28. *
  29. * @var int
  30. */
  31. private $id;
  32. /**
  33. * The node's name.
  34. *
  35. * @var string
  36. */
  37. private $name;
  38. /**
  39. * An optional parent node.
  40. *
  41. * If NULL, this node is the root of the data tree.
  42. *
  43. * @var Node
  44. */
  45. private $parent_node;
  46. /**
  47. * Child nodes.
  48. *
  49. * @var array
  50. */
  51. private $children = array();
  52. /**
  53. * Variables in this node.
  54. *
  55. * All variables in a node are also available in its descendants through
  56. * {@link get()}.
  57. *
  58. * @var array
  59. */
  60. private $variables = array();
  61. /**
  62. * Constructor.
  63. *
  64. * The id of the node is determined by the node counter.
  65. *
  66. * @param string $name The node's name.
  67. * @param Node|null &$parent_node A parent node (optional).
  68. * @param int|null $id An id to assign. If none is specified, a new unique
  69. * id is generated.
  70. * @uses $count
  71. */
  72. function __construct($name='', Node &$parent_node=null, $id=null) {
  73. $this->id = $id ? $id : ++self::$count;
  74. $this->name = $name;
  75. $this->parent_node = $parent_node;
  76. }
  77. /**
  78. * Get the node's unique id.
  79. *
  80. * @return int The node's id.
  81. */
  82. function get_id() {
  83. return $this->id;
  84. }
  85. /**
  86. * Get the node's name.
  87. *
  88. * @return string The node's name.
  89. */
  90. function get_name() {
  91. return $this->name;
  92. }
  93. /**
  94. * Get the node's parent.
  95. *
  96. * @return Node|null The parent node if any, NULL otherwise.
  97. */
  98. function get_parent() {
  99. return $this->parent_node;
  100. }
  101. /**
  102. * Get the node's children.
  103. *
  104. * @return array A list of child nodes.
  105. */
  106. function get_children() {
  107. return $this->children;
  108. }
  109. /**
  110. * Check if a node is the same instance or a copy of this node.
  111. *
  112. * @param Node $node The node to compare this node to.
  113. * @return bool Whether the nodes have the same unique id.
  114. */
  115. function is(Node $node) {
  116. return $node->get_id() == $this->id;
  117. }
  118. /**
  119. * Check if this node is the root node of the tree.
  120. *
  121. * A node is the root node if it has no parent.
  122. *
  123. * @return bool Whether this node is the root node.
  124. */
  125. function is_root() {
  126. return $this->parent_node === null;
  127. }
  128. /**
  129. * Check if this node is a leaf node of the tree.
  130. *
  131. * A node is a leaf if it has no children.
  132. *
  133. * @return bool Whether this node is a leaf node.
  134. */
  135. function is_leaf() {
  136. return !count($this->children);
  137. }
  138. /**
  139. * Add a child node.
  140. *
  141. * @param Node &$node The child node to add.
  142. * @param bool $set_parent Whether to set this node as the child's parent
  143. * (defaults to TRUE).
  144. */
  145. function add_child(Node &$node, $set_parent=true) {
  146. $this->children[] = $node;
  147. $set_parent && $node->set_parent($this);
  148. }
  149. /**
  150. * Add a child node.
  151. *
  152. * @param string $name The name of the node to add.
  153. * @param array $data Data to set in the created node (optional).
  154. * @return Node The created node.
  155. */
  156. function add($name, array $data=array()) {
  157. $node = new self($name, $this);
  158. $this->add_child($node, false);
  159. return $node->set($data);
  160. }
  161. /**
  162. * Remove a child node.
  163. *
  164. * @param Node &$child The node to remove.
  165. */
  166. function remove_child(Node &$child) {
  167. foreach( $this->children as $i => $node )
  168. $node->is($child) && array_splice($this->children, $i, 1);
  169. }
  170. /**
  171. * Remove this node from its parent.
  172. *
  173. * @throws \RuntimeException If the node has no parent.
  174. * @return Node This node.
  175. */
  176. function remove() {
  177. if( $this->is_root() )
  178. throw new \RuntimeException('Cannot remove the root node of a tree.');
  179. $this->parent_node->remove_child($this);
  180. foreach( $this->children as $child )
  181. $child->set_parent(null);
  182. return $this;
  183. }
  184. /**
  185. * Set the node's parent.
  186. *
  187. * Removes this node as child of the original parent, if a parent was
  188. * already set.
  189. *
  190. * @param Node|null $parent The parent node to set.
  191. * @return Node This node.
  192. */
  193. function set_parent($parent) {
  194. if( $this->parent_node !== null )
  195. $this->parent_node->remove_child($this);
  196. $this->parent_node = &$parent;
  197. return $this;
  198. }
  199. /**
  200. * Set the value of one or more variables in the node.
  201. *
  202. * @param string|array $name Either a single variable name, or a set of name/value pairs.
  203. * @param mixed $value The value of a single variable to set.
  204. * @return Node This node.
  205. */
  206. function set($name, $value=null) {
  207. if( is_array($name) ) {
  208. foreach( $name as $var => $val )
  209. $this->variables[$var] = $val;
  210. } else {
  211. $this->variables[$name] = $value;
  212. }
  213. return $this;
  214. }
  215. /**
  216. * Get the value of a variable.
  217. *
  218. * @param string $name The name of the variable to get the value of.
  219. * @return mixed The value of the variable if it exists, NULL otherwise.
  220. */
  221. function get($name) {
  222. // Variable inside this node?
  223. if( isset($this->variables[$name]) )
  224. return $this->variables[$name];
  225. // Variable in one of ancestors?
  226. if( $this->parent_node !== null )
  227. return $this->parent_node->get($name);
  228. // All nodes up to the tree's root node do not contain the variable
  229. return null;
  230. }
  231. /**
  232. * Set the value of a variable.
  233. *
  234. * This method provides a shortcut for {@link set()}.
  235. *
  236. * @param string $name The name of the variable to set the value of.
  237. * @param mixed $value The value to set.
  238. */
  239. function __set($name, $value) {
  240. $this->set($name, $value);
  241. }
  242. /**
  243. * Get the value of a variable.
  244. *
  245. * This method provides a shortcut for {@link get()}.
  246. *
  247. * @param string $name The name of the variable to get the value of.
  248. * @return mixed The value of the variable if it exists, NULL otherwise.
  249. */
  250. function __get($name) {
  251. return $this->get($name);
  252. }
  253. /**
  254. * Find all child nodes that have the specified name.
  255. *
  256. * @param string $name The name of the nodes to find.
  257. * @return array The positively matched nodes.
  258. */
  259. function find($name) {
  260. $has_name = function($child) use ($name) {
  261. return $child->get_name() == $name;
  262. };
  263. return array_values(array_filter($this->children, $has_name));
  264. }
  265. /**
  266. * Create a copy of this node.
  267. *
  268. * The copy will have the same list of children and variables. In case of
  269. * a 'deep copy', the list of children is also cloned recursively.
  270. *
  271. * @param bool $deep Whether to create a deep copy.
  272. * @return Node A copy of this node.
  273. */
  274. function copy($deep=false) {
  275. $copy = new self($this->name, $this->parent_node, $this->id);
  276. $copy->set($this->variables);
  277. foreach( $this->children as $child ) {
  278. if( $deep ) {
  279. $child_copy = $child->copy(true);
  280. $copy->add_child($child_copy);
  281. } else {
  282. $copy->add_child($child, false);
  283. }
  284. }
  285. return $copy;
  286. }
  287. }
  288. ?>