Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 0 additions & 6 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -1560,12 +1560,6 @@ parameters:
count: 1
path: src/Type/Php/JsonThrowOnErrorDynamicReturnTypeExtension.php

-
message: '#^Doing instanceof PHPStan\\Type\\Constant\\ConstantStringType is error\-prone and deprecated\. Use Type\:\:getConstantStrings\(\) instead\.$#'
identifier: phpstanApi.instanceofType
count: 2
path: src/Type/Php/LtrimFunctionReturnTypeExtension.php

-
message: '#^Doing instanceof PHPStan\\Type\\Constant\\ConstantStringType is error\-prone and deprecated\. Use Type\:\:getConstantStrings\(\) instead\.$#'
identifier: phpstanApi.instanceofType
Expand Down
24 changes: 20 additions & 4 deletions src/Type/Php/LtrimFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use PHPStan\Type\IntersectionType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use function count;
use function ltrim;

Expand Down Expand Up @@ -53,12 +54,27 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,

$trimChars = $scope->getType($functionCall->getArgs()[1]->value);

if ($trimChars instanceof ConstantStringType && $trimChars->getValue() === '\\' && $string->isClassString()->yes()) {
if ($string instanceof ConstantStringType) {
return new ConstantStringType(ltrim($string->getValue(), $trimChars->getValue()), true);
$trimConstantStrings = $trimChars->getConstantStrings();
if (count($trimConstantStrings) > 0) {
$result = [];
$stringConstantStrings = $string->getConstantStrings();

foreach ($trimConstantStrings as $trimConstantString) {
if (count($stringConstantStrings) > 0) {
foreach ($stringConstantStrings as $stringConstantString) {
$result[] = new ConstantStringType(
ltrim($stringConstantString->getValue(), $trimConstantString->getValue()),
true,
);
}
} elseif ($trimConstantString->getValue() === '\\' && $string->isClassString()->yes()) {
$result[] = new ClassStringType();
} else {
return $defaultType;
}
}

return new ClassStringType();
return TypeCombinator::union(...$result);
}

return $defaultType;
Expand Down
39 changes: 37 additions & 2 deletions src/Type/Php/TrimFunctionDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Type\Accessory\AccessoryLowercaseStringType;
use PHPStan\Type\Accessory\AccessoryUppercaseStringType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use function count;
use function in_array;
use function rtrim;
use function trim;

#[AutowiredService]
final class TrimFunctionDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
Expand All @@ -37,6 +41,7 @@ public function getTypeFromFunctionCall(

$stringType = $scope->getType($args[0]->value);
$accessory = [];
$defaultType = new StringType();
if ($stringType->isLowercaseString()->yes()) {
$accessory[] = new AccessoryLowercaseStringType();
}
Expand All @@ -45,10 +50,40 @@ public function getTypeFromFunctionCall(
}
if (count($accessory) > 0) {
$accessory[] = new StringType();
return new IntersectionType($accessory);
$defaultType = new IntersectionType($accessory);
}

return new StringType();
if (count($functionCall->getArgs()) !== 2) {
return $defaultType;
}

$trimChars = $scope->getType($functionCall->getArgs()[1]->value);

$trimConstantStrings = $trimChars->getConstantStrings();
if (count($trimConstantStrings) > 0) {
$result = [];
$stringConstantStrings = $stringType->getConstantStrings();
$functionName = $functionReflection->getName();

foreach ($trimConstantStrings as $trimConstantString) {
if (count($stringConstantStrings) === 0) {
return $defaultType;
}

foreach ($stringConstantStrings as $stringConstantString) {
$result[] = new ConstantStringType(
$functionName === 'rtrim'
? rtrim($stringConstantString->getValue(), $trimConstantString->getValue())
: trim($stringConstantString->getValue(), $trimConstantString->getValue()),
true,
);
}
}

return TypeCombinator::union(...$result);
}

return $defaultType;
}

}
36 changes: 36 additions & 0 deletions tests/PHPStan/Analyser/nsrt/trim.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Trim;

use function PHPStan\Testing\assertType;

class Foo
{

/**
* @param 'foo' $foo
* @param 'foo'|'bar' $fooOrBar
* @param string $string
*/
public function doTrim($foo, $fooOrBar, $string): void
{
assertType('string', trim($string, $foo));
assertType('string', ltrim($string, $foo));
assertType('string', rtrim($string, $foo));

assertType('lowercase-string', trim($foo, $string));
assertType('lowercase-string', ltrim($foo, $string));
assertType('lowercase-string', rtrim($foo, $string));
assertType('\'\'|\'foo\'', trim($foo, $fooOrBar));
assertType('\'\'|\'foo\'', ltrim($foo, $fooOrBar));
assertType('\'\'|\'foo\'', rtrim($foo, $fooOrBar));

assertType('lowercase-string', trim($fooOrBar, $string));
assertType('lowercase-string', ltrim($fooOrBar, $string));
assertType('lowercase-string', rtrim($fooOrBar, $string));
assertType('\'\'|\'bar\'', trim($fooOrBar, $foo));
assertType('\'\'|\'bar\'', ltrim($fooOrBar, $foo));
assertType('\'\'|\'bar\'', rtrim($fooOrBar, $foo));
}

}
Loading