From eec076d15760f957c5e62d17ccc1e85afc7ae71d Mon Sep 17 00:00:00 2001 From: Sylvain Fabre Date: Fri, 20 Mar 2026 13:09:54 +0100 Subject: [PATCH 1/3] Upgrade PHPUnit configuration to version 10 and fix code quality issues - Fix array_map usage with first-class callable syntax - Add setter to Address test entity to fix phpstan unused type warning - Make data provider method static (Rector fix) Co-Authored-By: Claude Sonnet 4.6 --- phpunit.xml.dist | 12 +- src/Serializer/LogSerializer.php | 254 +++++++++++------------ tests/Functional/Entity/Address.php | 53 ++--- tests/Serializer/LogSerializerTest.php | 272 ++++++++++++------------- 4 files changed, 299 insertions(+), 292 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 698933b..2b6dafb 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,19 +1,19 @@ - + - - - ./src - - ./tests/ + + + ./src + + diff --git a/src/Serializer/LogSerializer.php b/src/Serializer/LogSerializer.php index 7071b82..4b20f8b 100755 --- a/src/Serializer/LogSerializer.php +++ b/src/Serializer/LogSerializer.php @@ -1,127 +1,127 @@ -propertyAccessor = PropertyAccess::createPropertyAccessor(); - } - - public function formatEntity(EntityManagerInterface $entityManager, object $entity): string - { - $metadata = $entityManager->getMetadataFactory()->getMetadataFor($entity::class); - $data = array(); - - // Regular fields - $fields = $metadata->getFieldNames(); - foreach ($fields as $field) { - $data[$field] = $this->formatField($entity, $field); - } - - // Associations - $associations = $metadata->getAssociationNames(); - foreach ($associations as $association) { - if ($metadata->isAssociationInverseSide($association) === false) { - $data[$association] = $this->formatField($entity, $association); - // If the number of associations is greater than the limit then we slice the associations array - if (is_array($data[$association]) && count($data[$association]) > self::ASSOCIATION_MAX_TO_LOG) { - $data[$association] = array_slice($data[$association], 0, self::ASSOCIATION_MAX_TO_LOG); - } - } - } - - return json_encode($data, JSON_PRETTY_PRINT); - } - - public function formatValueAsString($value): string - { - return json_encode($this->formatValue($value)); - } - - /** - * Returns a formatted value of a given entities' field - */ - private function formatField(object $entity, string $field): mixed - { - if ($this->propertyAccessor->isReadable($entity, $field)) { - $value = $this->propertyAccessor->getValue($entity, $field); - return $this->formatValue($value); - } - - return null; - } - - /** - * Returns a formatted value depending on the given value's type. - */ - private function formatValue($value): mixed - { - return match (gettype($value)) { - 'string' => mb_substr($value, 0, Log::MAX_STRING_LENGTH), - 'NULL', 'boolean', 'double', 'integer' => $value, - 'object' => $this->formatObject($value), - 'array' => array_map(__METHOD__, $value), - 'resource', 'resource (closed)', 'unknown type' - => throw new \InvalidArgumentException('Unhandled type'), - }; - } - - /** - * Returns a scalar representation of the object - */ - private function formatObject(object $value): mixed - { - if ($value instanceof Money) { - // Required because Money does not provide a __toString() method - // @link https://github.com/moneyphp/money/issues/184 - // Case when we change the amount of a Money in one of our entities. - // We log amount & currency code for log readability. - return $value->getAmount() . ' ' . $value->getCurrency()->getCode(); - } - - if (method_exists($value, 'getId')) { - return $value->getId(); - } - - if ($value instanceof \DateTimeInterface) { - return $value->format(\DateTimeInterface::ISO8601); - } - - if ($value instanceof \DateTimeZone) { - return $value->getName(); - } - - if ($value instanceof Collection) { - return array_map(static function (object $item): mixed { - return $item->getId(); - }, $value->toArray()); - } - - if ($value instanceof \BackedEnum) { - return $value->value; - } - - if (method_exists($value, '__toString')) { - return $value->__toString(); - } - - throw new UnsupportObjectException($value); - } -} +propertyAccessor = PropertyAccess::createPropertyAccessor(); + } + + public function formatEntity(EntityManagerInterface $entityManager, object $entity): string + { + $metadata = $entityManager->getMetadataFactory()->getMetadataFor($entity::class); + $data = array(); + + // Regular fields + $fields = $metadata->getFieldNames(); + foreach ($fields as $field) { + $data[$field] = $this->formatField($entity, $field); + } + + // Associations + $associations = $metadata->getAssociationNames(); + foreach ($associations as $association) { + if ($metadata->isAssociationInverseSide($association) === false) { + $data[$association] = $this->formatField($entity, $association); + // If the number of associations is greater than the limit then we slice the associations array + if (is_array($data[$association]) && count($data[$association]) > self::ASSOCIATION_MAX_TO_LOG) { + $data[$association] = array_slice($data[$association], 0, self::ASSOCIATION_MAX_TO_LOG); + } + } + } + + return json_encode($data, JSON_PRETTY_PRINT); + } + + public function formatValueAsString($value): string + { + return json_encode($this->formatValue($value)); + } + + /** + * Returns a formatted value of a given entities' field + */ + private function formatField(object $entity, string $field): mixed + { + if ($this->propertyAccessor->isReadable($entity, $field)) { + $value = $this->propertyAccessor->getValue($entity, $field); + return $this->formatValue($value); + } + + return null; + } + + /** + * Returns a formatted value depending on the given value's type. + */ + private function formatValue($value): mixed + { + return match (gettype($value)) { + 'string' => mb_substr($value, 0, Log::MAX_STRING_LENGTH), + 'NULL', 'boolean', 'double', 'integer' => $value, + 'object' => $this->formatObject($value), + 'array' => array_map($this->formatValue(...), $value), + 'resource', 'resource (closed)', 'unknown type' + => throw new \InvalidArgumentException('Unhandled type'), + }; + } + + /** + * Returns a scalar representation of the object + */ + private function formatObject(object $value): mixed + { + if ($value instanceof Money) { + // Required because Money does not provide a __toString() method + // @link https://github.com/moneyphp/money/issues/184 + // Case when we change the amount of a Money in one of our entities. + // We log amount & currency code for log readability. + return $value->getAmount() . ' ' . $value->getCurrency()->getCode(); + } + + if (method_exists($value, 'getId')) { + return $value->getId(); + } + + if ($value instanceof \DateTimeInterface) { + return $value->format(\DateTimeInterface::ISO8601); + } + + if ($value instanceof \DateTimeZone) { + return $value->getName(); + } + + if ($value instanceof Collection) { + return array_map(static function (object $item): mixed { + return $item->getId(); + }, $value->toArray()); + } + + if ($value instanceof \BackedEnum) { + return $value->value; + } + + if (method_exists($value, '__toString')) { + return $value->__toString(); + } + + throw new UnsupportObjectException($value); + } +} diff --git a/tests/Functional/Entity/Address.php b/tests/Functional/Entity/Address.php index e5ea238..dae2e45 100755 --- a/tests/Functional/Entity/Address.php +++ b/tests/Functional/Entity/Address.php @@ -1,23 +1,30 @@ -streetName; - } -} +streetName; + } + + public function setStreetName(?string $streetName): self + { + $this->streetName = $streetName; + + return $this; + } +} diff --git a/tests/Serializer/LogSerializerTest.php b/tests/Serializer/LogSerializerTest.php index 45eff35..3326f43 100755 --- a/tests/Serializer/LogSerializerTest.php +++ b/tests/Serializer/LogSerializerTest.php @@ -1,136 +1,136 @@ -setEmail('test@gmail.com'); - - $tag = new Tag(); - - $post = new Post($author); - $post->addTag($tag); - - $entityManager = self::getContainer()->get(EntityManagerInterface::class); - - $formatter = self::getContainer()->get(LogSerializer::class); - self::assertSame( - json_encode(array_merge( - $this->helperFormatEntity($author), - [ - 'email' => $author->getEmail(), - 'registeredAt' => $author->getRegisteredAt()->format(\DateTimeInterface::ISO8601), - 'address' => $author->getAddress(), - ] - ), JSON_PRETTY_PRINT), - $formatter->formatEntity($entityManager, $author) - ); - - self::assertSame( - json_encode($this->helperFormatEntity($tag), JSON_PRETTY_PRINT), - $formatter->formatEntity($entityManager, $tag) - ); - self::assertSame( - json_encode(array_merge( - $this->helperFormatEntity($post), - [ - 'author' => $author->getId(), - 'tags' => [$tag->getId()], - ] - ), JSON_PRETTY_PRINT), - $formatter->formatEntity($entityManager, $post) - ); - } - - public function helperFormatEntity(AbstractEntity $entity): array - { - return [ - 'id' => $entity->getId(), - 'createdAt' => $entity->getCreatedAt()->format(\DateTimeInterface::ISO8601), - 'updatedAt' => $entity->getUpdatedAt()->format(\DateTimeInterface::ISO8601), - ]; - } - - /** - * @dataProvider providerFormatValueAsString - */ - public function testFormatValueAsStringWorks($value, $formatted): void - { - $formatter = new LogSerializer(); - self::assertSame($formatted, $formatter->formatValueAsString($value)); - } - - public function providerFormatValueAsString(): iterable - { - yield [null, 'null']; - yield [[null], '[null]']; - - yield [true, 'true']; - yield [[true], '[true]']; - - yield ['foo', '"foo"']; - yield [['foo'], '["foo"]']; - yield [ - str_repeat('a', 70000), - '"' . str_repeat('a', Log::MAX_STRING_LENGTH) . '"', - ]; - - yield [1, '1']; - yield [[1], '[1]']; - - yield [1.5, '1.5']; - yield [[1.5], '[1.5]']; - - yield [new \DateTimeImmutable('@1529500134'), '"2018-06-20T13:08:54+0000"']; - yield [[new \DateTimeImmutable('@1529500134')], '["2018-06-20T13:08:54+0000"]']; - - yield [new DateTimeZone('Europe/Paris'), '"Europe\/Paris"']; - yield [[new DateTimeZone('Europe/Paris')], '["Europe\/Paris"]']; - - yield [Money::EUR('100'), '"100 EUR"']; - yield [[Money::EUR('100')], '["100 EUR"]']; - - yield [new Currency('EUR'), '"EUR"']; - yield [[new Currency('EUR')], '["EUR"]']; - - $entity = new Author(); - yield [$entity, (string) $entity->getId()]; - yield [[$entity], '[' . $entity->getId() . ']']; - - // Doctrine collection - $collection = new ArrayCollection(); - $collection->add($entity); - yield [$collection, '[' . $entity->getId() . ']']; - } - - public function testUnsupportedObjectThrowsAnException(): void - { - $formatter = new LogSerializer(); - - $this->expectException(UnsupportObjectException::class); - - $formatter->formatValueAsString(new ObjectWithoutId()); - } -} +setEmail('test@gmail.com'); + + $tag = new Tag(); + + $post = new Post($author); + $post->addTag($tag); + + $entityManager = self::getContainer()->get(EntityManagerInterface::class); + + $formatter = self::getContainer()->get(LogSerializer::class); + self::assertSame( + json_encode(array_merge( + $this->helperFormatEntity($author), + [ + 'email' => $author->getEmail(), + 'registeredAt' => $author->getRegisteredAt()->format(\DateTimeInterface::ISO8601), + 'address' => $author->getAddress(), + ] + ), JSON_PRETTY_PRINT), + $formatter->formatEntity($entityManager, $author) + ); + + self::assertSame( + json_encode($this->helperFormatEntity($tag), JSON_PRETTY_PRINT), + $formatter->formatEntity($entityManager, $tag) + ); + self::assertSame( + json_encode(array_merge( + $this->helperFormatEntity($post), + [ + 'author' => $author->getId(), + 'tags' => [$tag->getId()], + ] + ), JSON_PRETTY_PRINT), + $formatter->formatEntity($entityManager, $post) + ); + } + + public function helperFormatEntity(AbstractEntity $entity): array + { + return [ + 'id' => $entity->getId(), + 'createdAt' => $entity->getCreatedAt()->format(\DateTimeInterface::ISO8601), + 'updatedAt' => $entity->getUpdatedAt()->format(\DateTimeInterface::ISO8601), + ]; + } + + /** + * @dataProvider providerFormatValueAsString + */ + public function testFormatValueAsStringWorks($value, $formatted): void + { + $formatter = new LogSerializer(); + self::assertSame($formatted, $formatter->formatValueAsString($value)); + } + + public static function providerFormatValueAsString(): iterable + { + yield [null, 'null']; + yield [[null], '[null]']; + + yield [true, 'true']; + yield [[true], '[true]']; + + yield ['foo', '"foo"']; + yield [['foo'], '["foo"]']; + yield [ + str_repeat('a', 70000), + '"' . str_repeat('a', Log::MAX_STRING_LENGTH) . '"', + ]; + + yield [1, '1']; + yield [[1], '[1]']; + + yield [1.5, '1.5']; + yield [[1.5], '[1.5]']; + + yield [new \DateTimeImmutable('@1529500134'), '"2018-06-20T13:08:54+0000"']; + yield [[new \DateTimeImmutable('@1529500134')], '["2018-06-20T13:08:54+0000"]']; + + yield [new DateTimeZone('Europe/Paris'), '"Europe\/Paris"']; + yield [[new DateTimeZone('Europe/Paris')], '["Europe\/Paris"]']; + + yield [Money::EUR('100'), '"100 EUR"']; + yield [[Money::EUR('100')], '["100 EUR"]']; + + yield [new Currency('EUR'), '"EUR"']; + yield [[new Currency('EUR')], '["EUR"]']; + + $entity = new Author(); + yield [$entity, (string) $entity->getId()]; + yield [[$entity], '[' . $entity->getId() . ']']; + + // Doctrine collection + $collection = new ArrayCollection(); + $collection->add($entity); + yield [$collection, '[' . $entity->getId() . ']']; + } + + public function testUnsupportedObjectThrowsAnException(): void + { + $formatter = new LogSerializer(); + + $this->expectException(UnsupportObjectException::class); + + $formatter->formatValueAsString(new ObjectWithoutId()); + } +} From 4a7a240c0e22eeccea63de2d7dbdba9a8f9ccb4b Mon Sep 17 00:00:00 2001 From: Sylvain Fabre Date: Fri, 20 Mar 2026 14:09:59 +0100 Subject: [PATCH 2/3] Fix CRLF line endings, bump php-quality-config to ^2.2, remove cacheDirectory Co-Authored-By: Claude Sonnet 4.6 --- composer.json | 2 +- phpunit.xml.dist | 2 +- src/Serializer/LogSerializer.php | 254 ++++++++++++++-------------- tests/Functional/Entity/Address.php | 60 +++---- 4 files changed, 159 insertions(+), 159 deletions(-) diff --git a/composer.json b/composer.json index a164ad7..f00efdc 100755 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "symfony/yaml": "^7.0", "symfony/validator": "^7.0", "phpstan/phpstan-symfony": "^2", - "assoconnect/php-quality-config": "^2", + "assoconnect/php-quality-config": "^2.2", "symfony/security-core": "^7.0" }, "require": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 2b6dafb..53c4ef5 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,5 +1,5 @@ - + diff --git a/src/Serializer/LogSerializer.php b/src/Serializer/LogSerializer.php index 4b20f8b..0f5db0d 100755 --- a/src/Serializer/LogSerializer.php +++ b/src/Serializer/LogSerializer.php @@ -1,127 +1,127 @@ -propertyAccessor = PropertyAccess::createPropertyAccessor(); - } - - public function formatEntity(EntityManagerInterface $entityManager, object $entity): string - { - $metadata = $entityManager->getMetadataFactory()->getMetadataFor($entity::class); - $data = array(); - - // Regular fields - $fields = $metadata->getFieldNames(); - foreach ($fields as $field) { - $data[$field] = $this->formatField($entity, $field); - } - - // Associations - $associations = $metadata->getAssociationNames(); - foreach ($associations as $association) { - if ($metadata->isAssociationInverseSide($association) === false) { - $data[$association] = $this->formatField($entity, $association); - // If the number of associations is greater than the limit then we slice the associations array - if (is_array($data[$association]) && count($data[$association]) > self::ASSOCIATION_MAX_TO_LOG) { - $data[$association] = array_slice($data[$association], 0, self::ASSOCIATION_MAX_TO_LOG); - } - } - } - - return json_encode($data, JSON_PRETTY_PRINT); - } - - public function formatValueAsString($value): string - { - return json_encode($this->formatValue($value)); - } - - /** - * Returns a formatted value of a given entities' field - */ - private function formatField(object $entity, string $field): mixed - { - if ($this->propertyAccessor->isReadable($entity, $field)) { - $value = $this->propertyAccessor->getValue($entity, $field); - return $this->formatValue($value); - } - - return null; - } - - /** - * Returns a formatted value depending on the given value's type. - */ - private function formatValue($value): mixed - { - return match (gettype($value)) { - 'string' => mb_substr($value, 0, Log::MAX_STRING_LENGTH), - 'NULL', 'boolean', 'double', 'integer' => $value, - 'object' => $this->formatObject($value), - 'array' => array_map($this->formatValue(...), $value), - 'resource', 'resource (closed)', 'unknown type' - => throw new \InvalidArgumentException('Unhandled type'), - }; - } - - /** - * Returns a scalar representation of the object - */ - private function formatObject(object $value): mixed - { - if ($value instanceof Money) { - // Required because Money does not provide a __toString() method - // @link https://github.com/moneyphp/money/issues/184 - // Case when we change the amount of a Money in one of our entities. - // We log amount & currency code for log readability. - return $value->getAmount() . ' ' . $value->getCurrency()->getCode(); - } - - if (method_exists($value, 'getId')) { - return $value->getId(); - } - - if ($value instanceof \DateTimeInterface) { - return $value->format(\DateTimeInterface::ISO8601); - } - - if ($value instanceof \DateTimeZone) { - return $value->getName(); - } - - if ($value instanceof Collection) { - return array_map(static function (object $item): mixed { - return $item->getId(); - }, $value->toArray()); - } - - if ($value instanceof \BackedEnum) { - return $value->value; - } - - if (method_exists($value, '__toString')) { - return $value->__toString(); - } - - throw new UnsupportObjectException($value); - } -} +propertyAccessor = PropertyAccess::createPropertyAccessor(); + } + + public function formatEntity(EntityManagerInterface $entityManager, object $entity): string + { + $metadata = $entityManager->getMetadataFactory()->getMetadataFor($entity::class); + $data = array(); + + // Regular fields + $fields = $metadata->getFieldNames(); + foreach ($fields as $field) { + $data[$field] = $this->formatField($entity, $field); + } + + // Associations + $associations = $metadata->getAssociationNames(); + foreach ($associations as $association) { + if ($metadata->isAssociationInverseSide($association) === false) { + $data[$association] = $this->formatField($entity, $association); + // If the number of associations is greater than the limit then we slice the associations array + if (is_array($data[$association]) && count($data[$association]) > self::ASSOCIATION_MAX_TO_LOG) { + $data[$association] = array_slice($data[$association], 0, self::ASSOCIATION_MAX_TO_LOG); + } + } + } + + return json_encode($data, JSON_PRETTY_PRINT); + } + + public function formatValueAsString($value): string + { + return json_encode($this->formatValue($value)); + } + + /** + * Returns a formatted value of a given entities' field + */ + private function formatField(object $entity, string $field): mixed + { + if ($this->propertyAccessor->isReadable($entity, $field)) { + $value = $this->propertyAccessor->getValue($entity, $field); + return $this->formatValue($value); + } + + return null; + } + + /** + * Returns a formatted value depending on the given value's type. + */ + private function formatValue($value): mixed + { + return match (gettype($value)) { + 'string' => mb_substr($value, 0, Log::MAX_STRING_LENGTH), + 'NULL', 'boolean', 'double', 'integer' => $value, + 'object' => $this->formatObject($value), + 'array' => array_map($this->formatValue(...), $value), + 'resource', 'resource (closed)', 'unknown type' + => throw new \InvalidArgumentException('Unhandled type'), + }; + } + + /** + * Returns a scalar representation of the object + */ + private function formatObject(object $value): mixed + { + if ($value instanceof Money) { + // Required because Money does not provide a __toString() method + // @link https://github.com/moneyphp/money/issues/184 + // Case when we change the amount of a Money in one of our entities. + // We log amount & currency code for log readability. + return $value->getAmount() . ' ' . $value->getCurrency()->getCode(); + } + + if (method_exists($value, 'getId')) { + return $value->getId(); + } + + if ($value instanceof \DateTimeInterface) { + return $value->format(\DateTimeInterface::ISO8601); + } + + if ($value instanceof \DateTimeZone) { + return $value->getName(); + } + + if ($value instanceof Collection) { + return array_map(static function (object $item): mixed { + return $item->getId(); + }, $value->toArray()); + } + + if ($value instanceof \BackedEnum) { + return $value->value; + } + + if (method_exists($value, '__toString')) { + return $value->__toString(); + } + + throw new UnsupportObjectException($value); + } +} diff --git a/tests/Functional/Entity/Address.php b/tests/Functional/Entity/Address.php index dae2e45..ab35594 100755 --- a/tests/Functional/Entity/Address.php +++ b/tests/Functional/Entity/Address.php @@ -1,30 +1,30 @@ -streetName; - } - - public function setStreetName(?string $streetName): self - { - $this->streetName = $streetName; - - return $this; - } -} +streetName; + } + + public function setStreetName(?string $streetName): self + { + $this->streetName = $streetName; + + return $this; + } +} From bd5b790d3e8fbb2eaaa553e48d787815e1d19e51 Mon Sep 17 00:00:00 2001 From: Sylvain Fabre Date: Sat, 21 Mar 2026 21:49:22 +0100 Subject: [PATCH 3/3] Fix CRLF line endings in LogSerializerTest Co-Authored-By: Claude Sonnet 4.6 --- tests/Serializer/LogSerializerTest.php | 272 ++++++++++++------------- 1 file changed, 136 insertions(+), 136 deletions(-) diff --git a/tests/Serializer/LogSerializerTest.php b/tests/Serializer/LogSerializerTest.php index 3326f43..2caa74b 100755 --- a/tests/Serializer/LogSerializerTest.php +++ b/tests/Serializer/LogSerializerTest.php @@ -1,136 +1,136 @@ -setEmail('test@gmail.com'); - - $tag = new Tag(); - - $post = new Post($author); - $post->addTag($tag); - - $entityManager = self::getContainer()->get(EntityManagerInterface::class); - - $formatter = self::getContainer()->get(LogSerializer::class); - self::assertSame( - json_encode(array_merge( - $this->helperFormatEntity($author), - [ - 'email' => $author->getEmail(), - 'registeredAt' => $author->getRegisteredAt()->format(\DateTimeInterface::ISO8601), - 'address' => $author->getAddress(), - ] - ), JSON_PRETTY_PRINT), - $formatter->formatEntity($entityManager, $author) - ); - - self::assertSame( - json_encode($this->helperFormatEntity($tag), JSON_PRETTY_PRINT), - $formatter->formatEntity($entityManager, $tag) - ); - self::assertSame( - json_encode(array_merge( - $this->helperFormatEntity($post), - [ - 'author' => $author->getId(), - 'tags' => [$tag->getId()], - ] - ), JSON_PRETTY_PRINT), - $formatter->formatEntity($entityManager, $post) - ); - } - - public function helperFormatEntity(AbstractEntity $entity): array - { - return [ - 'id' => $entity->getId(), - 'createdAt' => $entity->getCreatedAt()->format(\DateTimeInterface::ISO8601), - 'updatedAt' => $entity->getUpdatedAt()->format(\DateTimeInterface::ISO8601), - ]; - } - - /** - * @dataProvider providerFormatValueAsString - */ - public function testFormatValueAsStringWorks($value, $formatted): void - { - $formatter = new LogSerializer(); - self::assertSame($formatted, $formatter->formatValueAsString($value)); - } - - public static function providerFormatValueAsString(): iterable - { - yield [null, 'null']; - yield [[null], '[null]']; - - yield [true, 'true']; - yield [[true], '[true]']; - - yield ['foo', '"foo"']; - yield [['foo'], '["foo"]']; - yield [ - str_repeat('a', 70000), - '"' . str_repeat('a', Log::MAX_STRING_LENGTH) . '"', - ]; - - yield [1, '1']; - yield [[1], '[1]']; - - yield [1.5, '1.5']; - yield [[1.5], '[1.5]']; - - yield [new \DateTimeImmutable('@1529500134'), '"2018-06-20T13:08:54+0000"']; - yield [[new \DateTimeImmutable('@1529500134')], '["2018-06-20T13:08:54+0000"]']; - - yield [new DateTimeZone('Europe/Paris'), '"Europe\/Paris"']; - yield [[new DateTimeZone('Europe/Paris')], '["Europe\/Paris"]']; - - yield [Money::EUR('100'), '"100 EUR"']; - yield [[Money::EUR('100')], '["100 EUR"]']; - - yield [new Currency('EUR'), '"EUR"']; - yield [[new Currency('EUR')], '["EUR"]']; - - $entity = new Author(); - yield [$entity, (string) $entity->getId()]; - yield [[$entity], '[' . $entity->getId() . ']']; - - // Doctrine collection - $collection = new ArrayCollection(); - $collection->add($entity); - yield [$collection, '[' . $entity->getId() . ']']; - } - - public function testUnsupportedObjectThrowsAnException(): void - { - $formatter = new LogSerializer(); - - $this->expectException(UnsupportObjectException::class); - - $formatter->formatValueAsString(new ObjectWithoutId()); - } -} +setEmail('test@gmail.com'); + + $tag = new Tag(); + + $post = new Post($author); + $post->addTag($tag); + + $entityManager = self::getContainer()->get(EntityManagerInterface::class); + + $formatter = self::getContainer()->get(LogSerializer::class); + self::assertSame( + json_encode(array_merge( + $this->helperFormatEntity($author), + [ + 'email' => $author->getEmail(), + 'registeredAt' => $author->getRegisteredAt()->format(\DateTimeInterface::ISO8601), + 'address' => $author->getAddress(), + ] + ), JSON_PRETTY_PRINT), + $formatter->formatEntity($entityManager, $author) + ); + + self::assertSame( + json_encode($this->helperFormatEntity($tag), JSON_PRETTY_PRINT), + $formatter->formatEntity($entityManager, $tag) + ); + self::assertSame( + json_encode(array_merge( + $this->helperFormatEntity($post), + [ + 'author' => $author->getId(), + 'tags' => [$tag->getId()], + ] + ), JSON_PRETTY_PRINT), + $formatter->formatEntity($entityManager, $post) + ); + } + + public function helperFormatEntity(AbstractEntity $entity): array + { + return [ + 'id' => $entity->getId(), + 'createdAt' => $entity->getCreatedAt()->format(\DateTimeInterface::ISO8601), + 'updatedAt' => $entity->getUpdatedAt()->format(\DateTimeInterface::ISO8601), + ]; + } + + /** + * @dataProvider providerFormatValueAsString + */ + public function testFormatValueAsStringWorks($value, $formatted): void + { + $formatter = new LogSerializer(); + self::assertSame($formatted, $formatter->formatValueAsString($value)); + } + + public static function providerFormatValueAsString(): iterable + { + yield [null, 'null']; + yield [[null], '[null]']; + + yield [true, 'true']; + yield [[true], '[true]']; + + yield ['foo', '"foo"']; + yield [['foo'], '["foo"]']; + yield [ + str_repeat('a', 70000), + '"' . str_repeat('a', Log::MAX_STRING_LENGTH) . '"', + ]; + + yield [1, '1']; + yield [[1], '[1]']; + + yield [1.5, '1.5']; + yield [[1.5], '[1.5]']; + + yield [new \DateTimeImmutable('@1529500134'), '"2018-06-20T13:08:54+0000"']; + yield [[new \DateTimeImmutable('@1529500134')], '["2018-06-20T13:08:54+0000"]']; + + yield [new DateTimeZone('Europe/Paris'), '"Europe\/Paris"']; + yield [[new DateTimeZone('Europe/Paris')], '["Europe\/Paris"]']; + + yield [Money::EUR('100'), '"100 EUR"']; + yield [[Money::EUR('100')], '["100 EUR"]']; + + yield [new Currency('EUR'), '"EUR"']; + yield [[new Currency('EUR')], '["EUR"]']; + + $entity = new Author(); + yield [$entity, (string) $entity->getId()]; + yield [[$entity], '[' . $entity->getId() . ']']; + + // Doctrine collection + $collection = new ArrayCollection(); + $collection->add($entity); + yield [$collection, '[' . $entity->getId() . ']']; + } + + public function testUnsupportedObjectThrowsAnException(): void + { + $formatter = new LogSerializer(); + + $this->expectException(UnsupportObjectException::class); + + $formatter->formatValueAsString(new ObjectWithoutId()); + } +}