Skip to content

PHPLIB-1635 BuilderEncoder accepts an instance of encoder instead of a class string #1608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,10 @@
<code><![CDATA[stdClass]]></code>
</MoreSpecificReturnType>
</file>
<file src="src/Builder/Encoder/AbstractExpressionEncoder.php">
<MixedAssignment>
<code><![CDATA[$val]]></code>
<code><![CDATA[$val]]></code>
<code><![CDATA[$value[$key]]]></code>
</MixedAssignment>
<file src="src/Builder/BuilderEncoder.php">
<MixedReturnStatement>
<code><![CDATA[$encoder->encode($value)]]></code>
</MixedReturnStatement>
</file>
<file src="src/Builder/Encoder/CombinedFieldQueryEncoder.php">
<MixedAssignment>
Expand Down Expand Up @@ -99,6 +97,13 @@
<code><![CDATA[$value]]></code>
</MixedAssignment>
</file>
<file src="src/Builder/Encoder/RecursiveEncode.php">
<MixedAssignment>
<code><![CDATA[$val]]></code>
<code><![CDATA[$val]]></code>
<code><![CDATA[$value[$key]]]></code>
</MixedAssignment>
</file>
<file src="src/Builder/Query.php">
<ArgumentTypeCoercion>
<code><![CDATA[$query]]></code>
Expand Down
25 changes: 17 additions & 8 deletions src/Builder/BuilderEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use MongoDB\Builder\Encoder\CombinedFieldQueryEncoder;
use MongoDB\Builder\Encoder\DateTimeEncoder;
use MongoDB\Builder\Encoder\DictionaryEncoder;
use MongoDB\Builder\Encoder\ExpressionEncoder;
use MongoDB\Builder\Encoder\FieldPathEncoder;
use MongoDB\Builder\Encoder\OperatorEncoder;
use MongoDB\Builder\Encoder\OutputWindowEncoder;
Expand All @@ -33,14 +32,15 @@

use function array_key_exists;
use function is_object;
use function is_string;

/** @template-implements Encoder<Type|stdClass|array|string|int, Pipeline|StageInterface|ExpressionInterface|QueryInterface> */
final class BuilderEncoder implements Encoder
{
/** @template-use EncodeIfSupported<Type|stdClass|array|string|int, Pipeline|StageInterface|ExpressionInterface|QueryInterface> */
use EncodeIfSupported;

/** @var array<class-string, class-string<ExpressionEncoder>> */
/** @var array<class-string, class-string<Encoder>> */
private array $defaultEncoders = [
Pipeline::class => PipelineEncoder::class,
Variable::class => VariableEncoder::class,
Expand All @@ -53,10 +53,10 @@ final class BuilderEncoder implements Encoder
DateTimeInterface::class => DateTimeEncoder::class,
];

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

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

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

// First attempt: match class name exactly
if (isset($encoderList[$valueClass])) {
return $this->cachedEncoders[$valueClass] = new $encoderList[$valueClass]($this);
$encoder = $encoderList[$valueClass];
if (is_string($encoder)) {
$encoder = new $encoder($this);
}

return $this->cachedEncoders[$valueClass] = $encoder;
}

// Second attempt: catch child classes
foreach ($encoderList as $className => $encoderClass) {
foreach ($encoderList as $className => $encoder) {
if ($value instanceof $className) {
return $this->cachedEncoders[$valueClass] = new $encoderClass($this);
if (is_string($encoder)) {
$encoder = new $encoder($this);
}

return $this->cachedEncoders[$valueClass] = $encoder;
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/Builder/Encoder/CombinedFieldQueryEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use LogicException;
use MongoDB\Builder\Type\CombinedFieldQuery;
use MongoDB\Codec\EncodeIfSupported;
use MongoDB\Codec\Encoder;
use MongoDB\Exception\UnsupportedValueException;
use stdClass;

Expand All @@ -17,13 +18,14 @@
use function sprintf;

/**
* @template-extends AbstractExpressionEncoder<stdClass, CombinedFieldQuery>
* @template-implements Encoder<stdClass, CombinedFieldQuery>
* @internal
*/
final class CombinedFieldQueryEncoder extends AbstractExpressionEncoder
final class CombinedFieldQueryEncoder implements Encoder
{
/** @template-use EncodeIfSupported<stdClass, CombinedFieldQuery> */
use EncodeIfSupported;
use RecursiveEncode;

public function canEncode(mixed $value): bool
{
Expand Down
5 changes: 3 additions & 2 deletions src/Builder/Encoder/DateTimeEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
use DateTimeInterface;
use MongoDB\BSON\UTCDateTime;
use MongoDB\Codec\EncodeIfSupported;
use MongoDB\Codec\Encoder;
use MongoDB\Exception\UnsupportedValueException;

/**
* @template-extends AbstractExpressionEncoder<UTCDateTime, DateTimeInterface>
* @template-implements Encoder<UTCDateTime, DateTimeInterface>
* @internal
*/
final class DateTimeEncoder extends AbstractExpressionEncoder
final class DateTimeEncoder implements Encoder
{
/** @template-use EncodeIfSupported<UTCDateTime, DateTimeInterface> */
use EncodeIfSupported;
Expand Down
5 changes: 3 additions & 2 deletions src/Builder/Encoder/DictionaryEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@

use MongoDB\Builder\Type\DictionaryInterface;
use MongoDB\Codec\EncodeIfSupported;
use MongoDB\Codec\Encoder;
use MongoDB\Exception\UnsupportedValueException;
use stdClass;

/**
* @template-extends AbstractExpressionEncoder<string|int|array|stdClass, DictionaryInterface>
* @template-implements Encoder<string|int|array|stdClass, DictionaryInterface>
* @internal
*/
final class DictionaryEncoder extends AbstractExpressionEncoder
final class DictionaryEncoder implements Encoder
{
/** @template-use EncodeIfSupported<string|int|array|stdClass, DictionaryInterface> */
use EncodeIfSupported;
Expand Down
21 changes: 0 additions & 21 deletions src/Builder/Encoder/ExpressionEncoder.php

This file was deleted.

5 changes: 3 additions & 2 deletions src/Builder/Encoder/FieldPathEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@

use MongoDB\Builder\Type\FieldPathInterface;
use MongoDB\Codec\EncodeIfSupported;
use MongoDB\Codec\Encoder;
use MongoDB\Exception\UnsupportedValueException;

/**
* @template-extends AbstractExpressionEncoder<string, FieldPathInterface>
* @template-implements Encoder<string, FieldPathInterface>
* @internal
*/
final class FieldPathEncoder extends AbstractExpressionEncoder
final class FieldPathEncoder implements Encoder
{
/** @template-use EncodeIfSupported<string, FieldPathInterface> */
use EncodeIfSupported;
Expand Down
6 changes: 4 additions & 2 deletions src/Builder/Encoder/OperatorEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use MongoDB\Builder\Type\OperatorInterface;
use MongoDB\Builder\Type\Optional;
use MongoDB\Codec\EncodeIfSupported;
use MongoDB\Codec\Encoder;
use MongoDB\Exception\UnsupportedValueException;
use stdClass;

Expand All @@ -17,13 +18,14 @@
use function sprintf;

/**
* @template-extends AbstractExpressionEncoder<stdClass, OperatorInterface>
* @template-implements Encoder<stdClass, OperatorInterface>
* @internal
*/
final class OperatorEncoder extends AbstractExpressionEncoder
final class OperatorEncoder implements Encoder
{
/** @template-use EncodeIfSupported<stdClass, OperatorInterface> */
use EncodeIfSupported;
use RecursiveEncode;

public function canEncode(mixed $value): bool
{
Expand Down
6 changes: 4 additions & 2 deletions src/Builder/Encoder/OutputWindowEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use MongoDB\Builder\Type\OutputWindow;
use MongoDB\Builder\Type\WindowInterface;
use MongoDB\Codec\EncodeIfSupported;
use MongoDB\Codec\Encoder;
use MongoDB\Exception\UnsupportedValueException;
use stdClass;

Expand All @@ -18,13 +19,14 @@
use function sprintf;

/**
* @template-extends AbstractExpressionEncoder<stdClass, OutputWindow>
* @template-implements Encoder<stdClass, OutputWindow>
* @internal
*/
final class OutputWindowEncoder extends AbstractExpressionEncoder
final class OutputWindowEncoder implements Encoder
{
/** @template-use EncodeIfSupported<stdClass, OutputWindow> */
use EncodeIfSupported;
use RecursiveEncode;

public function canEncode(mixed $value): bool
{
Expand Down
8 changes: 5 additions & 3 deletions src/Builder/Encoder/PipelineEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@

use MongoDB\Builder\Pipeline;
use MongoDB\Codec\EncodeIfSupported;
use MongoDB\Codec\Encoder;
use MongoDB\Exception\UnsupportedValueException;

/**
* @template-extends AbstractExpressionEncoder<list<mixed>, Pipeline>
* @template-implements Encoder<list<mixed>, Pipeline>
* @internal
*/
final class PipelineEncoder extends AbstractExpressionEncoder
final class PipelineEncoder implements Encoder
{
/** @template-use EncodeIfSupported<list<mixed>, Pipeline> */
use EncodeIfSupported;
use RecursiveEncode;

/** @psalm-assert-if-true Pipeline $value */
public function canEncode(mixed $value): bool
Expand All @@ -32,7 +34,7 @@

$encoded = [];
foreach ($value->getIterator() as $stage) {
$encoded[] = $this->encoder->encodeIfSupported($stage);
$encoded[] = $this->recursiveEncode($stage);

Check notice

Code scanning / Psalm

MixedAssignment Note

Unable to determine the type of this assignment
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the return type be derived from @psalm-return on RecursiveEncode::recursiveEncode()? If not, does this need to be ignored in the baseline?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's already ignored in the baseline, but the first time, GitHub stills shows it.

}

return $encoded;
Expand Down
6 changes: 4 additions & 2 deletions src/Builder/Encoder/QueryEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use MongoDB\Builder\Type\QueryInterface;
use MongoDB\Builder\Type\QueryObject;
use MongoDB\Codec\EncodeIfSupported;
use MongoDB\Codec\Encoder;
use MongoDB\Exception\UnsupportedValueException;
use stdClass;

Expand All @@ -16,13 +17,14 @@
use function sprintf;

/**
* @template-extends AbstractExpressionEncoder<stdClass, QueryObject>
* @template-implements Encoder<stdClass, QueryObject>
* @internal
*/
final class QueryEncoder extends AbstractExpressionEncoder
final class QueryEncoder implements Encoder
{
/** @template-use EncodeIfSupported<stdClass, QueryObject> */
use EncodeIfSupported;
use RecursiveEncode;

public function canEncode(mixed $value): bool
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,13 @@

namespace MongoDB\Builder\Encoder;

use MongoDB\BSON\Type;
use MongoDB\Builder\BuilderEncoder;
use stdClass;

use function get_object_vars;
use function is_array;

/**
* @template BSONType of Type|stdClass|array|string|int
* @template NativeType
* @template-implements ExpressionEncoder<BSONType, NativeType>
* @internal
*/
abstract class AbstractExpressionEncoder implements ExpressionEncoder
trait RecursiveEncode
{
final public function __construct(protected readonly BuilderEncoder $encoder)
{
Expand All @@ -33,7 +26,7 @@ final public function __construct(protected readonly BuilderEncoder $encoder)
*
* @template T
*/
final protected function recursiveEncode(mixed $value): mixed
private function recursiveEncode(mixed $value): mixed
{
if (is_array($value)) {
foreach ($value as $key => $val) {
Expand Down
5 changes: 3 additions & 2 deletions src/Builder/Encoder/VariableEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@

use MongoDB\Builder\Expression\Variable;
use MongoDB\Codec\EncodeIfSupported;
use MongoDB\Codec\Encoder;
use MongoDB\Exception\UnsupportedValueException;

/**
* @template-extends AbstractExpressionEncoder<string, Variable>
* @template-implements Encoder<string, Variable>
* @internal
*/
final class VariableEncoder extends AbstractExpressionEncoder
final class VariableEncoder implements Encoder
{
/** @template-use EncodeIfSupported<string, Variable> */
use EncodeIfSupported;
Expand Down
Loading
Loading