@ -2642,19 +2642,23 @@ class EvaluatedValue
$evaluationState = new \stdClass();
$evaluationState = new \stdClass();
$evaluationState->isUnknownConstValue = false;
$evaluationState->isUnknownConstValue = false;
$evaluator = null;
// The holder permits recursive evaluator access without taking a
$evaluator = new ConstExprEvaluator(
// reference to an object-typed local. TypePHP intentionally rejects
// object references because objects already have identity.
$evaluator = new \stdClass();
$evaluator->instance = null;
$evaluator->instance = new ConstExprEvaluator(
static function (Expr $expr) use (
static function (Expr $expr) use (
$allConstInfos,
$allConstInfos,
$evaluationState,
$evaluationState,
& $evaluator,
$evaluator,
) {
) {
// php-parser's ConstExprEvaluator predates PHP 8.5 constant
// php-parser's ConstExprEvaluator predates PHP 8.5 constant
// expression casts. Keep the compatibility logic in TypePHP:
// expression casts. Keep the compatibility logic in TypePHP:
// validation has already rejected void and disallowed object
// validation has already rejected void and disallowed object
// casts before declaration values reach gen_stub.php.
// casts before declaration values reach gen_stub.php.
if ($expr instanceof Expr\Cast) {
if ($expr instanceof Expr\Cast) {
$value = $evaluator->evaluateDirectly($expr->expr);
$value = $evaluator->instance-> evaluateDirectly($expr->expr);
return match (true) {
return match (true) {
$expr instanceof Expr\Cast\Int_ => (int) $value,
$expr instanceof Expr\Cast\Int_ => (int) $value,
$expr instanceof Expr\Cast\Double => (float) $value,
$expr instanceof Expr\Cast\Double => (float) $value,
@ -2737,7 +2741,7 @@ class EvaluatedValue
}
}
);
);
$result = $evaluator->evaluateDirectly($expr);
$result = $evaluator->instance-> evaluateDirectly($expr);
$enumCaseRef = null;
$enumCaseRef = null;
if ($result instanceof \TypePhp\Entity\EnumCaseRef) {
if ($result instanceof \TypePhp\Entity\EnumCaseRef) {
@ -6049,13 +6053,14 @@ function generateArgInfoCode(
$code .= "\n";
$code .= "\n";
}
}
$generatedFuncInfos = [];
$generatedFuncInfos = new \stdClass();
$generatedFuncInfos->values = [];
$argInfoCode = generateCodeWithConditions(
$argInfoCode = generateCodeWithConditions(
$fileInfo->getAllFuncInfos(), "\n",
$fileInfo->getAllFuncInfos(), "\n",
static function (FuncInfo $funcInfo) use (& $generatedFuncInfos, $fileInfo) {
static function (FuncInfo $funcInfo) use ($generatedFuncInfos, $fileInfo) {
/* If there already is an equivalent arginfo structure, only emit a #define */
/* If there already is an equivalent arginfo structure, only emit a #define */
if ($generatedFuncInfo = $funcInfo->findEquivalent($generatedFuncInfos)) {
if ($generatedFuncInfo = $funcInfo->findEquivalent($generatedFuncInfos->values )) {
$code = sprintf(
$code = sprintf(
"#define %s %s\n",
"#define %s %s\n",
$funcInfo->getArgInfoName(), $generatedFuncInfo->getArgInfoName()
$funcInfo->getArgInfoName(), $generatedFuncInfo->getArgInfoName()
@ -6064,7 +6069,7 @@ function generateArgInfoCode(
$code = $funcInfo->toArgInfoCode($fileInfo->getMinimumPhpVersionIdCompatibility());
$code = $funcInfo->toArgInfoCode($fileInfo->getMinimumPhpVersionIdCompatibility());
}
}
$generatedFuncInfos[] = $funcInfo;
$generatedFuncInfos->values [] = $funcInfo;
return $code;
return $code;
}
}
);
);
@ -6085,16 +6090,17 @@ function generateArgInfoCode(
$code .= "$framelessFunctionCode\n";
$code .= "$framelessFunctionCode\n";
}
}
$generatedFunctionDeclarations = [];
$generatedFunctionDeclarations = new \stdClass();
$generatedFunctionDeclarations->keys = [];
$code .= generateCodeWithConditions(
$code .= generateCodeWithConditions(
$fileInfo->getAllFuncInfos(), "",
$fileInfo->getAllFuncInfos(), "",
static function (FuncInfo $funcInfo) use ($fileInfo, & $generatedFunctionDeclarations) {
static function (FuncInfo $funcInfo) use ($fileInfo, $generatedFunctionDeclarations) {
$key = $funcInfo->getDeclarationKey();
$key = $funcInfo->getDeclarationKey();
if (isset($generatedFunctionDeclarations[$key])) {
if (isset($generatedFunctionDeclarations->keys [$key])) {
return null;
return null;
}
}
$generatedFunctionDeclarations[$key] = true;
$generatedFunctionDeclarations->keys [$key] = true;
return $fileInfo->declarationPrefix . $funcInfo->getDeclaration();
return $fileInfo->declarationPrefix . $funcInfo->getDeclaration();
}
}
);
);
@ -6184,10 +6190,12 @@ function generateFunctionEntries(?Name $className, array $funcInfos, ?string $co
* the name of a zend_string already created with that content
* the name of a zend_string already created with that content
*/
*/
function generateFunctionAttributeInitialization(iterable $funcInfos, array $allConstInfos, ?int $phpVersionIdMinimumCompatibility, ?string $parentCond = null, array & $declaredStrings = []): string {
function generateFunctionAttributeInitialization(iterable $funcInfos, array $allConstInfos, ?int $phpVersionIdMinimumCompatibility, ?string $parentCond = null, array & $declaredStrings = []): string {
return generateCodeWithConditions(
$declaredStringState = new \stdClass();
$declaredStringState->values = $declaredStrings;
$code = generateCodeWithConditions(
$funcInfos,
$funcInfos,
"",
"",
static function (FuncInfo $funcInfo) use ($allConstInfos, $phpVersionIdMinimumCompatibility, & $declaredStrings ) {
static function (FuncInfo $funcInfo) use ($allConstInfos, $phpVersionIdMinimumCompatibility, $declaredStringState ) {
$code = null;
$code = null;
if ($funcInfo->name instanceof MethodName) {
if ($funcInfo->name instanceof MethodName) {
@ -6200,10 +6208,10 @@ function generateFunctionAttributeInitialization(iterable $funcInfos, array $all
// conditionally available; string reuse is only among declarations
// conditionally available; string reuse is only among declarations
// that are always there
// that are always there
if ($funcInfo->cond) {
if ($funcInfo->cond) {
$empty = [] ;
$useDeclared = new \stdClass() ;
$useDeclared = &$empty;
$useDeclared->values = [];
} else {
} else {
$useDeclared = &$declaredStrings;
$useDeclared = $declaredStringState;
}
}
foreach ($funcInfo->attributes as $key => $attribute) {
foreach ($funcInfo->attributes as $key => $attribute) {
@ -6213,7 +6221,7 @@ function generateFunctionAttributeInitialization(iterable $funcInfos, array $all
"func_" . $funcInfo->name->getNameForAttributes() . "_$key",
"func_" . $funcInfo->name->getNameForAttributes() . "_$key",
$allConstInfos,
$allConstInfos,
$phpVersionIdMinimumCompatibility,
$phpVersionIdMinimumCompatibility,
\std::ref($useDeclared)
\std::ref($useDeclared->values )
);
);
}
}
@ -6225,7 +6233,7 @@ function generateFunctionAttributeInitialization(iterable $funcInfos, array $all
"func_{$funcInfo->name->getNameForAttributes()}_arg{$index}_$key",
"func_{$funcInfo->name->getNameForAttributes()}_arg{$index}_$key",
$allConstInfos,
$allConstInfos,
$phpVersionIdMinimumCompatibility,
$phpVersionIdMinimumCompatibility,
\std::ref($useDeclared)
\std::ref($useDeclared->values )
);
);
}
}
}
}
@ -6234,6 +6242,13 @@ function generateFunctionAttributeInitialization(iterable $funcInfos, array $all
},
},
$parentCond
$parentCond
);
);
// Keep the property read as a separate PHP value assignment. Calls above
// turn `values` into a zend_reference; assigning that reference expression
// directly to the by-reference parameter would rebind the local Reference
// wrapper instead of writing the array back to its caller.
$updatedDeclaredStrings = $declaredStringState->values;
$declaredStrings = $updatedDeclaredStrings;
return $code;
}
}
/**
/**
@ -6253,10 +6268,12 @@ function generateGlobalConstantAttributeInitialization(
if ($phpVersionIdMinimumCompatibility !== null & & $phpVersionIdMinimumCompatibility < PHP_85_VERSION_ID ) {
if ($phpVersionIdMinimumCompatibility !== null & & $phpVersionIdMinimumCompatibility < PHP_85_VERSION_ID ) {
$isConditional = true;
$isConditional = true;
}
}
$declaredStringState = new \stdClass();
$declaredStringState->values = $declaredStrings;
$code = generateCodeWithConditions(
$code = generateCodeWithConditions(
$constInfos,
$constInfos,
"",
"",
static function (ConstInfo $constInfo) use ($allConstInfos, $isConditional, & $declaredStrings ) {
static function (ConstInfo $constInfo) use ($allConstInfos, $isConditional, $declaredStringState ) {
$code = "";
$code = "";
if ($constInfo->attributes === []) {
if ($constInfo->attributes === []) {
@ -6266,10 +6283,10 @@ function generateGlobalConstantAttributeInitialization(
// conditionally available; string reuse is only among declarations
// conditionally available; string reuse is only among declarations
// that are always there
// that are always there
if ($constInfo->cond) {
if ($constInfo->cond) {
$empty = [] ;
$useDeclared = new \stdClass() ;
$useDeclared = &$empty;
$useDeclared->values = [];
} else {
} else {
$useDeclared = &$declaredStrings;
$useDeclared = $declaredStringState;
}
}
$constName = str_replace('\\', '\\\\', $constInfo->name->__toString());
$constName = str_replace('\\', '\\\\', $constInfo->name->__toString());
$constVarName = 'const_' . $constName;
$constVarName = 'const_' . $constName;
@ -6281,7 +6298,7 @@ function generateGlobalConstantAttributeInitialization(
$constVarName . "_$key",
$constVarName . "_$key",
$allConstInfos,
$allConstInfos,
PHP_85_VERSION_ID,
PHP_85_VERSION_ID,
\std::ref($useDeclared)
\std::ref($useDeclared->values )
);
);
}
}
@ -6289,6 +6306,8 @@ function generateGlobalConstantAttributeInitialization(
},
},
$parentCond
$parentCond
);
);
$updatedDeclaredStrings = $declaredStringState->values;
$declaredStrings = $updatedDeclaredStrings;
if ($code & & $isConditional) {
if ($code & & $isConditional) {
return "\n#if (PHP_VERSION_ID >= " . PHP_85_VERSION_ID . ")\n" . $code . "#endif\n";
return "\n#if (PHP_VERSION_ID >= " . PHP_85_VERSION_ID . ")\n" . $code . "#endif\n";
}
}
@ -6308,20 +6327,22 @@ function generateConstantAttributeInitialization(
?string $parentCond = null,
?string $parentCond = null,
array & $declaredStrings = []
array & $declaredStrings = []
): string {
): string {
return generateCodeWithConditions(
$declaredStringState = new \stdClass();
$declaredStringState->values = $declaredStrings;
$code = generateCodeWithConditions(
$constInfos,
$constInfos,
"",
"",
static function (ConstInfo $constInfo) use ($allConstInfos, $phpVersionIdMinimumCompatibility, & $declaredStrings ) {
static function (ConstInfo $constInfo) use ($allConstInfos, $phpVersionIdMinimumCompatibility, $declaredStringState ) {
$code = null;
$code = null;
// Make sure we don't try and use strings that might only be
// Make sure we don't try and use strings that might only be
// conditionally available; string reuse is only among declarations
// conditionally available; string reuse is only among declarations
// that are always there
// that are always there
if ($constInfo->cond) {
if ($constInfo->cond) {
$empty = [] ;
$useDeclared = new \stdClass() ;
$useDeclared = &$empty;
$useDeclared->values = [];
} else {
} else {
$useDeclared = &$declaredStrings;
$useDeclared = $declaredStringState;
}
}
foreach ($constInfo->attributes as $key => $attribute) {
foreach ($constInfo->attributes as $key => $attribute) {
$code .= $attribute->generateCode(
$code .= $attribute->generateCode(
@ -6329,7 +6350,7 @@ function generateConstantAttributeInitialization(
"const_" . $constInfo->name->getDeclarationName() . "_$key",
"const_" . $constInfo->name->getDeclarationName() . "_$key",
$allConstInfos,
$allConstInfos,
$phpVersionIdMinimumCompatibility,
$phpVersionIdMinimumCompatibility,
\std::ref($useDeclared)
\std::ref($useDeclared->values )
);
);
}
}
@ -6337,6 +6358,9 @@ function generateConstantAttributeInitialization(
},
},
$parentCond
$parentCond
);
);
$updatedDeclaredStrings = $declaredStringState->values;
$declaredStrings = $updatedDeclaredStrings;
return $code;
}
}
/**
/**
@ -7161,22 +7185,24 @@ function generateStubFile(string $stubFile, string $objectFile, bool $forceRegen
}
}
}
}
array_map(
$argCount = max(count($aliasArgs), count($aliasedArgs));
function(?ArgInfo $aliasArg, ?ArgInfo $aliasedArg) use ($aliasFunc, $aliasedFunc, & $errors) {
for ($argIndex = 0; $argIndex < $argCount; $argIndex++) {
$aliasArg = $aliasArgs[$argIndex] ?? null;
$aliasedArg = $aliasedArgs[$argIndex] ?? null;
if ($aliasArg === null) {
if ($aliasArg === null) {
assert($aliasedArg !== null);
assert($aliasedArg !== null);
$errors[] = "{$aliasFunc->name}(): Argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() is missing";
$errors[] = "{$aliasFunc->name}(): Argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() is missing";
return null ;
continue ;
}
}
if ($aliasedArg === null) {
if ($aliasedArg === null) {
$errors[] = "{$aliasedFunc->name}(): Argument \$$aliasArg->name of alias function {$aliasFunc->name}() is missing";
$errors[] = "{$aliasedFunc->name}(): Argument \$$aliasArg->name of alias function {$aliasFunc->name}() is missing";
return null ;
continue ;
}
}
if ($aliasArg->name !== $aliasedArg->name) {
if ($aliasArg->name !== $aliasedArg->name) {
$errors[] = "{$aliasFunc->name}(): Argument \$$aliasArg->name and argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() must have the same name";
$errors[] = "{$aliasFunc->name}(): Argument \$$aliasArg->name and argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() must have the same name";
return null ;
continue ;
}
}
if ($aliasArg->type != $aliasedArg->type) {
if ($aliasArg->type != $aliasedArg->type) {
@ -7186,9 +7212,7 @@ function generateStubFile(string $stubFile, string $objectFile, bool $forceRegen
if ($aliasArg->defaultValue !== $aliasedArg->defaultValue) {
if ($aliasArg->defaultValue !== $aliasedArg->defaultValue) {
$errors[] = "{$aliasFunc->name}(): Argument \$$aliasArg->name and argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() must have the same default value";
$errors[] = "{$aliasFunc->name}(): Argument \$$aliasArg->name and argument \$$aliasedArg->name of aliased function {$aliasedFunc->name}() must have the same default value";
}
}
},
}
$aliasArgs, $aliasedArgs
);
$aliasedReturn = $aliasedFunc->return;
$aliasedReturn = $aliasedFunc->return;
$aliasReturn = $aliasFunc->return;
$aliasReturn = $aliasFunc->return;