vendor/doctrine/doctrine-bundle/DependencyInjection/Configuration.php line 444

Open in your IDE?
  1. <?php
  2. namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
  3. use Doctrine\DBAL\Schema\LegacySchemaManagerFactory;
  4. use Doctrine\ORM\EntityManager;
  5. use Doctrine\ORM\EntityRepository;
  6. use Doctrine\ORM\Mapping\ClassMetadataFactory;
  7. use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
  8. use Doctrine\ORM\Proxy\ProxyFactory;
  9. use InvalidArgumentException;
  10. use ReflectionClass;
  11. use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
  12. use Symfony\Component\Config\Definition\Builder\NodeDefinition;
  13. use Symfony\Component\Config\Definition\Builder\TreeBuilder;
  14. use Symfony\Component\Config\Definition\ConfigurationInterface;
  15. use Symfony\Component\DependencyInjection\Exception\LogicException;
  16. use function array_diff_key;
  17. use function array_intersect_key;
  18. use function array_key_exists;
  19. use function array_keys;
  20. use function array_pop;
  21. use function assert;
  22. use function class_exists;
  23. use function constant;
  24. use function count;
  25. use function defined;
  26. use function implode;
  27. use function in_array;
  28. use function is_array;
  29. use function is_bool;
  30. use function is_int;
  31. use function is_string;
  32. use function key;
  33. use function method_exists;
  34. use function reset;
  35. use function sprintf;
  36. use function strlen;
  37. use function strpos;
  38. use function strtoupper;
  39. use function substr;
  40. use function trigger_deprecation;
  41. /**
  42. * This class contains the configuration information for the bundle
  43. *
  44. * This information is solely responsible for how the different configuration
  45. * sections are normalized, and merged.
  46. *
  47. * @final since 2.9
  48. */
  49. class Configuration implements ConfigurationInterface
  50. {
  51. private bool $debug;
  52. /** @param bool $debug Whether to use the debug mode */
  53. public function __construct(bool $debug)
  54. {
  55. $this->debug = $debug;
  56. }
  57. public function getConfigTreeBuilder(): TreeBuilder
  58. {
  59. $treeBuilder = new TreeBuilder('doctrine');
  60. $rootNode = $treeBuilder->getRootNode();
  61. $this->addDbalSection($rootNode);
  62. $this->addOrmSection($rootNode);
  63. return $treeBuilder;
  64. }
  65. /**
  66. * Add DBAL section to configuration tree
  67. */
  68. private function addDbalSection(ArrayNodeDefinition $node): void
  69. {
  70. // Key that should not be rewritten to the connection config
  71. $excludedKeys = ['default_connection' => true, 'driver_schemes' => true, 'driver_scheme' => true, 'types' => true, 'type' => true];
  72. $node
  73. ->children()
  74. ->arrayNode('dbal')
  75. ->beforeNormalization()
  76. ->ifTrue(static function ($v) use ($excludedKeys) {
  77. if (! is_array($v)) {
  78. return false;
  79. }
  80. if (array_key_exists('connections', $v) || array_key_exists('connection', $v)) {
  81. return false;
  82. }
  83. // Is there actually anything to use once excluded keys are considered?
  84. return (bool) array_diff_key($v, $excludedKeys);
  85. })
  86. ->then(static function ($v) use ($excludedKeys) {
  87. $connection = [];
  88. foreach ($v as $key => $value) {
  89. if (isset($excludedKeys[$key])) {
  90. continue;
  91. }
  92. $connection[$key] = $v[$key];
  93. unset($v[$key]);
  94. }
  95. $v['connections'] = [($v['default_connection'] ?? 'default') => $connection];
  96. return $v;
  97. })
  98. ->end()
  99. ->children()
  100. ->scalarNode('default_connection')->end()
  101. ->end()
  102. ->fixXmlConfig('type')
  103. ->children()
  104. ->arrayNode('types')
  105. ->useAttributeAsKey('name')
  106. ->prototype('array')
  107. ->beforeNormalization()
  108. ->ifString()
  109. ->then(static function ($v) {
  110. return ['class' => $v];
  111. })
  112. ->end()
  113. ->children()
  114. ->scalarNode('class')->isRequired()->end()
  115. ->booleanNode('commented')
  116. ->setDeprecated(
  117. 'doctrine/doctrine-bundle',
  118. '2.0',
  119. 'The doctrine-bundle type commenting features were removed; the corresponding config parameter was deprecated in 2.0 and will be dropped in 3.0.',
  120. )
  121. ->end()
  122. ->end()
  123. ->end()
  124. ->end()
  125. ->end()
  126. ->fixXmlConfig('driver_scheme')
  127. ->children()
  128. ->arrayNode('driver_schemes')
  129. ->useAttributeAsKey('scheme')
  130. ->normalizeKeys(false)
  131. ->scalarPrototype()->end()
  132. ->info('Defines a driver for given URL schemes. Schemes being driver names cannot be redefined. However, other default schemes can be overwritten.')
  133. ->validate()
  134. ->always()
  135. ->then(static function (array $value) {
  136. $unsupportedSchemes = [];
  137. foreach ($value as $scheme => $driver) {
  138. if (! in_array($scheme, ['pdo-mysql', 'pdo-sqlite', 'pdo-pgsql', 'pdo-oci', 'oci8', 'ibm-db2', 'pdo-sqlsrv', 'mysqli', 'pgsql', 'sqlsrv', 'sqlite3'], true)) {
  139. continue;
  140. }
  141. $unsupportedSchemes[] = $scheme;
  142. }
  143. if ($unsupportedSchemes) {
  144. throw new InvalidArgumentException(sprintf('Registering a scheme with the name of one of the official drivers is forbidden, as those are defined in DBAL itself. The following schemes are forbidden: %s', implode(', ', $unsupportedSchemes)));
  145. }
  146. return $value;
  147. })
  148. ->end()
  149. ->end()
  150. ->end()
  151. ->fixXmlConfig('connection')
  152. ->append($this->getDbalConnectionsNode())
  153. ->end();
  154. }
  155. /**
  156. * Return the dbal connections node
  157. */
  158. private function getDbalConnectionsNode(): ArrayNodeDefinition
  159. {
  160. $treeBuilder = new TreeBuilder('connections');
  161. $node = $treeBuilder->getRootNode();
  162. $connectionNode = $node
  163. ->requiresAtLeastOneElement()
  164. ->useAttributeAsKey('name')
  165. ->prototype('array');
  166. assert($connectionNode instanceof ArrayNodeDefinition);
  167. $this->configureDbalDriverNode($connectionNode);
  168. $collationKey = defined('Doctrine\DBAL\Connection::PARAM_ASCII_STR_ARRAY')
  169. ? 'collate'
  170. : 'collation';
  171. $connectionNode
  172. ->fixXmlConfig('option')
  173. ->fixXmlConfig('mapping_type')
  174. ->fixXmlConfig('slave')
  175. ->fixXmlConfig('replica')
  176. ->fixXmlConfig('default_table_option')
  177. ->children()
  178. ->scalarNode('driver')->defaultValue('pdo_mysql')->end()
  179. ->scalarNode('platform_service')
  180. ->setDeprecated(
  181. 'doctrine/doctrine-bundle',
  182. '2.9',
  183. 'The "platform_service" configuration key is deprecated since doctrine-bundle 2.9. DBAL 4 will not support setting a custom platform via connection params anymore.',
  184. )
  185. ->end()
  186. ->booleanNode('auto_commit')->end()
  187. ->scalarNode('schema_filter')->end()
  188. ->booleanNode('logging')->defaultValue($this->debug)->end()
  189. ->booleanNode('profiling')->defaultValue($this->debug)->end()
  190. ->booleanNode('profiling_collect_backtrace')
  191. ->defaultValue(false)
  192. ->info('Enables collecting backtraces when profiling is enabled')
  193. ->end()
  194. ->booleanNode('profiling_collect_schema_errors')
  195. ->defaultValue(true)
  196. ->info('Enables collecting schema errors when profiling is enabled')
  197. ->end()
  198. ->booleanNode('disable_type_comments')->end()
  199. ->scalarNode('server_version')->end()
  200. ->scalarNode('driver_class')->end()
  201. ->scalarNode('wrapper_class')->end()
  202. ->booleanNode('keep_slave')
  203. ->setDeprecated(
  204. 'doctrine/doctrine-bundle',
  205. '2.2',
  206. 'The "keep_slave" configuration key is deprecated since doctrine-bundle 2.2. Use the "keep_replica" configuration key instead.',
  207. )
  208. ->end()
  209. ->booleanNode('keep_replica')->end()
  210. ->arrayNode('options')
  211. ->useAttributeAsKey('key')
  212. ->prototype('variable')->end()
  213. ->end()
  214. ->arrayNode('mapping_types')
  215. ->useAttributeAsKey('name')
  216. ->prototype('scalar')->end()
  217. ->end()
  218. ->arrayNode('default_table_options')
  219. ->info(sprintf(
  220. "This option is used by the schema-tool and affects generated SQL. Possible keys include 'charset','%s', and 'engine'.",
  221. $collationKey,
  222. ))
  223. ->useAttributeAsKey('name')
  224. ->prototype('scalar')->end()
  225. ->end()
  226. ->scalarNode('schema_manager_factory')
  227. ->cannotBeEmpty()
  228. ->defaultValue($this->getDefaultSchemaManagerFactory())
  229. ->end()
  230. ->scalarNode('result_cache')->end()
  231. ->end();
  232. // dbal < 2.11
  233. $slaveNode = $connectionNode
  234. ->children()
  235. ->arrayNode('slaves')
  236. ->setDeprecated(
  237. 'doctrine/doctrine-bundle',
  238. '2.2',
  239. 'The "slaves" configuration key will be renamed to "replicas" in doctrine-bundle 3.0. "slaves" is deprecated since doctrine-bundle 2.2.',
  240. )
  241. ->useAttributeAsKey('name')
  242. ->prototype('array');
  243. $this->configureDbalDriverNode($slaveNode);
  244. // dbal >= 2.11
  245. $replicaNode = $connectionNode
  246. ->children()
  247. ->arrayNode('replicas')
  248. ->useAttributeAsKey('name')
  249. ->prototype('array');
  250. $this->configureDbalDriverNode($replicaNode);
  251. return $node;
  252. }
  253. /**
  254. * Adds config keys related to params processed by the DBAL drivers
  255. *
  256. * These keys are available for replica configurations too.
  257. */
  258. private function configureDbalDriverNode(ArrayNodeDefinition $node): void
  259. {
  260. $node
  261. ->validate()
  262. ->always(static function (array $values) {
  263. if (! isset($values['url'])) {
  264. return $values;
  265. }
  266. $urlConflictingOptions = ['host' => true, 'port' => true, 'user' => true, 'password' => true, 'path' => true, 'dbname' => true, 'unix_socket' => true, 'memory' => true];
  267. $urlConflictingValues = array_keys(array_intersect_key($values, $urlConflictingOptions));
  268. if ($urlConflictingValues) {
  269. $tail = count($urlConflictingValues) > 1 ? sprintf('or "%s" options', array_pop($urlConflictingValues)) : 'option';
  270. trigger_deprecation(
  271. 'doctrine/doctrine-bundle',
  272. '2.4',
  273. 'Setting the "doctrine.dbal.%s" %s while the "url" one is defined is deprecated',
  274. implode('", "', $urlConflictingValues),
  275. $tail,
  276. );
  277. }
  278. return $values;
  279. })
  280. ->end()
  281. ->children()
  282. ->scalarNode('url')->info('A URL with connection information; any parameter value parsed from this string will override explicitly set parameters')->end()
  283. ->scalarNode('dbname')->end()
  284. ->scalarNode('host')->info('Defaults to "localhost" at runtime.')->end()
  285. ->scalarNode('port')->info('Defaults to null at runtime.')->end()
  286. ->scalarNode('user')->info('Defaults to "root" at runtime.')->end()
  287. ->scalarNode('password')->info('Defaults to null at runtime.')->end()
  288. ->booleanNode('override_url')->setDeprecated(
  289. 'doctrine/doctrine-bundle',
  290. '2.4',
  291. 'The "doctrine.dbal.override_url" configuration key is deprecated.',
  292. )->end()
  293. ->scalarNode('dbname_suffix')->end()
  294. ->scalarNode('application_name')->end()
  295. ->scalarNode('charset')->end()
  296. ->scalarNode('path')->end()
  297. ->booleanNode('memory')->end()
  298. ->scalarNode('unix_socket')->info('The unix socket to use for MySQL')->end()
  299. ->booleanNode('persistent')->info('True to use as persistent connection for the ibm_db2 driver')->end()
  300. ->scalarNode('protocol')->info('The protocol to use for the ibm_db2 driver (default to TCPIP if omitted)')->end()
  301. ->booleanNode('service')
  302. ->info('True to use SERVICE_NAME as connection parameter instead of SID for Oracle')
  303. ->end()
  304. ->scalarNode('servicename')
  305. ->info(
  306. 'Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter ' .
  307. 'for Oracle depending on the service parameter.',
  308. )
  309. ->end()
  310. ->scalarNode('sessionMode')
  311. ->info('The session mode to use for the oci8 driver')
  312. ->end()
  313. ->scalarNode('server')
  314. ->info('The name of a running database server to connect to for SQL Anywhere.')
  315. ->end()
  316. ->scalarNode('default_dbname')
  317. ->info(
  318. 'Override the default database (postgres) to connect to for PostgreSQL connexion.',
  319. )
  320. ->end()
  321. ->scalarNode('sslmode')
  322. ->info(
  323. 'Determines whether or with what priority a SSL TCP/IP connection will be negotiated with ' .
  324. 'the server for PostgreSQL.',
  325. )
  326. ->end()
  327. ->scalarNode('sslrootcert')
  328. ->info(
  329. 'The name of a file containing SSL certificate authority (CA) certificate(s). ' .
  330. 'If the file exists, the server\'s certificate will be verified to be signed by one of these authorities.',
  331. )
  332. ->end()
  333. ->scalarNode('sslcert')
  334. ->info(
  335. 'The path to the SSL client certificate file for PostgreSQL.',
  336. )
  337. ->end()
  338. ->scalarNode('sslkey')
  339. ->info(
  340. 'The path to the SSL client key file for PostgreSQL.',
  341. )
  342. ->end()
  343. ->scalarNode('sslcrl')
  344. ->info(
  345. 'The file name of the SSL certificate revocation list for PostgreSQL.',
  346. )
  347. ->end()
  348. ->booleanNode('pooled')->info('True to use a pooled server with the oci8/pdo_oracle driver')->end()
  349. ->booleanNode('MultipleActiveResultSets')->info('Configuring MultipleActiveResultSets for the pdo_sqlsrv driver')->end()
  350. ->booleanNode('use_savepoints')->info('Use savepoints for nested transactions')->end()
  351. ->scalarNode('instancename')
  352. ->info(
  353. 'Optional parameter, complete whether to add the INSTANCE_NAME parameter in the connection.' .
  354. ' It is generally used to connect to an Oracle RAC server to select the name' .
  355. ' of a particular instance.',
  356. )
  357. ->end()
  358. ->scalarNode('connectstring')
  359. ->info(
  360. 'Complete Easy Connect connection descriptor, see https://docs.oracle.com/database/121/NETAG/naming.htm.' .
  361. 'When using this option, you will still need to provide the user and password parameters, but the other ' .
  362. 'parameters will no longer be used. Note that when using this parameter, the getHost and getPort methods' .
  363. ' from Doctrine\DBAL\Connection will no longer function as expected.',
  364. )
  365. ->end()
  366. ->end()
  367. ->beforeNormalization()
  368. ->ifTrue(static function ($v) {
  369. return ! isset($v['sessionMode']) && isset($v['session_mode']);
  370. })
  371. ->then(static function ($v) {
  372. $v['sessionMode'] = $v['session_mode'];
  373. unset($v['session_mode']);
  374. return $v;
  375. })
  376. ->end()
  377. ->beforeNormalization()
  378. ->ifTrue(static function ($v) {
  379. return ! isset($v['MultipleActiveResultSets']) && isset($v['multiple_active_result_sets']);
  380. })
  381. ->then(static function ($v) {
  382. $v['MultipleActiveResultSets'] = $v['multiple_active_result_sets'];
  383. unset($v['multiple_active_result_sets']);
  384. return $v;
  385. })
  386. ->end();
  387. }
  388. /**
  389. * Add the ORM section to configuration tree
  390. */
  391. private function addOrmSection(ArrayNodeDefinition $node): void
  392. {
  393. // Key that should not be rewritten to the entity-manager config
  394. $excludedKeys = [
  395. 'default_entity_manager' => true,
  396. 'auto_generate_proxy_classes' => true,
  397. 'enable_lazy_ghost_objects' => true,
  398. 'proxy_dir' => true,
  399. 'proxy_namespace' => true,
  400. 'resolve_target_entities' => true,
  401. 'resolve_target_entity' => true,
  402. 'controller_resolver' => true,
  403. ];
  404. $node
  405. ->children()
  406. ->arrayNode('orm')
  407. ->beforeNormalization()
  408. ->ifTrue(static function ($v) use ($excludedKeys) {
  409. if (! empty($v) && ! class_exists(EntityManager::class)) {
  410. throw new LogicException('The doctrine/orm package is required when the doctrine.orm config is set.');
  411. }
  412. if (! is_array($v)) {
  413. return false;
  414. }
  415. if (array_key_exists('entity_managers', $v) || array_key_exists('entity_manager', $v)) {
  416. return false;
  417. }
  418. // Is there actually anything to use once excluded keys are considered?
  419. return (bool) array_diff_key($v, $excludedKeys);
  420. })
  421. ->then(static function ($v) use ($excludedKeys) {
  422. $entityManager = [];
  423. foreach ($v as $key => $value) {
  424. if (isset($excludedKeys[$key])) {
  425. continue;
  426. }
  427. $entityManager[$key] = $v[$key];
  428. unset($v[$key]);
  429. }
  430. $v['entity_managers'] = [($v['default_entity_manager'] ?? 'default') => $entityManager];
  431. return $v;
  432. })
  433. ->end()
  434. ->children()
  435. ->scalarNode('default_entity_manager')->end()
  436. ->scalarNode('auto_generate_proxy_classes')->defaultValue(false)
  437. ->info('Auto generate mode possible values are: "NEVER", "ALWAYS", "FILE_NOT_EXISTS", "EVAL", "FILE_NOT_EXISTS_OR_CHANGED"')
  438. ->validate()
  439. ->ifTrue(function ($v) {
  440. $generationModes = $this->getAutoGenerateModes();
  441. if (is_int($v) && in_array($v, $generationModes['values']/*array(0, 1, 2, 3)*/)) {
  442. return false;
  443. }
  444. if (is_bool($v)) {
  445. return false;
  446. }
  447. if (is_string($v)) {
  448. if (in_array(strtoupper($v), $generationModes['names']/*array('NEVER', 'ALWAYS', 'FILE_NOT_EXISTS', 'EVAL', 'FILE_NOT_EXISTS_OR_CHANGED')*/)) {
  449. return false;
  450. }
  451. }
  452. return true;
  453. })
  454. ->thenInvalid('Invalid auto generate mode value %s')
  455. ->end()
  456. ->validate()
  457. ->ifString()
  458. ->then(static function ($v) {
  459. return constant('Doctrine\ORM\Proxy\ProxyFactory::AUTOGENERATE_' . strtoupper($v));
  460. })
  461. ->end()
  462. ->end()
  463. ->booleanNode('enable_lazy_ghost_objects')->defaultValue(! method_exists(ProxyFactory::class, 'resetUninitializedProxy'))
  464. ->end()
  465. ->scalarNode('proxy_dir')->defaultValue('%kernel.cache_dir%/doctrine/orm/Proxies')->end()
  466. ->scalarNode('proxy_namespace')->defaultValue('Proxies')->end()
  467. ->arrayNode('controller_resolver')
  468. ->canBeDisabled()
  469. ->children()
  470. ->booleanNode('auto_mapping')
  471. ->defaultTrue()
  472. ->info('Set to false to disable using route placeholders as lookup criteria when the primary key doesn\'t match the argument name')
  473. ->end()
  474. ->booleanNode('evict_cache')
  475. ->info('Set to true to fetch the entity from the database instead of using the cache, if any')
  476. ->defaultFalse()
  477. ->end()
  478. ->end()
  479. ->end()
  480. ->end()
  481. ->fixXmlConfig('entity_manager')
  482. ->append($this->getOrmEntityManagersNode())
  483. ->fixXmlConfig('resolve_target_entity', 'resolve_target_entities')
  484. ->append($this->getOrmTargetEntityResolverNode())
  485. ->end()
  486. ->end();
  487. }
  488. /**
  489. * Return ORM target entity resolver node
  490. */
  491. private function getOrmTargetEntityResolverNode(): NodeDefinition
  492. {
  493. $treeBuilder = new TreeBuilder('resolve_target_entities');
  494. $node = $treeBuilder->getRootNode();
  495. $node
  496. ->useAttributeAsKey('interface')
  497. ->prototype('scalar')
  498. ->cannotBeEmpty()
  499. ->end();
  500. return $node;
  501. }
  502. /**
  503. * Return ORM entity listener node
  504. */
  505. private function getOrmEntityListenersNode(): NodeDefinition
  506. {
  507. $treeBuilder = new TreeBuilder('entity_listeners');
  508. $node = $treeBuilder->getRootNode();
  509. $normalizer = static function ($mappings) {
  510. $entities = [];
  511. foreach ($mappings as $entityClass => $mapping) {
  512. $listeners = [];
  513. foreach ($mapping as $listenerClass => $listenerEvent) {
  514. $events = [];
  515. foreach ($listenerEvent as $eventType => $eventMapping) {
  516. if ($eventMapping === null) {
  517. $eventMapping = [null];
  518. }
  519. foreach ($eventMapping as $method) {
  520. $events[] = [
  521. 'type' => $eventType,
  522. 'method' => $method,
  523. ];
  524. }
  525. }
  526. $listeners[] = [
  527. 'class' => $listenerClass,
  528. 'event' => $events,
  529. ];
  530. }
  531. $entities[] = [
  532. 'class' => $entityClass,
  533. 'listener' => $listeners,
  534. ];
  535. }
  536. return ['entities' => $entities];
  537. };
  538. $node
  539. ->beforeNormalization()
  540. // Yaml normalization
  541. ->ifTrue(static function ($v) {
  542. return is_array(reset($v)) && is_string(key(reset($v)));
  543. })
  544. ->then($normalizer)
  545. ->end()
  546. ->fixXmlConfig('entity', 'entities')
  547. ->children()
  548. ->arrayNode('entities')
  549. ->useAttributeAsKey('class')
  550. ->prototype('array')
  551. ->fixXmlConfig('listener')
  552. ->children()
  553. ->arrayNode('listeners')
  554. ->useAttributeAsKey('class')
  555. ->prototype('array')
  556. ->fixXmlConfig('event')
  557. ->children()
  558. ->arrayNode('events')
  559. ->prototype('array')
  560. ->children()
  561. ->scalarNode('type')->end()
  562. ->scalarNode('method')->defaultNull()->end()
  563. ->end()
  564. ->end()
  565. ->end()
  566. ->end()
  567. ->end()
  568. ->end()
  569. ->end()
  570. ->end()
  571. ->end()
  572. ->end();
  573. return $node;
  574. }
  575. /**
  576. * Return ORM entity manager node
  577. */
  578. private function getOrmEntityManagersNode(): ArrayNodeDefinition
  579. {
  580. $treeBuilder = new TreeBuilder('entity_managers');
  581. $node = $treeBuilder->getRootNode();
  582. $node
  583. ->requiresAtLeastOneElement()
  584. ->useAttributeAsKey('name')
  585. ->prototype('array')
  586. ->addDefaultsIfNotSet()
  587. ->append($this->getOrmCacheDriverNode('query_cache_driver'))
  588. ->append($this->getOrmCacheDriverNode('metadata_cache_driver'))
  589. ->append($this->getOrmCacheDriverNode('result_cache_driver'))
  590. ->append($this->getOrmEntityListenersNode())
  591. ->fixXmlConfig('schema_ignore_class', 'schema_ignore_classes')
  592. ->children()
  593. ->scalarNode('connection')->end()
  594. ->scalarNode('class_metadata_factory_name')->defaultValue(ClassMetadataFactory::class)->end()
  595. ->scalarNode('default_repository_class')->defaultValue(EntityRepository::class)->end()
  596. ->scalarNode('auto_mapping')->defaultFalse()->end()
  597. ->scalarNode('naming_strategy')->defaultValue('doctrine.orm.naming_strategy.default')->end()
  598. ->scalarNode('quote_strategy')->defaultValue('doctrine.orm.quote_strategy.default')->end()
  599. ->scalarNode('entity_listener_resolver')->defaultNull()->end()
  600. ->scalarNode('repository_factory')->defaultValue('doctrine.orm.container_repository_factory')->end()
  601. ->arrayNode('schema_ignore_classes')
  602. ->prototype('scalar')->end()
  603. ->end()
  604. ->booleanNode('report_fields_where_declared')
  605. ->defaultValue(! class_exists(AnnotationDriver::class))
  606. ->info('Set to "true" to opt-in to the new mapping driver mode that was added in Doctrine ORM 2.16 and will be mandatory in ORM 3.0. See https://github.com/doctrine/orm/pull/10455.')
  607. ->validate()
  608. ->ifTrue(static fn (bool $v): bool => ! class_exists(AnnotationDriver::class) && ! $v)
  609. ->thenInvalid('The setting "report_fields_where_declared" cannot be disabled for ORM 3.')
  610. ->end()
  611. ->end()
  612. ->booleanNode('validate_xml_mapping')->defaultFalse()->info('Set to "true" to opt-in to the new mapping driver mode that was added in Doctrine ORM 2.14 and will be mandatory in ORM 3.0. See https://github.com/doctrine/orm/pull/6728.')->end()
  613. ->end()
  614. ->children()
  615. ->arrayNode('second_level_cache')
  616. ->children()
  617. ->append($this->getOrmCacheDriverNode('region_cache_driver'))
  618. ->scalarNode('region_lock_lifetime')->defaultValue(60)->end()
  619. ->booleanNode('log_enabled')->defaultValue($this->debug)->end()
  620. ->scalarNode('region_lifetime')->defaultValue(3600)->end()
  621. ->booleanNode('enabled')->defaultValue(true)->end()
  622. ->scalarNode('factory')->end()
  623. ->end()
  624. ->fixXmlConfig('region')
  625. ->children()
  626. ->arrayNode('regions')
  627. ->useAttributeAsKey('name')
  628. ->prototype('array')
  629. ->children()
  630. ->append($this->getOrmCacheDriverNode('cache_driver'))
  631. ->scalarNode('lock_path')->defaultValue('%kernel.cache_dir%/doctrine/orm/slc/filelock')->end()
  632. ->scalarNode('lock_lifetime')->defaultValue(60)->end()
  633. ->scalarNode('type')->defaultValue('default')->end()
  634. ->scalarNode('lifetime')->defaultValue(0)->end()
  635. ->scalarNode('service')->end()
  636. ->scalarNode('name')->end()
  637. ->end()
  638. ->end()
  639. ->end()
  640. ->end()
  641. ->fixXmlConfig('logger')
  642. ->children()
  643. ->arrayNode('loggers')
  644. ->useAttributeAsKey('name')
  645. ->prototype('array')
  646. ->children()
  647. ->scalarNode('name')->end()
  648. ->scalarNode('service')->end()
  649. ->end()
  650. ->end()
  651. ->end()
  652. ->end()
  653. ->end()
  654. ->end()
  655. ->fixXmlConfig('hydrator')
  656. ->children()
  657. ->arrayNode('hydrators')
  658. ->useAttributeAsKey('name')
  659. ->prototype('scalar')->end()
  660. ->end()
  661. ->end()
  662. ->fixXmlConfig('mapping')
  663. ->children()
  664. ->arrayNode('mappings')
  665. ->useAttributeAsKey('name')
  666. ->prototype('array')
  667. ->beforeNormalization()
  668. ->ifString()
  669. ->then(static function ($v) {
  670. return ['type' => $v];
  671. })
  672. ->end()
  673. ->treatNullLike([])
  674. ->treatFalseLike(['mapping' => false])
  675. ->performNoDeepMerging()
  676. ->children()
  677. ->scalarNode('mapping')->defaultValue(true)->end()
  678. ->scalarNode('type')->end()
  679. ->scalarNode('dir')->end()
  680. ->scalarNode('alias')->end()
  681. ->scalarNode('prefix')->end()
  682. ->booleanNode('is_bundle')->end()
  683. ->end()
  684. ->end()
  685. ->end()
  686. ->arrayNode('dql')
  687. ->fixXmlConfig('string_function')
  688. ->fixXmlConfig('numeric_function')
  689. ->fixXmlConfig('datetime_function')
  690. ->children()
  691. ->arrayNode('string_functions')
  692. ->useAttributeAsKey('name')
  693. ->prototype('scalar')->end()
  694. ->end()
  695. ->arrayNode('numeric_functions')
  696. ->useAttributeAsKey('name')
  697. ->prototype('scalar')->end()
  698. ->end()
  699. ->arrayNode('datetime_functions')
  700. ->useAttributeAsKey('name')
  701. ->prototype('scalar')->end()
  702. ->end()
  703. ->end()
  704. ->end()
  705. ->end()
  706. ->fixXmlConfig('filter')
  707. ->children()
  708. ->arrayNode('filters')
  709. ->info('Register SQL Filters in the entity manager')
  710. ->useAttributeAsKey('name')
  711. ->prototype('array')
  712. ->beforeNormalization()
  713. ->ifString()
  714. ->then(static function ($v) {
  715. return ['class' => $v];
  716. })
  717. ->end()
  718. ->beforeNormalization()
  719. // The content of the XML node is returned as the "value" key so we need to rename it
  720. ->ifTrue(static function ($v) {
  721. return is_array($v) && isset($v['value']);
  722. })
  723. ->then(static function ($v) {
  724. $v['class'] = $v['value'];
  725. unset($v['value']);
  726. return $v;
  727. })
  728. ->end()
  729. ->fixXmlConfig('parameter')
  730. ->children()
  731. ->scalarNode('class')->isRequired()->end()
  732. ->booleanNode('enabled')->defaultFalse()->end()
  733. ->arrayNode('parameters')
  734. ->useAttributeAsKey('name')
  735. ->prototype('variable')->end()
  736. ->end()
  737. ->end()
  738. ->end()
  739. ->end()
  740. ->end()
  741. ->end();
  742. return $node;
  743. }
  744. /**
  745. * Return a ORM cache driver node for an given entity manager
  746. */
  747. private function getOrmCacheDriverNode(string $name): ArrayNodeDefinition
  748. {
  749. $treeBuilder = new TreeBuilder($name);
  750. $node = $treeBuilder->getRootNode();
  751. $node
  752. ->beforeNormalization()
  753. ->ifString()
  754. ->then(static function ($v): array {
  755. return ['type' => $v];
  756. })
  757. ->end()
  758. ->children()
  759. ->scalarNode('type')->defaultNull()->end()
  760. ->scalarNode('id')->end()
  761. ->scalarNode('pool')->end()
  762. ->end();
  763. if ($name !== 'metadata_cache_driver') {
  764. $node->addDefaultsIfNotSet();
  765. }
  766. return $node;
  767. }
  768. /**
  769. * Find proxy auto generate modes for their names and int values
  770. *
  771. * @return array{names: list<string>, values: list<int>}
  772. */
  773. private function getAutoGenerateModes(): array
  774. {
  775. $constPrefix = 'AUTOGENERATE_';
  776. $prefixLen = strlen($constPrefix);
  777. $refClass = new ReflectionClass(ProxyFactory::class);
  778. $constsArray = $refClass->getConstants();
  779. $namesArray = [];
  780. $valuesArray = [];
  781. foreach ($constsArray as $key => $value) {
  782. if (strpos($key, $constPrefix) !== 0) {
  783. continue;
  784. }
  785. $namesArray[] = substr($key, $prefixLen);
  786. $valuesArray[] = (int) $value;
  787. }
  788. return [
  789. 'names' => $namesArray,
  790. 'values' => $valuesArray,
  791. ];
  792. }
  793. private function getDefaultSchemaManagerFactory(): string
  794. {
  795. if (class_exists(LegacySchemaManagerFactory::class)) {
  796. return 'doctrine.dbal.legacy_schema_manager_factory';
  797. }
  798. return 'doctrine.dbal.default_schema_manager_factory';
  799. }
  800. }