What PHP Attributes Enums Readonly Properties and Fibers Do
PHP attributes (8.0), enums (8.1), readonly properties (8.1), readonly classes (8.2), and fibers (8.1) are version-gated features with specific syntax and failure modes.
What PHP Attributes Enums Readonly Properties and Fibers Do
Each of these language additions belongs to a specific PHP minor release. You cannot use an enum or a readonly property on PHP 8.0, and you cannot use a readonly class on PHP 8.1. The runtime does not throw a catchable exception. It refuses to compile. A South Florida shared host running PHP 8.0.28 will parse attributes but die on the enum keyword. The cPanel MultiPHP selector often shows 8.x as available while the domain default stays on 7.4. Call phpversion() or run php -v against the actual domain before you write a single line that depends on these syntax additions.
The PHP.net migration appendices are the canonical source for every addition's release boundary. The 8.0 appendix covers attributes, named arguments, the nullsafe operator, the match expression, constructor property promotion, and the static return type. The 8.1 appendix adds enums, fibers, readonly properties, first-class callables, and array unpacking with string keys. The 8.2 appendix adds readonly classes and deprecation notices for dynamic properties. A deprecation in 8.1 becomes a fatal error in 8.2 or later. The gate is absolute: you cannot polyfill syntax. A construct from a higher minor release simply does not parse on a lower one.
What PHP Attributes Enums Readonly Properties and Fibers Do
Attributes in PHP 8.0
Attributes use the #[...] syntax. They attach structured metadata to classes, methods, properties, and parameters without touching the runtime. Before attributes, metadata lived in docblock annotations that needed a separate parser. An attribute is a class marked with the Attribute attribute. The reflection API methods getAttributes() and newInstance() retrieve them. Write an attribute on PHP 7.4 and the engine treats #[ as a comment followed by a syntax error. The script stops dead.
Enums in PHP 8.1
A backed enum maps each case to a scalar, either string or int. A pure enum has no backing value. Every case is a singleton object of the enum type. Enums can implement interfaces and contain methods. They cannot extend classes or be extended. An enum on PHP 8.0 produces a parse error, not a runtime exception. The engine refuses to compile. Enum interface implementation lets you type-hint against a contract while keeping the fixed set of cases.
Readonly Properties and Classes
Readonly properties shipped in PHP 8.1. A property declared readonly initializes once, in the constructor or at declaration, and never mutates afterward. Attempting a modification throws an Error. This is engine-level prevention. Readonly classes, a PHP 8.2 addition, apply the constraint to every typed property automatically. All typed properties become implicitly readonly. Dynamic properties are forbidden with a deprecation notice that becomes a fatal error in PHP 9.0. Writing a readonly property or readonly class on PHP 8.0 triggers a parse error.
Fibers in PHP 8.1
Fibers are a low-level cooperative concurrency primitive. A fiber is a callable that suspends and resumes. It does not run concurrently. It yields control back to the main execution context explicitly via Fiber::suspend(). This makes fibers a building block for async frameworks like Revolt or AMPHP. Using a fiber on PHP 8.0 causes a parse error because the Fiber class and its suspend() and resume() methods do not exist. The match expression arms and named arguments from PHP 8.0 often appear alongside fibers in 8.1 codebases, compounding the version dependency.
PHP 8.1 Enums Explained
From the engine's perspective, enums are class-like structures with a fixed set of instances. Declare a backed enum with its backing type after the name: enum Suit: string. The backing value must be a literal scalar, never an expression. Call from() to convert a backing value to a case. It throws a ValueError on an unknown value. Call tryFrom() to get null instead. Define methods on an enum the same way you would on a class. A public function color(): string with a match expression on $this is the standard pattern for mapping a case to derived data.
Enum interface implementation follows class syntax. An enum can implement JsonSerializable, Stringable, or a custom interface. It must satisfy every method of the contract. This is practical when an enum needs to serialize itself or fit an existing type system. Enum case objects are serializable. Their serialized representation includes the enum class name and the case name. A pure enum has no backing value and cannot use from() or tryFrom(). A parse error occurs if you declare a backing type on a pure enum or omit it on a backed enum. The version gate is strict: PHP 8.0 and earlier do not understand the enum keyword.
PHP 8.0 Attributes Usage
Place the #[...] syntax immediately before the declaration you are annotating. Define the attribute class with the #[Attribute] attribute and specify targets: #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)]. To retrieve attributes, use ReflectionClass, ReflectionMethod, or ReflectionProperty and call getAttributes(). You get back an array of ReflectionAttribute objects. Each one can instantiate the attribute class via newInstance(), passing the arguments from the attribute declaration. Attribute argument types are validated at call time, not at parse time. A misspelled attribute class or a type mismatch surfaces when you call newInstance(), not when the script compiles.
The failure on an unsupported release is a parse error. PHP 7.4 sees #[ and treats the hash as a comment start and the bracket as an unexpected token. The script dies before any code runs. This is not catchable. Check the version by comparing PHP_VERSION_ID against the integer constant for the target minor release: PHP_VERSION_ID >= 80000 for attributes. A South Florida shared host defaulting to PHP 7.4 despite showing 8.0 in the MultiPHP selector is a configuration failure. Verify with a one-line script that outputs PHP_VERSION before you build on the assumption.
| Feature | PHP Minor Version | Syntax Example | Unsupported Version Behaviour |
|---|---|---|---|
| Attributes (#[...]) | 8.0 | #[Route('/home')] | Parse error: unexpected '[' |
| Enums (backed and pure) | 8.1 | enum Status: string { case Draft = 'draft'; } | Parse error: unexpected 'enum' |
| Readonly properties | 8.1 | public readonly string $id; | Parse error: unexpected 'readonly' |
| Readonly classes | 8.2 | readonly class Point { public int $x; } | Parse error: unexpected 'readonly' before 'class' |
| Fibers | 8.1 | $fiber = new Fiber(fn() => Fiber::suspend('data')); | Fatal error: Class 'Fiber' not found |
PHP 8.2 Readonly Classes
A readonly class applies the readonly constraint to every typed property automatically. You cannot declare a non-readonly property. You cannot add dynamic properties later. Declare the class with the readonly keyword before class: readonly class UserData. The constructor is the only place where properties can be initialized. A readonly class cannot contain static properties. It cannot be combined with the final keyword because readonly classes are implicitly final. Attempting to extend a readonly class produces a fatal error.
The deprecation notices in PHP 8.2 are part of the version gate. Dynamic properties on classes that do not explicitly allow them via the #[AllowDynamicProperties] attribute trigger a deprecation notice. In PHP 9.0, that deprecation becomes a fatal error. The static return type, introduced in PHP 8.0, pairs with readonly classes in factory methods that return static instead of self to preserve late static binding. Writing a readonly class on PHP 8.1 results in a parse error. The readonly keyword is not permitted on a class declaration in that release. The PHP.net migration appendices for 8.2 document the exact error message and the migration path for dynamic properties.
PHP Fibers Async
Suspend the fiber at the point where an I/O operation would block. Resume it when the operation completes. The fiber does not run concurrently with the main execution context. It is cooperative: the fiber must explicitly call Fiber::suspend() to yield control. The resume() method sends a value back into the fiber at the suspension point. The throw() method injects an exception. A fiber that returns or throws without being resumed again is terminated. A fiber cannot be resumed once it has returned.
The nullsafe operator from PHP 8.0 and the match expression arms syntax handle nullable results from I/O operations concisely inside fiber-based code. Named arguments order flexibility helps when calling fiber-aware libraries with many optional parameters. First-class callable syntax from PHP 8.1, which creates a Closure from any callable using the strlen(...) notation, simplifies passing functions as fiber entry points. Array unpacking string keys, also an 8.1 addition, works inside fiber callbacks that need to destructure associative arrays. A parse error on any of these supporting constructs when the host runs PHP 8.0 means the fiber itself is unusable. The entire 8.1 set is a package: you either have the minor release or you do not.
Version Gate Enforcement and Hosting Reality
The cPanel MultiPHP selector shows a list of installed PHP releases. A South Florida shared host may list 8.0, 8.1, and 8.2 while the domain default stays on 7.4. Changing the MultiPHP setting applies to the domain's .htaccess handler. A cPanel update can reset the handler to the server default without warning. The only reliable test is a phpinfo() page or a php -v command on the actual domain URL. The minor-release gate is binary: you cannot run enum code on 8.0, and you cannot run readonly class code on 8.1. The parse error is identical whether the construct is new to 8.1 or 8.2.
Deprecation notices are the soft gate. A construct deprecated in 8.1 still runs but logs a notice. The same construct in 8.2 may be removed and throw a fatal error. The PHP.net migration appendices list every deprecation and removal per minor release. For a South Florida PHP project that must run on a shared host with a fixed version, the appendices are the checklist of what syntax is safe to commit. A composer.json can specify a PHP version constraint with "php": ">=8.1", but Composer only checks the constraint during composer install. It does not prevent a deployment script from pushing 8.2 syntax to an 8.1 server.
Common Questions
What is the exact PHP version for attributes?
Attributes require PHP 8.0 or later. The #[...] syntax does not parse on PHP 7.4 or earlier. The reflection API methods like getAttributes() are available from 8.0 forward.
Can I use enums on PHP 8.0?
No. Enums were introduced in PHP 8.1. Attempting to use the enum keyword on PHP 8.0 results in a parse error. There is no polyfill for enum syntax.
What happens if I deploy readonly classes to PHP 8.1?
A parse error occurs. Readonly classes are a PHP 8.2 addition. PHP 8.1 supports readonly properties but not the readonly class declaration syntax.
Are fibers the same as async/await in JavaScript?
No. Fibers are a low-level cooperative concurrency primitive. They susprend and resume explicitly. They do not run concurrently. Higher-level libraries build async/await patterns on top of fibers.
How do I check which PHP version my shared host is really running?
Create a file with phpinfo() or phpversion() and access it through the domain URL. Do not rely on the cPanel MultiPHP selector label alone. A cPanel update can reset the handler to the server default.
What is the canonical source for feature version requirements?
The PHP.net migration appendices. Each minor release has its own appendix that lists new additions, deprecations, and backward-incompatible changes. The appendix for 8.0 covers attributes; 8.1 covers enums, readonly properties, and fibers; 8.2 covers readonly classes.
Can I use named arguments with PHP 8.1 enums?
Yes. Named arguments are a PHP 8.0 addition and work in any PHP 8.x code including enum methods. Named arguments order is independent of the parameter declaration order.