edmondscommerce /
doctrine-static-meta
| 1 | <?php declare(strict_types=1); |
||
| 2 | |||
| 3 | namespace EdmondsCommerce\DoctrineStaticMeta\Entity\Factory; |
||
| 4 | |||
| 5 | use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\EntityInterface; |
||
| 6 | use Psr\Container\ContainerInterface; |
||
| 7 | use ReflectionMethod; |
||
| 8 | |||
| 9 | class EntityDependencyInjector |
||
| 10 | { |
||
| 11 | public const INJECT_DEPENDENCY_METHOD_PREFIX = 'inject'; |
||
| 12 | |||
| 13 | private const TYPE_KEY_STATIC = 'static'; |
||
| 14 | private const TYPE_KEY_INSTANCE = 'instance'; |
||
| 15 | /** |
||
| 16 | * @var ContainerInterface |
||
| 17 | */ |
||
| 18 | protected $container; |
||
| 19 | |||
| 20 | |||
| 21 | /** |
||
| 22 | * This array is keyed by Entity FQN and the values are the inject*** method names that are used for injecting |
||
| 23 | * dependencies |
||
| 24 | * |
||
| 25 | * @var array|ReflectionMethod |
||
| 26 | */ |
||
| 27 | private $entityInjectMethods = []; |
||
| 28 | |||
| 29 | public function __construct(ContainerInterface $container) |
||
| 30 | { |
||
| 31 | $this->container = $container; |
||
| 32 | } |
||
| 33 | |||
| 34 | /** |
||
| 35 | * This method loops over the inject methods for an Entity and then injects the relevant dependencies |
||
| 36 | * |
||
| 37 | * We match the method argument type with the dependency to be injected. |
||
| 38 | * |
||
| 39 | * @param EntityInterface $entity |
||
| 40 | */ |
||
| 41 | public function injectEntityDependencies(EntityInterface $entity): void |
||
| 42 | { |
||
| 43 | $this->buildEntityInjectMethodsForEntity($entity); |
||
| 44 | $entityFqn = $this->leadingSlash($entity::getDoctrineStaticMeta()->getReflectionClass()->getName()); |
||
| 45 | $this->injectStatic($entity, $this->entityInjectMethods[$entityFqn][self::TYPE_KEY_STATIC]); |
||
| 46 | $this->inject($entity, $this->entityInjectMethods[$entityFqn][self::TYPE_KEY_INSTANCE]); |
||
| 47 | } |
||
| 48 | |||
| 49 | /** |
||
| 50 | * Build the array of entity methods to dependencies ready to be used for injection |
||
| 51 | * |
||
| 52 | * @param EntityInterface $entity |
||
| 53 | */ |
||
| 54 | private function buildEntityInjectMethodsForEntity(EntityInterface $entity): void |
||
| 55 | { |
||
| 56 | $reflection = $entity::getDoctrineStaticMeta()->getReflectionClass(); |
||
| 57 | $entityFqn = $this->leadingSlash($reflection->getName()); |
||
| 58 | if (array_key_exists($entityFqn, $this->entityInjectMethods)) { |
||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
| 59 | return; |
||
| 60 | } |
||
| 61 | $this->entityInjectMethods[$entityFqn] = [ |
||
| 62 | self::TYPE_KEY_INSTANCE => [], |
||
| 63 | self::TYPE_KEY_STATIC => [], |
||
| 64 | ]; |
||
| 65 | $methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC); |
||
| 66 | foreach ($methods as $method) { |
||
| 67 | if (!\ts\stringStartsWith($method->getName(), self::INJECT_DEPENDENCY_METHOD_PREFIX)) { |
||
| 68 | continue; |
||
| 69 | } |
||
| 70 | $typeKey = $method->isStatic() ? self::TYPE_KEY_STATIC : self::TYPE_KEY_INSTANCE; |
||
| 71 | |||
| 72 | $this->entityInjectMethods[$entityFqn][$typeKey][$method->getName()] = |
||
| 73 | $this->getDependencyForInjectMethod($method); |
||
| 74 | } |
||
| 75 | } |
||
| 76 | |||
| 77 | private function leadingSlash(string $fqn): string |
||
| 78 | { |
||
| 79 | return '\\' . ltrim($fqn, '\\'); |
||
| 80 | } |
||
| 81 | |||
| 82 | private function getDependencyForInjectMethod(ReflectionMethod $method): string |
||
| 83 | { |
||
| 84 | $params = $method->getParameters(); |
||
| 85 | if (1 !== count($params)) { |
||
| 86 | throw new \RuntimeException( |
||
| 87 | 'Invalid method signature for ' . |
||
| 88 | $method->getName() . |
||
| 89 | ', should only take one argument which is the dependency to be injected' |
||
| 90 | ); |
||
| 91 | } |
||
| 92 | $type = current($params)->getType(); |
||
| 93 | if (null === $type) { |
||
| 94 | throw new \RuntimeException( |
||
| 95 | 'Invalid method signature for ' . |
||
| 96 | $method->getName() . |
||
| 97 | ', the object being set must be type hinted' |
||
| 98 | ); |
||
| 99 | } |
||
| 100 | |||
| 101 | return $type->getName(); |
||
| 102 | } |
||
| 103 | |||
| 104 | private function injectStatic(EntityInterface $entity, array $methods) |
||
| 105 | { |
||
| 106 | foreach ($methods as $methodName => $dependencyFqn) { |
||
| 107 | $entity::$methodName($this->container->get($dependencyFqn)); |
||
| 108 | } |
||
| 109 | } |
||
| 110 | |||
| 111 | private function inject(EntityInterface $entity, array $methods) |
||
| 112 | { |
||
| 113 | foreach ($methods as $methodName => $dependencyFqn) { |
||
| 114 | $entity->$methodName($this->container->get($dependencyFqn)); |
||
| 115 | } |
||
| 116 | } |
||
| 117 | } |
||
| 118 |