/wp-includes/shortcodes.php

  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. * Add hook for shortcode tag. 
  46. * 
  47. * There can only be one hook for each shortcode. Which means that if another 
  48. * plugin has a similar shortcode, it will override yours or yours will override 
  49. * theirs depending on which order the plugins are included and/or ran. 
  50. * 
  51. * Simplest example of a shortcode tag using the API: 
  52. * 
  53. * // [footag foo="bar"] 
  54. * function footag_func( $atts ) { 
  55. * return "foo = { 
  56. * $atts[foo] 
  57. * }"; 
  58. * } 
  59. * add_shortcode( 'footag', 'footag_func' ); 
  60. * 
  61. * Example with nice attribute defaults: 
  62. * 
  63. * // [bartag foo="bar"] 
  64. * function bartag_func( $atts ) { 
  65. * $args = shortcode_atts( array( 
  66. * 'foo' => 'no foo',  
  67. * 'baz' => 'default baz',  
  68. * ), $atts ); 
  69. * 
  70. * return "foo = {$args['foo']}"; 
  71. * } 
  72. * add_shortcode( 'bartag', 'bartag_func' ); 
  73. * 
  74. * Example with enclosed content: 
  75. * 
  76. * // [baztag]content[/baztag] 
  77. * function baztag_func( $atts, $content = '' ) { 
  78. * return "content = $content"; 
  79. * } 
  80. * add_shortcode( 'baztag', 'baztag_func' ); 
  81. * 
  82. * @since 2.5.0 
  83. * 
  84. * @global array $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 ( '' == trim( $tag ) ) { 
  93. $message = __( 'Invalid shortcode name: Empty name given.' ); 
  94. _doing_it_wrong( __FUNCTION__, $message, '4.4.0' ); 
  95. return; 
  96.  
  97. if ( 0 !== preg_match( '@[<>&/\[\]\x00-\x20=]@', $tag ) ) { 
  98. /** translators: 1: shortcode name, 2: space separated list of reserved characters */ 
  99. $message = sprintf( __( 'Invalid shortcode name: %1$s. Do not use spaces or reserved characters: %2$s' ), $tag, '& / < > [ ] =' ); 
  100. _doing_it_wrong( __FUNCTION__, $message, '4.4.0' ); 
  101. return; 
  102.  
  103. $shortcode_tags[ $tag ] = $func; 
  104.  
  105. /** 
  106. * Removes hook for shortcode. 
  107. * 
  108. * @since 2.5.0 
  109. * 
  110. * @global array $shortcode_tags 
  111. * 
  112. * @param string $tag Shortcode tag to remove hook for. 
  113. */ 
  114. function remove_shortcode($tag) { 
  115. global $shortcode_tags; 
  116.  
  117. unset($shortcode_tags[$tag]); 
  118.  
  119. /** 
  120. * Clear all shortcodes. 
  121. * 
  122. * This function is simple, it clears all of the shortcode tags by replacing the 
  123. * shortcodes global by a empty array. This is actually a very efficient method 
  124. * for removing all shortcodes. 
  125. * 
  126. * @since 2.5.0 
  127. * 
  128. * @global array $shortcode_tags 
  129. */ 
  130. function remove_all_shortcodes() { 
  131. global $shortcode_tags; 
  132.  
  133. $shortcode_tags = array(); 
  134.  
  135. /** 
  136. * Whether a registered shortcode exists named $tag 
  137. * 
  138. * @since 3.6.0 
  139. * 
  140. * @global array $shortcode_tags List of shortcode tags and their callback hooks. 
  141. * 
  142. * @param string $tag Shortcode tag to check. 
  143. * @return bool Whether the given shortcode exists. 
  144. */ 
  145. function shortcode_exists( $tag ) { 
  146. global $shortcode_tags; 
  147. return array_key_exists( $tag, $shortcode_tags ); 
  148.  
  149. /** 
  150. * Whether the passed content contains the specified shortcode 
  151. * 
  152. * @since 3.6.0 
  153. * 
  154. * @global array $shortcode_tags 
  155. * 
  156. * @param string $content Content to search for shortcodes. 
  157. * @param string $tag Shortcode tag to check. 
  158. * @return bool Whether the passed content contains the given shortcode. 
  159. */ 
  160. function has_shortcode( $content, $tag ) { 
  161. if ( false === strpos( $content, '[' ) ) { 
  162. return false; 
  163.  
  164. if ( shortcode_exists( $tag ) ) { 
  165. preg_match_all( '/' . get_shortcode_regex() . '/', $content, $matches, PREG_SET_ORDER ); 
  166. if ( empty( $matches ) ) 
  167. return false; 
  168.  
  169. foreach ( $matches as $shortcode ) { 
  170. if ( $tag === $shortcode[2] ) { 
  171. return true; 
  172. } elseif ( ! empty( $shortcode[5] ) && has_shortcode( $shortcode[5], $tag ) ) { 
  173. return true; 
  174. return false; 
  175.  
  176. /** 
  177. * Search content for shortcodes and filter shortcodes through their hooks. 
  178. * 
  179. * If there are no shortcode tags defined, then the content will be returned 
  180. * without any filtering. This might cause issues when plugins are disabled but 
  181. * the shortcode will still show up in the post or content. 
  182. * 
  183. * @since 2.5.0 
  184. * 
  185. * @global array $shortcode_tags List of shortcode tags and their callback hooks. 
  186. * 
  187. * @param string $content Content to search for shortcodes. 
  188. * @param bool $ignore_html When true, shortcodes inside HTML elements will be skipped. 
  189. * @return string Content with shortcodes filtered out. 
  190. */ 
  191. function do_shortcode( $content, $ignore_html = false ) { 
  192. global $shortcode_tags; 
  193.  
  194. if ( false === strpos( $content, '[' ) ) { 
  195. return $content; 
  196.  
  197. if (empty($shortcode_tags) || !is_array($shortcode_tags)) 
  198. return $content; 
  199.  
  200. // Find all registered tag names in $content. 
  201. preg_match_all( '@\[([^<>&/\[\]\x00-\x20=]++)@', $content, $matches ); 
  202. $tagnames = array_intersect( array_keys( $shortcode_tags ), $matches[1] ); 
  203.  
  204. if ( empty( $tagnames ) ) { 
  205. return $content; 
  206.  
  207. $content = do_shortcodes_in_html_tags( $content, $ignore_html, $tagnames ); 
  208.  
  209. $pattern = get_shortcode_regex( $tagnames ); 
  210. $content = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $content ); 
  211.  
  212. // Always restore square braces so we don't break things like <!--[if IE ]> 
  213. $content = unescape_invalid_shortcodes( $content ); 
  214.  
  215. return $content; 
  216.  
  217. /** 
  218. * Retrieve the shortcode regular expression for searching. 
  219. * 
  220. * The regular expression combines the shortcode tags in the regular expression 
  221. * in a regex class. 
  222. * 
  223. * The regular expression contains 6 different sub matches to help with parsing. 
  224. * 
  225. * 1 - An extra [ to allow for escaping shortcodes with double [[]] 
  226. * 2 - The shortcode name 
  227. * 3 - The shortcode argument list 
  228. * 4 - The self closing / 
  229. * 5 - The content of a shortcode when it wraps some content. 
  230. * 6 - An extra ] to allow for escaping shortcodes with double [[]] 
  231. * 
  232. * @since 2.5.0 
  233. * @since 4.4.0 Added the `$tagnames` parameter. 
  234. * 
  235. * @global array $shortcode_tags 
  236. * 
  237. * @param array $tagnames Optional. List of shortcodes to find. Defaults to all registered shortcodes. 
  238. * @return string The shortcode search regular expression 
  239. */ 
  240. function get_shortcode_regex( $tagnames = null ) { 
  241. global $shortcode_tags; 
  242.  
  243. if ( empty( $tagnames ) ) { 
  244. $tagnames = array_keys( $shortcode_tags ); 
  245. $tagregexp = join( '|', array_map('preg_quote', $tagnames) ); 
  246.  
  247. // WARNING! Do not change this regex without changing do_shortcode_tag() and strip_shortcode_tag() 
  248. // Also, see shortcode_unautop() and shortcode.js. 
  249. return 
  250. '\\[' // Opening bracket 
  251. . '(\\[?)' // 1: Optional second opening bracket for escaping shortcodes: [[tag]] 
  252. . "($tagregexp)" // 2: Shortcode name 
  253. . '(?![\\w-])' // Not followed by word character or hyphen 
  254. . '(' // 3: Unroll the loop: Inside the opening shortcode tag 
  255. . '[^\\]\\/]*' // Not a closing bracket or forward slash 
  256. . '(?:' 
  257. . '\\/(?!\\])' // A forward slash not followed by a closing bracket 
  258. . '[^\\]\\/]*' // Not a closing bracket or forward slash 
  259. . ')*?' 
  260. . ')' 
  261. . '(?:' 
  262. . '(\\/)' // 4: Self closing tag ... 
  263. . '\\]' // ... and closing bracket 
  264. . '|' 
  265. . '\\]' // Closing bracket 
  266. . '(?:' 
  267. . '(' // 5: Unroll the loop: Optionally, anything between the opening and closing shortcode tags 
  268. . '[^\\[]*+' // Not an opening bracket 
  269. . '(?:' 
  270. . '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag 
  271. . '[^\\[]*+' // Not an opening bracket 
  272. . ')*+' 
  273. . ')' 
  274. . '\\[\\/\\2\\]' // Closing shortcode tag 
  275. . ')?' 
  276. . ')' 
  277. . '(\\]?)'; // 6: Optional second closing brocket for escaping shortcodes: [[tag]] 
  278.  
  279. /** 
  280. * Regular Expression callable for do_shortcode() for calling shortcode hook. 
  281. * @see get_shortcode_regex for details of the match array contents. 
  282. * 
  283. * @since 2.5.0 
  284. * @access private 
  285. * 
  286. * @global array $shortcode_tags 
  287. * 
  288. * @param array $m Regular expression match array 
  289. * @return string|false False on failure. 
  290. */ 
  291. function do_shortcode_tag( $m ) { 
  292. global $shortcode_tags; 
  293.  
  294. // allow [[foo]] syntax for escaping a tag 
  295. if ( $m[1] == '[' && $m[6] == ']' ) { 
  296. return substr($m[0], 1, -1); 
  297.  
  298. $tag = $m[2]; 
  299. $attr = shortcode_parse_atts( $m[3] ); 
  300.  
  301. if ( ! is_callable( $shortcode_tags[ $tag ] ) ) { 
  302. /** translators: %s: shortcode tag */ 
  303. $message = sprintf( __( 'Attempting to parse a shortcode without a valid callback: %s' ), $tag ); 
  304. _doing_it_wrong( __FUNCTION__, $message, '4.3.0' ); 
  305. return $m[0]; 
  306.  
  307. /** 
  308. * Filters whether to call a shortcode callback. 
  309. * 
  310. * Passing a truthy value to the filter will effectively short-circuit the 
  311. * shortcode generation process, returning that value instead. 
  312. * 
  313. * @since 4.7.0 
  314. * 
  315. * @param bool|string $return Short-circuit return value. Either false or the value to replace the shortcode with. 
  316. * @param string $tag Shortcode name. 
  317. * @param array $attr Shortcode attributes array,  
  318. * @param array $m Regular expression match array. 
  319. */ 
  320. $return = apply_filters( 'pre_do_shortcode_tag', false, $tag, $attr, $m ); 
  321. if ( false !== $return ) { 
  322. return $return; 
  323.  
  324. $content = isset( $m[5] ) ? $m[5] : null; 
  325.  
  326. $output = $m[1] . call_user_func( $shortcode_tags[ $tag ], $attr, $content, $tag ) . $m[6]; 
  327.  
  328. /** 
  329. * Filters the output created by a shortcode callback. 
  330. * 
  331. * @since 4.7.0 
  332. * 
  333. * @param string $output Shortcode output. 
  334. * @param string $tag Shortcode name. 
  335. * @param array $attr Shortcode attributes array,  
  336. * @param array $m Regular expression match array. 
  337. */ 
  338. return apply_filters( 'do_shortcode_tag', $output, $tag, $attr, $m ); 
  339.  
  340. /** 
  341. * Search only inside HTML elements for shortcodes and process them. 
  342. * 
  343. * Any [ or ] characters remaining inside elements will be HTML encoded 
  344. * to prevent interference with shortcodes that are outside the elements. 
  345. * Assumes $content processed by KSES already. Users with unfiltered_html 
  346. * capability may get unexpected output if angle braces are nested in tags. 
  347. * 
  348. * @since 4.2.3 
  349. * 
  350. * @param string $content Content to search for shortcodes 
  351. * @param bool $ignore_html When true, all square braces inside elements will be encoded. 
  352. * @param array $tagnames List of shortcodes to find. 
  353. * @return string Content with shortcodes filtered out. 
  354. */ 
  355. function do_shortcodes_in_html_tags( $content, $ignore_html, $tagnames ) { 
  356. // Normalize entities in unfiltered HTML before adding placeholders. 
  357. $trans = array( '[' => '[', ']' => ']' ); 
  358. $content = strtr( $content, $trans ); 
  359. $trans = array( '[' => '[', ']' => ']' ); 
  360.  
  361. $pattern = get_shortcode_regex( $tagnames ); 
  362. $textarr = wp_html_split( $content ); 
  363.  
  364. foreach ( $textarr as &$element ) { 
  365. if ( '' == $element || '<' !== $element[0] ) { 
  366. continue; 
  367.  
  368. $noopen = false === strpos( $element, '[' ); 
  369. $noclose = false === strpos( $element, ']' ); 
  370. if ( $noopen || $noclose ) { 
  371. // This element does not contain shortcodes. 
  372. if ( $noopen xor $noclose ) { 
  373. // Need to encode stray [ or ] chars. 
  374. $element = strtr( $element, $trans ); 
  375. continue; 
  376.  
  377. if ( $ignore_html || '<!--' === substr( $element, 0, 4 ) || '<![CDATA[' === substr( $element, 0, 9 ) ) { 
  378. // Encode all [ and ] chars. 
  379. $element = strtr( $element, $trans ); 
  380. continue; 
  381.  
  382. $attributes = wp_kses_attr_parse( $element ); 
  383. if ( false === $attributes ) { 
  384. // Some plugins are doing things like [name] <[email]>. 
  385. if ( 1 === preg_match( '%^<\s*\[\[?[^\[\]]+\]%', $element ) ) { 
  386. $element = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $element ); 
  387.  
  388. // Looks like we found some crazy unfiltered HTML. Skipping it for sanity. 
  389. $element = strtr( $element, $trans ); 
  390. continue; 
  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. $double = strpos( $attr, '"' ); 
  406. $single = strpos( $attr, "'" ); 
  407. if ( ( false === $single || $open < $single ) && ( false === $double || $open < $double ) ) { 
  408. // $attr like '[shortcode]' or 'name = [shortcode]' implies unfiltered_html. 
  409. // In this specific situation we assume KSES did not run because the input 
  410. // was written by an administrator, so we should avoid changing the output 
  411. // and we do not need to run KSES here. 
  412. $attr = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $attr ); 
  413. } else { 
  414. // $attr like 'name = "[shortcode]"' or "name = '[shortcode]'" 
  415. // We do not know if $content was unfiltered. Assume KSES ran before shortcodes. 
  416. $count = 0; 
  417. $new_attr = preg_replace_callback( "/$pattern/", 'do_shortcode_tag', $attr, -1, $count ); 
  418. if ( $count > 0 ) { 
  419. // Sanitize the shortcode output using KSES. 
  420. $new_attr = wp_kses_one_attr( $new_attr, $elname ); 
  421. if ( '' !== trim( $new_attr ) ) { 
  422. // The shortcode is safe to use now. 
  423. $attr = $new_attr; 
  424. $element = $front . implode( '', $attributes ) . $back; 
  425.  
  426. // Now encode any remaining [ or ] chars. 
  427. $element = strtr( $element, $trans ); 
  428.  
  429. $content = implode( '', $textarr ); 
  430.  
  431. return $content; 
  432.  
  433. /** 
  434. * Remove placeholders added by do_shortcodes_in_html_tags(). 
  435. * 
  436. * @since 4.2.3 
  437. * 
  438. * @param string $content Content to search for placeholders. 
  439. * @return string Content with placeholders removed. 
  440. */ 
  441. function unescape_invalid_shortcodes( $content ) { 
  442. // Clean up entire string, avoids re-parsing HTML. 
  443. $trans = array( '[' => '[', ']' => ']' ); 
  444. $content = strtr( $content, $trans ); 
  445.  
  446. return $content; 
  447.  
  448. /** 
  449. * Retrieve the shortcode attributes regex. 
  450. * 
  451. * @since 4.4.0 
  452. * 
  453. * @return string The shortcode attribute regular expression 
  454. */ 
  455. function get_shortcode_atts_regex() { 
  456. return '/([\w-]+)\s*=\s*"([^"]*)"(?:\s|$)|([\w-]+)\s*=\s*\'([^\']*)\'(?:\s|$)|([\w-]+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/'; 
  457.  
  458. /** 
  459. * Retrieve all attributes from the shortcodes tag. 
  460. * 
  461. * The attributes list has the attribute name as the key and the value of the 
  462. * attribute as the value in the key/value pair. This allows for easier 
  463. * retrieval of the attributes, since all attributes have to be known. 
  464. * 
  465. * @since 2.5.0 
  466. * 
  467. * @param string $text 
  468. * @return array|string List of attribute values. 
  469. * Returns empty array if trim( $text ) == '""'. 
  470. * Returns empty string if trim( $text ) == ''. 
  471. * All other matches are checked for not empty(). 
  472. */ 
  473. function shortcode_parse_atts($text) { 
  474. $atts = array(); 
  475. $pattern = get_shortcode_atts_regex(); 
  476. $text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text); 
  477. if ( preg_match_all($pattern, $text, $match, PREG_SET_ORDER) ) { 
  478. foreach ($match as $m) { 
  479. if (!empty($m[1])) 
  480. $atts[strtolower($m[1])] = stripcslashes($m[2]); 
  481. elseif (!empty($m[3])) 
  482. $atts[strtolower($m[3])] = stripcslashes($m[4]); 
  483. elseif (!empty($m[5])) 
  484. $atts[strtolower($m[5])] = stripcslashes($m[6]); 
  485. elseif (isset($m[7]) && strlen($m[7])) 
  486. $atts[] = stripcslashes($m[7]); 
  487. elseif (isset($m[8])) 
  488. $atts[] = stripcslashes($m[8]); 
  489.  
  490. // Reject any unclosed HTML elements 
  491. foreach( $atts as &$value ) { 
  492. if ( false !== strpos( $value, '<' ) ) { 
  493. if ( 1 !== preg_match( '/^[^<]*+(?:<[^>]*+>[^<]*+)*+$/', $value ) ) { 
  494. $value = ''; 
  495. } else { 
  496. $atts = ltrim($text); 
  497. return $atts; 
  498.  
  499. /** 
  500. * Combine user attributes with known attributes and fill in defaults when needed. 
  501. * 
  502. * The pairs should be considered to be all of the attributes which are 
  503. * supported by the caller and given as a list. The returned attributes will 
  504. * only contain the attributes in the $pairs list. 
  505. * 
  506. * If the $atts list has unsupported attributes, then they will be ignored and 
  507. * removed from the final returned list. 
  508. * 
  509. * @since 2.5.0 
  510. * 
  511. * @param array $pairs Entire list of supported attributes and their defaults. 
  512. * @param array $atts User defined attributes in shortcode tag. 
  513. * @param string $shortcode Optional. The name of the shortcode, provided for context to enable filtering 
  514. * @return array Combined and filtered attribute list. 
  515. */ 
  516. function shortcode_atts( $pairs, $atts, $shortcode = '' ) { 
  517. $atts = (array)$atts; 
  518. $out = array(); 
  519. foreach ($pairs as $name => $default) { 
  520. if ( array_key_exists($name, $atts) ) 
  521. $out[$name] = $atts[$name]; 
  522. else 
  523. $out[$name] = $default; 
  524. /** 
  525. * Filters a shortcode's default attributes. 
  526. * 
  527. * If the third parameter of the shortcode_atts() function is present then this filter is available. 
  528. * The third parameter, $shortcode, is the name of the shortcode. 
  529. * 
  530. * @since 3.6.0 
  531. * @since 4.4.0 Added the `$shortcode` parameter. 
  532. * 
  533. * @param array $out The output array of shortcode attributes. 
  534. * @param array $pairs The supported attributes and their defaults. 
  535. * @param array $atts The user defined shortcode attributes. 
  536. * @param string $shortcode The shortcode name. 
  537. */ 
  538. if ( $shortcode ) { 
  539. $out = apply_filters( "shortcode_atts_{$shortcode}", $out, $pairs, $atts, $shortcode ); 
  540.  
  541. return $out; 
  542.  
  543. /** 
  544. * Remove all shortcode tags from the given content. 
  545. * 
  546. * @since 2.5.0 
  547. * 
  548. * @global array $shortcode_tags 
  549. * 
  550. * @param string $content Content to remove shortcode tags. 
  551. * @return string Content without shortcode tags. 
  552. */ 
  553. function strip_shortcodes( $content ) { 
  554. global $shortcode_tags; 
  555.  
  556. if ( false === strpos( $content, '[' ) ) { 
  557. return $content; 
  558.  
  559. if (empty($shortcode_tags) || !is_array($shortcode_tags)) 
  560. return $content; 
  561.  
  562. // Find all registered tag names in $content. 
  563. preg_match_all( '@\[([^<>&/\[\]\x00-\x20=]++)@', $content, $matches ); 
  564.  
  565. $tags_to_remove = array_keys( $shortcode_tags ); 
  566.  
  567. /** 
  568. * Filters the list of shortcode tags to remove from the content. 
  569. * 
  570. * @since 4.7.0 
  571. * 
  572. * @param array $tag_array Array of shortcode tags to remove. 
  573. * @param string $content Content shortcodes are being removed from. 
  574. */ 
  575. $tags_to_remove = apply_filters( 'strip_shortcodes_tagnames', $tags_to_remove, $content ); 
  576.  
  577. $tagnames = array_intersect( $tags_to_remove, $matches[1] ); 
  578.  
  579. if ( empty( $tagnames ) ) { 
  580. return $content; 
  581.  
  582. $content = do_shortcodes_in_html_tags( $content, true, $tagnames ); 
  583.  
  584. $pattern = get_shortcode_regex( $tagnames ); 
  585. $content = preg_replace_callback( "/$pattern/", 'strip_shortcode_tag', $content ); 
  586.  
  587. // Always restore square braces so we don't break things like <!--[if IE ]> 
  588. $content = unescape_invalid_shortcodes( $content ); 
  589.  
  590. return $content; 
  591.  
  592. /** 
  593. * Strips a shortcode tag based on RegEx matches against post content. 
  594. * 
  595. * @since 3.3.0 
  596. * 
  597. * @param array $m RegEx matches against post content. 
  598. * @return string|false The content stripped of the tag, otherwise false. 
  599. */ 
  600. function strip_shortcode_tag( $m ) { 
  601. // allow [[foo]] syntax for escaping a tag 
  602. if ( $m[1] == '[' && $m[6] == ']' ) { 
  603. return substr($m[0], 1, -1); 
  604.  
  605. return $m[1] . $m[6]; 
.