public function testSort() { $graph = new Structures_Graph(); $name1 = 'node1'; $node1 = new Structures_Graph_Node(); $node1->setData($name1); $graph->addNode($node1); $name11 = 'node11'; $node11 = new Structures_Graph_Node(); $node11->setData($name11); $graph->addNode($node11); $node1->connectTo($node11); $name12 = 'node12'; $node12 = new Structures_Graph_Node(); $node12->setData($name12); $graph->addNode($node12); $node1->connectTo($node12); $name121 = 'node121'; $node121 = new Structures_Graph_Node(); $node121->setData($name121); $graph->addNode($node121); $node12->connectTo($node121); $name2 = 'node2'; $node2 = new Structures_Graph_Node(); $node2->setData($name2); $graph->addNode($node2); $name21 = 'node21'; $node21 = new Structures_Graph_Node(); $node21->setData($name21); $graph->addNode($node21); $node2->connectTo($node21); $nodes = Structures_Graph_Manipulator_TopologicalSorter::sort($graph); $this->assertCount(2, $nodes[0]); $this->assertEquals('node1', $nodes[0][0]->getData()); $this->assertEquals('node2', $nodes[0][1]->getData()); $this->assertCount(3, $nodes[1]); $this->assertEquals('node11', $nodes[1][0]->getData()); $this->assertEquals('node12', $nodes[1][1]->getData()); $this->assertEquals('node21', $nodes[1][2]->getData()); $this->assertCount(1, $nodes[2]); $this->assertEquals('node121', $nodes[2][0]->getData()); }
/** * Sort a list of arrays of array(downloaded packagefilename) by dependency. * * This uses the topological sort method from graph theory, and the * Structures_Graph package to properly sort dependencies for installation. * @param array an array of downloaded PEAR_Downloader_Packages * @return array array of array(packagefilename, package.xml contents) */ function sortPackagesForInstall(&$packages) { require_once 'Structures/Graph.php'; require_once 'Structures/Graph/Node.php'; require_once 'Structures/Graph/Manipulator/TopologicalSorter.php'; $depgraph = new Structures_Graph(true); $nodes = array(); $reg =& $this->config->getRegistry(); foreach ($packages as $i => $package) { $pname = $reg->parsedPackageNameToString(array('channel' => $package->getChannel(), 'package' => strtolower($package->getPackage()))); $nodes[$pname] = new Structures_Graph_Node(); $nodes[$pname]->setData($packages[$i]); $depgraph->addNode($nodes[$pname]); } $deplinks = array(); foreach ($nodes as $package => $node) { $pf =& $node->getData(); $pdeps = $pf->getDeps(true); if (!$pdeps) { continue; } if ($pf->getPackagexmlVersion() == '1.0') { foreach ($pdeps as $dep) { if ($dep['type'] != 'pkg' || isset($dep['optional']) && $dep['optional'] == 'yes') { continue; } $dname = $reg->parsedPackageNameToString(array('channel' => 'pear.php.net', 'package' => strtolower($dep['name']))); if (isset($nodes[$dname])) { if (!isset($deplinks[$dname])) { $deplinks[$dname] = array(); } $deplinks[$dname][$package] = 1; // dependency is in installed packages continue; } $dname = $reg->parsedPackageNameToString(array('channel' => 'pecl.php.net', 'package' => strtolower($dep['name']))); if (isset($nodes[$dname])) { if (!isset($deplinks[$dname])) { $deplinks[$dname] = array(); } $deplinks[$dname][$package] = 1; // dependency is in installed packages continue; } } } else { // the only ordering we care about is: // 1) subpackages must be installed before packages that depend on them // 2) required deps must be installed before packages that depend on them if (isset($pdeps['required']['subpackage'])) { $t = $pdeps['required']['subpackage']; if (!isset($t[0])) { $t = array($t); } $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } if (isset($pdeps['group'])) { if (!isset($pdeps['group'][0])) { $pdeps['group'] = array($pdeps['group']); } foreach ($pdeps['group'] as $group) { if (isset($group['subpackage'])) { $t = $group['subpackage']; if (!isset($t[0])) { $t = array($t); } $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } } } if (isset($pdeps['optional']['subpackage'])) { $t = $pdeps['optional']['subpackage']; if (!isset($t[0])) { $t = array($t); } $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } if (isset($pdeps['required']['package'])) { $t = $pdeps['required']['package']; if (!isset($t[0])) { $t = array($t); } $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } if (isset($pdeps['group'])) { if (!isset($pdeps['group'][0])) { $pdeps['group'] = array($pdeps['group']); } foreach ($pdeps['group'] as $group) { if (isset($group['package'])) { $t = $group['package']; if (!isset($t[0])) { $t = array($t); } $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } } } } } $this->_detectDepCycle($deplinks); foreach ($deplinks as $dependent => $parents) { foreach ($parents as $parent => $unused) { $nodes[$dependent]->connectTo($nodes[$parent]); } } $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph); $ret = array(); for ($i = 0, $count = count($installOrder); $i < $count; $i++) { foreach ($installOrder[$i] as $index => $sortedpackage) { $data =& $installOrder[$i][$index]->getData(); $ret[] =& $nodes[$reg->parsedPackageNameToString(array('channel' => $data->getChannel(), 'package' => strtolower($data->getPackage())))]->getData(); } } $packages = $ret; return; }
/** * * sort returns the graph's nodes, sorted by topological order. * * The result is an array with * as many entries as topological levels. Each entry in this array is an array of nodes within * the given topological level. * * @return array The graph's nodes, sorted by topological order. * @access public */ function sort(&$graph) { // We only sort graphs if (!is_a($graph, 'Structures_Graph')) { return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC); } if (!Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph)) { return Pear::raiseError('Structures_Graph_Manipulator_TopologicalSorter::sort received an graph that has cycles', STRUCTURES_GRAPH_ERROR_GENERIC); } Structures_Graph_Manipulator_TopologicalSorter::_sort($graph); $result = array(); // Fill out result array $nodes =& $graph->getNodes(); $nodeKeys = array_keys($nodes); foreach ($nodeKeys as $key) { if (!array_key_exists($nodes[$key]->getMetadata('topological-sort-level'), $result)) { $result[$nodes[$key]->getMetadata('topological-sort-level')] = array(); } $result[$nodes[$key]->getMetadata('topological-sort-level')][] =& $nodes[$key]; $nodes[$key]->unsetMetadata('topological-sort-level'); } return $result; }
/** * Retorna el orden hacia adelante en el cual se deben sincronizar las tablas * El orden predeterminado es el orden topologico de las tablas * @return array Arreglo id_tabla => toba_datos_tabla */ function orden_sincronizacion() { if ($this->_info_estructura['sinc_orden_automatico']) { //-- Se construye el orden topológico $sorter = new Structures_Graph_Manipulator_TopologicalSorter(); $deps = $this->get_tablas_activas(); $rel = $this->get_relaciones_activas(); $grafo = self::grafo_relaciones($deps, $rel); $parciales = $sorter->sort($grafo); $ordenes = array(); for ($i = 0; $i < count($parciales); $i++) { for ($j = 0; $j < count($parciales[$i]); $j++) { $ordenes[] = $parciales[$i][$j]->getData(); } } $tablas = array(); foreach ($ordenes as $orden) { $tablas[$orden['identificador']] = $this->_dependencias[$orden['identificador']]; } return $tablas; } else { //-- Se toma el orden natural en el cual se definieron las tablas $ordenes = $this->get_tablas_activas(); $tablas = array(); foreach ($ordenes as $orden) { $tablas[$orden['identificador']] = $this->_dependencias[$orden['identificador']]; } return $tablas; } }