265 lines
5.3 KiB
PHP
265 lines
5.3 KiB
PHP
|
<?php
|
||
|
|
||
|
class Graph
|
||
|
{
|
||
|
|
||
|
protected $initialList;
|
||
|
protected $open;
|
||
|
protected $closed;
|
||
|
public $nodes;
|
||
|
|
||
|
/**
|
||
|
* @throws ErrorException
|
||
|
*/
|
||
|
public function __construct(
|
||
|
$__initialList
|
||
|
)
|
||
|
{
|
||
|
$this->initialList = $__initialList;
|
||
|
$this->open = [];
|
||
|
/**
|
||
|
* Step 2. Create a list called CLOSED initially empty
|
||
|
*/
|
||
|
$this->closed = [];
|
||
|
$this->nodes = [];
|
||
|
/**
|
||
|
* Step 1. Create a Search graph
|
||
|
*/
|
||
|
$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];
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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->parent = $node;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected function pushToList(&$__list, $__nodes)
|
||
|
{
|
||
|
foreach ($__nodes as $node) {
|
||
|
array_push($__list, $node);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @throws Exception
|
||
|
*/
|
||
|
protected function processOpen()
|
||
|
{
|
||
|
/**
|
||
|
* 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);
|
||
|
|
||
|
/**
|
||
|
* Step 5. If $node is a GOAL node - we are done - we skip this step because we are done
|
||
|
* only when $this->initialList / $this->open is empty
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Step 6. Expand $node by generating a set M of successors that are not already ancestors of
|
||
|
* $node in G. Install them into G.
|
||
|
*/
|
||
|
$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, $successors);
|
||
|
|
||
|
/**
|
||
|
* Step 8. We skip this because we do not do any re-ordering on the graph
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Step 9. Go back to Step 3
|
||
|
*/
|
||
|
$this->processOpen();
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @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);
|
||
|
|
||
|
array_push($this->open, $startNode);
|
||
|
|
||
|
try {
|
||
|
/**
|
||
|
* Step 3. If OPEN is empty - throw Exception (we are actually done)
|
||
|
*/
|
||
|
$this->processOpen();
|
||
|
} catch (Exception $e) {
|
||
|
return $e->getMessage();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// public function createNode($file, $className, $extendClass = null) {
|
||
|
//
|
||
|
// $node = new Node($file, $className);
|
||
|
//
|
||
|
// if ($extendClass) {
|
||
|
// $parent = new Node('', $extendClass);
|
||
|
// array_push($node['parents'], $parent);
|
||
|
// }
|
||
|
//
|
||
|
// return $node;
|
||
|
// }
|
||
|
//
|
||
|
// /**
|
||
|
// * @throws ErrorException
|
||
|
// */
|
||
|
// public function addNode($file, $className, $extendClassName)
|
||
|
// {
|
||
|
// if ($className === null) {
|
||
|
// throw new ErrorException("classname cannot be null");
|
||
|
// }
|
||
|
//
|
||
|
// echo "size of graph is " . sizeof($this->nodes) . "\n";
|
||
|
//
|
||
|
// $node = $this->createNode($file, $className, $extendClassName);
|
||
|
//
|
||
|
// array_push($this->nodes, $node);
|
||
|
// }
|
||
|
//
|
||
|
// public function sort()
|
||
|
// {
|
||
|
//
|
||
|
// echo "sorting graph";
|
||
|
//
|
||
|
// $baseClasses = [];
|
||
|
//
|
||
|
// $extendClasses = [];
|
||
|
//
|
||
|
// foreach ($this->nodes as $node) {
|
||
|
//
|
||
|
// if ($node['name'] === 'R3') {
|
||
|
//
|
||
|
// // skip R3 - it is the main parent of everything
|
||
|
//
|
||
|
// array_push($newGraph, $node);
|
||
|
//
|
||
|
// continue;
|
||
|
// }
|
||
|
//
|
||
|
// if (sizeof($node['parents']) === 0) {
|
||
|
// array_push($baseClasses, $node);
|
||
|
// } else {
|
||
|
// array_push($extendClasses, $node);
|
||
|
// }
|
||
|
// }
|
||
|
//
|
||
|
// foreach ($baseClasses as $baseClass) {
|
||
|
// array_push($baseClass['parents'], $newGraph[0]);
|
||
|
// array_push($newGraph[0]['children'], $baseClass);
|
||
|
// }
|
||
|
//
|
||
|
// foreach ($extendClasses as $extendClass) {
|
||
|
//
|
||
|
// findRecursive($extendClass['name'], $newGraph);
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
// }
|
||
|
// }
|
||
|
}
|
||
|
|
||
|
class Node
|
||
|
{
|
||
|
public $file = '';
|
||
|
public $name = '';
|
||
|
public $parent = null;
|
||
|
public $children = null;
|
||
|
|
||
|
function __construct(
|
||
|
$__file,
|
||
|
$__name,
|
||
|
$__parent = null,
|
||
|
$__children = null
|
||
|
)
|
||
|
{
|
||
|
$this->file = $__file;
|
||
|
$this->name = $__name;
|
||
|
$this->parent = $__parent;
|
||
|
$this->children = $__children;
|
||
|
}
|
||
|
|
||
|
}
|