From 0f5b60ac701cebd26adf180f878516a652f4ba06 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 12 Aug 2026 10:07:28 -0400 Subject: [PATCH 1/3] ext/gettext/tests: updates for musl-1.2.6 As of v1.2.6, musl's bind_textdomain_codeset() no longer returns NULL to indicate that the codeset is UTF-8 "by default." Instead you get "UTF-8". Two tests require minor updates for this. musl commit: https://git.musl-libc.org/cgit/musl/commit/src/locale/bind_textdomain_codeset.c?id=cabbd8697d39c09ca37fb3d63b86b42526b81fda Close GH-23235 --- ext/gettext/tests/bug53251.phpt | 12 +++++++++--- .../gettext_bind_textdomain_codeset-retval.phpt | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ext/gettext/tests/bug53251.phpt b/ext/gettext/tests/bug53251.phpt index d568be6bc079..b04abe9f1fa0 100644 --- a/ext/gettext/tests/bug53251.phpt +++ b/ext/gettext/tests/bug53251.phpt @@ -21,15 +21,21 @@ $expected = [true, true, false, "UTF-8", "UTF-8"]; // "foo" is unbound, so in the first call, you will get false instead // of a string. // -// bind_textdomain_codeset() always returns false on musl -// because musl only supports UTF-8. For more information: +// Prior to v1.2.6, bind_textdomain_codeset() always returns false on +// musl because musl only supports UTF-8. For more information: // // * https://github.com/php/doc-en/issues/4311, // * https://github.com/php/php-src/issues/17163 // $expected_musl = [false, true, false, false, false]; -var_dump($results === $expected || $results === $expected_musl); +// As of v1.2.6, bind_textdomain_codeset() returns "UTF-8" in all +// three cases on musl because UTF-8 is the only supported codeset. +$expected_musl126 = [false, true, "UTF-8", "UTF-8", "UTF-8"]; + +var_dump($results === $expected + || $results === $expected_musl + || $results === $expected_musl126); ?> --EXPECT-- bool(true) diff --git a/ext/gettext/tests/gettext_bind_textdomain_codeset-retval.phpt b/ext/gettext/tests/gettext_bind_textdomain_codeset-retval.phpt index 44957ad8d700..bba70c9848e9 100644 --- a/ext/gettext/tests/gettext_bind_textdomain_codeset-retval.phpt +++ b/ext/gettext/tests/gettext_bind_textdomain_codeset-retval.phpt @@ -17,11 +17,12 @@ gettext } // bind_textdomain_codeset() always returns false on musl - // because musl only supports UTF-8. For more information: + // prior to v1.2.6. For more information: // // * https://github.com/php/doc-en/issues/4311, // * https://github.com/php/php-src/issues/17163 // + // As of v1.2.6, it returns "UTF-8" as well. $result = bind_textdomain_codeset('messages', "UTF-8"); var_dump($result === false || $result === "UTF-8"); From 3f26310b166584041aa1b878371103a3bfc63e64 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 12 Aug 2026 23:14:16 +0100 Subject: [PATCH 2/3] Zend: remove Z_PARAM_FUNC_OR_NULL_WITH_ZVAL() (#23234) And related Z_PARAM_GET_PREV_ZVAL() These were introduced in https://github.com/php/php-src/commit/11796229f2f047768ee4870328f8dfd7e1a315a6 to simplify memory management. But this is no longer relevant (well never was as one could have grabbed the zval from the FCI) since the refactoring to only use FCCs in https://github.com/php/php-src/commit/0e5d654409295e19570fb6a7d2e970d736e0f8a5 The following SourceGraph searches confirm there is no usage: - https://sourcegraph.com/search?q=context:global+Z_PARAM_FUNC_OR_NULL_WITH_ZVAL&patternType=keyword&sm=0 - https://sourcegraph.com/search?q=context:global+Z_PARAM_GET_PREV_ZVAL&patternType=keyword&sm=0 --- Zend/zend_API.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Zend/zend_API.h b/Zend/zend_API.h index b91d586eedc3..f52f7aecb2b1 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -1696,10 +1696,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string SEPARATE_ZVAL_NOREF(_arg); \ } -/* get the zval* for a previously parsed argument */ -#define Z_PARAM_GET_PREV_ZVAL(dest) \ - zend_parse_arg_zval_deref(_arg, &dest, 0); - /* old "|" */ #define Z_PARAM_OPTIONAL \ _optional = 1; @@ -1870,10 +1866,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string #define Z_PARAM_FUNC_NO_TRAMPOLINE_FREE_OR_NULL(dest_fci, dest_fcc) \ Z_PARAM_FUNC_EX2(dest_fci, dest_fcc, 1, 0, false) -#define Z_PARAM_FUNC_OR_NULL_WITH_ZVAL(dest_fci, dest_fcc, dest_zp) \ - Z_PARAM_FUNC_EX2(dest_fci, dest_fcc, 1, 0, true) \ - Z_PARAM_GET_PREV_ZVAL(dest_zp) - /* old "h" */ #define Z_PARAM_ARRAY_HT_EX2(dest, check_null, deref, separate) \ Z_PARAM_PROLOGUE(deref, separate); \ From c127a96fd77c6429c269009c39c7e4c34b15dfe4 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Wed, 12 Aug 2026 23:17:44 +0100 Subject: [PATCH 3/3] gen_stub.php clean up and small refactoring (#23230) --- build/gen_stub.php | 119 +++++++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index 2f81a7642e73..80115dc97e20 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -174,7 +174,7 @@ function processStubFile(string $stubFile, Context $context, bool $includeOnly = reportFilePutContents($arginfoFile, $arginfoCode); if ($declCode !== '') { reportFilePutContents($declFile, $declCode); - } else if (file_exists($declFile)) { + } elseif (file_exists($declFile)) { unlink($declFile); } } @@ -259,7 +259,7 @@ public function equals(SimpleType $other): bool { return false; } - assert(get_class($other) === self::class); + assert($other instanceof self); return Type::equals($this->keyType, $other->keyType) && Type::equals($this->valueType, $other->valueType); @@ -864,7 +864,7 @@ public function __toString(): string public function getDeclarationName(): string { - return $this->name->toString(); + throw new Exception("ConstName does not have a declaration name"); } } @@ -1157,9 +1157,11 @@ class VersionFlags { /** * Keys are the PHP versions, values are arrays of flags + * @var array $flagsByVersion */ private array $flagsByVersion; + /** @param string[] $baseFlags */ public function __construct(array $baseFlags) { $this->flagsByVersion = []; foreach (ALL_PHP_VERSION_IDS as $version) { @@ -1408,9 +1410,7 @@ public function getDeclaration(): ?string return null; } - $name = $this->alias ?? $this->name; - - return $name->getDeclaration(); + return ($this->alias ?? $this->name)->getDeclaration(); } public function getFramelessDeclaration(): ?string { @@ -1418,6 +1418,8 @@ public function getFramelessDeclaration(): ?string { return null; } + assert($this->name instanceof FunctionName); + $code = ''; $infos = ''; foreach ($this->framelessFunctionInfos as $framelessFunctionInfo) { @@ -1439,6 +1441,7 @@ public function getFramelessDeclaration(): ?string { } private function getFramelessFunctionInfosName(): string { + assert($this->name instanceof FunctionName); return $this->name->getFramelessFunctionInfosName(); } @@ -1447,6 +1450,7 @@ public function getFunctionEntry(): string { if ($this->isMethod()) { throw new Exception('Frameless methods are not supported yet'); } + assert($this->name instanceof FunctionName); if ($this->name->getNamespace()) { throw new Exception('Namespaced direct calls to frameless functions are not supported yet'); } @@ -1460,11 +1464,12 @@ public function getFunctionEntry(): string { $flagsByPhpVersions = $this->getArginfoFlagsByPhpVersions(); if ($this->isMethod()) { + assert($this->name instanceof MethodName); $zendName = '"' . $this->name->methodName . '"'; if ($this->alias) { if ($this->alias instanceof MethodName) { $name = "zim_" . $this->alias->getDeclarationClassName() . "_" . $this->alias->methodName; - } else if ($this->alias instanceof FunctionName) { + } elseif ($this->alias instanceof FunctionName) { $name = "zif_" . $this->alias->getNonNamespacedName(); } else { throw new Error("Cannot happen"); @@ -1483,7 +1488,7 @@ public function getFunctionEntry(): string { return rtrim($flagsCode) . "\n"; } } - } else if ($this->name instanceof FunctionName) { + } elseif ($this->name instanceof FunctionName) { $functionName = $this->name->getFunctionName(); $declarationName = $this->alias ? $this->alias->getNonNamespacedName() : $this->name->getDeclarationName(); $name = "zif_$declarationName"; @@ -1602,7 +1607,7 @@ private function getArginfoFlagsByPhpVersions(): VersionFlags $flags = new VersionFlags($flags); - if ($this->isMethod() === false && $this->supportsCompileTimeEval) { + if (!$this->isMethod() && $this->supportsCompileTimeEval) { $flags->addForVersionsAbove("ZEND_ACC_COMPILE_TIME_EVAL", PHP_82_VERSION_ID); } @@ -1876,7 +1881,7 @@ private function getReturnValueSection(DOMDocument $doc): DOMElement { $returnType = $this->return->getMethodSynopsisType(); if ($returnType === null) { $returnDescriptionPara->appendChild(new DOMText("Description.")); - } else if (count($returnType->types) === 1) { + } elseif (count($returnType->types) === 1) { $type = $returnType->types[0]; $descriptionNode = match ($type->name) { @@ -2257,21 +2262,18 @@ static function (Expr $expr) use ($allConstInfos, &$isUnknownConstValue) { } $constType = ($const->phpDocType ?? $const->type)->tryToSimpleType(); - if ($constType) { - if ($constType->isBool()) { - return true; - } elseif ($constType->isInt()) { - return 1; - } elseif ($constType->isFloat()) { - return M_PI; - } elseif ($constType->isString()) { - return $const->name; - } elseif ($constType->isArray()) { - return []; - } + if ($constType === null) { + return null; } - return null; + return match (true) { + $constType->isBool() => true, + $constType->isInt() => 1, + $constType->isFloat() => M_PI, + $constType->isString() => $const->name, + $constType->isArray() => [], + default => null, + }; } throw new Exception("Constant " . $constName . " cannot be found"); @@ -2306,9 +2308,9 @@ public function initializeZval(string $zvalName, bool $alreadyExists = false, st if ($this->type->isNull()) { $code .= "\tZVAL_NULL(&$zvalName);\n"; } elseif ($this->type->isBool()) { - if ($cExpr == 'true') { + if ($cExpr === 'true') { $code .= "\tZVAL_TRUE(&$zvalName);\n"; - } elseif ($cExpr == 'false') { + } elseif ($cExpr === 'false') { $code .= "\tZVAL_FALSE(&$zvalName);\n"; } else { $code .= "\tZVAL_BOOL(&$zvalName, $cExpr);\n"; @@ -2328,7 +2330,7 @@ public function initializeZval(string $zvalName, bool $alreadyExists = false, st $code .= "\tZVAL_STR(&$zvalName, $forStringDef);\n"; } } elseif ($this->type->isArray()) { - if ($cExpr == '[]') { + if ($cExpr === '[]') { $code .= "\tZVAL_EMPTY_ARRAY(&$zvalName);\n"; } else { throw new Exception("Unimplemented default value"); @@ -2579,6 +2581,7 @@ protected function getVariableTypeName(): string protected function getFieldSynopsisDefaultLinkend(): string { + assert($this->name instanceof ClassConstName); $className = str_replace(["\\", "_"], ["-", "-"], $this->name->class->toLowerString()); return "$className.constants." . strtolower(str_replace("_", "-", trim($this->name->getDeclarationName(), "_"))); @@ -2637,19 +2640,19 @@ public function getPredefinedConstantEntry(DOMDocument $doc, int $indentationLev return $this->getPredefinedConstantElement($doc, $indentationLevel, "entry"); } - public function discardInfoForOldPhpVersions(?int $phpVersionIdMinimumCompatibility): void { + public function discardInfoForOldPhpVersions(?int $minimumPhpVersionIdCompatibility): void { $this->type = null; $this->flags &= ~Modifiers::FINAL; $this->isDeprecated = false; $this->attributes = []; - $this->phpVersionIdMinimumCompatibility = $phpVersionIdMinimumCompatibility; + $this->phpVersionIdMinimumCompatibility = $minimumPhpVersionIdCompatibility; } /** @param array $allConstInfos */ public function getDeclaration(array $allConstInfos): string { $type = $this->phpDocType ?? $this->type; - $simpleType = $type ? $type->tryToSimpleType() : null; + $simpleType = $type?->tryToSimpleType(); if ($simpleType && $simpleType->name === "mixed") { $simpleType = null; } @@ -2988,10 +2991,10 @@ class StringBuilder { * - freeing the zend_string, if needed * * @param string $varName - * @param string $strContent + * @param string $content * @param ?int $minPHPCompatibility * @param bool $interned - * @return string[] + * @return array{0: string, 1: string, 2: string} */ public static function getString( string $varName, @@ -3099,11 +3102,11 @@ protected function getFieldSynopsisValueString(array $allConstInfos): ?string return $this->defaultValueString; } - public function discardInfoForOldPhpVersions(?int $phpVersionIdMinimumCompatibility): void { + public function discardInfoForOldPhpVersions(?int $minimumPhpVersionIdCompatibility): void { $this->type = null; $this->flags &= ~Modifiers::READONLY; $this->attributes = []; - $this->phpVersionIdMinimumCompatibility = $phpVersionIdMinimumCompatibility; + $this->phpVersionIdMinimumCompatibility = $minimumPhpVersionIdCompatibility; } /** @param array $allConstInfos */ @@ -3319,7 +3322,7 @@ public function generateCode(string $invocation, string $nameSuffix, array $allC $initValue = ''; if ($arg->value instanceof Node\Scalar\String_) { $strVal = $arg->value->value; - [$strInit, $strUse, $strRelease] = StringBuilder::getString( + [$strInit, $strUse] = StringBuilder::getString( 'unused', $strVal, $phpVersionIdMinimumCompatibility @@ -3345,7 +3348,7 @@ public function generateCode(string $invocation, string $nameSuffix, array $allC $code .= $initValue; } if ($arg->name) { - [$stringInit, $nameCode, $stringRelease] = StringBuilder::getString( + [$stringInit, $nameCode] = StringBuilder::getString( "", $arg->name->name, $phpVersionIdMinimumCompatibility, @@ -3969,7 +3972,7 @@ private static function createOoElement( return null; } - $type = $typeOverride !== null ? $typeOverride : $classInfo->type; + $type = $typeOverride ?? $classInfo->type; $ooElement = $doc->createElement("oo$type"); $ooElement->appendChild(new DOMText("\n$indentation ")); @@ -4202,7 +4205,7 @@ private function appendInheritedMemberSectionToClassSynopsis(DOMDocument $doc, D $indentation = str_repeat(" ", $indentationLevel); $classSynopsis->appendChild(new DOMText("\n\n$indentation")); - $classSynopsisInfo = $doc->createElement("classsynopsisinfo", "$inheritedLabel"); + $classSynopsisInfo = $doc->createElement("classsynopsisinfo", $inheritedLabel); $classSynopsisInfo->setAttribute("role", "comment"); $classSynopsis->appendChild($classSynopsisInfo); @@ -4243,7 +4246,7 @@ public function __construct(array $fileTags) { if ($tag->name === 'generate-function-entries') { $this->generateFunctionEntries = true; $this->declarationPrefix = $tag->value ? $tag->value . " " : ""; - } else if ($tag->name === 'generate-legacy-arginfo') { + } elseif ($tag->name === 'generate-legacy-arginfo') { if ($tag->value && !in_array((int) $tag->value, ALL_PHP_VERSION_IDS, true)) { throw new Exception( "Legacy PHP version must be one of: \"" . PHP_70_VERSION_ID . "\" (PHP 7.0), \"" . PHP_80_VERSION_ID . "\" (PHP 8.0), " . @@ -4254,12 +4257,12 @@ public function __construct(array $fileTags) { } $this->minimumPhpVersionIdCompatibility = ($tag->value ? (int) $tag->value : PHP_70_VERSION_ID); - } else if ($tag->name === 'generate-class-entries') { + } elseif ($tag->name === 'generate-class-entries') { $this->generateClassEntries = true; $this->declarationPrefix = $tag->value ? $tag->value . " " : ""; - } else if ($tag->name === 'undocumentable') { + } elseif ($tag->name === 'undocumentable') { $this->isUndocumentable = true; - } else if ($tag->name === 'generate-c-enums') { + } elseif ($tag->name === 'generate-c-enums') { $this->generateCEnums = true; } } @@ -4451,7 +4454,7 @@ private function handleStatements(array $stmts, PrettyPrinterAbstract $prettyPri AttributeInfo::createFromGroups($classStmt->attrGroups) ); } - } else if ($classStmt instanceof Stmt\Property) { + } elseif ($classStmt instanceof Stmt\Property) { if (!($classStmt->flags & Class_::VISIBILITY_MODIFIER_MASK)) { throw new Exception("Visibility modifier is required"); } @@ -4468,7 +4471,7 @@ private function handleStatements(array $stmts, PrettyPrinterAbstract $prettyPri AttributeInfo::createFromGroups($classStmt->attrGroups) ); } - } else if ($classStmt instanceof Stmt\ClassMethod) { + } elseif ($classStmt instanceof Stmt\ClassMethod) { if (!($classStmt->flags & Class_::VISIBILITY_MODIFIER_MASK)) { throw new Exception("Visibility modifier is required"); } @@ -4482,13 +4485,13 @@ private function handleStatements(array $stmts, PrettyPrinterAbstract $prettyPri $this->isUndocumentable, $this->getMinimumPhpVersionIdCompatibility() ); - } else if ($classStmt instanceof Stmt\EnumCase) { + } elseif ($classStmt instanceof Stmt\EnumCase) { $enumCaseInfos[] = new EnumCaseInfo( new EnumCaseName($className, $classStmt->name->toString()), $classStmt->expr, $classStmt->expr ? $prettyPrinter->prettyPrintExpr($classStmt->expr) : null, ); - } else if ($classStmt instanceof TraitUse) { + } elseif ($classStmt instanceof TraitUse) { if ($classStmt->adaptations) { throw new Exception("Trait adaptations are not supported"); } @@ -4535,22 +4538,22 @@ private static function handlePreprocessorConditions(array &$conds, Stmt $stmt): $text = trim($comment->getText()); if (preg_match('/^#\s*if\s+(.+)$/', $text, $matches)) { $conds[] = $matches[1]; - } else if (preg_match('/^#\s*ifdef\s+(.+)$/', $text, $matches)) { + } elseif (preg_match('/^#\s*ifdef\s+(.+)$/', $text, $matches)) { $conds[] = "defined($matches[1])"; - } else if (preg_match('/^#\s*ifndef\s+(.+)$/', $text, $matches)) { + } elseif (preg_match('/^#\s*ifndef\s+(.+)$/', $text, $matches)) { $conds[] = "!defined($matches[1])"; - } else if (preg_match('/^#\s*else$/', $text)) { + } elseif (preg_match('/^#\s*else$/', $text)) { if (empty($conds)) { throw new Exception("Encountered else without corresponding #if"); } $cond = array_pop($conds); $conds[] = "!($cond)"; - } else if (preg_match('/^#\s*endif$/', $text)) { + } elseif (preg_match('/^#\s*endif$/', $text)) { if (empty($conds)) { throw new Exception("Encountered #endif without corresponding #if"); } array_pop($conds); - } else if ($text[0] === '#') { + } elseif ($text[0] === '#') { throw new Exception("Unrecognized preprocessor directive \"$text\""); } } @@ -4746,7 +4749,7 @@ public function getType(): string { public function getVariableName(): string { $value = $this->value; - if ($value === null || strlen($value) === 0) { + if ($value === null || $value === '') { throw new Exception("@$this->name doesn't have any value"); } @@ -4948,7 +4951,7 @@ function parseFunctionLike( if ($preferRef) { $sendBy = ArgInfo::SEND_PREFER_REF; - } else if ($param->byRef) { + } elseif ($param->byRef) { $sendBy = ArgInfo::SEND_BY_REF; } else { $sendBy = ArgInfo::SEND_BY_VAL; @@ -5036,7 +5039,7 @@ function parseFunctionLike( } /** - * @param array $attributes + * @param array> $attributes */ function parseConstLike( PrettyPrinterAbstract $prettyPrinter, @@ -5129,7 +5132,7 @@ function parseConstLike( } /** - * @param array $attributes + * @param array> $attributes */ function parseProperty( Name $class, @@ -5247,9 +5250,9 @@ function parseClass( } elseif ($class instanceof Interface_) { $classKind = "interface"; $extends = $class->extends; - } else if ($class instanceof Trait_) { + } elseif ($class instanceof Trait_) { $classKind = "trait"; - } else if ($class instanceof Enum_) { + } elseif ($class instanceof Enum_) { $classKind = "enum"; $implements = $class->implements; } else { @@ -5925,7 +5928,7 @@ function collectEnumSynopsisItemDescriptions(string $className, DOMElement $syno continue; } - $enumCaseName = $enumCaseNames[0]->textContent; + $enumCaseName = $enumCaseNames->item(0)->textContent; $enumCaseDescriptionElements["$className::$enumCaseName"] = $enumItemDescription; } @@ -6310,7 +6313,7 @@ function initPhpParser() { if ($fileInfo) { $fileInfos[] = $fileInfo; } - } else if (is_dir($location)) { + } elseif (is_dir($location)) { array_push($fileInfos, ...processDirectory($location, $context)); } else { echo "$location is neither a file nor a directory.\n";