use InvalidArgumentException;
use Civi;
+use CRM_Core_Error;
use CRM_Core_PseudoConstant;
/**
*
*/
class PropertyBag implements \ArrayAccess {
- /**
- * @var array
- * - see legacyWarning
- */
- public static $legacyWarnings = [];
protected $props = ['default' => []];
'isNotifyProcessorOnCancelRecur' => TRUE,
];
+
+ /**
+ * @var bool
+ * Temporary, internal variable to help ease transition to PropertyBag.
+ * Used by cast() to suppress legacy warnings.
+ */
+ protected $suppressLegacyWarnings = FALSE;
+
/**
* Get the property bag.
*
*/
public function offsetExists ($offset): bool {
$prop = $this->handleLegacyPropNames($offset, TRUE);
- return $prop && isset($this->props['default'][$prop]);
+ // If there's no prop, assume it's a custom property.
+ $prop = $prop ?? $offset;
+ return array_key_exists($prop, $this->props['default']);
}
/**
* @param mixed $offset
* @return mixed
*/
- public function offsetGet ($offset) {
- $prop = $this->handleLegacyPropNames($offset);
+ public function offsetGet($offset) {
+ try {
+ $prop = $this->handleLegacyPropNames($offset);
+ }
+ catch (InvalidArgumentException $e) {
+
+ 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');
+ }
+ catch (BadMethodCallException $e) {
+ CRM_Core_Error::deprecatedFunctionWarning(
+ "proper setCustomProperty('$offset', \$value) to store the value (since it is not a core value), then access it with getCustomProperty('$offset'). NULL is returned but in future an exception will be thrown."
+ . $e->getMessage(),
+ "PropertyBag array access to get unset property '$offset'"
+ );
+ return NULL;
+ }
+ }
+
+ CRM_Core_Error::deprecatedFunctionWarning(
+ "get" . ucfirst($offset) . "()",
+ "PropertyBag array access for core property '$offset'"
+ );
return $this->get($prop, 'default');
}
// This is fine if it's something particular to a payment processor
// (which should be using setCustomProperty) however it could also lead to
// things like 'my_weirly_named_contact_id'.
- $this->legacyWarning($e->getMessage() . " We have merged this in for now as a custom property. Please rewrite your code to use PropertyBag->setCustomProperty if it is a genuinely custom property, or a standardised setter like PropertyBag->setContactID for standard properties");
+ //
+ // From 5.28 we suppress this when using PropertyBag::cast() to ease transition.
+ if (!$this->suppressLegacyWarnings) {
+ CRM_Core_Error::deprecatedFunctionWarning(
+ "proper setCustomProperty('$offset', \$value) for non-core properties. "
+ . $e->getMessage(),
+ "PropertyBag array access to set '$offset'"
+ );
+ }
$this->setCustomProperty($offset, $value, 'default');
return;
}
// These lines are here (and not in try block) because the catch must only
// catch the case when the prop is custom.
$setter = 'set' . ucfirst($prop);
+ if (!$this->suppressLegacyWarnings) {
+ CRM_Core_Error::deprecatedFunctionWarning(
+ "$setter()",
+ "PropertyBag array access to set core property '$offset'"
+ );
+ }
$this->$setter($value, 'default');
}
unset($this->props['default'][$prop]);
}
- /**
- * Log legacy warnings info.
- *
- * @param string $message
- */
- protected function legacyWarning($message) {
- if (empty(static::$legacyWarnings)) {
- // First time we have been called.
- register_shutdown_function([PropertyBag::class, 'writeLegacyWarnings']);
- }
- // Store warnings instead of logging immediately, as calls to Civi::log()
- // can take over half a second to work in some hosting environments.
- static::$legacyWarnings[$message] = TRUE;
-
- // For unit tests:
- $this->lastWarning = $message;
- }
-
/**
* Save any legacy warnings to log.
*
throw new \InvalidArgumentException("Unknown property '$prop'.");
}
// Remaining case is legacy name that's been translated.
- $this->legacyWarning("We have translated '$prop' to '$newName' for you, but please update your code to use the propper setters and getters.");
+ if (!$this->suppressLegacyWarnings) {
+ CRM_Core_Error::deprecatedFunctionWarning("Canonical property name '$newName'", "Legacy property name '$prop'");
+ }
+
return $newName;
}
* @return mixed
*/
protected function get($prop, $label) {
- if (array_key_exists($prop, $this->props['default'])) {
+ if (array_key_exists($prop, $this->props[$label] ?? [])) {
return $this->props[$label][$prop];
}
throw new \BadMethodCallException("Property '$prop' has not been set.");
/**
* This is used to merge values from an array.
- * It's a transitional function and should not be used!
+ * It's a transitional, internal function and should not be used!
*
* @param array $data
*/
public function mergeLegacyInputParams($data) {
+ // 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;
foreach ($data as $key => $value) {
if ($value !== NULL && $value !== '') {
$this->offsetSet($key, $value);
}
}
+ $this->suppressLegacyWarnings = FALSE;
}
/**
if (isset(static::$propMap[$prop])) {
throw new \InvalidArgumentException("Attempted to get '$prop' via getCustomProperty - must use using its getter.");
}
+
+ if (!array_key_exists($prop, $this->props[$label] ?? [])) {
+ throw new \BadMethodCallException("Property '$prop' has not been set.");
+ }
return $this->props[$label][$prop] ?? NULL;
}