Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type replacer #525

Merged
merged 1 commit into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

```
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
61 changes: 57 additions & 4 deletions docs/drivers/metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ use Phpro\SoapClient\Soap\Metadata\MetadataOptions;

return Config::create()
//...
->setTypeMetadataOptions(
MetadataOptions::empty()->withTypesManipulator(new IntersectDuplicateTypesStrategy())
->setDuplicateTypeIntersectStrategy(
new IntersectDuplicateTypesStrategy()
)
// ...
```
Expand All @@ -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
5 changes: 0 additions & 5 deletions spec/Phpro/SoapClient/CodeGenerator/Config/ConfigSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
74 changes: 57 additions & 17 deletions src/Phpro/SoapClient/CodeGenerator/Config/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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()),
Expand Down Expand Up @@ -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
Expand Down
69 changes: 0 additions & 69 deletions src/Phpro/SoapClient/CodeGenerator/Config/ConfigInterface.php

This file was deleted.

1 change: 1 addition & 0 deletions src/Phpro/SoapClient/CodeGenerator/Util/Normalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Normalizer
'callable' => 'callable',
'iterable' => 'iterable',
'array' => 'array',
'void' => 'mixed',
];

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand All @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
Loading
Loading