From 4a64771b9713f2efd97561cfe559b6a60308cabb Mon Sep 17 00:00:00 2001 From: Lars Strojny Date: Wed, 3 Jul 2024 16:40:24 +0200 Subject: [PATCH] Support laminas code ^4.5.0 --- composer.json | 2 +- phpstan.neon | 2 +- .../Assembler/ClassMapAssembler.php | 18 +++-- .../Assembler/ClientConstructorAssembler.php | 50 ++++++++----- .../Assembler/ClientMethodAssembler.php | 25 +++++-- .../Assembler/ConstructorAssembler.php | 5 +- .../Assembler/IteratorAssembler.php | 65 +++++++++------- .../Assembler/JsonSerializableAssembler.php | 20 +++-- .../Assembler/PropertyAssembler.php | 13 ++-- .../Assembler/ResultProviderAssembler.php | 38 ++++++---- .../CodeGenerator/ClientFactoryGenerator.php | 34 +++++---- .../Assembler/PropertyAssemblerTest.php | 74 +++++++++++++++---- 12 files changed, 226 insertions(+), 120 deletions(-) diff --git a/composer.json b/composer.json index 800251be..855b7fb9 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "require": { "php": "~8.1.0 || ~8.2.0 || ~8.3.0", "azjezz/psl": "^2.1", - "laminas/laminas-code": "^4.14.0", + "laminas/laminas-code": "^4.5.0", "php-soap/cached-engine": "~0.2", "php-soap/engine": "^2.10.1", "php-soap/encoding": "~0.4", diff --git a/phpstan.neon b/phpstan.neon index edc3b6ef..530d49eb 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,4 +3,4 @@ parameters: ignoreErrors: # This is something we hope to fix in PHP 7.4 # https://wiki.php.net/rfc/covariant-returns-and-contravariant-parameters - - '#Call to an undefined method Phpro\\.*(Context|RuleSet)Interface.*$#' + - '#Call to an undefined method Phpro\\.*(Context|RuleSet)Interface.*$#'# diff --git a/src/Phpro/SoapClient/CodeGenerator/Assembler/ClassMapAssembler.php b/src/Phpro/SoapClient/CodeGenerator/Assembler/ClassMapAssembler.php index abffae01..bb8748c5 100644 --- a/src/Phpro/SoapClient/CodeGenerator/Assembler/ClassMapAssembler.php +++ b/src/Phpro/SoapClient/CodeGenerator/Assembler/ClassMapAssembler.php @@ -50,12 +50,7 @@ public function assemble(ContextInterface $context) $linefeed = $file::LINE_FEED; $classMap = $this->assembleClassMap($typeMap, $linefeed, $file->getIndentation()); $code = $this->assembleClassMapCollection($classMap, $linefeed).$linefeed; - $class->addMethodFromGenerator( - (new MethodGenerator('getCollection')) - ->setStatic(true) - ->setBody('return '.$code) - ->setReturnType(ClassMapCollection::class) - ); + $class->addMethodFromGenerator($this->generateGetCollectionMethod($code)); } catch (\Exception $e) { throw AssemblerException::fromException($e); } @@ -104,4 +99,15 @@ private function assembleClassMapCollection(string $classMap, string $linefeed): return sprintf(implode($linefeed, $code), $classMap); } + + private function generateGetCollectionMethod(string $code): MethodGenerator + { + $method = new MethodGenerator('getCollection'); + + $method->setStatic(true); + $method->setBody('return ' . $code); + $method->setReturnType(ClassMapCollection::class); + + return $method; + } } diff --git a/src/Phpro/SoapClient/CodeGenerator/Assembler/ClientConstructorAssembler.php b/src/Phpro/SoapClient/CodeGenerator/Assembler/ClientConstructorAssembler.php index c66a73cb..6679eee9 100644 --- a/src/Phpro/SoapClient/CodeGenerator/Assembler/ClientConstructorAssembler.php +++ b/src/Phpro/SoapClient/CodeGenerator/Assembler/ClientConstructorAssembler.php @@ -33,25 +33,8 @@ public function assemble(ContextInterface $context) $class = $context->getClass(); try { $caller = $this->generateClassNameAndAddImport(Caller::class, $class); - $class->addPropertyFromGenerator( - (new PropertyGenerator('caller')) - ->setVisibility(PropertyGenerator::VISIBILITY_PRIVATE) - ->omitDefaultValue(true) - ->setDocBlock((new DocBlockGenerator()) - ->setWordWrap(false) - ->setTags([ - [ - 'name' => 'var', - 'description' => $caller, - ], - ])), - ); - $class->addMethodFromGenerator( - (new MethodGenerator('__construct')) - ->setParameter(new ParameterGenerator('caller', Caller::class)) - ->setVisibility(MethodGenerator::VISIBILITY_PUBLIC) - ->setBody('$this->caller = $caller;') - ); + $class->addPropertyFromGenerator($this->generateCallerProperty($caller)); + $class->addMethodFromGenerator($this->generateConstructor()); } catch (\Exception $e) { throw AssemblerException::fromException($e); } @@ -59,6 +42,35 @@ public function assemble(ContextInterface $context) return true; } + private function generateConstructor(): MethodGenerator + { + $method = new MethodGenerator('__construct'); + + $method->setParameter(new ParameterGenerator('caller', Caller::class)); + $method->setVisibility(MethodGenerator::VISIBILITY_PUBLIC); + $method->setBody('$this->caller = $caller;'); + + return $method; + } + + private function generateCallerProperty(string $caller): PropertyGenerator + { + $property = new PropertyGenerator('caller'); + + $property->setVisibility(PropertyGenerator::VISIBILITY_PRIVATE); + $property->omitDefaultValue(true); + $property->setDocBlock((new DocBlockGenerator()) + ->setWordWrap(false) + ->setTags([ + [ + 'name' => 'var', + 'description' => $caller, + ], + ])); + + return $property; + } + /** * @param non-empty-string $fqcn */ diff --git a/src/Phpro/SoapClient/CodeGenerator/Assembler/ClientMethodAssembler.php b/src/Phpro/SoapClient/CodeGenerator/Assembler/ClientMethodAssembler.php index 5c0213bf..9d16f8e5 100644 --- a/src/Phpro/SoapClient/CodeGenerator/Assembler/ClientMethodAssembler.php +++ b/src/Phpro/SoapClient/CodeGenerator/Assembler/ClientMethodAssembler.php @@ -54,12 +54,7 @@ public function assemble(ContextInterface $context): bool $methodBody = $this->generateMethodBody($class, $param, $method, $context); $class->addMethodFromGenerator( - (new MethodGenerator($phpMethodName)) - ->setParameters($param === null ? [] : [$param]) - ->setVisibility(MethodGenerator::VISIBILITY_PUBLIC) - ->setBody($methodBody) - ->setReturnType($this->decideOnReturnType($context, true)) - ->setDocBlock($docblock) + $this->generateMethod($phpMethodName, $param, $methodBody, $context, $docblock) ); } catch (\Exception $e) { throw AssemblerException::fromException($e); @@ -68,6 +63,24 @@ public function assemble(ContextInterface $context): bool return true; } + private function generateMethod( + string $phpMethodName, + ?ParameterGenerator $param, + string $methodBody, + ClientMethodContext $context, + DocBlockGenerator $docblock + ): MethodGenerator { + $method = new MethodGenerator($phpMethodName); + + $method->setParameters($param === null ? [] : [$param]); + $method->setVisibility(MethodGenerator::VISIBILITY_PUBLIC); + $method->setBody($methodBody); + $method->setReturnType($this->decideOnReturnType($context, true)); + $method->setDocBlock($docblock); + + return $method; + } + private function generateMethodBody( ClassGenerator $class, ?ParameterGenerator $param, diff --git a/src/Phpro/SoapClient/CodeGenerator/Assembler/ConstructorAssembler.php b/src/Phpro/SoapClient/CodeGenerator/Assembler/ConstructorAssembler.php index fa36a240..22e9fc33 100644 --- a/src/Phpro/SoapClient/CodeGenerator/Assembler/ConstructorAssembler.php +++ b/src/Phpro/SoapClient/CodeGenerator/Assembler/ConstructorAssembler.php @@ -68,8 +68,9 @@ public function assemble(ContextInterface $context) private function assembleConstructor(Type $type): MethodGenerator { $body = []; - $constructor = (new MethodGenerator('__construct')) - ->setVisibility(MethodGenerator::VISIBILITY_PUBLIC); + $constructor = (new MethodGenerator('__construct')); + + $constructor->setVisibility(MethodGenerator::VISIBILITY_PUBLIC); $docblock = (new DocBlockGenerator()) ->setWordWrap(false) diff --git a/src/Phpro/SoapClient/CodeGenerator/Assembler/IteratorAssembler.php b/src/Phpro/SoapClient/CodeGenerator/Assembler/IteratorAssembler.php index 2b0ac765..98915803 100644 --- a/src/Phpro/SoapClient/CodeGenerator/Assembler/IteratorAssembler.php +++ b/src/Phpro/SoapClient/CodeGenerator/Assembler/IteratorAssembler.php @@ -66,34 +66,7 @@ private function implementGetIterator(ClassGenerator $class, Property $firstProp $methodName = 'getIterator'; $class->removeMethod($methodName); - $class->addMethodFromGenerator( - (new MethodGenerator($methodName)) - ->setParameters([]) - ->setVisibility(MethodGenerator::VISIBILITY_PUBLIC) - ->setBody(sprintf( - 'return new \\ArrayIterator($this->%1$s);', - $firstProperty->getName() - )) - ->setReturnType('ArrayIterator') - ->setDocBlock( - (new DocBlockGenerator()) - ->setWordWrap(false) - ->setTags([ - [ - 'name' => 'return', - 'description' => '\\ArrayIterator|'. $firstProperty->getType() .'[]' - ], - [ - 'name' => 'phpstan-return', - 'description' => '\\ArrayIterator'.$arrayInfo, - ], - [ - 'name' => 'psalm-return', - 'description' => '\\ArrayIterator'.$arrayInfo, - ] - ]) - ) - ); + $class->addMethodFromGenerator($this->generateGetIteratorMethod($methodName, $firstProperty, $arrayInfo)); $class->setDocBlock( (new DocBlockGenerator()) @@ -110,4 +83,40 @@ private function implementGetIterator(ClassGenerator $class, Property $firstProp ]) ); } + + private function generateGetIteratorMethod( + string $methodName, + Property $firstProperty, + string $arrayInfo + ): MethodGenerator { + $method = new MethodGenerator($methodName); + + $method->setParameters([]); + $method->setVisibility(MethodGenerator::VISIBILITY_PUBLIC); + $method->setBody(sprintf( + 'return new \\ArrayIterator($this->%1$s);', + $firstProperty->getName() + )); + $method->setReturnType('ArrayIterator'); + $method->setDocBlock( + (new DocBlockGenerator()) + ->setWordWrap(false) + ->setTags([ + [ + 'name' => 'return', + 'description' => '\\ArrayIterator|' . $firstProperty->getType() . '[]' + ], + [ + 'name' => 'phpstan-return', + 'description' => '\\ArrayIterator' . $arrayInfo, + ], + [ + 'name' => 'psalm-return', + 'description' => '\\ArrayIterator' . $arrayInfo, + ] + ]) + ); + + return $method; + } } diff --git a/src/Phpro/SoapClient/CodeGenerator/Assembler/JsonSerializableAssembler.php b/src/Phpro/SoapClient/CodeGenerator/Assembler/JsonSerializableAssembler.php index 923da09b..35b0f2c6 100644 --- a/src/Phpro/SoapClient/CodeGenerator/Assembler/JsonSerializableAssembler.php +++ b/src/Phpro/SoapClient/CodeGenerator/Assembler/JsonSerializableAssembler.php @@ -54,13 +54,19 @@ private function implementJsonSerialize(Type $type, ClassGenerator $class) { $methodName = 'jsonSerialize'; $class->removeMethod($methodName); - $class->addMethodFromGenerator( - (new MethodGenerator($methodName)) - ->setParameters([]) - ->setVisibility(MethodGenerator::VISIBILITY_PUBLIC) - ->setBody($this->generateJsonSerializeBody($type, $class)) - ->setReturnType('array') - ); + $class->addMethodFromGenerator($this->generateJsonSerializeMethod($methodName, $type, $class)); + } + + private function generateJsonSerializeMethod(string $methodName, Type $type, ClassGenerator $class): MethodGenerator + { + $method = new MethodGenerator($methodName); + + $method->setParameters([]); + $method->setVisibility(MethodGenerator::VISIBILITY_PUBLIC); + $method->setBody($this->generateJsonSerializeBody($type, $class)); + $method->setReturnType('array'); + + return $method; } /** diff --git a/src/Phpro/SoapClient/CodeGenerator/Assembler/PropertyAssembler.php b/src/Phpro/SoapClient/CodeGenerator/Assembler/PropertyAssembler.php index 33c841eb..d3951a89 100644 --- a/src/Phpro/SoapClient/CodeGenerator/Assembler/PropertyAssembler.php +++ b/src/Phpro/SoapClient/CodeGenerator/Assembler/PropertyAssembler.php @@ -56,11 +56,12 @@ public function assemble(ContextInterface $context) $class->removeProperty($property->getName()); } - $propertyGenerator = (new PropertyGenerator($property->getName())) - ->setVisibility($this->options->visibility()) - ->omitDefaultValue( - !$this->options->useOptionalValue() && !(new IsConsideredNullableType())($property->getMeta()) - ); + $propertyGenerator = new PropertyGenerator($property->getName()); + + $propertyGenerator->setVisibility($this->options->visibility()); + $propertyGenerator->omitDefaultValue( + !$this->options->useOptionalValue() && !(new IsConsideredNullableType())($property->getMeta()) + ); if ($this->options->useDocBlocks()) { $propertyGenerator->setDocBlock( @@ -76,7 +77,7 @@ public function assemble(ContextInterface $context) ); } - if ($this->options->useTypeHints()) { + if ($this->options->useTypeHints() && method_exists($propertyGenerator, 'setType')) { $propertyGenerator->setType(TypeGenerator::fromTypeString($property->getPhpType())); } diff --git a/src/Phpro/SoapClient/CodeGenerator/Assembler/ResultProviderAssembler.php b/src/Phpro/SoapClient/CodeGenerator/Assembler/ResultProviderAssembler.php index f9defcea..6eb26045 100644 --- a/src/Phpro/SoapClient/CodeGenerator/Assembler/ResultProviderAssembler.php +++ b/src/Phpro/SoapClient/CodeGenerator/Assembler/ResultProviderAssembler.php @@ -86,23 +86,29 @@ private function implementGetResult(ContextInterface $context, ClassGenerator $c $methodName = 'getResult'; $class->removeMethod($methodName); - $class->addMethodFromGenerator( - (new MethodGenerator($methodName)) - ->setParameters([]) - ->setVisibility(MethodGenerator::VISIBILITY_PUBLIC) - ->setReturnType(ResultInterface::class) - ->setBody($this->generateGetResultBody($property)) - ->setDocBlock( - (new DocBlockGenerator()) - ->setWordWrap(false) - ->setTags([ - [ - 'name' => 'return', - 'description' => $this->generateGetResultReturnTag($property) - ] - ]) - ) + $class->addMethodFromGenerator($this->generateGetResultMethod($methodName, $property)); + } + + private function generateGetResultMethod(string $methodName, Property $property): MethodGenerator + { + $method = new MethodGenerator($methodName); + + $method->setParameters([]); + $method->setVisibility(MethodGenerator::VISIBILITY_PUBLIC); + $method->setReturnType(ResultInterface::class); + $method->setBody($this->generateGetResultBody($property)); + $method->setDocBlock( + (new DocBlockGenerator()) + ->setWordWrap(false) + ->setTags([ + [ + 'name' => 'return', + 'description' => $this->generateGetResultReturnTag($property) + ] + ]) ); + + return $method; } /** diff --git a/src/Phpro/SoapClient/CodeGenerator/ClientFactoryGenerator.php b/src/Phpro/SoapClient/CodeGenerator/ClientFactoryGenerator.php index f12e5345..b20412ca 100644 --- a/src/Phpro/SoapClient/CodeGenerator/ClientFactoryGenerator.php +++ b/src/Phpro/SoapClient/CodeGenerator/ClientFactoryGenerator.php @@ -65,23 +65,29 @@ public function generate(FileGenerator $file, $context): string $class->addUse(EventDispatchingCaller::class); $class->addUse(EngineCaller::class); $class->addUse(EncoderRegistry::class); - $class->addMethodFromGenerator( - (new MethodGenerator('factory')) - ->setStatic(true) - ->setBody(sprintf(self::BODY, $context->getClientName(), $context->getClassmapName())) - ->setReturnType($context->getClientFqcn()) - ->setParameter(new ParameterGenerator('wsdl', 'string')) - ->setDocBlock( - (new DocBlockGenerator()) - ->setShortDescription( - 'This factory can be used as a starting point '. - 'to create your own specialized factory. Feel free to modify.' - ) - ) - ); + $class->addMethodFromGenerator($this->generateFactoryMethod($context)); $file->setClass($class); return $file->generate(); } + + private function generateFactoryMethod(ClientFactoryContext $context): MethodGenerator + { + $method = new MethodGenerator('factory'); + + $method->setStatic(true); + $method->setBody(sprintf(self::BODY, $context->getClientName(), $context->getClassmapName())); + $method->setReturnType($context->getClientFqcn()); + $method->setParameter(new ParameterGenerator('wsdl', 'string')); + $method->setDocBlock( + (new DocBlockGenerator()) + ->setShortDescription( + 'This factory can be used as a starting point ' . + 'to create your own specialized factory. Feel free to modify.' + ) + ); + + return $method; + } } diff --git a/test/PhproTest/SoapClient/Unit/CodeGenerator/Assembler/PropertyAssemblerTest.php b/test/PhproTest/SoapClient/Unit/CodeGenerator/Assembler/PropertyAssemblerTest.php index 62f129dc..420ac273 100644 --- a/test/PhproTest/SoapClient/Unit/CodeGenerator/Assembler/PropertyAssemblerTest.php +++ b/test/PhproTest/SoapClient/Unit/CodeGenerator/Assembler/PropertyAssemblerTest.php @@ -36,6 +36,39 @@ function it_is_an_assembler() */ function it_assembles_property_without_default_value() { + $assembler = new PropertyAssembler( + PropertyAssemblerOptions::create()->withTypeHints(false) + ); + $context = $this->createContext(); + $assembler->assemble($context); + $code = $context->getClass()->generate(); + $expected = <<assertEquals($expected, $code); + } + + /** + * @test + */ + function it_assembles_property_with_type() + { + if (!method_exists(PropertyGenerator::class, 'setType')) { + $this->markTestSkipped('This test requires laminas-code >=4.6.0'); + } + $assembler = new PropertyAssembler(); $context = $this->createContext(); $assembler->assemble($context); @@ -64,7 +97,9 @@ class MyType function it_assembles_property_with_default_value() { $assembler = new PropertyAssembler( - PropertyAssemblerOptions::create()->withOptionalValue() + PropertyAssemblerOptions::create() + ->withTypeHints(false) + ->withOptionalValue() ); $context = $this->createContext(); $assembler->assemble($context); @@ -79,7 +114,7 @@ class MyType * * @var null | string */ - private ?string \$prop1 = null; + private \$prop1 = null; } CODE; @@ -94,7 +129,9 @@ class MyType function it_assembles_with_visibility_without_default_value() { $assembler = new PropertyAssembler( - PropertyAssemblerOptions::create()->withVisibility(PropertyGenerator::VISIBILITY_PUBLIC) + PropertyAssemblerOptions::create() + ->withVisibility(PropertyGenerator::VISIBILITY_PUBLIC) + ->withTypeHints(false) ); $context = $this->createContext(); $assembler->assemble($context); @@ -109,7 +146,7 @@ class MyType * * @var string */ - public string \$prop1; + public \$prop1; } CODE; @@ -123,7 +160,9 @@ class MyType function it_assembles_without_doc_blocks() { $assembler = new PropertyAssembler( - PropertyAssemblerOptions::create()->withDocBlocks(false) + PropertyAssemblerOptions::create() + ->withDocBlocks(false) + ->withTypeHints(false) ); $context = $this->createContext(); $assembler->assemble($context); @@ -133,7 +172,7 @@ function it_assembles_without_doc_blocks() class MyType { - private string \$prop1; + private \$prop1; } CODE; @@ -175,7 +214,10 @@ class MyType */ function it_assembles_a_doc_block_that_does_not_wrap() { - $assembler = new PropertyAssembler(); + $assembler = new PropertyAssembler( + PropertyAssemblerOptions::create() + ->withTypeHints(false) + ); $context = $this->createContextWithLongType(); $assembler->assemble($context); @@ -189,7 +231,7 @@ class MyType /** * @var \\This\\Is\\My\\Very\\Very\\Long\\Namespace\\And\\Class\\Name\\That\\Should\\Not\\Never\\Ever\\Wrap */ - private \\This\\Is\\My\\Very\\Very\\Long\\Namespace\\And\\Class\\Name\\That\\Should\\Not\\Never\\Ever\\Wrap \$prop1; + private \$prop1; } CODE; @@ -201,7 +243,9 @@ class MyType */ function it_assembles_properties_with_advanced_types() { - $assembler = new PropertyAssembler(); + $assembler = new PropertyAssembler( + PropertyAssemblerOptions::create()->withTypeHints(false) + ); $class = new ClassGenerator('MyType', 'MyNamespace'); $type = new Type($namespace = 'MyNamespace', 'MyType', [ $property = Property::fromMetaData( @@ -224,7 +268,7 @@ class MyType /** * @var array, string> */ - private array \$prop1; + private \$prop1; } CODE; @@ -243,7 +287,9 @@ function it_overwrite_props_during_assembling() $assembler1->assemble($context); $assembler2 = new PropertyAssembler( - PropertyAssemblerOptions::create()->withVisibility(PropertyGenerator::VISIBILITY_PUBLIC) + PropertyAssemblerOptions::create() + ->withVisibility(PropertyGenerator::VISIBILITY_PUBLIC) + ->withTypeHints(false) ); $assembler2->assemble($context); @@ -258,7 +304,7 @@ class MyType * * @var string */ - public string \$prop1; + public \$prop1; } CODE; @@ -271,7 +317,7 @@ class MyType function it_assembles_property_with_null() { $assembler = new PropertyAssembler( - PropertyAssemblerOptions::create() + PropertyAssemblerOptions::create()->withTypeHints(false) ); $context = $this->createContextWithNullableType(); $assembler->assemble($context); @@ -286,7 +332,7 @@ class MyType * * @var null | string */ - private ?string \$prop1 = null; + private \$prop1 = null; } CODE;