shortcodes.php 20 KB

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