refactor(php): 优化编译器基础类和通用方法调用实现

- 在CompilerBase.php中添加占位符表达式检查逻辑,统一返回TYPE_OBJECT类型
- 更新UniversalMethodCall.php中的方法处理器配置,将多个方法从php_fn改为direct_method
- 为genUniversalDirectMethod方法添加intCastArgs参数支持
- 实现genUniversalCppFn方法用于C++函数调用
- 为genUniversalPhpFn方法添加constArgs参数和接收器位置控制
- 重构字符串方法配置,移除toString方法和isTyped数组方法
- 更新测试文件,移除toString方法相关测试用例
- 改进代码生成逻辑,支持参数类型转换和常量参数插入
pull/1/head
韩天峰 3 months ago
parent 5c86bf39f1
commit c04778cc51
  1. 10
      src/Php/CompilerBase.php
  2. 107
      src/Php/UniversalMethodCall.php
  3. 5
      tests/aot/string_method/misc.phpt

@ -2283,6 +2283,9 @@ class CompilerBase extends \PhpAot\Core\Translator
if (in_array($name, self::STREAM_FUNCTIONS) || $name === 'stream_cast') {
return self::TYPE_STREAM;
}
if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) {
return self::TYPE_OBJECT;
}
if ($this->hasFunction($name)) {
return $this->getFunction($name)->returnType;
}
@ -2295,6 +2298,9 @@ class CompilerBase extends \PhpAot\Core\Translator
// Class definition resolution (handles this_, typed VarExpr)
$classDef = $this->resolveObjectClassDef($expr->var);
if ($classDef !== null && $classDef->hasMethod($method)) {
if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) {
return self::TYPE_OBJECT;
}
return $classDef->getMethod($method)->getReturnType();
}
if ($this->isVarExpr($expr->var)) {
@ -2321,6 +2327,10 @@ class CompilerBase extends \PhpAot\Core\Translator
break;
case 'Expr_StaticCall':
if ($this->isNameExpr($expr->class) && $this->isIdExpr($expr->name)) {
// First-class callable syntax creates a Closure, not a method return value
if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) {
return self::TYPE_OBJECT;
}
$className = $this->parseIdentifier($expr->class);
if ($className === 'self') {
$className = $this->getFullClassName();

@ -39,7 +39,7 @@ trait UniversalMethodCall
self::TYPE_STR => [
// --- stdext string_methods (all use PHP standard functions) ---
'length' => ['handler' => 'php_fn', 'fn' => 'strlen', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'isEmpty' => ['handler' => 'str_is_empty', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0],
'isEmpty' => ['handler' => 'direct_method', 'method' => 'empty', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0],
'lower' => ['handler' => 'php_fn', 'fn' => 'strtolower', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'upper' => ['handler' => 'php_fn', 'fn' => 'strtoupper', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'lowerFirst' => ['handler' => 'php_fn', 'fn' => 'lcfirst', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
@ -56,7 +56,7 @@ trait UniversalMethodCall
'trim' => ['handler' => 'php_fn', 'fn' => 'trim', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 2],
'lTrim' => ['handler' => 'php_fn', 'fn' => 'ltrim', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1],
'rTrim' => ['handler' => 'php_fn', 'fn' => 'rtrim', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1],
'parseStr' => ['handler' => 'php_fn', 'fn' => 'swoole_parse_str', 'return_type' => self::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0],
'parseStr' => ['handler' => 'cpp_fn', 'fn' => 'php::fn::parse_str', 'return_type' => self::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0],
'parseUrl' => ['handler' => 'php_fn', 'fn' => 'parse_url', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 1],
'contains' => ['handler' => 'php_fn', 'fn' => 'str_contains', 'return_type' => self::TYPE_BOOL, 'min_args' => 1, 'max_args' => 1],
'incr' => ['handler' => 'php_fn', 'fn' => 'str_increment', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
@ -90,16 +90,16 @@ trait UniversalMethodCall
'md5' => ['handler' => 'php_fn', 'fn' => 'md5', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1],
'sha1' => ['handler' => 'php_fn', 'fn' => 'sha1', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1],
'crc32' => ['handler' => 'php_fn', 'fn' => 'crc32', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'hash' => ['handler' => 'php_fn', 'fn' => 'swoole_hash', 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 2],
'hashCode' => ['handler' => 'php_fn', 'fn' => 'crc32', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'hash' => ['handler' => 'php_fn', 'fn' => 'hash', 'receiver_pos' => 2, 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 2],
'hashCode' => ['handler' => 'direct_method', 'method' => 'hashCode', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'base64Decode' => ['handler' => 'php_fn', 'fn' => 'base64_decode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'base64Encode' => ['handler' => 'php_fn', 'fn' => 'base64_encode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'urlDecode' => ['handler' => 'php_fn', 'fn' => 'urldecode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'urlEncode' => ['handler' => 'php_fn', 'fn' => 'urlencode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'rawUrlEncode' => ['handler' => 'php_fn', 'fn' => 'rawurlencode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'rawUrlDecode' => ['handler' => 'php_fn', 'fn' => 'rawurldecode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'match' => ['handler' => 'php_fn', 'fn' => 'swoole_str_match', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 3],
'matchAll' => ['handler' => 'php_fn', 'fn' => 'swoole_str_match_all', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 3],
'match' => ['handler' => 'direct_method', 'method' => 'match', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 3, 'int_cast_args' => [1, 2]],
'matchAll' => ['handler' => 'direct_method', 'method' => 'matchAll', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 3, 'int_cast_args' => [1, 2]],
'isNumeric' => ['handler' => 'php_fn', 'fn' => 'is_numeric', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0],
// mbstring
'mbUpperFirst' => ['handler' => 'php_fn', 'fn' => 'mb_ucfirst', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1],
@ -127,8 +127,8 @@ trait UniversalMethodCall
// serialize
'unserialize' => ['handler' => 'php_fn', 'fn' => 'unserialize', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 0],
'unmarshal' => ['handler' => 'php_fn', 'fn' => 'unserialize', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 0],
'jsonDecode' => ['handler' => 'php_fn', 'fn' => 'swoole_str_json_decode', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 2],
'jsonDecodeToObject' => ['handler' => 'php_fn', 'fn' => 'swoole_str_json_decode_to_object', 'return_type' => self::TYPE_OBJECT, 'min_args' => 0, 'max_args' => 2],
'jsonDecode' => ['handler' => 'php_fn', 'fn' => 'json_decode', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 2, 'const_args' => [1 => 'true']],
'jsonDecodeToObject' => ['handler' => 'php_fn', 'fn' => 'json_decode', 'return_type' => self::TYPE_OBJECT, 'min_args' => 0, 'max_args' => 2, 'const_args' => [1 => 'false']],
// phpx C++ methods (no PHP function equivalent)
'equals' => ['handler' => 'direct_method', 'method' => 'equals', 'return_type' => self::TYPE_BOOL, 'min_args' => 1, 'max_args' => 2],
'append' => ['handler' => 'direct_method_mutate', 'method' => 'append', 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 1],
@ -136,7 +136,6 @@ trait UniversalMethodCall
'toInt' => ['handler' => 'convert_fn', 'fn' => 'toInt', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'toFloat' => ['handler' => 'convert_fn', 'fn' => 'toFloat', 'return_type' => self::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
'toBool' => ['handler' => 'convert_fn', 'fn' => 'toBool', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0],
'toString' => ['handler' => 'identity', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
],
self::TYPE_ARRAY => [
// --- stdext array_methods (all use PHP standard functions) ---
@ -175,7 +174,6 @@ trait UniversalMethodCall
'merge' => ['handler' => 'php_fn', 'fn' => 'array_merge', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1],
'contains' => ['handler' => 'php_fn', 'fn' => 'in_array', 'receiver_pos' => 2, 'return_type' => self::TYPE_BOOL, 'min_args' => 1, 'max_args' => 2],
'join' => ['handler' => 'php_fn', 'fn' => 'implode', 'receiver_pos' => 2, 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 1],
'isTyped' => ['handler' => 'php_fn', 'fn' => 'swoole_array_is_typed', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1],
'isEmpty' => ['handler' => 'direct_method', 'method' => 'empty', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0],
// mutating via PHP reference functions
'sort' => ['handler' => 'php_fn_ref', 'fn' => 'sort', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1],
@ -185,8 +183,8 @@ trait UniversalMethodCall
'unshift' => ['handler' => 'php_fn_ref', 'fn' => 'array_unshift', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => -1],
'splice' => ['handler' => 'php_fn_ref', 'fn' => 'array_splice', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 3],
'walk' => ['handler' => 'php_fn_ref', 'fn' => 'array_walk', 'return_type' => self::TYPE_BOOL, 'min_args' => 1, 'max_args' => 2],
'replaceStr' => ['handler' => 'php_fn', 'fn' => 'swoole_array_replace_str', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2],
'iReplaceStr' => ['handler' => 'php_fn', 'fn' => 'swoole_array_ireplace_str', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2],
'replaceStr' => ['handler' => 'php_fn', 'fn' => 'str_replace', 'receiver_pos' => 3, 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2],
'iReplaceStr' => ['handler' => 'php_fn', 'fn' => 'str_ireplace', 'receiver_pos' => 3, 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2],
// serialize
'serialize' => ['handler' => 'php_fn', 'fn' => 'serialize', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
'marshal' => ['handler' => 'php_fn', 'fn' => 'serialize', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0],
@ -267,13 +265,12 @@ trait UniversalMethodCall
'calc_op' => $this->genUniversalCalcOp($receiver, $def['op'], $expr->args),
'calc_inc' => '(' . $receiver . ' + 1)',
'calc_dec' => '(' . $receiver . ' - 1)',
'direct_method' => $this->genUniversalDirectMethod($receiver, $def['method'], $expr->args),
'direct_method' => $this->genUniversalDirectMethod($receiver, $def['method'], $expr->args, $def['int_cast_args'] ?? []),
'direct_method_mutate' => $this->genUniversalMutatingMethod($receiver, $def['method'], $expr->args),
'convert_fn' => $this->genUniversalConvertFn($receiver, $def['fn'], $def['return_type']),
'str_is_empty' => '(' . $receiver . '.length() == 0)',
'identity' => $receiver,
'php_fn' => $this->genUniversalPhpFn($receiver, $def['fn'], $expr->args, $def['receiver_pos'] ?? 0),
'php_fn' => $this->genUniversalPhpFn($receiver, $def['fn'], $expr->args, $def['receiver_pos'] ?? 0, $def['const_args'] ?? []),
'php_fn_ref' => $this->genUniversalPhpFnRef($receiver, $def['fn'], $expr->args, $def['return_type']),
'cpp_fn' => $this->genUniversalCppFn($receiver, $def['fn'], $expr->args, $def['receiver_pos'] ?? 0),
default => null,
};
}
@ -291,18 +288,21 @@ trait UniversalMethodCall
$this->context->beforeStmtLines[] = $streamVar . ' = ' . $receiver . ';';
$methodCall = match ($def['handler']) {
'php_fn' => $this->genUniversalPhpFn($streamVar, $def['fn'], $expr->args, $def['receiver_pos'] ?? 0),
'direct_method' => $this->genUniversalDirectMethod($streamVar, $def['method'], $expr->args),
'php_fn' => $this->genUniversalPhpFn($streamVar, $def['fn'], $expr->args, $def['receiver_pos'] ?? 0, $def['const_args'] ?? []),
'direct_method' => $this->genUniversalDirectMethod($streamVar, $def['method'], $expr->args, $def['int_cast_args'] ?? []),
'cpp_fn' => $this->genUniversalCppFn($streamVar, $def['fn'], $expr->args, $def['receiver_pos'] ?? 0),
default => null,
};
$errorMsg = "Cannot call method '{$method}' on a closed or invalid stream resource";
$lambda = "[&]() -> php::Variant {\n";
$lambda .= "if (!{$streamVar}.isResource()) {\n";
$lambda .= "throwError(\"{$errorMsg}\");\n";
$lambda .= "return php::null;\n";
$lambda .= "}\n";
$lambda .= "return {$methodCall};\n";
$this->indentLevel++;
$lambda .= $this->getIndent() . "if (!{$streamVar}.isResource()) {\n";
$lambda .= $this->getIndent() . "throwError(\"{$errorMsg}\");\n";
$lambda .= $this->getIndent() . "return php::null;\n";
$lambda .= $this->getIndent() . "}\n";
$this->indentLevel--;
$lambda .= $this->getIndent() . "return {$methodCall};\n";
$lambda .= "}()";
$tmpVar = $this->addTmpVar(self::TYPE_VAR);
@ -352,14 +352,18 @@ trait UniversalMethodCall
return '(' . $object . ' ' . $op . ' ' . $argExpr . ')';
}
protected function genUniversalDirectMethod(string $object, string $cppMethod, array $args): string
protected function genUniversalDirectMethod(string $object, string $cppMethod, array $args, array $intCastArgs = []): string
{
if (empty($args)) {
return $object . '.' . $cppMethod . '()';
}
$argExprs = [];
foreach ($args as $arg) {
$argExprs[] = $this->parseExpr($arg->value);
foreach ($args as $i => $arg) {
$expr = $this->parseExpr($arg->value);
if (in_array($i, $intCastArgs, true)) {
$expr = 'php::toInt(' . $expr . ')';
}
$argExprs[] = $expr;
}
return $object . '.' . $cppMethod . '(' . implode(', ', $argExprs) . ')';
}
@ -375,7 +379,12 @@ trait UniversalMethodCall
return 'php::' . $fn . '(' . $receiver . ')';
}
protected function genUniversalPhpFn(string $receiver, string $phpFunc, array $args, int $receiverPos = 0): string
/**
* @param int $receiverPos Position of the receiver in the final argument list.
* 0 (default) = receiver first. Non-zero values are 1-indexed (1 = before 1st user arg,
* 2 = before 2nd, etc.). e.g. hash(algo, data, binary) needs receiver as data → receiverPos=2.
*/
protected function genUniversalPhpFn(string $receiver, string $phpFunc, array $args, int $receiverPos = 0, array $constArgs = []): string
{
$argExprs = [];
$userArgs = [];
@ -400,9 +409,53 @@ trait UniversalMethodCall
}
}
if ($constArgs) {
$maxPos = max(array_keys($constArgs));
$totalSlots = max(count($argExprs) + count($constArgs), $maxPos + 1);
$final = [];
$regIdx = 0;
for ($i = 0; $i < $totalSlots; $i++) {
if (isset($constArgs[$i])) {
$final[] = $constArgs[$i];
} else {
$final[] = $argExprs[$regIdx++];
}
}
$argExprs = $final;
}
return 'php::call(' . $this->getFuncPtr($phpFunc) . ', php::ArgList{' . implode(', ', $argExprs) . '})';
}
// Same receiverPos semantics as genUniversalPhpFn.
protected function genUniversalCppFn(string $receiver, string $cppFunc, array $args, int $receiverPos = 0): string
{
$argExprs = [];
$userArgs = [];
foreach ($args as $arg) {
$userArgs[] = $this->parseExpr($arg->value);
}
if ($receiverPos === 0) {
$argExprs = [$receiver];
foreach ($userArgs as $ua) {
$argExprs[] = $ua;
}
} else {
foreach ($userArgs as $i => $ua) {
if ($i === $receiverPos - 1) {
$argExprs[] = $receiver;
}
$argExprs[] = $ua;
}
if ($receiverPos > count($userArgs)) {
$argExprs[] = $receiver;
}
}
return $cppFunc . '(' . implode(', ', $argExprs) . ')';
}
protected function genUniversalPhpFnRef(string $receiver, string $phpFunc, array $args, string $returnType): string
{
$tmpRef = $this->addTmpVar(self::TYPE_REF);

@ -112,10 +112,6 @@ function main()
$s = "O'Reilly";
var_dump($s->addSlashes());
// toString (identity)
$s = "hello";
var_dump($s->toString());
// toInt / toFloat / toBool
$s = "123";
var_dump($s->toInt());
@ -182,7 +178,6 @@ string(1) "a"
string(1) "c"
string(11) "Hello World"
string(9) "O\'Reilly"
string(5) "hello"
int(123)
float(3.14)
bool(true)

Loading…
Cancel
Save