From 1f5525d2ffd60278018732d3c58f78453cdf57f9 Mon Sep 17 00:00:00 2001 From: Jake Spurlock Date: Tue, 1 Oct 2019 03:41:58 +0000 Subject: [PATCH] Shortcodes: Improve handling from `shortcode_parse_attts()`. Ensure consistency between `shortcode_parse_attts()` when being used directly. Props mauteri, birgire, SergeyBiryukov, kadamwhite, whyisjake. Fixes #47863. git-svn-id: https://develop.svn.wordpress.org/trunk@46369 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/shortcodes.php | 14 +++- tests/phpunit/tests/shortcode.php | 132 ++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/shortcodes.php b/src/wp-includes/shortcodes.php index ed042262cc..df29332215 100644 --- a/src/wp-includes/shortcodes.php +++ b/src/wp-includes/shortcodes.php @@ -488,8 +488,9 @@ function get_shortcode_atts_regex() { * retrieval of the attributes, since all attributes have to be known. * * @since 2.5.0 + * @since 5.3.0 Support of a full shortcode input. * - * @param string $text + * @param string $text Any single shortcode of any format or key/value pair string. * @return array|string List of attribute values. * Returns empty array if trim( $text ) == '""'. * Returns empty string if trim( $text ) == ''. @@ -498,7 +499,13 @@ function get_shortcode_atts_regex() { function shortcode_parse_atts( $text ) { $atts = array(); $pattern = get_shortcode_atts_regex(); - $text = preg_replace( "/[\x{00a0}\x{200b}]+/u", ' ', $text ); + $text = trim( preg_replace( "/[\x{00a0}\x{200b}]+/u", ' ', $text ) ); + + // Remove everything but attributes from shortcode. + if ( preg_match( '#^\[[\w-]+([^\]]*?)\/?\]#', $text, $matches ) ) { + $text = $matches[1]; + } + if ( preg_match_all( $pattern, $text, $match, PREG_SET_ORDER ) ) { foreach ( $match as $m ) { if ( ! empty( $m[1] ) ) { @@ -516,7 +523,7 @@ function shortcode_parse_atts( $text ) { } } - // Reject any unclosed HTML elements + // Reject any unclosed HTML elements. foreach ( $atts as &$value ) { if ( false !== strpos( $value, '<' ) ) { if ( 1 !== preg_match( '/^[^<]*+(?:<[^>]*+>[^<]*+)*+$/', $value ) ) { @@ -527,6 +534,7 @@ function shortcode_parse_atts( $text ) { } else { $atts = ltrim( $text ); } + return $atts; } diff --git a/tests/phpunit/tests/shortcode.php b/tests/phpunit/tests/shortcode.php index a978779c9a..82c68a08a3 100644 --- a/tests/phpunit/tests/shortcode.php +++ b/tests/phpunit/tests/shortcode.php @@ -972,4 +972,136 @@ EOF; ); $this->assertEquals( 'test-shortcode-tag', $this->tagname ); } + + /** + * Testing the `shortcode_parse_atts()` function. + * + * @ticket 47863 + * + * @covers ::shortcode_parse_atts + * @dataProvider data_shortcode_parse_atts + * + * @param string $text A single shortcode format or key/value pair string. + * @param string|array $expected Expected results. + */ + public function test_shortcode_parse_atts( $text, $expected ) { + $actual = shortcode_parse_atts( $text ); + $this->assertSame( $expected, $actual ); + } + + /** + * Data provider for `test_shortcode_parse_atts()`. + * + * @return array { + * @type array { + * @type string $text A single shortcode format or key/value pair string. + * @type string|array $expected The expected result. + * } + * } + */ + public function data_shortcode_parse_atts() { + + return array( + array( + '', + '', + ), + array( + ' ', + '', + ), + array( + '""', + array(), + ), + array( + '\'\'', + array(), + ), + array( + '[unittest]', + '', + ), + array( + '[unitest]Unit Test[/unittest]', + '', + ), + array( + '[unittest title="unittest" link="https://unit.test/"]', + array( + 'title' => 'unittest', + 'link' => 'https://unit.test/', + ), + ), + array( + '[unittest title="unittest" link="https://unit.test/"/]', + array( + 'title' => 'unittest', + 'link' => 'https://unit.test/', + ), + ), + array( + '[unit_test title="unittest" link="https://unit.test/"/]', + array( + 'title' => 'unittest', + 'link' => 'https://unit.test/', + ), + ), + array( + '[unit-test title="unittest" link="https://unit.test/"/]', + array( + 'title' => 'unittest', + 'link' => 'https://unit.test/', + ), + ), + array( + '[unittest link=https://unit.test/ /]', + array( + 'link' => 'https://unit.test/', + ), + ), + array( + '[unittest link=https://unit.test/ ]', + array( + 'link' => 'https://unit.test/', + ), + ), + array( + '[unittest link=https://unit.test/]', + array( + 'link' => 'https://unit.test', + ), + ), + array( + '[unittest link https://unit.test/ /]', + array( + 'link', + 'https://unit.test/', + ), + ), + array( + '[unittest title="unittest" link="https://unit.test/"]Unit Test[/unittest]', + array( + 'title' => 'unittest', + 'link' => 'https://unit.test/', + ), + ), + array( + '[unittest title="unittest" link="https://unit.test/"][unit_test foo="bar" bar="foo"][/unittest]', + array( + 'title' => 'unittest', + 'link' => 'https://unit.test/', + ), + ), + array( + 'title="unittest" link="https://unit.test/"', + array( + 'title' => 'unittest', + 'link' => 'https://unit.test/', + ), + ), + ); + + } + }