Skip to content

Commit

Permalink
Merge pull request #8652 from VincentLanglet/improveSerializeSupport
Browse files Browse the repository at this point in the history
serialize is not pure for array of object
  • Loading branch information
orklah committed Nov 3, 2022
2 parents 2b3fa46 + c02ed9d commit 3daad0c
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 7 deletions.
8 changes: 2 additions & 6 deletions src/Psalm/Internal/Codebase/Functions.php
Expand Up @@ -530,12 +530,8 @@ public function isCallMapFunctionPure(
if ($function_id === 'serialize' && isset($args[0]) && $type_provider) {
$serialize_type = $type_provider->getType($args[0]->value);

if ($serialize_type) {
foreach ($serialize_type->getAtomicTypes() as $atomic_serialize_type) {
if ($atomic_serialize_type->isObjectType()) {
return false;
}
}
if ($serialize_type && $serialize_type->canContainObjectType($codebase)) {
return false;
}
}

Expand Down
56 changes: 56 additions & 0 deletions src/Psalm/Internal/TypeVisitor/CanContainObjectTypeVisitor.php
@@ -0,0 +1,56 @@
<?php

namespace Psalm\Internal\TypeVisitor;

use Psalm\Codebase;
use Psalm\Internal\Type\Comparator\AtomicTypeComparator;
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
use Psalm\Type\Atomic;
use Psalm\Type\Atomic\TObject;
use Psalm\Type\NodeVisitor;
use Psalm\Type\TypeNode;
use Psalm\Type\Union;

class CanContainObjectTypeVisitor extends NodeVisitor
{
/**
* @var bool
*/
private $contains_object_type = false;

/**
* @var Codebase
*/
private $codebase;

public function __construct(Codebase $codebase)
{
$this->codebase = $codebase;
}

protected function enterNode(TypeNode $type): ?int
{
if ($type instanceof Union
&& (
UnionTypeComparator::canBeContainedBy($this->codebase, new Union([new TObject()]), $type)
&& UnionTypeComparator::canBeContainedBy($this->codebase, $type, new Union([new TObject()]))
)
||
$type instanceof Atomic
&& (
AtomicTypeComparator::isContainedBy($this->codebase, new TObject(), $type)
|| AtomicTypeComparator::isContainedBy($this->codebase, $type, new TObject())
)
) {
$this->contains_object_type = true;
return NodeVisitor::STOP_TRAVERSAL;
}

return null;
}

public function matches(): bool
{
return $this->contains_object_type;
}
}
10 changes: 10 additions & 0 deletions src/Psalm/Type/Union.php
Expand Up @@ -7,6 +7,7 @@
use Psalm\Codebase;
use Psalm\Internal\DataFlow\DataFlowNode;
use Psalm\Internal\Type\TypeCombiner;
use Psalm\Internal\TypeVisitor\CanContainObjectTypeVisitor;
use Psalm\Internal\TypeVisitor\ContainsClassLikeVisitor;
use Psalm\Internal\TypeVisitor\ContainsLiteralVisitor;
use Psalm\Internal\TypeVisitor\FromDocblockSetter;
Expand Down Expand Up @@ -1492,6 +1493,15 @@ public function containsAnyLiteral(): bool
return $literal_visitor->matches();
}

public function canContainObjectType(Codebase $codebase): bool
{
$object_type_visitor = new CanContainObjectTypeVisitor($codebase);

$object_type_visitor->traverseArray($this->types);

return $object_type_visitor->matches();
}

/**
* @return list<TTemplateParam>
*/
Expand Down
7 changes: 6 additions & 1 deletion tests/UnusedCodeTest.php
Expand Up @@ -751,9 +751,14 @@ public function __wakeup(): void
}
}
function test(): bool {
function test(Foo|int $foo, mixed $bar, iterable $baz): bool {
try {
serialize(new Foo());
serialize([new Foo()]);
serialize([[new Foo()]]);
serialize($foo);
serialize($bar);
serialize($baz);
unserialize("");
} catch (\Throwable) {
return false;
Expand Down

0 comments on commit 3daad0c

Please sign in to comment.