Skip to content

Commit b5361d7

Browse files
gen_stub: add ReturnInfo::beginArgInfo()
The vast majority of the decisions about the use of `ZEND_BEGIN_ARG_INFO_EX` or one of its variations are based on the return information of the function - is the type builtin, is the return information tentative, does it include an object mask, etc. Accordingly, move the logic into the `ReturnInfo` class. The logic is actually moved into two methods, `ReturnInfo::beginArgInfo()`, which needs to handle the case of tentative returns being used when PHP < 8.1 is supported, and `::beginArgInfoCompatible()`, which can assume that PHP 8.1+ is supported and thus make use of early returns and guard clauses. Further improvements to the logic will be made in a subsequent commit. In the process, make `ReturnInfo::$byRef` private.
1 parent 24b7c7a commit b5361d7

File tree

1 file changed

+69
-60
lines changed

1 file changed

+69
-60
lines changed

build/gen_stub.php

Lines changed: 69 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,7 +1146,7 @@ class ReturnInfo {
11461146
self::REFCOUNT_N,
11471147
];
11481148

1149-
public /* readonly */ bool $byRef;
1149+
private /* readonly */ bool $byRef;
11501150
// NOT readonly - gets removed when discarding info for older PHP versions
11511151
public ?Type $type;
11521152
public /* readonly */ ?Type $phpDocType;
@@ -1193,6 +1193,69 @@ private function setRefcount(?string $refcount): void
11931193

11941194
$this->refcount = $refcount;
11951195
}
1196+
1197+
public function beginArgInfo(string $funcInfoName, int $minArgs, bool $php81MinimumCompatibility): string {
1198+
$code = $this->beginArgInfoCompatible($funcInfoName, $minArgs);
1199+
if ($this->type !== null && $this->tentativeReturnType && !$php81MinimumCompatibility) {
1200+
$realCode = "#if (PHP_VERSION_ID >= " . PHP_81_VERSION_ID . ")\n";
1201+
$realCode .= $code;
1202+
$realCode .= sprintf(
1203+
"#else\nZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n#endif\n",
1204+
$funcInfoName, $this->byRef, $minArgs
1205+
);
1206+
return $realCode;
1207+
}
1208+
return $code;
1209+
}
1210+
1211+
/**
1212+
* Assumes PHP 8.1 compatibility, if that is not the case the caller is
1213+
* responsible for making the use of a tentative return type conditional
1214+
* based on the PHP version. Separate to allow using early returns
1215+
*/
1216+
private function beginArgInfoCompatible(string $funcInfoName, int $minArgs): string {
1217+
if ($this->type !== null) {
1218+
if (null !== $simpleReturnType = $this->type->tryToSimpleType()) {
1219+
if ($simpleReturnType->isBuiltin) {
1220+
return sprintf(
1221+
"%s(%s, %d, %d, %s, %d)\n",
1222+
$this->tentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX",
1223+
$funcInfoName, $this->byRef,
1224+
$minArgs,
1225+
$simpleReturnType->toTypeCode(), $this->type->isNullable()
1226+
);
1227+
}
1228+
return sprintf(
1229+
"%s(%s, %d, %d, %s, %d)\n",
1230+
$this->tentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX",
1231+
$funcInfoName, $this->byRef,
1232+
$minArgs,
1233+
$simpleReturnType->toEscapedName(), $this->type->isNullable()
1234+
);
1235+
}
1236+
$arginfoType = $this->type->toArginfoType();
1237+
if ($arginfoType->hasClassType()) {
1238+
return sprintf(
1239+
"%s(%s, %d, %d, %s, %s)\n",
1240+
$this->tentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX",
1241+
$funcInfoName, $this->byRef,
1242+
$minArgs,
1243+
$arginfoType->toClassTypeString(), $arginfoType->toTypeMask()
1244+
);
1245+
}
1246+
return sprintf(
1247+
"%s(%s, %d, %d, %s)\n",
1248+
$this->tentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX",
1249+
$funcInfoName, $this->byRef,
1250+
$minArgs,
1251+
$arginfoType->toTypeMask()
1252+
);
1253+
}
1254+
return sprintf(
1255+
"ZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n",
1256+
$funcInfoName, $this->byRef, $minArgs
1257+
);
1258+
}
11961259
}
11971260

11981261
class FuncInfo {
@@ -5012,65 +5075,11 @@ protected function pName_FullyQualified(Name\FullyQualified $node): string {
50125075
}
50135076

50145077
function funcInfoToCode(FileInfo $fileInfo, FuncInfo $funcInfo): string {
5015-
$code = '';
5016-
$returnType = $funcInfo->return->type;
5017-
$isTentativeReturnType = $funcInfo->return->tentativeReturnType;
5018-
$php81MinimumCompatibility = $fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_81_VERSION_ID;
5019-
5020-
if ($returnType !== null) {
5021-
if ($isTentativeReturnType && !$php81MinimumCompatibility) {
5022-
$code .= "#if (PHP_VERSION_ID >= " . PHP_81_VERSION_ID . ")\n";
5023-
}
5024-
if (null !== $simpleReturnType = $returnType->tryToSimpleType()) {
5025-
if ($simpleReturnType->isBuiltin) {
5026-
$code .= sprintf(
5027-
"%s(%s, %d, %d, %s, %d)\n",
5028-
$isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX",
5029-
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
5030-
$funcInfo->numRequiredArgs,
5031-
$simpleReturnType->toTypeCode(), $returnType->isNullable()
5032-
);
5033-
} else {
5034-
$code .= sprintf(
5035-
"%s(%s, %d, %d, %s, %d)\n",
5036-
$isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX",
5037-
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
5038-
$funcInfo->numRequiredArgs,
5039-
$simpleReturnType->toEscapedName(), $returnType->isNullable()
5040-
);
5041-
}
5042-
} else {
5043-
$arginfoType = $returnType->toArginfoType();
5044-
if ($arginfoType->hasClassType()) {
5045-
$code .= sprintf(
5046-
"%s(%s, %d, %d, %s, %s)\n",
5047-
$isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX",
5048-
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
5049-
$funcInfo->numRequiredArgs,
5050-
$arginfoType->toClassTypeString(), $arginfoType->toTypeMask()
5051-
);
5052-
} else {
5053-
$code .= sprintf(
5054-
"%s(%s, %d, %d, %s)\n",
5055-
$isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX",
5056-
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
5057-
$funcInfo->numRequiredArgs,
5058-
$arginfoType->toTypeMask()
5059-
);
5060-
}
5061-
}
5062-
if ($isTentativeReturnType && !$php81MinimumCompatibility) {
5063-
$code .= sprintf(
5064-
"#else\nZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n#endif\n",
5065-
$funcInfo->getArgInfoName(), $funcInfo->return->byRef, $funcInfo->numRequiredArgs
5066-
);
5067-
}
5068-
} else {
5069-
$code .= sprintf(
5070-
"ZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n",
5071-
$funcInfo->getArgInfoName(), $funcInfo->return->byRef, $funcInfo->numRequiredArgs
5072-
);
5073-
}
5078+
$code = $funcInfo->return->beginArgInfo(
5079+
$funcInfo->getArgInfoName(),
5080+
$funcInfo->numRequiredArgs,
5081+
$fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_81_VERSION_ID
5082+
);
50745083

50755084
foreach ($funcInfo->args as $argInfo) {
50765085
$code .= $argInfo->toZendInfo();

0 commit comments

Comments
 (0)