-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #24 from prezly/feature/versionable-serialization
Feature - Versionable serialization
- Loading branch information
Showing
54 changed files
with
2,322 additions
and
705 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
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
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
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
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
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
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
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
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,45 @@ | ||
<?php | ||
namespace Prezly\Slate\Serialization; | ||
|
||
use Prezly\Slate\Serialization\Exceptions\UnsupprotedVersionException; | ||
use Prezly\Slate\Serialization\Versions\v0_40_VersionSerializer; | ||
use Prezly\Slate\Serialization\Versions\v0_46_VersionSerializer; | ||
use Prezly\Slate\Serialization\Versions\VersionSerializer; | ||
|
||
class DefaultVersionSerializerFactory implements VersionSerializerFactory | ||
{ | ||
private const SERIALIZATION_VERSIONS = [ | ||
'0.40' => v0_40_VersionSerializer::class, | ||
'0.41' => v0_40_VersionSerializer::class, | ||
'0.42' => v0_40_VersionSerializer::class, | ||
'0.43' => v0_40_VersionSerializer::class, | ||
'0.44' => v0_40_VersionSerializer::class, | ||
'0.45' => v0_40_VersionSerializer::class, | ||
// 0.46 - leaves data combined into text nodes | ||
'0.46' => v0_46_VersionSerializer::class, | ||
'0.47' => v0_46_VersionSerializer::class, | ||
]; | ||
|
||
/** @var array */ | ||
private $serialization_versions; | ||
|
||
public function __construct(array $serialization_versions = null) | ||
{ | ||
$this->serialization_versions = $serialization_versions ?? self::SERIALIZATION_VERSIONS; | ||
} | ||
|
||
public function getSerializer(string $version): VersionSerializer | ||
{ | ||
$generic_version = implode('.', array_slice(explode('.', $version), 0, 2)); | ||
|
||
if (! isset($this->serialization_versions[$generic_version])) { | ||
throw new UnsupprotedVersionException($version); | ||
} | ||
|
||
$serializer_class = $this->serialization_versions[$generic_version]; | ||
/** @var \Prezly\Slate\Serialization\Versions\VersionSerializer $serializer */ | ||
$serializer = new $serializer_class(); | ||
|
||
return $serializer; | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
src/Serialization/Exceptions/UnsupprotedVersionException.php
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,14 @@ | ||
<?php | ||
namespace Prezly\Slate\Serialization\Exceptions; | ||
|
||
use InvalidArgumentException; | ||
|
||
class UnsupprotedVersionException extends InvalidArgumentException | ||
{ | ||
public function __construct(string $version) | ||
{ | ||
$message = "Unsupported serialization version requested: {$version}"; | ||
|
||
parent::__construct($message, 0, null); | ||
} | ||
} |
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,92 @@ | ||
<?php | ||
namespace Prezly\Slate\Serialization; | ||
|
||
use Prezly\Slate\Model\Entity; | ||
use Prezly\Slate\Model\Value; | ||
use Prezly\Slate\Serialization\Support\ShapeValidator; | ||
use stdClass; | ||
|
||
class Serializer implements ValueSerializer | ||
{ | ||
public const LATEST_SERIALIZATION_VERSION = '0.47'; | ||
|
||
/** @var string */ | ||
private $default_version; | ||
|
||
/** @var int */ | ||
private $json_encode_options; | ||
|
||
/** @var \Prezly\Slate\Serialization\VersionSerializerFactory */ | ||
private $factory; | ||
|
||
/** | ||
* @param string|null $default_version Default serialization version to use | ||
* when serializing/unserializing with no version set. | ||
* @param int|null $json_encode_options JSON options to use for json_encode(). | ||
* @param VersionSerializerFactory|null Factory used to get VersionSerializer for a given factory. | ||
*/ | ||
public function __construct( | ||
?string $default_version = self::LATEST_SERIALIZATION_VERSION, | ||
int $json_encode_options = null, | ||
?VersionSerializerFactory $factory = null | ||
) { | ||
$this->default_version = $default_version ?? self::LATEST_SERIALIZATION_VERSION; | ||
$this->json_encode_options = $json_encode_options; | ||
$this->factory = $factory ?? new DefaultVersionSerializerFactory(); | ||
} | ||
|
||
/** | ||
* Serialize value to JSON | ||
* | ||
* Optionally you can provide desired serialization version. | ||
* | ||
* If no version argument provided, default serialization version | ||
* will be used (which is set to LATEST by default). | ||
* | ||
* @param \Prezly\Slate\Model\Value $value | ||
* @param string|null $version | ||
* @return string | ||
*/ | ||
public function toJson(Value $value, ?string $version = null): string | ||
{ | ||
return json_encode( | ||
$this->serializeValue($value, $version), | ||
$this->json_encode_options | ||
); | ||
} | ||
|
||
/** | ||
* Unserialize value from JSON | ||
* | ||
* Optional you can provide serialization version to use | ||
* in case if value JSON does not have "version" property. | ||
* | ||
* If no version argument is given, default serialization | ||
* version will be implied (which is set to LATEST by default). | ||
* | ||
* @param string $value | ||
* @param string|null $default_version | ||
* @return \Prezly\Slate\Model\Value | ||
*/ | ||
public function fromJson(string $value, ?string $default_version = null): Value | ||
{ | ||
return $this->unserializeValue(json_decode($value, false)); | ||
} | ||
|
||
private function serializeValue(Value $value, ?string $version): stdClass | ||
{ | ||
$version = $version ?? $this->default_version; | ||
$object = $this->factory->getSerializer($version)->serializeValue($value); | ||
$object->version = $version; | ||
|
||
return $object; | ||
} | ||
|
||
private function unserializeValue($value, ?string $default_version = null): Value | ||
{ | ||
$object = ShapeValidator::validateSlateObject($value, Entity::VALUE); | ||
$version = $object->version ?? $default_version ?? $this->default_version; | ||
|
||
return $this->factory->getSerializer($version)->unserializeValue($object); | ||
} | ||
} |
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,71 @@ | ||
<?php | ||
namespace Prezly\Slate\Serialization\Support; | ||
|
||
use InvalidArgumentException; | ||
use stdClass; | ||
|
||
/** | ||
* @internal Please do not use this class outside of this package. | ||
* It's considered internal API and thus is not a subject for semantic versioning. | ||
* The interface may change in future without major version bump. | ||
*/ | ||
class ShapeValidator | ||
{ | ||
/** | ||
* @param \stdClass|mixed $object | ||
* @param string|null $object_type | ||
* @param callable[] $shape [ string $property_name => string $check_function, ... ] | ||
* @return \stdClass | ||
* @throws \InvalidArgumentException | ||
*/ | ||
public static function validateSlateObject($object, string $object_type = null, array $shape = []): stdClass | ||
{ | ||
// Validate it's an stdClass | ||
if (! $object instanceof stdClass) { | ||
throw new InvalidArgumentException(sprintf( | ||
'Unexpected JSON value given: %s. An object is expected to construct %s.', | ||
gettype($object), | ||
ucfirst($object_type) ?: 'a Slate structure object' | ||
)); | ||
} | ||
|
||
// Validate "object" property presence | ||
if (! property_exists($object, 'object')) { | ||
throw new InvalidArgumentException(sprintf( | ||
'Invalid JSON structure given to construct %s. It should have "object" property.', | ||
ucfirst($object_type) | ||
)); | ||
} | ||
|
||
// Validate "object" property value | ||
if ($object_type !== null && $object_type !== $object->object) { | ||
throw new InvalidArgumentException(sprintf( | ||
'Invalid JSON structure given to construct %s. It should have "object" property set to "%s".', | ||
ucfirst($object_type), | ||
$object_type | ||
)); | ||
} | ||
|
||
// Validate Shape | ||
foreach ($shape as $property => $checker) { | ||
if (! property_exists($object, $property)) { | ||
throw new InvalidArgumentException(sprintf( | ||
'Unexpected JSON structure given for %s. A %s should have "%s" property.', | ||
ucfirst($object_type), | ||
ucfirst($object_type), | ||
$property | ||
)); | ||
} | ||
if (! $checker($object->$property)) { | ||
throw new InvalidArgumentException(sprintf( | ||
'Unexpected JSON structure given for %s. The "%s" property should be %s.', | ||
ucfirst($object_type), | ||
$property, | ||
substr($checker, 0, 3) === 'is_' ? substr($checker, 3) : $checker | ||
)); | ||
} | ||
} | ||
|
||
return $object; | ||
} | ||
} |
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,11 @@ | ||
<?php | ||
namespace Prezly\Slate\Serialization; | ||
|
||
use Prezly\Slate\Model\Value; | ||
|
||
interface ValueSerializer | ||
{ | ||
public function toJson(Value $value, ?string $version = null): string; | ||
|
||
public function fromJson(string $value, ?string $default_version = null): 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,9 @@ | ||
<?php | ||
namespace Prezly\Slate\Serialization; | ||
|
||
use Prezly\Slate\Serialization\Versions\VersionSerializer; | ||
|
||
interface VersionSerializerFactory | ||
{ | ||
public function getSerializer(string $version): VersionSerializer; | ||
} |
Oops, something went wrong.