From 8ffe8b72307f87e9627472200433caa693adfd8d Mon Sep 17 00:00:00 2001
From: Bram Goffings <bramgoffings@gmail.com>
Date: Mon, 2 Apr 2012 10:49:14 +0200
Subject: [PATCH] graph

---
 core/includes/graph.inc                   |  145 ----------------------------
 core/includes/module.inc                  |    5 +-
 core/includes/update.inc                  |   25 +++---
 core/lib/Drupal/Component/Graph/Graph.php |  150 +++++++++++++++++++++++++++++
 core/modules/simpletest/tests/graph.test  |   17 ++--
 5 files changed, 173 insertions(+), 169 deletions(-)
 delete mode 100644 core/includes/graph.inc
 create mode 100644 core/lib/Drupal/Component/Graph/Graph.php

diff --git a/core/includes/graph.inc b/core/includes/graph.inc
deleted file mode 100644
index 9ef86a1..0000000
--- a/core/includes/graph.inc
+++ /dev/null
@@ -1,145 +0,0 @@
-<?php
-
-/**
- * @file
- * Directed acyclic graph functions.
- */
-
-
-/**
- * Performs a depth-first sort on a directed acyclic graph.
- *
- * @param $graph
- *   A three dimensional associated array, with the first keys being the names
- *   of the vertices, these can be strings or numbers. The second key is
- *   'edges' and the third one are again vertices, each such key representing
- *   an edge. Values of array elements are copied over.
- *
- *   Example:
- *   @code
- *     $graph[1]['edges'][2] = 1;
- *     $graph[2]['edges'][3] = 1;
- *     $graph[2]['edges'][4] = 1;
- *     $graph[3]['edges'][4] = 1;
- *   @endcode
- *
- *   On return you will also have:
- *   @code
- *     $graph[1]['paths'][2] = 1;
- *     $graph[1]['paths'][3] = 1;
- *     $graph[2]['reverse_paths'][1] = 1;
- *     $graph[3]['reverse_paths'][1] = 1;
- *   @endcode
- *
- * @return
- *   The passed-in $graph with more secondary keys filled in:
- *   - 'paths': Contains a list of vertices than can be reached on a path from
- *     this vertex.
- *   - 'reverse_paths': Contains a list of vertices that has a path from them
- *     to this vertex.
- *   - 'weight': If there is a path from a vertex to another then the weight of
- *     the latter is higher.
- *   - 'component': Vertices in the same component have the same component
- *     identifier.
- *
- * @see _drupal_depth_first_search()
- */
-function drupal_depth_first_search(&$graph) {
-  $state = array(
-    // The order of last visit of the depth first search. This is the reverse
-    // of the topological order if the graph is acyclic.
-    'last_visit_order' => array(),
-    // The components of the graph.
-    'components' => array(),
-  );
-  // Perform the actual sort.
-  foreach ($graph as $start => $data) {
-    _drupal_depth_first_search($graph, $state, $start);
-  }
-
-  // We do such a numbering that every component starts with 0. This is useful
-  // for module installs as we can install every 0 weighted module in one
-  // request, and then every 1 weighted etc.
-  $component_weights = array();
-
-  foreach ($state['last_visit_order'] as $vertex) {
-    $component = $graph[$vertex]['component'];
-    if (!isset($component_weights[$component])) {
-      $component_weights[$component] = 0;
-    }
-    $graph[$vertex]['weight'] = $component_weights[$component]--;
-  }
-}
-
-/**
- * Performs a depth-first sort on a graph.
- *
- * @param $graph
- *   A three dimensional associated graph array.
- * @param $state
- *   An associative array. The key 'last_visit_order' stores a list of the
- *   vertices visited. The key components stores list of vertices belonging
- *   to the same the component.
- * @param $start
- *   An arbitrary vertex where we started traversing the graph.
- * @param $component
- *   The component of the last vertex.
- *
- * @see drupal_depth_first_search()
- */
-function _drupal_depth_first_search(&$graph, &$state, $start, &$component = NULL) {
-  // Assign new component for each new vertex, i.e. when not called recursively.
-  if (!isset($component)) {
-    $component = $start;
-  }
-  // Nothing to do, if we already visited this vertex.
-  if (isset($graph[$start]['paths'])) {
-    return;
-  }
-  // Mark $start as visited.
-  $graph[$start]['paths'] = array();
-
-  // Assign $start to the current component.
-  $graph[$start]['component'] = $component;
-  $state['components'][$component][] = $start;
-
-  // Visit edges of $start.
-  if (isset($graph[$start]['edges'])) {
-    foreach ($graph[$start]['edges'] as $end => $v) {
-      // Mark that $start can reach $end.
-      $graph[$start]['paths'][$end] = $v;
-
-      if (isset($graph[$end]['component']) && $component != $graph[$end]['component']) {
-        // This vertex already has a component, use that from now on and
-        // reassign all the previously explored vertices.
-        $new_component = $graph[$end]['component'];
-        foreach ($state['components'][$component] as $vertex) {
-          $graph[$vertex]['component'] = $new_component;
-          $state['components'][$new_component][] = $vertex;
-        }
-        unset($state['components'][$component]);
-        $component = $new_component;
-      }
-      // Only visit existing vertices.
-      if (isset($graph[$end])) {
-        // Visit the connected vertex.
-        _drupal_depth_first_search($graph, $state, $end, $component);
-
-        // All vertices reachable by $end are also reachable by $start.
-        $graph[$start]['paths'] += $graph[$end]['paths'];
-      }
-    }
-  }
-
-  // Now that any other subgraph has been explored, add $start to all reverse
-  // paths.
-  foreach ($graph[$start]['paths'] as $end => $v) {
-    if (isset($graph[$end])) {
-      $graph[$end]['reverse_paths'][$start] = $v;
-    }
-  }
-
-  // Record the order of the last visit. This is the reverse of the
-  // topological order if the graph is acyclic.
-  $state['last_visit_order'][] = $start;
-}
diff --git a/core/includes/module.inc b/core/includes/module.inc
index df9c138..3628855 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -5,6 +5,8 @@
  * API for loading and interacting with Drupal modules.
  */
 
+use Drupal\Component\Graph\Graph;
+
 /**
  * Load all the modules that have been enabled in the system table.
  *
@@ -240,7 +242,6 @@ function system_list_reset() {
  *     without this module.
  */
 function _module_build_dependencies($files) {
-  require_once DRUPAL_ROOT . '/core/includes/graph.inc';
   foreach ($files as $filename => $file) {
     $graph[$file->name]['edges'] = array();
     if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
@@ -250,7 +251,7 @@ function _module_build_dependencies($files) {
       }
     }
   }
-  drupal_depth_first_search($graph);
+  Graph::search($graph);
   foreach ($graph as $module => $data) {
     $files[$module]->required_by = isset($data['reverse_paths']) ? $data['reverse_paths'] : array();
     $files[$module]->requires = isset($data['paths']) ? $data['paths'] : array();
diff --git a/core/includes/update.inc b/core/includes/update.inc
index 1660d7d..a79d853 100644
--- a/core/includes/update.inc
+++ b/core/includes/update.inc
@@ -8,6 +8,8 @@
  * installation. It is included and used extensively by update.php.
  */
 
+use Drupal\Component\Graph\Graph;
+
 /**
  * Minimum schema version of Drupal 7 required for upgrade to Drupal 8.
  *
@@ -562,7 +564,7 @@ function update_get_update_list() {
  *
  * In addition, the returned array also includes detailed information about the
  * dependency chain for each update, as provided by the depth-first search
- * algorithm in drupal_depth_first_search().
+ * algorithm in Drupal\Component\Graph\Graph::search().
  *
  * @param $starting_updates
  *   An array whose keys contain the names of modules with updates to be run
@@ -575,7 +577,7 @@ function update_get_update_list() {
  *   request, arranged in the order in which the update functions should be
  *   run. (This includes the provided starting update for each module and all
  *   subsequent updates that are available.) The values are themselves arrays
- *   containing all the keys provided by the drupal_depth_first_search()
+ *   containing all the keys provided by the Drupal\Component\Graph\Graph::search()
  *   algorithm, which encode detailed information about the dependency chain
  *   for this update function (for example: 'paths', 'reverse_paths', 'weight',
  *   and 'component'), as well as the following additional keys:
@@ -588,7 +590,7 @@ function update_get_update_list() {
  *   - 'module': The name of the module that this update function belongs to.
  *   - 'number': The number of this update function within that module.
  *
- * @see drupal_depth_first_search()
+ * @see Drupal\Component\Graph\Graph::search()
  */
 function update_resolve_dependencies($starting_updates) {
   // Obtain a dependency graph for the requested update functions.
@@ -596,8 +598,7 @@ function update_resolve_dependencies($starting_updates) {
   $graph = update_build_dependency_graph($update_functions);
 
   // Perform the depth-first search and sort the results.
-  require_once DRUPAL_ROOT . '/core/includes/graph.inc';
-  drupal_depth_first_search($graph);
+  Graph::search($graph);
   uasort($graph, 'drupal_sort_weight');
 
   foreach ($graph as $function => &$data) {
@@ -692,18 +693,18 @@ function update_get_update_function_list($starting_updates) {
  *
  * @return
  *   A multidimensional array representing the dependency graph, suitable for
- *   passing in to drupal_depth_first_search(), but with extra information
+ *   passing in to Drupal\Component\Graph\Graph::search(), but with extra information
  *   about each update function also included. Each array key contains the name
  *   of an update function, including all update functions from the provided
  *   list as well as any outside update functions which they directly depend
  *   on. Each value is an associative array containing the following keys:
  *   - 'edges': A representation of any other update functions that immediately
- *     depend on this one. See drupal_depth_first_search() for more details on
- *     the format.
+ *     depend on this one. See Drupal\Component\Graph\Graph::search() for more
+ *     details on the format.
  *   - 'module': The name of the module that this update function belongs to.
  *   - 'number': The number of this update function within that module.
  *
- * @see drupal_depth_first_search()
+ * @see Drupal\Component\Graph\Graph::search()
  * @see update_resolve_dependencies()
  */
 function update_build_dependency_graph($update_functions) {
@@ -851,7 +852,7 @@ function update_retrieve_dependencies() {
 
 /**
  * Updates config with values set on Drupal 7.x
- * 
+ *
  * Provide a generalised method to migrate variables from Drupal 7 to Drupal 8's
  * configuration management system.
  *
@@ -863,8 +864,8 @@ function update_retrieve_dependencies() {
  *   An array to map new to old configuration naming conventions. Example:
  *   @code
  *     array('new_config' => 'old_config')
- *   @endcode 
- *   This would update the value for new_config to the value old_config has in 
+ *   @endcode
+ *   This would update the value for new_config to the value old_config has in
  *   the variable table.
  */
 function update_variables_to_config($config_name, $variable_map = array()) {
diff --git a/core/lib/Drupal/Component/Graph/Graph.php b/core/lib/Drupal/Component/Graph/Graph.php
new file mode 100644
index 0000000..c486394
--- /dev/null
+++ b/core/lib/Drupal/Component/Graph/Graph.php
@@ -0,0 +1,150 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Component\Graph\Graph.
+ */
+
+namespace Drupal\Component\Graph;
+
+/**
+ * Directed acyclic graph functions.
+ */
+class Graph {
+
+  /**
+   * Performs a depth-first sort on a directed acyclic graph.
+   *
+   * @param $graph
+   *   A three dimensional associated array, with the first keys being the names
+   *   of the vertices, these can be strings or numbers. The second key is
+   *   'edges' and the third one are again vertices, each such key representing
+   *   an edge. Values of array elements are copied over.
+   *
+   *   Example:
+   *   @code
+   *     $graph[1]['edges'][2] = 1;
+   *     $graph[2]['edges'][3] = 1;
+   *     $graph[2]['edges'][4] = 1;
+   *     $graph[3]['edges'][4] = 1;
+   *   @endcode
+   *
+   *   On return you will also have:
+   *   @code
+   *     $graph[1]['paths'][2] = 1;
+   *     $graph[1]['paths'][3] = 1;
+   *     $graph[2]['reverse_paths'][1] = 1;
+   *     $graph[3]['reverse_paths'][1] = 1;
+   *   @endcode
+   *
+   * @return
+   *   The passed-in $graph with more secondary keys filled in:
+   *   - 'paths': Contains a list of vertices than can be reached on a path from
+   *     this vertex.
+   *   - 'reverse_paths': Contains a list of vertices that has a path from them
+   *     to this vertex.
+   *   - 'weight': If there is a path from a vertex to another then the weight of
+   *     the latter is higher.
+   *   - 'component': Vertices in the same component have the same component
+   *     identifier.
+   */
+  public static function search(&$graph) {
+    $state = array(
+      // The order of last visit of the depth first search. This is the reverse
+      // of the topological order if the graph is acyclic.
+      'last_visit_order' => array(),
+      // The components of the graph.
+      'components' => array(),
+    );
+    // Perform the actual sort.
+    foreach ($graph as $start => $data) {
+      self::sort($graph, $state, $start);
+    }
+
+    // We do such a numbering that every component starts with 0. This is useful
+    // for module installs as we can install every 0 weighted module in one
+    // request, and then every 1 weighted etc.
+    $component_weights = array();
+
+    foreach ($state['last_visit_order'] as $vertex) {
+      $component = $graph[$vertex]['component'];
+      if (!isset($component_weights[$component])) {
+        $component_weights[$component] = 0;
+      }
+      $graph[$vertex]['weight'] = $component_weights[$component]--;
+    }
+  }
+
+  /**
+   * Performs a depth-first sort on a graph.
+   *
+   * @param $graph
+   *   A three dimensional associated graph array.
+   * @param $state
+   *   An associative array. The key 'last_visit_order' stores a list of the
+   *   vertices visited. The key components stores list of vertices belonging
+   *   to the same the component.
+   * @param $start
+   *   An arbitrary vertex where we started traversing the graph.
+   * @param $component
+   *   The component of the last vertex.
+   *
+   * @see Drupal\Core\Utility\Graph::search()
+   */
+  protected static function sort(&$graph, &$state, $start, &$component = NULL) {
+    // Assign new component for each new vertex, i.e. when not called recursively.
+    if (!isset($component)) {
+      $component = $start;
+    }
+    // Nothing to do, if we already visited this vertex.
+    if (isset($graph[$start]['paths'])) {
+      return;
+    }
+    // Mark $start as visited.
+    $graph[$start]['paths'] = array();
+
+    // Assign $start to the current component.
+    $graph[$start]['component'] = $component;
+    $state['components'][$component][] = $start;
+
+    // Visit edges of $start.
+    if (isset($graph[$start]['edges'])) {
+      foreach ($graph[$start]['edges'] as $end => $v) {
+        // Mark that $start can reach $end.
+        $graph[$start]['paths'][$end] = $v;
+
+        if (isset($graph[$end]['component']) && $component != $graph[$end]['component']) {
+          // This vertex already has a component, use that from now on and
+          // reassign all the previously explored vertices.
+          $new_component = $graph[$end]['component'];
+          foreach ($state['components'][$component] as $vertex) {
+            $graph[$vertex]['component'] = $new_component;
+            $state['components'][$new_component][] = $vertex;
+          }
+          unset($state['components'][$component]);
+          $component = $new_component;
+        }
+        // Only visit existing vertices.
+        if (isset($graph[$end])) {
+          // Visit the connected vertex.
+          self::sort($graph, $state, $end, $component);
+
+          // All vertices reachable by $end are also reachable by $start.
+          $graph[$start]['paths'] += $graph[$end]['paths'];
+        }
+      }
+    }
+
+    // Now that any other subgraph has been explored, add $start to all reverse
+    // paths.
+    foreach ($graph[$start]['paths'] as $end => $v) {
+      if (isset($graph[$end])) {
+        $graph[$end]['reverse_paths'][$start] = $v;
+      }
+    }
+
+    // Record the order of the last visit. This is the reverse of the
+    // topological order if the graph is acyclic.
+    $state['last_visit_order'][] = $start;
+  }
+}
diff --git a/core/modules/simpletest/tests/graph.test b/core/modules/simpletest/tests/graph.test
index e60cd39..8b25707 100644
--- a/core/modules/simpletest/tests/graph.test
+++ b/core/modules/simpletest/tests/graph.test
@@ -5,6 +5,8 @@
  * Provides unit tests for graph.inc.
  */
 
+use Drupal\Component\Graph\Graph;
+
 /**
  * Unit tests for the graph handling features.
  */
@@ -17,11 +19,6 @@ class GraphUnitTest extends DrupalUnitTestCase {
     );
   }
 
-  function setUp() {
-    require_once DRUPAL_ROOT . '/core/includes/graph.inc';
-    parent::setUp();
-  }
-
   /**
    * Test depth-first-search features.
    */
@@ -42,7 +39,7 @@ class GraphUnitTest extends DrupalUnitTestCase {
       8 => array(9),
       9 => array(),
     ));
-    drupal_depth_first_search($graph);
+    Graph::search($graph);
 
     $expected_paths = array(
       1 => array(2, 3, 4),
@@ -106,7 +103,7 @@ class GraphUnitTest extends DrupalUnitTestCase {
    * Verify expected paths in a graph.
    *
    * @param $graph
-   *   A graph array processed by drupal_depth_first_search().
+   *   A graph array processed by Drupal\Component\Graph\Graph::search().
    * @param $expected_paths
    *   An associative array containing vertices with their expected paths.
    */
@@ -123,7 +120,7 @@ class GraphUnitTest extends DrupalUnitTestCase {
    * Verify expected reverse paths in a graph.
    *
    * @param $graph
-   *   A graph array processed by drupal_depth_first_search().
+   *   A graph array processed by Drupal\Component\Graph\Graph::search().
    * @param $expected_reverse_paths
    *   An associative array containing vertices with their expected reverse
    *   paths.
@@ -141,7 +138,7 @@ class GraphUnitTest extends DrupalUnitTestCase {
    * Verify expected components in a graph.
    *
    * @param $graph
-   *   A graph array processed by drupal_depth_first_search().
+   *   A graph array processed by Drupal\Component\Graph\Graph::search().
    * @param $expected_components
    *   An array containing of components defined as a list of their vertices.
    */
@@ -162,7 +159,7 @@ class GraphUnitTest extends DrupalUnitTestCase {
    * Verify expected order in a graph.
    *
    * @param $graph
-   *   A graph array processed by drupal_depth_first_search().
+   *   A graph array processed by Drupal\Component\Graph\Graph::search().
    * @param $expected_orders
    *   An array containing lists of vertices in their expected order.
    */
-- 
1.7.5.4

