This is part of a SDK for interacting with Communicator's SOAP api.
The aim is that the user will not have to interact with SOAP (some people complain about it).
Please note that the wired capitalisation on some properties are intentional and are required for correct conversion to a SOAP request.
I would especially like feedback on the general structure of the package, ie.
- Create service
- Create request
- Call method on service
- Service calls SOAP and provides response to another class to format / validate
Usage example:
use Jmsfwk\Communicator\DataService\GetContactSubscription;
use Jmsfwk\Communicator\Resources\CommunicatorCredentials;
use Jmsfwk\Communicator\Resources\SubscriptionInfo;
use Jmsfwk\Communicator\Services\DataService;
$credentials = new CommunicatorCredentials('username', 'password');
$dataService = new DataService($credentials);
$request = new GetContactSubscription('[email protected]', 12345);
//try {...
$subscriptionInfo = $dataService->getContactSubscription($request);
Service.php
abstract class Service
{
const NS = 'http://ws.communicatorcorp.com/';
/**
* @var SoapClient
*/
protected $client;
/**
* @var CommunicatorCredentials
*/
protected $credentials;
/**
* Service constructor.
*
* @param CommunicatorCredentials $credentials
* @param SoapClient $client
*/
public function __construct(CommunicatorCredentials $credentials,
SoapClient $client = null)
{
$this->credentials = $credentials;
if ($client === null) {
$client = self::makeClient();
}
$this->client = $client;
$header = new SoapHeader(self::NS, 'CommunicatorCredentials',
$credentials->getHeader());
$this->client->__setSoapHeaders($header);
}
public static function makeClient(array $soapSettings = []): SoapClient
{
$defaults = [
'trace' => false,
'exception' => true,
'soap_version' => SOAP_1_2,
];
return new SoapClient(static::getWSDL(), array_merge($defaults,
$soapSettings));
}
abstract protected static function getWSDL(): string;
}
DataService.php
namespace Jmsfwk\Communicator\Services;
use Jmsfwk\Communicator\DataService\GetContactSubscription;
use Jmsfwk\Communicator\Resources\SubscriptionInfo;
use SoapClient;
class DataService extends Service
{
const WSDL = 'https://ws.communicatorcorp.com/DataService.asmx?WSDL';
public function getContactSubscription(
GetContactSubscription $request): SubscriptionInfo
{
$response = $this->client->GetContactSubscription($request);
return GetContactSubscription::formatResponse(
$response->GetContactSubscriptionResult);
}
public static function makeClient(array $soapSettings = []): SoapClient
{
$defaults = [
'trace' => true,
'exception' => true,
'soap_version' => SOAP_1_2,
];
return new SoapClient(self::WSDL, array_merge($defaults, $soapSettings));
}
}
CommunicatorCredentials.php
namespace Jmsfwk\Communicator\Resources;
use SoapHeader;
use SoapVar;
class CommunicatorCredentials extends Resource
{
/** Communicator's namespace */
const NAME_SPACE = 'http://ws.communicatorcorp.com/';
/** The name of the XML node in the SOAP request */
const HEADER_NAME = 'CommunicatorCredentials';
/** @var string */
public $Username;
/** @var string */
public $Password;
/**
* @param string $username
* @param string $password
*/
public function __construct(string $username, string $password)
{
$this->Username = $username;
$this->Password = $password;
}
/**
* Build a SoapHeader for authenticating with Communicator.
*
* Communicator requires that the authentication header is built of SoapVar's
* instead of an object matching the required fields.
*
* @return SoapHeader
*/
public function getHeader(): SoapHeader
{
$data = new SoapVar(
[
new SoapVar($this->Username, XSD_STRING, null,
null, 'Username', self::NAME_SPACE),
new SoapVar($this->Password, XSD_STRING, null,
null, 'Password', self::NAME_SPACE),
],
SOAP_ENC_OBJECT
);
return new SoapHeader(self::NAME_SPACE, self::HEADER_NAME, $data);
}
}
GetContactSubscription.php
namespace Jmsfwk\Communicator\DataService;
use Jmsfwk\Communicator\Exceptions\BadResponseData;
use Jmsfwk\Communicator\Resources\Resource;
use Jmsfwk\Communicator\Resources\SubscriptionInfo;
use TypeError;
use UnexpectedValueException;
class GetContactSubscription extends Resource
{
protected $emailAddress;
protected $mailingListId;
public function __construct(string $emailAddress, int $mailingListId)
{
$this->emailAddress = $emailAddress;
$this->mailingListId = $mailingListId;
}
public static function formatResponse(
stdClass $response): SubscriptionInfo
{
try {
return new SubscriptionInfo(
$response->MailingListId,
$response->IsSubscribed,
new SubscriptionSourceType($response->SubscriptionSourceType),
DateTime::createFromFormat("Y-m-d\TH:i:s",
$response->DateLastAction)
);
} catch (UnexpectedValueException $e) {
throw new BadResponseData($e->getMessage(), $e->getCode(), $e);
} catch (TypeError $e) {
throw new BadResponseData($e->getMessage(), $e->getCode(), $e);
}
}
}
SubscriptionInfo.php
namespace Jmsfwk\Communicator\Resources;
class SubscriptionInfo extends Resource
{
public $MailingListId;
public $IsSubscribed;
public $DateLastAction;
public $SubscriptionSourceType = 'TestCase';
public function __construct($MailingListId, $IsSubscribed = true)
{
$this->MailingListId = $MailingListId;
$this->IsSubscribed = $IsSubscribed;
$this->DateLastAction = date("Y-m-d\TH:i:s");
}
}