Skip to content

Commit

Permalink
Introduce codecs to work with lazy BSON objects
Browse files Browse the repository at this point in the history
  • Loading branch information
alcaeus committed Jul 19, 2023
1 parent 835a2b7 commit e7c3a8b
Show file tree
Hide file tree
Showing 15 changed files with 1,009 additions and 26 deletions.
10 changes: 10 additions & 0 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@
<code>($value is NativeType ? BSONType : $value)</code>
</MixedInferredReturnType>
</file>
<file src="src/Codec/LazyBSONArrayCodec.php">
<MixedAssignment>
<code>$return[]</code>
</MixedAssignment>
</file>
<file src="src/Codec/LazyBSONDocumentCodec.php">
<MixedAssignment>
<code>$return[$field]</code>
</MixedAssignment>
</file>
<file src="src/Command/ListCollections.php">
<MixedAssignment>
<code>$cmd[$option]</code>
Expand Down
123 changes: 123 additions & 0 deletions src/Codec/ArrayCodec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php
/*
* Copyright 2023-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace MongoDB\Codec;

use MongoDB\Exception\UnsupportedValueException;

use function array_map;
use function is_array;

/**
* Codec to recursively encode/decode values in arrays.
*
* @template-implements Codec<array, array>
*/
class ArrayCodec implements Codec, KnowsCodecLibrary
{
private ?CodecLibrary $library = null;

public function attachCodecLibrary(CodecLibrary $library): void
{
$this->library = $library;
}

/**
* @param mixed $value
* @psalm-assert-if-true array $value
*/
public function canDecode($value): bool
{
return is_array($value);
}

/**
* @param mixed $value
* @psalm-assert-if-true array $value
*/
public function canEncode($value): bool
{
return is_array($value);
}

/** @param mixed $value */
public function decode($value): array
{
if (! $this->canDecode($value)) {
throw UnsupportedValueException::invalidDecodableValue($value);
}

return array_map(
/**
* @param mixed $item
* @return mixed
*/
function ($item) {
return $this->getLibrary()->decodeIfSupported($item);
},
$value,
);
}

/**
* @param mixed $value
* @return mixed
* @psalm-return ($value is array ? array : $value)
*/
public function decodeIfSupported($value)
{
return $this->canDecode($value) ? $this->decode($value) : $value;
}

/** @param mixed $value */
public function encode($value): array
{
if (! $this->canEncode($value)) {
throw UnsupportedValueException::invalidEncodableValue($value);
}

return array_map(
/**
* @param mixed $item
* @return mixed
*/
function ($item) {
return $this->getLibrary()->encodeIfSupported($item);
},
$value,
);
}

/**
* @param mixed $value
* @return mixed
* @psalm-return ($value is array ? array : $value)
*/
public function encodeIfSupported($value)
{
return $this->canEncode($value) ? $this->encode($value) : $value;
}

private function getLibrary(): CodecLibrary
{
if (! $this->library) {
$this->library = new CodecLibrary();
}

return $this->library;
}
}
110 changes: 110 additions & 0 deletions src/Codec/LazyBSONArrayCodec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php
/*
* Copyright 2023-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace MongoDB\Codec;

use MongoDB\BSON\PackedArray;
use MongoDB\Exception\UnsupportedValueException;
use MongoDB\Model\LazyBSONArray;

/**
* Codec for lazy decoding of BSON PackedArray instances
*
* @template-implements Codec<PackedArray, LazyBSONArray>
*/
class LazyBSONArrayCodec implements Codec, KnowsCodecLibrary
{
private ?CodecLibrary $library = null;

public function attachCodecLibrary(CodecLibrary $library): void
{
$this->library = $library;
}

/**
* @param mixed $value
* @psalm-assert-if-true PackedArray $value
*/
public function canDecode($value): bool
{
return $value instanceof PackedArray;
}

/**
* @param mixed $value
* @psalm-assert-if-true LazyBSONArray $value
*/
public function canEncode($value): bool
{
return $value instanceof LazyBSONArray;
}

/** @param mixed $value */
public function decode($value): LazyBSONArray
{
if (! $value instanceof PackedArray) {
throw UnsupportedValueException::invalidDecodableValue($value);
}

return new LazyBSONArray($value, $this->getLibrary());
}

/**
* @param mixed $value
* @return mixed
* @psalm-return ($value is PackedArray ? LazyBSONArray : $value)
*/
public function decodeIfSupported($value)
{
return $this->canDecode($value) ? $this->decode($value) : $value;
}

/** @param mixed $value */
public function encode($value): PackedArray
{
if (! $value instanceof LazyBSONArray) {
throw UnsupportedValueException::invalidEncodableValue($value);
}

$return = [];
/** @var mixed $offsetValue */
foreach ($value as $offsetValue) {
$return[] = $this->getLibrary()->encodeIfSupported($offsetValue);
}

return PackedArray::fromPHP($return);
}

/**
* @param mixed $value
* @return mixed
* @psalm-return ($value is LazyBSONArray ? PackedArray : $value)
*/
public function encodeIfSupported($value)
{
return $this->canEncode($value) ? $this->encode($value) : $value;
}

private function getLibrary(): CodecLibrary
{
if (! $this->library) {
$this->library = new LazyBSONCodecLibrary();
}

return $this->library;
}
}
31 changes: 31 additions & 0 deletions src/Codec/LazyBSONCodecLibrary.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
/*
* Copyright 2023-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace MongoDB\Codec;

class LazyBSONCodecLibrary extends CodecLibrary
{
public function __construct()
{
parent::__construct(
new LazyBSONDocumentCodec(),
new LazyBSONArrayCodec(),
new ArrayCodec(),
new ObjectCodec(),
);
}
}
110 changes: 110 additions & 0 deletions src/Codec/LazyBSONDocumentCodec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php
/*
* Copyright 2023-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace MongoDB\Codec;

use MongoDB\BSON\Document;
use MongoDB\Exception\UnsupportedValueException;
use MongoDB\Model\LazyBSONDocument;

/**
* Codec for lazy decoding of BSON Document instances
*
* @template-implements DocumentCodec<LazyBSONDocument>
*/
class LazyBSONDocumentCodec implements DocumentCodec, KnowsCodecLibrary
{
private ?CodecLibrary $library = null;

public function attachCodecLibrary(CodecLibrary $library): void
{
$this->library = $library;
}

/**
* @param mixed $value
* @psalm-assert-if-true Document $value
*/
public function canDecode($value): bool
{
return $value instanceof Document;
}

/**
* @param mixed $value
* @psalm-assert-if-true LazyBSONDocument $value
*/
public function canEncode($value): bool
{
return $value instanceof LazyBSONDocument;
}

/** @param mixed $value */
public function decode($value): LazyBSONDocument
{
if (! $value instanceof Document) {
throw UnsupportedValueException::invalidDecodableValue($value);
}

return new LazyBSONDocument($value, $this->getLibrary());
}

/**
* @param mixed $value
* @return mixed
* @psalm-return ($value is Document ? LazyBSONDocument : $value)
*/
public function decodeIfSupported($value)
{
return $this->canDecode($value) ? $this->decode($value) : $value;
}

/** @param mixed $value */
public function encode($value): Document
{
if (! $value instanceof LazyBSONDocument) {
throw UnsupportedValueException::invalidEncodableValue($value);
}

$return = [];
/** @var mixed $fieldValue */
foreach ($value as $field => $fieldValue) {
$return[$field] = $this->getLibrary()->encodeIfSupported($fieldValue);
}

return Document::fromPHP($return);
}

/**
* @param mixed $value
* @return mixed
* @psalm-return ($value is LazyBSONDocument ? Document : $value)
*/
public function encodeIfSupported($value)
{
return $this->canEncode($value) ? $this->encode($value) : $value;
}

private function getLibrary(): CodecLibrary
{
if (! $this->library) {
$this->library = new LazyBSONCodecLibrary();
}

return $this->library;
}
}
Loading

0 comments on commit e7c3a8b

Please sign in to comment.