From 5f73847725265c360a322523849bb31444ceb641 Mon Sep 17 00:00:00 2001 From: Artem Vasilev Date: Thu, 12 Dec 2024 21:21:57 +0300 Subject: [PATCH 1/7] Update dependencies --- composer.json | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 9e2720a..71f4494 100755 --- a/composer.json +++ b/composer.json @@ -1,16 +1,14 @@ { "name": "webmasterskaya/base-soap-lib-dev", - "description": "Набор инструментов для генерации кода и разработки PHP-SOAP приложения, совместимый с PHP 7.1", + "description": "Набор инструментов для генерации кода и разработки PHP-SOAP приложения", "type": "library", "license": "MIT", "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpro/soap-client": "~v1", - "laminas/laminas-code": "~3.1.0", - "webmasterskaya/base-soap-lib": "^0.0.2" - }, + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "phpro/soap-client": "~v3", + "laminas/laminas-code": "^4.14.0", + "webmasterskaya/base-soap-lib": "^1.0" + }, "autoload": { "psr-4": { "Webmasterskaya\\Soap\\Base\\Dev\\": "src/" From d6064d14eb69e1ed8708e4dee1ce4b78447ac6ac Mon Sep 17 00:00:00 2001 From: Artem Vasilev Date: Thu, 12 Dec 2024 21:22:12 +0300 Subject: [PATCH 2/7] Remove deprecated class --- .../Assembler/ArrayPropertyAssembler.php | 49 ------------------- 1 file changed, 49 deletions(-) delete mode 100755 src/CodeGenerator/Assembler/ArrayPropertyAssembler.php diff --git a/src/CodeGenerator/Assembler/ArrayPropertyAssembler.php b/src/CodeGenerator/Assembler/ArrayPropertyAssembler.php deleted file mode 100755 index a0f4782..0000000 --- a/src/CodeGenerator/Assembler/ArrayPropertyAssembler.php +++ /dev/null @@ -1,49 +0,0 @@ -canAssemble($context)) { - $iteratorAssembler->assemble($context); - } - - $arrayAccessAssembler = new ArrayAccessAssembler(); - if ($arrayAccessAssembler->canAssemble($context)) { - $arrayAccessAssembler->assemble($context); - } - - $countableAssembler = new CountableAssembler(); - if ($countableAssembler->canAssemble($context)) { - $countableAssembler->assemble($context); - } - - $arrayTypePatchAssembler = new ArrayTypePatchAssembler(); - if ($arrayTypePatchAssembler->canAssemble($context)) { - $arrayTypePatchAssembler->assemble($context); - } - } catch (\Exception $e) { - throw AssemblerException::fromException($e); - } - } -} \ No newline at end of file From 739b7a4674a3df4c4fc131a286349eab63e013d5 Mon Sep 17 00:00:00 2001 From: Artem Vasilev Date: Thu, 12 Dec 2024 21:22:37 +0300 Subject: [PATCH 3/7] Add CodeGeneratorEngineFactory --- src/Soap/CodeGeneratorEngineFactory.php | 62 +++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/Soap/CodeGeneratorEngineFactory.php diff --git a/src/Soap/CodeGeneratorEngineFactory.php b/src/Soap/CodeGeneratorEngineFactory.php new file mode 100644 index 0000000..1b87554 --- /dev/null +++ b/src/Soap/CodeGeneratorEngineFactory.php @@ -0,0 +1,62 @@ +withTypesManipulator( + new IntersectDuplicateTypesStrategy() + ); + + return new LazyEngine(static function () use ( + $wsdlLocation, + $loader, + $metadataOptions, + $parserContext, + $preferredSoapVersion + ) { + $wsdl = (new Wsdl1Reader($loader))($wsdlLocation, $parserContext); + $metadataProvider = new Wsdl1MetadataProvider( + $wsdl, + ServiceSelectionCriteria::defaults() + ->withAllowHttpPorts(false) + ->withPreferredSoapVersion($preferredSoapVersion) + ); + + return new SimpleEngine( + new PartialDriver( + metadata: MetadataFactory::manipulated($metadataProvider->getMetadata(), $metadataOptions), + ), + new NoopTransport() + ); + }); + } +} \ No newline at end of file From 2bac12cb07af3fbc240b33b77f18727fb4e55ac1 Mon Sep 17 00:00:00 2001 From: Artem Vasilev Date: Thu, 12 Dec 2024 21:23:08 +0300 Subject: [PATCH 4/7] Add new assemblers implementation --- .../Assembler/ClientConstructorAssembler.php | 73 +++++++++++++++++++ .../Assembler/ExtendingTypeAssembler.php | 8 ++ .../Assembler/StrictTypesAssembler.php | 8 ++ 3 files changed, 89 insertions(+) create mode 100644 src/CodeGenerator/Assembler/ClientConstructorAssembler.php create mode 100644 src/CodeGenerator/Assembler/ExtendingTypeAssembler.php create mode 100644 src/CodeGenerator/Assembler/StrictTypesAssembler.php diff --git a/src/CodeGenerator/Assembler/ClientConstructorAssembler.php b/src/CodeGenerator/Assembler/ClientConstructorAssembler.php new file mode 100644 index 0000000..3a29975 --- /dev/null +++ b/src/CodeGenerator/Assembler/ClientConstructorAssembler.php @@ -0,0 +1,73 @@ +getClass(); + try { + $caller = $this->generateClassNameAndAddImport(CallerInterface::class, $class); + $class->addPropertyFromGenerator( + (new PropertyGenerator( + name: 'caller', + flags: AbstractMemberGenerator::FLAG_PRIVATE, + type: TypeGenerator::fromTypeString($caller) + )) + ->setDocBlock(new DocBlockGenerator(tags: [new VarTag(description: $caller)])) + ->omitDefaultValue(true) + ); + $class->addMethodFromGenerator( + (new MethodGenerator( + name: '__construct', + parameters: [ + new ParameterGenerator('caller', CallerInterface::class) + ], + body: '$this->caller = $caller;' + )) + ); + } catch (\Exception $e) { + throw AssemblerException::fromException($e); + } + + return true; + } + + /** + * @param non-empty-string $fqcn + */ + private function generateClassNameAndAddImport(string $fqcn, ClassGenerator $class): string + { + $fqcn = non_empty_string()->assert(ltrim($fqcn, '\\')); + $parts = explode('\\', $fqcn); + $className = array_pop($parts); + + if (!\in_array($fqcn, $class->getUses(), true)) { + $class->addUse($fqcn); + } + + return $className; + } +} \ No newline at end of file diff --git a/src/CodeGenerator/Assembler/ExtendingTypeAssembler.php b/src/CodeGenerator/Assembler/ExtendingTypeAssembler.php new file mode 100644 index 0000000..747cd13 --- /dev/null +++ b/src/CodeGenerator/Assembler/ExtendingTypeAssembler.php @@ -0,0 +1,8 @@ + Date: Thu, 12 Dec 2024 21:23:28 +0300 Subject: [PATCH 5/7] Assemblers refactoring --- .../Assembler/ArrayAccessAssembler.php | 32 +++---------------- .../Assembler/ArrayPropertyPatchAssembler.php | 10 +++--- .../Assembler/ArrayTypePatchAssembler.php | 21 +++--------- .../Assembler/ClassMapAssembler.php | 18 +++-------- .../Assembler/ConstructorAssembler.php | 10 ++---- .../Assembler/CountableAssembler.php | 27 +++++++--------- .../Assembler/FluentSetterAssembler.php | 18 ++++------- .../Assembler/PropertyAssembler.php | 12 ++++++- 8 files changed, 50 insertions(+), 98 deletions(-) diff --git a/src/CodeGenerator/Assembler/ArrayAccessAssembler.php b/src/CodeGenerator/Assembler/ArrayAccessAssembler.php index b62c7fd..44f0fdb 100644 --- a/src/CodeGenerator/Assembler/ArrayAccessAssembler.php +++ b/src/CodeGenerator/Assembler/ArrayAccessAssembler.php @@ -61,13 +61,7 @@ private function implementOffsetExists(ClassGenerator $class, Property $firstPro $methodGenerator->setVisibility(MethodGenerator::VISIBILITY_PUBLIC); $methodGenerator->setParameters([['name' => 'offset']]); $methodGenerator->setReturnType('bool'); - $methodGenerator->setDocBlock( - DocBlockGenerator::fromArray([ - 'tags' => [ - new Tag\GenericTag('inheritDoc') - ] - ]) - ); + $methodGenerator->setDocBlock(new DocBlockGenerator(tags: [new Tag\GenericTag('inheritDoc')])); $methodGenerator->setBody( sprintf('return is_array($this->%1$s) && isset($this->%1$s[$offset]);', $firstProperty->getName()) @@ -84,13 +78,7 @@ private function implementOffsetGet(ClassGenerator $class, Property $firstProper $methodGenerator->setVisibility(MethodGenerator::VISIBILITY_PUBLIC); $methodGenerator->setParameters([['name' => 'offset']]); $methodGenerator->setReturnType(null); - $methodGenerator->setDocBlock( - DocBlockGenerator::fromArray([ - 'tags' => [ - new Tag\GenericTag('inheritDoc') - ] - ]) - ); + $methodGenerator->setDocBlock(new DocBlockGenerator(tags: [new Tag\GenericTag('inheritDoc')])); $lines = [ sprintf('return (is_array($this->%1$s) && isset($this->%1$s[$offset]))', $firstProperty->getName()), @@ -112,13 +100,7 @@ private function implementOffsetSet(ClassGenerator $class, Property $firstProper $methodGenerator = new MethodGenerator($methodName); $methodGenerator->setVisibility(MethodGenerator::VISIBILITY_PUBLIC); $methodGenerator->setParameters([['name' => 'offset'], ['name' => 'value']]); - $methodGenerator->setDocBlock( - DocBlockGenerator::fromArray([ - 'tags' => [ - new Tag\GenericTag('inheritDoc') - ] - ]) - ); + $methodGenerator->setDocBlock(new DocBlockGenerator(tags: [new Tag\GenericTag('inheritDoc')])); $lines = [ sprintf('if (!($value instanceof %s)) {', $firstProperty->getType()), @@ -175,13 +157,7 @@ private function implementOffsetUnset(ClassGenerator $class, Property $firstProp $methodGenerator = new MethodGenerator($methodName); $methodGenerator->setVisibility(MethodGenerator::VISIBILITY_PUBLIC); $methodGenerator->setParameters([['name' => 'offset']]); - $methodGenerator->setDocBlock( - DocBlockGenerator::fromArray([ - 'tags' => [ - new Tag\GenericTag('inheritDoc') - ] - ]) - ); + $methodGenerator->setDocBlock(new DocBlockGenerator(tags: [new Tag\GenericTag('inheritDoc')])); $lines = [ sprintf('if(is_array($this->%1$s)) {', $firstProperty->getName()), diff --git a/src/CodeGenerator/Assembler/ArrayPropertyPatchAssembler.php b/src/CodeGenerator/Assembler/ArrayPropertyPatchAssembler.php index c260430..3802f92 100644 --- a/src/CodeGenerator/Assembler/ArrayPropertyPatchAssembler.php +++ b/src/CodeGenerator/Assembler/ArrayPropertyPatchAssembler.php @@ -10,18 +10,18 @@ use Phpro\SoapClient\CodeGenerator\Model\Property; use Phpro\SoapClient\CodeGenerator\Model\Type; use Phpro\SoapClient\CodeGenerator\Util\Normalizer; -use Phpro\SoapClient\Exception\MetadataException; -use Phpro\SoapClient\Soap\Engine\Metadata\MetadataInterface; +use Soap\Engine\Exception\MetadataException; +use Soap\Engine\Metadata\Metadata; class ArrayPropertyPatchAssembler implements Assembler\AssemblerInterface { /** - * @var MetadataInterface + * @var Metadata */ private $metadata; - public function __construct(MetadataInterface $metadata) + public function __construct(Metadata $metadata) { $this->metadata = $metadata; } @@ -115,7 +115,7 @@ private function getTypeProperty($context): ?Property $property = $context->getProperty(); try { - $foundPropertyType = $this->metadata->getTypes()->fetchOneByName( + $foundPropertyType = $this->metadata->getTypes()->fetchFirstByName( Normalizer::getClassNameFromFQN($property->getType()) ); } catch (MetadataException $e) { diff --git a/src/CodeGenerator/Assembler/ArrayTypePatchAssembler.php b/src/CodeGenerator/Assembler/ArrayTypePatchAssembler.php index cd3885b..b5f55a7 100644 --- a/src/CodeGenerator/Assembler/ArrayTypePatchAssembler.php +++ b/src/CodeGenerator/Assembler/ArrayTypePatchAssembler.php @@ -167,9 +167,7 @@ private function applySetterPatch(Generator\ClassGenerator $class, Property $pro if ($options && $options->useDocBlocks()) { $docBlock = $method->getDocBlock(); - $method->setDocBlock( - Generator\DocBlockGenerator::fromArray($this->replaceDocblockParam($docBlock, $property)) - ); + $method->setDocBlock(new Generator\DocBlockGenerator(...$this->replaceDocblockParam($docBlock, $property))); } $method->setParameter(['name' => $property->getName()]); @@ -234,19 +232,10 @@ private function applyPropertyTypePatch(Generator\ClassGenerator $class, Propert { $class->removeProperty($property->getName()); $class->addPropertyFromGenerator( - Generator\PropertyGenerator::fromArray([ - 'name' => $property->getName(), - 'visibility' => Generator\PropertyGenerator::VISIBILITY_PRIVATE, - 'omitdefaultvalue' => true, - 'docblock' => DocBlockGeneratorFactory::fromArray([ - 'tags' => [ - [ - 'name' => 'var', - 'description' => sprintf('%s[]', $property->getType()), - ], - ] - ]) - ]) + (new Generator\PropertyGenerator($property->getName())) + ->setVisibility(Generator\PropertyGenerator::VISIBILITY_PRIVATE) + ->setDocBlock(new Generator\DocBlockGenerator(tags: [new Generator\DocBlock\Tag\VarTag(description: sprintf('%s[]', $property->getType()))])) + ->omitDefaultValue(true) ); } diff --git a/src/CodeGenerator/Assembler/ClassMapAssembler.php b/src/CodeGenerator/Assembler/ClassMapAssembler.php index 6b2d5ab..a7b1407 100755 --- a/src/CodeGenerator/Assembler/ClassMapAssembler.php +++ b/src/CodeGenerator/Assembler/ClassMapAssembler.php @@ -33,12 +33,8 @@ public function canAssemble(ContextInterface $context): bool */ public function assemble(ContextInterface $context) { - $class = ClassGenerator::fromArray( - [ - 'name' => $context->getName(), - 'implementedinterfaces' => [Normalizer::getClassNameFromFQN(ClientClassMapCollectionInterface::class)] - ] - ); + $class = new ClassGenerator($context->getName()); + $class->setImplementedInterfaces([Normalizer::getClassNameFromFQN(ClientClassMapCollectionInterface::class)]); $file = $context->getFile(); $file->setClass($class); $file->setNamespace($context->getNamespace()); @@ -54,14 +50,8 @@ public function assemble(ContextInterface $context) $classMap = $this->assembleClassMap($typeMap, $linefeed, $file->getIndentation()); $code = $this->assembleClassMapCollection($classMap, $linefeed).$linefeed; $class->addMethodFromGenerator( - MethodGenerator::fromArray( - [ - 'name' => '__invoke', - 'static' => false, - 'body' => 'return '.$code, - 'returntype' => ClassMapCollection::class, - ] - ) + (new MethodGenerator('__invoke', body: 'return ' . $code)) + ->setReturnType(ClassMapCollection::class) ); } catch (\Exception $e) { throw AssemblerException::fromException($e); diff --git a/src/CodeGenerator/Assembler/ConstructorAssembler.php b/src/CodeGenerator/Assembler/ConstructorAssembler.php index 22a90a8..9308639 100644 --- a/src/CodeGenerator/Assembler/ConstructorAssembler.php +++ b/src/CodeGenerator/Assembler/ConstructorAssembler.php @@ -2,6 +2,7 @@ namespace Webmasterskaya\Soap\Base\Dev\CodeGenerator\Assembler; +use Laminas\Code\Generator\DocBlockGenerator; use Laminas\Code\Generator\MethodGenerator; use Phpro\SoapClient\CodeGenerator\Context\ContextInterface; use Phpro\SoapClient\CodeGenerator\Context\TypeContext; @@ -62,13 +63,8 @@ public function assemble(ContextInterface $context) private function assembleConstructor(Type $type): MethodGenerator { $body = []; - $constructor = MethodGenerator::fromArray([ - 'name' => '__construct', - 'visibility' => MethodGenerator::VISIBILITY_PUBLIC, - ]); - $docblock = DocBlockGeneratorFactory::fromArray([ - 'shortdescription' => 'Constructor' - ]); + $constructor = new MethodGenerator('__construct'); + $docblock = new DocBlockGenerator('Constructor'); foreach ($type->getProperties() as $property) { $body[] = sprintf('$this->%1$s = $%1$s;', $property->getName()); diff --git a/src/CodeGenerator/Assembler/CountableAssembler.php b/src/CodeGenerator/Assembler/CountableAssembler.php index 82c3da4..e3bb8c3 100644 --- a/src/CodeGenerator/Assembler/CountableAssembler.php +++ b/src/CodeGenerator/Assembler/CountableAssembler.php @@ -48,21 +48,18 @@ private function implementCount($class, $firstProperty) $methodName = 'count'; $class->removeMethod($methodName); - $methodGenerator = new MethodGenerator($methodName); - $methodGenerator->setVisibility(MethodGenerator::VISIBILITY_PUBLIC); - $methodGenerator->setReturnType('int'); - $methodGenerator->setDocBlock( - DocBlockGenerator::fromArray([ - 'shortDescription' => 'Count elements of an object', - 'longDescription' => 'The return value is cast to an integer.', - 'tags' => [ - new Tag\ReturnTag([ - 'datatype' => 'int', - ]), - new Tag\GenericTag('link', 'https://php.net/manual/en/countable.count.php') - ] - ]) - ); + $methodGenerator = (new MethodGenerator($methodName)) + ->setReturnType('int') + ->setDocBlock( + new DocBlockGenerator( + 'Count elements of an object', + 'The return value is cast to an integer.', + [ + new Tag\ReturnTag('int'), + new Tag\GenericTag('link', 'https://php.net/manual/en/countable.count.php') + ] + ) + ); $methodGenerator->setBody( sprintf('return is_array($this->%1$s) ? count($this->%1$s) : 0;', $firstProperty->getName()) diff --git a/src/CodeGenerator/Assembler/FluentSetterAssembler.php b/src/CodeGenerator/Assembler/FluentSetterAssembler.php index df22fd2..58fd5ad 100644 --- a/src/CodeGenerator/Assembler/FluentSetterAssembler.php +++ b/src/CodeGenerator/Assembler/FluentSetterAssembler.php @@ -2,6 +2,9 @@ namespace Webmasterskaya\Soap\Base\Dev\CodeGenerator\Assembler; +use Laminas\Code\Generator\DocBlock\Tag\ParamTag; +use Laminas\Code\Generator\DocBlock\Tag\ReturnTag; +use Laminas\Code\Generator\DocBlockGenerator; use Laminas\Code\Generator\MethodGenerator; use Phpro\SoapClient\CodeGenerator\Assembler\FluentSetterAssemblerOptions; use Phpro\SoapClient\CodeGenerator\Context\ContextInterface; @@ -50,7 +53,6 @@ public function assemble(ContextInterface $context) $methodGenerator = new MethodGenerator($methodName); $methodGenerator->setParameters([$parameterOptions]); - $methodGenerator->setVisibility(MethodGenerator::VISIBILITY_PUBLIC); $methodGenerator->setBody( sprintf( '$this->%1$s = $%1$s;%2$sreturn $this;', @@ -63,17 +65,9 @@ public function assemble(ContextInterface $context) } if ($this->options->useDocBlocks()) { $methodGenerator->setDocBlock( - DocBlockGeneratorFactory::fromArray([ - 'tags' => [ - [ - 'name' => 'param', - 'description' => sprintf('%s $%s', $property->getType(), $property->getName()), - ], - [ - 'name' => 'return', - 'description' => '$this', - ], - ], + new DocBlockGenerator(tags: [ + new ParamTag(description: sprintf('%s $%s', $property->getType(), $property->getName())), + new ReturnTag(description: '$this') ]) ); } diff --git a/src/CodeGenerator/Assembler/PropertyAssembler.php b/src/CodeGenerator/Assembler/PropertyAssembler.php index 89b554c..9d2ea6c 100644 --- a/src/CodeGenerator/Assembler/PropertyAssembler.php +++ b/src/CodeGenerator/Assembler/PropertyAssembler.php @@ -2,7 +2,17 @@ namespace Webmasterskaya\Soap\Base\Dev\CodeGenerator\Assembler; +use Phpro\SoapClient\CodeGenerator\Context\ContextInterface; + class PropertyAssembler extends \Phpro\SoapClient\CodeGenerator\Assembler\PropertyAssembler { - + /** + * @param \Phpro\SoapClient\CodeGenerator\Context\PropertyContext$context + * @return void + */ + public function assemble(ContextInterface $context) + { + var_dump($context->getType()->getName()); + parent::assemble($context); + } } \ No newline at end of file From 6aa71c7d52bff0b98022623c737aad6555a38de2 Mon Sep 17 00:00:00 2001 From: Artem Vasilev Date: Thu, 12 Dec 2024 22:56:31 +0300 Subject: [PATCH 6/7] Implement dev exceptions --- src/Exception/AssemblerException.php | 16 ++++++++++++++++ src/Exception/RuntimeException.php | 7 +++++++ 2 files changed, 23 insertions(+) create mode 100644 src/Exception/AssemblerException.php create mode 100644 src/Exception/RuntimeException.php diff --git a/src/Exception/AssemblerException.php b/src/Exception/AssemblerException.php new file mode 100644 index 0000000..fa335e5 --- /dev/null +++ b/src/Exception/AssemblerException.php @@ -0,0 +1,16 @@ +getMessage(), $e->getCode(), $e); + } +} \ No newline at end of file diff --git a/src/Exception/RuntimeException.php b/src/Exception/RuntimeException.php new file mode 100644 index 0000000..89580ec --- /dev/null +++ b/src/Exception/RuntimeException.php @@ -0,0 +1,7 @@ + Date: Thu, 12 Dec 2024 22:57:00 +0300 Subject: [PATCH 7/7] Extend Client Assemblers --- .../Assembler/ClientConstructorAssembler.php | 4 +- .../Assembler/ClientMethodAssembler.php | 202 ++++++++++++++++++ 2 files changed, 205 insertions(+), 1 deletion(-) diff --git a/src/CodeGenerator/Assembler/ClientConstructorAssembler.php b/src/CodeGenerator/Assembler/ClientConstructorAssembler.php index 3a29975..c716e47 100644 --- a/src/CodeGenerator/Assembler/ClientConstructorAssembler.php +++ b/src/CodeGenerator/Assembler/ClientConstructorAssembler.php @@ -30,15 +30,17 @@ public function assemble(ContextInterface $context) $class = $context->getClass(); try { $caller = $this->generateClassNameAndAddImport(CallerInterface::class, $class); + $class->removeProperty('caller'); $class->addPropertyFromGenerator( (new PropertyGenerator( name: 'caller', flags: AbstractMemberGenerator::FLAG_PRIVATE, - type: TypeGenerator::fromTypeString($caller) + type: TypeGenerator::fromTypeString(CallerInterface::class) )) ->setDocBlock(new DocBlockGenerator(tags: [new VarTag(description: $caller)])) ->omitDefaultValue(true) ); + $class->removeMethod('__construct'); $class->addMethodFromGenerator( (new MethodGenerator( name: '__construct', diff --git a/src/CodeGenerator/Assembler/ClientMethodAssembler.php b/src/CodeGenerator/Assembler/ClientMethodAssembler.php index 6b7e2b0..2c62ad1 100644 --- a/src/CodeGenerator/Assembler/ClientMethodAssembler.php +++ b/src/CodeGenerator/Assembler/ClientMethodAssembler.php @@ -2,7 +2,209 @@ namespace Webmasterskaya\Soap\Base\Dev\CodeGenerator\Assembler; +use Laminas\Code\Generator\ClassGenerator; +use Laminas\Code\Generator\DocBlock\Tag\ParamTag; +use Laminas\Code\Generator\DocBlock\Tag\ReturnTag; +use Laminas\Code\Generator\DocBlock\Tag\ThrowsTag; +use Laminas\Code\Generator\DocBlockGenerator; +use Laminas\Code\Generator\MethodGenerator; +use Laminas\Code\Generator\ParameterGenerator; +use Phpro\SoapClient\CodeGenerator\Context\ClientMethodContext; +use Phpro\SoapClient\CodeGenerator\Context\ContextInterface; +use Phpro\SoapClient\CodeGenerator\GeneratorInterface; +use Phpro\SoapClient\CodeGenerator\Model\ClientMethod; +use Phpro\SoapClient\CodeGenerator\Util\Normalizer; +use Webmasterskaya\Soap\Base\Dev\Exception\AssemblerException; +use Webmasterskaya\Soap\Base\Exception\SoapException; +use Webmasterskaya\Soap\Base\Type\MultiArgumentRequest; +use Webmasterskaya\Soap\Base\Type\RequestInterface; +use Webmasterskaya\Soap\Base\Type\ResultInterface; + class ClientMethodAssembler extends \Phpro\SoapClient\CodeGenerator\Assembler\ClientMethodAssembler { + public function assemble(ContextInterface $context): bool + { + if (!$context instanceof ClientMethodContext) { + throw new AssemblerException( + __METHOD__ . ' expects an ' . ClientMethodContext::class . ' as input ' . get_class($context) . ' given' + ); + } + $class = $context->getClass(); + $method = $context->getMethod(); + try { + $phpMethodName = Normalizer::normalizeMethodName($method->getMethodName()); + $param = $this->createParamsFromContext($context); + $class->removeMethod($phpMethodName); + // TODO: Разобраться с ClientGenerator и убрать эту зачистку неймспейсов + $class + ->removeUse('Phpro\SoapClient\Type\ResultInterface') + ->removeUse('Phpro\SoapClient\Exception\SoapException') + ->removeUse('Phpro\SoapClient\Type\RequestInterface') + ->removeUse('Phpro\SoapClient\Caller\Caller'); + $docblock = $method->shouldGenerateAsMultiArgumentsRequest() + ? $this->generateMultiArgumentDocblock($context) + : $this->generateSingleArgumentDocblock($context); + $methodBody = $this->generateMethodBody($class, $param, $method, $context); + + $class->addMethodFromGenerator( + (new MethodGenerator( + name: $phpMethodName, + parameters: $param === null ? [] : [$param], + body: $methodBody, + docBlock: $docblock + ))->setReturnType($this->decideOnReturnType($context, true)) + ); + } catch (\Exception $e) { + throw AssemblerException::fromException($e); + } + + return true; + } + + private function generateMethodBody( + ClassGenerator $class, + ?ParameterGenerator $param, + ClientMethod $method, + $context + ): string { + $assertInstanceOf = static fn(string $class): string => '\\Psl\\Type\\instance_of(\\' . ltrim( + $class, + '\\' + ) . '::class)->assert($response);'; + + $code = [ + sprintf( + '/** @var %s $response */', + $this->decideOnReturnType($context, true) + ), + sprintf( + '$response = ($this->caller)(\'%s\', %s);', + $method->getMethodName(), + $param === null + ? 'new ' . $this->generateClassNameAndAddImport(MultiArgumentRequest::class, $class) . '([])' + : '$' . $param->getName() + ), + '', + $assertInstanceOf($this->decideOnReturnType($context, true)), + $assertInstanceOf(ResultInterface::class), + '', + 'return $response;', + ]; + + return implode($class::LINE_FEED, $code); + } + + /** + * @param ClientMethodContext $context + * + * @return ParameterGenerator|null + */ + private function createParamsFromContext(ClientMethodContext $context): ?ParameterGenerator + { + $method = $context->getMethod(); + $paramsCount = $method->getParametersCount(); + + if ($paramsCount === 0) { + return null; + } + + if (!$method->shouldGenerateAsMultiArgumentsRequest()) { + $param = current($context->getMethod()->getParameters()); + + return new ParameterGenerator(...$param->toArray()); + } + + return new ParameterGenerator(name: 'multiArgumentRequest', type: MultiArgumentRequest::class); + } + + /** + * @param ClientMethodContext $context + * + * @return DocBlockGenerator + */ + private function generateMultiArgumentDocblock(ClientMethodContext $context): DocBlockGenerator + { + $class = $context->getClass(); + $description = ['MultiArgumentRequest with following params:' . GeneratorInterface::EOL]; + foreach ($context->getMethod()->getParameters() as $parameter) { + $description[] = $parameter->getType() . ' $' . $parameter->getName(); + } + + return new DocBlockGenerator( + shortDescription: $context->getMethod()->getMeta()->docs()->unwrapOr(''), + longDescription: implode(GeneratorInterface::EOL, $description), + tags: [ + new ParamTag( + description: sprintf( + '%s $%s', + $this->generateClassNameAndAddImport( + MultiArgumentRequest::class, + $class + ), + 'multiArgumentRequest' + ) + ), + new ReturnTag( + description: sprintf( + '%s & %s', + $this->generateClassNameAndAddImport(ResultInterface::class, $class), + $this->decideOnReturnType($context, false) + ) + ), + new ThrowsTag( + description: $this->generateClassNameAndAddImport( + SoapException::class, + $class + ) + ) + ] + ); + } + + /** + * @param ClientMethodContext $context + * + * @return DocBlockGenerator + */ + private function generateSingleArgumentDocblock(ClientMethodContext $context): DocBlockGenerator + { + $method = $context->getMethod(); + $class = $context->getClass(); + $param = current($method->getParameters()); + + $tags = [ + new ReturnTag( + description: sprintf( + '%s & %s', + $this->generateClassNameAndAddImport(ResultInterface::class, $class), + $this->decideOnReturnType($context, false) + ) + ), + new ThrowsTag( + description: $this->generateClassNameAndAddImport( + SoapException::class, + $class + ) + ) + ]; + + if ($param) { + array_unshift( + $tags, + new ParamTag( + description: sprintf( + '%s & %s $%s', + $this->generateClassNameAndAddImport(RequestInterface::class, $class), + $this->generateClassNameAndAddImport($param->getType(), $class, true), + $param->getName() + ) + ) + ); + } + return (new DocBlockGenerator( + shortDescription: $context->getMethod()->getMeta()->docs()->unwrapOr(''), + tags: $tags + ))->setWordWrap(false); + } } \ No newline at end of file