encode = $encode; $this->tag->setEncoding($encode); // check children foreach ($this->children as $id => $child) { /** @var AbstractNode $node */ $node = $child['node']; $node->propagateEncoding($encode); } } /** * Checks if this node has children. * * @return bool */ public function hasChildren() { return ! empty($this->children); } /** * Returns the child by id. * * @param int $id * @return AbstractNode * @throws ChildNotFoundException */ public function getChild($id) { if ( ! isset($this->children[$id])) { throw new ChildNotFoundException("Child '$id' not found in this node."); } return $this->children[$id]['node']; } /** * Returns a new array of child nodes * * @return array */ public function getChildren() { $nodes = array(); try { $child = $this->firstChild(); do { $nodes[] = $child; $child = $this->nextChild($child->id()); } while ( ! is_null($child)); } catch (ChildNotFoundException $e) { // we are done looking for children } return $nodes; } /** * Counts children * * @return int */ public function countChildren() { return count($this->children); } /** * Adds a child node to this node and returns the id of the child for this * parent. * * @param AbstractNode $child * @return bool * @throws CircularException */ public function addChild(AbstractNode $child) { $key = null; // check integrity if ($this->isAncestor($child->id())) { throw new CircularException('Can not add child. It is my ancestor.'); } // check if child is itself if ($child->id() == $this->id) { throw new CircularException('Can not set itself as a child.'); } if ($this->hasChildren()) { if (isset($this->children[$child->id()])) { // we already have this child return false; } $sibling = $this->lastChild(); $key = $sibling->id(); $this->children[$key]['next'] = $child->id(); } // add the child $this->children[$child->id()] = array( 'node' => $child, 'next' => null, 'prev' => $key, ); // tell child I am the new parent $child->setParent($this); //clear any cache $this->clear(); return true; } /** * Removes the child by id. * * @param int $id * @return $this */ public function removeChild($id) { if ( ! isset($this->children[$id])) { return $this; } // handle moving next and previous assignments. $next = $this->children[$id]['next']; $prev = $this->children[$id]['prev']; if ( ! is_null($next)) { $this->children[$next]['prev'] = $prev; } if ( ! is_null($prev)) { $this->children[$prev]['next'] = $next; } // remove the child unset($this->children[$id]); //clear any cache $this->clear(); return $this; } /** * Attempts to get the next child. * * @param int $id * @return AbstractNode * @uses $this->getChild() * @throws ChildNotFoundException */ public function nextChild($id) { $child = $this->getChild($id); $next = $this->children[$child->id()]['next']; return $this->getChild($next); } /** * Attempts to get the previous child. * * @param int $id * @return AbstractNode * @uses $this->getChild() * @throws ChildNotFoundException */ public function previousChild($id) { $child = $this->getchild($id); $next = $this->children[$child->id()]['prev']; return $this->getChild($next); } /** * Checks if the given node id is a child of the * current node. * * @param int $id * @return bool */ public function isChild($id) { foreach ($this->children as $childId => $child) { if ($id == $childId) { return true; } } return false; } /** * Removes the child with id $childId and replace it with the new child * $newChild. * * @param int $childId * @param AbstractNode $newChild * @throws ChildNotFoundException */ public function replaceChild($childId, AbstractNode $newChild) { $oldChild = $this->children[$childId]; $newChild->prev = $oldChild['prev']; $newChild->next = $oldChild['next']; $keys = array_keys($this->children); $index = array_search($childId, $keys, true); $keys[$index] = $newChild->id(); $this->children = array_combine($keys, $this->children); $this->children[$newChild->id()] = array( 'prev' => $oldChild['prev'], 'node' => $newChild, 'next' => $oldChild['next'] ); if ($oldChild['prev'] && isset($this->children[$newChild->prev])) { $this->children[$oldChild['prev']]['next'] = $newChild->id(); } if ($oldChild['next'] && isset($this->children[$newChild->next])) { $this->children[$oldChild['next']]['prev'] = $newChild->id(); } } /** * Shortcut to return the first child. * * @return AbstractNode * @uses $this->getChild() */ public function firstChild() { reset($this->children); $key = key($this->children); return $this->getChild($key); } /** * Attempts to get the last child. * * @return AbstractNode */ public function lastChild() { end($this->children); $key = key($this->children); return $this->getChild($key); } /** * Checks if the given node id is a descendant of the * current node. * * @param int $id * @return bool */ public function isDescendant($id) { if ($this->isChild($id)) { return true; } foreach ($this->children as $childId => $child) { /** @var InnerNode $node */ $node = $child['node']; if ($node instanceof InnerNode && $node->hasChildren() && $node->isDescendant($id) ) { return true; } } return false; } /** * Sets the parent node. * * @param InnerNode $parent * @return $this * @throws CircularException */ public function setParent(InnerNode $parent) { // check integrity if ($this->isDescendant($parent->id())) { throw new CircularException('Can not add descendant "'.$parent->id().'" as my parent.'); } return parent::setParent($parent); } }