*/ private $inResolution = []; private ?Configuration $configuration = null; /** @var object[]|callable[] */ private array $dependencies = []; private ?Connection $connection = null; private ?EntityManagerInterface $em = null; private bool $frozen = false; private ConfigurationLoader $configurationLoader; private ConnectionLoader $connectionLoader; private ?EntityManagerLoader $emLoader = null; /** @var callable[] */ private array $factories = []; public static function fromConnection( ConfigurationLoader $configurationLoader, ConnectionLoader $connectionLoader, ?LoggerInterface $logger = null ): self { $dependencyFactory = new self($logger); $dependencyFactory->configurationLoader = $configurationLoader; $dependencyFactory->connectionLoader = $connectionLoader; return $dependencyFactory; } public static function fromEntityManager( ConfigurationLoader $configurationLoader, EntityManagerLoader $emLoader, ?LoggerInterface $logger = null ): self { $dependencyFactory = new self($logger); $dependencyFactory->configurationLoader = $configurationLoader; $dependencyFactory->emLoader = $emLoader; return $dependencyFactory; } private function __construct(?LoggerInterface $logger) { if ($logger === null) { return; } $this->setDefinition(LoggerInterface::class, static function () use ($logger): LoggerInterface { return $logger; }); } public function isFrozen(): bool { return $this->frozen; } public function freeze(): void { $this->frozen = true; } private function assertNotFrozen(): void { if ($this->frozen) { throw FrozenDependencies::new(); } } public function hasEntityManager(): bool { return $this->emLoader !== null; } public function setConfigurationLoader(ConfigurationLoader $configurationLoader): void { $this->assertNotFrozen(); $this->configurationLoader = $configurationLoader; } public function getConfiguration(): Configuration { if ($this->configuration === null) { $this->configuration = $this->configurationLoader->getConfiguration(); $this->freeze(); } return $this->configuration; } public function getConnection(): Connection { if ($this->connection === null) { $this->connection = $this->hasEntityManager() ? $this->getEntityManager()->getConnection() : $this->connectionLoader->getConnection($this->getConfiguration()->getConnectionName()); $this->freeze(); } return $this->connection; } public function getEntityManager(): EntityManagerInterface { if ($this->em === null) { if ($this->emLoader === null) { throw MissingDependency::noEntityManager(); } $this->em = $this->emLoader->getEntityManager($this->getConfiguration()->getEntityManagerName()); $this->freeze(); } return $this->em; } public function getVersionComparator(): Comparator { return $this->getDependency(Comparator::class, static function (): AlphabeticalComparator { return new AlphabeticalComparator(); }); } public function getLogger(): LoggerInterface { return $this->getDependency(LoggerInterface::class, static function (): LoggerInterface { return new NullLogger(); }); } public function getEventDispatcher(): EventDispatcher { return $this->getDependency(EventDispatcher::class, function (): EventDispatcher { return new EventDispatcher( $this->getConnection(), $this->getConnection()->getEventManager() ); }); } public function getClassNameGenerator(): ClassNameGenerator { return $this->getDependency(ClassNameGenerator::class, static function (): ClassNameGenerator { return new ClassNameGenerator(); }); } public function getSchemaDumper(): SchemaDumper { return $this->getDependency(SchemaDumper::class, function (): SchemaDumper { $excludedTables = []; $metadataConfig = $this->getConfiguration()->getMetadataStorageConfiguration(); if ($metadataConfig instanceof TableMetadataStorageConfiguration) { $excludedTables[] = sprintf('/^%s$/', preg_quote($metadataConfig->getTableName(), '/')); } return new SchemaDumper( $this->getConnection()->getDatabasePlatform(), $this->getConnection()->createSchemaManager(), $this->getMigrationGenerator(), $this->getMigrationSqlGenerator(), $excludedTables ); }); } private function getEmptySchemaProvider(): SchemaProvider { return $this->getDependency(EmptySchemaProvider::class, function (): SchemaProvider { return new EmptySchemaProvider($this->connection->createSchemaManager()); }); } public function hasSchemaProvider(): bool { try { $this->getSchemaProvider(); } catch (MissingDependency $exception) { return false; } return true; } public function getSchemaProvider(): SchemaProvider { return $this->getDependency(SchemaProvider::class, function (): SchemaProvider { if ($this->hasEntityManager()) { return new OrmSchemaProvider($this->getEntityManager()); } throw MissingDependency::noSchemaProvider(); }); } public function getDiffGenerator(): DiffGenerator { return $this->getDependency(DiffGenerator::class, function (): DiffGenerator { return new DiffGenerator( $this->getConnection()->getConfiguration(), $this->getConnection()->createSchemaManager(), $this->getSchemaProvider(), $this->getConnection()->getDatabasePlatform(), $this->getMigrationGenerator(), $this->getMigrationSqlGenerator(), $this->getEmptySchemaProvider() ); }); } public function getSchemaDiffProvider(): SchemaDiffProvider { return $this->getDependency(SchemaDiffProvider::class, function (): LazySchemaDiffProvider { return LazySchemaDiffProvider::fromDefaultProxyFactoryConfiguration( new DBALSchemaDiffProvider( $this->getConnection()->createSchemaManager(), $this->getConnection()->getDatabasePlatform() ) ); }); } private function getFileBuilder(): FileBuilder { return $this->getDependency(FileBuilder::class, static function (): FileBuilder { return new ConcatenationFileBuilder(); }); } private function getParameterFormatter(): ParameterFormatter { return $this->getDependency(ParameterFormatter::class, function (): ParameterFormatter { return new InlineParameterFormatter($this->getConnection()); }); } public function getMigrationsFinder(): MigrationFinder { return $this->getDependency(MigrationFinder::class, function (): MigrationFinder { $configs = $this->getConfiguration(); $needsRecursiveFinder = $configs->areMigrationsOrganizedByYear() || $configs->areMigrationsOrganizedByYearAndMonth(); return $needsRecursiveFinder ? new RecursiveRegexFinder() : new GlobFinder(); }); } public function getMigrationRepository(): MigrationsRepository { return $this->getDependency(MigrationsRepository::class, function (): MigrationsRepository { return new FilesystemMigrationsRepository( $this->getConfiguration()->getMigrationClasses(), $this->getConfiguration()->getMigrationDirectories(), $this->getMigrationsFinder(), $this->getMigrationFactory() ); }); } public function getMigrationFactory(): MigrationFactory { return $this->getDependency(MigrationFactory::class, function (): MigrationFactory { return new DbalMigrationFactory($this->getConnection(), $this->getLogger()); }); } /** * @param object|callable $service */ public function setService(string $id, $service): void { $this->assertNotFrozen(); $this->dependencies[$id] = $service; } public function getMetadataStorage(): MetadataStorage { return $this->getDependency(MetadataStorage::class, function (): MetadataStorage { return new TableMetadataStorage( $this->getConnection(), $this->getVersionComparator(), $this->getConfiguration()->getMetadataStorageConfiguration(), $this->getMigrationRepository() ); }); } private function getVersionExecutor(): Executor { return $this->getDependency(Executor::class, function (): Executor { return new DbalExecutor( $this->getMetadataStorage(), $this->getEventDispatcher(), $this->getConnection(), $this->getSchemaDiffProvider(), $this->getLogger(), $this->getParameterFormatter(), $this->getStopwatch() ); }); } public function getQueryWriter(): QueryWriter { return $this->getDependency(QueryWriter::class, function (): QueryWriter { return new FileQueryWriter( $this->getFileBuilder(), $this->getLogger() ); }); } public function getVersionAliasResolver(): AliasResolver { return $this->getDependency(AliasResolver::class, function (): AliasResolver { return new DefaultAliasResolver( $this->getMigrationPlanCalculator(), $this->getMetadataStorage(), $this->getMigrationStatusCalculator() ); }); } public function getMigrationStatusCalculator(): MigrationStatusCalculator { return $this->getDependency(MigrationStatusCalculator::class, function (): MigrationStatusCalculator { return new CurrentMigrationStatusCalculator( $this->getMigrationPlanCalculator(), $this->getMetadataStorage() ); }); } public function getMigrationPlanCalculator(): MigrationPlanCalculator { return $this->getDependency(MigrationPlanCalculator::class, function (): MigrationPlanCalculator { return new SortedMigrationPlanCalculator( $this->getMigrationRepository(), $this->getMetadataStorage(), $this->getVersionComparator() ); }); } public function getMigrationGenerator(): Generator { return $this->getDependency(Generator::class, function (): Generator { return new Generator($this->getConfiguration()); }); } public function getMigrationSqlGenerator(): SqlGenerator { return $this->getDependency(SqlGenerator::class, function (): SqlGenerator { return new SqlGenerator( $this->getConfiguration(), $this->getConnection()->getDatabasePlatform() ); }); } public function getConsoleInputMigratorConfigurationFactory(): MigratorConfigurationFactory { return $this->getDependency(MigratorConfigurationFactory::class, function (): MigratorConfigurationFactory { return new ConsoleInputMigratorConfigurationFactory( $this->getConfiguration() ); }); } public function getMigrationStatusInfosHelper(): MigrationStatusInfosHelper { return $this->getDependency(MigrationStatusInfosHelper::class, function (): MigrationStatusInfosHelper { return new MigrationStatusInfosHelper( $this->getConfiguration(), $this->getConnection(), $this->getVersionAliasResolver(), $this->getMigrationPlanCalculator(), $this->getMigrationStatusCalculator(), $this->getMetadataStorage() ); }); } public function getMigrator(): Migrator { return $this->getDependency(Migrator::class, function (): Migrator { return new DbalMigrator( $this->getConnection(), $this->getEventDispatcher(), $this->getVersionExecutor(), $this->getLogger(), $this->getStopwatch() ); }); } public function getStopwatch(): Stopwatch { return $this->getDependency(Stopwatch::class, static function (): Stopwatch { return new Stopwatch(true); }); } public function getRollup(): Rollup { return $this->getDependency(Rollup::class, function (): Rollup { return new Rollup( $this->getMetadataStorage(), $this->getMigrationRepository() ); }); } /** * @return mixed */ private function getDependency(string $id, callable $callback) { if (! isset($this->inResolution[$id]) && array_key_exists($id, $this->factories) && ! array_key_exists($id, $this->dependencies)) { $this->inResolution[$id] = true; $this->dependencies[$id] = call_user_func($this->factories[$id], $this); unset($this->inResolution); } if (! array_key_exists($id, $this->dependencies)) { $this->dependencies[$id] = $callback(); } return $this->dependencies[$id]; } public function setDefinition(string $id, callable $service): void { $this->assertNotFrozen(); $this->factories[$id] = $service; } }