Skip to content

Add shortcut functions for applying headers #3

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

Merged
merged 1 commit into from
Jan 28, 2022
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
30 changes: 22 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,38 @@ Makes it possible to build the content of a `soap:Header` element.

```php
use Soap\Xml\Builder\SoapHeaders;
use Soap\Xml\Manipulator\PrependSoapHeaders;use VeeWee\Xml\Dom\Document;
use function VeeWee\Xml\Dom\Builder\children;
use Soap\Xml\Builder\SoapHeader;
use Soap\Xml\Builder\Header\Actor;
use Soap\Xml\Builder\Header\MustUnderstand;
use Soap\Xml\Manipulator\PrependSoapHeaders;
use VeeWee\Xml\Dom\Document;
use function VeeWee\Xml\Dom\Builder\namespaced_element;
use function VeeWee\Xml\Dom\Builder\element;
use function VeeWee\Xml\Dom\Builder\value;

$doc = Document::fromXmlString($xml);
$builder = new SoapHeaders(
children(
element('user', value('josbos')),
element('password', value('topsecret'))
)
new SoapHeader(
$targetNamespace,
'Auth',
children(
namespaced_element($targetNamespace, 'user', value('josbos')),
namespaced_element($targetNamespace, 'password', value('topsecret'))
),
// Optionally, you can provide additional configurators for setting
// SOAP-ENV specific attributes:
Actor::next(),
new MustUnderstand()
),
$header2,
$header3
);

$header = $doc->build($builder)[0];
$headers = $doc->build($builder);

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

## Locator
Expand Down
40 changes: 40 additions & 0 deletions src/Builder/Header/Actor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);

namespace Soap\Xml\Builder\Header;

use DOMNode;
use VeeWee\Xml\Dom\Builder\Builder;
use VeeWee\Xml\Exception\RuntimeException;
use function VeeWee\Xml\Dom\Builder\namespaced_attribute;
use function VeeWee\Xml\Dom\Locator\Node\detect_document;
use function VeeWee\Xml\Dom\Locator\root_namespace_uri;
use function VeeWee\Xml\Dom\Predicate\is_element;

final class Actor implements Builder
{
public function __construct(
private string $actor
) {
}

public static function next(): self
{
return new self('http://schemas.xmlsoap.org/soap/actor/next');
}

/**
* @psalm-suppress MissingThrowsDocblock
*/
public function __invoke(DOMNode $node): DOMNode
{
$document = detect_document($node);
$namespace = root_namespace_uri()($document) ?? '';

if (!is_element($node)) {
throw RuntimeException::withMessage('Unable to add attribute to '.get_class($node));
}

return namespaced_attribute($namespace, 'soapenv:actor', $this->actor)($node);
}
}
30 changes: 30 additions & 0 deletions src/Builder/Header/MustUnderstand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);

namespace Soap\Xml\Builder\Header;

use DOMNode;
use VeeWee\Xml\Dom\Builder\Builder;
use VeeWee\Xml\Exception\RuntimeException;
use function VeeWee\Xml\Dom\Builder\namespaced_attribute;
use function VeeWee\Xml\Dom\Locator\Node\detect_document;
use function VeeWee\Xml\Dom\Locator\root_namespace_uri;
use function VeeWee\Xml\Dom\Predicate\is_element;

final class MustUnderstand implements Builder
{
/**
* @psalm-suppress MissingThrowsDocblock
*/
public function __invoke(DOMNode $node): DOMNode
{
$document = detect_document($node);
$namespace = root_namespace_uri()($document) ?? '';

if (!is_element($node)) {
throw RuntimeException::withMessage('Unable to add attribute to '.get_class($node));
}

return namespaced_attribute($namespace, 'soapenv:mustUnderstand', '1')($node);
}
}
44 changes: 44 additions & 0 deletions src/Builder/SoapHeader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);

namespace Soap\Xml\Builder;

use DOMElement;
use DOMNode;
use VeeWee\Xml\Dom\Builder\Builder;
use function VeeWee\Xml\Dom\Builder\children;
use function VeeWee\Xml\Dom\Builder\namespaced_element;

final class SoapHeader implements Builder
{
/**
* @var list<callable(DOMNode): DOMElement>
*/
private array $configurators;

/**
* @no-named-arguments
* @param list<callable(DOMNode): DOMElement> $configurators
*/
public function __construct(
private string $namespace,
private string $name,
callable ... $configurators,
) {
$this->configurators = $configurators;
}

/**
* @psalm-suppress MissingThrowsDocblock
*/
public function __invoke(DOMNode $node): DOMNode
{
return children(
namespaced_element(
$this->namespace,
$this->name,
...$this->configurators
)
)($node);
}
}
2 changes: 0 additions & 2 deletions src/Builder/SoapHeaders.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ public function __construct(callable ... $configurators)

/**
* @psalm-suppress MissingThrowsDocblock
*
* @param callable(DOMElement): DOMElement ...$configurators
*/
public function __invoke(DOMNode $node): DOMNode
{
Expand Down
16 changes: 13 additions & 3 deletions src/Manipulator/PrependSoapHeaders.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@

final class PrependSoapHeaders
{
private DOMElement $soapHeaders;
/**
* @var list<DOMElement>
*/
private array $soapHeaders;

public function __construct(DOMElement $soapHeaders)
/**
* @no-named-arguments
*/
public function __construct(DOMElement ... $soapHeaders)
{
$this->soapHeaders = $soapHeaders;
}
Expand All @@ -29,6 +35,10 @@ public function __invoke(DOMDocument $document): DOMElement
$doc = Document::fromUnsafeDocument($document);
$envelope = $doc->locate(new SoapEnvelopeLocator());

return $envelope->insertBefore($this->soapHeaders, $envelope->firstChild);
foreach (array_reverse($this->soapHeaders) as $header) {
$envelope->insertBefore($header, $envelope->firstChild);
}

return $envelope;
}
}
59 changes: 59 additions & 0 deletions tests/Unit/Builder/SoapHeaderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);

namespace SoapTest\Xml\Unit\Builder;

use PHPUnit\Framework\TestCase;
use Soap\Xml\Builder\Header\Actor;
use Soap\Xml\Builder\Header\MustUnderstand;
use Soap\Xml\Builder\SoapHeader;
use Soap\Xml\Builder\SoapHeaders;
use Soap\Xml\Manipulator\PrependSoapHeaders;
use VeeWee\Xml\Dom\Document;
use function VeeWee\Xml\Dom\Builder\children;
use function VeeWee\Xml\Dom\Builder\namespaced_element;
use function VeeWee\Xml\Dom\Builder\value;
use function VeeWee\Xml\Dom\Configurator\comparable;

final class SoapHeaderTest extends TestCase
{
public function test_it_can_create_a_header_element(): void
{
$tns = 'https://foo.bar';
$builder = new SoapHeaders(
new SoapHeader(
$tns,
'x:Auth',
children(
namespaced_element($tns, 'x:user', value('josbos')),
namespaced_element($tns, 'x:password', value('topsecret'))
)
),
new SoapHeader($tns, 'Acting', Actor::next()),
new SoapHeader($tns, 'Understanding', new MustUnderstand())
);
$doc = Document::fromXmlFile(FIXTURE_DIR.'/empty-envelope.xml');
$headers = $doc->build($builder);
$doc->manipulate(new PrependSoapHeaders(...$headers));

$expected = <<<EOXML
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/"
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<soap:Header xmlns:soap="http://www.w3.org/2003/05/soap-envelope/">
<x:Auth xmlns:x="https://foo.bar">
<x:user>josbos</x:user>
<x:password>topsecret</x:password>
</x:Auth>
<Acting xmlns="https://foo.bar" soap:actor="http://schemas.xmlsoap.org/soap/actor/next" />
<Understanding xmlns="https://foo.bar" soap:mustUnderstand="1" />
</soap:Header>
</soap:Envelope>
EOXML;


static::assertXmlStringEqualsXmlString(
Document::fromXmlString($expected, comparable())->toXmlString(),
Document::fromUnsafeDocument($doc->toUnsafeDocument(), comparable())->toXmlString()
);
}
}
39 changes: 39 additions & 0 deletions tests/Unit/Manipulator/PrependSoapHeadersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,26 @@
use Soap\Xml\Builder\SoapHeaders;
use Soap\Xml\Manipulator\PrependSoapHeaders;
use VeeWee\Xml\Dom\Document;
use function VeeWee\Xml\Dom\Builder\attribute;

final class PrependSoapHeadersTest extends TestCase
{
public function test_it_can_prepend_no_header(): void
{
$doc = Document::fromXmlFile(FIXTURE_DIR.'/empty-envelope-with-body.xml');
$doc->manipulate(new PrependSoapHeaders());

$expected = <<<EOXML
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/"
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<soap:Body></soap:Body>
</soap:Envelope>
EOXML;

static::assertXmlStringEqualsXmlString($expected, $doc->toXmlString());
}


public function test_it_can_prepend_a_soap_header_on_an_envelope(): void
{
$doc = Document::fromXmlFile(FIXTURE_DIR.'/empty-envelope-with-body.xml');
Expand All @@ -27,4 +44,26 @@ public function test_it_can_prepend_a_soap_header_on_an_envelope(): void

static::assertXmlStringEqualsXmlString($expected, $doc->toXmlString());
}

public function test_it_can_prepend_mulitple_soap_header_on_an_envelope(): void
{
$doc = Document::fromXmlFile(FIXTURE_DIR.'/empty-envelope-with-body.xml');
$headers = $doc->build(
new SoapHeaders(),
new SoapHeaders(attribute('id', '2')),
);

$doc->manipulate(new PrependSoapHeaders(...$headers));

$expected = <<<EOXML
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/"
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<soap:Header></soap:Header>
<soap:Header id="2"></soap:Header>
<soap:Body></soap:Body>
</soap:Envelope>
EOXML;

static::assertXmlStringEqualsXmlString($expected, $doc->toXmlString());
}
}