<?php

/**
 * PHP 8.4 property hooks & asymmetric visibility
 */
class NewFeatures
{
    public protected(set) ?string $name;
    protected private(set) ?int $age;

    // public static array $static = [];

    public ?string $backedGetOnly {
        get => $this->backedGetOnly;
    }

    public ?string $backedSetOnly {
        set (?string $value) {
            $this->backedSetOnly = $value;
        }
    }

    public ?string $backedGetAndSet {
        set (?string $value) {
            $this->backedGetAndSet = $value;
        }

        get => $this->backedGetAndSet;
    }

    public $things = [];

    public string $virtualGetOnly {
        get => \implode(', ', $this->things);
    }

    public string $virtualSetOnly {
        set (string $value) {
            $this->things[] = $value;
        }
    }

    public string $virtualGetAndSet {
        set (string $value) {
            $this->things[] = $value;
        }

        get => \implode(', ', $this->things);
    }
}

$newFeatures = new NewFeatures();

$refObj = new ReflectionObject($newFeatures);
$refProps = $refObj->getProperties();

foreach ($refProps as $refProp) {
    propInfo($refProp, $newFeatures);
}

echo 'the end' . "\n\n";

function propInfo(ReflectionProperty $refProperty, $obj)
{
    $info = array(
        'hooks' => \array_keys($refProperty->getHooks()),
        'isVirtual' => $refProperty->isVirtual(), // at least one hook and none of the hooks reference the property
        'value' => null,
    );
    $isWriteOnly = $info['isVirtual'] && \in_array('get', $info['hooks'], true) === false;
    if ($isWriteOnly) {
        return;
    }
    $refProperty->setAccessible(true);
    if ($refProperty->isInitialized($obj)) {
        try {
            $info['value'] = $refProperty->isStatic() === false && $refProperty->isVirtual() === false
                ? $refProperty->getRawValue($obj)
                : $refProperty->getValue($obj);
        } catch (\Error $e) {
            echo 'Error: ' . $e->getMessage() . "\n";
        }
    }
}
