Skip to content

Commit a436b81

Browse files
committed
Add CodecLibrary to keep track of multiple codecs
1 parent 4f588db commit a436b81

File tree

3 files changed

+287
-0
lines changed

3 files changed

+287
-0
lines changed

src/Codec/CodecLibrary.php

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<?php
2+
3+
namespace MongoDB\Codec;
4+
5+
use MongoDB\Exception\UnexpectedValueException;
6+
7+
use function array_map;
8+
use function get_debug_type;
9+
use function sprintf;
10+
11+
class CodecLibrary implements Codec
12+
{
13+
/** @var array<Decoder> */
14+
private $decoders = [];
15+
16+
/** @var array<Encoder> */
17+
private $encoders = [];
18+
19+
/** @param Decoder|Encoder $items */
20+
public function __construct(...$items)
21+
{
22+
array_map(
23+
function ($item) {
24+
if ($item instanceof Decoder) {
25+
$this->attachDecoder($item);
26+
}
27+
28+
if ($item instanceof Encoder) {
29+
$this->attachEncoder($item);
30+
}
31+
32+
// Yes, we'll silently discard everything. Please let me already have union types...
33+
},
34+
$items
35+
);
36+
}
37+
38+
/** @return static */
39+
final public function attachCodec(Codec $codec): self
40+
{
41+
$this->decoders[] = $codec;
42+
$this->encoders[] = $codec;
43+
if ($codec instanceof KnowsCodecLibrary) {
44+
$codec->attachLibrary($this);
45+
}
46+
47+
return $this;
48+
}
49+
50+
/** @return static */
51+
final public function attachDecoder(Decoder $decoder): self
52+
{
53+
$this->decoders[] = $decoder;
54+
if ($decoder instanceof KnowsCodecLibrary) {
55+
$decoder->attachLibrary($this);
56+
}
57+
58+
return $this;
59+
}
60+
61+
/** @return static */
62+
final public function attachEncoder(Encoder $encoder): self
63+
{
64+
$this->encoders[] = $encoder;
65+
if ($encoder instanceof KnowsCodecLibrary) {
66+
$encoder->attachLibrary($this);
67+
}
68+
69+
return $this;
70+
}
71+
72+
/** @param mixed $value */
73+
final public function canDecode($value): bool
74+
{
75+
foreach ($this->decoders as $decoder) {
76+
if ($decoder->canDecode($value)) {
77+
return true;
78+
}
79+
}
80+
81+
return $value === null;
82+
}
83+
84+
/** @param mixed $value */
85+
final public function canEncode($value): bool
86+
{
87+
foreach ($this->encoders as $encoder) {
88+
if ($encoder->canEncode($value)) {
89+
return true;
90+
}
91+
}
92+
93+
return $value === null;
94+
}
95+
96+
/**
97+
* @param mixed $value
98+
* @return mixed
99+
*/
100+
final public function decode($value)
101+
{
102+
foreach ($this->decoders as $decoder) {
103+
if (! $decoder->canDecode($value)) {
104+
continue;
105+
}
106+
107+
return $decoder->decode($value);
108+
}
109+
110+
if ($value === null) {
111+
return null;
112+
}
113+
114+
throw new UnexpectedValueException(sprintf('No decoder found for value of type "%s"', get_debug_type($value)));
115+
}
116+
117+
/**
118+
* @param mixed $value
119+
* @return mixed
120+
*/
121+
final public function encode($value)
122+
{
123+
foreach ($this->encoders as $encoder) {
124+
if (! $encoder->canEncode($value)) {
125+
continue;
126+
}
127+
128+
return $encoder->encode($value);
129+
}
130+
131+
if ($value === null) {
132+
return null;
133+
}
134+
135+
throw new UnexpectedValueException(sprintf('No encoder found for value of type "%s"', get_debug_type($value)));
136+
}
137+
}

src/Codec/KnowsCodecLibrary.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace MongoDB\Codec;
4+
5+
interface KnowsCodecLibrary
6+
{
7+
public function attachLibrary(CodecLibrary $library): void;
8+
}

tests/Codec/CodecLibraryTest.php

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<?php
2+
3+
namespace MongoDB\Tests\Codec;
4+
5+
use MongoDB\Codec\Codec;
6+
use MongoDB\Codec\CodecLibrary;
7+
use MongoDB\Codec\KnowsCodecLibrary;
8+
use MongoDB\Exception\UnexpectedValueException;
9+
use MongoDB\Tests\TestCase;
10+
11+
class CodecLibraryTest extends TestCase
12+
{
13+
public function testDecode(): void
14+
{
15+
$codec = $this->getCodecLibrary();
16+
17+
$this->assertTrue($codec->canDecode('cigam'));
18+
$this->assertFalse($codec->canDecode('magic'));
19+
20+
$this->assertSame('magic', $codec->decode('cigam'));
21+
}
22+
23+
public function testDecodeNull(): void
24+
{
25+
$codec = $this->getCodecLibrary();
26+
27+
$this->assertTrue($codec->canDecode(null));
28+
$this->assertNull($codec->decode(null));
29+
}
30+
31+
public function testDecodeUnsupportedValue(): void
32+
{
33+
$this->expectException(UnexpectedValueException::class);
34+
$this->expectExceptionMessage('No decoder found for value of type "string"');
35+
36+
$this->getCodecLibrary()->decode('foo');
37+
}
38+
39+
public function testEncode(): void
40+
{
41+
$codec = $this->getCodecLibrary();
42+
43+
$this->assertTrue($codec->canEncode('magic'));
44+
$this->assertFalse($codec->canEncode('cigam'));
45+
46+
$this->assertSame('cigam', $codec->encode('magic'));
47+
}
48+
49+
public function testEncodeNull(): void
50+
{
51+
$codec = $this->getCodecLibrary();
52+
53+
$this->assertTrue($codec->canEncode(null));
54+
$this->assertNull($codec->encode(null));
55+
}
56+
57+
public function testEncodeUnsupportedValue(): void
58+
{
59+
$this->expectException(UnexpectedValueException::class);
60+
$this->expectExceptionMessage('No encoder found for value of type "string"');
61+
62+
$this->getCodecLibrary()->encode('foo');
63+
}
64+
65+
private function getCodecLibrary(): CodecLibrary
66+
{
67+
return new CodecLibrary(
68+
/** @template-implements Codec<string, string> */
69+
new class implements Codec
70+
{
71+
public function canDecode($value): bool
72+
{
73+
return $value === 'cigam';
74+
}
75+
76+
public function canEncode($value): bool
77+
{
78+
return $value === 'magic';
79+
}
80+
81+
public function decode($value)
82+
{
83+
return 'magic';
84+
}
85+
86+
public function encode($value)
87+
{
88+
return 'cigam';
89+
}
90+
}
91+
);
92+
}
93+
94+
public function testLibraryAttachesToCodecs(): void
95+
{
96+
$codec = $this->getTestCodec();
97+
$library = $this->getCodecLibrary();
98+
99+
$library->attachCodec($codec);
100+
$this->assertSame($library, $codec->library);
101+
}
102+
103+
public function testLibraryAttachesToCodecsWhenCreating(): void
104+
{
105+
$codec = $this->getTestCodec();
106+
$library = new CodecLibrary($codec);
107+
108+
$this->assertSame($library, $codec->library);
109+
}
110+
111+
private function getTestCodec(): Codec
112+
{
113+
return new class implements Codec, KnowsCodecLibrary {
114+
public $library;
115+
116+
public function attachLibrary(CodecLibrary $library): void
117+
{
118+
$this->library = $library;
119+
}
120+
121+
public function canDecode($value): bool
122+
{
123+
return false;
124+
}
125+
126+
public function canEncode($value): bool
127+
{
128+
return false;
129+
}
130+
131+
public function decode($value)
132+
{
133+
return null;
134+
}
135+
136+
public function encode($value)
137+
{
138+
return null;
139+
}
140+
};
141+
}
142+
}

0 commit comments

Comments
 (0)