Skip to content

Language: property hooks — isset() and empty() must use get hook (zend_property_hooks.c) #4701

Description

@PurHur

Category

language

Problem

PHP 8.4 property hooks: isset($obj->prop) and empty($obj->prop) on a hooked property must run the get hook (or Zend-equivalent logic) before testing definedness, not only read raw backing storage. This compiler has get/set hook dispatch on fetch/store (lib/VM.php fetchPropertyWithHooks) but objectPropertyIsSet() may disagree with Zend when the backing field is unset and only the get hook supplies a value.

Repro sketch: test/repro-maintainer/property_hook_isset.php.

php-src reference

Repro

<?php
class Box {
    public string $label {
        get => strtoupper($this->label);
        set (string $v) { $this->label = $v; }
    }
    public function __construct() { $this->label = 'hi'; }
}
$o = new Box();
var_export(isset($o->label));
echo "\n";
var_export(empty($o->label));
echo "\n";
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/vm.php test/repro-maintainer/property_hook_isset.php'
php test/repro-maintainer/property_hook_isset.php
Check Zend Document VM actual
isset($o->label) after construct true
empty($o->label) for non-empty hook value false

Add a get-only case (no set hook) per #4687 when that lands.

Scope

  • lib/VM.php — objectPropertyIsSet, empty() lowering on property fetches
  • lib/SourcePreprocessor/PropertyHooks.php — hook method names
  • lib/JIT/PropertyHookDispatch.php — JIT isset/empty (phase 2 OK to defer)
  • test/compliance/cases/language/property_hook_isset_empty.phpt

Done when

Links

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 7, 2026

    @PurHur
    OwnerAuthor

    claim: worker-lane-a — starting this run

  3. PurHur commented on Jun 7, 2026

    @PurHur
    OwnerAuthor

    Merged via #7495.

    Root cause: empty($obj->hook) did not recover the preceding PropertyFetch when php-cfg cleared Empty_.expr, so it lowered to TYPE_EMPTY on an uninitialized temp (always empty). isset() already fused to TYPE_ISSET + objectPropertyIsSet().

    Fix: Recover empty operand like isset; run get hook + truthiness in emptyObjectProperty(); preserve nullable get-hook null (no coerce to "").

    Verification:

    ./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/vm.php test/repro-maintainer/property_hook_isset.php'
    # true / false
    
    ./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php vendor/bin/phpunit --filter property_hook_isset'
    # OK (1 test, 1 assertion)
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