fix(compiler): resolve generated C++ symbol ambiguities

master
韩天峰 2 days ago
parent b3c7ee9bd3
commit 02a98ffc61
  1. 28
      docs/en/CPP_SYMBOL_NAMING.md
  2. 28
      docs/zh-cn/CPP_SYMBOL_NAMING.md
  3. 9
      phpunit/src/CompilerBaseApiTest.php
  4. 9
      src/CompilerBase.php
  5. 3
      src/Metadata/Constants.php
  6. 2
      src/Parser/AssignOpTrait.php
  7. 2
      src/Parser/ForeachTrait.php
  8. 2
      src/Translator.php

@ -13,7 +13,7 @@ This document is the internal C++ naming convention for TypePHP, PHPX, and TypeP
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| `typephp_` | TypePHP-specific runtime or compiled-artifact support logic | `typephp_call_parent_constructor()` | TypePHP/PHPX runtime | Internal or explicitly exported ABI | | `typephp_` | TypePHP-specific runtime or compiled-artifact support logic | `typephp_call_parent_constructor()` | TypePHP/PHPX runtime | Internal or explicitly exported ABI |
| `php::` | C++ wrappers for PHP runtime capabilities such as ZendAPI, zval, HashTable, and call frames | `php::deindirect()` | PHPX C++ API | PHPX API | | `php::` | C++ wrappers for PHP runtime capabilities such as ZendAPI, zval, HashTable, and call frames | `php::deindirect()` | PHPX C++ API | PHPX API |
| `typephp_<project>` | The private C++ namespace of a single compiled project | `namespace typephp_tpc` | Current generated project | Non-public ABI | | `typephp_project_<project>` | The private C++ namespace of a single compiled project | `namespace typephp_project_tpc` | Current generated project | Non-public ABI |
| `php_` | C++ callable symbols mapped from user PHP functions and class methods | `php_app__user__save()` | Visible to the linker | TypePHP/stub callable ABI | | `php_` | C++ callable symbols mapped from user PHP functions and class methods | `php_app__user__save()` | Visible to the linker | TypePHP/stub callable ABI |
Core constraints: Core constraints:
@ -21,7 +21,7 @@ Core constraints:
1. Do not add new global framework `php_*` helpers. 1. Do not add new global framework `php_*` helpers.
2. Capabilities that are unrelated to TypePHP and only wrap ZendAPI must be placed in `namespace php`. 2. Capabilities that are unrelated to TypePHP and only wrap ZendAPI must be placed in `namespace php`.
3. Logic that is unique to TypePHP and needs to be called across generated files uses the `typephp_` prefix. 3. Logic that is unique to TypePHP and needs to be called across generated files uses the `typephp_` prefix.
4. Data and functions that serve only one compiled project go into the `typephp_<project>` namespace. 4. Data and functions that serve only one compiled project go into the `typephp_project_<project>` namespace.
5. Global `php_*` callable names are reserved for the compiled ABI of user PHP declarations. 5. Global `php_*` callable names are reserved for the compiled ABI of user PHP declarations.
## 2. `typephp_`: TypePHP-specific Logic ## 2. `typephp_`: TypePHP-specific Logic
@ -144,23 +144,23 @@ php::stdCreateObject();
Do not mechanically preserve Zend's snake_case names as global C++ names. Lower-level calls can continue to use the original Zend API, such as `zend_objects_new()`, but the wrapper layer exposed to generated code should use `php::`. Do not mechanically preserve Zend's snake_case names as global C++ names. Lower-level calls can continue to use the original Zend API, such as `zend_objects_new()`, but the wrapper layer exposed to generated code should use `php::`.
## 4. `typephp_<project>`: Project-private Namespace ## 4. `typephp_project_<project>`: Project-private Namespace
Each TypePHP compiled project has an independent C++ namespace: Each TypePHP compiled project has an independent C++ namespace:
```text ```text
typephp_<target-name> typephp_project_<target-name>
``` ```
For example, if the project name is `tpc`: For example, if the project name is `tpc`:
```cpp ```cpp
namespace typephp_tpc { namespace typephp_project_tpc {
// Project-private generated state and helpers. // Project-private generated state and helpers.
} }
``` ```
The `-` and `*` in the project name are converted to `_`, and the remaining characters must satisfy the compiler's target identifier validation. Because of the fixed `typephp_` prefix, the final C++ namespace is a valid identifier even if the project name starts with a digit. The `-` and `*` in the project name are converted to `_`, and the remaining characters must satisfy the compiler's target identifier validation. The distinct `typephp_project_` prefix prevents generated namespaces from colliding with global `typephp_*` runtime helpers. It also keeps the final C++ namespace valid when the project name starts with a digit.
### 4.1 Content That Should Go into This Namespace ### 4.1 Content That Should Go into This Namespace
@ -175,7 +175,7 @@ The `-` and `*` in the project name are converted to `_`, and the remaining char
Illustration: Illustration:
```cpp ```cpp
namespace typephp_demo { namespace typephp_project_demo {
static php::Str literal_strings[] = { static php::Str literal_strings[] = {
php::Str{"hello"}, php::Str{"hello"},
@ -195,12 +195,12 @@ static void module_init() {
// Initialize this project's generated state. // Initialize this project's generated state.
} }
} // namespace typephp_demo } // namespace typephp_project_demo
``` ```
### 4.2 Visibility and ABI ### 4.2 Visibility and ABI
- Names inside `typephp_<project>` are implementation details, not library stub ABI. - Names inside `typephp_project_<project>` are implementation details, not library stub ABI.
- Objects and functions that can be limited to `static` should continue to be marked `static`. - Objects and functions that can be limited to `static` should continue to be marked `static`.
- Generated headers may declare project-internal accessors that must be used across translation units, but must not expose underlying arrays or cache tables. - Generated headers may declare project-internal accessors that must be used across translation units, but must not expose underlying arrays or cache tables.
- External handwritten C++ code must not depend on literal indexes, cache indexes, or project-internal storage names. - External handwritten C++ code must not depend on literal indexes, cache indexes, or project-internal storage names.
@ -211,10 +211,10 @@ static void module_init() {
Historical generated names may still appear in the project namespace, for example: Historical generated names may still appear in the project namespace, for example:
```cpp ```cpp
typephp_demo::php_class_entry_App_User typephp_project_demo::php_class_entry_App_User
``` ```
Although the member name starts with `php_`, the full symbol resides in `typephp_demo`, so it is a project-private implementation rather than the global user callable ABI described in Section 5. New project-internal helpers should prefer short names without `php_`, such as `get_class()`, `get_func()`, and `get_str()`. Although the member name starts with `php_`, the full symbol resides in `typephp_project_demo`, so it is a project-private implementation rather than the global user callable ABI described in Section 5. New project-internal helpers should prefer short names without `php_`, such as `get_class()`, `get_func()`, and `get_str()`.
## 5. `php_`: The C++ ABI of User PHP Callables ## 5. `php_`: The C++ ABI of User PHP Callables
@ -315,7 +315,7 @@ When adding a C++ API, judge in the following order:
1. **Is it the compiled body of a user PHP function or class method?** 1. **Is it the compiled body of a user PHP function or class method?**
- Yes: use the existing `php_` callable ABI generator; do not handwrite another mapping. - Yes: use the existing `php_` callable ABI generator; do not handwrite another mapping.
2. **Does it serve only one current TypePHP project?** 2. **Does it serve only one current TypePHP project?**
- Yes: place it in `typephp_<project>`, and use `static` or private accessors where possible. - Yes: place it in `typephp_project_<project>`, and use `static` or private accessors where possible.
3. **Does it implement TypePHP-specific semantics?** 3. **Does it implement TypePHP-specific semantics?**
- Yes: use the `typephp_` prefix. - Yes: use the `typephp_` prefix.
4. **Is it only a C++ wrapper of Zend/PHP runtime capabilities?** 4. **Is it only a C++ wrapper of Zend/PHP runtime capabilities?**
@ -330,7 +330,7 @@ When adding or modifying generated helpers, check:
- [ ] No new global `php_*` helpers in `typephp_helper.h`; - [ ] No new global `php_*` helpers in `typephp_helper.h`;
- [ ] ZendAPI wrappers are in `namespace php`; - [ ] ZendAPI wrappers are in `namespace php`;
- [ ] TypePHP-specific logic uses `typephp_`; - [ ] TypePHP-specific logic uses `typephp_`;
- [ ] Project caches and storage are in `typephp_<project>`; - [ ] Project caches and storage are in `typephp_project_<project>`;
- [ ] Project-private tables are not exposed directly via `extern` through generated headers; - [ ] Project-private tables are not exposed directly via `extern` through generated headers;
- [ ] User callables still use the unified `php_` ABI generator; - [ ] User callables still use the unified `php_` ABI generator;
- [ ] New names do not collide with user-declarable PHP functions or methods; - [ ] New names do not collide with user-declarable PHP functions or methods;
@ -350,7 +350,7 @@ tests/compiler/basic/helper-symbol-collision.phpt
| --- | --- | | --- | --- |
| `php_` callable prefix and combination separator | `src/CompilerBase.php` | | `php_` callable prefix and combination separator | `src/CompilerBase.php` |
| Callable combination collision detection | `src/Preprocessor.php` | | Callable combination collision detection | `src/Preprocessor.php` |
| `typephp_<project>` generation and project-private tables | `src/Translator.php` | | `typephp_project_<project>` generation and project-private tables | `src/Translator.php` |
| TypePHP extension prefix constants | `src/Metadata/Constants.php` | | TypePHP extension prefix constants | `src/Metadata/Constants.php` |
| PHPX/TypePHP helper classification | `vendor/swoole/phpx/include/typephp_helper.h` | | PHPX/TypePHP helper classification | `vendor/swoole/phpx/include/typephp_helper.h` |
| Embed module accessor concatenation | `vendor/swoole/phpx/src/misc/typephp_main.cc` | | Embed module accessor concatenation | `vendor/swoole/phpx/src/misc/typephp_main.cc` |

@ -13,7 +13,7 @@
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| `typephp_` | TypePHP 独有的运行时或编译产物支持逻辑 | `typephp_call_parent_constructor()` | TypePHP/PHPX 运行时 | 内部或显式导出 ABI | | `typephp_` | TypePHP 独有的运行时或编译产物支持逻辑 | `typephp_call_parent_constructor()` | TypePHP/PHPX 运行时 | 内部或显式导出 ABI |
| `php::` | 对 ZendAPI、zval、HashTable、call frame 等 PHP 运行时能力的 C++ 封装 | `php::deindirect()` | PHPX C++ API | PHPX API | | `php::` | 对 ZendAPI、zval、HashTable、call frame 等 PHP 运行时能力的 C++ 封装 | `php::deindirect()` | PHPX C++ API | PHPX API |
| `typephp_<project>` | 单个编译项目的私有 C++ 命名空间 | `namespace typephp_tpc` | 当前生成项目 | 非公共 ABI | | `typephp_project_<project>` | 单个编译项目的私有 C++ 命名空间 | `namespace typephp_project_tpc` | 当前生成项目 | 非公共 ABI |
| `php_` | 用户 PHP 函数和类方法映射后的 C++ callable 符号 | `php_app__user__save()` | 链接器可见 | TypePHP/stub callable ABI | | `php_` | 用户 PHP 函数和类方法映射后的 C++ callable 符号 | `php_app__user__save()` | 链接器可见 | TypePHP/stub callable ABI |
核心约束: 核心约束:
@ -21,7 +21,7 @@
1. 不得新增全局的框架 `php_*` helper。 1. 不得新增全局的框架 `php_*` helper。
2. 与 TypePHP 无关、只是包装 ZendAPI 的能力必须放入 `namespace php` 2. 与 TypePHP 无关、只是包装 ZendAPI 的能力必须放入 `namespace php`
3. TypePHP 独有且需要跨生成文件调用的逻辑使用 `typephp_` 前缀。 3. TypePHP 独有且需要跨生成文件调用的逻辑使用 `typephp_` 前缀。
4. 只服务于一个编译项目的数据和函数放入 `typephp_<project>` 命名空间。 4. 只服务于一个编译项目的数据和函数放入 `typephp_project_<project>` 命名空间。
5. 全局 `php_*` callable 名称保留给用户 PHP 声明的编译 ABI。 5. 全局 `php_*` callable 名称保留给用户 PHP 声明的编译 ABI。
## 2. `typephp_`:TypePHP 独有逻辑 ## 2. `typephp_`:TypePHP 独有逻辑
@ -144,23 +144,23 @@ php::stdCreateObject();
不要把 Zend 的 snake_case 名称机械地保留为全局 C++ 名称。底层调用可以继续使用 Zend 原始 API,例如 `zend_objects_new()`,但对生成代码暴露的包装层应使用 `php::` 不要把 Zend 的 snake_case 名称机械地保留为全局 C++ 名称。底层调用可以继续使用 Zend 原始 API,例如 `zend_objects_new()`,但对生成代码暴露的包装层应使用 `php::`
## 4. `typephp_<project>`:项目私有命名空间 ## 4. `typephp_project_<project>`:项目私有命名空间
每个 TypePHP 编译项目拥有独立的 C++ 命名空间: 每个 TypePHP 编译项目拥有独立的 C++ 命名空间:
```text ```text
typephp_<target-name> typephp_project_<target-name>
``` ```
例如项目名为 `tpc` 例如项目名为 `tpc`
```cpp ```cpp
namespace typephp_tpc { namespace typephp_project_tpc {
// Project-private generated state and helpers. // Project-private generated state and helpers.
} }
``` ```
项目名中的 `-``*` 会转换为 `_`,其余字符必须满足编译器的 target identifier 校验。由于固定带有 `typephp_` 前缀,即使项目名以数字开头,最终 C++ namespace 仍是合法标识符。 项目名中的 `-``*` 会转换为 `_`,其余字符必须满足编译器的 target identifier 校验。独立的 `typephp_project_` 前缀可防止生成 namespace 与全局 `typephp_*` 运行时 helper 冲突;即使项目名以数字开头,最终 C++ namespace 仍是合法标识符。
### 4.1 应放入该命名空间的内容 ### 4.1 应放入该命名空间的内容
@ -175,7 +175,7 @@ namespace typephp_tpc {
示意: 示意:
```cpp ```cpp
namespace typephp_demo { namespace typephp_project_demo {
static php::Str literal_strings[] = { static php::Str literal_strings[] = {
php::Str{"hello"}, php::Str{"hello"},
@ -195,12 +195,12 @@ static void module_init() {
// Initialize this project's generated state. // Initialize this project's generated state.
} }
} // namespace typephp_demo } // namespace typephp_project_demo
``` ```
### 4.2 可见性与 ABI ### 4.2 可见性与 ABI
- `typephp_<project>` 内的名称是实现细节,不是 library stub ABI。 - `typephp_project_<project>` 内的名称是实现细节,不是 library stub ABI。
- 可限制为 `static` 的对象和函数应继续标记为 `static` - 可限制为 `static` 的对象和函数应继续标记为 `static`
- 生成头文件可以声明必须跨 translation unit 使用的项目内部 accessor,但不应暴露底层数组或缓存表。 - 生成头文件可以声明必须跨 translation unit 使用的项目内部 accessor,但不应暴露底层数组或缓存表。
- 外部手写 C++ 代码不得依赖 literal index、cache index 或项目内部 storage 名称。 - 外部手写 C++ 代码不得依赖 literal index、cache index 或项目内部 storage 名称。
@ -211,10 +211,10 @@ static void module_init() {
项目 namespace 中仍可能出现历史生成名称,例如: 项目 namespace 中仍可能出现历史生成名称,例如:
```cpp ```cpp
typephp_demo::php_class_entry_App_User typephp_project_demo::php_class_entry_App_User
``` ```
虽然成员名以 `php_` 开头,但完整符号位于 `typephp_demo` 中,因此它属于项目私有实现,而不是第 5 节所述的全局用户 callable ABI。新增项目内部 helper 应优先使用不带 `php_` 的短名称,例如 `get_class()`、`get_func()` 和 `get_str()` 虽然成员名以 `php_` 开头,但完整符号位于 `typephp_project_demo` 中,因此它属于项目私有实现,而不是第 5 节所述的全局用户 callable ABI。新增项目内部 helper 应优先使用不带 `php_` 的短名称,例如 `get_class()`、`get_func()` 和 `get_str()`
## 5. `php_`:用户 PHP callable 的 C++ ABI ## 5. `php_`:用户 PHP callable 的 C++ ABI
@ -315,7 +315,7 @@ TypePHP 扩展不得分别编译或静态链接包含进程级 Zend 状态的 PH
1. **它是否是用户 PHP 函数或类方法的编译本体?** 1. **它是否是用户 PHP 函数或类方法的编译本体?**
- 是:使用既有 `php_` callable ABI 生成器,禁止手写另一套映射。 - 是:使用既有 `php_` callable ABI 生成器,禁止手写另一套映射。
2. **它是否只服务于当前一个 TypePHP 项目?** 2. **它是否只服务于当前一个 TypePHP 项目?**
- 是:放入 `typephp_<project>`,并尽可能使用 `static` 或私有 accessor。 - 是:放入 `typephp_project_<project>`,并尽可能使用 `static` 或私有 accessor。
3. **它是否实现 TypePHP 独有语义?** 3. **它是否实现 TypePHP 独有语义?**
- 是:使用 `typephp_` 前缀。 - 是:使用 `typephp_` 前缀。
4. **它是否只是对 Zend/PHP 运行时能力的 C++ 封装?** 4. **它是否只是对 Zend/PHP 运行时能力的 C++ 封装?**
@ -330,7 +330,7 @@ TypePHP 扩展不得分别编译或静态链接包含进程级 Zend 状态的 PH
- [ ] `typephp_helper.h` 中没有新增全局 `php_*` helper; - [ ] `typephp_helper.h` 中没有新增全局 `php_*` helper;
- [ ] ZendAPI 包装位于 `namespace php` - [ ] ZendAPI 包装位于 `namespace php`
- [ ] TypePHP 独有逻辑使用 `typephp_` - [ ] TypePHP 独有逻辑使用 `typephp_`
- [ ] 项目缓存和 storage 位于 `typephp_<project>` - [ ] 项目缓存和 storage 位于 `typephp_project_<project>`
- [ ] 项目私有表没有通过生成头文件直接 `extern` 暴露; - [ ] 项目私有表没有通过生成头文件直接 `extern` 暴露;
- [ ] 用户 callable 仍使用统一的 `php_` ABI 生成器; - [ ] 用户 callable 仍使用统一的 `php_` ABI 生成器;
- [ ] 新名称不会与用户可声明的 PHP 函数或方法发生冲突; - [ ] 新名称不会与用户可声明的 PHP 函数或方法发生冲突;
@ -350,7 +350,7 @@ tests/compiler/basic/helper-symbol-collision.phpt
| --- | --- | | --- | --- |
| `php_` callable 前缀与组合分隔符 | `src/CompilerBase.php` | | `php_` callable 前缀与组合分隔符 | `src/CompilerBase.php` |
| callable 组合冲突检测 | `src/Preprocessor.php` | | callable 组合冲突检测 | `src/Preprocessor.php` |
| `typephp_<project>` 生成及项目私有表 | `src/Translator.php` | | `typephp_project_<project>` 生成及项目私有表 | `src/Translator.php` |
| TypePHP extension 前缀常量 | `src/Metadata/Constants.php` | | TypePHP extension 前缀常量 | `src/Metadata/Constants.php` |
| PHPX/TypePHP helper 分类 | `vendor/swoole/phpx/include/typephp_helper.h` | | PHPX/TypePHP helper 分类 | `vendor/swoole/phpx/include/typephp_helper.h` |
| embed module accessor 拼接 | `vendor/swoole/phpx/src/misc/typephp_main.cc` | | embed module accessor 拼接 | `vendor/swoole/phpx/src/misc/typephp_main.cc` |

@ -5,6 +5,7 @@ namespace TypePhp\Tests;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use TypePhp\CompilerTest; use TypePhp\CompilerTest;
use TypePhp\CompilerBase; use TypePhp\CompilerBase;
use TypePhp\Metadata\Constants;
use TypePhp\Type; use TypePhp\Type;
use TypePhp\Exception\TestError; use TypePhp\Exception\TestError;
use TypePhp\Platform\Macos; use TypePhp\Platform\Macos;
@ -733,13 +734,15 @@ YAML, 'myproject.yml', 'examples/tetris-sdl');
$this->assertSame('demo.so', $this->invokeMethod('getTargetFileName')); $this->assertSame('demo.so', $this->invokeMethod('getTargetFileName'));
} }
public function testGeneratedZendModuleAlwaysUsesTypePhpPrefix(): void public function testGeneratedZendModuleAndProjectNamespaceUseDistinctPrefixes(): void
{ {
$this->compiler->setTargetName('demo'); $this->compiler->setTargetName('demo');
$this->assertSame('typephp_demo', $this->compiler->getModuleName()); $this->assertSame('typephp_demo', $this->compiler->getModuleName());
$this->assertSame('typephp_project_demo', $this->compiler->getProjectNamespace());
$this->compiler->setTargetName('123'); $this->compiler->setTargetName('123');
$this->assertSame('typephp_123', $this->compiler->getModuleName()); $this->assertSame('typephp_123', $this->compiler->getModuleName());
$this->assertSame('typephp_project_123', $this->compiler->getProjectNamespace());
} }
public function testParseProjectYamlResolvesRelativePathOptionsAgainstYamlDirectory(): void public function testParseProjectYamlResolvesRelativePathOptionsAgainstYamlDirectory(): void
@ -1270,7 +1273,7 @@ YAML);
$compiler->genDataDeclarations($dataFile); $compiler->genDataDeclarations($dataFile);
$data = file_get_contents($dataFile); $data = file_get_contents($dataFile);
$extension = file_get_contents($compiler->genExtension()); $extension = file_get_contents($compiler->genExtension());
$namespace = 'typephp_' . $target; $namespace = Constants::CPP_PROJECT_NAMESPACE_PREFIX . $target;
$this->assertStringContainsString('namespace ' . $namespace . ' {', $data, $mode); $this->assertStringContainsString('namespace ' . $namespace . ' {', $data, $mode);
$this->assertStringContainsString('using namespace ' . $namespace . ';', $data, $mode); $this->assertStringContainsString('using namespace ' . $namespace . ';', $data, $mode);
@ -1303,7 +1306,7 @@ YAML);
$this->assertStringContainsString('#include <typephp_runtime.h>', $extension); $this->assertStringContainsString('#include <typephp_runtime.h>', $extension);
$this->assertStringContainsString('TYPEPHP_EMBED_GET_MODULE_FUNCTION(' . $target . ')', $extension); $this->assertStringContainsString('TYPEPHP_EMBED_GET_MODULE_FUNCTION(' . $target . ')', $extension);
$this->assertStringContainsString( $this->assertStringContainsString(
'return &' . $namespace . '::' . $namespace . '_module_entry;', 'return &' . $namespace . '::typephp_' . $target . '_module_entry;',
$extension, $extension,
); );
} else { } else {

@ -1546,13 +1546,16 @@ class CompilerBase implements PropertyAccessContext
return $list; return $list;
} }
protected function parseArrayKey(NodeAbstract $expr): string protected function parseArrayKey(NodeAbstract $expr, bool $keepStringObject = false): string
{ {
$this->assertNotNativeObjectArrayKey($expr); $this->assertNotNativeObjectArrayKey($expr);
$key = $this->parseIdentifier($expr); $key = $this->parseIdentifier($expr);
if (str_starts_with($key, self::LITERAL_STRING_GETTER . '(')) { if (str_starts_with($key, self::LITERAL_STRING_GETTER . '(')) {
$key = "{$key}.str()"; // Array initializers and setters use zend_string* keys, while item()
} elseif ($this->isZeroLiteral($expr)) { // uses php::String to avoid an ambiguous conversion to Variant.
return $keepStringObject ? $key : "{$key}.str()";
}
if ($this->isZeroLiteral($expr)) {
$key = self::VALUE_ZERO; $key = self::VALUE_ZERO;
} }
return $key; return $key;

@ -14,6 +14,9 @@ class Constants
{ {
public const string EXTENSION_PREFIX = 'typephp_'; public const string EXTENSION_PREFIX = 'typephp_';
/** Keep generated project namespaces disjoint from global typephp_* runtime helpers. */
public const string CPP_PROJECT_NAMESPACE_PREFIX = 'typephp_project_';
public const array CPP_RESERVED_NAMES = [ public const array CPP_RESERVED_NAMES = [
'alignas', 'alignas',
'alignof', 'alignof',

@ -287,7 +287,7 @@ trait AssignOpTrait
continue; continue;
} }
if ($item instanceof ArrayItem) { if ($item instanceof ArrayItem) {
$key = $item->key ? $this->parseArrayKey($item->key) : (string) $k; $key = $item->key ? $this->parseArrayKey($item->key, true) : (string) $k;
if ($item->value instanceof Expr\List_) { if ($item->value instanceof Expr\List_) {
$nestedTmp = $this->genTmpVarName(); $nestedTmp = $this->genTmpVarName();
$this->addLocalVar($nestedTmp, Type::ARRAY); $this->addLocalVar($nestedTmp, Type::ARRAY);

@ -26,7 +26,7 @@ trait ForeachTrait
if ($item->byRef) { if ($item->byRef) {
$this->fatalError($item, 'Foreach list destructuring cannot bind items by reference'); $this->fatalError($item, 'Foreach list destructuring cannot bind items by reference');
} }
$key = $item->key ? $this->parseArrayKey($item->key) : (string) $k; $key = $item->key ? $this->parseArrayKey($item->key, true) : (string) $k;
if ($item->value instanceof Expr\List_) { if ($item->value instanceof Expr\List_) {
$nestedTmpVar = $this->genTmpVarName(); $nestedTmpVar = $this->genTmpVarName();
$this->addLocalVar($nestedTmpVar, Type::VAR); $this->addLocalVar($nestedTmpVar, Type::VAR);

@ -1592,7 +1592,7 @@ CODE;
public function getProjectNamespace(): string public function getProjectNamespace(): string
{ {
return $this->getModuleName(); return Constants::CPP_PROJECT_NAMESPACE_PREFIX . $this->targetName;
} }
/** /**

Loading…
Cancel
Save