Unverified Commit 9c84eca9 authored by Mark Halliwell's avatar Mark Halliwell
Browse files

Issue #3085493 by markcarver, AaronMcHale: "Notice: Array to string...


Issue #3085493 by markcarver, AaronMcHale: "Notice: Array to string conversion" when form element or field has a suffix and a Ajax callback

Signed-off-by: default avatarMark Carver <mark.carver@me.com>
parent 859a30a4
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -84,6 +84,27 @@ function hook_bootstrap_inline_element_types(array &$types) {
  }
}

/**
 * Allows sub-themes to alter which element properties should be strings.
 *
 * Note: this only applies with using \Drupal\bootstrap\Utility\Element objects.
 * This is primarily used in cases where the value of the property in
 * question is only supported as an already rendered string and not a render
 * array (i.e. prefix/suffix) due to the limitations of upstream (core) code.
 * It should not be relied on as a way to "easily convert" properties to
 * strings to circumvent supporting OOP code.
 *
 * @param array $properties
 *   An indexed array of property names, without the # prefix, passed by
 *   reference.
 *
 * @see \Drupal\bootstrap\Utility\Element::isStringProperty
 */
function hook_element_string_properties_alter(array &$properties) {
  // A contrib module property that should always be a string.
  $properties[] = 'contrib_module_property';
}

/**
 * @} End of "addtogroup".
 */
+86 −8
Original line number Diff line number Diff line
@@ -18,6 +18,15 @@ use Drupal\Core\Render\Element as CoreElement;
 */
class Element extends DrupalAttributes {

  /**
   * A list of property names whose values should be treated as strings.
   *
   * @var array
   *
   * @see \Drupal\bootstrap\Utility\Element::isStringProperty
   */
  protected static $propertyStrings;

  /**
   * The current state of the form.
   *
@@ -153,20 +162,30 @@ class Element extends DrupalAttributes {
   *   The name of the property to set.
   * @param mixed $value
   *   The value of the property to set.
   * @param bool $forceString
   *   Optional. Forces the value to be appended as a string. By default, this
   *   is automatically determined based on the property name provided. Manually
   *   passing TRUE or FALSE will override any automatic determination.
   *
   * @return static
   *
   * @see \Drupal\bootstrap\Utility\Element::isStringProperty
   */
  public function appendProperty($name, $value) {
  public function appendProperty($name, $value, $forceString = NULL) {
    $property = &$this->getProperty($name);
    $element = Element::create($value);

    if (!isset($forceString)) {
      $forceString = static::isStringProperty($name);
    }

    // If property isn't set, just set it.
    if (!isset($property)) {
      $property = $element->getArray();
      $property = $forceString ? (string) $element->renderPlain() : $element->getArray();
      return $this;
    }

    if (is_array($property)) {
    if (!$forceString && is_array($property)) {
      $property[] = $element->getArray();
    }
    else {
@@ -482,6 +501,40 @@ class Element extends DrupalAttributes {
      isset($value['#post_render']) || isset($value['#process']));
  }

  /**
   * Checks whether a property's value should always be treated as a string.
   *
   * This is primarily used in cases where the value of the property in
   * question is only supported as an already rendered string and not a render
   * array (i.e. prefix/suffix) due to the limitations of upstream (core) code.
   * It should not be relied on as a way to "easily convert" properties to
   * strings to circumvent supporting OOP code.
   *
   * @param string $name
   *   The property name to check.
   *
   * @return bool
   *   TRUE or FALSE
   *
   * @see hook_element_string_properties_alter
   * @see \Drupal\bootstrap\Utility\Element::appendProperty
   * @see \Drupal\bootstrap\Utility\Element::prependProperty
   */
  public static function isStringProperty($name) {
    if (!isset(static::$propertyStrings)) {
      $cache = Bootstrap::getTheme()->getCache('element_string_properties');
      if (!(static::$propertyStrings = $cache->getAll())) {
        $properties = ['field_prefix', 'field_suffix', 'prefix', 'suffix'];
        \Drupal::theme()->alter('bootstrap_element_string_properties', $properties);
        static::$propertyStrings = array_map(function ($property) {
          return $property[0] === '#' ? substr($property, 1) : $property;
        }, $properties);
        $cache->setMultiple(static::$propertyStrings);
      }
    }
    return in_array($name[0] === '#' ? substr($name, 1) : $name, static::$propertyStrings, TRUE);
  }

  /**
   * Checks if the element is a specific type of element.
   *
@@ -530,20 +583,30 @@ class Element extends DrupalAttributes {
   *   The name of the property to set.
   * @param mixed $value
   *   The value of the property to set.
   * @param bool $forceString
   *   Optional. Forces the value to be appended as a string. By default, this
   *   is automatically determined based on the property name provided. Manually
   *   passing TRUE or FALSE will override any automatic determination.
   *
   * @return static
   *
   * @see \Drupal\bootstrap\Utility\Element::isStringProperty
   */
  public function prependProperty($name, $value) {
  public function prependProperty($name, $value, $forceString = NULL) {
    $property = &$this->getProperty($name);
    $element = Element::create($value);

    if (!isset($forceString)) {
      $forceString = static::isStringProperty($name);
    }

    // If property isn't set, just set it.
    if (!isset($property)) {
      $property = $element->getArray();
      $property = $forceString ? (string) $element->renderPlain() : $element->getArray();
      return $this;
    }

    if (is_array($property)) {
    if (!$forceString && is_array($property)) {
      array_unshift($property, $element->getArray());
    }
    else {
@@ -730,11 +793,26 @@ class Element extends DrupalAttributes {
   *   The value of the property to set.
   * @param bool $recurse
   *   Flag indicating wither to set the same property on child elements.
   * @param bool $forceString
   *   Optional. Forces the value to be appended as a string. By default, this
   *   is automatically determined based on the property name provided. Manually
   *   passing TRUE or FALSE will override any automatic determination.
   *
   * @return static
   *
   * @see \Drupal\bootstrap\Utility\Element::isStringProperty
   */
  public function setProperty($name, $value, $recurse = FALSE) {
  public function setProperty($name, $value, $recurse = FALSE, $forceString = NULL) {
    if (!isset($forceString)) {
      $forceString = static::isStringProperty($name);
    }

    if ($forceString) {
      $this->array["#$name"] = (string) Element::create($value)->renderPlain();
    }
    else {
      $this->array["#$name"] = $value instanceof Element ? $value->getArray() : $value;
    }
    if ($recurse) {
      foreach ($this->children() as $child) {
        $child->setProperty($name, $value, $recurse);