From 9b8dda24648b5a002e797b15c6d40776c739b719 Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Thu, 4 Apr 2024 17:18:12 +0200 Subject: [PATCH] PHP: use `$this` as default `$_root` only in top-level types See the same change in Python: e776c982f8ba5b039bdcacd1db48ecbe77588df6 This commit by itself doesn't fix any observable issue because this fallback behavior is also performed in the runtime library: https://github.com/kaitai-io/kaitai_struct_php_runtime/blob/e21bc2a3/lib/Kaitai/Struct/Struct.php#L12 However, this poses a problem because using `$this` as a fallback for `$_root` should be done **only** in the top-level type. This is why it can actually only be done here in KSC and not in the runtime library, because the runtime library has no way of knowing whether it's dealing with a top-level type or not. Therefore, this commit adds the fallback in KSC so that it can be removed from the runtime library. The fact that the runtime library does it in every type is the reason why the NestedTypesImport test currently fails (see https://github.com/kaitai-io/ci_artifacts/blob/f82e17355f/test_out/php/ci.json#L719-L729). The scenario is that `nested_types_import.ksy` contains `type: nested_types3::subtype_b` (see [`nested_types_import.ksy:15`](https://github.com/kaitai-io/kaitai_struct_tests/blob/ec0fafac31/formats/nested_types_import.ksy#L15)), which is an external type from the perspective of `nested_types_import`, so `_root` is not passed to it and remains `null`. The fact that `$_root` is `null` is noticed by the runtime library, which replaces it with `$this`. This is wrong, because the type of `$_root` in `NestedTypes3\SubtypeB` is supposed to be `NestedTypes3`, not `NestedTypes3\SubtypeB`. However, this turns into an actual error once `NestedTypes3\SubtypeB::_read()` creates an instance of `NestedTypes3\SubtypeA\SubtypeCc` on this line - [`NestedTypes3.php:88`](https://github.com/kaitai-io/ci_targets/blob/24ec7d3efd/compiled/php/NestedTypes3.php#L88): ```php $this->_m_aCc = new \Kaitai\Struct\Tests\NestedTypes3\SubtypeA\SubtypeCc($this->_io, $this, $this->_root); ``` `$this->_root` is a `NestedTypes3\SubtypeB` object, but `NestedTypes3\SubtypeA\SubtypeCc` requires that the `$_root` argument type is `NestedTypes3`. Since this is not the case, a `TypeError` is thrown. --- .../io/kaitai/struct/languages/PHPCompiler.scala | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/shared/src/main/scala/io/kaitai/struct/languages/PHPCompiler.scala b/shared/src/main/scala/io/kaitai/struct/languages/PHPCompiler.scala index aca722ccb..3a2527dee 100644 --- a/shared/src/main/scala/io/kaitai/struct/languages/PHPCompiler.scala +++ b/shared/src/main/scala/io/kaitai/struct/languages/PHPCompiler.scala @@ -102,6 +102,18 @@ class PHPCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) val tParent = kaitaiType2NativeType(parentType) val tRoot = translator.types2classAbs(rootClassName) + val pRootValue = if (name == rootClassName) { + // We could technically use the [null coalescing operator + // (`??`)](https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.null-coalesce-op) + // available since PHP 7 (that's OK, we don't support any lower version + // than that), which does approximately this, but also has an additional + // feature of suppressing the error if the left-hand side is an undefined + // variable, which we don't need here. + s"$pRoot === null ? $$this : $pRoot" + } else { + pRoot + } + out.puts( s"public function __construct($paramsArg" + s"$tIo $pIo, " + @@ -109,7 +121,7 @@ class PHPCompiler(typeProvider: ClassTypeProvider, config: RuntimeConfig) s"$tRoot $pRoot = null" + endianAdd + ") {" ) out.inc - out.puts(s"parent::__construct($pIo, $pParent, $pRoot);") + out.puts(s"parent::__construct($pIo, $pParent, $pRootValue);") if (isHybrid) handleAssignmentSimple(EndianIdentifier, "$is_le")