vendor/doctrine/mongodb-odm/src/Configuration.php line 266

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\ODM\MongoDB;
  4. use Doctrine\Common\Annotations\AnnotationReader;
  5. use Doctrine\Common\Cache\Cache;
  6. use Doctrine\Common\Cache\Psr6\CacheAdapter;
  7. use Doctrine\Common\Cache\Psr6\DoctrineProvider;
  8. use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory;
  9. use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactoryInterface;
  10. use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
  11. use Doctrine\ODM\MongoDB\PersistentCollection\DefaultPersistentCollectionFactory;
  12. use Doctrine\ODM\MongoDB\PersistentCollection\DefaultPersistentCollectionGenerator;
  13. use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionFactory;
  14. use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionGenerator;
  15. use Doctrine\ODM\MongoDB\Proxy\FileLocator;
  16. use Doctrine\ODM\MongoDB\Repository\DefaultGridFSRepository;
  17. use Doctrine\ODM\MongoDB\Repository\DefaultRepositoryFactory;
  18. use Doctrine\ODM\MongoDB\Repository\DocumentRepository;
  19. use Doctrine\ODM\MongoDB\Repository\GridFSRepository;
  20. use Doctrine\ODM\MongoDB\Repository\RepositoryFactory;
  21. use Doctrine\Persistence\Mapping\Driver\MappingDriver;
  22. use Doctrine\Persistence\ObjectRepository;
  23. use InvalidArgumentException;
  24. use Jean85\PrettyVersions;
  25. use LogicException;
  26. use MongoDB\Client;
  27. use MongoDB\Driver\Manager;
  28. use MongoDB\Driver\WriteConcern;
  29. use ProxyManager\Configuration as ProxyManagerConfiguration;
  30. use ProxyManager\Factory\LazyLoadingGhostFactory;
  31. use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
  32. use ProxyManager\GeneratorStrategy\FileWriterGeneratorStrategy;
  33. use Psr\Cache\CacheItemPoolInterface;
  34. use ReflectionClass;
  35. use stdClass;
  36. use Symfony\Component\VarExporter\LazyGhostTrait;
  37. use Throwable;
  38. use function array_diff_key;
  39. use function array_intersect_key;
  40. use function array_key_exists;
  41. use function class_exists;
  42. use function interface_exists;
  43. use function is_string;
  44. use function trait_exists;
  45. use function trigger_deprecation;
  46. use function trim;
  47. use const PHP_VERSION_ID;
  48. /**
  49. * Configuration class for the DocumentManager. When setting up your DocumentManager
  50. * you can optionally specify an instance of this class as the second argument.
  51. * If you do not pass a configuration object, a blank one will be created for you.
  52. *
  53. * <?php
  54. *
  55. * $config = new Configuration();
  56. * $dm = DocumentManager::create(new Connection(), $config);
  57. *
  58. * @phpstan-import-type CommitOptions from UnitOfWork
  59. * @phpstan-type KmsProvider array{type: string, ...}
  60. */
  61. class Configuration
  62. {
  63. /**
  64. * Never autogenerate a proxy/hydrator/persistent collection and rely that
  65. * it was generated by some process before deployment. Copied from
  66. * \Doctrine\Common\Proxy\AbstractProxyFactory.
  67. */
  68. public const AUTOGENERATE_NEVER = 0;
  69. /**
  70. * Always generates a new proxy/hydrator/persistent collection in every request.
  71. *
  72. * This is only sane during development.
  73. * Copied from \Doctrine\Common\Proxy\AbstractProxyFactory.
  74. */
  75. public const AUTOGENERATE_ALWAYS = 1;
  76. /**
  77. * Autogenerate the proxy/hydrator/persistent collection class when the file does not exist.
  78. *
  79. * This strategy causes a file exists call whenever any proxy/hydrator is used the
  80. * first time in a request. Copied from \Doctrine\Common\Proxy\AbstractProxyFactory.
  81. */
  82. public const AUTOGENERATE_FILE_NOT_EXISTS = 2;
  83. /**
  84. * Generate the proxy/hydrator/persistent collection classes using eval().
  85. *
  86. * This strategy is only sane for development.
  87. * Copied from \Doctrine\Common\Proxy\AbstractProxyFactory.
  88. */
  89. public const AUTOGENERATE_EVAL = 3;
  90. /**
  91. * Autogenerate the proxy class when the proxy file does not exist or
  92. * when the proxied file changed.
  93. *
  94. * This strategy causes a file_exists() call whenever any proxy is used the
  95. * first time in a request. When the proxied file is changed, the proxy will
  96. * be updated.
  97. */
  98. public const AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED = 4;
  99. /**
  100. * Array of attributes for this configuration instance.
  101. *
  102. * @phpstan-var array{
  103. * autoGenerateHydratorClasses?: self::AUTOGENERATE_*,
  104. * autoGeneratePersistentCollectionClasses?: self::AUTOGENERATE_*,
  105. * autoGenerateProxyClasses?: self::AUTOGENERATE_*,
  106. * classMetadataFactoryName?: class-string<ClassMetadataFactoryInterface>,
  107. * defaultCommitOptions?: CommitOptions,
  108. * defaultDocumentRepositoryClassName?: class-string<ObjectRepository<object>>,
  109. * defaultGridFSRepositoryClassName?: class-string<GridFSRepository<object>>,
  110. * defaultDB?: string,
  111. * documentNamespaces?: array<string, string>,
  112. * filters?: array<string, array{
  113. * class: class-string,
  114. * parameters: array<string, mixed>
  115. * }>,
  116. * hydratorDir?: string,
  117. * hydratorNamespace?: string,
  118. * metadataCacheImpl?: Cache,
  119. * metadataDriverImpl?: MappingDriver,
  120. * persistentCollectionFactory?: PersistentCollectionFactory,
  121. * persistentCollectionGenerator?: PersistentCollectionGenerator,
  122. * persistentCollectionDir?: string,
  123. * persistentCollectionNamespace?: string,
  124. * proxyDir?: string,
  125. * proxyNamespace?: string,
  126. * repositoryFactory?: RepositoryFactory,
  127. * kmsProvider?: KmsProvider,
  128. * defaultMasterKey?: array<string, mixed>|null,
  129. * autoEncryption?: array<string, mixed>,
  130. * }
  131. */
  132. private array $attributes = [];
  133. private ?CacheItemPoolInterface $metadataCache = null;
  134. /** @deprecated */
  135. private ProxyManagerConfiguration $proxyManagerConfiguration;
  136. private bool $useTransactionalFlush = false;
  137. private bool $lazyGhostObject = false;
  138. private bool $nativeLazyObject = false;
  139. private static string $version;
  140. /**
  141. * Provides the driver options to be used when creating the MongoDB client.
  142. *
  143. * @return array<string, mixed>
  144. */
  145. public function getDriverOptions(): array
  146. {
  147. $driverOptions = [
  148. 'driver' => [
  149. 'name' => 'doctrine-odm',
  150. 'version' => self::getVersion(),
  151. ],
  152. ];
  153. if (isset($this->attributes['kmsProvider'])) {
  154. $driverOptions['autoEncryption'] = $this->getAutoEncryptionOptions();
  155. }
  156. return $driverOptions;
  157. }
  158. /**
  159. * Get options to create a ClientEncryption instance.
  160. *
  161. * @see https://www.php.net/manual/en/mongodb-driver-clientencryption.construct.php
  162. *
  163. * @return array{keyVaultClient?: Client|Manager, keyVaultNamespace: string, kmsProviders: array<string, mixed>, tlsOptions?: array<string, mixed>}
  164. */
  165. public function getClientEncryptionOptions(): array
  166. {
  167. if (! isset($this->attributes['kmsProvider'])) {
  168. throw ConfigurationException::clientEncryptionOptionsNotSet();
  169. }
  170. return array_intersect_key($this->getAutoEncryptionOptions(), [
  171. 'keyVaultClient' => 1,
  172. 'keyVaultNamespace' => 1,
  173. 'kmsProviders' => 1,
  174. 'tlsOptions' => 1,
  175. ]);
  176. }
  177. /**
  178. * Adds a namespace under a certain alias.
  179. *
  180. * @deprecated Document short aliases are deprecated - use ::class constant instead.
  181. */
  182. public function addDocumentNamespace(string $alias, string $namespace): void
  183. {
  184. trigger_deprecation(
  185. 'doctrine/mongodb-odm',
  186. '2.3',
  187. 'Document short namespace aliases such as "%s" are deprecated, use ::class constant instead.',
  188. $alias,
  189. );
  190. $this->attributes['documentNamespaces'][$alias] = $namespace;
  191. }
  192. /**
  193. * Resolves a registered namespace alias to the full namespace.
  194. *
  195. * @deprecated Document short aliases are deprecated - use ::class constant instead.
  196. *
  197. * @throws MongoDBException
  198. */
  199. public function getDocumentNamespace(string $documentNamespaceAlias): string
  200. {
  201. trigger_deprecation(
  202. 'doctrine/mongodb-odm',
  203. '2.3',
  204. 'Document short namespace aliases such as "%s" are deprecated, use ::class constant instead.',
  205. $documentNamespaceAlias,
  206. );
  207. if (! isset($this->attributes['documentNamespaces'][$documentNamespaceAlias])) {
  208. throw MongoDBException::unknownDocumentNamespace($documentNamespaceAlias);
  209. }
  210. return trim($this->attributes['documentNamespaces'][$documentNamespaceAlias], '\\');
  211. }
  212. /**
  213. * Retrieves the list of registered document namespace aliases.
  214. *
  215. * @deprecated Document short aliases are deprecated - use ::class constant instead.
  216. *
  217. * @return array<string, string>
  218. */
  219. public function getDocumentNamespaces(): array
  220. {
  221. trigger_deprecation(
  222. 'doctrine/mongodb-odm',
  223. '2.3',
  224. 'Document short namespace aliases are deprecated, use ::class constant instead.',
  225. );
  226. return $this->attributes['documentNamespaces'];
  227. }
  228. /**
  229. * Set the document alias map
  230. *
  231. * @deprecated Document short aliases are deprecated - use ::class constant instead.
  232. *
  233. * @param array<string, string> $documentNamespaces
  234. */
  235. public function setDocumentNamespaces(array $documentNamespaces): void
  236. {
  237. trigger_deprecation(
  238. 'doctrine/mongodb-odm',
  239. '2.3',
  240. 'Document short namespace aliases are deprecated, use ::class constant instead.',
  241. );
  242. $this->attributes['documentNamespaces'] = $documentNamespaces;
  243. }
  244. /**
  245. * Sets the cache driver implementation that is used for metadata caching.
  246. *
  247. * @todo Force parameter to be a Closure to ensure lazy evaluation
  248. * (as soon as a metadata cache is in effect, the driver never needs to initialize).
  249. */
  250. public function setMetadataDriverImpl(MappingDriver $driverImpl): void
  251. {
  252. $this->attributes['metadataDriverImpl'] = $driverImpl;
  253. }
  254. /**
  255. * Add a new default annotation driver with a correctly configured annotation reader.
  256. *
  257. * @param string[] $paths
  258. */
  259. public function newDefaultAnnotationDriver(array $paths = []): AnnotationDriver
  260. {
  261. $reader = new AnnotationReader();
  262. return new AnnotationDriver($reader, $paths);
  263. }
  264. /**
  265. * Gets the cache driver implementation that is used for the mapping metadata.
  266. */
  267. public function getMetadataDriverImpl(): ?MappingDriver
  268. {
  269. return $this->attributes['metadataDriverImpl'] ?? null;
  270. }
  271. public function getMetadataCacheImpl(): ?Cache
  272. {
  273. trigger_deprecation(
  274. 'doctrine/mongodb-odm',
  275. '2.2',
  276. 'Using "%s" is deprecated. Please use "%s::getMetadataCache" instead.',
  277. __METHOD__,
  278. self::class,
  279. );
  280. return $this->attributes['metadataCacheImpl'] ?? null;
  281. }
  282. public function setMetadataCacheImpl(Cache $cacheImpl): void
  283. {
  284. trigger_deprecation(
  285. 'doctrine/mongodb-odm',
  286. '2.2',
  287. 'Using "%s" is deprecated. Please use "%s::setMetadataCache" instead.',
  288. __METHOD__,
  289. self::class,
  290. );
  291. $this->attributes['metadataCacheImpl'] = $cacheImpl;
  292. $this->metadataCache = CacheAdapter::wrap($cacheImpl);
  293. }
  294. public function getMetadataCache(): ?CacheItemPoolInterface
  295. {
  296. return $this->metadataCache;
  297. }
  298. public function setMetadataCache(CacheItemPoolInterface $cache): void
  299. {
  300. $this->metadataCache = $cache;
  301. $this->attributes['metadataCacheImpl'] = DoctrineProvider::wrap($cache);
  302. }
  303. /**
  304. * Sets the directory where Doctrine generates any necessary proxy class files.
  305. */
  306. public function setProxyDir(string $dir): void
  307. {
  308. $this->attributes['proxyDir'] = $dir;
  309. unset($this->proxyManagerConfiguration);
  310. }
  311. /**
  312. * Gets the directory where Doctrine generates any necessary proxy class files.
  313. */
  314. public function getProxyDir(): ?string
  315. {
  316. return $this->attributes['proxyDir'] ?? null;
  317. }
  318. /**
  319. * Gets an int flag that indicates whether proxy classes should always be regenerated
  320. * during each script execution.
  321. *
  322. * @return self::AUTOGENERATE_*
  323. */
  324. public function getAutoGenerateProxyClasses(): int
  325. {
  326. return $this->attributes['autoGenerateProxyClasses'] ?? self::AUTOGENERATE_FILE_NOT_EXISTS;
  327. }
  328. /**
  329. * Sets an int flag that indicates whether proxy classes should always be regenerated
  330. * during each script execution.
  331. *
  332. * @param self::AUTOGENERATE_* $mode
  333. */
  334. public function setAutoGenerateProxyClasses(int $mode): void
  335. {
  336. $this->attributes['autoGenerateProxyClasses'] = $mode;
  337. unset($this->proxyManagerConfiguration);
  338. }
  339. public function getProxyNamespace(): ?string
  340. {
  341. return $this->attributes['proxyNamespace'] ?? null;
  342. }
  343. public function setProxyNamespace(string $ns): void
  344. {
  345. $this->attributes['proxyNamespace'] = $ns;
  346. unset($this->proxyManagerConfiguration);
  347. }
  348. public function setHydratorDir(string $dir): void
  349. {
  350. $this->attributes['hydratorDir'] = $dir;
  351. }
  352. public function getHydratorDir(): ?string
  353. {
  354. return $this->attributes['hydratorDir'] ?? null;
  355. }
  356. /**
  357. * Gets an int flag that indicates whether hydrator classes should always be regenerated
  358. * during each script execution.
  359. *
  360. * @return self::AUTOGENERATE_*
  361. */
  362. public function getAutoGenerateHydratorClasses(): int
  363. {
  364. return $this->attributes['autoGenerateHydratorClasses'] ?? self::AUTOGENERATE_ALWAYS;
  365. }
  366. /**
  367. * Sets an int flag that indicates whether hydrator classes should always be regenerated
  368. * during each script execution.
  369. *
  370. * @param self::AUTOGENERATE_* $mode
  371. */
  372. public function setAutoGenerateHydratorClasses(int $mode): void
  373. {
  374. $this->attributes['autoGenerateHydratorClasses'] = $mode;
  375. }
  376. public function getHydratorNamespace(): ?string
  377. {
  378. return $this->attributes['hydratorNamespace'] ?? null;
  379. }
  380. public function setHydratorNamespace(string $ns): void
  381. {
  382. $this->attributes['hydratorNamespace'] = $ns;
  383. }
  384. public function setPersistentCollectionDir(string $dir): void
  385. {
  386. $this->attributes['persistentCollectionDir'] = $dir;
  387. }
  388. public function getPersistentCollectionDir(): ?string
  389. {
  390. return $this->attributes['persistentCollectionDir'] ?? null;
  391. }
  392. /**
  393. * Gets a integer flag that indicates how and when persistent collection
  394. * classes should be generated.
  395. *
  396. * @return self::AUTOGENERATE_*
  397. */
  398. public function getAutoGeneratePersistentCollectionClasses(): int
  399. {
  400. return $this->attributes['autoGeneratePersistentCollectionClasses'] ?? self::AUTOGENERATE_ALWAYS;
  401. }
  402. /**
  403. * Sets a integer flag that indicates how and when persistent collection
  404. * classes should be generated.
  405. *
  406. * @param self::AUTOGENERATE_* $mode
  407. */
  408. public function setAutoGeneratePersistentCollectionClasses(int $mode): void
  409. {
  410. $this->attributes['autoGeneratePersistentCollectionClasses'] = $mode;
  411. }
  412. public function getPersistentCollectionNamespace(): ?string
  413. {
  414. return $this->attributes['persistentCollectionNamespace'] ?? null;
  415. }
  416. public function setPersistentCollectionNamespace(string $ns): void
  417. {
  418. $this->attributes['persistentCollectionNamespace'] = $ns;
  419. }
  420. /**
  421. * Sets the default DB to use for all Documents that do not specify
  422. * a database.
  423. */
  424. public function setDefaultDB(string $defaultDB): void
  425. {
  426. $this->attributes['defaultDB'] = $defaultDB;
  427. }
  428. /**
  429. * Gets the default DB to use for all Documents that do not specify a database.
  430. */
  431. public function getDefaultDB(): ?string
  432. {
  433. return $this->attributes['defaultDB'] ?? null;
  434. }
  435. /**
  436. * @param class-string<ClassMetadataFactoryInterface> $cmfName
  437. *
  438. * @throws MongoDBException If is not a ClassMetadataFactoryInterface.
  439. */
  440. public function setClassMetadataFactoryName(string $cmfName): void
  441. {
  442. $reflectionClass = new ReflectionClass($cmfName);
  443. if (! $reflectionClass->implementsInterface(ClassMetadataFactoryInterface::class)) {
  444. throw MongoDBException::invalidClassMetadataFactory($cmfName);
  445. }
  446. $this->attributes['classMetadataFactoryName'] = $cmfName;
  447. }
  448. /** @return class-string<ClassMetadataFactoryInterface> */
  449. public function getClassMetadataFactoryName(): string
  450. {
  451. if (! isset($this->attributes['classMetadataFactoryName'])) {
  452. $this->attributes['classMetadataFactoryName'] = ClassMetadataFactory::class;
  453. }
  454. return $this->attributes['classMetadataFactoryName'];
  455. }
  456. /** @phpstan-return CommitOptions */
  457. public function getDefaultCommitOptions(): array
  458. {
  459. if (! isset($this->attributes['defaultCommitOptions'])) {
  460. $this->attributes['defaultCommitOptions'] = ['writeConcern' => new WriteConcern(1)];
  461. }
  462. return $this->attributes['defaultCommitOptions'];
  463. }
  464. /** @phpstan-param CommitOptions $defaultCommitOptions */
  465. public function setDefaultCommitOptions(array $defaultCommitOptions): void
  466. {
  467. foreach (UnitOfWork::DEPRECATED_WRITE_OPTIONS as $deprecatedOption) {
  468. if (array_key_exists($deprecatedOption, $defaultCommitOptions)) {
  469. trigger_deprecation(
  470. 'doctrine/mongodb-odm',
  471. '2.6',
  472. 'The "%s" commit option used in the configuration is deprecated.',
  473. $deprecatedOption,
  474. );
  475. }
  476. }
  477. $this->attributes['defaultCommitOptions'] = $defaultCommitOptions;
  478. }
  479. /**
  480. * Add a filter to the list of possible filters.
  481. *
  482. * @param array<string, mixed> $parameters
  483. * @param class-string $className
  484. */
  485. public function addFilter(string $name, string $className, array $parameters = []): void
  486. {
  487. $this->attributes['filters'][$name] = [
  488. 'class' => $className,
  489. 'parameters' => $parameters,
  490. ];
  491. }
  492. /** @return class-string|null */
  493. public function getFilterClassName(string $name): ?string
  494. {
  495. return isset($this->attributes['filters'][$name])
  496. ? $this->attributes['filters'][$name]['class']
  497. : null;
  498. }
  499. /** @return array<string, mixed> */
  500. public function getFilterParameters(string $name): array
  501. {
  502. return isset($this->attributes['filters'][$name])
  503. ? $this->attributes['filters'][$name]['parameters']
  504. : [];
  505. }
  506. /**
  507. * @param class-string<ObjectRepository<object>> $className
  508. *
  509. * @throws MongoDBException If is not an ObjectRepository.
  510. */
  511. public function setDefaultDocumentRepositoryClassName(string $className): void
  512. {
  513. $reflectionClass = new ReflectionClass($className);
  514. if (! $reflectionClass->implementsInterface(ObjectRepository::class)) {
  515. throw MongoDBException::invalidDocumentRepository($className);
  516. }
  517. $this->attributes['defaultDocumentRepositoryClassName'] = $className;
  518. }
  519. /** @return class-string<ObjectRepository<object>> */
  520. public function getDefaultDocumentRepositoryClassName(): string
  521. {
  522. return $this->attributes['defaultDocumentRepositoryClassName'] ?? DocumentRepository::class;
  523. }
  524. /**
  525. * @param class-string<GridFSRepository<object>> $className
  526. *
  527. * @throws MongoDBException If the class does not implement the GridFSRepository interface.
  528. */
  529. public function setDefaultGridFSRepositoryClassName(string $className): void
  530. {
  531. $reflectionClass = new ReflectionClass($className);
  532. if (! $reflectionClass->implementsInterface(GridFSRepository::class)) {
  533. throw MongoDBException::invalidGridFSRepository($className);
  534. }
  535. $this->attributes['defaultGridFSRepositoryClassName'] = $className;
  536. }
  537. /** @return class-string<GridFSRepository<object>> */
  538. public function getDefaultGridFSRepositoryClassName(): string
  539. {
  540. return $this->attributes['defaultGridFSRepositoryClassName'] ?? DefaultGridFSRepository::class;
  541. }
  542. public function setRepositoryFactory(RepositoryFactory $repositoryFactory): void
  543. {
  544. $this->attributes['repositoryFactory'] = $repositoryFactory;
  545. }
  546. public function getRepositoryFactory(): RepositoryFactory
  547. {
  548. return $this->attributes['repositoryFactory'] ?? new DefaultRepositoryFactory();
  549. }
  550. public function setPersistentCollectionFactory(PersistentCollectionFactory $persistentCollectionFactory): void
  551. {
  552. $this->attributes['persistentCollectionFactory'] = $persistentCollectionFactory;
  553. }
  554. public function getPersistentCollectionFactory(): PersistentCollectionFactory
  555. {
  556. if (! isset($this->attributes['persistentCollectionFactory'])) {
  557. $this->attributes['persistentCollectionFactory'] = new DefaultPersistentCollectionFactory();
  558. }
  559. return $this->attributes['persistentCollectionFactory'];
  560. }
  561. public function setPersistentCollectionGenerator(PersistentCollectionGenerator $persistentCollectionGenerator): void
  562. {
  563. $this->attributes['persistentCollectionGenerator'] = $persistentCollectionGenerator;
  564. }
  565. public function getPersistentCollectionGenerator(): PersistentCollectionGenerator
  566. {
  567. if (! isset($this->attributes['persistentCollectionGenerator'])) {
  568. if ($this->getPersistentCollectionDir() === null) {
  569. throw ConfigurationException::persistentCollectionDirMissing();
  570. }
  571. if ($this->getPersistentCollectionNamespace() === null) {
  572. throw ConfigurationException::persistentCollectionNamespaceMissing();
  573. }
  574. $this->attributes['persistentCollectionGenerator'] = new DefaultPersistentCollectionGenerator(
  575. $this->getPersistentCollectionDir(),
  576. $this->getPersistentCollectionNamespace(),
  577. );
  578. }
  579. return $this->attributes['persistentCollectionGenerator'];
  580. }
  581. /** @deprecated */
  582. public function buildGhostObjectFactory(): LazyLoadingGhostFactory
  583. {
  584. return new LazyLoadingGhostFactory($this->getProxyManagerConfiguration());
  585. }
  586. /** @deprecated */
  587. public function getProxyManagerConfiguration(): ProxyManagerConfiguration
  588. {
  589. if (isset($this->proxyManagerConfiguration)) {
  590. return $this->proxyManagerConfiguration;
  591. }
  592. $proxyManagerConfiguration = new ProxyManagerConfiguration();
  593. $proxyManagerConfiguration->setProxiesTargetDir($this->getProxyDir());
  594. $proxyManagerConfiguration->setProxiesNamespace($this->getProxyNamespace());
  595. switch ($this->getAutoGenerateProxyClasses()) {
  596. case self::AUTOGENERATE_FILE_NOT_EXISTS:
  597. $proxyManagerConfiguration->setGeneratorStrategy(new FileWriterGeneratorStrategy(
  598. new FileLocator($proxyManagerConfiguration->getProxiesTargetDir()),
  599. ));
  600. break;
  601. case self::AUTOGENERATE_EVAL:
  602. $proxyManagerConfiguration->setGeneratorStrategy(new EvaluatingGeneratorStrategy());
  603. break;
  604. default:
  605. throw new InvalidArgumentException('Invalid proxy generation strategy given - only AUTOGENERATE_FILE_NOT_EXISTS and AUTOGENERATE_EVAL are supported.');
  606. }
  607. return $this->proxyManagerConfiguration = $proxyManagerConfiguration;
  608. }
  609. public function setUseTransactionalFlush(bool $useTransactionalFlush): void
  610. {
  611. $this->useTransactionalFlush = $useTransactionalFlush;
  612. }
  613. public function isTransactionalFlushEnabled(): bool
  614. {
  615. return $this->useTransactionalFlush;
  616. }
  617. /**
  618. * Generate proxy classes using Symfony VarExporter's LazyGhostTrait if true.
  619. * Otherwise, use ProxyManager's LazyLoadingGhostFactory (deprecated)
  620. */
  621. public function setUseLazyGhostObject(bool $flag): void
  622. {
  623. if ($this->nativeLazyObject) {
  624. throw new LogicException('Cannot enable or disable LazyGhostObject when native lazy objects are enabled.');
  625. }
  626. if ($flag && ! trait_exists(LazyGhostTrait::class)) {
  627. throw new LogicException('Package "symfony/var-exporter" >= 8.0 does not provide lazy ghost objects, use native lazy objects instead.');
  628. }
  629. if (! $flag) {
  630. if (! class_exists(ProxyManagerConfiguration::class)) {
  631. throw new LogicException('Package "friendsofphp/proxy-manager-lts" is required to disable LazyGhostObject.');
  632. }
  633. if (PHP_VERSION_ID < 80400) {
  634. trigger_deprecation('doctrine/mongodb-odm', '2.10', 'Using "friendsofphp/proxy-manager-lts" is deprecated. Use "symfony/var-exporter" LazyGhostObjects instead.');
  635. }
  636. }
  637. $this->lazyGhostObject = $flag;
  638. }
  639. public function isLazyGhostObjectEnabled(): bool
  640. {
  641. // Always false if native lazy objects are enabled
  642. return $this->lazyGhostObject && ! $this->nativeLazyObject;
  643. }
  644. public function setUseNativeLazyObject(bool $nativeLazyObject): void
  645. {
  646. if (PHP_VERSION_ID < 80400 && $nativeLazyObject) {
  647. throw new LogicException('Native lazy objects require PHP 8.4 or higher.');
  648. }
  649. $this->nativeLazyObject = $nativeLazyObject;
  650. }
  651. public function isNativeLazyObjectEnabled(): bool
  652. {
  653. if (PHP_VERSION_ID >= 80400 && ! $this->nativeLazyObject) {
  654. trigger_deprecation('doctrine/mongodb-odm', '2.14', 'Not using native lazy objects is deprecated and will be impossible in Doctrine MongoDB ODM 3.0.');
  655. }
  656. return $this->nativeLazyObject;
  657. }
  658. /**
  659. * Set the KMS provider to use for auto-encryption. The name of the KMS provider
  660. * must be specified in the 'type' key of the array.
  661. *
  662. * @see https://www.php.net/manual/en/mongodb-driver-clientencryption.construct.php
  663. *
  664. * @param KmsProvider $kmsProvider
  665. */
  666. public function setKmsProvider(array $kmsProvider): void
  667. {
  668. if (! isset($kmsProvider['type'])) {
  669. throw ConfigurationException::kmsProviderTypeRequired();
  670. }
  671. if (! is_string($kmsProvider['type'])) {
  672. throw ConfigurationException::kmsProviderTypeMustBeString();
  673. }
  674. $this->attributes['kmsProvider'] = $kmsProvider;
  675. }
  676. /**
  677. * Set the default master key to use when creating encrypted collections.
  678. *
  679. * @param array<string, mixed>|null $masterKey
  680. */
  681. public function setDefaultMasterKey(?array $masterKey): void
  682. {
  683. $this->attributes['defaultMasterKey'] = $masterKey;
  684. }
  685. /**
  686. * Set the options for auto-encryption.
  687. *
  688. * @see https://www.php.net/manual/en/mongodb-driver-manager.construct.php#mongodb-driver-manager.construct-autoencryption
  689. *
  690. * @param array{ keyVaultClient?: Client|Manager, keyVaultNamespace?: string, tlsOptions?: array<string, mixed>, schemaMap?: array<string, mixed>, bypassAutoEncryption?: bool, bypassQueryAnalysis?: bool, encryptedFieldsMap?: array<string,mixed>, extraOptions?: array<string, mixed>} $options
  691. */
  692. public function setAutoEncryption(array $options): void
  693. {
  694. if (isset($options['kmsProviders'])) {
  695. throw ConfigurationException::kmsProvidersOptionMustUseSetter();
  696. }
  697. $this->attributes['autoEncryption'] = $options;
  698. }
  699. /**
  700. * Get the default KMS provider name used when creating encrypted collections.
  701. */
  702. public function getDefaultKmsProvider(): ?string
  703. {
  704. return $this->attributes['kmsProvider']['type'] ?? null;
  705. }
  706. /**
  707. * Get the default master key used when creating encrypted collections.
  708. *
  709. * @return array<string, mixed>|null
  710. */
  711. public function getDefaultMasterKey(): ?array
  712. {
  713. if (! isset($this->attributes['kmsProvider']) || $this->attributes['kmsProvider']['type'] === 'local') {
  714. return null;
  715. }
  716. return $this->attributes['defaultMasterKey'] ?? throw ConfigurationException::masterKeyRequired($this->attributes['kmsProvider']['type']);
  717. }
  718. private static function getVersion(): string
  719. {
  720. if (! isset(self::$version)) {
  721. try {
  722. self::$version = PrettyVersions::getVersion('doctrine/mongodb-odm')->getPrettyVersion();
  723. } catch (Throwable) {
  724. return self::$version = 'unknown';
  725. }
  726. }
  727. return self::$version;
  728. }
  729. /** @return array<string, mixed> */
  730. private function getAutoEncryptionOptions(): array
  731. {
  732. $kmsProviderName = $this->attributes['kmsProvider']['type'];
  733. $kmsProviderOpts = array_diff_key($this->attributes['kmsProvider'], ['type' => 0]);
  734. // To use "Automatic Credentials", the provider options must be an empty document.
  735. // Fix the empty array to an empty stdClass object, as the driver expects it.
  736. if ($kmsProviderOpts === []) {
  737. $kmsProviderOpts = new stdClass();
  738. }
  739. return [
  740. // Each kmsProvider must be an object, it can be empty
  741. 'kmsProviders' => [$kmsProviderName => $kmsProviderOpts],
  742. 'keyVaultNamespace' => $this->getDefaultDB() . '.datakeys',
  743. ...$this->attributes['autoEncryption'] ?? [],
  744. ];
  745. }
  746. /**
  747. * Pipelines using a search index that does not exist or is not queryable
  748. * will return zero documents. By enabling this feature, an additional query
  749. * is performed when the pipeline doesn't return any results to check if the
  750. * search index exists. If the index does not exist, an exception is thrown.
  751. * This feature is enabled by default.
  752. * This applies to $search, $searchMeta and $vectorSearch pipelines.
  753. */
  754. public function setAssertSearchIndexExistsForEmptyResult(bool $enabled): void
  755. {
  756. $this->attributes['assertSearchIndexExistsForEmptyResult'] = $enabled;
  757. }
  758. public function assertSearchIndexExistsForEmptyResult(): bool
  759. {
  760. return $this->attributes['assertSearchIndexExistsForEmptyResult'] ?? true;
  761. }
  762. }
  763. interface_exists(MappingDriver::class);