Fix array value accessing non existent properties on an ArrayAccess object
authorRich Lott / Artful Robot <forums@artfulrobot.uk>
Tue, 10 Mar 2020 12:19:56 +0000 (12:19 +0000)
committerRich Lott / Artful Robot <forums@artfulrobot.uk>
Tue, 10 Mar 2020 12:19:56 +0000 (12:19 +0000)
CRM/Utils/Array.php
Civi/Payment/PropertyBag.php

index 5c6b1ac9230b12699b0c29f907b8cf0137ba9089..1a3d8a002072b1cab93d60e1a9bf724d69bb6718 100644 (file)
@@ -39,7 +39,8 @@ class CRM_Utils_Array {
       return array_key_exists($key, $list) ? $list[$key] : $default;
     }
     if ($list instanceof ArrayAccess) {
-      return $list[$key] ?? $default;
+      // ArrayAccess requires offsetExists is implemented for the equivalent to array_key_exists.
+      return $list->offsetExists($key) ? $list[$key] : $default;
     }
     // @todo - eliminate these from core & uncomment this line.
     // CRM_Core_Error::deprecatedFunctionWarning('You have passed an invalid parameter for the "list"');
index 6cea55241e7ec38f40ffe0c4bf99e3b59f018e1f..377e70ef7b504a242b0ecddcdb788aeb0c7c0017 100644 (file)
@@ -106,8 +106,8 @@ class PropertyBag implements \ArrayAccess {
    * @return bool TRUE if we have that value (on our default store)
    */
   public function offsetExists ($offset): bool {
-    $prop = $this->handleLegacyPropNames($offset);
-    return isset($this->props['default'][$prop]);
+    $prop = $this->handleLegacyPropNames($offset, TRUE);
+    return $prop && isset($this->props['default'][$prop]);
   }
 
   /**
@@ -199,16 +199,21 @@ class PropertyBag implements \ArrayAccess {
 
   /**
    * @param string $prop
+   * @param bool $silent if TRUE return NULL instead of throwing an exception. This is because offsetExists should be safe and not throw exceptions.
    * @return string canonical name.
    * @throws \InvalidArgumentException if prop name not known.
    */
-  protected function handleLegacyPropNames($prop) {
+  protected function handleLegacyPropNames($prop, $silent = FALSE) {
     $newName = static::$propMap[$prop] ?? NULL;
     if ($newName === TRUE) {
       // Good, modern name.
       return $prop;
     }
     if ($newName === NULL) {
+      if ($silent) {
+        // Only for use by offsetExists
+        return;
+      }
       throw new \InvalidArgumentException("Unknown property '$prop'.");
     }
     // Remaining case is legacy name that's been translated.