Category
stdlib | runtime
Problem
Many ext/standard/* Internal::execute() handlers begin with:
if (null === $frame->returnVar) {
return;
}
When a builtin is used as a statement (return value discarded), the VM never validates arguments or runs side effects. Zend still validates types and throws.
Verified today (Docker php-compiler:22.04-dev):
| Call (statement) |
Zend PHP 8.2 |
bin/vm.php today |
strip_tags(E::A); |
TypeError |
silent no-op, script continues |
fputcsv($fp, [E::A]); |
Error (enum in fields) |
silent no-op; stream may be invalid |
Assigning the return ($r = strip_tags(E::A);) does hit validation — behavior differs from Zend for the same call site.
This pattern appears across dozens of builtins (strip_tags.php, fputcsv.php, hash_.php, md5.php, scandir.php, … — grep null === $frame->returnVar in ext/standard/).
php-src reference
Repro (add test/repro-maintainer/parity_builtin_void_return_skipped.php)
<?php
enum E: string { case A = 'a'; }
// strip_tags — must throw even when return discarded
strip_tags(E::A);
echo "strip_tags_after\n";
$fp = fopen('php://memory', 'r+');
fputcsv($fp, [E::A]);
echo "fputcsv_after\n";
fclose($fp);
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh
php8.2 test/repro-maintainer/parity_builtin_void_return_skipped.php 2>&1 || true
php bin/vm.php test/repro-maintainer/parity_builtin_void_return_skipped.php 2>&1 || true'
| Engine |
Expected |
VM today |
| Zend |
Fatal on first call; no strip_tags_after |
Prints strip_tags_after (and often fputcsv_after) |
Scope (this repo)
| Path |
Work |
ext/standard/*.php |
Remove or relocate returnVar === null early returns after argc/type validation; only skip writing $frame->returnVar |
lib/VM.php / Frame |
Optional: central helper so builtins cannot skip validation |
| Tests |
test/compliance/cases/stdlib/builtin_void_return_validates.phpt |
PHP-in-PHP: fix in PHP builtin bodies only — do not add C branches in runtime/.
php-src-strict
Default CI/compliance must match Zend: invalid operands throw even in void context.
Done when
Related
Category
stdlib|runtimeProblem
Many
ext/standard/*Internal::execute()handlers begin with:When a builtin is used as a statement (return value discarded), the VM never validates arguments or runs side effects. Zend still validates types and throws.
Verified today (Docker
php-compiler:22.04-dev):bin/vm.phptodaystrip_tags(E::A);TypeErrorfputcsv($fp, [E::A]);Error(enum in fields)Assigning the return (
$r = strip_tags(E::A);) does hit validation — behavior differs from Zend for the same call site.This pattern appears across dozens of builtins (
strip_tags.php,fputcsv.php,hash_.php,md5.php,scandir.php, … — grepnull === $frame->returnVarinext/standard/).php-src reference
Zend/zend_execute.c—ZEND_CALL/zend_call_function()always runs the callee; return value may be discarded by the opcode, but the function body still executes.ext/standard/string.c—PHP_FUNCTION(strip_tags)Repro (add
test/repro-maintainer/parity_builtin_void_return_skipped.php)strip_tags_afterstrip_tags_after(and oftenfputcsv_after)Scope (this repo)
ext/standard/*.phpreturnVar === nullearly returns after argc/type validation; only skip writing$frame->returnVarlib/VM.php/Frametest/compliance/cases/stdlib/builtin_void_return_validates.phptPHP-in-PHP: fix in PHP builtin bodies only — do not add C branches in
runtime/.php-src-strict
Default CI/compliance must match Zend: invalid operands throw even in void context.
Done when
strip_tags(E::A);andfputcsv($fp, [enum])without printing*_afterext/standard/— no handler returns before validation whenreturnVaris nullRelated