10000 [VarDumper] algo to clone any PHP variable to a breadth-first queue · symfony/symfony@07135a0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 07135a0

Browse files
[VarDumper] algo to clone any PHP variable to a breadth-first queue
1 parent 4bf9300 commit 07135a0

File tree

5 files changed

+444
-0
lines changed

5 files changed

+444
-0
lines changed
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\VarDumper\Cloner;
13+
14+
use Symfony\Component\VarDumper\Exception\ThrowingCasterException;
15+
16+
/**
17+
* AbstractCloner implements a generic caster mechanism for objects and resources.
18+
*
19+
* @author Nicolas Grekas <p@tchwork.com>
20+
*/
21+
abstract class AbstractCloner implements ClonerInterface
22+
{
23+
protected $maxItems = 2500;
24+
protected $maxString = -1;
25+
26+
private $data = array(array(null));
27+
private $prevErrorHandler;
28+
29+
/**
30+
* Sets the maximum number of items to clone past the first level in nested structures.
31+
*
32+
* @param int $maxItems
33+
*/
34+
public function setMaxItems($maxItems)
35+
{
36+
$this->maxItems = (int) $maxItems;
37+
}
38+
39+
/**
40+
* Sets the maximum cloned length for strings.
41+
*
42+
* @param int $maxString
43+
*/
44+
public function setMaxString($maxString)
45+
{
46+
$this->maxString = (int) $maxString;
47+
}
48+
49+
/**
50+
* {@inheritdoc}
51+
*/
52+
public function cloneVar($var)
53+
{
54+
$this->prevErrorHandler = set_error_handler(array($this, 'handleError'));
55+
try {
56+
if (!function_exists('iconv')) {
57+
$this->maxString = -1;
58+
}
59+
$data = $this->doClone($var);
60+
} catch (\Exception $e) {
61+
}
62+
restore_error_handler();
63+
$this->prevErrorHandler = null;
64+
65+
if (isset($e)) {
66+
throw $e;
67+
}
68+
69+
return new Data($data);
70+
}
71+
72+
/**
73+
* Effectively clones the PHP variable.
74+
*
75+
* @param mixed $var Any PHP variable.
76+
*
77+
* @return array The cloned variable represented in an array.
78+
*/
79+
abstract protected function doClone($var);
80+
81+
/**
82+
* Casts an object to an array representation.
83+
*
84+
* @param string $class The class of the object.
85+
* @param object $obj The object itself.
86+
*
87+
* @return array The object casted as array.
88+
*/
89+
protected function castObject($class, $obj)
90+
{
91+
if (method_exists($obj, '__debugInfo')) {
92+
if (!$a = $this->callCaster(array($this, '__debugInfo'), $obj, array())) {
93+
$a = (array) $obj;
94+
}
95+
} else {
96+
$a = (array) $obj;
97+
}
98+
99+
return $a;
100+
}
101+
102+
/**
103+
* Casts a resource to an array representation.
104+
*
105+
* @param string $type The type of the resource.
106+
* @param resource $res The resource.
107+
*
108+
* @return array The resource casted as array.
109+
*/
110+
protected function castResource($type, $res)
111+
{
112+
$a = array();
113+
114+
return $a;
115+
}
116+
117+
/**
118+
* Calls a custom caster.
119+
*
120+
* @param callable $callback The caster.
121+
* @param object|resource $obj The object/resource being casted.
122+
* @param array $a The result of the previous cast for chained casters.
123+
*
124+
* @return array The casted object/resource.
125+
*/
126+
private function callCaster($callback, $obj, $a)
127+
{
128+
try {
129+
// Ignore invalid $callback
130+
$cast = @call_user_func($callback, $obj, $a);
131+
132+
if (is_array($cast)) {
133+
$a = $cast;
134+
}
135+
} catch (\Exception $e) {
136+
$a["\0~\0"] = new ThrowingCasterException($callback, $e);
137+
}
138+
139+
return $a;
140+
}
141+
142+
/**
143+
* Special handling for errors: cloning must be fail-safe.
144+
*
145+
* @internal
146+
*/
147+
public function handleError($type, $msg, $file, $line, $context)
148+
{
149+
if (E_RECOVERABLE_ERROR === $type || E_USER_ERROR === $type) {
150+
// Cloner never dies
151+
throw new \ErrorException($msg, 0, $type, $file, $line);
152+
}
153+
154+
if ($this->prevErrorHandler) {
155+
return call_user_func($this->prevErrorHandler, $type, $msg, $file, $line, $context);
156+
}
157+
158+
return false;
159+
}
160+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\VarDumper\Cloner;
13+
14+
/**
15+
* @author Nicolas Grekas <p@tchwork.com>
16+
*/
17+
interface ClonerInterface
18+
{
19+
/**
20+
* Clones a PHP variable.
21+
*
22+
* @param mixed $var Any PHP variable.
23+
*
24+
* @return Data The cloned variable represented by a Data object.
25+
*/
26+
public function cloneVar($var);
27+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\VarDumper\Cloner;
13+
14+
/**
15+
* @author Nicolas Grekas <p@tchwork.com>
16+
*/
17+
class Data
18+
{
19+
private $data;
20+
21+
/**
22+
* @param array $data A array as returned by ClonerInterface::cloneVar().
23+
*/
24+
public function __construct(array $data)
25+
{
26+
$this->data = $data;
27+
}
28+
29+
public function getRawData()
30+
{
31+
return $this->data;
32+
}
33+
}

0 commit comments

Comments
 (0)
0