1717
1818namespace MongoDB ;
1919
20- use Composer \InstalledVersions ;
2120use Iterator ;
22- use MongoDB \BSON \Document ;
23- use MongoDB \BSON \PackedArray ;
24- use MongoDB \Builder \BuilderEncoder ;
2521use MongoDB \Builder \Pipeline ;
26- use MongoDB \Codec \Encoder ;
2722use MongoDB \Driver \BulkWriteCommand ;
2823use MongoDB \Driver \BulkWriteCommandResult ;
2924use MongoDB \Driver \ClientEncryption ;
3833use MongoDB \Exception \InvalidArgumentException ;
3934use MongoDB \Exception \UnexpectedValueException ;
4035use MongoDB \Exception \UnsupportedException ;
41- use MongoDB \Model \BSONArray ;
42- use MongoDB \Model \BSONDocument ;
36+ use MongoDB \Model \AutoEncryptionOptions ;
4337use MongoDB \Model \DatabaseInfo ;
38+ use MongoDB \Model \DriverOptions ;
4439use MongoDB \Operation \ClientBulkWriteCommand ;
4540use MongoDB \Operation \DropDatabase ;
4641use MongoDB \Operation \ListDatabaseNames ;
4742use MongoDB \Operation \ListDatabases ;
4843use MongoDB \Operation \Watch ;
4944use stdClass ;
5045use Stringable ;
51- use Throwable ;
5246
5347use function array_diff_key ;
54- use function is_array ;
55- use function is_string ;
5648
49+ /** @psalm-import-type stage from Builder\Pipeline */
5750class Client implements Stringable
5851{
5952 public const DEFAULT_URI = 'mongodb://127.0.0.1/ ' ;
6053
61- private const DEFAULT_TYPE_MAP = [
62- 'array ' => BSONArray::class,
63- 'document ' => BSONDocument::class,
64- 'root ' => BSONDocument::class,
65- ];
66-
67- private const HANDSHAKE_SEPARATOR = '/ ' ;
68-
69- private static ?string $ version = null ;
70-
7154 private Manager $ manager ;
7255
7356 private ReadConcern $ readConcern ;
@@ -76,14 +59,9 @@ class Client implements Stringable
7659
7760 private string $ uri ;
7861
79- private array $ typeMap ;
80-
81- /** @psalm-var Encoder<array|stdClass|Document|PackedArray, mixed> */
82- private readonly Encoder $ builderEncoder ;
83-
8462 private WriteConcern $ writeConcern ;
8563
86- private bool $ autoEncryptionEnabled ;
64+ private DriverOptions $ driverOptions ;
8765
8866 /**
8967 * Constructs a new Client instance.
@@ -113,34 +91,14 @@ class Client implements Stringable
11391 */
11492 public function __construct (?string $ uri = null , array $ uriOptions = [], array $ driverOptions = [])
11593 {
116- $ driverOptions += ['typeMap ' => self ::DEFAULT_TYPE_MAP ];
117-
118- if (! is_array ($ driverOptions ['typeMap ' ])) {
119- throw InvalidArgumentException::invalidType ('"typeMap" driver option ' , $ driverOptions ['typeMap ' ], 'array ' );
120- }
121-
122- if (isset ($ driverOptions ['autoEncryption ' ]) && is_array ($ driverOptions ['autoEncryption ' ])) {
123- $ driverOptions ['autoEncryption ' ] = $ this ->prepareEncryptionOptions ($ driverOptions ['autoEncryption ' ]);
124- }
125-
126- if (isset ($ driverOptions ['builderEncoder ' ]) && ! $ driverOptions ['builderEncoder ' ] instanceof Encoder) {
127- throw InvalidArgumentException::invalidType ('"builderEncoder" option ' , $ driverOptions ['builderEncoder ' ], Encoder::class);
128- }
129-
130- $ driverOptions ['driver ' ] = $ this ->mergeDriverInfo ($ driverOptions ['driver ' ] ?? []);
94+ $ this ->driverOptions = DriverOptions::fromArray ($ driverOptions );
13195
13296 $ this ->uri = $ uri ?? self ::DEFAULT_URI ;
133- $ this ->builderEncoder = $ driverOptions ['builderEncoder ' ] ?? new BuilderEncoder ();
134- $ this ->typeMap = $ driverOptions ['typeMap ' ];
135-
136- /* Database and Collection objects may need to know whether auto
137- * encryption is enabled for dropping collections. Track this via an
138- * internal option until PHPC-2615 is implemented. */
139- $ this ->autoEncryptionEnabled = isset ($ driverOptions ['autoEncryption ' ]['keyVaultNamespace ' ]);
14097
141- $ driverOptions = array_diff_key ($ driverOptions , ['builderEncoder ' => 1 , 'typeMap ' => 1 ]);
98+ $ driverOptions = array_diff_key ($ this -> driverOptions -> toArray () , ['builderEncoder ' => 1 , 'typeMap ' => 1 ]);
14299
143100 $ this ->manager = new Manager ($ uri , $ uriOptions , $ driverOptions );
101+
144102 $ this ->readConcern = $ this ->manager ->getReadConcern ();
145103 $ this ->readPreference = $ this ->manager ->getReadPreference ();
146104 $ this ->writeConcern = $ this ->manager ->getWriteConcern ();
@@ -156,8 +114,8 @@ public function __debugInfo(): array
156114 return [
157115 'manager ' => $ this ->manager ,
158116 'uri ' => $ this ->uri ,
159- 'typeMap ' => $ this ->typeMap ,
160- 'builderEncoder ' => $ this ->builderEncoder ,
117+ 'typeMap ' => $ this ->driverOptions -> typeMap ,
118+ 'builderEncoder ' => $ this ->driverOptions -> builderEncoder ,
161119 'writeConcern ' => $ this ->writeConcern ,
162120 ];
163121 }
@@ -225,13 +183,13 @@ public function bulkWrite(BulkWriteCommand|ClientBulkWrite $bulk, array $options
225183 /**
226184 * Returns a ClientEncryption instance for explicit encryption and decryption
227185 *
228- * @param array $options Encryption options
186+ * @param array{kmsProviders?: stdClass|array<string, array>, keyVaultClient?: Client|Manager} $ options
229187 */
230188 public function createClientEncryption (array $ options ): ClientEncryption
231189 {
232- $ options = $ this -> prepareEncryptionOptions ($ options );
190+ $ options = AutoEncryptionOptions:: fromArray ($ options );
233191
234- return $ this ->manager ->createClientEncryption ($ options );
192+ return $ this ->manager ->createClientEncryption ($ options-> toArray () );
235193 }
236194
237195 /**
@@ -268,7 +226,11 @@ public function dropDatabase(string $databaseName, array $options = []): void
268226 */
269227 public function getCollection (string $ databaseName , string $ collectionName , array $ options = []): Collection
270228 {
271- $ options += ['typeMap ' => $ this ->typeMap , 'builderEncoder ' => $ this ->builderEncoder , 'autoEncryptionEnabled ' => $ this ->autoEncryptionEnabled ];
229+ $ options += [
230+ 'typeMap ' => $ this ->driverOptions ->typeMap ,
231+ 'builderEncoder ' => $ this ->driverOptions ->builderEncoder ,
232+ 'autoEncryptionEnabled ' => $ this ->driverOptions ->isAutoEncryptionEnabled (),
233+ ];
272234
273235 return new Collection ($ this ->manager , $ databaseName , $ collectionName , $ options );
274236 }
@@ -283,7 +245,11 @@ public function getCollection(string $databaseName, string $collectionName, arra
283245 */
284246 public function getDatabase (string $ databaseName , array $ options = []): Database
285247 {
286- $ options += ['typeMap ' => $ this ->typeMap , 'builderEncoder ' => $ this ->builderEncoder , 'autoEncryptionEnabled ' => $ this ->autoEncryptionEnabled ];
248+ $ options += [
249+ 'typeMap ' => $ this ->driverOptions ->typeMap ,
250+ 'builderEncoder ' => $ this ->driverOptions ->builderEncoder ,
251+ 'autoEncryptionEnabled ' => $ this ->driverOptions ->isAutoEncryptionEnabled (),
252+ ];
287253
288254 return new Database ($ this ->manager , $ databaseName , $ options );
289255 }
@@ -319,7 +285,7 @@ public function getReadPreference(): ReadPreference
319285 */
320286 public function getTypeMap (): array
321287 {
322- return $ this ->typeMap ;
288+ return $ this ->driverOptions -> typeMap ;
323289 }
324290
325291 /**
@@ -418,8 +384,8 @@ public function startSession(array $options = []): Session
418384 * Create a change stream for watching changes to the cluster.
419385 *
420386 * @see Watch::__construct() for supported options
421- * @param array $pipeline Aggregation pipeline
422- * @param array $options Command options
387+ * @psalm- param list<stage> $pipeline Aggregation pipeline
388+ * @param array $options Command options
423389 * @throws InvalidArgumentException for parameter/option parsing errors
424390 */
425391 public function watch (array $ pipeline = [], array $ options = []): ChangeStream
@@ -428,7 +394,8 @@ public function watch(array $pipeline = [], array $options = []): ChangeStream
428394 $ pipeline = new Pipeline (...$ pipeline );
429395 }
430396
431- $ pipeline = $ this ->builderEncoder ->encodeIfSupported ($ pipeline );
397+ /** @var array<array-key, mixed> $pipeline */
398+ $ pipeline = $ this ->driverOptions ->builderEncoder ->encodeIfSupported ($ pipeline );
432399
433400 if (! isset ($ options ['readPreference ' ]) && ! is_in_transaction ($ options )) {
434401 $ options ['readPreference ' ] = $ this ->readPreference ;
@@ -441,76 +408,11 @@ public function watch(array $pipeline = [], array $options = []): ChangeStream
441408 }
442409
443410 if (! isset ($ options ['typeMap ' ])) {
444- $ options ['typeMap ' ] = $ this ->typeMap ;
411+ $ options ['typeMap ' ] = $ this ->driverOptions -> typeMap ;
445412 }
446413
447414 $ operation = new Watch ($ this ->manager , null , null , $ pipeline , $ options );
448415
449416 return $ operation ->execute ($ server );
450417 }
451-
452- private static function getVersion (): string
453- {
454- if (self ::$ version === null ) {
455- try {
456- self ::$ version = InstalledVersions::getPrettyVersion ('mongodb/mongodb ' ) ?? 'unknown ' ;
457- } catch (Throwable ) {
458- self ::$ version = 'error ' ;
459- }
460- }
461-
462- return self ::$ version ;
463- }
464-
465- private function mergeDriverInfo (array $ driver ): array
466- {
467- $ mergedDriver = [
468- 'name ' => 'PHPLIB ' ,
469- 'version ' => self ::getVersion (),
470- ];
471-
472- if (isset ($ driver ['name ' ])) {
473- if (! is_string ($ driver ['name ' ])) {
474- throw InvalidArgumentException::invalidType ('"name" handshake option ' , $ driver ['name ' ], 'string ' );
475- }
476-
477- $ mergedDriver ['name ' ] .= self ::HANDSHAKE_SEPARATOR . $ driver ['name ' ];
478- }
479-
480- if (isset ($ driver ['version ' ])) {
481- if (! is_string ($ driver ['version ' ])) {
482- throw InvalidArgumentException::invalidType ('"version" handshake option ' , $ driver ['version ' ], 'string ' );
483- }
484-
485- $ mergedDriver ['version ' ] .= self ::HANDSHAKE_SEPARATOR . $ driver ['version ' ];
486- }
487-
488- if (isset ($ driver ['platform ' ])) {
489- $ mergedDriver ['platform ' ] = $ driver ['platform ' ];
490- }
491-
492- return $ mergedDriver ;
493- }
494-
495- private function prepareEncryptionOptions (array $ options ): array
496- {
497- if (isset ($ options ['keyVaultClient ' ])) {
498- if ($ options ['keyVaultClient ' ] instanceof self) {
499- $ options ['keyVaultClient ' ] = $ options ['keyVaultClient ' ]->manager ;
500- } elseif (! $ options ['keyVaultClient ' ] instanceof Manager) {
501- throw InvalidArgumentException::invalidType ('"keyVaultClient" option ' , $ options ['keyVaultClient ' ], [self ::class, Manager::class]);
502- }
503- }
504-
505- // The server requires an empty document for automatic credentials.
506- if (isset ($ options ['kmsProviders ' ]) && is_array ($ options ['kmsProviders ' ])) {
507- foreach ($ options ['kmsProviders ' ] as $ name => $ provider ) {
508- if ($ provider === []) {
509- $ options ['kmsProviders ' ][$ name ] = new stdClass ();
510- }
511- }
512- }
513-
514- return $ options ;
515- }
516418}
0 commit comments