Skip to content

Commit

Permalink
Merge pull request #23 from Ricorocks-Digital-Agency/headers
Browse files Browse the repository at this point in the history
Headers
  • Loading branch information
lukeraymonddowning authored Jun 3, 2021
2 parents 3b2283a + a36497a commit ef907e5
Show file tree
Hide file tree
Showing 16 changed files with 572 additions and 74 deletions.
82 changes: 72 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@ A Laravel SOAP client that provides a clean interface for handling requests and
- [Installation](#installation)
- [Using Soap](#using-soap)
- [Features/API](#features/api)
* [Headers](#headers)
* [Global Headers](#global-headers)
* [To](#to)
* [Functions](#functions)
* [Call](#call)
* [Parameters](#parameters)
* [Nodes](#nodes)
- [Options](#options)
* [Tracing](#tracing)
* [Authentication](#authentication)
* [Global Options](#global-options)
* [Options](#options)
* [Tracing](#tracing)
* [Authentication](#authentication)
* [Global Options](#global-options)
- [Hooks](#hooks)
- [Faking](#faking)
- [Configuration](#configuration)
Expand Down Expand Up @@ -45,6 +47,62 @@ Soap::to()

## Features/API

### Headers

You can set the headers for each soap request that will be passed to the Soap Client using the `withHeaders` method.

```php
Soap::to('...')->withHeaders(...$headers)->call('...');
```

Each header should be a `Header` instance, which provides a fluent interface for constructing a new [PHP Soap Header](https://www.php.net/manual/en/soapheader.construct.php) and can be composed as follows:
```php
$header = Soap::header()
->name('Authentication')
->namespace('test.com')
->data([
'user' => '...',
'password' => '...'
])
->mustUnderstand()
->actor('foo.co.uk')
```
This can also be expressed as:
```php
$header = Soap::header('Authentication', 'test.com', [
'user' => '...',
'password' => '...'
])
->mustUnderstand()
->actor('foo.co.uk')
```
Plus, the `soap_header` helper method can be used:
```php
$header = soap_header('Authentication', 'test.com')
->data([
'user' => '...',
'password' => '...'
])
```
> The `data` for the header can either be an array or a `SoapVar`, as per the `SoapHeader` constructor
#### Global Headers
Soap allows you to set headers that should be included for every request:
```php
Soap::headers(...$headers)
```
Again, each header should be an instance of `Header`.

You may also want to include headers on every request, but only for a certain endpoint or action:

```php
// Only requests to this endpoint will include these headers
Soap::headers(soap_header('Auth', 'test.com'))->for('https://api.example.com');

// Only requests to this endpoint and the method Customers will include these headers
Soap::headers(soap_header('Brand', 'test.com'))->for('https://api.example.com', 'Customers');
```

These calls are usually placed in the `boot` method of one of your application's Service Providers.
### To

The endpoint to be accessed
Expand Down Expand Up @@ -151,29 +209,33 @@ Now, just by adding or removing a body to the `soap_node()` the outputted array

A node can be made with either the Facade `Soap::node()` or the helper method `soap_node()`.

## Options
### Options

You can set custom options for each soap request that will be passed to the Soap Client using the `withOptions` method.

```php
Soap::to('...')->withOptions(['soap_version' => SOAP_1_2])->call('...');
```

See [https://www.php.net/manual/en/soapclient.construct.php](https://www.php.net/manual/en/soapclient.construct.php)
> See [https://www.php.net/manual/en/soapclient.construct.php](https://www.php.net/manual/en/soapclient.construct.php)
for more details and available options.

Soap also provides a number of methods that add syntactical sugar to the most commonly used options, which are detailed
below.

### Tracing
#### Tracing
Soap allows you to easily trace your interactions with the SOAP endpoint being accessed.

To trace all requests, set the following in the register method of your `ServiceProvider`:

```php
Soap::trace()
```
Now, all `Response` objects returned will have a `Trace` object attached, accessible via `$response->getTrace()`. This has two properties `xmlRequest` and `xmlResponse` - storing the raw XML values for each.
Now, all `Response` objects returned will have a `Trace` object attached, accessible via `$response->getTrace()`. This has four properties which are wrappers for the respective methods found on the `SoapClient`:
- `xmlRequest` (`__getLastRequest`)
- `xmlResponse` (`__getLastResponse`)
- `requestHeaders` (`__getLastRequestHeaders`)
- `responseHeaders` (`__getLastResponseHeaders`)

Tracing can also be declared locally:
```php
Expand All @@ -183,7 +245,7 @@ Now, just this `Response` will have a valid `Trace`.

Tracing is null safe. If `$response->getTrace()` is called when a `Trace` hasn't been set, a new `Trace` is returned. This `Trace`'s properties will all return `null`.

### Authentication
#### Authentication

You can authenticate using Basic or Digest by calling `withBasicAuth` and `withDigestAuth` respectively.

Expand All @@ -192,7 +254,7 @@ Soap::to('...')->withBasicAuth('username', 'password')->call('...');
Soap::to('...')->withDigestAuth('username', 'password')->call('...');
```

### Global Options
#### Global Options

Sometimes, you may wish to include the same set of options on every SOAP request. You can do that using the `options`
method on the `Soap` facade:
Expand Down
5 changes: 4 additions & 1 deletion src/Facades/Soap.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
namespace RicorocksDigitalAgency\Soap\Facades;


use Closure;
use Illuminate\Support\Facades\Facade;
use RicorocksDigitalAgency\Soap\Header;
use RicorocksDigitalAgency\Soap\HeaderSet;
use RicorocksDigitalAgency\Soap\Inclusion;
use RicorocksDigitalAgency\Soap\OptionSet;
use RicorocksDigitalAgency\Soap\Parameters\Node;
Expand All @@ -16,6 +17,8 @@
* @package RicorocksDigitalAgency\Soap\Facades
*
* @method static Request to(string $endpoint)
* @method static Header header(?string $name = null, ?string $namespace = null, $data = null, bool $mustUnderstand = false, ?string $actor = null)
* @method static HeaderSet headers(Header ...$headers)
* @method static Node node(array $attributes = [])
* @method static Inclusion include(array $parameters)
* @method static OptionSet options(array $options)
Expand Down
59 changes: 59 additions & 0 deletions src/Header.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace RicorocksDigitalAgency\Soap;

use Illuminate\Contracts\Support\Arrayable;

class Header implements Arrayable
{
public $name;
public $namespace;
public $data;
public $actor;
public $mustUnderstand;

public function __construct(?string $name = null, ?string $namespace = null, $data = null, bool $mustUnderstand = false, ?string $actor = null)
{
$this->name = $name;
$this->namespace = $namespace;
$this->data = $data;
$this->mustUnderstand = $mustUnderstand;
$this->actor = $actor;
}

public function name(string $name): self
{
return tap($this, fn () => $this->name = $name);
}

public function namespace(string $namespace): self
{
return tap($this, fn () => $this->namespace = $namespace);
}

public function data($data = null): self
{
return tap($this, fn () => $this->data = $data);
}

public function actor(?string $actor = null): self
{
return tap($this, fn () => $this->actor = $actor);
}

public function mustUnderstand(bool $mustUnderstand = true): self
{
return tap($this, fn () => $this->mustUnderstand = $mustUnderstand);
}

public function toArray()
{
return [
'name' => $this->name,
'namespace' => $this->namespace,
'data' => $this->data,
'mustUnderstand' => $this->mustUnderstand,
'actor' => $this->actor,
];
}
}
20 changes: 20 additions & 0 deletions src/HeaderSet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace RicorocksDigitalAgency\Soap;

use RicorocksDigitalAgency\Soap\Support\Scoped;

class HeaderSet extends Scoped
{
protected $headers;

public function __construct(Header ...$headers)
{
$this->headers = $headers;
}

public function getHeaders()
{
return $this->headers;
}
}
8 changes: 8 additions & 0 deletions src/Ray/SoapWatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,18 @@ protected function handleRequest(Request $request, Response $response)
[
'Endpoint' => $request->getEndpoint(),
'Method' => $request->getMethod(),
'Headers' => $this->headers($request),
'Request' => $request->getBody(),
'Response' => $response->response,
],
"SOAP"
);
}

protected function headers(Request $request)
{
return $request->getHeaders()
? collect($request->getHeaders())->map->toArray()->toArray()
: [];
}
}
5 changes: 5 additions & 0 deletions src/Request/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace RicorocksDigitalAgency\Soap\Request;

use RicorocksDigitalAgency\Soap\Header;
use RicorocksDigitalAgency\Soap\Response\Response;

interface Request
Expand Down Expand Up @@ -43,4 +44,8 @@ public function withOptions(array $options): self;
public function withBasicAuth($login, $password): self;

public function withDigestAuth($login, $password): self;

public function withHeaders(Header ...$headers): self;

public function getHeaders(): array;
}
60 changes: 45 additions & 15 deletions src/Request/SoapClientRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

namespace RicorocksDigitalAgency\Soap\Request;

use RicorocksDigitalAgency\Soap\Header;
use RicorocksDigitalAgency\Soap\Parameters\Builder;
use RicorocksDigitalAgency\Soap\Response\Response;
use RicorocksDigitalAgency\Soap\Support\Tracing\Trace;
use SoapClient;
use SoapHeader;

class SoapClientRequest implements Request
{
Expand All @@ -17,6 +19,7 @@ class SoapClientRequest implements Request
protected Response $response;
protected $hooks = [];
protected $options = [];
protected $headers = [];

public function __construct(Builder $builder)
{
Expand Down Expand Up @@ -57,7 +60,9 @@ protected function getRealResponse()
{
return tap(
Response::new($this->makeRequest()),
fn($response) => data_get($this->options, 'trace') ? $this->addTrace($response) : $response
fn($response) => data_get($this->options, 'trace')
? $response->setTrace(Trace::client($this->client()))
: $response
);
}

Expand All @@ -68,12 +73,34 @@ protected function makeRequest()

protected function client()
{
return $this->client ??= app(
SoapClient::class,
[
'wsdl' => $this->endpoint,
'options' => $this->options
]
return $this->client ??= $this->constructClient();
}

protected function constructClient()
{
$client = resolve(SoapClient::class, [
'wsdl' => $this->endpoint,
'options' => $this->options
]);

return tap($client, fn ($client) => $client->__setSoapHeaders($this->constructHeaders()));
}

protected function constructHeaders()
{
if (empty($this->headers)) {
return;
}

return array_map(
fn ($header) => resolve(SoapHeader::class, [
'namespace' => $header->namespace,
'name' => $header->name,
'data' => $header->data,
'mustUnderstand' => $header->mustUnderstand,
'actor' => $header->actor
]),
$this->headers
);
}

Expand All @@ -87,14 +114,6 @@ public function getBody()
return $this->body;
}

protected function addTrace($response)
{
return $response->setTrace(
Trace::thisXmlRequest($this->client()->__getLastRequest())
->thisXmlResponse($this->client()->__getLastResponse())
);
}

public function functions(): array
{
return $this->client()->__getFunctions();
Expand Down Expand Up @@ -168,4 +187,15 @@ public function withOptions(array $options): Request
$this->options = array_merge($this->getOptions(), $options);
return $this;
}

public function withHeaders(Header ...$headers): Request
{
$this->headers = array_merge($this->getHeaders(), $headers);
return $this;
}

public function getHeaders(): array
{
return $this->headers;
}
}
Loading

0 comments on commit ef907e5

Please sign in to comment.