User:Woozle/tree.php

About
This implements a hierarchy ("acyclic directional graph", I think...) where each node can have zero or more children and zero or one parent.

This is the version where node names are only kept in the parent, which can make debugging difficult.

Code
<?php /* FILE: tree.php -- general tree-handling classes HISTORY: 2010-10-13 Extricated from shop.php 2011-12-18 added access counter ($intRefs) class clsTreeNode { public $vVal;	// data value protected $arSubs;	// sub-nodes protected $objParent; protected $intRefs;

/*     ACTION: Creates node with a set of subnodes USED BY: clsShopCart::AddrShipObj etc.     DEPRECATED: - difficult to search for - blocks other possible initializations (PHP has no function overloading) */   public function __construct($iNodes=NULL) { if (is_array($iNodes)) { $this->arSubs = $iNodes;

foreach ($iNodes as $name => $node) { $node->Parent($this); }	}	$this->objParent = NULL; }   public function Parent(clsTreeNode $iNode=NULL) { if (!is_null($iNode)) { $this->objParent = $iNode; }	return $this->objParent; }   public function HasParent { return (!is_null($this->objParent)); }   protected function NodeAdd($iName,clsTreeNode $iNode) { $this->arSubs[$iName] = $iNode; $iNode->Parent($this); }   public function Node($iName,clsTreeNode $iNode=NULL) { if (!is_null($iNode)) { $this->NodeAdd($iName,$iNode); }	if (isset($this->arSubs[$iName])) { return $this->arSubs[$iName]; } else { return NULL; }   }    public function Exists($iName) { if (isset($this->arSubs[$iName])) { return isset($this->arSubs[$iName]); } else { return FALSE; }   }    public function Loaded($iName=NULL) { if (is_null($iName)) { return isset($this->vVal); } else { if ($this->Exists($iName)) { return $this->Node($iName)->Loaded; } else { return FALSE; }	}   }    /*      RETURNS: TRUE if there's a value by that name that is loaded. NOTE: The value may exist, but if it's not loaded this will still be FALSE. Why do we do it that way? DOCUMENT!! Might not be the way to do it. */   public function Filled($iName=NULL) { if (is_null($iName)) { if (!$this->Loaded) { $this->vVal = $this->Value;	// is this in danger of being recursive? }	   return !empty($this->vVal); } else { if ($this->Loaded($iName)) { return $this->Node($iName)->Filled; } else { return FALSE; }	}   }    public function Spawn { $obj = new clsTreeNode; return $obj; }   public function Value($iVal=NULL) { if (!is_null($iVal)) { $this->vVal = $iVal; }	return $this->vVal; }   /*      FUTURE: For consistency, should probably be renamed NodeValue */   public function SubValue($iName,$iVal=NULL) { if (!is_null($iVal)) { $obj = $this->Spawn; $obj->Value($iVal); } else { $obj = NULL; }	return $this->Node($iName,$obj)->Value; }   public function Nodes($iNodes=NULL) { if (is_array($iNodes)) { $this->arSubs = $iNodes; }	return $this->arSubs; }   public function HasNodes { if (isset($this->arSubs)) { return (count($this->arSubs) > 0); } else { return FALSE; }   }    public function AccessCount { return $this->intRefs; }   public function IncCount { $this->intRefs++; }   public function ResetCount { $this->intRefs = 0; if ($this->HasNodes) { foreach ($this->Nodes as $name => $node) { $node->ResetCount; }	}   }    public function DumpHTML { $out = NULL; if ($this->intRefs > 0) { $out = '[x'.$this->intRefs.' ]'; }	$this->intRefs++; if ($this->HasNodes) { $out = ''; foreach ($this->Nodes as $name => $node) { $isAlias = ($node->Parent != $this); if ($isAlias) { $out .= ''; }		$strCls = get_class($node); $out .= ' ['.$strCls.'] '.$name.' = ['.$node->Value.']'; $out .= $node->DumpHTML; if ($isAlias) { $out .= ''; }		if (!$node->HasParent) { $out .= 'INTERNAL ERROR: parent not set'; }	   }	    $out .= ''; }	return $out; } }