Container.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  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\Pimple;
  12. use Hyperf\Contract\ContainerInterface;
  13. use Hyperf\Pimple\Exception\InvalidDefinitionException;
  14. use Hyperf\Pimple\Exception\NotFoundException;
  15. use Hyperf\Pimple\Exception\NotSupportException;
  16. use Pimple;
  17. use Psr\Container\ContainerInterface as PsrContainerInterface;
  18. use ReflectionClass;
  19. use ReflectionException;
  20. use ReflectionMethod;
  21. use ReflectionParameter;
  22. class Container implements ContainerInterface
  23. {
  24. /**
  25. * @var ReflectionClass[]
  26. */
  27. protected $reflection = [];
  28. public function __construct(protected Pimple\Container $pimple)
  29. {
  30. $this->pimple[ContainerInterface::class] = $this;
  31. $this->pimple[PsrContainerInterface::class] = $this;
  32. }
  33. public function get($id)
  34. {
  35. if ($this->has($id)) {
  36. return $this->pimple[$id];
  37. }
  38. return $this->pimple[$id] = $this->make($id);
  39. }
  40. public function has($id): bool
  41. {
  42. return isset($this->pimple[$id]);
  43. }
  44. public function make(string $name, array $parameters = [])
  45. {
  46. if (! class_exists($name)) {
  47. throw new NotFoundException("Entry {$name} is not found.");
  48. }
  49. $ref = $this->reflection[$name] ?? new ReflectionClass($name);
  50. $constructor = $ref->getConstructor();
  51. $args = [];
  52. if ($constructor && $constructor->isPublic()) {
  53. $args = $this->resolveParameters($constructor, $parameters);
  54. }
  55. $instance = new $name(...$args);
  56. $this->reflection[$name] = $ref;
  57. return $instance;
  58. }
  59. public function set(string $name, $entry): void
  60. {
  61. $this->pimple[$name] = $entry;
  62. }
  63. public function define(string $name, $definition): void
  64. {
  65. throw new NotSupportException('Method define is not support.');
  66. }
  67. public function unbind(string $name): void
  68. {
  69. $this->pimple[$name] = null;
  70. }
  71. protected function resolveParameters(ReflectionMethod $method, $parameters = [])
  72. {
  73. $args = [];
  74. foreach ($method->getParameters() as $index => $parameter) {
  75. if (array_key_exists($parameter->getName(), $parameters)) {
  76. $value = $parameters[$parameter->getName()];
  77. } elseif (array_key_exists($index, $parameters)) {
  78. $value = $parameters[$index];
  79. } elseif ($parameter->getType() && $this->has($parameter->getType()->getName())) {
  80. $value = $this->get($parameter->getType()->getName());
  81. } else {
  82. if ($parameter->isDefaultValueAvailable() || $parameter->isOptional()) {
  83. $value = $this->getParameterDefaultValue($parameter, $method);
  84. } else {
  85. throw new InvalidDefinitionException(sprintf(
  86. 'Parameter $%s of %s has no value defined or guessable',
  87. $parameter->getName(),
  88. $this->getFunctionName($method)
  89. ));
  90. }
  91. }
  92. $args[] = $value;
  93. }
  94. return $args;
  95. }
  96. protected function getParameterDefaultValue(ReflectionParameter $parameter, ReflectionMethod $function)
  97. {
  98. try {
  99. return $parameter->getDefaultValue();
  100. } catch (ReflectionException $e) {
  101. throw new InvalidDefinitionException(sprintf(
  102. 'The parameter "%s" of %s has no type defined or guessable. It has a default value, '
  103. . 'but the default value can\'t be read through Reflection because it is a PHP internal class.',
  104. $parameter->getName(),
  105. $this->getFunctionName($function)
  106. ));
  107. }
  108. }
  109. private function getFunctionName(ReflectionMethod $method): string
  110. {
  111. return $method->getName() . '()';
  112. }
  113. }