-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
479 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,9 @@ | ||
# CHANGELOG | ||
|
||
## 0.6.2 | ||
|
||
* feat: Data collector. | ||
|
||
## 0.6.1 | ||
|
||
* fix: Add caching for `ObjectMapperTable`. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of rekalogika/mapper package. | ||
* | ||
* (c) Priyadi Iman Nurcahyo <https://rekalogika.dev> | ||
* | ||
* For the full copyright and license information, please view the LICENSE file | ||
* that was distributed with this source code. | ||
*/ | ||
|
||
use Rekalogika\Mapper\Debug\MapperDataCollector; | ||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; | ||
|
||
return static function (ContainerConfigurator $containerConfigurator): void { | ||
$services = $containerConfigurator->services(); | ||
|
||
$services | ||
->set('rekalogika.mapper.data_collector', MapperDataCollector::class) | ||
->tag('data_collector', [ | ||
'id' => 'rekalogika_mapper', | ||
]); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of rekalogika/mapper package. | ||
* | ||
* (c) Priyadi Iman Nurcahyo <https://rekalogika.dev> | ||
* | ||
* For the full copyright and license information, please view the LICENSE file | ||
* that was distributed with this source code. | ||
*/ | ||
|
||
namespace Rekalogika\Mapper\Debug; | ||
|
||
use Rekalogika\Mapper\Transformer\Contracts\TransformerInterface; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\HttpKernel\DataCollector\DataCollector; | ||
use Symfony\Component\PropertyInfo\Type; | ||
|
||
final class MapperDataCollector extends DataCollector | ||
{ | ||
public function getName() | ||
{ | ||
return 'rekalogika_mapper'; | ||
} | ||
|
||
public function collect( | ||
Request $request, | ||
Response $response, | ||
?\Throwable $exception = null | ||
) { | ||
} | ||
|
||
/** | ||
* @param class-string<TransformerInterface> $transformerClass | ||
*/ | ||
public function createTraceData( | ||
?string $path, | ||
mixed $source, | ||
mixed $target, | ||
?Type $sourceType, | ||
?Type $targetType, | ||
string $transformerClass | ||
): TraceData { | ||
$traceData = new TraceData( | ||
$path, | ||
$this->cloneVar($source), | ||
$this->cloneVar($target), | ||
$sourceType, | ||
$targetType, | ||
$transformerClass | ||
); | ||
|
||
return $traceData; | ||
} | ||
|
||
public function collectTraceData(TraceData $traceData): void | ||
{ | ||
/** @psalm-suppress MixedArrayAssignment */ | ||
$this->data['mappings'][] = $traceData; | ||
} | ||
|
||
/** | ||
* @return array<int,TraceData> | ||
*/ | ||
public function getMappings(): array | ||
{ | ||
/** @var array<int,TraceData> */ | ||
return $this->data['mappings']; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of rekalogika/mapper package. | ||
* | ||
* (c) Priyadi Iman Nurcahyo <https://rekalogika.dev> | ||
* | ||
* For the full copyright and license information, please view the LICENSE file | ||
* that was distributed with this source code. | ||
*/ | ||
|
||
namespace Rekalogika\Mapper\Debug; | ||
|
||
use Rekalogika\Mapper\Exception\LogicException; | ||
use Rekalogika\Mapper\Transformer\Contracts\TransformerInterface; | ||
use Symfony\Component\PropertyInfo\Type; | ||
use Symfony\Component\VarDumper\Cloner\Data; | ||
|
||
final class TraceData | ||
{ | ||
private ?float $time = null; | ||
|
||
/** @var array<int,self> */ | ||
private array $nestedTraceData = []; | ||
|
||
/** | ||
* @param class-string<TransformerInterface> $transformerClass | ||
*/ | ||
public function __construct( | ||
private ?string $path, | ||
private Data $source, | ||
private Data $target, | ||
private ?Type $sourceType, | ||
private ?Type $targetType, | ||
private string $transformerClass, | ||
) { | ||
} | ||
|
||
public function getSourceType(): ?Type | ||
{ | ||
return $this->sourceType; | ||
} | ||
|
||
public function getTargetType(): ?Type | ||
{ | ||
return $this->targetType; | ||
} | ||
|
||
public function finalizeTime(float $time): self | ||
{ | ||
if (count($this->nestedTraceData) === 0) { | ||
// If this is the last trace data (no nested trace data) | ||
$this->time = $time; | ||
} else { | ||
// If this is not the last trace data (has nested trace data), we | ||
// don't use the given time, but we calculate the time from the | ||
// nested trace data | ||
$this->time = array_sum(array_map(fn (self $traceData) => $traceData->getTime(), $this->nestedTraceData)); | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
public function getTime(): float | ||
{ | ||
if ($this->time === null) { | ||
throw new LogicException('Time is not set'); | ||
} | ||
|
||
return $this->time; | ||
} | ||
|
||
/** | ||
* @return class-string<TransformerInterface> | ||
*/ | ||
public function getTransformerClass(): string | ||
{ | ||
return $this->transformerClass; | ||
} | ||
|
||
/** | ||
* @return array<int,self> | ||
*/ | ||
public function getNestedTraceData(): array | ||
{ | ||
return $this->nestedTraceData; | ||
} | ||
|
||
public function addNestedTraceData(self $traceData): void | ||
{ | ||
$this->nestedTraceData[] = $traceData; | ||
} | ||
|
||
public function getSource(): Data | ||
{ | ||
return $this->source; | ||
} | ||
|
||
public function getTarget(): Data | ||
{ | ||
return $this->target; | ||
} | ||
|
||
public function getPath(): ?string | ||
{ | ||
return $this->path; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of rekalogika/mapper package. | ||
* | ||
* (c) Priyadi Iman Nurcahyo <https://rekalogika.dev> | ||
* | ||
* For the full copyright and license information, please view the LICENSE file | ||
* that was distributed with this source code. | ||
*/ | ||
|
||
namespace Rekalogika\Mapper\Debug; | ||
|
||
use Rekalogika\Mapper\Context\Context; | ||
use Rekalogika\Mapper\Context\ContextMemberNotFoundException; | ||
use Rekalogika\Mapper\MainTransformer\MainTransformerInterface; | ||
use Rekalogika\Mapper\MainTransformer\Model\Path; | ||
use Rekalogika\Mapper\Transformer\Contracts\MainTransformerAwareInterface; | ||
use Rekalogika\Mapper\Transformer\Contracts\MainTransformerAwareTrait; | ||
use Rekalogika\Mapper\Transformer\Contracts\TransformerInterface; | ||
use Symfony\Component\PropertyInfo\Type; | ||
use Symfony\Component\VarDumper\Cloner\Data; | ||
|
||
final class TraceableTransformer implements | ||
TransformerInterface, | ||
MainTransformerAwareInterface | ||
{ | ||
use MainTransformerAwareTrait; | ||
|
||
private bool $withMainTransformerCalled = false; | ||
|
||
public function __construct( | ||
private TransformerInterface $decorated, | ||
private MapperDataCollector $dataCollector | ||
) { | ||
} | ||
|
||
public function getDecorated(): TransformerInterface | ||
{ | ||
return $this->decorated; | ||
} | ||
|
||
public function withMainTransformer(MainTransformerInterface $mainTransformer): static | ||
{ | ||
if ($this->withMainTransformerCalled) { | ||
return $this; | ||
} | ||
|
||
$this->withMainTransformerCalled = true; | ||
|
||
if ($this->decorated instanceof MainTransformerAwareInterface) { | ||
$this->decorated = $this->decorated->withMainTransformer($mainTransformer); | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
public function transform( | ||
mixed $source, | ||
mixed $target, | ||
?Type $sourceType, | ||
?Type $targetType, | ||
Context $context | ||
): mixed { | ||
try { | ||
$path = $context(Path::class)->getLast(); | ||
} catch (ContextMemberNotFoundException) { | ||
$path = null; | ||
} | ||
|
||
$traceData = $this->dataCollector->createTraceData( | ||
$path, | ||
$source, | ||
$target, | ||
$sourceType, | ||
$targetType, | ||
$this->decorated::class | ||
); | ||
|
||
try { | ||
// add trace data to parent trace data | ||
$context(TraceData::class) | ||
->addNestedTraceData($traceData); | ||
} catch (ContextMemberNotFoundException) { | ||
// if we are the root transformer, add the trace data to the | ||
// context, and collect it | ||
$context = $context->with($traceData); | ||
$this->dataCollector->collectTraceData($traceData); | ||
} | ||
|
||
$start = microtime(true); | ||
/** @var mixed */ | ||
$result = $this->decorated->transform($source, $target, $sourceType, $targetType, $context); | ||
$time = microtime(true) - $start; | ||
|
||
$traceData->finalizeTime($time); | ||
|
||
return $result; | ||
} | ||
|
||
public function getSupportedTransformation(): iterable | ||
{ | ||
return $this->decorated->getSupportedTransformation(); | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
src/DependencyInjection/CompilerPass/DebugTransformerPass.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of rekalogika/mapper package. | ||
* | ||
* (c) Priyadi Iman Nurcahyo <https://rekalogika.dev> | ||
* | ||
* For the full copyright and license information, please view the LICENSE file | ||
* that was distributed with this source code. | ||
*/ | ||
|
||
namespace Rekalogika\Mapper\DependencyInjection\CompilerPass; | ||
|
||
use Rekalogika\Mapper\Debug\TraceableTransformer; | ||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\Reference; | ||
|
||
final class DebugTransformerPass implements CompilerPassInterface | ||
{ | ||
public function process(ContainerBuilder $container) | ||
{ | ||
$taggedServices = $container->findTaggedServiceIds('rekalogika.mapper.transformer'); | ||
$dataCollector = new Reference('rekalogika.mapper.data_collector'); | ||
|
||
foreach ($taggedServices as $serviceId => $tags) { | ||
$decoratedServiceId = 'debug.' . $serviceId; | ||
|
||
$service = $container->getDefinition($serviceId); | ||
/** @var array<string,mixed> */ | ||
$tagAttributes = $service->getTag('rekalogika.mapper.transformer')[0] ?? []; | ||
$service->clearTag('rekalogika.mapper.transformer'); | ||
|
||
$container->register($decoratedServiceId, TraceableTransformer::class) | ||
->setDecoratedService($serviceId) | ||
->addTag('rekalogika.mapper.transformer', $tagAttributes) | ||
->setArguments([ | ||
$service, | ||
$dataCollector, | ||
]); | ||
} | ||
} | ||
} |
Oops, something went wrong.