[VarDumper] Added setMinDepth to VarCloner
This new function allows VarCloner users to specify a minimum tree depth that must be fully explored before we start limiting the number of cloned items via the existing setMaxItems functionality. It’s useful for dumping arguments from a backtrace to ensure some minimum level of detail, while keeping a very low setMaxItems value to ensure fast performance.
This commit is contained in:
parent
de5c60de0e
commit
d6534f5cfc
@ -1,6 +1,11 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
3.4.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added `AbstractCloner::setMinDepth()` function to ensure minimum tree depth
|
||||||
|
|
||||||
2.7.0
|
2.7.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -129,6 +129,7 @@ abstract class AbstractCloner implements ClonerInterface
|
|||||||
|
|
||||||
protected $maxItems = 2500;
|
protected $maxItems = 2500;
|
||||||
protected $maxString = -1;
|
protected $maxString = -1;
|
||||||
|
protected $minDepth = 1;
|
||||||
protected $useExt;
|
protected $useExt;
|
||||||
|
|
||||||
private $casters = array();
|
private $casters = array();
|
||||||
@ -168,7 +169,7 @@ abstract class AbstractCloner implements ClonerInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the maximum number of items to clone past the first level in nested structures.
|
* Sets the maximum number of items to clone past the minimum depth in nested structures.
|
||||||
*
|
*
|
||||||
* @param int $maxItems
|
* @param int $maxItems
|
||||||
*/
|
*/
|
||||||
@ -187,6 +188,17 @@ abstract class AbstractCloner implements ClonerInterface
|
|||||||
$this->maxString = (int) $maxString;
|
$this->maxString = (int) $maxString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the minimum tree depth where we are guaranteed to clone all the items. After this
|
||||||
|
* depth is reached, only setMaxItems items will be cloned.
|
||||||
|
*
|
||||||
|
* @param int $minDepth
|
||||||
|
*/
|
||||||
|
public function setMinDepth($minDepth)
|
||||||
|
{
|
||||||
|
$this->minDepth = (int) $minDepth;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clones a PHP variable.
|
* Clones a PHP variable.
|
||||||
*
|
*
|
||||||
|
@ -26,7 +26,7 @@ class VarCloner extends AbstractCloner
|
|||||||
{
|
{
|
||||||
$useExt = $this->useExt;
|
$useExt = $this->useExt;
|
||||||
$len = 1; // Length of $queue
|
$len = 1; // Length of $queue
|
||||||
$pos = 0; // Number of cloned items past the first level
|
$pos = 0; // Number of cloned items past the minimum depth
|
||||||
$refsCounter = 0; // Hard references counter
|
$refsCounter = 0; // Hard references counter
|
||||||
$queue = array(array($var)); // This breadth-first queue is the return value
|
$queue = array(array($var)); // This breadth-first queue is the return value
|
||||||
$arrayRefs = array(); // Map of queue indexes to stub array objects
|
$arrayRefs = array(); // Map of queue indexes to stub array objects
|
||||||
@ -36,6 +36,10 @@ class VarCloner extends AbstractCloner
|
|||||||
$values = array(); // Map of stub objects' hashes to original values
|
$values = array(); // Map of stub objects' hashes to original values
|
||||||
$maxItems = $this->maxItems;
|
$maxItems = $this->maxItems;
|
||||||
$maxString = $this->maxString;
|
$maxString = $this->maxString;
|
||||||
|
$minDepth = $this->minDepth;
|
||||||
|
$currentDepth = 0; // Current tree depth
|
||||||
|
$currentDepthFinalIndex = 0; // Final $queue index for current tree depth
|
||||||
|
$minimumDepthReached = $minDepth === 0; // Becomes true when minimum tree depth has been reached
|
||||||
$cookie = (object) array(); // Unique object used to detect hard references
|
$cookie = (object) array(); // Unique object used to detect hard references
|
||||||
$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable
|
$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable
|
||||||
$a = null; // Array cast for nested structures
|
$a = null; // Array cast for nested structures
|
||||||
@ -57,6 +61,15 @@ class VarCloner extends AbstractCloner
|
|||||||
$hashOffset = self::$hashOffset;
|
$hashOffset = self::$hashOffset;
|
||||||
|
|
||||||
for ($i = 0; $i < $len; ++$i) {
|
for ($i = 0; $i < $len; ++$i) {
|
||||||
|
// Detect when we move on to the next tree depth
|
||||||
|
if ($i > $currentDepthFinalIndex) {
|
||||||
|
++$currentDepth;
|
||||||
|
$currentDepthFinalIndex = $len - 1;
|
||||||
|
if ($currentDepth >= $minDepth) {
|
||||||
|
$minimumDepthReached = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$indexed = true; // Whether the currently iterated array is numerically indexed or not
|
$indexed = true; // Whether the currently iterated array is numerically indexed or not
|
||||||
$j = -1; // Position in the currently iterated array
|
$j = -1; // Position in the currently iterated array
|
||||||
$fromObjCast = array_keys($queue[$i]);
|
$fromObjCast = array_keys($queue[$i]);
|
||||||
@ -166,7 +179,7 @@ class VarCloner extends AbstractCloner
|
|||||||
$stub->handle = $h;
|
$stub->handle = $h;
|
||||||
}
|
}
|
||||||
$stub->value = null;
|
$stub->value = null;
|
||||||
if (0 <= $maxItems && $maxItems <= $pos) {
|
if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) {
|
||||||
$stub->cut = count($a);
|
$stub->cut = count($a);
|
||||||
$a = null;
|
$a = null;
|
||||||
}
|
}
|
||||||
@ -193,7 +206,7 @@ class VarCloner extends AbstractCloner
|
|||||||
$stub->handle = $h;
|
$stub->handle = $h;
|
||||||
$a = $this->castResource($stub, 0 < $i);
|
$a = $this->castResource($stub, 0 < $i);
|
||||||
$stub->value = null;
|
$stub->value = null;
|
||||||
if (0 <= $maxItems && $maxItems <= $pos) {
|
if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) {
|
||||||
$stub->cut = count($a);
|
$stub->cut = count($a);
|
||||||
$a = null;
|
$a = null;
|
||||||
}
|
}
|
||||||
@ -226,7 +239,7 @@ class VarCloner extends AbstractCloner
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($a) {
|
if ($a) {
|
||||||
if ($i && 0 <= $maxItems) {
|
if ($minimumDepthReached && 0 <= $maxItems) {
|
||||||
$k = count($a);
|
$k = count($a);
|
||||||
if ($pos < $maxItems) {
|
if ($pos < $maxItems) {
|
||||||
if ($maxItems < $pos += $k) {
|
if ($maxItems < $pos += $k) {
|
||||||
|
@ -152,6 +152,261 @@ Symfony\Component\VarDumper\Cloner\Data Object
|
|||||||
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
EOTXT;
|
||||||
|
$this->assertStringMatchesFormat($expected, print_r($clone, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLimits()
|
||||||
|
{
|
||||||
|
// Level 0:
|
||||||
|
$data = array(
|
||||||
|
// Level 1:
|
||||||
|
array(
|
||||||
|
// Level 2:
|
||||||
|
array(
|
||||||
|
// Level 3:
|
||||||
|
'Level 3 Item 0',
|
||||||
|
'Level 3 Item 1',
|
||||||
|
'Level 3 Item 2',
|
||||||
|
'Level 3 Item 3',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'Level 3 Item 4',
|
||||||
|
'Level 3 Item 5',
|
||||||
|
'Level 3 Item 6',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'Level 3 Item 7',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'Level 3 Item 8',
|
||||||
|
),
|
||||||
|
'Level 2 Item 0',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'Level 2 Item 1',
|
||||||
|
),
|
||||||
|
'Level 1 Item 0',
|
||||||
|
array(
|
||||||
|
// Test setMaxString:
|
||||||
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||||
|
'SHORT',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$cloner = new VarCloner();
|
||||||
|
$cloner->setMinDepth(2);
|
||||||
|
$cloner->setMaxItems(5);
|
||||||
|
$cloner->setMaxString(20);
|
||||||
|
$clone = $cloner->cloneVar($data);
|
||||||
|
|
||||||
|
$expected = <<<EOTXT
|
||||||
|
Symfony\Component\VarDumper\Cloner\Data Object
|
||||||
|
(
|
||||||
|
[data:Symfony\Component\VarDumper\Cloner\Data:private] => Array
|
||||||
|
(
|
||||||
|
[0] => Array
|
||||||
|
(
|
||||||
|
[0] => Symfony\Component\VarDumper\Cloner\Stub Object
|
||||||
|
(
|
||||||
|
[type] => array
|
||||||
|
[class] => indexed
|
||||||
|
[value] => 5
|
||||||
|
[cut] => 0
|
||||||
|
[handle] => 0
|
||||||
|
[refCount] => 0
|
||||||
|
[position] => 1
|
||||||
|
[attr] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[1] => Array
|
||||||
|
(
|
||||||
|
[0] => Symfony\Component\VarDumper\Cloner\Stub Object
|
||||||
|
(
|
||||||
|
[type] => array
|
||||||
|
[class] => indexed
|
||||||
|
[value] => 3
|
||||||
|
[cut] => 0
|
||||||
|
[handle] => 0
|
||||||
|
[refCount] => 0
|
||||||
|
[position] => 2
|
||||||
|
[attr] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[1] => Symfony\Component\VarDumper\Cloner\Stub Object
|
||||||
|
(
|
||||||
|
[type] => array
|
||||||
|
[class] => indexed
|
||||||
|
[value] => 2
|
||||||
|
[cut] => 0
|
||||||
|
[handle] => 0
|
||||||
|
[refCount] => 0
|
||||||
|
[position] => 3
|
||||||
|
[attr] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[2] => Symfony\Component\VarDumper\Cloner\Stub Object
|
||||||
|
(
|
||||||
|
[type] => array
|
||||||
|
[class] => indexed
|
||||||
|
[value] => 1
|
||||||
|
[cut] => 0
|
||||||
|
[handle] => 0
|
||||||
|
[refCount] => 0
|
||||||
|
[position] => 4
|
||||||
|
[attr] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[3] => Level 1 Item 0
|
||||||
|
[4] => Symfony\Component\VarDumper\Cloner\Stub Object
|
||||||
|
(
|
||||||
|
[type] => array
|
||||||
|
[class] => indexed
|
||||||
|
[value] => 2
|
||||||
|
[cut] => 0
|
||||||
|
[handle] => 0
|
||||||
|
[refCount] => 0
|
||||||
|
[position] => 5
|
||||||
|
[attr] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[2] => Array
|
||||||
|
(
|
||||||
|
[0] => Symfony\Component\VarDumper\Cloner\Stub Object
|
||||||
|
(
|
||||||
|
[type] => array
|
||||||
|
[class] => indexed
|
||||||
|
[value] => 4
|
||||||
|
[cut] => 0
|
||||||
|
[handle] => 0
|
||||||
|
[refCount] => 0
|
||||||
|
[position] => 6
|
||||||
|
[attr] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[1] => Symfony\Component\VarDumper\Cloner\Stub Object
|
||||||
|
(
|
||||||
|
[type] => array
|
||||||
|
[class] => indexed
|
||||||
|
[value] => 3
|
||||||
|
[cut] => 2
|
||||||
|
[handle] => 0
|
||||||
|
[refCount] => 0
|
||||||
|
[position] => 7
|
||||||
|
[attr] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[2] => Symfony\Component\VarDumper\Cloner\Stub Object
|
||||||
|
(
|
||||||
|
[type] => array
|
||||||
|
[class] => assoc
|
||||||
|
[value] => 1
|
||||||
|
[cut] => 1
|
||||||
|
[handle] => 0
|
||||||
|
[refCount] => 0
|
||||||
|
[position] => 0
|
||||||
|
[attr] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[3] => Array
|
||||||
|
(
|
||||||
|
[0] => Symfony\Component\VarDumper\Cloner\Stub Object
|
||||||
|
(
|
||||||
|
[type] => array
|
||||||
|
[class] => assoc
|
||||||
|
[value] => 1
|
||||||
|
[cut] => 1
|
||||||
|
[handle] => 0
|
||||||
|
[refCount] => 0
|
||||||
|
[position] => 0
|
||||||
|
[attr] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[1] => Level 2 Item 0
|
||||||
|
)
|
||||||
|
|
||||||
|
[4] => Array
|
||||||
|
(
|
||||||
|
[0] => Level 2 Item 1
|
||||||
|
)
|
||||||
|
|
||||||
|
[5] => Array
|
||||||
|
(
|
||||||
|
[0] => Symfony\Component\VarDumper\Cloner\Stub Object
|
||||||
|
(
|
||||||
|
[type] => string
|
||||||
|
[class] => utf8
|
||||||
|
[value] => ABCDEFGHIJKLMNOPQRST
|
||||||
|
[cut] => 6
|
||||||
|
[handle] => 0
|
||||||
|
[refCount] => 0
|
||||||
|
[position] => 0
|
||||||
|
[attr] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[1] => SHORT
|
||||||
|
)
|
||||||
|
|
||||||
|
[6] => Array
|
||||||
|
(
|
||||||
|
[0] => Level 3 Item 0
|
||||||
|
[1] => Level 3 Item 1
|
||||||
|
[2] => Level 3 Item 2
|
||||||
|
[3] => Level 3 Item 3
|
||||||
|
)
|
||||||
|
|
||||||
|
[7] => Array
|
||||||
|
(
|
||||||
|
[0] => Level 3 Item 4
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[position:Symfony\Component\VarDumper\Cloner\Data:private] => 0
|
||||||
|
[key:Symfony\Component\VarDumper\Cloner\Data:private] => 0
|
||||||
|
[maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20
|
||||||
|
[maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||||
|
[useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1
|
||||||
|
)
|
||||||
|
|
||||||
EOTXT;
|
EOTXT;
|
||||||
$this->assertStringMatchesFormat($expected, print_r($clone, true));
|
$this->assertStringMatchesFormat($expected, print_r($clone, true));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user