Skip to content

Commit 8b3671e

Browse files
committed
further cleanup and working on making AbstractModels more slice-ish
1 parent b30420f commit 8b3671e

22 files changed

+335
-199
lines changed

src/AbstractClient.php

Lines changed: 16 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -23,85 +23,30 @@
2323
use GuzzleHttp\ClientInterface;
2424
use Psr\Http\Message\ResponseInterface;
2525
use Psr\Http\Message\StreamInterface;
26-
use Psr\Http\Message\UriInterface;
2726

2827
/**
2928
* Class AbstractClient
3029
*/
3130
abstract class AbstractClient
3231
{
3332
/** @var \DCarbone\PHPConsulAPI\Config */
34-
protected Config $config;
33+
protected Config $_config;
3534

3635
/**
3736
* AbstractConsulClient constructor.
3837
* @param \DCarbone\PHPConsulAPI\Config $config
3938
*/
4039
public function __construct(Config $config)
4140
{
42-
$this->config = $config;
41+
$this->_config = $config;
4342
}
4443

4544
/**
4645
* @return \DCarbone\PHPConsulAPI\Config
4746
*/
4847
public function getConfig(): Config
4948
{
50-
return $this->config;
51-
}
52-
53-
/**
54-
* @param \DCarbone\Go\Time\Duration $duration
55-
* @param \Psr\Http\Message\ResponseInterface $response
56-
* @param \Psr\Http\Message\UriInterface $uri
57-
* @return \DCarbone\PHPConsulAPI\QueryMeta
58-
*/
59-
protected function _buildQueryMeta(
60-
Time\Duration $duration,
61-
ResponseInterface $response,
62-
UriInterface $uri
63-
): QueryMeta {
64-
$qm = new QueryMeta();
65-
66-
$qm->RequestTime = $duration;
67-
$qm->RequestUrl = (string)$uri;
68-
69-
if ('' !== ($h = $response->getHeaderLine(Consul::headerConsulIndex))) {
70-
$qm->LastIndex = (int)$h;
71-
}
72-
73-
$qm->LastContentHash = $response->getHeaderLine(Consul::headerConsulContentHash);
74-
75-
// note: do not need to check both as guzzle response compares headers insensitively
76-
if ('' !== ($h = $response->getHeaderLine(Consul::headerConsulKnownLeader))) {
77-
$qm->KnownLeader = (bool)$h;
78-
}
79-
// note: do not need to check both as guzzle response compares headers insensitively
80-
if ('' !== ($h = $response->getHeaderLine(Consul::headerConsulLastContact))) {
81-
$qm->LastContact = (int)$h;
82-
}
83-
84-
if ('' !== ($h = $response->getHeaderLine(Consul::headerConsulTranslateAddresses))) {
85-
$qm->AddressTranslationEnabled = (bool)$h;
86-
}
87-
88-
if ('' !== ($h = $response->getHeaderLine(Consul::headerCache))) {
89-
$qm->CacheAge = Time::Duration(\intval($h, 10) * Time::Second);
90-
}
91-
92-
return $qm;
93-
}
94-
95-
/**
96-
* @param \DCarbone\Go\Time\Duration $duration
97-
* @return \DCarbone\PHPConsulAPI\WriteMeta
98-
*/
99-
protected function _buildWriteMeta(Time\Duration $duration): WriteMeta
100-
{
101-
$wm = new WriteMeta();
102-
$wm->RequestTime = $duration;
103-
104-
return $wm;
49+
return $this->_config;
10550
}
10651

10752
/**
@@ -113,7 +58,7 @@ protected function _buildWriteMeta(Time\Duration $duration): WriteMeta
11358
*/
11459
protected function _newRequest(string $method, string $path, $body, ?RequestOptions $opts): Request
11560
{
116-
$r = new Request($method, $path, $this->config, $body);
61+
$r = new Request($method, $path, $this->_config, $body);
11762
$r->applyOptions($opts);
11863
return $r;
11964
}
@@ -173,10 +118,10 @@ protected function _do(Request $r): RequestResponse
173118

174119
try {
175120
// If we actually have a client defined...
176-
if (isset($this->config->HttpClient) && $this->config->HttpClient instanceof ClientInterface) {
177-
$response = $this->config->HttpClient->send(
121+
if (isset($this->_config->HttpClient) && $this->_config->HttpClient instanceof ClientInterface) {
122+
$response = $this->_config->HttpClient->send(
178123
$r->toPsrRequest(),
179-
$this->config->getGuzzleRequestOptions($r)
124+
$this->_config->getGuzzleRequestOptions($r)
180125
);
181126
} // Otherwise, throw error to be caught below
182127
else {
@@ -337,7 +282,9 @@ protected function _decodeBody(StreamInterface $body): DecodedBody
337282
protected function _executePut(string $path, $body, ?WriteOptions $opts): WriteResponse
338283
{
339284
$resp = $this->_requireOK($this->_doPut($path, $body, $opts));
340-
return new WriteResponse($this->_buildWriteMeta($resp->Duration), $resp->Err);
285+
$ret = new WriteResponse();
286+
$this->_hydrateResponse($resp, $ret);
287+
return $ret;
341288
}
342289

343290
/**
@@ -349,7 +296,9 @@ protected function _executePut(string $path, $body, ?WriteOptions $opts): WriteR
349296
protected function _executeDelete(string $path, ?WriteOptions $opts): WriteResponse
350297
{
351298
$resp = $this->_requireOK($this->_doDelete($path, $opts));
352-
return new WriteResponse($this->_buildWriteMeta($resp->Duration), $resp->Err);
299+
$ret = new WriteResponse();
300+
$this->_hydrateResponse($resp, $ret);
301+
return $ret;
353302
}
354303

355304
/**
@@ -408,12 +357,12 @@ protected function _hydrateResponse(RequestResponse $resp, AbstractResponse $ret
408357
{
409358
// determine if this response contains a *Meta field
410359
if (\property_exists($ret, Hydration::FIELD_QUERY_META)) {
411-
$ret->QueryMeta = $this->_buildQueryMeta($resp->Duration, $resp->Response, $resp->RequestMeta->uri);
360+
$ret->QueryMeta = $resp->buildQueryMeta();
412361
} elseif (\property_exists($ret, Hydration::FIELD_WRITE_META)) {
413-
$ret->WriteMeta = $this->_buildWriteMeta($resp->Duration);
362+
$ret->WriteMeta = $resp->buildWriteMeta();
414363
}
415364

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

419368
// if there was an error in the response, set and return

src/AbstractModels.php

Lines changed: 111 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -23,57 +23,54 @@
2323
*/
2424
abstract class AbstractModels implements \Iterator, \ArrayAccess, \Countable, \JsonSerializable
2525
{
26-
/** @var \DCarbone\PHPConsulAPI\AbstractModel[] */
27-
protected array $_list = [];
28-
2926
/** @var string */
3027
protected string $containedClass;
3128

29+
/** @var \DCarbone\PHPConsulAPI\AbstractModel[] */
30+
private array $_list = [];
31+
32+
/** @var int */
33+
private int $_size = 0;
34+
3235
/**
3336
* AbstractModels constructor.
3437
* @param array|null $children
3538
*/
3639
public function __construct(?array $children = [])
3740
{
41+
if (!isset($this->containedClass)) {
42+
throw new \DomainException(
43+
\sprintf(
44+
'Class "%s" must define $containedClass',
45+
\get_called_class()
46+
)
47+
);
48+
}
3849
if (null === $children) {
3950
return;
4051
}
41-
foreach (\array_filter($children) as $child) {
42-
if (\is_array($child)) {
43-
$this->_list[] = $this->newChild($child);
44-
} elseif ($child instanceof $this->containedClass) {
45-
$this->_list[] = $child;
46-
} else {
47-
throw new \InvalidArgumentException(
48-
\sprintf(
49-
\get_class($this) . ' accepts only ' . $this->containedClass . ' as a child, saw %s',
50-
\is_object($child) ? \get_class($child) : \gettype($child)
51-
)
52-
);
53-
}
52+
foreach ($children as $child) {
53+
$this->append($child);
5454
}
5555
}
5656

5757
/**
5858
* @param \DCarbone\PHPConsulAPI\AbstractModel|null $value
5959
*/
60-
public function append(AbstractModel $value = null): void
60+
public function append(?AbstractModel $value): void
6161
{
62-
if (null === $value || $value instanceof $this->containedClass) {
63-
$this->_list[] = $value;
64-
} else {
65-
throw new \InvalidArgumentException(
66-
\sprintf(
67-
'%s accepts only objects of type %s or null as values',
68-
\get_class($this),
69-
$this->containedClass,
70-
)
71-
);
72-
}
62+
// validate provided value is either null or instance of allowed child class
63+
$value = $this->_validateValue($value);
64+
65+
// set offset to current value of _size, and iterate size by 1
66+
$offset = $this->_size++;
67+
68+
// if value is passed, clone then set.
69+
$this->_list[$offset] = $value;
7370
}
7471

7572
/**
76-
* @return \DCarbone\PHPConsulAPI\AbstractModel|mixed
73+
* @return \DCarbone\PHPConsulAPI\AbstractModel|false
7774
*/
7875
public function current()
7976
{
@@ -96,7 +93,7 @@ public function key()
9693
/**
9794
* @return bool
9895
*/
99-
public function valid()
96+
public function valid(): bool
10097
{
10198
return null !== \key($this->_list);
10299
}
@@ -115,17 +112,18 @@ public function offsetExists($offset)
115112
return \is_int($offset) && isset($this->_list[$offset]);
116113
}
117114

115+
/**
116+
* @param mixed $offset
117+
* @return \DCarbone\PHPConsulAPI\AbstractModel|null
118+
*/
118119
public function offsetGet($offset)
119120
{
120-
if (\is_int($offset) && isset($this->_list[$offset])) {
121+
$this->_validateOffset($offset);
122+
if (isset($this->_list[$offset])) {
121123
return $this->_list[$offset];
122124
}
123-
throw new \OutOfRangeException(
124-
\sprintf(
125-
'Offset %s does not exist in this list',
126-
\is_int($offset) ? (string) $offset : \gettype($offset)
127-
)
128-
);
125+
126+
return $this->_list[$offset];
129127
}
130128

131129
/**
@@ -134,42 +132,108 @@ public function offsetGet($offset)
134132
*/
135133
public function offsetSet($offset, $value): void
136134
{
137-
if (!\is_int($offset)) {
138-
throw new \InvalidArgumentException('Offset must be int');
139-
}
140-
if (null !== $value && !($value instanceof $this->containedClass)) {
141-
throw new \InvalidArgumentException('Value must be instance of ' . $this->containedClass);
135+
// if incoming offset is null, assume [] (append) operation.
136+
if (null === $offset) {
137+
$this->append($value);
138+
return;
142139
}
143-
$this->_list[$offset] = $value;
140+
141+
// validate provided offset value
142+
$this->_validateOffset($offset);
143+
144+
// validate value input and set
145+
$this->_list[$offset] = $this->_validateValue($value);
144146
}
145147

146148
/**
147149
* @param mixed $offset
148150
*/
149151
public function offsetUnset($offset): void
150152
{
151-
unset($this->_list[$offset]);
153+
// validate provided offset value
154+
$this->_validateOffset($offset);
155+
156+
// null out value in list
157+
$this->_list[$offset] = null;
152158
}
153159

154160
/**
155161
* @return int
156162
*/
157163
public function count(): int
158164
{
159-
return \count($this->_list);
165+
return $this->_size;
160166
}
161167

162168
/**
163169
* @return \DCarbone\PHPConsulAPI\AbstractModel[]
164170
*/
165171
public function jsonSerialize(): array
166172
{
167-
return \array_filter((array) $this->_list);
173+
$out = [];
174+
foreach ($this->_list as $i => $item) {
175+
if (null === $item) {
176+
$out[$i] = null;
177+
} else {
178+
$out[$i] = clone $item;
179+
}
180+
}
181+
return $out;
168182
}
169183

170184
/**
171185
* @param array $data
172-
* @return \static
186+
* @return \DCarbone\PHPConsulAPI\AbstractModel
173187
*/
174188
abstract protected function newChild(array $data): AbstractModel;
189+
190+
/**
191+
* @param mixed $offset
192+
*/
193+
private function _validateOffset($offset): void
194+
{
195+
if (!\is_int($offset)) {
196+
throw new \InvalidArgumentException(
197+
\sprintf(
198+
'Cannot use offset of type "%s" with "%s"',
199+
\gettype($offset),
200+
\get_class($this)
201+
)
202+
);
203+
}
204+
if (0 > $offset || $offset >= $this->_size) {
205+
throw new \OutOfRangeException(\sprintf('Offset %d does not exist in this list', $offset));
206+
}
207+
}
208+
209+
/**
210+
* @param mixed $value
211+
* @return \DCarbone\PHPConsulAPI\AbstractModel|null
212+
*/
213+
private function _validateValue($value): ?AbstractModel
214+
{
215+
// fast path for null values
216+
if (null === $value) {
217+
return null;
218+
}
219+
220+
// if instance of contained class, clone and move on
221+
if ($value instanceof $this->containedClass) {
222+
return clone $value;
223+
}
224+
225+
// if array, construct new child
226+
if (\is_array($value)) {
227+
return $this->newChild($value);
228+
}
229+
230+
// if we make it down here, fail.
231+
throw new \InvalidArgumentException(
232+
\sprintf(
233+
'%s accepts only objects of type %s, null, or associative array definition as values',
234+
\get_class($this),
235+
$this->containedClass,
236+
)
237+
);
238+
}
175239
}

0 commit comments

Comments
 (0)