Fix #19914: Fixed ArrayHelper::keyExists() and ::remove() functions when the key is a float and the value is null

This commit is contained in:
rhertogh
2023-08-18 13:31:09 +02:00
committed by GitHub
parent 786a75ca63
commit e916e9d564
4 changed files with 72 additions and 11 deletions

View File

@ -16,6 +16,7 @@ Yii Framework 2 Change Log
- Enh #19884: Added support Enums in Query Builder (sk1t0n) - Enh #19884: Added support Enums in Query Builder (sk1t0n)
- Bug #19908: Fix associative array cell content rendering in Table widget (rhertogh) - Bug #19908: Fix associative array cell content rendering in Table widget (rhertogh)
- Bug #19906: Fixed multiline strings in the `\yii\console\widgets\Table` widget (rhertogh) - Bug #19906: Fixed multiline strings in the `\yii\console\widgets\Table` widget (rhertogh)
- Bug #19914: Fixed `ArrayHelper::keyExists()` and `::remove()` functions when the key is a float and the value is `null` (rhertogh)
- Enh #19920: Broadened the accepted type of `Cookie::$expire` from `int` to `int|string|\DateTimeInterface|null` (rhertogh) - Enh #19920: Broadened the accepted type of `Cookie::$expire` from `int` to `int|string|\DateTimeInterface|null` (rhertogh)

View File

@ -282,7 +282,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
*/ */
public function __get($name) public function __get($name)
{ {
if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) { if (array_key_exists($name, $this->_attributes)) {
return $this->_attributes[$name]; return $this->_attributes[$name];
} }
@ -290,7 +290,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
return null; return null;
} }
if (isset($this->_related[$name]) || array_key_exists($name, $this->_related)) { if (array_key_exists($name, $this->_related)) {
return $this->_related[$name]; return $this->_related[$name];
} }
$value = parent::__get($name); $value = parent::__get($name);

View File

@ -327,7 +327,12 @@ class BaseArrayHelper
*/ */
public static function remove(&$array, $key, $default = null) public static function remove(&$array, $key, $default = null)
{ {
if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) { // ToDo: This check can be removed when the minimum PHP version is >= 8.1 (Yii2.2)
if (is_float($key)) {
$key = (int)$key;
}
if (is_array($array) && array_key_exists($key, $array)) {
$value = $array[$key]; $value = $array[$key];
unset($array[$key]); unset($array[$key]);
@ -608,17 +613,20 @@ class BaseArrayHelper
* Checks if the given array contains the specified key. * Checks if the given array contains the specified key.
* This method enhances the `array_key_exists()` function by supporting case-insensitive * This method enhances the `array_key_exists()` function by supporting case-insensitive
* key comparison. * key comparison.
* @param string $key the key to check * @param string|int $key the key to check
* @param array|ArrayAccess $array the array with keys to check * @param array|ArrayAccess $array the array with keys to check
* @param bool $caseSensitive whether the key comparison should be case-sensitive * @param bool $caseSensitive whether the key comparison should be case-sensitive
* @return bool whether the array contains the specified key * @return bool whether the array contains the specified key
*/ */
public static function keyExists($key, $array, $caseSensitive = true) public static function keyExists($key, $array, $caseSensitive = true)
{ {
// ToDo: This check can be removed when the minimum PHP version is >= 8.1 (Yii2.2)
if (is_float($key)) {
$key = (int)$key;
}
if ($caseSensitive) { if ($caseSensitive) {
// Function `isset` checks key faster but skips `null`, `array_key_exists` handles this case if (is_array($array) && array_key_exists($key, $array)) {
// https://www.php.net/manual/en/function.array-key-exists.php#107786
if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) {
return true; return true;
} }
// Cannot use `array_has_key` on Objects for PHP 7.4+, therefore we need to check using [[ArrayAccess::offsetExists()]] // Cannot use `array_has_key` on Objects for PHP 7.4+, therefore we need to check using [[ArrayAccess::offsetExists()]]

View File

@ -135,6 +135,29 @@ class ArrayHelperTest extends TestCase
$this->assertEquals('defaultValue', $default); $this->assertEquals('defaultValue', $default);
} }
/**
* @return void
*/
public function testRemoveWithFloat()
{
if (version_compare(PHP_VERSION, '8.1.0', '>=')) {
$this->markTestSkipped('Using floats as array key is deprecated.');
}
$array = ['name' => 'b', 'age' => 3, 1.1 => null];
$name = ArrayHelper::remove($array, 'name');
$this->assertEquals($name, 'b');
$this->assertEquals($array, ['age' => 3, 1.1 => null]);
$floatVal = ArrayHelper::remove($array, 1.1);
$this->assertNull($floatVal);
$this->assertEquals($array, ['age' => 3]);
$default = ArrayHelper::remove($array, 'nonExisting', 'defaultValue');
$this->assertEquals('defaultValue', $default);
}
public function testRemoveValueMultiple() public function testRemoveValueMultiple()
{ {
$array = [ $array = [
@ -506,14 +529,21 @@ class ArrayHelperTest extends TestCase
/** /**
* @see https://github.com/yiisoft/yii2/pull/11549 * @see https://github.com/yiisoft/yii2/pull/11549
*/ */
public function test() public function testGetValueWithFloatKeys()
{ {
if (version_compare(PHP_VERSION, '8.1.0', '>=')) {
$this->markTestSkipped('Using floats as array key is deprecated.');
}
$array = []; $array = [];
$array[1.0] = 'some value'; $array[1.1] = 'some value';
$array[2.1] = null;
$result = ArrayHelper::getValue($array, 1.0);
$result = ArrayHelper::getValue($array, 1.2);
$this->assertEquals('some value', $result); $this->assertEquals('some value', $result);
$result = ArrayHelper::getValue($array, 2.2);
$this->assertNull($result);
} }
public function testIndex() public function testIndex()
@ -712,6 +742,7 @@ class ArrayHelperTest extends TestCase
'a' => 1, 'a' => 1,
'B' => 2, 'B' => 2,
]; ];
$this->assertTrue(ArrayHelper::keyExists('a', $array)); $this->assertTrue(ArrayHelper::keyExists('a', $array));
$this->assertFalse(ArrayHelper::keyExists('b', $array)); $this->assertFalse(ArrayHelper::keyExists('b', $array));
$this->assertTrue(ArrayHelper::keyExists('B', $array)); $this->assertTrue(ArrayHelper::keyExists('B', $array));
@ -723,6 +754,27 @@ class ArrayHelperTest extends TestCase
$this->assertFalse(ArrayHelper::keyExists('c', $array, false)); $this->assertFalse(ArrayHelper::keyExists('c', $array, false));
} }
public function testKeyExistsWithFloat()
{
if (version_compare(PHP_VERSION, '8.1.0', '>=')) {
$this->markTestSkipped('Using floats as array key is deprecated.');
}
$array = [
1 => 3,
2.2 => 4, // Note: Floats are cast to ints, which means that the fractional part will be truncated.
3.3 => null,
];
$this->assertTrue(ArrayHelper::keyExists(1, $array));
$this->assertTrue(ArrayHelper::keyExists(1.1, $array));
$this->assertTrue(ArrayHelper::keyExists(2, $array));
$this->assertTrue(ArrayHelper::keyExists('2', $array));
$this->assertTrue(ArrayHelper::keyExists(2.2, $array));
$this->assertTrue(ArrayHelper::keyExists(3, $array));
$this->assertTrue(ArrayHelper::keyExists(3.3, $array));
}
public function testKeyExistsArrayAccess() public function testKeyExistsArrayAccess()
{ {
$array = new TraversableArrayAccessibleObject([ $array = new TraversableArrayAccessibleObject([