-
Notifications
You must be signed in to change notification settings - Fork 457
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Type\Php; | ||
|
||
use PhpParser\Node\Expr\FuncCall; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Reflection\FunctionReflection; | ||
use PHPStan\Type\Accessory\AccessoryArrayListType; | ||
use PHPStan\Type\Accessory\AccessoryLowercaseStringType; | ||
use PHPStan\Type\Accessory\NonEmptyArrayType; | ||
use PHPStan\Type\ArrayType; | ||
use PHPStan\Type\Constant\ConstantArrayTypeBuilder; | ||
use PHPStan\Type\Constant\ConstantStringType; | ||
use PHPStan\Type\DynamicFunctionReturnTypeExtension; | ||
use PHPStan\Type\StringType; | ||
use PHPStan\Type\Type; | ||
use PHPStan\Type\TypeCombinator; | ||
use PHPStan\Type\TypeTraverser; | ||
use PHPStan\Type\TypeUtils; | ||
use PHPStan\Type\UnionType; | ||
|
||
final class ArrayChangeKeyCaseFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension | ||
{ | ||
|
||
public function isFunctionSupported(FunctionReflection $functionReflection): bool | ||
{ | ||
return 'array_change_key_case' === $functionReflection->getName(); | ||
} | ||
|
||
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type | ||
{ | ||
if (!isset($functionCall->getArgs()[0])) { | ||
return null; | ||
} | ||
|
||
$arrayType = $scope->getType($functionCall->getArgs()[0]->value); | ||
if (!isset($functionCall->getArgs()[1])) { | ||
$case = CASE_LOWER; | ||
} else { | ||
$caseType = $scope->getType($functionCall->getArgs()[1]->value); | ||
$scalarValues = $caseType->getConstantScalarValues(); | ||
if (\count($scalarValues) === 1) { | ||
$case = $scalarValues[0]; | ||
} else { | ||
$case = null; | ||
} | ||
} | ||
|
||
$constantArrays = $arrayType->getConstantArrays(); | ||
if (count($constantArrays) > 0) { | ||
$arrayTypes = []; | ||
foreach ($constantArrays as $constantArray) { | ||
$newConstantArrayBuilder = ConstantArrayTypeBuilder::createEmpty(); | ||
foreach ($constantArray->getKeyTypes() as $i => $keyType) { | ||
$valueType = $constantArray->getOffsetValueType($keyType); | ||
if ($keyType->isString()->yes()) { | ||
if (!isset($case)) { | ||
$keyType = TypeCombinator::union( | ||
Check failure on line 58 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.2, ubuntu-latest)
Check failure on line 58 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.2, ubuntu-latest)
Check failure on line 58 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.2, windows-latest)
|
||
new ConstantStringType(strtolower($keyType->getValue())), | ||
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.3)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.4)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.1)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.1, ubuntu-latest)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.2)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.3, ubuntu-latest)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.2, ubuntu-latest)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.4, ubuntu-latest)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.0, ubuntu-latest)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.3, ubuntu-latest)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.4, ubuntu-latest)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.2, windows-latest)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.1, windows-latest)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.4, windows-latest)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.3, windows-latest)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.3, windows-latest)
Check failure on line 59 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.4, windows-latest)
|
||
new ConstantStringType(strtoupper($keyType->getValue())), | ||
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.3)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.4)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.1)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.1, ubuntu-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.2)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.3, ubuntu-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.2, ubuntu-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.4, ubuntu-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.0, ubuntu-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.3, ubuntu-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.4, ubuntu-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.2, ubuntu-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.2, windows-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.1, windows-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.4, windows-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.3, windows-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.3, windows-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.4, windows-latest)
Check failure on line 60 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.0, windows-latest)
|
||
); | ||
} elseif ($case === CASE_LOWER) { | ||
Check failure on line 62 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.2, ubuntu-latest)
|
||
$keyType = new ConstantStringType(strtolower($keyType->getValue())); | ||
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.3)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.4)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.1)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.1, ubuntu-latest)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.2)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.3, ubuntu-latest)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.2, ubuntu-latest)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.4, ubuntu-latest)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.0, ubuntu-latest)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.3, ubuntu-latest)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.4, ubuntu-latest)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.2, windows-latest)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.1, windows-latest)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.4, windows-latest)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.3, windows-latest)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.3, windows-latest)
Check failure on line 63 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.4, windows-latest)
|
||
} else { | ||
$keyType = new ConstantStringType(strtoupper($keyType->getValue())); | ||
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.3)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.4)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.1)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.1, ubuntu-latest)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan with result cache (8.2)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.3, ubuntu-latest)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.2, ubuntu-latest)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.4, ubuntu-latest)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.0, ubuntu-latest)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.3, ubuntu-latest)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.4, ubuntu-latest)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.2, windows-latest)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.1, windows-latest)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.4, windows-latest)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (8.3, windows-latest)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.3, windows-latest)
Check failure on line 65 in src/Type/Php/ArrayChangeKeyCaseFunctionReturnTypeExtension.php GitHub Actions / PHPStan (7.4, windows-latest)
|
||
} | ||
} | ||
|
||
$newConstantArrayBuilder->setOffsetValueType( | ||
$keyType, | ||
$valueType, | ||
$constantArray->isOptionalKey($i), | ||
); | ||
} | ||
$newConstantArrayType = $newConstantArrayBuilder->getArray(); | ||
if ($constantArray->isList()->yes()) { | ||
$newConstantArrayType = AccessoryArrayListType::intersectWith($newConstantArrayType); | ||
} | ||
$arrayTypes[] = $newConstantArrayType; | ||
} | ||
|
||
$newArrayType = TypeCombinator::union(...$arrayTypes); | ||
} else { | ||
$keysType = $arrayType->getIterableKeyType(); | ||
|
||
$keysType = TypeTraverser::map($keysType, static function (Type $type, callable $traverse) use ($case): Type { | ||
if ($type instanceof UnionType) { | ||
return $traverse($type); | ||
} | ||
|
||
if ($type->isString()->yes()) { | ||
if ($case === CASE_LOWER) { | ||
return TypeCombinator::intersect($type, new AccessoryLowercaseStringType()); | ||
} elseif ($type->isLowercaseString()->yes()) { | ||
return TypeCombinator::intersect( | ||
new StringType(), | ||
...array_filter( | ||
TypeUtils::getAccessoryTypes($type), | ||
static fn(Type $accessory): bool => !$accessory instanceof AccessoryLowercaseStringType | ||
), | ||
); | ||
} | ||
} | ||
|
||
return $type; | ||
}); | ||
|
||
$newArrayType = TypeCombinator::intersect(new ArrayType( | ||
$keysType, | ||
$arrayType->getIterableValueType(), | ||
), ...TypeUtils::getAccessoryTypes($arrayType)); | ||
} | ||
|
||
if ($arrayType->isIterableAtLeastOnce()->yes()) { | ||
$newArrayType = TypeCombinator::intersect($newArrayType, new NonEmptyArrayType()); | ||
} | ||
|
||
return $newArrayType; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace ArrayChangeKeyCase; | ||
|
||
use function PHPStan\Testing\assertType; | ||
|
||
class HelloWorld | ||
{ | ||
/** | ||
* @param array<string> $arr1 | ||
* @param array<string, string> $arr2 | ||
* @param array<string|int, string> $arr3 | ||
* @param array<int, string> $arr4 | ||
* @param array<lowercase-string, string> $arr5 | ||
* @param array<lowercase-string&non-falsy-string, string> $arr6 | ||
* @param array{foo: 1, bar?: 2} $arr7 | ||
* @param list<string> $list | ||
* @param non-empty-array<string> $nonEmpty | ||
*/ | ||
public function sayHello( | ||
array $arr1, | ||
array $arr2, | ||
array $arr3, | ||
array $arr4, | ||
array $arr5, | ||
array $arr6, | ||
array $arr7, | ||
array $list, | ||
array $nonEmpty, | ||
int $case | ||
): void { | ||
assertType('array<string>', array_change_key_case($arr1)); | ||
assertType('array<string>', array_change_key_case($arr1, CASE_LOWER)); | ||
assertType('array<string>', array_change_key_case($arr1, CASE_UPPER)); | ||
assertType('array<string>', array_change_key_case($arr1, $case)); | ||
|
||
assertType('array<lowercase-string, string>', array_change_key_case($arr2)); | ||
assertType('array<lowercase-string, string>', array_change_key_case($arr2, CASE_LOWER)); | ||
assertType('array<string, string>', array_change_key_case($arr2, CASE_UPPER)); | ||
assertType('array<string, string>', array_change_key_case($arr2, $case)); | ||
|
||
assertType('array<int|lowercase-string, string>', array_change_key_case($arr3)); | ||
assertType('array<int|lowercase-string, string>', array_change_key_case($arr3, CASE_LOWER)); | ||
assertType('array<int|string, string>', array_change_key_case($arr3, CASE_UPPER)); | ||
assertType('array<int|string, string>', array_change_key_case($arr3, $case)); | ||
|
||
assertType('array<int, string>', array_change_key_case($arr4)); | ||
assertType('array<int, string>', array_change_key_case($arr4, CASE_LOWER)); | ||
assertType('array<int, string>', array_change_key_case($arr4, CASE_UPPER)); | ||
assertType('array<int, string>', array_change_key_case($arr4, $case)); | ||
|
||
assertType('array<lowercase-string, string>', array_change_key_case($arr5)); | ||
assertType('array<lowercase-string, string>', array_change_key_case($arr5, CASE_LOWER)); | ||
assertType('array<string, string>', array_change_key_case($arr5, CASE_UPPER)); | ||
assertType('array<string, string>', array_change_key_case($arr5, $case)); | ||
|
||
assertType('array<lowercase-string&non-falsy-string, string>', array_change_key_case($arr6)); | ||
assertType('array<lowercase-string&non-falsy-string, string>', array_change_key_case($arr6, CASE_LOWER)); | ||
assertType('array<non-falsy-string, string>', array_change_key_case($arr6, CASE_UPPER)); | ||
assertType('array<non-falsy-string, string>', array_change_key_case($arr6, $case)); | ||
|
||
assertType('array{foo: 1, bar?: 2}', array_change_key_case($arr7)); | ||
assertType('array{foo: 1, bar?: 2}', array_change_key_case($arr7, CASE_LOWER)); | ||
assertType('array{FOO: 1, BAR?: 2}', array_change_key_case($arr7, CASE_UPPER)); | ||
assertType("non-empty-array<'BAR'|'bar'|'FOO'|'foo', 1|2>", array_change_key_case($arr7, $case)); | ||
|
||
assertType('list<string>', array_change_key_case($list)); | ||
assertType('list<string>', array_change_key_case($list, CASE_LOWER)); | ||
assertType('list<string>', array_change_key_case($list, CASE_UPPER)); | ||
assertType('list<string>', array_change_key_case($list, $case)); | ||
|
||
assertType('non-empty-array<string>', array_change_key_case($nonEmpty)); | ||
assertType('non-empty-array<string>', array_change_key_case($nonEmpty, CASE_LOWER)); | ||
assertType('non-empty-array<string>', array_change_key_case($nonEmpty, CASE_UPPER)); | ||
assertType('non-empty-array<string>', array_change_key_case($nonEmpty, $case)); | ||
} | ||
} |