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
6 changes: 3 additions & 3 deletions src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -747,8 +747,8 @@ public function specifyTypesInCondition(
) {
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
$iterableValueType = $expr->expr->name->toLowerString() === 'array_key_first'
? $arrayType->getFirstIterableValueType()
: $arrayType->getLastIterableValueType();
? $arrayType->getIterableValueType()
: $arrayType->getIterableValueType();

return $specifiedTypes->unionWith(
$this->create($dimFetch, $iterableValueType, TypeSpecifierContext::createTrue(), $scope),
Expand All @@ -775,7 +775,7 @@ public function specifyTypesInCondition(
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);

return $specifiedTypes->unionWith(
$this->create($dimFetch, $arrayType->getLastIterableValueType(), TypeSpecifierContext::createTrue(), $scope),
$this->create($dimFetch, $arrayType->getIterableValueType(), TypeSpecifierContext::createTrue(), $scope),
);
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/Type/IntersectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -690,12 +690,12 @@ public function getIterableKeyType(): Type

public function getFirstIterableKeyType(): Type
{
return $this->intersectTypes(static fn (Type $type): Type => $type->getFirstIterableKeyType());
return $this->intersectTypes(static fn (Type $type): Type => $type->getIterableKeyType());
}

public function getLastIterableKeyType(): Type
{
return $this->intersectTypes(static fn (Type $type): Type => $type->getLastIterableKeyType());
return $this->intersectTypes(static fn (Type $type): Type => $type->getIterableKeyType());
}

public function getIterableValueType(): Type
Expand All @@ -705,12 +705,12 @@ public function getIterableValueType(): Type

public function getFirstIterableValueType(): Type
{
return $this->intersectTypes(static fn (Type $type): Type => $type->getFirstIterableValueType());
return $this->intersectTypes(static fn (Type $type): Type => $type->getIterableValueType());
}

public function getLastIterableValueType(): Type
{
return $this->intersectTypes(static fn (Type $type): Type => $type->getLastIterableValueType());
return $this->intersectTypes(static fn (Type $type): Type => $type->getIterableValueType());
}

public function isArray(): TrinaryLogic
Expand Down
49 changes: 49 additions & 0 deletions src/Type/Php/ArrayFirstLastDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Php;

use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
use PHPStan\Type\NullType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use function count;
use function in_array;

#[AutowiredService]
final class ArrayFirstLastDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
{

public function isFunctionSupported(FunctionReflection $functionReflection): bool
{
return in_array($functionReflection->getName(), ['array_first', 'array_last'], true);
}

public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type
{
$args = $functionCall->getArgs();

if (count($args) < 1) {
return null;
}

$argType = $scope->getType($args[0]->value);
$iterableAtLeastOnce = $argType->isIterableAtLeastOnce();

if ($iterableAtLeastOnce->no()) {
return new NullType();
}

$valueType = $argType->getIterableValueType();

if ($iterableAtLeastOnce->yes()) {
return $valueType;
}

return TypeCombinator::union($valueType, new NullType());
}

}
2 changes: 1 addition & 1 deletion src/Type/Php/ArrayKeyFirstDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
return new NullType();
}

$keyType = $argType->getFirstIterableKeyType();
$keyType = $argType->getIterableKeyType();
if ($iterableAtLeastOnce->yes()) {
return $keyType;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Type/Php/ArrayKeyLastDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
return new NullType();
}

$keyType = $argType->getLastIterableKeyType();
$keyType = $argType->getIterableKeyType();
if ($iterableAtLeastOnce->yes()) {
return $keyType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ public function getTypeFromFunctionCall(
}

$itemType = $functionReflection->getName() === 'reset'
? $argType->getFirstIterableValueType()
: $argType->getLastIterableValueType();
? $argType->getIterableValueType()
: $argType->getIterableValueType();
if ($iterableAtLeastOnce->yes()) {
return $itemType;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Type/Php/ArrayPopFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
return new NullType();
}

$itemType = $argType->getLastIterableValueType();
$itemType = $argType->getIterableValueType();
if ($iterableAtLeastOnce->yes()) {
return $itemType;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Type/Php/ArrayShiftFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
return new NullType();
}

$itemType = $argType->getFirstIterableValueType();
$itemType = $argType->getIterableValueType();
if ($iterableAtLeastOnce->yes()) {
return $itemType;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Type/StaticType.php
Original file line number Diff line number Diff line change
Expand Up @@ -401,12 +401,12 @@ public function getIterableKeyType(): Type

public function getFirstIterableKeyType(): Type
{
return $this->getStaticObjectType()->getFirstIterableKeyType();
return $this->getStaticObjectType()->getIterableKeyType();
}

public function getLastIterableKeyType(): Type
{
return $this->getStaticObjectType()->getLastIterableKeyType();
return $this->getStaticObjectType()->getIterableKeyType();
}

public function getIterableValueType(): Type
Expand All @@ -416,12 +416,12 @@ public function getIterableValueType(): Type

public function getFirstIterableValueType(): Type
{
return $this->getStaticObjectType()->getFirstIterableValueType();
return $this->getStaticObjectType()->getIterableValueType();
}

public function getLastIterableValueType(): Type
{
return $this->getStaticObjectType()->getLastIterableValueType();
return $this->getStaticObjectType()->getIterableValueType();
}

public function isOffsetAccessible(): TrinaryLogic
Expand Down
8 changes: 4 additions & 4 deletions src/Type/Traits/LateResolvableTypeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,12 @@ public function getIterableKeyType(): Type

public function getFirstIterableKeyType(): Type
{
return $this->resolve()->getFirstIterableKeyType();
return $this->resolve()->getIterableKeyType();
}

public function getLastIterableKeyType(): Type
{
return $this->resolve()->getLastIterableKeyType();
return $this->resolve()->getIterableKeyType();
}

public function getIterableValueType(): Type
Expand All @@ -215,12 +215,12 @@ public function getIterableValueType(): Type

public function getFirstIterableValueType(): Type
{
return $this->resolve()->getFirstIterableValueType();
return $this->resolve()->getIterableValueType();
}

public function getLastIterableValueType(): Type
{
return $this->resolve()->getLastIterableValueType();
return $this->resolve()->getIterableValueType();
}

public function isArray(): TrinaryLogic
Expand Down
4 changes: 4 additions & 0 deletions src/Type/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,18 @@ public function getArraySize(): Type;

public function getIterableKeyType(): Type;

/** @deprecated use getIterableKeyType */
public function getFirstIterableKeyType(): Type;

/** @deprecated use getIterableKeyType */
public function getLastIterableKeyType(): Type;

public function getIterableValueType(): Type;

/** @deprecated use getIterableValueType */
public function getFirstIterableValueType(): Type;

/** @deprecated use getIterableValueType */
public function getLastIterableValueType(): Type;

public function isArray(): TrinaryLogic;
Expand Down
2 changes: 1 addition & 1 deletion src/Type/TypeCombinator.php
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,7 @@ private static function optimizeConstantArrays(array $types): array
$valueTypes = [];
foreach ($results as $result) {
$keyTypes[] = $result->getIterableKeyType();
$valueTypes[] = $result->getLastIterableValueType();
$valueTypes[] = $result->getIterableValueType();
if ($result->isList()->yes()) {
continue;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Type/UnionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -639,12 +639,12 @@ public function getIterableKeyType(): Type

public function getFirstIterableKeyType(): Type
{
return $this->unionTypes(static fn (Type $type): Type => $type->getFirstIterableKeyType());
return $this->unionTypes(static fn (Type $type): Type => $type->getIterableKeyType());
}

public function getLastIterableKeyType(): Type
{
return $this->unionTypes(static fn (Type $type): Type => $type->getLastIterableKeyType());
return $this->unionTypes(static fn (Type $type): Type => $type->getIterableKeyType());
}

public function getIterableValueType(): Type
Expand All @@ -654,12 +654,12 @@ public function getIterableValueType(): Type

public function getFirstIterableValueType(): Type
{
return $this->unionTypes(static fn (Type $type): Type => $type->getFirstIterableValueType());
return $this->unionTypes(static fn (Type $type): Type => $type->getIterableValueType());
}

public function getLastIterableValueType(): Type
{
return $this->unionTypes(static fn (Type $type): Type => $type->getLastIterableValueType());
return $this->unionTypes(static fn (Type $type): Type => $type->getIterableValueType());
}

public function isArray(): TrinaryLogic
Expand Down
44 changes: 22 additions & 22 deletions tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4790,7 +4790,7 @@ public static function dataArrayFunctions(): array
'array_map(function (): \stdClass {}, $conditionalKeysArray)',
],
[
'stdClass',
'\'foo\'|stdClass',
'array_pop($stringKeys)',
],
[
Expand All @@ -4802,7 +4802,7 @@ public static function dataArrayFunctions(): array
'array_pop($stdClassesWithIsset)',
],
[
'\'foo\'',
'\'foo\'|stdClass',
'array_shift($stringKeys)',
],
[
Expand Down Expand Up @@ -7169,23 +7169,23 @@ public static function dataArrayPointerFunctions(): array
'reset($emptyConstantArray)',
],
[
'1',
'1|2',
'reset($constantArray)',
],
[
'\'baz\'|\'foo\'',
'\'bar\'|\'baz\'|\'foo\'',
'reset($conditionalArray)',
],
[
'0|1',
'0|1|2',
'reset($constantArrayOptionalKeys1)',
],
[
'0',
'0|1|2',
'reset($constantArrayOptionalKeys2)',
],
[
'0',
'0|1|2',
'reset($constantArrayOptionalKeys3)',
],
[
Expand All @@ -7205,23 +7205,23 @@ public static function dataArrayPointerFunctions(): array
'end($emptyConstantArray)',
],
[
'2',
'1|2',
'end($constantArray)',
],
[
'\'bar\'|\'baz\'',
'\'bar\'|\'baz\'|\'foo\'',
'end($secondConditionalArray)',
],
[
'2',
'0|1|2',
'end($constantArrayOptionalKeys1)',
],
[
'2',
'0|1|2',
'end($constantArrayOptionalKeys2)',
],
[
'1|2',
'0|1|2',
'end($constantArrayOptionalKeys3)',
],
];
Expand Down Expand Up @@ -8644,43 +8644,43 @@ public static function dataPhp73Functions(): array
'array_key_last($emptyArray)',
],
[
'0',
'0|1|2',
'array_key_first($literalArray)',
],
[
'2',
'0|1|2',
'array_key_last($literalArray)',
],
[
'0',
'0|1|2|3',
'array_key_first($anotherLiteralArray)',
],
[
'2|3',
'0|1|2|3',
'array_key_last($anotherLiteralArray)',
],
[
"'a'|'b'",
"'a'|'b'|'c'",
'array_key_first($constantArrayOptionalKeys1)',
],
[
"'c'",
"'a'|'b'|'c'",
'array_key_last($constantArrayOptionalKeys1)',
],
[
"'a'",
"'a'|'b'|'c'",
'array_key_first($constantArrayOptionalKeys2)',
],
[
"'c'",
"'a'|'b'|'c'",
'array_key_last($constantArrayOptionalKeys2)',
],
[
"'a'",
"'a'|'b'|'c'",
'array_key_first($constantArrayOptionalKeys3)',
],
[
"'b'|'c'",
"'a'|'b'|'c'",
'array_key_last($constantArrayOptionalKeys3)',
],
[
Expand Down
Loading
Loading