fix: register TypePHP functions with strict types

pull/46/head
韩天峰 2 weeks ago
parent b69582011a
commit 525a4e4412
  1. 27
      src/Translator.php
  2. 4
      src/gen_stub.php
  3. 38
      tests/compiler/declare/strict_types-default.phpt
  4. 18
      tests/compiler/declare/strict_types.phpt

@ -898,11 +898,11 @@ CODE;
} }
$fullName = $functionDef->getNamespacedName(); $fullName = $functionDef->getNamespacedName();
$zifName = $this->escapeZendFnName($fullName); $zifName = $this->escapeZendFnName($fullName);
if ($functionDef->namespace) { // TypePHP is always strict. Store the flag in the registered
$code .= $this->getIndent() . 'ZEND_NAMED_FE("' . $this->escapeString($fullName) . '", ZEND_FN(' . $zifName . '), arginfo_' . $zifName . ')' . PHP_EOL; // zend_function instead of rewriting shared metadata on every call.
} else { $code .= $this->getIndent() . 'ZEND_RAW_FENTRY("' . $this->escapeString($fullName)
$code .= $this->getIndent() . 'ZEND_FE(' . $zifName . ', arginfo_' . $zifName . ')' . PHP_EOL; . '", ZEND_FN(' . $zifName . '), arginfo_' . $zifName
} . ', ZEND_ACC_STRICT_TYPES, NULL, NULL)' . PHP_EOL;
} }
$code .= $this->getIndent() . "ZEND_FE_END\n};\n// clang-format on" . PHP_EOL . PHP_EOL; $code .= $this->getIndent() . "ZEND_FE_END\n};\n// clang-format on" . PHP_EOL . PHP_EOL;
@ -2569,8 +2569,6 @@ CODE;
} elseif ($key === 'strict_types') { } elseif ($key === 'strict_types') {
if (!($declare->value instanceof Node\Scalar\Int_) or $declare->value->value !== 1) { 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'); $this->fatalError($v, 'declare(strict_types=0) is not allowed, only strict_types=1 is supported');
} else {
$this->setStrictTypes(1);
} }
} else { } else {
$this->fatalError($v, 'declare(' . $key . '=' . $value . ') is not supported'); $this->fatalError($v, 'declare(' . $key . '=' . $value . ') is not supported');
@ -3249,17 +3247,6 @@ CODE;
$cppCode .= $this->genWrapperRequiredArgCountCheck($functionDef, $displayName); $cppCode .= $this->genWrapperRequiredArgCountCheck($functionDef, $displayName);
} }
/**
* Tthe 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) { foreach ($functionDef->argInfoList as $k => $argInfo) {
$var = 'arg_' . $argInfo->name; $var = 'arg_' . $argInfo->name;
if ($argInfo->variadic) { if ($argInfo->variadic) {
@ -4996,8 +4983,4 @@ CODE;
return $code; return $code;
} }
private function genStrictTypesCode(): string
{
return "execute_data->func->common.fn_flags |= ZEND_ACC_STRICT_TYPES;" . PHP_EOL;
}
} }

@ -1688,7 +1688,9 @@ class FuncInfo {
private function getArginfoFlagsByPhpVersions(): VersionFlags private function getArginfoFlagsByPhpVersions(): VersionFlags
{ {
$flags = []; // TypePHP is always strict. Register functions and methods with the
// flag once so dynamic calls made by native bodies inherit that mode.
$flags = ["ZEND_ACC_STRICT_TYPES"];
if ($this->isMethod()) { if ($this->isMethod()) {
if ($this->flags & Modifiers::PROTECTED) { if ($this->flags & Modifiers::PROTECTED) {

@ -0,0 +1,38 @@
--TEST--
TypePHP enables strict types without a declare directive
--FILE--
<?php
function checkStrictCaller(string $label): void
{
$callable = 'strlen';
try {
$callable(123);
echo $label, "=accepted\n";
} catch (TypeError $error) {
echo $label, "=TypeError\n";
}
}
class StrictMethodCaller
{
public function check(): void
{
checkStrictCaller('method');
}
}
function main(): void
{
checkStrictCaller('main');
$function = 'checkStrictCaller';
$function('function');
$method = [new StrictMethodCaller(), 'check'];
$method();
}
?>
--EXPECT--
main=TypeError
function=TypeError
method=TypeError

@ -4,14 +4,14 @@ declare: strict types 1
<?php <?php
declare(strict_types=1); declare(strict_types=1);
function main() { function main() {
parse_url(0); $callable = 'strlen';
try {
$callable(123);
echo "accepted\n";
} catch (TypeError $error) {
echo "TypeError\n";
}
} }
?> ?>
--EXPECTF-- --EXPECT--
Fatal error: Uncaught TypeError: parse_url(): Argument #1 ($url) must be of type string, int given in %s:%d TypeError
Stack trace:
#0 %s
#1 %s
#2 {main}
thrown in %s on line %d

Loading…
Cancel
Save