diff --git a/.aiignore b/.aiignore index 38f7c569..df178378 100644 --- a/.aiignore +++ b/.aiignore @@ -4,3 +4,6 @@ # cache and debug var/cache/ var/xdebug-profile/ + +# secret +.env diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0fd60494..b1b1596c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -14,8 +14,9 @@ Type of Change Please delete options that are not relevant: - [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] Improvements (non-breaking changes such as refactoring, performance optimization, etc.) - [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Breaking change (bug fix, new feature, dependency upgrade, etc.) - [ ] Documentation update - [ ] Other (please specify): diff --git a/AGENTS.md b/AGENTS.md index 4c43d9b8..77692075 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -43,20 +43,20 @@ Directory Structure ### Examples -- `examples`: samples how to use `DefinitionGeneratorFacade` and `TransferGeneratorFacade` +- `examples`: samples on how to use `DefinitionGeneratorFacade` and `TransferGeneratorFacade` ### Source - `src`: code source - `src/Command`: Symfony console commands to generate definition and transfer object files - `src/DefinitionGenerator`: definition generator module -- `src/Dependency`: wrapper over 3-part dependencies +- `src/Dependency`: wrapper over third-party dependencies - `src/Generated`: directory where generated transfer objects are saved * should not contain any custom-written code - * each transfer object generator run will overwrite all the files in the directory + * each transfer object generator run overwrites all the files in the directory * can be used across modules -- `src/Generated/_tmp`: temporary directory includes newly generated transfer objects before they are finally moved to the `src/Generated` - * in case of an unexpected error, the directory might not be deleted +- `src/Generated/_tmp`: temporary directory including newly generated transfer objects before they are finally moved to the `src/Generated` + * in case of an unexpected error, the directory might not be deleted. - `src/Shared`: contains code shared across modules * can be used across modules - `src/Transfer`: transfer object module @@ -100,7 +100,7 @@ Code Style ### Tests -- tests classes should be a `final` +- test classes should be `final` - tests should have at least one test group Module Structure @@ -108,12 +108,12 @@ Module Structure #### Facade -- each module should have a facade class with an interface -- the facade class and interface name should include the module name with `Facade` suffix -- the facade is used for communication between modules -- the facade used factories -- the facade should not include any business logic -- the facade `public` methods should have specification doc-block +- each module should have a facade class with an interface. +- the facade class and interface name should include the module name with `Facade` suffix. +- the facade is used for communication between modules. +- the facade uses factories. +- the facade should not include any business logic. +- the facade `public` methods should have a specification doc-block. ### Factory @@ -129,26 +129,34 @@ Module Structure Unit and Integration Tests -------------------------- -- tests should follow a similar structure to the existing ones -- separate test implementation by comment sections: "Arrange", "Act", "Assert" (optionally with "Expect") -- use `setUp` method to initialize the tested object's stubs and mocks -- use `PHPUnit` attributes -- use [PHP generator](https://www.php.net/manual/en/class.generator.php) for the data providers +- tests should follow a similar structure to the existing ones. +- separate test implementation by comment sections: "Arrange", "Act", "Assert" (optionally with "Expect"). +- use `setUp` method to initialize the tested object's stubs and mocks. +- use `PHPUnit` attributes. +- use [PHP generator](https://www.php.net/manual/en/class.generator.php) for the data providers. + +How To Install Project +---------------------- + +The project is installed by running the following command: +```console +docker/sdk install +``` How To Build/Start/Stop Docker Environment ------------------------------------------- -Docker Environment can be built by running the following command: +Docker Environment is built by running the following command: ```console docker/sdk build ``` -Docker Environment can be started by running the following command: +Docker Environment is started by running the following command: ```console docker/sdk start ``` -Docker Environment can be stopped by running the following command: +Docker Environment is stopped by running the following command: ```console docker/sdk stop ``` @@ -156,7 +164,7 @@ docker/sdk stop How to Run PHP Script --------------------- -The PHP script can be run by command: +The PHP script runs by command: ```console docker/sdk cli [path-to-script] ``` @@ -169,7 +177,7 @@ docker/sdk cli ./examples/try-transfer-generator.php How to Generate Internal Transfer Objects ----------------------------------------- -The all project transfer objects (generator's, examples, tests) can be generated with the following command: +All project transfer objects (generator's, examples, tests) can be generated with the following command: ```console docker/sdk to-generate-bulk ``` @@ -182,29 +190,29 @@ docker/sdk to-generate How to Generate Transfer Objects By Configuration File ------------------------------------------------------ -Transfer Objects can be generated by configuration file path, relative from the project's root, by running the following command: +Transfer objects can be generated by a configuration file path, relative from the project's root, by running the following command: ```console docker/sdk to-generate [path-to-configuration-file] ``` -How To Run PHPUnit Tests +How to Run PHPUnit Tests ------------------------ -### All Tests +### How to Run All Tests All tests can be run with the following command: ```console docker/sdk phpunit ``` -### Specific Group +### How to Run Test Group A test group can be run with the following command: ```console docker/sdk phpunit-group ``` -### Specific Test Case +### How to Run Test Case A test case can be run with the following command: ```console @@ -217,26 +225,54 @@ can be run with the following command: docker/sdk phpunit 'Picamator\\Tests\\Unit\\TransferObject\\Command\\Helper\\InputNormalizerTest' ``` -How To Run PHPStan +How to Run PHPStan ------------------ -PHPStan can be run with the following command: +For all project's files, PHPStan can be run with the following command: ```console docker/sdk phpstan ``` -How To Run PHP CodeSniffer +For the specific file: +```console +docker/sdk phpstan +``` + +How to Run PHP CodeSniffer -------------------------- -PHP CodeSniffer can be run with the following command: +For all project's files, PHP CodeSniffer can be run with the following command: ```console docker/sdk phpcs ``` -How To Run Composer +For the specific file: +```console +docker/sdk phpcs +``` + +How to Run PHP Code Beautifier and Fixer +---------------------------------------- + +For all project's files, PHP Code Beautifier and Fixer can be run with the following command: +```console +docker/sdk phpcbf +``` + +For the specific file: +```console +docker/sdk phpcbf +``` + +How to Run Composer ------------------- -PHPStan can be run with the following command: +Composer can be run with the following command: ```console docker/sdk composer ``` + +The command supports multiple arguments, for example: +```console +docker/sdk composer install +``` diff --git a/docker/sdk b/docker/sdk index 882a8978..826798dd 100755 --- a/docker/sdk +++ b/docker/sdk @@ -12,7 +12,7 @@ function wrap_in_yellow() { # function to print a command/option with its description function print_command() { - printf " %-35s %s\n" "$(wrap_in_yellow "$1")" "$2" + printf " %-40s %s\n" "$(wrap_in_yellow "$1")" "$2" } # function to print an example with its description @@ -30,7 +30,7 @@ function show_usage() { echo echo "Options:" print_command "-x" "Enable Xdebug (debug, coverage)" - print_command "-p" "Enable Xdebug (profiler)" + print_command "-p" "Enable Xdebug (profile)" echo echo "Commands:" print_command "install" "Install development environment" @@ -39,20 +39,20 @@ function show_usage() { print_command "stop" "Stop Docker containers" print_command "cli [script]" "Open container shell or execute PHP script" print_command "composer " "Run composer command" - print_command "phpstan" "Run PHPStan" + print_command "phpstan [file]" "Run PHPStan for all project's files or for specified one" print_command "phpunit [filter]" "Run PHPUnit tests" - print_command "phpunit-group " "Run PHPUnit tests by group" - print_command "phpcs" "Run PHP CodeSniffer" - print_command "phpcbf" "Run PHP Code Beautifier and Fixer" + print_command "phpunit-group [group]" "Run PHPUnit tests by group" + print_command "phpcs [file]" "Run PHP CodeSniffer for all project's files or for specified one" + print_command "phpcbf [file]" "Run PHP Code Beautifier and Fixer for all project's files or for specified one" print_command "hook-install" "Install CaptainHook" - print_command "hook " "Run CaptainHook command" - print_command "to-generate [c]" "Generate transfer objects" - print_command "to-generate-bulk [b]" "Generate bulk transfer objects" - print_command "df-generate" "Generate definitions" + print_command "hook [cmd]" "Run CaptainHook command" + print_command "to-generate [config]" "Generate Transfer Objects from YML definitions" + print_command "to-generate-bulk [bulk]" "Generate Transfer Objects in bulk from a config list" + print_command "df-generate" "Generate definition files from JSON blueprints" echo echo "Examples:" print_example "$0 -x start" "Start containers (Xdebug: debug, coverage)" - print_example "$0 -p start" "Start containers (Xdebug: profiler)" + print_example "$0 -p start" "Start containers (Xdebug: profile)" print_example "$0 composer install" "Run composer install inside container" } @@ -91,7 +91,7 @@ case $1 in if [ "$XDEBUG_MODE" == "" ]; then docker compose up -d --remove-orphans else - XDEBUG_MODE={$XDEBUG_MODE} docker compose up -d --remove-orphans + XDEBUG_MODE=${XDEBUG_MODE} docker compose up -d --remove-orphans fi ;; stop) @@ -108,7 +108,7 @@ case $1 in $DOCKER_EXEC composer "${@:2}" ;; phpstan) - $DOCKER_EXEC composer phpstan + $DOCKER_EXEC composer phpstan "${@:2}" ;; phpunit) if [ -n "$2" ]; then @@ -118,13 +118,13 @@ case $1 in fi ;; phpunit-group) - $DOCKER_EXEC composer phpunit-group "$2" + $DOCKER_EXEC composer phpunit-group "${@:2}" ;; phpcs) - $DOCKER_EXEC composer phpcs + $DOCKER_EXEC composer phpcs "${@:2}" ;; phpcbf) - $DOCKER_EXEC composer phpcbf + $DOCKER_EXEC composer phpcbf "${@:2}" ;; hook-install) $DOCKER_EXEC composer captainhook install --only-enabled --run-mode=docker --run-exec="docker exec -i $DOCKER_CONTAINER_NAME" diff --git a/src/Transfer/AbstractTransfer.php b/src/Transfer/AbstractTransfer.php index 4250425a..1b21b514 100644 --- a/src/Transfer/AbstractTransfer.php +++ b/src/Transfer/AbstractTransfer.php @@ -13,7 +13,10 @@ */ abstract class AbstractTransfer implements TransferInterface { - use AttributeTrait; + use AttributeTrait { + getInitiatorAttribute as private; + getTransformerAttribute as private; + } /** * @var int<0, max> @@ -115,6 +118,10 @@ final public function toArray(): array $index = $metaData[$propertyName]; $value = $this->_data[$index]; + if ($value === null) { + continue; + } + $data[$propertyName] = $this->getTransformerAttribute($constantName)->toArray($value); unset($metaData[$propertyName]); } diff --git a/src/Transfer/Attribute/AttributeTrait.php b/src/Transfer/Attribute/AttributeTrait.php index 6239f3c1..57ecb11b 100644 --- a/src/Transfer/Attribute/AttributeTrait.php +++ b/src/Transfer/Attribute/AttributeTrait.php @@ -17,7 +17,7 @@ trait AttributeTrait */ private static array $_attributeCache = []; - final protected function getInitiatorAttribute(string $constantName): InitiatorAttributeInterface + protected function getInitiatorAttribute(string $constantName): InitiatorAttributeInterface { /** @var \ReflectionAttribute $reflectionAttribute */ $reflectionAttribute = $this->getConstantReflection( @@ -34,7 +34,7 @@ final protected function getInitiatorAttribute(string $constantName): InitiatorA /** * @throws \Picamator\TransferObject\Transfer\Exception\AttributeTransferException */ - final protected function getTransformerAttribute(string $constantName): TransformerAttributeInterface + protected function getTransformerAttribute(string $constantName): TransformerAttributeInterface { /** @var \ReflectionAttribute $reflectionAttribute */ $reflectionAttribute = $this->getConstantReflection( diff --git a/src/Transfer/Attribute/Transformer/DateTimeTransformerAttribute.php b/src/Transfer/Attribute/Transformer/DateTimeTransformerAttribute.php index 9795a9ee..626cf846 100644 --- a/src/Transfer/Attribute/Transformer/DateTimeTransformerAttribute.php +++ b/src/Transfer/Attribute/Transformer/DateTimeTransformerAttribute.php @@ -45,10 +45,10 @@ public function fromArray(mixed $data): DateTimeInterface } /** - * @param DateTimeInterface|null $data + * @param DateTimeInterface $data */ - public function toArray(mixed $data): ?string + public function toArray(mixed $data): string { - return $data?->format(self::DATE_TIME_FORMAT); + return $data->format(self::DATE_TIME_FORMAT); } } diff --git a/src/Transfer/Attribute/Transformer/EnumTransformerAttribute.php b/src/Transfer/Attribute/Transformer/EnumTransformerAttribute.php index 8a01d1c0..c444bde9 100644 --- a/src/Transfer/Attribute/Transformer/EnumTransformerAttribute.php +++ b/src/Transfer/Attribute/Transformer/EnumTransformerAttribute.php @@ -35,11 +35,11 @@ public function fromArray(mixed $data): ?BackedEnum } /** - * @param BackedEnum|null $data + * @param BackedEnum $data */ - public function toArray(mixed $data): string|int|null + public function toArray(mixed $data): string|int { - return $data?->value; + return $data->value; } /** diff --git a/src/Transfer/Attribute/Transformer/NumberTransformerAttribute.php b/src/Transfer/Attribute/Transformer/NumberTransformerAttribute.php index 18ae171b..5e216a81 100644 --- a/src/Transfer/Attribute/Transformer/NumberTransformerAttribute.php +++ b/src/Transfer/Attribute/Transformer/NumberTransformerAttribute.php @@ -40,10 +40,10 @@ public function fromArray(mixed $data): Number } /** - * @param \BcMath\Number|null $data + * @param \BcMath\Number $data */ - public function toArray(mixed $data): ?string + public function toArray(mixed $data): string { - return $data?->__toString(); + return $data->__toString(); } } diff --git a/src/Transfer/Attribute/Transformer/TransferTransformerAttribute.php b/src/Transfer/Attribute/Transformer/TransferTransformerAttribute.php index 4acb6797..c29f13d1 100644 --- a/src/Transfer/Attribute/Transformer/TransferTransformerAttribute.php +++ b/src/Transfer/Attribute/Transformer/TransferTransformerAttribute.php @@ -28,12 +28,12 @@ public function fromArray(mixed $data): TransferInterface } /** - * @param \Picamator\TransferObject\Transfer\TransferInterface|null $data + * @param \Picamator\TransferObject\Transfer\TransferInterface $data * - * @return array|null + * @return array */ - public function toArray(mixed $data): ?array + public function toArray(mixed $data): array { - return $data?->toArray(); + return $data->toArray(); } } diff --git a/src/TransferGenerator/Definition/Parser/Expander/AbstractPropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/AbstractPropertyExpander.php index 9481cec4..dd1e8e15 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/AbstractPropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/AbstractPropertyExpander.php @@ -8,12 +8,31 @@ abstract class AbstractPropertyExpander implements PropertyExpanderInterface { - use PropertyExpanderTrait; + private ?PropertyExpanderInterface $nextExpander = null; + + public function setNextExpander(PropertyExpanderInterface $expander): PropertyExpanderInterface + { + $this->nextExpander = $expander; + + return $expander; + } + + public function expandPropertyTransfer(array $propertyType, DefinitionPropertyTransfer $propertyTransfer): void + { + $matchedType = $this->matchType($propertyType); + if ($matchedType !== null) { + $this->handleExpander($matchedType, $propertyTransfer); + } + + $this->nextExpander?->expandPropertyTransfer($propertyType, $propertyTransfer); + } /** * @param array|null> $propertyType + * + * @return string|array|null>|null */ - abstract protected function matchType(array $propertyType): ?string; + abstract protected function matchType(array $propertyType): string|array|null; - abstract protected function handleExpander(string $matchedType, DefinitionPropertyTransfer $propertyTransfer): void; + abstract protected function handleExpander(mixed $matchedType, DefinitionPropertyTransfer $propertyTransfer): void; } diff --git a/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php index 1cbc30c6..665acd49 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/AttributesPropertyExpander.php @@ -8,10 +8,8 @@ use Picamator\TransferObject\Generated\DefinitionPropertyTransfer; use Picamator\TransferObject\TransferGenerator\Definition\Parser\Expander\Builder\NamespaceBuilderInterface; -final class AttributesPropertyExpander implements PropertyExpanderInterface +final class AttributesPropertyExpander extends AbstractPropertyExpander implements PropertyExpanderInterface { - use PropertyExpanderTrait; - private const string ATTRIBUTES_KEY = 'attributes'; private const string ATTRIBUTES_REGEX = '#^(?[^()]+)\s*(?.*)$#'; @@ -37,7 +35,7 @@ protected function matchType(array $propertyType): ?array /** * @param array $matchedType */ - protected function handleExpander(array $matchedType, DefinitionPropertyTransfer $propertyTransfer): void + protected function handleExpander(mixed $matchedType, DefinitionPropertyTransfer $propertyTransfer): void { foreach ($matchedType as $attribute) { $attributeTransfer = $this->getAttributeTransfer($attribute); diff --git a/src/TransferGenerator/Definition/Parser/Expander/Builder/AttributesNamespaceBuilder.php b/src/TransferGenerator/Definition/Parser/Expander/Builder/AttributesNamespaceBuilder.php index 6cc5e07a..99c58932 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/Builder/AttributesNamespaceBuilder.php +++ b/src/TransferGenerator/Definition/Parser/Expander/Builder/AttributesNamespaceBuilder.php @@ -8,8 +8,11 @@ readonly class AttributesNamespaceBuilder implements NamespaceBuilderInterface { - protected const array SHORTCUT_MAP = [ - 'sf-assert:' => 'Symfony\Component\Validator\Constraints\\', + /** + * @var array + */ + protected const array SHORTCUT_PATTERN_MAP = [ + '#^(?sf-assert:\s*)(?.+)$#' => 'Symfony\Component\Validator\Constraints\\', ]; public function __construct( @@ -24,17 +27,14 @@ public function createNamespaceTransfer(string $namespace): DefinitionNamespaceT return $this->namespaceBuilder->createNamespaceTransfer($namespace); } - public static function renderShortcut(string $namespace): string + private function renderShortcut(string $namespace): string { - foreach (static::SHORTCUT_MAP as $shortcut => $fullName) { - if (!str_starts_with($namespace, $shortcut)) { + foreach (static::SHORTCUT_PATTERN_MAP as $pattern => $replacement) { + if (preg_match($pattern, $namespace, $matches) !== 1) { continue; } - /** @var string $fullName */ - $namespace = str_replace($shortcut, $fullName, $namespace); - - return str_replace(' ', '', $namespace); + return $replacement . $matches['className']; } return $namespace; diff --git a/src/TransferGenerator/Definition/Parser/Expander/CollectionTypePropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/CollectionTypePropertyExpander.php index 0ff456e9..fa26276b 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/CollectionTypePropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/CollectionTypePropertyExpander.php @@ -24,7 +24,10 @@ protected function matchType(array $propertyType): ?string return $matchType; } - protected function handleExpander(string $matchedType, DefinitionPropertyTransfer $propertyTransfer): void + /** + * @param string $matchedType + */ + protected function handleExpander(mixed $matchedType, DefinitionPropertyTransfer $propertyTransfer): void { $propertyTransfer->collectionType = $this->typeBuilder->createPrefixTypeTransfer($matchedType); $propertyTransfer->isRequired = true; diff --git a/src/TransferGenerator/Definition/Parser/Expander/DateTimeTypePropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/DateTimeTypePropertyExpander.php index b9135216..7e43025a 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/DateTimeTypePropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/DateTimeTypePropertyExpander.php @@ -24,7 +24,10 @@ protected function matchType(array $propertyType): ?string return $matchType; } - protected function handleExpander(string $matchedType, DefinitionPropertyTransfer $propertyTransfer): void + /** + * @param string $matchedType + */ + protected function handleExpander(mixed $matchedType, DefinitionPropertyTransfer $propertyTransfer): void { $propertyTransfer->dateTimeType = $this->typeBuilder->createTypeTransfer($matchedType); } diff --git a/src/TransferGenerator/Definition/Parser/Expander/EnumTypePropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/EnumTypePropertyExpander.php index e07a7a2d..b8177501 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/EnumTypePropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/EnumTypePropertyExpander.php @@ -24,7 +24,10 @@ protected function matchType(array $propertyType): ?string return $matchType; } - protected function handleExpander(string $matchedType, DefinitionPropertyTransfer $propertyTransfer): void + /** + * @param string $matchedType + */ + protected function handleExpander(mixed $matchedType, DefinitionPropertyTransfer $propertyTransfer): void { $propertyTransfer->enumType = $this->typeBuilder->createTypeTransfer($matchedType); } diff --git a/src/TransferGenerator/Definition/Parser/Expander/NumberTypePropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/NumberTypePropertyExpander.php index d5dc4ee8..4140a4d6 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/NumberTypePropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/NumberTypePropertyExpander.php @@ -24,7 +24,10 @@ protected function matchType(array $propertyType): ?string return $matchType; } - protected function handleExpander(string $matchedType, DefinitionPropertyTransfer $propertyTransfer): void + /** + * @param string $matchedType + */ + protected function handleExpander(mixed $matchedType, DefinitionPropertyTransfer $propertyTransfer): void { $propertyTransfer->numberType = $this->typeBuilder->createTypeTransfer($matchedType); } diff --git a/src/TransferGenerator/Definition/Parser/Expander/PropertyExpanderTrait.php b/src/TransferGenerator/Definition/Parser/Expander/PropertyExpanderTrait.php deleted file mode 100644 index fb92cadb..00000000 --- a/src/TransferGenerator/Definition/Parser/Expander/PropertyExpanderTrait.php +++ /dev/null @@ -1,29 +0,0 @@ -nextExpander = $expander; - - return $expander; - } - - public function expandPropertyTransfer(array $propertyType, DefinitionPropertyTransfer $propertyTransfer): void - { - $matchedType = $this->matchType($propertyType); - if ($matchedType !== null) { - $this->handleExpander($matchedType, $propertyTransfer); - } - - $this->nextExpander?->expandPropertyTransfer($propertyType, $propertyTransfer); - } -} diff --git a/src/TransferGenerator/Definition/Parser/Expander/ProtectedPropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/ProtectedPropertyExpander.php index 098c905c..045c4a04 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/ProtectedPropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/ProtectedPropertyExpander.php @@ -15,7 +15,10 @@ protected function matchType(array $propertyType): string return array_key_exists(self::PROTECTED_KEY, $propertyType) ? '1' : '0'; } - protected function handleExpander(string $matchedType, DefinitionPropertyTransfer $propertyTransfer): void + /** + * @param string $matchedType + */ + protected function handleExpander(mixed $matchedType, DefinitionPropertyTransfer $propertyTransfer): void { $propertyTransfer->isProtected = $matchedType === '1'; } diff --git a/src/TransferGenerator/Definition/Parser/Expander/RequiredPropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/RequiredPropertyExpander.php index 2334b9f6..ef7655a4 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/RequiredPropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/RequiredPropertyExpander.php @@ -15,7 +15,10 @@ protected function matchType(array $propertyType): string return array_key_exists(self::REQUIRED_KEY, $propertyType) ? '1' : '0'; } - protected function handleExpander(string $matchedType, DefinitionPropertyTransfer $propertyTransfer): void + /** + * @param string $matchedType + */ + protected function handleExpander(mixed $matchedType, DefinitionPropertyTransfer $propertyTransfer): void { if (!$this->isNullableAllowed($propertyTransfer)) { $propertyTransfer->isRequired = true; @@ -33,11 +36,6 @@ private function isNullableAllowed(DefinitionPropertyTransfer $propertyTransfer) return false; } - // phpcs:disable SlevomatCodingStandard.ControlStructures.UselessIfConditionWithReturn - if ($propertyTransfer->collectionType !== null) { - return false; - } - - return true; + return $propertyTransfer->collectionType === null; } } diff --git a/src/TransferGenerator/Definition/Parser/Expander/TypePropertyExpander.php b/src/TransferGenerator/Definition/Parser/Expander/TypePropertyExpander.php index 90d94817..17b8f54f 100644 --- a/src/TransferGenerator/Definition/Parser/Expander/TypePropertyExpander.php +++ b/src/TransferGenerator/Definition/Parser/Expander/TypePropertyExpander.php @@ -29,7 +29,10 @@ protected function matchType(array $propertyType): ?string return $matchType; } - protected function handleExpander(string $matchedType, DefinitionPropertyTransfer $propertyTransfer): void + /** + * @param string $matchedType + */ + protected function handleExpander(mixed $matchedType, DefinitionPropertyTransfer $propertyTransfer): void { $builtInTypeTransfer = $this->getBuiltInTypeTransfer($matchedType); if ($builtInTypeTransfer !== null) { diff --git a/src/TransferGenerator/Definition/Reader/DefinitionReader.php b/src/TransferGenerator/Definition/Reader/DefinitionReader.php index 6e50852c..b0142957 100644 --- a/src/TransferGenerator/Definition/Reader/DefinitionReader.php +++ b/src/TransferGenerator/Definition/Reader/DefinitionReader.php @@ -38,8 +38,9 @@ public function getDefinitions(): Generator yield from $contentGenerator; - /** @phpstan-ignore assignOp.invalid */ - $count += $contentGenerator->getReturn(); + /** @var int $contentGeneratorReturn */ + $contentGeneratorReturn = $contentGenerator->getReturn(); + $count += $contentGeneratorReturn; } } catch (FinderException | TransferGeneratorDefinitionException | YmlParserException $e) { yield $this->createErrorDefinitionTransfer($e, fileName: $fileName ?? ''); diff --git a/src/TransferGenerator/Generator/Render/Expander/MetaConstantsTemplateExpander.php b/src/TransferGenerator/Generator/Render/Expander/MetaConstantsTemplateExpander.php index 68b07dd2..b4d3842f 100644 --- a/src/TransferGenerator/Generator/Render/Expander/MetaConstantsTemplateExpander.php +++ b/src/TransferGenerator/Generator/Render/Expander/MetaConstantsTemplateExpander.php @@ -9,9 +9,9 @@ final class MetaConstantsTemplateExpander extends AbstractTemplateExpander { - private const string META_CONSTANT_SNAKE_CASE_REGEX = '#(?templateTransfer->metaAttributes->offsetExists($property)) { + $metaAttributes = $this->templateTransfer->metaAttributes[$property] ?? null; + if ($metaAttributes === null) { return ''; } - $metaAttributes = $this->renderIterable( - /** @phpstan-ignore argument.type */ - iterable: $this->templateTransfer->metaAttributes[$property], + $renderedMetaAttributes = $this->renderIterable( + iterable: $metaAttributes, template: self::META_ATTRIBUTE_TEMPLATE, ); - return PHP_EOL . $metaAttributes; + return PHP_EOL . $renderedMetaAttributes; } public function renderDocBlock(string $property): string { - if (!$this->templateTransfer->docBlocks->offsetExists($property)) { + $docBlocks = $this->templateTransfer->docBlocks[$property] ?? null; + if ($docBlocks === null) { return ''; } - return sprintf( - self::DOC_BLOCK_TEMPLATE, - $this->templateTransfer->docBlocks[$property], - ); + return sprintf(self::DOC_BLOCK_TEMPLATE, $docBlocks); } public function renderPropertyAttributes(string $property): string { - if (!$this->templateTransfer->propertyAttributes->offsetExists($property)) { + $attributes = $this->templateTransfer->propertyAttributes[$property] ?? null; + if ($attributes === null) { return ''; } - $attributes = $this->renderIterable( - /** @phpstan-ignore argument.type */ - iterable: $this->templateTransfer->propertyAttributes[$property], + $renderedAttributes = $this->renderIterable( + iterable: $attributes, template: self::PROPERTY_ATTRIBUTE_TEMPLATE, ); - return PHP_EOL . $attributes; + return PHP_EOL . $renderedAttributes; } } diff --git a/tests/integration/DefinitionGenerator/Generated/Tagesschau/NewsTransfer.php b/tests/integration/DefinitionGenerator/Generated/Tagesschau/NewsTransfer.php index 83f7d10c..0c5eeda7 100644 --- a/tests/integration/DefinitionGenerator/Generated/Tagesschau/NewsTransfer.php +++ b/tests/integration/DefinitionGenerator/Generated/Tagesschau/NewsTransfer.php @@ -37,7 +37,7 @@ final class NewsTransfer extends AbstractTransfer self::REGION_ID_PROP => self::REGION_ID_INDEX, self::REGION_IDS_PROP => self::REGION_IDS_INDEX, self::RESSORT_PROP => self::RESSORT_INDEX, - self::SHARE_U_R_L_PROP => self::SHARE_U_R_L_INDEX, + self::SHARE_URL_PROP => self::SHARE_URL_INDEX, self::SOPHORA_ID_PROP => self::SOPHORA_ID_INDEX, self::TAGS_PROP => self::TAGS_INDEX, self::TEASER_IMAGE_PROP => self::TEASER_IMAGE_INDEX, @@ -200,13 +200,13 @@ final class NewsTransfer extends AbstractTransfer } // shareURL - public const string SHARE_U_R_L_PROP = 'shareURL'; - private const int SHARE_U_R_L_INDEX = 12; + public const string SHARE_URL_PROP = 'shareURL'; + private const int SHARE_URL_INDEX = 12; public ?string $shareURL { - get => $this->getData(self::SHARE_U_R_L_INDEX); + get => $this->getData(self::SHARE_URL_INDEX); set { - $this->setData(self::SHARE_U_R_L_INDEX, $value); + $this->setData(self::SHARE_URL_INDEX, $value); } } diff --git a/tests/integration/Transfer/Generated/SymfonyAttributeTransfer.php b/tests/integration/Transfer/Generated/SymfonyAttributeTransfer.php index 30e41cdc..1c9e60dc 100644 --- a/tests/integration/Transfer/Generated/SymfonyAttributeTransfer.php +++ b/tests/integration/Transfer/Generated/SymfonyAttributeTransfer.php @@ -5,7 +5,7 @@ namespace Picamator\Tests\Integration\TransferObject\Transfer\Generated; use Picamator\TransferObject\Transfer\AbstractTransfer; -use Symfony\Component\Validator\Constraints\IsTrue; +use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; /** @@ -29,9 +29,9 @@ final class SymfonyAttributeTransfer extends AbstractTransfer public const string I_AM_ASSERT_PROP = 'iAmAssert'; private const int I_AM_ASSERT_INDEX = 0; - #[IsTrue] #[NotBlank] - public ?bool $iAmAssert { + #[Length(min: 50)] + public string $iAmAssert { get => $this->getData(self::I_AM_ASSERT_INDEX); set { $this->setData(self::I_AM_ASSERT_INDEX, $value); diff --git a/tests/integration/Transfer/TransferTest.php b/tests/integration/Transfer/TransferTest.php index 76b988d6..339103ea 100644 --- a/tests/integration/Transfer/TransferTest.php +++ b/tests/integration/Transfer/TransferTest.php @@ -477,6 +477,7 @@ public function testSymfonyAssertAttribute(): void { // Arrange $symfonyAttributeTransfer = new SymfonyAttributeTransfer(); + $symfonyAttributeTransfer->iAmAssert = 'Short text'; $validator = Validation::createValidatorBuilder() ->enableAttributeMapping() @@ -487,7 +488,10 @@ public function testSymfonyAssertAttribute(): void // Assert $this->assertCount(1, $actual, 'Expected one error message'); - $this->assertSame('This value should not be blank.', $actual[0]?->getMessage()); + + /** @var string $message */ + $message = $actual[0]?->getMessage() ?? ''; + $this->assertStringStartsWith('This value is too short.', $message); } #[TestDox('Reserved constant should not collide with transfer')] diff --git a/tests/integration/Transfer/data/config/definition/symfony-attributes.transfer.yml b/tests/integration/Transfer/data/config/definition/symfony-attributes.transfer.yml index 5af37595..fbbfc6db 100644 --- a/tests/integration/Transfer/data/config/definition/symfony-attributes.transfer.yml +++ b/tests/integration/Transfer/data/config/definition/symfony-attributes.transfer.yml @@ -1,7 +1,8 @@ # $schema: ./../../../../../../schema/definition.schema.json SymfonyAttribute: iAmAssert: - type: bool + type: string + required: attributes: - - "sf-assert: IsTrue" - "sf-assert:NotBlank" + - "sf-assert:Length(min: 50)" diff --git a/tests/unit/TransferGenerator/Definition/Parser/Expander/Builder/AttributesNamespaceBuilderTest.php b/tests/unit/TransferGenerator/Definition/Parser/Expander/Builder/AttributesNamespaceBuilderTest.php new file mode 100644 index 00000000..7b876c18 --- /dev/null +++ b/tests/unit/TransferGenerator/Definition/Parser/Expander/Builder/AttributesNamespaceBuilderTest.php @@ -0,0 +1,49 @@ +namespaceBuilderMock = $this->createMock(NamespaceBuilderInterface::class); + + $this->builder = new AttributesNamespaceBuilder($this->namespaceBuilderMock); + } + + #[TestDox('Create Attribute Namespace replacing shortcuts $namespace to $expected')] + #[TestWith(['sf-assert:NotBlank', 'Symfony\Component\Validator\Constraints\NotBlank'])] + #[TestWith(['sf-assert: NotBlank', 'Symfony\Component\Validator\Constraints\NotBlank'])] + #[TestWith(['NotBlank', 'NotBlank'])] + public function testCreateNamespaceTransfer(string $namespace, string $expected): void + { + // Arrange + $namespaceTransfer = new DefinitionNamespaceTransfer(); + + // Expect + $this->namespaceBuilderMock->expects($this->once()) + ->method('createNamespaceTransfer') + ->with($expected) + ->willReturn($namespaceTransfer) + ->seal(); + + // Act + $this->builder->createNamespaceTransfer($namespace); + } +} diff --git a/tests/unit/TransferGenerator/Definition/Parser/Expander/Builder/NamespaceBuilderTest.php b/tests/unit/TransferGenerator/Definition/Parser/Expander/Builder/NamespaceBuilderTest.php index a43ba157..972a2511 100644 --- a/tests/unit/TransferGenerator/Definition/Parser/Expander/Builder/NamespaceBuilderTest.php +++ b/tests/unit/TransferGenerator/Definition/Parser/Expander/Builder/NamespaceBuilderTest.php @@ -26,9 +26,9 @@ protected function setUp(): void /** * @param array $expected */ - #[DataProvider('definitionNamespaceDataProvider')] - #[TestDoxFormatter('definitionNamespaceTestDoxFormatter')] - public function testCreateDefinitionNamespaceTransfer(string $namespace, array $expected): void + #[DataProvider('namespaceDataProvider')] + #[TestDoxFormatter('namespaceTestDoxFormatter')] + public function testCreateNamespaceTransfer(string $namespace, array $expected): void { // Act $actual = $this->builder->createNamespaceTransfer($namespace); @@ -40,7 +40,7 @@ public function testCreateDefinitionNamespaceTransfer(string $namespace, array $ /** * @param array $expected */ - public static function definitionNamespaceTestDoxFormatter(string $namespace, array $expected): string + public static function namespaceTestDoxFormatter(string $namespace, array $expected): string { return sprintf( 'Definition namespace "%s" expected "%s"', @@ -52,7 +52,7 @@ public static function definitionNamespaceTestDoxFormatter(string $namespace, ar /** * @return Generator */ - public static function definitionNamespaceDataProvider(): Generator + public static function namespaceDataProvider(): Generator { yield 'namespace without alias' => [ '\Picamator\TransferObject\Generated', diff --git a/tests/unit/TransferGenerator/Generator/Render/Expander/MetaConstantsTemplateExpanderTest.php b/tests/unit/TransferGenerator/Generator/Render/Expander/MetaConstantsTemplateExpanderTest.php index 1a3df6be..1e421890 100644 --- a/tests/unit/TransferGenerator/Generator/Render/Expander/MetaConstantsTemplateExpanderTest.php +++ b/tests/unit/TransferGenerator/Generator/Render/Expander/MetaConstantsTemplateExpanderTest.php @@ -28,6 +28,12 @@ protected function setUp(): void #[TestWith(['UPPERCASE', 'UPPERCASE'])] #[TestWith(['UPPER_CASE', 'UPPER_CASE'])] #[TestWith(['Mixed_CaSE', 'MIXED_CASE'])] + #[TestWith(['snake_case', 'SNAKE_CASE'])] + #[TestWith(['PascalCase', 'PASCAL_CASE'])] + #[TestWith(['property1', 'PROPERTY1'])] + #[TestWith(['HTTPResponse', 'HTTP_RESPONSE'])] + #[TestWith(['myAPIKey', 'MY_API_KEY'])] + #[TestWith(['XMLHttpRequest', 'XML_HTTP_REQUEST'])] public function testExpandTemplateTransfer(string $propertyName, string $expected): void { // Arrange