Traits may declare properties, static members, abstract static methods and, since PHP 8.2, constants. Because the trait is copied, each using class gets its own static property. PHP 8.5 binds traits before the parent class, so when both declare a property or constant of the same name, the trait's wins; earlier versions raised a fatal error.
<?php
trait Repository {
public const int PAGE_SIZE = 20; // trait constants: PHP 8.2+
private static int $created = 0; // one copy per using class
public string $source = 'trait';
abstract public static function table(): string;
public static function make(): static { self::$created++; return new static(); }
public static function created(): string { return static::table() . '=' . self::$created; }
}
class Entity { public string $source = 'parent'; }
final class Product extends Entity { use Repository;
public static function table(): string { return 'products'; } }
final class Customer { use Repository;
public static function table(): string { return 'customers'; } }
Product::make(); Product::make(); Customer::make();
echo Product::created(), ' ', Customer::created(), ' page ', Product::PAGE_SIZE, "\n";
echo 'source: ', (new Product())->source, "\n"; // trait beats parent in 8.5
try { echo Repository::PAGE_SIZE; } catch (Error $e) { echo $e->getMessage(), "\n"; }Output
products=2 customers=1 page 20 source: trait Cannot access trait constant Repository::PAGE_SIZE directly
Calling a static method on the trait itself (Repository::make()) has been deprecated since PHP 8.1. A class that redeclares a trait property must match its visibility, type, readonly and default, or PHP stops with a fatal error. PHP 8.5 can also mark a trait #[\Deprecated] (Built-In Attributes).