From 6009cfadc2e310b723a8c091cd9d94db968fe07e Mon Sep 17 00:00:00 2001 From: Hamish Friedlander Date: Mon, 27 Aug 2012 16:08:10 +1200 Subject: [PATCH] NEW Allow debugging of config cyclic errors It is possible to specify before and after rules on config fragments that conflict - A before B and B before A isnt possible to solve. This used to just throw an error with no way to debug. Now if you specify debug as a GET parameter and the site is not in live mode youll get a basic dump of the remaining DAG graph --- core/DAG.php | 65 +++++++++++++++++++++++++++++++- core/manifest/ConfigManifest.php | 25 +++++++++++- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/core/DAG.php b/core/DAG.php index 9988af21a..c2acc7f31 100644 --- a/core/DAG.php +++ b/core/DAG.php @@ -4,7 +4,7 @@ * A Directed Acyclic Graph - used for doing topological sorts on dependencies, such as the before/after conditions * in config yaml fragments */ -class SS_DAG { +class SS_DAG implements IteratorAggregate { /** @var array|null - The nodes/vertices in the graph. Should be a numeric sequence of items (no string keys, no gaps). */ protected $data; @@ -68,7 +68,68 @@ class SS_DAG { $dag = $withedges; } - if ($dag) throw new Exception("DAG has cyclic requirements"); + if ($dag) { + $remainder = new SS_DAG($data); $remainder->dag = $dag; + throw new SS_DAG_CyclicException("DAG has cyclic requirements", $remainder); + } return $sorted; } + + function getIterator() { + return new SS_DAG_Iterator($this->data, $this->dag); + } +} + +class SS_DAG_CyclicException extends Exception { + + public $dag; + + function __construct($message, $dag) { + $this->dag = $dag; + parent::__construct($message); + } + +} + +class SS_DAG_Iterator implements Iterator { + + protected $data; + protected $dag; + + protected $dagkeys; + protected $i; + + function __construct($data, $dag) { + $this->data = $data; + $this->dag = $dag; + $this->rewind(); + } + + function key() { + return $this->i; + } + + function current() { + $res = array(); + + $res['from'] = $this->data[$this->i]; + + $res['to'] = array(); + foreach ($this->dag[$this->i] as $to) $res['to'][] = $this->data[$to]; + + return $res; + } + + function next() { + $this->i = array_shift($this->dagkeys); + } + + function rewind() { + $this->dagkeys = array_keys($this->dag); + $this->next(); + } + + function valid() { + return $this->i !== null; + } } diff --git a/core/manifest/ConfigManifest.php b/core/manifest/ConfigManifest.php index 75c35eaee..076bc98e2 100644 --- a/core/manifest/ConfigManifest.php +++ b/core/manifest/ConfigManifest.php @@ -268,7 +268,30 @@ class SS_ConfigManifest { } } - $this->yamlConfigFragments = $dag->sort(); + try { + $this->yamlConfigFragments = $dag->sort(); + } + catch (SS_DAG_CyclicException $e) { + + if (!Director::isLive() && isset($_REQUEST['debug'])) { + $res = '

Remaining config fragment graph

'; + $res .= '
'; + + foreach ($e->dag as $node) { + $res .= "
{$node['from']['module']}/{$node['from']['file']}#{$node['from']['name']} marked to come after
    "; + foreach ($node['to'] as $to) { + $res .= "
  • {$to['module']}/{$to['file']}#{$to['name']}
  • "; + } + $res .= "
"; + } + + $res .= '
'; + echo $res; + } + + throw $e; + } + } /**