# PHP

A subset that can «bold»write a normal program«/»: `<?php ... ?>` tags (text outside a tag is emitted verbatim as HTML, and with «bold»no opening tag at all«/» the whole file is PHP), `$variables`, `echo` / `print`, the full set of arithmetic / comparison / logical / bitwise operators, `if` / `while` / `do-while` / `for` / `foreach` / `switch`, functions (default arguments, recursion, `global`) and subscripted arrays (both `[]` and `array()` literals).

Arithmetic, control flow and function calls are all there.

## How to run it on your phone

```
vml run examples/php/fib.php
```

Compiling takes a while (one to two minutes for C, a few seconds for scripting languages).
Once the program is running, **the gamepad is at the bottom of the screen** - the D-pad
and the four action buttons are all there. Tap the back arrow in the top-right corner to
return to the Shell.

## What to know before you write

- `ui_*` is written «bold»directly«/» (every other name is emitted as a bare `CALL`, and the linker pulls in the matching `.vml` by prefix)
- `echo` takes several arguments at once (printed with no separator); string concatenation uses `.`
- The array literals `[10, 20, 30]` and `array(10, 20, 30)` are equivalent; `$a[] = v` «bold»appends«/»
- `require` / `include` are «bold»declarations«/» (no-ops) here, so removing them still compiles
- Variables (`$name`) and keywords are «bold»case-insensitive«/»; comments come in three forms: `//`, `#`, `/* */`

## Examples

| File | What it shows |
|---|---|
| `fib.php` | Fibonacci (recursive plus iterative plus arrays) |
| `hello.php` | The smallest program (`echo` plus string concatenation plus arithmetic) |
| `sort.php` | Bubble sort (array read/write plus nested loops plus swapping) |

## Pitfalls we hit on real devices

- ⚠ «bold»Associative arrays (`["k" => 1]`) are not supported«/», and that is a «bold»compile-time error«/» (keys are never silently dropped). Objects / classes / interfaces / closures / anonymous functions / `try`-`catch` / `isset` / `unset` / `empty` / `goto` are not supported either.
- ⚠ «bold»Array elements carry no type«/»: `echo $arr[i]` always goes down the «bold»integer«/» channel, so a string element prints as a «bold»pointer value«/» (the case `24-knownred-elemfmt.php` pins this down).
- ⚠ «bold»Implicit string/number concatenation does not convert«/»: `"x" . 5` gives `"x"`, not `"x5"` - at runtime that cell cannot tell an integer from a string pointer, and forcing a conversion would be worse.
- ⚠ «bold»`switch` breaks out of every case automatically«/» (fall-through is not supported), and a case value «bold»must be a compile-time integer constant«/» (a string or range case is a compile-time error).
- ⚠ A user function's «bold»float parameters / return value are passed as integers«/» (truncated with `D2I`) - floats are fully usable only «bold»at the top level«/» and «bold»between builtins«/»; crossing a user function loses the fraction.
- An undefined variable is «bold»treated as 0«/» (no `Undefined variable` warning, deliberately unlike real PHP); `break 2;` / `continue 2;` «bold»ignore the level«/» (treated as 1).

---

The rest of this page is taken straight from the VML source (`third_party/vml/VMLPrepares/PhpCompiler/`):
the `README` covers what this front end supports and how to compile it, and the
language reference covers the syntax itself. When upstream changes, regenerating
this page brings it up to date.

## Language reference

### PHP language subset specification (Dolaima)

Applies to: `VMLPrepares/PhpCompiler`. The «bold»single source of truth for the criteria«/» is
`scripts/vml-php-probe/run.sh` (currently «bold»25 pass / 0 fail / 1 known-red«/»). Everything
described here as "supported" has a case that can actually go red; most "unsupported" items
are pinned by an `EXPECT-ERR` case.

---

> «bold»Version«/»: v1.0 | «bold»Date«/»: 2026-10-13 | «bold»Revised by«/»: Shenzhen Tanso Intelligent Technology Co., Ltd.

#### Specification standard

| Field | Value |
|:-----|:----|
| «bold»Target standard«/» | PHP 5/7 subset |
| «bold»Publication year«/» | 2004 |
| «bold»Completeness«/» | Subset (not itemised) |
| «bold»Tests«/» | 25 passing / 0 failing / 1 known-red |

#### Keywords

`if` `elseif` `else` `endif` `while` `endwhile` `do` `for` `endfor` `foreach`
`endforeach` `as` `switch` `endswitch` `case` `default` `break` `continue`
`return` `function` `global` `echo` `print` `true` `false` `null` `nil` `and`
`or` `xor` `not` `array`

#### Overview
A subset of PHP 5/7 is supported, compiled to VML assembly. It accepts both `<?php … ?>` segments interleaved with HTML (text after `?>` is emitted verbatim as HTML; with «bold»no opening tag at all«/» the whole file is treated as PHP) and plain PHP files. Values are «bold»statically tiered«/»: every value is classified at compile time as `Int` (32-bit / 4 bytes) or `Float` (double precision); `/` is always double precision, and any other arithmetic goes double precision if either side is a float. Builtins are emitted with dedicated code paths, and every other name is emitted as `CALL <original name>`, with the linker's `SharedPrefixMap` pulling in the library by prefix (`ui_` → `vmlui`, `tty_` → `tty`, …); a name that is not in the library is a «bold»link-time error«/», never silent. `require` / `include` are «bold»declarations only«/» and load nothing. One «bold»deliberate«/» difference from the other frontends: no preprocessor is run (in PHP `#` starts a comment).

#### Supported language features

The features this frontend accepts are organised by the sections below; each one states what is accepted and what it means:

- «bold»Files and tags«/»: `<?php … ?>` / `<?= … ?>` / no opening tag means the whole file is PHP — §1
- «bold»Lexical«/»: variables `$name`, comments (`//`, `#`, `/* */`), numbers, single- and double-quoted strings with interpolation, identifiers (keywords are case-insensitive) — §2
- «bold»Statements«/»: `echo` / `print`, assignment and compound assignment, subscript read/write and `[]` append, `if` / `elseif` / `else` (both `{ }` and `: … endif;`), `while` / `do…while`, `for`, `foreach`, `switch`, `break` / `continue`, `function` / `return`, `global`, `require` / `include`, HTML segments — §3
- «bold»Expressions and operators«/»: the precedence table, arithmetic, comparison (including `===`), two logical families, bitwise operators, `.` concatenation, ternary and `?:` / `??`, increment/decrement — §4
- «bold»Type tiers«/»: how `Int` / `Float` is decided, and how `echo` dispatches — §5
- «bold»Builtin functions«/»: `strlen` / `count` / `strtoupper` / `strtolower` / `substr` / `strpos` / `implode` / `explode` / `intval` / `floatval` / `abs` / `max` / `min` / `rand` / `sprintf` / `printf` / `array_push` / `array_pop` / `intdiv` and more — §6
- «bold»Arrays«/»: the `[count, e0, e1, …]` layout and pointer semantics — §7
- «bold»Unsupported items«/»: associative arrays, objects and classes, closures, `try` / `catch`, `isset` / `unset` / `empty`, `goto` and more (most «bold»report loudly«/») — §8
- «bold»Known differences«/»: ten items that run but differ from real PHP — §9

See the sections below for details.

#### 1. Files and tags

| Form | Behaviour |
|---|---|
| `<?php … ?>` | the PHP segment runs; text after `?>` is emitted «bold»verbatim as HTML«/», and `?>` swallows one following newline |
| `<?= expr ?>` | equivalent to `<?php echo expr ?>` |
| «bold»no opening tag at all«/» | the «bold»whole file is PHP«/» (the main usage in this repo's examples) |

Criteria: `cases/17-tagmode.php`.

> "Is there an opening tag" is a «bold»textual«/» pre-check (looking for `<?php` / `<?=`). If you
> write `"<?php"` inside a string literal it will be taken as a tag — a documented trade-off.

#### 2. Lexical

* «bold»Variables«/»: `$name`. Reading an unassigned variable yields 0 (a subset behaviour,
  deliberately different from PHP's `Warning: Undefined variable`).
* «bold»Comments«/»: `// …`, `# …`, `/* … */` (not nested).
* «bold»Numbers«/»: decimal, `0x` hex, fractions, `1e3`. All are «bold»32-bit integers«/»; double
  precision is used only where floating point is required (section 5).
* «bold»Strings«/»: single quotes recognise only `\\` and `\'`; double quotes recognise
  `\n \t \r \0 \\ \" \$` and perform «bold»`$var` interpolation«/».
* «bold»Identifiers«/»: bare names (function names). Keywords are «bold»case-insensitive«/».

#### 3. Statements

`echo`/`print`, assignment (including `+= -= *= /= %= .=`), `if/elseif/else` (both `{ }` and
`: … endif;`), `while`, `do…while`, `for`, `foreach` (`as $v` and `as $k => $v`),
`switch/case/default`, `break`/`continue`, `function` with default parameters, `return`,
`global`, `require`/`include`, and HTML segments (which behave as `echo "text";`).

#### 4. Operators

Precedence roughly follows PHP. Arithmetic `+ - * / % «bold»` (`«/»` uses the shared `ipow`,
so it is an «bold»integer«/» power); comparisons `== != === !== < <= > >= <>` (`===`/`!==` are
synonyms for `==`/`!=` — there are no type tags to distinguish); logical `&& || !` and
`and or not xor` (both short-circuit and normalise to 0/1); bitwise `& | ^ ~ << >>`;
concatenation `.`; ternary `?:` and `??`; prefix/postfix `++`/`--`.

#### 5. Value tiers

Every value is statically classified as `Int` (32-bit general register / 4-byte stack slot)
or `Float` (D0 / 8-byte slot).

* A literal with a fraction or exponent is `Float`, otherwise `Int`.
* «bold»`/` is always double precision«/» (PHP semantics: `17/25` is `0.68`, not `0`).
* Any other arithmetic: if either side is `Float`, the double path is used.
* Strings and arrays are «bold»pointers«/» (4 bytes).
* «bold»`echo` dispatch«/»: `String` → `print_str`; `Float` → `print_float` (after `D2F`);
  anything else → `print_int`.

> Integers stay 32-bit / 4-byte so that they line up with `Lib/`'s 4-byte parameter area.
> Making everything a double breaks that immediately (the Lua front end hit exactly this).

#### 6. Built-in functions

Special-cased (the front end lays out the arguments itself): `strlen`, `count`/`sizeof`,
`strtoupper`, `strtolower`, `substr`, `strpos`, `implode`, `explode`, `intval`, `floatval`,
`abs`, `max`, `min`, `rand` (0 or 2 args), `sprintf`, `printf`, `array_push`, `array_pop`,
`intdiv`, `print_r`/`var_dump`, and the `require`/`include` family (no-ops).

«bold»Everything else is emitted as `CALL <name>` directly«/» (`ui_2d_clear(...)` →
`CALL ui_2d_clear`); at link time `SharedPrefixMap` (`ui_` → `vmlui`, `str` → `string`, …)
pulls the library in. A name that does not exist in any library is reported «bold»at link time«/»
(`undefined function 'x'`), never silently ignored.

#### 7. Arrays

Same layout as `Lib/shared/array.c`: `[count, e0, e1, …]`, 4 bytes per cell. An array value is
a «bold»pointer«/», so it can be passed and returned.

#### 8. Unsupported

Associative arrays (`key => value` — a «bold»compile-time error«/», keys are never silently dropped),
`{$expr}` interpolation, `$a[i]` interpolation, objects/classes/traits, closures,
`try`/`catch`/`throw`, string or range `switch` cases («bold»compile-time error«/»),
floating-point parameters/returns of «bold»user-defined«/» functions (truncated to integers),
`isset`/`unset`/`empty`/`list`/`die`/`exit`, namespaces/autoloading, `goto`.

#### 9. Known deviations (runs, but differs from real PHP)

1. «bold»Array elements carry no type«/»: `echo $arr[i]` always uses the integer channel, so a
   string element prints as its «bold»pointer value«/» (`cases/24-knownred-elemfmt.php`, `KNOWN-RED`).
2. «bold»No implicit string/number concatenation conversion«/»: `"x" . 5` gives `"x"`, not `"x5"`.
3. «bold»Every `switch` case breaks automatically«/» — fallthrough is not supported (an explicit
   `break;` is harmless).
4. «bold»User functions cross the boundary as integers«/»: float arguments are `D2I`-truncated,
   likewise return values. Floats are fully usable at the top level and between built-ins.
5. «bold»`break 2;` / `continue 2;` levels are ignored.«/»
6. `$a[i]++` evaluates the index expression «bold»twice«/» (observable if it has side effects).
7. «bold»Undefined variables are 0«/» (no warning).
8. «bold»`echo` of an array«/» prints its pointer.
9. «bold»`implode` handles integer elements only«/» (the library's `join_str` does).
10. «bold»`print` does not return 1«/» (it is handled as a statement).

#### 10. Criteria

```bash
scripts/vml-php-probe/run.sh          # 25 pass / 0 fail / 1 known-red
```

This uses the «bold»real channel«/»: every case is compiled, assembled, linked (including the
`Lib/php` module set) and run by the single command `vmlcli <case.php> --lang php`.

Two regression cases that genuinely go red (both measured):

* `cases/23-ctrlflow-vmb.php` — changing `CodeGenerator.PlaceLbl` back to `AddLabel`
  (emitting a `LABEL` pseudo-instruction) shifts the `.vmb` symbol table and the program
  prints nothing (expected `6 30`).
* `cases/03-strings.php` — dropping `len(a)` from the concatenation buffer size produces a
  long run of `ababab…` (overlapping heap blocks, self-replication).

Full records of the platform-level defects (symptom / root cause / measurements / workaround)
are in [`bugs/vml_php_bugs/README.md`](../../../bugs/vml_php_bugs/README.md).

## Compiler README

### PHP compiler (`VMLPrepares/PhpCompiler`)

«bold»Path«/»: `third_party/vml/VMLPrepares/PhpCompiler/`
«bold»Extension«/»: `.php`
«bold»Standard library«/»: `Lib/php/` (`builtin.vml` = library aggregator; `vml_ui.php` = documentation-level declaration table)
«bold»Probe«/»: `scripts/vml-php-probe/run.sh` — measured «bold»25 pass / 0 fail / 1 known-red«/»
«bold»Language subset and deviations«/»: [`PHP_LANGUAGE_SPEC.md`](PHP_LANGUAGE_SPEC.md) ([EN](PHP_LANGUAGE_SPEC.en.md))
«bold»Wiring«/»: [`INTEGRATION.md`](INTEGRATION.md) (the shared files are intentionally left untouched here)

#### What it is

A PHP subset front end that is «bold»enough to write ordinary programs«/»: `<?php … ?>` tags
(text outside the tags is emitted verbatim as HTML), `$variables`, `echo`/`print`,
full arithmetic/comparison/logical/bitwise operators, `if/elseif/else` (including the
`endif` alternate syntax), `while` / `do-while` / `for` / `foreach` (including `as $k => $v`),
`switch`, `break`/`continue`, functions (default parameters, recursion, `global`),
indexed arrays (`[]` and `array()`), single/double quoted strings (double quotes support
`$var` interpolation), `//` `#` `/* */` comments, and a set of built-in functions (see the spec).

The three essentials — «bold»complete operators + control flow + function calls«/» — are all present:
the three programs under `Examples/php/` run (recursion, arrays passed to and returned from
functions, bubble sort).

#### Running it

Wiring is done (see `INTEGRATION.md`), so this uses the «bold»real channel«/» — the same
"compile → assemble → link (including the `Lib/php` module set) → run" pipeline as the mobile
host `MauiVml`:

```bash
dotnet build scripts/vmlcli/vmlcli.csproj -c Release
export VML_HOME=$PWD/third_party/vml

dotnet scripts/vmlcli/bin/Release/net10.0/vmlcli.dll third_party/vml/Examples/php/hello.php --lang php
scripts/vml-php-probe/run.sh
```

> ⚠ «bold»Criteria must enter through the real channel.«/» An earlier version of the probe compiled
> with `PhpCompiler.dll --vmb` and then ran the `.vmb` with vmlcli; that bypassed the compiler
> registry and `Lib/<lang>/` resolution, so defects like "php is not registered in vmlcli",
> "the php entry is missing from `vmltool.config.xml`" or "`array.vml` is missing from `Libs=`"
> could not be observed at all. See PHP-BUG-06 in
> [`bugs/vml_php_bugs/`](../../../bugs/vml_php_bugs/README.md).

#### Where library functions come from

Two paths, neither of which reads a declaration file:

1. «bold»Built-in table«/» (`CodeGenerator.Builtins.cs`): `strlen`, `count`, `substr`,
   `strtoupper`, `strtolower`, `strpos`, `implode`, `explode`, `intval`, `floatval`,
   `abs`, `max`, `min`, `rand`, `sprintf`, `printf`, `array_push`, `array_pop`,
   `intdiv`, `print_r`/`var_dump`, `require` family.
   These need the front end to lay out arguments itself (a destination buffer for strings,
   the `[len, e0, …]` layout for arrays), so they do not go through name resolution.
2. «bold»Everything else: emit `CALL <name>` directly«/» (`ui_2d_clear`, `tty_puts`, any library
   export). At link time `CompilerHelper`'s `SharedPrefixMap` (`ui_` → `vmlui`, `str` → `string`, …)
   pulls the matching `.vml` in — the same rule the Lua / Ruby / Python front ends use.

`require 'vml_ui.php';` is a «bold»declaration«/» (the front end treats `require`/`include` as no-ops):
«bold»deleting it still compiles and runs correctly«/» (`LoadKind = documentation-level`).
`Lib/php/vml_ui.php` is therefore a comment table for humans.

#### One deliberate difference from the other front ends: no preprocessor

`CompilerHelper.CompileFileStandard` runs the preprocessor whenever the source contains `#`,
but in PHP `#` starts a comment — the two collide. So PHP deliberately «bold»bypasses that template«/»:
the preprocessor only runs when the source really contains `#include`.

#### Known defects / limitations

See [`bugs/vml_php_bugs/`](../../../bugs/vml_php_bugs/README.md) (2 platform-level defects,
2 front-end bugs that were fixed, 2 limitations) and the "Unsupported" section of the spec.
The most important platform-level defect: in a `.vmb`, the symbol table is **shifted by one entry
per `LABEL` pseudo-instruction**. This front end avoids it by never emitting pseudo-instructions
for labels, guarded by a regression case that really does go red
(`scripts/vml-php-probe/cases/23-ctrlflow-vmb.php`).
