wp/wp-includes/shortcodes.php
changeset 21 48c4eec2b7e6
parent 18 be944660c56a
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
    29  * @subpackage Shortcodes
    29  * @subpackage Shortcodes
    30  * @since 2.5.0
    30  * @since 2.5.0
    31  */
    31  */
    32 
    32 
    33 /**
    33 /**
    34  * Container for storing shortcode tags and their hook to call for the shortcode
    34  * Container for storing shortcode tags and their hook to call for the shortcode.
    35  *
    35  *
    36  * @since 2.5.0
    36  * @since 2.5.0
    37  *
    37  *
    38  * @name $shortcode_tags
    38  * @name $shortcode_tags
    39  * @var array
    39  * @var array
   103 
   103 
   104 	unset( $shortcode_tags[ $tag ] );
   104 	unset( $shortcode_tags[ $tag ] );
   105 }
   105 }
   106 
   106 
   107 /**
   107 /**
   108  * Clear all shortcodes.
   108  * Clears all shortcodes.
   109  *
   109  *
   110  * This function is simple, it clears all of the shortcode tags by replacing the
   110  * This function clears all of the shortcode tags by replacing the shortcodes global with
   111  * shortcodes global by a empty array. This is actually a very efficient method
   111  * an empty array. This is actually an efficient method for removing all shortcodes.
   112  * for removing all shortcodes.
       
   113  *
   112  *
   114  * @since 2.5.0
   113  * @since 2.5.0
   115  *
   114  *
   116  * @global array $shortcode_tags
   115  * @global array $shortcode_tags
   117  */
   116  */
   120 
   119 
   121 	$shortcode_tags = array();
   120 	$shortcode_tags = array();
   122 }
   121 }
   123 
   122 
   124 /**
   123 /**
   125  * Whether a registered shortcode exists named $tag
   124  * Determines whether a registered shortcode exists named $tag.
   126  *
   125  *
   127  * @since 3.6.0
   126  * @since 3.6.0
   128  *
   127  *
   129  * @global array $shortcode_tags List of shortcode tags and their callback hooks.
   128  * @global array $shortcode_tags List of shortcode tags and their callback hooks.
   130  *
   129  *
   135 	global $shortcode_tags;
   134 	global $shortcode_tags;
   136 	return array_key_exists( $tag, $shortcode_tags );
   135 	return array_key_exists( $tag, $shortcode_tags );
   137 }
   136 }
   138 
   137 
   139 /**
   138 /**
   140  * Whether the passed content contains the specified shortcode
   139  * Determines whether the passed content contains the specified shortcode.
   141  *
   140  *
   142  * @since 3.6.0
   141  * @since 3.6.0
   143  *
   142  *
   144  * @global array $shortcode_tags
   143  * @global array $shortcode_tags
   145  *
   144  *
   146  * @param string $content Content to search for shortcodes.
   145  * @param string $content Content to search for shortcodes.
   147  * @param string $tag     Shortcode tag to check.
   146  * @param string $tag     Shortcode tag to check.
   148  * @return bool Whether the passed content contains the given shortcode.
   147  * @return bool Whether the passed content contains the given shortcode.
   149  */
   148  */
   150 function has_shortcode( $content, $tag ) {
   149 function has_shortcode( $content, $tag ) {
   151 	if ( false === strpos( $content, '[' ) ) {
   150 	if ( ! str_contains( $content, '[' ) ) {
   152 		return false;
   151 		return false;
   153 	}
   152 	}
   154 
   153 
   155 	if ( shortcode_exists( $tag ) ) {
   154 	if ( shortcode_exists( $tag ) ) {
   156 		preg_match_all( '/' . get_shortcode_regex() . '/', $content, $matches, PREG_SET_ORDER );
   155 		preg_match_all( '/' . get_shortcode_regex() . '/', $content, $matches, PREG_SET_ORDER );
   168 	}
   167 	}
   169 	return false;
   168 	return false;
   170 }
   169 }
   171 
   170 
   172 /**
   171 /**
   173  * Search content for shortcodes and filter shortcodes through their hooks.
   172  * Returns a list of registered shortcode names found in the given content.
       
   173  *
       
   174  * Example usage:
       
   175  *
       
   176  *     get_shortcode_tags_in_content( '[audio src="file.mp3"][/audio] [foo] [gallery ids="1,2,3"]' );
       
   177  *     // array( 'audio', 'gallery' )
       
   178  *
       
   179  * @since 6.3.2
       
   180  *
       
   181  * @param string $content The content to check.
       
   182  * @return string[] An array of registered shortcode names found in the content.
       
   183  */
       
   184 function get_shortcode_tags_in_content( $content ) {
       
   185 	if ( false === strpos( $content, '[' ) ) {
       
   186 		return array();
       
   187 	}
       
   188 
       
   189 	preg_match_all( '/' . get_shortcode_regex() . '/', $content, $matches, PREG_SET_ORDER );
       
   190 	if ( empty( $matches ) ) {
       
   191 		return array();
       
   192 	}
       
   193 
       
   194 	$tags = array();
       
   195 	foreach ( $matches as $shortcode ) {
       
   196 		$tags[] = $shortcode[2];
       
   197 
       
   198 		if ( ! empty( $shortcode[5] ) ) {
       
   199 			$deep_tags = get_shortcode_tags_in_content( $shortcode[5] );
       
   200 			if ( ! empty( $deep_tags ) ) {
       
   201 				$tags = array_merge( $tags, $deep_tags );
       
   202 			}
       
   203 		}
       
   204 	}
       
   205 
       
   206 	return $tags;
       
   207 }
       
   208 
       
   209 /**
       
   210  * Searches content for shortcodes and filter shortcodes through their hooks.
   174  *
   211  *
   175  * This function is an alias for do_shortcode().
   212  * This function is an alias for do_shortcode().
   176  *
   213  *
   177  * @since 5.4.0
   214  * @since 5.4.0
   178  *
   215  *
   186 function apply_shortcodes( $content, $ignore_html = false ) {
   223 function apply_shortcodes( $content, $ignore_html = false ) {
   187 	return do_shortcode( $content, $ignore_html );
   224 	return do_shortcode( $content, $ignore_html );
   188 }
   225 }
   189 
   226 
   190 /**
   227 /**
   191  * Search content for shortcodes and filter shortcodes through their hooks.
   228  * Searches content for shortcodes and filter shortcodes through their hooks.
   192  *
   229  *
   193  * If there are no shortcode tags defined, then the content will be returned
   230  * If there are no shortcode tags defined, then the content will be returned
   194  * without any filtering. This might cause issues when plugins are disabled but
   231  * without any filtering. This might cause issues when plugins are disabled but
   195  * the shortcode will still show up in the post or content.
   232  * the shortcode will still show up in the post or content.
   196  *
   233  *
   204  * @return string Content with shortcodes filtered out.
   241  * @return string Content with shortcodes filtered out.
   205  */
   242  */
   206 function do_shortcode( $content, $ignore_html = false ) {
   243 function do_shortcode( $content, $ignore_html = false ) {
   207 	global $shortcode_tags;
   244 	global $shortcode_tags;
   208 
   245 
   209 	if ( false === strpos( $content, '[' ) ) {
   246 	if ( ! str_contains( $content, '[' ) ) {
   210 		return $content;
   247 		return $content;
   211 	}
   248 	}
   212 
   249 
   213 	if ( empty( $shortcode_tags ) || ! is_array( $shortcode_tags ) ) {
   250 	if ( empty( $shortcode_tags ) || ! is_array( $shortcode_tags ) ) {
   214 		return $content;
   251 		return $content;
   220 
   257 
   221 	if ( empty( $tagnames ) ) {
   258 	if ( empty( $tagnames ) ) {
   222 		return $content;
   259 		return $content;
   223 	}
   260 	}
   224 
   261 
       
   262 	// Ensure this context is only added once if shortcodes are nested.
       
   263 	$has_filter   = has_filter( 'wp_get_attachment_image_context', '_filter_do_shortcode_context' );
       
   264 	$filter_added = false;
       
   265 
       
   266 	if ( ! $has_filter ) {
       
   267 		$filter_added = add_filter( 'wp_get_attachment_image_context', '_filter_do_shortcode_context' );
       
   268 	}
       
   269 
   225 	$content = do_shortcodes_in_html_tags( $content, $ignore_html, $tagnames );
   270 	$content = do_shortcodes_in_html_tags( $content, $ignore_html, $tagnames );
   226 
   271 
   227 	$pattern = get_shortcode_regex( $tagnames );
   272 	$pattern = get_shortcode_regex( $tagnames );
   228 	$content = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $content );
   273 	$content = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $content );
   229 
   274 
   230 	// Always restore square braces so we don't break things like <!--[if IE ]>.
   275 	// Always restore square braces so we don't break things like <!--[if IE ]>.
   231 	$content = unescape_invalid_shortcodes( $content );
   276 	$content = unescape_invalid_shortcodes( $content );
   232 
   277 
       
   278 	// Only remove the filter if it was added in this scope.
       
   279 	if ( $filter_added ) {
       
   280 		remove_filter( 'wp_get_attachment_image_context', '_filter_do_shortcode_context' );
       
   281 	}
       
   282 
   233 	return $content;
   283 	return $content;
   234 }
   284 }
   235 
   285 
   236 /**
   286 /**
   237  * Retrieve the shortcode regular expression for searching.
   287  * Filter the `wp_get_attachment_image_context` hook during shortcode rendering.
       
   288  *
       
   289  * When wp_get_attachment_image() is called during shortcode rendering, we need to make clear
       
   290  * that the context is a shortcode and not part of the theme's template rendering logic.
       
   291  *
       
   292  * @since 6.3.0
       
   293  * @access private
       
   294  *
       
   295  * @return string The filtered context value for wp_get_attachment_images when doing shortcodes.
       
   296  */
       
   297 function _filter_do_shortcode_context() {
       
   298 	return 'do_shortcode';
       
   299 }
       
   300 
       
   301 /**
       
   302  * Retrieves the shortcode regular expression for searching.
   238  *
   303  *
   239  * The regular expression combines the shortcode tags in the regular expression
   304  * The regular expression combines the shortcode tags in the regular expression
   240  * in a regex class.
   305  * in a regex class.
   241  *
   306  *
   242  * The regular expression contains 6 different sub matches to help with parsing.
   307  * The regular expression contains 6 different sub matches to help with parsing.
   262 	if ( empty( $tagnames ) ) {
   327 	if ( empty( $tagnames ) ) {
   263 		$tagnames = array_keys( $shortcode_tags );
   328 		$tagnames = array_keys( $shortcode_tags );
   264 	}
   329 	}
   265 	$tagregexp = implode( '|', array_map( 'preg_quote', $tagnames ) );
   330 	$tagregexp = implode( '|', array_map( 'preg_quote', $tagnames ) );
   266 
   331 
   267 	// WARNING! Do not change this regex without changing do_shortcode_tag() and strip_shortcode_tag().
   332 	/*
   268 	// Also, see shortcode_unautop() and shortcode.js.
   333 	 * WARNING! Do not change this regex without changing do_shortcode_tag() and strip_shortcode_tag().
       
   334 	 * Also, see shortcode_unautop() and shortcode.js.
       
   335 	 */
   269 
   336 
   270 	// phpcs:disable Squiz.Strings.ConcatenationSpacing.PaddingFound -- don't remove regex indentation
   337 	// phpcs:disable Squiz.Strings.ConcatenationSpacing.PaddingFound -- don't remove regex indentation
   271 	return '\\['                             // Opening bracket.
   338 	return '\\['                             // Opening bracket.
   272 		. '(\\[?)'                           // 1: Optional second opening bracket for escaping shortcodes: [[tag]].
   339 		. '(\\[?)'                           // 1: Optional second opening bracket for escaping shortcodes: [[tag]].
   273 		. "($tagregexp)"                     // 2: Shortcode name.
   340 		. "($tagregexp)"                     // 2: Shortcode name.
   293 		.             ')*+'
   360 		.             ')*+'
   294 		.         ')'
   361 		.         ')'
   295 		.         '\\[\\/\\2\\]'             // Closing shortcode tag.
   362 		.         '\\[\\/\\2\\]'             // Closing shortcode tag.
   296 		.     ')?'
   363 		.     ')?'
   297 		. ')'
   364 		. ')'
   298 		. '(\\]?)';                          // 6: Optional second closing brocket for escaping shortcodes: [[tag]].
   365 		. '(\\]?)';                          // 6: Optional second closing bracket for escaping shortcodes: [[tag]].
   299 	// phpcs:enable
   366 	// phpcs:enable
   300 }
   367 }
   301 
   368 
   302 /**
   369 /**
   303  * Regular Expression callable for do_shortcode() for calling shortcode hook.
   370  * Regular Expression callable for do_shortcode() for calling shortcode hook.
   307  * @since 2.5.0
   374  * @since 2.5.0
   308  * @access private
   375  * @access private
   309  *
   376  *
   310  * @global array $shortcode_tags
   377  * @global array $shortcode_tags
   311  *
   378  *
   312  * @param array $m Regular expression match array.
   379  * @param array $m {
   313  * @return string|false Shortcode output on success, false on failure.
   380  *     Regular expression match array.
       
   381  *
       
   382  *     @type string $0 Entire matched shortcode text.
       
   383  *     @type string $1 Optional second opening bracket for escaping shortcodes.
       
   384  *     @type string $2 Shortcode name.
       
   385  *     @type string $3 Shortcode arguments list.
       
   386  *     @type string $4 Optional self closing slash.
       
   387  *     @type string $5 Content of a shortcode when it wraps some content.
       
   388  *     @type string $6 Optional second closing bracket for escaping shortcodes.
       
   389  * }
       
   390  * @return string Shortcode output.
   314  */
   391  */
   315 function do_shortcode_tag( $m ) {
   392 function do_shortcode_tag( $m ) {
   316 	global $shortcode_tags;
   393 	global $shortcode_tags;
   317 
   394 
   318 	// Allow [[foo]] syntax for escaping a tag.
   395 	// Allow [[foo]] syntax for escaping a tag.
   338 	 *
   415 	 *
   339 	 * Returning a non-false value from filter will short-circuit the
   416 	 * Returning a non-false value from filter will short-circuit the
   340 	 * shortcode generation process, returning that value instead.
   417 	 * shortcode generation process, returning that value instead.
   341 	 *
   418 	 *
   342 	 * @since 4.7.0
   419 	 * @since 4.7.0
       
   420 	 * @since 6.5.0 The `$attr` parameter is always an array.
   343 	 *
   421 	 *
   344 	 * @param false|string $return      Short-circuit return value. Either false or the value to replace the shortcode with.
   422 	 * @param false|string $output Short-circuit return value. Either false or the value to replace the shortcode with.
   345 	 * @param string       $tag         Shortcode name.
   423 	 * @param string       $tag    Shortcode name.
   346 	 * @param array|string $attr        Shortcode attributes array or empty string.
   424 	 * @param array        $attr   Shortcode attributes array, can be empty if the original arguments string cannot be parsed.
   347 	 * @param array        $m           Regular expression match array.
   425 	 * @param array        $m      Regular expression match array.
   348 	 */
   426 	 */
   349 	$return = apply_filters( 'pre_do_shortcode_tag', false, $tag, $attr, $m );
   427 	$return = apply_filters( 'pre_do_shortcode_tag', false, $tag, $attr, $m );
   350 	if ( false !== $return ) {
   428 	if ( false !== $return ) {
   351 		return $return;
   429 		return $return;
   352 	}
   430 	}
   357 
   435 
   358 	/**
   436 	/**
   359 	 * Filters the output created by a shortcode callback.
   437 	 * Filters the output created by a shortcode callback.
   360 	 *
   438 	 *
   361 	 * @since 4.7.0
   439 	 * @since 4.7.0
       
   440 	 * @since 6.5.0 The `$attr` parameter is always an array.
   362 	 *
   441 	 *
   363 	 * @param string       $output Shortcode output.
   442 	 * @param string $output Shortcode output.
   364 	 * @param string       $tag    Shortcode name.
   443 	 * @param string $tag    Shortcode name.
   365 	 * @param array|string $attr   Shortcode attributes array or empty string.
   444 	 * @param array  $attr   Shortcode attributes array, can be empty if the original arguments string cannot be parsed.
   366 	 * @param array        $m      Regular expression match array.
   445 	 * @param array  $m      Regular expression match array.
   367 	 */
   446 	 */
   368 	return apply_filters( 'do_shortcode_tag', $output, $tag, $attr, $m );
   447 	return apply_filters( 'do_shortcode_tag', $output, $tag, $attr, $m );
   369 }
   448 }
   370 
   449 
   371 /**
   450 /**
   372  * Search only inside HTML elements for shortcodes and process them.
   451  * Searches only inside HTML elements for shortcodes and process them.
   373  *
   452  *
   374  * Any [ or ] characters remaining inside elements will be HTML encoded
   453  * Any [ or ] characters remaining inside elements will be HTML encoded
   375  * to prevent interference with shortcodes that are outside the elements.
   454  * to prevent interference with shortcodes that are outside the elements.
   376  * Assumes $content processed by KSES already.  Users with unfiltered_html
   455  * Assumes $content processed by KSES already.  Users with unfiltered_html
   377  * capability may get unexpected output if angle braces are nested in tags.
   456  * capability may get unexpected output if angle braces are nested in tags.
   401 	foreach ( $textarr as &$element ) {
   480 	foreach ( $textarr as &$element ) {
   402 		if ( '' === $element || '<' !== $element[0] ) {
   481 		if ( '' === $element || '<' !== $element[0] ) {
   403 			continue;
   482 			continue;
   404 		}
   483 		}
   405 
   484 
   406 		$noopen  = false === strpos( $element, '[' );
   485 		$noopen  = ! str_contains( $element, '[' );
   407 		$noclose = false === strpos( $element, ']' );
   486 		$noclose = ! str_contains( $element, ']' );
   408 		if ( $noopen || $noclose ) {
   487 		if ( $noopen || $noclose ) {
   409 			// This element does not contain shortcodes.
   488 			// This element does not contain shortcodes.
   410 			if ( $noopen xor $noclose ) {
   489 			if ( $noopen xor $noclose ) {
   411 				// Need to encode stray '[' or ']' chars.
   490 				// Need to encode stray '[' or ']' chars.
   412 				$element = strtr( $element, $trans );
   491 				$element = strtr( $element, $trans );
   413 			}
   492 			}
   414 			continue;
   493 			continue;
   415 		}
   494 		}
   416 
   495 
   417 		if ( $ignore_html || '<!--' === substr( $element, 0, 4 ) || '<![CDATA[' === substr( $element, 0, 9 ) ) {
   496 		if ( $ignore_html || str_starts_with( $element, '<!--' ) || str_starts_with( $element, '<![CDATA[' ) ) {
   418 			// Encode all '[' and ']' chars.
   497 			// Encode all '[' and ']' chars.
   419 			$element = strtr( $element, $trans );
   498 			$element = strtr( $element, $trans );
   420 			continue;
   499 			continue;
   421 		}
   500 		}
   422 
   501 
   425 			// Some plugins are doing things like [name] <[email]>.
   504 			// Some plugins are doing things like [name] <[email]>.
   426 			if ( 1 === preg_match( '%^<\s*\[\[?[^\[\]]+\]%', $element ) ) {
   505 			if ( 1 === preg_match( '%^<\s*\[\[?[^\[\]]+\]%', $element ) ) {
   427 				$element = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $element );
   506 				$element = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $element );
   428 			}
   507 			}
   429 
   508 
   430 			// Looks like we found some crazy unfiltered HTML. Skipping it for sanity.
   509 			// Looks like we found some unexpected unfiltered HTML. Skipping it for confidence.
   431 			$element = strtr( $element, $trans );
   510 			$element = strtr( $element, $trans );
   432 			continue;
   511 			continue;
   433 		}
   512 		}
   434 
   513 
   435 		// Get element name.
   514 		// Get element name.
   455 				 * was written by an administrator, so we should avoid changing the output
   534 				 * was written by an administrator, so we should avoid changing the output
   456 				 * and we do not need to run KSES here.
   535 				 * and we do not need to run KSES here.
   457 				 */
   536 				 */
   458 				$attr = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $attr );
   537 				$attr = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $attr );
   459 			} else {
   538 			} else {
   460 				// $attr like 'name = "[shortcode]"' or "name = '[shortcode]'".
   539 				/*
   461 				// We do not know if $content was unfiltered. Assume KSES ran before shortcodes.
   540 				 * $attr like 'name = "[shortcode]"' or "name = '[shortcode]'".
       
   541 				 * We do not know if $content was unfiltered. Assume KSES ran before shortcodes.
       
   542 				 */
   462 				$count    = 0;
   543 				$count    = 0;
   463 				$new_attr = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $attr, -1, $count );
   544 				$new_attr = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $attr, -1, $count );
   464 				if ( $count > 0 ) {
   545 				if ( $count > 0 ) {
   465 					// Sanitize the shortcode output using KSES.
   546 					// Sanitize the shortcode output using KSES.
   466 					$new_attr = wp_kses_one_attr( $new_attr, $elname );
   547 					$new_attr = wp_kses_one_attr( $new_attr, $elname );
   481 
   562 
   482 	return $content;
   563 	return $content;
   483 }
   564 }
   484 
   565 
   485 /**
   566 /**
   486  * Remove placeholders added by do_shortcodes_in_html_tags().
   567  * Removes placeholders added by do_shortcodes_in_html_tags().
   487  *
   568  *
   488  * @since 4.2.3
   569  * @since 4.2.3
   489  *
   570  *
   490  * @param string $content Content to search for placeholders.
   571  * @param string $content Content to search for placeholders.
   491  * @return string Content with placeholders removed.
   572  * @return string Content with placeholders removed.
   501 
   582 
   502 	return $content;
   583 	return $content;
   503 }
   584 }
   504 
   585 
   505 /**
   586 /**
   506  * Retrieve the shortcode attributes regex.
   587  * Retrieves the shortcode attributes regex.
   507  *
   588  *
   508  * @since 4.4.0
   589  * @since 4.4.0
   509  *
   590  *
   510  * @return string The shortcode attribute regular expression
   591  * @return string The shortcode attribute regular expression.
   511  */
   592  */
   512 function get_shortcode_atts_regex() {
   593 function get_shortcode_atts_regex() {
   513 	return '/([\w-]+)\s*=\s*"([^"]*)"(?:\s|$)|([\w-]+)\s*=\s*\'([^\']*)\'(?:\s|$)|([\w-]+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|\'([^\']*)\'(?:\s|$)|(\S+)(?:\s|$)/';
   594 	return '/([\w-]+)\s*=\s*"([^"]*)"(?:\s|$)|([\w-]+)\s*=\s*\'([^\']*)\'(?:\s|$)|([\w-]+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|\'([^\']*)\'(?:\s|$)|(\S+)(?:\s|$)/';
   514 }
   595 }
   515 
   596 
   516 /**
   597 /**
   517  * Retrieve all attributes from the shortcodes tag.
   598  * Retrieves all attributes from the shortcodes tag.
   518  *
   599  *
   519  * The attributes list has the attribute name as the key and the value of the
   600  * The attributes list has the attribute name as the key and the value of the
   520  * attribute as the value in the key/value pair. This allows for easier
   601  * attribute as the value in the key/value pair. This allows for easier
   521  * retrieval of the attributes, since all attributes have to be known.
   602  * retrieval of the attributes, since all attributes have to be known.
   522  *
   603  *
   523  * @since 2.5.0
   604  * @since 2.5.0
   524  *
   605  * @since 6.5.0 The function now always returns an array,
   525  * @param string $text
   606  *              even if the original arguments string cannot be parsed or is empty.
   526  * @return array|string List of attribute values.
   607  *
   527  *                      Returns empty array if '""' === trim( $text ).
   608  * @param string $text Shortcode arguments list.
   528  *                      Returns empty string if '' === trim( $text ).
   609  * @return array Array of attribute values keyed by attribute name.
   529  *                      All other matches are checked for not empty().
   610  *               Returns empty array if there are no attributes
       
   611  *               or if the original arguments string cannot be parsed.
   530  */
   612  */
   531 function shortcode_parse_atts( $text ) {
   613 function shortcode_parse_atts( $text ) {
   532 	$atts    = array();
   614 	$atts    = array();
   533 	$pattern = get_shortcode_atts_regex();
   615 	$pattern = get_shortcode_atts_regex();
   534 	$text    = preg_replace( "/[\x{00a0}\x{200b}]+/u", ' ', $text );
   616 	$text    = preg_replace( "/[\x{00a0}\x{200b}]+/u", ' ', $text );
   549 			}
   631 			}
   550 		}
   632 		}
   551 
   633 
   552 		// Reject any unclosed HTML elements.
   634 		// Reject any unclosed HTML elements.
   553 		foreach ( $atts as &$value ) {
   635 		foreach ( $atts as &$value ) {
   554 			if ( false !== strpos( $value, '<' ) ) {
   636 			if ( str_contains( $value, '<' ) ) {
   555 				if ( 1 !== preg_match( '/^[^<]*+(?:<[^>]*+>[^<]*+)*+$/', $value ) ) {
   637 				if ( 1 !== preg_match( '/^[^<]*+(?:<[^>]*+>[^<]*+)*+$/', $value ) ) {
   556 					$value = '';
   638 					$value = '';
   557 				}
   639 				}
   558 			}
   640 			}
   559 		}
   641 		}
   560 	} else {
       
   561 		$atts = ltrim( $text );
       
   562 	}
   642 	}
   563 
   643 
   564 	return $atts;
   644 	return $atts;
   565 }
   645 }
   566 
   646 
   567 /**
   647 /**
   568  * Combine user attributes with known attributes and fill in defaults when needed.
   648  * Combines user attributes with known attributes and fill in defaults when needed.
   569  *
   649  *
   570  * The pairs should be considered to be all of the attributes which are
   650  * The pairs should be considered to be all of the attributes which are
   571  * supported by the caller and given as a list. The returned attributes will
   651  * supported by the caller and given as a list. The returned attributes will
   572  * only contain the attributes in the $pairs list.
   652  * only contain the attributes in the $pairs list.
   573  *
   653  *
   612 
   692 
   613 	return $out;
   693 	return $out;
   614 }
   694 }
   615 
   695 
   616 /**
   696 /**
   617  * Remove all shortcode tags from the given content.
   697  * Removes all shortcode tags from the given content.
   618  *
   698  *
   619  * @since 2.5.0
   699  * @since 2.5.0
   620  *
   700  *
   621  * @global array $shortcode_tags
   701  * @global array $shortcode_tags
   622  *
   702  *
   624  * @return string Content without shortcode tags.
   704  * @return string Content without shortcode tags.
   625  */
   705  */
   626 function strip_shortcodes( $content ) {
   706 function strip_shortcodes( $content ) {
   627 	global $shortcode_tags;
   707 	global $shortcode_tags;
   628 
   708 
   629 	if ( false === strpos( $content, '[' ) ) {
   709 	if ( ! str_contains( $content, '[' ) ) {
   630 		return $content;
   710 		return $content;
   631 	}
   711 	}
   632 
   712 
   633 	if ( empty( $shortcode_tags ) || ! is_array( $shortcode_tags ) ) {
   713 	if ( empty( $shortcode_tags ) || ! is_array( $shortcode_tags ) ) {
   634 		return $content;
   714 		return $content;