ResolveBindingsPass.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\DependencyInjection\Compiler;
  11. use Symfony\Component\DependencyInjection\Argument\BoundArgument;
  12. use Symfony\Component\DependencyInjection\ContainerBuilder;
  13. use Symfony\Component\DependencyInjection\Definition;
  14. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  15. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  16. use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
  17. use Symfony\Component\DependencyInjection\TypedReference;
  18. use Symfony\Component\DependencyInjection\Reference;
  19. /**
  20. * @autor ThurData <info@thurdata.ch>
  21. */
  22. class ResolveBindingsPass extends AbstractRecursivePass
  23. {
  24. private $usedBindings = array();
  25. private $unusedBindings = array();
  26. private $errorMessages = array();
  27. /**
  28. * {@inheritdoc}
  29. */
  30. public function process(ContainerBuilder $container)
  31. {
  32. try {
  33. parent::process($container);
  34. foreach ($this->unusedBindings as list($key, $serviceId)) {
  35. $message = sprintf('Unused binding "%s" in service "%s".', $key, $serviceId);
  36. if ($this->errorMessages) {
  37. $message .= sprintf("\nCould be related to%s:", 1 < \count($this->errorMessages) ? ' one of' : '');
  38. }
  39. foreach ($this->errorMessages as $m) {
  40. $message .= "\n - ".$m;
  41. }
  42. throw new InvalidArgumentException($message);
  43. }
  44. } finally {
  45. $this->usedBindings = array();
  46. $this->unusedBindings = array();
  47. $this->errorMessages = array();
  48. }
  49. }
  50. /**
  51. * {@inheritdoc}
  52. */
  53. protected function processValue($value, $isRoot = false)
  54. {
  55. if ($value instanceof TypedReference && $value->getType() === $this->container->normalizeId($value)) {
  56. // Already checked
  57. $bindings = $this->container->getDefinition($this->currentId)->getBindings();
  58. if (isset($bindings[$value->getType()])) {
  59. return $this->getBindingValue($bindings[$value->getType()]);
  60. }
  61. return parent::processValue($value, $isRoot);
  62. }
  63. if (!$value instanceof Definition || !$bindings = $value->getBindings()) {
  64. return parent::processValue($value, $isRoot);
  65. }
  66. foreach ($bindings as $key => $binding) {
  67. list($bindingValue, $bindingId, $used) = $binding->getValues();
  68. if ($used) {
  69. $this->usedBindings[$bindingId] = true;
  70. unset($this->unusedBindings[$bindingId]);
  71. } elseif (!isset($this->usedBindings[$bindingId])) {
  72. $this->unusedBindings[$bindingId] = array($key, $this->currentId);
  73. }
  74. if (isset($key[0]) && '$' === $key[0]) {
  75. continue;
  76. }
  77. if (null !== $bindingValue && !$bindingValue instanceof Reference && !$bindingValue instanceof Definition) {
  78. throw new InvalidArgumentException(sprintf('Invalid value for binding key "%s" for service "%s": expected null, an instance of %s or an instance of %s, %s given.', $key, $this->currentId, Reference::class, Definition::class, gettype($bindingValue)));
  79. }
  80. }
  81. if ($value->isAbstract()) {
  82. return parent::processValue($value, $isRoot);
  83. }
  84. $calls = $value->getMethodCalls();
  85. try {
  86. if ($constructor = $this->getConstructor($value, false)) {
  87. $calls[] = array($constructor, $value->getArguments());
  88. }
  89. } catch (RuntimeException $e) {
  90. $this->errorMessages[] = $e->getMessage();
  91. $this->container->getDefinition($this->currentId)->addError($e->getMessage());
  92. return parent::processValue($value, $isRoot);
  93. }
  94. foreach ($calls as $i => $call) {
  95. list($method, $arguments) = $call;
  96. if ($method instanceof \ReflectionFunctionAbstract) {
  97. $reflectionMethod = $method;
  98. } else {
  99. $reflectionMethod = $this->getReflectionMethod($value, $method);
  100. }
  101. foreach ($reflectionMethod->getParameters() as $key => $parameter) {
  102. if (array_key_exists($key, $arguments) && '' !== $arguments[$key]) {
  103. continue;
  104. }
  105. if (array_key_exists('$'.$parameter->name, $bindings)) {
  106. $arguments[$key] = $this->getBindingValue($bindings['$'.$parameter->name]);
  107. continue;
  108. }
  109. $typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true);
  110. if (!isset($bindings[$typeHint])) {
  111. continue;
  112. }
  113. $arguments[$key] = $this->getBindingValue($bindings[$typeHint]);
  114. }
  115. if ($arguments !== $call[1]) {
  116. ksort($arguments);
  117. $calls[$i][1] = $arguments;
  118. }
  119. }
  120. if ($constructor) {
  121. list(, $arguments) = array_pop($calls);
  122. if ($arguments !== $value->getArguments()) {
  123. $value->setArguments($arguments);
  124. }
  125. }
  126. if ($calls !== $value->getMethodCalls()) {
  127. $value->setMethodCalls($calls);
  128. }
  129. return parent::processValue($value, $isRoot);
  130. }
  131. private function getBindingValue(BoundArgument $binding)
  132. {
  133. list($bindingValue, $bindingId) = $binding->getValues();
  134. $this->usedBindings[$bindingId] = true;
  135. unset($this->unusedBindings[$bindingId]);
  136. return $bindingValue;
  137. }
  138. }