Skip to content

Commit 9a718ee

Browse files
jack-wormanondrejmirtes
authored andcommitted
Fix-GH-12021
1 parent e2fa790 commit 9a718ee

File tree

3 files changed

+44
-0
lines changed

3 files changed

+44
-0
lines changed

src/Type/Php/NonEmptyStringFunctionsReturnTypeExtension.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@
22

33
namespace PHPStan\Type\Php;
44

5+
use PhpParser\Node\Arg;
56
use PhpParser\Node\Expr\FuncCall;
67
use PHPStan\Analyser\Scope;
78
use PHPStan\Reflection\FunctionReflection;
89
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
910
use PHPStan\Type\Accessory\AccessoryNonFalsyStringType;
11+
use PHPStan\Type\Constant\ConstantIntegerType;
1012
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
1113
use PHPStan\Type\IntersectionType;
1214
use PHPStan\Type\StringType;
1315
use PHPStan\Type\Type;
1416
use function count;
1517
use function in_array;
18+
use const ENT_SUBSTITUTE;
1619

1720
final class NonEmptyStringFunctionsReturnTypeExtension implements DynamicFunctionReturnTypeExtension
1821
{
@@ -45,6 +48,15 @@ public function getTypeFromFunctionCall(
4548
return null;
4649
}
4750

51+
if (in_array($functionReflection->getName(), [
52+
'htmlspecialchars',
53+
'htmlentities',
54+
], true)) {
55+
if (!$this->isSubstituteFlagSet($args, $scope)) {
56+
return new StringType();
57+
}
58+
}
59+
4860
$argType = $scope->getType($args[0]->value);
4961
if ($argType->isNonFalsyString()->yes()) {
5062
return new IntersectionType([
@@ -62,4 +74,22 @@ public function getTypeFromFunctionCall(
6274
return new StringType();
6375
}
6476

77+
/**
78+
* @param Arg[] $args
79+
*/
80+
private function isSubstituteFlagSet(
81+
array $args,
82+
Scope $scope,
83+
): bool
84+
{
85+
if (!isset($args[1])) {
86+
return true;
87+
}
88+
$flagsType = $scope->getType($args[1]->value);
89+
if (!$flagsType instanceof ConstantIntegerType) {
90+
return false;
91+
}
92+
return (bool) ($flagsType->getValue() & ENT_SUBSTITUTE);
93+
}
94+
6595
}

tests/PHPStan/Analyser/nsrt/non-empty-string.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use function strtolower;
99
use function strtoupper;
1010
use function ucfirst;
11+
use const ENT_SUBSTITUTE;
1112

1213
class Foo
1314
{
@@ -333,9 +334,17 @@ public function doFoo(string $s, string $nonEmpty, string $nonFalsy, int $i, boo
333334
assertType('string', ucwords($s));
334335
assertType('non-empty-string', ucwords($nonEmpty));
335336
assertType('string', htmlspecialchars($s));
337+
assertType('string', htmlspecialchars($s, ENT_SUBSTITUTE));
338+
assertType('string', htmlspecialchars($s, 0));
336339
assertType('non-empty-string', htmlspecialchars($nonEmpty));
340+
assertType('non-empty-string', htmlspecialchars($nonEmpty, ENT_SUBSTITUTE));
341+
assertType('string', htmlspecialchars($nonEmpty, 0));
337342
assertType('string', htmlentities($s));
343+
assertType('string', htmlentities($s, ENT_SUBSTITUTE));
344+
assertType('string', htmlentities($s, 0));
338345
assertType('non-empty-string', htmlentities($nonEmpty));
346+
assertType('non-empty-string', htmlentities($nonEmpty, ENT_SUBSTITUTE));
347+
assertType('string', htmlentities($nonEmpty, 0));
339348

340349
assertType('string', urlencode($s));
341350
assertType('non-empty-string', urlencode($nonEmpty));

tests/PHPStan/Analyser/nsrt/non-falsy-string.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace NonFalseyString;
44

55
use function PHPStan\Testing\assertType;
6+
use const ENT_SUBSTITUTE;
67

78
class Foo {
89
/**
@@ -95,7 +96,11 @@ function stringFunctions(string $s, $nonFalsey, $arrayOfNonFalsey, $nonEmptyArra
9596
assertType('non-falsy-string', ucfirst($nonFalsey));
9697
assertType('non-falsy-string', ucwords($nonFalsey));
9798
assertType('non-falsy-string', htmlspecialchars($nonFalsey));
99+
assertType('non-falsy-string', htmlspecialchars($nonFalsey, ENT_SUBSTITUTE));
100+
assertType('string', htmlspecialchars($nonFalsey, 0));
98101
assertType('non-falsy-string', htmlentities($nonFalsey));
102+
assertType('non-falsy-string', htmlentities($nonFalsey, ENT_SUBSTITUTE));
103+
assertType('string', htmlentities($nonFalsey, 0));
99104

100105
assertType('non-falsy-string', urlencode($nonFalsey));
101106
assertType('non-falsy-string', urldecode($nonFalsey));

0 commit comments

Comments
 (0)