Skip to content

Multiple closures in an array lose identity after serialize/unserialize roundtrip #131

@JoshSalway

Description

@JoshSalway

Description

When multiple closures are defined on the same source line with identical signatures, they all resolve to the same closure after a serialize/unserialize roundtrip.

use Laravel\SerializableClosure\SerializableClosure;

$closures = [fn() => 'a', fn() => 'b', fn() => 'c'];

$serialized = serialize(array_map(fn($c) => new SerializableClosure($c), $closures));
$unserialized = array_map(fn($sc) => $sc->getClosure(), unserialize($serialized));

echo $unserialized[0](); // 'a'
echo $unserialized[1](); // 'a' — expected 'b'
echo $unserialized[2](); // 'a' — expected 'c'

All three closures resolve to the first one.

Relationship to #120

PR #120 (merged Jan 8) fixed the case where closures on the same line have different signatures — it introduced candidate collection and verifyCandidateSignature() to pick the right one.

This issue is the remaining edge case: closures with identical signatures on the same line, where all candidates pass signature verification and the first is always returned.

Impact

Affects anyone serializing arrays of closures defined inline, e.g. job chains, pipeline stages, event listener lists.

Upstream

The same bug exists in opis/closure 4.x (tested on v4.5.0). Multiple closures on the same line return a, a, a instead of a, b, c. The root cause is the same: ClosureParser::findFunctionIndex() always returns the first T_FN/T_FUNCTION token on the line. Reported upstream as opis/closure#164, with a proof-of-concept fix in opis/closure#165. (Note: opis/closure v4.5.0 fixed a different issue -- nested re-serialization cache coherence via opis/closure#163 -- not this bug.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions