Browse Source

Add super rudimentary dependency graph solver

master
Adam Pippin 3 years ago
parent
commit
be9857cd2f
  1. 123
      app/Util/DependencyGraph.php

123
app/Util/DependencyGraph.php

@ -0,0 +1,123 @@
<?php
declare(strict_types=1);
namespace App\Util;
class DependencyGraph
{
/** @var DependencyGraphNode[] */
protected $nodes;
public function __construct()
{
$this->nodes = [];
}
public function add(string $name, array $depends_on = []): void
{
if (!isset($this->nodes[$name]))
{
$node = new DependencyGraphNode($name);
$this->nodes[$name] = $node;
$node->DependsOn = $depends_on;
}
else
{
// Probably unnecessary -- if the same node is added multiple times
// with different dependencies something's probably way messed up,
// but... we'll try our best?!
$this->nodes[$name] = array_unique(array_merge($this->nodes[$name]->DependsOn, $depends_on));
}
}
public function link(): void
{
foreach ($this->nodes as $node)
{
foreach ($node->DependsOn as $depends_on)
{
if (!isset($this->nodes[$depends_on]))
{
throw new \Exception('Unmet dependency on node: '.$depends_on);
}
if (!in_array($node->Name, $this->nodes[$depends_on]->DependedOnBy))
{
$this->nodes[$depends_on]->DependedOnBy[] = $node->Name;
}
}
}
}
public function flatten(): array
{
$state = [];
// Find leaf nodes, add each one to our state array.
foreach ($this->nodes as $node)
{
if (sizeof($node->DependedOnBy) == 0)
{
$state[] = $node;
}
}
// Then walk through the array and insert each node's dependencies before
// it recursively.
$iter = 0;
for ($i = 0; $i < sizeof($state); $i++)
{
if ($state[$i]->Processed)
{
continue;
}
$state[$i]->Processed = true;
$depends_on = [];
foreach ($state[$i]->DependsOn as $depends_on_name)
{
$depends_on[] = $this->nodes[$depends_on_name];
}
array_splice($state, $i, 0, $depends_on);
--$i;
}
// Remove all but the first appearance of a node.
// array_unique guarantees that the _first_ element will be retained,
// so this satisfies what we're trying to do.
return array_unique(array_map(static function($node) { return $node->Name; }, $state));
}
}
class DependencyGraphNode
{
/** @var string */
public $Name;
/** @var string */
public $DependsOn;
/** @var string */
public $DependedOnBy;
/** @var bool */
public $Processed;
public function __construct(string $name)
{
$this->Name = $name;
$this->DependsOn = [];
$this->DependedOnBy = [];
$this->Processed = false;
}
public function dependsOn(string $node): void
{
$this->DependsOn[] = $node;
}
public function dependedOnBy(string $node): void
{
$this->DependedOnBy[] = $node;
}
}
Loading…
Cancel
Save