Skip to content

Commit 20bfba9

Browse files
committed
feat: PresetTransformer
1 parent eafa73f commit 20bfba9

File tree

12 files changed

+357
-25
lines changed

12 files changed

+357
-25
lines changed

config/services.php

+14-9
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
use Rekalogika\Mapper\Transformer\Implementation\ObjectToArrayTransformer;
5050
use Rekalogika\Mapper\Transformer\Implementation\ObjectToObjectTransformer;
5151
use Rekalogika\Mapper\Transformer\Implementation\ObjectToStringTransformer;
52+
use Rekalogika\Mapper\Transformer\Implementation\PresetTransformer;
5253
use Rekalogika\Mapper\Transformer\Implementation\ScalarToScalarTransformer;
5354
use Rekalogika\Mapper\Transformer\Implementation\StringToBackedEnumTransformer;
5455
use Rekalogika\Mapper\Transformer\Implementation\SymfonyUidTransformer;
@@ -120,7 +121,7 @@
120121

121122
$services
122123
->set(ScalarToScalarTransformer::class)
123-
->tag('rekalogika.mapper.transformer', ['priority' => -350]);
124+
->tag('rekalogika.mapper.transformer', ['priority' => -300]);
124125

125126
$services
126127
->set(ObjectMapperTransformer::class)
@@ -130,29 +131,33 @@
130131
service('rekalogika.mapper.object_mapper.table_factory'),
131132
service('rekalogika.mapper.object_mapper.resolver'),
132133
])
133-
->tag('rekalogika.mapper.transformer', ['priority' => -400]);
134+
->tag('rekalogika.mapper.transformer', ['priority' => -350]);
134135

135136
$services
136137
->set(DateTimeTransformer::class)
137-
->tag('rekalogika.mapper.transformer', ['priority' => -450]);
138+
->tag('rekalogika.mapper.transformer', ['priority' => -400]);
138139

139140
$services
140141
->set(StringToBackedEnumTransformer::class)
142+
->tag('rekalogika.mapper.transformer', ['priority' => -450]);
143+
144+
$services
145+
->set(SymfonyUidTransformer::class)
141146
->tag('rekalogika.mapper.transformer', ['priority' => -500]);
142147

143148
$services
144-
->set(ClassMethodTransformer::class)
145-
->args([
146-
service('rekalogika.mapper.sub_mapper.factory'),
147-
])
149+
->set(ObjectToStringTransformer::class)
148150
->tag('rekalogika.mapper.transformer', ['priority' => -550]);
149151

150152
$services
151-
->set(SymfonyUidTransformer::class)
153+
->set(PresetTransformer::class)
152154
->tag('rekalogika.mapper.transformer', ['priority' => -600]);
153155

154156
$services
155-
->set(ObjectToStringTransformer::class)
157+
->set(ClassMethodTransformer::class)
158+
->args([
159+
service('rekalogika.mapper.sub_mapper.factory'),
160+
])
156161
->tag('rekalogika.mapper.transformer', ['priority' => -650]);
157162

158163
$services

src/Debug/TraceData.php

+22-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ final class TraceData
3939
private ?string $callerClass = null;
4040
private ?string $callerType = null;
4141
private ?string $callerName = null;
42+
private bool $refused = false;
4243

4344
/**
4445
* @param null|array<int,Type|MixedType> $possibleTargetTypes
@@ -57,7 +58,18 @@ public function __construct(
5758
$this->existingTargetType = \get_debug_type($existingTargetValue);
5859
}
5960

60-
public function finalizeTime(float $time): void
61+
public function refusedToTransform(): void
62+
{
63+
$this->refused = true;
64+
}
65+
66+
public function finalize(float $time, mixed $result): void
67+
{
68+
$this->finalizeTime($time);
69+
$this->finalizeResult($result);
70+
}
71+
72+
private function finalizeTime(float $time): void
6173
{
6274
if (count($this->nestedTraceData) === 0) {
6375
// If this is the last trace data (no nested trace data)
@@ -70,7 +82,7 @@ public function finalizeTime(float $time): void
7082
}
7183
}
7284

73-
public function finalizeResult(mixed $result): void
85+
private function finalizeResult(mixed $result): void
7486
{
7587
$this->resultType = \get_debug_type($result);
7688
}
@@ -144,7 +156,6 @@ public function getSelectedTargetTypeHtml(): string
144156
return TypeUtil::getTypeStringHtml($this->selectedTargetType);
145157
}
146158
return 'mixed';
147-
148159
}
149160

150161
public function getResultType(): string
@@ -227,4 +238,12 @@ public function getCaller(): ?array
227238
'name' => $this->callerName,
228239
];
229240
}
241+
242+
/**
243+
* Get the value of refused
244+
*/
245+
public function isRefused(): bool
246+
{
247+
return $this->refused;
248+
}
230249
}

src/Debug/TraceableTransformer.php

+13-7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Rekalogika\Mapper\MainTransformer\Model\DebugContext;
1919
use Rekalogika\Mapper\MainTransformer\Model\Path;
2020
use Rekalogika\Mapper\Transformer\AbstractTransformerDecorator;
21+
use Rekalogika\Mapper\Transformer\Exception\RefuseToTransformException;
2122
use Rekalogika\Mapper\Transformer\MainTransformerAwareInterface;
2223
use Rekalogika\Mapper\Transformer\MainTransformerAwareTrait;
2324
use Rekalogika\Mapper\Transformer\TransformerInterface;
@@ -109,15 +110,20 @@ public function transform(
109110
$this->dataCollector->collectTraceData($traceData);
110111
}
111112

112-
$start = microtime(true);
113-
/** @var mixed */
114-
$result = $this->decorated->transform($source, $target, $sourceType, $targetType, $context);
115-
$time = microtime(true) - $start;
113+
try {
114+
$start = microtime(true);
115+
/** @var mixed */
116+
$result = $this->decorated->transform($source, $target, $sourceType, $targetType, $context);
117+
$time = microtime(true) - $start;
116118

117-
$traceData->finalizeTime($time);
118-
$traceData->finalizeResult($result);
119+
$traceData->finalize($time, $result);
119120

120-
return $result;
121+
return $result;
122+
} catch (RefuseToTransformException $e) {
123+
$traceData->refusedToTransform();
124+
125+
throw $e;
126+
}
121127
}
122128

123129
public function getSupportedTransformation(): iterable

src/MainTransformer/Implementation/MainTransformer.php

+4
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ public function transform(
219219
context: $context
220220
);
221221
} catch (RefuseToTransformException) {
222+
if ($targetTypeForTransformer !== null) {
223+
$objectCache->undoPreCache($source, $targetTypeForTransformer);
224+
}
225+
222226
continue;
223227
}
224228

src/ObjectCache/ObjectCache.php

+25
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,23 @@ public function preCache(mixed $source, Type $targetType): void
7777
$this->preCache->offsetGet($source)?->offsetSet($targetTypeString, true);
7878
}
7979

80+
public function undoPreCache(mixed $source, Type $targetType): void
81+
{
82+
if (!is_object($source)) {
83+
return;
84+
}
85+
86+
if ($this->isBlacklisted($source)) {
87+
return;
88+
}
89+
90+
$targetTypeString = $this->typeResolver->getTypeString($targetType);
91+
92+
if (isset($this->preCache[$source][$targetTypeString])) {
93+
unset($this->preCache[$source][$targetTypeString]);
94+
}
95+
}
96+
8097
public function containsTarget(mixed $source, Type $targetType): bool
8198
{
8299
if (!is_object($source)) {
@@ -156,4 +173,12 @@ public function saveTarget(
156173
unset($this->preCache[$source][$targetTypeString]);
157174
}
158175
}
176+
177+
/**
178+
* @return \WeakMap<object,\ArrayObject<string,object>>
179+
*/
180+
public function getInternalMapping(): \WeakMap
181+
{
182+
return $this->cache;
183+
}
159184
}
+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of rekalogika/mapper package.
7+
*
8+
* (c) Priyadi Iman Nurcahyo <https://rekalogika.dev>
9+
*
10+
* For the full copyright and license information, please view the LICENSE file
11+
* that was distributed with this source code.
12+
*/
13+
14+
namespace Rekalogika\Mapper\Transformer\Context;
15+
16+
use Rekalogika\Mapper\ObjectCache\ObjectCache;
17+
use Rekalogika\Mapper\Transformer\Exception\PresetMappingNotFound;
18+
use Rekalogika\Mapper\Transformer\Model\SplObjectStorageWrapper;
19+
20+
final readonly class PresetMapping
21+
{
22+
/**
23+
* @var \WeakMap<object,\ArrayObject<class-string,object>>
24+
*/
25+
private \WeakMap $mappings;
26+
27+
/**
28+
* @param iterable<object,iterable<class-string,object>> $mappings
29+
*/
30+
public function __construct(iterable $mappings)
31+
{
32+
/**
33+
* @var \WeakMap<object,\ArrayObject<class-string,object>>
34+
*/
35+
$weakMap = new \WeakMap();
36+
37+
foreach ($mappings as $source => $classToTargetMapping) {
38+
$classToTargetMappingArray = [];
39+
40+
foreach ($classToTargetMapping as $class => $target) {
41+
$classToTargetMappingArray[$class] = $target;
42+
}
43+
44+
$weakMap[$source] = new \ArrayObject($classToTargetMappingArray);
45+
}
46+
47+
$this->mappings = $weakMap;
48+
}
49+
50+
public static function fromObjectCache(ObjectCache $objectCache): self
51+
{
52+
$objectCacheWeakMap = $objectCache->getInternalMapping();
53+
54+
/** @var SplObjectStorageWrapper<object,\ArrayObject<class-string,object>> */
55+
$presetMapping = new SplObjectStorageWrapper(new \SplObjectStorage());
56+
57+
/**
58+
* @var object $source
59+
* @var \ArrayObject<class-string,object> $classToTargetMapping
60+
*/
61+
foreach ($objectCacheWeakMap as $source => $classToTargetMapping) {
62+
$newTargetClass = $source::class;
63+
/** @var object */
64+
$newTarget = $source;
65+
66+
/**
67+
* @var string $targetClass
68+
* @var object $target
69+
*/
70+
foreach ($classToTargetMapping as $targetClass => $target) {
71+
if (!class_exists($targetClass)) {
72+
continue;
73+
}
74+
75+
$newSource = $target;
76+
77+
if (!$presetMapping->offsetExists($newSource)) {
78+
/** @var \ArrayObject<class-string,object> */
79+
$arrayObject = new \ArrayObject();
80+
$presetMapping->offsetSet($newSource, $arrayObject);
81+
}
82+
83+
$presetMapping->offsetGet($newSource)?->offsetSet($newTargetClass, $newTarget);
84+
}
85+
}
86+
87+
return new self($presetMapping);
88+
}
89+
90+
/**
91+
* @template T of object
92+
* @param object $source
93+
* @param class-string<T> $targetClass
94+
* @return T
95+
* @throws PresetMappingNotFound
96+
*/
97+
public function findResult(object $source, string $targetClass): object
98+
{
99+
$mappings = $this->mappings[$source] ?? null;
100+
101+
if (null === $mappings) {
102+
throw new PresetMappingNotFound();
103+
}
104+
105+
$result = $mappings[$targetClass] ?? null;
106+
107+
if (null === $result) {
108+
throw new PresetMappingNotFound();
109+
}
110+
111+
if (!($result instanceof $targetClass)) {
112+
throw new PresetMappingNotFound();
113+
}
114+
115+
return $result;
116+
}
117+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of rekalogika/mapper package.
7+
*
8+
* (c) Priyadi Iman Nurcahyo <https://rekalogika.dev>
9+
*
10+
* For the full copyright and license information, please view the LICENSE file
11+
* that was distributed with this source code.
12+
*/
13+
14+
namespace Rekalogika\Mapper\Transformer\Exception;
15+
16+
use Rekalogika\Mapper\Exception\RuntimeException;
17+
18+
class PresetMappingNotFound extends RuntimeException
19+
{
20+
}

0 commit comments

Comments
 (0)