feature #14080 [VarDumper] Add casters for Reflection* classes (nicolas-grekas)
This PR was merged into the 2.7 branch.
Discussion
----------
[VarDumper] Add casters for Reflection* classes
| Q | A
| ------------- | ---
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | yes
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
Will need a rebase on top of #14079 and #14077 to be mergeable/green.
Commits
-------
64d6e76
[VarDumper] Add casters for Reflection* classes
This commit is contained in:
commit
5188cfecc5
@ -20,19 +20,221 @@ use Symfony\Component\VarDumper\Cloner\Stub;
|
|||||||
*/
|
*/
|
||||||
class ReflectionCaster
|
class ReflectionCaster
|
||||||
{
|
{
|
||||||
|
private static $extraMap = array(
|
||||||
|
'docComment' => 'getDocComment',
|
||||||
|
'extension' => 'getExtensionName',
|
||||||
|
'isDisabled' => 'isDisabled',
|
||||||
|
'isDeprecated' => 'isDeprecated',
|
||||||
|
'isInternal' => 'isInternal',
|
||||||
|
'isUserDefined' => 'isUserDefined',
|
||||||
|
'isGenerator' => 'isGenerator',
|
||||||
|
'isVariadic' => 'isVariadic',
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated since Symfony 2.7, to be removed in 3.0.
|
||||||
|
*/
|
||||||
public static function castReflector(\Reflector $c, array $a, Stub $stub, $isNested)
|
public static function castReflector(\Reflector $c, array $a, Stub $stub, $isNested)
|
||||||
{
|
{
|
||||||
$a["\0~\0reflection"] = $c->__toString();
|
trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.7 and will be removed in 3.0.', E_USER_DEPRECATED);
|
||||||
|
$a[Caster::PREFIX_VIRTUAL.'reflection'] = $c->__toString();
|
||||||
|
|
||||||
return $a;
|
return $a;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested)
|
public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested)
|
||||||
{
|
{
|
||||||
|
$prefix = Caster::PREFIX_VIRTUAL;
|
||||||
|
$c = new \ReflectionFunction($c);
|
||||||
|
|
||||||
$stub->class = 'Closure'; // HHVM generates unique class names for closures
|
$stub->class = 'Closure'; // HHVM generates unique class names for closures
|
||||||
$a = static::castReflector(new \ReflectionFunction($c), $a, $stub, $isNested);
|
$a = static::castFunctionAbstract($c, $a, $stub, $isNested);
|
||||||
unset($a["\0+\0000"], $a['name'], $a["\0+\0this"], $a["\0+\0parameter"]);
|
|
||||||
|
if (isset($a[$prefix.'parameters'])) {
|
||||||
|
foreach ($a[$prefix.'parameters'] as &$v) {
|
||||||
|
$param = $v;
|
||||||
|
$v = array();
|
||||||
|
foreach (static::castParameter($param, array(), $stub, true) as $k => $param) {
|
||||||
|
if ("\0" === $k[0]) {
|
||||||
|
$v[substr($k, 3)] = $param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($v['position'], $v['isVariadic'], $v['byReference'], $v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($f = $c->getFileName()) {
|
||||||
|
$a[$prefix.'file'] = $f;
|
||||||
|
$a[$prefix.'line'] = $c->getStartLine().' to '.$c->getEndLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
$prefix = Caster::PREFIX_DYNAMIC;
|
||||||
|
unset($a['name'], $a[$prefix.'0'], $a[$prefix.'this'], $a[$prefix.'parameter'], $a[Caster::PREFIX_VIRTUAL.'extra']);
|
||||||
|
|
||||||
return $a;
|
return $a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function castClass(\ReflectionClass $c, array $a, Stub $stub, $isNested, $filter = 0)
|
||||||
|
{
|
||||||
|
$prefix = Caster::PREFIX_VIRTUAL;
|
||||||
|
|
||||||
|
if ($n = \Reflection::getModifierNames($c->getModifiers())) {
|
||||||
|
$a[$prefix.'modifiers'] = implode(' ', $n);
|
||||||
|
}
|
||||||
|
|
||||||
|
self::addMap($a, $c, array(
|
||||||
|
'extends' => 'getParentClass',
|
||||||
|
'implements' => 'getInterfaceNames',
|
||||||
|
'constants' => 'getConstants',
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($c->getProperties() as $n) {
|
||||||
|
$a[$prefix.'properties'][$n->name] = $n;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($c->getMethods() as $n) {
|
||||||
|
$a[$prefix.'methods'][$n->name] = $n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) {
|
||||||
|
self::addExtra($a, $c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, array $a, Stub $stub, $isNested, $filter = 0)
|
||||||
|
{
|
||||||
|
$prefix = Caster::PREFIX_VIRTUAL;
|
||||||
|
|
||||||
|
self::addMap($a, $c, array(
|
||||||
|
'returnsReference' => 'returnsReference',
|
||||||
|
'class' => 'getClosureScopeClass',
|
||||||
|
'this' => 'getClosureThis',
|
||||||
|
));
|
||||||
|
|
||||||
|
if (isset($a[$prefix.'this'])) {
|
||||||
|
$a[$prefix.'this'] = new CutStub($a[$prefix.'this']);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($c->getParameters() as $v) {
|
||||||
|
$k = '$'.$v->name;
|
||||||
|
if ($v->isPassedByReference()) {
|
||||||
|
$k = '&'.$k;
|
||||||
|
}
|
||||||
|
if (method_exists($v, 'isVariadic') && $v->isVariadic()) {
|
||||||
|
$k = '...'.$k;
|
||||||
|
}
|
||||||
|
$a[$prefix.'parameters'][$k] = $v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($v = $c->getStaticVariables()) {
|
||||||
|
foreach ($v as $k => &$v) {
|
||||||
|
$a[$prefix.'use']['$'.$k] =& $v;
|
||||||
|
}
|
||||||
|
unset($v);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) {
|
||||||
|
self::addExtra($a, $c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function castMethod(\ReflectionMethod $c, array $a, Stub $stub, $isNested)
|
||||||
|
{
|
||||||
|
$a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers()));
|
||||||
|
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function castParameter(\ReflectionParameter $c, array $a, Stub $stub, $isNested)
|
||||||
|
{
|
||||||
|
$prefix = Caster::PREFIX_VIRTUAL;
|
||||||
|
|
||||||
|
self::addMap($a, $c, array(
|
||||||
|
'position' => 'getPosition',
|
||||||
|
'isVariadic' => 'isVariadic',
|
||||||
|
'byReference' => 'isPassedByReference',
|
||||||
|
));
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($c->isArray()) {
|
||||||
|
$a[$prefix.'typeHint'] = 'array';
|
||||||
|
} elseif (method_exists($c, 'isCallable') && $c->isCallable()) {
|
||||||
|
$a[$prefix.'typeHint'] = 'callable';
|
||||||
|
} elseif ($v = $c->getClass()) {
|
||||||
|
$a[$prefix.'typeHint'] = $v->name;
|
||||||
|
}
|
||||||
|
} catch (\ReflectionException $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$a[$prefix.'default'] = $v = $c->getDefaultValue();
|
||||||
|
if (method_exists($c, 'isDefaultValueConstant') && $c->isDefaultValueConstant()) {
|
||||||
|
$a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v);
|
||||||
|
}
|
||||||
|
} catch (\ReflectionException $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function castProperty(\ReflectionProperty $c, array $a, Stub $stub, $isNested)
|
||||||
|
{
|
||||||
|
$a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers()));
|
||||||
|
self::addExtra($a, $c);
|
||||||
|
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function castExtension(\ReflectionExtension $c, array $a, Stub $stub, $isNested)
|
||||||
|
{
|
||||||
|
self::addMap($a, $c, array(
|
||||||
|
'version' => 'getVersion',
|
||||||
|
'dependencies' => 'getDependencies',
|
||||||
|
'iniEntries' => 'getIniEntries',
|
||||||
|
'isPersistent' => 'isPersistent',
|
||||||
|
'isTemporary' => 'isTemporary',
|
||||||
|
'constants' => 'getConstants',
|
||||||
|
'functions' => 'getFunctions',
|
||||||
|
'classes' => 'getClasses',
|
||||||
|
));
|
||||||
|
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function castZendExtension(\ReflectionZendExtension $c, array $a, Stub $stub, $isNested)
|
||||||
|
{
|
||||||
|
self::addMap($a, $c, array(
|
||||||
|
'version' => 'getVersion',
|
||||||
|
'author' => 'getAuthor',
|
||||||
|
'copyright' => 'getCopyright',
|
||||||
|
'url' => 'getURL',
|
||||||
|
));
|
||||||
|
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function addExtra(&$a, \Reflector $c)
|
||||||
|
{
|
||||||
|
$a =& $a[Caster::PREFIX_VIRTUAL.'extra'];
|
||||||
|
|
||||||
|
if (method_exists($c, 'getFileName') && $m = $c->getFileName()) {
|
||||||
|
$a['file'] = $m;
|
||||||
|
$a['line'] = $c->getStartLine().' to '.$c->getEndLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
self::addMap($a, $c, self::$extraMap, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function addMap(&$a, \Reflector $c, $map, $prefix = Caster::PREFIX_VIRTUAL)
|
||||||
|
{
|
||||||
|
foreach ($map as $k => $m) {
|
||||||
|
if (method_exists($c, $m) && false !== ($m = $c->$m()) && null !== $m) {
|
||||||
|
$a[$prefix.$k] = $m instanceof \Reflector ? $m->name : $m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,13 @@ abstract class AbstractCloner implements ClonerInterface
|
|||||||
'Symfony\Component\VarDumper\Caster\ConstStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castStub',
|
'Symfony\Component\VarDumper\Caster\ConstStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castStub',
|
||||||
|
|
||||||
'Closure' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClosure',
|
'Closure' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClosure',
|
||||||
'Reflector' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castReflector',
|
'ReflectionClass' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClass',
|
||||||
|
'ReflectionFunctionAbstract' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castFunctionAbstract',
|
||||||
|
'ReflectionMethod' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castMethod',
|
||||||
|
'ReflectionParameter' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castParameter',
|
||||||
|
'ReflectionProperty' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castProperty',
|
||||||
|
'ReflectionExtension' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castExtension',
|
||||||
|
'ReflectionZendExtension' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castZendExtension',
|
||||||
|
|
||||||
'Doctrine\Common\Persistence\ObjectManager' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals',
|
'Doctrine\Common\Persistence\ObjectManager' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals',
|
||||||
'Doctrine\Common\Proxy\Proxy' => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castCommonProxy',
|
'Doctrine\Common\Proxy\Proxy' => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castCommonProxy',
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\VarDumper\Tests\Caster;
|
||||||
|
|
||||||
|
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||||
|
use Symfony\Component\VarDumper\Dumper\CliDumper;
|
||||||
|
use Symfony\Component\VarDumper\Test\VarDumperTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class ReflectionCasterTest extends VarDumperTestCase
|
||||||
|
{
|
||||||
|
public function testReflectionCaster()
|
||||||
|
{
|
||||||
|
$var = new \ReflectionClass('ReflectionClass');
|
||||||
|
|
||||||
|
$this->assertDumpMatchesFormat(
|
||||||
|
<<<'EOTXT'
|
||||||
|
ReflectionClass {
|
||||||
|
+name: "ReflectionClass"
|
||||||
|
implements: array:1 [
|
||||||
|
0 => "Reflector"
|
||||||
|
]
|
||||||
|
constants: array:3 [
|
||||||
|
"IS_IMPLICIT_ABSTRACT" => 16
|
||||||
|
"IS_EXPLICIT_ABSTRACT" => 32
|
||||||
|
"IS_FINAL" => 64
|
||||||
|
]
|
||||||
|
properties: array:1 [
|
||||||
|
"name" => ReflectionProperty {
|
||||||
|
+name: "name"
|
||||||
|
+class: "ReflectionClass"
|
||||||
|
modifiers: "public"
|
||||||
|
extra: null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
methods: array:%d [
|
||||||
|
%A
|
||||||
|
"export" => ReflectionMethod {
|
||||||
|
+name: "export"
|
||||||
|
+class: "ReflectionClass"
|
||||||
|
parameters: array:2 [
|
||||||
|
"$argument" => ReflectionParameter {
|
||||||
|
+name: "argument"
|
||||||
|
position: 0
|
||||||
|
}
|
||||||
|
"$return" => ReflectionParameter {
|
||||||
|
+name: "return"
|
||||||
|
position: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
modifiers: "public static"
|
||||||
|
}
|
||||||
|
%A
|
||||||
|
}
|
||||||
|
EOTXT
|
||||||
|
, $var
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -39,11 +39,19 @@ class CliDumperTest extends VarDumperTestCase
|
|||||||
ob_start();
|
ob_start();
|
||||||
$dumper->dump($data);
|
$dumper->dump($data);
|
||||||
$out = ob_get_clean();
|
$out = ob_get_clean();
|
||||||
$closureLabel = PHP_VERSION_ID >= 50400 ? 'public method' : 'function';
|
|
||||||
$out = preg_replace('/[ \t]+$/m', '', $out);
|
$out = preg_replace('/[ \t]+$/m', '', $out);
|
||||||
$intMax = PHP_INT_MAX;
|
$intMax = PHP_INT_MAX;
|
||||||
$res1 = (int) $var['res'];
|
$res1 = (int) $var['res'];
|
||||||
$res2 = (int) $var[8];
|
$res2 = (int) $var[8];
|
||||||
|
$closure54 = '';
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID >= 50400) {
|
||||||
|
$closure54 = <<<EOTXT
|
||||||
|
|
||||||
|
class: "Symfony\Component\VarDumper\Tests\CliDumperTest"
|
||||||
|
this: Symfony\Component\VarDumper\Tests\CliDumperTest {#%d …}
|
||||||
|
EOTXT;
|
||||||
|
}
|
||||||
|
|
||||||
$this->assertStringMatchesFormat(
|
$this->assertStringMatchesFormat(
|
||||||
<<<EOTXT
|
<<<EOTXT
|
||||||
@ -76,17 +84,16 @@ array:25 [
|
|||||||
+foo: "foo"
|
+foo: "foo"
|
||||||
+"bar": "bar"
|
+"bar": "bar"
|
||||||
}
|
}
|
||||||
"closure" => Closure {#%d
|
"closure" => Closure {#%d{$closure54}
|
||||||
reflection: """
|
parameters: array:2 [
|
||||||
Closure [ <user> {$closureLabel} Symfony\Component\VarDumper\Tests\Fixture\{closure} ] {
|
"\$a" => []
|
||||||
@@ {$var['file']} {$var['line']} - {$var['line']}
|
"&\$b" => array:2 [
|
||||||
|
"typeHint" => "PDO"
|
||||||
- Parameters [2] {
|
"default" => null
|
||||||
Parameter #0 [ <required> \$a ]
|
]
|
||||||
Parameter #1 [ <optional> PDO or NULL &\$b = NULL ]
|
]
|
||||||
}
|
file: "{$var['file']}"
|
||||||
}
|
line: "{$var['line']} to {$var['line']}"
|
||||||
"""
|
|
||||||
}
|
}
|
||||||
"line" => {$var['line']}
|
"line" => {$var['line']}
|
||||||
"nobj" => array:1 [
|
"nobj" => array:1 [
|
||||||
|
@ -47,6 +47,15 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
|
|||||||
$dumpId = $dumpId[0];
|
$dumpId = $dumpId[0];
|
||||||
$res1 = (int) $var['res'];
|
$res1 = (int) $var['res'];
|
||||||
$res2 = (int) $var[8];
|
$res2 = (int) $var[8];
|
||||||
|
$closure54 = '';
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID >= 50400) {
|
||||||
|
$closure54 = <<<EOTXT
|
||||||
|
|
||||||
|
<span class=sf-dump-meta>class</span>: "<span class=sf-dump-str title="48 characters">Symfony\Component\VarDumper\Tests\HtmlDumperTest</span>"
|
||||||
|
<span class=sf-dump-meta>this</span>: <abbr title="Symfony\Component\VarDumper\Tests\HtmlDumperTest" class=sf-dump-note>HtmlDumperTest</abbr> {<a class=sf-dump-ref>#%d</a> …}
|
||||||
|
EOTXT;
|
||||||
|
}
|
||||||
|
|
||||||
$this->assertStringMatchesFormat(
|
$this->assertStringMatchesFormat(
|
||||||
<<<EOTXT
|
<<<EOTXT
|
||||||
@ -79,17 +88,16 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
|
|||||||
+<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="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>"
|
+"<span class=sf-dump-public title="Runtime added dynamic property">bar</span>": "<span class=sf-dump-str title="3 characters">bar</span>"
|
||||||
</samp>}
|
</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</span> {<a class=sf-dump-ref>#%d</a><samp>{$closure54}
|
||||||
<span class=sf-dump-meta>reflection</span>: """
|
<span class=sf-dump-meta>parameters</span>: <span class=sf-dump-note>array:2</span> [<samp>
|
||||||
<span class=sf-dump-str title="%d characters">Closure [ <user> {$closureLabel} Symfony\Component\VarDumper\Tests\Fixture\{closure} ] {</span>
|
"<span class=sf-dump-key>\$a</span>" => []
|
||||||
<span class=sf-dump-str title="%d characters"> @@ {$var['file']} {$var['line']} - {$var['line']}</span>
|
"<span class=sf-dump-key>&\$b</span>" => <span class=sf-dump-note>array:2</span> [<samp>
|
||||||
|
"<span class=sf-dump-key>typeHint</span>" => "<span class=sf-dump-str title="3 characters">PDO</span>"
|
||||||
<span class=sf-dump-str title="%d characters"> - Parameters [2] {</span>
|
"<span class=sf-dump-key>default</span>" => <span class=sf-dump-const>null</span>
|
||||||
<span class=sf-dump-str title="%d characters"> Parameter #0 [ <required> \$a ]</span>
|
</samp>]
|
||||||
<span class=sf-dump-str title="%d characters"> Parameter #1 [ <optional> PDO or NULL &\$b = NULL ]</span>
|
</samp>]
|
||||||
<span class=sf-dump-str title="%d characters"> }</span>
|
<span class=sf-dump-meta>file</span>: "<span class=sf-dump-str title="%d characters">{$var['file']}</span>"
|
||||||
<span class=sf-dump-str title="%d characters">}</span>
|
<span class=sf-dump-meta>line</span>: "<span class=sf-dump-str title="%d characters">{$var['line']} to {$var['line']}</span>"
|
||||||
"""
|
|
||||||
</samp>}
|
</samp>}
|
||||||
"<span class=sf-dump-key>line</span>" => <span class=sf-dump-num>{$var['line']}</span>
|
"<span class=sf-dump-key>line</span>" => <span class=sf-dump-num>{$var['line']}</span>
|
||||||
"<span class=sf-dump-key>nobj</span>" => <span class=sf-dump-note>array:1</span> [<samp>
|
"<span class=sf-dump-key>nobj</span>" => <span class=sf-dump-note>array:1</span> [<samp>
|
||||||
|
Reference in New Issue
Block a user