Skip to content

Commit

Permalink
feat: process proxies of all classes in the inheritance chain (#222)
Browse files Browse the repository at this point in the history
  • Loading branch information
priyadi authored Oct 11, 2024
1 parent b959fa7 commit 2924e87
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* refactor: spin off `resolveTargetClass()` to separate class
* perf: proxy warming
* feat: process proxies of all classes in the inheritance chain

## 1.10.0

Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ psalm:
clean:
rm -rf tests/var

.PHONY: warmup
warmup:
$(PHP) tests/bin/console cache:warmup --env=test

.PHONY: phpunit
phpunit: clean
phpunit: clean warmup
$(eval c ?=)
$(PHP) vendor/bin/phpunit $(c)

Expand Down
5 changes: 5 additions & 0 deletions src/Proxy/Implementation/ProxyFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ public function warmingCreateProxy(string $class): void
$sourceCode = $this->proxyGenerator
->generateProxyCode($class, $targetProxyClass);

if (!class_exists($targetProxyClass, false)) {
// @phpstan-ignore ekinoBannedCode.expression
eval($sourceCode);
}

$proxyRegistry = $this->proxyRegistry;

if (!$proxyRegistry instanceof WarmableProxyRegistryInterface) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,32 @@ final class CachingTargetClassResolver implements TargetClassResolverInterface
/**
* @var array<class-string,array<class-string,class-string>>
*/
private array $cache = [];
private array $resolveTargetClassCache = [];

/**
* @var array<class-string,array<class-string,list<class-string>>>
*/
private array $getAllConcreteTargetClassesCache = [];

public function __construct(
private readonly TargetClassResolverInterface $decorated,
) {}

#[\Override]
public function resolveTargetClass(string $sourceClass, string $targetClass): string
{
return $this->cache[$sourceClass][$targetClass]
public function resolveTargetClass(
string $sourceClass,
string $targetClass,
): string {
return $this->resolveTargetClassCache[$sourceClass][$targetClass]
??= $this->decorated->resolveTargetClass($sourceClass, $targetClass);
}

#[\Override]
public function getAllConcreteTargetClasses(
string $sourceClass,
string $targetClass,
): array {
return $this->getAllConcreteTargetClassesCache[$sourceClass][$targetClass]
??= $this->decorated->getAllConcreteTargetClasses($sourceClass, $targetClass);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/
final readonly class TargetClassResolver implements TargetClassResolverInterface
{
#[\Override]
public function resolveTargetClass(
string $sourceClass,
string $targetClass,
Expand Down Expand Up @@ -72,4 +73,32 @@ public function resolveTargetClass(

return $targetClass;
}

#[\Override]
public function getAllConcreteTargetClasses(
string $sourceClass,
string $targetClass,
): array {
$sourceReflection = new \ReflectionClass($sourceClass);
$targetReflection = new \ReflectionClass($targetClass);

$targetAttributes = $targetReflection->getAttributes(InheritanceMap::class);

if ($targetAttributes !== []) {
return array_values(array_unique($targetAttributes[0]->newInstance()->getMap()));
} elseif ($targetReflection->isAbstract() || $targetReflection->isInterface()) {
$sourceClasses = ClassUtil::getAllClassesFromObject($sourceClass);

foreach ($sourceClasses as $currentSourceClass) {
$sourceReflection = new \ReflectionClass($currentSourceClass);
$sourceAttributes = $sourceReflection->getAttributes(InheritanceMap::class);

if ($sourceAttributes !== []) {
return array_keys($sourceAttributes[0]->newInstance()->getMap());
}
}
}

return [$targetClass];
}
}
10 changes: 10 additions & 0 deletions src/Transformer/MetadataUtil/TargetClassResolverInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,14 @@ public function resolveTargetClass(
string $sourceClass,
string $targetClass,
): string;

/**
* @param class-string $sourceClass
* @param class-string $targetClass
* @return list<class-string>
*/
public function getAllConcreteTargetClasses(
string $sourceClass,
string $targetClass,
): array;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Rekalogika\Mapper\CacheWarmer\WarmableObjectToObjectMetadataFactoryInterface;
use Rekalogika\Mapper\CacheWarmer\WarmableProxyFactoryInterface;
use Rekalogika\Mapper\Exception\RuntimeException;
use Rekalogika\Mapper\Transformer\MetadataUtil\TargetClassResolver\TargetClassResolver;
use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\ObjectToObjectMetadata;
use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\ObjectToObjectMetadataFactoryInterface;
use Rekalogika\Mapper\Util\ClassUtil;
Expand Down Expand Up @@ -139,14 +140,15 @@ public function warmingCreateObjectToObjectMetadata(
string $sourceClass,
string $targetClass,
): ObjectToObjectMetadata {
$result = $this->decorated->createObjectToObjectMetadata($sourceClass, $targetClass);

if (!$this->cacheItemPool instanceof WarmableCacheInterface) {
return $result;
return $this->decorated
->createObjectToObjectMetadata($sourceClass, $targetClass);
}

// warm proxy
$this->proxyFactory->warmingCreateProxy($result->getTargetClass());
$this->warmProxy($sourceClass, $targetClass);

$result = $this->decorated
->createObjectToObjectMetadata($sourceClass, $targetClass);

// warm cache
$cacheKey = hash('xxh128', $sourceClass . $targetClass);
Expand All @@ -157,4 +159,24 @@ public function warmingCreateObjectToObjectMetadata(

return $result;
}

/**
* @param class-string $sourceClass
* @param class-string $targetClass
*/
private function warmProxy(string $sourceClass, string $targetClass): void
{
$targetClassResolver = new TargetClassResolver();

$allConcreteTargetClasses = $targetClassResolver
->getAllConcreteTargetClasses($sourceClass, $targetClass);

foreach ($allConcreteTargetClasses as $concreteTargetClass) {
$this->proxyFactory->warmingCreateProxy($concreteTargetClass);
}

$this->proxyFactory->warmingCreateProxy($targetClass);

clearstatcache();
}
}

0 comments on commit 2924e87

Please sign in to comment.