Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 793cada

Browse files
[DI][EventDispatcher] Add & wire closure-proxy argument type
1 parent e98c068 commit 793cada

File tree

11 files changed

+214
-50
lines changed

11 files changed

+214
-50
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Argument;
13+
14+
use Symfony\Component\DependencyInjection\Reference;
15+
16+
/**
17+
* @author Nicolas Grekas <[email protected]>
18+
*/
19+
class ClosureProxyArgument
20+
{
21+
private $reference;
22+
private $method;
23+
24+
public function __construct(Reference $reference, $method)
25+
{
26+
$this->reference = $reference;
27+
$this->method = $method;
28+
}
29+
30+
/**
31+
* {@inheritdoc}
32+
*/
33+
public function getValues()
34+
{
35+
return array($this->reference, $this->method);
36+
}
37+
38+
/**
39+
* {@inheritdoc}
40+
*/
41+
public function setValues(array $values)
42+
{
43+
list($this->reference, $this->method) = $values;
44+
}
45+
}

src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Dumper;
1313

14+
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1415
use Symfony\Component\DependencyInjection\Variable;
1516
use Symfony\Component\DependencyInjection\Definition;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -61,6 +62,7 @@ class PhpDumper extends Dumper
6162
private $docStar;
6263
private $serviceIdToMethodNameMap;
6364
private $usedMethodNames;
65+
private $classResources = array();
6466

6567
/**
6668
* @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface
@@ -112,6 +114,7 @@ public function dump(array $options = array())
112114
'debug' => true,
113115
), $options);
114116

117+
$this->classResources = array();
115118
$this->initializeMethodNamesMap($options['base_class']);
116119

117120
$this->docStar = $options['debug'] ? '*' : '';
@@ -159,6 +162,11 @@ public function dump(array $options = array())
159162
;
160163
$this->targetDirRegex = null;
161164

165+
foreach ($this->classResources as $r) {
166+
$this->container->addClassResource($r);
167+
}
168+
$this->classResources = array();
169+
162170
$unusedEnvs = array();
163171
foreach ($this->container->getEnvCounters() as $env => $use) {
164172
if (!$use) {
@@ -1398,6 +1406,16 @@ private function dumpValue($value, $interpolate = true)
13981406
}
13991407

14001408
return sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments));
1409+
} elseif ($value instanceof ClosureProxyArgument) {
1410+
list($reference, $method) = $value->getValues();
1411+
1412+
$class = $this->container->findDefinition((string) $reference)->getClass();
1413+
if (!isset($this->classResources[$class])) {
1414+
$this->classResources[$class] = new \ReflectionClass($class);
1415+
}
1416+
$r = $this->classResources[$class]->getMethod($method);
1417+
1418+
return sprintf('/** @closure-proxy %s::%s */ function %s { return %s->%s; }', $class, $method, $this->generateSignature($r), $this->dumpValue($reference), $this->generateCall($r));
14011419
} elseif ($value instanceof Variable) {
14021420
return '$'.$value;
14031421
} elseif ($value instanceof Reference) {
@@ -1654,4 +1672,101 @@ private function doExport($value)
16541672

16551673
return $export;
16561674
}
1675+
1676+
private function generateSignature(\ReflectionFunctionAbstract $r)
1677+
{
1678+
$signature = array();
1679+
1680+
foreach ($r->getParameters() as $p) {
1681+
$k = '$'.$p->name;
1682+
if (method_exists($p, 'isVariadic') && $p->isVariadic()) {
1683+
$k = '...'.$k;
1684+
}
1685+
if ($p->isPassedByReference()) {
1686+
$k = '&'.$k;
1687+
}
1688+
if (method_exists($p, 'getType')) {
1689+
if ($type = $p->getType()) {
1690+
$k = $this->generateTypeHint($type, $r).' '.$k;
1691+
}
1692+
} elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $p, $type)) {
1693+
$k = $type[1].' '.$k;
1694+
}
1695+
if ($type && $p->allowsNull()) {
1696+
$k = '?'.$k;
1697+
}
1698+
1699+
try {
1700+
$k .= ' = '.$this->dumpValue($p->getDefaultValue(), false);
1701+
if ($type && $p->allowsNull() && null === $p->getDefaultValue()) {
1702+
$k = substr($k, 1);
1703+
}
1704+
} catch (\ReflectionException $e) {
1705+
if ($type && $p->allowsNull() && !class_exists('ReflectionNamedType', false)) {
1706+
$k .= ' = null';
1707+
$k = substr($k, 1);
1708+
}
1709+
}
1710+
1711+
$signature[] = $k;
1712+
}
1713+
1714+
$signature = ($r->returnsReference() ? '&(' : '(').implode(', ', $signature).')';
1715+
1716+
if (method_exists($r, 'getReturnType') && $type = $r->getReturnType()) {
1717+
$signature .= ': '.($type->allowsNull() ? '?' : '').$this->generateTypeHint($type, $r);
1718+
}
1719+
1720+
return $signature;
1721+
}
1722+
1723+
private function generateCall(\ReflectionFunctionAbstract $r)
1724+
{
1725+
$signature = array();
1726+
1727+
foreach ($r->getParameters() as $p) {
1728+
$k = '$'.$p->name;
1729+
if (method_exists($p, 'isVariadic') && $p->isVariadic()) {
1730+
$k = '...'.$k;
1731+
}
1732+
1733+
$signature[] = $k;
1734+
}
1735+
1736+
$signature = ($r->isClosure() ? '' : $r->name).'('.implode(', ', $signature).')';
1737+
1738+
return $signature;
1739+
}
1740+
1741+
private function generateTypeHint($type, \ReflectionFunctionAbstract $r)
1742+
{
1743+
$type = $type instanceof \ReflectionNamedType ? $type->getName() : $type->__toString();
1744+
1745+
switch (strtolower($type)) {
1746+
case 'parent':
1747+
if ($r instanceof \ReflectionMethod) {
1748+
$type = '\\'.$r->getDeclaringClass()->getParentClass()->name;
1749+
}
1750+
1751+
return $type;
1752+
1753+
case 'self':
1754+
if ($r instanceof \ReflectionMethod) {
1755+
$type = '\\'.$r->getDeclaringClass()->name;
1756+
}
1757+
1758+
return $type;
1759+
1760+
case 'array':
1761+
case 'bool':
1762+
case 'float':
1763+
case 'int':
1764+
case 'iterable':
1765+
case 'string':
1766+
case 'void':
1767+
return $type;
1768+
}
1769+
1770+
return '\\'.$type;
1771+
}
16571772
}

src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Dumper;
1313

14+
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1415
use Symfony\Component\DependencyInjection\ContainerInterface;
1516
use Symfony\Component\DependencyInjection\Parameter;
1617
use Symfony\Component\DependencyInjection\Reference;
@@ -283,6 +284,10 @@ private function convertParameters(array $parameters, $type, \DOMElement $parent
283284
if (is_array($value)) {
284285
$element->setAttribute('type', 'collection');
285286
$this->convertParameters($value, $type, $element, 'key');
287+
} elseif ($value instanceof ClosureProxyArgument) {
288+
list($reference, $method) = $value->getValues();
289+
$element->setAttribute('type', 'closure-proxy');
290+
$element->setAttribute('id', $reference.':'.$method);
286291
} elseif ($value instanceof Reference) {
287292
$element->setAttribute('type', 'service');
288293
$element->setAttribute('id', (string) $value);

src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\DependencyInjection\Dumper;
1313

1414
use Symfony\Component\Yaml\Dumper as YmlDumper;
15+
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1516
use Symfony\Component\DependencyInjection\Alias;
1617
use Symfony\Component\DependencyInjection\ContainerInterface;
1718
use Symfony\Component\DependencyInjection\Definition;
@@ -252,6 +253,10 @@ private function dumpValue($value)
252253
}
253254

254255
return $code;
256+
} elseif ($value instanceof ClosureProxyArgument) {
257+
list($reference, $method) = $value->getValues();
258+
259+
return array($this->getServiceCall((string) $reference, $reference), $method);
255260
} elseif ($value instanceof Reference) {
256261
return $this->getServiceCall((string) $value, $value);
257262
} elseif ($value instanceof Parameter) {

src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Config\Util\XmlUtils;
1616
use Symfony\Component\DependencyInjection\ContainerInterface;
1717
use Symfony\Component\DependencyInjection\Alias;
18+
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1819
use Symfony\Component\DependencyInjection\Definition;
1920
use Symfony\Component\DependencyInjection\ChildDefinition;
2021
use Symfony\Component\DependencyInjection\Reference;
@@ -398,6 +399,9 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true)
398399
case 'expression':
399400
$arguments[$key] = new Expression($arg->nodeValue);
400401
break;
402+
case 'closure-proxy':
403+
$arguments[$key] = new ClosureProxyArgument(new Reference($arg->getAttribute('id')), $arg->getAttribute('method'));
404+
break;
401405
case 'collection':
402406
$arguments[$key] = $this->getArgumentsAsPhp($arg, $name, false);
403407
break;

src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@
164164
<xsd:attribute name="index" type="xsd:integer" />
165165
<xsd:attribute name="on-invalid" type="xsd:string" />
166166
<xsd:attribute name="strict" type="boolean" />
167+
<xsd:attribute name="method" type="xsd:string" />
167168
</xsd:complexType>
168169

169170
<xsd:complexType name="call" mixed="true">
@@ -189,6 +190,7 @@
189190
<xsd:enumeration value="expression" />
190191
<xsd:enumeration value="string" />
191192
<xsd:enumeration value="constant" />
193+
<xsd:enumeration value="closure-proxy" />
192194
</xsd:restriction>
193195
</xsd:simpleType>
194196

src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,13 @@ public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatc
4646
$this->name = is_object($listener[0]) ? get_class($listener[0]) : $listener[0];
4747
$this->pretty = $this->name.'::'.$listener[1];
4848
} elseif ($listener instanceof \Closure) {
49-
$this->pretty = $this->name = 'closure';
49+
$r = new \ReflectionFunction($listener);
50+
if (preg_match('#^/\*\* @closure-proxy ([^: ]++)::([^: ]++) \*/$#', $r->getDocComment(), $m)) {
51+
$this->name = $m[1];
52+
$this->pretty = $m[1].'::'.$m[2];
53+
} else {
54+
$this->pretty = $this->name = 'closure';
55+
}
5056
} elseif (is_string($listener)) {
5157
$this->pretty = $this->name = $listener;
5258
} else {

src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111

1212
namespace Symfony\Component\EventDispatcher\DependencyInjection;
1313

14+
use Symfony\Component\DependencyInjection\Argument\ClosureProxyArgument;
1415
use Symfony\Component\DependencyInjection\ContainerBuilder;
1516
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1617
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
18+
use Symfony\Component\DependencyInjection\Reference;
1719

1820
/**
1921
* Compiler pass to register tagged services for an event dispatcher.
@@ -59,10 +61,6 @@ public function process(ContainerBuilder $container)
5961

6062
foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) {
6163
$def = $container->getDefinition($id);
62-
if (!$def->isPublic()) {
63-
throw new InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id));
64-
}
65-
6664
if ($def->isAbstract()) {
6765
throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id));
6866
}
@@ -82,16 +80,12 @@ public function process(ContainerBuilder $container)
8280
$event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
8381
}
8482

85-
$definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority));
83+
$definition->addMethodCall('addListener', array($event['event'], new ClosureProxyArgument(new Reference($id), $event['method']), $priority));
8684
}
8785
}
8886

8987
foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) {
9088
$def = $container->getDefinition($id);
91-
if (!$def->isPublic()) {
92-
throw new InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id));
93-
}
94-
9589
if ($def->isAbstract()) {
9690
throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as event subscribers are lazy-loaded.', $id));
9791
}
@@ -108,7 +102,17 @@ public function process(ContainerBuilder $container)
108102
throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
109103
}
110104

111-
$definition->addMethodCall('addSubscriberService', array($id, $class));
105+
foreach ($class::getSubscribedEvents() as $eventName => $params) {
106+
if (is_string($params)) {
107+
$definition->addMethodCall('addListener', array($eventName, new ClosureProxyArgument(new Reference($id), $params)));
108+
} elseif (is_string($params[0])) {
109+
$definition->addMethodCall('addListener', array($eventName, new ClosureProxyArgument(new Reference($id), $params[0]), isset($params[1]) ? $params[1] : 0));
110+
} else {
111+
foreach ($params as $listener) {
112+
$definition->addMethodCall('addListener', array($eventName, new ClosureProxyArgument(new Reference($id), $listener[0]), isset($listener[1]) ? $listener[1] : 0));
113+
}
114+
}
115+
}
112116
}
113117
}
114118
}

0 commit comments

Comments
 (0)