Skip to content

Commit

Permalink
Fix comparison with get_class() in traits always evaluate to true
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm committed Jan 8, 2023
1 parent 5503d87 commit 503e820
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/Type/Php/GetClassDynamicReturnTypeExtension.php
Expand Up @@ -17,6 +17,7 @@
use PHPStan\Type\ObjectType;
use PHPStan\Type\ObjectWithoutClassType;
use PHPStan\Type\StaticType;
use PHPStan\Type\ThisType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\TypeWithClassName;
Expand All @@ -34,7 +35,12 @@ public function isFunctionSupported(FunctionReflection $functionReflection): boo
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
{
$args = $functionCall->getArgs();

if (count($args) === 0) {
if ($scope->isInTrait()) {
return new ClassStringType();
}

if ($scope->isInClass()) {
return new ConstantStringType($scope->getClassReflection()->getName(), true);
}
Expand All @@ -44,6 +50,10 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,

$argType = $scope->getType($args[0]->value);

if ($scope->isInTrait() && $argType instanceof ThisType) {
return new ClassStringType();
}

return TypeTraverser::map(
$argType,
static function (Type $type, callable $traverse): Type {
Expand Down
Expand Up @@ -619,4 +619,63 @@ public function testBug8586(): void
$this->analyse([__DIR__ . '/data/bug-8586.php'], []);
}

public function testBug3633(): void
{
$this->checkAlwaysTrueStrictComparison = true;
$this->analyse([__DIR__ . '/data/bug-3633.php'], [
[
'Strict comparison using === between class-string<Bug3633\HelloWorld> and \'Bug3633\\\OtherClass\' will always evaluate to false.',
37,
],
[
'Strict comparison using === between \'Bug3633\\\HelloWorld\' and \'Bug3633\\\HelloWorld\' will always evaluate to true.',
41,
],
[
'Strict comparison using === between \'Bug3633\\\HelloWorld\' and \'Bug3633\\\OtherClass\' will always evaluate to false.',
44,
],
[
'Strict comparison using === between class-string<Bug3633\OtherClass> and \'Bug3633\\\HelloWorld\' will always evaluate to false.',
64,
],
[
'Strict comparison using === between \'Bug3633\\\OtherClass\' and \'Bug3633\\\HelloWorld\' will always evaluate to false.',
71,
],
[
'Strict comparison using === between \'Bug3633\\\OtherClass\' and \'Bug3633\\\OtherClass\' will always evaluate to true.',
74,
],
[
'Strict comparison using === between class-string<Bug3633\FinalClass> and \'Bug3633\\\HelloWorld\' will always evaluate to false.',
93,
],
[
'Strict comparison using === between class-string<Bug3633\FinalClass> and \'Bug3633\\\OtherClass\' will always evaluate to false.',
96,
],
[
'Strict comparison using === between \'Bug3633\\\FinalClass\' and \'Bug3633\\\FinalClass\' will always evaluate to true.',
102,
],
[
'Strict comparison using === between \'Bug3633\\\FinalClass\' and \'Bug3633\\\HelloWorld\' will always evaluate to false.',
106,
],
[
'Strict comparison using === between \'Bug3633\\\FinalClass\' and \'Bug3633\\\OtherClass\' will always evaluate to false.',
109,
],
[
'Strict comparison using !== between \'Bug3633\\\FinalClass\' and \'Bug3633\\\FinalClass\' will always evaluate to false.',
112,
],
[
'Strict comparison using === between \'Bug3633\\\FinalClass\' and \'Bug3633\\\FinalClass\' will always evaluate to true.',
115,
],
]);
}

}
128 changes: 128 additions & 0 deletions tests/PHPStan/Rules/Comparison/data/bug-3633.php
@@ -0,0 +1,128 @@
<?php declare(strict_types = 1);

namespace Bug3633;

trait Foo {
public function test($obj): void {
if (get_class($this) === HelloWorld::class) {
echo "OK";
}
if (get_class($this) === OtherClass::class) {
echo "OK";
}

if (get_class() === HelloWorld::class) {
echo "OK";
}
if (get_class() === OtherClass::class) {
echo "OK";
}

if (get_class($obj) === HelloWorld::class) {
echo "OK";
}
if (get_class($obj) === OtherClass::class) {
echo "OK";
}
}
}

class HelloWorld {
use Foo;

public function bar($obj): void {
if (get_class($this) === HelloWorld::class) {
echo "OK";
}
if (get_class($this) === OtherClass::class) {
echo "OK";
}

if (get_class() === HelloWorld::class) {
echo "OK";
}
if (get_class() === OtherClass::class) {
echo "OK";
}

if (get_class($obj) === HelloWorld::class) {
echo "OK";
}
if (get_class($obj) === OtherClass::class) {
echo "OK";
}


$this->test();
}
}

class OtherClass {
use Foo;

public function bar($obj): void {
if (get_class($this) === HelloWorld::class) {
echo "OK";
}
if (get_class($this) === OtherClass::class) {
echo "OK";
}

if (get_class() === HelloWorld::class) {
echo "OK";
}
if (get_class() === OtherClass::class) {
echo "OK";
}

if (get_class($obj) === HelloWorld::class) {
echo "OK";
}
if (get_class($obj) === OtherClass::class) {
echo "OK";
}

$this->test();
}
}

final class FinalClass {
use Foo;

public function bar($obj): void {
if (get_class($this) === HelloWorld::class) {
echo "OK";
}
if (get_class($this) === OtherClass::class) {
echo "OK";
}
if (get_class($this) !== FinalClass::class) {
echo "OK";
}
if (get_class($this) === FinalClass::class) {
echo "OK";
}

if (get_class() === HelloWorld::class) {
echo "OK";
}
if (get_class() === OtherClass::class) {
echo "OK";
}
if (get_class() !== FinalClass::class) {
echo "OK";
}
if (get_class() === FinalClass::class) {
echo "OK";
}

if (get_class($obj) === HelloWorld::class) {
echo "OK";
}
if (get_class($obj) === OtherClass::class) {
echo "OK";
}

$this->test();
}
}

0 comments on commit 503e820

Please sign in to comment.