: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$attr = preg_replace( "%^[^\s\"']+(\s+|$)%", '', $attr );
if ( 0 === $working ) { // Not well-formed, remove and try again.
$attr = wp_kses_html_error( $attr );
if ( 1 === $mode && false === array_key_exists( $attrname, $attrarr ) ) {
* Special case, for when the attribute list ends with a valueless
* attribute like "selected".
$attrarr[ $attrname ] = array(
* Finds all attributes of an HTML element.
* Does not modify input. May return "evil" output.
* Based on `wp_kses_split2()` and `wp_kses_attr()`.
* @param string $element HTML element.
* @return array|false List of attributes found in the element. Returns false on failure.
function wp_kses_attr_parse( $element ) {
$valid = preg_match( '%^(<\s*)(/\s*)?([a-zA-Z0-9]+\s*)([^>]*)(>?)$%', $element, $matches );
// Closing elements do not get parsed.
// Is there a closing XHTML slash at the end of the attributes?
if ( 1 === preg_match( '%\s*/\s*$%', $attr, $matches ) ) {
$xhtml_slash = $matches[0];
$attr = substr( $attr, 0, -strlen( $xhtml_slash ) );
$attrarr = wp_kses_hair_parse( $attr );
if ( false === $attrarr ) {
// Make sure all input is returned by adding front and back matter.
array_unshift( $attrarr, $begin . $slash . $elname );
array_push( $attrarr, $xhtml_slash . $end );
* Builds an attribute list from string containing attributes.
* Does not modify input. May return "evil" output.
* In case of unexpected input, returns false instead of stripping things.
* Based on `wp_kses_hair()` but does not return a multi-dimensional array.
* @param string $attr Attribute list from HTML element to closing HTML element tag.
* @return array|false List of attributes found in $attr. Returns false on failure.
function wp_kses_hair_parse( $attr ) {
[_a-zA-Z][-_a-zA-Z0-9:.]* # Attribute name.
\[\[?[^\[\]]+\]\]? # Shortcode in the name position implies unfiltered_html.
\s*=\s* # All values begin with "=".
\'[^\']*\' # Single-quoted.
(?:\s|$) # Must have a space.
(?:\s|$) # If attribute has no value, space is required.
\s* # Trailing space is optional except as mentioned above.
* Although it is possible to reduce this procedure to a single regexp,
* we must run that regexp twice to get exactly the expected result.
* Note: do NOT remove the `x` modifiers as they are essential for the above regex!
$validation = "/^($regex)+$/x";
$extraction = "/$regex/x";
if ( 1 === preg_match( $validation, $attr ) ) {
preg_match_all( $extraction, $attr, $attrarr );
* Performs different checks for attribute values.
* The currently implemented checks are "maxlen", "minlen", "maxval", "minval",
* @param string $value Attribute value.
* @param string $vless Whether the attribute is valueless. Use 'y' or 'n'.
* @param string $checkname What $checkvalue is checking for.
* @param mixed $checkvalue What constraint the value should pass.
* @return bool Whether check passes.
function wp_kses_check_attr_val( $value, $vless, $checkname, $checkvalue ) {
switch ( strtolower( $checkname ) ) {
* The maxlen check makes sure that the attribute value has a length not
* greater than the given value. This can be used to avoid Buffer Overflows
* in WWW clients and various Internet servers.
if ( strlen( $value ) > $checkvalue ) {
* The minlen check makes sure that the attribute value has a length not
* smaller than the given value.
if ( strlen( $value ) < $checkvalue ) {
* The maxval check does two things: it checks that the attribute value is
* an integer from 0 and up, without an excessive amount of zeroes or
* whitespace (to avoid Buffer Overflows). It also checks that the attribute
* value is not greater than the given value.
* This check can be used to avoid Denial of Service attacks.
if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) {
if ( $value > $checkvalue ) {
* The minval check makes sure that the attribute value is a positive integer,
* and that it is not smaller than the given value.
if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) {
if ( $value < $checkvalue ) {
* The valueless check makes sure if the attribute has a value
* (like `<a href="blah">`) or not (`<option selected>`). If the given value
* is a "y" or a "Y", the attribute must not have a value.
* If the given value is an "n" or an "N", the attribute must have a value.
if ( strtolower( $checkvalue ) !== $vless ) {
* The values check is used when you want to make sure that the attribute
* has one of the given values.
if ( false === array_search( strtolower( $value ), $checkvalue, true ) ) {
* The value_callback check is used when you want to make sure that the attribute
* value is accepted by the callback function.
if ( ! call_user_func( $checkvalue, $value ) ) {
* Sanitizes a string and removed disallowed URL protocols.
* This function removes all non-allowed protocols from the beginning of the
* string. It ignores whitespace and the case of the letters, and it does
* understand HTML entities. It does its work recursively, so it won't be
* fooled by a string like `javascript:javascript:alert(57)`.
* @param string $content Content to filter bad protocols from.
* @param string[] $allowed_protocols Array of allowed URL protocols.
* @return string Filtered content.
function wp_kses_bad_protocol( $content, $allowed_protocols ) {
$content = wp_kses_no_null( $content );
// Short-circuit if the string starts with `https://` or `http://`. Most common cases.
( str_starts_with( $content, 'https://' ) && in_array( 'https', $allowed_protocols, true ) ) ||
( str_starts_with( $content, 'http://' ) && in_array( 'http', $allowed_protocols, true ) )
$original_content = $content;
$content = wp_kses_bad_protocol_once( $content, $allowed_protocols );
} while ( $original_content !== $content && ++$iterations < 6 );
if ( $original_content !== $content ) {
* Removes any invalid control characters in a text string.
* Also removes any instance of the `\0` string.
* @param string $content Content to filter null characters from.
* @param array $options Set 'slash_zero' => 'keep' when '\0' is allowed. Default is 'remove'.
* @return string Filtered content.
function wp_kses_no_null( $content, $options = null ) {
if ( ! isset( $options['slash_zero'] ) ) {
$options = array( 'slash_zero' => 'remove' );
$content = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', $content );
if ( 'remove' === $options['slash_zero'] ) {
$content = preg_replace( '/\\\\+0+/', '', $content );
* Strips slashes from in front of quotes.
* This function changes the character sequence `\"` to just `"`. It leaves all other
* slashes alone. The quoting from `preg_replace(//e)` requires this.
* @param string $content String to strip slashes from.
* @return string Fixed string with quoted slashes.
function wp_kses_stripslashes( $content ) {
return preg_replace( '%\\\\"%', '"', $content );
* Converts the keys of an array to lowercase.
* @param array $inarray Unfiltered array.
* @return array Fixed array with all lowercase keys.
function wp_kses_array_lc( $inarray ) {
foreach ( (array) $inarray as $inkey => $inval ) {
$outkey = strtolower( $inkey );
$outarray[ $outkey ] = array();
foreach ( (array) $inval as $inkey2 => $inval2 ) {
$outkey2 = strtolower( $inkey2 );
$outarray[ $outkey ][ $outkey2 ] = $inval2;
* Handles parsing errors in `wp_kses_hair()`.
* The general plan is to remove everything to and including some whitespace,
* but it deals with quotes and apostrophes as well.
function wp_kses_html_error( $attr ) {
return preg_replace( '/^("[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*/', '', $attr );
* Sanitizes content from bad protocols and other characters.
* This function searches for URL protocols at the beginning of the string, while
* handling whitespace and HTML entities.
* @param string $content Content to check for bad protocols.
* @param string[] $allowed_protocols Array of allowed URL protocols.
* @param int $count Depth of call recursion to this function.
* @return string Sanitized content.
function wp_kses_bad_protocol_once( $content, $allowed_protocols, $count = 1 ) {
$content = preg_replace( '/(�*58(?![;0-9])|�*3a(?![;a-f0-9]))/i', '$1;', $content );
$content2 = preg_split( '/:|�*58;|�*3a;|:/i', $content, 2 );
if ( isset( $content2[1] ) && ! preg_match( '%/\?%', $content2[0] ) ) {
$content = trim( $content2[1] );
$protocol = wp_kses_bad_protocol_once2( $content2[0], $allowed_protocols );
if ( 'feed:' === $protocol ) {
$content = wp_kses_bad_protocol_once( $content, $allowed_protocols, ++$count );
if ( empty( $content ) ) {
$content = $protocol . $content;
* Callback for `wp_kses_bad_protocol_once()` regular expression.
* This function processes URL protocols, checks to see if they're in the
* list of allowed protocols or not, and returns different data depending
* @param string $scheme URI scheme to check against the list of allowed protocols.
* @param string[] $allowed_protocols Array of allowed URL protocols.
* @return string Sanitized content.
function wp_kses_bad_protocol_once2( $scheme, $allowed_protocols ) {
$scheme = wp_kses_decode_entities( $scheme );
$scheme = preg_replace( '/\s/', '', $scheme );
$scheme = wp_kses_no_null( $scheme );
$scheme = strtolower( $scheme );
foreach ( (array) $allowed_protocols as $one_protocol ) {
if ( strtolower( $one_protocol ) === $scheme ) {
* Converts and fixes HTML entities.
* This function normalizes HTML entities. It will convert `AT&T` to the correct
* `AT&T`, `:` to `:`, `&#XYZZY;` to `&#XYZZY;` and so on.
* When `$context` is set to 'xml', HTML entities are converted to their code points. For
* example, `AT&T…&#XYZZY;` is converted to `AT&T…&#XYZZY;`.
* @since 5.5.0 Added `$context` parameter.
* @param string $content Content to normalize entities.
* @param string $context Context for normalization. Can be either 'html' or 'xml'.
* @return string Content with normalized entities.
function wp_kses_normalize_entities( $content, $context = 'html' ) {
// Disarm all entities by converting & to &
$content = str_replace( '&', '&', $content );
// Change back the allowed entities in our list of allowed entities.
if ( 'xml' === $context ) {
$content = preg_replace_callback( '/&([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_xml_named_entities', $content );
$content = preg_replace_callback( '/&([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_named_entities', $content );
$content = preg_replace_callback( '/&#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $content );
$content = preg_replace_callback( '/&#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $content );
* Callback for `wp_kses_normalize_entities()` regular expression.
* This function only accepts valid named entity references, which are finite,
* case-sensitive, and highly scrutinized by HTML and XML validators.
* @global array $allowedentitynames
* @param array $matches preg_replace_callback() matches array.
* @return string Correctly encoded entity.
function wp_kses_named_entities( $matches ) {
global $allowedentitynames;
if ( empty( $matches[1] ) ) {
return ( ! in_array( $i, $allowedentitynames, true ) ) ? "&$i;" : "&$i;";
* Callback for `wp_kses_normalize_entities()` regular expression.
* This function only accepts valid named entity references, which are finite,
* case-sensitive, and highly scrutinized by XML validators. HTML named entity
* references are converted to their code points.