|
11 | 11 |
|
12 | 12 | namespace Symfony\Component\DependencyInjection\Dumper; |
13 | 13 |
|
| 14 | +use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument; |
14 | 15 | use Symfony\Component\DependencyInjection\Argument\IteratorArgument; |
15 | 16 | use Symfony\Component\DependencyInjection\Variable; |
16 | 17 | use Symfony\Component\DependencyInjection\Definition; |
@@ -62,6 +63,8 @@ class PhpDumper extends Dumper |
62 | 63 | private $docStar; |
63 | 64 | private $serviceIdToMethodNameMap; |
64 | 65 | private $usedMethodNames; |
| 66 | + private $classResources = array(); |
| 67 | + private $baseClass; |
65 | 68 |
|
66 | 69 | /** |
67 | 70 | * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface |
@@ -117,7 +120,9 @@ public function dump(array $options = array()) |
117 | 120 | 'debug' => true, |
118 | 121 | ), $options); |
119 | 122 |
|
| 123 | + $this->classResources = array(); |
120 | 124 | $this->initializeMethodNamesMap($options['base_class']); |
| 125 | + $this->baseClass = $options['base_class']; |
121 | 126 |
|
122 | 127 | $this->docStar = $options['debug'] ? '*' : ''; |
123 | 128 |
|
@@ -164,6 +169,11 @@ public function dump(array $options = array()) |
164 | 169 | ; |
165 | 170 | $this->targetDirRegex = null; |
166 | 171 |
|
| 172 | + foreach ($this->classResources as $r) { |
| 173 | + $this->container->addClassResource($r); |
| 174 | + } |
| 175 | + $this->classResources = array(); |
| 176 | + |
167 | 177 | $unusedEnvs = array(); |
168 | 178 | foreach ($this->container->getEnvCounters() as $env => $use) { |
169 | 179 | if (!$use) { |
@@ -1418,6 +1428,32 @@ private function dumpValue($value, $interpolate = true) |
1418 | 1428 | } |
1419 | 1429 |
|
1420 | 1430 | return sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)); |
| 1431 | + } elseif ($value instanceof ClosureProxyArgument) { |
| 1432 | + list($reference, $method) = $value->getValues(); |
| 1433 | + $method = substr($this->dumpLiteralClass($this->dumpValue($method)), 1); |
| 1434 | + |
| 1435 | + if ('service_container' === (string) $reference) { |
| 1436 | + $class = $this->baseClass; |
| 1437 | + } elseif (!$this->container->hasDefinition((string) $reference) && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) { |
| 1438 | + return 'null'; |
| 1439 | + } else { |
| 1440 | + $class = substr($this->dumpLiteralClass($this->dumpValue($this->container->findDefinition((string) $reference)->getClass())), 1); |
| 1441 | + } |
| 1442 | + if (false !== strpos($class, '$') || false !== strpos($method, '$')) { |
| 1443 | + throw new RuntimeException(sprintf('Cannot dump definition for service "%s": dynamic class names or methods, and closure-proxies are incompatible with each other.', $reference)); |
| 1444 | + } |
| 1445 | + if (!method_exists($class, $method)) { |
| 1446 | + throw new InvalidArgumentException(sprintf('Cannot create closure-proxy for service "%s": method "%s::%s" does not exist.', $reference, $class, $method)); |
| 1447 | + } |
| 1448 | + if (!isset($this->classResources[$class])) { |
| 1449 | + $this->classResources[$class] = new \ReflectionClass($class); |
| 1450 | + } |
| 1451 | + $r = $this->classResources[$class]->getMethod($method); |
| 1452 | + if (!$r->isPublic()) { |
| 1453 | + throw new InvalidArgumentException(sprintf('Cannot create closure-proxy for service "%s": method "%s::%s" must be public.', $reference, $class, $method)); |
| 1454 | + } |
| 1455 | + |
| 1456 | + return sprintf("/** @closure-proxy %s::%s */ function %s {\n return %s->%s;\n }", $class, $method, $this->generateSignature($r), $this->dumpValue($reference), $this->generateCall($r)); |
1421 | 1457 | } elseif ($value instanceof Variable) { |
1422 | 1458 | return '$'.$value; |
1423 | 1459 | } elseif ($value instanceof Reference) { |
@@ -1674,4 +1710,93 @@ private function doExport($value) |
1674 | 1710 |
|
1675 | 1711 | return $export; |
1676 | 1712 | } |
| 1713 | + |
| 1714 | + private function generateSignature(\ReflectionFunctionAbstract $r) |
| 1715 | + { |
| 1716 | + $signature = array(); |
| 1717 | + |
| 1718 | + foreach ($r->getParameters() as $p) { |
| 1719 | + $k = '$'.$p->name; |
| 1720 | + if (method_exists($p, 'isVariadic') && $p->isVariadic()) { |
| 1721 | + $k = '...'.$k; |
| 1722 | + } |
| 1723 | + if ($p->isPassedByReference()) { |
| 1724 | + $k = '&'.$k; |
| 1725 | + } |
| 1726 | + if (method_exists($p, 'getType')) { |
| 1727 | + $type = $p->getType(); |
| 1728 | + } elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $p, $type)) { |
| 1729 | + $type = $type[1]; |
| 1730 | + } |
| 1731 | + if ($type && $type = $this->generateTypeHint($type, $r)) { |
| 1732 | + $k = $type.' '.$k; |
| 1733 | + } |
| 1734 | + if ($type && $p->allowsNull()) { |
| 1735 | + $k = '?'.$k; |
| 1736 | + } |
| 1737 | + |
| 1738 | + try { |
| 1739 | + $k .= ' = '.$this->dumpValue($p->getDefaultValue(), false); |
| 1740 | + if ($type && $p->allowsNull() && null === $p->getDefaultValue()) { |
| 1741 | + $k = substr($k, 1); |
| 1742 | + } |
| 1743 | + } catch (\ReflectionException $e) { |
| 1744 | + if ($type && $p->allowsNull() && !class_exists('ReflectionNamedType', false)) { |
| 1745 | + $k .= ' = null'; |
| 1746 | + $k = substr($k, 1); |
| 1747 | + } |
| 1748 | + } |
| 1749 | + |
| 1750 | + $signature[] = $k; |
| 1751 | + } |
| 1752 | + |
| 1753 | + return ($r->returnsReference() ? '&(' : '(').implode(', ', $signature).')'; |
| 1754 | + } |
| 1755 | + |
| 1756 | + private function generateCall(\ReflectionFunctionAbstract $r) |
| 1757 | + { |
| 1758 | + $call = array(); |
| 1759 | + |
| 1760 | + foreach ($r->getParameters() as $p) { |
| 1761 | + $k = '$'.$p->name; |
| 1762 | + if (method_exists($p, 'isVariadic') && $p->isVariadic()) { |
| 1763 | + $k = '...'.$k; |
| 1764 | + } |
| 1765 | + |
| 1766 | + $call[] = $k; |
| 1767 | + } |
| 1768 | + |
| 1769 | + return ($r->isClosure() ? '' : $r->name).'('.implode(', ', $call).')'; |
| 1770 | + } |
| 1771 | + |
| 1772 | + private function generateTypeHint($type, \ReflectionFunctionAbstract $r) |
| 1773 | + { |
| 1774 | + if (is_string($type)) { |
| 1775 | + $name = $type; |
| 1776 | + |
| 1777 | + if ('callable' === $name || 'array' === $name) { |
| 1778 | + return $name; |
| 1779 | + } |
| 1780 | + } else { |
| 1781 | + $name = $type instanceof \ReflectionNamedType ? $type->getName() : $type->__toString(); |
| 1782 | + |
| 1783 | + if ($type->isBuiltin()) { |
| 1784 | + return $name; |
| 1785 | + } |
| 1786 | + } |
| 1787 | + $lcName = strtolower($name); |
| 1788 | + |
| 1789 | + if ('self' !== $lcName && 'parent' !== $lcName) { |
| 1790 | + return '\\'.$name; |
| 1791 | + } |
| 1792 | + if (!$r instanceof \ReflectionMethod) { |
| 1793 | + return; |
| 1794 | + } |
| 1795 | + if ('self' === $lcName) { |
| 1796 | + return '\\'.$r->getDeclaringClass()->name; |
| 1797 | + } |
| 1798 | + if ($parent = $r->getDeclaringClass()->getParentClass()) { |
| 1799 | + return '\\'.$parent->name; |
| 1800 | + } |
| 1801 | + } |
1677 | 1802 | } |
0 commit comments