Skip to content

Commit

Permalink
[WIP] Read additional meta-information from special soap-enc types
Browse files Browse the repository at this point in the history
  • Loading branch information
veewee committed Jun 5, 2024
1 parent 399615a commit 2a7f4a4
Show file tree
Hide file tree
Showing 39 changed files with 845 additions and 42 deletions.
14 changes: 12 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"require": {
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"ext-dom": "*",
"goetas-webservices/xsd-reader": "^0.4.1",
"php-soap/engine": "^2.8",
"goetas-webservices/xsd-reader": "dev-merged-prs as 0.4.4",
"php-soap/engine": "dev-main as 2.8",
"php-soap/wsdl": "^1.4",
"php-soap/xml": "^1.6.0",
"veewee/xml": "^3.0",
Expand All @@ -38,5 +38,15 @@
"name": "Toon Verwerft",
"email": "[email protected]"
}
],
"repositories": [
{
"type": "path",
"url": "../engine"
},
{
"type": "path",
"url": "../xsd-reader"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Soap\Engine\Metadata\Model\Type as EngineType;
use Soap\Engine\Metadata\Model\TypeMeta;
use Soap\Engine\Metadata\Model\XsdType;
use Soap\WsdlReader\Metadata\Detector;
use Soap\WsdlReader\Model\Definitions\Message;
use Soap\WsdlReader\Model\Definitions\Namespaces;
use Soap\WsdlReader\Model\Definitions\Part;
Expand All @@ -30,7 +31,12 @@ public function __invoke(Message $message): array
{
return pull(
$message->parts->items,
fn (Part $part): XsdType => $this->findXsdType($part->element)->withXmlTargetNodeName($part->name),
fn (Part $part): XsdType => $this->findXsdType($part->element)
->withXmlTargetNodeName($part->name)
->withMeta(
static fn (TypeMeta $meta): TypeMeta => $meta
->withIsElement(true)
),
static fn (Part $message): string => $message->name
);
}
Expand All @@ -55,32 +61,35 @@ private function findXsdType(?QNamed $type): XsdType

private function createSimpleTypeByQNamed(QNamed $type): XsdType
{
$namespace = $this->namespaces->lookupNamespaceByQname($type);
$namespace = $this->namespaces->lookupNamespaceByQname($type)->unwrapOr('');

return XsdType::guess($type->localName)
->withXmlNamespaceName($type->prefix)
->withXmlNamespace($namespace->unwrapOr(''))
->withXmlNamespace($namespace)
->withXmlTypeName($type->localName)
->withMeta(
static fn (TypeMeta $meta): TypeMeta => $meta
->withIsSimple(true)
->withIsElement(true)
fn (TypeMeta $meta): TypeMeta => $meta
->withIsSimple($this->guessIfQnamedIsSimple($namespace, $type))
);
}

private function guessIfQNamedIsSimple(string $namespace, QNamed $type): bool
{
return match (true) {
Detector\Soap11ArrayDetector::detect($namespace, $type->localName),
Detector\Soap12ArrayDetector::detect($namespace, $type->localName),
Detector\Soap11StructDetector::detect($namespace, $type->localName),
Detector\Soap12StructDetector::detect($namespace, $type->localName),
Detector\ApacheMapDetector::detect($namespace, $type->localName) => false,
default => true
};
}

private function createAnyType(): XsdType
{
$namespace = Xmlns::xsd()->value();

return XsdType::guess('anyType')
->withXmlTypeName('anyType')
->withXmlNamespaceName($this->namespaces->lookupNameFromNamespace($namespace)->unwrapOr(''))
->withXmlNamespace($namespace)
->withXmlTypeName('anyType')
->withMeta(
static fn (TypeMeta $meta): TypeMeta => $meta
->withIsSimple(true)
->withIsElement(true)
);
return XsdType::any()
->withXmlNamespaceName($this->namespaces->lookupNameFromNamespace($namespace)->unwrapOr(''));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public function __invoke(EngineType $engineType, mixed $xsdType, TypesConverterC
->withMaxOccurs($max)
->withIsNullable($isNullable)
->withIsList($isList)
->withIsRepeatingElement($isList)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php
declare(strict_types=1);

namespace Soap\WsdlReader\Metadata\Converter\Types\Configurator\SoapEnc;

use GoetasWebservices\XML\XSDReader\Schema\Element\ElementSingle;
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
use GoetasWebservices\XML\XSDReader\Schema\Type\Type as XsdType;
use Psl\Option\Option;
use Soap\Engine\Metadata\Model\TypeMeta;
use Soap\Engine\Metadata\Model\XsdType as MetaType;
use Soap\WsdlReader\Metadata\Converter\Types\Detector\AttributesCustomAttributeDetector;
use Soap\WsdlReader\Metadata\Converter\Types\Detector\NamedAttributesDetector;
use Soap\WsdlReader\Metadata\Converter\Types\SoapEnc\ArrayTypeInfo;
use Soap\WsdlReader\Metadata\Converter\Types\TypesConverterContext;
use Soap\WsdlReader\Metadata\Detector\Soap11ArrayDetector;
use Soap\Xml\Xmlns;
use function Psl\Option\none;
use function Psl\Option\some;
use function Psl\Vec\filter_nulls;

final class Soap11ArrayConfigurator
{
public function __invoke(MetaType $metaType, XsdType $xsdType, TypesConverterContext $context): MetaType
{
$base = $xsdType->getRestriction()?->getBase();
if (!$xsdType instanceof ComplexType || !$base instanceof ComplexType) {
return $metaType;
}

$namespace = $base->getSchema()->getTargetNamespace();
$typeName = $base->getName();
if (!Soap11ArrayDetector::detect($namespace, $typeName)) {
return $metaType;
}

return $this->parseFromElement($metaType, $xsdType, $context)
->or($this->parseFromAttribute($metaType, $xsdType, $context))
->unwrapOr($metaType);
}

/**
* @param ComplexType $xsdType
* @return Option<MetaType>
*/
private function parseFromElement(MetaType $metaType, XsdType $xsdType, TypesConverterContext $context): Option
{
if (!$xsdType->getElements()) {
return none();
}

$element = $xsdType->getElements()[0];
if (!$element instanceof ElementSingle) {
return none();
}

$type = $element->getType();
$typeName = $type?->getName();
if (!$type || !$typeName) {
return none();
}

$namespace = $type->getSchema()->getTargetNamespace() ?? '';
$arrayTypeInfo = new ArrayTypeInfo(
$context->knownNamespaces->lookupNameFromNamespace($namespace)->unwrap(),
$typeName,
'['.($element->getMax() > -1 ? (string) $element->getMax() : '').']'
);

return some(
$this->applyArrayTypInfoToMeta($metaType, $arrayTypeInfo, $namespace, $element->getName())
);
}

/**
* @return Option<MetaType>
*/
private function parseFromAttribute(MetaType $metaType, XsdType $xsdType, TypesConverterContext $context): Option
{
$attrs = (new NamedAttributesDetector())($xsdType);
$arrayTypeResult = (new AttributesCustomAttributeDetector())($attrs, 'arrayType', 'arrayType');

if ($arrayTypeResult->isNone()) {
return none();
}

$arrayType = $arrayTypeResult->unwrap();
$arrayTypeInfo = ArrayTypeInfo::parseSoap11($arrayType->getValue());
if (!$arrayTypeInfo->prefix) {
$arrayTypeInfo = $arrayTypeInfo->withPrefix(
$context->knownNamespaces->lookupNameFromNamespace(Xmlns::xsd()->value())->unwrap()
);
}
$namespace = $context->knownNamespaces->lookupNamespaceFromName($arrayTypeInfo->prefix)->unwrap();

return some($this->applyArrayTypInfoToMeta($metaType, $arrayTypeInfo, $namespace));
}

private function applyArrayTypInfoToMeta(
MetaType $metaType,
ArrayTypeInfo $arrayTypeInfo,
string $namespace,
?string $arrayNodeName = null
): MetaType {
return $metaType
->withBaseType('array')
->withMemberTypes([$arrayTypeInfo->type])
->withMeta(
static fn (TypeMeta $meta): TypeMeta => $meta
->withIsElement(true)
->withIsSimple(false)
->withIsList(true)
->withIsAlias(true)
->withMinOccurs(0)
->withMaxOccurs($arrayTypeInfo->getMaxOccurs())
->withUnions(
filter_nulls([
[
'type' => $arrayTypeInfo->type,
'namespace' => $namespace,
'isList' => false,
]
])
)
->withArrayType([
'type' => $arrayTypeInfo->toString(),
'itemType' => $arrayTypeInfo->itemType(),
'namespace' => $namespace,
])
->withArrayNodeName($arrayNodeName)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?php
declare(strict_types=1);

namespace Soap\WsdlReader\Metadata\Converter\Types\Configurator\SoapEnc;

use GoetasWebservices\XML\XSDReader\Schema\Element\ElementSingle;
use GoetasWebservices\XML\XSDReader\Schema\CustomAttribute;
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
use GoetasWebservices\XML\XSDReader\Schema\Type\Type as XsdType;
use Psl\Option\Option;
use Soap\Engine\Metadata\Model\TypeMeta;
use Soap\Engine\Metadata\Model\XsdType as MetaType;
use Soap\WsdlReader\Metadata\Converter\Types\Detector\AttributesCustomAttributeDetector;
use Soap\WsdlReader\Metadata\Converter\Types\Detector\NamedAttributesDetector;
use Soap\WsdlReader\Metadata\Converter\Types\SoapEnc\ArrayTypeInfo;
use Soap\WsdlReader\Metadata\Converter\Types\TypesConverterContext;
use Soap\WsdlReader\Metadata\Detector\Soap12ArrayDetector;
use Soap\Xml\Xmlns;
use function Psl\Option\none;
use function Psl\Option\some;
use function Psl\Vec\filter_nulls;

final class Soap12ArrayConfigurator
{
public function __invoke(MetaType $metaType, XsdType $xsdType, TypesConverterContext $context): MetaType
{
$base = $xsdType->getRestriction()?->getBase();
if (!$xsdType instanceof ComplexType || !$base instanceof ComplexType) {
return $metaType;
}

$namespace = $base->getSchema()->getTargetNamespace();
$typeName = $base->getName();
if (!Soap12ArrayDetector::detect($namespace, $typeName)) {
return $metaType;
}

return $this->parseFromElement($metaType, $xsdType, $context)
->or($this->parseFromAttribute($metaType, $xsdType, $context))
->unwrapOr($metaType);
}

/**
* @param ComplexType $xsdType
* @return Option<MetaType>
*/
private function parseFromElement(MetaType $metaType, XsdType $xsdType, TypesConverterContext $context): Option
{
if (!$xsdType->getElements()) {
return none();
}

$element = $xsdType->getElements()[0];
if (!$element instanceof ElementSingle) {
return none();
}

$type = $element->getType();
$typeName = $type?->getName();
if (!$type || !$typeName) {
return none();
}

$namespace = $type->getSchema()->getTargetNamespace() ?? '';
$arrayTypeInfo = new ArrayTypeInfo(
$context->knownNamespaces->lookupNameFromNamespace($namespace)->unwrap(),
$typeName,
'['.($element->getMax() > -1 ? (string) $element->getMax() : '').']'
);

return some(
$this->applyArrayTypInfoToMeta($metaType, $arrayTypeInfo, $namespace, $element->getName())
);
}

/**
* @return Option<MetaType>
*/
private function parseFromAttribute(MetaType $metaType, XsdType $xsdType, TypesConverterContext $context): Option
{
$attrs = (new NamedAttributesDetector())($xsdType);

$itemTypeResult = (new AttributesCustomAttributeDetector())($attrs, 'itemType', 'itemType');
$arraySizeResult = (new AttributesCustomAttributeDetector())($attrs, 'arraySize', 'arraySize');

if (!$itemTypeResult->isSome()) {
return none();
}

$itemType = $itemTypeResult->unwrap();
$arrayTypeInfo = ArrayTypeInfo::parseSoap12(
$itemType->getValue(),
$arraySizeResult->map(static fn (CustomAttribute $meta): string => $meta->getValue())->unwrapOr('*')
);

if (!$arrayTypeInfo->prefix) {
$arrayTypeInfo = $arrayTypeInfo->withPrefix(
$context->knownNamespaces->lookupNameFromNamespace(Xmlns::xsd()->value())->unwrap()
);
}
$namespace = $context->knownNamespaces->lookupNamespaceFromName($arrayTypeInfo->prefix)->unwrap();

return some($this->applyArrayTypInfoToMeta($metaType, $arrayTypeInfo, $namespace));
}

private function applyArrayTypInfoToMeta(
MetaType $metaType,
ArrayTypeInfo $arrayTypeInfo,
string $namespace,
?string $arrayNodeName = null
): MetaType {
return $metaType
->withBaseType('array')
->withMemberTypes([$arrayTypeInfo->type])
->withMeta(
static fn (TypeMeta $meta): TypeMeta => $meta
->withIsElement(true)
->withIsSimple(false)
->withIsList(true)
->withIsAlias(true)
->withMinOccurs(0)
->withMaxOccurs($arrayTypeInfo->getMaxOccurs())
->withUnions(
filter_nulls([
[
'type' => $arrayTypeInfo->type,
'namespace' => $namespace,
'isList' => false,
]
])
)
->withArrayType([
'type' => $arrayTypeInfo->toString(),
'itemType' => $arrayTypeInfo->itemType(),
'namespace' => $namespace,
])
->withArrayNodeName($arrayNodeName)
);
}
}
Loading

0 comments on commit 2a7f4a4

Please sign in to comment.