function HTMLRestrictions::doIntersect

Same name and namespace in other branches
  1. 9 core/modules/ckeditor5/src/HTMLRestrictions.php \Drupal\ckeditor5\HTMLRestrictions::doIntersect()
  2. 11.x core/modules/ckeditor5/src/HTMLRestrictions.php \Drupal\ckeditor5\HTMLRestrictions::doIntersect()

Computes intersection of two HTML restrictions, without wildcard support.

Parameters

\Drupal\ckeditor5\HTMLRestrictions $other: The HTML restrictions to compare to.

Return value

\Drupal\ckeditor5\HTMLRestrictions Returns a new HTML restrictions value object with all the elements that are also allowed in $other.

File

core/modules/ckeditor5/src/HTMLRestrictions.php, line 744

Class

HTMLRestrictions
Represents a set of HTML restrictions.

Namespace

Drupal\ckeditor5

Code

public function doIntersect(HTMLRestrictions $other) : HTMLRestrictions {
  $intersection_based_on_tags = array_intersect_key($this->elements, $other->elements);
  $intersection = [];
  // Additional filtering is necessary beyond the array_intersect_key that
  // computed $intersection_based_on_tags because tag configuration can have
  // boolean values that have different logic than array values.
  foreach (array_keys($intersection_based_on_tags) as $tag) {
    // If either does not allow attributes, neither does the intersection.
    if ($this->elements[$tag] === FALSE || $other->elements[$tag] === FALSE) {
      $intersection[$tag] = FALSE;
      continue;
    }
    // If both allow all attributes, so does the intersection.
    if ($this->elements[$tag] === TRUE && $other->elements[$tag] === TRUE) {
      $intersection[$tag] = TRUE;
      continue;
    }
    // If the first allows all attributes, return the second.
    if ($this->elements[$tag] === TRUE) {
      $intersection[$tag] = $other->elements[$tag];
      continue;
    }
    // And vice versa.
    if ($other->elements[$tag] === TRUE) {
      $intersection[$tag] = $this->elements[$tag];
      continue;
    }
    // In all other cases, we need to return the most restrictive
    // intersection of per-attribute restrictions.
    // @see ::validateAllowedRestrictionsPhase3()
    assert(is_array($this->elements[$tag]));
    assert(is_array($other->elements[$tag]));
    $intersection[$tag] = [];
    $attributes_intersection = array_intersect_key($this->elements[$tag], $other->elements[$tag]);
    foreach (array_keys($attributes_intersection) as $attr) {
      // If both allow all attribute values, so does the intersection.
      if ($this->elements[$tag][$attr] === TRUE && $other->elements[$tag][$attr] === TRUE) {
        $intersection[$tag][$attr] = TRUE;
        continue;
      }
      // If the first allows all attribute values, return the second.
      if ($this->elements[$tag][$attr] === TRUE) {
        $intersection[$tag][$attr] = $other->elements[$tag][$attr];
        continue;
      }
      // And vice versa.
      if ($other->elements[$tag][$attr] === TRUE) {
        $intersection[$tag][$attr] = $this->elements[$tag][$attr];
        continue;
      }
      // If either allows no attribute values, nor does the intersection.
      if ($this->elements[$tag][$attr] === FALSE || $other->elements[$tag][$attr] === FALSE) {
        // Special case: the global attribute `*` HTML tag.
        // @see https://html.spec.whatwg.org/multipage/dom.html#global-attributes
        // @see validateAllowedRestrictionsPhase2()
        // @see validateAllowedRestrictionsPhase4()
        assert($tag === '*');
        $intersection[$tag][$attr] = FALSE;
        continue;
      }
      assert(is_array($this->elements[$tag][$attr]));
      assert(is_array($other->elements[$tag][$attr]));
      $intersection[$tag][$attr] = array_intersect_key($this->elements[$tag][$attr], $other->elements[$tag][$attr]);
      // It is not permitted to specify an empty attribute value
      // restrictions array.
      if (empty($intersection[$tag][$attr])) {
        unset($intersection[$tag][$attr]);
      }
    }
    // HTML tags must not have an empty array of allowed attributes.
    if ($intersection[$tag] === []) {
      $intersection[$tag] = FALSE;
      // Special case: the global attribute `*` HTML tag.
      // @see https://html.spec.whatwg.org/multipage/dom.html#global-attributes
      // @see validateAllowedRestrictionsPhase2()
      // @see validateAllowedRestrictionsPhase4()
      if ($tag === '*') {
        unset($intersection[$tag]);
      }
    }
  }
  // Special case: wildcard attributes, and the ability to define restrictions
  // for all concrete attributes matching them using:
  // - prefix wildcard, f.e. `*-foo`
  // - infix wildcard, f.e. `*-entity-*`
  // - suffix wildcard, f.e. `data-*`, to match `data-foo`, `data-bar`, etc.
  foreach ($intersection as $tag => $tag_config) {
    // If there are no per-attribute restrictions for this tag in either
    // operand, then no wildcard attribute postprocessing is needed.
    if (!(is_array($this->elements[$tag]) && is_array($other->elements[$tag]))) {
      continue;
    }
    $other_wildcard_attributes = array_filter(array_keys($other->elements[$tag]), [
      __CLASS__,
      'isWildcardAttributeName',
    ]);
    $this_wildcard_attributes = array_filter(array_keys($this->elements[$tag]), [
      __CLASS__,
      'isWildcardAttributeName',
    ]);
    // If the same wildcard attribute restrictions are present in both or
    // neither, no adjustment necessary: the intersection is already correct.
    $in_both = array_intersect($other_wildcard_attributes, $this_wildcard_attributes);
    $other_wildcard_attributes = array_diff($other_wildcard_attributes, $in_both);
    $this_wildcard_attributes = array_diff($this_wildcard_attributes, $in_both);
    $wildcard_attributes_to_analyze = array_merge($other_wildcard_attributes, $this_wildcard_attributes);
    if (empty($wildcard_attributes_to_analyze)) {
      continue;
    }
    // Otherwise, the wildcard attribute name (f.e. `data-*`) is allowed in
    // one of the two with the same attribute value restrictions (e.g. TRUE to
    // allow all attribute values, or an array of specific allowed attribute
    // values), and the intersection must contain the most restrictive
    // configuration.
    foreach ($wildcard_attributes_to_analyze as $wildcard_attribute_name) {
      $other_has_wildcard = isset($other->elements[$tag][$wildcard_attribute_name]);
      $wildcard_operand = $other_has_wildcard ? $other : $this;
      $concrete_operand = $other_has_wildcard ? $this : $other;
      $concrete_tag_config = $concrete_operand->elements[$tag];
      $wildcard_attribute_restriction = $wildcard_operand->elements[$tag][$wildcard_attribute_name];
      $regex = self::getRegExForWildCardAttributeName($wildcard_attribute_name);
      foreach ($concrete_tag_config as $html_tag_attribute_name => $html_tag_attribute_restrictions) {
        if ($html_tag_attribute_restrictions === $wildcard_attribute_restriction && preg_match($regex, $html_tag_attribute_name) === 1) {
          $tag_config = $tag_config === FALSE ? [] : $tag_config;
          $tag_config[$html_tag_attribute_name] = $html_tag_attribute_restrictions;
        }
      }
      $intersection[$tag] = $tag_config;
    }
  }
  return new self($intersection);
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.