Skip to content

Commit b04bb5a

Browse files
authored
Recognize [1 => 'method', 0 => $obj] as callable
1 parent 778b569 commit b04bb5a

File tree

3 files changed

+79
-18
lines changed

3 files changed

+79
-18
lines changed

src/Type/Constant/ConstantArrayType.php

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -503,23 +503,46 @@ public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope)
503503
return $acceptors;
504504
}
505505

506-
/** @deprecated Use findTypeAndMethodNames() instead */
507-
public function findTypeAndMethodName(): ?ConstantArrayTypeAndMethod
506+
/**
507+
* @return array{Type, Type}|array{}
508+
*/
509+
private function getClassOrObjectAndMethods(): array
508510
{
509511
if (count($this->keyTypes) !== 2) {
510-
return null;
512+
return [];
511513
}
512514

513-
if ($this->keyTypes[0]->isSuperTypeOf(new ConstantIntegerType(0))->no()) {
514-
return null;
515+
$classOrObject = null;
516+
$method = null;
517+
foreach ($this->keyTypes as $i => $keyType) {
518+
if ($keyType->isSuperTypeOf(new ConstantIntegerType(0))->yes()) {
519+
$classOrObject = $this->valueTypes[$i];
520+
continue;
521+
}
522+
523+
if (!$keyType->isSuperTypeOf(new ConstantIntegerType(1))->yes()) {
524+
continue;
525+
}
526+
527+
$method = $this->valueTypes[$i];
515528
}
516529

517-
if ($this->keyTypes[1]->isSuperTypeOf(new ConstantIntegerType(1))->no()) {
518-
return null;
530+
if ($classOrObject === null || $method === null) {
531+
return [];
519532
}
520533

521-
[$classOrObject, $method] = $this->valueTypes;
534+
return [$classOrObject, $method];
535+
}
536+
537+
/** @deprecated Use findTypeAndMethodNames() instead */
538+
public function findTypeAndMethodName(): ?ConstantArrayTypeAndMethod
539+
{
540+
$callableArray = $this->getClassOrObjectAndMethods();
541+
if ($callableArray === []) {
542+
return null;
543+
}
522544

545+
[$classOrObject, $method] = $callableArray;
523546
if (!$method instanceof ConstantStringType) {
524547
return ConstantArrayTypeAndMethod::createUnknown();
525548
}
@@ -544,19 +567,12 @@ public function findTypeAndMethodName(): ?ConstantArrayTypeAndMethod
544567
/** @return ConstantArrayTypeAndMethod[] */
545568
public function findTypeAndMethodNames(): array
546569
{
547-
if (count($this->keyTypes) !== 2) {
548-
return [];
549-
}
550-
551-
if ($this->keyTypes[0]->isSuperTypeOf(new ConstantIntegerType(0))->no()) {
552-
return [];
553-
}
554-
555-
if ($this->keyTypes[1]->isSuperTypeOf(new ConstantIntegerType(1))->no()) {
570+
$callableArray = $this->getClassOrObjectAndMethods();
571+
if ($callableArray === []) {
556572
return [];
557573
}
558574

559-
[$classOrObject, $methods] = $this->valueTypes;
575+
[$classOrObject, $methods] = $callableArray;
560576
if (count($methods->getConstantStrings()) === 0) {
561577
return [ConstantArrayTypeAndMethod::createUnknown()];
562578
}

tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,4 +1036,23 @@ public function testLooseComparisonAgainstEnumsNoPhpdoc(): void
10361036
$this->analyse([__DIR__ . '/data/loose-comparison-against-enums.php'], $issues);
10371037
}
10381038

1039+
public function testBug10502(): void
1040+
{
1041+
$tipText = 'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.';
1042+
1043+
$this->checkAlwaysTrueCheckTypeFunctionCall = true;
1044+
$this->treatPhpDocTypesAsCertain = true;
1045+
$this->analyse([__DIR__ . '/data/bug-10502.php'], [
1046+
[
1047+
"Call to function is_callable() with array{ArrayObject<int, int>, 'count'} will always evaluate to true.",
1048+
23,
1049+
],
1050+
[
1051+
"Call to function is_callable() with array{1: 'count', 0: ArrayObject<int, int>} will always evaluate to true.",
1052+
24,
1053+
$tipText,
1054+
],
1055+
]);
1056+
}
1057+
10391058
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Bug10502;
4+
5+
use ArrayObject;
6+
7+
/** @param ?ArrayObject<int, int> $x */
8+
function doFoo(?ArrayObject $x):void {
9+
$callable1 = [$x, 'count'];
10+
$callable2 = array_reverse($callable1, true);
11+
12+
var_dump(
13+
is_callable($callable1),
14+
is_callable($callable2)
15+
);
16+
}
17+
18+
function doBar():void {
19+
$callable1 = [new ArrayObject([0]), 'count'];
20+
$callable2 = array_reverse($callable1, true);
21+
22+
var_dump(
23+
is_callable($callable1),
24+
is_callable($callable2)
25+
);
26+
}

0 commit comments

Comments
 (0)