diff --git a/UPGRADING.md b/UPGRADING.md index f48dfcca..6c830703 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -33,6 +33,9 @@ return Config::create() )) ``` +**Note:** In case you are using a custom configuration class by implementing the `ConfigInterface` : this is not supported anymore. +Since code generation gets complexer and complexer, we decided to make the configuration more strict. + Regenerate classes: ``` @@ -71,18 +74,16 @@ In case you are using a non-default metadata strategy, you can now configure it ```php use Phpro\SoapClient\CodeGenerator\Config\Config; use Phpro\SoapClient\Soap\Metadata\Manipulators\DuplicateTypes\RemoveDuplicateTypesStrategy; -use Phpro\SoapClient\Soap\Metadata\MetadataOptions; return Config::create() - ->setTypeMetadataOptions( - MetadataOptions::empty()->withTypesManipulator(new RemoveDuplicateTypesStrategy()) + ->setDuplicateTypeIntersectStrategy( + new RemoveDuplicateTypesStrategy() ) ``` [More information about the metadata options can be found here.](/docs/drivers/metadata.md) - # V2 to V3 ```bash diff --git a/composer.json b/composer.json index 85bc31a8..a89b8cf0 100644 --- a/composer.json +++ b/composer.json @@ -15,11 +15,11 @@ "php": "~8.1.0 || ~8.2.0 || ~8.3.0", "azjezz/psl": "^2.1", "laminas/laminas-code": "^4.8.0", - "php-soap/cached-engine": "~0.1", - "php-soap/engine": "^2.9", + "php-soap/cached-engine": "~0.2", + "php-soap/engine": "^2.10.1", "php-soap/encoding": "~0.2", "php-soap/psr18-transport": "^1.6", - "php-soap/wsdl-reader": "~0.15", + "php-soap/wsdl-reader": "~0.16", "psr/event-dispatcher": "^1.0", "psr/log": "^1.0 || ^2.0 || ^3.0", "symfony/console": "~5.4 || ~6.0 || ~7.0", diff --git a/docs/drivers/metadata.md b/docs/drivers/metadata.md index 1e00cac2..68961d3d 100644 --- a/docs/drivers/metadata.md +++ b/docs/drivers/metadata.md @@ -22,8 +22,8 @@ use Phpro\SoapClient\Soap\Metadata\MetadataOptions; return Config::create() //... - ->setTypeMetadataOptions( - MetadataOptions::empty()->withTypesManipulator(new IntersectDuplicateTypesStrategy()) + ->setDuplicateTypeIntersectStrategy( + new IntersectDuplicateTypesStrategy() ) // ... ``` @@ -41,8 +41,61 @@ use Phpro\SoapClient\Soap\Metadata\MetadataOptions; return Config::create() //... - ->setTypeMetadataOptions( - MetadataOptions::empty()->withTypesManipulator(new RemoveDuplicateTypesStrategy()) + ->setDuplicateTypeIntersectStrategy( + new IntersectDuplicateTypesStrategy() ) // ... ``` + +### Type replacements + +Depending on what XML encoders you configure, you might want to replace some types with other types. +Take following example: + +By default, a "date" type from the XSD namespace `http://www.w3.org/2001/XMLSchema` will be converted to a `DateTimeImmutable` object. +However, if you configure an encoder that does not support `DateTimeImmutable`, +you might want to replace it with a `int` type that represents the amount of seconds since the unix epoch. + +This can be configured in the [client configuration](/docs/code-generation/configuration.md): + +```php +use Phpro\SoapClient\CodeGenerator\Config\Config; +use Phpro\SoapClient\Soap\Metadata\Manipulators\TypeReplacer\TypeReplacers; + +return Config::create() + //... + ->setTypeReplacements( + TypeReplacers::defaults() + ->add(new MyDateReplacer()) + ) + // ... +``` + +The `MyDateReplacer` class should implement the `TypeReplacerInterface` and should return the correct type for the given type. + +```php +use Phpro\SoapClient\Soap\Metadata\Manipulators\TypeReplacer\TypeReplacer; +use Soap\Engine\Metadata\Model\XsdType; +use Soap\WsdlReader\Metadata\Predicate\IsOfType;use Soap\Xml\Xmlns; + +final class MyDateReplacer implements TypeReplacer +{ + public function __invoke(XsdType $xsdType) : XsdType + { + $check = new IsOfType(Xmlns::xsd()->value(), 'date'); + if (!$check($xsdType)) { + return $xsdType; + } + + return $xsdType->copy('int')->withBaseType('int'); + } +} +``` + +This way, the generated code will use the `int` type instead of the `DateTimeImmutable` type for the `date` type in the XSD. + +The TypeReplacers contain a default set of type replacements that are being used to improve the generated code: + +* `array` for SOAP 1.1 and 1.2 Arrays +* `object` for SOAP 1.1 Objects +* `array` for Apache Map types diff --git a/spec/Phpro/SoapClient/CodeGenerator/Config/ConfigSpec.php b/spec/Phpro/SoapClient/CodeGenerator/Config/ConfigSpec.php index 8c0ffc6f..438b9e66 100644 --- a/spec/Phpro/SoapClient/CodeGenerator/Config/ConfigSpec.php +++ b/spec/Phpro/SoapClient/CodeGenerator/Config/ConfigSpec.php @@ -23,11 +23,6 @@ function it_is_initializable() $this->shouldHaveType(Config::class); } - function it_is_a_config_class() - { - $this->shouldImplement(ConfigInterface::class); - } - function it_has_an_engine(Engine $engine) { $this->setEngine($engine); diff --git a/src/Phpro/SoapClient/CodeGenerator/Config/Config.php b/src/Phpro/SoapClient/CodeGenerator/Config/Config.php index 75e181e8..fe36ef77 100644 --- a/src/Phpro/SoapClient/CodeGenerator/Config/Config.php +++ b/src/Phpro/SoapClient/CodeGenerator/Config/Config.php @@ -10,15 +10,19 @@ use Phpro\SoapClient\CodeGenerator\Util\Normalizer; use Phpro\SoapClient\Exception\InvalidArgumentException; use Phpro\SoapClient\Soap\Metadata\Manipulators\DuplicateTypes\IntersectDuplicateTypesStrategy; +use Phpro\SoapClient\Soap\Metadata\Manipulators\MethodsManipulatorChain; +use Phpro\SoapClient\Soap\Metadata\Manipulators\TypeReplacer\ReplaceMethodTypesManipulator; +use Phpro\SoapClient\Soap\Metadata\Manipulators\TypeReplacer\ReplaceTypesManipulator; +use Phpro\SoapClient\Soap\Metadata\Manipulators\TypeReplacer\TypeReplacer; +use Phpro\SoapClient\Soap\Metadata\Manipulators\TypeReplacer\TypeReplacers; +use Phpro\SoapClient\Soap\Metadata\Manipulators\TypesManipulatorChain; +use Phpro\SoapClient\Soap\Metadata\Manipulators\TypesManipulatorInterface; +use Phpro\SoapClient\Soap\Metadata\MetadataFactory; use Phpro\SoapClient\Soap\Metadata\MetadataOptions; use Soap\Engine\Engine; +use Soap\Engine\Metadata\Metadata; -/** - * Class Config - * - * @package Phpro\SoapClient\CodeGenerator\Config - */ -final class Config implements ConfigInterface +final class Config { /** * @var string @@ -50,7 +54,11 @@ final class Config implements ConfigInterface */ protected $typeDestination = ''; - protected MetadataOptions $typeMetadataOptions; + protected TypesManipulatorInterface $duplicateTypeIntersectStrategy; + + protected TypeReplacer $typeReplacementStrategy; + + protected ?MetadataOptions $metadataOptions; /** * @var RuleSetInterface @@ -74,12 +82,13 @@ final class Config implements ConfigInterface public function __construct() { - $this->typeMetadataOptions = MetadataOptions::empty()->withTypesManipulator( - // Working with duplicate types is hard (see FAQ). - // Therefore, we decided to combine all duplicate types into 1 big intersected type by default instead. - // The resulting type will always be usable, but might contain some additional empty properties. - new IntersectDuplicateTypesStrategy() - ); + $this->typeReplacementStrategy = TypeReplacers::defaults(); + + // Working with duplicate types is hard (see FAQ). + // Therefore, we decided to combine all duplicate types into 1 big intersected type by default instead. + // The resulting type will always be usable, but might contain some additional empty properties. + $this->duplicateTypeIntersectStrategy = new IntersectDuplicateTypesStrategy(); + $this->ruleSet = new RuleSet([ new Rules\AssembleRule(new Assembler\PropertyAssembler()), new Rules\AssembleRule(new Assembler\ClassMapAssembler()), @@ -259,18 +268,49 @@ public function setTypeDestination($typeDestination): self return $this; } - public function getTypeMetadataOptions(): MetadataOptions + public function getMetadataOptions(): MetadataOptions + { + return $this->metadataOptions ?? MetadataOptions::empty() + ->withTypesManipulator( + new TypesManipulatorChain( + $this->duplicateTypeIntersectStrategy, + new ReplaceTypesManipulator($this->typeReplacementStrategy), + ) + )->withMethodsManipulator( + new MethodsManipulatorChain( + new ReplaceMethodTypesManipulator($this->typeReplacementStrategy) + ) + ); + } + + public function getManipulatedMetadata(): Metadata + { + return MetadataFactory::manipulated( + $this->getEngine()->getMetadata(), + $this->getMetadataOptions() + ); + } + + public function setTypeReplacementStrategy(TypeReplacer $typeReplacementStrategy): self { - return $this->typeMetadataOptions; + $this->typeReplacementStrategy = $typeReplacementStrategy; + + return $this; } - public function setTypeMetadataOptions(MetadataOptions $typeMetadataOptions): self + public function setDuplicateTypeIntersectStrategy(TypesManipulatorInterface $duplicateTypeIntersectStrategy): self { - $this->typeMetadataOptions = $typeMetadataOptions; + $this->duplicateTypeIntersectStrategy = $duplicateTypeIntersectStrategy; return $this; } + public function setMetadataOptions(MetadataOptions $metadataOptions): self + { + $this->metadataOptions = $metadataOptions; + + return $this; + } /** * @return string diff --git a/src/Phpro/SoapClient/CodeGenerator/Config/ConfigInterface.php b/src/Phpro/SoapClient/CodeGenerator/Config/ConfigInterface.php deleted file mode 100644 index a1b6599a..00000000 --- a/src/Phpro/SoapClient/CodeGenerator/Config/ConfigInterface.php +++ /dev/null @@ -1,69 +0,0 @@ - 'callable', 'iterable' => 'iterable', 'array' => 'array', + 'void' => 'mixed', ]; /** diff --git a/src/Phpro/SoapClient/Console/Command/GenerateClassmapCommand.php b/src/Phpro/SoapClient/Console/Command/GenerateClassmapCommand.php index 5b85a3d4..b7ecf515 100644 --- a/src/Phpro/SoapClient/Console/Command/GenerateClassmapCommand.php +++ b/src/Phpro/SoapClient/Console/Command/GenerateClassmapCommand.php @@ -5,6 +5,7 @@ use Phpro\SoapClient\CodeGenerator\ClassMapGenerator; use Phpro\SoapClient\CodeGenerator\Model\TypeMap; use Phpro\SoapClient\Console\Helper\ConfigHelper; +use Phpro\SoapClient\Soap\Metadata\Manipulators\TypesManipulatorChain; use Phpro\SoapClient\Util\Filesystem; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; @@ -72,9 +73,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io = new SymfonyStyle($input, $output); $config = $this->getConfigHelper()->load($input); + // For class-maps, we don't want to do anything with duplicate types: + // All types should be listed with namespace and name, even if that means there will be a duplicate entry. + $config->setDuplicateTypeIntersectStrategy(new TypesManipulatorChain()); + $typeMap = TypeMap::fromMetadata( non_empty_string()->assert($config->getTypeNamespace()), - $config->getEngine()->getMetadata()->getTypes() + $config->getManipulatedMetadata()->getTypes(), ); $generator = new ClassMapGenerator( @@ -86,7 +91,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->handleClassmap($generator, $typeMap, $path); $io->success('Generated classmap at ' . $path); - + return 0; } diff --git a/src/Phpro/SoapClient/Console/Command/GenerateClientCommand.php b/src/Phpro/SoapClient/Console/Command/GenerateClientCommand.php index 46b0e4d4..523571bd 100644 --- a/src/Phpro/SoapClient/Console/Command/GenerateClientCommand.php +++ b/src/Phpro/SoapClient/Console/Command/GenerateClientCommand.php @@ -6,14 +6,14 @@ use Phpro\SoapClient\CodeGenerator\GeneratorInterface; use Phpro\SoapClient\CodeGenerator\Model\Client; use Phpro\SoapClient\CodeGenerator\Model\ClientMethodMap; -use Phpro\SoapClient\CodeGenerator\TypeGenerator; use Phpro\SoapClient\Console\Helper\ConfigHelper; +use Phpro\SoapClient\Soap\Metadata\Manipulators\TypesManipulatorChain; +use Phpro\SoapClient\Soap\Metadata\MetadataFactory; use Phpro\SoapClient\Util\Filesystem; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Style\SymfonyStyle; use Laminas\Code\Generator\FileGenerator; use function Psl\Type\instance_of; @@ -77,7 +77,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $destination = $config->getClientDestination().'/'.$config->getClientName().'.php'; $methodMap = ClientMethodMap::fromMetadata( non_empty_string()->assert($config->getTypeNamespace()), - $config->getEngine()->getMetadata()->getMethods() + $config->getManipulatedMetadata()->getMethods(), ); $client = new Client( diff --git a/src/Phpro/SoapClient/Console/Command/GenerateTypesCommand.php b/src/Phpro/SoapClient/Console/Command/GenerateTypesCommand.php index 5a69055f..058bbaba 100644 --- a/src/Phpro/SoapClient/Console/Command/GenerateTypesCommand.php +++ b/src/Phpro/SoapClient/Console/Command/GenerateTypesCommand.php @@ -75,10 +75,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $config = $this->getConfigHelper()->load($input); $typeMap = TypeMap::fromMetadata( non_empty_string()->assert($config->getTypeNamespace()), - MetadataFactory::manipulated( - $config->getEngine()->getMetadata(), - $config->getTypeMetadataOptions() - )->getTypes() + $config->getManipulatedMetadata()->getTypes(), ); $generator = new TypeGenerator($config->getRuleSet()); diff --git a/src/Phpro/SoapClient/Console/Helper/ConfigHelper.php b/src/Phpro/SoapClient/Console/Helper/ConfigHelper.php index 3585cd02..3533409f 100644 --- a/src/Phpro/SoapClient/Console/Helper/ConfigHelper.php +++ b/src/Phpro/SoapClient/Console/Helper/ConfigHelper.php @@ -33,16 +33,15 @@ public function getName():string /** * Attempts to load the configuration file, returns it on success * @param InputInterface $input - * @return ConfigInterface */ - public function load(InputInterface $input): ConfigInterface + public function load(InputInterface $input): Config { $configFile = $input->getOption('config'); if (!$configFile || !$this->filesystem->fileExists($configFile)) { throw InvalidArgumentException::invalidConfigFile(); } $config = include $configFile; - if (!$config instanceof ConfigInterface) { + if (!$config instanceof Config) { throw InvalidArgumentException::invalidConfigFile(); } diff --git a/src/Phpro/SoapClient/Exception/InvalidArgumentException.php b/src/Phpro/SoapClient/Exception/InvalidArgumentException.php index a3e63a79..d3c72fa5 100644 --- a/src/Phpro/SoapClient/Exception/InvalidArgumentException.php +++ b/src/Phpro/SoapClient/Exception/InvalidArgumentException.php @@ -23,7 +23,7 @@ public static function destinationConfigurationIsMissing(): self public static function invalidConfigFile(): self { - return new static('You have to provide a code-generator config file which returns a ConfigInterface.'); + return new static('You have to provide a code-generator config file which returns a Config class instance.'); } public static function clientNamespaceIsMissing(): self diff --git a/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/ApacheMapReplacer.php b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/ApacheMapReplacer.php new file mode 100644 index 00000000..0aebf4e6 --- /dev/null +++ b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/ApacheMapReplacer.php @@ -0,0 +1,27 @@ +copy('array') + ->withBaseType('array') + ->withMeta( + // Mark as simple to make sure no additional types are generated for this type. + static fn (TypeMeta $meta): TypeMeta => $meta->withIsSimple(true) + ); + } +} diff --git a/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/ReplaceMethodTypesManipulator.php b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/ReplaceMethodTypesManipulator.php new file mode 100644 index 00000000..19003276 --- /dev/null +++ b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/ReplaceMethodTypesManipulator.php @@ -0,0 +1,41 @@ +replaceMethodTypes(...)) + ); + } + + private function replaceMethodTypes(Method $method): Method + { + return new Method( + $method->getName(), + new ParameterCollection(...map( + $method->getParameters(), + fn(Parameter $parameter): Parameter => new Parameter( + $parameter->getName(), + ($this->typeReplacer)($parameter->getType()), + ) + )), + ($this->typeReplacer)($method->getReturnType()), + ); + } +} diff --git a/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/ReplaceTypesManipulator.php b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/ReplaceTypesManipulator.php new file mode 100644 index 00000000..b04ee765 --- /dev/null +++ b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/ReplaceTypesManipulator.php @@ -0,0 +1,42 @@ +replaceTypeTypes(...)) + ); + } + + private function replaceTypeTypes(Type $type): Type + { + return new Type( + ($this->typeReplacer)($type->getXsdType()), + new PropertyCollection( + ...map( + $type->getProperties(), + fn(Property $property): Property => new Property( + $property->getName(), + ($this->typeReplacer)($property->getType()) + ), + ) + ) + ); + } +} diff --git a/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/SoapArrayReplacer.php b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/SoapArrayReplacer.php new file mode 100644 index 00000000..03f9f150 --- /dev/null +++ b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/SoapArrayReplacer.php @@ -0,0 +1,44 @@ +isArrayType($xsdType)) { + return $xsdType; + } + + return $xsdType->copy('array') + ->withBaseType('array') + ->withMeta( + // Mark as simple to make sure no additional types are generated for this type. + static fn (TypeMeta $meta): TypeMeta => $meta->withIsSimple(true) + ); + } + + private function isArrayType(XsdType $type): bool + { + $checks = [ + new IsOfType(EncodingStyle::SOAP_11->value, 'Array'), + ...map( + EncodingStyle::listKnownSoap12Version(), + /** + * @param non-empty-string $namespace + */ + static fn (string $namespace): IsOfType => new IsOfType($namespace, 'Array') + ), + ]; + + return any($checks, static fn (IsOfType $check): bool => $check($type)); + } +} diff --git a/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/SoapObjectReplacer.php b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/SoapObjectReplacer.php new file mode 100644 index 00000000..6e101eb2 --- /dev/null +++ b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/SoapObjectReplacer.php @@ -0,0 +1,27 @@ +value, 'Struct'); + if (!$check($xsdType)) { + return $xsdType; + } + + return $xsdType->copy('struct') + ->withBaseType('object') + ->withMeta( + // Mark as simple to make sure no additional types are generated for this type. + static fn (TypeMeta $meta): TypeMeta => $meta->withIsSimple(true) + ); + } +} diff --git a/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/TypeReplacer.php b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/TypeReplacer.php new file mode 100644 index 00000000..cecfc5d9 --- /dev/null +++ b/src/Phpro/SoapClient/Soap/Metadata/Manipulators/TypeReplacer/TypeReplacer.php @@ -0,0 +1,11 @@ + + */ + private array $replacers; + + /** + * @no-named-arguments + */ + private function __construct(TypeReplacer ...$replacers) + { + $this->replacers = $replacers; + } + + public static function defaults(): self + { + return new self( + new ApacheMapReplacer(), + new SoapObjectReplacer(), + new SoapArrayReplacer(), + ); + } + + public static function empty(): self + { + return new self(); + } + + public function add(TypeReplacer $replacer): self + { + $new = clone $this; + $new->replacers[] = $replacer; + + return $new; + } + + public function __invoke(XsdType $type): XsdType + { + return reduce( + $this->replacers, + static fn(XsdType $type, TypeReplacer $replacer): XsdType => $replacer($type), + $type, + ); + } +} diff --git a/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/ApacheMapReplacerTest.php b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/ApacheMapReplacerTest.php new file mode 100644 index 00000000..7f2eb2e0 --- /dev/null +++ b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/ApacheMapReplacerTest.php @@ -0,0 +1,42 @@ + [ + $baseType = (new XsdType('')) + ->withXmlNamespace(ApacheMapDetector::NAMESPACE) + ->withXmlTypeName('Map'), + $baseType->copy('array') + ->withBaseType('array') + ->withMeta( + static fn (TypeMeta $meta): TypeMeta => $meta->withIsSimple(true) + ) + ]; + yield 'nomap' => [ + $baseType = (new XsdType('')) + ->withXmlNamespace('http://none') + ->withXmlTypeName('Map'), + $baseType + ]; + } +} diff --git a/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/ReplaceMethodTypesManipulatorTest.php b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/ReplaceMethodTypesManipulatorTest.php new file mode 100644 index 00000000..39b03d1c --- /dev/null +++ b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/ReplaceMethodTypesManipulatorTest.php @@ -0,0 +1,62 @@ +copy($type->getName() . '_replaced'); + } + }; + + $replace = new ReplaceMethodTypesManipulator($replacements); + $actual = $replace($methods); + + self::assertEquals( + new MethodCollection( + new Method( + 'hello', + new ParameterCollection( + new Parameter('param1', $int->copy('int_replaced')), + new Parameter('param2', $string->copy('string_replaced')), + ), + $object->copy('object_replaced') + ) + ), + $actual + ); + } +} diff --git a/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/ReplaceTypesManipulatorTest.php b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/ReplaceTypesManipulatorTest.php new file mode 100644 index 00000000..668be25a --- /dev/null +++ b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/ReplaceTypesManipulatorTest.php @@ -0,0 +1,57 @@ +copy($type->getName() . '_replaced'); + } + }; + + $replace = new ReplaceTypesManipulator($replacements); + $actual = $replace($types); + + self::assertEquals( + new TypeCollection( + new Type( + $object->copy('object_replaced'), + new PropertyCollection( + new Property('property1', $int->copy('int_replaced')) + ) + ), + new Type( + $object->copy('object_replaced'), + new PropertyCollection( + new Property('property1', $string->copy('string_replaced')) + ) + ), + ), + $actual + ); + } +} diff --git a/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/SoapArrayReplacerTest.php b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/SoapArrayReplacerTest.php new file mode 100644 index 00000000..3b0ff4ed --- /dev/null +++ b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/SoapArrayReplacerTest.php @@ -0,0 +1,56 @@ + [ + $baseType = (new XsdType('')) + ->withXmlNamespace(EncodingStyle::SOAP_11->value) + ->withXmlTypeName('Array'), + $baseType->copy('array') + ->withBaseType('array') + ->withMeta( + static fn (TypeMeta $meta): TypeMeta => $meta->withIsSimple(true) + ) + ]; + + foreach (EncodingStyle::listKnownSoap12Version() as $namespace) { + yield 'soap12-array'.$namespace => [ + $baseType = (new XsdType('')) + ->withXmlNamespace($namespace) + ->withXmlTypeName('Array'), + $baseType->copy('array') + ->withBaseType('array') + ->withMeta( + static fn (TypeMeta $meta): TypeMeta => $meta->withIsSimple(true) + ) + ]; + } + + yield 'no-array' => [ + $baseType = (new XsdType('')) + ->withXmlNamespace('http://none') + ->withXmlTypeName('Array'), + $baseType + ]; + } +} diff --git a/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/SoapObjectReplacerTest.php b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/SoapObjectReplacerTest.php new file mode 100644 index 00000000..7532d90a --- /dev/null +++ b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/SoapObjectReplacerTest.php @@ -0,0 +1,43 @@ + [ + $baseType = (new XsdType('')) + ->withXmlNamespace(EncodingStyle::SOAP_11->value) + ->withXmlTypeName('Struct'), + $baseType->copy('struct') + ->withBaseType('object') + ->withMeta( + static fn (TypeMeta $meta): TypeMeta => $meta->withIsSimple(true) + ) + ]; + yield 'no-struct' => [ + $baseType = (new XsdType('')) + ->withXmlNamespace('http://none') + ->withXmlTypeName('Struct'), + $baseType + ]; + } +} diff --git a/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/TypeReplacersTest.php b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/TypeReplacersTest.php new file mode 100644 index 00000000..91436a48 --- /dev/null +++ b/test/PhproTest/SoapClient/Unit/Soap/Metadata/Manipulators/TypeReplacer/TypeReplacersTest.php @@ -0,0 +1,40 @@ +add( + new class() implements TypeReplacer { + public function __invoke(XsdType $type): XsdType + { + return $type->copy($type->getName() . '_replaced'); + } + } + ) + ->add( + new class() implements TypeReplacer { + public function __invoke(XsdType $type): XsdType + { + return $type->copy($type->getName() . '_again'); + } + } + ); + + $type = XsdType::create('hello'); + $actual = $replace($type); + + self::assertSame('hello_replaced_again', $actual->getName()); + } +}