# How TypePHP Resolves Universal Methods at Compile Time: Static Dispatch in Swoole's PHP Compiler

> Discover how TypePHP uses static dispatch to resolve universal methods at compile time. Learn how this technique eliminates runtime overhead for functions like length() and upper() in Swoole's PHP compiler.

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

---

**TypePHP resolves universal methods like `length()`, `upper()`, and `count()` by looking up static type information in the `UNIVERSAL_METHODS` table at compile time and generating direct C++ calls to underlying PHP functions, eliminating runtime dispatch overhead.**

The swoole/typephp project provides a compiled PHP implementation that transforms high-level method calls into optimized native code. When TypePHP encounters universal methods—standard operations available on strings, arrays, and other built-in types—it bypasses dynamic method resolution entirely by resolving these calls during the compilation phase.

## The 7-Step Compile-Time Resolution Pipeline

TypePHP handles universal method resolution through a deterministic pipeline defined across three core files. The process transforms a PHP method call into a direct function invocation without runtime overhead.

### Step 1: AST Interception via MethodCallTrait

When the parser encounters a method invocation, it produces a `Node\Expr\MethodCall` AST node. The `MethodCallTrait` intercepts this node at lines 568-737 in [`src/Parser/MethodCallTrait.php`](https://github.com/swoole/typephp/blob/main/src/Parser/MethodCallTrait.php) and immediately forwards it to the universal method handling path.

The trait determines whether the current node qualifies for universal method processing before delegating to the specialized parser.

### Step 2: Static Type Detection

Type inference occurs via `CompilerBase`, which uses the `UniversalMethodCall` trait. At line 3126 in [`CompilerBase.php`](https://github.com/swoole/typephp/blob/main/CompilerBase.php), the compiler calls `detectUniversalMethodReturnType` to determine the receiver's compile-time type—such as `Type::STR` for strings or `Type::ARRAY` for arrays.

This static type information is crucial because it dictates which universal method entries are valid for the specific receiver.

### Step 3: UNIVERSAL_METHODS Table Lookup

The core resolution logic resides in [`src/Parser/UniversalMethodCall.php`](https://github.com/swoole/typephp/blob/main/src/Parser/UniversalMethodCall.php). The `findUniversalMethodAnyType()` method first queries the static `UNIVERSAL_METHODS` table defined at lines 17-84 for strings and lines 107-108 for arrays.

Each entry contains:

- A **handler** type (e.g., `php_fn`, `calc_op`)
- The underlying PHP or C++ function name (`fn` or `method`)
- The expected **return type** (`return_type`)
- Argument constraints (`min_args`, `max_args`)

### Step 4: Extension Method Fallback

If the method is absent from `UNIVERSAL_METHODS`, the compiler falls back to user-defined extensions. The `findExtensionMethod()` function searches the *MethodsFor* registry, which `getMethodsForRegistry()` builds lazily at lines 41-104 in [`UniversalMethodCall.php`](https://github.com/swoole/typephp/blob/main/UniversalMethodCall.php).

This registry allows developers to register custom static extensions that behave like built-in universal methods.

### Step 5: Argument Validation

Before code generation, `validateUniversalMethodArgs()` at line 678 enforces constraints:

- Verifies the argument count matches `min_args` and `max_args`
- Ensures mutable receivers are allowed when using mutating handlers

### Step 6: Target-Specific Code Generation

Based on the handler type, TypePHP invokes specific generators:

- `genUniversalPhpFn()` (lines 810-827): Emits calls to PHP built-ins like `strlen` or `count`
- `genUniversalCalcOp()`: Generates arithmetic operations (`+`, `-`, etc.)
- `genUniversalDirectMethod()`: Produces direct C++ class method invocations

For example, `genUniversalPhpFn()` generates code in the format `php::fn($receiver, ...)`.

### Step 7: IR Insertion

The generated C++ snippet replaces the original method-call node in the intermediate representation. This insertion occurs inside `parseUniversalMethodCall()` around line 601. The compiled binary contains the direct function call with no runtime dispatch logic.

## Concrete Examples: From PHP to C++

The following examples demonstrate how TypePHP transforms high-level PHP code into low-level C++ calls during compilation.

### String Length Resolution

When compiling `$s->length()` where `$s` is `Type::STR`:

```php
// PHP source
$s = "hello";
$len = $s->length();

```

The compiler looks up the `length` entry in `UNIVERSAL_METHODS`:

```php
'length' => [
    'handler' => 'php_fn', 
    'fn' => 'strlen', 
    'return_type' => Type::INT
]

```

This generates the C++ code:

```cpp
// Generated C++
php::strlen($s)

```

### String Uppercase Resolution

For `$s->upper()` on a string receiver:

```php
// PHP source
$upper = $s->upper();

```

Maps to:

```cpp
// Generated C++
php::strtoupper($s)

```

### Array Count Resolution

For `$arr->count()` where `$arr` is `Type::ARRAY`:

```php
// PHP source
$count = $arr->count();

```

Resolves to:

```cpp
// Generated C++
php::count($arr)

```

## Key Source Files and Architecture

Understanding the file structure helps navigate the resolution implementation:

- **[`src/Parser/UniversalMethodCall.php`](https://github.com/swoole/typephp/blob/main/src/Parser/UniversalMethodCall.php)**: Contains the `UNIVERSAL_METHODS` table, return type resolution logic, *MethodsFor* registry construction, and code generation helpers (`genUniversalPhpFn`, `genUniversalCalcOp`, `genUniversalDirectMethod`).

- **[`src/Parser/MethodCallTrait.php`](https://github.com/swoole/typephp/blob/main/src/Parser/MethodCallTrait.php)**: Handles AST node detection at lines 568-737, determines universal method applicability, and delegates to `parseUniversalMethodCall()`.

- **[`src/CompilerBase.php`](https://github.com/swoole/typephp/blob/main/src/CompilerBase.php)**: Integrates the `UniversalMethodCall` trait into the main compilation pipeline and provides type inference via `detectUniversalMethodReturnType` at line 3126.

## Summary

TypePHP eliminates runtime method lookup overhead for standard operations through compile-time resolution:

- **Static analysis** determines receiver types using `CompilerBase` and `detectUniversalMethodReturnType`
- **Lookup tables** in `UNIVERSAL_METHODS` map method names to handlers and underlying functions
- **Fallback registry** allows user-defined extensions via the *MethodsFor* system
- **Direct code generation** produces C++ calls to `php::strlen`, `php::count`, and similar functions
- **Zero runtime cost** results because the compiled binary contains only the resolved function calls

## Frequently Asked Questions

### What happens if a universal method is not found in the UNIVERSAL_METHODS table?

If `findUniversalMethodAnyType()` fails to locate the method in the static `UNIVERSAL_METHODS` table, TypePHP falls back to the extension method registry. The `findExtensionMethod()` function searches the *MethodsFor* registry, which is built lazily by `getMethodsForRegistry()` in [`UniversalMethodCall.php`](https://github.com/swoole/typephp/blob/main/UniversalMethodCall.php). If no extension exists, the compiler raises a resolution error.

### How does TypePHP handle argument validation for universal methods?

The `validateUniversalMethodArgs()` method at line 678 in [`UniversalMethodCall.php`](https://github.com/swoole/typephp/blob/main/UniversalMethodCall.php) enforces compile-time checks. It validates that the provided argument count falls within the `min_args` and `max_args` constraints defined in the method entry, and verifies that mutable receivers are only used with mutating handlers when required.

### Can users extend universal methods with custom handlers?

Yes. Developers can register custom handlers through the *MethodsFor* registry system. The `getMethodsForRegistry()` method at lines 41-104 builds this registry lazily, allowing user-defined static extensions to integrate seamlessly with the universal method resolution pipeline alongside built-in methods like `length()` and `count()`.

### Why does TypePHP compile universal methods to direct function calls?

By resolving `length()`, `upper()`, and `count()` at compile time through the `UNIVERSAL_METHODS` lookup and generating direct C++ calls via `genUniversalPhpFn()`, TypePHP eliminates dynamic dispatch overhead. This approach provides zero-cost abstractions where standard PHP operations compile to direct invocations of `php::strlen` or `php::count` without runtime method resolution or virtual table lookups.