Skip to content

Commit d5550fd

Browse files
committed
PHPLIB-1635 BuilderEncoder accepts an instance of encoder instead of a class string
1 parent dd77c8d commit d5550fd

File tree

3 files changed

+64
-6
lines changed

3 files changed

+64
-6
lines changed

psalm-baseline.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@
4141
<code><![CDATA[stdClass]]></code>
4242
</MoreSpecificReturnType>
4343
</file>
44+
<file src="src/Builder/BuilderEncoder.php">
45+
<MixedReturnStatement>
46+
<code><![CDATA[$encoder->encode($value)]]></code>
47+
</MixedReturnStatement>
48+
</file>
4449
<file src="src/Builder/Encoder/AbstractExpressionEncoder.php">
4550
<MixedAssignment>
4651
<code><![CDATA[$val]]></code>

src/Builder/BuilderEncoder.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ final class BuilderEncoder implements Encoder
5353
DateTimeInterface::class => DateTimeEncoder::class,
5454
];
5555

56-
/** @var array<class-string, ExpressionEncoder|null> */
56+
/** @var array<class-string, Encoder|null> */
5757
private array $cachedEncoders = [];
5858

59-
/** @param array<class-string, class-string<ExpressionEncoder>> $customEncoders */
59+
/** @param array<class-string, Encoder> $customEncoders */
6060
public function __construct(private readonly array $customEncoders = [])
6161
{
6262
}
@@ -82,7 +82,7 @@ public function encode(mixed $value): Type|stdClass|array|string|int
8282
return $encoder->encode($value);
8383
}
8484

85-
private function getEncoderFor(object $value): ExpressionEncoder|null
85+
private function getEncoderFor(object $value): Encoder|null
8686
{
8787
$valueClass = $value::class;
8888
if (array_key_exists($valueClass, $this->cachedEncoders)) {
@@ -93,13 +93,22 @@ private function getEncoderFor(object $value): ExpressionEncoder|null
9393

9494
// First attempt: match class name exactly
9595
if (isset($encoderList[$valueClass])) {
96-
return $this->cachedEncoders[$valueClass] = new $encoderList[$valueClass]($this);
96+
$encoder = $encoderList[$valueClass];
97+
if (is_string($encoder)) {
98+
$encoder = new $encoder($this);
99+
}
100+
101+
return $this->cachedEncoders[$valueClass] = $encoder;
97102
}
98103

99104
// Second attempt: catch child classes
100-
foreach ($encoderList as $className => $encoderClass) {
105+
foreach ($encoderList as $className => $encoder) {
101106
if ($value instanceof $className) {
102-
return $this->cachedEncoders[$valueClass] = new $encoderClass($this);
107+
if (is_string($encoder)) {
108+
$encoder = new $encoder($this);
109+
}
110+
111+
return $this->cachedEncoders[$valueClass] = $encoder;
103112
}
104113
}
105114

tests/Builder/BuilderEncoderTest.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
use MongoDB\Builder\Pipeline;
1616
use MongoDB\Builder\Query;
1717
use MongoDB\Builder\Stage;
18+
use MongoDB\Builder\Type\FieldPathInterface;
1819
use MongoDB\Builder\Type\Sort;
1920
use MongoDB\Builder\Variable;
21+
use MongoDB\Codec\EncodeIfSupported;
22+
use MongoDB\Codec\Encoder;
2023
use PHPUnit\Framework\Attributes\DataProvider;
2124
use PHPUnit\Framework\TestCase;
2225

@@ -379,6 +382,47 @@ public function testDateTimeEncoding(): void
379382
$this->assertSamePipeline($expected, $pipeline);
380383
}
381384

385+
public function testCustomEncoder(): void
386+
{
387+
$customEncoders = [
388+
FieldPathInterface::class => new class implements Encoder {
389+
use EncodeIfSupported;
390+
391+
public function canEncode(mixed $value): bool
392+
{
393+
return $value instanceof FieldPathInterface;
394+
}
395+
396+
public function encode(mixed $value)
397+
{
398+
return '$prefix.' . $value->name;
399+
}
400+
},
401+
];
402+
$codec = new BuilderEncoder($customEncoders);
403+
404+
$pipeline = new Pipeline(
405+
Stage::project(
406+
threeFavorites: Expression::slice(
407+
Expression::arrayFieldPath('items'),
408+
n: 3,
409+
),
410+
),
411+
);
412+
413+
$expected = [
414+
[
415+
'$project' => [
416+
'threeFavorites' => [
417+
'$slice' => ['$prefix.items', 3],
418+
],
419+
],
420+
],
421+
];
422+
423+
$this->assertSamePipeline($expected, $pipeline, $codec);
424+
}
425+
382426
/** @param list<array<string, mixed>> $expected */
383427
private static function assertSamePipeline(array $expected, Pipeline $pipeline): void
384428
{

0 commit comments

Comments
 (0)