← Dolaima← Docs indexPHP原文中文

PHP

A subset that can write a normal program: <?php ... ?> tags (text outside a tag is emitted verbatim as HTML, and with 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

Examples

FileWhat it shows
fib.phpFibonacci (recursive plus iterative plus arrays)
hello.phpThe smallest program (echo plus string concatenation plus arithmetic)
sort.phpBubble sort (array read/write plus nested loops plus swapping)

Pitfalls we hit on real devices


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 single source of truth for the criteria is scripts/vml-php-probe/run.sh (currently 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.


Version: v1.0 | Date: 2026-10-13 | Revised by: Shenzhen Tanso Intelligent Technology Co., Ltd.

Specification standard

FieldValue
Target standardPHP 5/7 subset
Publication year2004
CompletenessSubset (not itemised)
Tests25 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 no opening tag at all the whole file is treated as PHP) and plain PHP files. Values are 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 link-time error, never silent. require / include are declarations only and load nothing. One 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:

See the sections below for details.

1. Files and tags

FormBehaviour
<?php … ?>the PHP segment runs; text after ?> is emitted verbatim as HTML, and ?> swallows one following newline
<?= expr ?>equivalent to <?php echo expr ?>
no opening tag at allthe whole file is PHP (the main usage in this repo's examples)

Criteria: cases/17-tagmode.php.

"Is there an opening tag" is a 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

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 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).

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).

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 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 pointer, so it can be passed and returned.

8. Unsupported

Associative arrays (key => value — a 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 (compile-time error), floating-point parameters/returns of 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. Array elements carry no type: echo $arr[i] always uses the integer channel, so a string element prints as its pointer value (cases/24-knownred-elemfmt.php, KNOWN-RED).
  2. No implicit string/number concatenation conversion: "x" . 5 gives "x", not "x5".
  3. Every switch case breaks automatically — fallthrough is not supported (an explicit break; is harmless).
  4. 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. break 2; / continue 2; levels are ignored.
  6. $a[i]++ evaluates the index expression twice (observable if it has side effects).
  7. Undefined variables are 0 (no warning).
  8. echo of an array prints its pointer.
  9. implode handles integer elements only (the library's join_str does).
  10. print does not return 1 (it is handled as a statement).

10. Criteria

scripts/vml-php-probe/run.sh          # 25 pass / 0 fail / 1 known-red

This uses the 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):

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)

Path: third_party/vml/VMLPrepares/PhpCompiler/ Extension: .php Standard library: Lib/php/ (builtin.vml = library aggregator; vml_ui.php = documentation-level declaration table) Probe: scripts/vml-php-probe/run.sh — measured 25 pass / 0 fail / 1 known-red Language subset and deviations: [PHP_LANGUAGE_SPEC.md](PHP_LANGUAGE_SPEC.md) (EN) Wiring: [INTEGRATION.md](INTEGRATION.md) (the shared files are intentionally left untouched here)

What it is

A PHP subset front end that is 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 — 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 real channel — the same "compile → assemble → link (including the Lib/php module set) → run" pipeline as the mobile host MauiVml:

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

⚠ 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. 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. 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 declaration (the front end treats require/include as no-ops): 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 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).