Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Deep Closure Comparator Laravel Package

eliashaeussler/deep-closure-comparator

PHPUnit comparator/assertion that deeply compares closures inside objects/arrays by serializing them via opis/closure. Use DeepClosureAssert::assertEquals() as a drop-in replacement for PHPUnit’s assertEquals when closures should be treated as equal.

View on GitHub
Deep Wiki
Context7

Getting Started

Install via Composer:

composer require --dev eliashaeussler/deep-closure-comparator

Then register the comparator in your PHPUnit configuration (phpunit.xml):

<phpunit>
    <extensions>
        <extension class="EliasHaeussler\DeepClosureComparator\PHPUnit\ComparatorExtension"/>
    </extensions>
</phpunit>

First use case: comparing two closures for structural equality (same parameters, same body, same static bindings) in unit tests:

$this->assertEquals(
    fn ($x) => $x * 2,
    $someVariableThatShouldBeTheSameClosure,
);

Implementation Patterns

  • Testing higher-order functions: Verify that functions like map, filter, or custom wrappers return expected closures:
    $expected = fn ($item) => $item->isActive();
    $actual = $this->service->createFilter();
    $this->assertEquals($expected, $actual);
    
  • Integration with factories/providers: Assert that dependency injection or factory methods produce closures matching predefined ones—especially useful for fluent APIs or configuration-based callback generation.
  • Combine with Closure::fromCallable(): Validate when converting callables to closures:
    $callable = [$this, 'handler'];
    $closure = Closure::fromCallable($callable);
    $this->assertEquals(Closure::fromCallable([$this, 'handler']), $closure);
    

Gotchas and Tips

  • Strict equality only: This comparator checks semantic equality—not reference identity. Two distinct closures with identical signature and body are considered equal.
  • Static binding matters: Closures bound to different objects (e.g., via bindTo()) are not equal, even with identical code:
    $a = fn () => 42;
    $b = $a->bindTo(new StdClass());
    $this->assertNotEquals($a, $b); // ✅ Correctly different
    
  • Reflection-dependent: Uses ReflectionFunction internally—avoid closures that rely on runtime-mutated state or anonymous classes referencing non-serializable resources.
  • No support for internal closures: Closures wrapping internal (engine/ext) functions (e.g., strlen::class) cannot be compared meaningfully—expect EqualsComparisonException.
  • PHP 8.0+ required: Uses typed properties and named arguments extensively; won’t work with PHP 7.x.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport