- Enhance logging handlers (Console, DockerJson, File, JsonFile, MultiFile) - Improve exception and line formatters - Update logger initialization and processor management - Add Ansible playbooks for staging 502 error troubleshooting - Update deployment documentation - Fix serializer and queue components - Update error kernel and queued log handler
135 lines
4.6 KiB
PHP
135 lines
4.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\DI;
|
|
|
|
use App\Framework\Core\ValueObjects\ClassName;
|
|
use App\Framework\Reflection\ReflectionProvider;
|
|
use App\Framework\Reflection\WrappedReflectionMethod;
|
|
|
|
/**
|
|
* Führt Methoden auf beliebigen Klassen mit automatischer Dependency Injection aus
|
|
*/
|
|
final readonly class MethodInvoker
|
|
{
|
|
private ParameterResolver $parameterResolver;
|
|
|
|
public function __construct(
|
|
private Container $container,
|
|
private ReflectionProvider $reflectionProvider
|
|
) {
|
|
$this->parameterResolver = new ParameterResolver($container, $reflectionProvider);
|
|
}
|
|
|
|
/**
|
|
* Führt eine Methode auf einer Klasse aus und löst alle Parameter automatisch auf
|
|
*/
|
|
public function invoke(ClassName|string $className, string $methodName, array $overrides = []): mixed
|
|
{
|
|
if(is_string($className)) {
|
|
$className = ClassName::create($className);
|
|
}
|
|
$instance = $this->container->get($className->getFullyQualified());
|
|
|
|
return $this->invokeOn($instance, $methodName, $overrides);
|
|
}
|
|
|
|
/**
|
|
* Führt eine Methode auf einer bereits existierenden Instanz aus
|
|
*/
|
|
public function invokeOn(object $instance, string $methodName, array $overrides = []): mixed
|
|
{
|
|
$className = ClassName::fromObject($instance);
|
|
$reflection = $this->reflectionProvider->getClass($className);
|
|
|
|
if (! $reflection->hasMethod($methodName)) {
|
|
throw new \InvalidArgumentException(
|
|
"Method $methodName does not exist on class " . $instance::class
|
|
);
|
|
}
|
|
|
|
// Framework WrappedReflectionMethod verwenden
|
|
$method = $this->reflectionProvider->getMethod($className, $methodName);
|
|
$nativeMethod = $this->reflectionProvider->getNativeMethod($className, $methodName);
|
|
|
|
if (! $nativeMethod->isPublic()) {
|
|
throw new \InvalidArgumentException(
|
|
"Method $methodName on class " . $instance::class . " is not public"
|
|
);
|
|
}
|
|
|
|
$parameters = $this->resolveMethodParameters($className, $methodName, $overrides);
|
|
|
|
return $nativeMethod->invokeArgs($instance, $parameters);
|
|
}
|
|
|
|
/**
|
|
* Führt eine statische Methode aus
|
|
*/
|
|
public function invokeStatic(string $className, string $methodName, array $overrides = []): mixed
|
|
{
|
|
$classNameObj = ClassName::create($className);
|
|
$reflection = $this->reflectionProvider->getClass($classNameObj);
|
|
|
|
if (! $reflection->hasMethod($methodName)) {
|
|
throw new \InvalidArgumentException(
|
|
"Method '{$methodName}' does not exist on class '{$className}'"
|
|
);
|
|
}
|
|
|
|
// Framework WrappedReflectionMethod verwenden
|
|
$method = $this->reflectionProvider->getMethod($classNameObj, $methodName);
|
|
$nativeMethod = $this->reflectionProvider->getNativeMethod($classNameObj, $methodName);
|
|
|
|
if (! $nativeMethod->isStatic()) {
|
|
throw new \InvalidArgumentException(
|
|
"Method '{$methodName}' on class '{$className}' is not static"
|
|
);
|
|
}
|
|
|
|
$parameters = $this->resolveMethodParameters($classNameObj, $methodName, $overrides);
|
|
|
|
return $nativeMethod->invokeArgs(null, $parameters);
|
|
}
|
|
|
|
/**
|
|
* Erstellt eine neue Instanz mit automatischer Dependency Injection
|
|
* und optionalen Parameter-Overrides für den Constructor
|
|
*
|
|
* @template T of object
|
|
* @param class-string<T> $className
|
|
* @param array<string, mixed> $overrides Named constructor parameters
|
|
* @return T
|
|
*/
|
|
public function make(string $className, array $overrides = []): object
|
|
{
|
|
$classNameObj = ClassName::create($className);
|
|
$reflection = $this->reflectionProvider->getClass($classNameObj);
|
|
|
|
if (! $reflection->isInstantiable()) {
|
|
throw new \InvalidArgumentException(
|
|
"Class '{$className}' is not instantiable (interface, abstract class, or trait)"
|
|
);
|
|
}
|
|
|
|
// Check if class has constructor
|
|
if (! $reflection->hasMethod('__construct')) {
|
|
return $reflection->newInstance();
|
|
}
|
|
|
|
// Resolve constructor parameters with overrides
|
|
$parameters = $this->resolveMethodParameters($classNameObj, '__construct', $overrides);
|
|
|
|
return $reflection->newInstance(...$parameters);
|
|
}
|
|
|
|
/**
|
|
* Löst alle Parameter einer Methode auf
|
|
*/
|
|
private function resolveMethodParameters(ClassName $className, string $methodName, array $overrides): array
|
|
{
|
|
return $this->parameterResolver->resolveMethodParameters($className, $methodName, $overrides);
|
|
}
|
|
}
|