Skip to content

Commit

Permalink
Merge pull request #10290 from robchett/method_exists_exponential_growth
Browse files Browse the repository at this point in the history
Fix memory explosion with calls to method_exists
  • Loading branch information
orklah committed Oct 17, 2023
2 parents db82d26 + e2d1e83 commit 1cdef40
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class AtomicMethodCallAnalysisResult
public array $invalid_method_call_types = [];

/**
* @var array<string>
* @var array<string, bool>
*/
public array $existent_method_ids = [];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ public static function analyze(
$all_intersection_return_type = null;
$all_intersection_existent_method_ids = [];

// insersection types are also fun, they also complicate matters
// intersection types are also fun, they also complicate matters
if ($intersection_types) {
[$all_intersection_return_type, $all_intersection_existent_method_ids]
= self::getIntersectionReturnType(
Expand Down Expand Up @@ -525,7 +525,7 @@ public static function analyze(
/**
* @param TNamedObject|TTemplateParam $lhs_type_part
* @param array<string, Atomic> $intersection_types
* @return array{?Union, array<string>}
* @return array{?Union, array<string, bool>}
*/
private static function getIntersectionReturnType(
StatementsAnalyzer $statements_analyzer,
Expand Down Expand Up @@ -646,7 +646,8 @@ private static function handleInvalidClass(
&& $stmt->name instanceof PhpParser\Node\Identifier
&& isset($lhs_type_part->methods[strtolower($stmt->name->name)])
) {
$result->existent_method_ids[] = $lhs_type_part->methods[strtolower($stmt->name->name)];
$method_id = $lhs_type_part->methods[strtolower($stmt->name->name)];
$result->existent_method_ids[$method_id] = true;
} elseif (!$is_intersection) {
if ($stmt->name instanceof PhpParser\Node\Identifier) {
$codebase->analyzer->addMixedMemberName(
Expand Down Expand Up @@ -915,7 +916,7 @@ private static function handleCallableObject(
?TemplateResult $inferred_template_result = null
): void {
$method_id = 'object::__invoke';
$result->existent_method_ids[] = $method_id;
$result->existent_method_ids[$method_id] = true;
$result->has_valid_method_call_type = true;

if ($lhs_type_part_callable !== null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ public static function analyze(

$cased_method_id = $fq_class_name . '::' . $stmt_name->name;

$result->existent_method_ids[] = $method_id->__toString();

$result->existent_method_ids[$method_id->__toString()] = true;

if ($context->collect_initializations && $context->calling_method_id) {
[$calling_method_class] = explode('::', $context->calling_method_id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static function handleMagicMethod(
if ($stmt->isFirstClassCallable()) {
if (isset($class_storage->pseudo_methods[$method_name_lc])) {
$result->has_valid_method_call_type = true;
$result->existent_method_ids[] = $method_id->__toString();
$result->existent_method_ids[$method_id->__toString()] = true;
$result->return_type = self::createFirstClassCallableReturnType(
$class_storage->pseudo_methods[$method_name_lc],
);
Expand Down Expand Up @@ -110,7 +110,7 @@ public static function handleMagicMethod(

if ($found_method_and_class_storage) {
$result->has_valid_method_call_type = true;
$result->existent_method_ids[] = $method_id->__toString();
$result->existent_method_ids[$method_id->__toString()] = true;

[$pseudo_method_storage, $defining_class_storage] = $found_method_and_class_storage;

Expand Down Expand Up @@ -198,7 +198,7 @@ public static function handleMagicMethod(
}

$result->has_valid_method_call_type = true;
$result->existent_method_ids[] = $method_id->__toString();
$result->existent_method_ids[$method_id->__toString()] = true;

$array_values = array_map(
static fn(PhpParser\Node\Arg $arg): PhpParser\Node\Expr\ArrayItem => new VirtualArrayItem(
Expand Down Expand Up @@ -235,7 +235,7 @@ public static function handleMagicMethod(
}

/**
* @param array<string> $all_intersection_existent_method_ids
* @param array<string, bool> $all_intersection_existent_method_ids
*/
public static function handleMissingOrMagicMethod(
StatementsAnalyzer $statements_analyzer,
Expand Down Expand Up @@ -267,7 +267,7 @@ public static function handleMissingOrMagicMethod(
&& $found_method_and_class_storage
) {
$result->has_valid_method_call_type = true;
$result->existent_method_ids[] = $method_id->__toString();
$result->existent_method_ids[$method_id->__toString()] = true;

[$pseudo_method_storage, $defining_class_storage] = $found_method_and_class_storage;

Expand Down
16 changes: 16 additions & 0 deletions tests/MethodCallTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,22 @@ function foo(DateTime $d1, DateTime $d2) : void {
);
}',
],
'methodExistsDoesntExhaustMemory' => [
'code' => '<?php
class C {}
function f(C $c): void {
method_exists($c, \'a\') ? $c->a() : [];
method_exists($c, \'b\') ? $c->b() : [];
method_exists($c, \'c\') ? $c->c() : [];
method_exists($c, \'d\') ? $c->d() : [];
method_exists($c, \'e\') ? $c->e() : [];
method_exists($c, \'f\') ? $c->f() : [];
method_exists($c, \'g\') ? $c->g() : [];
method_exists($c, \'h\') ? $c->h() : [];
method_exists($c, \'i\') ? $c->i() : [];
}',
],
'callMethodAfterCheckingExistence' => [
'code' => '<?php
class A {}
Expand Down

0 comments on commit 1cdef40

Please sign in to comment.