# TypePHP Compile-Time Attributes: Supported Registry and Code Generation Explained

> Discover TypePHP's 18 compile-time attributes like Getter and NotNull. Learn how they generate accessor methods, runtime checks, and WebAssembly exports during compilation.

- Repository: [Swoole Project/typephp](https://github.com/swoole/typephp)
- Tags: deep-dive
- Published: 2026-08-30

---

**TypePHP supports 18 compile-time attributes—from `#[Getter]` and `#[NotNull]` to `#[WasmExport]`—that generate accessor methods, runtime validation checks, and WebAssembly exports during the compilation phase.**

TypePHP (swoole/typephp) extends standard PHP with a compile-time attribute system processed before runtime. These attributes eliminate boilerplate by triggering specific code generation phases defined in [`CompileTimeAttributeRegistry.php`](https://github.com/swoole/typephp/blob/main/CompileTimeAttributeRegistry.php). Understanding the registry and its associated lowering classes reveals exactly what PHP code the compiler emits for each annotation.

## How the Compile-Time Attribute Registry Works

All supported attributes are declared centrally in **[`src/Transform/CompileTimeAttributeRegistry.php`](https://github.com/swoole/typephp/blob/main/src/Transform/CompileTimeAttributeRegistry.php)**. This registry maps each attribute name to its valid targets (classes, properties, methods, functions, parameters), the compilation phase when it executes, and any conflicts with other attributes. The compiler references this registry during three primary phases:

- **Preprocess** – Influences stub generation and native-class handling before AST transformation.
- **Enter** – Validates semantics (e.g., override correctness) as the compiler enters a node.
- **Class-leave** – Generates methods after processing an entire class definition.
- **Function-leave** – Inserts validation checks after processing function parameters.

## Accessor and Utility Generation (Class-Leave Phase)

When the compiler finishes analyzing a class, it executes lowering classes that generate methods for properties annotated with specific attributes.

### Getter, Setter, and With Methods

The **`#[Getter]`**, **`#[Setter]`**, and **`#[With]`** attributes target properties and trigger code generation in [`GetterLowering.php`](https://github.com/swoole/typephp/blob/main/GetterLowering.php) and [`PropertyMethodLowering.php`](https://github.com/swoole/typephp/blob/main/PropertyMethodLowering.php).

- **`#[Getter]`** emits a public `getX()` method returning the property value.
- **`#[Setter]`** emits a public `setX($value)` void method assigning the property.
- **`#[With]`** emits a fluent `withX($value)` method returning a clone with the new value.

```php
class User
{
    #[Getter]
    #[Setter]
    private string $name;
}

```

*Generated output*:

```php
public function getName(): string {
    return $this->name;
}
public function setName(string $value): void {
    $this->name = $value;
}

```

### Printer and Arrayable Implementations

Class-level attributes **`#[Printer]`** and **`#[Arrayable]`** generate utility methods for string representation and array conversion.

- **`#[Printer]`** (processed by [`PrinterLowering.php`](https://github.com/swoole/typephp/blob/main/PrinterLowering.php)) creates a `__toString()` implementation that formats property values into a readable string.
- **`#[Arrayable]`** (processed by [`ArrayableLowering.php`](https://github.com/swoole/typephp/blob/main/ArrayableLowering.php)) adds `toArray()` and related helpers for serializing the object.

```php
#[Printer]
class Point {
    public int $x;
    public int $y;
}

```

*Generated output*:

```php
public function __toString(): string {
    return sprintf('Point(x=%d, y=%d)', $this->x, $this->y);
}

```

### Constructor Property Promotion

The **`#[Constructor]`** attribute (processed by [`ConstructorLowering.php`](https://github.com/swoole/typephp/blob/main/ConstructorLowering.php)) targets declared properties to generate a constructor that automatically assigns them.

```php
class Config
{
    #[Constructor]
    private string $env;
}

```

*Generated output*:

```php
public function __construct(string $env) {
    $this->env = $env;
}

```

## Parameter Validation and Safety Checks (Function-Leave Phase)

Attributes applied to function or method parameters trigger validation code insertion after the function signature is parsed.

### NotNull, NotEmpty, and Validate Constraints

The **`#[NotNull]`**, **`#[NotEmpty]`**, and **`#[Validate]`** attributes target parameters and are processed by [`ParameterValidationLowering.php`](https://github.com/swoole/typephp/blob/main/ParameterValidationLowering.php).

- **`#[NotNull]`** inserts a runtime check throwing `CompileTimeAttributeError` if the argument is `null`.
- **`#[NotEmpty]`** validates that strings or arrays are non-empty.
- **`#[Validate]`** accepts custom rules (e.g., email format) and throws on validation failure.

```php
function createUser(
    #[NotNull] $id,
    #[Validate(['type' => 'email'])] $email
): void {}

```

*Generated runtime checks*:

```php
if ($id === null) {
    throw new CompileTimeAttributeError('NotNull violation');
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    throw new CompileTimeAttributeError('Validate violation');
}

```

## Compilation Control and Optimization (Preprocess and Enter Phases)

Certain attributes influence the compilation pipeline itself rather than generating runtime PHP code.

### Native, NoExport, WasmExport, and MethodsFor

Processed during the preprocess phase (primarily in [`Preprocessor.php`](https://github.com/swoole/typephp/blob/main/Preprocessor.php)):

- **`#[Native]`** marks a class as a native implementation, preventing PHP stub emission while signaling the compiler to handle it as an internal type.
- **`#[NoExport]`** prevents the annotated symbol from appearing in generated `*.stub.php` library files, keeping implementation details private.
- **`#[WasmExport]`** registers the function in the WebAssembly export table when targeting WASM backends.
- **`#[MethodsFor]`** generates "method-for" implementations used by the compiler to create automatic method families.
- **`#[ArrayDef]`** generates array-definition metadata for properties, used by the runtime for type-checking.

```php
#[NoExport]
function internalHelper() { /* ... */ }

#[WasmExport]
function add(int $a, int $b): int { return $a + $b; }

```

### Override, MustUse, Immutable, Hot, and Cold

Processed during the enter phase (primarily in [`Visitor.php`](https://github.com/swoole/typephp/blob/main/Visitor.php)):

- **`#[Override]`** validates that a method or property actually overrides a parent member, emitting an error if no matching parent exists.
- **`#[MustUse]`** marks return values as required, triggering compiler warnings if the result is discarded.
- **`#[Immutable]`** enforces that the annotated method, property hook, or parameter cannot mutate after assignment.
- **`#[Hot]`** and **`#[Cold]`** provide optimization hints to the compiler, indicating frequently or rarely executed code paths.

```php
#[Hot]
public function calculate(): int { /* ... */ }

#[Override]
public function toString(): string { /* ... */ }

```

## Source Code Architecture and Lowering Classes

Each attribute category delegates to specific lowering implementations that register generated methods via `CompileTimeAttributeDiagnostic::markGenerated()`:

| Attribute | Lowering Class (Source Path) |
|-----------|------------------------------|
| `#[Getter]` | [`src/Transform/GetterLowering.php`](https://github.com/swoole/typephp/blob/main/src/Transform/GetterLowering.php) |
| `#[Setter]` / `#[With]` | [`src/Transform/PropertyMethodLowering.php`](https://github.com/swoole/typephp/blob/main/src/Transform/PropertyMethodLowering.php) |
| `#[Printer]` | [`src/Transform/PrinterLowering.php`](https://github.com/swoole/typephp/blob/main/src/Transform/PrinterLowering.php) |
| `#[Arrayable]` | [`src/Transform/ArrayableLowering.php`](https://github.com/swoole/typephp/blob/main/src/Transform/ArrayableLowering.php) |
| `#[NotNull]` / `#[NotEmpty]` / `#[Validate]` | [`src/Transform/ParameterValidationLowering.php`](https://github.com/swoole/typephp/blob/main/src/Transform/ParameterValidationLowering.php) |
| `#[Override]` / `#[MustUse]` / `#[Hot]` / `#[Cold]` / `#[Immutable]` | [`src/Transform/Visitor.php`](https://github.com/swoole/typephp/blob/main/src/Transform/Visitor.php) |
| `#[Native]` / `#[NoExport]` / `#[WasmExport]` / `#[MethodsFor]` / `#[ArrayDef]` | [`src/Preprocessor.php`](https://github.com/swoole/typephp/blob/main/src/Preprocessor.php) |
| `#[Constructor]` | [`src/Transform/ConstructorLowering.php`](https://github.com/swoole/typephp/blob/main/src/Transform/ConstructorLowering.php) |

## Summary

- **TypePHP compile-time attributes** are defined in [`CompileTimeAttributeRegistry.php`](https://github.com/swoole/typephp/blob/main/CompileTimeAttributeRegistry.php) and processed across four phases: Preprocess, Enter, Class-leave, and Function-leave.
- **Accessor attributes** (`#[Getter]`, `#[Setter]`, `#[With]`) generate boilerplate methods during the Class-leave phase via dedicated lowering classes.
- **Validation attributes** (`#[NotNull]`, `#[NotEmpty]`, `#[Validate]`) inject runtime checks at the start of functions during the Function-leave phase.
- **Utility attributes** (`#[Printer]`, `#[Arrayable]`, `#[Constructor]`) auto-generate `__toString()`, `toArray()`, and constructor implementations.
- **Control attributes** (`#[NoExport]`, `#[WasmExport]`, `#[Native]`) influence stub generation and export tables without emitting PHP runtime code.
- **Semantic attributes** (`#[Override]`, `#[MustUse]`, `#[Immutable]`, `#[Hot]`, `#[Cold]`) enforce correctness and optimization hints during the Enter phase.

## Frequently Asked Questions

### What is the difference between the Preprocess and Class-leave phases in TypePHP?

The **Preprocess** phase executes before the abstract syntax tree (AST) is fully traversed, handling attributes like `#[NoExport]` and `#[WasmExport]` that determine whether code appears in stub files or export tables. The **Class-leave** phase runs after the compiler finishes analyzing an entire class, allowing attributes like `#[Getter]` to generate methods based on the complete property set.

### How does TypePHP handle validation failures for `#[NotNull]` or `#[Validate]`?

When [`ParameterValidationLowering.php`](https://github.com/swoole/typephp/blob/main/ParameterValidationLowering.php) processes these attributes during the Function-leave phase, it injects conditional checks at the top of the function body. If a check fails, the generated code throws a `CompileTimeAttributeError` with a descriptive message indicating which constraint was violated.

### Can multiple compile-time attributes be applied to the same property or method?

Yes, the registry allows stacking compatible attributes. For example, a property can simultaneously use `#[Getter]`, `#[Setter]`, and `#[NotNull]` (the latter affecting constructor parameters). However, the registry defines conflicts—for instance, certain optimization hints or mutually exclusive export controls cannot be combined.

### Where does TypePHP store the mapping between attribute names and their generation logic?

The authoritative mapping lives in **[`src/Transform/CompileTimeAttributeRegistry.php`](https://github.com/swoole/typephp/blob/main/src/Transform/CompileTimeAttributeRegistry.php)**, which defines each attribute's target eligibility (class, property, method, etc.), processing phase, and argument parser. The actual code generation logic resides in the respective lowering classes referenced by the compiler's transformation pipeline.