Skip to content

Commit d9a0ff3

Browse files
committed
Merge branch 'invalid-sync-entity-exception'
2 parents 5125a52 + c3b3c7c commit d9a0ff3

File tree

6 files changed

+169
-41
lines changed

6 files changed

+169
-41
lines changed

lib/lk-util/Command/Generate/GenerateBuilder.php

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class GenerateBuilder extends GenerateCommand
4343
];
4444

4545
private ?string $ClassFqcn;
46+
4647
private ?string $BuilderFqcn;
4748

4849
/**
@@ -59,6 +60,16 @@ class GenerateBuilder extends GenerateCommand
5960
*/
6061
private ?array $Skip;
6162

63+
// --
64+
65+
/**
66+
* camelCase name => parameter received by reference, or
67+
* parameter/property/method with a generic template type
68+
*
69+
* @var array<string,ReflectionParameter|ReflectionProperty|ReflectionMethod>
70+
*/
71+
private array $ToDeclare = [];
72+
6273
public function description(): string
6374
{
6475
return 'Generate a builder that creates instances of a class via a fluent interface';
@@ -124,6 +135,7 @@ protected function run(string ...$args)
124135
{
125136
$this->reset();
126137
$this->Skip = array_merge($this->Skip, self::SKIP);
138+
$this->ToDeclare = [];
127139

128140
$classFqcn = $this->getRequiredFqcnOptionValue(
129141
'class',
@@ -177,14 +189,6 @@ protected function run(string ...$args)
177189
*/
178190
$_params = [];
179191

180-
/**
181-
* camelCase name => parameter received by reference or with a generic
182-
* template type
183-
*
184-
* @var array<string,ReflectionParameter|ReflectionProperty|ReflectionMethod>
185-
*/
186-
$toDeclare = [];
187-
188192
/** @var array<string,array<string,PhpDocTemplateTag>> */
189193
$declareTemplates = [];
190194

@@ -210,7 +214,7 @@ protected function run(string ...$args)
210214
// Variables can't be passed to __call by reference, so this
211215
// parameter needs to be received via a declared method
212216
if ($_param->isPassedByReference()) {
213-
$toDeclare[$name] = $_param;
217+
$this->ToDeclare[$name] = $_param;
214218
}
215219
}
216220
}
@@ -301,12 +305,9 @@ protected function run(string ...$args)
301305
$propertyFile,
302306
$templates
303307
);
304-
if ($templates &&
305-
count($templates) === 1 &&
306-
($type === 'class-string<' . ($key = array_keys($templates)[0]) . '>' ||
307-
$type === $key)) {
308+
if (count($templates) === 1) {
308309
$declareTemplates[$name] = $templates;
309-
$toDeclare[$name] = $toDeclare[$name] ?? $_property;
310+
$this->ToDeclare[$name] = $this->ToDeclare[$name] ?? $_property;
310311
}
311312
} else {
312313
$type = $_property->hasType()
@@ -370,7 +371,7 @@ protected function run(string ...$args)
370371
$propertyFile = $_constructor->getFileName();
371372
$propertyNamespace = $_constructor->getDeclaringClass()->getNamespaceName();
372373
$declaringClass = $this->getTypeAlias($_constructor->getDeclaringClass()->getName());
373-
$declare = array_key_exists($name, $toDeclare);
374+
$declare = array_key_exists($name, $this->ToDeclare);
374375

375376
// If the parameter has a matching property, retrieve its DocBlock
376377
if ($_property = $_allProperties[$name] ?? null) {
@@ -401,13 +402,10 @@ protected function run(string ...$args)
401402
$propertyFile,
402403
$templates
403404
);
404-
if ($templates &&
405-
count($templates) === 1 &&
406-
($type === 'class-string<' . ($key = array_keys($templates)[0]) . '>' ||
407-
$type === $key)) {
405+
if (count($templates) === 1) {
408406
$declareTemplates[$name] = $templates;
409407
if (!$declare) {
410-
$toDeclare[$name] = $_param;
408+
$this->ToDeclare[$name] = $_param;
411409
$declare = true;
412410
}
413411
}
@@ -561,7 +559,7 @@ protected function run(string ...$args)
561559
$returnsVoid = false;
562560

563561
if ($declare) {
564-
$toDeclare[$name] = $_method;
562+
$this->ToDeclare[$name] = $_method;
565563
}
566564

567565
$_type = $phpDoc->Return->Type ?? null;
@@ -580,7 +578,7 @@ protected function run(string ...$args)
580578
$type === $key)) {
581579
$declareTemplates[$name] = $templates;
582580
if (!$declare) {
583-
$toDeclare[$name] = $_method;
581+
$this->ToDeclare[$name] = $_method;
584582
$declare = true;
585583
}
586584
}
@@ -784,7 +782,7 @@ protected function run(string ...$args)
784782
),
785783
);
786784

787-
foreach ($toDeclare as $name => $_param) {
785+
foreach ($this->ToDeclare as $name => $_param) {
788786
if ($_param instanceof ReflectionMethod) {
789787
$_params = $_param->getParameters();
790788
$return = $returnsValue[$name] ? 'return ' : '';
@@ -856,13 +854,23 @@ private function getSummary(
856854
if ($summary !== null) {
857855
$summary = rtrim($summary, '.');
858856
}
857+
$byRef = false;
858+
if ($declare && $name !== null) {
859+
$declaring = $this->ToDeclare[$name] ?? null;
860+
if ($declaring instanceof ReflectionParameter) {
861+
$byRef = $declaring->isPassedByReference();
862+
}
863+
}
859864
if ($member) {
860865
$class = $typeNameCallback($member->getDeclaringClass()->getName());
861866
$name = $member->getName();
862867
$param = '';
863-
$see = $member instanceof ReflectionMethod ? $class . '::' . $name . '()' : $class . '::$' . $name;
868+
$see =
869+
$member instanceof ReflectionMethod
870+
? $class . '::' . $name . '()'
871+
: $class . '::$' . $name;
864872
} else {
865-
$param = "`\$$name` in ";
873+
$param = $declare ? "\$$name in " : "`\$$name` in ";
866874
$see = $class . '::__construct()';
867875
}
868876
if ($default !== null) {
@@ -872,15 +880,27 @@ private function getSummary(
872880
$defaultSuffix = $defaultPrefix = '';
873881
}
874882

875-
return $summary !== null
876-
? ($declare
877-
? $summary . $defaultSuffix
878-
: " $summary" . ($link ? " ({$defaultPrefix}see {@see $see})" : $defaultSuffix))
879-
: (($declare
880-
? ($member instanceof ReflectionMethod ? "Call $see on a new instance" : "Pass a variable to $param$see by reference")
881-
: ($link
882-
? " See {@see $see}"
883-
: ($member instanceof ReflectionMethod ? " Call $see on a new instance" : ($param !== '' ? " Pass \$value to $param$see" : " Set $see"))))
884-
. $defaultSuffix);
883+
return
884+
$summary !== null
885+
? ($declare
886+
? $summary . $defaultSuffix
887+
: " $summary"
888+
. ($link
889+
? " ({$defaultPrefix}see {@see $see})"
890+
: $defaultSuffix))
891+
: (($declare
892+
? ($member instanceof ReflectionMethod
893+
? "Call $see on a new instance"
894+
: ($byRef
895+
? "Pass a variable to $param$see by reference"
896+
: "Pass a value to $param$see"))
897+
: ($link
898+
? " See {@see $see}"
899+
: ($member instanceof ReflectionMethod
900+
? " Call $see on a new instance"
901+
: ($param !== ''
902+
? " Pass \$value to $param$see"
903+
: " Set $see"))))
904+
. $defaultSuffix);
885905
}
886906
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Lkrms\Sync\Exception;
4+
5+
use Lkrms\Sync\Contract\ISyncEntity;
6+
use Lkrms\Sync\Contract\ISyncProvider;
7+
use Throwable;
8+
9+
/**
10+
* Thrown when an entity has invalid data or is not the droid you're looking for
11+
*/
12+
class SyncInvalidEntityException extends SyncException
13+
{
14+
protected ISyncProvider $Provider;
15+
16+
protected string $EntityType;
17+
18+
/**
19+
* @var int|string|null
20+
*/
21+
protected $EntityId;
22+
23+
protected ?ISyncEntity $Entity;
24+
25+
/**
26+
* Creates a new SyncInvalidEntityException object
27+
*
28+
* @template T of ISyncEntity
29+
*
30+
* @param class-string<T> $entityType
31+
* @param T|int|string|null $entityOrId
32+
*/
33+
public function __construct(
34+
string $message,
35+
ISyncProvider $provider,
36+
string $entityType,
37+
$entityOrId,
38+
?Throwable $previous = null
39+
) {
40+
$this->Provider = $provider;
41+
$this->EntityType = $entityType;
42+
43+
if ($entityOrId instanceof ISyncEntity) {
44+
$this->Entity = $entityOrId;
45+
$this->EntityId = $this->Entity->id();
46+
} else {
47+
$this->EntityId = $entityOrId;
48+
$this->Entity = null;
49+
}
50+
51+
parent::__construct($message, $previous);
52+
}
53+
}

src/Sync/Support/DbSyncDefinitionBuilder.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
* @method $this overrides(array<int-mask-of<OP::*>,Closure(DbSyncDefinition<TEntity,TProvider>, OP::*, ISyncContext, mixed...): (iterable<TEntity>|TEntity)> $value) An array that maps sync operations to closures that override other implementations (see {@see SyncDefinition::$Overrides})
2929
* @method $this keyMap(array<array-key,array-key|array-key[]>|null $value) An array that maps provider (backend) keys to one or more entity keys (see {@see SyncDefinition::$KeyMap})
3030
* @method $this keyMapFlags(int-mask-of<ArrayMapperFlag::*> $value) Passed to the array mapper if `$keyMap` is provided
31-
* @method $this pipelineFromBackend(IPipeline<mixed[],TEntity,array{0:OP::*,1:ISyncContext,2?:int|string|TEntity|TEntity[]|null,...}>|null $value) A pipeline that maps data from the provider to entity-compatible associative arrays, or `null` if mapping is not required
32-
* @method $this pipelineToBackend(IPipeline<TEntity,mixed[],array{0:OP::*,1:ISyncContext,2?:int|string|TEntity|TEntity[]|null,...}>|null $value) A pipeline that maps serialized entities to data compatible with the provider, or `null` if mapping is not required
3331
* @method $this readFromReadList(bool $value = true) If true, perform READ operations by iterating over entities returned by READ_LIST (default: false; see {@see SyncDefinition::$ReadFromReadList})
3432
* @method $this returnEntitiesFrom(SyncEntitySource::*|null $value) Where to acquire entity data for the return value of a successful CREATE, UPDATE or DELETE operation
3533
*
@@ -78,4 +76,28 @@ public function provider(DbSyncProvider $value)
7876
{
7977
return $this->getWithValue(__FUNCTION__, $value);
8078
}
79+
80+
/**
81+
* A pipeline that maps data from the provider to entity-compatible associative arrays, or `null` if mapping is not required
82+
*
83+
* @template T of ISyncEntity
84+
* @param IPipeline<mixed[],T,array{0:OP::*,1:ISyncContext,2?:int|string|T|T[]|null,...}>|null $value
85+
* @return $this<T,TProvider>
86+
*/
87+
public function pipelineFromBackend(?IPipeline $value)
88+
{
89+
return $this->getWithValue(__FUNCTION__, $value);
90+
}
91+
92+
/**
93+
* A pipeline that maps serialized entities to data compatible with the provider, or `null` if mapping is not required
94+
*
95+
* @template T of ISyncEntity
96+
* @param IPipeline<T,mixed[],array{0:OP::*,1:ISyncContext,2?:int|string|T|T[]|null,...}>|null $value
97+
* @return $this<T,TProvider>
98+
*/
99+
public function pipelineToBackend(?IPipeline $value)
100+
{
101+
return $this->getWithValue(__FUNCTION__, $value);
102+
}
81103
}

src/Sync/Support/HttpSyncDefinitionBuilder.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@
4040
* @method $this overrides(array<int-mask-of<OP::*>,Closure(HttpSyncDefinition<TEntity,TProvider>, OP::*, ISyncContext, mixed...): (iterable<TEntity>|TEntity)> $value) An array that maps sync operations to closures that override other implementations (see {@see SyncDefinition::$Overrides})
4141
* @method $this keyMap(array<array-key,array-key|array-key[]>|null $value) An array that maps provider (backend) keys to one or more entity keys (see {@see SyncDefinition::$KeyMap})
4242
* @method $this keyMapFlags(int-mask-of<ArrayMapperFlag::*> $value) Passed to the array mapper if `$keyMap` is provided
43-
* @method $this pipelineFromBackend(IPipeline<mixed[],TEntity,array{0:OP::*,1:ISyncContext,2?:int|string|TEntity|TEntity[]|null,...}>|null $value) A pipeline that maps data from the provider to entity-compatible associative arrays, or `null` if mapping is not required
44-
* @method $this pipelineToBackend(IPipeline<TEntity,mixed[],array{0:OP::*,1:ISyncContext,2?:int|string|TEntity|TEntity[]|null,...}>|null $value) A pipeline that maps serialized entities to data compatible with the provider, or `null` if mapping is not required
4543
* @method $this readFromReadList(bool $value = true) If true, perform READ operations by iterating over entities returned by READ_LIST (default: false; see {@see SyncDefinition::$ReadFromReadList})
4644
* @method $this returnEntitiesFrom(SyncEntitySource::*|null $value) Where to acquire entity data for the return value of a successful CREATE, UPDATE or DELETE operation
4745
*
@@ -90,4 +88,28 @@ public function provider(HttpSyncProvider $value)
9088
{
9189
return $this->getWithValue(__FUNCTION__, $value);
9290
}
91+
92+
/**
93+
* A pipeline that maps data from the provider to entity-compatible associative arrays, or `null` if mapping is not required
94+
*
95+
* @template T of ISyncEntity
96+
* @param IPipeline<mixed[],T,array{0:OP::*,1:ISyncContext,2?:int|string|T|T[]|null,...}>|null $value
97+
* @return $this<T,TProvider>
98+
*/
99+
public function pipelineFromBackend(?IPipeline $value)
100+
{
101+
return $this->getWithValue(__FUNCTION__, $value);
102+
}
103+
104+
/**
105+
* A pipeline that maps serialized entities to data compatible with the provider, or `null` if mapping is not required
106+
*
107+
* @template T of ISyncEntity
108+
* @param IPipeline<T,mixed[],array{0:OP::*,1:ISyncContext,2?:int|string|T|T[]|null,...}>|null $value
109+
* @return $this<T,TProvider>
110+
*/
111+
public function pipelineToBackend(?IPipeline $value)
112+
{
113+
return $this->getWithValue(__FUNCTION__, $value);
114+
}
93115
}

src/Sync/Support/SyncSerializeRules.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ final class SyncSerializeRules implements ISyncSerializeRules, IReadable, IImmut
229229
* @param class-string<TEntity> $entity
230230
* @param array<array<array<string|Closure>|string>|array<string|Closure>|string> $remove
231231
* @param array<array<array<string|Closure>|string>|array<string|Closure>|string> $replace
232-
* @param SyncSerializeRules<TEntity>|null $inherit
232+
* @param SyncSerializeRules<TEntity>|null $inherit Inherit rules from another instance
233233
*/
234234
public function __construct(
235235
string $entity,

src/Sync/Support/SyncSerializeRulesBuilder.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
* @method $this replace(array<array<array<string|Closure>|string>|array<string|Closure>|string> $value) Values to replace with IDs (see {@see SyncSerializeRules::$Replace})
2323
* @method $this recurseRules(?bool $value = true) Apply path-based rules to every instance of $Entity? (default: true)
2424
* @method $this flags(?int $value) Set SyncSerializeRules::$Flags
25-
* @method $this inherit(SyncSerializeRules<TEntity>|null $value) Pass $value to `$inherit` in SyncSerializeRules::__construct()
2625
*
2726
* @uses SyncSerializeRules
2827
*
@@ -57,4 +56,16 @@ public function entity(string $value)
5756
{
5857
return $this->getWithValue(__FUNCTION__, $value);
5958
}
59+
60+
/**
61+
* Inherit rules from another instance
62+
*
63+
* @template T of ISyncEntity
64+
* @param SyncSerializeRules<T>|null $value
65+
* @return $this<T>
66+
*/
67+
public function inherit(?SyncSerializeRules $value)
68+
{
69+
return $this->getWithValue(__FUNCTION__, $value);
70+
}
6071
}

0 commit comments

Comments
 (0)