8000 bug #29054 [VarDumper] fix dump of closures created from callables (n… · symfony/symfony@41eaba5 · GitHub
[go: up one dir, main page]

Skip to content

Commit 41eaba5

Browse files
bug #29054 [VarDumper] fix dump of closures created from callables (nicolas-grekas)
This PR was merged into the 3.4 branch. Discussion ---------- [VarDumper] fix dump of closures created from callables | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - We are missing displaying full information about closures created using `ReflectionMethod::getClosure()` or `Closure::fromCallable()`. This PR fixes it. For VarDumper but also other places where we have logic to display them. Commits ------- 1c1818b [VarDumper] fix dump of closures created from callables
2 parents 85df96a + 1c1818b commit 41eaba5

File tree

11 files changed

+124
-13
lines changed

11 files changed

+124
-13
lines changed

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,20 @@ private function getCallableData($callable, array $options = array())
368368
if ($callable instanceof \Closure) {
369369
$data['type'] = 'closure';
370370

371+
$r = new \ReflectionFunction($callable);
372+
if (false !== strpos($r->name, '{closure}')) {
373+
return $data;
374+
}
375+
$data['name'] = $r->name;
376+
377+
$class = ($class = $r->getClosureThis()) ? \get_class($class) : null;
378+
if ($scopeClass = $r->getClosureScopeClass() ?: $class) {
379+
$data['class'] = $scopeClass;
380+
if (!$class) {
381+
$data['static'] = true;
382+
}
383+
}
384+
371385
return $data;
372386
}
373387

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,20 @@ protected function describeCallable($callable, array $options = array())
354354
if ($callable instanceof \Closure) {
355355
$string .= "\n- Type: `closure`";
356356

357+
$r = new \ReflectionFunction($callable);
358+
if (false !== strpos($r->name, '{closure}')) {
359+
return $this->write($string."\n");
360+
}
361+
$string .= "\n".sprintf('- Name: `%s`', $r->name);
362+
363+
$class = ($class = $r->getClosureThis()) ? \get_class($class) : null;
364+
if ($scopeClass = $r->getClosureScopeClass() ?: $class) {
365+
$string .= "\n".sprintf('- Class: `%s`', $class);
366+
if (!$class) {
367+
$string .= "\n- Static: yes";
368+
}
369+
}
370+
357371
return $this->write($string."\n");
358372
}
359373

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,7 @@ protected function describeRouteCollection(RouteCollection $routes, array $optio
5656

5757
if ($showControllers) {
5858
$controller = $route->getDefault('_controller');
59-
if ($controller instanceof \Closure) {
60-
$controller = 'Closure';
61-
} elseif (\is_object($controller)) {
62-
$controller = \get_class($controller);
63-
}
64-
$row[] = $controller;
59+
$row[] = $this->formatCallable($controller);
6560
}
6661

6762
$tableRows[] = $row;
@@ -474,7 +469,18 @@ private function formatCallable($callable)
474469
}
475470

476471
if ($callable instanceof \Closure) {
477-
return '\Closure()';
472+
$r = new \ReflectionFunction($callable);
473+
if (false !== strpos($r->name, '{closure}')) {
474+
return 'Closure()';
475+
}
476+
if ($class = $r->getClosureScopeClass()) {
477+
return sprintf('%s::%s()', $class, $r->name);
478+
}
479+
if ($class = $r->getClosureThis()) {
480+
return sprintf('%s::%s()', \get_class($class), $r->name);
481+
}
482+
483+
return $r->name.'()';
478484
}
479485

480486
if (method_exists($callable, '__invoke')) {

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,20 @@ private function getCallableDocument($callable)
580580
if ($callable instanceof \Closure) {
581581
$callableXML->setAttribute('type', 'closure');
582582

583+
$r = new \ReflectionFunction($callable);
584+
if (false !== strpos($r->name, '{closure}')) {
585+
return $dom;
586+
}
587+
$callableXML->setAttribute('name', $r->name);
588+
589+
$class = ($class = $r->getClosureThis()) ? \get_class($class) : null;
590+
if ($scopeClass = $r->getClosureScopeClass() ?: $class) {
591+
$callableXML->setAttribute('class', $class);
592+
if (!$class) {
593+
$callableXML->setAttribute('static', 'true');
594+
}
595+
}
596+
583597
return $dom;
584598
}
585599

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
\Closure()
1+
Closure()

src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
 Order   Callable   Priority 
77
------- ------------------- ----------
88
#1 global_function() 255
9-
#2 \Closure() -1
9+
#2 Closure() -1
1010
------- ------------------- ----------
1111

src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
 Order   Callable   Priority 
1010
------- ------------------- ----------
1111
#1 global_function() 255
12-
#2 \Closure() -1
12+
#2 Closure() -1
1313
------- ------------------- ----------
1414

1515
"event2" event

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ class WrappedListener
3434
public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
3535
{
3636
$this->listener = $listener;
37-
$this->name = $name;
3837
$this->stopwatch = $stopwatch;
3938
$this->dispatcher = $dispatcher;
4039
$this->called = false;
@@ -44,7 +43,17 @@ public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatc
4443
$this->name = \is_object($listener[0]) ? \get_class($listener[0]) : $listener[0];
4544
$this->pretty = $this->name.'::'.$listener[1];
4645
} elseif ($listener instanceof \Closure) {
47-
$this->pretty = $this->name = 'closure';
46+
$r = new \ReflectionFunction($listener);
47+
if (false !== strpos($r->name, '{closure}')) {
48+
$this->pretty = $this->name = 'closure';
49+
} elseif ($this->name = $r->getClosureScopeClass()) {
50+
$this->pretty = $this->name.'::'.$r->name;
51+
} elseif ($class = $r->getClosureThis()) {
52+
$this->name = \get_class($class);
53+
$this->pretty = $this->name.'::'.$r->name;
54+
} else {
55+
$this->pretty = $this->name = $r->name;
56+
}
4857
} elseif (\is_string($listener)) {
4958
$this->pretty = $this->name = $listener;
5059
} else {

src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,12 +380,27 @@ protected function parseController($controller)
380380
if ($controller instanceof \Closure) {
381381
$r = new \ReflectionFunction($controller);
382382

383-
return array(
383+
$controller = array(
384384
'class' => $r->getName(),
385385
'method' => null,
386386
'file' => $r->getFileName(),
387387
'line' => $r->getStartLine(),
388388
);
389+
390+
if (false !== strpos($r->name, '{closure}')) {
391+
return $controller;
392+
}
393+
$controller['method'] = $r->name;
394+
395+
if ($class = $r->getClosureScopeClass()) {
396+
$controller['class'] = $class;
397+
} elseif ($class = $r->getClosureThis()) {
398+
$controller['class'] = \get_class($class);
399+
} else {
400+
return $r->name;
401+
}
402+
403+
return $controller;
389404
}
390405

391406
if (\is_object($controller)) {

src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested,
3939
$stub->class = 'Closure'; // HHVM generates unique class names for closures
4040
$a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter);
4141

42+
if (false === strpos($c->name, '{closure}')) {
43+
if (isset($a[$prefix.'class'])) {
44+
$stub->class = $a[$prefix.'class']->value.'::'.$c->name;
45+
} elseif (isset($a[$prefix.'this'])) {
46+
$stub->class = $a[$prefix.'this']->class.'::'.$c->name;
47+
} else {
48+
$stub->class = $c->name;
49+
}
50+
unset($a[$prefix.'class']);
51+
}
52+
4253
if (isset($a[$prefix.'parameters'])) {
4354
foreach ($a[$prefix.'parameters']->value as &$v) {
4455
$param = $v;

src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,34 @@ public function testClosureCaster()
8585
);
8686
}
8787

88+
public function testFromCallableClosureCaster()
89+
{
90+
if (\defined('HHVM_VERSION_ID')) {
91+
$this->markTestSkipped('Not for HHVM.');
92+
}
93+
$var = array(
94+
(new \ReflectionMethod($this, __FUNCTION__))->getClosure($this),
95+
(new \ReflectionMethod(__CLASS__, 'tearDownAfterClass'))->getClosure(),
96+
);
97+
98+
$this->assertDumpMatchesFormat(
99+
<<<EOTXT
100+
array:2 [
101+
0 => Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest::testFromCallableClosureCaster {
102+
this: Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest { …}
103+
file: "%sReflectionCasterTest.php"
104+
line: "%d to %d"
105+
}
106+
1 => %sTestCase::tearDownAfterClass {
107+
file: "%sTestCase.php"
108+
line: "%d to %d"
109+
}
110+
]
111+
EOTXT
112+
, $var
113+
);
114+
}
115+
88116
public function testClosureCasterExcludingVerbosity()
89117
{
90118
$var = function () {};

0 commit comments

Comments
 (0)
0