You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

PHP8中公共属性的Getter/Setter等效实现:子类公共属性变更时自动更新基类$isDirty属性的方案问询

Great question! This is a common pain point when balancing IDE auto-completion and dirty state tracking in PHP inheritance. Let's break down two solid solutions that let you keep public-facing properties (for IDE support) while automatically updating the $isDirty flag whenever those properties change.

PHP 8.1 introduced property accessors, which let you define getter/setter logic that behaves exactly like a public property. IDEs recognize these as regular public properties (so auto-completion works perfectly), and you can inject your dirty-tracking logic directly into the setter.

Here's how to implement it:

class BaseClass {
    private bool $isDirty = false;

    // Protected method so subclasses can trigger dirty state
    protected function markAsDirty(): void {
        $this->isDirty = true;
    }

    // Optional: Expose a way to check dirty state
    public function isDirty(): bool {
        return $this->isDirty;
    }
}

class ChildClass extends BaseClass {
    // Private backing fields to store actual values
    private string $_username;
    private int $_age;

    // Public accessor property for username (IDE auto-completes this)
    public string $username {
        get => $this->_username;
        set {
            // Optional optimization: Only mark dirty if value changes
            if ($this->_username !== $value) {
                $this->_username = $value;
                $this->markAsDirty();
            }
        }
    }

    // Public accessor property for age
    public int $age {
        get => $this->_age;
        set {
            if ($this->_age !== $value) {
                $this->_age = $value;
                $this->markAsDirty();
            }
        }
    }
}

How this works:

  • When you run $child->username = "new_user";, PHP automatically invokes the setter logic, which calls markAsDirty() to update the base class's $isDirty flag.
  • IDEs like PhpStorm or VS Code treat $username and $age as regular public properties, so auto-completion works flawlessly.
  • Backing fields ($_username, $_age) keep internal state encapsulated while exposing a clean public API.
Solution 2: PHPDoc + Magic Methods (Compatible with PHP 8.0+)

If you're stuck on PHP 8.0 (before property accessors were added), you can use PHPDoc annotations to trick IDEs into recognizing "virtual" public properties, paired with __get and __set magic methods to handle value storage and dirty tracking.

Here's the implementation:

class BaseClass {
    private bool $isDirty = false;
    // Store subclass property values in a protected array
    protected array $attributes = [];

    protected function markAsDirty(): void {
        $this->isDirty = true;
    }

    public function isDirty(): bool {
        return $this->isDirty;
    }
}

/**
 * @property string $username Public username property
 * @property int $age Public age property
 */
class ChildClass extends BaseClass {
    // __set triggers when assigning to an undefined/inaccessible property
    public function __set(string $name, mixed $value): void {
        // Validate against properties defined in PHPDoc
        if (in_array($name, ['username', 'age'])) {
            if ($this->attributes[$name] !== $value) {
                $this->attributes[$name] = $value;
                $this->markAsDirty();
            }
        } else {
            // Fallback for unexpected properties (optional)
            parent::__set($name, $value);
        }
    }

    // __get triggers when accessing an undefined/inaccessible property
    public function __get(string $name): mixed {
        if (array_key_exists($name, $this->attributes)) {
            return $this->attributes[$name];
        }

        throw new \InvalidArgumentException("Property $name does not exist");
    }

    // Optional: Make isset() work with virtual properties
    public function __isset(string $name): bool {
        return in_array($name, ['username', 'age']) && array_key_exists($name, $this->attributes);
    }
}

How this works:

  • @property PHPDoc annotations tell IDEs that $username and $age are public properties of ChildClass, enabling proper auto-completion.
  • When you assign to $child->username, PHP calls __set (since the property isn't actually declared in the class). We store the value in the base class's $attributes array and trigger markAsDirty().
  • The __get method retrieves values from $attributes when the property is accessed.

Caveats for this approach:

  • You'll need to manually keep PHPDoc annotations and the allowed property list in __set/__get in sync.
  • Some static analysis tools might flag virtual properties as undefined, but most modern IDEs respect the @property annotation.

Between the two solutions, property accessors are the cleanest and most native approach if you're on PHP 8.1 or newer—they eliminate magic methods entirely and integrate seamlessly with IDEs. If you're stuck on PHP 8.0, the PHPDoc + magic method combo is a reliable workaround.

内容的提问来源于stack exchange,提问作者Andrew Traub

火山引擎 最新活动