DefinitionSource.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of Hyperf.
  5. *
  6. * @link https://www.hyperf.io
  7. * @document https://hyperf.wiki
  8. * @contact group@hyperf.io
  9. * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10. */
  11. namespace Hyperf\Di\Definition;
  12. use Hyperf\Di\ReflectionManager;
  13. use ReflectionFunctionAbstract;
  14. use ReflectionNamedType;
  15. use function class_exists;
  16. use function interface_exists;
  17. use function is_callable;
  18. use function is_string;
  19. use function method_exists;
  20. class DefinitionSource implements DefinitionSourceInterface
  21. {
  22. protected array $source;
  23. public function __construct(array $source)
  24. {
  25. $this->source = $this->normalizeSource($source);
  26. }
  27. /**
  28. * Returns the DI definition for the entry name.
  29. */
  30. public function getDefinition(string $name): ?DefinitionInterface
  31. {
  32. return $this->source[$name] ??= $this->autowire($name);
  33. }
  34. /**
  35. * @return array definitions indexed by their name
  36. */
  37. public function getDefinitions(): array
  38. {
  39. return $this->source;
  40. }
  41. /**
  42. * @param array|callable|string $definition
  43. */
  44. public function addDefinition(string $name, $definition): static
  45. {
  46. $this->source[$name] = $this->normalizeDefinition($name, $definition);
  47. return $this;
  48. }
  49. public function clearDefinitions(): void
  50. {
  51. $this->source = [];
  52. }
  53. /**
  54. * Read the type-hinting from the parameters of the function.
  55. */
  56. protected function getParametersDefinition(ReflectionFunctionAbstract $constructor): array
  57. {
  58. $parameters = [];
  59. foreach ($constructor->getParameters() as $index => $parameter) {
  60. // Skip optional parameters.
  61. if ($parameter->isOptional()) {
  62. continue;
  63. }
  64. $parameterType = $parameter->getType();
  65. if ($parameterType instanceof ReflectionNamedType && ! $parameterType->isBuiltin()) {
  66. $parameters[$index] = new Reference($parameterType->getName());
  67. }
  68. }
  69. return $parameters;
  70. }
  71. /**
  72. * Normalize the user definition source to a standard definition source.
  73. */
  74. protected function normalizeSource(array $source): array
  75. {
  76. $definitions = [];
  77. foreach ($source as $identifier => $definition) {
  78. $normalizedDefinition = $this->normalizeDefinition($identifier, $definition);
  79. if (! is_null($normalizedDefinition)) {
  80. $definitions[$identifier] = $normalizedDefinition;
  81. }
  82. }
  83. return $definitions;
  84. }
  85. /**
  86. * @param array|callable|string $definition
  87. */
  88. protected function normalizeDefinition(string $identifier, $definition): ?DefinitionInterface
  89. {
  90. if ($definition instanceof PriorityDefinition) {
  91. $definition = $definition->getDefinition();
  92. }
  93. if (is_string($definition) && class_exists($definition)) {
  94. if (method_exists($definition, '__invoke')) {
  95. return new FactoryDefinition($identifier, $definition, []);
  96. }
  97. return $this->autowire($identifier, new ObjectDefinition($identifier, $definition));
  98. }
  99. if (is_callable($definition)) {
  100. return new FactoryDefinition($identifier, $definition, []);
  101. }
  102. return null;
  103. }
  104. protected function autowire(string $name, ?ObjectDefinition $definition = null): ?ObjectDefinition
  105. {
  106. $className = $definition ? $definition->getClassName() : $name;
  107. if (! class_exists($className) && ! interface_exists($className)) {
  108. return $definition;
  109. }
  110. $definition = $definition ?: new ObjectDefinition($name);
  111. /**
  112. * Constructor.
  113. */
  114. $class = ReflectionManager::reflectClass($className);
  115. $constructor = $class->getConstructor();
  116. if ($constructor && $constructor->isPublic()) {
  117. $constructorInjection = new MethodInjection('__construct', $this->getParametersDefinition($constructor));
  118. $definition->completeConstructorInjection($constructorInjection);
  119. }
  120. return $definition;
  121. }
  122. }