Skip to content

Commit dcf466d

Browse files
authored
Merge pull request #3 from veewee/headers
Add shortcut functions for applying headers
2 parents ba6a389 + e476dea commit dcf466d

File tree

8 files changed

+247
-13
lines changed

8 files changed

+247
-13
lines changed

Diff for: README.md

+22-8
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,38 @@ Makes it possible to build the content of a `soap:Header` element.
1010

1111
```php
1212
use Soap\Xml\Builder\SoapHeaders;
13-
use Soap\Xml\Manipulator\PrependSoapHeaders;use VeeWee\Xml\Dom\Document;
14-
use function VeeWee\Xml\Dom\Builder\children;
13+
use Soap\Xml\Builder\SoapHeader;
14+
use Soap\Xml\Builder\Header\Actor;
15+
use Soap\Xml\Builder\Header\MustUnderstand;
16+
use Soap\Xml\Manipulator\PrependSoapHeaders;
17+
use VeeWee\Xml\Dom\Document;
18+
use function VeeWee\Xml\Dom\Builder\namespaced_element;
1519
use function VeeWee\Xml\Dom\Builder\element;
1620
use function VeeWee\Xml\Dom\Builder\value;
1721

1822
$doc = Document::fromXmlString($xml);
1923
$builder = new SoapHeaders(
20-
children(
21-
element('user', value('josbos')),
22-
element('password', value('topsecret'))
23-
)
24+
new SoapHeader(
25+
$targetNamespace,
26+
'Auth',
27+
children(
28+
namespaced_element($targetNamespace, 'user', value('josbos')),
29+
namespaced_element($targetNamespace, 'password', value('topsecret'))
30+
),
31+
// Optionally, you can provide additional configurators for setting
32+
// SOAP-ENV specific attributes:
33+
Actor::next(),
34+
new MustUnderstand()
35+
),
36+
$header2,
37+
$header3
2438
);
2539

26-
$header = $doc->build($builder)[0];
40+
$headers = $doc->build($builder);
2741

2842
// You can prepend the soap:Header as first element of the soap:envelope
2943
// Like this
30-
$doc->manipulate(new PrependSoapHeaders($header));
44+
$doc->manipulate(new PrependSoapHeaders(...$headers));
3145
```
3246

3347
## Locator

Diff for: src/Builder/Header/Actor.php

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Soap\Xml\Builder\Header;
5+
6+
use DOMNode;
7+
use VeeWee\Xml\Dom\Builder\Builder;
8+
use VeeWee\Xml\Exception\RuntimeException;
9+
use function VeeWee\Xml\Dom\Builder\namespaced_attribute;
10+
use function VeeWee\Xml\Dom\Locator\Node\detect_document;
11+
use function VeeWee\Xml\Dom\Locator\root_namespace_uri;
12+
use function VeeWee\Xml\Dom\Predicate\is_element;
13+
14+
final class Actor implements Builder
15+
{
16+
public function __construct(
17+
private string $actor
18+
) {
19+
}
20+
21+
public static function next(): self
22+
{
23+
return new self('http://schemas.xmlsoap.org/soap/actor/next');
24+
}
25+
26+
/**
27+
* @psalm-suppress MissingThrowsDocblock
28+
*/
29+
public function __invoke(DOMNode $node): DOMNode
30+
{
31+
$document = detect_document($node);
32+
$namespace = root_namespace_uri()($document) ?? '';
33+
34+
if (!is_element($node)) {
35+
throw RuntimeException::withMessage('Unable to add attribute to '.get_class($node));
36+
}
37+
38+
return namespaced_attribute($namespace, 'soapenv:actor', $this->actor)($node);
39+
}
40+
}

Diff for: src/Builder/Header/MustUnderstand.php

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Soap\Xml\Builder\Header;
5+
6+
use DOMNode;
7+
use VeeWee\Xml\Dom\Builder\Builder;
8+
use VeeWee\Xml\Exception\RuntimeException;
9+
use function VeeWee\Xml\Dom\Builder\namespaced_attribute;
10+
use function VeeWee\Xml\Dom\Locator\Node\detect_document;
11+
use function VeeWee\Xml\Dom\Locator\root_namespace_uri;
12+
use function VeeWee\Xml\Dom\Predicate\is_element;
13+
14+
final class MustUnderstand implements Builder
15+
{
16+
/**
17+
* @psalm-suppress MissingThrowsDocblock
18+
*/
19+
public function __invoke(DOMNode $node): DOMNode
20+
{
21+
$document = detect_document($node);
22+
$namespace = root_namespace_uri()($document) ?? '';
23+
24+
if (!is_element($node)) {
25+
throw RuntimeException::withMessage('Unable to add attribute to '.get_class($node));
26+
}
27+
28+
return namespaced_attribute($namespace, 'soapenv:mustUnderstand', '1')($node);
29+
}
30+
}

Diff for: src/Builder/SoapHeader.php

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Soap\Xml\Builder;
5+
6+
use DOMElement;
7+
use DOMNode;
8+
use VeeWee\Xml\Dom\Builder\Builder;
9+
use function VeeWee\Xml\Dom\Builder\children;
10+
use function VeeWee\Xml\Dom\Builder\namespaced_element;
11+
12+
final class SoapHeader implements Builder
13+
{
14+
/**
15+
* @var list<callable(DOMNode): DOMElement>
16+
*/
17+
private array $configurators;
18+
19+
/**
20+
* @no-named-arguments
21+
* @param list<callable(DOMNode): DOMElement> $configurators
22+
*/
23+
public function __construct(
24+
private string $namespace,
25+
private string $name,
26+
callable ... $configurators,
27+
) {
28+
$this->configurators = $configurators;
29+
}
30+
31+
/**
32+
* @psalm-suppress MissingThrowsDocblock
33+
*/
34+
public function __invoke(DOMNode $node): DOMNode
35+
{
36+
return children(
37+
namespaced_element(
38+
$this->namespace,
39+
$this->name,
40+
...$this->configurators
41+
)
42+
)($node);
43+
}
44+
}

Diff for: src/Builder/SoapHeaders.php

-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ public function __construct(callable ... $configurators)
2727

2828
/**
2929
* @psalm-suppress MissingThrowsDocblock
30-
*
31-
* @param callable(DOMElement): DOMElement ...$configurators
3230
*/
3331
public function __invoke(DOMNode $node): DOMNode
3432
{

Diff for: src/Manipulator/PrependSoapHeaders.php

+13-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,15 @@
1212

1313
final class PrependSoapHeaders
1414
{
15-
private DOMElement $soapHeaders;
15+
/**
16+
* @var list<DOMElement>
17+
*/
18+
private array $soapHeaders;
1619

17-
public function __construct(DOMElement $soapHeaders)
20+
/**
21+
* @no-named-arguments
22+
*/
23+
public function __construct(DOMElement ... $soapHeaders)
1824
{
1925
$this->soapHeaders = $soapHeaders;
2026
}
@@ -29,6 +35,10 @@ public function __invoke(DOMDocument $document): DOMElement
2935
$doc = Document::fromUnsafeDocument($document);
3036
$envelope = $doc->locate(new SoapEnvelopeLocator());
3137

32-
return $envelope->insertBefore($this->soapHeaders, $envelope->firstChild);
38+
foreach (array_reverse($this->soapHeaders) as $header) {
39+
$envelope->insertBefore($header, $envelope->firstChild);
40+
}
41+
42+
return $envelope;
3343
}
3444
}

Diff for: tests/Unit/Builder/SoapHeaderTest.php

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace SoapTest\Xml\Unit\Builder;
5+
6+
use PHPUnit\Framework\TestCase;
7+
use Soap\Xml\Builder\Header\Actor;
8+
use Soap\Xml\Builder\Header\MustUnderstand;
9+
use Soap\Xml\Builder\SoapHeader;
10+
use Soap\Xml\Builder\SoapHeaders;
11+
use Soap\Xml\Manipulator\PrependSoapHeaders;
12+
use VeeWee\Xml\Dom\Document;
13+
use function VeeWee\Xml\Dom\Builder\children;
14+
use function VeeWee\Xml\Dom\Builder\namespaced_element;
15+
use function VeeWee\Xml\Dom\Builder\value;
16+
use function VeeWee\Xml\Dom\Configurator\comparable;
17+
18+
final class SoapHeaderTest extends TestCase
19+
{
20+
public function test_it_can_create_a_header_element(): void
21+
{
22+
$tns = 'https://foo.bar';
23+
$builder = new SoapHeaders(
24+
new SoapHeader(
25+
$tns,
26+
'x:Auth',
27+
children(
28+
namespaced_element($tns, 'x:user', value('josbos')),
29+
namespaced_element($tns, 'x:password', value('topsecret'))
30+
)
31+
),
32+
new SoapHeader($tns, 'Acting', Actor::next()),
33+
new SoapHeader($tns, 'Understanding', new MustUnderstand())
34+
);
35+
$doc = Document::fromXmlFile(FIXTURE_DIR.'/empty-envelope.xml');
36+
$headers = $doc->build($builder);
37+
$doc->manipulate(new PrependSoapHeaders(...$headers));
38+
39+
$expected = <<<EOXML
40+
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/"
41+
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
42+
<soap:Header xmlns:soap="http://www.w3.org/2003/05/soap-envelope/">
43+
<x:Auth xmlns:x="https://foo.bar">
44+
<x:user>josbos</x:user>
45+
<x:password>topsecret</x:password>
46+
</x:Auth>
47+
<Acting xmlns="https://foo.bar" soap:actor="http://schemas.xmlsoap.org/soap/actor/next" />
48+
<Understanding xmlns="https://foo.bar" soap:mustUnderstand="1" />
49+
</soap:Header>
50+
</soap:Envelope>
51+
EOXML;
52+
53+
54+
static::assertXmlStringEqualsXmlString(
55+
Document::fromXmlString($expected, comparable())->toXmlString(),
56+
Document::fromUnsafeDocument($doc->toUnsafeDocument(), comparable())->toXmlString()
57+
);
58+
}
59+
}

Diff for: tests/Unit/Manipulator/PrependSoapHeadersTest.php

+39
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,26 @@
77
use Soap\Xml\Builder\SoapHeaders;
88
use Soap\Xml\Manipulator\PrependSoapHeaders;
99
use VeeWee\Xml\Dom\Document;
10+
use function VeeWee\Xml\Dom\Builder\attribute;
1011

1112
final class PrependSoapHeadersTest extends TestCase
1213
{
14+
public function test_it_can_prepend_no_header(): void
15+
{
16+
$doc = Document::fromXmlFile(FIXTURE_DIR.'/empty-envelope-with-body.xml');
17+
$doc->manipulate(new PrependSoapHeaders());
18+
19+
$expected = <<<EOXML
20+
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/"
21+
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
22+
<soap:Body></soap:Body>
23+
</soap:Envelope>
24+
EOXML;
25+
26+
static::assertXmlStringEqualsXmlString($expected, $doc->toXmlString());
27+
}
28+
29+
1330
public function test_it_can_prepend_a_soap_header_on_an_envelope(): void
1431
{
1532
$doc = Document::fromXmlFile(FIXTURE_DIR.'/empty-envelope-with-body.xml');
@@ -27,4 +44,26 @@ public function test_it_can_prepend_a_soap_header_on_an_envelope(): void
2744

2845
static::assertXmlStringEqualsXmlString($expected, $doc->toXmlString());
2946
}
47+
48+
public function test_it_can_prepend_mulitple_soap_header_on_an_envelope(): void
49+
{
50+
$doc = Document::fromXmlFile(FIXTURE_DIR.'/empty-envelope-with-body.xml');
51+
$headers = $doc->build(
52+
new SoapHeaders(),
53+
new SoapHeaders(attribute('id', '2')),
54+
);
55+
56+
$doc->manipulate(new PrependSoapHeaders(...$headers));
57+
58+
$expected = <<<EOXML
59+
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/"
60+
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
61+
<soap:Header></soap:Header>
62+
<soap:Header id="2"></soap:Header>
63+
<soap:Body></soap:Body>
64+
</soap:Envelope>
65+
EOXML;
66+
67+
static::assertXmlStringEqualsXmlString($expected, $doc->toXmlString());
68+
}
3069
}

0 commit comments

Comments
 (0)