Merge pull request #22579 from eileenmcnaughton/api4
[civicrm-core.git] / Civi / Payment / PropertyBag.php
index 06f96d42fa65fa1a0c276fc3b29b04b53290f853..24987057ef23e95069a66eb1f8a4c2d5ce9124eb 100644 (file)
@@ -25,13 +25,24 @@ class PropertyBag implements \ArrayAccess {
   protected $props = ['default' => []];
 
   protected static $propMap = [
+    'amount'                      => TRUE,
+    'total_amount'                => 'amount',
     'billingStreetAddress'        => TRUE,
+    'billing_street_address'      => 'billingStreetAddress',
+    'street_address'              => 'billingStreetAddress',
     'billingSupplementalAddress1' => TRUE,
     'billingSupplementalAddress2' => TRUE,
     'billingSupplementalAddress3' => TRUE,
     'billingCity'                 => TRUE,
+    'billing_city'                => 'billingCity',
+    'city'                        => 'billingCity',
     'billingPostalCode'           => TRUE,
+    'billing_postal_code'         => 'billingPostalCode',
+    'postal_code'                 => 'billingPostalCode',
     'billingCounty'               => TRUE,
+    'billingStateProvince'        => TRUE,
+    'billing_state_province'      => 'billingStateProvince',
+    'state_province'              => 'billingStateProvince',
     'billingCountry'              => TRUE,
     'contactID'                   => TRUE,
     'contact_id'                  => 'contactID',
@@ -62,6 +73,8 @@ class PropertyBag implements \ArrayAccess {
     'frequency_interval'          => 'recurFrequencyInterval',
     'recurFrequencyUnit'          => TRUE,
     'frequency_unit'              => 'recurFrequencyUnit',
+    'recurInstallments'           => TRUE,
+    'installments'                => 'recurInstallments',
     'subscriptionId'              => 'recurProcessorID',
     'recurProcessorID'            => TRUE,
     'transactionID'               => TRUE,
@@ -75,8 +88,29 @@ class PropertyBag implements \ArrayAccess {
    * @var bool
    * Temporary, internal variable to help ease transition to PropertyBag.
    * Used by cast() to suppress legacy warnings.
+   * For paymentprocessors that have not converted to propertyBag we need to support "legacy" properties - eg. "is_recur"
+   *   without warnings. Setting this allows us to pass a propertyBag into doPayment() and expect it to "work" with
+   *   existing payment processors.
    */
-  protected $suppressLegacyWarnings = FALSE;
+  protected $suppressLegacyWarnings = TRUE;
+
+  /**
+   * Get the value of the suppressLegacyWarnings parameter
+   * @return bool
+   */
+  public function getSuppressLegacyWarnings() {
+    return $this->suppressLegacyWarnings;
+  }
+
+  /**
+   * Set the suppressLegacyWarnings parameter - useful for unit tests.
+   * Eg. you could set to FALSE for unit tests on a paymentprocessor to capture use of legacy keys in that processor
+   * code.
+   * @param bool $suppressLegacyWarnings
+   */
+  public function setSuppressLegacyWarnings(bool $suppressLegacyWarnings) {
+    $this->suppressLegacyWarnings = $suppressLegacyWarnings;
+  }
 
   /**
    * Get the property bag.
@@ -129,11 +163,13 @@ class PropertyBag implements \ArrayAccess {
     }
     catch (InvalidArgumentException $e) {
 
-      CRM_Core_Error::deprecatedFunctionWarning(
-        "proper getCustomProperty('$offset') for non-core properties. "
-        . $e->getMessage(),
-        "PropertyBag array access to get '$offset'"
-      );
+      if (!$this->getSuppressLegacyWarnings()) {
+        CRM_Core_Error::deprecatedFunctionWarning(
+          "proper getCustomProperty('$offset') for non-core properties. "
+          . $e->getMessage(),
+          "PropertyBag array access to get '$offset'"
+        );
+      }
 
       try {
         return $this->getCustomProperty($offset, 'default');
@@ -148,10 +184,12 @@ class PropertyBag implements \ArrayAccess {
       }
     }
 
-    CRM_Core_Error::deprecatedFunctionWarning(
-      "get" . ucfirst($offset) . "()",
-      "PropertyBag array access for core property '$offset'"
-    );
+    if (!$this->getSuppressLegacyWarnings()) {
+      CRM_Core_Error::deprecatedFunctionWarning(
+        "get" . ucfirst($offset) . "()",
+        "PropertyBag array access for core property '$offset'"
+      );
+    }
     return $this->get($prop, 'default');
   }
 
@@ -230,18 +268,23 @@ class PropertyBag implements \ArrayAccess {
     if ($newName === NULL && substr($prop, -2) === '-' . \CRM_Core_BAO_LocationType::getBilling()
       && isset(static::$propMap[substr($prop, 0, -2)])
     ) {
-      $newName = substr($prop, 0, -2);
+      $billingAddressProp = substr($prop, 0, -2);
+      $newName = static::$propMap[$billingAddressProp] ?? NULL;
+      if ($newName === TRUE) {
+        // Good, modern name.
+        return $billingAddressProp;
+      }
     }
 
     if ($newName === NULL) {
       if ($silent) {
         // Only for use by offsetExists
-        return;
+        return NULL;
       }
       throw new \InvalidArgumentException("Unknown property '$prop'.");
     }
     // Remaining case is legacy name that's been translated.
-    if (!$this->suppressLegacyWarnings) {
+    if (!$this->getSuppressLegacyWarnings()) {
       CRM_Core_Error::deprecatedFunctionWarning("Canonical property name '$newName'", "Legacy property name '$prop'");
     }
 
@@ -317,13 +360,14 @@ class PropertyBag implements \ArrayAccess {
     // Suppress legacy warnings for merging an array of data as this
     // suits our migration plan at this moment. Future behaviour may differ.
     // @see https://github.com/civicrm/civicrm-core/pull/17643
-    $this->suppressLegacyWarnings = TRUE;
+    $suppressLegacyWarnings = $this->getSuppressLegacyWarnings();
+    $this->setSuppressLegacyWarnings(TRUE);
     foreach ($data as $key => $value) {
       if ($value !== NULL && $value !== '') {
         $this->offsetSet($key, $value);
       }
     }
-    $this->suppressLegacyWarnings = FALSE;
+    $this->setSuppressLegacyWarnings($suppressLegacyWarnings);
   }
 
   /**
@@ -562,6 +606,27 @@ class PropertyBag implements \ArrayAccess {
     return $this->set('billingCounty', $label, (string) $input);
   }
 
+  /**
+   * BillingStateProvince getter.
+   *
+   * @return string
+   */
+  public function getBillingStateProvince($label = 'default') {
+    return $this->get('billingStateProvince', $label);
+  }
+
+  /**
+   * BillingStateProvince setter.
+   *
+   * Nb. we can't validate this unless we have the country ID too, so we don't.
+   *
+   * @param string $input
+   * @param string $label e.g. 'default'
+   */
+  public function setBillingStateProvince($input, $label = 'default') {
+    return $this->set('billingStateProvince', $label, (string) $input);
+  }
+
   /**
    * BillingCountry getter.
    *
@@ -990,6 +1055,34 @@ class PropertyBag implements \ArrayAccess {
     return $this->set('recurFrequencyUnit', $label, $recurFrequencyUnit);
   }
 
+  /**
+   * @param string $label
+   *
+   * @return int
+   */
+  public function getRecurInstallments($label = 'default') {
+    return $this->get('recurInstallments', $label);
+  }
+
+  /**
+   * @param int $recurInstallments
+   * @param string $label
+   *
+   * @return \Civi\Payment\PropertyBag
+   * @throws \CRM_Core_Exception
+   */
+  public function setRecurInstallments($recurInstallments, $label = 'default') {
+    // Counts zero as positive which is ok - means no installments
+    try {
+      \CRM_Utils_Type::validate($recurInstallments, 'Positive');
+    }
+    catch (\CRM_Core_Exception $e) {
+      throw new InvalidArgumentException('recurInstallments must be 0 or a positive integer');
+    }
+
+    return $this->set('recurInstallments', $label, (int) $recurInstallments);
+  }
+
   /**
    * Set the unique payment processor service provided ID for a particular subscription.
    *