From cb4e61def9b84b4ec16c1df42f450db1aa23b577 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Mon, 12 Aug 2024 10:49:35 +0200 Subject: [PATCH 01/39] Add Symfony's Serializer support --- src/bundle/Resources/config/services.yml | 36 +++++++++++++++---- src/contracts/Output/Generator.php | 7 ++++ src/contracts/Output/NormalizerDispatcher.php | 31 ++++++++++++++++ .../Output/NormalizerDispatcherInterface.php | 16 +++++++++ .../Output/ValueObjectVisitorDispatcher.php | 16 +++++++++ src/contracts/Output/Visitor.php | 18 +++++----- src/lib/Output/Generator/Json.php | 14 ++++++-- src/lib/Output/Generator/Xml.php | 13 +++++-- src/lib/Output/Normalizer/TestData.php | 18 ++++++++++ src/lib/Output/Normalizer/TestNormalizer.php | 27 ++++++++++++++ 10 files changed, 175 insertions(+), 21 deletions(-) create mode 100644 src/contracts/Output/NormalizerDispatcher.php create mode 100644 src/contracts/Output/NormalizerDispatcherInterface.php create mode 100644 src/lib/Output/Normalizer/TestData.php create mode 100644 src/lib/Output/Normalizer/TestNormalizer.php diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index 1d3a0cd41..9fb8fc9e1 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -320,23 +320,26 @@ services: ibexa.rest.output.visitor.json: class: Ibexa\Contracts\Rest\Output\Visitor arguments: - - '@Ibexa\Rest\Output\Generator\Json' - - '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher' + $generator: '@Ibexa\Rest\Output\Generator\Json' + $valueObjectVisitorDispatcher: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher' + $normalizerDispatcher: '@Ibexa\Contracts\Rest\Output\NormalizerDispatcherInterface' tags: - { name: ibexa.rest.output.visitor, regexps: ibexa.rest.output.visitor.json.regexps } ibexa.rest.output.visitor.xml: class: Ibexa\Contracts\Rest\Output\Visitor arguments: - - '@Ibexa\Rest\Output\Generator\Xml' - - '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher' + $generator: '@Ibexa\Rest\Output\Generator\Xml' + $valueObjectVisitorDispatcher: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher' + $normalizerDispatcher: '@Ibexa\Contracts\Rest\Output\NormalizerDispatcherInterface' tags: - { name: ibexa.rest.output.visitor, regexps: ibexa.rest.output.visitor.xml.regexps } # format output generators Ibexa\Rest\Output\Generator\Xml: arguments: - - '@Ibexa\Rest\Output\Generator\Xml\FieldTypeHashGenerator' + $hashGenerator: '@Ibexa\Rest\Output\Generator\Xml\FieldTypeHashGenerator' + $encoder: '@ibexa.rest.serializer.encoder.xml' calls: - [ setFormatOutput, [ "%kernel.debug%" ] ] @@ -350,7 +353,8 @@ services: Ibexa\Rest\Output\Generator\Json: arguments: - - '@Ibexa\Rest\Output\Generator\Json\FieldTypeHashGenerator' + $fieldTypeHashGenerator: '@Ibexa\Rest\Output\Generator\Json\FieldTypeHashGenerator' + $encoder: '@ibexa.rest.serializer.encoder.json' calls: - [ setFormatOutput, [ "%kernel.debug%" ] ] @@ -403,6 +407,26 @@ services: Ibexa\Contracts\Rest\Input\MediaTypeParserInterface: '@Ibexa\Contracts\Rest\Input\MediaTypeParser' + ibexa.rest.serializer.encoder.json: + class: Symfony\Component\Serializer\Encoder\JsonEncoder + tags: + - ibexa.rest.serializer.encoder + + ibexa.rest.serializer.encoder.xml: + class: Symfony\Component\Serializer\Encoder\XmlEncoder + tags: + - ibexa.rest.serializer.encoder + + Ibexa\Contracts\Rest\Output\NormalizerDispatcherInterface: '@Ibexa\Contracts\Rest\Output\NormalizerDispatcher' + + Ibexa\Contracts\Rest\Output\NormalizerDispatcher: + arguments: + $normalizer: '@ibexa.rest.serializer' + + Ibexa\Rest\Output\Normalizer\TestNormalizer: + tags: + - ibexa.rest.serializer.normalizer + Ibexa\Contracts\Rest\Input\Parser\Query\Criterion\BaseCriterionProcessor: abstract: true arguments: diff --git a/src/contracts/Output/Generator.php b/src/contracts/Output/Generator.php index 798ceacb7..6e844f137 100644 --- a/src/contracts/Output/Generator.php +++ b/src/contracts/Output/Generator.php @@ -29,6 +29,8 @@ abstract class Generator */ protected $formatOutput = false; + protected array $normalizedData = []; + public function setFormatOutput($formatOutput) { $this->formatOutput = (bool)$formatOutput; @@ -423,6 +425,11 @@ protected function checkEnd($type, $data) } } + public function setNormalizedData(array $data): void + { + $this->normalizedData = $data; + } + /** * Serializes a boolean value. * diff --git a/src/contracts/Output/NormalizerDispatcher.php b/src/contracts/Output/NormalizerDispatcher.php new file mode 100644 index 000000000..fcc21eaa6 --- /dev/null +++ b/src/contracts/Output/NormalizerDispatcher.php @@ -0,0 +1,31 @@ +normalizer->supportsNormalization($data); + } + + public function visit(mixed $data, Generator $generator): void + { + $normalizedData = $this->normalizer->normalize($data); + + $generator->setNormalizedData($normalizedData); + } +} \ No newline at end of file diff --git a/src/contracts/Output/NormalizerDispatcherInterface.php b/src/contracts/Output/NormalizerDispatcherInterface.php new file mode 100644 index 000000000..739298793 --- /dev/null +++ b/src/contracts/Output/NormalizerDispatcherInterface.php @@ -0,0 +1,16 @@ +outputVisitor = $outputVisitor; @@ -39,6 +42,11 @@ public function setOutputGenerator(Generator $outputGenerator) $this->outputGenerator = $outputGenerator; } + public function setNormalizerDispatcher(NormalizerDispatcherInterface $normalizerDispatcher): void + { + $this->normalizerDispatcher = $normalizerDispatcher; + } + /** * @param string $visitedClassName The FQN of the visited class * @param \Ibexa\Contracts\Rest\Output\ValueObjectVisitor $visitor The visitor object @@ -58,6 +66,10 @@ public function addVisitor($visitedClassName, ValueObjectVisitor $visitor) */ public function visit($data) { + //TODO + $data = new TestData(); + $data->setName('77656677556655566'); + if ($data instanceof Error) { // Skip internal PHP errors serialization throw $data; @@ -76,6 +88,10 @@ public function visit($data) } } while ($className = get_parent_class($className)); + if ($this->normalizerDispatcher->supportsNormalization($data)) { + return $this->normalizerDispatcher->visit($data, $this->outputGenerator); + } + throw new Exceptions\NoVisitorFoundException($checkedClassNames); } } diff --git a/src/contracts/Output/Visitor.php b/src/contracts/Output/Visitor.php index a71e7536d..c0f978acb 100644 --- a/src/contracts/Output/Visitor.php +++ b/src/contracts/Output/Visitor.php @@ -40,17 +40,17 @@ class Visitor */ private $statusCode; - /** - * Construct from Generator and an array of concrete view model visitors. - * - * @param \Ibexa\Contracts\Rest\Output\Generator $generator - * @param \Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher $valueObjectVisitorDispatcher - */ - public function __construct(Generator $generator, ValueObjectVisitorDispatcher $valueObjectVisitorDispatcher) - { + private NormalizerDispatcherInterface $normalizerDispatcher; + + public function __construct( + Generator $generator, + ValueObjectVisitorDispatcher $valueObjectVisitorDispatcher, + NormalizerDispatcherInterface $normalizerDispatcher, + ) { $this->generator = $generator; $this->valueObjectVisitorDispatcher = $valueObjectVisitorDispatcher; - $this->response = new Response('', Response::HTTP_OK); + $this->normalizerDispatcher = $normalizerDispatcher; + $this->response = new Response('', 200); } /** diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index 260f18d0d..a882a85d1 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -8,6 +8,7 @@ namespace Ibexa\Rest\Output\Generator; use Ibexa\Contracts\Rest\Output\Generator; +use Symfony\Component\Serializer\Encoder\EncoderInterface; /** * Json generator. @@ -46,8 +47,11 @@ class Json extends Generator * @param \Ibexa\Rest\Output\Generator\Json\FieldTypeHashGenerator $fieldTypeHashGenerator * @param string $vendor */ - public function __construct(Json\FieldTypeHashGenerator $fieldTypeHashGenerator, $vendor = 'vnd.ibexa.api') - { + public function __construct( + Json\FieldTypeHashGenerator $fieldTypeHashGenerator, + protected EncoderInterface $encoder, + $vendor = 'vnd.ibexa.api', + ) { $this->fieldTypeHashGenerator = $fieldTypeHashGenerator; $this->vendor = $vendor; } @@ -73,7 +77,7 @@ public function startDocument($data) */ public function isEmpty() { - return $this->isEmpty; + return $this->normalizedData === [] && $this->isEmpty; } /** @@ -87,6 +91,10 @@ public function isEmpty() */ public function endDocument($data) { + if ($this->normalizedData !== []) { + return $this->encoder->encode($this->normalizedData, 'json'); + } + $this->checkEndDocument($data); $jsonEncodeOptions = 0; diff --git a/src/lib/Output/Generator/Xml.php b/src/lib/Output/Generator/Xml.php index b8317ccde..3720d5fde 100644 --- a/src/lib/Output/Generator/Xml.php +++ b/src/lib/Output/Generator/Xml.php @@ -8,6 +8,7 @@ namespace Ibexa\Rest\Output\Generator; use Ibexa\Contracts\Rest\Output\Generator; +use Symfony\Component\Serializer\Encoder\EncoderInterface; /** * Xml generator. @@ -42,13 +43,19 @@ class Xml extends Generator */ protected $vendor; + protected EncoderInterface $encoder; + /** * @param \Ibexa\Rest\Output\Generator\Xml\FieldTypeHashGenerator $hashGenerator * @param string $vendor */ - public function __construct(Xml\FieldTypeHashGenerator $hashGenerator, $vendor = 'vnd.ibexa.api') - { + public function __construct( + Xml\FieldTypeHashGenerator $hashGenerator, + EncoderInterface $encoder, + $vendor = 'vnd.ibexa.api', + ) { $this->hashGenerator = $hashGenerator; + $this->encoder = $encoder; $this->vendor = $vendor; } @@ -76,7 +83,7 @@ public function startDocument($data) */ public function isEmpty() { - return $this->isEmpty; + return $this->normalizedData === [] && $this->isEmpty; } /** diff --git a/src/lib/Output/Normalizer/TestData.php b/src/lib/Output/Normalizer/TestData.php new file mode 100644 index 000000000..ed730a1f9 --- /dev/null +++ b/src/lib/Output/Normalizer/TestData.php @@ -0,0 +1,18 @@ +name; + } + + public function setName(string $name): void + { + $this->name = $name; + } +} \ No newline at end of file diff --git a/src/lib/Output/Normalizer/TestNormalizer.php b/src/lib/Output/Normalizer/TestNormalizer.php new file mode 100644 index 000000000..489445da7 --- /dev/null +++ b/src/lib/Output/Normalizer/TestNormalizer.php @@ -0,0 +1,27 @@ + $object->getName(), + ]; + + return $result; + } + + public function supportsNormalization(mixed $data, string $format = null): bool + { + return $data instanceof TestData; + } +} From 5bf3e437a1a0aff4015a9a25bc833be6cf78586c Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Thu, 22 Aug 2024 17:33:47 +0200 Subject: [PATCH 02/39] Add `AdapterNormalizer` --- .../Compiler/ValueObjectVisitorPass.php | 34 +++-- src/bundle/Resources/config/services.yml | 16 +- src/contracts/Output/AdapterNormalizer.php | 137 ++++++++++++++++++ src/contracts/Output/Generator.php | 12 +- src/contracts/Output/NormalizerDispatcher.php | 31 ---- .../Output/NormalizerDispatcherInterface.php | 16 -- .../Output/ValueObjectVisitorDispatcher.php | 24 +-- src/contracts/Output/Visitor.php | 68 ++++----- src/lib/Output/Generator/Json.php | 11 +- src/lib/Output/Generator/Xml.php | 7 +- src/lib/Output/Normalizer/TestData.php | 57 +++++--- src/lib/Output/Normalizer/TestNormalizer.php | 65 +++++---- 12 files changed, 297 insertions(+), 181 deletions(-) create mode 100644 src/contracts/Output/AdapterNormalizer.php delete mode 100644 src/contracts/Output/NormalizerDispatcher.php delete mode 100644 src/contracts/Output/NormalizerDispatcherInterface.php diff --git a/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorPass.php b/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorPass.php index a84f1fccf..415b20beb 100644 --- a/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorPass.php +++ b/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorPass.php @@ -7,6 +7,7 @@ namespace Ibexa\Bundle\Rest\DependencyInjection\Compiler; +use Ibexa\Contracts\Rest\Output\AdapterNormalizer; use Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -26,26 +27,31 @@ public function process(ContainerBuilder $container) return; } - $definition = $container->getDefinition(ValueObjectVisitorDispatcher::class); + $definitions = [ + $container->getDefinition(ValueObjectVisitorDispatcher::class), + $container->getDefinition(AdapterNormalizer::class), + ]; $taggedServiceIds = $container->findTaggedServiceIds( self::OUTPUT_VALUE_OBJECT_VISITOR_SERVICE_TAG ); - foreach ($taggedServiceIds as $id => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['type'])) { - throw new \LogicException( - sprintf( - 'The "%s" service tag needs a "type" attribute to identify the field type.', - self::OUTPUT_VALUE_OBJECT_VISITOR_SERVICE_TAG - ) + foreach ($definitions as $definition) { + foreach ($taggedServiceIds as $id => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['type'])) { + throw new \LogicException( + sprintf( + 'The "%s" service tag needs a "type" attribute to identify the field type.', + self::OUTPUT_VALUE_OBJECT_VISITOR_SERVICE_TAG + ) + ); + } + + $definition->addMethodCall( + 'addVisitor', + [$attribute['type'], new Reference($id)] ); } - - $definition->addMethodCall( - 'addVisitor', - [$attribute['type'], new Reference($id)] - ); } } } diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index 9fb8fc9e1..a609e3332 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -321,8 +321,10 @@ services: class: Ibexa\Contracts\Rest\Output\Visitor arguments: $generator: '@Ibexa\Rest\Output\Generator\Json' + $normalizer: '@ibexa.rest.serializer' + $encoder: '@ibexa.rest.serializer.encoder.json' $valueObjectVisitorDispatcher: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher' - $normalizerDispatcher: '@Ibexa\Contracts\Rest\Output\NormalizerDispatcherInterface' + $locationService: '@ibexa.api.service.location' tags: - { name: ibexa.rest.output.visitor, regexps: ibexa.rest.output.visitor.json.regexps } @@ -330,8 +332,10 @@ services: class: Ibexa\Contracts\Rest\Output\Visitor arguments: $generator: '@Ibexa\Rest\Output\Generator\Xml' + $normalizer: '@ibexa.rest.serializer' + $encoder: '@ibexa.rest.serializer.encoder.xml' $valueObjectVisitorDispatcher: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher' - $normalizerDispatcher: '@Ibexa\Contracts\Rest\Output\NormalizerDispatcherInterface' + $locationService: '@ibexa.api.service.location' tags: - { name: ibexa.rest.output.visitor, regexps: ibexa.rest.output.visitor.xml.regexps } @@ -417,11 +421,11 @@ services: tags: - ibexa.rest.serializer.encoder - Ibexa\Contracts\Rest\Output\NormalizerDispatcherInterface: '@Ibexa\Contracts\Rest\Output\NormalizerDispatcher' - - Ibexa\Contracts\Rest\Output\NormalizerDispatcher: + Ibexa\Contracts\Rest\Output\AdapterNormalizer: arguments: - $normalizer: '@ibexa.rest.serializer' + $encoder: '@ibexa.rest.serializer.encoder.json' + tags: + - { name: ibexa.rest.serializer.normalizer, priority: -1000 } Ibexa\Rest\Output\Normalizer\TestNormalizer: tags: diff --git a/src/contracts/Output/AdapterNormalizer.php b/src/contracts/Output/AdapterNormalizer.php new file mode 100644 index 000000000..3508f9dce --- /dev/null +++ b/src/contracts/Output/AdapterNormalizer.php @@ -0,0 +1,137 @@ + + */ + private array $visitors; + + public function __construct( + private readonly EncoderInterface $encoder, + ) { + } + + /** + * @param class-string $visitedClassName + */ + public function addVisitor(string $visitedClassName, ValueObjectVisitor $visitor): void + { + $this->visitors[$visitedClassName] = $visitor; + } + + /** + * @param array $context + */ + public function normalize(mixed $object, ?string $format = null, array $context = []): mixed + { + $eligibleVisitor = $this->getEligibleVisitor(is_object($object) ? $object::class : null); + if ($eligibleVisitor instanceof ValueObjectVisitor) { + return $this->visitValueObject($object, $eligibleVisitor); + } + + return $this->normalizer->normalize($object, $format, $context); + } + + /** + * @param array $context + */ + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool + { + if (($context[self::CALLED_CONTEXT] ?? false) === true) { + return false; + } + + $className = is_object($data) ? $data::class : null; + $eligibleVisitor = $this->getEligibleVisitor($className); + + if ($eligibleVisitor instanceof ValueObjectVisitor) { + return true; + } + + return $this->normalizer->supportsNormalization( + $data, + null, + $context + [self::CALLED_CONTEXT => true], + ); + } + + /** + * @return array + */ + private function visitValueObject(object $object, ValueObjectVisitor $valueObjectVisitor): array + { + $visitor = $this->createVisitor(); + $generator = $visitor->getGenerator(); + + $generator->reset(); + $generator->startDocument($object); + + $valueObjectVisitor->visit($visitor, $generator, $object); + + $generator->endDocument($object); + + return $generator->toArray(); + } + + /** + * @param class-string|null $className + */ + private function getEligibleVisitor(?string $className): ?ValueObjectVisitor + { + if ($className === null) { + return null; + } + + do { + if (isset($this->visitors[$className])) { + return $this->visitors[$className]; + } + } while ($className = get_parent_class($className)); + + return null; + } + + private function createVisitor(): Visitor + { + $fieldTypeHashGenerator = new Json\FieldTypeHashGenerator($this->normalizer); + $valueObjectVisitorDispatcher = new ValueObjectVisitorDispatcher(); + + $generator = new Json( + $fieldTypeHashGenerator, + $this->encoder, + ); + + $visitor = new Visitor( + $generator, + $this->normalizer, + $this->encoder, + $valueObjectVisitorDispatcher, + ); + + $valueObjectVisitorDispatcher->setVisitors($this->visitors); + $valueObjectVisitorDispatcher->setOutputVisitor($visitor); + $valueObjectVisitorDispatcher->setOutputGenerator($generator); + + return $visitor; + } +} diff --git a/src/contracts/Output/Generator.php b/src/contracts/Output/Generator.php index 6e844f137..e1fc2b4ae 100644 --- a/src/contracts/Output/Generator.php +++ b/src/contracts/Output/Generator.php @@ -29,8 +29,6 @@ abstract class Generator */ protected $formatOutput = false; - protected array $normalizedData = []; - public function setFormatOutput($formatOutput) { $this->formatOutput = (bool)$formatOutput; @@ -425,11 +423,6 @@ protected function checkEnd($type, $data) } } - public function setNormalizedData(array $data): void - { - $this->normalizedData = $data; - } - /** * Serializes a boolean value. * @@ -438,4 +431,9 @@ public function setNormalizedData(array $data): void * @return mixed */ abstract public function serializeBool($boolValue); + + /** + * @return array + */ + abstract public function toArray(): array; } diff --git a/src/contracts/Output/NormalizerDispatcher.php b/src/contracts/Output/NormalizerDispatcher.php deleted file mode 100644 index fcc21eaa6..000000000 --- a/src/contracts/Output/NormalizerDispatcher.php +++ /dev/null @@ -1,31 +0,0 @@ -normalizer->supportsNormalization($data); - } - - public function visit(mixed $data, Generator $generator): void - { - $normalizedData = $this->normalizer->normalize($data); - - $generator->setNormalizedData($normalizedData); - } -} \ No newline at end of file diff --git a/src/contracts/Output/NormalizerDispatcherInterface.php b/src/contracts/Output/NormalizerDispatcherInterface.php deleted file mode 100644 index 739298793..000000000 --- a/src/contracts/Output/NormalizerDispatcherInterface.php +++ /dev/null @@ -1,16 +0,0 @@ -outputVisitor = $outputVisitor; @@ -42,11 +39,6 @@ public function setOutputGenerator(Generator $outputGenerator) $this->outputGenerator = $outputGenerator; } - public function setNormalizerDispatcher(NormalizerDispatcherInterface $normalizerDispatcher): void - { - $this->normalizerDispatcher = $normalizerDispatcher; - } - /** * @param string $visitedClassName The FQN of the visited class * @param \Ibexa\Contracts\Rest\Output\ValueObjectVisitor $visitor The visitor object @@ -56,6 +48,14 @@ public function addVisitor($visitedClassName, ValueObjectVisitor $visitor) $this->visitors[$visitedClassName] = $visitor; } + /** + * @param array $visitors + */ + public function setVisitors(array $visitors): void + { + $this->visitors = $visitors; + } + /** * @param object $data The visited object * @@ -66,10 +66,6 @@ public function addVisitor($visitedClassName, ValueObjectVisitor $visitor) */ public function visit($data) { - //TODO - $data = new TestData(); - $data->setName('77656677556655566'); - if ($data instanceof Error) { // Skip internal PHP errors serialization throw $data; @@ -88,10 +84,6 @@ public function visit($data) } } while ($className = get_parent_class($className)); - if ($this->normalizerDispatcher->supportsNormalization($data)) { - return $this->normalizerDispatcher->visit($data, $this->outputGenerator); - } - throw new Exceptions\NoVisitorFoundException($checkedClassNames); } } diff --git a/src/contracts/Output/Visitor.php b/src/contracts/Output/Visitor.php index c0f978acb..8d568fe1e 100644 --- a/src/contracts/Output/Visitor.php +++ b/src/contracts/Output/Visitor.php @@ -4,52 +4,38 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); namespace Ibexa\Contracts\Rest\Output; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Rest\Output\Normalizer\TestData; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Serializer\Encoder\EncoderInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; /** * Visits a value object into an HTTP Response. */ class Visitor { - /** - * @var \Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher - */ - protected $valueObjectVisitorDispatcher = []; - - /** - * Generator. - * - * @var \Ibexa\Contracts\Rest\Output\Generator - */ - protected $generator; - /** * HTTP Response Object. - * - * @var \Symfony\Component\HttpFoundation\Response */ - protected $response; + protected Response $response; /** * Used to ensure that the status code can't be overwritten. - * - * @var int */ - private $statusCode; - - private NormalizerDispatcherInterface $normalizerDispatcher; + private ?int $statusCode = null; public function __construct( - Generator $generator, - ValueObjectVisitorDispatcher $valueObjectVisitorDispatcher, - NormalizerDispatcherInterface $normalizerDispatcher, + private readonly Generator $generator, + private readonly NormalizerInterface $normalizer, + private readonly EncoderInterface $encoder, + private readonly ValueObjectVisitorDispatcher $valueObjectVisitorDispatcher, + private readonly ?LocationService $locationService = null, //TODO to remove ) { - $this->generator = $generator; - $this->valueObjectVisitorDispatcher = $valueObjectVisitorDispatcher; - $this->normalizerDispatcher = $normalizerDispatcher; $this->response = new Response('', 200); } @@ -87,16 +73,18 @@ public function setStatus($statusCode) /** * Visit struct returned by controllers. * - * @param mixed $data - * * @return \Symfony\Component\HttpFoundation\Response */ - public function visit($data) + public function visit(mixed $data) { - $this->generator->reset(); - $this->generator->startDocument($data); + //TODO to remove + $data = new TestData(); + $data->setName('test test'); + $location = $this->locationService->loadLocation(2); + $location = new RestLocation($location, 2); + $data->setLocation($location); - $this->visitValueObject($data); + $normalizedData = $this->normalizer->normalize($data); //@todo Needs refactoring! // A hackish solution to enable outer visitors to disable setting @@ -111,7 +99,7 @@ public function visit($data) $response = clone $this->response; - $response->setContent($this->generator->isEmpty() ? null : $this->generator->endDocument($data)); + $response->setContent($this->encoder->encode($normalizedData, 'json')); // reset the inner response $this->response = new Response(null, Response::HTTP_OK); @@ -143,19 +131,19 @@ public function visitValueObject($data) * @param string $type * * @see \Ibexa\Rest\Generator::getMediaType() - * - * @return string */ - public function getMediaType($type) + public function getMediaType(string $type): string { return $this->generator->getMediaType($type); } - /** - * @return \Symfony\Component\HttpFoundation\Response - */ - public function getResponse() + public function getResponse(): Response { return $this->response; } + + public function getGenerator(): Generator + { + return $this->generator; + } } diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index a882a85d1..e260599ac 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -77,7 +77,7 @@ public function startDocument($data) */ public function isEmpty() { - return $this->normalizedData === [] && $this->isEmpty; + return $this->isEmpty; } /** @@ -91,10 +91,6 @@ public function isEmpty() */ public function endDocument($data) { - if ($this->normalizedData !== []) { - return $this->encoder->encode($this->normalizedData, 'json'); - } - $this->checkEndDocument($data); $jsonEncodeOptions = 0; @@ -332,4 +328,9 @@ public function serializeBool($boolValue) { return (bool)$boolValue; } + + public function toArray(): array + { + return json_decode(json_encode($this->json), true); + } } diff --git a/src/lib/Output/Generator/Xml.php b/src/lib/Output/Generator/Xml.php index 3720d5fde..5c5e22902 100644 --- a/src/lib/Output/Generator/Xml.php +++ b/src/lib/Output/Generator/Xml.php @@ -83,7 +83,7 @@ public function startDocument($data) */ public function isEmpty() { - return $this->normalizedData === [] && $this->isEmpty; + return $this->isEmpty; } /** @@ -271,4 +271,9 @@ public function serializeBool($boolValue) { return $boolValue ? 'true' : 'false'; } + + public function toArray(): array + { + return []; + } } diff --git a/src/lib/Output/Normalizer/TestData.php b/src/lib/Output/Normalizer/TestData.php index ed730a1f9..35e3c6600 100644 --- a/src/lib/Output/Normalizer/TestData.php +++ b/src/lib/Output/Normalizer/TestData.php @@ -1,18 +1,39 @@ -name; - } - - public function setName(string $name): void - { - $this->name = $name; - } -} \ No newline at end of file +name; + } + + public function setName(string $name): void + { + $this->name = $name; + } + + public function getLocation(): RestLocation + { + return $this->location; + } + + public function setLocation(RestLocation $location): void + { + $this->location = $location; + } +} diff --git a/src/lib/Output/Normalizer/TestNormalizer.php b/src/lib/Output/Normalizer/TestNormalizer.php index 489445da7..169abec7b 100644 --- a/src/lib/Output/Normalizer/TestNormalizer.php +++ b/src/lib/Output/Normalizer/TestNormalizer.php @@ -1,27 +1,38 @@ - $object->getName(), - ]; - - return $result; - } - - public function supportsNormalization(mixed $data, string $format = null): bool - { - return $data instanceof TestData; - } -} + $object->getName(), + 'Location' => $this->normalizer->normalize($object->getLocation())['Location'], + ]; + + return $result; + } + + public function supportsNormalization(mixed $data, string $format = null): bool + { + return $data instanceof TestData; + } +} From b478bae367102ded111b60fdc5ca1ecd8b2f8ff2 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Fri, 23 Aug 2024 13:33:17 +0200 Subject: [PATCH 03/39] Remove `ValueObjectVisitorDispatcher` --- .../Compiler/ValueObjectVisitorPass.php | 58 ------------ .../ValueObjectVisitorResolverPass.php | 53 +++++++++++ src/bundle/IbexaRestBundle.php | 2 +- src/bundle/Resources/config/services.yml | 11 ++- src/contracts/Output/AdapterNormalizer.php | 53 +++-------- .../Output/ValueObjectVisitorDispatcher.php | 89 ------------------- .../Output/ValueObjectVisitorResolver.php | 38 ++++++++ .../ValueObjectVisitorResolverInterface.php | 14 +++ src/contracts/Output/Visitor.php | 27 ++++-- src/lib/Output/Normalizer/TestData.php | 3 +- 10 files changed, 142 insertions(+), 206 deletions(-) delete mode 100644 src/bundle/DependencyInjection/Compiler/ValueObjectVisitorPass.php create mode 100644 src/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPass.php delete mode 100644 src/contracts/Output/ValueObjectVisitorDispatcher.php create mode 100644 src/contracts/Output/ValueObjectVisitorResolver.php create mode 100644 src/contracts/Output/ValueObjectVisitorResolverInterface.php diff --git a/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorPass.php b/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorPass.php deleted file mode 100644 index 415b20beb..000000000 --- a/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorPass.php +++ /dev/null @@ -1,58 +0,0 @@ -hasDefinition(ValueObjectVisitorDispatcher::class)) { - return; - } - - $definitions = [ - $container->getDefinition(ValueObjectVisitorDispatcher::class), - $container->getDefinition(AdapterNormalizer::class), - ]; - - $taggedServiceIds = $container->findTaggedServiceIds( - self::OUTPUT_VALUE_OBJECT_VISITOR_SERVICE_TAG - ); - foreach ($definitions as $definition) { - foreach ($taggedServiceIds as $id => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['type'])) { - throw new \LogicException( - sprintf( - 'The "%s" service tag needs a "type" attribute to identify the field type.', - self::OUTPUT_VALUE_OBJECT_VISITOR_SERVICE_TAG - ) - ); - } - - $definition->addMethodCall( - 'addVisitor', - [$attribute['type'], new Reference($id)] - ); - } - } - } - } -} diff --git a/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPass.php b/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPass.php new file mode 100644 index 000000000..f32e21a75 --- /dev/null +++ b/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPass.php @@ -0,0 +1,53 @@ +hasDefinition(ValueObjectVisitorResolver::class)) { + return; + } + + $definition = $container->getDefinition(ValueObjectVisitorResolver::class); + + $taggedServiceIds = $container->findTaggedServiceIds( + self::OUTPUT_VALUE_OBJECT_VISITOR_SERVICE_TAG + ); + + foreach ($taggedServiceIds as $id => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['type'])) { + throw new \LogicException( + sprintf( + 'The "%s" service tag needs a "type" attribute to identify the field type.', + self::OUTPUT_VALUE_OBJECT_VISITOR_SERVICE_TAG + ) + ); + } + + $definition->addMethodCall( + 'addVisitor', + [$attribute['type'], new Reference($id)] + ); + } + } + } +} diff --git a/src/bundle/IbexaRestBundle.php b/src/bundle/IbexaRestBundle.php index 0bdbb1d74..8c6c1a6bf 100644 --- a/src/bundle/IbexaRestBundle.php +++ b/src/bundle/IbexaRestBundle.php @@ -22,7 +22,7 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new Compiler\InputHandlerPass()); $container->addCompilerPass(new Compiler\InputParserPass()); $container->addCompilerPass(new Compiler\OutputVisitorPass()); - $container->addCompilerPass(new Compiler\ValueObjectVisitorPass()); + $container->addCompilerPass(new Compiler\ValueObjectVisitorResolverPass()); if ($container->hasExtension('lexik_jwt_authentication')) { $container->addCompilerPass(new Compiler\LexikAuthorizationHeaderBridgePass()); diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index a609e3332..607471b2b 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -323,7 +323,7 @@ services: $generator: '@Ibexa\Rest\Output\Generator\Json' $normalizer: '@ibexa.rest.serializer' $encoder: '@ibexa.rest.serializer.encoder.json' - $valueObjectVisitorDispatcher: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher' + $valueObjectVisitorResolver: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver' $locationService: '@ibexa.api.service.location' tags: - { name: ibexa.rest.output.visitor, regexps: ibexa.rest.output.visitor.json.regexps } @@ -334,7 +334,7 @@ services: $generator: '@Ibexa\Rest\Output\Generator\Xml' $normalizer: '@ibexa.rest.serializer' $encoder: '@ibexa.rest.serializer.encoder.xml' - $valueObjectVisitorDispatcher: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher' + $valueObjectVisitorResolver: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver' $locationService: '@ibexa.api.service.location' tags: - { name: ibexa.rest.output.visitor, regexps: ibexa.rest.output.visitor.xml.regexps } @@ -371,8 +371,6 @@ services: - { name: monolog.logger, channel: ibexa.rest } # value objects visitors - Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher: ~ - ibexa.rest.output.value_object_visitor.Exception.InvalidArgumentException: class: Ibexa\Rest\Server\Output\ValueObjectVisitor\InvalidArgumentException tags: @@ -424,6 +422,7 @@ services: Ibexa\Contracts\Rest\Output\AdapterNormalizer: arguments: $encoder: '@ibexa.rest.serializer.encoder.json' + $valueObjectVisitorResolver: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver' tags: - { name: ibexa.rest.serializer.normalizer, priority: -1000 } @@ -436,6 +435,10 @@ services: arguments: $parsingDispatcher: '@Ibexa\Contracts\Rest\Input\ParsingDispatcher' + Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolverInterface: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitor' + + Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver: ~ + Ibexa\Contracts\Rest\Input\Parser\Query\SortClause\BaseSortClauseProcessor: abstract: true arguments: diff --git a/src/contracts/Output/AdapterNormalizer.php b/src/contracts/Output/AdapterNormalizer.php index 3508f9dce..9f03aba2f 100644 --- a/src/contracts/Output/AdapterNormalizer.php +++ b/src/contracts/Output/AdapterNormalizer.php @@ -21,30 +21,21 @@ final class AdapterNormalizer implements NormalizerInterface, NormalizerAwareInt private const string CALLED_CONTEXT = __CLASS__ . '_CALLED'; - /** - * @var array - */ - private array $visitors; - public function __construct( private readonly EncoderInterface $encoder, + private readonly ValueObjectVisitorResolverInterface $valueObjectVisitorResolver, ) { } - /** - * @param class-string $visitedClassName - */ - public function addVisitor(string $visitedClassName, ValueObjectVisitor $visitor): void - { - $this->visitors[$visitedClassName] = $visitor; - } - /** * @param array $context */ public function normalize(mixed $object, ?string $format = null, array $context = []): mixed { - $eligibleVisitor = $this->getEligibleVisitor(is_object($object) ? $object::class : null); + $eligibleVisitor = is_object($object) + ? $this->valueObjectVisitorResolver->resolveValueObjectVisitor($object) + : null; + if ($eligibleVisitor instanceof ValueObjectVisitor) { return $this->visitValueObject($object, $eligibleVisitor); } @@ -61,8 +52,9 @@ public function supportsNormalization(mixed $data, ?string $format = null, array return false; } - $className = is_object($data) ? $data::class : null; - $eligibleVisitor = $this->getEligibleVisitor($className); + $eligibleVisitor = is_object($data) + ? $this->valueObjectVisitorResolver->resolveValueObjectVisitor($data) + : null; if ($eligibleVisitor instanceof ValueObjectVisitor) { return true; @@ -93,45 +85,20 @@ private function visitValueObject(object $object, ValueObjectVisitor $valueObjec return $generator->toArray(); } - /** - * @param class-string|null $className - */ - private function getEligibleVisitor(?string $className): ?ValueObjectVisitor - { - if ($className === null) { - return null; - } - - do { - if (isset($this->visitors[$className])) { - return $this->visitors[$className]; - } - } while ($className = get_parent_class($className)); - - return null; - } - private function createVisitor(): Visitor { $fieldTypeHashGenerator = new Json\FieldTypeHashGenerator($this->normalizer); - $valueObjectVisitorDispatcher = new ValueObjectVisitorDispatcher(); $generator = new Json( $fieldTypeHashGenerator, $this->encoder, ); - $visitor = new Visitor( + return new Visitor( $generator, $this->normalizer, $this->encoder, - $valueObjectVisitorDispatcher, + $this->valueObjectVisitorResolver, ); - - $valueObjectVisitorDispatcher->setVisitors($this->visitors); - $valueObjectVisitorDispatcher->setOutputVisitor($visitor); - $valueObjectVisitorDispatcher->setOutputGenerator($generator); - - return $visitor; } } diff --git a/src/contracts/Output/ValueObjectVisitorDispatcher.php b/src/contracts/Output/ValueObjectVisitorDispatcher.php deleted file mode 100644 index 13e485153..000000000 --- a/src/contracts/Output/ValueObjectVisitorDispatcher.php +++ /dev/null @@ -1,89 +0,0 @@ -outputVisitor = $outputVisitor; - } - - public function setOutputGenerator(Generator $outputGenerator) - { - $this->outputGenerator = $outputGenerator; - } - - /** - * @param string $visitedClassName The FQN of the visited class - * @param \Ibexa\Contracts\Rest\Output\ValueObjectVisitor $visitor The visitor object - */ - public function addVisitor($visitedClassName, ValueObjectVisitor $visitor) - { - $this->visitors[$visitedClassName] = $visitor; - } - - /** - * @param array $visitors - */ - public function setVisitors(array $visitors): void - { - $this->visitors = $visitors; - } - - /** - * @param object $data The visited object - * - * @throws \Ibexa\Contracts\Rest\Output\Exceptions\NoVisitorFoundException - * @throws \Ibexa\Contracts\Rest\Output\Exceptions\InvalidTypeException - * - * @return mixed - */ - public function visit($data) - { - if ($data instanceof Error) { - // Skip internal PHP errors serialization - throw $data; - } - - if (!is_object($data)) { - throw new Exceptions\InvalidTypeException($data); - } - $checkedClassNames = []; - - $className = get_class($data); - do { - $checkedClassNames[] = $className; - if (isset($this->visitors[$className])) { - return $this->visitors[$className]->visit($this->outputVisitor, $this->outputGenerator, $data); - } - } while ($className = get_parent_class($className)); - - throw new Exceptions\NoVisitorFoundException($checkedClassNames); - } -} diff --git a/src/contracts/Output/ValueObjectVisitorResolver.php b/src/contracts/Output/ValueObjectVisitorResolver.php new file mode 100644 index 000000000..7b6979f9b --- /dev/null +++ b/src/contracts/Output/ValueObjectVisitorResolver.php @@ -0,0 +1,38 @@ + + */ + private array $visitors; + + /** + * @param class-string $visitedClassName + */ + public function addVisitor(string $visitedClassName, $visitor): void + { + $this->visitors[$visitedClassName] = $visitor; + } + + public function resolveValueObjectVisitor(object $object): ?ValueObjectVisitor + { + $className = $object::class; + + do { + if (isset($this->visitors[$className])) { + return $this->visitors[$className]; + } + } while ($className = get_parent_class($className)); + + return null; + } +} diff --git a/src/contracts/Output/ValueObjectVisitorResolverInterface.php b/src/contracts/Output/ValueObjectVisitorResolverInterface.php new file mode 100644 index 000000000..5fd6be947 --- /dev/null +++ b/src/contracts/Output/ValueObjectVisitorResolverInterface.php @@ -0,0 +1,14 @@ +response = new Response('', 200); @@ -112,17 +114,24 @@ public function visit(mixed $data) * Visit struct returned by controllers. * * Can be called by sub-visitors to visit nested objects. - * - * @param object $data - * - * @return mixed */ - public function visitValueObject($data) + public function visitValueObject(mixed $data): void { - $this->valueObjectVisitorDispatcher->setOutputGenerator($this->generator); - $this->valueObjectVisitorDispatcher->setOutputVisitor($this); + if ($data instanceof Error) { + // Skip internal PHP errors serialization + throw $data; + } + + if (!is_object($data)) { + throw new Exceptions\InvalidTypeException($data); + } + + $visitor = $this->valueObjectVisitorResolver->resolveValueObjectVisitor($data); + if (!$visitor instanceof ValueObjectVisitor) { + throw new Exceptions\NoVisitorFoundException([$data::class]); + } - return $this->valueObjectVisitorDispatcher->visit($data); + $visitor->visit($this, $this->generator, $data); } /** diff --git a/src/lib/Output/Normalizer/TestData.php b/src/lib/Output/Normalizer/TestData.php index 35e3c6600..49ee2bd07 100644 --- a/src/lib/Output/Normalizer/TestData.php +++ b/src/lib/Output/Normalizer/TestData.php @@ -1,11 +1,10 @@ Date: Fri, 23 Aug 2024 13:40:00 +0200 Subject: [PATCH 04/39] Rollback --- src/lib/Output/Generator/Json.php | 10 +++------- src/lib/Output/Generator/Xml.php | 11 ++--------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index e260599ac..937fa952a 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -8,7 +8,6 @@ namespace Ibexa\Rest\Output\Generator; use Ibexa\Contracts\Rest\Output\Generator; -use Symfony\Component\Serializer\Encoder\EncoderInterface; /** * Json generator. @@ -47,12 +46,9 @@ class Json extends Generator * @param \Ibexa\Rest\Output\Generator\Json\FieldTypeHashGenerator $fieldTypeHashGenerator * @param string $vendor */ - public function __construct( - Json\FieldTypeHashGenerator $fieldTypeHashGenerator, - protected EncoderInterface $encoder, - $vendor = 'vnd.ibexa.api', - ) { - $this->fieldTypeHashGenerator = $fieldTypeHashGenerator; + public function __construct(Json\FieldTypeHashGenerator $hashGenerator, $vendor = 'vnd.ibexa.api') + { + $this->fieldTypeHashGenerator = $hashGenerator; $this->vendor = $vendor; } diff --git a/src/lib/Output/Generator/Xml.php b/src/lib/Output/Generator/Xml.php index 5c5e22902..04aa742a4 100644 --- a/src/lib/Output/Generator/Xml.php +++ b/src/lib/Output/Generator/Xml.php @@ -8,7 +8,6 @@ namespace Ibexa\Rest\Output\Generator; use Ibexa\Contracts\Rest\Output\Generator; -use Symfony\Component\Serializer\Encoder\EncoderInterface; /** * Xml generator. @@ -43,19 +42,13 @@ class Xml extends Generator */ protected $vendor; - protected EncoderInterface $encoder; - /** * @param \Ibexa\Rest\Output\Generator\Xml\FieldTypeHashGenerator $hashGenerator * @param string $vendor */ - public function __construct( - Xml\FieldTypeHashGenerator $hashGenerator, - EncoderInterface $encoder, - $vendor = 'vnd.ibexa.api', - ) { + public function __construct(Xml\FieldTypeHashGenerator $hashGenerator, $vendor = 'vnd.ibexa.api') + { $this->hashGenerator = $hashGenerator; - $this->encoder = $encoder; $this->vendor = $vendor; } From 1faaf7dc33b82d55cea37bb5ece3d4dc114d9255 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Fri, 23 Aug 2024 13:40:37 +0200 Subject: [PATCH 05/39] Rollback --- src/lib/Output/Generator/Json.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index 937fa952a..112b60858 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -46,9 +46,9 @@ class Json extends Generator * @param \Ibexa\Rest\Output\Generator\Json\FieldTypeHashGenerator $fieldTypeHashGenerator * @param string $vendor */ - public function __construct(Json\FieldTypeHashGenerator $hashGenerator, $vendor = 'vnd.ibexa.api') + public function __construct(Json\FieldTypeHashGenerator $fieldTypeHashGenerator, $vendor = 'vnd.ibexa.api') { - $this->fieldTypeHashGenerator = $hashGenerator; + $this->fieldTypeHashGenerator = $fieldTypeHashGenerator; $this->vendor = $vendor; } From d59508e3fb19caf8d85b75dc85abe7d9755074d1 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Fri, 23 Aug 2024 13:43:19 +0200 Subject: [PATCH 06/39] Rollback --- src/bundle/Resources/config/services.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index 607471b2b..9becbc333 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -342,8 +342,7 @@ services: # format output generators Ibexa\Rest\Output\Generator\Xml: arguments: - $hashGenerator: '@Ibexa\Rest\Output\Generator\Xml\FieldTypeHashGenerator' - $encoder: '@ibexa.rest.serializer.encoder.xml' + - '@Ibexa\Rest\Output\Generator\Xml\FieldTypeHashGenerator' calls: - [ setFormatOutput, [ "%kernel.debug%" ] ] @@ -357,8 +356,7 @@ services: Ibexa\Rest\Output\Generator\Json: arguments: - $fieldTypeHashGenerator: '@Ibexa\Rest\Output\Generator\Json\FieldTypeHashGenerator' - $encoder: '@ibexa.rest.serializer.encoder.json' + - '@Ibexa\Rest\Output\Generator\Json\FieldTypeHashGenerator' calls: - [ setFormatOutput, [ "%kernel.debug%" ] ] From 2e731e3862919ccb64a5cc3866fb9d41608816da Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Fri, 23 Aug 2024 13:52:02 +0200 Subject: [PATCH 07/39] Fixup --- src/contracts/Output/AdapterNormalizer.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/contracts/Output/AdapterNormalizer.php b/src/contracts/Output/AdapterNormalizer.php index 9f03aba2f..105a01a52 100644 --- a/src/contracts/Output/AdapterNormalizer.php +++ b/src/contracts/Output/AdapterNormalizer.php @@ -89,10 +89,7 @@ private function createVisitor(): Visitor { $fieldTypeHashGenerator = new Json\FieldTypeHashGenerator($this->normalizer); - $generator = new Json( - $fieldTypeHashGenerator, - $this->encoder, - ); + $generator = new Json($fieldTypeHashGenerator); return new Visitor( $generator, From b56999e9ab703ed7d6f36299cddf3a535198e160 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Tue, 27 Aug 2024 15:29:51 +0200 Subject: [PATCH 08/39] Applied review remarks --- src/bundle/Resources/config/services.yml | 2 +- .../Output/ValueObjectVisitorResolver.php | 2 +- src/contracts/Output/Visitor.php | 15 +-------------- ...ormalizer.php => VisitorAdapterNormalizer.php} | 13 ++++++++++++- 4 files changed, 15 insertions(+), 17 deletions(-) rename src/contracts/Output/{AdapterNormalizer.php => VisitorAdapterNormalizer.php} (85%) diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index 9becbc333..4d6854c29 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -417,7 +417,7 @@ services: tags: - ibexa.rest.serializer.encoder - Ibexa\Contracts\Rest\Output\AdapterNormalizer: + Ibexa\Contracts\Rest\Output\VisitorAdapterNormalizer: arguments: $encoder: '@ibexa.rest.serializer.encoder.json' $valueObjectVisitorResolver: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver' diff --git a/src/contracts/Output/ValueObjectVisitorResolver.php b/src/contracts/Output/ValueObjectVisitorResolver.php index 7b6979f9b..507350562 100644 --- a/src/contracts/Output/ValueObjectVisitorResolver.php +++ b/src/contracts/Output/ValueObjectVisitorResolver.php @@ -18,7 +18,7 @@ final class ValueObjectVisitorResolver implements ValueObjectVisitorResolverInte /** * @param class-string $visitedClassName */ - public function addVisitor(string $visitedClassName, $visitor): void + public function addVisitor(string $visitedClassName, ValueObjectVisitor $visitor): void { $this->visitors[$visitedClassName] = $visitor; } diff --git a/src/contracts/Output/Visitor.php b/src/contracts/Output/Visitor.php index db1a3c459..81cad3207 100644 --- a/src/contracts/Output/Visitor.php +++ b/src/contracts/Output/Visitor.php @@ -9,9 +9,6 @@ namespace Ibexa\Contracts\Rest\Output; use Error; -use Ibexa\Contracts\Core\Repository\LocationService; -use Ibexa\Rest\Output\Normalizer\TestData; -use Ibexa\Rest\Server\Values\RestLocation; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Serializer\Encoder\EncoderInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; @@ -36,7 +33,6 @@ public function __construct( private readonly NormalizerInterface $normalizer, private readonly EncoderInterface $encoder, private readonly ValueObjectVisitorResolverInterface $valueObjectVisitorResolver, - private readonly ?LocationService $locationService = null, //TODO to remove ) { $this->response = new Response('', 200); } @@ -74,18 +70,9 @@ public function setStatus($statusCode) /** * Visit struct returned by controllers. - * - * @return \Symfony\Component\HttpFoundation\Response */ - public function visit(mixed $data) + public function visit(mixed $data): Response { - //TODO to remove - $data = new TestData(); - $data->setName('test test'); - $location = $this->locationService->loadLocation(2); - $location = new RestLocation($location, 2); - $data->setLocation($location); - $normalizedData = $this->normalizer->normalize($data); //@todo Needs refactoring! diff --git a/src/contracts/Output/AdapterNormalizer.php b/src/contracts/Output/VisitorAdapterNormalizer.php similarity index 85% rename from src/contracts/Output/AdapterNormalizer.php rename to src/contracts/Output/VisitorAdapterNormalizer.php index 105a01a52..99cfd7d4c 100644 --- a/src/contracts/Output/AdapterNormalizer.php +++ b/src/contracts/Output/VisitorAdapterNormalizer.php @@ -9,13 +9,14 @@ namespace Ibexa\Contracts\Rest\Output; use Ibexa\Rest\Output\Generator\Json; +use LogicException; use Symfony\Component\Serializer\Encoder\EncoderInterface; use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -final class AdapterNormalizer implements NormalizerInterface, NormalizerAwareInterface, ContextAwareNormalizerInterface +final class VisitorAdapterNormalizer implements NormalizerInterface, NormalizerAwareInterface, ContextAwareNormalizerInterface { use NormalizerAwareTrait; @@ -60,6 +61,16 @@ public function supportsNormalization(mixed $data, ?string $format = null, array return true; } + if (!$this->normalizer instanceof ContextAwareNormalizerInterface) { + throw new LogicException( + sprintf( + 'Normalizer "%s" must be an instance of "%s".', + $this->normalizer::class, + ContextAwareNormalizerInterface::class, + ) + ); + } + return $this->normalizer->supportsNormalization( $data, null, From a6cb0188e5d26a45151714a4775d973b0b33f675 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Tue, 27 Aug 2024 15:47:19 +0200 Subject: [PATCH 09/39] Code cleanup --- .../ValueObjectVisitorResolverPass.php | 2 +- src/lib/Output/Generator/Json.php | 2 +- src/lib/Output/Normalizer/TestData.php | 38 ----- src/lib/Output/Normalizer/TestNormalizer.php | 38 ----- ...=> ValueObjectVisitorResolverPassTest.php} | 14 +- .../ValueObjectVisitorDispatcherTest.php | 157 ------------------ tests/lib/Output/VisitorTest.php | 124 ++++---------- 7 files changed, 44 insertions(+), 331 deletions(-) delete mode 100644 src/lib/Output/Normalizer/TestData.php delete mode 100644 src/lib/Output/Normalizer/TestNormalizer.php rename tests/bundle/DependencyInjection/Compiler/{ValueObjectVisitorPassTest.php => ValueObjectVisitorResolverPassTest.php} (81%) delete mode 100644 tests/lib/Output/ValueObjectVisitorDispatcherTest.php diff --git a/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPass.php b/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPass.php index f32e21a75..8c875c3d1 100644 --- a/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPass.php +++ b/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPass.php @@ -16,7 +16,7 @@ * Compiler pass for the ibexa.rest.output.value_object.visitor tag. * Maps a fully qualified class to a value object visitor. */ -class ValueObjectVisitorResolverPass implements CompilerPassInterface +final class ValueObjectVisitorResolverPass implements CompilerPassInterface { public const string OUTPUT_VALUE_OBJECT_VISITOR_SERVICE_TAG = 'ibexa.rest.output.value_object.visitor'; diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index 112b60858..6f32f8a79 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -327,6 +327,6 @@ public function serializeBool($boolValue) public function toArray(): array { - return json_decode(json_encode($this->json), true); + return json_decode(json_encode($this->json, JSON_THROW_ON_ERROR), true, JSON_THROW_ON_ERROR); } } diff --git a/src/lib/Output/Normalizer/TestData.php b/src/lib/Output/Normalizer/TestData.php deleted file mode 100644 index 49ee2bd07..000000000 --- a/src/lib/Output/Normalizer/TestData.php +++ /dev/null @@ -1,38 +0,0 @@ -name; - } - - public function setName(string $name): void - { - $this->name = $name; - } - - public function getLocation(): RestLocation - { - return $this->location; - } - - public function setLocation(RestLocation $location): void - { - $this->location = $location; - } -} diff --git a/src/lib/Output/Normalizer/TestNormalizer.php b/src/lib/Output/Normalizer/TestNormalizer.php deleted file mode 100644 index 169abec7b..000000000 --- a/src/lib/Output/Normalizer/TestNormalizer.php +++ /dev/null @@ -1,38 +0,0 @@ - $object->getName(), - 'Location' => $this->normalizer->normalize($object->getLocation())['Location'], - ]; - - return $result; - } - - public function supportsNormalization(mixed $data, string $format = null): bool - { - return $data instanceof TestData; - } -} diff --git a/tests/bundle/DependencyInjection/Compiler/ValueObjectVisitorPassTest.php b/tests/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPassTest.php similarity index 81% rename from tests/bundle/DependencyInjection/Compiler/ValueObjectVisitorPassTest.php rename to tests/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPassTest.php index ca5f09e26..5d037e0ce 100644 --- a/tests/bundle/DependencyInjection/Compiler/ValueObjectVisitorPassTest.php +++ b/tests/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPassTest.php @@ -7,16 +7,16 @@ namespace Ibexa\Tests\Bundle\Rest\DependencyInjection\Compiler; -use Ibexa\Bundle\Rest\DependencyInjection\Compiler\ValueObjectVisitorPass; -use Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher; +use Ibexa\Bundle\Rest\DependencyInjection\Compiler\ValueObjectVisitorResolverPass; +use Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; -class ValueObjectVisitorPassTest extends TestCase +final class ValueObjectVisitorResolverPassTest extends TestCase { - public function testProcess() + public function testProcess(): void { $visitorDefinition = new Definition(); $visitorDefinition->addTag('ibexa.rest.output.value_object.visitor', ['type' => 'test']); @@ -24,16 +24,16 @@ public function testProcess() $containerBuilder = new ContainerBuilder(); $containerBuilder->addDefinitions( [ - ValueObjectVisitorDispatcher::class => new Definition(), + ValueObjectVisitorResolver::class => new Definition(), 'ezpublish_rest.output.value_object_visitor.test' => $visitorDefinition, ] ); - $compilerPass = new ValueObjectVisitorPass(); + $compilerPass = new ValueObjectVisitorResolverPass(); $compilerPass->process($containerBuilder); $dispatcherMethodCalls = $containerBuilder - ->getDefinition(ValueObjectVisitorDispatcher::class) + ->getDefinition(ValueObjectVisitorResolver::class) ->getMethodCalls(); self::assertTrue(isset($dispatcherMethodCalls[0][0]), 'Failed asserting that dispatcher has a method call'); self::assertEquals('addVisitor', $dispatcherMethodCalls[0][0], "Failed asserting that called method is 'addVisitor'"); diff --git a/tests/lib/Output/ValueObjectVisitorDispatcherTest.php b/tests/lib/Output/ValueObjectVisitorDispatcherTest.php deleted file mode 100644 index 3616077e4..000000000 --- a/tests/lib/Output/ValueObjectVisitorDispatcherTest.php +++ /dev/null @@ -1,157 +0,0 @@ -getValueObjectVisitorMock(); - $visitor - ->expects(self::at(0)) - ->method('visit') - ->with($this->getOutputVisitorMock(), $this->getOutputGeneratorMock(), $data); - - $valueObjectDispatcher = $this->getValueObjectDispatcher(); - $valueObjectDispatcher->addVisitor('stdClass', $visitor); - - $valueObjectDispatcher->visit($data); - } - - public function testVisitValueObjectInvalidType() - { - $this->expectException(InvalidTypeException::class); - - $this->getValueObjectDispatcher()->visit(42); - } - - public function testVisitValueObjectNoMatch() - { - $this->expectException(NoVisitorFoundException::class); - - $dispatcher = $this->getValueObjectDispatcher(); - - $dispatcher->visit(new stdClass()); - } - - public function testVisitValueObjectParentMatch() - { - $data = new ValueObject(); - - $valueObjectVisitor = $this->getValueObjectVisitorMock(); - $valueObjectVisitor - ->expects(self::at(0)) - ->method('visit') - ->with($this->getOutputVisitorMock(), $this->getOutputGeneratorMock(), $data); - - $dispatcher = $this->getValueObjectDispatcher(); - $dispatcher->addVisitor('stdClass', $valueObjectVisitor); - - $dispatcher->visit($data); - } - - public function testVisitValueObjectSecondRuleParentMatch() - { - $data = new ValueObject(); - - $valueObjectVisitor1 = $this->getValueObjectVisitorMock(); - $valueObjectVisitor2 = $this->getValueObjectVisitorMock(); - - $dispatcher = $this->getValueObjectDispatcher(); - $dispatcher->addVisitor('WontMatch', $valueObjectVisitor1); - $dispatcher->addVisitor('stdClass', $valueObjectVisitor2); - - $valueObjectVisitor1 - ->expects(self::never()) - ->method('visit'); - - $valueObjectVisitor2 - ->expects(self::at(0)) - ->method('visit') - ->with($this->getOutputVisitorMock(), $this->getOutputGeneratorMock(), $data); - - $dispatcher->visit($data); - } - - public function testVisitError(): void - { - $this->expectException(Error::class); - - $dispatcher = $this->getValueObjectDispatcher(); - $dispatcher->visit($this->createMock(Error::class)); - } - - /** - * @return \Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher - */ - private function getValueObjectDispatcher() - { - $dispatcher = new ValueObjectVisitorDispatcher(); - $dispatcher->setOutputGenerator($this->getOutputGeneratorMock()); - $dispatcher->setOutputVisitor($this->getOutputVisitorMock()); - - return $dispatcher; - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Rest\Output\ValueObjectVisitor - */ - private function getValueObjectVisitorMock() - { - return $this->getMockForAbstractClass(ValueObjectVisitor::class); - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Rest\Output\Visitor - */ - private function getOutputVisitorMock() - { - if (!isset($this->outputVisitorMock)) { - $this->outputVisitorMock = $this->createMock(Visitor::class); - } - - return $this->outputVisitorMock; - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Rest\Output\Generator - */ - private function getOutputGeneratorMock() - { - if (!isset($this->outputGeneratorMock)) { - $this->outputGeneratorMock = $this->createMock(Generator::class); - } - - return $this->outputGeneratorMock; - } -} diff --git a/tests/lib/Output/VisitorTest.php b/tests/lib/Output/VisitorTest.php index fea5c4178..a1b4c3f1f 100644 --- a/tests/lib/Output/VisitorTest.php +++ b/tests/lib/Output/VisitorTest.php @@ -4,101 +4,38 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); namespace Ibexa\Tests\Rest\Output; use Ibexa\Contracts\Rest\Output\Generator; -use Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher; +use Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolverInterface; use Ibexa\Contracts\Rest\Output\Visitor; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use stdClass; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Serializer\Encoder\EncoderInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -/** - * Visitor test. - */ -class VisitorTest extends TestCase +final class VisitorTest extends TestCase { - public function testVisitDocument() + public function testVisitDocument(): void { - $data = new stdClass(); - - $generator = $this->getGeneratorMock(); - $generator - ->expects(self::at(1)) - ->method('startDocument') - ->with($data); - - $generator - ->expects(self::at(2)) - ->method('isEmpty') - ->willReturn(false); - - $generator - ->expects(self::at(3)) - ->method('endDocument') - ->with($data) - ->willReturn('Hello world!'); - - $visitor = $this->getMockBuilder(Visitor::class) - ->setMethods(['visitValueObject']) - ->setConstructorArgs([$generator, $this->getValueObjectDispatcherMock()]) - ->getMock(); - - self::assertEquals( - new Response('Hello world!', Response::HTTP_OK, []), - $visitor->visit($data) - ); + //TODO refactor } - public function testVisitEmptyDocument() + public function testVisitEmptyDocument(): void { - $data = new stdClass(); - - $generator = $this->getGeneratorMock(); - $generator - ->expects(self::at(1)) - ->method('startDocument') - ->with($data); - - $generator - ->expects(self::at(2)) - ->method('isEmpty') - ->willReturn(true); - - $generator - ->expects(self::never()) - ->method('endDocument'); - - $visitor = $this->getMockBuilder(Visitor::class) - ->setMethods(['visitValueObject']) - ->setConstructorArgs([$generator, $this->getValueObjectDispatcherMock()]) - ->getMock(); - - self::assertEquals( - new Response(null, Response::HTTP_OK, []), - $visitor->visit($data) - ); + //TODO refactor } - public function testVisitValueObject() + public function testVisitValueObject(): void { - $data = new stdClass(); - - /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Rest\Output\Generator $generatorMock */ - $generatorMock = $this->getGeneratorMock(); - - $valueObjectDispatcherMock = $this->getValueObjectDispatcherMock(); - $valueObjectDispatcherMock - ->expects(self::once()) - ->method('visit') - ->with($data); - - $visitor = new Visitor($generatorMock, $valueObjectDispatcherMock); - $visitor->visit($data); + //TODO refactor } - public function testSetHeaders() + public function testSetHeaders(): void { $data = new stdClass(); @@ -142,7 +79,7 @@ public function testSetFilteredHeaders() ); } - public function testSetHeadersNoOverwrite() + public function testSetHeadersNoOverwrite(): void { $data = new stdClass(); @@ -162,7 +99,7 @@ public function testSetHeadersNoOverwrite() ); } - public function testSetHeaderResetAfterVisit() + public function testSetHeaderResetAfterVisit(): void { $data = new stdClass(); @@ -183,7 +120,7 @@ public function testSetHeaderResetAfterVisit() ); } - public function testSetStatusCode() + public function testSetStatusCode(): void { $data = new stdClass(); @@ -199,7 +136,7 @@ public function testSetStatusCode() ); } - public function testSetStatusCodeNoOverride() + public function testSetStatusCodeNoOverride(): void { $data = new stdClass(); @@ -217,28 +154,37 @@ public function testSetStatusCodeNoOverride() ); } - /** - * @return \Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher|\PHPUnit\Framework\MockObject\MockObject - */ - public function getValueObjectDispatcherMock() + public function getValueObjectVisitorResolverMock(): ValueObjectVisitorResolverInterface&MockObject { - return $this->createMock(ValueObjectVisitorDispatcher::class); + return $this->createMock(ValueObjectVisitorResolverInterface::class); } - protected function getGeneratorMock() + protected function getGeneratorMock(): Generator&MockObject { return $this->createMock(Generator::class); } - protected function getVisitorMock() + protected function getNormalizerMock(): NormalizerInterface&MockObject + { + return $this->createMock(NormalizerInterface::class); + } + + protected function getEncoderMock(): EncoderInterface&MockObject + { + return $this->createMock(EncoderInterface::class); + } + + protected function getVisitorMock(): Visitor&MockObject { return $this->getMockBuilder(Visitor::class) ->setMethods(['visitValueObject']) ->setConstructorArgs( [ $this->getGeneratorMock(), - $this->getValueObjectDispatcherMock(), - ] + $this->getNormalizerMock(), + $this->getEncoderMock(), + $this->getValueObjectVisitorResolverMock(), + ], ) ->getMock(); } From 6bc9690762473047e14815c05a72cd904db3ae39 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Tue, 27 Aug 2024 15:49:05 +0200 Subject: [PATCH 10/39] Code cleanup --- src/bundle/Resources/config/services.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index 4d6854c29..40341b03f 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -324,7 +324,6 @@ services: $normalizer: '@ibexa.rest.serializer' $encoder: '@ibexa.rest.serializer.encoder.json' $valueObjectVisitorResolver: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver' - $locationService: '@ibexa.api.service.location' tags: - { name: ibexa.rest.output.visitor, regexps: ibexa.rest.output.visitor.json.regexps } @@ -335,7 +334,6 @@ services: $normalizer: '@ibexa.rest.serializer' $encoder: '@ibexa.rest.serializer.encoder.xml' $valueObjectVisitorResolver: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver' - $locationService: '@ibexa.api.service.location' tags: - { name: ibexa.rest.output.visitor, regexps: ibexa.rest.output.visitor.xml.regexps } From 97dc64bb7d94b872e47764cbf8fb2c32327e0b1f Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Tue, 27 Aug 2024 16:48:48 +0200 Subject: [PATCH 11/39] Code cleanup --- src/bundle/Resources/config/services.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index 40341b03f..b82b7c3a1 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -405,6 +405,11 @@ services: Ibexa\Contracts\Rest\Input\MediaTypeParserInterface: '@Ibexa\Contracts\Rest\Input\MediaTypeParser' + Ibexa\Contracts\Rest\Input\Parser\Query\Criterion\BaseCriterionProcessor: + abstract: true + arguments: + $parsingDispatcher: '@Ibexa\Contracts\Rest\Input\ParsingDispatcher' + ibexa.rest.serializer.encoder.json: class: Symfony\Component\Serializer\Encoder\JsonEncoder tags: @@ -426,11 +431,6 @@ services: tags: - ibexa.rest.serializer.normalizer - Ibexa\Contracts\Rest\Input\Parser\Query\Criterion\BaseCriterionProcessor: - abstract: true - arguments: - $parsingDispatcher: '@Ibexa\Contracts\Rest\Input\ParsingDispatcher' - Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolverInterface: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitor' Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver: ~ From 9659e6aa46d6932d7cd22c737e9ff0825af1470d Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Tue, 27 Aug 2024 17:35:55 +0200 Subject: [PATCH 12/39] Fixup --- src/bundle/Resources/config/services.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index b82b7c3a1..59b6630d6 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -427,10 +427,6 @@ services: tags: - { name: ibexa.rest.serializer.normalizer, priority: -1000 } - Ibexa\Rest\Output\Normalizer\TestNormalizer: - tags: - - ibexa.rest.serializer.normalizer - Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolverInterface: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitor' Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver: ~ From 8697942367a86bc2f6eeb37c63a38e92f47721b0 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Tue, 27 Aug 2024 17:43:19 +0200 Subject: [PATCH 13/39] Change login code to 200 --- tests/bundle/Functional/TestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bundle/Functional/TestCase.php b/tests/bundle/Functional/TestCase.php index ce0b09185..f56aeb7cc 100644 --- a/tests/bundle/Functional/TestCase.php +++ b/tests/bundle/Functional/TestCase.php @@ -399,7 +399,7 @@ protected function login(): \stdClass { $request = $this->createAuthenticationHttpRequest($this->getLoginUsername(), $this->getLoginPassword()); $response = $this->sendHttpRequest($request); - self::assertHttpResponseCodeEquals($response, 201); + self::assertHttpResponseCodeEquals($response, 200); //TODO is 200 correct here? return json_decode($response->getBody()->getContents(), false, JSON_THROW_ON_ERROR)->Session; } From d182d858cb148efe5d4ef5979357f0a3dffc3429 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Wed, 28 Aug 2024 11:54:02 +0200 Subject: [PATCH 14/39] Set proper encoding format --- src/bundle/Resources/config/services.yml | 2 ++ src/contracts/Output/Visitor.php | 3 ++- src/contracts/Output/VisitorAdapterNormalizer.php | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index 59b6630d6..20f989e09 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -324,6 +324,7 @@ services: $normalizer: '@ibexa.rest.serializer' $encoder: '@ibexa.rest.serializer.encoder.json' $valueObjectVisitorResolver: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver' + $format: 'json' tags: - { name: ibexa.rest.output.visitor, regexps: ibexa.rest.output.visitor.json.regexps } @@ -334,6 +335,7 @@ services: $normalizer: '@ibexa.rest.serializer' $encoder: '@ibexa.rest.serializer.encoder.xml' $valueObjectVisitorResolver: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver' + $format: 'xml' tags: - { name: ibexa.rest.output.visitor, regexps: ibexa.rest.output.visitor.xml.regexps } diff --git a/src/contracts/Output/Visitor.php b/src/contracts/Output/Visitor.php index 81cad3207..157364ca0 100644 --- a/src/contracts/Output/Visitor.php +++ b/src/contracts/Output/Visitor.php @@ -33,6 +33,7 @@ public function __construct( private readonly NormalizerInterface $normalizer, private readonly EncoderInterface $encoder, private readonly ValueObjectVisitorResolverInterface $valueObjectVisitorResolver, + private readonly string $format, ) { $this->response = new Response('', 200); } @@ -88,7 +89,7 @@ public function visit(mixed $data): Response $response = clone $this->response; - $response->setContent($this->encoder->encode($normalizedData, 'json')); + $response->setContent($this->encoder->encode($normalizedData, $this->format)); // reset the inner response $this->response = new Response(null, Response::HTTP_OK); diff --git a/src/contracts/Output/VisitorAdapterNormalizer.php b/src/contracts/Output/VisitorAdapterNormalizer.php index 99cfd7d4c..e4aad5d22 100644 --- a/src/contracts/Output/VisitorAdapterNormalizer.php +++ b/src/contracts/Output/VisitorAdapterNormalizer.php @@ -107,6 +107,7 @@ private function createVisitor(): Visitor $this->normalizer, $this->encoder, $this->valueObjectVisitorResolver, + 'json', ); } } From ae726acb44d9efc8065a99c0e6e66ea2712d9d85 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Wed, 4 Sep 2024 16:26:03 +0200 Subject: [PATCH 15/39] Handle XML --- src/contracts/Output/Generator.php | 14 +++++ src/contracts/Output/Visitor.php | 6 ++- .../Output/VisitorAdapterNormalizer.php | 27 ++++++---- src/lib/Output/Generator/InMemory/Xml.php | 51 +++++++++++++++++++ src/lib/Output/Generator/Json.php | 10 ++++ src/lib/Output/Generator/Xml.php | 10 ++++ 6 files changed, 107 insertions(+), 11 deletions(-) create mode 100644 src/lib/Output/Generator/InMemory/Xml.php diff --git a/src/contracts/Output/Generator.php b/src/contracts/Output/Generator.php index e1fc2b4ae..d297b0ed1 100644 --- a/src/contracts/Output/Generator.php +++ b/src/contracts/Output/Generator.php @@ -436,4 +436,18 @@ abstract public function serializeBool($boolValue); * @return array */ abstract public function toArray(): array; + + /** + * @param array $data + * + * @return array + */ + abstract public function getEncoderContext(array $data): array; + + /** + * @param array $normalizedData + * + * @return array + */ + abstract public function transformData(array $normalizedData): array; } diff --git a/src/contracts/Output/Visitor.php b/src/contracts/Output/Visitor.php index 157364ca0..f158db06f 100644 --- a/src/contracts/Output/Visitor.php +++ b/src/contracts/Output/Visitor.php @@ -74,7 +74,7 @@ public function setStatus($statusCode) */ public function visit(mixed $data): Response { - $normalizedData = $this->normalizer->normalize($data); + [$normalizedData, $encoderContext] = $this->normalizer->normalize($data, $this->format); //@todo Needs refactoring! // A hackish solution to enable outer visitors to disable setting @@ -89,7 +89,9 @@ public function visit(mixed $data): Response $response = clone $this->response; - $response->setContent($this->encoder->encode($normalizedData, $this->format)); + $content = $this->encoder->encode($normalizedData, $this->format, $encoderContext); + + $response->setContent($content); // reset the inner response $this->response = new Response(null, Response::HTTP_OK); diff --git a/src/contracts/Output/VisitorAdapterNormalizer.php b/src/contracts/Output/VisitorAdapterNormalizer.php index e4aad5d22..176f7855e 100644 --- a/src/contracts/Output/VisitorAdapterNormalizer.php +++ b/src/contracts/Output/VisitorAdapterNormalizer.php @@ -8,6 +8,7 @@ namespace Ibexa\Contracts\Rest\Output; +use Ibexa\Rest\Output\Generator\InMemory\Xml as InMemoryXml; use Ibexa\Rest\Output\Generator\Json; use LogicException; use Symfony\Component\Serializer\Encoder\EncoderInterface; @@ -38,7 +39,7 @@ public function normalize(mixed $object, ?string $format = null, array $context : null; if ($eligibleVisitor instanceof ValueObjectVisitor) { - return $this->visitValueObject($object, $eligibleVisitor); + return $this->visitValueObject($object, $eligibleVisitor, $format); } return $this->normalizer->normalize($object, $format, $context); @@ -79,11 +80,14 @@ public function supportsNormalization(mixed $data, ?string $format = null, array } /** - * @return array + * @return array, array> */ - private function visitValueObject(object $object, ValueObjectVisitor $valueObjectVisitor): array - { - $visitor = $this->createVisitor(); + private function visitValueObject( + object $object, + ValueObjectVisitor $valueObjectVisitor, + string $format + ): array { + $visitor = $this->createVisitor($format); $generator = $visitor->getGenerator(); $generator->reset(); @@ -93,21 +97,26 @@ private function visitValueObject(object $object, ValueObjectVisitor $valueObjec $generator->endDocument($object); - return $generator->toArray(); + $normalizedData = $generator->toArray(); + $encoderContext = $generator->getEncoderContext($normalizedData); + + return [$generator->transformData($normalizedData), $encoderContext]; } - private function createVisitor(): Visitor + private function createVisitor(string $format): Visitor { $fieldTypeHashGenerator = new Json\FieldTypeHashGenerator($this->normalizer); - $generator = new Json($fieldTypeHashGenerator); + $generator = $format === 'xml' + ? new InMemoryXml($fieldTypeHashGenerator) + : new Json($fieldTypeHashGenerator); return new Visitor( $generator, $this->normalizer, $this->encoder, $this->valueObjectVisitorResolver, - 'json', + $format, ); } } diff --git a/src/lib/Output/Generator/InMemory/Xml.php b/src/lib/Output/Generator/InMemory/Xml.php new file mode 100644 index 000000000..d2f0e0121 --- /dev/null +++ b/src/lib/Output/Generator/InMemory/Xml.php @@ -0,0 +1,51 @@ +generateMediaTypeWithVendor($name, 'xml', $this->vendor); + } + + /** + * @param string $name + * @param string $value + */ + public function startAttribute($name, $value): void + { + $this->checkStartAttribute($name); + + $this->json->{'@' . $name} = $value; + } + + public function transformData(array $normalizedData): array + { + $topNodeName = array_key_first($normalizedData); + $data = array_filter( + $normalizedData[$topNodeName], + static fn (string $key): bool => str_starts_with($key, '@'), + ARRAY_FILTER_USE_KEY, + ); + $data['#'] = $normalizedData[$topNodeName]; + + return $data; + } + + public function getEncoderContext(array $data): array + { + return [ + XmlEncoder::ROOT_NODE_NAME => array_key_first($data), + ]; + } +} diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index 6f32f8a79..5aad23ced 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -329,4 +329,14 @@ public function toArray(): array { return json_decode(json_encode($this->json, JSON_THROW_ON_ERROR), true, JSON_THROW_ON_ERROR); } + + public function getEncoderContext(array $data): array + { + return []; + } + + public function transformData(array $normalizedData): array + { + return $normalizedData; + } } diff --git a/src/lib/Output/Generator/Xml.php b/src/lib/Output/Generator/Xml.php index 04aa742a4..a5bda2cdd 100644 --- a/src/lib/Output/Generator/Xml.php +++ b/src/lib/Output/Generator/Xml.php @@ -269,4 +269,14 @@ public function toArray(): array { return []; } + + public function getEncoderContext(array $data): array + { + return []; + } + + public function transformData(array $normalizedData): array + { + return $normalizedData; + } } From be01db621355578bd598a8b7fbbdea2217068ac1 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Wed, 4 Sep 2024 16:45:14 +0200 Subject: [PATCH 16/39] Rollback --- tests/bundle/Functional/TestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bundle/Functional/TestCase.php b/tests/bundle/Functional/TestCase.php index f56aeb7cc..ce0b09185 100644 --- a/tests/bundle/Functional/TestCase.php +++ b/tests/bundle/Functional/TestCase.php @@ -399,7 +399,7 @@ protected function login(): \stdClass { $request = $this->createAuthenticationHttpRequest($this->getLoginUsername(), $this->getLoginPassword()); $response = $this->sendHttpRequest($request); - self::assertHttpResponseCodeEquals($response, 200); //TODO is 200 correct here? + self::assertHttpResponseCodeEquals($response, 201); return json_decode($response->getBody()->getContents(), false, JSON_THROW_ON_ERROR)->Session; } From a373e56794579bb284510b43d41a819351cf57f9 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Wed, 4 Sep 2024 16:56:34 +0200 Subject: [PATCH 17/39] Rollback --- tests/bundle/Functional/TestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bundle/Functional/TestCase.php b/tests/bundle/Functional/TestCase.php index ce0b09185..f09c038e3 100644 --- a/tests/bundle/Functional/TestCase.php +++ b/tests/bundle/Functional/TestCase.php @@ -399,7 +399,7 @@ protected function login(): \stdClass { $request = $this->createAuthenticationHttpRequest($this->getLoginUsername(), $this->getLoginPassword()); $response = $this->sendHttpRequest($request); - self::assertHttpResponseCodeEquals($response, 201); + self::assertHttpResponseCodeEquals($response, 200); return json_decode($response->getBody()->getContents(), false, JSON_THROW_ON_ERROR)->Session; } From f3b23cc31b698596bee55915e976dc7c17920724 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Wed, 4 Sep 2024 17:57:29 +0200 Subject: [PATCH 18/39] Use dispatched visitor instead of creating a new one --- src/contracts/Output/Visitor.php | 7 ++++++- .../Output/VisitorAdapterNormalizer.php | 19 +++++++++++++------ tests/bundle/Functional/TestCase.php | 2 +- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/contracts/Output/Visitor.php b/src/contracts/Output/Visitor.php index f158db06f..3e2d9f582 100644 --- a/src/contracts/Output/Visitor.php +++ b/src/contracts/Output/Visitor.php @@ -74,7 +74,7 @@ public function setStatus($statusCode) */ public function visit(mixed $data): Response { - [$normalizedData, $encoderContext] = $this->normalizer->normalize($data, $this->format); + [$normalizedData, $encoderContext] = $this->normalizer->normalize($data, $this->format, ['visitor' => $this]); //@todo Needs refactoring! // A hackish solution to enable outer visitors to disable setting @@ -145,4 +145,9 @@ public function getGenerator(): Generator { return $this->generator; } + + public function setGenerator(Generator $generator): void + { + $this->generator = $generator; + } } diff --git a/src/contracts/Output/VisitorAdapterNormalizer.php b/src/contracts/Output/VisitorAdapterNormalizer.php index 176f7855e..99a10591e 100644 --- a/src/contracts/Output/VisitorAdapterNormalizer.php +++ b/src/contracts/Output/VisitorAdapterNormalizer.php @@ -39,7 +39,7 @@ public function normalize(mixed $object, ?string $format = null, array $context : null; if ($eligibleVisitor instanceof ValueObjectVisitor) { - return $this->visitValueObject($object, $eligibleVisitor, $format); + return $this->visitValueObject($object, $eligibleVisitor, $format, $context); } return $this->normalizer->normalize($object, $format, $context); @@ -80,15 +80,19 @@ public function supportsNormalization(mixed $data, ?string $format = null, array } /** + * @param array $context + * * @return array, array> */ private function visitValueObject( object $object, ValueObjectVisitor $valueObjectVisitor, - string $format + string $format, + array $context, ): array { - $visitor = $this->createVisitor($format); - $generator = $visitor->getGenerator(); + $generator = $this->createGenerator($format); + + $visitor = $context['visitor'] ?? $this->createVisitor($format, $generator); $generator->reset(); $generator->startDocument($object); @@ -103,14 +107,17 @@ private function visitValueObject( return [$generator->transformData($normalizedData), $encoderContext]; } - private function createVisitor(string $format): Visitor + private function createGenerator(string $format): Generator { $fieldTypeHashGenerator = new Json\FieldTypeHashGenerator($this->normalizer); - $generator = $format === 'xml' + return $format === 'xml' ? new InMemoryXml($fieldTypeHashGenerator) : new Json($fieldTypeHashGenerator); + } + private function createVisitor(string $format, Generator $generator): Visitor + { return new Visitor( $generator, $this->normalizer, diff --git a/tests/bundle/Functional/TestCase.php b/tests/bundle/Functional/TestCase.php index f09c038e3..ce0b09185 100644 --- a/tests/bundle/Functional/TestCase.php +++ b/tests/bundle/Functional/TestCase.php @@ -399,7 +399,7 @@ protected function login(): \stdClass { $request = $this->createAuthenticationHttpRequest($this->getLoginUsername(), $this->getLoginPassword()); $response = $this->sendHttpRequest($request); - self::assertHttpResponseCodeEquals($response, 200); + self::assertHttpResponseCodeEquals($response, 201); return json_decode($response->getBody()->getContents(), false, JSON_THROW_ON_ERROR)->Session; } From a515652f10b4fc29b9c789f5c06c05e4030d1b9b Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Thu, 5 Sep 2024 09:55:36 +0200 Subject: [PATCH 19/39] Reuse already initialized visitor --- src/bundle/Resources/config/services.yml | 6 +++++- src/contracts/Output/VisitorAdapterNormalizer.php | 9 +++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index 20f989e09..fc64ec5ce 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -331,7 +331,7 @@ services: ibexa.rest.output.visitor.xml: class: Ibexa\Contracts\Rest\Output\Visitor arguments: - $generator: '@Ibexa\Rest\Output\Generator\Xml' + $generator: '@Ibexa\Rest\Output\Generator\InMemory\Xml' $normalizer: '@ibexa.rest.serializer' $encoder: '@ibexa.rest.serializer.encoder.xml' $valueObjectVisitorResolver: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver' @@ -346,6 +346,10 @@ services: calls: - [ setFormatOutput, [ "%kernel.debug%" ] ] + Ibexa\Rest\Output\Generator\InMemory\Xml: + arguments: + $fieldTypeHashGenerator: '@Ibexa\Rest\Output\Generator\Json\FieldTypeHashGenerator' + Ibexa\Rest\Output\Generator\Xml\FieldTypeHashGenerator: arguments: $normalizer: '@ibexa.rest.serializer' diff --git a/src/contracts/Output/VisitorAdapterNormalizer.php b/src/contracts/Output/VisitorAdapterNormalizer.php index 99a10591e..e834ca08e 100644 --- a/src/contracts/Output/VisitorAdapterNormalizer.php +++ b/src/contracts/Output/VisitorAdapterNormalizer.php @@ -90,9 +90,8 @@ private function visitValueObject( string $format, array $context, ): array { - $generator = $this->createGenerator($format); - - $visitor = $context['visitor'] ?? $this->createVisitor($format, $generator); + $visitor = $context['visitor'] ?? $this->createVisitor($format); + $generator = $visitor->getGenerator(); $generator->reset(); $generator->startDocument($object); @@ -116,8 +115,10 @@ private function createGenerator(string $format): Generator : new Json($fieldTypeHashGenerator); } - private function createVisitor(string $format, Generator $generator): Visitor + private function createVisitor(string $format): Visitor { + $generator = $this->createGenerator($format); + return new Visitor( $generator, $this->normalizer, From 20b0b1e404a43bccb11172c0696c34c57b4f251f Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Thu, 5 Sep 2024 10:19:43 +0200 Subject: [PATCH 20/39] Fixup --- src/lib/Output/Generator/InMemory/Xml.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/Output/Generator/InMemory/Xml.php b/src/lib/Output/Generator/InMemory/Xml.php index d2f0e0121..92f6c0c90 100644 --- a/src/lib/Output/Generator/InMemory/Xml.php +++ b/src/lib/Output/Generator/InMemory/Xml.php @@ -33,11 +33,14 @@ public function transformData(array $normalizedData): array { $topNodeName = array_key_first($normalizedData); $data = array_filter( - $normalizedData[$topNodeName], + $normalizedData[$topNodeName] ?? [], static fn (string $key): bool => str_starts_with($key, '@'), ARRAY_FILTER_USE_KEY, ); - $data['#'] = $normalizedData[$topNodeName]; + + if ($topNodeName !== null) { + $data['#'] = $normalizedData[$topNodeName]; + } return $data; } From ed2f8866d29802d95bfaccef073fafbf328fadc4 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Thu, 5 Sep 2024 12:35:59 +0200 Subject: [PATCH 21/39] Fix PHPStan issues --- phpstan-baseline.neon | 220 ------------------ src/contracts/Output/Visitor.php | 18 +- .../Output/VisitorAdapterNormalizer.php | 15 +- src/lib/Output/Generator/InMemory/Xml.php | 22 ++ 4 files changed, 41 insertions(+), 234 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 079fac3fa..98731c21c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -50,11 +50,6 @@ parameters: count: 1 path: src/bundle/DependencyInjection/Compiler/OutputVisitorPass.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Rest\\\\DependencyInjection\\\\Compiler\\\\ValueObjectVisitorPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/Compiler/ValueObjectVisitorPass.php - - message: "#^Method Ibexa\\\\Bundle\\\\Rest\\\\DependencyInjection\\\\Configuration\\:\\:addRestRootResourcesSection\\(\\) has parameter \\$rootNode with no type specified\\.$#" count: 1 @@ -405,26 +400,6 @@ parameters: count: 1 path: src/contracts/Output/ValueObjectVisitor.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\ValueObjectVisitorDispatcher\\:\\:addVisitor\\(\\) has no return type specified\\.$#" - count: 1 - path: src/contracts/Output/ValueObjectVisitorDispatcher.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\ValueObjectVisitorDispatcher\\:\\:setOutputGenerator\\(\\) has no return type specified\\.$#" - count: 1 - path: src/contracts/Output/ValueObjectVisitorDispatcher.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\ValueObjectVisitorDispatcher\\:\\:setOutputVisitor\\(\\) has no return type specified\\.$#" - count: 1 - path: src/contracts/Output/ValueObjectVisitorDispatcher.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) has no return type specified\\.$#" - count: 1 - path: src/contracts/Output/Visitor.php - - message: "#^Method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setStatus\\(\\) has no return type specified\\.$#" count: 1 @@ -440,16 +415,6 @@ parameters: count: 1 path: src/contracts/Output/Visitor.php - - - message: "#^Property Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:\\$statusCode \\(int\\) does not accept null\\.$#" - count: 1 - path: src/contracts/Output/Visitor.php - - - - message: "#^Property Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:\\$valueObjectVisitorDispatcher \\(Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\ValueObjectVisitorDispatcher\\) does not accept default value of type array\\.$#" - count: 1 - path: src/contracts/Output/Visitor.php - - message: "#^Method Ibexa\\\\Rest\\\\FieldTypeProcessor\\\\BaseRelationProcessor\\:\\:setLocationService\\(\\) has no return type specified\\.$#" count: 1 @@ -2540,11 +2505,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/ContentList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/ContentList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\ContentTypeGroup\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -2560,11 +2520,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/ContentTypeGroupList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/ContentTypeGroupList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\ContentTypeGroupRefList\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -2575,21 +2530,11 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/ContentTypeInfoList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/ContentTypeInfoList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\ContentTypeList\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 path: src/lib/Server/Output/ValueObjectVisitor/ContentTypeList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/ContentTypeList.php - - message: "#^Access to offset 'Alpha2' on an unknown class Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\Countries\\.$#" count: 2 @@ -2615,11 +2560,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/CountryList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/CountryList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\CreatedContent\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -2830,11 +2770,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/FieldDefinitionList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/FieldDefinitionList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\ImageVariation\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -2890,31 +2825,16 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/ObjectStateGroupList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/ObjectStateGroupList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\ObjectStateList\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 path: src/lib/Server/Output/ValueObjectVisitor/ObjectStateList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/ObjectStateList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\Options\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 path: src/lib/Server/Output/ValueObjectVisitor/Options.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, int given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/Options.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\PermanentRedirect\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -2940,11 +2860,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/PolicyList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/PolicyList.php - - message: "#^Access to an undefined property Ibexa\\\\Rest\\\\Server\\\\Values\\\\RestRole\\:\\:\\$id\\.$#" count: 1 @@ -3130,11 +3045,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/RoleList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/RoleList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\Root\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -3155,11 +3065,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/SectionList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/SectionList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\SeeOther\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -3235,41 +3140,21 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/UserGroupList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/UserGroupList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\UserGroupRefList\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 path: src/lib/Server/Output/ValueObjectVisitor/UserGroupRefList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/UserGroupRefList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\UserList\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 path: src/lib/Server/Output/ValueObjectVisitor/UserList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/UserList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\UserRefList\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 path: src/lib/Server/Output/ValueObjectVisitor/UserRefList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/UserRefList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\UserSession\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -3280,11 +3165,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/UserSession.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/UserSession.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\Version\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -3315,11 +3195,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/VersionList.php - - - message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:setHeader\\(\\) expects string, false given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/VersionList.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\VersionTranslationInfo\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -3560,11 +3435,6 @@ parameters: count: 1 path: tests/bundle/DependencyInjection/Compiler/OutputVisitorPassTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Rest\\\\DependencyInjection\\\\Compiler\\\\ValueObjectVisitorPassTest\\:\\:testProcess\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/bundle/DependencyInjection/Compiler/ValueObjectVisitorPassTest.php - - message: "#^Parameter \\#2 \\$method of function method_exists expects string, array\\\\|int\\|string given\\.$#" count: 1 @@ -6685,101 +6555,11 @@ parameters: count: 1 path: tests/lib/Output/ValueObjectVisitorBaseTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\ValueObjectVisitorDispatcherTest\\:\\:testVisitValueObject\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/ValueObjectVisitorDispatcherTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\ValueObjectVisitorDispatcherTest\\:\\:testVisitValueObjectInvalidType\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/ValueObjectVisitorDispatcherTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\ValueObjectVisitorDispatcherTest\\:\\:testVisitValueObjectNoMatch\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/ValueObjectVisitorDispatcherTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\ValueObjectVisitorDispatcherTest\\:\\:testVisitValueObjectParentMatch\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/ValueObjectVisitorDispatcherTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\ValueObjectVisitorDispatcherTest\\:\\:testVisitValueObjectSecondRuleParentMatch\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/ValueObjectVisitorDispatcherTest.php - - - - message: "#^Parameter \\#1 \\$data of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\ValueObjectVisitorDispatcher\\:\\:visit\\(\\) expects object, int given\\.$#" - count: 1 - path: tests/lib/Output/ValueObjectVisitorDispatcherTest.php - - - - message: "#^Property Ibexa\\\\Tests\\\\Rest\\\\Output\\\\ValueObjectVisitorDispatcherTest\\:\\:\\$outputGeneratorMock \\(Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Generator&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" - count: 1 - path: tests/lib/Output/ValueObjectVisitorDispatcherTest.php - - - - message: "#^Property Ibexa\\\\Tests\\\\Rest\\\\Output\\\\ValueObjectVisitorDispatcherTest\\:\\:\\$outputVisitorMock \\(Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" - count: 1 - path: tests/lib/Output/ValueObjectVisitorDispatcherTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\VisitorTest\\:\\:getGeneratorMock\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/VisitorTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\VisitorTest\\:\\:getVisitorMock\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/VisitorTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\VisitorTest\\:\\:testSetFilteredHeaders\\(\\) has no return type specified\\.$#" count: 1 path: tests/lib/Output/VisitorTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\VisitorTest\\:\\:testSetHeaderResetAfterVisit\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/VisitorTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\VisitorTest\\:\\:testSetHeaders\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/VisitorTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\VisitorTest\\:\\:testSetHeadersNoOverwrite\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/VisitorTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\VisitorTest\\:\\:testSetStatusCode\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/VisitorTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\VisitorTest\\:\\:testSetStatusCodeNoOverride\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/VisitorTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\VisitorTest\\:\\:testVisitDocument\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/VisitorTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\VisitorTest\\:\\:testVisitEmptyDocument\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/VisitorTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\VisitorTest\\:\\:testVisitValueObject\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Output/VisitorTest.php - - message: "#^Call to method setRequestParser\\(\\) on an unknown class Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\Base\\.$#" count: 1 diff --git a/src/contracts/Output/Visitor.php b/src/contracts/Output/Visitor.php index 3e2d9f582..5b665901a 100644 --- a/src/contracts/Output/Visitor.php +++ b/src/contracts/Output/Visitor.php @@ -43,11 +43,8 @@ public function __construct( * * Does not allow overwriting of response headers. The first definition of * a header will be used. - * - * @param string $name - * @param string $value */ - public function setHeader($name, $value) + public function setHeader(string $name, mixed $value): void { if (!$this->response->headers->has($name)) { $this->response->headers->set($name, $value); @@ -74,7 +71,13 @@ public function setStatus($statusCode) */ public function visit(mixed $data): Response { - [$normalizedData, $encoderContext] = $this->normalizer->normalize($data, $this->format, ['visitor' => $this]); + $normalizedData = $this->normalizer->normalize($data, $this->format, ['visitor' => $this]); + $encoderContext = []; + + if (isset($normalizedData[VisitorAdapterNormalizer::ENCODER_CONTEXT])) { + $encoderContext = $normalizedData[VisitorAdapterNormalizer::ENCODER_CONTEXT]; + unset($normalizedData[VisitorAdapterNormalizer::ENCODER_CONTEXT]); + } //@todo Needs refactoring! // A hackish solution to enable outer visitors to disable setting @@ -145,9 +148,4 @@ public function getGenerator(): Generator { return $this->generator; } - - public function setGenerator(Generator $generator): void - { - $this->generator = $generator; - } } diff --git a/src/contracts/Output/VisitorAdapterNormalizer.php b/src/contracts/Output/VisitorAdapterNormalizer.php index e834ca08e..9925cb101 100644 --- a/src/contracts/Output/VisitorAdapterNormalizer.php +++ b/src/contracts/Output/VisitorAdapterNormalizer.php @@ -23,6 +23,8 @@ final class VisitorAdapterNormalizer implements NormalizerInterface, NormalizerA private const string CALLED_CONTEXT = __CLASS__ . '_CALLED'; + public const string ENCODER_CONTEXT = 'ENCODER_CONTEXT'; + public function __construct( private readonly EncoderInterface $encoder, private readonly ValueObjectVisitorResolverInterface $valueObjectVisitorResolver, @@ -82,12 +84,12 @@ public function supportsNormalization(mixed $data, ?string $format = null, array /** * @param array $context * - * @return array, array> + * @return array */ private function visitValueObject( object $object, ValueObjectVisitor $valueObjectVisitor, - string $format, + ?string $format, array $context, ): array { $visitor = $context['visitor'] ?? $this->createVisitor($format); @@ -102,8 +104,11 @@ private function visitValueObject( $normalizedData = $generator->toArray(); $encoderContext = $generator->getEncoderContext($normalizedData); + $transformedData = $generator->transformData($normalizedData); + + $transformedData[self::ENCODER_CONTEXT] = $encoderContext; - return [$generator->transformData($normalizedData), $encoderContext]; + return $transformedData; } private function createGenerator(string $format): Generator @@ -115,8 +120,10 @@ private function createGenerator(string $format): Generator : new Json($fieldTypeHashGenerator); } - private function createVisitor(string $format): Visitor + private function createVisitor(?string $format): Visitor { + $format = $format ?: 'json'; + $generator = $this->createGenerator($format); return new Visitor( diff --git a/src/lib/Output/Generator/InMemory/Xml.php b/src/lib/Output/Generator/InMemory/Xml.php index 92f6c0c90..b4a9c9bad 100644 --- a/src/lib/Output/Generator/InMemory/Xml.php +++ b/src/lib/Output/Generator/InMemory/Xml.php @@ -29,6 +29,28 @@ public function startAttribute($name, $value): void $this->json->{'@' . $name} = $value; } + public function startValueElement(string $name, $value, array $attributes = []): void + { + $this->checkStartValueElement($name); + + if (empty($attributes)) { + $jsonValue = $value; + } else { + $jsonValue = new Json\JsonObject($this->json); + foreach ($attributes as $attributeName => $attributeValue) { + $jsonValue->{'@' . $attributeName} = $attributeValue; + } + /** @phpstan-ignore-next-line */ + $jsonValue->{'#'} = $value; + } + + if ($this->json instanceof Json\ArrayObject) { + $this->json[] = $jsonValue; + } else { + $this->json->$name = $jsonValue; + } + } + public function transformData(array $normalizedData): array { $topNodeName = array_key_first($normalizedData); From 3d01d41d72800e6205b2ce1906245b9d11324d8c Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Thu, 5 Sep 2024 16:31:58 +0200 Subject: [PATCH 22/39] Fixups --- src/bundle/Resources/config/services.yml | 10 ++++- src/lib/Output/Generator/InMemory/Xml.php | 22 +++++++++- .../InMemory/Xml/FieldTypeHashGenerator.php | 40 +++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/lib/Output/Generator/InMemory/Xml/FieldTypeHashGenerator.php diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index fc64ec5ce..0975d87cd 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -348,7 +348,15 @@ services: Ibexa\Rest\Output\Generator\InMemory\Xml: arguments: - $fieldTypeHashGenerator: '@Ibexa\Rest\Output\Generator\Json\FieldTypeHashGenerator' + $fieldTypeHashGenerator: '@Ibexa\Rest\Output\Generator\InMemory\Xml\FieldTypeHashGenerator' + + Ibexa\Rest\Output\Generator\InMemory\Xml\FieldTypeHashGenerator: + arguments: + $normalizer: '@ibexa.rest.serializer' + $logger: '@logger' + $strictMode: '%ibexa.rest.strict_mode%' + tags: + - { name: monolog.logger, channel: ibexa.rest } Ibexa\Rest\Output\Generator\Xml\FieldTypeHashGenerator: arguments: diff --git a/src/lib/Output/Generator/InMemory/Xml.php b/src/lib/Output/Generator/InMemory/Xml.php index b4a9c9bad..1ab615eda 100644 --- a/src/lib/Output/Generator/InMemory/Xml.php +++ b/src/lib/Output/Generator/InMemory/Xml.php @@ -64,7 +64,27 @@ public function transformData(array $normalizedData): array $data['#'] = $normalizedData[$topNodeName]; } - return $data; + return $this->clearEmptyArrays($data); + } + + /** + * @param array $array + * + * @return array + */ + private function clearEmptyArrays(array &$array): array + { + foreach ($array as $key => $value) { + if (is_array($value)) { + $array[$key] = $this->clearEmptyArrays($value); + + if (empty($array[$key])) { + unset($array[$key]); + } + } + } + + return $array; } public function getEncoderContext(array $data): array diff --git a/src/lib/Output/Generator/InMemory/Xml/FieldTypeHashGenerator.php b/src/lib/Output/Generator/InMemory/Xml/FieldTypeHashGenerator.php new file mode 100644 index 000000000..d70af652d --- /dev/null +++ b/src/lib/Output/Generator/InMemory/Xml/FieldTypeHashGenerator.php @@ -0,0 +1,40 @@ + $hashArray + * + * @return \Ibexa\Rest\Output\Generator\Json\JsonObject + */ + protected function generateHashArray($parent, array $hashArray) + { + $object = new JsonObject($parent); + + /** @phpstan-ignore-next-line */ + $object->value = []; + + foreach ($hashArray as $hashKey => $hashItem) { + $object->value[] = [ + '@key' => $hashKey, + '#' => $this->generateValue($object, $hashItem), + ]; + } + + return $object; + } +} From 847a54d9c5e19fd458b2b79b7f9c9ecaa69eaf77 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Fri, 6 Sep 2024 09:24:06 +0200 Subject: [PATCH 23/39] Fixups --- .../Compiler/ValueObjectVisitorResolverPass.php | 2 +- src/contracts/Output/ValueObjectVisitorResolver.php | 4 +--- tests/lib/Output/VisitorTest.php | 1 + 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPass.php b/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPass.php index 8c875c3d1..387bd281f 100644 --- a/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPass.php +++ b/src/bundle/DependencyInjection/Compiler/ValueObjectVisitorResolverPass.php @@ -16,7 +16,7 @@ * Compiler pass for the ibexa.rest.output.value_object.visitor tag. * Maps a fully qualified class to a value object visitor. */ -final class ValueObjectVisitorResolverPass implements CompilerPassInterface +final readonly class ValueObjectVisitorResolverPass implements CompilerPassInterface { public const string OUTPUT_VALUE_OBJECT_VISITOR_SERVICE_TAG = 'ibexa.rest.output.value_object.visitor'; diff --git a/src/contracts/Output/ValueObjectVisitorResolver.php b/src/contracts/Output/ValueObjectVisitorResolver.php index 507350562..d23fc68ef 100644 --- a/src/contracts/Output/ValueObjectVisitorResolver.php +++ b/src/contracts/Output/ValueObjectVisitorResolver.php @@ -10,9 +10,7 @@ final class ValueObjectVisitorResolver implements ValueObjectVisitorResolverInterface { - /** - * @var array - */ + /** @var array */ private array $visitors; /** diff --git a/tests/lib/Output/VisitorTest.php b/tests/lib/Output/VisitorTest.php index a1b4c3f1f..baced28e2 100644 --- a/tests/lib/Output/VisitorTest.php +++ b/tests/lib/Output/VisitorTest.php @@ -184,6 +184,7 @@ protected function getVisitorMock(): Visitor&MockObject $this->getNormalizerMock(), $this->getEncoderMock(), $this->getValueObjectVisitorResolverMock(), + 'json', ], ) ->getMock(); From 5cf488f3164b94c9efcb8d518394b10113c25582 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Tue, 1 Oct 2024 16:28:31 +0200 Subject: [PATCH 24/39] Make unit tests use the new XML generator --- phpstan-baseline.neon | 2 +- src/lib/Output/Generator/InMemory/Xml.php | 69 +++++++++++++------ src/lib/Output/Generator/Json.php | 2 +- src/lib/Output/Generator/Json/JsonObject.php | 4 ++ .../lib/Output/ValueObjectVisitorBaseTest.php | 12 ++-- .../ValueObjectVisitor/ExceptionTest.php | 2 +- 6 files changed, 62 insertions(+), 29 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 98731c21c..8fda67528 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -6526,7 +6526,7 @@ parameters: path: tests/lib/Output/ValueObjectVisitorBaseTest.php - - message: "#^Property Ibexa\\\\Tests\\\\Rest\\\\Output\\\\ValueObjectVisitorBaseTest\\:\\:\\$generator \\(Ibexa\\\\Rest\\\\Output\\\\Generator\\\\Xml\\) in isset\\(\\) is not nullable\\.$#" + message: "#^Property Ibexa\\\\Tests\\\\Rest\\\\Output\\\\ValueObjectVisitorBaseTest\\:\\:\\$generator \\(Ibexa\\\\Rest\\\\Output\\\\Generator\\\\InMemory\\\\Xml\\) in isset\\(\\) is not nullable\\.$#" count: 1 path: tests/lib/Output/ValueObjectVisitorBaseTest.php diff --git a/src/lib/Output/Generator/InMemory/Xml.php b/src/lib/Output/Generator/InMemory/Xml.php index 1ab615eda..5523759de 100644 --- a/src/lib/Output/Generator/InMemory/Xml.php +++ b/src/lib/Output/Generator/InMemory/Xml.php @@ -10,6 +10,8 @@ use Ibexa\Rest\Output\Generator\Json; use Symfony\Component\Serializer\Encoder\XmlEncoder; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; +use Symfony\Component\Serializer\Serializer; final class Xml extends Json { @@ -29,6 +31,11 @@ public function startAttribute($name, $value): void $this->json->{'@' . $name} = $value; } + public function serializeBool($boolValue) + { + return $boolValue ? 'true' : 'false'; + } + public function startValueElement(string $name, $value, array $attributes = []): void { $this->checkStartValueElement($name); @@ -51,6 +58,26 @@ public function startValueElement(string $name, $value, array $attributes = []): } } + /** + * End document. + * + * Returns the generated document as a string. + */ + public function endDocument(mixed $data): string + { + parent::endDocument($data); + + $normalizedData = $this->toArray(); + + $encoderContext = $this->getEncoderContext($normalizedData); + $encoderContext['as_collection'] = true; + $transformedData = $this->transformData($normalizedData); + + $serializer = new Serializer([new ObjectNormalizer()], [new XmlEncoder()]); + + return $serializer->encode($transformedData, 'xml', $encoderContext); + } + public function transformData(array $normalizedData): array { $topNodeName = array_key_first($normalizedData); @@ -64,28 +91,30 @@ public function transformData(array $normalizedData): array $data['#'] = $normalizedData[$topNodeName]; } - return $this->clearEmptyArrays($data); + return $data; } - /** - * @param array $array - * - * @return array - */ - private function clearEmptyArrays(array &$array): array - { - foreach ($array as $key => $value) { - if (is_array($value)) { - $array[$key] = $this->clearEmptyArrays($value); - - if (empty($array[$key])) { - unset($array[$key]); - } - } - } - - return $array; - } +// /** +// * @param array $array +// * +// * @return array +// */ +// private function clearEmptyArrays(array &$array): array +// { +// foreach ($array as $key => &$value) { +// if (is_array($value)) { +// // Recursively apply the function to the nested array +// $this->clearEmptyArrays($value); +// +// // Remove the field if it's an empty array after recursion +// if (empty($value)) { +// unset($array[$key]); +// } +// } +// } +// +// return $array; +// } public function getEncoderContext(array $data): array { diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index 5aad23ced..b934129b7 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -318,7 +318,7 @@ public function generateFieldTypeHash($hashElementName, $hashValue) * * @param bool $boolValue * - * @return bool + * @return mixed */ public function serializeBool($boolValue) { diff --git a/src/lib/Output/Generator/Json/JsonObject.php b/src/lib/Output/Generator/Json/JsonObject.php index 5245c6e88..8bc517e44 100644 --- a/src/lib/Output/Generator/Json/JsonObject.php +++ b/src/lib/Output/Generator/Json/JsonObject.php @@ -4,15 +4,19 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); namespace Ibexa\Rest\Output\Generator\Json; +use AllowDynamicProperties; + /** * Json object. * * Special JSON object (\stdClass) implementation, which allows to access the * parent object it is assigned to again. */ +#[AllowDynamicProperties] class JsonObject { /** diff --git a/tests/lib/Output/ValueObjectVisitorBaseTest.php b/tests/lib/Output/ValueObjectVisitorBaseTest.php index 51ac42ecc..b98688cdb 100644 --- a/tests/lib/Output/ValueObjectVisitorBaseTest.php +++ b/tests/lib/Output/ValueObjectVisitorBaseTest.php @@ -30,7 +30,7 @@ abstract class ValueObjectVisitorBaseTest extends Server\BaseTest /** * Output generator. * - * @var \Ibexa\Rest\Output\Generator\Xml + * @var \Ibexa\Rest\Output\Generator\InMemory\Xml */ protected $generator; @@ -90,15 +90,15 @@ protected function getResponseMock() /** * Gets the output generator. * - * @return \Ibexa\Rest\Output\Generator\Xml + * @return \Ibexa\Rest\Output\Generator\InMemory\Xml */ protected function getGenerator() { if (!isset($this->generator)) { - $this->generator = new Generator\Xml( - new Generator\Xml\FieldTypeHashGenerator( - $this->createMock(NormalizerInterface::class) - ) + $this->generator = new Generator\InMemory\Xml( + new Generator\InMemory\Xml\FieldTypeHashGenerator( + $this->createMock(NormalizerInterface::class), + ), ); } diff --git a/tests/lib/Server/Output/ValueObjectVisitor/ExceptionTest.php b/tests/lib/Server/Output/ValueObjectVisitor/ExceptionTest.php index 8aefc8f0e..50821f434 100644 --- a/tests/lib/Server/Output/ValueObjectVisitor/ExceptionTest.php +++ b/tests/lib/Server/Output/ValueObjectVisitor/ExceptionTest.php @@ -10,7 +10,7 @@ use DOMDocument; use DOMXPath; use Ibexa\Contracts\Rest\Output\ValueObjectVisitor; -use Ibexa\Rest\Output\Generator\Xml; +use Ibexa\Rest\Output\Generator\InMemory\Xml; use Ibexa\Rest\Server\Output\ValueObjectVisitor\Exception as ExceptionValueObjectVisitor; use Ibexa\Tests\Rest\Output\ValueObjectVisitorBaseTest; use Symfony\Contracts\Translation\TranslatorInterface; From 89572a8aefea92d4efb01203eb8d4c8eddd47565 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Tue, 1 Oct 2024 16:30:05 +0200 Subject: [PATCH 25/39] Rollback --- src/lib/Output/Generator/InMemory/Xml.php | 44 +++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/lib/Output/Generator/InMemory/Xml.php b/src/lib/Output/Generator/InMemory/Xml.php index 5523759de..1cd546ec7 100644 --- a/src/lib/Output/Generator/InMemory/Xml.php +++ b/src/lib/Output/Generator/InMemory/Xml.php @@ -91,30 +91,30 @@ public function transformData(array $normalizedData): array $data['#'] = $normalizedData[$topNodeName]; } - return $data; + return $this->clearEmptyArrays($data); } -// /** -// * @param array $array -// * -// * @return array -// */ -// private function clearEmptyArrays(array &$array): array -// { -// foreach ($array as $key => &$value) { -// if (is_array($value)) { -// // Recursively apply the function to the nested array -// $this->clearEmptyArrays($value); -// -// // Remove the field if it's an empty array after recursion -// if (empty($value)) { -// unset($array[$key]); -// } -// } -// } -// -// return $array; -// } + /** + * @param array $array + * + * @return array + */ + private function clearEmptyArrays(array &$array): array + { + foreach ($array as $key => &$value) { + if (is_array($value)) { + // Recursively apply the function to the nested array + $this->clearEmptyArrays($value); + + // Remove the field if it's an empty array after recursion + if (empty($value)) { + unset($array[$key]); + } + } + } + + return $array; + } public function getEncoderContext(array $data): array { From 7a6e45ca8010082136a8e1c8ffa67452a4bfb7a9 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Tue, 1 Oct 2024 16:33:15 +0200 Subject: [PATCH 26/39] Stan --- phpstan-baseline.neon | 55 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 8fda67528..52f142216 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1050,11 +1050,21 @@ parameters: count: 1 path: src/lib/Server/Controller/Content.php + - + message: "#^Parameter \\#1 \\$remoteId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentInfoByRemoteId\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Server/Controller/Content.php + - message: "#^Parameter \\#1 \\$versions of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\VersionList constructor expects array\\, iterable\\ given\\.$#" count: 1 path: src/lib/Server/Controller/Content.php + - + message: "#^Parameter \\#2 \\$string of function explode expects string, string\\|null given\\.$#" + count: 3 + path: src/lib/Server/Controller/Content.php + - message: "#^Parameter \\#3 \\$relations of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\Version constructor expects array\\, iterable\\ given\\.$#" count: 4 @@ -1220,6 +1230,11 @@ parameters: count: 1 path: src/lib/Server/Controller/ContentType.php + - + message: "#^Parameter \\#1 \\$contentTypeGroupIdentifier of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:loadContentTypeGroupByIdentifier\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Server/Controller/ContentType.php + - message: "#^Parameter \\#1 \\$contentTypeGroups of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\ContentTypeGroupList constructor expects array\\, iterable\\ given\\.$#" count: 1 @@ -1270,6 +1285,11 @@ parameters: count: 1 path: src/lib/Server/Controller/ContentType.php + - + message: "#^Parameter \\#2 \\$orderby of method Ibexa\\\\Rest\\\\Server\\\\Controller\\\\ContentType\\:\\:sortContentTypeList\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Server/Controller/ContentType.php + - message: "#^Unreachable statement \\- code above always terminates\\.$#" count: 1 @@ -1296,12 +1316,17 @@ parameters: path: src/lib/Server/Controller/Location.php - - message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, string given\\.$#" + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/Location.php - message: "#^Parameter \\#1 \\$remoteId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocationByRemoteId\\(\\) expects string, string\\|null given\\.$#" + count: 2 + path: src/lib/Server/Controller/Location.php + + - + message: "#^Parameter \\#1 \\$url of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\URLAliasService\\:\\:lookup\\(\\) expects string, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/Location.php @@ -1395,6 +1420,11 @@ parameters: count: 1 path: src/lib/Server/Controller/Role.php + - + message: "#^Left side of && is always true\\.$#" + count: 1 + path: src/lib/Server/Controller/Role.php + - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Controller\\\\Role\\:\\:assignRoleToUser\\(\\) has parameter \\$userId with no type specified\\.$#" count: 1 @@ -1505,6 +1535,11 @@ parameters: count: 1 path: src/lib/Server/Controller/Role.php + - + message: "#^Parameter \\#1 \\$identifier of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService\\:\\:loadRoleByIdentifier\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Server/Controller/Role.php + - message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, string given\\.$#" count: 4 @@ -1691,12 +1726,17 @@ parameters: path: src/lib/Server/Controller/User.php - - message: "#^Parameter \\#1 \\$href of method Ibexa\\\\Rest\\\\RequestParser\\:\\:parseHref\\(\\) expects string, string\\|null given\\.$#" + message: "#^Parameter \\#1 \\$email of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserService\\:\\:loadUsersByEmail\\(\\) expects string, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/User.php - - message: "#^Parameter \\#1 \\$id of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserService\\:\\:loadUserGroup\\(\\) expects int, string given\\.$#" + message: "#^Parameter \\#1 \\$href of method Ibexa\\\\Rest\\\\RequestParser\\:\\:parseHref\\(\\) expects string, string\\|null given\\.$#" + count: 2 + path: src/lib/Server/Controller/User.php + + - + message: "#^Parameter \\#1 \\$id of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserService\\:\\:loadUserGroup\\(\\) expects int, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/User.php @@ -1705,6 +1745,11 @@ parameters: count: 14 path: src/lib/Server/Controller/User.php + - + message: "#^Parameter \\#1 \\$login of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserService\\:\\:loadUserByLogin\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Server/Controller/User.php + - message: "#^Parameter \\#1 \\$path of method Ibexa\\\\Rest\\\\Server\\\\Controller\\\\User\\:\\:extractLocationIdFromPath\\(\\) expects string, string\\|null given\\.$#" count: 1 @@ -1712,7 +1757,7 @@ parameters: - message: "#^Parameter \\#1 \\$remoteId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentInfoByRemoteId\\(\\) expects string, string\\|null given\\.$#" - count: 1 + count: 2 path: src/lib/Server/Controller/User.php - @@ -1962,7 +2007,7 @@ parameters: - message: "#^Possibly invalid array key type \\(array\\\\|string\\)\\.$#" - count: 2 + count: 1 path: src/lib/Server/Input/Parser/Criterion.php - From 3b6da3a582d244896f4974f114e4b20dba5a4b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Wed, 2 Oct 2024 10:03:27 +0200 Subject: [PATCH 27/39] Made `transformData` method private --- src/contracts/Output/Generator.php | 7 ------- .../Output/VisitorAdapterNormalizer.php | 5 ++--- src/lib/Output/Generator/InMemory/Xml.php | 18 +++++++++++++++--- src/lib/Output/Generator/Json.php | 5 ----- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/contracts/Output/Generator.php b/src/contracts/Output/Generator.php index d297b0ed1..a9834aaa6 100644 --- a/src/contracts/Output/Generator.php +++ b/src/contracts/Output/Generator.php @@ -443,11 +443,4 @@ abstract public function toArray(): array; * @return array */ abstract public function getEncoderContext(array $data): array; - - /** - * @param array $normalizedData - * - * @return array - */ - abstract public function transformData(array $normalizedData): array; } diff --git a/src/contracts/Output/VisitorAdapterNormalizer.php b/src/contracts/Output/VisitorAdapterNormalizer.php index 9925cb101..50a0c9f20 100644 --- a/src/contracts/Output/VisitorAdapterNormalizer.php +++ b/src/contracts/Output/VisitorAdapterNormalizer.php @@ -104,11 +104,10 @@ private function visitValueObject( $normalizedData = $generator->toArray(); $encoderContext = $generator->getEncoderContext($normalizedData); - $transformedData = $generator->transformData($normalizedData); - $transformedData[self::ENCODER_CONTEXT] = $encoderContext; + $normalizedData[self::ENCODER_CONTEXT] = $encoderContext; - return $transformedData; + return $normalizedData; } private function createGenerator(string $format): Generator diff --git a/src/lib/Output/Generator/InMemory/Xml.php b/src/lib/Output/Generator/InMemory/Xml.php index 1cd546ec7..b8b5df2f9 100644 --- a/src/lib/Output/Generator/InMemory/Xml.php +++ b/src/lib/Output/Generator/InMemory/Xml.php @@ -71,14 +71,26 @@ public function endDocument(mixed $data): string $encoderContext = $this->getEncoderContext($normalizedData); $encoderContext['as_collection'] = true; - $transformedData = $this->transformData($normalizedData); $serializer = new Serializer([new ObjectNormalizer()], [new XmlEncoder()]); - return $serializer->encode($transformedData, 'xml', $encoderContext); + return $serializer->encode($normalizedData, 'xml', $encoderContext); } - public function transformData(array $normalizedData): array + public function toArray(): array + { + $data = parent::toArray(); + $this->transformData($data); + + return $data; + } + + /** + * @param array $normalizedData + * + * @return array + */ + private function transformData(array $normalizedData): array { $topNodeName = array_key_first($normalizedData); $data = array_filter( diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index b934129b7..59a7aa067 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -334,9 +334,4 @@ public function getEncoderContext(array $data): array { return []; } - - public function transformData(array $normalizedData): array - { - return $normalizedData; - } } From 176ea72e4de768e3d3274b8c9dfb0126c8e17817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Tue, 8 Oct 2024 08:52:14 +0200 Subject: [PATCH 28/39] Introduced normalizers for Generator state --- src/contracts/Output/Generator.php | 8 +++++ .../Output/VisitorAdapterNormalizer.php | 14 +++++--- src/lib/Output/Generator/Data/ArrayList.php | 32 +++++++++++++++++ src/lib/Output/Generator/InMemory/Xml.php | 34 ++++++++++++++++--- src/lib/Output/Generator/Json.php | 28 ++++++++------- .../Generator/Json/FieldTypeHashGenerator.php | 2 +- .../Output/Normalizer/ArrayListNormalizer.php | 20 +++++++++++ .../Normalizer/ArrayObjectNormalizer.php | 25 ++++++++++++++ .../Normalizer/JsonObjectNormalizer.php | 24 +++++++++++++ 9 files changed, 165 insertions(+), 22 deletions(-) create mode 100644 src/lib/Output/Generator/Data/ArrayList.php create mode 100644 src/lib/Output/Normalizer/ArrayListNormalizer.php create mode 100644 src/lib/Output/Normalizer/ArrayObjectNormalizer.php create mode 100644 src/lib/Output/Normalizer/JsonObjectNormalizer.php diff --git a/src/contracts/Output/Generator.php b/src/contracts/Output/Generator.php index a9834aaa6..61275e142 100644 --- a/src/contracts/Output/Generator.php +++ b/src/contracts/Output/Generator.php @@ -437,6 +437,14 @@ abstract public function serializeBool($boolValue); */ abstract public function toArray(): array; + public function getData(): object + { + throw new \LogicException(sprintf( + '%s does not maintain state', + get_class($this), + )); + } + /** * @param array $data * diff --git a/src/contracts/Output/VisitorAdapterNormalizer.php b/src/contracts/Output/VisitorAdapterNormalizer.php index 50a0c9f20..80c023abc 100644 --- a/src/contracts/Output/VisitorAdapterNormalizer.php +++ b/src/contracts/Output/VisitorAdapterNormalizer.php @@ -102,12 +102,18 @@ private function visitValueObject( $generator->endDocument($object); - $normalizedData = $generator->toArray(); - $encoderContext = $generator->getEncoderContext($normalizedData); + $data = $generator->getData(); - $normalizedData[self::ENCODER_CONTEXT] = $encoderContext; + return $this->normalizer->normalize($data, $format, $context + [ + self::CALLED_CONTEXT => true, + ]); - return $normalizedData; + // $encoderContext = $generator->getEncoderContext($normalizedData); + // $normalizedData = $generator->toArray(); + + // $normalizedData[self::ENCODER_CONTEXT] = $encoderContext; + + // return $normalizedData; } private function createGenerator(string $format): Generator diff --git a/src/lib/Output/Generator/Data/ArrayList.php b/src/lib/Output/Generator/Data/ArrayList.php new file mode 100644 index 000000000..5daf97888 --- /dev/null +++ b/src/lib/Output/Generator/Data/ArrayList.php @@ -0,0 +1,32 @@ +name = $name; + $this->parent = $parent; + parent::__construct(); + } + + /** + * @return object + */ + public function getParent(): object + { + return $this->parent; + } +} diff --git a/src/lib/Output/Generator/InMemory/Xml.php b/src/lib/Output/Generator/InMemory/Xml.php index b8b5df2f9..1cf1af94b 100644 --- a/src/lib/Output/Generator/InMemory/Xml.php +++ b/src/lib/Output/Generator/InMemory/Xml.php @@ -8,7 +8,11 @@ namespace Ibexa\Rest\Output\Generator\InMemory; +use Ibexa\Rest\Output\Generator\Data; use Ibexa\Rest\Output\Generator\Json; +use Ibexa\Rest\Output\Normalizer\ArrayListNormalizer; +use Ibexa\Rest\Output\Normalizer\ArrayObjectNormalizer; +use Ibexa\Rest\Output\Normalizer\JsonObjectNormalizer; use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; @@ -20,6 +24,16 @@ public function getMediaType($name): string return $this->generateMediaTypeWithVendor($name, 'xml', $this->vendor); } + #[\Override] + public function startList($name): void + { + $this->checkStartList($name); + $array = new Data\ArrayList($name, $this->json); + + $this->json->$name = $array; + $this->json = $array; + } + /** * @param string $name * @param string $value @@ -47,12 +61,12 @@ public function startValueElement(string $name, $value, array $attributes = []): foreach ($attributes as $attributeName => $attributeValue) { $jsonValue->{'@' . $attributeName} = $attributeValue; } - /** @phpstan-ignore-next-line */ + $jsonValue->{'#'} = $value; } if ($this->json instanceof Json\ArrayObject) { - $this->json[] = $jsonValue; + $this->json->append($jsonValue); } else { $this->json->$name = $jsonValue; } @@ -69,12 +83,22 @@ public function endDocument(mixed $data): string $normalizedData = $this->toArray(); - $encoderContext = $this->getEncoderContext($normalizedData); + $data = $this->getData(); + + $encoderContext = []; + // $encoderContext = $this->getEncoderContext($normalizedData); $encoderContext['as_collection'] = true; - $serializer = new Serializer([new ObjectNormalizer()], [new XmlEncoder()]); + $normalizers = [ + new ArrayListNormalizer(), + new JsonObjectNormalizer(), + new ArrayObjectNormalizer(), + new ObjectNormalizer(), + ]; + $encoders = [new XmlEncoder()]; + $serializer = new Serializer($normalizers, $encoders); - return $serializer->encode($normalizedData, 'xml', $encoderContext); + return $serializer->serialize($data, 'xml', $encoderContext); } public function toArray(): array diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index 59a7aa067..fa22860bc 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -17,7 +17,7 @@ class Json extends Generator /** * Data structure which is build during visiting;. * - * @var array + * @var \Ibexa\Rest\Output\Generator\Json\JsonObject|\Ibexa\Rest\Output\Generator\Json\ArrayObject|\Ibexa\Rest\Output\Generator\Data\ArrayList */ protected $json; @@ -63,7 +63,7 @@ public function startDocument($data) $this->isEmpty = true; - $this->json = new Json\JsonObject(); + $this->json = new Json\JsonObject(null); } /** @@ -89,14 +89,14 @@ public function endDocument($data) { $this->checkEndDocument($data); - $jsonEncodeOptions = 0; + $jsonEncodeOptions = JSON_THROW_ON_ERROR; if ($this->formatOutput && defined('JSON_PRETTY_PRINT')) { - $jsonEncodeOptions = JSON_PRETTY_PRINT; + $jsonEncodeOptions |= JSON_PRETTY_PRINT; } - $this->json = $this->convertArrayObjects($this->json); + $data = $this->convertArrayObjects($this->json); - return json_encode($this->json, $jsonEncodeOptions); + return json_encode($data, $jsonEncodeOptions); } /** @@ -139,20 +139,19 @@ public function startObjectElement($name, $mediaTypeName = null) $this->isEmpty = false; - $mediaTypeName = $mediaTypeName ?: $name; + $mediaTypeName ??= $name; $object = new Json\JsonObject($this->json); if ($this->json instanceof Json\ArrayObject) { - $this->json[] = $object; + $this->json->append($object); $this->json = $object; } else { $this->json->$name = $object; $this->json = $object; } - $this->startAttribute('media-type', $this->getMediaType($mediaTypeName)); - $this->endAttribute('media-type'); + $this->attribute('media-type', $this->getMediaType($mediaTypeName)); } /** @@ -181,7 +180,7 @@ public function startHashElement($name) $object = new Json\JsonObject($this->json); if ($this->json instanceof Json\ArrayObject) { - $this->json[] = $object; + $this->json->append($object); $this->json = $object; } else { $this->json->$name = $object; @@ -218,7 +217,7 @@ public function startValueElement(string $name, $value, array $attributes = []): } if ($this->json instanceof Json\ArrayObject) { - $this->json[] = $jsonValue; + $this->json->append($jsonValue); } else { $this->json->$name = $jsonValue; } @@ -330,6 +329,11 @@ public function toArray(): array return json_decode(json_encode($this->json, JSON_THROW_ON_ERROR), true, JSON_THROW_ON_ERROR); } + public function getData(): object + { + return $this->json; + } + public function getEncoderContext(array $data): array { return []; diff --git a/src/lib/Output/Generator/Json/FieldTypeHashGenerator.php b/src/lib/Output/Generator/Json/FieldTypeHashGenerator.php index 15303337b..6a97f8f13 100644 --- a/src/lib/Output/Generator/Json/FieldTypeHashGenerator.php +++ b/src/lib/Output/Generator/Json/FieldTypeHashGenerator.php @@ -105,7 +105,7 @@ protected function generateListArray($parent, array $listArray) { $arrayObject = new ArrayObject($parent); foreach ($listArray as $listItem) { - $arrayObject[] = $this->generateValue($arrayObject, $listItem); + $arrayObject->append($this->generateValue($arrayObject, $listItem)); } return $arrayObject; diff --git a/src/lib/Output/Normalizer/ArrayListNormalizer.php b/src/lib/Output/Normalizer/ArrayListNormalizer.php new file mode 100644 index 000000000..163fd3a33 --- /dev/null +++ b/src/lib/Output/Normalizer/ArrayListNormalizer.php @@ -0,0 +1,20 @@ + $context + * + * @return array + */ + public function normalize($object, ?string $format = null, array $context = []): array + { + return get_object_vars($object); + } + + public function supportsNormalization($data, ?string $format = null): bool + { + return $data instanceof ArrayObject; + } +} \ No newline at end of file diff --git a/src/lib/Output/Normalizer/JsonObjectNormalizer.php b/src/lib/Output/Normalizer/JsonObjectNormalizer.php new file mode 100644 index 000000000..c9484c28b --- /dev/null +++ b/src/lib/Output/Normalizer/JsonObjectNormalizer.php @@ -0,0 +1,24 @@ + $context + * + * @return array + */ + public function normalize($object, ?string $format = null, array $context = []): array + { + return get_object_vars($object); + } + + public function supportsNormalization($data, ?string $format = null): bool + { + return $data instanceof JsonObject; + } +} \ No newline at end of file From 7c5c50a7c3665b075130f88f6d625e0eddec6148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Tue, 8 Oct 2024 09:34:01 +0200 Subject: [PATCH 29/39] Introduced normalizers for Generator state --- src/contracts/Output/Generator.php | 2 +- .../Output/Normalizer/ArrayListNormalizer.php | 23 ++++++++++++++++--- .../Normalizer/ArrayObjectNormalizer.php | 18 ++++++++++++--- .../Normalizer/JsonObjectNormalizer.php | 23 ++++++++++++++++--- .../ValueObjectVisitor/BookmarkListTest.php | 1 + 5 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/contracts/Output/Generator.php b/src/contracts/Output/Generator.php index 61275e142..6c3b536e4 100644 --- a/src/contracts/Output/Generator.php +++ b/src/contracts/Output/Generator.php @@ -441,7 +441,7 @@ public function getData(): object { throw new \LogicException(sprintf( '%s does not maintain state', - get_class($this), + static::class, )); } diff --git a/src/lib/Output/Normalizer/ArrayListNormalizer.php b/src/lib/Output/Normalizer/ArrayListNormalizer.php index 163fd3a33..46b829a24 100644 --- a/src/lib/Output/Normalizer/ArrayListNormalizer.php +++ b/src/lib/Output/Normalizer/ArrayListNormalizer.php @@ -1,20 +1,37 @@ $context + */ public function normalize($object, ?string $format = null, array $context = []) { - dump($object); + $data = []; + foreach ($object as $key => $value) { + $data[$key] = $this->normalizer->normalize($value, $format, $context); + } + + return $data; } public function supportsNormalization($data, ?string $format = null): bool { return $data instanceof ArrayList; } -} \ No newline at end of file +} diff --git a/src/lib/Output/Normalizer/ArrayObjectNormalizer.php b/src/lib/Output/Normalizer/ArrayObjectNormalizer.php index 655d60b8c..9b5af3de6 100644 --- a/src/lib/Output/Normalizer/ArrayObjectNormalizer.php +++ b/src/lib/Output/Normalizer/ArrayObjectNormalizer.php @@ -1,5 +1,10 @@ $context * @@ -15,11 +19,19 @@ final class ArrayObjectNormalizer implements NormalizerInterface */ public function normalize($object, ?string $format = null, array $context = []): array { - return get_object_vars($object); + $data = get_object_vars($object); + + unset($data['_ref_parent']); + + foreach ($data as $key => $value) { + $data[$key] = $this->normalize($value, $format, $context); + } + + return $data; } public function supportsNormalization($data, ?string $format = null): bool { return $data instanceof ArrayObject; } -} \ No newline at end of file +} diff --git a/src/lib/Output/Normalizer/JsonObjectNormalizer.php b/src/lib/Output/Normalizer/JsonObjectNormalizer.php index c9484c28b..bb2dc7857 100644 --- a/src/lib/Output/Normalizer/JsonObjectNormalizer.php +++ b/src/lib/Output/Normalizer/JsonObjectNormalizer.php @@ -1,12 +1,21 @@ $context * @@ -14,11 +23,19 @@ final class JsonObjectNormalizer implements NormalizerInterface */ public function normalize($object, ?string $format = null, array $context = []): array { - return get_object_vars($object); + $vars = get_object_vars($object); + + unset($vars['_ref_parent']); + + foreach ($vars as $name => $value) { + $vars[$name] = $this->normalizer->normalize($value, $format, $context); + } + + return $vars; } public function supportsNormalization($data, ?string $format = null): bool { return $data instanceof JsonObject; } -} \ No newline at end of file +} diff --git a/tests/lib/Server/Output/ValueObjectVisitor/BookmarkListTest.php b/tests/lib/Server/Output/ValueObjectVisitor/BookmarkListTest.php index 9662f7735..f772d4706 100644 --- a/tests/lib/Server/Output/ValueObjectVisitor/BookmarkListTest.php +++ b/tests/lib/Server/Output/ValueObjectVisitor/BookmarkListTest.php @@ -94,6 +94,7 @@ public function testResultContainsBookmarkElement(string $result): void $document->loadXML($result); $xpath = new DOMXPath($document); + dump($result); self::assertEquals(count($this->data->items), $xpath->query($query)->length); } From 81e18eaf54dabc69ea50ad5d032ccbaa41c24504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Tue, 8 Oct 2024 09:46:53 +0200 Subject: [PATCH 30/39] Introduced normalizers for Generator state --- src/lib/Output/Generator/Json.php | 2 +- src/lib/Output/Normalizer/ArrayObjectNormalizer.php | 2 -- src/lib/Output/Normalizer/JsonObjectNormalizer.php | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index fa22860bc..37d8b8105 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -143,7 +143,7 @@ public function startObjectElement($name, $mediaTypeName = null) $object = new Json\JsonObject($this->json); - if ($this->json instanceof Json\ArrayObject) { + if ($this->json instanceof Json\ArrayObject || $this->json instanceof Data\ArrayList) { $this->json->append($object); $this->json = $object; } else { diff --git a/src/lib/Output/Normalizer/ArrayObjectNormalizer.php b/src/lib/Output/Normalizer/ArrayObjectNormalizer.php index 9b5af3de6..948b6d561 100644 --- a/src/lib/Output/Normalizer/ArrayObjectNormalizer.php +++ b/src/lib/Output/Normalizer/ArrayObjectNormalizer.php @@ -21,8 +21,6 @@ public function normalize($object, ?string $format = null, array $context = []): { $data = get_object_vars($object); - unset($data['_ref_parent']); - foreach ($data as $key => $value) { $data[$key] = $this->normalize($value, $format, $context); } diff --git a/src/lib/Output/Normalizer/JsonObjectNormalizer.php b/src/lib/Output/Normalizer/JsonObjectNormalizer.php index bb2dc7857..f76fbb04a 100644 --- a/src/lib/Output/Normalizer/JsonObjectNormalizer.php +++ b/src/lib/Output/Normalizer/JsonObjectNormalizer.php @@ -25,8 +25,6 @@ public function normalize($object, ?string $format = null, array $context = []): { $vars = get_object_vars($object); - unset($vars['_ref_parent']); - foreach ($vars as $name => $value) { $vars[$name] = $this->normalizer->normalize($value, $format, $context); } From 6e5d5b4af4521fda01795b78ffa98c907b9dcae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Tue, 8 Oct 2024 09:58:49 +0200 Subject: [PATCH 31/39] Introduced normalizers for Generator state --- src/contracts/Output/Generator.php | 2 +- src/lib/Output/Generator/InMemory/Xml.php | 10 +++++++--- src/lib/Output/Generator/Json.php | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/contracts/Output/Generator.php b/src/contracts/Output/Generator.php index 6c3b536e4..0620c26de 100644 --- a/src/contracts/Output/Generator.php +++ b/src/contracts/Output/Generator.php @@ -450,5 +450,5 @@ public function getData(): object * * @return array */ - abstract public function getEncoderContext(array $data): array; + abstract protected function getEncoderContext(array $data): array; } diff --git a/src/lib/Output/Generator/InMemory/Xml.php b/src/lib/Output/Generator/InMemory/Xml.php index 1cf1af94b..f96b4ce2d 100644 --- a/src/lib/Output/Generator/InMemory/Xml.php +++ b/src/lib/Output/Generator/InMemory/Xml.php @@ -85,8 +85,12 @@ public function endDocument(mixed $data): string $data = $this->getData(); - $encoderContext = []; - // $encoderContext = $this->getEncoderContext($normalizedData); + if (!$data instanceof Json\JsonObject) { + throw new \LogicException('Expected an instance of JsonObject'); + } + + $vars = get_object_vars($data); + $encoderContext = $this->getEncoderContext($vars); $encoderContext['as_collection'] = true; $normalizers = [ @@ -152,7 +156,7 @@ private function clearEmptyArrays(array &$array): array return $array; } - public function getEncoderContext(array $data): array + protected function getEncoderContext(array $data): array { return [ XmlEncoder::ROOT_NODE_NAME => array_key_first($data), diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index 37d8b8105..40cbc75da 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -334,7 +334,7 @@ public function getData(): object return $this->json; } - public function getEncoderContext(array $data): array + protected function getEncoderContext(array $data): array { return []; } From 0babd33e6213e811422c5910c75bbba7c66d2778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Tue, 8 Oct 2024 10:00:28 +0200 Subject: [PATCH 32/39] Introduced normalizers for Generator state --- src/contracts/Output/Generator.php | 5 ----- src/lib/Output/Generator/InMemory/Xml.php | 2 -- src/lib/Output/Generator/Json.php | 5 ----- 3 files changed, 12 deletions(-) diff --git a/src/contracts/Output/Generator.php b/src/contracts/Output/Generator.php index 0620c26de..f96b18d53 100644 --- a/src/contracts/Output/Generator.php +++ b/src/contracts/Output/Generator.php @@ -432,11 +432,6 @@ protected function checkEnd($type, $data) */ abstract public function serializeBool($boolValue); - /** - * @return array - */ - abstract public function toArray(): array; - public function getData(): object { throw new \LogicException(sprintf( diff --git a/src/lib/Output/Generator/InMemory/Xml.php b/src/lib/Output/Generator/InMemory/Xml.php index f96b4ce2d..58d054a3b 100644 --- a/src/lib/Output/Generator/InMemory/Xml.php +++ b/src/lib/Output/Generator/InMemory/Xml.php @@ -81,8 +81,6 @@ public function endDocument(mixed $data): string { parent::endDocument($data); - $normalizedData = $this->toArray(); - $data = $this->getData(); if (!$data instanceof Json\JsonObject) { diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index 40cbc75da..411fa9465 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -324,11 +324,6 @@ public function serializeBool($boolValue) return (bool)$boolValue; } - public function toArray(): array - { - return json_decode(json_encode($this->json, JSON_THROW_ON_ERROR), true, JSON_THROW_ON_ERROR); - } - public function getData(): object { return $this->json; From 26fd4bbda95dcf00dbf22ff2d726ad3e87f429b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Tue, 8 Oct 2024 10:05:34 +0200 Subject: [PATCH 33/39] Introduced normalizers for Generator state --- src/lib/Output/Generator/InMemory/Xml.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/lib/Output/Generator/InMemory/Xml.php b/src/lib/Output/Generator/InMemory/Xml.php index 58d054a3b..6fe594aa6 100644 --- a/src/lib/Output/Generator/InMemory/Xml.php +++ b/src/lib/Output/Generator/InMemory/Xml.php @@ -103,14 +103,6 @@ public function endDocument(mixed $data): string return $serializer->serialize($data, 'xml', $encoderContext); } - public function toArray(): array - { - $data = parent::toArray(); - $this->transformData($data); - - return $data; - } - /** * @param array $normalizedData * From 264eb4f9455d94e018605ae45618d5b151c9f1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Tue, 8 Oct 2024 10:35:37 +0200 Subject: [PATCH 34/39] Introduced normalizers for Generator state --- src/lib/Output/Generator/Data/ArrayList.php | 12 +++++++++++- src/lib/Output/Generator/Json.php | 3 +++ src/lib/Output/Normalizer/JsonObjectNormalizer.php | 11 +++++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/lib/Output/Generator/Data/ArrayList.php b/src/lib/Output/Generator/Data/ArrayList.php index 5daf97888..d92ec5489 100644 --- a/src/lib/Output/Generator/Data/ArrayList.php +++ b/src/lib/Output/Generator/Data/ArrayList.php @@ -15,7 +15,7 @@ final class ArrayList extends \ArrayObject public function __construct( string $name, - ?object $parent = null + object $parent ) { $this->name = $name; $this->parent = $parent; @@ -29,4 +29,14 @@ public function getParent(): object { return $this->parent; } + + public function getName(): string + { + return $this->name; + } + + public function setName(string $name): void + { + $this->name = $name; + } } diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index 411fa9465..42064756a 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -145,6 +145,9 @@ public function startObjectElement($name, $mediaTypeName = null) if ($this->json instanceof Json\ArrayObject || $this->json instanceof Data\ArrayList) { $this->json->append($object); + if ($this->json instanceof Data\ArrayList) { + $this->json->setName($name); + } $this->json = $object; } else { $this->json->$name = $object; diff --git a/src/lib/Output/Normalizer/JsonObjectNormalizer.php b/src/lib/Output/Normalizer/JsonObjectNormalizer.php index f76fbb04a..22d7472d7 100644 --- a/src/lib/Output/Normalizer/JsonObjectNormalizer.php +++ b/src/lib/Output/Normalizer/JsonObjectNormalizer.php @@ -7,6 +7,7 @@ namespace Ibexa\Rest\Output\Normalizer; +use Ibexa\Rest\Output\Generator\Data\ArrayList; use Ibexa\Rest\Output\Generator\Json\JsonObject; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; @@ -25,8 +26,14 @@ public function normalize($object, ?string $format = null, array $context = []): { $vars = get_object_vars($object); - foreach ($vars as $name => $value) { - $vars[$name] = $this->normalizer->normalize($value, $format, $context); + foreach ($vars as $key => $value) { + if ($value instanceof ArrayList) { + $name = $value->getName(); + unset($vars[$key]); + $vars[$name] = $this->normalizer->normalize($value, $format, $context); + } else { + $vars[$key] = $this->normalizer->normalize($value, $format, $context); + } } return $vars; From a78bb4b0e7120222df525a335d9b48ee81012333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Tue, 8 Oct 2024 10:46:44 +0200 Subject: [PATCH 35/39] Introduced normalizers for Generator state --- src/lib/Output/Normalizer/JsonObjectNormalizer.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/Output/Normalizer/JsonObjectNormalizer.php b/src/lib/Output/Normalizer/JsonObjectNormalizer.php index 22d7472d7..f7b8f0a2f 100644 --- a/src/lib/Output/Normalizer/JsonObjectNormalizer.php +++ b/src/lib/Output/Normalizer/JsonObjectNormalizer.php @@ -26,17 +26,17 @@ public function normalize($object, ?string $format = null, array $context = []): { $vars = get_object_vars($object); + $data = []; foreach ($vars as $key => $value) { if ($value instanceof ArrayList) { $name = $value->getName(); - unset($vars[$key]); - $vars[$name] = $this->normalizer->normalize($value, $format, $context); + $data[$name] = $this->normalizer->normalize($value, $format, $context); } else { - $vars[$key] = $this->normalizer->normalize($value, $format, $context); + $data[$key] = $this->normalizer->normalize($value, $format, $context); } } - return $vars; + return $data; } public function supportsNormalization($data, ?string $format = null): bool From a3f70663167932d8a2d57f98e41ad0a59a77e339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Fri, 11 Oct 2024 10:59:11 +0200 Subject: [PATCH 36/39] Regenerated PHPStan baseline files --- phpstan-baseline-7.4.neon | 2 +- phpstan-baseline-8.0.neon | 2 +- phpstan-baseline.neon | 55 +++++++++++++++++++++++++++++++++++---- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/phpstan-baseline-7.4.neon b/phpstan-baseline-7.4.neon index 1b506f523..42e784972 100644 --- a/phpstan-baseline-7.4.neon +++ b/phpstan-baseline-7.4.neon @@ -7,7 +7,7 @@ parameters: - message: "#^Parameter \\#2 \\$str of function explode expects string, string\\|null given\\.$#" - count: 1 + count: 4 path: src/lib/Server/Controller/Content.php - diff --git a/phpstan-baseline-8.0.neon b/phpstan-baseline-8.0.neon index 76d651cac..fc95b6db2 100644 --- a/phpstan-baseline-8.0.neon +++ b/phpstan-baseline-8.0.neon @@ -37,7 +37,7 @@ parameters: - message: "#^Parameter \\#2 \\$string of function explode expects string, string\\|null given\\.$#" - count: 1 + count: 4 path: src/lib/Server/Controller/Content.php - diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index c4a1c0df3..cf9f00da2 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1080,6 +1080,11 @@ parameters: count: 1 path: src/lib/RequestParser/Pattern.php + - + message: "#^Offset 2 does not exist on array\\{0\\: string, 1\\?\\: non\\-empty\\-string, 2\\?\\: non\\-empty\\-string\\}\\.$#" + count: 1 + path: src/lib/RequestParser/Pattern.php + - message: "#^PHPDoc tag @param references unknown parameter\\: \\$type$#" count: 1 @@ -1195,6 +1200,11 @@ parameters: count: 1 path: src/lib/Server/Controller/Content.php + - + message: "#^Parameter \\#1 \\$remoteId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentInfoByRemoteId\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Server/Controller/Content.php + - message: "#^Parameter \\#1 \\$versions of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\VersionList constructor expects array\\, iterable\\ given\\.$#" count: 1 @@ -1365,6 +1375,11 @@ parameters: count: 1 path: src/lib/Server/Controller/ContentType.php + - + message: "#^Parameter \\#1 \\$contentTypeGroupIdentifier of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:loadContentTypeGroupByIdentifier\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Server/Controller/ContentType.php + - message: "#^Parameter \\#1 \\$contentTypeGroups of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\ContentTypeGroupList constructor expects array\\, iterable\\ given\\.$#" count: 1 @@ -1415,6 +1430,11 @@ parameters: count: 1 path: src/lib/Server/Controller/ContentType.php + - + message: "#^Parameter \\#2 \\$orderby of method Ibexa\\\\Rest\\\\Server\\\\Controller\\\\ContentType\\:\\:sortContentTypeList\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Server/Controller/ContentType.php + - message: "#^Unreachable statement \\- code above always terminates\\.$#" count: 1 @@ -1446,12 +1466,17 @@ parameters: path: src/lib/Server/Controller/Location.php - - message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, string given\\.$#" + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/Location.php - message: "#^Parameter \\#1 \\$remoteId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocationByRemoteId\\(\\) expects string, string\\|null given\\.$#" + count: 2 + path: src/lib/Server/Controller/Location.php + + - + message: "#^Parameter \\#1 \\$url of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\URLAliasService\\:\\:lookup\\(\\) expects string, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/Location.php @@ -1545,6 +1570,11 @@ parameters: count: 1 path: src/lib/Server/Controller/Role.php + - + message: "#^Left side of && is always true\\.$#" + count: 1 + path: src/lib/Server/Controller/Role.php + - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Controller\\\\Role\\:\\:assignRoleToUser\\(\\) has parameter \\$userId with no type specified\\.$#" count: 1 @@ -1655,6 +1685,11 @@ parameters: count: 1 path: src/lib/Server/Controller/Role.php + - + message: "#^Parameter \\#1 \\$identifier of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService\\:\\:loadRoleByIdentifier\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Server/Controller/Role.php + - message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, string given\\.$#" count: 4 @@ -2031,12 +2066,17 @@ parameters: path: src/lib/Server/Controller/User.php - - message: "#^Parameter \\#1 \\$href of method Ibexa\\\\Rest\\\\RequestParser\\:\\:parseHref\\(\\) expects string, string\\|null given\\.$#" + message: "#^Parameter \\#1 \\$email of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserService\\:\\:loadUsersByEmail\\(\\) expects string, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/User.php - - message: "#^Parameter \\#1 \\$id of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserService\\:\\:loadUserGroup\\(\\) expects int, string given\\.$#" + message: "#^Parameter \\#1 \\$href of method Ibexa\\\\Rest\\\\RequestParser\\:\\:parseHref\\(\\) expects string, string\\|null given\\.$#" + count: 2 + path: src/lib/Server/Controller/User.php + + - + message: "#^Parameter \\#1 \\$id of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserService\\:\\:loadUserGroup\\(\\) expects int, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/User.php @@ -2050,6 +2090,11 @@ parameters: count: 1 path: src/lib/Server/Controller/User.php + - + message: "#^Parameter \\#1 \\$login of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserService\\:\\:loadUserByLogin\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Server/Controller/User.php + - message: "#^Parameter \\#1 \\$message of function trigger_error expects string, int given\\.$#" count: 4 @@ -2062,7 +2107,7 @@ parameters: - message: "#^Parameter \\#1 \\$remoteId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentInfoByRemoteId\\(\\) expects string, string\\|null given\\.$#" - count: 1 + count: 2 path: src/lib/Server/Controller/User.php - @@ -2317,7 +2362,7 @@ parameters: - message: "#^Possibly invalid array key type \\(array\\\\|string\\)\\.$#" - count: 2 + count: 1 path: src/lib/Server/Input/Parser/Criterion.php - From fd2cbf52d67f1936605969ce51c78b4804efac4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Fri, 11 Oct 2024 11:22:42 +0200 Subject: [PATCH 37/39] IBX-8784: Added `AbstractExceptionVisitor` template to allow fine-grained control of output --- .php-cs-fixer.php | 1 + phpstan-baseline.neon | 15 -- .../Exceptions/AbstractExceptionVisitor.php | 160 ++++++++++++++++++ .../Output/ValueObjectVisitor/Exception.php | 120 ++----------- 4 files changed, 173 insertions(+), 123 deletions(-) create mode 100644 src/contracts/Output/Exceptions/AbstractExceptionVisitor.php diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 4ba28536a..6b793736c 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -11,6 +11,7 @@ $configFactory = new InternalConfigFactory(); $configFactory->withRules([ 'declare_strict_types' => false, + 'phpdoc_no_empty_return' => false, ]); return $configFactory diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index cf9f00da2..0fcc5a72c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2890,11 +2890,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/ContentFieldValidationException.php - - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\ContentFieldValidationException\\:\\:visit\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/ContentFieldValidationException.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\ContentList\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -3175,16 +3170,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/DeletedUserSession.php - - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\Exception\\:\\:visit\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/Exception.php - - - - message: "#^Property Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\Exception\\:\\:\\$httpStatusCodes type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/Exception.php - - message: "#^Property Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\Exception\\:\\:\\$translator \\(Symfony\\\\Contracts\\\\Translation\\\\TranslatorInterface\\) does not accept Symfony\\\\Contracts\\\\Translation\\\\TranslatorInterface\\|null\\.$#" count: 1 diff --git a/src/contracts/Output/Exceptions/AbstractExceptionVisitor.php b/src/contracts/Output/Exceptions/AbstractExceptionVisitor.php new file mode 100644 index 000000000..47ebac409 --- /dev/null +++ b/src/contracts/Output/Exceptions/AbstractExceptionVisitor.php @@ -0,0 +1,160 @@ + + */ + protected static $httpStatusCodes = [ + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 418 => "I'm a teapot", + 421 => 'There are too many connections from your internet address', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Unordered Collection', + 426 => 'Upgrade Required', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 505 => 'HTTP Version not supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', + 509 => 'Bandwidth Limit Exceeded', + 510 => 'Not Extended', + ]; + + /** + * Returns HTTP status code. + * + * @return int + */ + protected function getStatus() + { + return 500; + } + + /** + * @param \Exception $data + * + * @return void + */ + public function visit(Visitor $visitor, Generator $generator, $data) + { + $generator->startObjectElement('ErrorMessage'); + + $visitor->setHeader('Content-Type', $generator->getMediaType('ErrorMessage')); + + $statusCode = $this->generateErrorCode($generator, $visitor, $data); + + $errorMessage = $this->getErrorMessage($data, $statusCode); + $generator->valueElement('errorMessage', $errorMessage); + + $errorDescription = $this->getErrorDescription($data, $statusCode); + $generator->valueElement('errorDescription', $errorDescription); + + if ($this->canDisplayExceptionTrace()) { + $generator->valueElement('trace', $data->getTraceAsString()); + $generator->valueElement('file', $data->getFile()); + $generator->valueElement('line', $data->getLine()); + } + + $previous = $data->getPrevious(); + if ($previous !== null && $this->canDisplayPreviousException()) { + $generator->startObjectElement('Previous', 'ErrorMessage'); + $visitor->visitValueObject($previous); + $generator->endObjectElement('Previous'); + } + + $generator->endObjectElement('ErrorMessage'); + } + + protected function generateErrorCode(Generator $generator, Visitor $visitor, \Exception $e): int + { + $statusCode = $this->getStatus(); + $visitor->setStatus($statusCode); + + $generator->valueElement('errorCode', $statusCode); + + return $statusCode; + } + + protected function getErrorMessage(\Exception $data, int $statusCode): string + { + return static::$httpStatusCodes[$statusCode] ?? static::$httpStatusCodes[500]; + } + + protected function getErrorDescription(\Exception $data, int $statusCode): string + { + $translator = $this->getTranslator(); + if ($statusCode < 500 || $this->canDisplayExceptionMessage()) { + $errorDescription = $data instanceof Translatable && $translator + ? /** @Ignore */ + $translator->trans($data->getMessageTemplate(), $data->getParameters(), 'ibexa_repository_exceptions') + : $data->getMessage(); + } else { + // Do not leak any file paths and sensitive data on production environments + $errorDescription = $translator + ? /** @Desc("An error has occurred. Please try again later or contact your Administrator.") */ + $translator->trans('non_verbose_error', [], 'ibexa_repository_exceptions') + : 'An error has occurred. Please try again later or contact your Administrator.'; + } + + return $errorDescription; + } + + protected function getTranslator(): ?TranslatorInterface + { + return null; + } + + protected function canDisplayExceptionTrace(): bool + { + return false; + } + + protected function canDisplayPreviousException(): bool + { + return false; + } + + protected function canDisplayExceptionMessage(): bool + { + return false; + } +} diff --git a/src/lib/Server/Output/ValueObjectVisitor/Exception.php b/src/lib/Server/Output/ValueObjectVisitor/Exception.php index 129abe7b4..6069f3392 100644 --- a/src/lib/Server/Output/ValueObjectVisitor/Exception.php +++ b/src/lib/Server/Output/ValueObjectVisitor/Exception.php @@ -6,18 +6,13 @@ */ namespace Ibexa\Rest\Server\Output\ValueObjectVisitor; -use Ibexa\Contracts\Rest\Output\Generator; -use Ibexa\Contracts\Rest\Output\ValueObjectVisitor; -use Ibexa\Contracts\Rest\Output\Visitor; -use Ibexa\Core\Base\Translatable; -use JMS\TranslationBundle\Annotation\Desc; -use JMS\TranslationBundle\Annotation\Ignore; +use Ibexa\Contracts\Rest\Output\Exceptions\AbstractExceptionVisitor; use Symfony\Contracts\Translation\TranslatorInterface; /** * Exception value object visitor. */ -class Exception extends ValueObjectVisitor +class Exception extends AbstractExceptionVisitor { /** * Is debug mode enabled? @@ -26,49 +21,6 @@ class Exception extends ValueObjectVisitor */ protected $debug = false; - /** - * Mapping of HTTP status codes to their respective error messages. - * - * @var array - */ - protected static $httpStatusCodes = [ - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Time-out', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested range not satisfiable', - 417 => 'Expectation Failed', - 418 => "I'm a teapot", - 421 => 'There are too many connections from your internet address', - 422 => 'Unprocessable Entity', - 423 => 'Locked', - 424 => 'Failed Dependency', - 425 => 'Unordered Collection', - 426 => 'Upgrade Required', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Time-out', - 505 => 'HTTP Version not supported', - 506 => 'Variant Also Negotiates', - 507 => 'Insufficient Storage', - 509 => 'Bandwidth Limit Exceeded', - 510 => 'Not Extended', - ]; - /** @var \Symfony\Contracts\Translation\TranslatorInterface */ protected $translator; @@ -84,72 +36,24 @@ public function __construct($debug = false, ?TranslatorInterface $translator = n $this->translator = $translator; } - /** - * Returns HTTP status code. - * - * @return int - */ - protected function getStatus() + protected function getTranslator(): ?TranslatorInterface { - return 500; + return $this->translator; } - /** - * Visit struct returned by controllers. - * - * @param \Ibexa\Contracts\Rest\Output\Visitor $visitor - * @param \Ibexa\Contracts\Rest\Output\Generator $generator - * @param \Exception $data - */ - public function visit(Visitor $visitor, Generator $generator, $data) + protected function canDisplayExceptionMessage(): bool { - $generator->startObjectElement('ErrorMessage'); - - $visitor->setHeader('Content-Type', $generator->getMediaType('ErrorMessage')); - - $statusCode = $this->generateErrorCode($generator, $visitor, $data); - - $generator->valueElement( - 'errorMessage', - static::$httpStatusCodes[$statusCode] ?? static::$httpStatusCodes[500] - ); - - if ($this->debug || $statusCode < 500) { - $errorDescription = $data instanceof Translatable && $this->translator - ? /** @Ignore */ $this->translator->trans($data->getMessageTemplate(), $data->getParameters(), 'ibexa_repository_exceptions') - : $data->getMessage(); - } else { - // Do not leak any file paths and sensitive data on production environments - $errorDescription = $this->translator - ? /** @Desc("An error has occurred. Please try again later or contact your Administrator.") */ $this->translator->trans('non_verbose_error', [], 'ibexa_repository_exceptions') - : 'An error has occurred. Please try again later or contact your Administrator.'; - } - - $generator->valueElement('errorDescription', $errorDescription); - - if ($this->debug) { - $generator->valueElement('trace', $data->getTraceAsString()); - $generator->valueElement('file', $data->getFile()); - $generator->valueElement('line', $data->getLine()); - } - - if ($previous = $data->getPrevious()) { - $generator->startObjectElement('Previous', 'ErrorMessage'); - $visitor->visitValueObject($previous); - $generator->endObjectElement('Previous'); - } - - $generator->endObjectElement('ErrorMessage'); + return $this->debug; } - protected function generateErrorCode(Generator $generator, Visitor $visitor, \Exception $e): int + protected function canDisplayExceptionTrace(): bool { - $statusCode = $this->getStatus(); - $visitor->setStatus($statusCode); - - $generator->valueElement('errorCode', $statusCode); + return $this->debug; + } - return $statusCode; + protected function canDisplayPreviousException(): bool + { + return true; } } From e16ec649f100b0c42e51962713bd6dfa25cd69f2 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Wed, 16 Oct 2024 00:36:38 +0200 Subject: [PATCH 38/39] Remove old Xml generator --- phpstan-baseline.neon | 57 +--- .../InMemory/Xml/FieldTypeHashGenerator.php | 29 +- .../Generator/Json/FieldTypeHashGenerator.php | 10 +- src/lib/Output/Generator/Xml.php | 277 ------------------ .../Generator/Xml/FieldTypeHashGenerator.php | 275 ----------------- tests/lib/Output/Generator/XmlTest.php | 2 - ...orTest__testGenerateComplexValueAuthor.out | 15 +- ...rTest__testGenerateHashArrayMixedValue.out | 9 +- ...eratorTest__testGenerateHashArrayValue.out | 9 +- ...est__testGenerateObjectUsingNormalizer.out | 4 +- ...ratorTest__testGenerateUsingNormalizer.out | 14 +- ...est__testGenerateWithMissingNormalizer.out | 4 +- .../_fixtures/testGeneratorStackedElement.xml | 4 +- .../ValueObjectVisitor/BookmarkListTest.php | 1 - 14 files changed, 34 insertions(+), 676 deletions(-) delete mode 100644 src/lib/Output/Generator/Xml.php delete mode 100644 src/lib/Output/Generator/Xml/FieldTypeHashGenerator.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index dff0a8ebe..01f357592 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -695,26 +695,11 @@ parameters: count: 1 path: src/lib/Output/Generator/Json/FieldTypeHashGenerator.php - - - message: "#^Method Ibexa\\\\Rest\\\\Output\\\\Generator\\\\Json\\\\FieldTypeHashGenerator\\:\\:generateArrayValue\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Output/Generator/Json/FieldTypeHashGenerator.php - - - - message: "#^Method Ibexa\\\\Rest\\\\Output\\\\Generator\\\\Json\\\\FieldTypeHashGenerator\\:\\:generateHashArray\\(\\) has parameter \\$hashArray with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Output/Generator/Json/FieldTypeHashGenerator.php - - message: "#^Method Ibexa\\\\Rest\\\\Output\\\\Generator\\\\Json\\\\FieldTypeHashGenerator\\:\\:generateHashValue\\(\\) has no return type specified\\.$#" count: 1 path: src/lib/Output/Generator/Json/FieldTypeHashGenerator.php - - - message: "#^Method Ibexa\\\\Rest\\\\Output\\\\Generator\\\\Json\\\\FieldTypeHashGenerator\\:\\:generateListArray\\(\\) has parameter \\$listArray with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Output/Generator/Json/FieldTypeHashGenerator.php - - message: "#^Method Ibexa\\\\Rest\\\\Output\\\\Generator\\\\Json\\\\FieldTypeHashGenerator\\:\\:isNumericArray\\(\\) has parameter \\$value with no value type specified in iterable type array\\.$#" count: 1 @@ -1460,11 +1445,6 @@ parameters: count: 1 path: src/lib/Server/Controller/User.php - - - message: "#^Parameter \\#1 \\$remoteId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentInfoByRemoteId\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: src/lib/Server/Controller/User.php - - message: "#^Parameter \\#5 \\$relations of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\RestUser constructor expects array\\, iterable\\ given\\.$#" count: 5 @@ -1712,7 +1692,7 @@ parameters: - message: "#^Possibly invalid array key type \\(array\\\\|string\\)\\.$#" - count: 1 + count: 2 path: src/lib/Server/Input/Parser/Criterion.php - @@ -1760,6 +1740,16 @@ parameters: count: 1 path: src/lib/Server/Input/Parser/Criterion/FullText.php + - + message: "#^Instantiated class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Location\\\\IsBookmarked not found\\.$#" + count: 1 + path: src/lib/Server/Input/Parser/Criterion/IsBookmarked.php + + - + message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\Criterion\\\\IsBookmarked\\:\\:parse\\(\\) has invalid return type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Location\\\\IsBookmarked\\.$#" + count: 1 + path: src/lib/Server/Input/Parser/Criterion/IsBookmarked.php + - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\Criterion\\\\LanguageCode\\:\\:parse\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 @@ -6020,31 +6010,6 @@ parameters: count: 1 path: tests/lib/Output/Generator/JsonTest.php - - - message: "#^Call to method setFormatOutput\\(\\) on an unknown class Ibexa\\\\Rest\\\\Output\\\\Generator\\\\Xml\\.$#" - count: 1 - path: tests/lib/Output/Generator/Xml/FieldTypeHashGeneratorTest.php - - - - message: "#^Instantiated class Ibexa\\\\Rest\\\\Output\\\\Generator\\\\Xml not found\\.$#" - count: 1 - path: tests/lib/Output/Generator/Xml/FieldTypeHashGeneratorTest.php - - - - message: "#^Instantiated class Ibexa\\\\Rest\\\\Output\\\\Generator\\\\Xml\\\\FieldTypeHashGenerator not found\\.$#" - count: 1 - path: tests/lib/Output/Generator/Xml/FieldTypeHashGeneratorTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\Generator\\\\Xml\\\\FieldTypeHashGeneratorTest\\:\\:initializeFieldTypeHashGenerator\\(\\) has invalid return type Ibexa\\\\Rest\\\\Output\\\\Generator\\\\Xml\\\\FieldTypeHashGenerator\\.$#" - count: 1 - path: tests/lib/Output/Generator/Xml/FieldTypeHashGeneratorTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\Generator\\\\Xml\\\\FieldTypeHashGeneratorTest\\:\\:initializeGenerator\\(\\) has invalid return type Ibexa\\\\Rest\\\\Output\\\\Generator\\\\Xml\\.$#" - count: 1 - path: tests/lib/Output/Generator/Xml/FieldTypeHashGeneratorTest.php - - message: "#^Parameter \\#1 \\$boolValue of method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Generator\\:\\:serializeBool\\(\\) expects bool, string given\\.$#" count: 1 diff --git a/src/lib/Output/Generator/InMemory/Xml/FieldTypeHashGenerator.php b/src/lib/Output/Generator/InMemory/Xml/FieldTypeHashGenerator.php index 76e6ae157..c4f000b54 100644 --- a/src/lib/Output/Generator/InMemory/Xml/FieldTypeHashGenerator.php +++ b/src/lib/Output/Generator/InMemory/Xml/FieldTypeHashGenerator.php @@ -8,7 +8,6 @@ namespace Ibexa\Rest\Output\Generator\InMemory\Xml; -use Ibexa\Rest\Output\Generator\Data\ArrayList; use Ibexa\Rest\Output\Generator\Json\FieldTypeHashGenerator as JsonFieldTypeHashGenerator; use Ibexa\Rest\Output\Generator\Json\JsonObject; @@ -24,17 +23,13 @@ protected function generateValue($parent, $value): mixed return sprintf('%F', $value); } elseif (is_array($value)) { return $this->generateArrayValue($parent, $value); + } elseif (is_object($value)) { + return $this->generateObjectValue($parent, $value); } else { return $value; } } - /** - * Generates an array value from $value. - * - * @param array $value - * @param string|null $key - */ protected function generateArrayValue($parent, $value) { if ($this->isNumericArray($value)) { @@ -44,22 +39,20 @@ protected function generateArrayValue($parent, $value) } } - /** - * Generates a JSON array from the given $hashArray with $parent. - * - * @param \Ibexa\Rest\Output\Generator\Json\ArrayObject|\Ibexa\Rest\Output\Generator\Json\JsonObject $parent - * @param array $listArray - * - * @return \Ibexa\Rest\Output\Generator\Json\ArrayObject - */ protected function generateListArray($parent, array $listArray) { - $arrayList = new ArrayList('value', $parent); + $object = new JsonObject($parent); + + /** @phpstan-ignore-next-line */ + $object->value = []; + foreach ($listArray as $listItem) { - $arrayList->append($this->generateValue($parent, $listItem)); + $object->value[] = [ + '#' => $this->generateValue($object, $listItem), + ]; } - return $arrayList; + return $object; } /** diff --git a/src/lib/Output/Generator/Json/FieldTypeHashGenerator.php b/src/lib/Output/Generator/Json/FieldTypeHashGenerator.php index 6a97f8f13..55d719350 100644 --- a/src/lib/Output/Generator/Json/FieldTypeHashGenerator.php +++ b/src/lib/Output/Generator/Json/FieldTypeHashGenerator.php @@ -80,7 +80,7 @@ protected function generateValue($parent, $value) * JSON array, otherwise a JSON object * * @param \Ibexa\Rest\Output\Generator\Json\ArrayObject|\Ibexa\Rest\Output\Generator\Json\JsonObject $parent - * @param array $value + * @param array $value * * @return \Ibexa\Rest\Output\Generator\Json\ArrayObject|\Ibexa\Rest\Output\Generator\Json\JsonObject */ @@ -97,9 +97,9 @@ protected function generateArrayValue($parent, array $value) * Generates a JSON array from the given $hashArray with $parent. * * @param \Ibexa\Rest\Output\Generator\Json\ArrayObject|\Ibexa\Rest\Output\Generator\Json\JsonObject $parent - * @param array $listArray + * @param array $listArray * - * @return \Ibexa\Rest\Output\Generator\Json\ArrayObject + * @return \Ibexa\Rest\Output\Generator\Json\ArrayObject|JsonObject */ protected function generateListArray($parent, array $listArray) { @@ -115,7 +115,7 @@ protected function generateListArray($parent, array $listArray) * Generates a JSON object from the given $hashArray with $parent. * * @param \Ibexa\Rest\Output\Generator\Json\ArrayObject|\Ibexa\Rest\Output\Generator\Json\JsonObject $parent - * @param array $hashArray + * @param array $hashArray * * @return \Ibexa\Rest\Output\Generator\Json\JsonObject */ @@ -152,7 +152,7 @@ protected function isNumericArray(array $value) * * @return mixed */ - private function generateObjectValue($parent, object $value) + protected function generateObjectValue($parent, object $value) { try { $value = $this->normalizer->normalize($value, 'json', ['parent' => $parent]); diff --git a/src/lib/Output/Generator/Xml.php b/src/lib/Output/Generator/Xml.php deleted file mode 100644 index 8851efa29..000000000 --- a/src/lib/Output/Generator/Xml.php +++ /dev/null @@ -1,277 +0,0 @@ -hashGenerator = $hashGenerator; - $this->vendor = $vendor; - } - - /** - * Start document. - * - * @param mixed $data - */ - public function startDocument($data) - { - $this->checkStartDocument($data); - - $this->isEmpty = true; - - $this->xmlWriter = new \XMLWriter(); - $this->xmlWriter->openMemory(); - $this->xmlWriter->setIndent($this->formatOutput); - $this->xmlWriter->startDocument('1.0', 'UTF-8'); - } - - /** - * Returns if the document is empty or already contains data. - * - * @return bool - */ - public function isEmpty() - { - return $this->isEmpty; - } - - /** - * End document. - * - * Returns the generated document as a string. - * - * @param mixed $data - * - * @return string - */ - public function endDocument($data) - { - $this->checkEndDocument($data); - - $this->xmlWriter->endDocument(); - - return $this->xmlWriter->outputMemory(); - } - - /** - * Start object element. - * - * @param string $name - * @param string $mediaTypeName - */ - public function startObjectElement($name, $mediaTypeName = null) - { - $this->checkStartObjectElement($name); - - $this->isEmpty = false; - - $mediaTypeName = $mediaTypeName ?: $name; - - $this->xmlWriter->startElement($name); - - $this->startAttribute('media-type', $this->getMediaType($mediaTypeName)); - $this->endAttribute('media-type'); - } - - /** - * End object element. - * - * @param string $name - */ - public function endObjectElement($name) - { - $this->checkEndObjectElement($name); - - $this->xmlWriter->endElement(); - } - - /** - * Start hash element. - * - * @param string $name - */ - public function startHashElement($name) - { - $this->checkStartHashElement($name); - - $this->isEmpty = false; - - $this->xmlWriter->startElement($name); - } - - /** - * End hash element. - * - * @param string $name - */ - public function endHashElement($name) - { - $this->checkEndHashElement($name); - - $this->xmlWriter->endElement(); - } - - public function startValueElement(string $name, $value, array $attributes = []): void - { - $this->checkStartValueElement($name); - - $this->xmlWriter->startElement($name); - - foreach ($attributes as $attributeName => $attributeValue) { - $this->xmlWriter->startAttribute($attributeName); - $this->xmlWriter->text($attributeValue); - $this->xmlWriter->endAttribute(); - } - - $this->xmlWriter->text((string)$value); - } - - /** - * End value element. - * - * @param string $name - */ - public function endValueElement($name) - { - $this->checkEndValueElement($name); - - $this->xmlWriter->endElement(); - } - - /** - * Start list. - * - * @param string $name - */ - public function startList($name) - { - $this->checkStartList($name); - } - - /** - * End list. - * - * @param string $name - */ - public function endList($name) - { - $this->checkEndList($name); - } - - /** - * Start attribute. - * - * @param string $name - * @param string $value - */ - public function startAttribute($name, $value) - { - $this->checkStartAttribute($name); - - $this->xmlWriter->startAttribute($name); - $this->xmlWriter->text((string)$value); - } - - /** - * End attribute. - * - * @param string $name - */ - public function endAttribute($name) - { - $this->checkEndAttribute($name); - - $this->xmlWriter->endAttribute(); - } - - /** - * Get media type. - * - * @param string $name - * - * @return string - */ - public function getMediaType($name) - { - return $this->generateMediaTypeWithVendor($name, 'xml', $this->vendor); - } - - /** - * Generates a generic representation of the scalar, hash or list given in - * $hashValue into the document, using an element of $hashElementName as - * its parent. - * - * @param string $hashElementName - * @param mixed $hashValue - */ - public function generateFieldTypeHash($hashElementName, $hashValue) - { - $this->hashGenerator->generateHashValue($this->xmlWriter, $hashElementName, $hashValue); - } - - /** - * Serializes a boolean value. - * - * @param bool $boolValue - * - * @return string - */ - public function serializeBool($boolValue) - { - return $boolValue ? 'true' : 'false'; - } - - public function toArray(): array - { - return []; - } - - public function getEncoderContext(array $data): array - { - return []; - } -} diff --git a/src/lib/Output/Generator/Xml/FieldTypeHashGenerator.php b/src/lib/Output/Generator/Xml/FieldTypeHashGenerator.php deleted file mode 100644 index 6c3715e23..000000000 --- a/src/lib/Output/Generator/Xml/FieldTypeHashGenerator.php +++ /dev/null @@ -1,275 +0,0 @@ -normalizer = $normalizer; - $this->logger = $logger ?? new NullLogger(); - $this->strictMode = $strictMode; - } - - /** - * Generates the field type value $hashValue into the $writer creating an - * element with $hashElementName as its parent. - * - * @param \XmlWriter $writer - * @param string $hashElementName - * @param mixed $hashValue - */ - public function generateHashValue(\XMLWriter $writer, $hashElementName, $hashValue) - { - $this->generateValue($writer, $hashValue, null, $hashElementName); - } - - /** - * Generates $value into a serialized representation. - * - * @param \XmlWriter $writer - * @param mixed $value - * @param string|null $key - * @param string $elementName - */ - protected function generateValue(\XmlWriter $writer, $value, $key = null, $elementName = 'value') - { - if ($value === null) { - $this->generateNullValue($writer, $key, $elementName); - } elseif (is_bool($value)) { - $this->generateBooleanValue($writer, $value, $key, $elementName); - } elseif (is_int($value)) { - $this->generateIntegerValue($writer, $value, $key, $elementName); - } elseif (is_float($value)) { - $this->generateFloatValue($writer, $value, $key, $elementName); - } elseif (is_string($value)) { - $this->generateStringValue($writer, $value, $key, $elementName); - } elseif (is_array($value)) { - $this->generateArrayValue($writer, $value, $key, $elementName); - } elseif (is_object($value)) { - $this->generateObjectValue($value, $writer, $key, $elementName); - } else { - throw new \Exception('Invalid type in Field value hash: ' . get_debug_type($value)); - } - } - - /** - * Generates an array value from $value. - * - * @param \XmlWriter $writer - * @param array $value - * @param string|null $key - * @param string $elementName - */ - protected function generateArrayValue(\XmlWriter $writer, $value, $key, $elementName = 'value') - { - if ($this->isNumericArray($value)) { - $this->generateListArray($writer, $value, $key, $elementName); - } else { - $this->generateHashArray($writer, $value, $key, $elementName); - } - } - - /** - * Generates $value as a hash of value items. - * - * @param \XmlWriter $writer - * @param array $value - * @param string|null $key - * @param string $elementName - */ - protected function generateHashArray(\XmlWriter $writer, $value, $key = null, $elementName = 'value') - { - $writer->startElement($elementName); - $this->generateKeyAttribute($writer, $key); - - foreach ($value as $hashKey => $hashItemValue) { - $this->generateValue($writer, $hashItemValue, $hashKey); - } - - $writer->endElement(); - } - - /** - * Generates $value as a list of value items. - * - * @param \XmlWriter $writer - * @param array $value - * @param string|null $key - * @param string $elementName - */ - protected function generateListArray(\XmlWriter $writer, $value, $key = null, $elementName = 'value') - { - $writer->startElement($elementName); - $this->generateKeyAttribute($writer, $key); - - foreach ($value as $listItemValue) { - $this->generateValue($writer, $listItemValue); - } - - $writer->endElement(); - } - - /** - * Checks if the given $value is a purely numeric array. - * - * @param array $value - * - * @return bool - */ - protected function isNumericArray(array $value) - { - foreach (array_keys($value) as $key) { - if (is_string($key)) { - return false; - } - } - - return true; - } - - /** - * Generates a null value. - * - * @param \XmlWriter $writer - * @param string|null $key - * @param string $elementName - */ - protected function generateNullValue(\XmlWriter $writer, $key = null, $elementName = 'value') - { - $writer->startElement($elementName); - $this->generateKeyAttribute($writer, $key); - // @todo: xsi:type? - $writer->endElement(); - } - - /** - * Generates a boolean value. - * - * @param \XmlWriter $writer - * @param bool $value - * @param string|null $key - * @param string $elementName - */ - protected function generateBooleanValue(\XmlWriter $writer, $value, $key = null, $elementName = 'value') - { - $writer->startElement($elementName); - $this->generateKeyAttribute($writer, $key); - $writer->text($value ? 'true' : 'false'); - $writer->endElement(); - } - - /** - * Generates a integer value. - * - * @param \XmlWriter $writer - * @param int $value - * @param string|null $key - * @param string $elementName - */ - protected function generateIntegerValue(\XmlWriter $writer, $value, $key = null, $elementName = 'value') - { - $writer->startElement($elementName); - $this->generateKeyAttribute($writer, $key); - $writer->text($value); - $writer->endElement(); - } - - /** - * Generates a float value. - * - * @param \XmlWriter $writer - * @param float $value - * @param string|null $key - * @param string $elementName - */ - protected function generateFloatValue(\XmlWriter $writer, $value, $key = null, $elementName = 'value') - { - $writer->startElement($elementName); - $this->generateKeyAttribute($writer, $key); - $writer->text(sprintf('%F', $value)); - $writer->endElement(); - } - - /** - * Generates a string value. - * - * @param \XmlWriter $writer - * @param string $value - * @param string|null $key - * @param string $elementName - */ - protected function generateStringValue(\XmlWriter $writer, $value, $key = null, $elementName = 'value') - { - $writer->startElement($elementName); - $this->generateKeyAttribute($writer, $key); - $writer->text($value); - $writer->endElement(); - } - - /** - * Generates a key attribute with $key as the value, if $key is not null. - * - * @param \XmlWriter $writer - * @param string|null $key - */ - protected function generateKeyAttribute(\XmlWriter $writer, $key = null) - { - if ($key !== null) { - $writer->startAttribute('key'); - $writer->text($key); - $writer->endAttribute(); - } - } - - private function generateObjectValue(object $value, \XmlWriter $writer, ?string $key, string $elementName): void - { - try { - $value = $this->normalizer->normalize($value, 'xml'); - } catch (ExceptionInterface $e) { - if ($this->strictMode) { - throw $e; - } - $message = sprintf( - 'Unable to normalize value for type "%s". %s. ' - . 'Ensure that a normalizer is registered with tag: "%s".', - get_class($value), - $e->getMessage(), - 'ibexa.rest.serializer.normalizer', - ); - $this->logger->error($message, [ - 'exception' => $e, - ]); - $value = null; - } - - if (is_object($value)) { - $this->generateNullValue($writer, $key, $elementName); - - return; - } - - $this->generateValue($writer, $value, $key, $elementName); - } -} diff --git a/tests/lib/Output/Generator/XmlTest.php b/tests/lib/Output/Generator/XmlTest.php index 2f4b31b71..15397e93f 100644 --- a/tests/lib/Output/Generator/XmlTest.php +++ b/tests/lib/Output/Generator/XmlTest.php @@ -79,8 +79,6 @@ public function testGeneratorStackedElement(): void $response = $generator->endDocument('test'); - dump($response); - self::assertSame( file_get_contents(__DIR__ . '/_fixtures/' . __FUNCTION__ . '.xml'), $response, diff --git a/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateComplexValueAuthor.out b/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateComplexValueAuthor.out index f390ad90d..50cdf03fe 100644 --- a/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateComplexValueAuthor.out +++ b/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateComplexValueAuthor.out @@ -1,15 +1,2 @@ - - - - 1 - Joe Sindelfingen - sindelfingen@example.com - - - 2 - Joe Bielefeld - bielefeld@example.com - - - +1Joe Sindelfingensindelfingen@example.com2Joe Bielefeldbielefeld@example.com diff --git a/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateHashArrayMixedValue.out b/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateHashArrayMixedValue.out index aba5a4d27..94495bd4c 100644 --- a/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateHashArrayMixedValue.out +++ b/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateHashArrayMixedValue.out @@ -1,9 +1,2 @@ - - - 23 - true - Sindelfingen - - - +23trueSindelfingen diff --git a/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateHashArrayValue.out b/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateHashArrayValue.out index 82f83d2ea..acf8b8c57 100644 --- a/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateHashArrayValue.out +++ b/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateHashArrayValue.out @@ -1,9 +1,2 @@ - - - 23 - true - Sindelfingen - - - +23trueSindelfingen diff --git a/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateObjectUsingNormalizer.out b/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateObjectUsingNormalizer.out index 40d692ade..358088985 100644 --- a/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateObjectUsingNormalizer.out +++ b/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateObjectUsingNormalizer.out @@ -1,4 +1,2 @@ - - - + diff --git a/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateUsingNormalizer.out b/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateUsingNormalizer.out index c57c5eb0a..5dfd754af 100644 --- a/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateUsingNormalizer.out +++ b/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateUsingNormalizer.out @@ -1,14 +1,2 @@ - - - 1 - foo - - bar - - - 42 - 56 - - - +1foobar4256 diff --git a/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateWithMissingNormalizer.out b/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateWithMissingNormalizer.out index 40d692ade..358088985 100644 --- a/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateWithMissingNormalizer.out +++ b/tests/lib/Output/Generator/_fixtures/Xml_FieldTypeHashGeneratorTest__testGenerateWithMissingNormalizer.out @@ -1,4 +1,2 @@ - - - + diff --git a/tests/lib/Output/Generator/_fixtures/testGeneratorStackedElement.xml b/tests/lib/Output/Generator/_fixtures/testGeneratorStackedElement.xml index df8161012..16f32ea29 100644 --- a/tests/lib/Output/Generator/_fixtures/testGeneratorStackedElement.xml +++ b/tests/lib/Output/Generator/_fixtures/testGeneratorStackedElement.xml @@ -1,4 +1,2 @@ - - - + diff --git a/tests/lib/Server/Output/ValueObjectVisitor/BookmarkListTest.php b/tests/lib/Server/Output/ValueObjectVisitor/BookmarkListTest.php index f772d4706..9662f7735 100644 --- a/tests/lib/Server/Output/ValueObjectVisitor/BookmarkListTest.php +++ b/tests/lib/Server/Output/ValueObjectVisitor/BookmarkListTest.php @@ -94,7 +94,6 @@ public function testResultContainsBookmarkElement(string $result): void $document->loadXML($result); $xpath = new DOMXPath($document); - dump($result); self::assertEquals(count($this->data->items), $xpath->query($query)->length); } From 46032c98ed94ea72dde04d8d7f900e9c7aaf7188 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Wed, 16 Oct 2024 00:43:51 +0200 Subject: [PATCH 39/39] CS --- phpstan-baseline.neon | 17 ++++++----------- .../Exceptions/AbstractExceptionVisitor.php | 1 + 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index f0ca600b3..742ec3d2a 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1445,6 +1445,11 @@ parameters: count: 1 path: src/lib/Server/Controller/User.php + - + message: "#^Parameter \\#1 \\$remoteId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentInfoByRemoteId\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Server/Controller/User.php + - message: "#^Parameter \\#5 \\$relations of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\RestUser constructor expects array\\, iterable\\ given\\.$#" count: 5 @@ -1692,7 +1697,7 @@ parameters: - message: "#^Possibly invalid array key type \\(array\\\\|string\\)\\.$#" - count: 2 + count: 1 path: src/lib/Server/Input/Parser/Criterion.php - @@ -1740,16 +1745,6 @@ parameters: count: 1 path: src/lib/Server/Input/Parser/Criterion/FullText.php - - - message: "#^Instantiated class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Location\\\\IsBookmarked not found\\.$#" - count: 1 - path: src/lib/Server/Input/Parser/Criterion/IsBookmarked.php - - - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\Criterion\\\\IsBookmarked\\:\\:parse\\(\\) has invalid return type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Location\\\\IsBookmarked\\.$#" - count: 1 - path: src/lib/Server/Input/Parser/Criterion/IsBookmarked.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\Criterion\\\\LanguageCode\\:\\:parse\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 diff --git a/src/contracts/Output/Exceptions/AbstractExceptionVisitor.php b/src/contracts/Output/Exceptions/AbstractExceptionVisitor.php index 47ebac409..30a273f41 100644 --- a/src/contracts/Output/Exceptions/AbstractExceptionVisitor.php +++ b/src/contracts/Output/Exceptions/AbstractExceptionVisitor.php @@ -4,6 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ + namespace Ibexa\Contracts\Rest\Output\Exceptions; use Ibexa\Contracts\Rest\Output\Generator;