移除 8.2、8.3 版本的支持

master
韩天峰 2 weeks ago
parent 2f229ee0ac
commit af0b6f9582
  1. 2
      README.md
  2. 2
      completions/tpc.bash
  3. 4
      docs/COMPILER_CLI.md
  4. 2
      docs/INCOMPATIBLE_PHP_FEATURES.md
  5. 23
      docs/INTERFACE_PROPERTY_HOOKS.md
  6. 18
      docs/PROPERTY_HOOKS.md
  7. 4
      phpunit/BashCompletionTest.php
  8. 2
      phpunit/README.md
  9. 6
      phpunit/TEST_REPORT.md
  10. 26
      phpunit/src/CompilerBaseApiTest.php
  11. 9
      phpunit/src/ConstantExpressionValidatorTest.php
  12. 39
      src/Build/SourcePipelineTrait.php
  13. 2
      src/Cli/CompletionMetadata.php
  14. 8
      src/CompilerBase.php
  15. 2
      src/Metadata/Constants.php
  16. 4
      src/Parser/NullsafeAccessTrait.php
  17. 6
      src/Preprocessor.php
  18. 15
      src/Transform/ConstantExpressionValidationVisitor.php
  19. 11
      src/Translator.php
  20. 124
      src/gen_stub.php

@ -1,5 +1,5 @@
# 依赖
- 编译器需要 PHP 8.4 以上版本;生成的扩展仍可面向 PHP 8.2~8.5
- 编译器和生成程序均需要 PHP 8.4 以上版本,支持 PHP 8.4~8.5
- 需要 GCC-9 以上版本,支持 C++17 标准
- 需要 CMake-3.24 以上版本
- 需要高精度数学库:`GMP`、`MPFR`、`libmpdec`

@ -68,7 +68,7 @@ _typephp_tpc()
return
;;
--php-version)
COMPREPLY=( $(compgen -W '8.2 8.3 8.4 8.5' -- "$current") )
COMPREPLY=( $(compgen -W '8.4 8.5' -- "$current") )
return
;;
--cxx-std)

@ -82,7 +82,7 @@ bin/tpc.php app.php --dry --build-dir /tmp/typephp-build
| 选项 | 说明 |
|---|---|
| `--php-version <8.2|8.3|8.4|8.5>` | 限制接受的 PHP 语法版本,默认 `8.5`。 |
| `--php-version <8.4|8.5>` | 限制接受的 PHP 语法版本,默认 `8.5`。 |
| `--cxx-std <ver>` | C++ 标准,例如 `c++17`、`c++20`。 |
| `--march <arch>` | 目标指令集,例如 `native`、`x86-64-v3`。 |
| `--target-platform <triple>` | 交叉编译目标 triple。 |
@ -93,6 +93,8 @@ bin/tpc.php app.php --dry --build-dir /tmp/typephp-build
`--php-version` 控制解析器接受的源码语法,也用于 `project.yml` 中依据 `PHP_VERSION` / `PHP_VERSION_ID` 选择源文件。它不负责选择链接的 PHP 安装目录。
TypePHP 和 PHPX 的最低运行时版本均为 PHP 8.4。`--php-version` 与实际链接的 `libphp.so` 不要求小版本完全相同,但两者都必须为 PHP 8.4 或更高版本。
## C++ 编译和链接参数
这些参数均可重复:

@ -16,7 +16,7 @@
- 不支持可变变量 `$$var`
- PHP 8.4 property hooks 会编译为 AOT getter/setter,并注册对应的 Zend hook 元数据;直接属性读写、Reflection 和对象遍历均受支持。当前不支持对 hook 属性取引用。
- PHP 8.4 Reflection Lazy Object 不能用于 TypePHP AOT 类。AOT 类以 persistent internal class 注册,而 Zend 的 `zend_object_make_lazy()` 明确拒绝 internal class;运行时动态加载的 ZendPHP user class 不受此限制。
- 支持 `private(set)``protected(set)` 非对称属性可见性;在 PHP 8.2~8.4 后端通过自定义属性写 handler 执行同等作用域检查。
- 支持 `private(set)``protected(set)` 非对称属性可见性,并通过 PHP 8.4+ 的类级对象 handler 执行同等作用域检查。
- 不支持闭包或箭头函数按引用返回。
- `__construct()` 不允许返回值。
- 参数默认值不允许出现在必填参数之前(`PHP`允许,但会直接丢弃此默认参数)。

@ -20,8 +20,7 @@ interface Named
- Interface 不持有属性槽,不生成 getter/setter 实现,也不产生访问时的契约检查。
- TypePHP 在编译期验证已知 AOT 类是否满足属性的可见性、类型和 `get`/`set` 能力。
- PHP 8.4 目标在 MINIT 注册原生 Zend Hook 元数据,使 Reflection 和动态 PHP 类获得相同契约。
- 编译器前端解析和验证该语法不依赖 PHP 8.4;但使用 Property Hooks 的最终目标运行时必须链接 PHP 8.4 或更高版本。
- PHP 8.3 不提供静默降级。缺少 Zend Hook 元数据会使动态属性访问、Reflection、JSON、序列化和 `eval()` 类链接的行为取决于执行路径,不能视为可靠支持。
- TypePHP、PHPX 和最终目标运行时的最低版本均为 PHP 8.4,不提供旧版本降级路径。
## 2. 语法与诊断
@ -72,7 +71,7 @@ php::registerAbstractPropertyHooks(
);
```
它只在 `PHP_VERSION_ID >= 80400`访问 PHP 8.4 ABI,并负责:
TypePHP/PHPX 已统一要求 PHP 8.4+,因此该 helper 直接访问 PHP 8.4 ABI,并负责:
- 持久化分配 `zend_property_info::hooks`
- 创建没有 handler 的 abstract `get`/`set` `zend_internal_function` 元数据;
@ -84,17 +83,12 @@ php::registerAbstractPropertyHooks(
## 5. PHP 版本边界
TypePHP 应区分编译器宿主与目标 PHP
TypePHP 区分源码语言版本与链接运行时
- PHP Parser 和 TypePHP 前端可以在 PHP 8.3 环境解析该语法;
- 构建后端以项目选择的 PHP language/target version 以及最终链接的 PHP headers/`libphp` 作为能力依据;编译器进程自身可以运行在更旧的 PHP 上;
- 发现 Property Hooks 且目标低于 PHP 8.4 时,在 C++ 编译前报告:
```text
Property Hooks require PHP 8.4 or later as the target runtime
```
PHPX 仍使用条件编译作为 ABI 防线,但不应把清晰的功能诊断推迟为 C++ 编译错误。
- `--php-version` 只允许 `8.4``8.5`,用于解析语法和处理项目条件;
- PHPX headers、`libphp` 与最终运行时必须为 PHP 8.4 或更高版本;
- `--php-version``libphp.so` 的小版本不要求完全一致,例如使用 8.5 语法模式并链接 PHP 8.4 时,最终能否构建仍由实际使用的 Zend API 决定;
- PHP 8.4 以下环境在 TypePHP/PHPX 构建入口直接拒绝。
## 6. TDD 覆盖
@ -107,8 +101,7 @@ PHPX 仍使用条件编译作为 ABI 防线,但不应把清晰的功能诊断
5. Reflection 的 abstract、virtual、hasHook/getHook 元数据;
6. PHP 8.4 动态 PHP 类的成功与失败链接;
7. O0/O3 结果一致,Interface 不生成属性槽或 Native Hook 实现;
8. PHP 8.3 目标得到明确的构建期错误;
9. PHPX helper 在 NTS/ZTS 和 PHP 8.4/8.5 下的生命周期与 ABI 回归。
8. PHPX helper 在 NTS/ZTS 和 PHP 8.4/8.5 下的生命周期与 ABI 回归。
## 7. 实施顺序

@ -166,17 +166,9 @@ zend_string_init(data, length, true);
- NTS 没有锁开销;
- ZTS 下在工作线程处理请求前已完成注册,不会并发修改 class entry。
## 7. PHP 版本隔离
## 7. PHP 版本边界
Property Hook 是 PHP 8.4 新特性。PHPX 对头文件、结构字段和实现均使用条件编译:
```cpp
#if PHP_VERSION_ID >= 80400
#include "zend_property_hooks.h"
#endif
```
PHP 8.2/8.3 构建不会引用以下 PHP 8.4 ABI:
TypePHP 与 PHPX 的最低版本均为 PHP 8.4,因此 Property Hook 实现直接使用以下 PHP 8.4 ABI:
- `zend_property_info::hooks`
- `zend_class_entry::num_hooked_props`
@ -185,7 +177,7 @@ PHP 8.2/8.3 构建不会引用以下 PHP 8.4 ABI:
- `ZEND_VIRTUAL_PROPERTY_OFFSET`
- `zend_hooked_object_get_iterator()`
旧版本 fallback 会在错误地尝试注册 Hook 时抛出明确错误。TypePHP/PHPX 需要针对目标 PHP 版本分别编译,不能把链接 PHP 8.4 的 PHPX 二进制直接用于 PHP 8.2/8.3
PHPX 头文件和 CMake 配置会拒绝 PHP 8.4 以下的 headers/`php-config`。PHP 8.4 与 8.5 仍分别构建对应 PHPX 二进制;`--php-version` 只控制源码语法,不要求与 `libphp.so` 的小版本完全相同,但两者都必须不低于 8.4
## 8. ABI 风险和升级检查
@ -196,7 +188,7 @@ PHP 8.2/8.3 构建不会引用以下 PHP 8.4 ABI:
1. TypePHP 与 PHPX 版本绑定,并针对具体 PHP 版本重新编译;
2. 注册流程与 Zend 编译器处理原生 Property Hook 的步骤一致;
3. 只复用 Zend 导出的 iterator,不复制其复杂实现;
4. PHP 8.4 以下版本已在编译期隔离
4. PHP 8.4 以下版本在构建入口统一拒绝
5. 所有注册均在 MINIT 完成,不增加请求热路径上的名称查找。
升级 PHP 版本时必须检查:
@ -221,7 +213,7 @@ Property Hook 改动至少需要覆盖:
- 序列化只包含真实存储状态;
- 动态 Zend 属性读写;
- 继承和属性可见性;
- PHP 8.4 构建以及 PHP 8.2/8.3 条件编译
- PHP 8.4 与 PHP 8.5 构建
当前核心回归测试位于:

@ -61,6 +61,10 @@ final class BashCompletionTest extends TestCase
"8.4\n",
$this->complete($script, ['tpc', '--php-version', '8.4']),
);
self::assertSame(
"8.4\n8.5\n",
$this->complete($script, ['tpc', '--php-version', '']),
);
self::assertSame(
"-O2\n",
$this->complete($script, ['tpc', '-O2']),

@ -219,7 +219,7 @@ jobs:
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
php-version: ['8.2', '8.3']
php-version: ['8.4', '8.5']
steps:
- uses: actions/checkout@v2

@ -223,9 +223,9 @@ OK (66 tests, 200+ assertions)
已准备好 CI/CD 集成,配置文件见 `phpunit/README.md`
### 测试矩阵
- ✅ Windows + PHP 8.2/8.3
- ✅ Linux + PHP 8.2/8.3
- ✅ macOS + PHP 8.2/8.3
- ✅ Windows + PHP 8.4/8.5
- ✅ Linux + PHP 8.4/8.5
- ✅ macOS + PHP 8.4/8.5
## 下一步计划

@ -142,14 +142,36 @@ class CompilerBaseApiTest extends TestCase
$parser->parse('<?php $value = "hello" |> trim(...);');
}
public function testPhpLanguageVersionAcceptsPipeAt85AndRejectsInvalidValue(): void
public function testPhpLanguageVersionAcceptsPipeAt85AndRejectsPre84Target(): void
{
$this->compiler->setPhpVersion('8.5');
$parser = $this->getPropertyValue('parser');
$this->assertNotEmpty($parser->parse('<?php $value = "hello" |> trim(...);'));
$this->expectException(TestError::class);
$this->compiler->setPhpVersion('8.1');
$this->expectExceptionMessage('Supported versions: 8.4, 8.5');
$this->compiler->setPhpVersion('8.3');
}
public function testLinkedPhpRuntimeMustBeAtLeast84WithoutMatchingLanguageMinor(): void
{
$php83 = $this->testDir . '/php83';
mkdir($php83 . '/include/php/main', 0777, true);
file_put_contents($php83 . '/include/php/main/php_version.h', "#define PHP_VERSION_ID 80399\n");
try {
$this->invokeMethod('validatePhpRuntimeMinimum', $php83);
self::fail('Expected PHP 8.3 target runtime to be rejected');
} catch (TestError $error) {
self::assertStringContainsString('requires libphp 8.4 or later', $error->getMessage());
}
$php84 = $this->testDir . '/php84';
mkdir($php84 . '/include/php/main', 0777, true);
file_put_contents($php84 . '/include/php/main/php_version.h', "#define PHP_VERSION_ID 80400\n");
$this->compiler->setPhpVersion('8.5');
$this->invokeMethod('validatePhpRuntimeMinimum', $php84);
$this->addToAssertionCount(1);
}
public function testMiscObjectCacheIsInvalidatedWhenCompileOptionsChange(): void

@ -173,9 +173,9 @@ final class ConstantExpressionValidatorTest extends PHPUnit\Framework\TestCase
yield 'parameter default allows new' => ['function f($value = new Value()) {}', '8.4'];
yield 'global const allows new' => ['const VALUE = new Value();', '8.4'];
yield 'static variable allows new' => ['function f() { static $value = new Value(); }', '8.4'];
yield 'PHP 8.3 static variable allows dynamic initializer' => [
yield 'PHP 8.4 static variable allows dynamic initializer' => [
'function f(int $seed) { static $value = loadValue($seed); }',
'8.3',
'8.4',
];
yield 'PHP 8.5 class constant allows static closure' => [
'class C { const VALUE = static function (): int { return 1; }; }',
@ -202,11 +202,6 @@ final class ConstantExpressionValidatorTest extends PHPUnit\Framework\TestCase
'8.4',
'New expressions are not supported in this context',
];
yield 'PHP 8.2 static variable rejects dynamic initializer' => [
'function f(int $seed) { static $value = loadValue($seed); }',
'8.2',
'Constant expression contains invalid operations',
];
yield 'property rejects new' => [
'class C { public mixed $value = new Value(); }',
'8.4',

@ -88,6 +88,10 @@ trait SourcePipelineTrait
$phpDir = $this->getPhpDir();
}
if (!($this->getPlatform() instanceof Wasi)) {
$this->validatePhpRuntimeMinimum($phpDir);
}
if ($this->getPlatform() instanceof Linux) {
try {
(new LibPhpxInstaller())->ensure($this->getPhpxDir(), $phpDir);
@ -173,6 +177,41 @@ trait SourcePipelineTrait
}
}
/** Validate the selected headers/libphp independently of --php-version. */
protected function validatePhpRuntimeMinimum(string $phpDir): void
{
$versionId = null;
$headers = [
$phpDir . '/include/php/main/php_version.h',
$phpDir . '/include/main/php_version.h',
];
foreach ($headers as $header) {
if (!is_file($header)) {
continue;
}
$contents = file_get_contents($header);
if (is_string($contents) && preg_match('/^#define\s+PHP_VERSION_ID\s+(\d+)/m', $contents, $matches)) {
$versionId = (int) $matches[1];
break;
}
}
if ($versionId === null) {
$phpConfig = $phpDir . '/bin/php-config';
if (is_executable($phpConfig)) {
$value = shell_exec(escapeshellarg($phpConfig) . ' --vernum 2>/dev/null');
if (is_string($value) && ctype_digit(trim($value))) {
$versionId = (int) trim($value);
}
}
}
if ($versionId !== null && $versionId < 80400) {
$version = intdiv($versionId, 10000) . '.' . intdiv($versionId % 10000, 100);
$this->error("TypePHP requires libphp 8.4 or later; selected PHP installation is {$version}: {$phpDir}");
}
}
protected function shouldIgnoreFile(string $file): bool
{
foreach ($this->ignorePaths as $ignorePath) {

@ -45,7 +45,7 @@ final class CompletionMetadata
'--optimize' => ['0', '1', '2', '3'],
'-m' => ['bin', 'lib', 'ext'],
'--mode' => ['bin', 'lib', 'ext'],
'--php-version' => ['8.2', '8.3', '8.4', '8.5'],
'--php-version' => ['8.4', '8.5'],
'--cxx-std' => ['c++17', 'c++20', 'c++23'],
'--sanitize' => ['address', 'undefined'],
'--wasm=' => ['component', 'browser'],

@ -485,8 +485,8 @@ class CompilerBase implements PropertyAccessContext
public function __construct(string $rootPath)
{
$this->osType = PHP_OS_FAMILY;
if (version_compare(PHP_VERSION, '8.2.0', '<')) {
$this->error('PHP 8.2.0 or later is required');
if (version_compare(PHP_VERSION, '8.4.0', '<')) {
$this->error('PHP 8.4.0 or later is required');
}
if (version_compare(PHP_VERSION, '8.6.0', '>=')) {
$this->error('PHP 8.6.0 or later is not supported');
@ -523,8 +523,8 @@ class CompilerBase implements PropertyAccessContext
/** Set the PHP language version accepted by the parser. */
public function setPhpVersion(string $version): void
{
if (!preg_match('/^8\.(2|3|4|5)(?:\.0)?$/', $version, $matches)) {
$this->error('Unsupported PHP language version: `' . $version . '`. Supported versions: 8.2, 8.3, 8.4, 8.5');
if (!preg_match('/^8\.(4|5)(?:\.0)?$/', $version, $matches)) {
$this->error('Unsupported PHP language version: `' . $version . '`. Supported versions: 8.4, 8.5');
}
$this->phpVersion = '8.' . $matches[1] . '.0';

@ -165,7 +165,7 @@ class Constants
],
'php-version' => [
'longPrefix' => 'php-version',
'description' => 'PHP language version to accept (8.2, 8.3, 8.4, or 8.5; default: 8.5)',
'description' => 'PHP language version to accept (8.4 or 8.5; default: 8.5)',
'required' => false,
],
'force' => [

@ -122,8 +122,8 @@ trait NullsafeAccessTrait
$this->context->beforeStmtLines[] = $code;
// C++ temporaries are function-scoped; release their zvals at the PHP statement boundary.
foreach (array_reverse($ownedTmpVars) as $tmpVar) {
$this->context->afterStmtLines[] = $tmpVar . '.unset();';
foreach (array_reverse($ownedTmpVars) as $ownedTmpVar) {
$this->context->afterStmtLines[] = $ownedTmpVar . '.unset();';
}
return "{$tmpFn}()";
}

@ -1440,9 +1440,6 @@ class Preprocessor extends CompilerBase
protected function parseClassPropertyDef(Node\Stmt\Property $v): void
{
if ($v->hooks !== [] && version_compare($this->phpVersion, '8.4', '<')) {
$this->fatalError($v, 'Property Hooks require PHP 8.4 or later as the target runtime');
}
$oriCtx = $this->context;
$this->context = $this->classDef->propertyContext;
$nullable = $v->type instanceof NullableType;
@ -1666,9 +1663,6 @@ class Preprocessor extends CompilerBase
if ($property->hooks === []) {
$this->fatalError($property, 'Interfaces may only include hooked properties');
}
if (version_compare($this->phpVersion, '8.4', '<')) {
$this->fatalError($property, 'Property Hooks require PHP 8.4 or later as the target runtime');
}
if ($property->flags & (Modifiers::PRIVATE | Modifiers::PROTECTED)) {
$this->fatalError($property, 'Property in interface cannot be protected or private');
}

@ -19,15 +19,13 @@ use TypePhp\Exception\SyntaxError;
* false: class constants, property defaults and enum cases.
* true: attributes, parameter defaults and global constants.
*
* Static variable initializers are constant expressions on PHP 8.2. PHP 8.3
* and later compile them as regular expressions and evaluate them only once.
* PHP 8.4+ compiles static variable initializers as regular expressions and
* evaluates them only once, so they do not use the constant-expression path.
*/
final class ConstantExpressionValidationVisitor extends NodeVisitorAbstract
{
private readonly ConstantExpressionValidator $validator;
private readonly bool $supportsDynamicStaticInitializers;
/** @param null|Closure(Node, string): never $fatalError */
public function __construct(
string $phpVersion,
@ -35,7 +33,6 @@ final class ConstantExpressionValidationVisitor extends NodeVisitorAbstract
)
{
$this->validator = new ConstantExpressionValidator($phpVersion);
$this->supportsDynamicStaticInitializers = version_compare($phpVersion, '8.3', '>=');
}
public function enterNode(Node $node): null
@ -94,14 +91,6 @@ final class ConstantExpressionValidationVisitor extends NodeVisitorAbstract
return null;
}
if ($node instanceof Node\Stmt\Static_ && !$this->supportsDynamicStaticInitializers) {
foreach ($node->vars as $variable) {
if ($variable->default !== null) {
$this->validator->validate($variable->default, allowDynamic: true);
}
}
}
return null;
}
}

@ -279,7 +279,7 @@ class Translator extends Preprocessor
$climate->tab()->out('--generate-completion=bash Generate Bash completion script');
$climate->tab()->out('--lto Enable Link Time Optimization (-flto)');
$climate->tab()->out('--no-literal-strings Disable literal strings optimization');
$climate->tab()->out('--php-version <ver> PHP language version to accept (8.2-8.5, default: 8.5)');
$climate->tab()->out('--php-version <ver> PHP language version to accept (8.4-8.5, default: 8.5)');
$climate->tab()->out('--no-progress Disable progress bar, output per-file compilation progress line by line');
$climate->tab()->out('--no-console Hide console window (Windows only, GUI application)');
$climate->tab()->out('--no-color Disable ANSI color output');
@ -1938,7 +1938,7 @@ CODE;
$buildCreateBody = function () use ($classDef, $className, $handlers, $initBlock, $delegateToParentAllocator): string {
$body = $classDef->ctorInit;
$body .= "auto obj = typephp_create_object_with_defaults(\n";
$body .= "class_type, create_object_{$className}, &{$handlers}, ";
$body .= "class_type, create_object_{$className}, ";
$body .= ($delegateToParentAllocator ? 'true' : 'false') . ",\n";
$body .= "[&](zend_object *obj) {\n";
$body .= $initBlock;
@ -1948,19 +1948,12 @@ CODE;
};
$code .= "typephp_install_property_handlers({$ce}, &{$handlers});\n";
$code .= "#if (PHP_VERSION_ID < 80400)\n";
$code .= "create_object_{$className} = php_get_create_object_fn({$ce});\n";
$code .= "{$ce}->create_object = [](zend_class_entry *class_type) -> zend_object* {\n";
$code .= $buildCreateBody();
$code .= "};\n";
if ($classDef->requireCtor || $this->classHasAsymmetricOrHookedProperty($classDef)) {
$code .= "#else\n";
$code .= "create_object_{$className} = php_get_create_object_fn({$ce});\n";
$code .= "{$ce}->create_object = [](zend_class_entry *class_type) -> zend_object* {\n";
$code .= $buildCreateBody();
$code .= "};\n";
}
$code .= "#endif\n";
}
}
return $code;

@ -27,11 +27,6 @@ const PHP_83_VERSION_ID = 80300;
const PHP_84_VERSION_ID = 80400;
const PHP_85_VERSION_ID = 80500;
const ALL_PHP_VERSION_IDS = [
PHP_70_VERSION_ID,
PHP_80_VERSION_ID,
PHP_81_VERSION_ID,
PHP_82_VERSION_ID,
PHP_83_VERSION_ID,
PHP_84_VERSION_ID,
PHP_85_VERSION_ID,
];
@ -1196,18 +1191,8 @@ class ReturnInfo {
$this->refcount = $refcount;
}
public function beginArgInfo(string $funcInfoName, int $minArgs, bool $php81MinimumCompatibility): string {
$code = $this->beginArgInfoCompatible($funcInfoName, $minArgs);
if ($this->type !== null && $this->tentativeReturnType && !$php81MinimumCompatibility) {
$realCode = "#if (PHP_VERSION_ID >= " . PHP_81_VERSION_ID . ")\n";
$realCode .= $code;
$realCode .= sprintf(
"#else\nZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n#endif\n",
$funcInfoName, $this->byRef, $minArgs
);
return $realCode;
}
return $code;
public function beginArgInfo(string $funcInfoName, int $minArgs): string {
return $this->beginArgInfoCompatible($funcInfoName, $minArgs);
}
/**
@ -1553,11 +1538,6 @@ class FuncInfo {
$code .= "\t{ 0 },\n";
$code .= "};\n";
$php84MinimumCompatibility = $this->minimumPhpVersionIdCompatibility === null || $this->minimumPhpVersionIdCompatibility >= PHP_84_VERSION_ID;
if (!$php84MinimumCompatibility) {
return "#if (PHP_VERSION_ID >= " . PHP_84_VERSION_ID . ")\n$code#endif\n";
}
return $code;
}
@ -1628,26 +1608,11 @@ class FuncInfo {
$docComment = $this->exposedDocComment ? '"' . $this->exposedDocComment->escape() . '"' : "NULL";
$framelessFuncInfosName = !empty($this->framelessFunctionInfos) ? $this->getFramelessFunctionInfosName() : "NULL";
// Assume 8.4+ here, if older versions are supported this is conditional
$code = $flagsByPhpVersions->generateVersionDependentFlagCode(
"\tZEND_RAW_FENTRY($zendName, $name, $argInfoName, %s, $framelessFuncInfosName, $docComment)\n",
PHP_84_VERSION_ID
);
$php84MinimumCompatibility = $this->minimumPhpVersionIdCompatibility === null || $this->minimumPhpVersionIdCompatibility >= PHP_84_VERSION_ID;
if (!$php84MinimumCompatibility) {
$code = "#if (PHP_VERSION_ID >= " . PHP_84_VERSION_ID . ")\n$code";
$code .= "#else\n";
$code .= $flagsByPhpVersions->generateVersionDependentFlagCode(
"\tZEND_RAW_FENTRY($zendName, $name, $argInfoName, %s)\n",
$this->minimumPhpVersionIdCompatibility,
PHP_83_VERSION_ID
);
$code .= "#endif\n";
}
return $code;
}
@ -2291,7 +2256,6 @@ OUPUT_EXAMPLE
$code = $this->return->beginArgInfo(
$this->getArgInfoName(),
$this->numRequiredArgs,
$minPHPCompatability === null || $minPHPCompatability >= PHP_81_VERSION_ID
);
foreach ($this->args as $argInfo) {
@ -2948,12 +2912,6 @@ class ConstInfo extends VariableLike
$commentCode = "NULL";
}
$php83MinimumCompatibility = $this->phpVersionIdMinimumCompatibility === null || $this->phpVersionIdMinimumCompatibility >= PHP_83_VERSION_ID;
if ($this->type && !$php83MinimumCompatibility) {
$code .= "#if (PHP_VERSION_ID >= " . PHP_83_VERSION_ID . ")\n";
}
if ($this->type) {
$typeCode = $this->getTypeCode($constName, $code);
@ -2970,11 +2928,7 @@ class ConstInfo extends VariableLike
);
}
if ($this->type && !$php83MinimumCompatibility) {
$code .= "#else\n";
}
if (!$this->type || !$php83MinimumCompatibility) {
if (!$this->type) {
if (!empty($this->attributes)) {
$template = "\tzend_class_constant *const_" . $this->name->getDeclarationName() . " = ";
} else {
@ -2987,10 +2941,6 @@ class ConstInfo extends VariableLike
);
}
if ($this->type && !$php83MinimumCompatibility) {
$code .= "#endif\n";
}
$code .= "\tzend_string_release_ex(const_{$constName}_name, true);\n";
return $code;
@ -3766,16 +3716,6 @@ class ClassInfo {
$code = '';
$php80MinimumCompatibility = $this->phpVersionIdMinimumCompatibility === null || $this->phpVersionIdMinimumCompatibility >= PHP_80_VERSION_ID;
$php81MinimumCompatibility = $this->phpVersionIdMinimumCompatibility === null || $this->phpVersionIdMinimumCompatibility >= PHP_81_VERSION_ID;
// TypePHP classes may target a pre-8.4 Zend runtime even when parsing
// newer PHP syntax, so class registration retains the old API branch.
$php84MinimumCompatibility = false;
if ($this->type === "enum" && !$php81MinimumCompatibility) {
$code .= "#if (PHP_VERSION_ID >= " . PHP_81_VERSION_ID . ")\n";
}
if ($this->cond) {
$code .= "#if {$this->cond}\n";
}
@ -3807,10 +3747,6 @@ class ClassInfo {
}
if ($this->type === "class" || $this->type === "trait") {
if (!$php84MinimumCompatibility) {
$code .= "#if (PHP_VERSION_ID >= " . PHP_84_VERSION_ID . ")\n";
}
$template = "\tclass_entry = zend_register_internal_class_with_flags(&ce, " . (isset($this->extends[0]) ? "class_entry_" . str_replace("\\", "_", $this->extends[0]->toString()) : "NULL") . ", %s);\n";
$entries = $flags->generateVersionDependentFlagCode($template, $this->phpVersionIdMinimumCompatibility ? max($this->phpVersionIdMinimumCompatibility, PHP_84_VERSION_ID) : null);
if ($entries !== '') {
@ -3819,15 +3755,6 @@ class ClassInfo {
$code .= sprintf($template, "0");
}
if (!$php84MinimumCompatibility) {
$code .= "#else\n";
$code .= "\tclass_entry = zend_register_internal_class_ex(&ce, " . (isset($this->extends[0]) ? "class_entry_" . str_replace("\\", "_", $this->extends[0]->toString()) : "NULL") . ");\n";
if (!$flags->isEmpty()) {
$code .= $flags->generateVersionDependentFlagCode("\tclass_entry->ce_flags |= %s;\n", $this->phpVersionIdMinimumCompatibility);
}
$code .= "#endif\n";
}
} else {
$code .= "\tclass_entry = zend_register_internal_interface(&ce);\n";
if (!$flags->isEmpty()) {
@ -3838,15 +3765,7 @@ class ClassInfo {
}
if ($this->exposedDocComment) {
if (!$php84MinimumCompatibility) {
$code .= "#if (PHP_VERSION_ID >= " . PHP_84_VERSION_ID . ")\n";
}
$code .= "\tclass_entry->doc_comment = " . $this->exposedDocComment->getInitCode() . "\n";
if (!$php84MinimumCompatibility) {
$code .= "#endif\n";
}
}
$code .= generateCodeWithConditions(
@ -3879,20 +3798,9 @@ class ClassInfo {
if ($this->alias) {
$code .= "\tzend_register_class_alias(\"" . str_replace("\\", "\\\\", $this->alias) . "\", class_entry);\n";
}
// Reusable strings for wrapping conditional PHP 8.0+ code
if ($php80MinimumCompatibility) {
$php80CondStart = '';
$php80CondEnd = '';
} else {
$php80CondStart = "\n#if (PHP_VERSION_ID >= " . PHP_80_VERSION_ID . ")";
$php80CondEnd = "#endif\n";
}
$declaredStrings = [];
if (!empty($this->attributes)) {
$code .= $php80CondStart;
foreach ($this->attributes as $key => $attribute) {
$code .= $attribute->generateCode(
"zend_add_class_attribute(class_entry",
@ -3903,25 +3811,18 @@ class ClassInfo {
);
}
$code .= $php80CondEnd;
}
if ($attributeInitializationCode = generateConstantAttributeInitialization($this->constInfos, $allConstInfos, $this->phpVersionIdMinimumCompatibility, $this->cond, $declaredStrings)) {
$code .= $php80CondStart;
$code .= "\n" . $attributeInitializationCode;
$code .= $php80CondEnd;
}
if ($attributeInitializationCode = generatePropertyAttributeInitialization($this->propertyInfos, $allConstInfos, $this->phpVersionIdMinimumCompatibility, $declaredStrings)) {
$code .= $php80CondStart;
$code .= "\n" . $attributeInitializationCode;
$code .= $php80CondEnd;
}
if ($attributeInitializationCode = generateFunctionAttributeInitialization($this->funcInfos, $allConstInfos, $this->phpVersionIdMinimumCompatibility, $this->cond, $declaredStrings)) {
$code .= $php80CondStart;
$code .= "\n" . $attributeInitializationCode;
$code .= $php80CondEnd;
}
$code .= "\n\treturn class_entry;\n";
@ -3932,10 +3833,6 @@ class ClassInfo {
$code .= "#endif\n";
}
if ($this->type === "enum" && !$php81MinimumCompatibility) {
$code .= "#endif\n";
}
return $code;
}
@ -4483,13 +4380,12 @@ class FileInfo {
} else if ($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), " .
"\"" . PHP_81_VERSION_ID . "\" (PHP 8.1), \"" . PHP_82_VERSION_ID . "\" (PHP 8.2), \"" . PHP_83_VERSION_ID . "\" (PHP 8.3), " .
"\"" . PHP_84_VERSION_ID . "\" (PHP 8.4), \"" . PHP_85_VERSION_ID . "\" (PHP 8.5), \"" . $tag->value . "\" provided"
"Legacy PHP version must be one of: \"" . PHP_84_VERSION_ID . "\" (PHP 8.4), " .
"\"" . PHP_85_VERSION_ID . "\" (PHP 8.5), \"" . $tag->value . "\" provided"
);
}
$this->minimumPhpVersionIdCompatibility = ($tag->value ? (int) $tag->value : PHP_70_VERSION_ID);
$this->minimumPhpVersionIdCompatibility = ($tag->value ? (int) $tag->value : PHP_84_VERSION_ID);
} else if ($tag->name === 'generate-class-entries') {
$this->generateClassEntries = true;
$this->declarationPrefix = $tag->value ? $tag->value . " " : "";
@ -5628,18 +5524,10 @@ function generateArgInfoCode(
}
}
$php80MinimumCompatibility = $fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_80_VERSION_ID;
if ($fileInfo->generateClassEntries) {
$declaredStrings = [];
$attributeInitializationCode = generateFunctionAttributeInitialization($fileInfo->funcInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null, $declaredStrings);
$attributeInitializationCode .= generateGlobalConstantAttributeInitialization($fileInfo->constInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null, $declaredStrings);
if ($attributeInitializationCode) {
if (!$php80MinimumCompatibility) {
$attributeInitializationCode = "\n#if (PHP_VERSION_ID >= " . PHP_80_VERSION_ID . ")" . $attributeInitializationCode . "#endif\n";
}
}
if ($attributeInitializationCode !== "") {
$code .= "\nstatic void register_{$stubFilenameWithoutExtension}_symbols(int module_number)\n";
$code .= "{\n";

Loading…
Cancel
Save