I am learning about Marcus Boerger's Standard PHP Library (SPL).
I have implemented my own RecursiveIterator which, by inheritence, implements the Iterator
interface. It also implements Countable
.
I am confused by the current(), getChildren() and hasChildren methods. That are documented at: http://www.php.net/~helly/php/ext/spl/interfaceRecursiveIterator.html
If
-
current()
quote: 'Returns the current element', and -
getChildren()
returns, and I quote, 'the sub iterator for the current element'
If, as is the case for current()
, the current element is taken to mean the child of the current object.
Then, surely the documentation is specifying that getChildren(), in effect, returns the grandchildren of the node in question.
Hence confused.
<?php
/**
*@desc Represents a node within a hierarchy
*/
class RecursableCountableIterableNode implements RecursiveIterator, Countable
{
public $title;
private $_componentsArray;
private $_iteratorPosition;
/**
*@desc adds component
*/
public function addComponent(
RecursableCountableIterableNode $incomingNodeObj
)
{
foreach ( $this->_componentsArray as $componentNodeObj )
{
if ( $incomingNodeObj === $componentNodeObj )
{
//its is already in there
return;
}
}
//add to the next element of the numerically indexed array
$this->_componentsArray[] = $incomingNodeObj;
}
/**
* @desc RecursiveIterator Interface
*/
/**
* @desc Implements the RecursiveIterator Interface
* @return boolean - Whether or not the node at the current element
* has children.
*
* Note: This method does NOT count the children of this node,
* it counts the components of the node at the *current* element.
* There is a subtle but important difference.
* It could have been better to name
* the interface method 'hasGrandChildren()'.
*/
public function hasChildren()
{
return ( boolean ) count( $this->current() );
}
/**
* @desc Gets the node of the current element which in effect is a container
* for childnodes.
*
* Note: According to the SPL, it does NOT get 'the child elements of
* the current node' ($this->_componentsArray) which was a surprise to me.
*
* @return RecursableCountableIterableNode - the
* sub iterator for the current element
*
*/
public function getChildren()
{
return $this->current();
}
/**
* @desc To adhere to countable interface.
* @returns integer - The number of elements in the compondents array.
*/
public function count()
{
return count( $this->_componentsArray );
}
/**
* Iterator methods
*/
/**
* @desc Rewind the iterator to the first element.
* @return void
*/
public function rewind()
{
$this->_iteratorPosition = 0;
}
/**
* @desc Return the current element.
* @return RecursableCountableIterableNode
*/
public function current()
{
return $this->_componentsArray[ $this->_iteratorPosition ];
}
/**
* @desc Return the key of the current element.
* @return integer
*/
public function key()
{
return $this->_iteratorPosition;
}
/**
* @desc Move forward to the next element.
* @return void
*/
public function next()
{
++$this->_iteratorPosition;
}
/**
* @desc Checks if current position has an element
* @return boolean
*/
public function valid()
{
return isset( $this->_componentsArray[ $this->_iteratorPosition ] );
}
}
In the class above, getChildren()
returns an object that implements RecursiveIterator and Countable. Because each RecursableCountableIterableNode
object holds instances of other RecursableCountableIterableNode
objects. I think its a form of Composite pattern.
Through experimentation I have managed to perform a recursive operation on the tree by using count()
(as a terminal condition to exit the recursive process) and foreach
to iterate over each node's children.
What's interesting is that, in effect, the count
feature implicity does a hasChildren
operation and the foreach
construct is implicitly doing a getChildren
operation in order to perform recursive traversal.
class NodeTreeProcessor
{
protected $output = '';
public function doProcessingWithNode(
RecursableCountableIterableNode $treeNodeObj
)
{
$this->output .= $treeNodeObj->title;
//Base case that stops the recursion.
if (!( count( $treeNodeObj ) > 0 ))
{
//it has no children
return;
}
//Recursive case.
foreach( $treeNodeObj as $childNode )
{
$this->doProcessingWithNode( $childNode );
}
}
}
Given that, I am thinking that in order to be a practical RecursiveIterator,
-
getChildren
really ought to return$this
instead of the node atcurrent()
, and -
hasChildren
really ought to return the boolean cast result ofcount($this)
is this right?
The specs say one thing - which I take literally. But my practical experience says another.