diff --git a/docs/guide-ru/helper-array.md b/docs/guide-ru/helper-array.md
index 2b4be9731a..f68de6c63f 100644
--- a/docs/guide-ru/helper-array.md
+++ b/docs/guide-ru/helper-array.md
@@ -47,7 +47,58 @@ $fullName = ArrayHelper::getValue($user, function ($user, $defaultValue) {
$username = ArrayHelper::getValue($comment, 'user.username', 'Unknown');
```
-В случае если вы хотите получить значение и тут же удалить его из массива, вы можете использовать метод `remove`
+
+## Запись значений
+
+```php
+$array = [
+ 'key' => [
+ 'in' => ['k' => 'value']
+ ]
+];
+
+ArrayHelper::setValue($array, 'key.in', ['arr' => 'val']);
+// путь для записи значения в `$array` можно указать как массив
+ArrayHelper::setValue($array, ['key', 'in'], ['arr' => 'val']);
+```
+
+В результате исходное значение `$array['key']['in']` будет перезаписано новым
+
+```php
+[
+ 'key' => [
+ 'in' => ['arr' => 'val']
+ ]
+]
+```
+
+Если путь содержит несуществующий ключ, то он будет создан
+
+```php
+// Если `$array['key']['in']['arr0']` не пустой, то значение будет добавлено в массив
+ArrayHelper::setValue($array, 'key.in.arr0.arr1', 'val');
+
+// если необходимо полностью переопределить значение `$array['key']['in']['arr0']`
+ArrayHelper::setValue($array, 'key.in.arr0', ['arr1' => 'val']);
+```
+
+Результатом будет следующим:
+
+```php
+[
+ 'key' => [
+ 'in' => [
+ 'k' => 'value',
+ 'arr0' => ['arr1' => 'val']
+ ]
+ ]
+]
+```
+
+
+## Изъять значение из массива
+
+Если вы хотите получить значение и тут же удалить его из массива, вы можете использовать метод `remove`
```php
$array = ['type' => 'A', 'options' => [1, 2]];
diff --git a/docs/guide/helper-array.md b/docs/guide/helper-array.md
index 1b0d9a30bc..f317eab4de 100644
--- a/docs/guide/helper-array.md
+++ b/docs/guide/helper-array.md
@@ -53,7 +53,58 @@ Third optional argument is default value which is `null` if not specified. Could
$username = ArrayHelper::getValue($comment, 'user.username', 'Unknown');
```
-In case you want to get the value and then immediately remove it from array you can use `remove` method:
+
+## Setting values
+
+```php
+$array = [
+ 'key' => [
+ 'in' => ['k' => 'value']
+ ]
+];
+
+ArrayHelper::setValue($array, 'key.in', ['arr' => 'val']);
+// the path to write the value in `$array` can be specified as an array
+ArrayHelper::setValue($array, ['key', 'in'], ['arr' => 'val']);
+```
+
+As a result, initial value of `$array['key']['in']` will be overwritten by new value
+
+```php
+[
+ 'key' => [
+ 'in' => ['arr' => 'val']
+ ]
+]
+```
+
+If the path contains a nonexistent key, it will be created
+
+```php
+// if `$array['key']['in']['arr0']` is not empty, the value will be added to the array
+ArrayHelper::setValue($array, 'key.in.arr0.arr1', 'val');
+
+// if you want to completely override the value `$array['key']['in']['arr0']`
+ArrayHelper::setValue($array, 'key.in.arr0', ['arr1' => 'val']);
+```
+
+The result will be
+
+```php
+[
+ 'key' => [
+ 'in' => [
+ 'k' => 'value',
+ 'arr0' => ['arr1' => 'val']
+ ]
+ ]
+]
+```
+
+
+## Take a value from an array
+
+In case you want to get a value and then immediately remove it from an array you can use `remove` method:
```php
$array = ['type' => 'A', 'options' => [1, 2]];
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 651ec0afed..83b0b3eb0c 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -30,6 +30,7 @@ Yii Framework 2 Change Log
- Bug #14341: Fixed regression in error handling introduced by fixing #14264 (samdark)
- Enh #13378: Added skipOnEmpty option to SluggableBehaviour (andrewnester)
- Enh #14389: Optimize `Validator::validateAttributes()` by calling `attributeNames()` only once (nicdnep)
+- Enh #6644: Added `yii\helpers\ArrayHelper::setValue()` (LAV45)
- Enh #14105: Implemented a solution for retrieving DBMS constraints in `yii\db\Schema` (sergeymakinen)
- Enh #14417: Added configuration for headers in PHP files generated by `message/extract` command (rob006)
- Enh #13835: Added `yii\web\Request::getOrigin()` method that returns HTTP_ORIGIN of current CORS request (yyxx9988)
diff --git a/framework/helpers/BaseArrayHelper.php b/framework/helpers/BaseArrayHelper.php
index 0fcae0c7ad..27db4b1b13 100644
--- a/framework/helpers/BaseArrayHelper.php
+++ b/framework/helpers/BaseArrayHelper.php
@@ -214,6 +214,82 @@ class BaseArrayHelper
return $default;
}
+ /**
+ * Writes a value into an associative array at the key path specified.
+ * If there is no such key path yet, it will be created recursively.
+ * If the key exists, it will be overwritten.
+ *
+ * ```php
+ * $array = [
+ * 'key' => [
+ * 'in' => [
+ * 'val1',
+ * 'key' => 'val'
+ * ]
+ * ]
+ * ];
+ * ```
+ *
+ * The result of `ArrayHelper::setValue($array, 'key.in.0', ['arr' => 'val']);` will be the following:
+ *
+ * ```php
+ * [
+ * 'key' => [
+ * 'in' => [
+ * ['arr' => 'val'],
+ * 'key' => 'val'
+ * ]
+ * ]
+ * ]
+ *
+ * ```
+ *
+ * The result of
+ * `ArrayHelper::setValue($array, 'key.in', ['arr' => 'val']);` or
+ * `ArrayHelper::setValue($array, ['key', 'in'], ['arr' => 'val']);`
+ * will be the following:
+ *
+ * ```php
+ * [
+ * 'key' => [
+ * 'in' => [
+ * 'arr' => 'val'
+ * ]
+ * ]
+ * ]
+ * ```
+ *
+ * @param array $array the array to write the value to
+ * @param string|array|null $path the path of where do you want to write a value to `$array`
+ * the path can be described by a string when each key should be separated by a dot
+ * you can also describe the path as an array of keys
+ * if the path is null then `$array` will be assigned the `$value`
+ * @param mixed $value the value to be written
+ * @since 2.0.13
+ */
+ public static function setValue(&$array, $path, $value)
+ {
+ if ($path === null) {
+ $array = $value;
+ return;
+ }
+
+ $keys = is_array($path) ? $path : explode('.', $path);
+
+ while (count($keys) > 1) {
+ $key = array_shift($keys);
+ if (!isset($array[$key])) {
+ $array[$key] = [];
+ }
+ if (!is_array($array[$key])) {
+ $array[$key] = [$array[$key]];
+ }
+ $array = &$array[$key];
+ }
+
+ $array[array_shift($keys)] = $value;
+ }
+
/**
* Removes an item from an array and returns the value. If the key does not exist in the array, the default value
* will be returned instead.
diff --git a/tests/framework/helpers/ArrayHelperTest.php b/tests/framework/helpers/ArrayHelperTest.php
index fb2cc57848..33b203c5cc 100644
--- a/tests/framework/helpers/ArrayHelperTest.php
+++ b/tests/framework/helpers/ArrayHelperTest.php
@@ -821,6 +821,246 @@ class ArrayHelperTest extends TestCase
$this->assertEquals(23, ArrayHelper::getValue($arrayObject, 'nonExisting'));
}
+ /**
+ * Data provider for [[testSetValue()]]
+ * @return array test data
+ */
+ public function dataProviderSetValue()
+ {
+ return [
+ [
+ [
+ 'key1' => 'val1',
+ 'key2' => 'val2',
+ ],
+ 'key', 'val',
+ [
+ 'key1' => 'val1',
+ 'key2' => 'val2',
+ 'key' => 'val',
+ ],
+ ],
+ [
+ [
+ 'key1' => 'val1',
+ 'key2' => 'val2',
+ ],
+ 'key2', 'val',
+ [
+ 'key1' => 'val1',
+ 'key2' => 'val',
+ ],
+ ],
+
+ [
+ [
+ 'key1' => 'val1',
+ ],
+ 'key.in', 'val',
+ [
+ 'key1' => 'val1',
+ 'key' => ['in' => 'val'],
+ ],
+ ],
+ [
+ [
+ 'key' => 'val1',
+ ],
+ 'key.in', 'val',
+ [
+ 'key' => [
+ 'val1',
+ 'in' => 'val'
+ ],
+ ],
+ ],
+ [
+ [
+ 'key' => 'val1',
+ ],
+ 'key', ['in' => 'val'],
+ [
+ 'key' => ['in' => 'val'],
+ ],
+ ],
+
+ [
+ [
+ 'key1' => 'val1',
+ ],
+ 'key.in.0', 'val',
+ [
+ 'key1' => 'val1',
+ 'key' => [
+ 'in' => ['val']
+ ],
+ ],
+ ],
+
+ [
+ [
+ 'key1' => 'val1',
+ ],
+ 'key.in.arr', 'val',
+ [
+ 'key1' => 'val1',
+ 'key' => [
+ 'in' => [
+ 'arr' => 'val'
+ ]
+ ],
+ ],
+ ],
+ [
+ [
+ 'key1' => 'val1',
+ ],
+ 'key.in.arr', ['val'],
+ [
+ 'key1' => 'val1',
+ 'key' => [
+ 'in' => [
+ 'arr' => ['val']
+ ]
+ ],
+ ],
+ ],
+ [
+ [
+ 'key' => [
+ 'in' => ['val1']
+ ],
+ ],
+ 'key.in.arr', 'val',
+ [
+ 'key' => [
+ 'in' => [
+ 'val1',
+ 'arr' => 'val'
+ ]
+ ],
+ ],
+ ],
+
+ [
+ [
+ 'key' => ['in' => 'val1'],
+ ],
+ 'key.in.arr', ['val'],
+ [
+ 'key' => [
+ 'in' => [
+ 'val1',
+ 'arr' => ['val']
+ ]
+ ],
+ ],
+ ],
+ [
+ [
+ 'key' => [
+ 'in' => [
+ 'val1',
+ 'key' => 'val'
+ ]
+ ],
+ ],
+ 'key.in.0', ['arr' => 'val'],
+ [
+ 'key' => [
+ 'in' => [
+ ['arr' => 'val'],
+ 'key' => 'val'
+ ]
+ ],
+ ],
+ ],
+ [
+ [
+ 'key' => [
+ 'in' => [
+ 'val1',
+ 'key' => 'val'
+ ]
+ ],
+ ],
+ 'key.in', ['arr' => 'val'],
+ [
+ 'key' => [
+ 'in' => ['arr' => 'val']
+ ],
+ ],
+ ],
+ [
+ [
+ 'key' => [
+ 'in' => [
+ 'key' => 'val',
+ 'data' => [
+ 'attr1',
+ 'attr2',
+ 'attr3',
+ ]
+ ]
+ ],
+ ],
+ 'key.in.schema', 'array',
+ [
+ 'key' => [
+ 'in' => [
+ 'key' => 'val',
+ 'schema' => 'array',
+ 'data' => [
+ 'attr1',
+ 'attr2',
+ 'attr3',
+ ]
+ ]
+ ],
+ ],
+ ],
+ [
+ [
+ 'key' => [
+ 'in.array' => [
+ 'key' => 'val',
+ ]
+ ],
+ ],
+ ['key', 'in.array', 'ok.schema'], 'array',
+ [
+ 'key' => [
+ 'in.array' => [
+ 'key' => 'val',
+ 'ok.schema' => 'array',
+ ]
+ ],
+ ],
+ ],
+ [
+ [
+ 'key' => ['val'],
+ ],
+ null, 'data',
+ 'data',
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderSetValue
+ *
+ * @param array $array_input
+ * @param string|array|null $key
+ * @param mixed $value
+ * @param mixed $expected
+ */
+ public function testSetValue($array_input, $key, $value, $expected)
+ {
+ ArrayHelper::setValue($array_input, $key, $value);
+ $this->assertEquals($expected, $array_input);
+ }
+
public function testIsAssociative()
{
$this->assertFalse(ArrayHelper::isAssociative('test'));