CRM_Utils_Array - All `pathGet()`-like methods will accept empty-path
authorTim Otten <totten@civicrm.org>
Tue, 5 Jul 2022 23:23:36 +0000 (16:23 -0700)
committerTim Otten <totten@civicrm.org>
Tue, 5 Jul 2022 23:23:36 +0000 (16:23 -0700)
These two are equivalent:

  (A) $data['sub1']['sub2']['sub3']
  (B) CRM_Utils_Array::pathGet($data, ['sub1', 'sub2', 'sub3'])

But what if the list of subpaths is empty -- ie you're accessing the top-level item?

  (A) $data
  (B) CRM_Utils_Array::pathGet($data, [])

It worked correctly on `pathGet()`, but accessing the top-level using a
related methods (eg `pathSet()`) would generate a PHP error.

This commit ensures that related methods can be used to check/update the
top-level item (ie path===[]).

Note that there is some decent test-coverage already for the regular
scenarios (eg `testGetSetPathParts()`).

CRM/Utils/Array.php
tests/phpunit/CRM/Utils/ArrayTest.php

index 627e27d0c1c4041383bb3dad7a6c3f175ad66b54..12822ae35769bda160e6902831b74e1f96793b29 100644 (file)
@@ -1077,6 +1077,9 @@ class CRM_Utils_Array {
    * @return bool
    */
   public static function pathIsset($values, $path) {
+    if ($path === []) {
+      return ($values !== NULL);
+    }
     foreach ($path as $key) {
       if (!is_array($values) || !isset($values[$key])) {
         return FALSE;
@@ -1100,6 +1103,11 @@ class CRM_Utils_Array {
    *   TRUE if anything has been removed. FALSE if no changes were required.
    */
   public static function pathUnset(&$values, $path, $cleanup = FALSE) {
+    if (count($path) === 0) {
+      $values = NULL;
+      return TRUE;
+    }
+
     if (count($path) === 1) {
       if (isset($values[$path[0]])) {
         unset($values[$path[0]]);
@@ -1131,6 +1139,10 @@ class CRM_Utils_Array {
    *   Ex: 456.
    */
   public static function pathSet(&$values, $pathParts, $value) {
+    if ($pathParts === []) {
+      $values = $value;
+      return;
+    }
     $r = &$values;
     $last = array_pop($pathParts);
     foreach ($pathParts as $part) {
index 17cc44dfbd329697bbde16ae8b5a84a99db841e1..42715789d20998937ba57576f153a03118872c7c 100644 (file)
@@ -207,6 +207,30 @@ class CRM_Utils_ArrayTest extends CiviUnitTestCase {
 
   }
 
+  public function testGetSet_EmptyPath() {
+    $emptyPath = [];
+
+    $x = 'hello';
+    $this->assertEquals(TRUE, CRM_Utils_Array::pathIsset($x, $emptyPath));
+    $this->assertEquals('hello', CRM_Utils_Array::pathGet($x, $emptyPath));
+    $this->assertEquals('hello', $x);
+
+    CRM_Utils_Array::pathSet($x, $emptyPath, 'bon jour');
+    $this->assertEquals(TRUE, CRM_Utils_Array::pathIsset($x, $emptyPath));
+    $this->assertEquals('bon jour', CRM_Utils_Array::pathGet($x, $emptyPath));
+    $this->assertEquals('bon jour', $x);
+
+    CRM_Utils_Array::pathUnset($x, $emptyPath);
+    $this->assertEquals(FALSE, CRM_Utils_Array::pathIsset($x, $emptyPath));
+    $this->assertEquals(NULL, CRM_Utils_Array::pathGet($x, $emptyPath));
+    $this->assertEquals(NULL, $x);
+
+    CRM_Utils_Array::pathSet($x, $emptyPath, 'buenos dias');
+    $this->assertEquals(TRUE, CRM_Utils_Array::pathIsset($x, $emptyPath));
+    $this->assertEquals('buenos dias', CRM_Utils_Array::pathGet($x, $emptyPath));
+    $this->assertEquals('buenos dias', $x);
+  }
+
   public function getSortExamples() {
     $red = ['label' => 'Red', 'id' => 1, 'weight' => '90'];
     $orange = ['label' => 'Orange', 'id' => 2, 'weight' => '70'];