Skip to content

Commit

Permalink
Merge pull request #6 from sunrise-php/release/v1.3.0
Browse files Browse the repository at this point in the history
v1.3.0
  • Loading branch information
fenric authored Jul 29, 2021
2 parents 5a77c75 + 93a85dd commit b206924
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 4 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"scripts": {
"test": [
"phpcs",
"phpunit --colors=always --coverage-text"
"XDEBUG_MODE=coverage phpunit --colors=always --coverage-text"
]
}
}
67 changes: 65 additions & 2 deletions src/Hydrator/Hydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,25 @@
use function array_key_exists;
use function class_exists;
use function constant;
use function ctype_digit;
use function defined;
use function in_array;
use function is_array;
use function is_int;
use function is_scalar;
use function is_string;
use function is_subclass_of;
use function json_decode;
use function json_last_error;
use function json_last_error_msg;
use function sprintf;
use function strtotime;

/**
* Import constants
*/
use const JSON_ERROR_NONE;

/**
* Hydrator
*/
Expand Down Expand Up @@ -172,6 +182,11 @@ private function hydrateProperty(
return;
}

if (is_subclass_of($type->getName(), JsonableObjectInterface::class)) {
$this->hydratePropertyWithJsonOneToOneAssociation($object, $class, $property, $type, $value);
return;
}

if (is_subclass_of($type->getName(), HydrableObjectInterface::class)) {
$this->hydratePropertyWithOneToOneAssociation($object, $class, $property, $type, $value);
return;
Expand Down Expand Up @@ -329,14 +344,19 @@ private function hydratePropertyWithEnumerableValue(
ReflectionNamedType $type,
$value
) : void {
if (!is_string($value)) {
if (!is_int($value) && !is_string($value)) {
throw new Exception\InvalidValueException(sprintf(
'The <%s.%s> property only accepts a string.',
'The <%s.%s> property only accepts an integer or a string.',
$class->getShortName(),
$property->getName(),
));
}

// support for integer cases...
if (is_int($value) || ctype_digit($value)) {
$value = '_' . $value;
}

$enum = $type->getName();
$constant = sprintf('%s::%s', $enum, $value);
if (!defined($constant)) {
Expand Down Expand Up @@ -385,6 +405,49 @@ private function hydratePropertyWithDateTime(
$property->setValue($object, $dateTime);
}

/**
* Hydrates the given property with the given JSON one-to-one value
*
* @param HydrableObjectInterface $object
* @param ReflectionClass $class
* @param ReflectionProperty $property
* @param ReflectionNamedType $type
* @param mixed $value
*
* @return void
*
* @throws Exception\InvalidValueException
* If the given value isn't valid.
*/
public function hydratePropertyWithJsonOneToOneAssociation(
HydrableObjectInterface $object,
ReflectionClass $class,
ReflectionProperty $property,
ReflectionNamedType $type,
$value
) : void {
if (!is_string($value)) {
throw new Exception\InvalidValueException(sprintf(
'The <%s.%s> property only accepts a string.',
$class->getShortName(),
$property->getName(),
));
}

json_decode(''); // reset previous error...
$value = (array) json_decode($value, true);
if (JSON_ERROR_NONE <> json_last_error()) {
throw new Exception\InvalidValueException(sprintf(
'The <%s.%s> property only accepts valid JSON data (%s).',
$class->getShortName(),
$property->getName(),
json_last_error_msg(),
));
}

$this->hydratePropertyWithOneToOneAssociation($object, $class, $property, $type, $value);
}

/**
* Hydrates the given property with the given one-to-one value
*
Expand Down
19 changes: 19 additions & 0 deletions src/Hydrator/JsonableObjectInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php declare(strict_types=1);

/**
* It's free open-source software released under the MIT License.
*
* @author Anatoly Fenric <[email protected]>
* @copyright Copyright (c) 2021, Anatoly Fenric
* @license https://github.com/sunrise-php/hydrator/blob/master/LICENSE
* @link https://github.com/sunrise-php/hydrator
*/

namespace Sunrise\Hydrator;

/**
* JsonableObjectInterface
*/
interface JsonableObjectInterface extends HydrableObjectInterface
{
}
4 changes: 4 additions & 0 deletions tests/Hydrator/Fixture/TestEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ final class TestEnum extends EnumerableObject
public const A = 'A:value';
public const B = 'B:value';
public const C = 'C:value';

public const _0 = '0:value';
public const _1 = '1:value';
public const _2 = '2:value';
}
16 changes: 16 additions & 0 deletions tests/Hydrator/Fixture/TestJsonDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

namespace Sunrise\Hydrator\Tests\Fixture;

/**
* Import classes
*/
use Sunrise\Hydrator\HydrableObjectInterface;

/**
* TestJsonDto
*/
final class TestJsonDto implements HydrableObjectInterface
{
public TestJsonableObject $json;
}
17 changes: 17 additions & 0 deletions tests/Hydrator/Fixture/TestJsonableObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php declare(strict_types=1);

namespace Sunrise\Hydrator\Tests\Fixture;

/**
* Import classes
*/
use Sunrise\Hydrator\JsonableObjectInterface;

/**
* TestJsonableObject
*/
final class TestJsonableObject implements JsonableObjectInterface
{
public string $foo;
public string $bar;
}
55 changes: 54 additions & 1 deletion tests/Hydrator/HydratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,41 @@ public function testHydrate() : void
$this->assertSame($data['alias'], $object->hidden);
}

/**
* @return void
*/
public function testJson() : void
{
$json = '{"foo":"foo:value","bar":"bar:value"}';

$object = (new Hydrator)->hydrate(new Fixture\TestJsonDto(), ['json' => $json]);

$this->assertSame('foo:value', $object->json->foo);
$this->assertSame('bar:value', $object->json->bar);
}

/**
* @return void
*/
public function testInvalidSyntaxJson() : void
{
$this->expectException(Exception\InvalidValueException::class);
$this->expectExceptionMessage('The <TestJsonDto.json> property only accepts valid JSON data (Syntax error).');

(new Hydrator)->hydrate(new Fixture\TestJsonDto(), ['json' => '{']);
}

/**
* @return void
*/
public function testInvalidJsonType() : void
{
$this->expectException(Exception\InvalidValueException::class);
$this->expectExceptionMessage('The <TestJsonDto.json> property only accepts a string.');

(new Hydrator)->hydrate(new Fixture\TestJsonDto(), ['json' => []]);
}

/**
* @return void
*/
Expand Down Expand Up @@ -257,6 +292,24 @@ public function testEnumerableValue() : void

$object = (new Hydrator)->hydrate(new Fixture\TestEnumDto(), ['foo' => 'C']);
$this->assertSame('C:value', $object->foo->getValue());

$object = (new Hydrator)->hydrate(new Fixture\TestEnumDto(), ['foo' => '0']);
$this->assertSame('0:value', $object->foo->getValue());

$object = (new Hydrator)->hydrate(new Fixture\TestEnumDto(), ['foo' => '1']);
$this->assertSame('1:value', $object->foo->getValue());

$object = (new Hydrator)->hydrate(new Fixture\TestEnumDto(), ['foo' => '2']);
$this->assertSame('2:value', $object->foo->getValue());

$object = (new Hydrator)->hydrate(new Fixture\TestEnumDto(), ['foo' => 0]);
$this->assertSame('0:value', $object->foo->getValue());

$object = (new Hydrator)->hydrate(new Fixture\TestEnumDto(), ['foo' => 1]);
$this->assertSame('1:value', $object->foo->getValue());

$object = (new Hydrator)->hydrate(new Fixture\TestEnumDto(), ['foo' => 2]);
$this->assertSame('2:value', $object->foo->getValue());
}

public function testUnknownEnumerableValue() : void
Expand All @@ -270,7 +323,7 @@ public function testUnknownEnumerableValue() : void
public function testInvalidEnumerableValue() : void
{
$this->expectException(Exception\InvalidValueException::class);
$this->expectExceptionMessage('The <TestEnumDto.foo> property only accepts a string.');
$this->expectExceptionMessage('The <TestEnumDto.foo> property only accepts an integer or a string.');

(new Hydrator)->hydrate(new Fixture\TestEnumDto(), ['foo' => []]);
}
Expand Down

0 comments on commit b206924

Please sign in to comment.