Commit 7cd1f96d authored by catch's avatar catch
Browse files

Issue #1397246 by webchick, heyrocker, sun: Add drupal_array_unset_nested_value().

parent 1878eb15
......@@ -6447,6 +6447,78 @@ function element_set_attributes(array &$element, array $map) {
}
}
/**
* Retrieves a value from a nested array with variable depth.
*
* This helper function should be used when the depth of the array element being
* retrieved may vary (that is, the number of parent keys is variable). It is
* primarily used for form structures and renderable arrays.
*
* Without this helper function the only way to get a nested array value with
* variable depth in one line would be using eval(), which should be avoided:
* @code
* // Do not do this! Avoid eval().
* // May also throw a PHP notice, if the variable array keys do not exist.
* eval('$value = $array[\'' . implode("']['", $parents) . "'];");
* @endcode
*
* Instead, use this helper function:
* @code
* $value = drupal_array_get_nested_value($form, $parents);
* @endcode
*
* The return value will be NULL, regardless of whether the actual value is NULL
* or whether the requested key does not exist. If it is required to know
* whether the nested array key actually exists, pass a third argument that is
* altered by reference:
* @code
* $key_exists = NULL;
* $value = drupal_array_get_nested_value($form, $parents, $key_exists);
* if ($key_exists) {
* // ... do something with $value ...
* }
* @endcode
*
* However if the number of array parent keys is static, the value should always
* be retrieved directly rather than calling this function. For instance:
* @code
* $value = $form['signature_settings']['signature'];
* @endcode
*
* @param $array
* The array from which to get the value.
* @param $parents
* An array of parent keys of the value, starting with the outermost key.
* @param $key_exists
* (optional) If given, an already defined variable that is altered by
* reference.
*
* @return
* The requested nested value. Possibly NULL if the value is NULL or not all
* nested parent keys exist. $key_exists is altered by reference and is a
* Boolean that indicates whether all nested parent keys exist (TRUE) or not
* (FALSE). This allows to distinguish between the two possibilities when NULL
* is returned.
*
* @see drupal_array_set_nested_value()
* @see drupal_array_unset_nested_value()
*/
function &drupal_array_get_nested_value(array &$array, array $parents, &$key_exists = NULL) {
$ref = &$array;
foreach ($parents as $parent) {
if (is_array($ref) && array_key_exists($parent, $ref)) {
$ref = &$ref[$parent];
}
else {
$key_exists = FALSE;
$null = NULL;
return $null;
}
}
$key_exists = TRUE;
return $ref;
}
/**
* Sets a value in a nested array with variable depth.
*
......@@ -6505,6 +6577,7 @@ function element_set_attributes(array &$element, array $map) {
* FALSE, PHP throws an error if trying to add into a value that is not an
* array. Defaults to FALSE.
*
* @see drupal_array_unset_nested_value()
* @see drupal_array_get_nested_value()
*/
function drupal_array_set_nested_value(array &$array, array $parents, $value, $force = FALSE) {
......@@ -6521,74 +6594,73 @@ function drupal_array_set_nested_value(array &$array, array $parents, $value, $f
}
/**
* Retrieves a value from a nested array with variable depth.
* Unsets a value in a nested array with variable depth.
*
* This helper function should be used when the depth of the array element being
* retrieved may vary (that is, the number of parent keys is variable). It is
* primarily used for form structures and renderable arrays.
* This helper function should be used when the depth of the array element you
* are changing may vary (that is, the number of parent keys is variable). It
* is primarily used for form structures and renderable arrays.
*
* Without this helper function the only way to get a nested array value with
* variable depth in one line would be using eval(), which should be avoided:
* Example:
* @code
* // Do not do this! Avoid eval().
* // May also throw a PHP notice, if the variable array keys do not exist.
* eval('$value = $array[\'' . implode("']['", $parents) . "'];");
* // Assume you have a 'signature' element somewhere in a form. It might be:
* $form['signature_settings']['signature'] = array(
* '#type' => 'text_format',
* '#title' => t('Signature'),
* );
* // Or, it might be further nested:
* $form['signature_settings']['user']['signature'] = array(
* '#type' => 'text_format',
* '#title' => t('Signature'),
* );
* @endcode
*
* Instead, use this helper function:
* To deal with the situation, the code needs to figure out the route to the
* element, given an array of parents that is either
* @code array('signature_settings', 'signature') @endcode in the first case or
* @code array('signature_settings', 'user', 'signature') @endcode in the second
* case.
*
* Without this helper function the only way to unset the signature element in
* one line would be using eval(), which should be avoided:
* @code
* $value = drupal_array_get_nested_value($form, $parents);
* // Do not do this! Avoid eval().
* eval('unset($form[\'' . implode("']['", $parents) . '\']);');
* @endcode
*
* The return value will be NULL, regardless of whether the actual value is NULL
* or whether the requested key does not exist. If it is required to know
* whether the nested array key actually exists, pass a third argument that is
* altered by reference:
* Instead, use this helper function:
* @code
* $key_exists = NULL;
* $value = drupal_array_get_nested_value($form, $parents, $key_exists);
* if ($key_exists) {
* // ... do something with $value ...
* }
* drupal_array_unset_nested_value($form, $parents, $element);
* @endcode
*
* However if the number of array parent keys is static, the value should always
* be retrieved directly rather than calling this function. For instance:
* be set directly rather than calling this function. For instance, for the
* first example we could just do:
* @code
* $value = $form['signature_settings']['signature'];
* unset($form['signature_settings']['signature']);
* @endcode
*
* @param $array
* The array from which to get the value.
* A reference to the array to modify.
* @param $parents
* An array of parent keys of the value, starting with the outermost key.
* @param $key_exists
* An array of parent keys, starting with the outermost key and including the
* key to be unset.
* @param $key_existed
* (optional) If given, an already defined variable that is altered by
* reference.
*
* @return
* The requested nested value. Possibly NULL if the value is NULL or not all
* nested parent keys exist. $key_exists is altered by reference and is a
* Boolean that indicates whether all nested parent keys exist (TRUE) or not
* (FALSE). This allows to distinguish between the two possibilities when NULL
* is returned.
*
* @see drupal_array_set_nested_value()
* @see drupal_array_get_nested_value()
*/
function &drupal_array_get_nested_value(array &$array, array $parents, &$key_exists = NULL) {
$ref = &$array;
foreach ($parents as $parent) {
if (is_array($ref) && array_key_exists($parent, $ref)) {
$ref = &$ref[$parent];
}
else {
$key_exists = FALSE;
$null = NULL;
return $null;
}
function drupal_array_unset_nested_value(array &$array, array $parents, &$key_existed = NULL) {
$unset_key = array_pop($parents);
$ref = &drupal_array_get_nested_value($array, $parents, $key_existed);
if ($key_existed && is_array($ref) && array_key_exists($unset_key, $ref)) {
$key_existed = TRUE;
unset($ref[$unset_key]);
}
else {
$key_existed = FALSE;
}
$key_exists = TRUE;
return $ref;
}
/**
......
......@@ -2411,6 +2411,118 @@ class DrupalAttributesUnitTest extends DrupalUnitTestCase {
}
}
/**
* Tests the various drupal_array_* helper functions.
*/
class CommonDrupalArrayUnitTest extends DrupalUnitTestCase {
/**
* Form array to check.
*/
protected $form;
/**
* Array of parents for the nested element.
*/
protected $parents;
public static function getInfo() {
return array(
'name' => 'drupal_array_*() tests',
'description' => 'Tests the various drupal_array_* helper functions.',
'group' => 'System',
);
}
function setUp() {
parent::setUp();
// Create a form structure with a nested element.
$this->form['fieldset']['element'] = array(
'#value' => 'Nested element',
);
// Set up parent array.
$this->parents = array('fieldset', 'element');
}
/**
* Tests getting nested array values.
*/
function testGet() {
// Verify getting a value of a nested element.
$value = drupal_array_get_nested_value($this->form, $this->parents);
$this->assertEqual($value['#value'], 'Nested element', 'Nested element value found.');
// Verify changing a value of a nested element by reference.
$value = &drupal_array_get_nested_value($this->form, $this->parents);
$value['#value'] = 'New value';
$value = drupal_array_get_nested_value($this->form, $this->parents);
$this->assertEqual($value['#value'], 'New value', 'Nested element value was changed by reference.');
$this->assertEqual($this->form['fieldset']['element']['#value'], 'New value', 'Nested element value was changed by reference.');
// Verify that an existing key is reported back.
$key_exists = NULL;
drupal_array_get_nested_value($this->form, $this->parents, $key_exists);
$this->assertIdentical($key_exists, TRUE, 'Existing key found.');
// Verify that a non-existing key is reported back and throws no errors.
$key_exists = NULL;
$parents = $this->parents;
$parents[] = 'foo';
drupal_array_get_nested_value($this->form, $parents, $key_exists);
$this->assertIdentical($key_exists, FALSE, 'Non-existing key not found.');
}
/**
* Tests setting nested array values.
*/
function testSet() {
$new_value = array(
'#value' => 'New value',
'#required' => TRUE,
);
// Verify setting the value of a nested element.
drupal_array_set_nested_value($this->form, $this->parents, $new_value);
$this->assertEqual($this->form['fieldset']['element']['#value'], 'New value', 'Changed nested element value found.');
$this->assertIdentical($this->form['fieldset']['element']['#required'], TRUE, 'New nested element value found.');
}
/**
* Tests unsetting nested array values.
*/
function testUnset() {
// Verify unsetting a non-existing nested element throws no errors and the
// non-existing key is properly reported.
$key_existed = NULL;
$parents = $this->parents;
$parents[] = 'foo';
drupal_array_unset_nested_value($this->form, $parents, $key_existed);
$this->assertTrue(isset($this->form['fieldset']['element']['#value']), 'Outermost nested element key still exists.');
$this->assertIdentical($key_existed, FALSE, 'Non-existing key not found.');
// Verify unsetting a nested element.
$key_existed = NULL;
drupal_array_unset_nested_value($this->form, $this->parents, $key_existed);
$this->assertFalse(isset($this->form['fieldset']['element']), 'Removed nested element not found.');
$this->assertIdentical($key_existed, TRUE, 'Existing key was found.');
}
/**
* Tests existence of array key.
*/
function testKeyExists() {
// Verify that existing key is found.
$this->assertIdentical(drupal_array_nested_key_exists($this->form, $this->parents), TRUE, 'Nested key found.');
// Verify that non-existing keys are not found.
$parents = $this->parents;
$parents[] = 'foo';
$this->assertIdentical(drupal_array_nested_key_exists($this->form, $parents), FALSE, 'Non-existing nested key not found.');
}
}
/**
* Tests converting PHP variables to JSON strings and back.
*/
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment