Skip to content

Commit

Permalink
Parser: Use try/finally to unset recursion stacks
Browse files Browse the repository at this point in the history
  • Loading branch information
jnvsor committed Dec 22, 2024
1 parent 05e76c2 commit 8d4b712
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 123 deletions.
Binary file modified build/kint.phar
Binary file not shown.
242 changes: 119 additions & 123 deletions src/Parser/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,45 +265,45 @@ private function parseArray(array &$var, ContextInterface $c): AbstractValue
return $this->applyPluginsComplete($var, $array, self::TRIGGER_RECURSION);
}

$this->array_ref_stack[$parentRef] = true;
try {
$this->array_ref_stack[$parentRef] = true;

$cdepth = $c->getDepth();
$ap = $c->getAccessPath();
$cdepth = $c->getDepth();
$ap = $c->getAccessPath();

if ($size > 0 && $this->depth_limit && $cdepth >= $this->depth_limit) {
$array = new ArrayValue($c, $size, $contents);
$array->flags |= AbstractValue::FLAG_DEPTH_LIMIT;
if ($size > 0 && $this->depth_limit && $cdepth >= $this->depth_limit) {
$array = new ArrayValue($c, $size, $contents);
$array->flags |= AbstractValue::FLAG_DEPTH_LIMIT;

$array = $this->applyPluginsComplete($var, $array, self::TRIGGER_DEPTH_LIMIT);
$array = $this->applyPluginsComplete($var, $array, self::TRIGGER_DEPTH_LIMIT);

unset($this->array_ref_stack[$parentRef]);
return $array;
}

return $array;
}
foreach ($var as $key => $_) {
$child = new ArrayContext($key);
$child->depth = $cdepth + 1;
$child->reference = null !== ReflectionReference::fromArrayElement($var, $key);

foreach ($var as $key => $_) {
$child = new ArrayContext($key);
$child->depth = $cdepth + 1;
$child->reference = null !== ReflectionReference::fromArrayElement($var, $key);
if (null !== $ap) {
$child->access_path = $ap.'['.\var_export($key, true).']';
}

if (null !== $ap) {
$child->access_path = $ap.'['.\var_export($key, true).']';
$contents[$key] = $this->parse($var[$key], $child);
}

$contents[$key] = $this->parse($var[$key], $child);
}

$array = new ArrayValue($c, $size, $contents);

if ($contents) {
$array->addRepresentation(new ContainerRepresentation('Contents', $contents, null, true));
}
$array = new ArrayValue($c, $size, $contents);

$array = $this->applyPluginsComplete($var, $array, self::TRIGGER_SUCCESS);
if ($contents) {
$array->addRepresentation(new ContainerRepresentation('Contents', $contents, null, true));
}

unset($this->array_ref_stack[$parentRef]);
$array = $this->applyPluginsComplete($var, $array, self::TRIGGER_SUCCESS);

return $array;
return $array;
} finally {
unset($this->array_ref_stack[$parentRef]);
}
}

/**
Expand Down Expand Up @@ -373,130 +373,126 @@ private function parseObject(object &$var, ContextInterface $c): AbstractValue

return $this->applyPluginsComplete($var, $object, self::TRIGGER_RECURSION);
}
try {
$this->object_hashes[$hash] = true;

$this->object_hashes[$hash] = true;

$cdepth = $c->getDepth();
$ap = $c->getAccessPath();

if ($this->depth_limit && $cdepth >= $this->depth_limit) {
$object = new InstanceValue($c, $classname, $hash, \spl_object_id($var));
$object->flags |= AbstractValue::FLAG_DEPTH_LIMIT;

$object = $this->applyPluginsComplete($var, $object, self::TRIGGER_DEPTH_LIMIT);

unset($this->object_hashes[$hash]);

return $object;
}
$cdepth = $c->getDepth();
$ap = $c->getAccessPath();

if (KINT_PHP81) {
$props = $this->getPropsOrdered(new ReflectionObject($var));
} else {
$props = $this->getPropsOrderedOld(new ReflectionObject($var)); // @codeCoverageIgnore
}
if ($this->depth_limit && $cdepth >= $this->depth_limit) {
$object = new InstanceValue($c, $classname, $hash, \spl_object_id($var));
$object->flags |= AbstractValue::FLAG_DEPTH_LIMIT;

$values = (array) $var;
$properties = [];

foreach ($props as $rprop) {
$rprop->setAccessible(true);
$name = $rprop->getName();

// Casting object to array:
// private properties show in the form "\0$owner_class_name\0$property_name";
// protected properties show in the form "\0*\0$property_name";
// public properties show in the form "$property_name";
// http://www.php.net/manual/en/language.types.array.php#language.types.array.casting
$key = $name;
if ($rprop->isProtected()) {
$key = "\0*\0".$name;
} elseif ($rprop->isPrivate()) {
$key = "\0".$rprop->getDeclaringClass()->getName()."\0".$name;
return $this->applyPluginsComplete($var, $object, self::TRIGGER_DEPTH_LIMIT);
}
$initialized = \array_key_exists($key, $values);
if ($key === (string) (int) $key) {
$key = (int) $key;

if (KINT_PHP81) {
$props = $this->getPropsOrdered(new ReflectionObject($var));
} else {
$props = $this->getPropsOrderedOld(new ReflectionObject($var)); // @codeCoverageIgnore
}

if ($rprop->isDefault()) {
$child = new PropertyContext(
$name,
$rprop->getDeclaringClass()->getName(),
ClassDeclaredContext::ACCESS_PUBLIC
);
$values = (array) $var;
$properties = [];

$child->readonly = KINT_PHP81 && $rprop->isReadOnly();
foreach ($props as $rprop) {
$rprop->setAccessible(true);
$name = $rprop->getName();

// Casting object to array:
// private properties show in the form "\0$owner_class_name\0$property_name";
// protected properties show in the form "\0*\0$property_name";
// public properties show in the form "$property_name";
// http://www.php.net/manual/en/language.types.array.php#language.types.array.casting
$key = $name;
if ($rprop->isProtected()) {
$child->access = ClassDeclaredContext::ACCESS_PROTECTED;
$key = "\0*\0".$name;
} elseif ($rprop->isPrivate()) {
$child->access = ClassDeclaredContext::ACCESS_PRIVATE;
$key = "\0".$rprop->getDeclaringClass()->getName()."\0".$name;
}
$initialized = \array_key_exists($key, $values);
if ($key === (string) (int) $key) {
$key = (int) $key;
}

if (KINT_PHP84) {
if ($rprop->isProtectedSet()) {
$child->access_set = ClassDeclaredContext::ACCESS_PROTECTED;
} elseif ($rprop->isPrivateSet()) {
$child->access_set = ClassDeclaredContext::ACCESS_PRIVATE;
if ($rprop->isDefault()) {
$child = new PropertyContext(
$name,
$rprop->getDeclaringClass()->getName(),
ClassDeclaredContext::ACCESS_PUBLIC
);

$child->readonly = KINT_PHP81 && $rprop->isReadOnly();

if ($rprop->isProtected()) {
$child->access = ClassDeclaredContext::ACCESS_PROTECTED;
} elseif ($rprop->isPrivate()) {
$child->access = ClassDeclaredContext::ACCESS_PRIVATE;
}

$hooks = $rprop->getHooks();
if (isset($hooks['get'])) {
$child->hooks |= PropertyContext::HOOK_GET;
if ($hooks['get']->returnsReference()) {
$child->hooks |= PropertyContext::HOOK_GET_REF;
if (KINT_PHP84) {
if ($rprop->isProtectedSet()) {
$child->access_set = ClassDeclaredContext::ACCESS_PROTECTED;
} elseif ($rprop->isPrivateSet()) {
$child->access_set = ClassDeclaredContext::ACCESS_PRIVATE;
}
}
if (isset($hooks['set'])) {
$child->hooks |= PropertyContext::HOOK_SET;

$child->hook_set_type = (string) $rprop->getSettableType();
if ($child->hook_set_type !== (string) $rprop->getType()) {
$child->hooks |= PropertyContext::HOOK_SET_TYPE;
} elseif ('' === $child->hook_set_type) {
$child->hook_set_type = null;

$hooks = $rprop->getHooks();
if (isset($hooks['get'])) {
$child->hooks |= PropertyContext::HOOK_GET;
if ($hooks['get']->returnsReference()) {
$child->hooks |= PropertyContext::HOOK_GET_REF;
}
}
if (isset($hooks['set'])) {
$child->hooks |= PropertyContext::HOOK_SET;

$child->hook_set_type = (string) $rprop->getSettableType();
if ($child->hook_set_type !== (string) $rprop->getType()) {
$child->hooks |= PropertyContext::HOOK_SET_TYPE;
} elseif ('' === $child->hook_set_type) {
$child->hook_set_type = null;
}
}
}
} else {
$child = new ClassOwnedContext($name, $rprop->getDeclaringClass()->getName());
}
} else {
$child = new ClassOwnedContext($name, $rprop->getDeclaringClass()->getName());
}

$child->reference = $initialized && null !== ReflectionReference::fromArrayElement($values, $key);
$child->depth = $cdepth + 1;
$child->reference = $initialized && null !== ReflectionReference::fromArrayElement($values, $key);
$child->depth = $cdepth + 1;

if (null !== $ap && $child->isAccessible($this->caller_class)) {
/** @psalm-var string $child->name */
if (Utils::isValidPhpName($child->name)) {
$child->access_path = $ap.'->'.$child->name;
} else {
$child->access_path = $ap.'->{'.\var_export($child->name, true).'}';
}
}

if (null !== $ap && $child->isAccessible($this->caller_class)) {
/** @psalm-var string $child->name */
if (Utils::isValidPhpName($child->name)) {
$child->access_path = $ap.'->'.$child->name;
if (KINT_PHP84 && $rprop->isVirtual()) {
$properties[] = new VirtualValue($child);
} elseif (!$initialized) {
$properties[] = new UninitializedValue($child);
} else {
$child->access_path = $ap.'->{'.\var_export($child->name, true).'}';
$properties[] = $this->parse($values[$key], $child);
}
}

if (KINT_PHP84 && $rprop->isVirtual()) {
$properties[] = new VirtualValue($child);
} elseif (!$initialized) {
$properties[] = new UninitializedValue($child);
} else {
$properties[] = $this->parse($values[$key], $child);
$object = new InstanceValue($c, $classname, $hash, \spl_object_id($var));
if ($props) {
$object->setChildren($properties);
}
}

$object = new InstanceValue($c, $classname, $hash, \spl_object_id($var));
if ($props) {
$object->setChildren($properties);
}
if ($properties) {
$object->addRepresentation(new ContainerRepresentation('Properties', $properties));
}

if ($properties) {
$object->addRepresentation(new ContainerRepresentation('Properties', $properties));
return $this->applyPluginsComplete($var, $object, self::TRIGGER_SUCCESS);
} finally {
unset($this->object_hashes[$hash]);
}

$object = $this->applyPluginsComplete($var, $object, self::TRIGGER_SUCCESS);
unset($this->object_hashes[$hash]);

return $object;
}

/**
Expand Down
46 changes: 46 additions & 0 deletions tests/Parser/ParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1425,4 +1425,50 @@ function () {

$this->fail("Didn't get warning");
}

public function testPluginsExceptionCleansUpRecursionMarkers()
{
$p = new Parser();
$b = new BaseContext('$v');
$v = new stdClass();
$v->v = $v;

$pl = new ProxyPlugin(
['object', 'array'],
Parser::TRIGGER_COMPLETE,
static function (&$var, AbstractValue $v) {
if ($v->getContext()->getDepth()) {
throw new Exception();
}
}
);
$p->addPlugin($pl);

try {
$o = $p->parse($v, clone $b);
} catch (Exception $e) {
}

$p->clearPlugins();

$o = $p->parse($v, clone $b);
$this->assertEquals(false, $o->flags & AbstractValue::FLAG_RECURSION);
$this->assertEquals(true, $o->getChildren()[0]->flags & AbstractValue::FLAG_RECURSION);

$v = [];
$v[] = &$v;

$p->addPlugin($pl);

try {
$o = $p->parse($v, clone $b);
} catch (Exception $e) {
}

$p->clearPlugins();

$o = $p->parse($v, clone $b);
$this->assertEquals(false, $o->flags & AbstractValue::FLAG_RECURSION);
$this->assertEquals(true, $o->getContents()[0]->flags & AbstractValue::FLAG_RECURSION);
}
}

0 comments on commit 8d4b712

Please sign in to comment.