Skip to content

Commit fe5e143

Browse files
committed
PHPLIB-1505 Add driver option "builderEncoder"
1 parent d2a9458 commit fe5e143

File tree

6 files changed

+122
-63
lines changed

6 files changed

+122
-63
lines changed

src/Client.php

+16-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
use Composer\InstalledVersions;
2121
use Iterator;
22+
use MongoDB\Builder\BuilderEncoder;
23+
use MongoDB\Codec\Encoder;
2224
use MongoDB\Driver\ClientEncryption;
2325
use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException;
2426
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
@@ -40,6 +42,7 @@
4042
use MongoDB\Operation\Watch;
4143
use Throwable;
4244

45+
use function array_diff_key;
4346
use function is_array;
4447
use function is_string;
4548

@@ -67,6 +70,8 @@ class Client
6770

6871
private array $typeMap;
6972

73+
private Encoder $builderEncoder;
74+
7075
private WriteConcern $writeConcern;
7176

7277
/**
@@ -78,6 +83,8 @@ class Client
7883
*
7984
* Supported driver-specific options:
8085
*
86+
* * builderEncoder (MongoDB\Builder\Encoder): Encoder for query builder
87+
*
8188
* * typeMap (array): Default type map for cursors and BSON documents.
8289
*
8390
* Other options are documented in MongoDB\Driver\Manager::__construct().
@@ -96,6 +103,10 @@ public function __construct(?string $uri = null, array $uriOptions = [], array $
96103
{
97104
$driverOptions += ['typeMap' => self::DEFAULT_TYPE_MAP];
98105

106+
if (isset($driverOptions['builderEncoder']) && ! $driverOptions['builderEncoder'] instanceof Encoder) {
107+
throw InvalidArgumentException::invalidType('"builderEncoder" option', $driverOptions['builderEncoder'], Encoder::class);
108+
}
109+
99110
if (! is_array($driverOptions['typeMap'])) {
100111
throw InvalidArgumentException::invalidType('"typeMap" driver option', $driverOptions['typeMap'], 'array');
101112
}
@@ -111,9 +122,10 @@ public function __construct(?string $uri = null, array $uriOptions = [], array $
111122
$driverOptions['driver'] = $this->mergeDriverInfo($driverOptions['driver'] ?? []);
112123

113124
$this->uri = $uri ?? self::DEFAULT_URI;
125+
$this->builderEncoder = $driverOptions['builderEncoder'] ?? new BuilderEncoder();
114126
$this->typeMap = $driverOptions['typeMap'];
115127

116-
unset($driverOptions['typeMap']);
128+
$driverOptions = array_diff_key($driverOptions, ['builderEncoder' => 1, 'typeMap' => 1]);
117129

118130
$this->manager = new Manager($uri, $uriOptions, $driverOptions);
119131
$this->readConcern = $this->manager->getReadConcern();
@@ -133,6 +145,7 @@ public function __debugInfo()
133145
'manager' => $this->manager,
134146
'uri' => $this->uri,
135147
'typeMap' => $this->typeMap,
148+
'builderEncoder' => $this->builderEncoder,
136149
'writeConcern' => $this->writeConcern,
137150
];
138151
}
@@ -329,7 +342,7 @@ final public function removeSubscriber(Subscriber $subscriber): void
329342
*/
330343
public function selectCollection(string $databaseName, string $collectionName, array $options = [])
331344
{
332-
$options += ['typeMap' => $this->typeMap];
345+
$options += ['typeMap' => $this->typeMap, 'builderEncoder' => $this->builderEncoder];
333346

334347
return new Collection($this->manager, $databaseName, $collectionName, $options);
335348
}
@@ -345,7 +358,7 @@ public function selectCollection(string $databaseName, string $collectionName, a
345358
*/
346359
public function selectDatabase(string $databaseName, array $options = [])
347360
{
348-
$options += ['typeMap' => $this->typeMap];
361+
$options += ['typeMap' => $this->typeMap, 'builderEncoder' => $this->builderEncoder];
349362

350363
return new Database($this->manager, $databaseName, $options);
351364
}

src/Collection.php

+13
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
use Countable;
2121
use Iterator;
2222
use MongoDB\BSON\JavascriptInterface;
23+
use MongoDB\Builder\BuilderEncoder;
2324
use MongoDB\Codec\DocumentCodec;
25+
use MongoDB\Codec\Encoder;
2426
use MongoDB\Driver\CursorInterface;
2527
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
2628
use MongoDB\Driver\Manager;
@@ -84,6 +86,8 @@ class Collection
8486

8587
private const WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE = 8;
8688

89+
private Encoder $builderEncoder;
90+
8791
private ?DocumentCodec $codec = null;
8892

8993
private ReadConcern $readConcern;
@@ -102,6 +106,8 @@ class Collection
102106
*
103107
* Supported options:
104108
*
109+
* * builderEncoder (MongoDB\Builder\Encoder): Encoder for query builder
110+
*
105111
* * codec (MongoDB\Codec\DocumentCodec): Codec used to decode documents
106112
* from BSON to PHP objects.
107113
*
@@ -134,6 +140,10 @@ public function __construct(private Manager $manager, private string $databaseNa
134140
throw new InvalidArgumentException('$collectionName is invalid: ' . $collectionName);
135141
}
136142

143+
if (isset($options['builderEncoder']) && ! $options['builderEncoder'] instanceof Encoder) {
144+
throw InvalidArgumentException::invalidType('"builderEncoder" option', $options['builderEncoder'], Encoder::class);
145+
}
146+
137147
if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) {
138148
throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class);
139149
}
@@ -154,6 +164,7 @@ public function __construct(private Manager $manager, private string $databaseNa
154164
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
155165
}
156166

167+
$this->builderEncoder = $options['builderEncoder'] ?? new BuilderEncoder();
157168
$this->codec = $options['codec'] ?? null;
158169
$this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern();
159170
$this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference();
@@ -170,6 +181,7 @@ public function __construct(private Manager $manager, private string $databaseNa
170181
public function __debugInfo()
171182
{
172183
return [
184+
'builderEncoder' => $this->builderEncoder,
173185
'codec' => $this->codec,
174186
'collectionName' => $this->collectionName,
175187
'databaseName' => $this->databaseName,
@@ -1084,6 +1096,7 @@ public function watch(array $pipeline = [], array $options = [])
10841096
public function withOptions(array $options = [])
10851097
{
10861098
$options += [
1099+
'builderEncoder' => $this->builderEncoder,
10871100
'codec' => $this->codec,
10881101
'readConcern' => $this->readConcern,
10891102
'readPreference' => $this->readPreference,

src/Database.php

+13
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
namespace MongoDB;
1919

2020
use Iterator;
21+
use MongoDB\Builder\BuilderEncoder;
22+
use MongoDB\Codec\Encoder;
2123
use MongoDB\Driver\ClientEncryption;
2224
use MongoDB\Driver\Cursor;
2325
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
@@ -61,6 +63,8 @@ class Database
6163

6264
private const WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE = 8;
6365

66+
private Encoder $builderEncoder;
67+
6468
private ReadConcern $readConcern;
6569

6670
private ReadPreference $readPreference;
@@ -77,6 +81,8 @@ class Database
7781
*
7882
* Supported options:
7983
*
84+
* * builderEncoder (MongoDB\Builder\Encoder): Encoder for query builder
85+
*
8086
* * readConcern (MongoDB\Driver\ReadConcern): The default read concern to
8187
* use for database operations and selected collections. Defaults to the
8288
* Manager's read concern.
@@ -102,6 +108,10 @@ public function __construct(private Manager $manager, private string $databaseNa
102108
throw new InvalidArgumentException('$databaseName is invalid: ' . $databaseName);
103109
}
104110

111+
if (isset($options['builderEncoder']) && ! $options['builderEncoder'] instanceof Encoder) {
112+
throw InvalidArgumentException::invalidType('"builderEncoder" option', $options['builderEncoder'], Encoder::class);
113+
}
114+
105115
if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
106116
throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class);
107117
}
@@ -118,6 +128,7 @@ public function __construct(private Manager $manager, private string $databaseNa
118128
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
119129
}
120130

131+
$this->builderEncoder = $options['builderEncoder'] ?? new BuilderEncoder();
121132
$this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern();
122133
$this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference();
123134
$this->typeMap = $options['typeMap'] ?? self::DEFAULT_TYPE_MAP;
@@ -133,6 +144,7 @@ public function __construct(private Manager $manager, private string $databaseNa
133144
public function __debugInfo()
134145
{
135146
return [
147+
'builderEncoder' => $this->builderEncoder,
136148
'databaseName' => $this->databaseName,
137149
'manager' => $this->manager,
138150
'readConcern' => $this->readConcern,
@@ -553,6 +565,7 @@ public function renameCollection(string $fromCollectionName, string $toCollectio
553565
public function selectCollection(string $collectionName, array $options = [])
554566
{
555567
$options += [
568+
'builderEncoder' => $this->builderEncoder,
556569
'readConcern' => $this->readConcern,
557570
'readPreference' => $this->readPreference,
558571
'typeMap' => $this->typeMap,

tests/ClientTest.php

+21-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace MongoDB\Tests;
44

55
use MongoDB\Client;
6+
use MongoDB\Codec\Encoder;
67
use MongoDB\Driver\ClientEncryption;
78
use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException;
89
use MongoDB\Driver\ReadConcern;
@@ -45,6 +46,10 @@ public function provideInvalidConstructorDriverOptions()
4546
{
4647
$options = [];
4748

49+
foreach ($this->getInvalidObjectValues() as $value) {
50+
$options[][] = ['builderEncoder' => $value];
51+
}
52+
4853
foreach ($this->getInvalidArrayValues(true) as $value) {
4954
$options[][] = ['typeMap' => $value];
5055
}
@@ -85,13 +90,15 @@ public function testSelectCollectionInheritsOptions(): void
8590
];
8691

8792
$driverOptions = [
93+
'builderEncoder' => $builderEncoder = $this->createMock(Encoder::class),
8894
'typeMap' => ['root' => 'array'],
8995
];
9096

9197
$client = new Client(static::getUri(), $uriOptions, $driverOptions);
9298
$collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName());
9399
$debug = $collection->__debugInfo();
94100

101+
$this->assertSame($builderEncoder, $debug['builderEncoder']);
95102
$this->assertInstanceOf(ReadConcern::class, $debug['readConcern']);
96103
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
97104
$this->assertInstanceOf(ReadPreference::class, $debug['readPreference']);
@@ -105,6 +112,7 @@ public function testSelectCollectionInheritsOptions(): void
105112
public function testSelectCollectionPassesOptions(): void
106113
{
107114
$collectionOptions = [
115+
'builderEncoder' => $builderEncoder = $this->createMock(Encoder::class),
108116
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
109117
'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED),
110118
'typeMap' => ['root' => 'array'],
@@ -115,6 +123,7 @@ public function testSelectCollectionPassesOptions(): void
115123
$collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName(), $collectionOptions);
116124
$debug = $collection->__debugInfo();
117125

126+
$this->assertSame($builderEncoder, $debug['builderEncoder']);
118127
$this->assertInstanceOf(ReadConcern::class, $debug['readConcern']);
119128
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
120129
$this->assertInstanceOf(ReadPreference::class, $debug['readPreference']);
@@ -129,11 +138,19 @@ public function testGetSelectsDatabaseAndInheritsOptions(): void
129138
{
130139
$uriOptions = ['w' => WriteConcern::MAJORITY];
131140

132-
$client = new Client(static::getUri(), $uriOptions);
141+
$driverOptions = [
142+
'builderEncoder' => $builderEncoder = $this->createMock(Encoder::class),
143+
'typeMap' => ['root' => 'array'],
144+
];
145+
146+
$client = new Client(static::getUri(), $uriOptions, $driverOptions);
133147
$database = $client->{$this->getDatabaseName()};
134148
$debug = $database->__debugInfo();
135149

150+
$this->assertSame($builderEncoder, $debug['builderEncoder']);
136151
$this->assertSame($this->getDatabaseName(), $debug['databaseName']);
152+
$this->assertIsArray($debug['typeMap']);
153+
$this->assertSame(['root' => 'array'], $debug['typeMap']);
137154
$this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']);
138155
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
139156
}
@@ -147,13 +164,15 @@ public function testSelectDatabaseInheritsOptions(): void
147164
];
148165

149166
$driverOptions = [
167+
'builderEncoder' => $builderEncoder = $this->createMock(Encoder::class),
150168
'typeMap' => ['root' => 'array'],
151169
];
152170

153171
$client = new Client(static::getUri(), $uriOptions, $driverOptions);
154172
$database = $client->selectDatabase($this->getDatabaseName());
155173
$debug = $database->__debugInfo();
156174

175+
$this->assertSame($builderEncoder, $debug['builderEncoder']);
157176
$this->assertInstanceOf(ReadConcern::class, $debug['readConcern']);
158177
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
159178
$this->assertInstanceOf(ReadPreference::class, $debug['readPreference']);
@@ -167,6 +186,7 @@ public function testSelectDatabaseInheritsOptions(): void
167186
public function testSelectDatabasePassesOptions(): void
168187
{
169188
$databaseOptions = [
189+
'builderEncoder' => $builderEncoder = $this->createMock(Encoder::class),
170190
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
171191
'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED),
172192
'typeMap' => ['root' => 'array'],

tests/Collection/CollectionFunctionalTest.php

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Closure;
66
use MongoDB\BSON\Javascript;
7+
use MongoDB\Codec\Encoder;
78
use MongoDB\Collection;
89
use MongoDB\Database;
910
use MongoDB\Driver\BulkWrite;
@@ -66,6 +67,7 @@ public function testConstructorOptionTypeChecks(array $options): void
6667
public function provideInvalidConstructorOptions(): array
6768
{
6869
return $this->createOptionDataProvider([
70+
'builderEncoder' => $this->getInvalidObjectValues(),
6971
'codec' => $this->getInvalidDocumentCodecValues(),
7072
'readConcern' => $this->getInvalidReadConcernValues(),
7173
'readPreference' => $this->getInvalidReadPreferenceValues(),
@@ -396,6 +398,7 @@ public function testWithOptionsInheritsOptions(): void
396398
public function testWithOptionsPassesOptions(): void
397399
{
398400
$collectionOptions = [
401+
'builderEncoder' => $builderEncoder = $this->createMock(Encoder::class),
399402
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
400403
'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED),
401404
'typeMap' => ['root' => 'array'],
@@ -405,6 +408,7 @@ public function testWithOptionsPassesOptions(): void
405408
$clone = $this->collection->withOptions($collectionOptions);
406409
$debug = $clone->__debugInfo();
407410

411+
$this->assertSame($builderEncoder, $debug['builderEncoder']);
408412
$this->assertInstanceOf(ReadConcern::class, $debug['readConcern']);
409413
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
410414
$this->assertInstanceOf(ReadPreference::class, $debug['readPreference']);

0 commit comments

Comments
 (0)