Skip to content

Commit

Permalink
further cleanup and working on making AbstractModels more slice-ish
Browse files Browse the repository at this point in the history
  • Loading branch information
dcarbone committed Feb 8, 2021
1 parent b30420f commit 8b3671e
Show file tree
Hide file tree
Showing 22 changed files with 335 additions and 199 deletions.
83 changes: 16 additions & 67 deletions src/AbstractClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,85 +23,30 @@
use GuzzleHttp\ClientInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;

/**
* Class AbstractClient
*/
abstract class AbstractClient
{
/** @var \DCarbone\PHPConsulAPI\Config */
protected Config $config;
protected Config $_config;

/**
* AbstractConsulClient constructor.
* @param \DCarbone\PHPConsulAPI\Config $config
*/
public function __construct(Config $config)
{
$this->config = $config;
$this->_config = $config;
}

/**
* @return \DCarbone\PHPConsulAPI\Config
*/
public function getConfig(): Config
{
return $this->config;
}

/**
* @param \DCarbone\Go\Time\Duration $duration
* @param \Psr\Http\Message\ResponseInterface $response
* @param \Psr\Http\Message\UriInterface $uri
* @return \DCarbone\PHPConsulAPI\QueryMeta
*/
protected function _buildQueryMeta(
Time\Duration $duration,
ResponseInterface $response,
UriInterface $uri
): QueryMeta {
$qm = new QueryMeta();

$qm->RequestTime = $duration;
$qm->RequestUrl = (string)$uri;

if ('' !== ($h = $response->getHeaderLine(Consul::headerConsulIndex))) {
$qm->LastIndex = (int)$h;
}

$qm->LastContentHash = $response->getHeaderLine(Consul::headerConsulContentHash);

// note: do not need to check both as guzzle response compares headers insensitively
if ('' !== ($h = $response->getHeaderLine(Consul::headerConsulKnownLeader))) {
$qm->KnownLeader = (bool)$h;
}
// note: do not need to check both as guzzle response compares headers insensitively
if ('' !== ($h = $response->getHeaderLine(Consul::headerConsulLastContact))) {
$qm->LastContact = (int)$h;
}

if ('' !== ($h = $response->getHeaderLine(Consul::headerConsulTranslateAddresses))) {
$qm->AddressTranslationEnabled = (bool)$h;
}

if ('' !== ($h = $response->getHeaderLine(Consul::headerCache))) {
$qm->CacheAge = Time::Duration(\intval($h, 10) * Time::Second);
}

return $qm;
}

/**
* @param \DCarbone\Go\Time\Duration $duration
* @return \DCarbone\PHPConsulAPI\WriteMeta
*/
protected function _buildWriteMeta(Time\Duration $duration): WriteMeta
{
$wm = new WriteMeta();
$wm->RequestTime = $duration;

return $wm;
return $this->_config;
}

/**
Expand All @@ -113,7 +58,7 @@ protected function _buildWriteMeta(Time\Duration $duration): WriteMeta
*/
protected function _newRequest(string $method, string $path, $body, ?RequestOptions $opts): Request
{
$r = new Request($method, $path, $this->config, $body);
$r = new Request($method, $path, $this->_config, $body);
$r->applyOptions($opts);
return $r;
}
Expand Down Expand Up @@ -173,10 +118,10 @@ protected function _do(Request $r): RequestResponse

try {
// If we actually have a client defined...
if (isset($this->config->HttpClient) && $this->config->HttpClient instanceof ClientInterface) {
$response = $this->config->HttpClient->send(
if (isset($this->_config->HttpClient) && $this->_config->HttpClient instanceof ClientInterface) {
$response = $this->_config->HttpClient->send(
$r->toPsrRequest(),
$this->config->getGuzzleRequestOptions($r)
$this->_config->getGuzzleRequestOptions($r)
);
} // Otherwise, throw error to be caught below
else {
Expand Down Expand Up @@ -337,7 +282,9 @@ protected function _decodeBody(StreamInterface $body): DecodedBody
protected function _executePut(string $path, $body, ?WriteOptions $opts): WriteResponse
{
$resp = $this->_requireOK($this->_doPut($path, $body, $opts));
return new WriteResponse($this->_buildWriteMeta($resp->Duration), $resp->Err);
$ret = new WriteResponse();
$this->_hydrateResponse($resp, $ret);
return $ret;
}

/**
Expand All @@ -349,7 +296,9 @@ protected function _executePut(string $path, $body, ?WriteOptions $opts): WriteR
protected function _executeDelete(string $path, ?WriteOptions $opts): WriteResponse
{
$resp = $this->_requireOK($this->_doDelete($path, $opts));
return new WriteResponse($this->_buildWriteMeta($resp->Duration), $resp->Err);
$ret = new WriteResponse();
$this->_hydrateResponse($resp, $ret);
return $ret;
}

/**
Expand Down Expand Up @@ -408,12 +357,12 @@ protected function _hydrateResponse(RequestResponse $resp, AbstractResponse $ret
{
// determine if this response contains a *Meta field
if (\property_exists($ret, Hydration::FIELD_QUERY_META)) {
$ret->QueryMeta = $this->_buildQueryMeta($resp->Duration, $resp->Response, $resp->RequestMeta->uri);
$ret->QueryMeta = $resp->buildQueryMeta();
} elseif (\property_exists($ret, Hydration::FIELD_WRITE_META)) {
$ret->WriteMeta = $this->_buildWriteMeta($resp->Duration);
$ret->WriteMeta = $resp->buildWriteMeta();
}

// todo: this can probably go away...
// todo: can probably assume that all responses have an Err field...
$hasErrField = \property_exists($ret, Hydration::FIELD_ERR);

// if there was an error in the response, set and return
Expand Down
158 changes: 111 additions & 47 deletions src/AbstractModels.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,57 +23,54 @@
*/
abstract class AbstractModels implements \Iterator, \ArrayAccess, \Countable, \JsonSerializable
{
/** @var \DCarbone\PHPConsulAPI\AbstractModel[] */
protected array $_list = [];

/** @var string */
protected string $containedClass;

/** @var \DCarbone\PHPConsulAPI\AbstractModel[] */
private array $_list = [];

/** @var int */
private int $_size = 0;

/**
* AbstractModels constructor.
* @param array|null $children
*/
public function __construct(?array $children = [])
{
if (!isset($this->containedClass)) {
throw new \DomainException(
\sprintf(
'Class "%s" must define $containedClass',
\get_called_class()
)
);
}
if (null === $children) {
return;
}
foreach (\array_filter($children) as $child) {
if (\is_array($child)) {
$this->_list[] = $this->newChild($child);
} elseif ($child instanceof $this->containedClass) {
$this->_list[] = $child;
} else {
throw new \InvalidArgumentException(
\sprintf(
\get_class($this) . ' accepts only ' . $this->containedClass . ' as a child, saw %s',
\is_object($child) ? \get_class($child) : \gettype($child)
)
);
}
foreach ($children as $child) {
$this->append($child);
}
}

/**
* @param \DCarbone\PHPConsulAPI\AbstractModel|null $value
*/
public function append(AbstractModel $value = null): void
public function append(?AbstractModel $value): void
{
if (null === $value || $value instanceof $this->containedClass) {
$this->_list[] = $value;
} else {
throw new \InvalidArgumentException(
\sprintf(
'%s accepts only objects of type %s or null as values',
\get_class($this),
$this->containedClass,
)
);
}
// validate provided value is either null or instance of allowed child class
$value = $this->_validateValue($value);

// set offset to current value of _size, and iterate size by 1
$offset = $this->_size++;

// if value is passed, clone then set.
$this->_list[$offset] = $value;
}

/**
* @return \DCarbone\PHPConsulAPI\AbstractModel|mixed
* @return \DCarbone\PHPConsulAPI\AbstractModel|false
*/
public function current()
{
Expand All @@ -96,7 +93,7 @@ public function key()
/**
* @return bool
*/
public function valid()
public function valid(): bool
{
return null !== \key($this->_list);
}
Expand All @@ -115,17 +112,18 @@ public function offsetExists($offset)
return \is_int($offset) && isset($this->_list[$offset]);
}

/**
* @param mixed $offset
* @return \DCarbone\PHPConsulAPI\AbstractModel|null
*/
public function offsetGet($offset)
{
if (\is_int($offset) && isset($this->_list[$offset])) {
$this->_validateOffset($offset);
if (isset($this->_list[$offset])) {
return $this->_list[$offset];
}
throw new \OutOfRangeException(
\sprintf(
'Offset %s does not exist in this list',
\is_int($offset) ? (string) $offset : \gettype($offset)
)
);

return $this->_list[$offset];
}

/**
Expand All @@ -134,42 +132,108 @@ public function offsetGet($offset)
*/
public function offsetSet($offset, $value): void
{
if (!\is_int($offset)) {
throw new \InvalidArgumentException('Offset must be int');
}
if (null !== $value && !($value instanceof $this->containedClass)) {
throw new \InvalidArgumentException('Value must be instance of ' . $this->containedClass);
// if incoming offset is null, assume [] (append) operation.
if (null === $offset) {
$this->append($value);
return;
}
$this->_list[$offset] = $value;

// validate provided offset value
$this->_validateOffset($offset);

// validate value input and set
$this->_list[$offset] = $this->_validateValue($value);
}

/**
* @param mixed $offset
*/
public function offsetUnset($offset): void
{
unset($this->_list[$offset]);
// validate provided offset value
$this->_validateOffset($offset);

// null out value in list
$this->_list[$offset] = null;
}

/**
* @return int
*/
public function count(): int
{
return \count($this->_list);
return $this->_size;
}

/**
* @return \DCarbone\PHPConsulAPI\AbstractModel[]
*/
public function jsonSerialize(): array
{
return \array_filter((array) $this->_list);
$out = [];
foreach ($this->_list as $i => $item) {
if (null === $item) {
$out[$i] = null;
} else {
$out[$i] = clone $item;
}
}
return $out;
}

/**
* @param array $data
* @return \static
* @return \DCarbone\PHPConsulAPI\AbstractModel
*/
abstract protected function newChild(array $data): AbstractModel;

/**
* @param mixed $offset
*/
private function _validateOffset($offset): void
{
if (!\is_int($offset)) {
throw new \InvalidArgumentException(
\sprintf(
'Cannot use offset of type "%s" with "%s"',
\gettype($offset),
\get_class($this)
)
);
}
if (0 > $offset || $offset >= $this->_size) {
throw new \OutOfRangeException(\sprintf('Offset %d does not exist in this list', $offset));
}
}

/**
* @param mixed $value
* @return \DCarbone\PHPConsulAPI\AbstractModel|null
*/
private function _validateValue($value): ?AbstractModel
{
// fast path for null values
if (null === $value) {
return null;
}

// if instance of contained class, clone and move on
if ($value instanceof $this->containedClass) {
return clone $value;
}

// if array, construct new child
if (\is_array($value)) {
return $this->newChild($value);
}

// if we make it down here, fail.
throw new \InvalidArgumentException(
\sprintf(
'%s accepts only objects of type %s, null, or associative array definition as values',
\get_class($this),
$this->containedClass,
)
);
}
}
Loading

0 comments on commit 8b3671e

Please sign in to comment.