"\']|"[^"]*"|\'[^\']*\')*)>/i', (string) $html, $tags, PREG_SET_ORDER ); foreach ( $tags as [ , $tag, $attributes ] ) { $tag = strtolower( $tag ); if ( in_array( $tag, self::NEVER_TAGS, true ) ) { continue; } $allowed[ $tag ] = $allowed[ $tag ] ?? []; preg_match_all( '/(?:^|\s)([a-z_:][a-z0-9_:.-]*)/i', (string) preg_replace( '/"[^"]*"|\'[^\']*\'/', '""', $attributes ), $names ); foreach ( array_map( 'strtolower', $names[1] ) as $name ) { if ( 0 !== strpos( $name, 'on' ) && !in_array( $name, self::NEVER_ATTRS, true ) ) { $allowed[ $tag ][ $name ] = true; } } } // Each property its style attributes use; wp_kses() would drop those outside WordPress's own short list if ( preg_match_all( '/\bstyle\s*=\s*(["\'])(.*?)\1/is', (string) $html, $styles ) ) { foreach ( $styles[2] as $style ) { preg_match_all( '/(?:^|;)\s*(-?-?[a-z][a-z0-9-]*)\s*:/i', html_entity_decode( $style ), $properties ); self::$properties = array_unique( array_merge( self::$properties, array_map( 'strtolower', $properties[1] ) ) ); } } if ( !has_filter( 'pre_kses', [ __CLASS__, 'scope' ] ) ) { add_filter( 'pre_kses', [ __CLASS__, 'scope' ], 10, 2 ); add_filter( 'safe_style_css', [ __CLASS__, 'properties' ] ); add_filter( 'safecss_filter_attr_allow_css', [ __CLASS__, 'allow_css' ] ); } $allowed[ self::MARK ] = []; /** * The tags and attributes a form's markup may print * * @param array $allowed As wp_kses() takes them, names in lowercase * @param string $html Markup */ return (array) apply_filters( 'remaily_allowed_html', $allowed, $html ); } /** * Notes whether this wp_kses() got this class's list: only then does CSS stay as written * * @param string $content Markup * @param array|string $allowed What wp_kses() lets through * * @return string */ public static function scope( $content, $allowed ) { self::$ours = is_array( $allowed ) && isset( $allowed[ self::MARK ] ); return $content; } /** * The markup's CSS properties, during its own wp_kses() * * @param string[] $properties Properties WordPress allows * * @return string[] */ public static function properties( $properties ) { return self::$ours ? array_merge( (array) $properties, self::$properties ) : $properties; } /** * Values such as calc(), var() and url() stay, during the markup's own wp_kses() * * @param bool $allow Whether WordPress allows it * * @return bool */ public static function allow_css( $allow ) { return self::$ours || $allow; } }