---
title: "TypePHP, native PHP, NativePHP: the ecosystem has a vocabulary problem"
url: https://www.exakat.io/typephp-native-php-nativephp-the-ecosystem-has-a-vocabulary-problem/
date: 2026-08-29
modified: 2026-08-29
author: "dams"
description: "TypePHP, native PHP, NativePHP: the ecosystem has a vocabulary problem PHP names things by accretion. A word gets used for something, then reused for something adjacent, then reused again by..."
categories:
  - "Technology"
tags:
  - "names"
  - "php"
image: https://www.exakat.io/wp-content/uploads/2026/08/stickers.320jpg.jpg
word_count: 1691
---

# TypePHP, native PHP, NativePHP: the ecosystem has a vocabulary problem

# TypePHP, native PHP, NativePHP: the ecosystem has a vocabulary problem

PHP names things by accretion. A word gets used for something, then reused for something adjacent, then reused again by a project that picked the name because it was available on Packagist. Nobody arbitrates. Ten years later you have a conference hallway conversation where two people agree enthusiastically about "native PHP" and turn out to be discussing entirely different technologies.

Right now we have two live examples running at once: a project name that two unrelated teams shipped in the same year, and a single adjective doing six jobs. They're the same problem at different scales.

## Start with TypePHP

The Swoole team open-sourced [**TypePHP**](https://github.com/swoole/typephp) very recently. Two things are true about it at the same time, and both of them mislead:

- It is an **AOT compiler**: it converts PHP source to C++17, then to native machine code. No opcodes, no OPcache, no JIT warm-up.
- It is an **extended type system**: `int`, `float` and `bool` can map directly onto C++ scalars, and it adds typed containers, high-precision numerics, compile-time attributes, and universal methods on primitives.

The name suggests "TypeScript, but for PHP", a gradual, additive layer you sprinkle on top of code that still runs everywhere. That is not what this is. TypeScript converts to JavaScript. TypePHP compiles to a binary and deliberately supports a *subset* of PHP: the global scope is declaration-only, binary builds require a `main()`, and a list of dynamic patterns are documented as unsupported. It is closer in spirit to Hack than to TypeScript.

The compiler itself is written in PHP and is self-hosting: `tpc` is produced by compiling the compiler's own PHP source with TypePHP.

Top-level executable statements are rejected in binary mode: a program needs a `main()`, and executable code has to live in a function or a method. That single constraint tells you more about the project than any benchmark: this is not a drop-in for your existing scripts.

The part that actually pays off is opting into native storage. A function can declare `use native_types`, at which point `int` becomes a C++ `int64_t` and the arithmetic compiles to plain CPU instructions instead of ZendVM dispatch. The project's own numbers on php-src's `bench.php` are roughly 8× against the interpreter, and their typed `std::array` beats PHP arrays by about 10× on a container-heavy loop. Those are their measurements on their hardware, not a promise.

There are three build modes, which matters later: `-m bin` (an executable), `-m ext` (a loadable PHP extension), and `-m lib` (a shared library). Same source, three artifacts.

## And now the other TypePHP

There is a second project called **TypePHP**. Different author, different repo, different Packagist vendor, no relation.

[`typephp/typephp`](https://github.com/typephp-php/typephp) by Reymart A. Calicdan, PHP 8.1+ and MIT-licensed, is a **transparent, pure-PHP runtime type checker**. It reads your PHPDoc annotations, including generics, typed arrays, array shapes, `key-of`/`value-of` extractions, and enforces them at runtime, by registering a stream wrapper that AST-transforms files as they're included. Its README opens with the line "No transpilation. No build steps. No C-extensions."

Read that next to the Swoole project, whose entire premise is transpilation to C++, a build step, and a C-extension output mode. The two TypePHPs are not just unrelated; on the axis that matters most, they are opposites. One removes the runtime. The other adds work to it.

And here is the sharp part: the userland one is the project people *think* they're getting when they hear the name. It really is the "TypeScript for PHP" idea: a gradual layer over code that still runs anywhere. The name you'd expect to belong to project A belongs to project B.

### What it catches

The flagship feature is runtime generics, with the bound tracked per object instance in a `\WeakMap`:

```
/**
* @template T
*/
class Collection
{
/** @param T $item */
public function add(mixed $item): void { /* ... */ }
}

/** @var Collection<User> $users */
$users = new Collection();

$users->add(new User('Alice')); // fine

$users->add(new Product('SKU-100'));
// TypeError: Argument $item (template T = User) must be of type User, Product given
```

And return contracts that the engine itself will never check, because `: array` is all PHP has:

```
/**
* @return list<int>
*/
public function assignableRoles(): array
{
return Role::cases(); // returns enum instances, not ints
}

// TypeError: Return value[0] must be of type int, App\Enums\Role returned
```

It's conceptually modelled on Python's [beartype](https://github.com/beartype/beartype), and it's honest about being opt-out: `TYPEPHP_DISABLE=true`, an `enabled => false` config switch, and `@typephp-ignore`docblock tags. It also detects when it's running under phpstan, psalm, pint, rector or composer and stays out of the way.

### Two projects, one name

| | `swoole/typephp` | `typephp/typephp` |
| --- | ---------------- | ----------------- |
| What it is | AOT compiler to native code | runtime PHPDoc enforcement |
| When types are checked | compile time | runtime |
| Build step | C++ toolchain, CMake, `libphp` | none |
| Types come from | its own syntax (`use native_types`, `std::`) | PHPDoc you probably already wrote |
| PHP support | a documented subset of 8.4–8.5 | 8.1+, all of it |
| Output | binary / extension / shared library | your app, unchanged |
| License | GPL-3.0 | MIT |

Both are named for the thing PHP core still doesn't have. Neither did anything wrong: "TypePHP" is the obvious name for a project that adds types to PHP, which is precisely why two people reached for it. Obvious names are the ones that collide.

If you write about either, use the vendor prefix. `swoole/typephp` and `typephp/typephp` are unambiguous; "TypePHP" is now a coin flip.

## Now say "native PHP" out loud

Here is the count. In current usage, "native" in a PHP sentence can mean at least six different things.

| Phrase | What it actually means |
| ------ | ---------------------- |
| "compiles to native code" | machine instructions for the CPU, as opposed to bytecode |
| "faster than native PHP" | stock, interpreted PHP: the thing you `apt install` |
| "just native PHP, no framework" | vanilla PHP, no Laravel, no Symfony |
| "that's a native function" | implemented in C inside the engine, not userland |
| "`use native_types`" | TypePHP's fixed C++ scalar storage |
| "NativePHP" | the project for building desktop and mobile applications |

The second and the first are already fighting. When Swoole's own promotional material said TypePHP is "tens to hundreds of times faster than native PHP," *native PHP* meant the interpreter, the allegedly traditional slow thing. Two paragraphs later, *native* meant the compiled binary, the fast thing. The word is on both sides of its own benchmark.

The third and fourth meanings have coexisted uneasily for two decades. Someone posts "I did it in native PHP" meaning no dependencies; someone replies about `array_map` being native, meaning written in C. They're both right and they're talking past each other. TypePHP makes this worse rather than better, because its `-m ext` mode produces a real PHP extension from PHP source: code that is now native in the C-implementation sense while having been written in the userland sense.

And then there's [**NativePHP**](https://nativephp.com/), Marcel Pociot's project, which lets you ship a Laravel app as a desktop application, via Electron, or a mobile one or a Desktop application, because it keeps on adding new destinations. Here "native" means *a native application*: a piece of software with a window and a dock icon and a system notification, as opposed to a page in a browser tab. It's the ordinary consumer-software sense of the word, imported wholesale into a language ecosystem that was already using it for two other purposes.

The irony is worth sitting with. NativePHP produces applications that are native in the packaging sense but rendered in a webview: not native in the platform-UI sense that iOS or GTK developers mean. Meanwhile TypePHP produces genuinely native machine code but, in binary mode, has no UI at all. If you wanted to build a native application with native PHP compiled to native code, you would need both projects, and the word would mean something different in each clause.

Somebody, somewhere, is going to write "a NativePHP app with native types compiled to a native binary, replacing our native PHP implementation." That sentence is grammatically correct, semantically meaningful, and completely unparsable without context. And yes, unparsable is not a word in many dictionaries, except may be online.

## This is a vocabulary shortage, not a naming failure

It's tempting to blame the projects. That's unfair. And making fun like this until now is possibly over the edge. Nobody did anything wrong: Swoole's compiler emits native code, so `native_types` is the honest name. NativePHP builds native apps, so NativePHP is the honest name. `array_map` is native to the engine. Two projects that add types to PHP both landed on TypePHP because that is what they do. The problem isn't that anyone chose badly. It's that we have a small pool of obvious words, no shared habit of reaching for a more specific one, and no place to look one up.

Other ecosystems have more words because they needed them earlier. Rust distinguishes `std` from `core` from `alloc`. The JVM world separates *intrinsic*, *native* (JNI), and *ahead-of-time*. Python has *builtin*, *C extension*, *CPython*, and *frozen module*, and people mostly use them correctly.

PHP could pick up the same discipline. Say **interpreted PHP** or **stock PHP** when you mean the ZendVM path. Say **internal function** when you mean C-implemented — the engine's own term, already in the manual. Say [**AOT-compiled**](https://php-dictionary.readthedocs.io/en/latest/index/aot.html) when you mean lowered to machine code. Say **framework-free** or **vanilla** when you mean no dependencies. Say **desktop app** or **mobile app** when that's what you're shipping. Reserve **native** for the cases where you'd have to define it anyway.

And when two projects share a name, write the vendor prefix. It costs seven characters and it removes the ambiguity completely.

None of that requires an RFC. It requires people writing docs, blog posts, and conference abstracts to spend one extra word.

The ecosystem is producing new concepts faster than it's producing terms for them, and 2026 is going to add more: WASI targets, embedded runtimes, hybrid compiled/interpreted deployments. Each one will arrive needing a name, and the path of least resistance will be to overload something we already have.

If you want to help, the **[PHP dictionary](https://example.com/php-dictionary)** is the right place to start: the ultimate fix for a vocabulary shortage is more vocabulary, written down where people can find it.