Skip to content

Commit 046ed23

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 8de7c06 commit 046ed23

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 {
@@ -4985,65 +5048,11 @@ protected function pName_FullyQualified(Name\FullyQualified $node): string {
49855048
}
49865049

49875050
function funcInfoToCode(FileInfo $fileInfo, FuncInfo $funcInfo): string {
4988-
$code = '';
4989-
$returnType = $funcInfo->return->type;
4990-
$isTentativeReturnType = $funcInfo->return->tentativeReturnType;
4991-
$php81MinimumCompatibility = $fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_81_VERSION_ID;
4992-
4993-
if ($returnType !== null) {
4994-
if ($isTentativeReturnType && !$php81MinimumCompatibility) {
4995-
$code .= "#if (PHP_VERSION_ID >= " . PHP_81_VERSION_ID . ")\n";
4996-
}
4997-
if (null !== $simpleReturnType = $returnType->tryToSimpleType()) {
4998-
if ($simpleReturnType->isBuiltin) {
4999-
$code .= sprintf(
5000-
"%s(%s, %d, %d, %s, %d)\n",
5001-
$isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX",
5002-
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
5003-
$funcInfo->numRequiredArgs,
5004-
$simpleReturnType->toTypeCode(), $returnType->isNullable()
5005-
);
5006-
} else {
5007-
$code .= sprintf(
5008-
"%s(%s, %d, %d, %s, %d)\n",
5009-
$isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX",
5010-
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
5011-
$funcInfo->numRequiredArgs,
5012-
$simpleReturnType->toEscapedName(), $returnType->isNullable()
5013-
);
5014-
}
5015-
} else {
5016-
$arginfoType = $returnType->toArginfoType();
5017-
if ($arginfoType->hasClassType()) {
5018-
$code .= sprintf(
5019-
"%s(%s, %d, %d, %s, %s)\n",
5020-
$isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX",
5021-
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
5022-
$funcInfo->numRequiredArgs,
5023-
$arginfoType->toClassTypeString(), $arginfoType->toTypeMask()
5024-
);
5025-
} else {
5026-
$code .= sprintf(
5027-
"%s(%s, %d, %d, %s)\n",
5028-
$isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX",
5029-
$funcInfo->getArgInfoName(), $funcInfo->return->byRef,
5030-
$funcInfo->numRequiredArgs,
5031-
$arginfoType->toTypeMask()
5032-
);
5033-
}
5034-
}
5035-
if ($isTentativeReturnType && !$php81MinimumCompatibility) {
5036-
$code .= sprintf(
5037-
"#else\nZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n#endif\n",
5038-
$funcInfo->getArgInfoName(), $funcInfo->return->byRef, $funcInfo->numRequiredArgs
5039-
);
5040-
}
5041-
} else {
5042-
$code .= sprintf(
5043-
"ZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n",
5044-
$funcInfo->getArgInfoName(), $funcInfo->return->byRef, $funcInfo->numRequiredArgs
5045-
);
5046-
}
5051+
$code = $funcInfo->return->beginArgInfo(
5052+
$funcInfo->getArgInfoName(),
5053+
$funcInfo->numRequiredArgs,
5054+
$fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_81_VERSION_ID
5055+
);
50475056

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

0 commit comments

Comments
 (0)