/** * Determines sequence in which entities should be synchronized. * It's based on their dependencies on other entities. * * @return array */ private function resolveSynchronizationSequence() { $unresolved = $this->dbSchema->getAllReferences(); $sequence = []; $triedRemovingSelfReferences = false; while (count($unresolved) > 0) { $resolvedInThisStep = []; // 1st step - move all entities with resolved dependencies to $sequence foreach ($unresolved as $entity => $deps) { if (count($deps) === 0) { unset($unresolved[$entity]); $sequence[] = $entity; $resolvedInThisStep[] = $entity; } } // 2nd step - update unresolved dependencies of remaining entities foreach ($resolvedInThisStep as $resolvedEntity) { foreach ($unresolved as $unresolvedEntity => $deps) { $unresolved[$unresolvedEntity] = array_diff($deps, [$resolvedEntity]); } } // Nothing changed - circular dependency if (count($resolvedInThisStep) === 0) { if (!$triedRemovingSelfReferences) { // At first try to remove all self-references as they have to run in 2-pass sync anyway $triedRemovingSelfReferences = true; foreach ($unresolved as $unresolvedEntity => $deps) { $unresolved[$unresolvedEntity] = array_diff($deps, [$unresolvedEntity]); } } else { // Simply eliminate dependencies one by one until it is resolvable reset($unresolved); $firstEntity = key($unresolved); array_pop($unresolved[$firstEntity]); } } } return $sequence; }