-
Notifications
You must be signed in to change notification settings - Fork 264
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add CodecLibrary to keep track of multiple codecs
- Loading branch information
Showing
3 changed files
with
287 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
<?php | ||
|
||
namespace MongoDB\Codec; | ||
|
||
use MongoDB\Exception\UnexpectedValueException; | ||
|
||
use function array_map; | ||
use function get_debug_type; | ||
use function sprintf; | ||
|
||
class CodecLibrary implements Codec | ||
{ | ||
/** @var array<Decoder> */ | ||
private $decoders = []; | ||
|
||
/** @var array<Encoder> */ | ||
private $encoders = []; | ||
|
||
/** @param Decoder|Encoder $items */ | ||
public function __construct(...$items) | ||
{ | ||
array_map( | ||
function ($item) { | ||
if ($item instanceof Decoder) { | ||
$this->attachDecoder($item); | ||
} | ||
|
||
if ($item instanceof Encoder) { | ||
$this->attachEncoder($item); | ||
} | ||
|
||
// Yes, we'll silently discard everything. Please let me already have union types... | ||
}, | ||
$items | ||
); | ||
} | ||
|
||
/** @return static */ | ||
final public function attachCodec(Codec $codec): self | ||
{ | ||
$this->decoders[] = $codec; | ||
$this->encoders[] = $codec; | ||
if ($codec instanceof KnowsCodecLibrary) { | ||
$codec->attachLibrary($this); | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** @return static */ | ||
final public function attachDecoder(Decoder $decoder): self | ||
{ | ||
$this->decoders[] = $decoder; | ||
if ($decoder instanceof KnowsCodecLibrary) { | ||
$decoder->attachLibrary($this); | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** @return static */ | ||
final public function attachEncoder(Encoder $encoder): self | ||
{ | ||
$this->encoders[] = $encoder; | ||
if ($encoder instanceof KnowsCodecLibrary) { | ||
$encoder->attachLibrary($this); | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** @param mixed $value */ | ||
final public function canDecode($value): bool | ||
{ | ||
foreach ($this->decoders as $decoder) { | ||
if ($decoder->canDecode($value)) { | ||
return true; | ||
} | ||
} | ||
|
||
return $value === null; | ||
} | ||
|
||
/** @param mixed $value */ | ||
final public function canEncode($value): bool | ||
{ | ||
foreach ($this->encoders as $encoder) { | ||
if ($encoder->canEncode($value)) { | ||
return true; | ||
} | ||
} | ||
|
||
return $value === null; | ||
} | ||
|
||
/** | ||
* @param mixed $value | ||
* @return mixed | ||
*/ | ||
final public function decode($value) | ||
{ | ||
foreach ($this->decoders as $decoder) { | ||
if (! $decoder->canDecode($value)) { | ||
continue; | ||
} | ||
|
||
return $decoder->decode($value); | ||
} | ||
|
||
if ($value === null) { | ||
return null; | ||
} | ||
|
||
throw new UnexpectedValueException(sprintf('No decoder found for value of type "%s"', get_debug_type($value))); | ||
} | ||
|
||
/** | ||
* @param mixed $value | ||
* @return mixed | ||
*/ | ||
final public function encode($value) | ||
{ | ||
foreach ($this->encoders as $encoder) { | ||
if (! $encoder->canEncode($value)) { | ||
continue; | ||
} | ||
|
||
return $encoder->encode($value); | ||
} | ||
|
||
if ($value === null) { | ||
return null; | ||
} | ||
|
||
throw new UnexpectedValueException(sprintf('No encoder found for value of type "%s"', get_debug_type($value))); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?php | ||
|
||
namespace MongoDB\Codec; | ||
|
||
interface KnowsCodecLibrary | ||
{ | ||
public function attachLibrary(CodecLibrary $library): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
<?php | ||
|
||
namespace MongoDB\Tests\Codec; | ||
|
||
use MongoDB\Codec\Codec; | ||
use MongoDB\Codec\CodecLibrary; | ||
use MongoDB\Codec\KnowsCodecLibrary; | ||
use MongoDB\Exception\UnexpectedValueException; | ||
use MongoDB\Tests\TestCase; | ||
|
||
class CodecLibraryTest extends TestCase | ||
{ | ||
public function testDecode(): void | ||
{ | ||
$codec = $this->getCodecLibrary(); | ||
|
||
$this->assertTrue($codec->canDecode('cigam')); | ||
$this->assertFalse($codec->canDecode('magic')); | ||
|
||
$this->assertSame('magic', $codec->decode('cigam')); | ||
} | ||
|
||
public function testDecodeNull(): void | ||
{ | ||
$codec = $this->getCodecLibrary(); | ||
|
||
$this->assertTrue($codec->canDecode(null)); | ||
$this->assertNull($codec->decode(null)); | ||
} | ||
|
||
public function testDecodeUnsupportedValue(): void | ||
{ | ||
$this->expectException(UnexpectedValueException::class); | ||
$this->expectExceptionMessage('No decoder found for value of type "string"'); | ||
|
||
$this->getCodecLibrary()->decode('foo'); | ||
} | ||
|
||
public function testEncode(): void | ||
{ | ||
$codec = $this->getCodecLibrary(); | ||
|
||
$this->assertTrue($codec->canEncode('magic')); | ||
$this->assertFalse($codec->canEncode('cigam')); | ||
|
||
$this->assertSame('cigam', $codec->encode('magic')); | ||
} | ||
|
||
public function testEncodeNull(): void | ||
{ | ||
$codec = $this->getCodecLibrary(); | ||
|
||
$this->assertTrue($codec->canEncode(null)); | ||
$this->assertNull($codec->encode(null)); | ||
} | ||
|
||
public function testEncodeUnsupportedValue(): void | ||
{ | ||
$this->expectException(UnexpectedValueException::class); | ||
$this->expectExceptionMessage('No encoder found for value of type "string"'); | ||
|
||
$this->getCodecLibrary()->encode('foo'); | ||
} | ||
|
||
private function getCodecLibrary(): CodecLibrary | ||
{ | ||
return new CodecLibrary( | ||
/** @template-implements Codec<string, string> */ | ||
new class implements Codec | ||
{ | ||
public function canDecode($value): bool | ||
{ | ||
return $value === 'cigam'; | ||
} | ||
|
||
public function canEncode($value): bool | ||
{ | ||
return $value === 'magic'; | ||
} | ||
|
||
public function decode($value) | ||
{ | ||
return 'magic'; | ||
} | ||
|
||
public function encode($value) | ||
{ | ||
return 'cigam'; | ||
} | ||
} | ||
); | ||
} | ||
|
||
public function testLibraryAttachesToCodecs(): void | ||
{ | ||
$codec = $this->getTestCodec(); | ||
$library = $this->getCodecLibrary(); | ||
|
||
$library->attachCodec($codec); | ||
$this->assertSame($library, $codec->library); | ||
} | ||
|
||
public function testLibraryAttachesToCodecsWhenCreating(): void | ||
{ | ||
$codec = $this->getTestCodec(); | ||
$library = new CodecLibrary($codec); | ||
|
||
$this->assertSame($library, $codec->library); | ||
} | ||
|
||
private function getTestCodec(): Codec | ||
{ | ||
return new class implements Codec, KnowsCodecLibrary { | ||
public $library; | ||
|
||
public function attachLibrary(CodecLibrary $library): void | ||
{ | ||
$this->library = $library; | ||
} | ||
|
||
public function canDecode($value): bool | ||
{ | ||
return false; | ||
} | ||
|
||
public function canEncode($value): bool | ||
{ | ||
return false; | ||
} | ||
|
||
public function decode($value) | ||
{ | ||
return null; | ||
} | ||
|
||
public function encode($value) | ||
{ | ||
return null; | ||
} | ||
}; | ||
} | ||
} |