Custom Attributes

Writing and Reading Attributes with Reflection, Including on Constants

An attribute (PHP 8.0) is metadata written #[Name(args)] before a declaration. Any class marked #[Attribute] can be one, and its flags limit where it may go. The engine only records attributes; code reads them through Reflection (Subsection 4.9.13), and newInstance() runs the attribute's constructor. PHP 8.5 lets attributes target global const constants, read through ReflectionConstant.

A Column mapping on Product and a Since attribute on a constantPHP
<?php
#[Attribute(Attribute::TARGET_PROPERTY)]
class Column { public function __construct(public string $name, public bool $key = false) {} }
#[Attribute(Attribute::TARGET_CONSTANT)]
class Since { public function __construct(public string $version) {} }
final class Product {
  #[Column('id', key: true)] public int $id = 0;
  #[Column('sku')]           public string $sku = '';
}
#[Since('1.4')]
const SHOP_API = 'v2';                             // PHP 8.5: attributes on constants
foreach (new ReflectionClass(Product::class)->getProperties() as $prop) {
  foreach ($prop->getAttributes(Column::class) as $attr) {
    $col = $attr->newInstance();                   // the constructor runs only now
    echo $prop->name, ' -> ', $col->name, $col->key ? ' (primary key)' : '', "\n";
  }
}
$since = new ReflectionConstant('SHOP_API')->getAttributes(Since::class)[0]->newInstance();
echo 'SHOP_API since ', $since->version, "\n";
Output
id -> id (primary key)
sku -> sku
SHOP_API since 1.4

Your own attribute's target is checked only at newInstance(): #[Column] on a class compiles, then fails with "Attribute "Column" cannot target class (allowed targets: property)". PHP's own attributes are checked at compile time, and 8.5's #[\DelayedTargetValidation] defers that for libraries that must also load on versions where a target is not yet valid.