diff --git a/composer.json b/composer.json index ac54af7..05d1d04 100644 --- a/composer.json +++ b/composer.json @@ -18,11 +18,11 @@ ], "license": "MIT", "require": { - "php": ">=7.2|^8.0", - "symfony/console": "^2.7|^3.0|^4.0|^5.0|^6.0", - "symfony/dependency-injection": "^2.2|^3.0|^4.0|^5.0|^6.0", - "symfony/yaml": "^2.2|^3.0|^4.0|^5.0|^6.0", - "symfony/config": "^2.2|^3.0|^4.0|^5.0|^6.0", + "php": "^8.0", + "symfony/console": "^4.0|^5.0|^6.0", + "symfony/dependency-injection": "^4.0|^5.0|^6.0", + "symfony/yaml": "^4.0|^5.0|^6.0", + "symfony/config": "^4.0|^5.0|^6.0", "goetas-webservices/xsd-reader": "^0.3.7 | ^0.4.1", "doctrine/inflector": "^2.0", "laminas/laminas-code": "^3.3.2|^4.0", @@ -47,7 +47,7 @@ }, "extra": { "branch-alias": { - "dev-master": "0.4-dev" + "dev-master": "1.x-dev" } }, "bin": [ diff --git a/src/Php/ClassGenerator.php b/src/Php/ClassGenerator.php index 1c80ccd..63cfc8c 100644 --- a/src/Php/ClassGenerator.php +++ b/src/Php/ClassGenerator.php @@ -14,16 +14,10 @@ use Laminas\Code\Generator\MethodGenerator; use Laminas\Code\Generator\ParameterGenerator; use Laminas\Code\Generator\PropertyGenerator; +use Laminas\Code\Generator\PropertyValueGenerator; class ClassGenerator { - private $strictTypes; - - public function __construct(bool $strictTypes = false) - { - $this->strictTypes = $strictTypes; - } - private function handleBody(Generator\ClassGenerator $class, PHPClass $type) { foreach ($type->getProperties() as $prop) { @@ -48,43 +42,60 @@ private function handleValueMethod(Generator\ClassGenerator $generator, PHPPrope { $type = $prop->getType(); - $docblock = new DocBlockGenerator('Construct'); + $docblock = new DocBlockGenerator(); $docblock->setWordWrap(false); $paramTag = new ParamTag('value'); $paramTag->setTypes(($type ? $type->getPhpType() : 'mixed')); - $docblock->setTag($paramTag); - $param = new ParameterGenerator('value'); - if ($type && !$type->isNativeType()) { - $param->setType($type->getPhpType()); + $param->setDefaultValue(null); + if ($type) { + $param->setType($this->getTypeAsPhpString($type)); + } else { + $docblock->setTag($paramTag); } $method = new MethodGenerator('__construct', [ $param, ]); - $method->setDocBlock($docblock); + if ($docblock->getTags()) { + $method->setDocBlock($docblock); + } $method->setBody('$this->value($value);'); $generator->addMethodFromGenerator($method); $docblock = new DocBlockGenerator('Gets or sets the inner value'); $docblock->setWordWrap(false); + + $parameter = new ParameterGenerator('value'); + $parameter->setVariadic(true); + $paramTag = new ParamTag('value'); + $paramTag->setDescription('if provided, allows to set the inner value'); if ($type && $type instanceof PHPClassOf) { $paramTag->setTypes($type->getArg()->getType()->getPhpType() . '[]'); + $parameter->setType('?array'); } elseif ($type) { $paramTag->setTypes($prop->getType()->getPhpType()); + $parameter->setType($this->getTypeAsPhpString($prop->getType())); + } else { + $docblock->setTag($paramTag); } - $docblock->setTag($paramTag); + + $method = new MethodGenerator('value', [$parameter]); $returnTag = new ReturnTag('mixed'); if ($type && $type instanceof PHPClassOf) { $returnTag->setTypes($type->getArg()->getType()->getPhpType() . '[]'); + $method->setReturnType('?array'); + $docblock->setTag($returnTag); } elseif ($type) { - $returnTag->setTypes($type->getPhpType()); + $method->setReturnType($this->getTypeAsPhpString($type)); + } else { + $docblock->setTag($returnTag); } - $docblock->setTag($returnTag); + $param = new ParameterGenerator('value'); $param->setDefaultValue(null); @@ -92,21 +103,21 @@ private function handleValueMethod(Generator\ClassGenerator $generator, PHPPrope if ($type && !$type->isNativeType()) { $param->setType($type->getPhpType()); } - $method = new MethodGenerator('value', []); + $method->setDocBlock($docblock); - $methodBody = 'if ($args = func_get_args()) {' . PHP_EOL; - $methodBody .= ' $this->' . $prop->getName() . ' = $args[0];' . PHP_EOL; + $methodBody = 'if ($value) {' . PHP_EOL; + $methodBody .= ' $this->' . $prop->getName() . ' = $value[0];' . PHP_EOL; $methodBody .= '}' . PHP_EOL; $methodBody .= 'return $this->' . $prop->getName() . ';' . PHP_EOL; $method->setBody($methodBody); $generator->addMethodFromGenerator($method); - $docblock = new DocBlockGenerator('Gets a string value'); + $docblock = new DocBlockGenerator('Gets the inner value as string'); $docblock->setWordWrap(false); - $docblock->setTag(new ReturnTag('string')); $method = new MethodGenerator('__toString'); + $method->setReturnType('string'); $method->setDocBlock($docblock); $method->setBody('return strval($this->' . $prop->getName() . ');'); $generator->addMethodFromGenerator($method); @@ -125,10 +136,7 @@ private function handleSetter(Generator\ClassGenerator $generator, PHPProperty $ } $patramTag = new ParamTag($prop->getName()); - $docblock->setTag($patramTag); - $return = new ReturnTag('self'); - $docblock->setTag($return); $type = $prop->getType(); @@ -138,9 +146,16 @@ private function handleSetter(Generator\ClassGenerator $generator, PHPProperty $ $parameter = new ParameterGenerator($prop->getName()); if ($type && $type instanceof PHPClassOf) { + $docblock->setTag($patramTag); $patramTag->setTypes($type->getArg() ->getType()->getPhpType() . '[]'); - $parameter->setType('array'); + + if ($type->getArg()->getDefault() === []) { + $parameter->setType('array'); + } else { + $parameter->setType('?array'); + } + if ($p = $type->getArg()->getType()->isSimpleType() ) { @@ -150,32 +165,20 @@ private function handleSetter(Generator\ClassGenerator $generator, PHPProperty $ } } elseif ($type) { if ($type->isNativeType()) { - $patramTag->setTypes($type->getPhpType()); - if ($this->strictTypes) { - $parameter->setType($type->getPhpType()); - } + $parameter->setType($this->getTypeAsPhpString($type)); } elseif ($p = $type->isSimpleType()) { if (($t = $p->getType()) && !$t->isNativeType()) { - $patramTag->setTypes($t->getPhpType()); $parameter->setType($t->getPhpType()); } elseif ($t) { - $patramTag->setTypes($t->getPhpType()); - if ($this->strictTypes) { - $parameter->setType($t->getPhpType()); - } + $parameter->setType($this->getTypeAsPhpString($t)); } } else { - $patramTag->setTypes($type->getPhpType()); $parameter->setType(($prop->getNullable() ? '?' : '') . $type->getPhpType()); } - } - if ($this->strictTypes && $prop->getDefault() === null) { - $parameter->setDefaultValue(null); - } - - if ($prop->getNullable() && $parameter->getType()) { - $parameter->setDefaultValue(null); + if ($prop->getNullable() && $parameter->getType()) { + $parameter->setDefaultValue(null); + } } $methodBody .= '$this->' . $prop->getName() . ' = $' . $prop->getName() . ';' . PHP_EOL; @@ -184,6 +187,10 @@ private function handleSetter(Generator\ClassGenerator $generator, PHPProperty $ $method->setDocBlock($docblock); $method->setParameter($parameter); + if ($prop->getDefault() === null) { + $method->setReturnType('static'); + } + $generator->addMethodFromGenerator($method); } @@ -199,17 +206,14 @@ private function handleGetter(Generator\ClassGenerator $generator, PHPProperty $ $docblock->setLongDescription($prop->getDoc()); } - $patramTag = new ParamTag('index', 'int|string'); - $docblock->setTag($patramTag); - - $docblock->setTag(new ReturnTag('bool')); - $paramIndex = new ParameterGenerator('index'); + $paramIndex->setType('int|string'); $method = new MethodGenerator('isset' . $inflector->classify($prop->getName()), [$paramIndex]); $method->setDocBlock($docblock); $method->setBody('return isset($this->' . $prop->getName() . '[$index]);'); + $method->setReturnType('bool'); $generator->addMethodFromGenerator($method); $docblock = new DocBlockGenerator(); @@ -219,15 +223,13 @@ private function handleGetter(Generator\ClassGenerator $generator, PHPProperty $ $docblock->setLongDescription($prop->getDoc()); } - $patramTag = new ParamTag('index', 'int|string'); - $docblock->setTag($patramTag); $paramIndex = new ParameterGenerator('index'); - - $docblock->setTag(new ReturnTag('void')); + $paramIndex->setType('int|string'); $method = new MethodGenerator('unset' . $inflector->classify($prop->getName()), [$paramIndex]); $method->setDocBlock($docblock); $method->setBody('unset($this->' . $prop->getName() . '[$index]);'); + $method->setReturnType('void'); $generator->addMethodFromGenerator($method); } // //// @@ -235,38 +237,39 @@ private function handleGetter(Generator\ClassGenerator $generator, PHPProperty $ $docblock = new DocBlockGenerator(); $docblock->setWordWrap(false); - $docblock->setShortDescription('Gets as ' . $prop->getName()); + $docblock->setShortDescription('Get the ' . $prop->getName()); if ($prop->getDoc()) { $docblock->setLongDescription($prop->getDoc()); } + $method = new MethodGenerator('get' . $inflector->classify($prop->getName())); + $method->setDocBlock($docblock); + $method->setBody('return $this->' . $prop->getName() . ';'); + $tag = new ReturnTag('mixed'); $type = $prop->getType(); if ($type && $type instanceof PHPClassOf) { $tt = $type->getArg()->getType(); $tag->setTypes($tt->getPhpType() . '[]'); - if ($p = $tt->isSimpleType()) { - if (($t = $p->getType())) { - $tag->setTypes($t->getPhpType() . '[]'); - } + $docblock->setTag($tag); + + + if ($type->getArg()->getDefault() === []) { + $method->setReturnType('array'); + } else { + $method->setReturnType('?array'); } } elseif ($type) { if ($p = $type->isSimpleType()) { if ($t = $p->getType()) { - $tag->setTypes($t->getPhpType()); + $method->setReturnType($this->getTypeAsPhpString($t)); } } else { - $tag->setTypes($type->getPhpType()); + $method->setReturnType($this->getTypeAsPhpString($type)); } } - $docblock->setTag($tag); - - $method = new MethodGenerator('get' . $inflector->classify($prop->getName())); - $method->setDocBlock($docblock); - $method->setBody('return $this->' . $prop->getName() . ';'); - $generator->addMethodFromGenerator($method); } @@ -283,15 +286,12 @@ private function handleAdder(Generator\ClassGenerator $generator, PHPProperty $p $docblock->setLongDescription($prop->getDoc()); } - $return = new ReturnTag(); - $return->setTypes('self'); - $docblock->setTag($return); - $patramTag = new ParamTag($propName, $type->getArg()->getType()->getPhpType()); $docblock->setTag($patramTag); $inflector = InflectorFactory::create()->build(); $method = new MethodGenerator('addTo' . $inflector->classify($prop->getName())); + $method->setReturnType('static'); $parameter = new ParameterGenerator($propName); $tt = $type->getArg()->getType(); @@ -336,22 +336,23 @@ private function handleProperty(Generator\ClassGenerator $class, PHPProperty $pr $class->addPropertyFromGenerator($generatedProp); - if ($prop->getType() && (!$prop->getType()->getNamespace() && $prop->getType()->getName() == 'array')) { - // $generatedProp->setDefaultValue(array(), PropertyValueGenerator::TYPE_AUTO, PropertyValueGenerator::OUTPUT_SINGLE_LINE); - } - $docBlock = new DocBlockGenerator(); $docBlock->setWordWrap(false); - $generatedProp->setDocBlock($docBlock); - if ($prop->getDoc()) { - $docBlock->setLongDescription($prop->getDoc()); - } $tag = new VarTag($prop->getName(), 'mixed'); $type = $prop->getType(); if ($type && $type instanceof PHPClassOf) { + + if ($type->getArg()->getDefault() === []) { + $generatedProp->setType(Generator\TypeGenerator::fromTypeString('array')); + $generatedProp->setDefaultValue($type->getArg()->getDefault(), PropertyValueGenerator::TYPE_ARRAY, PropertyValueGenerator::OUTPUT_SINGLE_LINE); + } else { + $generatedProp->setType(Generator\TypeGenerator::fromTypeString('?array')); + $generatedProp->setDefaultValue($type->getArg()->getDefault()); + } + $tt = $type->getArg()->getType(); $tag->setTypes($tt->getPhpType() . '[]'); if ($p = $tt->isSimpleType()) { @@ -359,17 +360,28 @@ private function handleProperty(Generator\ClassGenerator $class, PHPProperty $pr $tag->setTypes($t->getPhpType() . '[]'); } } - $generatedProp->setDefaultValue($type->getArg()->getDefault()); + + $docBlock->setTag($tag); } elseif ($type) { if ($type->isNativeType()) { - $tag->setTypes($type->getPhpType()); + $generatedProp->setType(Generator\TypeGenerator::fromTypeString($this->getTypeAsPhpString($type))); } elseif (($p = $type->isSimpleType()) && ($t = $p->getType())) { - $tag->setTypes($t->getPhpType()); + $generatedProp->setType(Generator\TypeGenerator::fromTypeString($this->getTypeAsPhpString($t))); } else { - $tag->setTypes($prop->getType()->getPhpType()); + $generatedProp->setType(Generator\TypeGenerator::fromTypeString($this->getTypeAsPhpString($prop->getType()))); } + } else { + $docBlock->setTag($tag); } - $docBlock->setTag($tag); + + if ($prop->getDoc()) { + $docBlock->setLongDescription($prop->getDoc()); + } + + if ($prop->getDoc() || $docBlock->getTags()) { + $generatedProp->setDocBlock($docBlock); + } + } public function generate(PHPClass $type) @@ -406,4 +418,9 @@ public function generate(PHPClass $type) return $class; } } + + private function getTypeAsPhpString(PHPClass $type): string + { + return ($type->getPhpType() === 'mixed' ? '' : '?') . $type->getPhpType(); + } } diff --git a/src/Php/PhpConverter.php b/src/Php/PhpConverter.php index 1d48fe7..79829b5 100644 --- a/src/Php/PhpConverter.php +++ b/src/Php/PhpConverter.php @@ -6,6 +6,7 @@ use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem; use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup; use GoetasWebservices\XML\XSDReader\Schema\Element\Element; +use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer; use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef; use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem; use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef; @@ -119,33 +120,6 @@ private function visitTypeBase(PHPClass $class, Type $type) } } - /** - * Process xsd:complexType xsd:choice xsd:element - * - * @param PHPClass $class - * @param Schema $schema - * @param Choice $choice - */ - private function visitChoice(PHPClass $class, Schema $schema, Choice $choice) - { - foreach ($choice->getElements() as $choiceOption) { - $property = $this->visitElement($class, $schema, $choiceOption); - $class->addProperty($property); - } - } - - private function visitGroup(PHPClass $class, Schema $schema, Group $group) - { - foreach ($group->getElements() as $childGroup) { - if ($childGroup instanceof Group) { - $this->visitGroup($class, $schema, $childGroup); - } else { - $property = $this->visitElement($class, $schema, $childGroup); - $class->addProperty($property); - } - } - } - private function visitAttributeGroup(PHPClass $class, Schema $schema, AttributeGroup $att) { foreach ($att->getAttributes() as $childAttr) { @@ -319,18 +293,27 @@ private function visitTypeAnonymous(Type $type, $name, PHPClass $parentClass) private function visitComplexType(PHPClass $class, ComplexType $type) { $schema = $type->getSchema(); - foreach ($type->getElements() as $element) { - if ($element instanceof Choice) { - $this->visitChoice($class, $schema, $element); - } elseif ($element instanceof Group) { - $this->visitGroup($class, $schema, $element); + foreach ($this->flattElements($type) as $element) { + $property = $this->visitElement($class, $schema, $element); + $class->addProperty($property); + } + } + + private function flattElements(ElementContainer $container) + { + $items = []; + foreach ($container->getElements() as $attr) { + if ($attr instanceof ElementContainer) { + $items = array_merge($items, $this->flattElements($attr)); } else { - $property = $this->visitElement($class, $schema, $element); - $class->addProperty($property); + $items[] = $attr; } } + + return $items; } + private function visitSimpleType(PHPClass $class, SimpleType $type) { if ($restriction = $type->getRestriction()) { diff --git a/tests/Converter/Validator/Xsd2ValidatorTest.php b/tests/Converter/Validator/Xsd2ValidatorTest.php index bdf5e04..97a44be 100644 --- a/tests/Converter/Validator/Xsd2ValidatorTest.php +++ b/tests/Converter/Validator/Xsd2ValidatorTest.php @@ -136,17 +136,6 @@ public function getRestrictionsValidations() ], ], ], - [ - '', - [ - [ - 'Regex' => [ - 'pattern' => '~[\x{0000}-\x{007F}\x{0080}-\x{00FF}]~u', - 'groups' => ['xsd_rules'], - ], - ], - ], - ], [ '', [ diff --git a/tests/PHP/AnyTypePHPConversionTest.php b/tests/PHP/AnyTypePHPConversionTest.php index f4f01c9..beff5ed 100644 --- a/tests/PHP/AnyTypePHPConversionTest.php +++ b/tests/PHP/AnyTypePHPConversionTest.php @@ -105,8 +105,8 @@ public function testSimpleAnyTypePHP() $returnTags = $single->getMethod('getId')->getDocBlock()->getTags(); - $this->assertCount(1, $returnTags); - $this->assertEquals(['mixed'], $returnTags[0]->getTypes()); + $this->assertCount(0, $returnTags); + $this->assertEquals('mixed', (string)$single->getMethod('getId')->getReturnType()); } public function testSimpleAnyTypeYaml() diff --git a/tests/PHP/PHPConversionTest.php b/tests/PHP/PHPConversionTest.php index 37e9b49..c49a5ee 100644 --- a/tests/PHP/PHPConversionTest.php +++ b/tests/PHP/PHPConversionTest.php @@ -146,9 +146,11 @@ public function testMulteplicity() $this->assertTrue($codegen->hasMethod('getId')); $this->assertTrue($codegen->hasMethod('setId')); + $this->assertEquals('static',(string)$codegen->getMethod('setId')->getReturnType()); + $this->assertEquals('array',(string)$codegen->getMethod('getId')->getReturnType()); - $this->assertNull($codegen->getMethod('issetId')->getParameters()['index']->getType()); - $this->assertNull($codegen->getMethod('issetId')->getParameters()['index']->getType()); + $this->assertEquals('int|string',$codegen->getMethod('issetId')->getParameters()['index']->getType()); + $this->assertEquals('int|string',$codegen->getMethod('unsetId')->getParameters()['index']->getType()); } public function testNestedMulteplicity() @@ -278,7 +280,7 @@ public function testNillableElement() $this->assertTrue($codegen->hasMethod('setDate2')); $this->assertNull($codegen->getMethod('setDate2')->getParameters()['date2']->getDefaultValue()); $this->assertTrue($codegen->hasMethod('setStr1')); - $this->assertNull($codegen->getMethod('setStr1')->getParameters()['str1']->getDefaultValue()); + $this->assertNull($codegen->getMethod('setStr1')->getParameters()['str1']->getDefaultValue()->getValue()); $this->assertTrue($codegen->hasMethod('setStr2')); $this->assertNull($codegen->getMethod('setStr2')->getParameters()['str2']->getDefaultValue()); }