home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / shortcodes.php < prev    next >
Encoding:
PHP Script  |  2017-08-20  |  20.0 KB  |  631 lines

  1. <?php
  2. /**
  3.  * WordPress API for creating bbcode-like tags or what WordPress calls
  4.  * "shortcodes". The tag and attribute parsing or regular expression code is
  5.  * based on the Textpattern tag parser.
  6.  *
  7.  * A few examples are below:
  8.  *
  9.  * [shortcode /]
  10.  * [shortcode foo="bar" baz="bing" /]
  11.  * [shortcode foo="bar"]content[/shortcode]
  12.  *
  13.  * Shortcode tags support attributes and enclosed content, but does not entirely
  14.  * support inline shortcodes in other shortcodes. You will have to call the
  15.  * shortcode parser in your function to account for that.
  16.  *
  17.  * {@internal
  18.  * Please be aware that the above note was made during the beta of WordPress 2.6
  19.  * and in the future may not be accurate. Please update the note when it is no
  20.  * longer the case.}}
  21.  *
  22.  * To apply shortcode tags to content:
  23.  *
  24.  *     $out = do_shortcode( $content );
  25.  *
  26.  * @link https://codex.wordpress.org/Shortcode_API
  27.  *
  28.  * @package WordPress
  29.  * @subpackage Shortcodes
  30.  * @since 2.5.0
  31.  */
  32.  
  33. /**
  34.  * Container for storing shortcode tags and their hook to call for the shortcode
  35.  *
  36.  * @since 2.5.0
  37.  *
  38.  * @name $shortcode_tags
  39.  * @var array
  40.  * @global array $shortcode_tags
  41.  */
  42. $shortcode_tags = array();
  43.  
  44. /**
  45.  * Adds a new shortcode.
  46.  *
  47.  * Care should be taken through prefixing or other means to ensure that the
  48.  * shortcode tag being added is unique and will not conflict with other,
  49.  * already-added shortcode tags. In the event of a duplicated tag, the tag
  50.  * loaded last will take precedence.
  51.  *
  52.  * @since 2.5.0
  53.  *
  54.  * @global array $shortcode_tags
  55.  *
  56.  * @param string   $tag      Shortcode tag to be searched in post content.
  57.  * @param callable $callback The callback function to run when the shortcode is found.
  58.  *                           Every shortcode callback is passed three parameters by default,
  59.  *                           including an array of attributes (`$atts`), the shortcode content
  60.  *                           or null if not set (`$content`), and finally the shortcode tag
  61.  *                           itself (`$shortcode_tag`), in that order.
  62.  */
  63. function add_shortcode( $tag, $callback ) {
  64.     global $shortcode_tags;
  65.  
  66.     if ( '' == trim( $tag ) ) {
  67.         $message = __( 'Invalid shortcode name: Empty name given.' );
  68.         _doing_it_wrong( __FUNCTION__, $message, '4.4.0' );
  69.         return;
  70.     }
  71.  
  72.     if ( 0 !== preg_match( '@[<>&/\[\]\x00-\x20=]@', $tag ) ) {
  73.         /* translators: 1: shortcode name, 2: space separated list of reserved characters */
  74.         $message = sprintf( __( 'Invalid shortcode name: %1$s. Do not use spaces or reserved characters: %2$s' ), $tag, '& / < > [ ] =' );
  75.         _doing_it_wrong( __FUNCTION__, $message, '4.4.0' );
  76.         return;
  77.     }
  78.  
  79.     $shortcode_tags[ $tag ] = $callback;
  80. }
  81.  
  82. /**
  83.  * Removes hook for shortcode.
  84.  *
  85.  * @since 2.5.0
  86.  *
  87.  * @global array $shortcode_tags
  88.  *
  89.  * @param string $tag Shortcode tag to remove hook for.
  90.  */
  91. function remove_shortcode($tag) {
  92.     global $shortcode_tags;
  93.  
  94.     unset($shortcode_tags[$tag]);
  95. }
  96.  
  97. /**
  98.  * Clear all shortcodes.
  99.  *
  100.  * This function is simple, it clears all of the shortcode tags by replacing the
  101.  * shortcodes global by a empty array. This is actually a very efficient method
  102.  * for removing all shortcodes.
  103.  *
  104.  * @since 2.5.0
  105.  *
  106.  * @global array $shortcode_tags
  107.  */
  108. function remove_all_shortcodes() {
  109.     global $shortcode_tags;
  110.  
  111.     $shortcode_tags = array();
  112. }
  113.  
  114. /**
  115.  * Whether a registered shortcode exists named $tag
  116.  *
  117.  * @since 3.6.0
  118.  *
  119.  * @global array $shortcode_tags List of shortcode tags and their callback hooks.
  120.  *
  121.  * @param string $tag Shortcode tag to check.
  122.  * @return bool Whether the given shortcode exists.
  123.  */
  124. function shortcode_exists( $tag ) {
  125.     global $shortcode_tags;
  126.     return array_key_exists( $tag, $shortcode_tags );
  127. }
  128.  
  129. /**
  130.  * Whether the passed content contains the specified shortcode
  131.  *
  132.  * @since 3.6.0
  133.  *
  134.  * @global array $shortcode_tags
  135.  *
  136.  * @param string $content Content to search for shortcodes.
  137.  * @param string $tag     Shortcode tag to check.
  138.  * @return bool Whether the passed content contains the given shortcode.
  139.  */
  140. function has_shortcode( $content, $tag ) {
  141.     if ( false === strpos( $content, '[' ) ) {
  142.         return false;
  143.     }
  144.  
  145.     if ( shortcode_exists( $tag ) ) {
  146.         preg_match_all( '/' . get_shortcode_regex() . '/', $content, $matches, PREG_SET_ORDER );
  147.         if ( empty( $matches ) )
  148.             return false;
  149.  
  150.         foreach ( $matches as $shortcode ) {
  151.             if ( $tag === $shortcode[2] ) {
  152.                 return true;
  153.             } elseif ( ! empty( $shortcode[5] ) && has_shortcode( $shortcode[5], $tag ) ) {
  154.                 return true;
  155.             }
  156.         }
  157.     }
  158.     return false;
  159. }
  160.  
  161. /**
  162.  * Search content for shortcodes and filter shortcodes through their hooks.
  163.  *
  164.  * If there are no shortcode tags defined, then the content will be returned
  165.  * without any filtering. This might cause issues when plugins are disabled but
  166.  * the shortcode will still show up in the post or content.
  167.  *
  168.  * @since 2.5.0
  169.  *
  170.  * @global array $shortcode_tags List of shortcode tags and their callback hooks.
  171.  *
  172.  * @param string $content Content to search for shortcodes.
  173.  * @param bool $ignore_html When true, shortcodes inside HTML elements will be skipped.
  174.  * @return string Content with shortcodes filtered out.
  175.  */
  176. function do_shortcode( $content, $ignore_html = false ) {
  177.     global $shortcode_tags;
  178.  
  179.     if ( false === strpos( $content, '[' ) ) {
  180.         return $content;
  181.     }
  182.  
  183.     if (empty($shortcode_tags) || !is_array($shortcode_tags))
  184.         return $content;
  185.  
  186.     // Find all registered tag names in $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;
  203. }
  204.  
  205. /**
  206.  * Retrieve the shortcode regular expression for searching.
  207.  *
  208.  * The regular expression combines the shortcode tags in the regular expression
  209.  * in a regex class.
  210.  *
  211.  * The regular expression contains 6 different sub matches to help with parsing.
  212.  *
  213.  * 1 - An extra [ to allow for escaping shortcodes with double [[]]
  214.  * 2 - The shortcode name
  215.  * 3 - The shortcode argument list
  216.  * 4 - The self closing /
  217.  * 5 - The content of a shortcode when it wraps some content.
  218.  * 6 - An extra ] to allow for escaping shortcodes with double [[]]
  219.  *
  220.  * @since 2.5.0
  221.  * @since 4.4.0 Added the `$tagnames` parameter.
  222.  *
  223.  * @global array $shortcode_tags
  224.  *
  225.  * @param array $tagnames Optional. List of shortcodes to find. Defaults to all registered shortcodes.
  226.  * @return string The shortcode search regular expression
  227.  */
  228. function get_shortcode_regex( $tagnames = null ) {
  229.     global $shortcode_tags;
  230.  
  231.     if ( empty( $tagnames ) ) {
  232.         $tagnames = array_keys( $shortcode_tags );
  233.     }
  234.     $tagregexp = join( '|', array_map('preg_quote', $tagnames) );
  235.  
  236.     // WARNING! Do not change this regex without changing do_shortcode_tag() and strip_shortcode_tag()
  237.     // Also, see shortcode_unautop() and shortcode.js.
  238.     return
  239.           '\\['                              // Opening bracket
  240.         . '(\\[?)'                           // 1: Optional second opening bracket for escaping shortcodes: [[tag]]
  241.         . "($tagregexp)"                     // 2: Shortcode name
  242.         . '(?![\\w-])'                       // Not followed by word character or hyphen
  243.         . '('                                // 3: Unroll the loop: Inside the opening shortcode tag
  244.         .     '[^\\]\\/]*'                   // Not a closing bracket or forward slash
  245.         .     '(?:'
  246.         .         '\\/(?!\\])'               // A forward slash not followed by a closing bracket
  247.         .         '[^\\]\\/]*'               // Not a closing bracket or forward slash
  248.         .     ')*?'
  249.         . ')'
  250.         . '(?:'
  251.         .     '(\\/)'                        // 4: Self closing tag ...
  252.         .     '\\]'                          // ... and closing bracket
  253.         . '|'
  254.         .     '\\]'                          // Closing bracket
  255.         .     '(?:'
  256.         .         '('                        // 5: Unroll the loop: Optionally, anything between the opening and closing shortcode tags
  257.         .             '[^\\[]*+'             // Not an opening bracket
  258.         .             '(?:'
  259.         .                 '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag
  260.         .                 '[^\\[]*+'         // Not an opening bracket
  261.         .             ')*+'
  262.         .         ')'
  263.         .         '\\[\\/\\2\\]'             // Closing shortcode tag
  264.         .     ')?'
  265.         . ')'
  266.         . '(\\]?)';                          // 6: Optional second closing brocket for escaping shortcodes: [[tag]]
  267. }
  268.  
  269. /**
  270.  * Regular Expression callable for do_shortcode() for calling shortcode hook.
  271.  * @see get_shortcode_regex for details of the match array contents.
  272.  *
  273.  * @since 2.5.0
  274.  * @access private
  275.  *
  276.  * @global array $shortcode_tags
  277.  *
  278.  * @param array $m Regular expression match array
  279.  * @return string|false False on failure.
  280.  */
  281. function do_shortcode_tag( $m ) {
  282.     global $shortcode_tags;
  283.  
  284.     // allow [[foo]] syntax for escaping a tag
  285.     if ( $m[1] == '[' && $m[6] == ']' ) {
  286.         return substr($m[0], 1, -1);
  287.     }
  288.  
  289.     $tag = $m[2];
  290.     $attr = shortcode_parse_atts( $m[3] );
  291.  
  292.     if ( ! is_callable( $shortcode_tags[ $tag ] ) ) {
  293.         /* translators: %s: shortcode tag */
  294.         $message = sprintf( __( 'Attempting to parse a shortcode without a valid callback: %s' ), $tag );
  295.         _doing_it_wrong( __FUNCTION__, $message, '4.3.0' );
  296.         return $m[0];
  297.     }
  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( '[' => '[', ']' => ']' );
  352.     $content = strtr( $content, $trans );
  353.     $trans = array( '[' => '[', ']' => ']' );
  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( '[' => '[', ']' => ']' );
  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|$)/';
  465. }
  466.  
  467. /**
  468.  * Retrieve all attributes from the shortcodes tag.
  469.  *
  470.  * The attributes list has the attribute name as the key and the value of the
  471.  * attribute as the value in the key/value pair. This allows for easier
  472.  * retrieval of the attributes, since all attributes have to be known.
  473.  *
  474.  * @since 2.5.0
  475.  *
  476.  * @param string $text
  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().
  481.  */
  482. function shortcode_parse_atts($text) {
  483.     $atts = array();
  484.     $pattern = get_shortcode_atts_regex();
  485.     $text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text);
  486.     if ( preg_match_all($pattern, $text, $match, PREG_SET_ORDER) ) {
  487.         foreach ($match as $m) {
  488.             if (!empty($m[1]))
  489.                 $atts[strtolower($m[1])] = stripcslashes($m[2]);
  490.             elseif (!empty($m[3]))
  491.                 $atts[strtolower($m[3])] = stripcslashes($m[4]);
  492.             elseif (!empty($m[5]))
  493.                 $atts[strtolower($m[5])] = stripcslashes($m[6]);
  494.             elseif (isset($m[7]) && strlen($m[7]))
  495.                 $atts[] = stripcslashes($m[7]);
  496.             elseif (isset($m[8]) && strlen($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.             }
  509.         }
  510.     } else {
  511.         $atts = ltrim($text);
  512.     }
  513.     return $atts;
  514. }
  515.  
  516. /**
  517.  * Combine user attributes with known attributes and fill in defaults when needed.
  518.  *
  519.  * The pairs should be considered to be all of the attributes which are
  520.  * supported by the caller and given as a list. The returned attributes will
  521.  * only contain the attributes in the $pairs list.
  522.  *
  523.  * If the $atts list has unsupported attributes, then they will be ignored and
  524.  * removed from the final returned list.
  525.  *
  526.  * @since 2.5.0
  527.  *
  528.  * @param array  $pairs     Entire list of supported attributes and their defaults.
  529.  * @param array  $atts      User defined attributes in shortcode tag.
  530.  * @param string $shortcode Optional. The name of the shortcode, provided for context to enable filtering
  531.  * @return array Combined and filtered attribute list.
  532.  */
  533. function shortcode_atts( $pairs, $atts, $shortcode = '' ) {
  534.     $atts = (array)$atts;
  535.     $out = array();
  536.     foreach ($pairs as $name => $default) {
  537.         if ( array_key_exists($name, $atts) )
  538.             $out[$name] = $atts[$name];
  539.         else
  540.             $out[$name] = $default;
  541.     }
  542.     /**
  543.      * Filters a shortcode's default attributes.
  544.      *
  545.      * If the third parameter of the shortcode_atts() function is present then this filter is available.
  546.      * The third parameter, $shortcode, is the name of the shortcode.
  547.      *
  548.      * @since 3.6.0
  549.      * @since 4.4.0 Added the `$shortcode` parameter.
  550.      *
  551.      * @param array  $out       The output array of 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.
  555.      */
  556.     if ( $shortcode ) {
  557.         $out = apply_filters( "shortcode_atts_{$shortcode}", $out, $pairs, $atts, $shortcode );
  558.     }
  559.  
  560.     return $out;
  561. }
  562.  
  563. /**
  564.  * Remove all shortcode tags from the given content.
  565.  *
  566.  * @since 2.5.0
  567.  *
  568.  * @global array $shortcode_tags
  569.  *
  570.  * @param string $content Content to remove shortcode tags.
  571.  * @return string Content without shortcode tags.
  572.  */
  573. function strip_shortcodes( $content ) {
  574.     global $shortcode_tags;
  575.  
  576.     if ( false === strpos( $content, '[' ) ) {
  577.         return $content;
  578.     }
  579.  
  580.     if (empty($shortcode_tags) || !is_array($shortcode_tags))
  581.         return $content;
  582.  
  583.     // Find all registered tag names in $content.
  584.     preg_match_all( '@\[([^<>&/\[\]\x00-\x20=]++)@', $content, $matches );
  585.  
  586.     $tags_to_remove = array_keys( $shortcode_tags );
  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.  */
  623. function strip_shortcode_tag( $m ) {
  624.     // allow [[foo]] syntax for escaping a tag
  625.     if ( $m[1] == '[' && $m[6] == ']' ) {
  626.         return substr($m[0], 1, -1);
  627.     }
  628.  
  629.     return $m[1] . $m[6];
  630. }
  631.