Skip to content

Commit 041aaa4

Browse files
committed
Fixed HasOffsetValueType accessory missing main-type
1 parent d81cb77 commit 041aaa4

File tree

6 files changed

+84
-15
lines changed

6 files changed

+84
-15
lines changed

src/Analyser/ResultCache/ResultCacheManager.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1131,7 +1131,7 @@ private function getComposerLocks(): array
11311131
}
11321132

11331133
/**
1134-
* @return array<string, string>
1134+
* @return array<string, array<mixed>>
11351135
*/
11361136
private function getComposerInstalled(): array
11371137
{
@@ -1149,6 +1149,10 @@ private function getComposerInstalled(): array
11491149
}
11501150

11511151
$installed = require $filePath;
1152+
if (!is_array($installed)) {
1153+
throw new ShouldNotHappenException();
1154+
}
1155+
11521156
$rootName = $installed['root']['name'];
11531157
unset($installed['root']);
11541158
unset($installed['versions'][$rootName]);

src/Type/MixedType.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,15 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni
169169

170170
public function setExistingOffsetValueType(Type $offsetType, Type $valueType): Type
171171
{
172-
return new self($this->isExplicitMixed);
172+
$types = [
173+
new ArrayType(new MixedType(), new MixedType()),
174+
new ObjectType(ArrayAccess::class),
175+
new NullType(),
176+
];
177+
if ($offsetType->isInteger()->yes()) {
178+
$types[] = new StringType();
179+
}
180+
return TypeCombinator::union(...$types);
173181
}
174182

175183
public function unsetOffset(Type $offsetType): Type
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug13270a;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
final class HelloWorld
8+
{
9+
/**
10+
* @param array<mixed> $data
11+
*/
12+
public function test(array $data): void
13+
{
14+
foreach($data as $k => $v) {
15+
assertType('non-empty-array<mixed>', $data);
16+
$data[$k]['a'] = true;
17+
assertType("non-empty-array<(non-empty-array&hasOffsetValue('a', true))|(ArrayAccess&hasOffsetValue('a', true))>", $data);
18+
foreach($data[$k] as $val) {
19+
}
20+
}
21+
}
22+
23+
/*
24+
public function doFoo(mixed $mixed, int $i): void
25+
{
26+
$mixed[$i]['a'] = true;
27+
dumpType($mixed);
28+
}
29+
*/
30+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Bug13270b;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Test
8+
{
9+
/**
10+
* @param mixed[] $data
11+
* @return mixed[]
12+
*/
13+
public function parseData(array $data): array
14+
{
15+
if (isset($data['price'])) {
16+
assertType('mixed~null', $data['price']);
17+
if (!array_key_exists('priceWithVat', $data['price'])) {
18+
$data['price']['priceWithVat'] = null;
19+
}
20+
assertType("(non-empty-array&hasOffsetValue('priceWithVat', mixed))|(ArrayAccess&hasOffsetValue('priceWithVat', null))", $data['price']);
21+
if (!array_key_exists('priceWithoutVat', $data['price'])) {
22+
$data['price']['priceWithoutVat'] = null;
23+
}
24+
}
25+
return $data;
26+
}
27+
}

tests/PHPStan/Analyser/nsrt/composer-array-bug.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,22 @@ public function doFoo(): void
1919
foreach ($this->config['authors'] as $key => $author) {
2020
if (!is_array($author)) {
2121
$this->errors[] = 'authors.'.$key.' : should be an array, '.gettype($author).' given';
22-
assertType("mixed", $this->config['authors']);
22+
assertType("mixed~(0|0.0|''|'0'|false)", $this->config['authors']);
2323
unset($this->config['authors'][$key]);
24-
assertType("mixed", $this->config['authors']);
24+
assertType("mixed~(0|0.0|''|'0'|false)", $this->config['authors']);
2525
continue;
2626
}
27-
assertType("mixed", $this->config['authors']);
27+
assertType("mixed~(0|0.0|''|'0'|false)", $this->config['authors']);
2828
foreach (['homepage', 'email', 'name', 'role'] as $authorData) {
2929
if (isset($author[$authorData]) && !is_string($author[$authorData])) {
3030
$this->errors[] = 'authors.'.$key.'.'.$authorData.' : invalid value, must be a string';
3131
unset($this->config['authors'][$key][$authorData]);
3232
}
3333
}
3434
if (isset($author['homepage'])) {
35-
assertType("mixed", $this->config['authors']);
35+
assertType("mixed~(0|0.0|''|'0'|false)", $this->config['authors']);
3636
unset($this->config['authors'][$key]['homepage']);
37-
assertType("mixed", $this->config['authors']);
37+
assertType("array|ArrayAccess|null", $this->config['authors']);
3838
}
3939
if (isset($author['email']) && !filter_var($author['email'], FILTER_VALIDATE_EMAIL)) {
4040
unset($this->config['authors'][$key]['email']);
@@ -44,8 +44,8 @@ public function doFoo(): void
4444
}
4545
}
4646

47-
assertType("non-empty-array&hasOffsetValue('authors', mixed)", $this->config);
48-
assertType("mixed", $this->config['authors']);
47+
assertType("non-empty-array&hasOffsetValue('authors', mixed~(0|0.0|''|'0'|false))", $this->config);
48+
assertType("mixed~(0|0.0|''|'0'|false)", $this->config['authors']);
4949

5050
if (empty($this->config['authors'])) {
5151
unset($this->config['authors']);

tests/PHPStan/Analyser/nsrt/composer-non-empty-array-after-unset.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ public function doFoo()
1515
if (!empty($this->config['authors'])) {
1616
assertType("mixed~(0|0.0|''|'0'|array{}|false|null)", $this->config['authors']);
1717
foreach ($this->config['authors'] as $key => $author) {
18-
assertType("mixed", $this->config['authors']);
18+
assertType("mixed~(0|0.0|''|'0'|false)", $this->config['authors']);
1919
if (!is_array($author)) {
2020
unset($this->config['authors'][$key]);
21-
assertType("mixed", $this->config['authors']);
21+
assertType("mixed~(0|0.0|''|'0'|false)", $this->config['authors']);
2222
continue;
2323
}
2424
foreach (['homepage', 'email', 'name', 'role'] as $authorData) {
@@ -33,13 +33,13 @@ public function doFoo()
3333
unset($this->config['authors'][$key]['email']);
3434
}
3535
if (empty($this->config['authors'][$key])) {
36-
assertType("mixed", $this->config['authors']);
36+
assertType("mixed~(0|0.0|''|'0'|false)", $this->config['authors']);
3737
unset($this->config['authors'][$key]);
38-
assertType("mixed", $this->config['authors']);
38+
assertType("mixed~(0|0.0|''|'0'|false)", $this->config['authors']);
3939
}
40-
assertType("mixed", $this->config['authors']);
40+
assertType("mixed~(0|0.0|''|'0'|false)", $this->config['authors']);
4141
}
42-
assertType("mixed", $this->config['authors']);
42+
assertType("mixed~(0|0.0|''|'0'|false)", $this->config['authors']);
4343
}
4444
}
4545

0 commit comments

Comments
 (0)