Skip to content

htmlspecialchars/strpos/mkdir/file_put_contents accept fewer arguments than PHP #506

Description

@mirchaemanuel

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&#039;b&quot;c&lt;d&gt;&amp;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:213strpos/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.rsparams: [string: Str], no
    flags parameter modeled at all.
  • src/builtins/io/mkdir.rsparams: [directory: Str], no permissions/
    recursive/context parameters.
  • src/builtins/io/file_put_contents.rsparams: [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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions