From 7f46445a38ad080713c198ea81f50b2c8dbd6f34 Mon Sep 17 00:00:00 2001 From: NathanFreeman <1056159381@qq.com> Date: Sat, 8 Aug 2026 16:01:07 +0800 Subject: [PATCH] support strict_types --- src/Translator.php | 40 ++++++++++++++++++++++++ tests/compiler/declare/strict_types.phpt | 17 ++++++++++ 2 files changed, 57 insertions(+) create mode 100644 tests/compiler/declare/strict_types.phpt diff --git a/src/Translator.php b/src/Translator.php index d3426cc3..8b0f1087 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -94,6 +94,7 @@ class Translator extends Preprocessor */ protected array $classCeList = []; protected array $classCeInfo = []; + private int $strictTypes = 0; protected function isConstructorNativeFunction(FunctionDef $func): bool { @@ -2416,6 +2417,12 @@ CODE; $this->resetMethod(); $this->resetFunction(); + /** + * Here, the value of strict_types needs to be reset, + * as not every file has declare(strict_types=1); set. + */ + $this->setStrictTypes(0); + $cppCode = ''; foreach ($stmts as $v) { $type = $v->getType(); @@ -2569,6 +2576,8 @@ CODE; } elseif ($key === 'strict_types') { if (!($declare->value instanceof Node\Scalar\Int_) or $declare->value->value !== 1) { $this->fatalError($v, 'declare(strict_types=0) is not allowed, only strict_types=1 is supported'); + } else { + $this->setStrictTypes(1); } } else { $this->fatalError($v, 'declare(' . $key . '=' . $value . ') is not supported'); @@ -3246,6 +3255,18 @@ CODE; if ($functionDef->argCountRequired > 0) { $cppCode .= $this->genWrapperRequiredArgCountCheck($functionDef, $displayName); } + + /** + * If the current file declares `declare(strict_types=1);`, the current function must be marked with the `ZEND_ACC_STRICT_TYPES` flag. + * This flag is used to ensure that when the function calls other functions through the ZendVM, it strictly validates argument types + * according to the declared mode. + * + * It is important to note that this mechanism only applies to functions dynamically invoked by the ZendVM. + * If the target function is compiled native code, type checking will not be triggered—this is by design in the ZendVM, + * whose philosophy is to trust the C code implementation of compiled internal functions. + */ + $cppCode .= $this->genStrictTypesCode(); + foreach ($functionDef->argInfoList as $k => $argInfo) { $var = 'arg_' . $argInfo->name; if ($argInfo->variadic) { @@ -4981,4 +5002,23 @@ CODE; return $code; } + + private function genStrictTypesCode(): string + { + /** + * Type checking is enabled only in binary mode. Since binary mode runs as a standalone executable, + * it must preserve behavioral semantics consistent with directly executed PHP code, + * so type checking must be retained to ensure safety. + * Conversely, when compiled as an extension (e.g., ext/curl), the compilation side does not need to + * enforce built-in type checking; whether it is enabled should be left to the calling PHP code to decide. + */ + return $this->strictTypes == 1 && $this->isBuildModeBin() && !$this->isWasiTarget() + ? "execute_data->func->common.fn_flags |= ZEND_ACC_STRICT_TYPES;" . PHP_EOL + : ''; + } + + private function setStrictTypes(int $value): void + { + $this->strictTypes = $value; + } } diff --git a/tests/compiler/declare/strict_types.phpt b/tests/compiler/declare/strict_types.phpt new file mode 100644 index 00000000..b2cfc285 --- /dev/null +++ b/tests/compiler/declare/strict_types.phpt @@ -0,0 +1,17 @@ +--TEST-- +declare: strict types 1 +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught TypeError: parse_url(): Argument #1 ($url) must be of type string, int given in %s:%d +Stack trace: +#0 %s +#1 %s +#2 {main} + thrown in %s on line %d +