You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
3.5 KiB
212 lines
3.5 KiB
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Util;
|
|
|
|
/**
|
|
* Represents a node in a tree.
|
|
*
|
|
* @author Adam Pippin <hello@adampippin.ca>
|
|
*/
|
|
class GraphNode
|
|
{
|
|
/**
|
|
* Parent of this node.
|
|
* @var GraphNode
|
|
*/
|
|
protected $parent;
|
|
|
|
/**
|
|
* Value of this node.
|
|
* @var mixed
|
|
*/
|
|
protected $value;
|
|
|
|
/**
|
|
* Children of this node.
|
|
* @var GraphNode[]
|
|
*/
|
|
protected $children;
|
|
|
|
/**
|
|
* Create a new graph node.
|
|
*
|
|
* @param mixed $value
|
|
*/
|
|
public function __construct($value = null)
|
|
{
|
|
$this->value = $value;
|
|
$this->children = [];
|
|
}
|
|
|
|
/**
|
|
* Set the value of this node.
|
|
*
|
|
* @param mixed $value
|
|
* @return void
|
|
*/
|
|
public function setValue($value): void
|
|
{
|
|
$this->value = $value;
|
|
}
|
|
|
|
/**
|
|
* Get the value of this node.
|
|
*
|
|
* @return mixed
|
|
*/
|
|
public function getValue()
|
|
{
|
|
return $this->value;
|
|
}
|
|
|
|
/**
|
|
* Set the parent node of this node.
|
|
*
|
|
* @param GraphNode $node
|
|
* @return void
|
|
*/
|
|
public function setParent(GraphNode $node): void
|
|
{
|
|
$this->parent = $node;
|
|
}
|
|
|
|
/**
|
|
* Determine whether this node has a parent.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function hasParent(): bool
|
|
{
|
|
return isset($this->parent);
|
|
}
|
|
|
|
/**
|
|
* Get this node's parent node.
|
|
*
|
|
* @return GraphNode
|
|
*/
|
|
public function getParent(): GraphNode
|
|
{
|
|
return $this->parent;
|
|
}
|
|
|
|
/**
|
|
* Count how many child nodes this node has.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function countChildren(): int
|
|
{
|
|
return sizeof($this->children);
|
|
}
|
|
|
|
/**
|
|
* Determine whether this node has any children.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function hasChildren(): bool
|
|
{
|
|
return sizeof($this->children) > 0;
|
|
}
|
|
|
|
/**
|
|
* Fetch all of this node's children.
|
|
*
|
|
* @return GraphNode[]
|
|
*/
|
|
public function getChildren(): array
|
|
{
|
|
return $this->children;
|
|
}
|
|
|
|
/**
|
|
* Add a node to this child's list of children.
|
|
*
|
|
* @param GraphNode $child
|
|
* @return void
|
|
*/
|
|
public function appendChild(GraphNode $child): void
|
|
{
|
|
$this->children[] = $child;
|
|
$child->setParent($this);
|
|
}
|
|
|
|
/**
|
|
* Replace a child node with another node.
|
|
*
|
|
* @param GraphNode $original
|
|
* @param GraphNode $new
|
|
* @return void
|
|
*/
|
|
public function replaceChild(GraphNode $original, GraphNode $new): void
|
|
{
|
|
for ($i = 0; $i < sizeof($this->children); $i++)
|
|
{
|
|
if ($this->children[$i] === $original)
|
|
{
|
|
$this->children[$i] = $new;
|
|
$new->setParent($this);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove all children of this node.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function clearChildren(): void
|
|
{
|
|
$this->children = [];
|
|
}
|
|
|
|
/**
|
|
* Add a child node by value.
|
|
*
|
|
* @param mixed $value value to add as a child
|
|
* @return GraphNode the node created as a child
|
|
*/
|
|
public function add($value): GraphNode
|
|
{
|
|
$this->children[] = new GraphNode($value);
|
|
end($this->children)->setParent($this);
|
|
return end($this->children);
|
|
}
|
|
|
|
/**
|
|
* Walk through this node and all children, calling callback on each node.
|
|
*
|
|
* Callback is called on a node's children before a node
|
|
*
|
|
* @param callable $callback callback(GraphNode $node): void
|
|
* @return void
|
|
*/
|
|
public function walk(callable $callback)
|
|
{
|
|
static::walkNodes([$this], $callback);
|
|
}
|
|
|
|
/**
|
|
* Internal function for recursively visiting all nodes.
|
|
*
|
|
* @param GraphNode[] $nodes
|
|
* @param callable $callback
|
|
* @return void
|
|
*/
|
|
protected static function walkNodes(array $nodes, callable $callback)
|
|
{
|
|
foreach ($nodes as $node)
|
|
{
|
|
if ($node->hasChildren())
|
|
{
|
|
static::walkNodes($node->getChildren(), $callback);
|
|
}
|
|
|
|
$callback($node);
|
|
}
|
|
}
|
|
}
|
|
|