fix(array): reject scalar offset writes safely

master
韩天峰 3 days ago
parent be5cc71a8b
commit 8d3d1febc2
  1. 7
      phpunit/code/explicit-native-int-array-dim-write.php
  2. 8
      phpunit/code/native-bool-array-dim-write.php
  3. 8
      phpunit/code/native-float-array-dim-write.php
  4. 8
      phpunit/code/native-int-array-dim-write.php
  5. 14
      phpunit/src/AssignTest.php
  6. 27
      src/Parser/ArrayExpressionTrait.php
  7. 4
      src/Parser/AssignOpTrait.php
  8. 33
      tests/compiler/array/scalar-offset-write.phpt

@ -0,0 +1,7 @@
<?php
function main(): void
{
$value = std::int(1);
$value[0] = 'invalid';
}

@ -0,0 +1,8 @@
<?php
use native_types;
function main(): void
{
$value = false;
$value[] = 'invalid';
}

@ -0,0 +1,8 @@
<?php
use native_types;
function main(): void
{
$value = 1.5;
$value[0] = 'invalid';
}

@ -0,0 +1,8 @@
<?php
use native_types;
function main(): void
{
$value = 1;
$value[] = 'invalid';
}

@ -173,4 +173,18 @@ class AssignTest extends \BaseTest
{
$this->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'];
}
}

@ -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();

@ -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);
}

@ -0,0 +1,33 @@
--TEST--
Dynamic scalar offset writes fail instead of being silently ignored
--FILE--
<?php
function expectOffsetWriteError(mixed $value, string $label): void
{
try {
$value[] = 'invalid';
echo $label, ":missing-error\n";
} catch (Error $error) {
echo $label, ":error\n";
}
}
function main(): void
{
expectOffsetWriteError(1, 'int');
expectOffsetWriteError(1.5, 'float');
expectOffsetWriteError(false, 'bool');
expectOffsetWriteError(new stdClass(), 'object');
$value = null;
$value[] = 'valid';
echo 'null:', $value[0], "\n";
}
?>
--EXPECT--
int:error
float:error
bool:error
object:error
null:valid
Loading…
Cancel
Save