Entrei rapidinho pra postar um bebê meu, que foi criado para ser extendido em inúmeros outros módulos.
Trata-se de uma estrutura de árvore genérica, com suporte a comentários e talz.
A intenção é extender a classe, e não utilizá-la de qualquer lado.
Finalizei uma extensão dela também, mas não vou publicá-la ainda... não foi registrada ainda!
Trata-se de uma TAD (Tipo Abstrato de Dados) Árvore, genérico, com suporte a qualquer quantidade de nós filhos, e folhas...
Criei uma rápida documentação sobre ela usando o PHP Documentor 1.3 RC 3... que eu tenho instalado em casa.
AHHHHHH.... esqueci de comentar que ela só suporta PHP5+
A um certo tempo só tenho trabalho com a versão 5... MUITO MAIS ESTÁVEL que a 4... não tem nem comparação!!!!
Os sources vou colocar aqui... mas são BEM extensos!!! Sugiro que peguem o ZIP no final do post.
Grato,
Arquivo: class.pTree.php
<?php
/**
* pTree - Generic Tree Implementation
*
* This class provides a Tree Data Structure implementation
*
* @package pTree
* @created 2005-02-20 01:04 GMT - 3:00hs
* @updated 2005-02-20 20:28 GMT - 3:00hs
*
* @author Guilherme Blanco <guilhermeblanco@gmail.com>
* @copyright Copyright® 2005, PontUKom Corp
*
* @version 1.0 Corrected isRoot, hasChilds, hasValue
* Avoid removed nodes wrong parsing
* @version 0.9 First public release
*
* @access protected
*/
/**
* @global boolean Tree Data Structure Loaded
*/
if (!defined("ADT_TREE"))
define("ADT_TREE", 1);
class pTree extends pTreeNode
{
/**
* @var string $separator Tree Path Separator
* @access protected
*/
protected $separator = "/";
/**
* Constructor - Generates a Tree Data Structure
*
* @access public
* @param string $treeSeparator Path Char Separator
* @param string $rootName Name of Root
* @return object $this Tree
*/
public function __construct($treeSeparator = "/", $rootName = "root")
{
parent::__construct($rootName);
$this->separator = $treeSeparator;
}
/**
* Add - Insert OR Update Tree Node with Value
*
* @access public
* @param mixed $node Node Path or Object
* @param mixed $value Node Value
* @return object $treeNode Tree Node Created OR Updated
*/
public function add($node, $value = null)
{
// Grabbing Node
$treeNode = $this->getChild($node);
// Retrieving basename
$nodeName = $this->getLastName($node);
// Checking possible node update
if ($treeNode->getName() == $nodeName)
// Node update
$treeNode->setValue($value);
else
// Creating and returning Node
$treeNode = $treeNode->addChild($nodeName, $value);
// Returning Node
return $treeNode;
}
/**
* Remove - Remove Tree Node and All Sub-Childs
*
* @access public
* @param mixed $node Node Name or Object
*/
public function remove($node)
{
// Grabbing Node
$treeNode = $this->getChild($node);
// Retrieving Parent Node
$parentNode = $treeNode->getParent();
// Removing Node
$parentNode->removeChild($treeNode);
}
/**
* Get - Retrieves Node Value
*
* @access public
* @param mixed $node Node Path or Object
* @return mixed $nodeValue Tree Node Value
*/
public function get($node)
{
// Grabbing Node
$treeNode = $this->getChild($node);
// Retrieving Node Value
return $treeNode->getValue();
}
/**
* Comment - Insert a Tree Node Comment
*
* @access public
* @param mixed $node Node Name or Object
* @param string $comment Node Comment
*/
public function comment($node, $comment)
{
// Grabbing Node
$treeNode = $this->getChild($node);
// Inserting Node Comment
$treeNode->addComment($comment);
}
/**
* Get Child - Retrieve a Child Node of Tree
*
* @access protected
* @param string $nodeName Name of Child Node
* @return mixed $treeNode Tree Node
*/
protected function getChild($nodeName)
{
// Splitting Path in an array
$arr = explode($this->separator, trim(str_replace(" ", "_", $nodeName)));
$child_block = $this;
// Retrieving Node Inheritance
for ($i = 0; $i < count($arr); $i++)
{
if (array_key_exists($arr[$i], $child_block->getAllChilds()))
$child_block = $child_block->getChildNode($arr[$i]);
}
// Returning Node
return $child_block;
}
/**
* Get Root - Retrieve Root Node
*
* @access public
* @return object $this Root Node of Tree
*/
public function getRoot()
{
return $this;
}
/**
* Is Root - Checks if a given Tree Node is the Root of Tree
*
* @access public
* @param string $node Node Name
* @return boolean $boolReturn Returns TRUE is Tree Node is the Root of the Tree; FALSE instead
*/
public function isRoot($node)
{
// Grabbing Node
$possRoot = $this->getChild($node);
// Retrieving basename
$nodeName = $this->getLastName($node);
if ($possRoot->getName() === $nodeName && $possRoot->getParent() === $this)
return true;
else
return false;
}
/**
* Has Childs - Checks if a Tree Node has Childs or not
*
* @access public
* @param string $node Node Name
* @return boolean $boolReturn Returns TRUE is Tree Node has Childs and FALSE if not
*/
public function hasChilds($node)
{
// Getting Node
$treeNode = $this->getChild($node);
// Retrieving basename
$nodeName = $this->getLastName($node);
if ($treeNode->getName() === $nodeName && count($treeNode->getAllChilds()) > 0)
return true;
return false;
}
/**
* Has Value - Checks if a Tree Node has Value or not
*
* @access public
* @param string $node Node Name
* @return boolean $boolReturn Returns TRUE is Tree Node has Value; FALSE instead
*/
public function hasValue($node)
{
// Getting Node
$treeNode = $this->getChild($node);
// Retrieving basename
$nodeName = $this->getLastName($node);
if ($treeNode->getName() === $nodeName && $treeNode->getValue() != null)
return true;
return false;
}
/**
* To String - Creates a Human readable output of Tree Node
*
* @access public
* @param integer $mode Output Mode
* @return string $output Formatted readable output of Tree
*/
public function toString($mode = 0)
{
$str = "";
// Building formatted output of first level child Nodes
foreach ($this->childNodes as $block_name => $block)
$str .= $block->toString($mode);
return $str;
}
/**
* Get Last Name - Retrieves the basename of a given Node Path
*
* @access protected
* @param string $nodePath Tree Node Path
* @return string $nodeName Tree Node Basename (Node Name)
*/
protected function getLastName($nodePath)
{
return substr(trim(str_replace(" ", "_", $nodePath)), ((strrpos($nodePath, $this->separator) == false) ? 0 : strrpos($nodePath, $this->separator) + 1), strlen($nodePath));
}
};
/**
* pTreeNode - Generic Node for Tree Implementation
*
* This class provides a Tree Node definition for generic tree data structure implementation
*
* @package pTreeNode
* @created 2005-02-19 16:04 GMT - 3:00hs
* @updated No updates yet
*
* @author Guilherme Blanco <guilhermeblanco@gmail.com>
* @copyright Copyright® 2005, PontUKom Corp
*
* @version 1.0 First public release
*
* @access protected
*/
class pTreeNode
{
/**
* @var string $name Node Name
* @access protected
*/
protected $name = "";
/**
* @var string $value Node Value
* @access protected
*/
private $value = null;
/**
* @var object $parentNode Parent TreeNode Reference
* @access protected
*/
private $parentNode = null;
/**
* @var string $comment Node Comment
* @access protected
*/
private $comment = "";
/**
* @var array $childNodes Child Nodes
* @access protected
*/
protected $childNodes = array();
/**
* @var integer $depth Depth of Node
* @access protected
*/
private $depth = -1;
/**
* Constructor - Should not be called directly. Please use {@link addChild} instead.
*
* @access public
* @param string $nodeName Name of Tree Node
* @param object $parentNode Parent Tree Node reference, if any
* @return object $this Tree Node
* @see addChild
*/
public function __construct($nodeName, $parentNode = null)
{
$this->name = trim(str_replace(" ", "_", $nodeName));
// Checking level
if ($parentNode != null)
{
$this->parentNode = $parentNode;
$this->depth = $parentNode->depth + 1;
$parentNode->childNodes[$this->name] = $this;
}
}
/**
* Destructor - Should not be called directly. This does the same as {@link removeAllChilds} method.
*
* @access private
* @see removeAllChilds
*/
public function __destruct()
{
$this->removeAllChilds();
}
/**
* Get Child - Retrieve a Child Node of the given Tree Node
*
* @access protected
* @param string $nodeName Name of Child Node
* @return mixed $treeNode Tree Node
*/
protected function getChildNode($nodeName)
{
return ((array_key_exists($nodeName, $this->childNodes)) ? $this->childNodes[$nodeName] : null);
}
/**
* Get All Childs - Retrieve All Child Nodes of the given Tree Node
*
* @access protected
* @return array $arrayTreeNodes Tree Nodes array
*/
protected function getAllChilds()
{
return $this->childNodes;
}
/**
* Remove Child - Remove Child Node
*
* @access protected
* @param mixed $node Name of Child Node or Child Tree Node Object
* @return boolean $boolRemoved TRUE
* @see removeAllChilds
*/
protected function removeChild($node)
{
// Retrieve Node, if node is a string
$nodeItem = (!is_object($node)) ? $this->getChild($node) : $node;
// Remove sub-Childs of Child
$nodeItem->removeAllChilds();
// Removing from Parent Tree Node
$this->childNodes[$nodeItem->name] = null;
unset($this->childNodes[$nodeItem->name]);
unset($nodeItem);
return true;
}
/**
* Remove All Childs - Remove All Child Nodes
*
* @access protected
* @return boolean $boolRemoved TRUE
*/
protected function removeAllChilds()
{
// Removing childs
foreach ($this->childNodes as $key => $value)
{
$this->childNodes[$key]->removeAllChilds();
$this->childNodes[$key] = null;
unset($this->childNodes[$key]);
}
$this->childNodes = array();
$this->value = null;
}
/**
* Add - Add a Tree Node with Value
*
* @access protected
* @param string $node Node Name
* @param mixed $value Value of new Node
* @return object $generatedTreeNode Generated Tree Node
*/
protected function addChild($node, $value = null)
{
// Tree Node doesn't exist; create one
$nodeItem = new pTreeNode($node, $this);
// Set up a Value
$nodeItem->setValue($value);
// Return new Tree Node
return $nodeItem;
}
/**
* Get Parent - Retrieve the parent Tree Node of item, or item if none
*
* @access public
* @return object $treeNode Tree Node
*/
public function getParent()
{
return (($this->parentNode != null) ? $this->parentNode : $this);
}
/**
* Set Value - Set up a Value in Tree Node
*
* @access protected
* @param mixed $nodeValue Value of a Tree Node
*/
protected function setValue($nodeValue)
{
$this->value = $nodeValue;
}
/**
* Get Value - Get the Value of given Tree Node
*
* @access public
* @return mixed $nodeValue Value of Tree Node
*/
public function getValue()
{
return $this->value;
}
/**
* Get Depth - Grab Tree Node Depth
*
* @access public
* @return integer $nodeDepth Depth of Tree Node
*/
public function getDepth()
{
return $this->depth;
}
/**
* Get Name - Retrieve Tree Node Name
*
* @access public
* @return string $nodeName Name of Tree Node
*/
public function getName()
{
return $this->name;
}
/**
* Add Comment - Set up a Tree Node Comment
*
* @access protected
* @param string $nodeComment Comment of Tree Node
*/
protected function addComment($nodeComment = "")
{
$this->comment = $nodeComment;
}
/**
* To String - Creates a Human readable output of Tree Node (reverse of {@link fromString} method)
*
* @access public
* @param integer $mode Output Mode
* @return string $output Formatted readable output of Tree
* @see fromString
*/
public function toString($mode = 0)
{
// Checking Mode Variables
$creturn = (($mode === 0) ? "<br />" : "\r\n");
$blank = (($mode === 0) ? " " : " ");
$ident = str_repeat($blank, 4 * $this->depth);
$str = "";
// Building Node Comment
if ($this->comment != "")
$str .= $ident."#".$blank.str_replace(array("<br />", "<br>", "\r\n", "\n"), $creturn.$ident."#".$blank, $this->comment).$creturn;
// Create Node
$str .= $ident."[".$this->name." = ".$this->parseValue($this->value)."] {".((count($this->childNodes) > 0) ? $creturn : "");
// Creating inner Nodes
foreach ($this->childNodes as $block_name => $block)
$str .= $block->toString($mode);
// Finishing Node
$str .= ((count($this->childNodes) > 0) ? $ident : "")."}".$creturn;
// Returning...
return $str;
}
/**
* From String - Generates Tree Structure using Formatted String Entry (reverse of {@link toString} method)
*
* @access public
* @param string $input Formatted input of Tree
* @return object $this Tree
* @see toString
*/
public function fromString($input = "")
{
// No String, return
if ($input == "") return;
$curObj = $this;
$curComment = "";
// Wrap new lines
$lines = explode("\n", $input);
foreach ($lines as $line)
{
$line = trim(str_replace("\r", "", $line));
// Comment line
if (eregi('#(.*)', $line, $res))
$curComment .= trim($res[1])."\n";
// Node definition
elseif (eregi('\[(.[^=]*)\s*=\s*(.*)\]\s*(.*)', $line, $res))
{
$value = $this->parseValue($res[2], 1);
$curObj = $curObj->addChild(trim($res[1]), $value);
$curObj->addComment(substr($curComment, 0, strlen($curComment) - 1));
if (strstr($res[3], "}"))
$curObj = $curObj->getParent();
$curComment = "";
}
// Bracket close
elseif (substr($line, 0, 1) == "}")
$curObj = $curObj->getParent();
unset($res);
unset($line);
}
// Checking error
if ($curObj !== $this)
trigger_error("<b>Tree:</b> Missing brackets in Node [".$curObj->getName()."]", E_USER_ERROR);
}
/**
* Parse Value - Encode/Decode Value parameter
*
* @access private
* @param mixed $nodeValue Value of Node (encoded or decoded)
* @param integer $type Type of processment: [0] Encode / [1] Decode
* @return mixed $nodeValue Value of Node
*/
private function parseValue($nodeValue, $type = 0)
{
// Encode Type (Called with type argument set as 0 [default])
if ($type == 0)
{
// Array
if (is_array($nodeValue))
{
$newNodeValue = "(";
foreach ($nodeValue as $key => $value)
$newNodeValue .= ((is_numeric($key)) ? $key : "\"".$key."\"")." => ".((is_numeric($value)) ? $value : "\"".$value."\"").", ";
$newNodeValue = substr($newNodeValue, 0, strlen($newNodeValue) - 2).")";
}
// Object
elseif (is_object($nodeValue))
$newNodeValue = serialize($nodeValue);
// String
elseif (!is_numeric($nodeValue))
$newNodeValue = "\"".$nodeValue."\"";
// Number
else
$newNodeValue = $nodeValue;
}
// Decode Type (Called with type argument set and different of 0)
else
{
$nodeValue = trim($nodeValue);
// Array
if (substr($nodeValue, 0, 1) == "(")
{
$newNodeValue = array();
eregi('\((.[^\)]*)\)', $nodeValue, $nodeValue);
$nodeValue= $nodeValue[1];
$itens = explode(",", $nodeValue);
for ($i = 0; $i < count($itens); $i++)
{
eregi("[\"]*([^\"=]+)[\"]* => [\"]*([^\"]*)[\"]*", $itens[$i], $res);
if (isset($res))
$newNodeValue[trim($res[1])] = $res[2];
}
}
// Object
elseif (substr($nodeValue, 0, 2) == "O:")
// Object serialized
$newNodeValue = unserialize($nodeValue);
// String
elseif (!is_numeric($nodeValue))
$newNodeValue = substr($nodeValue, 1, strlen($nodeValue) - 2);
// Number
else
$newNodeValue = (($nodeValue == "") ? null : $nodeValue);
}
return $newNodeValue;
}
};
?>
Arquivo: pTree_test.php
<?php
// Loading Abstract Data Type pTree
require_once "class.pTree.php";
// Creating tree
$tree = new pTree;
echo "<h1>Tree (Separator: \"/\")</h1>";
// Testing add with unlimited depth and value type
// add(nodeName [, nodeValue])
// PS.: Spaces are allowed, but converted to "_" char
$tree->add("child1");
$tree->add("child2", "value");
$tree->add("child3", array(1 => "value1", "item2" => 200));
$tree->add("child1/sub-child1", "valueTest");
$tree->add("child1/sub-child2", 200);
$tree->add("child1/sub-child3");
$tree->add("child4", "value");
$tree->add("child5");
$tree->add("child5/sub-child5.1");
$tree->add("child5/sub-child5.1/sub-child5.1.1");
$tree->add("child5/sub-child5.1/sub-child5.1.1/sub-child5.1.1.1");
$tree->add("child5/sub-child5.1/sub-child5.1.1/sub-child5.1.1.1/sub-child5.1.1.1.1", "Deep... but with value. =)");
// Test commenting a node (can be any node)
// comment(nodeName, nodeCommment)
// PS.: new line separator: \n
$tree->comment("child1/sub-child1", "Testing comments");
// Test retrieving a node
// get(nodeName)
echo "<b>Value of child2:</b> ".$tree->get("child2")."<br />";
echo "<b>Value of child1/sub-child2:</b> ".$tree->get("child1/sub-child2")."<br />";
// Converting tree into a Human readable text
echo "<h3>Displaying tree before removes</h3>";
echo "<pre>\n";
echo $tree->toString(1);
echo "</pre>";
// Test removing a node (can be any node). Sub-childs will be delete too
// remove(nodeName)
$tree->remove("child2");
$tree->remove("child5");
$tree->comment("child4", "Comments anywhere! =P");
// Retrieving content
$tree_content = $tree->toString(1);
// Displaying Tree after Node remove
echo "<h3>Displaying tree after removes</h3>";
echo "<pre>\n";
echo $tree_content;
echo "</pre>";
// Testing error handler (brackets test)
// Uncoment the next 2 lines to test
//$nTree = new pTree;
//$nTree->fromString(substr($tree_content, 0, strlen($tree_content) - 4));
// Reverse way... building new tree from a given text
$tree2 = new pTree(";");
$tree2->fromString($tree_content);
echo "<h1>Tree 2 (Separator: \";\")</h1>";
// Look at separator usage
// Updating content
$tree2->add("child1;sub-child3", "Now with value!");
// Commenting
$tree2->comment("child1;sub-child3", "Now I can put some comment here\nWith new line included!!!");
// Displaying tree2 node value, showing separator usage
echo "<b>Value of child1;sub-child3:</b> ".$tree2->get("child1;sub-child3")."<br />";
// Updating content
$tree2->add("child1;sub-child3", "Now with value!");
// Displaying tree
echo "<pre>\n";
echo $tree2->toString(1);
echo "</pre>";
// Testing other methods
echo "<h1>Testing Other Tools (using Tree 2)</h1>";
// isRoot(nodeName) => check if the node is root or not
echo "<b>child4</b> ".(($tree2->isRoot("child4")) ? "is" : "isn't")." root<br />";
// hasChilds(nodeName) => checks if nodeName has childNodes or not
echo "<b>child3</b> ".(($tree2->hasChilds("child3")) ? "has" : "hasn't")." childs<br />";
// hasValue(nodeName) => check if nodeName has a value or not
echo "<b>child1;sub-child1</b> ".(($tree2->hasValue("child1;sub-child1")) ? "has" : "hasn't")." value<br />";
// Displaying tree
echo "<h3>Displaying Object Tree 2 - Showing real pTree Structure</h3>";
echo "<pre>\n";
print_r($tree2);
echo "</pre>";
?>
Espero que gostem!!!











