Merge pull request 'fix(stub): 修复heredoc/nowdoc字符串生成非法C++的问题' (#36) from stub-fix-heredoc-nowdoc-illegal-cpp into master

Reviewed-on: #36
pull/40/head
韩天峰 1 month ago
commit d2595e34c5
  1. 25
      src/gen_stub.php
  2. 23
      tests/compiler/const/class-const-heredoc-nowdoc.phpt
  3. 51
      tests/compiler/const/heredoc-nowdoc-const-defaults.phpt

@ -834,7 +834,10 @@ class ArgInfo {
private function getDefaultValueAsArginfoString(): string {
if ($this->hasProperDefaultValue()) {
return '"' . addslashes($this->defaultValue) . '"';
// The default value is a PHP expression embedded in a C string.
// Escape for the outer C layer only; addslashes() leaves line
// breaks and other control bytes untouched, producing invalid C++.
return '"' . getTranslator()->escapeString($this->defaultValue) . '"';
}
return "NULL";
@ -2485,7 +2488,9 @@ class EvaluatedValue
if ($forStringDef === '') {
$forStringDef = "{$zvalName}_str";
}
$code .= "\tzend_string *$forStringDef = zend_string_init($cExpr, strlen($cExpr), 1);\n";
// getCExpr() emits a C string literal here. sizeof() preserves
// embedded NUL bytes, unlike strlen().
$code .= "\tzend_string *$forStringDef = zend_string_init($cExpr, sizeof($cExpr) - 1, 1);\n";
$code .= "\tZVAL_STR(&$zvalName, $forStringDef);\n";
}
} elseif ($this->type->isArray()) {
@ -2515,14 +2520,12 @@ class EvaluatedValue
return '"' . getTranslator()->escapeString((string) $this->value) . '"';
} elseif ($this->expr instanceof Expr\ConstFetch) {
return getTranslator()->getConstValue($this->expr->name->toString());
} elseif (!($this->expr instanceof String_)) {
// ConstExprEvaluator has already reduced concatenations and
// other constant string expressions to their PHP value. Emit
// that value as a C string literal instead of rejecting every
// non-literal string expression.
return '"' . getTranslator()->escapeString((string) $this->value) . '"';
}
$expr = preg_replace("/(^'|'$)/", '"', getTranslator()->escapeString($expr));
// ConstExprEvaluator has already decoded literal syntax and
// reduced constant string expressions. Emitting that value avoids
// leaking heredoc/nowdoc source syntax into generated C++.
return '"' . getTranslator()->escapeString((string) $this->value) . '"';
} elseif ($this->type->isInt() or $this->type->isFloat()) {
return strval($this->value);
} elseif ($this->type->isBool()) {
@ -5107,6 +5110,10 @@ function parseFunctionLike(
if ($param->default instanceof Expr\ClassConstFetch && $param->default->class->toLowerString() === "self") {
$defaultValue = getTranslator()->getClassConstValue($func, $name->className->name, $param->default->name->name);
$defaultValue = var_export($defaultValue, true);
} elseif ($param->default instanceof String_) {
// Keep this as a PHP expression. ArgInfo escapes the expression
// separately when embedding it in generated C++.
$defaultValue = var_export($param->default->value, true);
} else {
$defaultValue = $param->default ? $prettyPrinter->prettyPrintExpr($param->default) : null;
}

@ -0,0 +1,23 @@
--TEST--
class constants with heredoc and nowdoc syntax
--FILE--
<?php
class Test
{
const VALUE1 = <<<ABC
quote " slash \\ nul \0 tab \t ??
ABC;
const VALUE2 = <<<'DEF'
$value ?? "quoted" \n \path
DEF;
}
function main()
{
var_dump(bin2hex(Test::VALUE1), bin2hex(Test::VALUE2));
}
?>
--EXPECT--
string(60) "71756f7465202220736c617368205c206e756c2000207461622009203f3f"
string(54) "2476616c7565203f3f202271756f74656422205c6e205c70617468"

@ -0,0 +1,51 @@
--TEST--
global constants, property defaults and parameter defaults with heredoc/nowdoc syntax
--FILE--
<?php
const G_HEREDOC = <<<ABC
abc
ABC;
const G_NOWDOC = <<<'DEF'
def
DEF;
class WithProp
{
public string $p = <<<ABC
xyz
ABC;
}
function with_default(string $x = <<<'ABC'
$value ?? "quoted" \n \path
ABC): string {
return $x;
}
function with_binary_default(string $x = <<<ABC
A\0B
ABC): string {
return $x;
}
function main()
{
var_dump(G_HEREDOC, G_NOWDOC);
$o = new WithProp();
var_dump($o->p);
var_dump(bin2hex(with_default()), bin2hex(with_binary_default()));
$default = (new ReflectionFunction('with_default'))->getParameters()[0]->getDefaultValue();
$binaryDefault = (new ReflectionFunction('with_binary_default'))->getParameters()[0]->getDefaultValue();
var_dump(bin2hex($default), bin2hex($binaryDefault));
}
?>
--EXPECT--
string(3) "abc"
string(3) "def"
string(3) "xyz"
string(54) "2476616c7565203f3f202271756f74656422205c6e205c70617468"
string(6) "410042"
string(54) "2476616c7565203f3f202271756f74656422205c6e205c70617468"
string(6) "410042"
Loading…
Cancel
Save