Skip to content

Commit 74e8432

Browse files
Fix ObjectType::getOffsetValueType()
1 parent 9edcffb commit 74e8432

File tree

5 files changed

+157
-4
lines changed

5 files changed

+157
-4
lines changed

src/Type/ObjectType.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,14 +1228,14 @@ public function hasOffsetValueType(Type $offsetType): TrinaryLogic
12281228

12291229
public function getOffsetValueType(Type $offsetType): Type
12301230
{
1231-
if (!$this->isExtraOffsetAccessibleClass()->no()) {
1232-
return new MixedType();
1233-
}
1234-
12351231
if ($this->isInstanceOf(ArrayAccess::class)->yes()) {
12361232
return RecursionGuard::run($this, fn (): Type => $this->getMethod('offsetGet', new OutOfClassScope())->getOnlyVariant()->getReturnType());
12371233
}
12381234

1235+
if (!$this->isExtraOffsetAccessibleClass()->no()) {
1236+
return new MixedType();
1237+
}
1238+
12391239
return new ErrorType();
12401240
}
12411241

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug12125;
4+
5+
use ArrayAccess;
6+
use Exception;
7+
use stdClass;
8+
9+
use function PHPStan\Testing\assertType;
10+
11+
/** @var ArrayAccess<array-key, stdClass>|array<array-key, stdClass> $bug */
12+
$bug = [];
13+
14+
assertType('stdClass|null', $bug['key'] ?? null);
15+
16+
interface MyInterface {
17+
/** @return array<string, string> | ArrayAccess<string, string> */
18+
public function getStrings(): array | ArrayAccess;
19+
}
20+
21+
function myFunction(MyInterface $container): string {
22+
$strings = $container->getStrings();
23+
assertType('array<string, string>|ArrayAccess<string, string>', $strings);
24+
assertType('string|null', $strings['test']);
25+
return $strings['test'];
26+
}
27+
28+
function myOtherFunction(MyInterface $container): string {
29+
$strings = $container->getStrings();
30+
assertType('array<string, string>|ArrayAccess<string, string>', $strings);
31+
if (isset($strings['test'])) {
32+
assertType('string', $strings['test']);
33+
return $strings['test'];
34+
} else {
35+
throw new Exception();
36+
}
37+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Bug13144;
4+
5+
use ArrayObject;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
$arr = new ArrayObject(['a' => 1, 'b' => 2]);
10+
11+
assertType('ArrayObject<string, int>', $arr); // correctly inferred as `ArrayObject<string, int>`
12+
13+
$a = $arr['a']; // ok
14+
$b = $arr['b']; // ok
15+
16+
assertType('int|null', $a); // correctly inferred as `int|null`
17+
assertType('int|null', $b); // correctly inferred as `int|null`
18+
19+
20+
['a' => $a, 'b' => $b] = $arr; // ok
21+
22+
assertType('int|null', $a); // incorrectly inferred as `mixed`
23+
assertType('int|null', $b); // incorrectly inferred as `mixed`
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php // lint >= 8.0
2+
3+
namespace Bug9456;
4+
5+
use ArrayAccess;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
/**
10+
* @template TKey of array-key
11+
* @template TValue of mixed
12+
*
13+
* @implements ArrayAccess<TKey, TValue>
14+
*/
15+
class Collection implements ArrayAccess {
16+
/**
17+
* @param (callable(TValue, TKey): bool)|TValue|string $key
18+
* @param TValue|string|null $operator
19+
* @param TValue|null $value
20+
* @return static<int<0, 1>, static<TKey, TValue>>
21+
*/
22+
public function partition($key, $operator = null, $value = null) {} // @phpstan-ignore-line
23+
24+
/**
25+
* @param TKey $key
26+
* @return TValue
27+
*/
28+
public function offsetGet($key): mixed { return null; } // @phpstan-ignore-line
29+
30+
/**
31+
* @param TKey $key
32+
* @return bool
33+
*/
34+
public function offsetExists($key): bool { return true; }
35+
36+
/**
37+
* @param TKey|null $key
38+
* @param TValue $value
39+
* @return void
40+
*/
41+
public function offsetSet($key, $value): void {}
42+
43+
/**
44+
* @param TKey $key
45+
* @return void
46+
*/
47+
public function offsetUnset($key): void {}
48+
}
49+
50+
class HelloWorld
51+
{
52+
/**
53+
* @param Collection<int, string> $collection
54+
*/
55+
public function sayHello(Collection $collection): void
56+
{
57+
$result = $collection->partition('key');
58+
59+
assertType(
60+
'Bug9456\Collection<int<0, 1>, Bug9456\Collection<int, string>>',
61+
$result
62+
);
63+
assertType('Bug9456\Collection<int, string>', $result[0]);
64+
assertType('Bug9456\Collection<int, string>', $result[1]);
65+
66+
[$one, $two] = $collection->partition('key');
67+
68+
assertType('Bug9456\Collection<int, string>', $one);
69+
assertType('Bug9456\Collection<int, string>', $two);
70+
}
71+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Bug9575;
4+
5+
use SimpleXMLElement;
6+
use function PHPStan\Testing\assertType;
7+
8+
$string = <<<XML
9+
<a>
10+
<foo name="one" game="lonely">1</foo>
11+
</a>
12+
XML;
13+
14+
$xml = new SimpleXMLElement($string);
15+
foreach($xml->foo[0]->attributes() as $a => $b) {
16+
echo $a,'="',$b,"\"\n";
17+
}
18+
19+
assertType('(SimpleXMLElement|null)', $xml->foo);
20+
assertType('(SimpleXMLElement|null)', $xml->foo[0]);
21+
assertType('(SimpleXMLElement|null)', $xml->foobar);
22+
assertType('(SimpleXMLElement|null)', $xml->foo->attributes());

0 commit comments

Comments
 (0)