Skip to content

Commit

Permalink
feature #27768 [VarDumper] display the signature of callables (nicola…
Browse files Browse the repository at this point in the history
…s-grekas)

This PR was merged into the 4.2-dev branch.

Discussion
----------

[VarDumper] display the signature of callables

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | bobthecow/psysh#218
| License       | MIT
| Doc PR        | -

E.g.

![capture d ecran de 2018-06-29 12-08-13](https://user-images.githubusercontent.com/243674/42087047-4fdc70d6-7b95-11e8-972c-216651bf7d69.png)

or

![image](https://user-images.githubusercontent.com/243674/42086967-070bc960-7b95-11e8-9470-7e88f8acf12a.png)

Commits
-------

73b4ac7 [VarDumper] display the signature of callables
  • Loading branch information
fabpot committed Jun 30, 2018
2 parents 9bb990f + 73b4ac7 commit c52b2e9
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 13 deletions.
20 changes: 18 additions & 2 deletions src/Symfony/Component/VarDumper/Caster/ClassStub.php
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Component\VarDumper\Caster;

use Symfony\Component\VarDumper\Cloner\Stub;

/**
* Represents a PHP class identifier.
*
Expand Down Expand Up @@ -58,6 +60,20 @@ public function __construct(string $identifier, $callable = null)
$r = new \ReflectionClass($r[0]);
}
}

if (null !== $callable && $r instanceof \ReflectionFunctionAbstract) {
$s = ReflectionCaster::castFunctionAbstract($r, array(), new Stub(), true);
$s = ReflectionCaster::getSignature($s);

if ('()' === substr($identifier, -2)) {
$this->value = substr_replace($identifier, $s, -2);
} else {
$this->value .= $s;
}
if (isset($this->attr['ellipsis'])) {
$this->attr['ellipsis'] += \strlen($this->value) - \strlen($identifier);
}
}
} catch (\ReflectionException $e) {
return;
}
Expand All @@ -75,9 +91,9 @@ public static function wrapCallable($callable)
}

if (!is_array($callable)) {
$callable = new static($callable);
$callable = new static($callable, $callable);
} elseif (is_string($callable[0])) {
$callable[0] = new static($callable[0]);
$callable[0] = new static($callable[0], $callable);
} else {
$callable[1] = new static($callable[1], $callable);
}
Expand Down
48 changes: 48 additions & 0 deletions src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php
Expand Up @@ -38,6 +38,8 @@ public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested,

$a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter);

$stub->class .= self::getSignature($a);

if (isset($a[$prefix.'parameters'])) {
foreach ($a[$prefix.'parameters']->value as &$v) {
$param = $v;
Expand Down Expand Up @@ -294,6 +296,52 @@ public static function castZendExtension(\ReflectionZendExtension $c, array $a,
return $a;
}

public static function getSignature(array $a)
{
$prefix = Caster::PREFIX_VIRTUAL;
$signature = '';

if (isset($a[$prefix.'parameters'])) {
foreach ($a[$prefix.'parameters']->value as $k => $param) {
$signature .= ', ';
if ($type = $param->getType()) {
if (!$param->isOptional() && $param->allowsNull()) {
$signature .= '?';
}
$signature .= substr(strrchr('\\'.$type->getName(), '\\'), 1).' ';
}
$signature .= $k;

if (!$param->isDefaultValueAvailable()) {
continue;
}
$v = $param->getDefaultValue();
$signature .= ' = ';

if ($param->isDefaultValueConstant()) {
$signature .= substr(strrchr('\\'.$param->getDefaultValueConstantName(), '\\'), 1);
} elseif (null === $v) {
$signature .= 'null';
} elseif (\is_array($v)) {
$signature .= $v ? '[…'.\count($v).']' : '[]';
} elseif (\is_string($v)) {
$signature .= 10 > \strlen($v) && false === strpos($v, '\\') ? "'{$v}'" : "'…".\strlen($v)."'";
} elseif (\is_bool($v)) {
$signature .= $v ? 'true' : 'false';
} else {
$signature .= $v;
}
}
}
$signature = '('.substr($signature, 2).')';

if (isset($a[$prefix.'returnType'])) {
$signature .= ': '.substr(strrchr('\\'.$a[$prefix.'returnType'], '\\'), 1);
}

return $signature;
}

private static function addExtra(&$a, \Reflector $c)
{
$x = isset($a[Caster::PREFIX_VIRTUAL.'extra']) ? $a[Caster::PREFIX_VIRTUAL.'extra']->value : array();
Expand Down
Expand Up @@ -68,14 +68,14 @@ public function testClosureCaster()
$var = function ($x) use ($a, &$b) {};

$this->assertDumpMatchesFormat(
<<<EOTXT
Closure {
<<<'EOTXT'
Closure($x) {
%Aparameters: {
\$x: {}
$x: {}
}
use: {
\$a: 123
\$b: & 123
$a: 123
$b: & 123
}
file: "%sReflectionCasterTest.php"
line: "68 to 68"
Expand All @@ -90,7 +90,7 @@ public function testClosureCasterExcludingVerbosity()
$var = function () {};

$expectedDump = <<<EOTXT
Closure {
Closure() {
class: "Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest"
this: Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest { …}
}
Expand Down Expand Up @@ -140,7 +140,7 @@ public function testReturnType()

$this->assertDumpMatchesFormat(
<<<EOTXT
Closure {
Closure(): int {
returnType: "int"
class: "Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest"
this: Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest { …}
Expand Down
Expand Up @@ -141,7 +141,7 @@ public function testClassStub()

$expectedDump = <<<'EODUMP'
<foo></foo><bar><span class=sf-dump-note>array:1</span> [<samp>
<span class=sf-dump-index>0</span> => "<a href="%sFooInterface.php:10" rel="noopener noreferrer"><span class=sf-dump-str title="5 characters">hello</span></a>"
<span class=sf-dump-index>0</span> => "<a href="%sFooInterface.php:10" rel="noopener noreferrer"><span class=sf-dump-str title="39 characters">hello(?stdClass $a, stdClass $b = null)</span></a>"
</samp>]
</bar>
EODUMP;
Expand Down
Expand Up @@ -75,7 +75,7 @@ public function testGet()
+foo: "foo"
+"bar": "bar"
}
"closure" => Closure {#%d
"closure" => Closure(\$a, PDO &\$b = null) {#%d
class: "Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest"
this: Symfony\Component\VarDumper\Tests\Dumper\CliDumperTest {#%d …}
parameters: {
Expand Down
Expand Up @@ -78,7 +78,7 @@ public function testGet()
+<span class=sf-dump-public title="Public property">foo</span>: "<span class=sf-dump-str title="3 characters">foo</span>"
+"<span class=sf-dump-public title="Runtime added dynamic property">bar</span>": "<span class=sf-dump-str title="3 characters">bar</span>"
</samp>}
"<span class=sf-dump-key>closure</span>" => <span class=sf-dump-note>Closure</span> {<a class=sf-dump-ref>#%d</a><samp>
"<span class=sf-dump-key>closure</span>" => <span class=sf-dump-note>Closure(\$a, PDO &amp;\$b = null)</span> {<a class=sf-dump-ref>#%d</a><samp>
<span class=sf-dump-meta>class</span>: "<span class=sf-dump-str title="Symfony\Component\VarDumper\Tests\Dumper\HtmlDumperTest
55 characters"><span class="sf-dump-ellipsis sf-dump-ellipsis-class">Symfony\Component\VarDumper\Tests\Dumper</span><span class=sf-dump-ellipsis>\</span>HtmlDumperTest</span>"
<span class=sf-dump-meta>this</span>: <abbr title="Symfony\Component\VarDumper\Tests\Dumper\HtmlDumperTest" class=sf-dump-note>HtmlDumperTest</abbr> {<a class=sf-dump-ref>#%d</a> &%s;}
Expand Down
Expand Up @@ -7,5 +7,5 @@ interface FooInterface
/**
* Hello.
*/
public function foo();
public function foo(?\stdClass $a, \stdClass $b = null);
}

0 comments on commit c52b2e9

Please sign in to comment.