Ticket #29256: miqro-29256.4.patch
| File miqro-29256.4.patch, 18.3 KB (added by , 11 years ago) |
|---|
-
src/wp-includes/formatting.php
30 30 function wptexturize($text, $reset = false) { 31 31 global $wp_cockneyreplace; 32 32 static $static_characters, $static_replacements, $dynamic_characters, $dynamic_replacements, 33 $default_no_texturize_tags, $default_no_texturize_shortcodes, $run_texturize = true; 33 $default_no_texturize_tags, $default_no_texturize_shortcodes, $run_texturize = true, 34 $apos_flag, $apos, $prime, $double_prime, $opening_quote, $closing_quote, $opening_single_quote, 35 $closing_single_quote, $open_q_flag, $open_sq_flag; 34 36 35 37 // If there's nothing to do, just stop. 36 38 if ( empty( $text ) || false === $run_texturize ) { … … 105 107 $dynamic_replacements = array( 'apos' => array(), 'quote' => array(), 'dash' => array() ); 106 108 $dynamic = array(); 107 109 $spaces = wp_spaces_regexp(); 110 $apos_flag = '<!--apos-->'; // We need a semantic representation that is not identical to another quote. 111 $open_q_flag = '<!--oq-->'; 112 $open_sq_flag = '<!--osq-->'; 108 113 109 114 // '99' and '99" are ambiguous among other patterns; assume it's an abbreviated year at the end of a quotation. 110 115 if ( "'" !== $apos || "'" !== $closing_single_quote ) { 111 $dynamic[ '/\'(\d\d)\'(?=\Z|[.,)}\-\]]|>|' . $spaces . ')/' ] = $apos . '$1' . $closing_single_quote;116 $dynamic[ '/\'(\d\d)\'(?=\Z|[.,)}\-\]]|>|' . $spaces . ')/' ] = $apos_flag . '$1' . $closing_single_quote; 112 117 } 113 118 if ( "'" !== $apos || '"' !== $closing_quote ) { 114 $dynamic[ '/\'(\d\d)"(?=\Z|[.,)}\-\]]|>|' . $spaces . ')/' ] = $apos . '$1' . $closing_quote;119 $dynamic[ '/\'(\d\d)"(?=\Z|[.,)}\-\]]|>|' . $spaces . ')/' ] = $apos_flag . '$1' . $closing_quote; 115 120 } 116 121 117 122 // '99 '99s '99's (apostrophe) But never '9 or '99% or '999 or '99.0. 118 123 if ( "'" !== $apos ) { 119 $dynamic[ '/\'(?=\d\d(?:\Z|(?![%\d]|[.,]\d)))/' ] = $apos ;124 $dynamic[ '/\'(?=\d\d(?:\Z|(?![%\d]|[.,]\d)))/' ] = $apos_flag; 120 125 } 121 126 122 127 // Quoted Numbers like '0.42' 123 128 if ( "'" !== $opening_single_quote && "'" !== $closing_single_quote ) { 124 $dynamic[ '/(?<=\A|' . $spaces . ')\'(\d[.,\d]*)\'/' ] = $open ing_single_quote. '$1' . $closing_single_quote;129 $dynamic[ '/(?<=\A|' . $spaces . ')\'(\d[.,\d]*)\'/' ] = $open_sq_flag . '$1' . $closing_single_quote; 125 130 } 126 131 127 132 // Single quote at start, or preceded by (, {, <, [, ", -, or spaces. 128 133 if ( "'" !== $opening_single_quote ) { 129 $dynamic[ '/(?<=\A|[([{"\-]|<|' . $spaces . ')\'/' ] = $open ing_single_quote;134 $dynamic[ '/(?<=\A|[([{"\-]|<|' . $spaces . ')\'/' ] = $open_sq_flag; 130 135 } 131 136 132 137 // Apostrophe in a word. No spaces, double apostrophes, or other punctuation. 133 138 if ( "'" !== $apos ) { 134 $dynamic[ '/(?<!' . $spaces . ')\'(?!\Z|[.,:;"\'(){}[\]\-]|&[lg]t;|' . $spaces . ')/' ] = $apos ;139 $dynamic[ '/(?<!' . $spaces . ')\'(?!\Z|[.,:;"\'(){}[\]\-]|&[lg]t;|' . $spaces . ')/' ] = $apos_flag; 135 140 } 136 141 137 // 9' (prime)138 if ( "'" !== $prime ) {139 $dynamic[ '/(?<=\d)\'/' ] = $prime;140 }141 142 // Single quotes followed by spaces or ending punctuation.143 if ( "'" !== $closing_single_quote ) {144 $dynamic[ '/\'(?=\Z|[.,)}\-\]]|>|' . $spaces . ')/' ] = $closing_single_quote;145 }146 147 142 $dynamic_characters['apos'] = array_keys( $dynamic ); 148 143 $dynamic_replacements['apos'] = array_values( $dynamic ); 149 144 $dynamic = array(); … … 150 145 151 146 // Quoted Numbers like "42" 152 147 if ( '"' !== $opening_quote && '"' !== $closing_quote ) { 153 $dynamic[ '/(?<=\A|' . $spaces . ')"(\d[.,\d]*)"/' ] = $open ing_quote. '$1' . $closing_quote;148 $dynamic[ '/(?<=\A|' . $spaces . ')"(\d[.,\d]*)"/' ] = $open_q_flag . '$1' . $closing_quote; 154 149 } 155 150 156 // 9" (double prime)157 if ( '"' !== $double_prime ) {158 $dynamic[ '/(?<=\d)"/' ] = $double_prime;159 }160 161 151 // Double quote at start, or preceded by (, {, <, [, -, or spaces, and not followed by spaces. 162 152 if ( '"' !== $opening_quote ) { 163 $dynamic[ '/(?<=\A|[([{\-]|<|' . $spaces . ')"(?!' . $spaces . ')/' ] = $open ing_quote;153 $dynamic[ '/(?<=\A|[([{\-]|<|' . $spaces . ')"(?!' . $spaces . ')/' ] = $open_q_flag; 164 154 } 165 155 166 // Any remaining double quotes.167 if ( '"' !== $closing_quote ) {168 $dynamic[ '/"/' ] = $closing_quote;169 }170 171 156 $dynamic_characters['quote'] = array_keys( $dynamic ); 172 157 $dynamic_replacements['quote'] = array_values( $dynamic ); 173 158 $dynamic = array(); … … 271 256 272 257 if ( false !== strpos( $curl, "'" ) ) { 273 258 $curl = preg_replace( $dynamic_characters['apos'], $dynamic_replacements['apos'], $curl ); 259 $curl = wptexturize_primes( $curl, "'", $prime, $open_sq_flag, $closing_single_quote ); 260 $curl = str_replace( $apos_flag, $apos, $curl ); 261 $curl = str_replace( $open_sq_flag, $opening_single_quote, $curl ); 274 262 } 275 263 if ( false !== strpos( $curl, '"' ) ) { 276 264 $curl = preg_replace( $dynamic_characters['quote'], $dynamic_replacements['quote'], $curl ); 265 $curl = wptexturize_primes( $curl, '"', $double_prime, $open_q_flag, $closing_quote ); 266 $curl = str_replace( $open_q_flag, $opening_quote, $curl ); 277 267 } 278 268 if ( false !== strpos( $curl, '-' ) ) { 279 269 $curl = preg_replace( $dynamic_characters['dash'], $dynamic_replacements['dash'], $curl ); … … 295 285 } 296 286 297 287 /** 288 * Implements a logic tree to determine whether or not "7'." represents seven feet, 289 * then converts the special char into either a prime char or a closing quote char. 290 * 291 * @since 4.1.0 292 * 293 * @param string $haystack The plain text to be searched. 294 * @param string $needle The character to search for such as ' or ". 295 * @param string $prime The prime char to use for replacement. 296 * @param string $open_quote The opening quote char. Opening quote replacement must be accomplished already. 297 * @param string $close_quote The closing quote char to use for replacement. 298 * @return string The $haystack value after primes and quotes replacements. 299 */ 300 function wptexturize_primes( $haystack, $needle, $prime, $open_quote, $close_quote ) { 301 302 $spaces = wp_spaces_regexp(); 303 $flag = '<!--wp-prime-or-quote-->'; 304 $quote_pattern = "/$needle(?=\\Z|[.,)}\\-\\]]|>|" . $spaces . ")/"; 305 $prime_pattern = "/(?<=\\d)$needle/"; 306 $flag_after_digit = "/(?<=\\d)$flag/"; 307 $flag_no_digit = "/(?<!\\d)$flag/"; 308 309 $sentences = explode( $open_quote, $haystack ); 310 311 foreach( $sentences as $key => &$sentence ) { 312 if ( false === strpos( $sentence, $needle ) ) { 313 continue; 314 } elseif ( 0 !== $key && substr_count( $sentence, $close_quote ) === 0 ) { 315 $sentence = preg_replace( $quote_pattern, $flag, $sentence, -1, $count ); 316 if ( $count > 1) { 317 // This sentence appears to have multiple closing quotes. Attempt Vulcan logic. 318 $sentence = preg_replace( $flag_no_digit, $close_quote, $sentence, -1, $count2 ); 319 if ( 0 === $count2 ) { 320 // Closing quote still ambiguous. Look for a quote followed by a period. 321 $count2 = substr_count( $sentence, "$flag." ); 322 if ( $count2 > 0 ) { 323 // Assume the rightmost quote-period match is the end of quotation. 324 $pos = strrpos( $sentence, "$flag." ); 325 } else { 326 // When all else fails, make the rightmost candidate a closing quote. 327 // This is most likely to be problematic in the context of bug #18549. 328 $pos = strrpos( $sentence, $flag ); 329 } 330 $sentence = substr_replace( $sentence, $close_quote, $pos, strlen( $flag ) ); 331 } 332 // Use conventional replacement on any remaining primes and quotes. 333 $sentence = preg_replace( $prime_pattern, $prime, $sentence ); 334 $sentence = preg_replace( $flag_after_digit, $prime, $sentence ); 335 $sentence = str_replace( $flag, $close_quote, $sentence ); 336 } elseif ( 1 == $count ) { 337 // Found only one closing quote candidate, so give it priority over primes. 338 $sentence = str_replace( $flag, $close_quote, $sentence ); 339 $sentence = preg_replace( $prime_pattern, $prime, $sentence ); 340 } else { 341 // No closing quotes found. Just run primes pattern. 342 $sentence = preg_replace( $prime_pattern, $prime, $sentence ); 343 } 344 } else { 345 $sentence = preg_replace( $prime_pattern, $prime, $sentence ); 346 $sentence = preg_replace( $quote_pattern, $close_quote, $sentence ); 347 } 348 if ( '"' == $needle ) { 349 $sentence = str_replace( '"', $close_quote, $sentence ); 350 } 351 } 352 353 return implode( $open_quote, $sentences ); 354 } 355 356 /** 298 357 * Search for disabled element tags. Push element to stack on tag open and pop 299 358 * on tag close. 300 359 * -
tests/phpunit/tests/formatting/WPTexturize.php
90 90 //$this->assertEquals('Here is “<a href="http://example.com">a test with a link</a>”… and ellipses.', wptexturize('Here is "<a href="http://example.com">a test with a link</a>"... and ellipses.')); 91 91 //$this->assertEquals('Here is “a test <a href="http://example.com">with a link</a>”.', wptexturize('Here is "a test <a href="http://example.com">with a link</a>".')); 92 92 //$this->assertEquals('Here is “<a href="http://example.com">a test with a link</a>”and a work stuck to the end.', wptexturize('Here is "<a href="http://example.com">a test with a link</a>"and a work stuck to the end.')); 93 //$this->assertEquals('A test with a finishing number, “like 23”.', wptexturize('A test with a finishing number, "like 23".'));94 //$this->assertEquals('A test with a number, “like 62”, is nice to have.', wptexturize('A test with a number, "like 62", is nice to have.'));93 $this->assertEquals('A test with a finishing number, “like 23”.', wptexturize('A test with a finishing number, "like 23".')); 94 $this->assertEquals('A test with a number, “like 62”, is nice to have.', wptexturize('A test with a number, "like 62", is nice to have.')); 95 95 } 96 96 97 97 /** … … 114 114 $this->assertEquals('‘Class of ’99’', wptexturize("'Class of '99'")); 115 115 $this->assertEquals('‘Class of ’99’s’', wptexturize("'Class of '99's'")); 116 116 $this->assertEquals('‘Class of ’99’s’', wptexturize("'Class of '99’s'")); 117 //$this->assertEquals('“Class of 99”', wptexturize("\"Class of 99\""));117 $this->assertEquals('“Class of 99”', wptexturize("\"Class of 99\"")); 118 118 $this->assertEquals('“Class of ’99”', wptexturize("\"Class of '99\"")); 119 119 $this->assertEquals('{“Class of ’99”}', wptexturize("{\"Class of '99\"}")); 120 120 $this->assertEquals(' “Class of ’99” ', wptexturize(" \"Class of '99\" ")); … … 1823 1823 ), 1824 1824 ); 1825 1825 } 1826 1827 /** 1828 * Ensure primes logic is not too greedy at the end of a quotation. 1829 * 1830 * @ticket 29256 1831 * @dataProvider primes_vs_quotes 1832 */ 1833 function test_primes_vs_quotes( $input, $output ) { 1834 return $this->assertEquals( $output, wptexturize( $input ) ); 1835 } 1836 1837 function data_primes_vs_quotes() { 1838 return array( 1839 array( 1840 "George's porch is 99' long.", 1841 "George’s porch is 99′ long.", 1842 ), 1843 array( 1844 'The best year "was that time in 2012" when everyone partied, he said.', 1845 'The best year “was that time in 2012” when everyone partied, he said.', 1846 ), 1847 array( 1848 "I need 4 x 20' = 80' of trim.", // Works only with a space before the = char. 1849 "I need 4 x 20′ = 80′ of trim.", 1850 ), 1851 array( 1852 '"Lorem ipsum dolor sit amet 1234"', 1853 '“Lorem ipsum dolor sit amet 1234”', 1854 ), 1855 array( 1856 "'Etiam eu egestas dui 1234'", 1857 "‘Etiam eu egestas dui 1234’", 1858 ), 1859 array( 1860 'according to our source, "33% of all students scored less than 50" on the test.', 1861 'according to our source, “33% of all students scored less than 50” on the test.', 1862 ), 1863 array( 1864 "The doctor said, 'An average height is between 5' and 6' in study group 7'. He then produced a 6' chart of averages. A man of 7', incredibly, is very possible.", 1865 "The doctor said, ‘An average height is between 5′ and 6′ in study group 7’. He then produced a 6′ chart of averages. A man of 7′, incredibly, is very possible.", 1866 ), 1867 array( 1868 'Pirates have voted on "The Expendables 3" with their clicks -- and it turns out the Sylvester Stallone-starrer hasn\'t been astoundingly popular among digital thieves, relatively speaking. 1869 1870 As of Sunday, 5.12 million people worldwide had pirated "Expendables 3" since a high-quality copy hit torrent-sharing sites July 23, according to piracy-tracking firm Excipio. 1871 1872 That likely contributed to the action movie\'s dismal box-office debut this weekend. But over the same July 23-Aug. 18 time period, the movie was No. 4 in downloads, after "Captain America: The Winter Soldier" (7.31 million), "Divergent" (6.29 million) and "The Amazing Spider-Man 2" (5.88 million). Moreover, that\'s despite "Expendables 3" becoming available more than three weeks prior to the film\'s U.S. theatrical debut. 1873 1874 String with a number followed by a single quote \'Expendables 3\' vestibulum in arcu mi.', 1875 1876 'Pirates have voted on “The Expendables 3” with their clicks — and it turns out the Sylvester Stallone-starrer hasn’t been astoundingly popular among digital thieves, relatively speaking. 1877 1878 As of Sunday, 5.12 million people worldwide had pirated “Expendables 3” since a high-quality copy hit torrent-sharing sites July 23, according to piracy-tracking firm Excipio. 1879 1880 That likely contributed to the action movie’s dismal box-office debut this weekend. But over the same July 23-Aug. 18 time period, the movie was No. 4 in downloads, after “Captain America: The Winter Soldier” (7.31 million), “Divergent” (6.29 million) and “The Amazing Spider-Man 2” (5.88 million). Moreover, that’s despite “Expendables 3” becoming available more than three weeks prior to the film’s U.S. theatrical debut. 1881 1882 String with a number followed by a single quote ‘Expendables 3’ vestibulum in arcu mi.', 1883 ), 1884 ); 1885 } 1886 1887 /** 1888 * Make sure translation actually works. 1889 * 1890 * Also make sure opening and closing quotes are allowed to be identical. 1891 * 1892 * @ticket 29256 1893 * @dataProvider data_primes_quotes_translation 1894 */ 1895 function test_primes_quotes_translation( $input, $output ) { 1896 add_filter( 'gettext_with_context', array( $this, 'filter_translate2' ), 10, 4 ); 1897 1898 $result = wptexturize( $input, true ); 1899 1900 remove_filter( 'gettext_with_context', array( $this, 'filter_translate2' ), 10, 4 ); 1901 wptexturize( 'reset', true ); 1902 1903 return $this->assertEquals( $output, $result ); 1904 } 1905 1906 function filter_translate2( $translations, $text, $context, $domain ) { 1907 switch ($input) { 1908 case '–' : return '!endash!'; 1909 case '—' : return '!emdash!'; 1910 case '‘' : return '!q1!'; 1911 case '’' : 1912 if ( 'apostrophe' == $context ) { 1913 return '!apos!'; 1914 } else { 1915 return '!q1!'; 1916 } 1917 case '“' : return '!q2!'; 1918 case '”' : return '!q2!'; 1919 case '′' : return '!prime1!'; 1920 case '″' : return '!prime2!'; 1921 default : return $input; 1922 } 1923 } 1924 1925 function data_primes_quotes_translation() { 1926 return array( 1927 array( 1928 "George's porch is 99' long.", 1929 "George!apos!s porch is 99!prime1! long.", 1930 ), 1931 array( 1932 'The best year "was that time in 2012" when everyone partied, he said.', 1933 'The best year !q2!was that time in 2012!q2! when everyone partied, he said.', 1934 ), 1935 array( 1936 "I need 4 x 20' = 80' of trim.", // Works only with a space before the = char. 1937 "I need 4 x 20!prime1! = 80!prime1! of trim.", 1938 ), 1939 array( 1940 '"Lorem ipsum dolor sit amet 1234"', 1941 '!q2!Lorem ipsum dolor sit amet 1234!q2!', 1942 ), 1943 array( 1944 "'Etiam eu egestas dui 1234'", 1945 "!q1!Etiam eu egestas dui 1234!q1!", 1946 ), 1947 array( 1948 'according to our source, "33% of all students scored less than 50" on the test.', 1949 'according to our source, !q2!33% of all students scored less than 50!q2! on the test.', 1950 ), 1951 array( 1952 "The doctor said, 'An average height is between 5' and 6' in study group 7'. He then produced a 6' chart of averages. A man of 7', incredibly, is very possible.", 1953 "The doctor said, !q1!An average height is between 5!prime1! and 6!prime1! in study group 7!q1!. He then produced a 6!prime1! chart of averages. A man of 7!prime1!, incredibly, is very possible.", 1954 ), 1955 array( 1956 'Pirates have voted on "The Expendables 3" with their clicks -- and it turns out the Sylvester Stallone-starrer hasn\'t been astoundingly popular among digital thieves, relatively speaking. 1957 1958 As of Sunday, 5.12 million people worldwide had pirated "Expendables 3" since a high-quality copy hit torrent-sharing sites July 23, according to piracy-tracking firm Excipio. 1959 1960 That likely contributed to the action movie\'s dismal box-office debut this weekend. But over the same July 23-Aug. 18 time period, the movie was No. 4 in downloads, after "Captain America: The Winter Soldier" (7.31 million), "Divergent" (6.29 million) and "The Amazing Spider-Man 2" (5.88 million). Moreover, that\'s despite "Expendables 3" becoming available more than three weeks prior to the film\'s U.S. theatrical debut. 1961 1962 String with a number followed by a single quote \'Expendables 3\' vestibulum in arcu mi.', 1963 1964 'Pirates have voted on !q2!The Expendables 3!q2! with their clicks !emdash! and it turns out the Sylvester Stallone-starrer hasn!apos!t been astoundingly popular among digital thieves, relatively speaking. 1965 1966 As of Sunday, 5.12 million people worldwide had pirated !q2!Expendables 3!q2! since a high-quality copy hit torrent-sharing sites July 23, according to piracy-tracking firm Excipio. 1967 1968 That likely contributed to the action movie!apos!s dismal box-office debut this weekend. But over the same July 23-Aug. 18 time period, the movie was No. 4 in downloads, after !q2!Captain America: The Winter Soldier!q2! (7.31 million), !q2!Divergent!q2! (6.29 million) and !q2!The Amazing Spider-Man 2!q2! (5.88 million). Moreover, that!apos!s despite !q2!Expendables 3!q2! becoming available more than three weeks prior to the film!apos!s U.S. theatrical debut. 1969 1970 String with a number followed by a single quote !q1!Expendables 3!q1! vestibulum in arcu mi.', 1971 ), 1972 ); 1973 } 1826 1974 } 1975 No newline at end of file