<?php
declare(strict_types=1);
namespace Doctrine\ODM\MongoDB;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\Psr6\CacheAdapter;
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactoryInterface;
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
use Doctrine\ODM\MongoDB\PersistentCollection\DefaultPersistentCollectionFactory;
use Doctrine\ODM\MongoDB\PersistentCollection\DefaultPersistentCollectionGenerator;
use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionFactory;
use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionGenerator;
use Doctrine\ODM\MongoDB\Proxy\FileLocator;
use Doctrine\ODM\MongoDB\Repository\DefaultGridFSRepository;
use Doctrine\ODM\MongoDB\Repository\DefaultRepositoryFactory;
use Doctrine\ODM\MongoDB\Repository\DocumentRepository;
use Doctrine\ODM\MongoDB\Repository\GridFSRepository;
use Doctrine\ODM\MongoDB\Repository\RepositoryFactory;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\Persistence\ObjectRepository;
use InvalidArgumentException;
use Jean85\PrettyVersions;
use LogicException;
use MongoDB\Client;
use MongoDB\Driver\Manager;
use MongoDB\Driver\WriteConcern;
use ProxyManager\Configuration as ProxyManagerConfiguration;
use ProxyManager\Factory\LazyLoadingGhostFactory;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\GeneratorStrategy\FileWriterGeneratorStrategy;
use Psr\Cache\CacheItemPoolInterface;
use ReflectionClass;
use stdClass;
use Symfony\Component\VarExporter\LazyGhostTrait;
use Throwable;
use function array_diff_key;
use function array_intersect_key;
use function array_key_exists;
use function class_exists;
use function interface_exists;
use function is_string;
use function trait_exists;
use function trigger_deprecation;
use function trim;
use const PHP_VERSION_ID;
/**
* Configuration class for the DocumentManager. When setting up your DocumentManager
* you can optionally specify an instance of this class as the second argument.
* If you do not pass a configuration object, a blank one will be created for you.
*
* <?php
*
* $config = new Configuration();
* $dm = DocumentManager::create(new Connection(), $config);
*
* @phpstan-import-type CommitOptions from UnitOfWork
* @phpstan-type KmsProvider array{type: string, ...}
*/
class Configuration
{
/**
* Never autogenerate a proxy/hydrator/persistent collection and rely that
* it was generated by some process before deployment. Copied from
* \Doctrine\Common\Proxy\AbstractProxyFactory.
*/
public const AUTOGENERATE_NEVER = 0;
/**
* Always generates a new proxy/hydrator/persistent collection in every request.
*
* This is only sane during development.
* Copied from \Doctrine\Common\Proxy\AbstractProxyFactory.
*/
public const AUTOGENERATE_ALWAYS = 1;
/**
* Autogenerate the proxy/hydrator/persistent collection class when the file does not exist.
*
* This strategy causes a file exists call whenever any proxy/hydrator is used the
* first time in a request. Copied from \Doctrine\Common\Proxy\AbstractProxyFactory.
*/
public const AUTOGENERATE_FILE_NOT_EXISTS = 2;
/**
* Generate the proxy/hydrator/persistent collection classes using eval().
*
* This strategy is only sane for development.
* Copied from \Doctrine\Common\Proxy\AbstractProxyFactory.
*/
public const AUTOGENERATE_EVAL = 3;
/**
* Autogenerate the proxy class when the proxy file does not exist or
* when the proxied file changed.
*
* This strategy causes a file_exists() call whenever any proxy is used the
* first time in a request. When the proxied file is changed, the proxy will
* be updated.
*/
public const AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED = 4;
/**
* Array of attributes for this configuration instance.
*
* @phpstan-var array{
* autoGenerateHydratorClasses?: self::AUTOGENERATE_*,
* autoGeneratePersistentCollectionClasses?: self::AUTOGENERATE_*,
* autoGenerateProxyClasses?: self::AUTOGENERATE_*,
* classMetadataFactoryName?: class-string<ClassMetadataFactoryInterface>,
* defaultCommitOptions?: CommitOptions,
* defaultDocumentRepositoryClassName?: class-string<ObjectRepository<object>>,
* defaultGridFSRepositoryClassName?: class-string<GridFSRepository<object>>,
* defaultDB?: string,
* documentNamespaces?: array<string, string>,
* filters?: array<string, array{
* class: class-string,
* parameters: array<string, mixed>
* }>,
* hydratorDir?: string,
* hydratorNamespace?: string,
* metadataCacheImpl?: Cache,
* metadataDriverImpl?: MappingDriver,
* persistentCollectionFactory?: PersistentCollectionFactory,
* persistentCollectionGenerator?: PersistentCollectionGenerator,
* persistentCollectionDir?: string,
* persistentCollectionNamespace?: string,
* proxyDir?: string,
* proxyNamespace?: string,
* repositoryFactory?: RepositoryFactory,
* kmsProvider?: KmsProvider,
* defaultMasterKey?: array<string, mixed>|null,
* autoEncryption?: array<string, mixed>,
* }
*/
private array $attributes = [];
private ?CacheItemPoolInterface $metadataCache = null;
/** @deprecated */
private ProxyManagerConfiguration $proxyManagerConfiguration;
private bool $useTransactionalFlush = false;
private bool $lazyGhostObject = false;
private bool $nativeLazyObject = false;
private static string $version;
/**
* Provides the driver options to be used when creating the MongoDB client.
*
* @return array<string, mixed>
*/
public function getDriverOptions(): array
{
$driverOptions = [
'driver' => [
'name' => 'doctrine-odm',
'version' => self::getVersion(),
],
];
if (isset($this->attributes['kmsProvider'])) {
$driverOptions['autoEncryption'] = $this->getAutoEncryptionOptions();
}
return $driverOptions;
}
/**
* Get options to create a ClientEncryption instance.
*
* @see https://www.php.net/manual/en/mongodb-driver-clientencryption.construct.php
*
* @return array{keyVaultClient?: Client|Manager, keyVaultNamespace: string, kmsProviders: array<string, mixed>, tlsOptions?: array<string, mixed>}
*/
public function getClientEncryptionOptions(): array
{
if (! isset($this->attributes['kmsProvider'])) {
throw ConfigurationException::clientEncryptionOptionsNotSet();
}
return array_intersect_key($this->getAutoEncryptionOptions(), [
'keyVaultClient' => 1,
'keyVaultNamespace' => 1,
'kmsProviders' => 1,
'tlsOptions' => 1,
]);
}
/**
* Adds a namespace under a certain alias.
*
* @deprecated Document short aliases are deprecated - use ::class constant instead.
*/
public function addDocumentNamespace(string $alias, string $namespace): void
{
trigger_deprecation(
'doctrine/mongodb-odm',
'2.3',
'Document short namespace aliases such as "%s" are deprecated, use ::class constant instead.',
$alias,
);
$this->attributes['documentNamespaces'][$alias] = $namespace;
}
/**
* Resolves a registered namespace alias to the full namespace.
*
* @deprecated Document short aliases are deprecated - use ::class constant instead.
*
* @throws MongoDBException
*/
public function getDocumentNamespace(string $documentNamespaceAlias): string
{
trigger_deprecation(
'doctrine/mongodb-odm',
'2.3',
'Document short namespace aliases such as "%s" are deprecated, use ::class constant instead.',
$documentNamespaceAlias,
);
if (! isset($this->attributes['documentNamespaces'][$documentNamespaceAlias])) {
throw MongoDBException::unknownDocumentNamespace($documentNamespaceAlias);
}
return trim($this->attributes['documentNamespaces'][$documentNamespaceAlias], '\\');
}
/**
* Retrieves the list of registered document namespace aliases.
*
* @deprecated Document short aliases are deprecated - use ::class constant instead.
*
* @return array<string, string>
*/
public function getDocumentNamespaces(): array
{
trigger_deprecation(
'doctrine/mongodb-odm',
'2.3',
'Document short namespace aliases are deprecated, use ::class constant instead.',
);
return $this->attributes['documentNamespaces'];
}
/**
* Set the document alias map
*
* @deprecated Document short aliases are deprecated - use ::class constant instead.
*
* @param array<string, string> $documentNamespaces
*/
public function setDocumentNamespaces(array $documentNamespaces): void
{
trigger_deprecation(
'doctrine/mongodb-odm',
'2.3',
'Document short namespace aliases are deprecated, use ::class constant instead.',
);
$this->attributes['documentNamespaces'] = $documentNamespaces;
}
/**
* Sets the cache driver implementation that is used for metadata caching.
*
* @todo Force parameter to be a Closure to ensure lazy evaluation
* (as soon as a metadata cache is in effect, the driver never needs to initialize).
*/
public function setMetadataDriverImpl(MappingDriver $driverImpl): void
{
$this->attributes['metadataDriverImpl'] = $driverImpl;
}
/**
* Add a new default annotation driver with a correctly configured annotation reader.
*
* @param string[] $paths
*/
public function newDefaultAnnotationDriver(array $paths = []): AnnotationDriver
{
$reader = new AnnotationReader();
return new AnnotationDriver($reader, $paths);
}
/**
* Gets the cache driver implementation that is used for the mapping metadata.
*/
public function getMetadataDriverImpl(): ?MappingDriver
{
return $this->attributes['metadataDriverImpl'] ?? null;
}
public function getMetadataCacheImpl(): ?Cache
{
trigger_deprecation(
'doctrine/mongodb-odm',
'2.2',
'Using "%s" is deprecated. Please use "%s::getMetadataCache" instead.',
__METHOD__,
self::class,
);
return $this->attributes['metadataCacheImpl'] ?? null;
}
public function setMetadataCacheImpl(Cache $cacheImpl): void
{
trigger_deprecation(
'doctrine/mongodb-odm',
'2.2',
'Using "%s" is deprecated. Please use "%s::setMetadataCache" instead.',
__METHOD__,
self::class,
);
$this->attributes['metadataCacheImpl'] = $cacheImpl;
$this->metadataCache = CacheAdapter::wrap($cacheImpl);
}
public function getMetadataCache(): ?CacheItemPoolInterface
{
return $this->metadataCache;
}
public function setMetadataCache(CacheItemPoolInterface $cache): void
{
$this->metadataCache = $cache;
$this->attributes['metadataCacheImpl'] = DoctrineProvider::wrap($cache);
}
/**
* Sets the directory where Doctrine generates any necessary proxy class files.
*/
public function setProxyDir(string $dir): void
{
$this->attributes['proxyDir'] = $dir;
unset($this->proxyManagerConfiguration);
}
/**
* Gets the directory where Doctrine generates any necessary proxy class files.
*/
public function getProxyDir(): ?string
{
return $this->attributes['proxyDir'] ?? null;
}
/**
* Gets an int flag that indicates whether proxy classes should always be regenerated
* during each script execution.
*
* @return self::AUTOGENERATE_*
*/
public function getAutoGenerateProxyClasses(): int
{
return $this->attributes['autoGenerateProxyClasses'] ?? self::AUTOGENERATE_FILE_NOT_EXISTS;
}
/**
* Sets an int flag that indicates whether proxy classes should always be regenerated
* during each script execution.
*
* @param self::AUTOGENERATE_* $mode
*/
public function setAutoGenerateProxyClasses(int $mode): void
{
$this->attributes['autoGenerateProxyClasses'] = $mode;
unset($this->proxyManagerConfiguration);
}
public function getProxyNamespace(): ?string
{
return $this->attributes['proxyNamespace'] ?? null;
}
public function setProxyNamespace(string $ns): void
{
$this->attributes['proxyNamespace'] = $ns;
unset($this->proxyManagerConfiguration);
}
public function setHydratorDir(string $dir): void
{
$this->attributes['hydratorDir'] = $dir;
}
public function getHydratorDir(): ?string
{
return $this->attributes['hydratorDir'] ?? null;
}
/**
* Gets an int flag that indicates whether hydrator classes should always be regenerated
* during each script execution.
*
* @return self::AUTOGENERATE_*
*/
public function getAutoGenerateHydratorClasses(): int
{
return $this->attributes['autoGenerateHydratorClasses'] ?? self::AUTOGENERATE_ALWAYS;
}
/**
* Sets an int flag that indicates whether hydrator classes should always be regenerated
* during each script execution.
*
* @param self::AUTOGENERATE_* $mode
*/
public function setAutoGenerateHydratorClasses(int $mode): void
{
$this->attributes['autoGenerateHydratorClasses'] = $mode;
}
public function getHydratorNamespace(): ?string
{
return $this->attributes['hydratorNamespace'] ?? null;
}
public function setHydratorNamespace(string $ns): void
{
$this->attributes['hydratorNamespace'] = $ns;
}
public function setPersistentCollectionDir(string $dir): void
{
$this->attributes['persistentCollectionDir'] = $dir;
}
public function getPersistentCollectionDir(): ?string
{
return $this->attributes['persistentCollectionDir'] ?? null;
}
/**
* Gets a integer flag that indicates how and when persistent collection
* classes should be generated.
*
* @return self::AUTOGENERATE_*
*/
public function getAutoGeneratePersistentCollectionClasses(): int
{
return $this->attributes['autoGeneratePersistentCollectionClasses'] ?? self::AUTOGENERATE_ALWAYS;
}
/**
* Sets a integer flag that indicates how and when persistent collection
* classes should be generated.
*
* @param self::AUTOGENERATE_* $mode
*/
public function setAutoGeneratePersistentCollectionClasses(int $mode): void
{
$this->attributes['autoGeneratePersistentCollectionClasses'] = $mode;
}
public function getPersistentCollectionNamespace(): ?string
{
return $this->attributes['persistentCollectionNamespace'] ?? null;
}
public function setPersistentCollectionNamespace(string $ns): void
{
$this->attributes['persistentCollectionNamespace'] = $ns;
}
/**
* Sets the default DB to use for all Documents that do not specify
* a database.
*/
public function setDefaultDB(string $defaultDB): void
{
$this->attributes['defaultDB'] = $defaultDB;
}
/**
* Gets the default DB to use for all Documents that do not specify a database.
*/
public function getDefaultDB(): ?string
{
return $this->attributes['defaultDB'] ?? null;
}
/**
* @param class-string<ClassMetadataFactoryInterface> $cmfName
*
* @throws MongoDBException If is not a ClassMetadataFactoryInterface.
*/
public function setClassMetadataFactoryName(string $cmfName): void
{
$reflectionClass = new ReflectionClass($cmfName);
if (! $reflectionClass->implementsInterface(ClassMetadataFactoryInterface::class)) {
throw MongoDBException::invalidClassMetadataFactory($cmfName);
}
$this->attributes['classMetadataFactoryName'] = $cmfName;
}
/** @return class-string<ClassMetadataFactoryInterface> */
public function getClassMetadataFactoryName(): string
{
if (! isset($this->attributes['classMetadataFactoryName'])) {
$this->attributes['classMetadataFactoryName'] = ClassMetadataFactory::class;
}
return $this->attributes['classMetadataFactoryName'];
}
/** @phpstan-return CommitOptions */
public function getDefaultCommitOptions(): array
{
if (! isset($this->attributes['defaultCommitOptions'])) {
$this->attributes['defaultCommitOptions'] = ['writeConcern' => new WriteConcern(1)];
}
return $this->attributes['defaultCommitOptions'];
}
/** @phpstan-param CommitOptions $defaultCommitOptions */
public function setDefaultCommitOptions(array $defaultCommitOptions): void
{
foreach (UnitOfWork::DEPRECATED_WRITE_OPTIONS as $deprecatedOption) {
if (array_key_exists($deprecatedOption, $defaultCommitOptions)) {
trigger_deprecation(
'doctrine/mongodb-odm',
'2.6',
'The "%s" commit option used in the configuration is deprecated.',
$deprecatedOption,
);
}
}
$this->attributes['defaultCommitOptions'] = $defaultCommitOptions;
}
/**
* Add a filter to the list of possible filters.
*
* @param array<string, mixed> $parameters
* @param class-string $className
*/
public function addFilter(string $name, string $className, array $parameters = []): void
{
$this->attributes['filters'][$name] = [
'class' => $className,
'parameters' => $parameters,
];
}
/** @return class-string|null */
public function getFilterClassName(string $name): ?string
{
return isset($this->attributes['filters'][$name])
? $this->attributes['filters'][$name]['class']
: null;
}
/** @return array<string, mixed> */
public function getFilterParameters(string $name): array
{
return isset($this->attributes['filters'][$name])
? $this->attributes['filters'][$name]['parameters']
: [];
}
/**
* @param class-string<ObjectRepository<object>> $className
*
* @throws MongoDBException If is not an ObjectRepository.
*/
public function setDefaultDocumentRepositoryClassName(string $className): void
{
$reflectionClass = new ReflectionClass($className);
if (! $reflectionClass->implementsInterface(ObjectRepository::class)) {
throw MongoDBException::invalidDocumentRepository($className);
}
$this->attributes['defaultDocumentRepositoryClassName'] = $className;
}
/** @return class-string<ObjectRepository<object>> */
public function getDefaultDocumentRepositoryClassName(): string
{
return $this->attributes['defaultDocumentRepositoryClassName'] ?? DocumentRepository::class;
}
/**
* @param class-string<GridFSRepository<object>> $className
*
* @throws MongoDBException If the class does not implement the GridFSRepository interface.
*/
public function setDefaultGridFSRepositoryClassName(string $className): void
{
$reflectionClass = new ReflectionClass($className);
if (! $reflectionClass->implementsInterface(GridFSRepository::class)) {
throw MongoDBException::invalidGridFSRepository($className);
}
$this->attributes['defaultGridFSRepositoryClassName'] = $className;
}
/** @return class-string<GridFSRepository<object>> */
public function getDefaultGridFSRepositoryClassName(): string
{
return $this->attributes['defaultGridFSRepositoryClassName'] ?? DefaultGridFSRepository::class;
}
public function setRepositoryFactory(RepositoryFactory $repositoryFactory): void
{
$this->attributes['repositoryFactory'] = $repositoryFactory;
}
public function getRepositoryFactory(): RepositoryFactory
{
return $this->attributes['repositoryFactory'] ?? new DefaultRepositoryFactory();
}
public function setPersistentCollectionFactory(PersistentCollectionFactory $persistentCollectionFactory): void
{
$this->attributes['persistentCollectionFactory'] = $persistentCollectionFactory;
}
public function getPersistentCollectionFactory(): PersistentCollectionFactory
{
if (! isset($this->attributes['persistentCollectionFactory'])) {
$this->attributes['persistentCollectionFactory'] = new DefaultPersistentCollectionFactory();
}
return $this->attributes['persistentCollectionFactory'];
}
public function setPersistentCollectionGenerator(PersistentCollectionGenerator $persistentCollectionGenerator): void
{
$this->attributes['persistentCollectionGenerator'] = $persistentCollectionGenerator;
}
public function getPersistentCollectionGenerator(): PersistentCollectionGenerator
{
if (! isset($this->attributes['persistentCollectionGenerator'])) {
if ($this->getPersistentCollectionDir() === null) {
throw ConfigurationException::persistentCollectionDirMissing();
}
if ($this->getPersistentCollectionNamespace() === null) {
throw ConfigurationException::persistentCollectionNamespaceMissing();
}
$this->attributes['persistentCollectionGenerator'] = new DefaultPersistentCollectionGenerator(
$this->getPersistentCollectionDir(),
$this->getPersistentCollectionNamespace(),
);
}
return $this->attributes['persistentCollectionGenerator'];
}
/** @deprecated */
public function buildGhostObjectFactory(): LazyLoadingGhostFactory
{
return new LazyLoadingGhostFactory($this->getProxyManagerConfiguration());
}
/** @deprecated */
public function getProxyManagerConfiguration(): ProxyManagerConfiguration
{
if (isset($this->proxyManagerConfiguration)) {
return $this->proxyManagerConfiguration;
}
$proxyManagerConfiguration = new ProxyManagerConfiguration();
$proxyManagerConfiguration->setProxiesTargetDir($this->getProxyDir());
$proxyManagerConfiguration->setProxiesNamespace($this->getProxyNamespace());
switch ($this->getAutoGenerateProxyClasses()) {
case self::AUTOGENERATE_FILE_NOT_EXISTS:
$proxyManagerConfiguration->setGeneratorStrategy(new FileWriterGeneratorStrategy(
new FileLocator($proxyManagerConfiguration->getProxiesTargetDir()),
));
break;
case self::AUTOGENERATE_EVAL:
$proxyManagerConfiguration->setGeneratorStrategy(new EvaluatingGeneratorStrategy());
break;
default:
throw new InvalidArgumentException('Invalid proxy generation strategy given - only AUTOGENERATE_FILE_NOT_EXISTS and AUTOGENERATE_EVAL are supported.');
}
return $this->proxyManagerConfiguration = $proxyManagerConfiguration;
}
public function setUseTransactionalFlush(bool $useTransactionalFlush): void
{
$this->useTransactionalFlush = $useTransactionalFlush;
}
public function isTransactionalFlushEnabled(): bool
{
return $this->useTransactionalFlush;
}
/**
* Generate proxy classes using Symfony VarExporter's LazyGhostTrait if true.
* Otherwise, use ProxyManager's LazyLoadingGhostFactory (deprecated)
*/
public function setUseLazyGhostObject(bool $flag): void
{
if ($this->nativeLazyObject) {
throw new LogicException('Cannot enable or disable LazyGhostObject when native lazy objects are enabled.');
}
if ($flag && ! trait_exists(LazyGhostTrait::class)) {
throw new LogicException('Package "symfony/var-exporter" >= 8.0 does not provide lazy ghost objects, use native lazy objects instead.');
}
if (! $flag) {
if (! class_exists(ProxyManagerConfiguration::class)) {
throw new LogicException('Package "friendsofphp/proxy-manager-lts" is required to disable LazyGhostObject.');
}
if (PHP_VERSION_ID < 80400) {
trigger_deprecation('doctrine/mongodb-odm', '2.10', 'Using "friendsofphp/proxy-manager-lts" is deprecated. Use "symfony/var-exporter" LazyGhostObjects instead.');
}
}
$this->lazyGhostObject = $flag;
}
public function isLazyGhostObjectEnabled(): bool
{
// Always false if native lazy objects are enabled
return $this->lazyGhostObject && ! $this->nativeLazyObject;
}
public function setUseNativeLazyObject(bool $nativeLazyObject): void
{
if (PHP_VERSION_ID < 80400 && $nativeLazyObject) {
throw new LogicException('Native lazy objects require PHP 8.4 or higher.');
}
$this->nativeLazyObject = $nativeLazyObject;
}
public function isNativeLazyObjectEnabled(): bool
{
if (PHP_VERSION_ID >= 80400 && ! $this->nativeLazyObject) {
trigger_deprecation('doctrine/mongodb-odm', '2.14', 'Not using native lazy objects is deprecated and will be impossible in Doctrine MongoDB ODM 3.0.');
}
return $this->nativeLazyObject;
}
/**
* Set the KMS provider to use for auto-encryption. The name of the KMS provider
* must be specified in the 'type' key of the array.
*
* @see https://www.php.net/manual/en/mongodb-driver-clientencryption.construct.php
*
* @param KmsProvider $kmsProvider
*/
public function setKmsProvider(array $kmsProvider): void
{
if (! isset($kmsProvider['type'])) {
throw ConfigurationException::kmsProviderTypeRequired();
}
if (! is_string($kmsProvider['type'])) {
throw ConfigurationException::kmsProviderTypeMustBeString();
}
$this->attributes['kmsProvider'] = $kmsProvider;
}
/**
* Set the default master key to use when creating encrypted collections.
*
* @param array<string, mixed>|null $masterKey
*/
public function setDefaultMasterKey(?array $masterKey): void
{
$this->attributes['defaultMasterKey'] = $masterKey;
}
/**
* Set the options for auto-encryption.
*
* @see https://www.php.net/manual/en/mongodb-driver-manager.construct.php#mongodb-driver-manager.construct-autoencryption
*
* @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
*/
public function setAutoEncryption(array $options): void
{
if (isset($options['kmsProviders'])) {
throw ConfigurationException::kmsProvidersOptionMustUseSetter();
}
$this->attributes['autoEncryption'] = $options;
}
/**
* Get the default KMS provider name used when creating encrypted collections.
*/
public function getDefaultKmsProvider(): ?string
{
return $this->attributes['kmsProvider']['type'] ?? null;
}
/**
* Get the default master key used when creating encrypted collections.
*
* @return array<string, mixed>|null
*/
public function getDefaultMasterKey(): ?array
{
if (! isset($this->attributes['kmsProvider']) || $this->attributes['kmsProvider']['type'] === 'local') {
return null;
}
return $this->attributes['defaultMasterKey'] ?? throw ConfigurationException::masterKeyRequired($this->attributes['kmsProvider']['type']);
}
private static function getVersion(): string
{
if (! isset(self::$version)) {
try {
self::$version = PrettyVersions::getVersion('doctrine/mongodb-odm')->getPrettyVersion();
} catch (Throwable) {
return self::$version = 'unknown';
}
}
return self::$version;
}
/** @return array<string, mixed> */
private function getAutoEncryptionOptions(): array
{
$kmsProviderName = $this->attributes['kmsProvider']['type'];
$kmsProviderOpts = array_diff_key($this->attributes['kmsProvider'], ['type' => 0]);
// To use "Automatic Credentials", the provider options must be an empty document.
// Fix the empty array to an empty stdClass object, as the driver expects it.
if ($kmsProviderOpts === []) {
$kmsProviderOpts = new stdClass();
}
return [
// Each kmsProvider must be an object, it can be empty
'kmsProviders' => [$kmsProviderName => $kmsProviderOpts],
'keyVaultNamespace' => $this->getDefaultDB() . '.datakeys',
...$this->attributes['autoEncryption'] ?? [],
];
}
/**
* Pipelines using a search index that does not exist or is not queryable
* will return zero documents. By enabling this feature, an additional query
* is performed when the pipeline doesn't return any results to check if the
* search index exists. If the index does not exist, an exception is thrown.
* This feature is enabled by default.
* This applies to $search, $searchMeta and $vectorSearch pipelines.
*/
public function setAssertSearchIndexExistsForEmptyResult(bool $enabled): void
{
$this->attributes['assertSearchIndexExistsForEmptyResult'] = $enabled;
}
public function assertSearchIndexExistsForEmptyResult(): bool
{
return $this->attributes['assertSearchIndexExistsForEmptyResult'] ?? true;
}
}
interface_exists(MappingDriver::class);