Skip to content

Commit 3e2e42f

Browse files
authored
PHPLIB-1180: Add basic infrastructure for codecs (#1125)
* Add psalm stubs for new BSON classes * Add basic codec infrastructure * Introduce codec library * Exclude methods inherited from traits in PedantryTest This commit also uses dataset names for better visibility during debugging * Exclude stubs from git exports * Improve architecture document * Use native types for DocumentCodec interface * Add description to codec interfaces * Remove internal designation from Decoder and Encoder interfaces * Remove unnecessary psalm-param annotations * Rename attachLibrary method in KnowsCodecLibrary interface The new method name is more specific and helps prevent naming conflicts * Use better values in test codecs * Add comment explaining stub files are temporary * Add comment explaining potential mocking when using PHPUnit 10 * Introduce domain exceptions for decoding and encoding * Improve codec architecture documentation
1 parent 794bde5 commit 3e2e42f

18 files changed

+904
-2
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ tests export-ignore
44
docs export-ignore
55
examples export-ignore
66
mongo-orchestration export-ignore
7+
stubs export-ignore
78
tools export-ignore
89
Makefile export-ignore
910
phpcs.xml.dist export-ignore

psalm-baseline.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@
7575
<code>$mergedDriver['platform']</code>
7676
</MixedAssignment>
7777
</file>
78+
<file src="src/Codec/DecodeIfSupported.php">
79+
<MixedInferredReturnType occurrences="1">
80+
<code>($value is BSONType ? NativeType : $value)</code>
81+
</MixedInferredReturnType>
82+
</file>
83+
<file src="src/Codec/EncodeIfSupported.php">
84+
<MixedInferredReturnType occurrences="1">
85+
<code>($value is NativeType ? BSONType : $value)</code>
86+
</MixedInferredReturnType>
87+
</file>
7888
<file src="src/Command/ListCollections.php">
7989
<MixedAssignment occurrences="2">
8090
<code>$cmd[$option]</code>

psalm.xml.dist

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,9 @@
1515
<directory name="vendor" />
1616
</ignoreFiles>
1717
</projectFiles>
18+
<stubs>
19+
<file name="stubs/BSON/Document.stub.php"/>
20+
<file name="stubs/BSON/Iterator.stub.php"/>
21+
<file name="stubs/BSON/PackedArray.stub.php"/>
22+
</stubs>
1823
</psalm>

src/Codec/Codec.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
/*
3+
* Copyright 2023-present MongoDB, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace MongoDB\Codec;
19+
20+
/**
21+
* The Codec interface allows decoding BSON data to native PHP types and back
22+
* to BSON.
23+
*
24+
* @psalm-template BSONType
25+
* @psalm-template NativeType
26+
* @template-extends Decoder<BSONType, NativeType>
27+
* @template-extends Encoder<BSONType, NativeType>
28+
*/
29+
interface Codec extends Decoder, Encoder
30+
{
31+
}

src/Codec/CodecLibrary.php

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
/*
3+
* Copyright 2023-present MongoDB, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace MongoDB\Codec;
19+
20+
use MongoDB\Exception\InvalidArgumentException;
21+
use MongoDB\Exception\UnsupportedValueException;
22+
23+
class CodecLibrary implements Codec
24+
{
25+
use DecodeIfSupported;
26+
use EncodeIfSupported;
27+
28+
/** @var array<Decoder> */
29+
private $decoders = [];
30+
31+
/** @var array<Encoder> */
32+
private $encoders = [];
33+
34+
/** @param Decoder|Encoder $items */
35+
public function __construct(...$items)
36+
{
37+
foreach ($items as $item) {
38+
if (! $item instanceof Decoder && ! $item instanceof Encoder) {
39+
throw InvalidArgumentException::invalidType('$items', $item, [Decoder::class, Encoder::class]);
40+
}
41+
42+
if ($item instanceof Codec) {
43+
// Use attachCodec to avoid multiple calls to attachLibrary
44+
$this->attachCodec($item);
45+
46+
continue;
47+
}
48+
49+
if ($item instanceof Decoder) {
50+
$this->attachDecoder($item);
51+
}
52+
53+
if ($item instanceof Encoder) {
54+
$this->attachEncoder($item);
55+
}
56+
}
57+
}
58+
59+
/** @return static */
60+
final public function attachCodec(Codec $codec): self
61+
{
62+
$this->decoders[] = $codec;
63+
$this->encoders[] = $codec;
64+
if ($codec instanceof KnowsCodecLibrary) {
65+
$codec->attachCodecLibrary($this);
66+
}
67+
68+
return $this;
69+
}
70+
71+
/** @return static */
72+
final public function attachDecoder(Decoder $decoder): self
73+
{
74+
$this->decoders[] = $decoder;
75+
if ($decoder instanceof KnowsCodecLibrary) {
76+
$decoder->attachCodecLibrary($this);
77+
}
78+
79+
return $this;
80+
}
81+
82+
/** @return static */
83+
final public function attachEncoder(Encoder $encoder): self
84+
{
85+
$this->encoders[] = $encoder;
86+
if ($encoder instanceof KnowsCodecLibrary) {
87+
$encoder->attachCodecLibrary($this);
88+
}
89+
90+
return $this;
91+
}
92+
93+
/** @param mixed $value */
94+
final public function canDecode($value): bool
95+
{
96+
foreach ($this->decoders as $decoder) {
97+
if ($decoder->canDecode($value)) {
98+
return true;
99+
}
100+
}
101+
102+
return false;
103+
}
104+
105+
/** @param mixed $value */
106+
final public function canEncode($value): bool
107+
{
108+
foreach ($this->encoders as $encoder) {
109+
if ($encoder->canEncode($value)) {
110+
return true;
111+
}
112+
}
113+
114+
return false;
115+
}
116+
117+
/**
118+
* @param mixed $value
119+
* @return mixed
120+
*/
121+
final public function decode($value)
122+
{
123+
foreach ($this->decoders as $decoder) {
124+
if ($decoder->canDecode($value)) {
125+
return $decoder->decode($value);
126+
}
127+
}
128+
129+
throw UnsupportedValueException::invalidDecodableValue($value);
130+
}
131+
132+
/**
133+
* @param mixed $value
134+
* @return mixed
135+
*/
136+
final public function encode($value)
137+
{
138+
foreach ($this->encoders as $encoder) {
139+
if ($encoder->canEncode($value)) {
140+
return $encoder->encode($value);
141+
}
142+
}
143+
144+
throw UnsupportedValueException::invalidEncodableValue($value);
145+
}
146+
}

src/Codec/DecodeIfSupported.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
/*
3+
* Copyright 2023-present MongoDB, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace MongoDB\Codec;
19+
20+
use MongoDB\Exception\UnsupportedValueException;
21+
22+
/**
23+
* @psalm-template BSONType
24+
* @psalm-template NativeType
25+
*/
26+
trait DecodeIfSupported
27+
{
28+
/**
29+
* @param mixed $value
30+
* @psalm-assert-if-true BSONType $value
31+
*/
32+
abstract public function canDecode($value): bool;
33+
34+
/**
35+
* @param mixed $value
36+
* @psalm-param BSONType $value
37+
* @return mixed
38+
* @psalm-return NativeType
39+
* @throws UnsupportedValueException if the decoder does not support the value
40+
*/
41+
abstract public function decode($value);
42+
43+
/**
44+
* @param mixed $value
45+
* @return mixed
46+
* @psalm-return ($value is BSONType ? NativeType : $value)
47+
*/
48+
public function decodeIfSupported($value)
49+
{
50+
return $this->canDecode($value) ? $this->decode($value) : $value;
51+
}
52+
}

src/Codec/Decoder.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
/*
3+
* Copyright 2023-present MongoDB, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace MongoDB\Codec;
19+
20+
use MongoDB\Exception\UnsupportedValueException;
21+
22+
/**
23+
* @psalm-template BSONType
24+
* @psalm-template NativeType
25+
*/
26+
interface Decoder
27+
{
28+
/**
29+
* Checks if the decoder supports a given value.
30+
*
31+
* @param mixed $value
32+
* @psalm-assert-if-true BSONType $value
33+
*/
34+
public function canDecode($value): bool;
35+
36+
/**
37+
* Decodes a given value. If the decoder does not support the value, it
38+
* should throw an exception.
39+
*
40+
* @param mixed $value
41+
* @psalm-param BSONType $value
42+
* @return mixed
43+
* @psalm-return NativeType
44+
* @throws UnsupportedValueException if the decoder does not support the value
45+
*/
46+
public function decode($value);
47+
48+
/**
49+
* Decodes a given value if supported, otherwise returns the value as-is.
50+
*
51+
* The DecodeIfSupported trait provides a default implementation of this
52+
* method.
53+
*
54+
* @param mixed $value
55+
* @return mixed
56+
* @psalm-return ($value is BSONType ? NativeType : $value)
57+
*/
58+
public function decodeIfSupported($value);
59+
}

src/Codec/DocumentCodec.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
/*
3+
* Copyright 2023-present MongoDB, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace MongoDB\Codec;
19+
20+
use MongoDB\BSON\Document;
21+
use MongoDB\Exception\UnsupportedValueException;
22+
23+
/**
24+
* The DocumentCodec interface allows decoding BSON document data to native PHP
25+
* objects and back to BSON documents.
26+
*
27+
* @psalm-template ObjectType of object
28+
* @template-extends Codec<Document, ObjectType>
29+
*/
30+
interface DocumentCodec extends Codec
31+
{
32+
/**
33+
* @param mixed $value
34+
* @psalm-param Document $value
35+
* @psalm-return ObjectType
36+
* @throws UnsupportedValueException if the decoder does not support the value
37+
*/
38+
public function decode($value): object;
39+
40+
/**
41+
* @param mixed $value
42+
* @psalm-param ObjectType $value
43+
* @throws UnsupportedValueException if the encoder does not support the value
44+
*/
45+
public function encode($value): Document;
46+
}

0 commit comments

Comments
 (0)