From 8d3d1febc2b57e588ca524078aa1c246c29251df Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 4 Sep 2026 11:51:56 +0800 Subject: [PATCH] fix(array): reject scalar offset writes safely --- .../explicit-native-int-array-dim-write.php | 7 ++++ phpunit/code/native-bool-array-dim-write.php | 8 +++++ phpunit/code/native-float-array-dim-write.php | 8 +++++ phpunit/code/native-int-array-dim-write.php | 8 +++++ phpunit/src/AssignTest.php | 14 ++++++++ src/Parser/ArrayExpressionTrait.php | 27 +++++++++------ src/Parser/AssignOpTrait.php | 4 +-- tests/compiler/array/scalar-offset-write.phpt | 33 +++++++++++++++++++ 8 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 phpunit/code/explicit-native-int-array-dim-write.php create mode 100644 phpunit/code/native-bool-array-dim-write.php create mode 100644 phpunit/code/native-float-array-dim-write.php create mode 100644 phpunit/code/native-int-array-dim-write.php create mode 100644 tests/compiler/array/scalar-offset-write.phpt diff --git a/phpunit/code/explicit-native-int-array-dim-write.php b/phpunit/code/explicit-native-int-array-dim-write.php new file mode 100644 index 00000000..afa6aa47 --- /dev/null +++ b/phpunit/code/explicit-native-int-array-dim-write.php @@ -0,0 +1,7 @@ +exec("declare(strict_types=0) is not allowed, only strict_types=1 is supported", 'declare-strict-types-zero.php'); } + + /** @dataProvider nativeScalarArrayDimWriteProvider */ + public function testNativeScalarArrayDimWriteFailsInTypePhp(string $file): void + { + $this->exec('Cannot use [] for numbers', $file); + } + + public static function nativeScalarArrayDimWriteProvider(): iterable + { + yield 'native int append' => ['native-int-array-dim-write.php']; + yield 'native float keyed write' => ['native-float-array-dim-write.php']; + yield 'native bool append' => ['native-bool-array-dim-write.php']; + yield 'explicit std::int keyed write' => ['explicit-native-int-array-dim-write.php']; + } } diff --git a/src/Parser/ArrayExpressionTrait.php b/src/Parser/ArrayExpressionTrait.php index 36fda31b..0a0c02dd 100644 --- a/src/Parser/ArrayExpressionTrait.php +++ b/src/Parser/ArrayExpressionTrait.php @@ -216,17 +216,8 @@ trait ArrayExpressionTrait } else { $this->errorUndefinedVariable($node->var); } - } else { - $type = $this->getVarType($var); - if ($type === Type::BOOL || $type === Type::INT || $type === Type::FLOAT) { - $this->fatalError($node, 'Cannot use [] for numbers'); - } - } - if ($this->getVarType($var) === Type::STR) { - if ($node->dim === null) { - $this->fatalError($node, 'Cannot use [] for strings'); - } } + $this->assertArrayDimVariableTypeIsSupported($node, $var); } if ($node->dim === null) { @@ -267,6 +258,22 @@ trait ArrayExpressionTrait } } + /** + * Fixed native scalar variables cannot defer an offset operation to + * PHPX. Diagnose them here so invalid TypePHP does not become invalid C++. + * A php::Var remains runtime-checked because its value may have changed. + */ + protected function assertArrayDimVariableTypeIsSupported(Expr\ArrayDimFetch $node, string $var): void + { + $type = $this->getVarType($var); + if ($type === Type::BOOL || $type === Type::INT || $type === Type::FLOAT) { + $this->fatalError($node, 'Cannot use [] for numbers'); + } + if ($type === Type::STR && $node->dim === null) { + $this->fatalError($node, 'Cannot use [] for strings'); + } + } + private function parseArrayMixed(Expr\Array_ $node): string { $tmpVar = $this->genTmpVarName(); diff --git a/src/Parser/AssignOpTrait.php b/src/Parser/AssignOpTrait.php index c43d03f8..55f7f608 100644 --- a/src/Parser/AssignOpTrait.php +++ b/src/Parser/AssignOpTrait.php @@ -666,9 +666,7 @@ trait AssignOpTrait return $this->parseAssignPropertyFetch($left, $right, $propertyWriteTarget, $resultUnused); } elseif ($this->isArrayDimFetch($left) and $this->isVarExpr($left->var)) { $tmp = $this->parseIdentifier($left->var); - if ($this->getVarType($tmp) === Type::STR and $left->dim === null) { - $this->fatalError($left, 'Cannot use [] for strings'); - } + $this->assertArrayDimVariableTypeIsSupported($left, $tmp); if ($this->isStdContainerExpr($left)) { return $this->parseStdContainerAssign($left, $right); } diff --git a/tests/compiler/array/scalar-offset-write.phpt b/tests/compiler/array/scalar-offset-write.phpt new file mode 100644 index 00000000..d439e0b3 --- /dev/null +++ b/tests/compiler/array/scalar-offset-write.phpt @@ -0,0 +1,33 @@ +--TEST-- +Dynamic scalar offset writes fail instead of being silently ignored +--FILE-- + +--EXPECT-- +int:error +float:error +bool:error +object:error +null:valid