Skip to content

Commit

Permalink
array_flip() for constant arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 26, 2022
1 parent 899fa4e commit a6cec39
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 0 deletions.
21 changes: 21 additions & 0 deletions src/Type/Php/ArrayFlipFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Type\Accessory\NonEmptyArrayType;
use PHPStan\Type\ArrayType;
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeUtils;
use function count;

class ArrayFlipFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
Expand All @@ -30,6 +32,25 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
$array = $functionCall->getArgs()[0]->value;
$argType = $scope->getType($array);

$constantArrays = TypeUtils::getOldConstantArrays($argType);
if (count($constantArrays) > 0) {
$flipped = [];
foreach ($constantArrays as $constantArray) {
$builder = ConstantArrayTypeBuilder::createEmpty();
foreach ($constantArray->getKeyTypes() as $i => $keyType) {
$valueType = $constantArray->getValueTypes()[$i];
$builder->setOffsetValueType(
ArrayType::castToArrayKeyType($valueType),
$keyType,
$constantArray->isOptionalKey($i),
);
}
$flipped[] = $builder->getArray();
}

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

if ($argType->isArray()->yes()) {
$keyType = $argType->getIterableKeyType();
$itemType = $argType->getIterableValueType();
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7563.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7764.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5845.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/array-flip-constant.php');
}

/**
Expand Down
26 changes: 26 additions & 0 deletions tests/PHPStan/Analyser/data/array-flip-constant.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace ArrayFlipConstantArray;

use function array_flip;
use function PHPStan\Testing\assertType;

class Foo
{

public function doFoo(): void
{
$allowlist = ['name', 'description', 'author', 'type', 'homepage', 'require', 'require-dev', 'stability', 'license', 'autoload'];
assertType('array{name: 0, description: 1, author: 2, type: 3, homepage: 4, require: 5, require-dev: 6, stability: 7, license: 8, autoload: 9}', array_flip($allowlist));
}

public function doOptional(): void
{
$allowlist = ['name', 'description', 'author', 'type', 'homepage', 'require', 'require-dev', 'stability', 'license', 'autoload'];
if (rand(0, 1)) {
$allowlist[] = 'config';
}
assertType('array{name: 0, description: 1, author: 2, type: 3, homepage: 4, require: 5, require-dev: 6, stability: 7, license: 8, autoload: 9, config?: 10}', array_flip($allowlist));
}

}

0 comments on commit a6cec39

Please sign in to comment.