From cc204d7eee9e8e22ad032c60360175ba5a62ce9c Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Wed, 11 Feb 2026 12:39:00 +0100 Subject: [PATCH 01/17] Removed static variable usage for command's debug message --- src/Command/TransferGeneratorCommand.php | 35 +++++++++++++----------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/Command/TransferGeneratorCommand.php b/src/Command/TransferGeneratorCommand.php index c6fad108..60877576 100644 --- a/src/Command/TransferGeneratorCommand.php +++ b/src/Command/TransferGeneratorCommand.php @@ -122,27 +122,22 @@ private function generateTransfers(SymfonyStyle $io, string $configPath): bool } $this->writelnErrorMessages($io, $generatorTransfer); - $this->writelnDebugMessages($io, $generatorTransfer); + if ($this->isDebugMessages($io, $generatorTransfer)) { + $lastFileName ??= $generatorTransfer->fileName; + $this->writelnDebugMessages($io, $generatorTransfer, $lastFileName); + } } return $generatorFiber->getReturn(); } - private function writelnDebugMessages(SymfonyStyle $io, TransferGeneratorTransfer $generatorTransfer): void - { - if ( - !$io->isVerbose() - || $generatorTransfer->validator->isValid === false - || $generatorTransfer->fileName === null - || $generatorTransfer->className === null - ) { - return; - } - - static $fileName = $generatorTransfer->fileName; - - if ($fileName !== $generatorTransfer->fileName) { - $fileName = $generatorTransfer->fileName; + private function writelnDebugMessages( + SymfonyStyle $io, + TransferGeneratorTransfer $generatorTransfer, + ?string &$lastFileName, + ): void { + if ($lastFileName !== $generatorTransfer->fileName) { + $lastFileName = $generatorTransfer->fileName; $io->newLine(); } @@ -156,6 +151,14 @@ private function writelnDebugMessages(SymfonyStyle $io, TransferGeneratorTransfe ); } + private function isDebugMessages(SymfonyStyle $io, TransferGeneratorTransfer $generatorTransfer): bool + { + return $io->isVerbose() + && $generatorTransfer->validator->isValid === true + && $generatorTransfer->fileName !== null + && $generatorTransfer->className !== null; + } + private function writelnErrorMessages(SymfonyStyle $io, TransferGeneratorTransfer $generatorTransfer): void { if ($generatorTransfer->validator->isValid === true) { From 1f541cd2e1ea0204c0615ab5b50a54d0d5f7bfc8 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Wed, 11 Feb 2026 21:07:19 +0100 Subject: [PATCH 02/17] Fixed sorting imports on transfer objects, extracted template sorter to the seprate class --- .../Generator/Render/RenderFactory.php | 6 +++ .../Generator/Render/TemplateBuilder.php | 28 ++++--------- .../Generator/Render/TemplateSorter.php | 42 +++++++++++++++++++ .../Render/TemplateSorterInterface.php | 12 ++++++ 4 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 src/TransferGenerator/Generator/Render/TemplateSorter.php create mode 100644 src/TransferGenerator/Generator/Render/TemplateSorterInterface.php diff --git a/src/TransferGenerator/Generator/Render/RenderFactory.php b/src/TransferGenerator/Generator/Render/RenderFactory.php index 0f85ef62..0c113105 100644 --- a/src/TransferGenerator/Generator/Render/RenderFactory.php +++ b/src/TransferGenerator/Generator/Render/RenderFactory.php @@ -60,6 +60,7 @@ protected function createTemplateBuilder(): TemplateBuilderInterface { return new TemplateBuilder( $this->getConfig(), + $this->createTemplateSorter(), $this->createTemplateExpander(), ); } @@ -137,4 +138,9 @@ protected function createCollectionTypeTemplateExpander(): TemplateExpanderInter { return new CollectionTypeTemplateExpander(); } + + protected function createTemplateSorter(): TemplateSorterInterface + { + return new TemplateSorter(); + } } diff --git a/src/TransferGenerator/Generator/Render/TemplateBuilder.php b/src/TransferGenerator/Generator/Render/TemplateBuilder.php index 34bdf7be..0e9c545c 100644 --- a/src/TransferGenerator/Generator/Render/TemplateBuilder.php +++ b/src/TransferGenerator/Generator/Render/TemplateBuilder.php @@ -14,6 +14,7 @@ { public function __construct( private ConfigInterface $config, + private TemplateSorterInterface $templateSorter, private TemplateExpanderInterface $templateExpander, ) { } @@ -26,30 +27,19 @@ public function createTemplateTransfer(DefinitionTransfer $definitionTransfer): $templateTransfer->className = $definitionTransfer->content->className; $templateTransfer->imports[TransferEnum::ABSTRACT_CLASS->value] = TransferEnum::ABSTRACT_CLASS->value; - foreach ($definitionTransfer->content->properties as $propertyTransfer) { - $this->templateExpander->expandTemplateTransfer($propertyTransfer, $templateTransfer); - } + $this->expandProperties($definitionTransfer, $templateTransfer); - $this->sortTemplate($templateTransfer); + $this->templateSorter->sortTemplateTransfer($templateTransfer); return $templateTransfer; } - private function sortTemplate(TemplateTransfer $templateTransfer): void - { - $templateTransfer->imports->natsort(); - $templateTransfer->metaConstants->natsort(); - - if ($templateTransfer->metaInitiators->count() > 0) { - $templateTransfer->metaInitiators->natsort(); - } - - if ($templateTransfer->metaTransformers->count() > 0) { - $templateTransfer->metaTransformers->natsort(); - } - - foreach ($templateTransfer->metaAttributes as $metaAttributes) { - natsort($metaAttributes); + private function expandProperties( + DefinitionTransfer $definitionTransfer, + TemplateTransfer $templateTransfer, + ): void { + foreach ($definitionTransfer->content->properties as $propertyTransfer) { + $this->templateExpander->expandTemplateTransfer($propertyTransfer, $templateTransfer); } } diff --git a/src/TransferGenerator/Generator/Render/TemplateSorter.php b/src/TransferGenerator/Generator/Render/TemplateSorter.php new file mode 100644 index 00000000..b5c32087 --- /dev/null +++ b/src/TransferGenerator/Generator/Render/TemplateSorter.php @@ -0,0 +1,42 @@ +imports->uasort($this->sortNamespaces(...)); + $templateTransfer->metaConstants->natsort(); + + if ($templateTransfer->metaInitiators->count() > 0) { + $templateTransfer->metaInitiators->natsort(); + } + + if ($templateTransfer->metaTransformers->count() > 0) { + $templateTransfer->metaTransformers->natsort(); + } + + $this->sortMetaAttributes($templateTransfer); + } + + private function sortMetaAttributes(TemplateTransfer $templateTransfer): void + { + foreach ($templateTransfer->metaAttributes as &$metaAttributes) { + natsort($metaAttributes); + } + unset($metaAttributes); + } + + private function sortNamespaces(string $a, string $b): int + { + return strcasecmp( + str_replace(search: '\\', replace: '', subject: $a), + str_replace(search: '\\', replace: '', subject: $b), + ); + } +} diff --git a/src/TransferGenerator/Generator/Render/TemplateSorterInterface.php b/src/TransferGenerator/Generator/Render/TemplateSorterInterface.php new file mode 100644 index 00000000..d585f166 --- /dev/null +++ b/src/TransferGenerator/Generator/Render/TemplateSorterInterface.php @@ -0,0 +1,12 @@ + Date: Wed, 11 Feb 2026 21:11:58 +0100 Subject: [PATCH 03/17] Removed static variable usage to cache if the bcmath extention was loaded --- src/Transfer/Adapter/TransferAdapterTrait.php | 7 ++++--- .../Content/Property/NumberTypePropertyValidator.php | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Transfer/Adapter/TransferAdapterTrait.php b/src/Transfer/Adapter/TransferAdapterTrait.php index a0a9281c..f8d48b44 100644 --- a/src/Transfer/Adapter/TransferAdapterTrait.php +++ b/src/Transfer/Adapter/TransferAdapterTrait.php @@ -40,6 +40,8 @@ trait TransferAdapterTrait */ private ?WeakReference $_reflectionObjectReference = null; + private static bool $_isBcMathLoaded; + /** * @return \Traversable */ @@ -262,9 +264,8 @@ private function isBcMathType(mixed $type): bool private function isBcMathLoaded(): bool { - /** @var bool $isLoaded */ - static $isLoaded = \extension_loaded('bcmath'); + self::$_isBcMathLoaded ??= extension_loaded('bcmath'); - return $isLoaded; + return self::$_isBcMathLoaded; } } diff --git a/src/TransferGenerator/Definition/Validator/Content/Property/NumberTypePropertyValidator.php b/src/TransferGenerator/Definition/Validator/Content/Property/NumberTypePropertyValidator.php index b3865249..a4d72bcd 100644 --- a/src/TransferGenerator/Definition/Validator/Content/Property/NumberTypePropertyValidator.php +++ b/src/TransferGenerator/Definition/Validator/Content/Property/NumberTypePropertyValidator.php @@ -9,7 +9,7 @@ use Picamator\TransferObject\Generated\ValidatorMessageTransfer; use Picamator\TransferObject\Shared\Validator\ValidatorMessageTrait; -readonly class NumberTypePropertyValidator implements PropertyValidatorInterface +class NumberTypePropertyValidator implements PropertyValidatorInterface { use ValidatorMessageTrait; @@ -19,6 +19,8 @@ private const string EXTENSION_IS_NOT_LOADED_ERROR = 'PHP extension BCMath was not loaded. Please install and load extension.'; + private static bool $isBcMathLoaded; + public function isApplicable(DefinitionPropertyTransfer $propertyTransfer): bool { return $propertyTransfer->numberType !== null; @@ -57,9 +59,8 @@ private function getErrorMessage(DefinitionPropertyTransfer $propertyTransfer): protected function isBcMathLoaded(): bool { - /** @var bool $isLoaded */ - static $isLoaded = extension_loaded('bcmath'); + self::$isBcMathLoaded ??= extension_loaded('bcmath'); - return $isLoaded; + return self::$isBcMathLoaded; } } From a8fdc6872a8ada882779d35bcb6da61207e31087 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Wed, 11 Feb 2026 21:13:09 +0100 Subject: [PATCH 04/17] Added root namespace on TransferAdapterTrait --- src/Transfer/Adapter/TransferAdapterTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Transfer/Adapter/TransferAdapterTrait.php b/src/Transfer/Adapter/TransferAdapterTrait.php index f8d48b44..541d8016 100644 --- a/src/Transfer/Adapter/TransferAdapterTrait.php +++ b/src/Transfer/Adapter/TransferAdapterTrait.php @@ -264,7 +264,7 @@ private function isBcMathType(mixed $type): bool private function isBcMathLoaded(): bool { - self::$_isBcMathLoaded ??= extension_loaded('bcmath'); + self::$_isBcMathLoaded ??= \extension_loaded('bcmath'); return self::$_isBcMathLoaded; } From b72bca849bc830d434deb55e91be492ddd28c0c6 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Wed, 11 Feb 2026 21:14:12 +0100 Subject: [PATCH 05/17] Upgraded composer dependencies --- composer.lock | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/composer.lock b/composer.lock index f03819d9..b7268916 100644 --- a/composer.lock +++ b/composer.lock @@ -1464,11 +1464,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.38", + "version": "2.1.39", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dfaf1f530e1663aa167bc3e52197adb221582629", - "reference": "dfaf1f530e1663aa167bc3e52197adb221582629", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", + "reference": "c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", "shasum": "" }, "require": { @@ -1513,25 +1513,25 @@ "type": "github" } ], - "time": "2026-01-30T17:12:46+00:00" + "time": "2026-02-11T14:48:56+00:00" }, { "name": "phpstan/phpstan-strict-rules", - "version": "2.0.8", + "version": "2.0.10", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "1ed9e626a37f7067b594422411539aa807190573" + "reference": "1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/1ed9e626a37f7067b594422411539aa807190573", - "reference": "1ed9e626a37f7067b594422411539aa807190573", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f", + "reference": "1aba28b697c1e3b6bbec8a1725f8b11b6d3e5a5f", "shasum": "" }, "require": { "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.1.29" + "phpstan/phpstan": "^2.1.39" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "^1.2", @@ -1557,11 +1557,14 @@ "MIT" ], "description": "Extra strict and opinionated rules for PHPStan", + "keywords": [ + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.8" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.10" }, - "time": "2026-01-27T08:10:25+00:00" + "time": "2026-02-11T14:17:32+00:00" }, { "name": "phpunit/php-code-coverage", From 8b7f1c9aa53d1b5b102e43b774ffb3bd7b913d60 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Wed, 11 Feb 2026 21:22:00 +0100 Subject: [PATCH 06/17] Covered transfer generator command - debug message line separator --- ...dTransfer.php => CommandFirstTransfer.php} | 4 +-- .../Success/CommandSecondTransfer.php | 36 +++++++++++++++++++ .../Command/TransferGeneratorCommandTest.php | 3 +- ...ransfer.yml => command.first.transfer.yml} | 2 +- .../definition/command.second.transfer.yml | 4 +++ 5 files changed, 45 insertions(+), 4 deletions(-) rename tests/integration/Command/Generated/Success/{CommandTransfer.php => CommandFirstTransfer.php} (89%) create mode 100644 tests/integration/Command/Generated/Success/CommandSecondTransfer.php rename tests/integration/Command/data/config/success/definition/{command.transfer.yml => command.first.transfer.yml} (86%) create mode 100644 tests/integration/Command/data/config/success/definition/command.second.transfer.yml diff --git a/tests/integration/Command/Generated/Success/CommandTransfer.php b/tests/integration/Command/Generated/Success/CommandFirstTransfer.php similarity index 89% rename from tests/integration/Command/Generated/Success/CommandTransfer.php rename to tests/integration/Command/Generated/Success/CommandFirstTransfer.php index 4638e665..e751728a 100644 --- a/tests/integration/Command/Generated/Success/CommandTransfer.php +++ b/tests/integration/Command/Generated/Success/CommandFirstTransfer.php @@ -13,9 +13,9 @@ * * Note: Do not manually edit this file, as changes will be overwritten. * - * @see /tests/integration/Command/data/config/success/definition/command.transfer.yml Definition file path. + * @see /tests/integration/Command/data/config/success/definition/command.first.transfer.yml Definition file path. */ -final class CommandTransfer extends AbstractTransfer +final class CommandFirstTransfer extends AbstractTransfer { protected const int META_DATA_SIZE = 1; diff --git a/tests/integration/Command/Generated/Success/CommandSecondTransfer.php b/tests/integration/Command/Generated/Success/CommandSecondTransfer.php new file mode 100644 index 00000000..24c1d03f --- /dev/null +++ b/tests/integration/Command/Generated/Success/CommandSecondTransfer.php @@ -0,0 +1,36 @@ + self::RUN_INDEX, + ]; + + // run + public const string RUN_PROP = 'run'; + private const int RUN_INDEX = 0; + + public ?true $run { + get => $this->getData(self::RUN_INDEX); + set { + $this->setData(self::RUN_INDEX, $value); + } + } +} diff --git a/tests/integration/Command/TransferGeneratorCommandTest.php b/tests/integration/Command/TransferGeneratorCommandTest.php index db05939c..ab21f75c 100644 --- a/tests/integration/Command/TransferGeneratorCommandTest.php +++ b/tests/integration/Command/TransferGeneratorCommandTest.php @@ -65,7 +65,8 @@ public function testRunCommandWithValidConfigurationShouldShowSuccessMessage(): // Assert $this->commandTester->assertCommandIsSuccessful(); - $this->assertStringContainsString('command.transfer.yml: CommandTransfer', $output); + $this->assertStringContainsString('command.first.transfer.yml: CommandFirstTransfer', $output); + $this->assertStringContainsString('command.second.transfer.yml: CommandSecondTransfer', $output); $this->assertStringContainsString('All Transfer Objects were generated successfully!', $output); } diff --git a/tests/integration/Command/data/config/success/definition/command.transfer.yml b/tests/integration/Command/data/config/success/definition/command.first.transfer.yml similarity index 86% rename from tests/integration/Command/data/config/success/definition/command.transfer.yml rename to tests/integration/Command/data/config/success/definition/command.first.transfer.yml index 656e48bd..4c2cf5f4 100644 --- a/tests/integration/Command/data/config/success/definition/command.transfer.yml +++ b/tests/integration/Command/data/config/success/definition/command.first.transfer.yml @@ -1,4 +1,4 @@ # $schema: ./../../../../../../../schema/definition.schema.json -Command: +CommandFirst: run: type: true diff --git a/tests/integration/Command/data/config/success/definition/command.second.transfer.yml b/tests/integration/Command/data/config/success/definition/command.second.transfer.yml new file mode 100644 index 00000000..9e67b7b9 --- /dev/null +++ b/tests/integration/Command/data/config/success/definition/command.second.transfer.yml @@ -0,0 +1,4 @@ +# $schema: ./../../../../../../../schema/definition.schema.json +CommandSecond: + run: + type: true From b5d8c73b8c43296e1d9daf5f69a68aca1938e150 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Wed, 11 Feb 2026 21:59:17 +0100 Subject: [PATCH 07/17] Covered BuiltInTypeBuilderExpander by tests, fixed regex condition --- src/Shared/Parser/DocBlockParserTrait.php | 8 ++--- .../Expander/AttributesPropertyExpander.php | 6 ++-- .../Error/AddressStatisticsTransfer.php | 34 +++++++++++++++---- .../BuiltInTypeBuilderExpanderTest.php | 29 ++++++++++++++++ 4 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src/Shared/Parser/DocBlockParserTrait.php b/src/Shared/Parser/DocBlockParserTrait.php index 34953e9b..2e54d685 100644 --- a/src/Shared/Parser/DocBlockParserTrait.php +++ b/src/Shared/Parser/DocBlockParserTrait.php @@ -13,16 +13,16 @@ trait DocBlockParserTrait private const string TYPE_REGEX = '#(?[^<>]*)(?.*)#'; /** - * @return array|null + * @return array|null */ final protected function parseTypeWithDocBlock(string $type): ?array { - if (preg_match(self::TYPE_REGEX, $type, $matches) === false) { + if (preg_match(self::TYPE_REGEX, $type, $matches) !== 1) { return null; } - $type = $matches[self::TYPE_KEY] ?? ''; - $docBlock = $matches[self::DOC_BLOCK_KEY] ?? null; + $type = $matches[self::TYPE_KEY]; + $docBlock = $matches[self::DOC_BLOCK_KEY]; return [$type => $docBlock]; } diff --git a/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php index 20eeb248..313a9369 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php @@ -51,15 +51,15 @@ protected function handleExpander(array $matchedType, DefinitionPropertyTransfer private function getAttributeTransfer(string $attribute): ?DefinitionAttributeTransfer { - if (preg_match(self::ATTRIBUTES_REGEX, $attribute, $matches) === false) { + if (preg_match(self::ATTRIBUTES_REGEX, $attribute, $matches) !== 1) { return null; } - $namespace = $matches['namespace'] ?? ''; + $namespace = $matches['namespace']; $namespaceTransfer = $this->namespaceBuilder->createNamespaceTransfer($namespace); $builtInTypeTransfer = new DefinitionAttributeTransfer(); - $builtInTypeTransfer->arguments = $matches['arguments'] ?? null; + $builtInTypeTransfer->arguments = $matches['arguments']; $builtInTypeTransfer->namespace = $namespaceTransfer; return $builtInTypeTransfer; diff --git a/tests/integration/TransferGenerator/Generated/Error/AddressStatisticsTransfer.php b/tests/integration/TransferGenerator/Generated/Error/AddressStatisticsTransfer.php index 8cbb508b..98e746f4 100644 --- a/tests/integration/TransferGenerator/Generated/Error/AddressStatisticsTransfer.php +++ b/tests/integration/TransferGenerator/Generated/Error/AddressStatisticsTransfer.php @@ -5,6 +5,7 @@ namespace Picamator\Tests\Integration\TransferObject\TransferGenerator\Generated\Error; use Picamator\TransferObject\Transfer\AbstractTransfer; +use Picamator\TransferObject\Transfer\Attribute\Transformer\TransferTransformerAttribute; /** * Specification: @@ -13,22 +14,43 @@ * * Note: Do not manually edit this file, as changes will be overwritten. * - * @see /tests/integration/TransferGenerator/data/config/error/invalid-attribute-name/definition/address-statistics.transfer.yml Definition file path. + * @see /tests/integration/TransferGenerator/data/config/error/unsupported-type/definition/address-statistics.transfer.yml Definition file path. */ final class AddressStatisticsTransfer extends AbstractTransfer { - protected const int META_DATA_SIZE = 1; + protected const int META_DATA_SIZE = 2; protected const array META_DATA = [ - self::ADDRESS_BOOK_UUID_INDEX => self::ADDRESS_BOOK_UUID, + self::ADDRESS_BOOK_UUID_PROP => self::ADDRESS_BOOK_UUID_INDEX, + self::ADDRESS_UUID_PROP => self::ADDRESS_UUID_INDEX, + ]; + + protected const array META_TRANSFORMERS = [ + self::ADDRESS_BOOK_UUID_PROP => 'ADDRESS_BOOK_UUID_PROP', + self::ADDRESS_UUID_PROP => 'ADDRESS_UUID_PROP', ]; // addressBookUuid - public const string ADDRESS_BOOK_UUID = 'addressBookUuid'; + #[TransferTransformerAttribute(objectTransfer::class)] + public const string ADDRESS_BOOK_UUID_PROP = 'addressBookUuid'; private const int ADDRESS_BOOK_UUID_INDEX = 0; - public ?string $addressBookUuid { + public ?objectTransfer $addressBookUuid { get => $this->getData(self::ADDRESS_BOOK_UUID_INDEX); - set => $this->setData(self::ADDRESS_BOOK_UUID_INDEX, $value); + set { + $this->setData(self::ADDRESS_BOOK_UUID_INDEX, $value); + } + } + + // addressUuid + #[TransferTransformerAttribute(stringTransfer::class)] + public const string ADDRESS_UUID_PROP = 'addressUuid'; + private const int ADDRESS_UUID_INDEX = 1; + + public ?stringTransfer $addressUuid { + get => $this->getData(self::ADDRESS_UUID_INDEX); + set { + $this->setData(self::ADDRESS_UUID_INDEX, $value); + } } } diff --git a/tests/unit/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpanderTest.php b/tests/unit/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpanderTest.php index 6cce44a0..a6d9795b 100644 --- a/tests/unit/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpanderTest.php +++ b/tests/unit/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpanderTest.php @@ -4,6 +4,7 @@ namespace Picamator\Tests\Unit\TransferObject\DefinitionGenerator\Content\Expander; +use ArrayObject; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; @@ -13,6 +14,8 @@ use Picamator\TransferObject\DefinitionGenerator\Content\Expander\BuiltInTypeBuilderExpander; use Picamator\TransferObject\DefinitionGenerator\Exception\DefinitionGeneratorException; use Picamator\TransferObject\Generated\DefinitionBuilderTransfer; +use Picamator\TransferObject\Generated\DefinitionContentTransfer; +use Picamator\TransferObject\TransferGenerator\Definition\Enum\BuiltInTypeEnum; use stdClass; #[Group('definition-generator')] @@ -43,4 +46,30 @@ public function testUnsupportedTypeShouldThrowException(): void // Act $this->expander->expandBuilderTransfer($content, $builderTransfer); } + + #[TestDox('Array Object Type')] + public function testArrayObjectType(): void + { + // Arrange + $content = new Content( + type: GetTypeEnum::object, + propertyName: 'someArrayObject', + propertyValue: new ArrayObject(['test' => 1]), + ); + + $builderTransfer = new DefinitionBuilderTransfer(); + $builderTransfer->definitionContent = new DefinitionContentTransfer(); + + // Act + $this->expander->expandBuilderTransfer($content, $builderTransfer); + + // Assert + $this->assertCount(1, $builderTransfer->definitionContent->properties); + + /** @var \Picamator\TransferObject\Generated\DefinitionPropertyTransfer $propertyTransfer */ + $propertyTransfer = $builderTransfer->definitionContent->properties[0]; + $this->assertSame($content->propertyName, $propertyTransfer->propertyName); + $this->assertSame(BuiltInTypeEnum::ARRAY_OBJECT, $propertyTransfer->builtInType?->name); + $this->assertSame('', $propertyTransfer->builtInType->docBlock); + } } From cb9aba0b310c72e18f607027d16e3f401cd5a971 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Wed, 11 Feb 2026 22:03:28 +0100 Subject: [PATCH 08/17] Renamed variable on TypePropertyExpander --- .../Definition/Parser/Expander/TypePropertyExpander.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/TransferGenerator/Definition/Parser/Expander/TypePropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/TypePropertyExpander.php index 1eb14798..398936f6 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/TypePropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/TypePropertyExpander.php @@ -41,13 +41,13 @@ protected function handleExpander(string $matchedType, DefinitionPropertyTransfe private function getBuiltInTypeTransfer(string $matchedType): ?DefinitionBuiltInTypeTransfer { - $tapeWithDocBlock = $this->parseTypeWithDocBlock($matchedType); - if ($tapeWithDocBlock === null) { + $typeWithDocBlock = $this->parseTypeWithDocBlock($matchedType); + if ($typeWithDocBlock === null) { return null; } /** @var string $type */ - $type = array_key_first($tapeWithDocBlock); + $type = array_key_first($typeWithDocBlock); $type = BuiltInTypeEnum::tryFrom($type); if ($type === null) { @@ -56,7 +56,7 @@ private function getBuiltInTypeTransfer(string $matchedType): ?DefinitionBuiltIn $builtInTypeTransfer = new DefinitionBuiltInTypeTransfer(); $builtInTypeTransfer->name = $type; - $builtInTypeTransfer->docBlock = array_first($tapeWithDocBlock); + $builtInTypeTransfer->docBlock = array_first($typeWithDocBlock); return $builtInTypeTransfer; } From fc2161a9b09e557326d60039cee307e871a38936 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Wed, 11 Feb 2026 22:23:40 +0100 Subject: [PATCH 09/17] Fixed expanders when DocBlockParserTrait returns null --- .../Content/Expander/BuiltInTypeBuilderExpander.php | 5 ++--- src/Shared/Parser/DocBlockParserTrait.php | 6 +++--- .../Definition/Parser/Expander/TypePropertyExpander.php | 6 +----- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpander.php b/src/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpander.php index dfa0c63f..ac529be7 100644 --- a/src/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpander.php +++ b/src/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpander.php @@ -132,11 +132,10 @@ private function createDateTimePropertyTransfer(string $propertyName): Definitio private function createPropertyTransfer(string $propertyName, string $builtInType): DefinitionPropertyTransfer { - $tapeWithDocBlock = $this->parseTypeWithDocBlock($builtInType) ?? []; + $tapeWithDocBlock = $this->parseTypeWithDocBlock($builtInType); - /** @var string $type */ $type = array_key_first($tapeWithDocBlock); - $type = BuiltInTypeEnum::from($type); + $type = BuiltInTypeEnum::from((string)$type); $docBlock = array_first($tapeWithDocBlock); diff --git a/src/Shared/Parser/DocBlockParserTrait.php b/src/Shared/Parser/DocBlockParserTrait.php index 2e54d685..73f962be 100644 --- a/src/Shared/Parser/DocBlockParserTrait.php +++ b/src/Shared/Parser/DocBlockParserTrait.php @@ -13,12 +13,12 @@ trait DocBlockParserTrait private const string TYPE_REGEX = '#(?[^<>]*)(?.*)#'; /** - * @return array|null + * @return array */ - final protected function parseTypeWithDocBlock(string $type): ?array + final protected function parseTypeWithDocBlock(string $type): array { if (preg_match(self::TYPE_REGEX, $type, $matches) !== 1) { - return null; + return []; } $type = $matches[self::TYPE_KEY]; diff --git a/src/TransferGenerator/Definition/Parser/Expander/TypePropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/TypePropertyExpander.php index 398936f6..5fa3211b 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/TypePropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/TypePropertyExpander.php @@ -42,13 +42,9 @@ protected function handleExpander(string $matchedType, DefinitionPropertyTransfe private function getBuiltInTypeTransfer(string $matchedType): ?DefinitionBuiltInTypeTransfer { $typeWithDocBlock = $this->parseTypeWithDocBlock($matchedType); - if ($typeWithDocBlock === null) { - return null; - } - /** @var string $type */ $type = array_key_first($typeWithDocBlock); - $type = BuiltInTypeEnum::tryFrom($type); + $type = BuiltInTypeEnum::tryFrom((string)$type); if ($type === null) { return null; From bbe2092a7f93af2702577fea770ca98960004aa0 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Fri, 13 Feb 2026 21:08:43 +0100 Subject: [PATCH 10/17] Refactored DocBlockParserTrait to return object instead of array, covered by tests, trim extra spaces --- .../Expander/BuiltInTypeBuilderExpander.php | 9 ++--- src/Shared/Parser/DocBlockParserTrait.php | 23 ++++------- src/Shared/Parser/TypeDocBlock.php | 14 +++++++ .../Parser/Expander/TypePropertyExpander.php | 10 ++--- .../BuiltInTypeBuilderExpanderTest.php | 2 +- .../Shared/Parser/DocBlockParserInterface.php | 12 ++++++ .../unit/Shared/Parser/DocBlockParserTest.php | 40 +++++++++++++++++++ 7 files changed, 83 insertions(+), 27 deletions(-) create mode 100644 src/Shared/Parser/TypeDocBlock.php create mode 100644 tests/unit/Shared/Parser/DocBlockParserInterface.php create mode 100644 tests/unit/Shared/Parser/DocBlockParserTest.php diff --git a/src/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpander.php b/src/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpander.php index ac529be7..df846766 100644 --- a/src/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpander.php +++ b/src/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpander.php @@ -132,16 +132,13 @@ private function createDateTimePropertyTransfer(string $propertyName): Definitio private function createPropertyTransfer(string $propertyName, string $builtInType): DefinitionPropertyTransfer { - $tapeWithDocBlock = $this->parseTypeWithDocBlock($builtInType); + $typeDocBlock = $this->parseTypeWithDocBlock($builtInType); - $type = array_key_first($tapeWithDocBlock); - $type = BuiltInTypeEnum::from((string)$type); - - $docBlock = array_first($tapeWithDocBlock); + $type = BuiltInTypeEnum::from($typeDocBlock->type); $builtInTypeTransfer = new DefinitionBuiltInTypeTransfer(); $builtInTypeTransfer->name = $type; - $builtInTypeTransfer->docBlock = $docBlock; + $builtInTypeTransfer->docBlock = $typeDocBlock->docBlock; $propertyTransfer = new DefinitionPropertyTransfer(); $propertyTransfer->propertyName = $propertyName; diff --git a/src/Shared/Parser/DocBlockParserTrait.php b/src/Shared/Parser/DocBlockParserTrait.php index 73f962be..010173f1 100644 --- a/src/Shared/Parser/DocBlockParserTrait.php +++ b/src/Shared/Parser/DocBlockParserTrait.php @@ -6,24 +6,17 @@ trait DocBlockParserTrait { - private const string TYPE_KEY = 'type'; + private const string DOCK_BLOCK_REGEX = '#^(?[^<> ]+)\s*(?<.+>)$#'; - private const string DOC_BLOCK_KEY = 'docBlock'; - - private const string TYPE_REGEX = '#(?[^<>]*)(?.*)#'; - - /** - * @return array - */ - final protected function parseTypeWithDocBlock(string $type): array + final protected function parseTypeWithDocBlock(string $type): TypeDocBlock { - if (preg_match(self::TYPE_REGEX, $type, $matches) !== 1) { - return []; + if (!str_contains($type, '<') || preg_match(self::DOCK_BLOCK_REGEX, $type, $matches) !== 1) { + return new TypeDocBlock(type: $type); } - $type = $matches[self::TYPE_KEY]; - $docBlock = $matches[self::DOC_BLOCK_KEY]; - - return [$type => $docBlock]; + return new TypeDocBlock( + type: $matches['type'], + docBlock: $matches['docBlock'], + ); } } diff --git a/src/Shared/Parser/TypeDocBlock.php b/src/Shared/Parser/TypeDocBlock.php new file mode 100644 index 00000000..65b9076b --- /dev/null +++ b/src/Shared/Parser/TypeDocBlock.php @@ -0,0 +1,14 @@ +parseTypeWithDocBlock($matchedType); - - $type = array_key_first($typeWithDocBlock); - $type = BuiltInTypeEnum::tryFrom((string)$type); + $typeDocBlock = $this->parseTypeWithDocBlock($matchedType); + $type = BuiltInTypeEnum::tryFrom($typeDocBlock->type); if ($type === null) { return null; @@ -52,7 +52,7 @@ private function getBuiltInTypeTransfer(string $matchedType): ?DefinitionBuiltIn $builtInTypeTransfer = new DefinitionBuiltInTypeTransfer(); $builtInTypeTransfer->name = $type; - $builtInTypeTransfer->docBlock = array_first($typeWithDocBlock); + $builtInTypeTransfer->docBlock = $typeDocBlock->docBlock; return $builtInTypeTransfer; } diff --git a/tests/unit/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpanderTest.php b/tests/unit/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpanderTest.php index a6d9795b..ee49cdce 100644 --- a/tests/unit/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpanderTest.php +++ b/tests/unit/DefinitionGenerator/Content/Expander/BuiltInTypeBuilderExpanderTest.php @@ -70,6 +70,6 @@ public function testArrayObjectType(): void $propertyTransfer = $builderTransfer->definitionContent->properties[0]; $this->assertSame($content->propertyName, $propertyTransfer->propertyName); $this->assertSame(BuiltInTypeEnum::ARRAY_OBJECT, $propertyTransfer->builtInType?->name); - $this->assertSame('', $propertyTransfer->builtInType->docBlock); + $this->assertNull($propertyTransfer->builtInType->docBlock); } } diff --git a/tests/unit/Shared/Parser/DocBlockParserInterface.php b/tests/unit/Shared/Parser/DocBlockParserInterface.php new file mode 100644 index 00000000..5e4cd2d4 --- /dev/null +++ b/tests/unit/Shared/Parser/DocBlockParserInterface.php @@ -0,0 +1,12 @@ +docBlockParser = new class () implements DocBlockParserInterface { + use DocBlockParserTrait { + parseTypeWithDocBlock as public; + } + }; + } + + #[TestWith(['ArrayObject', 'ArrayObject', null])] + #[TestWith(['ArrayObject', 'ArrayObject', ''])] + #[TestWith(['ArrayObject ', 'ArrayObject', ''])] + #[TestDox('Parse type "$type" expected type "$expectedType" and docBlock "$expectedDocBlock"')] + public function testParseTypeWithDocBlock(string $type, string $expectedType, ?string $expectedDocBlock): void + { + // Act + $actual = $this->docBlockParser->parseTypeWithDocBlock($type); + + // Assert + $this->assertSame($expectedType, $actual->type); + $this->assertSame($expectedDocBlock, $actual->docBlock); + } +} From 1a7c1685dabba71e7195c597c1703bd4a6c03fed Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Fri, 13 Feb 2026 21:16:14 +0100 Subject: [PATCH 11/17] Changed reges on AttributesPropertyExpander to skip spaces between Attribute and argument definition --- .../Definition/Parser/Expander/AttributesPropertyExpander.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php index 313a9369..5dba24f7 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php @@ -14,7 +14,7 @@ final class AttributesPropertyExpander implements PropertyExpanderInterface private const string ATTRIBUTES_KEY = 'attributes'; - private const string ATTRIBUTES_REGEX = '#(?[^()]*)(?.*)#'; + private const string ATTRIBUTES_REGEX = '#^(?[^()]+)\s*(?.*)$#'; public function __construct( private readonly NamespaceBuilderInterface $namespaceBuilder, From bf789a9c0febe0603fae9fa623551b7a691cde62 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Fri, 13 Feb 2026 21:17:49 +0100 Subject: [PATCH 12/17] Removed varaible on AttributesPropertyExpander --- .../Definition/Parser/Expander/AttributesPropertyExpander.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php index 5dba24f7..1cbc30c6 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php @@ -55,8 +55,7 @@ private function getAttributeTransfer(string $attribute): ?DefinitionAttributeTr return null; } - $namespace = $matches['namespace']; - $namespaceTransfer = $this->namespaceBuilder->createNamespaceTransfer($namespace); + $namespaceTransfer = $this->namespaceBuilder->createNamespaceTransfer($matches['namespace']); $builtInTypeTransfer = new DefinitionAttributeTransfer(); $builtInTypeTransfer->arguments = $matches['arguments']; From 47d85cc4a0ebe893c320ff2880c5847407eff911 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Fri, 13 Feb 2026 21:28:05 +0100 Subject: [PATCH 13/17] Allowed more arguments on docker/sdk cli --- docker/sdk | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docker/sdk b/docker/sdk index b689e14c..9cde8ec9 100755 --- a/docker/sdk +++ b/docker/sdk @@ -84,8 +84,14 @@ case $1 in docker compose stop ;; cli) - if [ -n "$2" ]; then - $DOCKER_EXEC php -f $2 + if [ -n "$5" ]; then + $DOCKER_EXEC php "$2" "$3" "$4" "$5" + elif [ -n "$4" ]; then + $DOCKER_EXEC php "$2" "$3" "$4" + elif [ -n "$3" ]; then + $DOCKER_EXEC php "$2" "$3" + elif [ -n "$2" ]; then + $DOCKER_EXEC php $2 else $DOCKER_EXEC bash fi From 2cabb4292d6e54b6ffe5683aa710b03a1d5a2bd1 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Fri, 13 Feb 2026 22:38:09 +0100 Subject: [PATCH 14/17] Actualized OpenWeather url and response sample --- README.md | 2 +- .../DefinitionGenerator/data/README.md | 2 +- .../data/api-response/open-weather.json | 94 +++++++++---------- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index a85f56fc..d2ed2cc0 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ Usage Tests Definition Files and Transfer Object generators have been tested against the following APIs: * [NASA Open Api](https://api.nasa.gov/neo/rest/v1/neo/2465633?api_key=DEMO_KEY) -* [OpenWeather](https://openweathermap.org/current#example_JSON) +* [OpenWeather](https://openweathermap.org/current?collection=current_forecast#example_JSON) * [Content API for Shopping](https://developers.google.com/shopping-content/guides/products/products-api?hl=en) * [Frankfurter is a free, open-source currency data API](https://api.frankfurter.dev/v1/latest) * [Tagesschau API](https://tagesschau.api.bund.dev) diff --git a/tests/integration/DefinitionGenerator/data/README.md b/tests/integration/DefinitionGenerator/data/README.md index 613d4731..05c5745a 100644 --- a/tests/integration/DefinitionGenerator/data/README.md +++ b/tests/integration/DefinitionGenerator/data/README.md @@ -4,7 +4,7 @@ API Response Reference | File | Source | |---------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------| | [nasa-neo-rest-v1-neo-2465633.json](/tests/integration/DefinitionGenerator/data/api-response/nasa-neo-rest-v1-neo-2465633.json) | [NASA Open Api](https://api.nasa.gov/neo/rest/v1/neo/2465633?api_key=DEMO_KEY) | -| [open-weather.json](/tests/integration/DefinitionGenerator/data/api-response/open-weather.json) | [OpenWeather](https://openweathermap.org/current#example_JSON) | +| [open-weather.json](/tests/integration/DefinitionGenerator/data/api-response/open-weather.json) | [OpenWeather](https://openweathermap.org/current?collection=current_forecast#example_JSON) | | [google-shopping-content.json](/tests/integration/DefinitionGenerator/data/api-response/google-shopping-content.json) | [Content API for Shopping](https://developers.google.com/shopping-content/guides/products/products-api?hl=en) | | [frankfurter-dev.json](/tests/integration/DefinitionGenerator/data/api-response/frankfurter-dev-v1.json) | [Frankfurter is a free, open-source currency data API](https://api.frankfurter.dev/v1/latest) | | [tagesschau-api-bund-dev.json](/tests/integration/DefinitionGenerator/data/api-response/tagesschau-api-bund-dev-v2.json) | [Tagesschau API](https://tagesschau.api.bund.dev) | diff --git a/tests/integration/DefinitionGenerator/data/api-response/open-weather.json b/tests/integration/DefinitionGenerator/data/api-response/open-weather.json index 6c9d348f..13e7f17b 100644 --- a/tests/integration/DefinitionGenerator/data/api-response/open-weather.json +++ b/tests/integration/DefinitionGenerator/data/api-response/open-weather.json @@ -1,49 +1,49 @@ { - "coord": { - "lon": 7.367, - "lat": 45.133 - }, - "weather": [ - { - "id": 501, - "main": "Rain", - "description": "moderate rain", - "icon": "10d" - } - ], - "base": "stations", - "main": { - "temp": 284.2, - "feels_like": 282.93, - "temp_min": 283.06, - "temp_max": 286.82, - "pressure": 1021, - "humidity": 60, - "sea_level": 1021, - "grnd_level": 910 - }, - "visibility": 10000, - "wind": { - "speed": 4.09, - "deg": 121, - "gust": 3.47 - }, - "rain": { - "1h": 2.73 - }, - "clouds": { - "all": 83 - }, - "dt": 1726660758, - "sys": { - "type": 1, - "id": 6736, - "country": "IT", - "sunrise": 1726636384, - "sunset": 1726680975 - }, - "timezone": 7200, - "id": 3165523, - "name": "Province of Turin", - "cod": 200 + "coord": { + "lon": 7.367, + "lat": 45.133 + }, + "weather": [ + { + "id": 501, + "main": "Rain", + "description": "moderate rain", + "icon": "10d" + } + ], + "base": "stations", + "main": { + "temp": 284.2, + "feels_like": 282.93, + "temp_min": 283.06, + "temp_max": 286.82, + "pressure": 1021, + "humidity": 60, + "sea_level": 1021, + "grnd_level": 910 + }, + "visibility": 10000, + "wind": { + "speed": 4.09, + "deg": 121, + "gust": 3.47 + }, + "rain": { + "1h": 2.73 + }, + "clouds": { + "all": 83 + }, + "dt": 1726660758, + "sys": { + "type": 1, + "id": 6736, + "country": "IT", + "sunrise": 1726636384, + "sunset": 1726680975 + }, + "timezone": 7200, + "id": 3165523, + "name": "Province of Turin", + "cod": 200 } From 24fc560f5fe59177cb32c0a9ffb4cc81d17d94f2 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Fri, 13 Feb 2026 23:00:34 +0100 Subject: [PATCH 15/17] Added Wero Payment Wallet api response for the data provider --- README.md | 5 +- .../DefinitionGeneratorFacadeTest.php | 16 ++++ .../Generated/Wero/AmountTransfer.php | 48 ++++++++++ .../Wero/AuthenticationSettingsTransfer.php | 54 +++++++++++ .../Generated/Wero/ConsumerTransfer.php | 36 ++++++++ .../Generated/Wero/PaymentChargesTransfer.php | 91 +++++++++++++++++++ .../Generated/Wero/SettingsTransfer.php | 36 ++++++++ .../DefinitionGenerator/data/README.md | 7 +- .../api-response/wero-payment-charges-v1.json | 18 ++++ .../definition/paymentCharges.transfer.yml | 37 ++++++++ .../generator.config.yml | 5 + 11 files changed, 348 insertions(+), 5 deletions(-) create mode 100644 tests/integration/DefinitionGenerator/Generated/Wero/AmountTransfer.php create mode 100644 tests/integration/DefinitionGenerator/Generated/Wero/AuthenticationSettingsTransfer.php create mode 100644 tests/integration/DefinitionGenerator/Generated/Wero/ConsumerTransfer.php create mode 100644 tests/integration/DefinitionGenerator/Generated/Wero/PaymentChargesTransfer.php create mode 100644 tests/integration/DefinitionGenerator/Generated/Wero/SettingsTransfer.php create mode 100644 tests/integration/DefinitionGenerator/data/api-response/wero-payment-charges-v1.json create mode 100644 tests/integration/DefinitionGenerator/data/config/wero-payment-charges-v1/definition/paymentCharges.transfer.yml create mode 100644 tests/integration/DefinitionGenerator/data/config/wero-payment-charges-v1/generator.config.yml diff --git a/README.md b/README.md index d2ed2cc0..73d991c4 100644 --- a/README.md +++ b/README.md @@ -116,10 +116,11 @@ Definition Files and Transfer Object generators have been tested against the fol * [NASA Open Api](https://api.nasa.gov/neo/rest/v1/neo/2465633?api_key=DEMO_KEY) * [OpenWeather](https://openweathermap.org/current?collection=current_forecast#example_JSON) -* [Content API for Shopping](https://developers.google.com/shopping-content/guides/products/products-api?hl=en) -* [Frankfurter is a free, open-source currency data API](https://api.frankfurter.dev/v1/latest) +* [Google Content API for Shopping](https://developers.google.com/shopping-content/guides/products/products-api?hl=en) +* [Frankfurter - open-source currency data API](https://api.frankfurter.dev/v1/latest) * [Tagesschau API](https://tagesschau.api.bund.dev) * [Statistisches Bundesamt (Destatis)](https://www-genesis.destatis.de/genesisWS/swagger-ui/index.html#/find/findPost) +* [Wero - Digital Payment Wallet](https://developerhub.ppro.com/global-api/docs/wero) ### Scenario diff --git a/tests/integration/DefinitionGenerator/DefinitionGeneratorFacadeTest.php b/tests/integration/DefinitionGenerator/DefinitionGeneratorFacadeTest.php index 353ce155..bf629c1f 100644 --- a/tests/integration/DefinitionGenerator/DefinitionGeneratorFacadeTest.php +++ b/tests/integration/DefinitionGenerator/DefinitionGeneratorFacadeTest.php @@ -17,6 +17,7 @@ use Picamator\Tests\Integration\TransferObject\DefinitionGenerator\Generated\NasaNeo\AsteroidTransfer; use Picamator\Tests\Integration\TransferObject\DefinitionGenerator\Generated\OpenWeather\ForecastTransfer; use Picamator\Tests\Integration\TransferObject\DefinitionGenerator\Generated\Tagesschau\ArdNewsTransfer; +use Picamator\Tests\Integration\TransferObject\DefinitionGenerator\Generated\Wero\PaymentChargesTransfer; use Picamator\Tests\Integration\TransferObject\Helper\DefinitionGeneratorTrait; use Picamator\Tests\Integration\TransferObject\Helper\FilterArrayTrait; use Picamator\Tests\Integration\TransferObject\Helper\TransferGeneratorTrait; @@ -113,6 +114,12 @@ public static function generateDefinitionDataProvider(): Generator 'sampleFileName' => 'genesis-destatis-find.json', 'definitionFileName' => 'destatis.transfer.yml', ]; + + yield 'Wero' => [ + 'className' => 'PaymentCharges', + 'sampleFileName' => 'wero-payment-charges-v1.json', + 'definitionFileName' => 'paymentCharges.transfer.yml', + ]; } #[DataProvider('configPathDataProvider')] @@ -160,6 +167,10 @@ public static function configPathDataProvider(): Generator yield 'Destatis' => [ 'genesis-destatis-find.json', ]; + + yield 'Wero' => [ + 'wero-payment-charges-v1.json', + ]; } #[DataProvider('matchDefinitionDataProvider')] @@ -215,6 +226,11 @@ public static function matchDefinitionDataProvider(): Generator DestatisTransfer::class, 'genesis-destatis-find.json', ]; + + yield 'Wero' => [ + PaymentChargesTransfer::class, + 'wero-payment-charges-v1.json', + ]; } #[DataProvider('matchFilteredDefinitionDataProvider')] diff --git a/tests/integration/DefinitionGenerator/Generated/Wero/AmountTransfer.php b/tests/integration/DefinitionGenerator/Generated/Wero/AmountTransfer.php new file mode 100644 index 00000000..d5d50ace --- /dev/null +++ b/tests/integration/DefinitionGenerator/Generated/Wero/AmountTransfer.php @@ -0,0 +1,48 @@ + self::CURRENCY_INDEX, + self::VALUE_PROP => self::VALUE_INDEX, + ]; + + // currency + public const string CURRENCY_PROP = 'currency'; + private const int CURRENCY_INDEX = 0; + + public ?string $currency { + get => $this->getData(self::CURRENCY_INDEX); + set { + $this->setData(self::CURRENCY_INDEX, $value); + } + } + + // value + public const string VALUE_PROP = 'value'; + private const int VALUE_INDEX = 1; + + public ?int $value { + get => $this->getData(self::VALUE_INDEX); + set { + $this->setData(self::VALUE_INDEX, $value); + } + } +} diff --git a/tests/integration/DefinitionGenerator/Generated/Wero/AuthenticationSettingsTransfer.php b/tests/integration/DefinitionGenerator/Generated/Wero/AuthenticationSettingsTransfer.php new file mode 100644 index 00000000..48683f02 --- /dev/null +++ b/tests/integration/DefinitionGenerator/Generated/Wero/AuthenticationSettingsTransfer.php @@ -0,0 +1,54 @@ + self::SETTINGS_INDEX, + self::TYPE_PROP => self::TYPE_INDEX, + ]; + + protected const array META_TRANSFORMERS = [ + self::SETTINGS_PROP => 'SETTINGS_PROP', + ]; + + // settings + #[TransferTransformerAttribute(SettingsTransfer::class)] + public const string SETTINGS_PROP = 'settings'; + private const int SETTINGS_INDEX = 0; + + public ?SettingsTransfer $settings { + get => $this->getData(self::SETTINGS_INDEX); + set { + $this->setData(self::SETTINGS_INDEX, $value); + } + } + + // type + public const string TYPE_PROP = 'type'; + private const int TYPE_INDEX = 1; + + public ?string $type { + get => $this->getData(self::TYPE_INDEX); + set { + $this->setData(self::TYPE_INDEX, $value); + } + } +} diff --git a/tests/integration/DefinitionGenerator/Generated/Wero/ConsumerTransfer.php b/tests/integration/DefinitionGenerator/Generated/Wero/ConsumerTransfer.php new file mode 100644 index 00000000..86949006 --- /dev/null +++ b/tests/integration/DefinitionGenerator/Generated/Wero/ConsumerTransfer.php @@ -0,0 +1,36 @@ + self::COUNTRY_INDEX, + ]; + + // country + public const string COUNTRY_PROP = 'country'; + private const int COUNTRY_INDEX = 0; + + public ?string $country { + get => $this->getData(self::COUNTRY_INDEX); + set { + $this->setData(self::COUNTRY_INDEX, $value); + } + } +} diff --git a/tests/integration/DefinitionGenerator/Generated/Wero/PaymentChargesTransfer.php b/tests/integration/DefinitionGenerator/Generated/Wero/PaymentChargesTransfer.php new file mode 100644 index 00000000..3cf15dc7 --- /dev/null +++ b/tests/integration/DefinitionGenerator/Generated/Wero/PaymentChargesTransfer.php @@ -0,0 +1,91 @@ + self::AMOUNT_INDEX, + self::AUTHENTICATION_SETTINGS_PROP => self::AUTHENTICATION_SETTINGS_INDEX, + self::CONSUMER_PROP => self::CONSUMER_INDEX, + self::PAYMENT_METHOD_PROP => self::PAYMENT_METHOD_INDEX, + ]; + + protected const array META_INITIATORS = [ + self::AUTHENTICATION_SETTINGS_PROP => 'AUTHENTICATION_SETTINGS_PROP', + ]; + + protected const array META_TRANSFORMERS = [ + self::AMOUNT_PROP => 'AMOUNT_PROP', + self::AUTHENTICATION_SETTINGS_PROP => 'AUTHENTICATION_SETTINGS_PROP', + self::CONSUMER_PROP => 'CONSUMER_PROP', + ]; + + // amount + #[TransferTransformerAttribute(AmountTransfer::class)] + public const string AMOUNT_PROP = 'amount'; + private const int AMOUNT_INDEX = 0; + + public ?AmountTransfer $amount { + get => $this->getData(self::AMOUNT_INDEX); + set { + $this->setData(self::AMOUNT_INDEX, $value); + } + } + + // authenticationSettings + #[ArrayObjectInitiatorAttribute] + #[CollectionTransformerAttribute(AuthenticationSettingsTransfer::class)] + public const string AUTHENTICATION_SETTINGS_PROP = 'authenticationSettings'; + private const int AUTHENTICATION_SETTINGS_INDEX = 1; + + /** @var \ArrayObject */ + public ArrayObject $authenticationSettings { + get => $this->getData(self::AUTHENTICATION_SETTINGS_INDEX); + set { + $this->setData(self::AUTHENTICATION_SETTINGS_INDEX, $value); + } + } + + // consumer + #[TransferTransformerAttribute(ConsumerTransfer::class)] + public const string CONSUMER_PROP = 'consumer'; + private const int CONSUMER_INDEX = 2; + + public ?ConsumerTransfer $consumer { + get => $this->getData(self::CONSUMER_INDEX); + set { + $this->setData(self::CONSUMER_INDEX, $value); + } + } + + // paymentMethod + public const string PAYMENT_METHOD_PROP = 'paymentMethod'; + private const int PAYMENT_METHOD_INDEX = 3; + + public ?string $paymentMethod { + get => $this->getData(self::PAYMENT_METHOD_INDEX); + set { + $this->setData(self::PAYMENT_METHOD_INDEX, $value); + } + } +} diff --git a/tests/integration/DefinitionGenerator/Generated/Wero/SettingsTransfer.php b/tests/integration/DefinitionGenerator/Generated/Wero/SettingsTransfer.php new file mode 100644 index 00000000..910195fa --- /dev/null +++ b/tests/integration/DefinitionGenerator/Generated/Wero/SettingsTransfer.php @@ -0,0 +1,36 @@ + self::RETURN_URL_INDEX, + ]; + + // returnUrl + public const string RETURN_URL_PROP = 'returnUrl'; + private const int RETURN_URL_INDEX = 0; + + public ?string $returnUrl { + get => $this->getData(self::RETURN_URL_INDEX); + set { + $this->setData(self::RETURN_URL_INDEX, $value); + } + } +} diff --git a/tests/integration/DefinitionGenerator/data/README.md b/tests/integration/DefinitionGenerator/data/README.md index 05c5745a..fef3b8d6 100644 --- a/tests/integration/DefinitionGenerator/data/README.md +++ b/tests/integration/DefinitionGenerator/data/README.md @@ -4,8 +4,9 @@ API Response Reference | File | Source | |---------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------| | [nasa-neo-rest-v1-neo-2465633.json](/tests/integration/DefinitionGenerator/data/api-response/nasa-neo-rest-v1-neo-2465633.json) | [NASA Open Api](https://api.nasa.gov/neo/rest/v1/neo/2465633?api_key=DEMO_KEY) | -| [open-weather.json](/tests/integration/DefinitionGenerator/data/api-response/open-weather.json) | [OpenWeather](https://openweathermap.org/current?collection=current_forecast#example_JSON) | -| [google-shopping-content.json](/tests/integration/DefinitionGenerator/data/api-response/google-shopping-content.json) | [Content API for Shopping](https://developers.google.com/shopping-content/guides/products/products-api?hl=en) | -| [frankfurter-dev.json](/tests/integration/DefinitionGenerator/data/api-response/frankfurter-dev-v1.json) | [Frankfurter is a free, open-source currency data API](https://api.frankfurter.dev/v1/latest) | +| [open-weather.json](/tests/integration/DefinitionGenerator/data/api-response/open-weather.json) | [OpenWeather](https://openweathermap.org/current?collection=current_forecast#example_JSON) | +| [google-shopping-content.json](/tests/integration/DefinitionGenerator/data/api-response/google-shopping-content.json) | [Google Content API for Shopping](https://developers.google.com/shopping-content/guides/products/products-api?hl=en) | +| [frankfurter-dev.json](/tests/integration/DefinitionGenerator/data/api-response/frankfurter-dev-v1.json) | [Frankfurter - open-source currency data API](https://api.frankfurter.dev/v1/latest) | | [tagesschau-api-bund-dev.json](/tests/integration/DefinitionGenerator/data/api-response/tagesschau-api-bund-dev-v2.json) | [Tagesschau API](https://tagesschau.api.bund.dev) | | [genesis-destatis-find.json](/tests/integration/DefinitionGenerator/data/api-response/genesis-destatis-find.json) | [Statistisches Bundesamt (Destatis)](https://www-genesis.destatis.de/genesisWS/swagger-ui/index.html#/find/findPost) | +| [wero-payment-charges-v1.json](/tests/integration/DefinitionGenerator/data/api-response/wero-payment-charges-v1.json) | [Wero - Digital Payment Wallet](https://developerhub.ppro.com/global-api/docs/wero) | diff --git a/tests/integration/DefinitionGenerator/data/api-response/wero-payment-charges-v1.json b/tests/integration/DefinitionGenerator/data/api-response/wero-payment-charges-v1.json new file mode 100644 index 00000000..2808c629 --- /dev/null +++ b/tests/integration/DefinitionGenerator/data/api-response/wero-payment-charges-v1.json @@ -0,0 +1,18 @@ +{ + "paymentMethod": "WERO", + "amount": { + "value": 1000, + "currency": "EUR" + }, + "consumer": { + "country": "DE" + }, + "authenticationSettings": [ + { + "type": "REDIRECT", + "settings": { + "returnUrl": "https://www.webshop.com/order-results-page" + } + } + ] +} diff --git a/tests/integration/DefinitionGenerator/data/config/wero-payment-charges-v1/definition/paymentCharges.transfer.yml b/tests/integration/DefinitionGenerator/data/config/wero-payment-charges-v1/definition/paymentCharges.transfer.yml new file mode 100644 index 00000000..eecbe227 --- /dev/null +++ b/tests/integration/DefinitionGenerator/data/config/wero-payment-charges-v1/definition/paymentCharges.transfer.yml @@ -0,0 +1,37 @@ +# $schema: https://raw.githubusercontent.com/picamator/transfer-object/main/schema/definition.schema.json + +# PaymentCharges +PaymentCharges: + paymentMethod: + type: string + amount: + type: Amount + consumer: + type: Consumer + authenticationSettings: + collectionType: AuthenticationSettings + +# Amount +Amount: + value: + type: int + currency: + type: string + +# Consumer +Consumer: + country: + type: string + +# AuthenticationSettings +AuthenticationSettings: + type: + type: string + settings: + type: Settings + +# Settings +Settings: + returnUrl: + type: string + diff --git a/tests/integration/DefinitionGenerator/data/config/wero-payment-charges-v1/generator.config.yml b/tests/integration/DefinitionGenerator/data/config/wero-payment-charges-v1/generator.config.yml new file mode 100644 index 00000000..a3b75551 --- /dev/null +++ b/tests/integration/DefinitionGenerator/data/config/wero-payment-charges-v1/generator.config.yml @@ -0,0 +1,5 @@ +# $schema: ./../../../../../../schema/config.schema.json +generator: + transferNamespace: "Picamator\\Tests\\Integration\\TransferObject\\DefinitionGenerator\\Generated\\Wero" + transferPath: "${PROJECT_ROOT}/tests/integration/DefinitionGenerator/Generated/Wero" + definitionPath: "${PROJECT_ROOT}/tests/integration/DefinitionGenerator/data/config/wero-payment-charges-v1/definition" From 3bb783f755ff987f0442f23993eb1b9a66f8fdc7 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Fri, 13 Feb 2026 23:29:43 +0100 Subject: [PATCH 16/17] Unified error messages --- src/Shared/Filesystem/FileAppender.php | 4 ++-- src/Shared/Filesystem/FileReader.php | 2 +- src/Shared/Reader/FileReaderProgress.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Shared/Filesystem/FileAppender.php b/src/Shared/Filesystem/FileAppender.php index 9b65f13b..1aef156f 100644 --- a/src/Shared/Filesystem/FileAppender.php +++ b/src/Shared/Filesystem/FileAppender.php @@ -20,7 +20,7 @@ public function appendToFile(string $filename, string $content): void if ($writeResult === false) { throw new FileAppenderException( - sprintf('Failed to write content into the file "%s".', $filename), + sprintf('Failed to write content to file "%s".', $filename), ); } } @@ -35,7 +35,7 @@ public function closeFile(string $filename): void $isClosed = $this->fclose($file); if ($isClosed === false) { throw new FileAppenderException( - sprintf('Failed to close the file "%s".', $filename), + sprintf('Failed to close file "%s".', $filename), ); } diff --git a/src/Shared/Filesystem/FileReader.php b/src/Shared/Filesystem/FileReader.php index 12d72fce..9a7127d7 100644 --- a/src/Shared/Filesystem/FileReader.php +++ b/src/Shared/Filesystem/FileReader.php @@ -61,7 +61,7 @@ private function assertEndOfFile($file, string $filename, int $fileLine): void $this->fclose($file); throw new FileReaderException( - sprintf('Failed to read file "%s" line "%d".', $filename, $fileLine), + sprintf('Failed to read file "%s" at line "%d".', $filename, $fileLine), ); } diff --git a/src/Shared/Reader/FileReaderProgress.php b/src/Shared/Reader/FileReaderProgress.php index f051a0d5..e66e5c3d 100644 --- a/src/Shared/Reader/FileReaderProgress.php +++ b/src/Shared/Reader/FileReaderProgress.php @@ -60,9 +60,9 @@ private function getTotalBytes(string $filename): int if ($fileSize === false) { throw new FileReaderException( sprintf( - 'Failed to get file size "%s". Error: "%s".', + 'Failed to get size for file "%s". Error: "%s".', $filename, - error_get_last()['message'] ?? '', + error_get_last()['message'] ?? 'Unknown error.', ), ); } From 62ad1e843b5869c9c8571a8d3bfb1b1f609a89c7 Mon Sep 17 00:00:00 2001 From: Sergii Pryz Date: Fri, 13 Feb 2026 23:32:42 +0100 Subject: [PATCH 17/17] Actualised tests section on AGENTS.md --- AGENTS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 4e18c861..4c43d9b8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -100,7 +100,8 @@ Code Style ### Tests -- all tests classes should be a `final` +- tests classes should be a `final` +- tests should have at least one test group Module Structure ----------------