r3-v2/graph.php

286 lines
6.0 KiB
PHP

<?php
class Graph
{
protected $initialList;
protected $open;
protected $closed;
public $nodes;
public $searchNodes;
/**
* @throws ErrorException
*/
public function __construct(
$__initialList,
$__build = true
)
{
$this->initialList = $__initialList;
$this->open = null;
$this->closed = null;
$this->nodes = [];
$this->searchNodes = [];
/**
* Step 1. Create a Search graph
*/
if ($__build) {
$this->build();
}
}
/**
* @return Node
* @throws Exception
*/
protected function popFirst(&$__list)
{
/**
* Step 3. If OPEN is empty - throw error (we are actually done)
*/
if (sizeof($__list) === 0) {
throw new Exception("Done");
}
return array_splice($__list, 0, 1)[0];
}
/**
* @throws ErrorException
*/
protected function remove($__name, &$__list)
{
for ($i = 0; $i < sizeof($__list); $i++) {
if ($__list[$i]->name === $__name) {
return array_splice($__list, $i, 1)[0];
}
}
throw new ErrorException('Node not found in list : ' . $__name);
}
protected function removeAll($__parent, &$__list)
{
$nodes = [];
for ($i = sizeof($__list) - 1; $i >= 0; $i--) {
if ($__list[$i]->parent === $__parent) {
array_push($nodes, array_splice($__list, $i, 1)[0]);
}
}
return $nodes;
}
/**
* Step 6. Expand $node by generating a set M of successors that are not already ancestors of
* $node in G. Install them into G.
*/
protected function expand($node)
{
if ($node->name === 'R3') {
$children = $this->removeAll(null, $this->initialList);
} else {
$children = $this->removeAll($node->name, $this->initialList);
}
return $children;
}
protected function linkToParent($node)
{
foreach ($node->children as $child) {
$child->nameSpace = $node->nameSpace . $node->nameSpaceClassName . '.';
$child->parent = $node;
}
}
protected function pushToList(&$__list, $__nodes)
{
foreach ($__nodes as $node) {
array_push($__list, $node);
}
}
/**
* @throws Exception
*/
protected function processOpen($searchMode = false, $property = null, $value = null)
{
/**
* Step 3. If OPEN is empty - throw Exception (we are actually done)
*/
$node = $this->popFirst($this->open);
/**
* Step 4. Select the first node on OPEN - move it to CLOSED
*/
array_push($this->closed, $node);
// array_push($this->searchNodes, $node);
/**
* Step 5. If $node is a GOAL node - we are done - we skip this step for build because then we are done
* only when $this->open is empty
*/
if ($searchMode) {
if ($property === null) {
throw new ErrorException('Search mode cannot be true without a property specified');
}
if ($node->$property === $value) {
return $node;
}
}
/**
* Step 6. Expand $node by generating a set M of successors that are not already ancestors of
* $node in G. Install them into G.
*
* We skip 6. and 7. a) when in search mode, because we already have children linked to parents
*/
if (!$searchMode) {
$successors = $this->expand($node);
$node->children = $successors;
/**
* Step 7. a) Establish a link from each successor to its parent
*/
$this->linkToParent($node);
}
/**
* Step 7. b) Add all successors to OPEN
*/
$this->pushToList($this->open, $node->children);
/**
* Step 8. We skip this because we do not do any re-ordering on the graph
*/
/**
* Step 9. Go back to Step 3
*/
return $this->processOpen($searchMode, $property, $value);
}
/**
* @param $property
* @param $value
* @return Array | Node
* @throws ErrorException
*/
public function search($property, $value)
{
if (sizeof($this->nodes) <= 0) {
throw new ErrorException('This graph is not built yet');
}
/**
* Step 1. Create a Search graph containing only the start node - put it on a list called OPEN
*/
$startNode = $this->nodes[0];
$this->searchNodes = [];
array_push($this->searchNodes, $startNode);
$this->open = [];
array_push($this->open, $startNode);
/**
* Step 2. Create a list called CLOSED initially empty
*/
$this->closed = [];
try {
/**
* Step 3. If OPEN is empty - we could not find the node
*/
return $this->processOpen(true, $property, $value);
} catch (Exception $e) {
return $this->searchNodes;
}
}
/**
* @throws ErrorException
*/
protected function build()
{
if (sizeof($this->initialList) <= 0) {
throw new ErrorException('Bad initial list');
}
/**
* Step 1. Create a Search graph containing only the start node - put it on a list called OPEN
*/
$startNode = $this->remove("R3", $this->initialList);
array_push($this->nodes, $startNode);
$this->open = [];
array_push($this->open, $startNode);
/**
* Step 2. Create a list called CLOSED initially empty
*/
$this->closed = [];
try {
/**
* Step 3. If OPEN is empty - throw Exception (we are actually done)
*/
return $this->processOpen();
} catch (Exception $e) {
return $e->getMessage();
}
}
/**
* @return []
*/
public function walk()
{
try {
$this->search('name', 'all');
return $this->closed;
} catch (ErrorException $e) {
return [];
}
}
}
class Node
{
public $file = '';
public $name = '';
public $nameSpace = '';
public $parent = null;
public $children = null;
function __construct(
$__file,
$__name,
$__nameSpace = '',
$__nameSpaceClassName = '',
$__parent = null,
$__children = null
)
{
$this->file = $__file;
$this->name = $__name;
$this->nameSpace = $__nameSpace;
$this->nameSpaceClassName = $__nameSpaceClassName;
$this->parent = $__parent;
$this->children = $__children;
}
}