vendor/doctrine/migrations/src/DependencyFactory.php line 427

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\Migrations;
  4. use Doctrine\Common\EventManager;
  5. use Doctrine\DBAL\Connection;
  6. use Doctrine\Migrations\Configuration\Configuration;
  7. use Doctrine\Migrations\Configuration\Connection\ConnectionLoader;
  8. use Doctrine\Migrations\Configuration\EntityManager\EntityManagerLoader;
  9. use Doctrine\Migrations\Configuration\Migration\ConfigurationLoader;
  10. use Doctrine\Migrations\Exception\FrozenDependencies;
  11. use Doctrine\Migrations\Exception\MissingDependency;
  12. use Doctrine\Migrations\Finder\GlobFinder;
  13. use Doctrine\Migrations\Finder\MigrationFinder;
  14. use Doctrine\Migrations\Finder\RecursiveRegexFinder;
  15. use Doctrine\Migrations\Generator\ClassNameGenerator;
  16. use Doctrine\Migrations\Generator\ConcatenationFileBuilder;
  17. use Doctrine\Migrations\Generator\DiffGenerator;
  18. use Doctrine\Migrations\Generator\FileBuilder;
  19. use Doctrine\Migrations\Generator\Generator;
  20. use Doctrine\Migrations\Generator\SqlGenerator;
  21. use Doctrine\Migrations\Metadata\Storage\MetadataStorage;
  22. use Doctrine\Migrations\Metadata\Storage\TableMetadataStorage;
  23. use Doctrine\Migrations\Metadata\Storage\TableMetadataStorageConfiguration;
  24. use Doctrine\Migrations\Provider\DBALSchemaDiffProvider;
  25. use Doctrine\Migrations\Provider\EmptySchemaProvider;
  26. use Doctrine\Migrations\Provider\LazySchemaDiffProvider;
  27. use Doctrine\Migrations\Provider\OrmSchemaProvider;
  28. use Doctrine\Migrations\Provider\SchemaDiffProvider;
  29. use Doctrine\Migrations\Provider\SchemaProvider;
  30. use Doctrine\Migrations\Tools\Console\ConsoleInputMigratorConfigurationFactory;
  31. use Doctrine\Migrations\Tools\Console\Helper\MigrationStatusInfosHelper;
  32. use Doctrine\Migrations\Tools\Console\MigratorConfigurationFactory;
  33. use Doctrine\Migrations\Version\AliasResolver;
  34. use Doctrine\Migrations\Version\AlphabeticalComparator;
  35. use Doctrine\Migrations\Version\Comparator;
  36. use Doctrine\Migrations\Version\CurrentMigrationStatusCalculator;
  37. use Doctrine\Migrations\Version\DbalExecutor;
  38. use Doctrine\Migrations\Version\DbalMigrationFactory;
  39. use Doctrine\Migrations\Version\DefaultAliasResolver;
  40. use Doctrine\Migrations\Version\Executor;
  41. use Doctrine\Migrations\Version\MigrationFactory;
  42. use Doctrine\Migrations\Version\MigrationPlanCalculator;
  43. use Doctrine\Migrations\Version\MigrationStatusCalculator;
  44. use Doctrine\Migrations\Version\SortedMigrationPlanCalculator;
  45. use Doctrine\ORM\EntityManagerInterface;
  46. use Psr\Log\LoggerInterface;
  47. use Psr\Log\NullLogger;
  48. use Symfony\Component\Stopwatch\Stopwatch;
  49. use function array_key_exists;
  50. use function call_user_func;
  51. use function method_exists;
  52. use function preg_quote;
  53. use function sprintf;
  54. /**
  55. * The DependencyFactory is responsible for wiring up and managing internal class dependencies.
  56. */
  57. class DependencyFactory
  58. {
  59. /** @var array<string, bool> */
  60. private array $inResolution = [];
  61. private Configuration|null $configuration = null;
  62. /** @var object[]|callable[] */
  63. private array $dependencies = [];
  64. private Connection|null $connection = null;
  65. private EntityManagerInterface|null $em = null;
  66. private EventManager|null $eventManager = null;
  67. private bool $frozen = false;
  68. private ConfigurationLoader $configurationLoader;
  69. private ConnectionLoader $connectionLoader;
  70. private EntityManagerLoader|null $emLoader = null;
  71. /** @var callable[] */
  72. private array $factories = [];
  73. public static function fromConnection(
  74. ConfigurationLoader $configurationLoader,
  75. ConnectionLoader $connectionLoader,
  76. LoggerInterface|null $logger = null,
  77. ): self {
  78. $dependencyFactory = new self($logger);
  79. $dependencyFactory->configurationLoader = $configurationLoader;
  80. $dependencyFactory->connectionLoader = $connectionLoader;
  81. return $dependencyFactory;
  82. }
  83. public static function fromEntityManager(
  84. ConfigurationLoader $configurationLoader,
  85. EntityManagerLoader $emLoader,
  86. LoggerInterface|null $logger = null,
  87. ): self {
  88. $dependencyFactory = new self($logger);
  89. $dependencyFactory->configurationLoader = $configurationLoader;
  90. $dependencyFactory->emLoader = $emLoader;
  91. return $dependencyFactory;
  92. }
  93. private function __construct(LoggerInterface|null $logger)
  94. {
  95. if ($logger === null) {
  96. return;
  97. }
  98. $this->setDefinition(LoggerInterface::class, static fn (): LoggerInterface => $logger);
  99. }
  100. public function isFrozen(): bool
  101. {
  102. return $this->frozen;
  103. }
  104. public function freeze(): void
  105. {
  106. $this->frozen = true;
  107. }
  108. private function assertNotFrozen(): void
  109. {
  110. if ($this->frozen) {
  111. throw FrozenDependencies::new();
  112. }
  113. }
  114. public function hasEntityManager(): bool
  115. {
  116. return $this->emLoader !== null;
  117. }
  118. public function setConfigurationLoader(ConfigurationLoader $configurationLoader): void
  119. {
  120. $this->assertNotFrozen();
  121. $this->configurationLoader = $configurationLoader;
  122. }
  123. public function getConfiguration(): Configuration
  124. {
  125. if ($this->configuration === null) {
  126. $this->configuration = $this->configurationLoader->getConfiguration();
  127. $this->frozen = true;
  128. }
  129. return $this->configuration;
  130. }
  131. public function getConnection(): Connection
  132. {
  133. if ($this->connection === null) {
  134. $this->connection = $this->hasEntityManager()
  135. ? $this->getEntityManager()->getConnection()
  136. : $this->connectionLoader->getConnection($this->getConfiguration()->getConnectionName());
  137. $this->frozen = true;
  138. }
  139. return $this->connection;
  140. }
  141. public function getEntityManager(): EntityManagerInterface
  142. {
  143. if ($this->em === null) {
  144. if ($this->emLoader === null) {
  145. throw MissingDependency::noEntityManager();
  146. }
  147. $this->em = $this->emLoader->getEntityManager($this->getConfiguration()->getEntityManagerName());
  148. $this->frozen = true;
  149. }
  150. return $this->em;
  151. }
  152. public function getVersionComparator(): Comparator
  153. {
  154. return $this->getDependency(Comparator::class, static fn (): AlphabeticalComparator => new AlphabeticalComparator());
  155. }
  156. public function getLogger(): LoggerInterface
  157. {
  158. return $this->getDependency(LoggerInterface::class, static fn (): LoggerInterface => new NullLogger());
  159. }
  160. public function getEventDispatcher(): EventDispatcher
  161. {
  162. return $this->getDependency(EventDispatcher::class, fn (): EventDispatcher => new EventDispatcher(
  163. $this->getConnection(),
  164. $this->getEventManager(),
  165. ));
  166. }
  167. public function getClassNameGenerator(): ClassNameGenerator
  168. {
  169. return $this->getDependency(ClassNameGenerator::class, static fn (): ClassNameGenerator => new ClassNameGenerator());
  170. }
  171. public function getSchemaDumper(): SchemaDumper
  172. {
  173. return $this->getDependency(SchemaDumper::class, function (): SchemaDumper {
  174. $excludedTables = [];
  175. $metadataConfig = $this->getConfiguration()->getMetadataStorageConfiguration();
  176. if ($metadataConfig instanceof TableMetadataStorageConfiguration) {
  177. $excludedTables[] = sprintf('/^%s$/', preg_quote($metadataConfig->getTableName(), '/'));
  178. }
  179. return new SchemaDumper(
  180. $this->getConnection()->getDatabasePlatform(),
  181. $this->getConnection()->createSchemaManager(),
  182. $this->getMigrationGenerator(),
  183. $this->getMigrationSqlGenerator(),
  184. $excludedTables,
  185. );
  186. });
  187. }
  188. private function getEmptySchemaProvider(): SchemaProvider
  189. {
  190. return $this->getDependency(EmptySchemaProvider::class, fn (): SchemaProvider => new EmptySchemaProvider($this->getConnection()->createSchemaManager()));
  191. }
  192. public function hasSchemaProvider(): bool
  193. {
  194. try {
  195. $this->getSchemaProvider();
  196. } catch (MissingDependency) {
  197. return false;
  198. }
  199. return true;
  200. }
  201. public function getSchemaProvider(): SchemaProvider
  202. {
  203. return $this->getDependency(SchemaProvider::class, function (): SchemaProvider {
  204. if ($this->hasEntityManager()) {
  205. return new OrmSchemaProvider($this->getEntityManager());
  206. }
  207. throw MissingDependency::noSchemaProvider();
  208. });
  209. }
  210. public function getDiffGenerator(): DiffGenerator
  211. {
  212. return $this->getDependency(DiffGenerator::class, fn (): DiffGenerator => new DiffGenerator(
  213. $this->getConnection()->getConfiguration(),
  214. $this->getConnection()->createSchemaManager(),
  215. $this->getSchemaProvider(),
  216. $this->getConnection()->getDatabasePlatform(),
  217. $this->getMigrationGenerator(),
  218. $this->getMigrationSqlGenerator(),
  219. $this->getEmptySchemaProvider(),
  220. ));
  221. }
  222. public function getSchemaDiffProvider(): SchemaDiffProvider
  223. {
  224. return $this->getDependency(SchemaDiffProvider::class, fn (): LazySchemaDiffProvider => new LazySchemaDiffProvider(
  225. new DBALSchemaDiffProvider(
  226. $this->getConnection()->createSchemaManager(),
  227. $this->getConnection()->getDatabasePlatform(),
  228. ),
  229. ));
  230. }
  231. private function getFileBuilder(): FileBuilder
  232. {
  233. return $this->getDependency(FileBuilder::class, static fn (): FileBuilder => new ConcatenationFileBuilder());
  234. }
  235. private function getParameterFormatter(): ParameterFormatter
  236. {
  237. return $this->getDependency(ParameterFormatter::class, fn (): ParameterFormatter => new InlineParameterFormatter($this->getConnection()));
  238. }
  239. public function getMigrationsFinder(): MigrationFinder
  240. {
  241. return $this->getDependency(MigrationFinder::class, function (): MigrationFinder {
  242. $configs = $this->getConfiguration();
  243. $needsRecursiveFinder = $configs->areMigrationsOrganizedByYear() || $configs->areMigrationsOrganizedByYearAndMonth();
  244. return $needsRecursiveFinder ? new RecursiveRegexFinder() : new GlobFinder();
  245. });
  246. }
  247. public function getMigrationRepository(): MigrationsRepository
  248. {
  249. return $this->getDependency(MigrationsRepository::class, fn (): MigrationsRepository => new FilesystemMigrationsRepository(
  250. $this->getConfiguration()->getMigrationClasses(),
  251. $this->getConfiguration()->getMigrationDirectories(),
  252. $this->getMigrationsFinder(),
  253. $this->getMigrationFactory(),
  254. ));
  255. }
  256. public function getMigrationFactory(): MigrationFactory
  257. {
  258. return $this->getDependency(MigrationFactory::class, fn (): MigrationFactory => new DbalMigrationFactory($this->getConnection(), $this->getLogger()));
  259. }
  260. public function setService(string $id, object|callable $service): void
  261. {
  262. $this->assertNotFrozen();
  263. $this->dependencies[$id] = $service;
  264. }
  265. public function getMetadataStorage(): MetadataStorage
  266. {
  267. return $this->getDependency(MetadataStorage::class, fn (): MetadataStorage => new TableMetadataStorage(
  268. $this->getConnection(),
  269. $this->getVersionComparator(),
  270. $this->getConfiguration()->getMetadataStorageConfiguration(),
  271. $this->getMigrationRepository(),
  272. ));
  273. }
  274. private function getVersionExecutor(): Executor
  275. {
  276. return $this->getDependency(Executor::class, fn (): Executor => new DbalExecutor(
  277. $this->getMetadataStorage(),
  278. $this->getEventDispatcher(),
  279. $this->getConnection(),
  280. $this->getSchemaDiffProvider(),
  281. $this->getLogger(),
  282. $this->getParameterFormatter(),
  283. $this->getStopwatch(),
  284. ));
  285. }
  286. public function getQueryWriter(): QueryWriter
  287. {
  288. return $this->getDependency(QueryWriter::class, fn (): QueryWriter => new FileQueryWriter(
  289. $this->getFileBuilder(),
  290. $this->getLogger(),
  291. ));
  292. }
  293. public function getVersionAliasResolver(): AliasResolver
  294. {
  295. return $this->getDependency(AliasResolver::class, fn (): AliasResolver => new DefaultAliasResolver(
  296. $this->getMigrationPlanCalculator(),
  297. $this->getMetadataStorage(),
  298. $this->getMigrationStatusCalculator(),
  299. ));
  300. }
  301. public function getMigrationStatusCalculator(): MigrationStatusCalculator
  302. {
  303. return $this->getDependency(MigrationStatusCalculator::class, fn (): MigrationStatusCalculator => new CurrentMigrationStatusCalculator(
  304. $this->getMigrationPlanCalculator(),
  305. $this->getMetadataStorage(),
  306. ));
  307. }
  308. public function getMigrationPlanCalculator(): MigrationPlanCalculator
  309. {
  310. return $this->getDependency(MigrationPlanCalculator::class, fn (): MigrationPlanCalculator => new SortedMigrationPlanCalculator(
  311. $this->getMigrationRepository(),
  312. $this->getMetadataStorage(),
  313. $this->getVersionComparator(),
  314. ));
  315. }
  316. public function getMigrationGenerator(): Generator
  317. {
  318. return $this->getDependency(Generator::class, fn (): Generator => new Generator($this->getConfiguration()));
  319. }
  320. public function getMigrationSqlGenerator(): SqlGenerator
  321. {
  322. return $this->getDependency(SqlGenerator::class, fn (): SqlGenerator => new SqlGenerator(
  323. $this->getConfiguration(),
  324. $this->getConnection()->getDatabasePlatform(),
  325. ));
  326. }
  327. public function getConsoleInputMigratorConfigurationFactory(): MigratorConfigurationFactory
  328. {
  329. return $this->getDependency(MigratorConfigurationFactory::class, fn (): MigratorConfigurationFactory => new ConsoleInputMigratorConfigurationFactory(
  330. $this->getConfiguration(),
  331. ));
  332. }
  333. public function getMigrationStatusInfosHelper(): MigrationStatusInfosHelper
  334. {
  335. return $this->getDependency(MigrationStatusInfosHelper::class, fn (): MigrationStatusInfosHelper => new MigrationStatusInfosHelper(
  336. $this->getConfiguration(),
  337. $this->getConnection(),
  338. $this->getVersionAliasResolver(),
  339. $this->getMigrationPlanCalculator(),
  340. $this->getMigrationStatusCalculator(),
  341. $this->getMetadataStorage(),
  342. ));
  343. }
  344. public function getMigrator(): Migrator
  345. {
  346. return $this->getDependency(Migrator::class, fn (): Migrator => new DbalMigrator(
  347. $this->getConnection(),
  348. $this->getEventDispatcher(),
  349. $this->getVersionExecutor(),
  350. $this->getLogger(),
  351. $this->getStopwatch(),
  352. ));
  353. }
  354. public function getStopwatch(): Stopwatch
  355. {
  356. return $this->getDependency(Stopwatch::class, static fn (): Stopwatch => new Stopwatch(true));
  357. }
  358. public function getRollup(): Rollup
  359. {
  360. return $this->getDependency(Rollup::class, fn (): Rollup => new Rollup(
  361. $this->getMetadataStorage(),
  362. $this->getMigrationRepository(),
  363. ));
  364. }
  365. private function getDependency(string $id, callable $callback): mixed
  366. {
  367. if (! isset($this->inResolution[$id]) && array_key_exists($id, $this->factories) && ! array_key_exists($id, $this->dependencies)) {
  368. $this->inResolution[$id] = true;
  369. $this->dependencies[$id] = call_user_func($this->factories[$id], $this);
  370. unset($this->inResolution);
  371. }
  372. if (! array_key_exists($id, $this->dependencies)) {
  373. $this->dependencies[$id] = $callback();
  374. }
  375. return $this->dependencies[$id];
  376. }
  377. public function setDefinition(string $id, callable $service): void
  378. {
  379. $this->assertNotFrozen();
  380. $this->factories[$id] = $service;
  381. }
  382. private function getEventManager(): EventManager
  383. {
  384. if ($this->eventManager !== null) {
  385. return $this->eventManager;
  386. }
  387. if ($this->hasEntityManager()) {
  388. return $this->eventManager = $this->getEntityManager()->getEventManager();
  389. }
  390. if (method_exists(Connection::class, 'getEventManager')) {
  391. // DBAL < 4
  392. return $this->eventManager = $this->getConnection()->getEventManager();
  393. }
  394. return $this->eventManager = new EventManager();
  395. }
  396. }