Skip to content

Commit 3daad0c

Browse files
authored
Merge pull request #8652 from VincentLanglet/improveSerializeSupport
serialize is not pure for array of object
2 parents 2b3fa46 + c02ed9d commit 3daad0c

File tree

4 files changed

+74
-7
lines changed

4 files changed

+74
-7
lines changed

src/Psalm/Internal/Codebase/Functions.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -530,12 +530,8 @@ public function isCallMapFunctionPure(
530530
if ($function_id === 'serialize' && isset($args[0]) && $type_provider) {
531531
$serialize_type = $type_provider->getType($args[0]->value);
532532

533-
if ($serialize_type) {
534-
foreach ($serialize_type->getAtomicTypes() as $atomic_serialize_type) {
535-
if ($atomic_serialize_type->isObjectType()) {
536-
return false;
537-
}
538-
}
533+
if ($serialize_type && $serialize_type->canContainObjectType($codebase)) {
534+
return false;
539535
}
540536
}
541537

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace Psalm\Internal\TypeVisitor;
4+
5+
use Psalm\Codebase;
6+
use Psalm\Internal\Type\Comparator\AtomicTypeComparator;
7+
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
8+
use Psalm\Type\Atomic;
9+
use Psalm\Type\Atomic\TObject;
10+
use Psalm\Type\NodeVisitor;
11+
use Psalm\Type\TypeNode;
12+
use Psalm\Type\Union;
13+
14+
class CanContainObjectTypeVisitor extends NodeVisitor
15+
{
16+
/**
17+
* @var bool
18+
*/
19+
private $contains_object_type = false;
20+
21+
/**
22+
* @var Codebase
23+
*/
24+
private $codebase;
25+
26+
public function __construct(Codebase $codebase)
27+
{
28+
$this->codebase = $codebase;
29+
}
30+
31+
protected function enterNode(TypeNode $type): ?int
32+
{
33+
if ($type instanceof Union
34+
&& (
35+
UnionTypeComparator::canBeContainedBy($this->codebase, new Union([new TObject()]), $type)
36+
&& UnionTypeComparator::canBeContainedBy($this->codebase, $type, new Union([new TObject()]))
37+
)
38+
||
39+
$type instanceof Atomic
40+
&& (
41+
AtomicTypeComparator::isContainedBy($this->codebase, new TObject(), $type)
42+
|| AtomicTypeComparator::isContainedBy($this->codebase, $type, new TObject())
43+
)
44+
) {
45+
$this->contains_object_type = true;
46+
return NodeVisitor::STOP_TRAVERSAL;
47+
}
48+
49+
return null;
50+
}
51+
52+
public function matches(): bool
53+
{
54+
return $this->contains_object_type;
55+
}
56+
}

src/Psalm/Type/Union.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Psalm\Codebase;
88
use Psalm\Internal\DataFlow\DataFlowNode;
99
use Psalm\Internal\Type\TypeCombiner;
10+
use Psalm\Internal\TypeVisitor\CanContainObjectTypeVisitor;
1011
use Psalm\Internal\TypeVisitor\ContainsClassLikeVisitor;
1112
use Psalm\Internal\TypeVisitor\ContainsLiteralVisitor;
1213
use Psalm\Internal\TypeVisitor\FromDocblockSetter;
@@ -1492,6 +1493,15 @@ public function containsAnyLiteral(): bool
14921493
return $literal_visitor->matches();
14931494
}
14941495

1496+
public function canContainObjectType(Codebase $codebase): bool
1497+
{
1498+
$object_type_visitor = new CanContainObjectTypeVisitor($codebase);
1499+
1500+
$object_type_visitor->traverseArray($this->types);
1501+
1502+
return $object_type_visitor->matches();
1503+
}
1504+
14951505
/**
14961506
* @return list<TTemplateParam>
14971507
*/

tests/UnusedCodeTest.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,9 +751,14 @@ public function __wakeup(): void
751751
}
752752
}
753753
754-
function test(): bool {
754+
function test(Foo|int $foo, mixed $bar, iterable $baz): bool {
755755
try {
756756
serialize(new Foo());
757+
serialize([new Foo()]);
758+
serialize([[new Foo()]]);
759+
serialize($foo);
760+
serialize($bar);
761+
serialize($baz);
757762
unserialize("");
758763
} catch (\Throwable) {
759764
return false;

0 commit comments

Comments
 (0)