Summary
Several commonly used builtins accept fewer arguments/flags than PHP, with no
workaround other than rewriting the call. All four are enforced explicitly in
the compiler (fixed/capped arity in src/types/signatures.rs and the
per-builtin src/builtins/** declarations), not accidental gaps — but the
capped arity is real-world limiting for anything that needs entity encoding,
substring search from an offset, recursive directory creation, or append-mode
file writes.
Environment
- elephc v0.26.0 (release, commit
892ff162), macOS ARM64
- Verified against the compiler source (
tools/elephc-v0.26.0-src build tree) as well as the compiled binary
Table: function → PHP signature → elephc signature → repro
| Function |
PHP signature |
elephc signature |
Repro |
Observed |
htmlspecialchars |
(string $s, int $flags = ENT_QUOTES|ENT_SUBSTITUTE|ENT_HTML401, ...) |
(string $s) only |
echo htmlspecialchars("a'b", ENT_QUOTES); |
Undefined constant: ENT_QUOTES (bare 3 instead: htmlspecialchars() takes exactly 1 argument) |
strpos / strrpos |
(string $haystack, string $needle, int $offset = 0) |
(string $haystack, string $needle), third arg rejected |
strpos($s, "b", 2); |
strpos() takes exactly 2 arguments |
mkdir |
(string $directory, int $permissions = 0777, bool $recursive = false, ...) |
(string $directory) only, non-recursive |
mkdir('/tmp/a/b'); (parent missing) |
1-arg call only; no way to request recursive creation or non-default permissions |
file_put_contents |
(string $filename, mixed $data, int $flags = 0, ...) |
(string $filename, string $data) only |
file_put_contents($f, $s, FILE_APPEND); |
2-arg call only; no append mode, no LOCK_EX |
htmlspecialchars's single-argument behavior does match PHP >= 8.1's default
(ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401) byte-for-byte when no explicit
flags are needed — verified htmlspecialchars("a'b\"c<d>&e") gives
a'b"c<d>&e under both. The gap only bites when the flags
argument is passed explicitly, or when a non-default flag combination is
required.
Source pointers (v0.26.0)
src/types/signatures.rs:213 — strpos/strrpos signature is declared
with the full [haystack, needle, offset] param list, but
src/builtins/string/strpos.rs caps max_args: 2, so the third argument
is rejected by check_arity even though the type signature already models
it (offset: Int = 0) and the shared lowering path
(lower_string_position) presumably already knows how to consume it.
src/builtins/string/htmlspecialchars.rs — params: [string: Str], no
flags parameter modeled at all.
src/builtins/io/mkdir.rs — params: [directory: Str], no permissions/
recursive/context parameters.
src/builtins/io/file_put_contents.rs — params: [filename: Str, data: Str],
no flags/context parameters.
Expected
Each builtin should accept (at least) the commonly used PHP-compatible
optional arguments: htmlspecialchars($s, $flags), strpos($h, $n, $offset)
(the offset plumbing already exists in the signature — this one looks like
the smallest fix, essentially just raising max_args), mkdir($dir, $perms, $recursive), file_put_contents($file, $data, $flags).
Workarounds applied
htmlspecialchars: called with one argument; relies on the coincidence
that elephc's default matches PHP's current default.
strpos: rewritten as strpos(substr($haystack, $offset), $needle) plus
manual index translation back to the original string.
mkdir: directories created one level at a time, parent-first.
file_put_contents: never used to append; the app always writes whole
files.
Notes
Found building a pastebin single-binary app (--web) for the Discord
contest, where all four functions are used in the storage/rendering layer.
Summary
Several commonly used builtins accept fewer arguments/flags than PHP, with no
workaround other than rewriting the call. All four are enforced explicitly in
the compiler (fixed/capped arity in
src/types/signatures.rsand theper-builtin
src/builtins/**declarations), not accidental gaps — but thecapped arity is real-world limiting for anything that needs entity encoding,
substring search from an offset, recursive directory creation, or append-mode
file writes.
Environment
892ff162), macOS ARM64tools/elephc-v0.26.0-srcbuild tree) as well as the compiled binaryTable: function → PHP signature → elephc signature → repro
htmlspecialchars(string $s, int $flags = ENT_QUOTES|ENT_SUBSTITUTE|ENT_HTML401, ...)(string $s)onlyecho htmlspecialchars("a'b", ENT_QUOTES);Undefined constant: ENT_QUOTES(bare3instead:htmlspecialchars() takes exactly 1 argument)strpos/strrpos(string $haystack, string $needle, int $offset = 0)(string $haystack, string $needle), third arg rejectedstrpos($s, "b", 2);strpos() takes exactly 2 argumentsmkdir(string $directory, int $permissions = 0777, bool $recursive = false, ...)(string $directory)only, non-recursivemkdir('/tmp/a/b');(parent missing)file_put_contents(string $filename, mixed $data, int $flags = 0, ...)(string $filename, string $data)onlyfile_put_contents($f, $s, FILE_APPEND);LOCK_EXhtmlspecialchars's single-argument behavior does match PHP >= 8.1's default(
ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401) byte-for-byte when no explicitflags are needed — verified
htmlspecialchars("a'b\"c<d>&e")givesa'b"c<d>&eunder both. The gap only bites when the flagsargument is passed explicitly, or when a non-default flag combination is
required.
Source pointers (v0.26.0)
src/types/signatures.rs:213—strpos/strrpossignature is declaredwith the full
[haystack, needle, offset]param list, butsrc/builtins/string/strpos.rscapsmax_args: 2, so the third argumentis rejected by
check_arityeven though the type signature already modelsit (
offset: Int = 0) and the shared lowering path(
lower_string_position) presumably already knows how to consume it.src/builtins/string/htmlspecialchars.rs—params: [string: Str], noflags parameter modeled at all.
src/builtins/io/mkdir.rs—params: [directory: Str], no permissions/recursive/context parameters.
src/builtins/io/file_put_contents.rs—params: [filename: Str, data: Str],no flags/context parameters.
Expected
Each builtin should accept (at least) the commonly used PHP-compatible
optional arguments:
htmlspecialchars($s, $flags),strpos($h, $n, $offset)(the offset plumbing already exists in the signature — this one looks like
the smallest fix, essentially just raising
max_args),mkdir($dir, $perms, $recursive),file_put_contents($file, $data, $flags).Workarounds applied
htmlspecialchars: called with one argument; relies on the coincidencethat elephc's default matches PHP's current default.
strpos: rewritten asstrpos(substr($haystack, $offset), $needle)plusmanual index translation back to the original string.
mkdir: directories created one level at a time, parent-first.file_put_contents: never used to append; the app always writes wholefiles.
Notes
Found building a pastebin single-binary app (
--web) for the Discordcontest, where all four functions are used in the storage/rendering layer.