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
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Fixture;

final class FromExplode
{
public function go()
{
$this->run(explode(':', '1:1'));
}

private function run(array $items)
{
}
}

?>
-----
<?php

namespace Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Fixture;

final class FromExplode
{
public function go()
{
$this->run(explode(':', '1:1'));
}

/**
* @param string[] $items
*/
private function run(array $items)
{
}
}

?>
7 changes: 7 additions & 0 deletions src/NodeTypeResolver/NodeTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ObjectWithoutClassType;
use PHPStan\Type\StringType;
use PHPStan\Type\ThisType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
Expand Down Expand Up @@ -178,6 +180,11 @@ public function getType(Node $node): Type
}
}

// correction for explode() that always returns array
Copy link
Contributor

@staabm staabm Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumption is only true for php8+

https://3v4l.org/N1eUT

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is getType(), not getNativeType(), I guess it ok for most usage that allow based on docblock, but I think we can add check PhpVersionProvider->isAtLeastPhpVersion() for it.

Copy link
Contributor

@staabm staabm Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why rector re-implements the type inference for such functions instead of using return-type extensions from PHPStan?
(Which would already do php version specific types)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this PR, looking for fixture, use for docblock array iterable value type reason.

Other type resolver and corrector are for common resolve type that are needed, eg Intersection string to string, Intersection array to array, also node verify by its attribute, eg, Name with its attribute, eg

if (! $node instanceof FullyQualified && $node->hasAttribute(AttributeKey::NAMESPACED_NAME)) {
return $this->resolve(new FullyQualified($node->getAttribute(AttributeKey::NAMESPACED_NAME)));
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked locally, without this, it seems got mixed[] on explode() function:

-     * @param string[] $items
+     * @param mixed[] $items

When using scope->getType(), it got Intersection:

PHPStan\Type\IntersectionType #11668
   types: array (3)
   |  0 => PHPStan\Type\ArrayType #13424
   |  |  itemType: PHPStan\Type\IntersectionType #11020
   |  |  |  types: array (3)
   |  |  |  |  0 => PHPStan\Type\Accessory\AccessoryLowercaseStringType #11687
   |  |  |  |  1 => PHPStan\Type\Accessory\AccessoryUppercaseStringType #11035
   |  |  |  |  2 => PHPStan\Type\StringType #10771
   |  |  |  sortedTypes: false
   |  |  keyType: PHPStan\Type\IntegerRangeType #11763
   |  |  |  min: 0
   |  |  |  max: null
   |  1 => PHPStan\Type\Accessory\AccessoryArrayListType #12617
   |  2 => PHPStan\Type\Accessory\NonEmptyArrayType #10765
   sortedTypes: false

It possibly can be handled in new corrector, eg: AccessoryNonEmptyArrayTypeCorrector, I will create new PR if this can be handled there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if ($node instanceof FuncCall && $node->name instanceof Name && $node->name->toString() === 'explode') {
return new ArrayType(new IntegerType(), new StringType());
}

if ($node instanceof Ternary) {
$ternaryType = $this->resolveTernaryType($node);
if (! $ternaryType instanceof MixedType) {
Expand Down
Loading