Skip to content

Language: static closure bindTo()/bind() with $this must Error (Zend/zend_closures.c parity) #4613

Description

@PurHur

Category

language

Problem

PHP static function () { ... } closures must not receive a bound $this. Zend rejects Closure::bindTo($obj) and Closure::bind($closure, $obj, ...) on static closures with:

Cannot bind static closure to object

This compiler creates static closures without boundThis (lib/VM.php FLAG_STATIC check) but ClosureSupport::bindTo() (lib/VM/ClosureSupport.php) does not verify the closure is non-static — binding $this succeeds silently, breaking visibility/$this semantics vs Zend.

php-src reference

Repro (behavior gap today)

<?php
declare(strict_types=1);

class C {
    public int $x = 1;
    public function make(): Closure {
        return static function () {
            return 0;
        };
    }
}

$c = new C();
$fn = $c->make();
try {
    $bound = $fn->bindTo($c);
    var_export($bound !== null);
    echo "\n";
} catch (Throwable $e) {
    echo get_class($e), ': ', $e->getMessage(), "\n";
}
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh
php bin/vm.php repro.php
php repro.php
'
Runtime Result
Zend PHP Error: Cannot bind static closure to object
bin/vm.php true (bind appears to succeed) ❌

Also test Closure::bind($fn, $obj, 'C') — same Error on Zend.

Scope (this repo)

Layer Path
VM lib/VM/ClosureSupport.php — detect static closure (ClosureState / wrapped func FLAG_STATIC) before assigning boundThis
JIT/AOT lib/JIT/ClosureBindHelper.php — emit same Error path
Tests test/compliance/cases/language/static_closure_bind.phpt

Track static fn () => (arrow) separately if desugar omits static flag — should share the same guard.

Done when

  • Repro throws catchable Error with Zend message on VM
  • bindTo(null) / unbind still allowed on static closures (Zend permits)
  • JIT/AOT repro matches VM (./phpc jit repro.php)
  • ./script/ci-fast.sh --filter static_closure_bind green

Related

Activity

  1. added
    enhancementNew feature or request
    area:vmVirtual machine
    implementation-readySpec complete: repro, php-src ref, done-when — safe for workers to claim
    on Jun 2, 2026
  2. PurHur commented on Jun 2, 2026

    @PurHur
    OwnerAuthor

    claim: worker-lane-c — starting this run

  3. PurHur commented on Jun 2, 2026

    @PurHur
    OwnerAuthor

    Merged via #4619.

    Verification

    • ./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && vendor/bin/phpunit test/compliance/ClosureVMTest.php --filter static_closure_bind' — OK (1 test)
    • ./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/jit.php' on issue repro — Error: Cannot bind static closure to object

    VM and JIT now match Zend for static function + bindTo/bind with an object $this; bindTo(null) still allowed.

  4. PurHur commented on Jun 21, 2026

    @PurHur
    OwnerAuthor

    claim: cursor-agent-lane-c — reopening: static_closure_bind.phpt fatals in returnTypeCallableName (func->class Literal); fixing and re-verifying #4613 done-when.

  5. reopened this on Jun 21, 2026
  6. PurHur commented on Jun 21, 2026

    @PurHur
    OwnerAuthor

    Merged via #10425.

    Root cause: returnTypeCallableName() concatenated func->class (a PHPCfg\Operand\Literal) when enforcing : Closure return types, fatally aborting before ClosureSupport::bindTo() could throw.

    Fix: Read func->class->value for the callable label. VM/JIT now emit Error: Cannot bind static closure to object per Zend; bindTo(null) still returns an object.

    Verification:
    ```bash
    ./script/docker-exec.sh -- bash -lc 'source script/php-env.sh
    php bin/vm.php test/compliance/cases/language/static_closure_bind.phpt
    php bin/jit.php test/compliance/cases/language/static_closure_bind.phpt
    ./vendor/bin/phpunit --filter static_closure_bind
    '
    ```

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:compilerCompiler / CFG / JITarea:vmVirtual machineenhancementNew feature or requestimplementation-readySpec complete: repro, php-src ref, done-when — safe for workers to claimphase-2:languagePhase 2 – language features

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions