Attribute Subclassing https://3v4l.org/8jqK3#vnull
<?php #[Attribute(Attribute::TARGET_PROPERTY)]
class PropertyAttributes {
public function __construct(
public readonly ?string $name = null,
public readonly ?string $label = null,
) {}
}
#[Attribute(Attribute::TARGET_PROPERTY)]
class IntegerPropertyAttributes extends PropertyAttributes {
public function __construct(
?string $name = null,
?string $label = null,
public readonly ?int $default = null,
public readonly ?int $min = null,
public readonly ?int $max = null,
public readonly ?int $step = null,
) { parent::__construct($name, $label); }
}
#[Attribute(Attribute::TARGET_PROPERTY)]
class FloatPropertyAttributes extends PropertyAttributes {
public function __construct(
?string $name = null,
?string $label = null,
public readonly ?float $default = null,
public readonly ?float $min = null,
public readonly ?float $max = null,
) { parent::__construct($name, $label); }
}
class MyClass {
#[IntegerPropertyAttributes('prop','property: ',5,0,10,1)]
public int $prop;
#[FloatPropertyAttributes('float','double: ',5.5,0.0,9.9)]
public float $double;
}
$props = [['MyClass', 'prop'], ['MyClass', 'double']];
foreach ($props as $prop) { $refl = new ReflectionProperty($prop[0], $prop[1]);
$reflAttrs = $refl->getAttributes();
foreach ($reflAttrs as $attr)
echo buildHtmlFormControl($attr->newInstance());
}
function buildHtmlFormControl(PropertyAttributes $args): string {
$html = [];
$html[] = "<label>{$args->label} <input name=\"{$args->name}\" value=\"{$args->default}\"";
switch ($args::class) {
case 'IntegerPropertyAttributes':
$html[] = " type=\"number\" min=\"{$args->min}\" max=\"{$args->max}\" step=\"{$args->step}\""; break;
case 'FloatPropertyAttributes':
$html[] = " type=\"number\" min=\"{$args->min}\" max=\"{$args->max}\""; break;
}
$html[] = "></label>\n";
return implode('', $html);
}
?>