diff --git a/.travis.yml b/.travis.yml index b0098bb..a67f233 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,9 @@ language: php php: - - 5.4 - - 5.5 - 5.6 - 7.0 + - 7.1 - hhvm cache: diff --git a/composer.json b/composer.json index 8113336..b0212b4 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,9 @@ "require": { "php": ">=5.4", "doctrine/annotations": "~1.2", - "nette/php-generator": "~2.3", - "symfony/finder": "~2.7|~3.0", - "symfony/console": "~2.7|~3.0" + "nette/php-generator": "~2.6", + "symfony/finder": "~2.7|~3.0|~4.0", + "symfony/console": "~2.7|~3.0|~4.0" }, "require-dev": { "phpunit/phpunit": "~4.6" diff --git a/src/Skrz/Meta/BaseModule.php b/src/Skrz/Meta/BaseModule.php index d4d6824..b80a559 100644 --- a/src/Skrz/Meta/BaseModule.php +++ b/src/Skrz/Meta/BaseModule.php @@ -205,17 +205,8 @@ public function onGenerate(AbstractMetaSpec $spec, MetaSpecMatcher $matcher, Typ $hash->addBody("{$indent}hash_update(\$ctx, (string){$objectPath});"); } elseif ($baseType instanceof Type) { - $datetimeType = false; - - for ($t = $baseType; $t; $t = $t->getParentClass()) { - if ($t->getName() === "DateTime") { - $datetimeType = true; - break; - } - } - - if ($datetimeType) { - $hash->addBody("{$indent}hash_update(\$ctx, {$objectPath} instanceof \\DateTime ? {$objectPath}->format(\\DateTime::ISO8601) : '');"); + if ($baseType->isDateTime()) { + $hash->addBody("{$indent}hash_update(\$ctx, {$objectPath} instanceof \\DateTimeInterface ? {$objectPath}->format(\\DateTime::ISO8601) : '');"); } else { $propertyTypeMetaClassName = $spec->createMetaClassName($baseType); $namespace->addUse($propertyTypeMetaClassName, null, $propertyTypeMetaClassNameAlias); diff --git a/src/Skrz/Meta/DateTimeFormattingSerializer.php b/src/Skrz/Meta/DateTimeFormattingSerializer.php index 0c975a8..c05acd1 100644 --- a/src/Skrz/Meta/DateTimeFormattingSerializer.php +++ b/src/Skrz/Meta/DateTimeFormattingSerializer.php @@ -12,12 +12,22 @@ class DateTimeFormattingSerializer implements PropertySerializerInterface /** @var string */ private $format; + /** @var string */ + private $dateTimeClass; + /** @var string[] */ private $groups = null; - public function __construct($format) + private $emptyValue; + + public function __construct($format, $dateTimeClass = \DateTime::class, $emptyValue = '0000-00-00 00:00:00') { $this->format = $format; + $this->dateTimeClass = $dateTimeClass; + if (!substr($dateTimeClass, 0, 1) != "\\") { + $this->dateTimeClass = "\\" . $this->dateTimeClass; + } + $this->emptyValue = $emptyValue; } public function addGroup($group) @@ -36,8 +46,8 @@ public function matches(Property $property, $group) return ($this->groups === null || in_array($group, $this->groups)) && - $baseType instanceof Type && - strtolower($baseType->getName()) === "datetime"; + $baseType instanceof Type && $baseType->isDateTime(); + } public function matchesSerialize(Property $property, $group) @@ -55,14 +65,14 @@ public function serialize(Property $property, $group, $inputExpression) return StatementAndExpressionVO::withStatementAndExpression( "if ({$inputExpression} === null) {\n" . "\t\$datetimeStringReturn = null;\n" . - "} elseif ({$inputExpression} instanceof \\DateTime) {\n" . + "} elseif ({$inputExpression} instanceof \\DateTimeInterface) {\n" . "\t\$datetimeStringReturn = {$inputExpression}->format(" . var_export($this->format, true) . ");\n" . "} elseif (is_numeric({$inputExpression})) {\n" . - "\t\$datetimeStringReturn = (new \\DateTime('@' . intval({$inputExpression})))->format(" . var_export($this->format, true) . ");\n" . + "\t\$datetimeStringReturn = (new " . $this->dateTimeClass . "('@' . intval({$inputExpression})))->format(" . var_export($this->format, true) . ");\n" . "} elseif (is_string({$inputExpression})) {\n" . - "\t\$datetimeStringReturn = (new \\DateTime({$inputExpression}))->format(" . var_export($this->format, true) . ");\n" . + "\t\$datetimeStringReturn = (new " . $this->dateTimeClass . "({$inputExpression}))->format(" . var_export($this->format, true) . ");\n" . "} elseif (is_array({$inputExpression}) && isset({$inputExpression}['date'])) {\n" . - "\t\$datetimeStringReturn = (new \\DateTime({$inputExpression}['date']))->format(" . var_export($this->format, true) . ");\n" . + "\t\$datetimeStringReturn = (new " . $this->dateTimeClass . "({$inputExpression}['date']))->format(" . var_export($this->format, true) . ");\n" . "} else {\n" . "\tthrow new \\InvalidArgumentException('Could not serialize date of format ' . " . var_export($this->format, true) . " . '.');\n" . "}", @@ -73,18 +83,18 @@ public function serialize(Property $property, $group, $inputExpression) public function deserialize(Property $property, $group, $inputExpression) { return StatementAndExpressionVO::withStatementAndExpression( - "if ({$inputExpression} instanceof \\DateTime) {\n" . + "if ({$inputExpression} instanceof \\DateTimeInterface) {\n" . "\t\$datetimeInstanceReturn = {$inputExpression};\n" . "} elseif (is_numeric({$inputExpression})) {\n" . - "\t\$datetimeInstanceReturn = new \\DateTime('@' . intval({$inputExpression}));\n" . + "\t\$datetimeInstanceReturn = new " . $this->dateTimeClass . "('@' . intval({$inputExpression}));\n" . "} elseif (is_string({$inputExpression})) {\n" . - "\tif ({$inputExpression} === '0000-00-00 00:00:00') {\n" . + "\tif ({$inputExpression} === " . var_export($this->emptyValue, true) . ") {\n" . "\t\t\$datetimeInstanceReturn = null;\n" . "\t} else {\n" . - "\t\t\$datetimeInstanceReturn = \\DateTime::createFromFormat(" . var_export($this->format, true) . ", {$inputExpression});\n" . + "\t\t\$datetimeInstanceReturn = " . $this->dateTimeClass . "::createFromFormat(" . var_export($this->format, true) . ", {$inputExpression});\n" . "\t}\n" . "} elseif (is_array({$inputExpression}) && isset({$inputExpression}['date'])) {\n" . - "\t\$datetimeInstanceReturn = new \\DateTime({$inputExpression}['date']);\n" . + "\t\$datetimeInstanceReturn = new " . $this->dateTimeClass . "({$inputExpression}['date']);\n" . "} elseif ({$inputExpression} === null) {\n" . "\t\$datetimeInstanceReturn = null;\n" . "} else {\n" . diff --git a/src/Skrz/Meta/Reflection/Type.php b/src/Skrz/Meta/Reflection/Type.php index 9c107af..67b3cab 100644 --- a/src/Skrz/Meta/Reflection/Type.php +++ b/src/Skrz/Meta/Reflection/Type.php @@ -879,4 +879,20 @@ public function __toString() return $this->getName(); } + public function isDateTime() + { + if ($this->getName() === \DateTime::class + || $this->getName() === \DateTimeImmutable::class + ) { + return true; + } + for ($t = $this; $t; $t = $t->getParentClass()) { + if (in_array(\DateTimeInterface::class, $t->getInterfaces())) { + return true; + break; + } + } + return false; + } + } diff --git a/test/Skrz/Meta/Fixtures/PHP/PhpMetaSpec.php b/test/Skrz/Meta/Fixtures/PHP/PhpMetaSpec.php index 226c2ed..485af70 100644 --- a/test/Skrz/Meta/Fixtures/PHP/PhpMetaSpec.php +++ b/test/Skrz/Meta/Fixtures/PHP/PhpMetaSpec.php @@ -8,7 +8,7 @@ class PhpMetaSpec extends AbstractMetaSpec { - protected function configure() + protected function configure($dateTimeObject = \DateTime::class) { $this->match("Skrz\\Meta\\Fixtures\\PHP\\ClassWith*") ->addModule($phpModule = new PhpModule()); diff --git a/test/Skrz/Meta/PhpModuleDatetimeImmutableTest.php b/test/Skrz/Meta/PhpModuleDatetimeImmutableTest.php new file mode 100644 index 0000000..c0a6a42 --- /dev/null +++ b/test/Skrz/Meta/PhpModuleDatetimeImmutableTest.php @@ -0,0 +1,678 @@ +getPathname(); + }, iterator_to_array( + (new Finder()) + ->in(__DIR__ . "/Fixtures/PHP") + ->name("ClassWith*.php") + ->notName("*Meta*") + ->notName("ClassWithNonTransientPrivateProperty.php") + ->files() + )); + + $spec = new PhpMetaSpec(\DateTimeImmutable::class); + $spec->processFiles($files); + } + + /** + * @expectedException \Skrz\Meta\MetaException + */ + public function testClassWithNonTransientPrivateProperty() + { + $spec = new PhpMetaSpec(); + $spec->processFiles(array( + __DIR__ . "/Fixtures/PHP/ClassWithNonTransientPrivateProperty.php" + )); + } + + public function testClassWithNoPropertyFromArray() + { + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\Meta\\ClassWithNoPropertyMeta", ClassWithNoPropertyMeta::getInstance()); + + $instance = ClassWithNoPropertyMeta::fromArray(array()); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithNoProperty", $instance); + + $this->assertSame($instance, ClassWithNoPropertyMeta::fromArray(array(), null, $instance)); + } + + public function testClassWithNoPropertyToArray() + { + $instance = new ClassWithNoProperty(); + $array = ClassWithNoPropertyMeta::toArray($instance); + $this->assertEmpty($array); + } + + public function testClassWithPublicPropertyFromArray() + { + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\Meta\\ClassWithPublicPropertyMeta", ClassWithPublicPropertyMeta::getInstance()); + + $instance = ClassWithPublicPropertyMeta::fromArray(array("property" => "value")); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithPublicProperty", $instance); + $this->assertEquals("value", $instance->property); + + $this->assertSame($instance, ClassWithPublicPropertyMeta::fromArray(array(), null, $instance)); + } + + public function testClassWithPublicPropertyToArray() + { + $instance = new ClassWithPublicProperty(); + $instance->property = "some value"; + $array = ClassWithPublicPropertyMeta::toArray($instance); + $this->assertNotEmpty($array); + $this->assertArrayHasKey("property", $array); + $this->assertEquals("some value", $array["property"]); + } + + public function testClassWithProtectedPropertyFromArray() + { + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\Meta\\ClassWithProtectedPropertyMeta", ClassWithProtectedPropertyMeta::getInstance()); + + $instance = ClassWithProtectedPropertyMeta::fromArray(array("property" => "value")); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithProtectedProperty", $instance); + $this->assertEquals("value", $instance->getProperty()); + + $this->assertSame($instance, ClassWithProtectedPropertyMeta::fromArray(array(), null, $instance)); + } + + public function testClassWithProtectedPropertyToArray() + { + $instance = new ClassWithProtectedProperty(); + $instance->setProperty("some value"); + $array = ClassWithProtectedPropertyMeta::toArray($instance); + $this->assertNotEmpty($array); + $this->assertArrayHasKey("property", $array); + $this->assertEquals("some value", $array["property"]); + } + + public function testClassWithPrivatePropertyFromArray() + { + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\Meta\\ClassWithPrivatePropertyMeta", ClassWithPrivatePropertyMeta::getInstance()); + + $instance = ClassWithPrivatePropertyMeta::fromArray(array("property" => "value")); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithPrivateProperty", $instance); + $this->assertNull($instance->getProperty()); + + $this->assertSame($instance, ClassWithPrivatePropertyMeta::fromArray(array(), null, $instance)); + } + + public function testClassWithPrivatePropertyToArray() + { + $instance = new ClassWithPrivateProperty(); + $instance->setProperty("some value"); + $array = ClassWithPrivatePropertyMeta::toArray($instance); + $this->assertEmpty($array); + $this->assertArrayNotHasKey("property", $array); + } + + public function testClassWithCustomOffsetPropertyFromArray() + { + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\Meta\\ClassWithCustomOffsetPropertyMeta", ClassWithCustomOffsetPropertyMeta::getInstance()); + + $instance = ClassWithCustomOffsetPropertyMeta::fromArray(array("some-offset" => "value")); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithCustomOffsetProperty", $instance); + $this->assertEquals("value", $instance->property); + + $this->assertSame($instance, ClassWithCustomOffsetPropertyMeta::fromArray(array(), null, $instance)); + } + + public function testClassWithCustomOffsetPropertyToArray() + { + $instance = new ClassWithCustomOffsetProperty(); + $instance->property = "some value"; + $array = ClassWithCustomOffsetPropertyMeta::toArray($instance); + $this->assertNotEmpty($array); + $this->assertArrayHasKey("some-offset", $array); + $this->assertEquals("some value", $array["some-offset"]); + } + + public function testClassWithManyOffsetsPerPropertyFromArray() + { + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\Meta\\ClassWithManyArrayOffsetsPerPropertyMeta", ClassWithManyArrayOffsetsPerPropertyMeta::getInstance()); + + $instance = ClassWithManyArrayOffsetsPerPropertyMeta::fromArray(array("property" => "value")); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithManyArrayOffsetsPerProperty", $instance); + $this->assertEquals("value", $instance->property); + $this->assertSame($instance, ClassWithManyArrayOffsetsPerPropertyMeta::fromArray(array(), null, $instance)); + + $instance = ClassWithManyArrayOffsetsPerPropertyMeta::fromArray(array("foo" => "value"), "foo"); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithManyArrayOffsetsPerProperty", $instance); + $this->assertEquals("value", $instance->property); + $this->assertSame($instance, ClassWithManyArrayOffsetsPerPropertyMeta::fromArray(array(), "foo", $instance)); + + $instance = ClassWithManyArrayOffsetsPerPropertyMeta::fromArray(array("bar" => "value"), "bar"); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithManyArrayOffsetsPerProperty", $instance); + $this->assertEquals("value", $instance->property); + $this->assertSame($instance, ClassWithManyArrayOffsetsPerPropertyMeta::fromArray(array(), "bar", $instance)); + } + + public function testClassWithManyOffsetsPerPropertyToArray() + { + $instance = new ClassWithManyArrayOffsetsPerProperty(); + $instance->property = "some value"; + + $array = ClassWithManyArrayOffsetsPerPropertyMeta::toArray($instance); + $this->assertNotEmpty($array); + $this->assertArrayHasKey("property", $array); + $this->assertEquals("some value", $array["property"]); + + $array = ClassWithManyArrayOffsetsPerPropertyMeta::toArray($instance, "foo"); + $this->assertNotEmpty($array); + $this->assertArrayHasKey("foo", $array); + $this->assertEquals("some value", $array["foo"]); + + $array = ClassWithManyArrayOffsetsPerPropertyMeta::toArray($instance, "bar"); + $this->assertNotEmpty($array); + $this->assertArrayHasKey("bar", $array); + $this->assertEquals("some value", $array["bar"]); + } + + public function testClassWithPropertyReferencingClassFromArray() + { + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\Meta\\ClassWithPropertyReferencingClassMeta", ClassWithPropertyReferencingClassMeta::getInstance()); + + $instance = ClassWithPropertyReferencingClassMeta::fromArray(array( + "classWithPublicProperty" => array( + "property" => "value" + ) + )); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithPropertyReferencingClass", $instance); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithPublicProperty", $instance->classWithPublicProperty); + $this->assertEquals("value", $instance->classWithPublicProperty->property); + + $instanceAgain = ClassWithPropertyReferencingClassMeta::fromArray(array( + "classWithPublicProperty" => array( + "property" => "other value" + ) + ), null, $instance); + $this->assertSame($instance, $instanceAgain); + $this->assertSame($instance->classWithPublicProperty, $instanceAgain->classWithPublicProperty); + } + + public function testClassWithPropertyReferencingClassToArray() + { + $instance = new ClassWithPropertyReferencingClass(); + $instance->classWithPublicProperty = new ClassWithPublicProperty(); + $instance->classWithPublicProperty->property = "some value"; + + $array = ClassWithPropertyReferencingClassMeta::toArray($instance); + $this->assertNotEmpty($array); + $this->assertArrayHasKey("classWithPublicProperty", $array); + $this->assertNotEmpty($array["classWithPublicProperty"]); + $this->assertArrayHasKey("property", $array["classWithPublicProperty"]); + $this->assertEquals("some value", $array["classWithPublicProperty"]["property"]); + } + + public function testClassWithArrayPropertyFromArray() + { + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\Meta\\ClassWithArrayPropertyMeta", ClassWithArrayPropertyMeta::getInstance()); + + $instance = ClassWithArrayPropertyMeta::fromArray(array("array" => array("foo" => array("bar" => "baz")))); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithArrayProperty", $instance); + $this->assertNotEmpty($instance->array); + $this->assertArrayHasKey("foo", $instance->array); + $this->assertArrayHasKey("bar", $instance->array["foo"]); + $this->assertEquals("baz", $instance->array["foo"]["bar"]); + + $this->assertSame($instance, ClassWithArrayPropertyMeta::fromArray(array(), null, $instance)); + } + + public function testClassWithArrayPropertyToArray() + { + $instance = new ClassWithArrayProperty(); + $instance->array = array("foo" => array("bar" => "baz")); + $array = ClassWithArrayPropertyMeta::toArray($instance); + $this->assertNotEmpty($array); + $this->assertArrayHasKey("array", $array); + $this->assertArrayHasKey("foo", $array["array"]); + $this->assertArrayHasKey("bar", $array["array"]["foo"]); + $this->assertEquals("baz", $array["array"]["foo"]["bar"]); + } + + public function testClassWithArrayPropertyFromArrayWithArrayCollection() + { + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\Meta\\ClassWithArrayPropertyMeta", ClassWithArrayPropertyMeta::getInstance()); + + $instance = ClassWithArrayPropertyMeta::fromArray(array("array" => new ArrayCollection(array("foo" => array("bar" => "baz"))))); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithArrayProperty", $instance); + $this->assertNotEmpty($instance->array); + $this->assertArrayHasKey("foo", $instance->array); + $this->assertArrayHasKey("bar", $instance->array["foo"]); + $this->assertEquals("baz", $instance->array["foo"]["bar"]); + + $this->assertSame($instance, ClassWithArrayPropertyMeta::fromArray(array(), null, $instance)); + } + + public function testClassWithArrayPropertyToArrayWithArrayCollection() + { + $instance = new ClassWithArrayProperty(); + $instance->array = new ArrayCollection(array("foo" => array("bar" => "baz"))); + $array = ClassWithArrayPropertyMeta::toArray($instance); + $this->assertNotEmpty($array); + $this->assertArrayHasKey("array", $array); + $this->assertArrayHasKey("foo", $array["array"]); + $this->assertArrayHasKey("bar", $array["array"]["foo"]); + $this->assertEquals("baz", $array["array"]["foo"]["bar"]); + } + + public function testClassWithDatetimePropertyFromArray() + { + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\Meta\\ClassWithDatetimePropertyMeta", ClassWithDatetimePropertyMeta::getInstance()); + + $d = new \DateTime(); + $instance = ClassWithDatetimePropertyMeta::fromArray(array("datetime" => $d->format("Y-m-d H:i:s"))); + + $this->assertNotNull($instance->datetime); + $this->assertEquals($d->format(\DateTime::ATOM), $instance->datetime->format(\DateTime::ATOM)); + } + + public function testClassWithDatetimePropertyToArray() + { + $d = new \DateTime(); + $instance = new ClassWithDatetimeProperty(); + $instance->datetime = $d; + + $array = ClassWithDatetimePropertyMeta::toArray($instance); + + $this->assertArrayHasKey("datetime", $array); + $this->assertEquals($d->format("Y-m-d H:i:s"), $array["datetime"]); + } + + public function testClassWithDiscriminatorMapFromArray() + { + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\Meta\\ClassWithDiscriminatorMapMeta", ClassWithDiscriminatorMapMeta::getInstance()); + + /** @var ClassWithDiscriminatorValueA $aInstance */ + $aInstance = ClassWithDiscriminatorMapMeta::fromArray(array("value" => "a", "a" => 21, "b" => 42)); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithDiscriminatorValueA", $aInstance); + $this->assertEquals("a", $aInstance->value); + $this->assertEquals(21, $aInstance->a); + + /** @var ClassWithDiscriminatorValueB $bInstance */ + $bInstance = ClassWithDiscriminatorMapMeta::fromArray(array("value" => "b", "a" => 21, "b" => 42)); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithDiscriminatorValueB", $bInstance); + $this->assertEquals("b", $bInstance->value); + $this->assertEquals(42, $bInstance->b); + + /** @var ClassWithDiscriminatorValueA $aTopInstance */ + $aTopInstance = ClassWithDiscriminatorMapMeta::fromArray(array("a" => array("a" => 63)), "top"); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithDiscriminatorValueA", $aTopInstance); + $this->assertNull($aTopInstance->value); + $this->assertEquals(63, $aTopInstance->a); + + /** @var ClassWithDiscriminatorValueB $bTopInstance */ + $bTopInstance = ClassWithDiscriminatorMapMeta::fromArray(array("b" => array("b" => 84)), "top"); + $this->assertInstanceOf("Skrz\\Meta\\Fixtures\\PHP\\ClassWithDiscriminatorValueB", $bTopInstance); + $this->assertNull($bTopInstance->value); + $this->assertEquals(84, $bTopInstance->b); + } + + public function testClassWithDiscriminatorMapToArray() + { + $aInstance = new ClassWithDiscriminatorValueA(); + $aInstance->a = 21; + $aArray = ClassWithDiscriminatorMapMeta::toArray($aInstance); + + $this->assertArrayHasKey("value", $aArray); + $this->assertEquals("a", $aArray["value"]); + $this->assertArrayHasKey("a", $aArray); + $this->assertEquals(21, $aArray["a"]); + $this->assertArrayNotHasKey("b", $aArray); + + $bInstance = new ClassWithDiscriminatorValueB(); + $bInstance->b = 42; + $bArray = ClassWithDiscriminatorMapMeta::toArray($bInstance); + $this->assertArrayHasKey("value", $bArray); + $this->assertEquals("b", $bArray["value"]); + $this->assertArrayHasKey("b", $bArray); + $this->assertEquals(42, $bArray["b"]); + $this->assertArrayNotHasKey("a", $bArray); + } + + public function testOverwriteClassWithPublicPropertyFromArray() + { + $instance = new ClassWithPublicProperty(); + $instance->property = "foobar"; + $this->assertEquals("foobar", $instance->property); + + ClassWithPublicPropertyMeta::fromArray(["property" => null], null, $instance); + $this->assertNull($instance->property); + } + + public function testOverwriteClassWithDatetimePropertyFromArray() + { + $now = new \DateTime(); + + $instance = new ClassWithDatetimeProperty(); + $instance->datetime = $now; + $this->assertEquals($now, $instance->datetime); + + ClassWithDatetimePropertyMeta::fromArray(["datetime" => null], null, $instance); + $this->assertNull($instance->datetime); + } + + public function testOverwriteClassWithArrayPropertyFromArray() + { + $instance = new ClassWithArrayProperty(); + $instance->array = ["foo" => ["bar" => "baz"]]; + $this->assertEquals(["foo" => ["bar" => "baz"]], $instance->array); + + ClassWithArrayPropertyMeta::fromArray(["array" => null], null, $instance); + $this->assertNull($instance->array); + } + + public function testClassWithRecursiveProperty() + { + $a = new ClassWithRecursiveProperty(); + $a->property = $a; + + $this->assertEquals(0, count(Stack::$objects)); + $this->assertEquals(["property" => null], ClassWithRecursivePropertyMeta::toArray($a)); + $this->assertEquals(0, count(Stack::$objects)); + + $b = new ClassWithRecursiveProperty(); + $a->property = $b; + $b->property = $a; + + $this->assertEquals(0, count(Stack::$objects)); + $this->assertEquals(["property" => ["property" => null]], ClassWithRecursivePropertyMeta::toArray($a)); + $this->assertEquals(0, count(Stack::$objects)); + + try { + $b->property = "wtf"; + + $this->assertEquals(0, count(Stack::$objects)); + ClassWithRecursivePropertyMeta::toArray($a); + $this->fail("An exception should be thrown."); + + } catch (\Exception $e) { + $this->assertEquals(0, count(Stack::$objects)); + $this->assertEquals('You have to pass object of class Skrz\Meta\Fixtures\PHP\ClassWithRecursiveProperty.', $e->getMessage()); + } + } + + public function testClassWithMorePropertiesFiltered() + { + $instance = new ClassWithMoreProperties(); + $instance->a = "foo1"; + $instance->b = "foo2"; + $instance->c = "foo3"; + $instance->d = "foo4"; + $instance->e = "foo5"; + + $this->assertEquals( + [ + "a" => "foo1", + "b" => "foo2", + "c" => "foo3", + "d" => "foo4", + "e" => "foo5", + "f" => null, + "g" => [], + ], + ClassWithMorePropertiesMeta::toArray($instance) + ); + + $this->assertEquals( + [ + "a" => "foo1", + ], + ClassWithMorePropertiesMeta::toArray($instance, null, [ + "a" => true, + ]) + ); + + $this->assertEquals( + [ + "b" => "foo2", + "c" => "foo3", + "d" => "foo4", + ], + ClassWithMorePropertiesMeta::toArray($instance, null, [ + "b" => true, + "c" => true, + "d" => true, + ]) + ); + + $instance2 = new ClassWithMoreProperties(); + $instance2->a = "foo6"; + $instance->f = $instance2; + + $this->assertEquals( + [ + "e" => "foo5", + "f" => [ + "a" => "foo6", + "b" => null, + ], + ], + ClassWithMorePropertiesMeta::toArray($instance, null, [ + "e" => true, + "f" => [ + "a" => true, + "b" => true, + ], + ]) + ); + + $instance3 = new ClassWithMoreProperties(); + $instance3->a = "foo7"; + $instance->g = [$instance2, $instance3]; + + $this->assertEquals( + [ + "g" => [ + [ + "a" => "foo6", + ], + [ + "a" => "foo7", + ] + ], + ], + ClassWithMorePropertiesMeta::toArray($instance, null, [ + "g" => [ + "a" => true, + ], + ]) + ); + } + + public function testClassWithMorePropertiesFilteredByFieldsFromArray() + { + $instance = new ClassWithMoreProperties(); + $instance->a = "foo1"; + $instance->b = "foo2"; + $instance->c = "foo3"; + $instance->d = "foo4"; + $instance->e = "foo5"; + + $this->assertEquals( + [ + "a" => "foo1", + "b" => "foo2", + "c" => "foo3", + "d" => "foo4", + "e" => "foo5", + "f" => null, + "g" => [], + ], + ClassWithMorePropertiesMeta::toArray($instance) + ); + + $this->assertEquals( + [ + "a" => "foo1", + ], + ClassWithMorePropertiesMeta::toArray($instance, null, Fields::fromArray([ + "a" => true, + ])) + ); + + $this->assertEquals( + [ + "b" => "foo2", + "c" => "foo3", + "d" => "foo4", + ], + ClassWithMorePropertiesMeta::toArray($instance, null, Fields::fromArray([ + "b" => true, + "c" => true, + "d" => true, + ])) + ); + + $instance2 = new ClassWithMoreProperties(); + $instance2->a = "foo6"; + $instance->f = $instance2; + + $this->assertEquals( + [ + "e" => "foo5", + "f" => [ + "a" => "foo6", + "b" => null, + ], + ], + ClassWithMorePropertiesMeta::toArray($instance, null, Fields::fromArray([ + "e" => true, + "f" => [ + "a" => true, + "b" => true, + ], + ])) + ); + + $instance3 = new ClassWithMoreProperties(); + $instance3->a = "foo7"; + $instance->g = [$instance2, $instance3]; + + $this->assertEquals( + [ + "g" => [ + [ + "a" => "foo6", + ], + [ + "a" => "foo7", + ] + ], + ], + ClassWithMorePropertiesMeta::toArray($instance, null, Fields::fromArray([ + "g" => [ + "a" => true, + ], + ])) + ); + } + + public function testClassWithMorePropertiesFilteredByFieldsFromString() + { + $instance = new ClassWithMoreProperties(); + $instance->a = "foo1"; + $instance->b = "foo2"; + $instance->c = "foo3"; + $instance->d = "foo4"; + $instance->e = "foo5"; + + $this->assertEquals( + [ + "a" => "foo1", + "b" => "foo2", + "c" => "foo3", + "d" => "foo4", + "e" => "foo5", + "f" => null, + "g" => [], + ], + ClassWithMorePropertiesMeta::toArray($instance) + ); + + $this->assertEquals( + [ + "a" => "foo1", + ], + ClassWithMorePropertiesMeta::toArray($instance, null, Fields::fromString("a")) + ); + + $this->assertEquals( + [ + "b" => "foo2", + "c" => "foo3", + "d" => "foo4", + ], + ClassWithMorePropertiesMeta::toArray($instance, null, Fields::fromString("b,c,d")) + ); + + $instance2 = new ClassWithMoreProperties(); + $instance2->a = "foo6"; + $instance->f = $instance2; + + $this->assertEquals( + [ + "e" => "foo5", + "f" => [ + "a" => "foo6", + "b" => null, + ], + ], + ClassWithMorePropertiesMeta::toArray($instance, null, Fields::fromString("e,f{a,b}")) + ); + + $instance3 = new ClassWithMoreProperties(); + $instance3->a = "foo7"; + $instance->g = [$instance2, $instance3]; + + $this->assertEquals( + [ + "g" => [ + [ + "a" => "foo6", + ], + [ + "a" => "foo7", + ] + ], + ], + ClassWithMorePropertiesMeta::toArray($instance, null, Fields::fromString("g{a}")) + ); + } + +}