Files
michaelschiemer/src/Framework/Reflection/CachedReflectionProvider.php

175 lines
5.8 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Reflection;
use ReflectionClass;
use ReflectionMethod;
use ReflectionParameter;
use ReflectionProperty;
use ReflectionException;
final class CachedReflectionProvider implements ReflectionProvider
{
private array $classCache = [];
private array $methodCache = [];
private array $parameterCache = [];
private array $propertyCache = [];
private array $attributeCache = [];
private array $parameterInfoCache = [];
private array $instantiableCache = [];
private array $interfaceCache = [];
private array $wrappedClassCache = [];
private array $wrappedMethodCache = [];
public function getWrappedClass(string $className): WrappedReflectionClass
{
return $this->wrappedClassCache[$className] ??= new WrappedReflectionClass(
$this->getClass($className)
);
}
public function getWrappedMethod(string $className, string $methodName): WrappedReflectionMethod
{
$key = "{$className}::{$methodName}";
return $this->wrappedMethodCache[$key] ??= new WrappedReflectionMethod(
$this->getMethod($className, $methodName)
);
}
public function getClass(string $className): ReflectionClass
{
return $this->classCache[$className] ??= new ReflectionClass($className);
}
public function getMethod(string $className, string $methodName): ReflectionMethod
{
$key = "{$className}::{$methodName}";
return $this->methodCache[$key] ??= $this->getClass($className)->getMethod($methodName);
}
public function getMethodParameters(string $className, string $methodName): array
{
$key = "{$className}::{$methodName}::params";
return $this->parameterCache[$key] ??= $this->getMethod($className, $methodName)->getParameters();
}
public function getProperties(string $className): array
{
$key = "{$className}::properties";
return $this->propertyCache[$key] ??= $this->getClass($className)->getProperties();
}
public function getAttributes(string $className, ?string $attributeClass = null): array
{
$key = "{$className}::attributes::" . ($attributeClass ?? 'all');
return $this->attributeCache[$key] ??= $attributeClass
? $this->getClass($className)->getAttributes($attributeClass)
: $this->getClass($className)->getAttributes();
}
public function getMethodAttributes(string $className, string $methodName, ?string $attributeClass = null): array
{
$key = "{$className}::{$methodName}::attributes::" . ($attributeClass ?? 'all');
return $this->attributeCache[$key] ??= $attributeClass
? $this->getMethod($className, $methodName)->getAttributes($attributeClass)
: $this->getMethod($className, $methodName)->getAttributes();
}
public function hasAttribute(string $className, string $attributeClass): bool
{
return !empty($this->getAttributes($className, $attributeClass));
}
public function getParameterInfo(string $className, string $methodName): array
{
$key = "{$className}::{$methodName}::paramInfo";
return $this->parameterInfoCache[$key] ??= $this->buildParameterInfo($className, $methodName);
}
public function isInstantiable(string $className): bool
{
return $this->instantiableCache[$className] ??= $this->getClass($className)->isInstantiable();
}
public function implementsInterface(string $className, string $interfaceName): bool
{
$key = "{$className}::implements::{$interfaceName}";
return $this->interfaceCache[$key] ??= $this->getClass($className)->implementsInterface($interfaceName);
}
private function buildParameterInfo(string $className, string $methodName): array
{
$parameters = $this->getMethodParameters($className, $methodName);
$paramInfo = [];
foreach ($parameters as $param) {
$type = $param->getType();
$paramInfo[] = [
'name' => $param->getName(),
'type' => $type ? $type->getName() : null,
'isBuiltin' => $type ? $type->isBuiltin() : true,
'allowsNull' => $param->allowsNull(),
'isOptional' => $param->isOptional(),
'hasDefaultValue' => $param->isDefaultValueAvailable(),
'default' => $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null,
'position' => $param->getPosition(),
];
}
return $paramInfo;
}
public function forget(string $className): void
{
unset($this->classCache[$className]);
unset($this->instantiableCache[$className]);
unset($this->wrappedClassCache[$className]);
// Alle zugehörigen Caches leeren
$this->clearRelatedCaches($className);
}
public function flush(): void
{
$this->classCache = [];
$this->methodCache = [];
$this->parameterCache = [];
$this->propertyCache = [];
$this->attributeCache = [];
$this->parameterInfoCache = [];
$this->instantiableCache = [];
$this->interfaceCache = [];
$this->wrappedClassCache = [];
$this->wrappedMethodCache = [];
}
private function clearRelatedCaches(string $className): void
{
$prefix = $className . '::';
$caches = [
&$this->methodCache,
&$this->parameterCache,
&$this->propertyCache,
&$this->attributeCache,
&$this->parameterInfoCache,
&$this->interfaceCache,
&$this->wrappedMethodCache
];
foreach ($caches as &$cache) {
$cache = array_filter(
$cache,
fn($key) => !str_starts_with($key, $prefix),
ARRAY_FILTER_USE_KEY
);
}
}
}