Skip to content

Commit

Permalink
Support for is_countable()
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Dec 4, 2018
1 parent 81fc897 commit 7888e6e
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 0 deletions.
5 changes: 5 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,11 @@ services:
tags:
- phpstan.typeSpecifier.functionTypeSpecifyingExtension

-
class: PHPStan\Type\Php\IsCountableFunctionTypeSpecifyingExtension
tags:
- phpstan.typeSpecifier.functionTypeSpecifyingExtension

-
class: PHPStan\Type\Php\IsResourceFunctionTypeSpecifyingExtension
tags:
Expand Down
1 change: 1 addition & 0 deletions src/Reflection/SignatureMap/functionMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -5620,6 +5620,7 @@
'is_array' => ['bool', 'var'=>'mixed'],
'is_bool' => ['bool', 'var'=>'mixed'],
'is_callable' => ['bool', 'var'=>'mixed', 'syntax_only='=>'bool', '&w_callable_name='=>'string'],
'is_countable' => ['bool', 'var'=>'mixed'],
'is_dir' => ['bool', 'filename'=>'string'],
'is_double' => ['bool', 'var'=>''],
'is_executable' => ['bool', 'filename'=>'string'],
Expand Down
52 changes: 52 additions & 0 deletions src/Type/Php/IsCountableFunctionTypeSpecifyingExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Php;

use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\SpecifiedTypes;
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\Analyser\TypeSpecifierAwareExtension;
use PHPStan\Analyser\TypeSpecifierContext;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Type\ArrayType;
use PHPStan\Type\FunctionTypeSpecifyingExtension;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\UnionType;

class IsCountableFunctionTypeSpecifyingExtension implements FunctionTypeSpecifyingExtension, TypeSpecifierAwareExtension
{

/** @var \PHPStan\Analyser\TypeSpecifier */
private $typeSpecifier;

public function isFunctionSupported(FunctionReflection $functionReflection, FuncCall $node, TypeSpecifierContext $context): bool
{
return strtolower($functionReflection->getName()) === 'is_countable'
&& isset($node->args[0])
&& !$context->null();
}

public function specifyTypes(FunctionReflection $functionReflection, FuncCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
{
if ($context->null()) {
throw new \PHPStan\ShouldNotHappenException();
}

return $this->typeSpecifier->create(
$node->args[0]->value,
new UnionType([
new ArrayType(new MixedType(), new MixedType()),
new ObjectType(\Countable::class),
]),
$context
);
}

public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
{
$this->typeSpecifier = $typeSpecifier;
}

}
43 changes: 43 additions & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7608,6 +7608,49 @@ public function testGetParentClass(
);
}

public function dataIsCountable(): array
{
return [
[
'array|Countable',
'$union',
"'is'",
],
[
'string',
'$union',
"'is_not'",
],
];
}

/**
* @dataProvider dataIsCountable
* @param string $description
* @param string $expression
* @param string $evaluatedPointExpression
*/
public function testIsCountable(
string $description,
string $expression,
string $evaluatedPointExpression
): void
{
if (PHP_VERSION_ID < 70300) {
$this->markTestSkipped('Test requires PHP 7.3');
}
$this->assertTypes(
__DIR__ . '/data/is_countable.php',
$description,
$expression,
[],
[],
[],
[],
$evaluatedPointExpression
);
}

private function assertTypes(
string $file,
string $description,
Expand Down
22 changes: 22 additions & 0 deletions tests/PHPStan/Analyser/data/is_countable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace IsCountable;

class Foo
{

/**
* @param array|\Countable|string $union
*/
public function doFoo(
$union
)
{
if (is_countable($union)) {
'is';
} else {
'is_not';
}
}

}

0 comments on commit 7888e6e

Please sign in to comment.