Skip to content
Open
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 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 @@ -2397,6 +2397,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