Skip to content

Commit 14754ce

Browse files
authored
Merge pull request #10 from veewee/read-xml-from-memory
Read xml from memory
2 parents 3a1f3b5 + af4a2d7 commit 14754ce

24 files changed

+437
-80
lines changed

src/Decoder.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use Soap\WsdlReader\Model\Definitions\BindingUse;
1212
use Soap\WsdlReader\Model\Definitions\Namespaces;
1313
use function count;
14-
use function Psl\Type\non_empty_string;
14+
use function Psl\invariant;
1515
use function Psl\Vec\map;
1616

1717
final class Decoder implements SoapDecoder
@@ -39,9 +39,9 @@ public function decode(string $method, SoapResponse $response): mixed
3939

4040
// The SoapResponse only contains the payload of the response (with no headers).
4141
// It can be parsed directly as XML.
42-
$parts = (new OperationReader($meta))(
43-
non_empty_string()->assert($response->getPayload())
44-
);
42+
$payload = $response->getPayload();
43+
invariant($payload !== '', 'Expected a non-empty response payload. Received an empty HTTP response');
44+
$parts = (new OperationReader($meta))($payload)->elements();
4545

4646
return match(count($parts)) {
4747
0 => null,

src/Encoder/ElementEncoder.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33

44
namespace Soap\Encoding\Encoder;
55

6+
use Soap\Encoding\Xml\Node\Element;
67
use Soap\Encoding\Xml\Reader\ElementValueReader;
78
use Soap\Encoding\Xml\Writer\ElementValueBuilder;
89
use Soap\Encoding\Xml\Writer\XsdTypeXmlElementWriter;
910
use VeeWee\Reflecta\Iso\Iso;
10-
use VeeWee\Xml\Dom\Document;
1111

1212
/**
1313
* @implements XmlEncoder<mixed, string>
@@ -38,13 +38,13 @@ public function iso(Context $context): Iso
3838
(new ElementValueBuilder($context, $typeEncoder, $raw))
3939
),
4040
/**
41-
* @psalm-param non-empty-string $xml
41+
* @psalm-param non-empty-string|Element $xml
4242
* @psalm-return mixed
4343
*/
44-
static fn (string $xml): mixed => (new ElementValueReader())(
44+
static fn (Element|string $xml): mixed => (new ElementValueReader())(
4545
$context,
4646
$typeEncoder,
47-
Document::fromXmlString($xml)->locateDocumentElement(),
47+
($xml instanceof Element ? $xml : Element::fromString($xml))->element()
4848
)
4949
);
5050
}

src/Encoder/ObjectEncoder.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Soap\Encoding\Normalizer\PhpPropertyNameNormalizer;
99
use Soap\Encoding\TypeInference\ComplexTypeBuilder;
1010
use Soap\Encoding\TypeInference\XsiTypeDetector;
11+
use Soap\Encoding\Xml\Node\Element;
1112
use Soap\Encoding\Xml\Reader\DocumentToLookupArrayReader;
1213
use Soap\Encoding\Xml\Writer\AttributeBuilder;
1314
use Soap\Encoding\Xml\Writer\NilAttributeBuilder;
@@ -62,11 +63,15 @@ function (object|array $value) use ($context, $properties) : string {
6263
return $this->to($context, $properties, $value);
6364
},
6465
/**
65-
* @param non-empty-string $value
66+
* @param non-empty-string|Element $value
6667
* @return TObj
6768
*/
68-
function (string $value) use ($context, $properties) : object {
69-
return $this->from($context, $properties, $value);
69+
function (string|Element $value) use ($context, $properties) : object {
70+
return $this->from(
71+
$context,
72+
$properties,
73+
($value instanceof Element ? $value : Element::fromString($value))
74+
);
7075
}
7176
);
7277
}
@@ -131,11 +136,10 @@ function (Property $property) use ($context, $data, $defaultAction) : Closure {
131136

132137
/**
133138
* @param array<string, Property> $properties
134-
* @param non-empty-string $data
135139
*
136140
* @return TObj
137141
*/
138-
private function from(Context $context, array $properties, string $data): object
142+
private function from(Context $context, array $properties, Element $data): object
139143
{
140144
$nodes = (new DocumentToLookupArrayReader())($data);
141145
/** @var Iso<TObj, array<string, mixed>> $objectData */

src/Encoder/OptionalElementEncoder.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33

44
namespace Soap\Encoding\Encoder;
55

6+
use Soap\Encoding\Xml\Node\Element;
67
use Soap\Encoding\Xml\Writer\NilAttributeBuilder;
78
use Soap\Encoding\Xml\Writer\XsdTypeXmlElementWriter;
89
use VeeWee\Reflecta\Iso\Iso;
9-
use VeeWee\Xml\Dom\Document;
1010
use VeeWee\Xml\Xmlns\Xmlns;
1111

1212
/**
@@ -48,16 +48,17 @@ public function iso(Context $context): Iso
4848
/**
4949
* @return T|null
5050
*/
51-
static function (string $xml) use ($elementIso) : mixed {
51+
static function (Element|string $xml) use ($elementIso) : mixed {
5252
if ($xml === '') {
5353
return null;
5454
}
5555

56-
$documentElement = Document::fromXmlString($xml)->locateDocumentElement();
56+
$documentElement = ($xml instanceof Element ? $xml : Element::fromString($xml))->element();
5757
if ($documentElement->getAttributeNS(Xmlns::xsi()->value(), 'nil') === 'true') {
5858
return null;
5959
}
6060

61+
/** @var Iso<T|null, Element|non-empty-string> $elementIso */
6162
return $elementIso->from($xml);
6263
}
6364
);

src/Encoder/RepeatingElementEncoder.php

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@
33

44
namespace Soap\Encoding\Encoder;
55

6-
use DOMElement;
6+
use Soap\Encoding\Xml\Node\Element;
7+
use Soap\Encoding\Xml\Node\ElementList;
78
use Soap\Engine\Metadata\Model\TypeMeta;
89
use VeeWee\Reflecta\Iso\Iso;
9-
use VeeWee\Xml\Dom\Document;
1010
use function Psl\Str\join;
1111
use function Psl\Vec\map;
12-
use function VeeWee\Xml\Dom\Locator\Element\children as readChildren;
1312

1413
/**
1514
* @template T
@@ -57,11 +56,18 @@ static function (iterable $raw) use ($innerIso): string {
5756
/**
5857
* @return iterable<array-key, T>
5958
*/
60-
static function (string $xml) use ($innerIso): iterable {
61-
$doc = Document::fromXmlString('<list>'.$xml.'</list>');
59+
static function (Element|ElementList|string $xml) use ($innerIso): iterable {
6260

63-
return readChildren($doc->locateDocumentElement())->map(
64-
static fn (DOMElement $element): mixed => $innerIso->from($doc->stringifyNode($element))
61+
$elements = match (true) {
62+
$xml instanceof Element => [$xml],
63+
$xml instanceof ElementList => $xml->elements(),
64+
default => ElementList::fromString('<list>'.$xml.'</list>')->elements()
65+
};
66+
67+
/** @var Iso<T|null, Element|non-empty-string> $innerIso */
68+
return map(
69+
$elements,
70+
static fn (Element $element): mixed => $innerIso->from($element)
6571
);
6672
}
6773
);

src/Encoder/SoapEnc/ApacheMapEncoder.php

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
use Soap\Encoding\Encoder\SimpleType\ScalarTypeEncoder;
1010
use Soap\Encoding\Encoder\XmlEncoder;
1111
use Soap\Encoding\TypeInference\XsiTypeDetector;
12+
use Soap\Encoding\Xml\Node\Element;
1213
use Soap\Encoding\Xml\Reader\ElementValueReader;
1314
use Soap\Encoding\Xml\Writer\XsdTypeXmlElementWriter;
1415
use Soap\Encoding\Xml\Writer\XsiAttributeBuilder;
1516
use Soap\Engine\Metadata\Model\XsdType;
1617
use VeeWee\Reflecta\Iso\Iso;
17-
use VeeWee\Xml\Dom\Document;
1818
use function Psl\Dict\merge;
1919
use function Psl\Type\string;
2020
use function VeeWee\Xml\Dom\Assert\assert_element;
@@ -39,9 +39,12 @@ public function iso(Context $context): Iso
3939
*/
4040
fn (array $value): string => $this->encodeArray($context, $value),
4141
/**
42-
* @param non-empty-string $value
42+
* @param non-empty-string|Element $value
4343
*/
44-
fn (string $value): array => $this->decodeArray($context, $value),
44+
fn (string|Element $value): array => $this->decodeArray(
45+
$context,
46+
$value instanceof Element ? $value : Element::fromString($value)
47+
),
4548
));
4649
}
4750

@@ -76,14 +79,10 @@ private function encodeArray(Context $context, array $data): string
7679
);
7780
}
7881

79-
/**
80-
* @param non-empty-string $value
81-
*/
82-
private function decodeArray(Context $context, string $value): array
82+
private function decodeArray(Context $context, Element $value): array
8383
{
84-
$document = Document::fromXmlString($value);
85-
$xpath = $document->xpath();
86-
$element = $document->locateDocumentElement();
84+
$element = $value->element();
85+
$xpath = $value->document()->xpath();
8786

8887
return readChildren($element)->reduce(
8988
static function (array $map, DOMElement $item) use ($context, $xpath): array {

src/Encoder/SoapEnc/SoapArrayEncoder.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
use Soap\Encoding\Encoder\SimpleType\ScalarTypeEncoder;
1212
use Soap\Encoding\Encoder\XmlEncoder;
1313
use Soap\Encoding\TypeInference\XsiTypeDetector;
14+
use Soap\Encoding\Xml\Node\Element;
1415
use Soap\Encoding\Xml\Reader\ElementValueReader;
1516
use Soap\Encoding\Xml\Writer\XsdTypeXmlElementWriter;
1617
use Soap\Encoding\Xml\Writer\XsiAttributeBuilder;
1718
use Soap\Engine\Metadata\Model\XsdType;
1819
use Soap\WsdlReader\Model\Definitions\BindingUse;
1920
use Soap\WsdlReader\Parser\Xml\QnameParser;
2021
use VeeWee\Reflecta\Iso\Iso;
21-
use VeeWee\Xml\Dom\Document;
2222
use XMLWriter;
2323
use function count;
2424
use function Psl\Vec\map;
@@ -46,10 +46,13 @@ public function iso(Context $context): Iso
4646
*/
4747
fn (array $value): string => $this->encodeArray($context, $value),
4848
/**
49-
* @param non-empty-string $value
49+
* @param non-empty-string|Element $value
5050
* @return list<mixed>
5151
*/
52-
fn (string $value): array => $this->decodeArray($context, $value),
52+
fn (string|Element $value): array => $this->decodeArray(
53+
$context,
54+
$value instanceof Element ? $value : Element::fromString($value)
55+
),
5356
));
5457
}
5558

@@ -125,13 +128,11 @@ private function itemElement(Context $context, ?string $itemNodeName, string $it
125128
}
126129

127130
/**
128-
* @param non-empty-string $value
129131
* @return list<mixed>
130132
*/
131-
private function decodeArray(Context $context, string $value): array
133+
private function decodeArray(Context $context, Element $value): array
132134
{
133-
$document = Document::fromXmlString($value);
134-
$element = $document->locateDocumentElement();
135+
$element = $value->element();
135136

136137
return readChildren($element)->reduce(
137138
/**

src/Encoder/SoapEnc/SoapObjectEncoder.php

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
use Soap\Encoding\Encoder\SimpleType\ScalarTypeEncoder;
1010
use Soap\Encoding\Encoder\XmlEncoder;
1111
use Soap\Encoding\TypeInference\XsiTypeDetector;
12+
use Soap\Encoding\Xml\Node\Element;
1213
use Soap\Encoding\Xml\Reader\ElementValueReader;
1314
use Soap\Encoding\Xml\Writer\XsdTypeXmlElementWriter;
1415
use Soap\Encoding\Xml\Writer\XsiAttributeBuilder;
1516
use Soap\Engine\Metadata\Model\XsdType;
1617
use VeeWee\Reflecta\Iso\Iso;
17-
use VeeWee\Xml\Dom\Document;
1818
use function Psl\Dict\merge;
1919
use function VeeWee\Xml\Dom\Locator\Element\children as readChildren;
2020
use function VeeWee\Xml\Writer\Builder\children;
@@ -37,9 +37,12 @@ public function iso(Context $context): Iso
3737
*/
3838
fn (object $value): string => $this->encodeArray($context, $value),
3939
/**
40-
* @param non-empty-string $value
40+
* @param non-empty-string|Element $value
4141
*/
42-
fn (string $value): object => $this->decodeArray($context, $value),
42+
fn (string|Element $value): object => $this->decodeArray(
43+
$context,
44+
$value instanceof Element ? $value : Element::fromString($value)
45+
),
4346
));
4447
}
4548

@@ -68,13 +71,9 @@ private function encodeArray(Context $context, object $data): string
6871
);
6972
}
7073

71-
/**
72-
* @param non-empty-string $value
73-
*/
74-
private function decodeArray(Context $context, string $value): object
74+
private function decodeArray(Context $context, Element $value): object
7575
{
76-
$document = Document::fromXmlString($value);
77-
$element = $document->locateDocumentElement();
76+
$element = $value->element();
7877

7978
return (object) readChildren($element)->reduce(
8079
static function (array $map, DOMElement $item) use ($context): array {

src/EncoderRegistry.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Soap\WsdlReader\Model\Definitions\EncodingStyle;
2020
use Soap\Xml\Xmlns;
2121
use stdClass;
22+
use Stringable;
2223
use function Psl\Dict\pull;
2324

2425
final class EncoderRegistry
@@ -312,7 +313,7 @@ public function hasRegisteredComplexTypeForXsdType(XsdType $type): bool
312313
}
313314

314315
/**
315-
* @return XmlEncoder<mixed, string>
316+
* @return XmlEncoder<mixed, string|Stringable>
316317
*/
317318
public function detectEncoderForContext(Context $context): XmlEncoder
318319
{

src/Xml/Node/Element.php

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Soap\Encoding\Xml\Node;
5+
6+
use DOMElement;
7+
use Stringable;
8+
use VeeWee\Xml\Dom\Document;
9+
use function Psl\invariant;
10+
11+
final class Element implements Stringable
12+
{
13+
private ?DOMElement $element = null;
14+
/**
15+
* @var non-empty-string|null
16+
*/
17+
private ?string $value = null;
18+
19+
private function __construct()
20+
{
21+
}
22+
23+
/**
24+
* @param non-empty-string $xml
25+
*/
26+
public static function fromString(string $xml): Element
27+
{
28+
$new = new self();
29+
$new->element = null;
30+
$new->value = $xml;
31+
32+
return $new;
33+
}
34+
35+
public static function fromDOMElement(DOMElement $element): self
36+
{
37+
$new = new self();
38+
$new->element = $element;
39+
$new->value = null;
40+
41+
return $new;
42+
}
43+
44+
public function element(): DOMElement
45+
{
46+
if (!$this->element) {
47+
invariant($this->value !== null, 'Expected an XML value to be present');
48+
$this->element = Document::fromXmlString($this->value)->locateDocumentElement();
49+
}
50+
51+
return $this->element;
52+
}
53+
54+
/**
55+
* @return non-empty-string
56+
*/
57+
public function value(): string
58+
{
59+
if ($this->value === null) {
60+
invariant($this->element !== null, 'Expected an DOMElement to be present');
61+
$this->value = Document::fromXmlNode($this->element)->stringifyDocumentElement();
62+
}
63+
64+
return $this->value;
65+
}
66+
67+
public function document(): Document
68+
{
69+
return Document::fromUnsafeDocument($this->element()->ownerDocument);
70+
}
71+
72+
/**
73+
* @return non-empty-string
74+
*/
75+
public function __toString()
76+
{
77+
return $this->value();
78+
}
79+
}

0 commit comments

Comments
 (0)