Skip to content

Commit 56db18b

Browse files
committed
Merge branch 'sync'
2 parents be84508 + da79a6e commit 56db18b

18 files changed

+260
-81
lines changed

src/Concept/Provider.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
namespace Lkrms\Concept;
44

55
use Lkrms\Contract\IContainer;
6+
use Lkrms\Contract\IDateFormatter;
67
use Lkrms\Contract\IProvider;
78
use Lkrms\Exception\MethodNotImplementedException;
8-
use Lkrms\Support\DateFormatter;
99
use Lkrms\Support\ProviderContext;
1010
use Lkrms\Utility\Env;
1111

@@ -20,7 +20,7 @@ abstract class Provider implements IProvider
2020

2121
protected Env $Env;
2222

23-
private DateFormatter $DateFormatter;
23+
private IDateFormatter $DateFormatter;
2424

2525
/**
2626
* Creates a new provider object
@@ -32,13 +32,13 @@ public function __construct(IContainer $app, Env $env)
3232
}
3333

3434
/**
35-
* Get a DateFormatter to work with the backend's date format and/or
36-
* timezone
35+
* Get a date formatter to work with the backend's date and time format
36+
* and/or timezone
3737
*
38-
* The {@see DateFormatter} returned will be cached for the lifetime of the
38+
* The {@see IDateFormatter} returned will be cached for the lifetime of the
3939
* {@see Provider} instance.
4040
*/
41-
abstract protected function getDateFormatter(): DateFormatter;
41+
abstract protected function getDateFormatter(): IDateFormatter;
4242

4343
/**
4444
* @inheritDoc
@@ -99,7 +99,7 @@ final public function env(): Env
9999
/**
100100
* @inheritDoc
101101
*/
102-
final public function dateFormatter(): DateFormatter
102+
final public function dateFormatter(): IDateFormatter
103103
{
104104
return $this->DateFormatter
105105
?? ($this->DateFormatter = $this->getDateFormatter());

src/Contract/IProvider.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace Lkrms\Contract;
44

55
use Lkrms\Exception\MethodNotImplementedException;
6-
use Lkrms\Support\DateFormatter;
76

87
/**
98
* Services objects on behalf of a backend
@@ -52,9 +51,10 @@ public function getContext(?IContainer $container = null): IProviderContext;
5251
public function getBackendIdentifier(): array;
5352

5453
/**
55-
* Get a DateFormatter to work with the backend's date format and timezone
54+
* Get a date formatter to work with the backend's date and time format
55+
* and/or timezone
5656
*/
57-
public function dateFormatter(): DateFormatter;
57+
public function dateFormatter(): IDateFormatter;
5858

5959
/**
6060
* Throw an exception if the backend isn't reachable

src/Curler/Curler.php

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Lkrms\Concern\HasMutator;
77
use Lkrms\Concern\TReadable;
88
use Lkrms\Concern\TWritable;
9+
use Lkrms\Contract\IDateFormatter;
910
use Lkrms\Contract\IReadable;
1011
use Lkrms\Contract\IWritable;
1112
use Lkrms\Contract\ProvidesBuilder;
@@ -67,7 +68,7 @@
6768
* @property bool $ExpectJson Request JSON from upstream?
6869
* @property bool $PostJson Use JSON to serialize POST/PUT/PATCH/DELETE data?
6970
* @property bool $PreserveKeys Suppress removal of numeric indices from serialized lists?
70-
* @property DateFormatter|null $DateFormatter Specify the date format and timezone used upstream
71+
* @property IDateFormatter|null $DateFormatter Specify the date format and timezone used upstream
7172
* @property string|null $UserAgent Override the default User-Agent header
7273
* @property bool $AlwaysPaginate Pass every response to the pager?
7374
* @property bool $ObjectAsArray Return deserialized objects as associative arrays?
@@ -349,7 +350,7 @@ final class Curler implements IReadable, IWritable, ProvidesBuilder
349350
/**
350351
* Specify the date format and timezone used upstream
351352
*
352-
* @var DateFormatter|null
353+
* @var IDateFormatter|null
353354
*/
354355
protected $DateFormatter;
355356

@@ -428,7 +429,7 @@ public function __construct(
428429
bool $expectJson = true,
429430
bool $postJson = true,
430431
bool $preserveKeys = false,
431-
?DateFormatter $dateFormatter = null,
432+
?IDateFormatter $dateFormatter = null,
432433
?string $userAgent = null,
433434
bool $alwaysPaginate = false,
434435
bool $objectAsArray = true
@@ -799,11 +800,14 @@ private function prepareData($data)
799800
!($value instanceof CurlerFile ||
800801
$value instanceof DateTimeInterface)
801802
);
802-
$leafIterator = new RecursiveIteratorIterator($iterator);
803+
$leaves = new RecursiveIteratorIterator($iterator);
804+
$iterator = new RecursiveIteratorIterator(
805+
$iterator, RecursiveIteratorIterator::SELF_FIRST
806+
);
803807

804808
// Does `$data` contain a `CurlerFile`?
805809
$file = false;
806-
foreach ($leafIterator as $value) {
810+
foreach ($leaves as $value) {
807811
if ($value instanceof CurlerFile) {
808812
$file = true;
809813
break;
@@ -812,21 +816,32 @@ private function prepareData($data)
812816

813817
// With that answered, start over, replacing `CurlerFile` and
814818
// `DateTimeInterface` instances
815-
/** @var RecursiveObjectOrArrayIterator $iterator */
816819
foreach ($iterator as $value) {
820+
$replace = null;
821+
817822
if ($value instanceof CurlerFile) {
818-
$value = $value->getCurlFile();
823+
$replace = $value->getCurlFile();
819824
} elseif ($value instanceof DateTimeInterface) {
820-
$value = $this->getDateFormatter()->format($value);
821-
} elseif ($file) {
822-
// And if uploading a file, replace every object that isn't a
823-
// CURLFile with an array cURL can encode
824-
$iterator->maybeReplaceCurrentWithArray();
825+
$replace = $this->getDateFormatter()->format($value);
826+
} elseif (!$file) {
825827
continue;
826-
} else {
828+
}
829+
830+
/** @var RecursiveHasChildrenCallbackIterator<array-key,mixed> */
831+
$inner = $iterator->getInnerIterator();
832+
/** @var RecursiveObjectOrArrayIterator */
833+
$inner = $inner->getInnerIterator();
834+
835+
if ($replace !== null) {
836+
$inner->replace($replace);
827837
continue;
828838
}
829-
$iterator->replace($value);
839+
840+
// If uploading a file, replace every object that isn't a CURLFile
841+
// with an array cURL can encode
842+
if ($file) {
843+
$inner->maybeConvertToArray();
844+
}
830845
}
831846

832847
if ($file) {
@@ -855,7 +870,7 @@ private function getCookieKey(): ?string
855870
: null;
856871
}
857872

858-
private function getDateFormatter(): DateFormatter
873+
private function getDateFormatter(): IDateFormatter
859874
{
860875
return $this->DateFormatter
861876
?: ($this->DateFormatter = new DateFormatter());

src/Curler/CurlerBuilder.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
namespace Lkrms\Curler;
44

55
use Lkrms\Concept\Builder;
6+
use Lkrms\Contract\IDateFormatter;
67
use Lkrms\Curler\Catalog\CurlerProperty;
78
use Lkrms\Curler\Contract\ICurlerHeaders;
89
use Lkrms\Curler\Contract\ICurlerPager;
9-
use Lkrms\Support\DateFormatter;
1010

1111
/**
1212
* Creates Curler objects via a fluent interface
@@ -32,7 +32,7 @@
3232
* @method $this expectJson(bool $value = true) Request JSON from upstream? (default: true)
3333
* @method $this postJson(bool $value = true) Use JSON to serialize POST/PUT/PATCH/DELETE data? (default: true)
3434
* @method $this preserveKeys(bool $value = true) Suppress removal of numeric indices from serialized lists? (default: false)
35-
* @method $this dateFormatter(?DateFormatter $value) Specify the date format and timezone used upstream
35+
* @method $this dateFormatter(?IDateFormatter $value) Specify the date format and timezone used upstream
3636
* @method $this userAgent(?string $value) Override the default User-Agent header (see {@see Curler::$UserAgent})
3737
* @method $this alwaysPaginate(bool $value = true) Pass every response to the pager? (default: false)
3838
* @method $this objectAsArray(bool $value = true) Return deserialized objects as associative arrays? (default: true)

src/Support/Iterator/ObjectOrArrayIterator.php

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,15 @@
33
namespace Lkrms\Support\Iterator;
44

55
use Lkrms\Support\Iterator\Contract\MutableIterator;
6-
use Iterator;
6+
use LogicException;
77
use ReturnTypeWillChange;
8-
use RuntimeException;
98

109
/**
1110
* Iterates over an object's properties or an array's elements
1211
*
13-
* @implements Iterator<array-key,mixed>
1412
* @implements MutableIterator<array-key,mixed>
1513
*/
16-
class ObjectOrArrayIterator implements Iterator, MutableIterator
14+
class ObjectOrArrayIterator implements MutableIterator
1715
{
1816
/**
1917
* @var object|mixed[]
@@ -64,17 +62,15 @@ public function current()
6462
public function replace($value)
6563
{
6664
if (($key = current($this->Keys)) === false) {
67-
throw new RuntimeException('Current position is not valid');
65+
throw new LogicException('Current position is not valid');
6866
}
6967

7068
if ($this->IsObject) {
7169
$this->ObjectOrArray->{$key} = $value;
72-
7370
return $this;
7471
}
7572

7673
$this->ObjectOrArray[$key] = $value;
77-
7874
return $this;
7975
}
8076

@@ -87,7 +83,6 @@ public function key()
8783
if (($key = current($this->Keys)) === false) {
8884
return null;
8985
}
90-
9186
return $key;
9287
}
9388

src/Support/Iterator/RecursiveHasChildrenCallbackIterator.php

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,24 @@ public function hasChildren(): bool
4848
return false;
4949
}
5050

51-
$key = $this->Iterator->key();
52-
$current = $this->Iterator->current();
53-
54-
return ($this->Callback)($current, $key, $this->Iterator);
51+
return ($this->Callback)(
52+
$this->Iterator->current(),
53+
$this->Iterator->key(),
54+
$this->Iterator,
55+
);
5556
}
5657

5758
/**
58-
* @return RecursiveIterator<TKey,TValue>|null
59+
* @return self<TKey,TValue>|null
5960
*/
60-
public function getChildren(): ?RecursiveIterator
61+
public function getChildren(): ?self
6162
{
62-
return $this->Iterator->getChildren();
63+
/** @var RecursiveIterator<TKey,TValue>|null */
64+
$children = $this->Iterator->getChildren();
65+
66+
return
67+
$children === null
68+
? null
69+
: new self($children, $this->Callback);
6370
}
6471
}

src/Support/Iterator/RecursiveObjectOrArrayIterator.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,12 @@ public function getChildren(): ?self
4343
* If the current element is an object with children, replace it with an
4444
* array of its properties
4545
*
46-
* @return mixed|false `false` if the current position is invalid, otherwise
47-
* the current value.
46+
* @return $this
4847
*/
49-
public function maybeReplaceCurrentWithArray()
48+
public function maybeConvertToArray()
5049
{
5150
if (($current = $this->current()) === false) {
52-
return false;
51+
return $this;
5352
}
5453

5554
if (is_object($current) && $this->hasChildren()) {
@@ -61,6 +60,6 @@ public function maybeReplaceCurrentWithArray()
6160
return $this->replace($array);
6261
}
6362

64-
return $current;
63+
return $this;
6564
}
6665
}

src/Sync/Concept/HttpSyncProvider.php

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Lkrms\Sync\Concept;
44

5+
use Lkrms\Contract\IDateFormatter;
56
use Lkrms\Contract\IProvider;
67
use Lkrms\Curler\Contract\ICurlerHeaders;
78
use Lkrms\Curler\Contract\ICurlerPager;
@@ -35,20 +36,19 @@ final public function getCurler(
3536
string $path,
3637
?int $expiry = -1,
3738
?ICurlerHeaders $headers = null,
38-
?ICurlerPager $pager = null
39+
?ICurlerPager $pager = null,
40+
?IDateFormatter $dateFormatter = null
3941
): Curler {
4042
$curlerB = $this->buildCurler(
4143
CurlerBuilder::build()
4244
->baseUrl($this->getEndpointUrl($path))
4345
);
4446

45-
if (!is_null($expiry) && $expiry < 0) {
47+
if ($expiry !== null && $expiry < 0) {
4648
$expiry = $this->getExpiry($path);
4749
}
48-
if (!is_null($expiry)) {
49-
$curlerB = $curlerB
50-
->cacheResponse()
51-
->expiry($expiry);
50+
if ($expiry !== null) {
51+
$curlerB = $curlerB->cacheResponse()->expiry($expiry);
5252
} else {
5353
$curlerB = $curlerB->cacheResponse(false);
5454
}
@@ -65,6 +65,12 @@ final public function getCurler(
6565
$curlerB = $curlerB->pager($this->getPager($path));
6666
}
6767

68+
if ($dateFormatter) {
69+
$curlerB = $curlerB->dateFormatter($dateFormatter);
70+
} elseif (!$curlerB->issetB('dateFormatter')) {
71+
$curlerB = $curlerB->dateFormatter($this->getDateFormatter($path));
72+
}
73+
6874
return $curlerB->go();
6975
}
7076

@@ -192,4 +198,9 @@ protected function getExpiry(?string $path): ?int
192198
{
193199
return null;
194200
}
201+
202+
/**
203+
* @inheritDoc
204+
*/
205+
abstract protected function getDateFormatter(?string $path = null): IDateFormatter;
195206
}

0 commit comments

Comments
 (0)