function HTMLRestrictions::doIntersect

Same name in other branches
  1. 10 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.