Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ final class TrueInUnionBecomeBool
return true;
}

return strtoupper('warning');
return substr('warning', 1);
}
}

Expand All @@ -34,7 +34,7 @@ final class TrueInUnionBecomeBool
return true;
}

return strtoupper('warning');
return substr('warning', 1);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ final class TrueInUnion
return true;
}

return strtoupper('warning');
return substr('warning', 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of changing existing fixture, always make a new one. To make clear former one works.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is original fixture before the changes train, see PR order changes:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as before, do not change fixture, but rather add new one. Renaming a fuction that we talk about is confusing to read.

Copy link
Member

@TomasVotruba TomasVotruba Oct 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@samsonasik Exactly my point. Instead of renaming fixture content back and forth and having to read 3 PRs to understand, they should be always related only to current PR. These PRs should be independent on each other, standalone units.

}
}

Expand All @@ -34,7 +34,7 @@ final class TrueInUnion
return true;
}

return strtoupper('warning');
return substr('warning', 1);
}
}

Expand Down
9 changes: 9 additions & 0 deletions rules/DeadCode/Rector/Cast/RecastingRemovalRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
use PhpParser\Node\Expr\Cast\Int_;
use PhpParser\Node\Expr\Cast\Object_;
use PhpParser\Node\Expr\Cast\String_;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Reflection\Php\PhpPropertyReflection;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\MixedType;
Expand Down Expand Up @@ -103,6 +105,13 @@ public function refactor(Node $node): ?Node
return null;
}

// substr can return false on php 7.x
if ($node->expr instanceof FuncCall
&& $this->isName($node->expr, 'substr')
&& ! $node->expr->isFirstClassCallable()) {
$nodeType = new UnionType([new StringType(), new ConstantBooleanType(false)]);
}
Copy link
Member

@TomasVotruba TomasVotruba Oct 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be checked by version in Rector. If on PHP 8.0+, this should be skipped.
And new downgrade rule added to make it safe

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The downgrade rule need to exists first then, as without this, the cast string will be removed at

and add back the regression bug.

After downgrade rule created, then we can safely remove this check.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this first and add PHP version check, so we don't create regressions in existing rule.
Then make a downgrade rule 🙏

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need php version check, as phpstan already check it when run on php 7.x, just need add skip config in rector.php for now:

        // on php 7.x, substr() result can return false, so force (string) is needed
        RecastingRemovalRector::class => [
            __DIR__ . '/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/ClassLikeNameClassNameImportSkipVoter.php',
        ],

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


if ($nodeType instanceof ConstantArrayType && $nodeClass === Array_::class) {
if ($this->shouldSkip($node->expr)) {
return null;
Expand Down
6 changes: 0 additions & 6 deletions src/NodeTypeResolver/NodeTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ObjectWithoutClassType;
use PHPStan\Type\StringType;
use PHPStan\Type\ThisType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
Expand Down Expand Up @@ -621,11 +620,6 @@ private function resolveNativeTypeWithBuiltinMethodCallFallback(Expr $expr, Scop
return $scope->getNativeType($expr);
}

// substr can return false on php 7.x
if ($this->nodeNameResolver->isName($expr, 'substr') && ! $expr->isFirstClassCallable()) {
return new UnionType([new StringType(), new ConstantBooleanType(false)]);
}

return $scope->getType($expr);
}

Expand Down