Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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 resources/functionMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -1650,7 +1650,7 @@
'DomXsltStylesheet::result_dump_mem' => ['string', 'xmldoc'=>'DOMDocument'],
'DOTNET::__construct' => ['void', 'assembly_name'=>'string', 'class_name'=>'string', 'codepage='=>'int'],
'dotnet_load' => ['int', 'assembly_name'=>'string', 'datatype_name='=>'string', 'codepage='=>'int'],
'doubleval' => ['float', 'var'=>'scalar|array|resource|null'],
'doubleval' => ['float', 'var'=>'int|float|__stringNotStringable|bool|array|resource|null'],
'Ds\Deque::__construct' => ['void', 'values='=>'mixed'],
'Ds\Deque::count' => ['0|positive-int'],
'Ds\Deque::jsonSerialize' => ['array'],
Expand Down Expand Up @@ -2448,7 +2448,7 @@
'finfo_file' => ['string|false', 'finfo'=>'resource', 'file_name'=>'string', 'options='=>'int', 'context='=>'resource'],
'finfo_open' => ['resource|false', 'options='=>'int', 'arg='=>'string'],
'finfo_set_flags' => ['bool', 'finfo'=>'resource', 'options'=>'int'],
'floatval' => ['float', 'var'=>'scalar|array|resource|null'],
'floatval' => ['float', 'var'=>'int|float|__stringNotStringable|bool|array|resource|null'],
'flock' => ['bool', 'fp'=>'resource', 'operation'=>'int-mask<LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB>', '&w_wouldblock='=>'0|1'],
'floor' => ['__benevolent<float|false>', 'number'=>'float'],
'flush' => ['void'],
Expand Down Expand Up @@ -5075,7 +5075,7 @@
'intltz_to_date_time_zone' => ['DateTimeZone|false', 'obj'=>''],
'intltz_use_daylight_time' => ['bool', 'obj'=>''],
'intlz_create_default' => ['IntlTimeZone'],
'intval' => ['int', 'var'=>'scalar|array|resource|null', 'base='=>'int'],
'intval' => ['int', 'var'=>'int|float|__stringNotStringable|bool|array|resource|null', 'base='=>'int'],
'InvalidArgumentException::__clone' => ['void'],
'InvalidArgumentException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'(?Throwable)|(?InvalidArgumentException)'],
'InvalidArgumentException::__toString' => ['string'],
Expand Down
3 changes: 3 additions & 0 deletions src/PhpDoc/TypeNodeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
use PHPStan\Type\StaticType;
use PHPStan\Type\StaticTypeFactory;
use PHPStan\Type\StringAlwaysAcceptingObjectWithToStringType;
use PHPStan\Type\StringNeverAcceptingObjectWithToStringType;
use PHPStan\Type\StringType;
use PHPStan\Type\ThisType;
use PHPStan\Type\Type;
Expand Down Expand Up @@ -462,6 +463,8 @@ private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameSco
return StaticTypeFactory::falsey();
case '__stringandstringable':
return new StringAlwaysAcceptingObjectWithToStringType();
case '__stringnotstringable':
return new StringNeverAcceptingObjectWithToStringType();
}

if ($nameScope->getClassName() !== null) {
Expand Down
13 changes: 13 additions & 0 deletions src/Type/StringNeverAcceptingObjectWithToStringType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type;

class StringNeverAcceptingObjectWithToStringType extends StringType
{

public function accepts(Type $type, bool $strictTypes): AcceptsResult
{
return parent::accepts($type, true);
}

}
48 changes: 48 additions & 0 deletions tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2440,6 +2440,54 @@ public function testArrayRand(): void
]);
}

public function testBug6560(): void
{
$this->checkExplicitMixed = true;
$this->checkImplicitMixed = true;

$varName = PHP_VERSION_ID < 80000 ? '$var' : '$value';
$stringableName = PHP_VERSION_ID < 80000 ? 'class' : 'Stringable';

$this->analyse([__DIR__ . '/data/bug-6560.php'], [
[
sprintf('Parameter #1 %s of function strval expects bool|float|int|resource|string|null, array given.', $varName),
20,
],
[
sprintf('Parameter #1 %s of function strval expects bool|float|int|resource|string|null, stdClass given.', $varName),
74,
],
[
sprintf('Parameter #1 %s of function intval expects array|bool|float|int|resource|string|null, stdClass given.', $varName),
77,
],
[
sprintf('Parameter #1 %s of function floatval expects array|bool|float|int|resource|string|null, stdClass given.', $varName),
80,
],
[
sprintf('Parameter #1 %s of function intval expects array|bool|float|int|resource|string|null, %s@anonymous/tests/PHPStan/Rules/Functions/data/bug-6560.php:10 given.', $varName, $stringableName),
86,
],
[
sprintf('Parameter #1 %s of function floatval expects array|bool|float|int|resource|string|null, %s@anonymous/tests/PHPStan/Rules/Functions/data/bug-6560.php:10 given.', $varName, $stringableName),
89,
],
[
sprintf('Parameter #1 %s of function strval expects bool|float|int|resource|string|null, mixed given.', $varName),
92,
],
[
sprintf('Parameter #1 %s of function intval expects array|bool|float|int|resource|string|null, mixed given.', $varName),
95,
],
[
sprintf('Parameter #1 %s of function floatval expects array|bool|float|int|resource|string|null, mixed given.', $varName),
98,
],
]);
}

#[RequiresPhp('< 8.0')]
public function testArrayRandPhp7(): void
{
Expand Down
98 changes: 98 additions & 0 deletions tests/PHPStan/Rules/Functions/data/bug-6560.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php
$array = [];
$string = '';
$int = 1;
$float = .1;
$resource = fopen('./.env', 'r'); // Cannot cast resource to float. But works
\assert(false !== $resource);
$bool = true;
$object = new stdClass();
$stringable = new class() {
public function __toString(): string {
return '';
}
};
$null = null;
/** @var mixed $mixed */
$mixed = null;

echo (string) $array . PHP_EOL;
echo strval($array) . PHP_EOL;

echo (int) $array . PHP_EOL;
echo intval($array) . PHP_EOL;

echo (float) $array . PHP_EOL;
echo floatval($array) . PHP_EOL;

echo (string) $string . PHP_EOL;
echo strval($string) . PHP_EOL;

echo (int) $string . PHP_EOL;
echo intval($string) . PHP_EOL;

echo (float) $string . PHP_EOL;
echo floatval($string) . PHP_EOL;

echo (string) $int . PHP_EOL;
echo strval($int) . PHP_EOL;

echo (int) $int . PHP_EOL;
echo intval($int) . PHP_EOL;

echo (float) $int . PHP_EOL;
echo floatval($int) . PHP_EOL;

echo (string) $float . PHP_EOL;
echo strval($float) . PHP_EOL;

echo (int) $float . PHP_EOL;
echo intval($float) . PHP_EOL;

echo (float) $float . PHP_EOL;
echo floatval($float) . PHP_EOL;

echo (string) $resource . PHP_EOL;
echo strval($resource) . PHP_EOL;

echo (int) $resource . PHP_EOL;
echo intval($resource) . PHP_EOL;

echo (float) $resource . PHP_EOL;
echo floatval($resource) . PHP_EOL;

echo (string) $bool . PHP_EOL;
echo strval($bool) . PHP_EOL;

echo (int) $bool . PHP_EOL;
echo intval($bool) . PHP_EOL;

echo (float) $bool . PHP_EOL;
echo floatval($bool) . PHP_EOL;

echo (string) $object . PHP_EOL;
echo strval($object) . PHP_EOL;

echo (int) $object . PHP_EOL;
echo intval($object) . PHP_EOL;

echo (float) $object . PHP_EOL;
echo floatval($object) . PHP_EOL;

echo (string) $stringable . PHP_EOL;
echo strval($stringable) . PHP_EOL;

echo (int) $stringable . PHP_EOL;
echo intval($stringable) . PHP_EOL;

echo (float) $stringable . PHP_EOL;
echo floatval($stringable) . PHP_EOL;

echo (string) $mixed . PHP_EOL;
echo strval($mixed) . PHP_EOL;

echo (int) $mixed . PHP_EOL;
echo intval($mixed) . PHP_EOL;

echo (float) $mixed . PHP_EOL;
echo floatval($mixed) . PHP_EOL;
Loading