> **Tip**: Like all native_types, the Big* types can only be used in AOT compile mode and cannot run in the normal PHP interpreter. The AOT compiler performs compile-time evaluation of functions such as `std::bigInt()` and directly generates C++ code.
> **Tip**: Big* types can only be used in AOT compile mode and cannot run in
> the normal PHP interpreter. The compiler recognizes functions such as
> `std::bigInt()` and directly generates C++ code.
@ -586,9 +583,11 @@ This restriction also applies to comparison operations. Before comparing, both s
Big* types are a proprietary feature of the AOT compiler, relying on compile-time code generation and C++ underlying libraries. The source code cannot be directly interpreted and executed by the `php` command.
Big* types are a proprietary feature of the AOT compiler, relying on compile-time code generation and C++ underlying libraries. The source code cannot be directly interpreted and executed by the `php` command.
### 12.8 Enabling `use native_types`
### 12.8 No file-level opt-in required
Forgetting to add `use native_types` causes Big* variables to be treated as Var (generic type), losing most of the performance advantages of native types.
Big* constructors determine their result type directly. No file-level native
type declaration is required. `use varint_types` affects only inferred ordinary
integers and does not change BigInt, Decimal, or BigFloat storage.
---
---
@ -598,8 +597,6 @@ Forgetting to add `use native_types` causes Big* variables to be treated as Var
```php
```php
<?php
<?php
declare(strict_types=1);
use native_types;
/**
/**
* Compute the factorial of n, supporting arbitrarily large results
* Compute the factorial of n, supporting arbitrarily large results
@ -625,8 +622,6 @@ function main(): void {
```php
```php
<?php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
function main(): void {
// use Decimal to represent amounts exactly
// use Decimal to represent amounts exactly
@ -661,8 +656,6 @@ total: 64.7676
```php
```php
<?php
<?php
declare(strict_types=1);
use native_types;
function main(): void {
function main(): void {
// use BigFloat for high-precision math computation
// use BigFloat for high-precision math computation
Since TypePHP 0.8, inferred `int`, `float`, and `bool` locals use native
`php::Int`, `php::Float`, and `php::Bool` storage by default. The old
`use native_types` directive has been removed. Native locals are never
silently promoted to `php::Var` later.
Use `use varint_types` only for a file that needs Zend PHP integer widening
semantics. It boxes inferred integers, but not floats or booleans. Use
`std::any($value)` when only one value must be dynamic or reference-capable.
### Basic Native Types
### Basic Native Types
1. ✅ `std::int` - Native integer type (zend_long, 8 bytes)
1. ✅ `std::int` - Native integer type (zend_long, 8 bytes)
2. ✅ `std::float` - Native floating-point type (double, 8 bytes)
2. ✅ `std::float` - Native floating-point type (double, 8 bytes)
@ -24,7 +33,6 @@ Pay particular attention to `unset($obj->prop)` and assigning `null` on fixed-va
```php
```php
<?php
<?php
use native_types;
class User {
class User {
public int $id = 0;
public int $id = 0;
@ -263,8 +271,6 @@ BigInt, Decimal, and BigFloat all inherit from `php::Box` and are stored inside
### Declaration and Construction
### Declaration and Construction
```php
```php
use native_types;
// Construct a BigInt from an integer literal
// Construct a BigInt from an integer literal
$a = std::bigInt(100);
$a = std::bigInt(100);
$b = std::bigInt("123456789012345678901234567890"); // Very long integer string
$b = std::bigInt("123456789012345678901234567890"); // Very long integer string
@ -496,11 +502,15 @@ Both sides are Int
### Rule 1: Var Dominates
### Rule 1: Var Dominates
When at least one side of the operands is of type `Var` (not declared with `use native_types`), both sides are treated as `Var`, using ZendVM's `add_function` / `div_function` and other operation functions, fully following PHP's native type conversion (type juggling) semantics.
When at least one operand is `Var`, both sides use the PHPX/Zend arithmetic
path and follow PHP type-juggling semantics. A `Var` comes from an expression
such as `std::any(...)`, a dynamic runtime value, or an inferred integer in a
file that declares `use varint_types`.
```php
```php
use varint_types;
$a = 10; // Var, stores int(10)
$a = 10; // Var, stores int(10)
$b = 2.5; // Var, stores float(2.5)
$b = 2.5; // php::Float (varint_types affects only inferred integers)
$c = $a + $b; // Both sides are Var → ZendVM operation → float(12.5)
$c = $a + $b; // Both sides are Var → ZendVM operation → float(12.5)
```
```
@ -508,10 +518,11 @@ C++ code generation: `int64_t` and `double` values are implicitly converted to `
### Rule 2: Float Takes Precedence over Int
### Rule 2: Float Takes Precedence over Int
When both sides are native types (declared via `use native_types` or `std::int()`/`std::float()`), if either side is Float, both sides are converted to Float for the operation. Only when both sides are Int is integer arithmetic used.
When both sides are native types (the default, or explicitly constructed with
`std::int()` / `std::float()` inside a varint file), Float takes precedence.
Only two Int operands use integer arithmetic.
```php
```php
use native_types;
$a = 10; // php::Int
$a = 10; // php::Int
$b = 2.5; // php::Float
$b = 2.5; // php::Float
$c = $a + $b; // Float + Float → double addition
$c = $a + $b; // Float + Float → double addition
@ -521,7 +532,10 @@ $e = 3; // php::Int
$f = $d + $e; // Int + Int → int64_t addition
$f = $d + $e; // Int + Int → int64_t addition
```
```
> **Note**: native-type variables **do not change their own type** during operations. For example, `Int += Float` executes `int64_t += double` in C++, and the result is truncated to int64_t, which differs from PHP behavior (in PHP the variable becomes float). This is intentional semantics of `use native_types`.
> **Note**: native variables **do not change storage type** during operations.
> For example, `Int += Float` remains Int. This fixed-type behavior is now the
> default; choose `use varint_types` or `std::any()` when dynamic integer
> widening is required.
### Rule 3: Safe Promotion of High-Precision Types
### Rule 3: Safe Promotion of High-Precision Types
@ -560,15 +574,15 @@ When the operands include `BigInt`, `Decimal`, or `BigFloat`, only explicit and
Compound assignment operators such as `+=`, `-=`, `*=`, `/=`, `%=` follow the same type promotion rules, but the RHS is converted to the type of the LHS variable. If the LHS is Var, the RHS keeps its original type (Var's `operator+=` takes over); if the LHS is a native type, the RHS is explicitly converted to that type.
Compound assignment operators such as `+=`, `-=`, `*=`, `/=`, `%=` follow the same type promotion rules, but the RHS is converted to the type of the LHS variable. If the LHS is Var, the RHS keeps its original type (Var's `operator+=` takes over); if the LHS is a native type, the RHS is explicitly converted to that type.
```php
```php
$a = 10; // Var
use varint_types;
$a = 10; // Var because this file selected varint_types
@ -66,7 +66,7 @@ These items should be documented with the exact boundary.
| Binary mode requires global `main()` | Intentional Rule | This defines the binary entry ABI. |
| Binary mode requires global `main()` | Intentional Rule | This defines the binary entry ABI. |
| `main()` only accepts no parameters or `(int $argc, array $argv)` | Intentional Rule | Keeps the entry ABI explicit and stable. |
| `main()` only accepts no parameters or `(int $argc, array $argv)` | Intentional Rule | Keeps the entry ABI explicit and stable. |
| `main()` must return `void` | Intentional Rule | An integer exit-code convention could be added later, but current TypePHP rules reject return values. |
| `main()` must return `void` | Intentional Rule | An integer exit-code convention could be added later, but current TypePHP rules reject return values. |
| `declare(strict_types=...)` only supports `strict_types=1` | Intentional Rule | Supporting mixed strict/weak typing is possible, but TypePHP keeps strict behavior predictable. |
| TypePHP is always strict; `strict_types=0` is rejected | Intentional Rule | Per-file weak typing conflicts with TypePHP's fixed storage and static type guarantees. `strict_types=1` remains accepted as a redundant compatibility directive. |
| Default parameter before required parameter | Intentional Rule | PHP allows this legacy pattern but ignores the default. TypePHP rejects it to avoid misleading declarations. |
| Default parameter before required parameter | Intentional Rule | PHP allows this legacy pattern but ignores the default. TypePHP rejects it to avoid misleading declarations. |
| Child class overriding parent private property | Intentional Rule / Pending if dynamicized | PHP stores private properties by declaring class. TypePHP native/fixed layouts make this expensive. Rejecting it keeps property layout predictable. |
| Child class overriding parent private property | Intentional Rule / Pending if dynamicized | PHP stores private properties by declaring class. TypePHP native/fixed layouts make this expensive. Rejecting it keeps property layout predictable. |
| `__construct()` return value | Intentional Rule | PHP constructors should not return values. TypePHP rejects this explicitly. |
| `__construct()` return value | Intentional Rule | PHP constructors should not return values. TypePHP rejects this explicitly. |