private function orderFixturesByDependencies()
{
$sequenceForClasses = array();
// If fixtures were already ordered by number then we need
// to remove classes which are not instances of OrderedFixtureInterface
// in case fixtures implementing DependentFixtureInterface exist.
// This is because, in that case, the method orderFixturesByDependencies
// will handle all fixtures which are not instances of
// OrderedFixtureInterface
if ($this->orderFixturesByNumber) {
$count = count($this->orderedFixtures);
for ($i = 0; $i < $count; ++$i) {
if (!$this->orderedFixtures[$i] instanceof OrderedFixtureInterface) {
unset($this->orderedFixtures[$i]);
}
}
}
// First we determine which classes has dependencies and which don't
foreach ($this->fixtures as $fixture) {
$fixtureClass = get_class($fixture);
if ($fixture instanceof OrderedFixtureInterface) {
continue;
} elseif ($fixture instanceof DependentFixtureInterface) {
$dependenciesClasses = $fixture->getDependencies();
$this->validateDependencies($dependenciesClasses);
if (!is_array($dependenciesClasses) || empty($dependenciesClasses)) {
throw new \InvalidArgumentException(sprintf('Method "%s" in class "%s" must return an array of classes which are dependencies for the fixture, and it must be NOT empty.', 'getDependencies', $fixtureClass));
}
if (in_array($fixtureClass, $dependenciesClasses)) {
throw new \InvalidArgumentException(sprintf('Class "%s" can\'t have itself as a dependency', $fixtureClass));
}
// We mark this class as unsequenced
$sequenceForClasses[$fixtureClass] = -1;
} else {
// This class has no dependencies, so we assign 0
$sequenceForClasses[$fixtureClass] = 0;
}
}
// Now we order fixtures by sequence
$sequence = 1;
$lastCount = -1;
while (($count = count($unsequencedClasses = $this->getUnsequencedClasses($sequenceForClasses))) > 0 && $count !== $lastCount) {
foreach ($unsequencedClasses as $key => $class) {
$fixture = $this->fixtures[$class];
$dependencies = $fixture->getDependencies();
$unsequencedDependencies = $this->getUnsequencedClasses($sequenceForClasses, $dependencies);
if (count($unsequencedDependencies) === 0) {
$sequenceForClasses[$class] = $sequence++;
}
}
$lastCount = $count;
}
$orderedFixtures = array();
// If there're fixtures unsequenced left and they couldn't be sequenced,
// it means we have a circular reference
if ($count > 0) {
$msg = 'Classes "%s" have produced a CircularReferenceException. ';
$msg .= 'An example of this problem would be the following: Class C has class B as its dependency. ';
$msg .= 'Then, class B has class A has its dependency. Finally, class A has class C as its dependency. ';
$msg .= 'This case would produce a CircularReferenceException.';
throw new CircularReferenceException(sprintf($msg, implode(',', $unsequencedClasses)));
} else {
// We order the classes by sequence
asort($sequenceForClasses);
foreach ($sequenceForClasses as $class => $sequence) {
// If fixtures were ordered
$orderedFixtures[] = $this->fixtures[$class];
}
}
$this->orderedFixtures = array_merge($this->orderedFixtures, $orderedFixtures);
}