1
0
mirror of synced 2025-02-03 05:49:25 +03:00
doctrine2/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php

183 lines
5.3 KiB
PHP
Raw Normal View History

<?php
2008-07-27 19:38:56 +00:00
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
2012-05-26 14:37:00 +02:00
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
2008-07-27 19:38:56 +00:00
*/
namespace Doctrine\ORM\Internal;
/**
* CommitOrderCalculator implements topological sorting, which is an ordering
* algorithm for directed graphs (DG) and/or directed acyclic graphs (DAG) by
* using a depth-first searching (DFS) to traverse the graph built in memory.
* This algorithm have a linear running time based on nodes (V) and dependency
* between the nodes (E), resulting in a computational complexity of O(V + E).
*
* @since 2.0
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class CommitOrderCalculator
{
const NOT_VISITED = 0;
const IN_PROGRESS = 1;
const VISITED = 2;
2011-12-19 22:56:19 +01:00
2012-12-02 19:44:56 +00:00
/**
* Matrix of nodes (aka. vertex).
* Keys are provided hashes and values are the node definition objects.
2012-12-02 19:44:56 +00:00
*
* The node state definition contains the following properties:
*
* - <b>state</b> (integer)
* Whether the node is NOT_VISITED or IN_PROGRESS
*
* - <b>value</b> (object)
* Actual node value
*
* - <b>dependencyList</b> (array<string>)
* Map of node dependencies defined as hashes.
*
* @var array<stdClass>
2012-12-02 19:44:56 +00:00
*/
private $nodeList = array();
2012-12-02 19:44:56 +00:00
/**
* Volatile variable holding calculated nodes during sorting process.
*
2012-12-02 19:44:56 +00:00
* @var array
*/
private $sortedNodeList = array();
2011-12-19 22:56:19 +01:00
/**
* Checks for node (vertex) existence in graph.
*
* @param string $hash
*
* @return boolean
*/
public function hasNode($hash)
{
return isset($this->nodeList[$hash]);
}
2011-12-19 22:56:19 +01:00
/**
* Adds a new node (vertex) to the graph, assigning its hash and value.
2011-12-19 22:56:19 +01:00
*
* @param string $hash
* @param object $node
*
* @return void
*/
public function addNode($hash, $node)
{
$vertex = new \stdClass();
$vertex->hash = $hash;
$vertex->state = self::NOT_VISITED;
$vertex->value = $node;
$vertex->dependencyList = array();
2011-12-19 22:56:19 +01:00
$this->nodeList[$hash] = $vertex;
}
2012-12-02 19:44:56 +00:00
/**
* Adds a new dependency (edge) to the graph using their hashes.
*
* @param string $fromHash
* @param string $toHash
* @param integer $weight
2012-12-02 19:44:56 +00:00
*
* @return void
*/
public function addDependency($fromHash, $toHash, $weight)
{
$vertex = $this->nodeList[$fromHash];
$edge = new \stdClass();
$edge->from = $fromHash;
$edge->to = $toHash;
$edge->weight = $weight;
$vertex->dependencyList[$toHash] = $edge;
}
2011-12-19 22:56:19 +01:00
2012-12-02 19:44:56 +00:00
/**
* Return a valid order list of all current nodes.
* The desired topological sorting is the reverse post order of these searches.
2012-12-02 19:44:56 +00:00
*
* {@internal Highly performance-sensitive method.}
2012-12-02 19:44:56 +00:00
*
* @return array
2012-12-02 19:44:56 +00:00
*/
public function sort()
{
foreach ($this->nodeList as $vertex) {
if ($vertex->state !== self::NOT_VISITED) {
continue;
}
$this->visit($vertex);
}
$sortedList = $this->sortedNodeList;
$this->nodeList = array();
$this->sortedNodeList = array();
return array_reverse($sortedList);
}
2011-12-19 22:56:19 +01:00
2012-12-02 19:44:56 +00:00
/**
* Visit a given node definition for reordering.
2012-12-02 19:44:56 +00:00
*
* {@internal Highly performance-sensitive method.}
*
* @param \stdClass $vertex
2012-12-02 19:44:56 +00:00
*/
private function visit($vertex)
{
$vertex->state = self::IN_PROGRESS;
foreach ($vertex->dependencyList as $edge) {
$adjacentVertex = $this->nodeList[$edge->to];
switch ($adjacentVertex->state) {
case self::VISITED:
// Do nothing, since node was already visited
break;
case self::IN_PROGRESS:
if ($adjacentVertex->dependencyList[$vertex->hash]->weight < $edge->weight) {
$adjacentVertex->state = self::VISITED;
$this->sortedNodeList[] = $adjacentVertex->value;
}
break;
case self::NOT_VISITED:
$this->visit($adjacentVertex);
}
}
if ($vertex->state !== self::VISITED) {
$vertex->state = self::VISITED;
$this->sortedNodeList[] = $vertex->value;
}
}
}