Skip to content

Instantly share code, notes, and snippets.

@angelov
Created May 7, 2022 16:19
Show Gist options
  • Save angelov/f5cd2eb4ea21ed65fc82caa25c55ba8d to your computer and use it in GitHub Desktop.
Save angelov/f5cd2eb4ea21ed65fc82caa25c55ba8d to your computer and use it in GitHub Desktop.
part 3
<?php
declare(strict_types=1);
namespace App;
use Exception;
use ReflectionFunction;
use ReflectionParameter;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Handler\HandlerDescriptor;
use Symfony\Component\Messenger\Handler\HandlersLocatorInterface;
class WrappingHandlersLocator implements HandlersLocatorInterface
{
private HandlersLocatorInterface $decorated;
public function __construct(HandlersLocatorInterface $decorated)
{
$this->decorated = $decorated;
}
public function getHandlers(Envelope $envelope): iterable
{
/** @var iterable<int, HandlerDescriptor> $handlerDescriptors */
$handlerDescriptors = $this->decorated->getHandlers($envelope);
$message = $envelope->getMessage();
foreach ($handlerDescriptors as $handlerDescriptor) {
if ($this->shouldWrapHandler($handlerDescriptor, $message)) {
yield $this->wrapHandler($handlerDescriptor, $message);
} else {
yield $handlerDescriptor;
}
}
}
private function shouldWrapHandler(HandlerDescriptor $handlerDescriptor, object $message): bool
{
$arguments = $this->getHandlerArguments($handlerDescriptor);
return (count($arguments) == 0)
|| (count($arguments) > 1)
|| (!$arguments[0]->getType()->getName() == get_class($message));
}
/** @return array<int, ReflectionParameter> */
private function getHandlerArguments(HandlerDescriptor $handlerDescriptor): array
{
$function = new ReflectionFunction($handlerDescriptor->getHandler());
return $function->getParameters();
}
private function wrapHandler(HandlerDescriptor $handlerDescriptor, object $message): HandlerDescriptor
{
$handlerArguments = $this->getHandlerArguments($handlerDescriptor);
$handlerArgumentValues = $this->resolveValuesForHandlerArguments($handlerArguments, $message);
return new HandlerDescriptor(
fn() => $handlerDescriptor->getHandler()(...$handlerArgumentValues),
[
'alias' => $this->generateHandlerAlias($handlerDescriptor)
]
);
}
private function generateHandlerAlias(HandlerDescriptor $handlerDescriptor): string
{
$function = new ReflectionFunction($handlerDescriptor->getHandler());
return sprintf(
"%s::%s",
$function->getClosureScopeClass()?->getShortName(),
$function->getName()
);
}
/**
* @param array<int, ReflectionParameter> $arguments
* @return array<int, mixed>
*/
private function resolveValuesForHandlerArguments(array $arguments, object $message): array
{
return array_map(
fn(ReflectionParameter $argument) => $this->resolveValueForHandlerArgument($argument, $message),
$arguments
);
}
private function resolveValueForHandlerArgument(
ReflectionParameter $argument,
object $message
): mixed {
$attribute = $this->getAttributeForArgument($argument);
$messagePropertyName = $attribute?->messageProperty ?? $argument->getName();
if (property_exists($message, $messagePropertyName)) {
return $message->{$messagePropertyName};
}
if ($argument->isDefaultValueAvailable()) {
return $argument->getDefaultValue();
}
if ($attribute?->defaultValue) {
return $attribute->defaultValue;
}
throw new Exception(sprintf(
"Missing handler argument mapping or default value for the \"%s\" argument of \"%s::%s\".",
$argument->getName(),
$argument->getDeclaringClass()->getName(),
$argument->getDeclaringFunction()->getName()
));
}
private function getAttributeForArgument(ReflectionParameter $argument): ?ExtractedValue
{
$attributes = $argument->getAttributes(ExtractedValue::class);
if (!count($attributes)) {
return null;
}
return $attributes[0]->newInstance();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment