wp/wp-includes/shortcodes.php
changeset 7 cf61fcea0001
parent 5 5e2f62d02dcd
child 9 177826044cd9
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
     1 <?php
     1 <?php
     2 /**
     2 /**
     3  * WordPress API for creating bbcode like tags or what WordPress calls
     3  * WordPress API for creating bbcode-like tags or what WordPress calls
     4  * "shortcodes." The tag and attribute parsing or regular expression code is
     4  * "shortcodes". The tag and attribute parsing or regular expression code is
     5  * based on the Textpattern tag parser.
     5  * based on the Textpattern tag parser.
     6  *
     6  *
     7  * A few examples are below:
     7  * A few examples are below:
     8  *
     8  *
     9  * [shortcode /]
     9  * [shortcode /]
    40  * @global array $shortcode_tags
    40  * @global array $shortcode_tags
    41  */
    41  */
    42 $shortcode_tags = array();
    42 $shortcode_tags = array();
    43 
    43 
    44 /**
    44 /**
    45  * Add hook for shortcode tag.
    45  * Adds a new shortcode.
    46  *
    46  *
    47  * There can only be one hook for each shortcode. Which means that if another
    47  * Care should be taken through prefixing or other means to ensure that the
    48  * plugin has a similar shortcode, it will override yours or yours will override
    48  * shortcode tag being added is unique and will not conflict with other,
    49  * theirs depending on which order the plugins are included and/or ran.
    49  * already-added shortcode tags. In the event of a duplicated tag, the tag
    50  *
    50  * loaded last will take precedence.
    51  * Simplest example of a shortcode tag using the API:
    51  *
    52  *
    52  * @since 2.5.0
    53  *     // [footag foo="bar"]
    53  *
    54  *     function footag_func( $atts ) {
    54  * @global array $shortcode_tags
    55  *         return "foo = {
    55  *
    56  *             $atts[foo]
    56  * @param string   $tag      Shortcode tag to be searched in post content.
    57  *         }";
    57  * @param callable $callback The callback function to run when the shortcode is found.
    58  *     }
    58  *                           Every shortcode callback is passed three parameters by default,
    59  *     add_shortcode( 'footag', 'footag_func' );
    59  *                           including an array of attributes (`$atts`), the shortcode content
    60  *
    60  *                           or null if not set (`$content`), and finally the shortcode tag
    61  * Example with nice attribute defaults:
    61  *                           itself (`$shortcode_tag`), in that order.
    62  *
    62  */
    63  *     // [bartag foo="bar"]
    63 function add_shortcode( $tag, $callback ) {
    64  *     function bartag_func( $atts ) {
    64 	global $shortcode_tags;
    65  *         $args = shortcode_atts( array(
    65 
    66  *             'foo' => 'no foo',
    66 	if ( '' == trim( $tag ) ) {
    67  *             'baz' => 'default baz',
    67 		$message = __( 'Invalid shortcode name: Empty name given.' );
    68  *         ), $atts );
    68 		_doing_it_wrong( __FUNCTION__, $message, '4.4.0' );
    69  *
    69 		return;
    70  *         return "foo = {$args['foo']}";
    70 	}
    71  *     }
    71 
    72  *     add_shortcode( 'bartag', 'bartag_func' );
    72 	if ( 0 !== preg_match( '@[<>&/\[\]\x00-\x20=]@', $tag ) ) {
    73  *
    73 		/* translators: 1: shortcode name, 2: space separated list of reserved characters */
    74  * Example with enclosed content:
    74 		$message = sprintf( __( 'Invalid shortcode name: %1$s. Do not use spaces or reserved characters: %2$s' ), $tag, '& / < > [ ] =' );
    75  *
    75 		_doing_it_wrong( __FUNCTION__, $message, '4.4.0' );
    76  *     // [baztag]content[/baztag]
    76 		return;
    77  *     function baztag_func( $atts, $content = '' ) {
    77 	}
    78  *         return "content = $content";
    78 
    79  *     }
    79 	$shortcode_tags[ $tag ] = $callback;
    80  *     add_shortcode( 'baztag', 'baztag_func' );
       
    81  *
       
    82  * @since 2.5.0
       
    83  *
       
    84  * @uses $shortcode_tags
       
    85  *
       
    86  * @param string $tag Shortcode tag to be searched in post content.
       
    87  * @param callable $func Hook to run when shortcode is found.
       
    88  */
       
    89 function add_shortcode($tag, $func) {
       
    90 	global $shortcode_tags;
       
    91 
       
    92 	if ( is_callable($func) )
       
    93 		$shortcode_tags[$tag] = $func;
       
    94 }
    80 }
    95 
    81 
    96 /**
    82 /**
    97  * Removes hook for shortcode.
    83  * Removes hook for shortcode.
    98  *
    84  *
    99  * @since 2.5.0
    85  * @since 2.5.0
   100  *
    86  *
   101  * @uses $shortcode_tags
    87  * @global array $shortcode_tags
   102  *
    88  *
   103  * @param string $tag Shortcode tag to remove hook for.
    89  * @param string $tag Shortcode tag to remove hook for.
   104  */
    90  */
   105 function remove_shortcode($tag) {
    91 function remove_shortcode($tag) {
   106 	global $shortcode_tags;
    92 	global $shortcode_tags;
   115  * shortcodes global by a empty array. This is actually a very efficient method
   101  * shortcodes global by a empty array. This is actually a very efficient method
   116  * for removing all shortcodes.
   102  * for removing all shortcodes.
   117  *
   103  *
   118  * @since 2.5.0
   104  * @since 2.5.0
   119  *
   105  *
   120  * @uses $shortcode_tags
   106  * @global array $shortcode_tags
   121  */
   107  */
   122 function remove_all_shortcodes() {
   108 function remove_all_shortcodes() {
   123 	global $shortcode_tags;
   109 	global $shortcode_tags;
   124 
   110 
   125 	$shortcode_tags = array();
   111 	$shortcode_tags = array();
   155 	if ( false === strpos( $content, '[' ) ) {
   141 	if ( false === strpos( $content, '[' ) ) {
   156 		return false;
   142 		return false;
   157 	}
   143 	}
   158 
   144 
   159 	if ( shortcode_exists( $tag ) ) {
   145 	if ( shortcode_exists( $tag ) ) {
   160 		preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER );
   146 		preg_match_all( '/' . get_shortcode_regex() . '/', $content, $matches, PREG_SET_ORDER );
   161 		if ( empty( $matches ) )
   147 		if ( empty( $matches ) )
   162 			return false;
   148 			return false;
   163 
   149 
   164 		foreach ( $matches as $shortcode ) {
   150 		foreach ( $matches as $shortcode ) {
   165 			if ( $tag === $shortcode[2] ) {
   151 			if ( $tag === $shortcode[2] ) {
   182  * @since 2.5.0
   168  * @since 2.5.0
   183  *
   169  *
   184  * @global array $shortcode_tags List of shortcode tags and their callback hooks.
   170  * @global array $shortcode_tags List of shortcode tags and their callback hooks.
   185  *
   171  *
   186  * @param string $content Content to search for shortcodes.
   172  * @param string $content Content to search for shortcodes.
       
   173  * @param bool $ignore_html When true, shortcodes inside HTML elements will be skipped.
   187  * @return string Content with shortcodes filtered out.
   174  * @return string Content with shortcodes filtered out.
   188  */
   175  */
   189 function do_shortcode($content) {
   176 function do_shortcode( $content, $ignore_html = false ) {
   190 	global $shortcode_tags;
   177 	global $shortcode_tags;
   191 
   178 
   192 	if ( false === strpos( $content, '[' ) ) {
   179 	if ( false === strpos( $content, '[' ) ) {
   193 		return $content;
   180 		return $content;
   194 	}
   181 	}
   195 
   182 
   196 	if (empty($shortcode_tags) || !is_array($shortcode_tags))
   183 	if (empty($shortcode_tags) || !is_array($shortcode_tags))
   197 		return $content;
   184 		return $content;
   198 
   185 
   199 	$pattern = get_shortcode_regex();
   186 	// Find all registered tag names in $content.
   200 	return preg_replace_callback( "/$pattern/s", 'do_shortcode_tag', $content );
   187 	preg_match_all( '@\[([^<>&/\[\]\x00-\x20=]++)@', $content, $matches );
       
   188 	$tagnames = array_intersect( array_keys( $shortcode_tags ), $matches[1] );
       
   189 
       
   190 	if ( empty( $tagnames ) ) {
       
   191 		return $content;
       
   192 	}
       
   193 
       
   194 	$content = do_shortcodes_in_html_tags( $content, $ignore_html, $tagnames );
       
   195 
       
   196 	$pattern = get_shortcode_regex( $tagnames );
       
   197 	$content = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $content );
       
   198 
       
   199 	// Always restore square braces so we don't break things like <!--[if IE ]>
       
   200 	$content = unescape_invalid_shortcodes( $content );
       
   201 
       
   202 	return $content;
   201 }
   203 }
   202 
   204 
   203 /**
   205 /**
   204  * Retrieve the shortcode regular expression for searching.
   206  * Retrieve the shortcode regular expression for searching.
   205  *
   207  *
   214  * 4 - The self closing /
   216  * 4 - The self closing /
   215  * 5 - The content of a shortcode when it wraps some content.
   217  * 5 - The content of a shortcode when it wraps some content.
   216  * 6 - An extra ] to allow for escaping shortcodes with double [[]]
   218  * 6 - An extra ] to allow for escaping shortcodes with double [[]]
   217  *
   219  *
   218  * @since 2.5.0
   220  * @since 2.5.0
   219  *
   221  * @since 4.4.0 Added the `$tagnames` parameter.
   220  * @uses $shortcode_tags
   222  *
   221  *
   223  * @global array $shortcode_tags
       
   224  *
       
   225  * @param array $tagnames Optional. List of shortcodes to find. Defaults to all registered shortcodes.
   222  * @return string The shortcode search regular expression
   226  * @return string The shortcode search regular expression
   223  */
   227  */
   224 function get_shortcode_regex() {
   228 function get_shortcode_regex( $tagnames = null ) {
   225 	global $shortcode_tags;
   229 	global $shortcode_tags;
   226 	$tagnames = array_keys($shortcode_tags);
   230 
       
   231 	if ( empty( $tagnames ) ) {
       
   232 		$tagnames = array_keys( $shortcode_tags );
       
   233 	}
   227 	$tagregexp = join( '|', array_map('preg_quote', $tagnames) );
   234 	$tagregexp = join( '|', array_map('preg_quote', $tagnames) );
   228 
   235 
   229 	// WARNING! Do not change this regex without changing do_shortcode_tag() and strip_shortcode_tag()
   236 	// WARNING! Do not change this regex without changing do_shortcode_tag() and strip_shortcode_tag()
   230 	// Also, see shortcode_unautop() and shortcode.js.
   237 	// Also, see shortcode_unautop() and shortcode.js.
   231 	return
   238 	return
   263  * Regular Expression callable for do_shortcode() for calling shortcode hook.
   270  * Regular Expression callable for do_shortcode() for calling shortcode hook.
   264  * @see get_shortcode_regex for details of the match array contents.
   271  * @see get_shortcode_regex for details of the match array contents.
   265  *
   272  *
   266  * @since 2.5.0
   273  * @since 2.5.0
   267  * @access private
   274  * @access private
   268  * @uses $shortcode_tags
   275  *
       
   276  * @global array $shortcode_tags
   269  *
   277  *
   270  * @param array $m Regular expression match array
   278  * @param array $m Regular expression match array
   271  * @return mixed False on failure.
   279  * @return string|false False on failure.
   272  */
   280  */
   273 function do_shortcode_tag( $m ) {
   281 function do_shortcode_tag( $m ) {
   274 	global $shortcode_tags;
   282 	global $shortcode_tags;
   275 
   283 
   276 	// allow [[foo]] syntax for escaping a tag
   284 	// allow [[foo]] syntax for escaping a tag
   279 	}
   287 	}
   280 
   288 
   281 	$tag = $m[2];
   289 	$tag = $m[2];
   282 	$attr = shortcode_parse_atts( $m[3] );
   290 	$attr = shortcode_parse_atts( $m[3] );
   283 
   291 
   284 	if ( isset( $m[5] ) ) {
   292 	if ( ! is_callable( $shortcode_tags[ $tag ] ) ) {
   285 		// enclosing tag - extra parameter
   293 		/* translators: %s: shortcode tag */
   286 		return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, $m[5], $tag ) . $m[6];
   294 		$message = sprintf( __( 'Attempting to parse a shortcode without a valid callback: %s' ), $tag );
   287 	} else {
   295 		_doing_it_wrong( __FUNCTION__, $message, '4.3.0' );
   288 		// self-closing tag
   296 		return $m[0];
   289 		return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, null,  $tag ) . $m[6];
   297 	}
   290 	}
   298 
       
   299 	/**
       
   300 	 * Filters whether to call a shortcode callback.
       
   301 	 *
       
   302 	 * Passing a truthy value to the filter will effectively short-circuit the
       
   303 	 * shortcode generation process, returning that value instead.
       
   304 	 *
       
   305 	 * @since 4.7.0
       
   306 	 *
       
   307 	 * @param bool|string $return      Short-circuit return value. Either false or the value to replace the shortcode with.
       
   308 	 * @param string       $tag         Shortcode name.
       
   309 	 * @param array|string $attr        Shortcode attributes array or empty string.
       
   310 	 * @param array        $m           Regular expression match array.
       
   311 	 */
       
   312 	$return = apply_filters( 'pre_do_shortcode_tag', false, $tag, $attr, $m );
       
   313 	if ( false !== $return ) {
       
   314 		return $return;
       
   315 	}
       
   316 
       
   317 	$content = isset( $m[5] ) ? $m[5] : null;
       
   318 
       
   319 	$output = $m[1] . call_user_func( $shortcode_tags[ $tag ], $attr, $content, $tag ) . $m[6];
       
   320 
       
   321 	/**
       
   322 	 * Filters the output created by a shortcode callback.
       
   323 	 *
       
   324 	 * @since 4.7.0
       
   325 	 *
       
   326 	 * @param string       $output Shortcode output.
       
   327 	 * @param string       $tag    Shortcode name.
       
   328 	 * @param array|string $attr   Shortcode attributes array or empty string.
       
   329 	 * @param array        $m      Regular expression match array.
       
   330 	 */
       
   331 	return apply_filters( 'do_shortcode_tag', $output, $tag, $attr, $m );
       
   332 }
       
   333 
       
   334 /**
       
   335  * Search only inside HTML elements for shortcodes and process them.
       
   336  *
       
   337  * Any [ or ] characters remaining inside elements will be HTML encoded
       
   338  * to prevent interference with shortcodes that are outside the elements.
       
   339  * Assumes $content processed by KSES already.  Users with unfiltered_html
       
   340  * capability may get unexpected output if angle braces are nested in tags.
       
   341  *
       
   342  * @since 4.2.3
       
   343  *
       
   344  * @param string $content Content to search for shortcodes
       
   345  * @param bool $ignore_html When true, all square braces inside elements will be encoded.
       
   346  * @param array $tagnames List of shortcodes to find.
       
   347  * @return string Content with shortcodes filtered out.
       
   348  */
       
   349 function do_shortcodes_in_html_tags( $content, $ignore_html, $tagnames ) {
       
   350 	// Normalize entities in unfiltered HTML before adding placeholders.
       
   351 	$trans = array( '&#91;' => '&#091;', '&#93;' => '&#093;' );
       
   352 	$content = strtr( $content, $trans );
       
   353 	$trans = array( '[' => '&#91;', ']' => '&#93;' );
       
   354 
       
   355 	$pattern = get_shortcode_regex( $tagnames );
       
   356 	$textarr = wp_html_split( $content );
       
   357 
       
   358 	foreach ( $textarr as &$element ) {
       
   359 		if ( '' == $element || '<' !== $element[0] ) {
       
   360 			continue;
       
   361 		}
       
   362 
       
   363 		$noopen = false === strpos( $element, '[' );
       
   364 		$noclose = false === strpos( $element, ']' );
       
   365 		if ( $noopen || $noclose ) {
       
   366 			// This element does not contain shortcodes.
       
   367 			if ( $noopen xor $noclose ) {
       
   368 				// Need to encode stray [ or ] chars.
       
   369 				$element = strtr( $element, $trans );
       
   370 			}
       
   371 			continue;
       
   372 		}
       
   373 
       
   374 		if ( $ignore_html || '<!--' === substr( $element, 0, 4 ) || '<![CDATA[' === substr( $element, 0, 9 ) ) {
       
   375 			// Encode all [ and ] chars.
       
   376 			$element = strtr( $element, $trans );
       
   377 			continue;
       
   378 		}
       
   379 
       
   380 		$attributes = wp_kses_attr_parse( $element );
       
   381 		if ( false === $attributes ) {
       
   382 			// Some plugins are doing things like [name] <[email]>.
       
   383 			if ( 1 === preg_match( '%^<\s*\[\[?[^\[\]]+\]%', $element ) ) {
       
   384 				$element = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $element );
       
   385 			}
       
   386 
       
   387 			// Looks like we found some crazy unfiltered HTML.  Skipping it for sanity.
       
   388 			$element = strtr( $element, $trans );
       
   389 			continue;
       
   390 		}
       
   391 
       
   392 		// Get element name
       
   393 		$front = array_shift( $attributes );
       
   394 		$back = array_pop( $attributes );
       
   395 		$matches = array();
       
   396 		preg_match('%[a-zA-Z0-9]+%', $front, $matches);
       
   397 		$elname = $matches[0];
       
   398 
       
   399 		// Look for shortcodes in each attribute separately.
       
   400 		foreach ( $attributes as &$attr ) {
       
   401 			$open = strpos( $attr, '[' );
       
   402 			$close = strpos( $attr, ']' );
       
   403 			if ( false === $open || false === $close ) {
       
   404 				continue; // Go to next attribute.  Square braces will be escaped at end of loop.
       
   405 			}
       
   406 			$double = strpos( $attr, '"' );
       
   407 			$single = strpos( $attr, "'" );
       
   408 			if ( ( false === $single || $open < $single ) && ( false === $double || $open < $double ) ) {
       
   409 				// $attr like '[shortcode]' or 'name = [shortcode]' implies unfiltered_html.
       
   410 				// In this specific situation we assume KSES did not run because the input
       
   411 				// was written by an administrator, so we should avoid changing the output
       
   412 				// and we do not need to run KSES here.
       
   413 				$attr = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $attr );
       
   414 			} else {
       
   415 				// $attr like 'name = "[shortcode]"' or "name = '[shortcode]'"
       
   416 				// We do not know if $content was unfiltered. Assume KSES ran before shortcodes.
       
   417 				$count = 0;
       
   418 				$new_attr = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $attr, -1, $count );
       
   419 				if ( $count > 0 ) {
       
   420 					// Sanitize the shortcode output using KSES.
       
   421 					$new_attr = wp_kses_one_attr( $new_attr, $elname );
       
   422 					if ( '' !== trim( $new_attr ) ) {
       
   423 						// The shortcode is safe to use now.
       
   424 						$attr = $new_attr;
       
   425 					}
       
   426 				}
       
   427 			}
       
   428 		}
       
   429 		$element = $front . implode( '', $attributes ) . $back;
       
   430 
       
   431 		// Now encode any remaining [ or ] chars.
       
   432 		$element = strtr( $element, $trans );
       
   433 	}
       
   434 
       
   435 	$content = implode( '', $textarr );
       
   436 
       
   437 	return $content;
       
   438 }
       
   439 
       
   440 /**
       
   441  * Remove placeholders added by do_shortcodes_in_html_tags().
       
   442  *
       
   443  * @since 4.2.3
       
   444  *
       
   445  * @param string $content Content to search for placeholders.
       
   446  * @return string Content with placeholders removed.
       
   447  */
       
   448 function unescape_invalid_shortcodes( $content ) {
       
   449         // Clean up entire string, avoids re-parsing HTML.
       
   450         $trans = array( '&#91;' => '[', '&#93;' => ']' );
       
   451         $content = strtr( $content, $trans );
       
   452 
       
   453         return $content;
       
   454 }
       
   455 
       
   456 /**
       
   457  * Retrieve the shortcode attributes regex.
       
   458  *
       
   459  * @since 4.4.0
       
   460  *
       
   461  * @return string The shortcode attribute regular expression
       
   462  */
       
   463 function get_shortcode_atts_regex() {
       
   464 	return '/([\w-]+)\s*=\s*"([^"]*)"(?:\s|$)|([\w-]+)\s*=\s*\'([^\']*)\'(?:\s|$)|([\w-]+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|\'([^\']*)\'(?:\s|$)|(\S+)(?:\s|$)/';
   291 }
   465 }
   292 
   466 
   293 /**
   467 /**
   294  * Retrieve all attributes from the shortcodes tag.
   468  * Retrieve all attributes from the shortcodes tag.
   295  *
   469  *
   298  * retrieval of the attributes, since all attributes have to be known.
   472  * retrieval of the attributes, since all attributes have to be known.
   299  *
   473  *
   300  * @since 2.5.0
   474  * @since 2.5.0
   301  *
   475  *
   302  * @param string $text
   476  * @param string $text
   303  * @return array List of attributes and their value.
   477  * @return array|string List of attribute values.
       
   478  *                      Returns empty array if trim( $text ) == '""'.
       
   479  *                      Returns empty string if trim( $text ) == ''.
       
   480  *                      All other matches are checked for not empty().
   304  */
   481  */
   305 function shortcode_parse_atts($text) {
   482 function shortcode_parse_atts($text) {
   306 	$atts = array();
   483 	$atts = array();
   307 	$pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/';
   484 	$pattern = get_shortcode_atts_regex();
   308 	$text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text);
   485 	$text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text);
   309 	if ( preg_match_all($pattern, $text, $match, PREG_SET_ORDER) ) {
   486 	if ( preg_match_all($pattern, $text, $match, PREG_SET_ORDER) ) {
   310 		foreach ($match as $m) {
   487 		foreach ($match as $m) {
   311 			if (!empty($m[1]))
   488 			if (!empty($m[1]))
   312 				$atts[strtolower($m[1])] = stripcslashes($m[2]);
   489 				$atts[strtolower($m[1])] = stripcslashes($m[2]);
   314 				$atts[strtolower($m[3])] = stripcslashes($m[4]);
   491 				$atts[strtolower($m[3])] = stripcslashes($m[4]);
   315 			elseif (!empty($m[5]))
   492 			elseif (!empty($m[5]))
   316 				$atts[strtolower($m[5])] = stripcslashes($m[6]);
   493 				$atts[strtolower($m[5])] = stripcslashes($m[6]);
   317 			elseif (isset($m[7]) && strlen($m[7]))
   494 			elseif (isset($m[7]) && strlen($m[7]))
   318 				$atts[] = stripcslashes($m[7]);
   495 				$atts[] = stripcslashes($m[7]);
   319 			elseif (isset($m[8]))
   496 			elseif (isset($m[8]) && strlen($m[8]))
   320 				$atts[] = stripcslashes($m[8]);
   497 				$atts[] = stripcslashes($m[8]);
       
   498 			elseif (isset($m[9]))
       
   499 				$atts[] = stripcslashes($m[9]);
       
   500 		}
       
   501 
       
   502 		// Reject any unclosed HTML elements
       
   503 		foreach( $atts as &$value ) {
       
   504 			if ( false !== strpos( $value, '<' ) ) {
       
   505 				if ( 1 !== preg_match( '/^[^<]*+(?:<[^>]*+>[^<]*+)*+$/', $value ) ) {
       
   506 					$value = '';
       
   507 				}
       
   508 			}
   321 		}
   509 		}
   322 	} else {
   510 	} else {
   323 		$atts = ltrim($text);
   511 		$atts = ltrim($text);
   324 	}
   512 	}
   325 	return $atts;
   513 	return $atts;
   335  * If the $atts list has unsupported attributes, then they will be ignored and
   523  * If the $atts list has unsupported attributes, then they will be ignored and
   336  * removed from the final returned list.
   524  * removed from the final returned list.
   337  *
   525  *
   338  * @since 2.5.0
   526  * @since 2.5.0
   339  *
   527  *
   340  * @param array $pairs Entire list of supported attributes and their defaults.
   528  * @param array  $pairs     Entire list of supported attributes and their defaults.
   341  * @param array $atts User defined attributes in shortcode tag.
   529  * @param array  $atts      User defined attributes in shortcode tag.
   342  * @param string $shortcode Optional. The name of the shortcode, provided for context to enable filtering
   530  * @param string $shortcode Optional. The name of the shortcode, provided for context to enable filtering
   343  * @return array Combined and filtered attribute list.
   531  * @return array Combined and filtered attribute list.
   344  */
   532  */
   345 function shortcode_atts( $pairs, $atts, $shortcode = '' ) {
   533 function shortcode_atts( $pairs, $atts, $shortcode = '' ) {
   346 	$atts = (array)$atts;
   534 	$atts = (array)$atts;
   347 	$out = array();
   535 	$out = array();
   348 	foreach($pairs as $name => $default) {
   536 	foreach ($pairs as $name => $default) {
   349 		if ( array_key_exists($name, $atts) )
   537 		if ( array_key_exists($name, $atts) )
   350 			$out[$name] = $atts[$name];
   538 			$out[$name] = $atts[$name];
   351 		else
   539 		else
   352 			$out[$name] = $default;
   540 			$out[$name] = $default;
   353 	}
   541 	}
   354 	/**
   542 	/**
   355 	 * Filter a shortcode's default attributes.
   543 	 * Filters a shortcode's default attributes.
   356 	 *
   544 	 *
   357 	 * If the third parameter of the shortcode_atts() function is present then this filter is available.
   545 	 * If the third parameter of the shortcode_atts() function is present then this filter is available.
   358 	 * The third parameter, $shortcode, is the name of the shortcode.
   546 	 * The third parameter, $shortcode, is the name of the shortcode.
   359 	 *
   547 	 *
   360 	 * @since 3.6.0
   548 	 * @since 3.6.0
   361 	 *
   549 	 * @since 4.4.0 Added the `$shortcode` parameter.
   362 	 * @param array $out The output array of shortcode attributes.
   550 	 *
   363 	 * @param array $pairs The supported attributes and their defaults.
   551 	 * @param array  $out       The output array of shortcode attributes.
   364 	 * @param array $atts The user defined shortcode attributes.
   552 	 * @param array  $pairs     The supported attributes and their defaults.
       
   553 	 * @param array  $atts      The user defined shortcode attributes.
       
   554 	 * @param string $shortcode The shortcode name.
   365 	 */
   555 	 */
   366 	if ( $shortcode )
   556 	if ( $shortcode ) {
   367 		$out = apply_filters( "shortcode_atts_{$shortcode}", $out, $pairs, $atts );
   557 		$out = apply_filters( "shortcode_atts_{$shortcode}", $out, $pairs, $atts, $shortcode );
       
   558 	}
   368 
   559 
   369 	return $out;
   560 	return $out;
   370 }
   561 }
   371 
   562 
   372 /**
   563 /**
   373  * Remove all shortcode tags from the given content.
   564  * Remove all shortcode tags from the given content.
   374  *
   565  *
   375  * @since 2.5.0
   566  * @since 2.5.0
   376  *
   567  *
   377  * @uses $shortcode_tags
   568  * @global array $shortcode_tags
   378  *
   569  *
   379  * @param string $content Content to remove shortcode tags.
   570  * @param string $content Content to remove shortcode tags.
   380  * @return string Content without shortcode tags.
   571  * @return string Content without shortcode tags.
   381  */
   572  */
   382 function strip_shortcodes( $content ) {
   573 function strip_shortcodes( $content ) {
   387 	}
   578 	}
   388 
   579 
   389 	if (empty($shortcode_tags) || !is_array($shortcode_tags))
   580 	if (empty($shortcode_tags) || !is_array($shortcode_tags))
   390 		return $content;
   581 		return $content;
   391 
   582 
   392 	$pattern = get_shortcode_regex();
   583 	// Find all registered tag names in $content.
   393 
   584 	preg_match_all( '@\[([^<>&/\[\]\x00-\x20=]++)@', $content, $matches );
   394 	return preg_replace_callback( "/$pattern/s", 'strip_shortcode_tag', $content );
   585 
   395 }
   586 	$tags_to_remove = array_keys( $shortcode_tags );
   396 
   587 
       
   588 	/**
       
   589 	 * Filters the list of shortcode tags to remove from the content.
       
   590 	 *
       
   591 	 * @since 4.7.0
       
   592 	 *
       
   593 	 * @param array  $tag_array Array of shortcode tags to remove.
       
   594 	 * @param string $content   Content shortcodes are being removed from.
       
   595 	 */
       
   596 	$tags_to_remove = apply_filters( 'strip_shortcodes_tagnames', $tags_to_remove, $content );
       
   597 
       
   598 	$tagnames = array_intersect( $tags_to_remove, $matches[1] );
       
   599 
       
   600 	if ( empty( $tagnames ) ) {
       
   601 		return $content;
       
   602 	}
       
   603 
       
   604 	$content = do_shortcodes_in_html_tags( $content, true, $tagnames );
       
   605 
       
   606 	$pattern = get_shortcode_regex( $tagnames );
       
   607 	$content = preg_replace_callback( "/$pattern/", 'strip_shortcode_tag', $content );
       
   608 
       
   609 	// Always restore square braces so we don't break things like <!--[if IE ]>
       
   610 	$content = unescape_invalid_shortcodes( $content );
       
   611 
       
   612 	return $content;
       
   613 }
       
   614 
       
   615 /**
       
   616  * Strips a shortcode tag based on RegEx matches against post content.
       
   617  *
       
   618  * @since 3.3.0
       
   619  *
       
   620  * @param array $m RegEx matches against post content.
       
   621  * @return string|false The content stripped of the tag, otherwise false.
       
   622  */
   397 function strip_shortcode_tag( $m ) {
   623 function strip_shortcode_tag( $m ) {
   398 	// allow [[foo]] syntax for escaping a tag
   624 	// allow [[foo]] syntax for escaping a tag
   399 	if ( $m[1] == '[' && $m[6] == ']' ) {
   625 	if ( $m[1] == '[' && $m[6] == ']' ) {
   400 		return substr($m[0], 1, -1);
   626 		return substr($m[0], 1, -1);
   401 	}
   627 	}