Changeset 30807
- Timestamp:
- 12/10/2014 10:56:25 PM (10 years ago)
- Location:
- branches/4.1
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/4.1/src/wp-includes/wp-db.php
r30768 r30807 145 145 146 146 /** 147 * Cached column info, for sanity checking data before inserting148 *149 * @since 4.1.0150 * @access protected151 * @var array152 */153 protected $col_meta = array();154 155 /**156 * Calculated character sets on tables157 *158 * @since 4.1.0159 * @access protected160 * @var array161 */162 protected $table_charset = array();163 164 /**165 * Whether text fields in the current query need to be sanity checked.166 *167 * @since 4.1.0168 * @access protected169 * @var bool170 */171 protected $check_current_query = true;172 173 /**174 147 * Saved info on the table column 175 148 * … … 675 648 */ 676 649 public function __set( $name, $value ) { 677 $protected_members = array(678 'col_meta',679 'table_charset',680 'check_current_query',681 );682 if ( in_array( $name, $protected_members, true ) ) {683 return;684 }685 650 $this->$name = $value; 686 651 } … … 1479 1444 $this->has_connected = true; 1480 1445 $this->set_charset( $this->dbh ); 1446 $this->set_sql_mode(); 1481 1447 $this->ready = true; 1482 $this->set_sql_mode();1483 1448 $this->select( $this->dbname, $this->dbh ); 1484 1449 … … 1577 1542 public function query( $query ) { 1578 1543 if ( ! $this->ready ) { 1579 $this->check_current_query = true;1580 1544 return false; 1581 1545 } … … 1597 1561 // Log how the function was called 1598 1562 $this->func_call = "\$db->query(\"$query\")"; 1599 1600 // If we're writing to the database, make sure the query will write safely.1601 if ( $this->check_current_query && ! $this->check_ascii( $query ) ) {1602 $stripped_query = $this->strip_invalid_text_from_query( $query );1603 if ( $stripped_query !== $query ) {1604 $this->insert_id = 0;1605 return false;1606 }1607 }1608 1609 $this->check_current_query = true;1610 1563 1611 1564 // Keep track of the last query for debug.. … … 1778 1731 */ 1779 1732 function _insert_replace_helper( $table, $data, $format = null, $type = 'INSERT' ) { 1780 if ( ! in_array( strtoupper( $type ), array( 'REPLACE', 'INSERT' ) ) ) {1733 if ( ! in_array( strtoupper( $type ), array( 'REPLACE', 'INSERT' ) ) ) 1781 1734 return false; 1782 }1783 1784 $data = $this->process_fields( $table, $data, $format );1785 if ( false === $data ) {1786 return false;1787 }1788 1789 $formats = $values = array();1790 foreach ( $data as $value ) {1791 $formats[] = $value['format'];1792 $values[] = $value['value'];1793 }1794 1795 $fields = '`' . implode( '`, `', array_keys( $data ) ) . '`';1796 $formats = implode( ', ', $formats );1797 1798 $sql = "$type INTO `$table` ($fields) VALUES ($formats)";1799 1800 1735 $this->insert_id = 0; 1801 $this->check_current_query = false; 1802 return $this->query( $this->prepare( $sql, $values ) ); 1736 $formats = $format = (array) $format; 1737 $fields = array_keys( $data ); 1738 $formatted_fields = array(); 1739 foreach ( $fields as $field ) { 1740 if ( !empty( $format ) ) 1741 $form = ( $form = array_shift( $formats ) ) ? $form : $format[0]; 1742 elseif ( isset( $this->field_types[$field] ) ) 1743 $form = $this->field_types[$field]; 1744 else 1745 $form = '%s'; 1746 $formatted_fields[] = $form; 1747 } 1748 $sql = "{$type} INTO `$table` (`" . implode( '`,`', $fields ) . "`) VALUES (" . implode( ",", $formatted_fields ) . ")"; 1749 return $this->query( $this->prepare( $sql, $data ) ); 1803 1750 } 1804 1751 … … 1823 1770 */ 1824 1771 public function update( $table, $data, $where, $format = null, $where_format = null ) { 1825 if ( ! is_array( $data ) || ! is_array( $where ) ) {1772 if ( ! is_array( $data ) || ! is_array( $where ) ) 1826 1773 return false; 1827 } 1828 1829 $data = $this->process_fields( $table, $data, $format ); 1830 if ( false === $data ) { 1831 return false; 1832 } 1833 $where = $this->process_fields( $table, $where, $where_format ); 1834 if ( false === $where ) { 1835 return false; 1836 } 1837 1838 $fields = $conditions = $values = array(); 1839 foreach ( $data as $field => $value ) { 1840 $fields[] = "`$field` = " . $value['format']; 1841 $values[] = $value['value']; 1842 } 1843 foreach ( $where as $field => $value ) { 1844 $conditions[] = "`$field` = " . $value['format']; 1845 $values[] = $value['value']; 1846 } 1847 1848 $fields = implode( ', ', $fields ); 1849 $conditions = implode( ' AND ', $conditions ); 1850 1851 $sql = "UPDATE `$table` SET $fields WHERE $conditions"; 1852 1853 $this->check_current_query = false; 1854 return $this->query( $this->prepare( $sql, $values ) ); 1774 1775 $formats = $format = (array) $format; 1776 $bits = $wheres = array(); 1777 foreach ( (array) array_keys( $data ) as $field ) { 1778 if ( !empty( $format ) ) 1779 $form = ( $form = array_shift( $formats ) ) ? $form : $format[0]; 1780 elseif ( isset($this->field_types[$field]) ) 1781 $form = $this->field_types[$field]; 1782 else 1783 $form = '%s'; 1784 $bits[] = "`$field` = {$form}"; 1785 } 1786 1787 $where_formats = $where_format = (array) $where_format; 1788 foreach ( (array) array_keys( $where ) as $field ) { 1789 if ( !empty( $where_format ) ) 1790 $form = ( $form = array_shift( $where_formats ) ) ? $form : $where_format[0]; 1791 elseif ( isset( $this->field_types[$field] ) ) 1792 $form = $this->field_types[$field]; 1793 else 1794 $form = '%s'; 1795 $wheres[] = "`$field` = {$form}"; 1796 } 1797 1798 $sql = "UPDATE `$table` SET " . implode( ', ', $bits ) . ' WHERE ' . implode( ' AND ', $wheres ); 1799 return $this->query( $this->prepare( $sql, array_merge( array_values( $data ), array_values( $where ) ) ) ); 1855 1800 } 1856 1801 … … 1872 1817 */ 1873 1818 public function delete( $table, $where, $where_format = null ) { 1874 if ( ! is_array( $where ) ) {1819 if ( ! is_array( $where ) ) 1875 1820 return false; 1876 } 1877 1878 $where = $this->process_fields( $table, $where, $where_format ); 1879 if ( false === $where ) { 1880 return false; 1881 } 1882 1883 $conditions = $values = array(); 1884 foreach ( $where as $field => $value ) { 1885 $conditions[] = "`$field` = " . $value['format']; 1886 $values[] = $value['value']; 1887 } 1888 1889 $conditions = implode( ' AND ', $conditions ); 1890 1891 $sql = "DELETE FROM `$table` WHERE $conditions"; 1892 1893 $this->check_current_query = false; 1894 return $this->query( $this->prepare( $sql, $values ) ); 1895 } 1896 1897 /** 1898 * Processes arrays of field/value pairs and field formats. 1899 * 1900 * This is a helper method for wpdb's CRUD methods, which take field/value 1901 * pairs for inserts, updates, and where clauses. This method first pairs 1902 * each value with a format. Then it determines the charset of that field, 1903 * using that to determine if any invalid text would be stripped. If text is 1904 * stripped, then field processing is rejected and the query fails. 1905 * 1906 * @since 4.1.0 1907 * @access protected 1908 * 1909 * @param string $table Table name. 1910 * @param array $data Field/value pair. 1911 * @param mixed $format Format for each field. 1912 * @return array|bool Returns an array of fields that contain paired values 1913 * and formats. Returns false for invalid values. 1914 */ 1915 protected function process_fields( $table, $data, $format ) { 1916 $data = $this->process_field_formats( $data, $format ); 1917 $data = $this->process_field_charsets( $data, $table ); 1918 if ( false === $data ) { 1919 return false; 1920 } 1921 1922 $converted_data = $this->strip_invalid_text( $data ); 1923 1924 if ( $data !== $converted_data ) { 1925 return false; 1926 } 1927 1928 return $data; 1929 } 1930 1931 /** 1932 * Prepares arrays of value/format pairs as passed to wpdb CRUD methods. 1933 * 1934 * @since 4.1.0 1935 * @access protected 1936 * 1937 * @param array $data Array of fields to values. 1938 * @param mixed $format Formats to be mapped to the values in $data. 1939 * @return array Array, keyed by field names with values being an array 1940 * of 'value' and 'format' keys. 1941 */ 1942 protected function process_field_formats( $data, $format ) { 1943 $formats = $original_formats = (array) $format; 1944 1945 foreach ( $data as $field => $value ) { 1946 $value = array( 1947 'value' => $value, 1948 'format' => '%s', 1949 ); 1950 1951 if ( ! empty( $format ) ) { 1952 $value['format'] = array_shift( $formats ); 1953 if ( ! $value['format'] ) { 1954 $value['format'] = reset( $original_formats ); 1955 } 1821 1822 $wheres = array(); 1823 1824 $where_formats = $where_format = (array) $where_format; 1825 1826 foreach ( array_keys( $where ) as $field ) { 1827 if ( !empty( $where_format ) ) { 1828 $form = ( $form = array_shift( $where_formats ) ) ? $form : $where_format[0]; 1956 1829 } elseif ( isset( $this->field_types[ $field ] ) ) { 1957 $value['format'] = $this->field_types[ $field ]; 1958 } 1959 1960 $data[ $field ] = $value; 1961 } 1962 1963 return $data; 1964 } 1965 1966 /** 1967 * Adds field charsets to field/value/format arrays generated by 1968 * the {@see wpdb::process_field_formats()} method. 1969 * 1970 * @since 4.1.0 1971 * @access protected 1972 * 1973 * @param array $data As it comes from the {@see wpdb::process_field_formats()} method. 1974 * @param string $table Table name. 1975 * @return The same array as $data with additional 'charset' keys. 1976 */ 1977 protected function process_field_charsets( $data, $table ) { 1978 foreach ( $data as $field => $value ) { 1979 if ( '%d' === $value['format'] || '%f' === $value['format'] ) { 1980 // We can skip this field if we know it isn't a string. 1981 // This checks %d/%f versus ! %s because it's sprintf() could take more. 1982 $value['charset'] = false; 1983 } elseif ( $this->check_ascii( $value['value'] ) ) { 1984 // If it's ASCII, then we don't need the charset. We can skip this field. 1985 $value['charset'] = false; 1830 $form = $this->field_types[ $field ]; 1986 1831 } else { 1987 $value['charset'] = $this->get_col_charset( $table, $field ); 1988 if ( is_wp_error( $value['charset'] ) ) { 1989 return false; 1990 } 1991 1992 // This isn't ASCII. Don't have strip_invalid_text() re-check. 1993 $value['ascii'] = false; 1994 } 1995 1996 $data[ $field ] = $value; 1997 } 1998 1999 return $data; 1832 $form = '%s'; 1833 } 1834 1835 $wheres[] = "$field = $form"; 1836 } 1837 1838 $sql = "DELETE FROM $table WHERE " . implode( ' AND ', $wheres ); 1839 return $this->query( $this->prepare( $sql, $where ) ); 2000 1840 } 2001 1841 … … 2018 1858 2019 1859 if ( $query ) { 2020 $this->check_current_query = false;2021 1860 $this->query( $query ); 2022 1861 } … … 2047 1886 $this->func_call = "\$db->get_row(\"$query\",$output,$y)"; 2048 1887 if ( $query ) { 2049 $this->check_current_query = false;2050 1888 $this->query( $query ); 2051 1889 } else { … … 2085 1923 public function get_col( $query = null , $x = 0 ) { 2086 1924 if ( $query ) { 2087 $this->check_current_query = false;2088 1925 $this->query( $query ); 2089 1926 } … … 2114 1951 2115 1952 if ( $query ) { 2116 $this->check_current_query = false;2117 1953 $this->query( $query ); 2118 1954 } else { … … 2156 1992 2157 1993 /** 2158 * Retrieves the character set for the given table.2159 *2160 * @since 4.1.02161 * @access protected2162 *2163 * @param string $table Table name.2164 * @return string|WP_Error Table character set, {@see WP_Error} object if it couldn't be found.2165 */2166 protected function get_table_charset( $table ) {2167 $tablekey = strtolower( $table );2168 2169 /**2170 * Filter the table charset value before the DB is checked.2171 *2172 * Passing a non-null value to the filter will effectively short-circuit2173 * checking the DB for the charset, returning that value instead.2174 *2175 * @since 4.1.02176 *2177 * @param string $charset The character set to use. Default null.2178 * @param string $table The name of the table being checked.2179 */2180 $charset = apply_filters( 'pre_get_table_charset', null, $table );2181 if ( null !== $charset ) {2182 return $charset;2183 }2184 2185 if ( isset( $this->table_charset[ $tablekey ] ) ) {2186 return $this->table_charset[ $tablekey ];2187 }2188 2189 $charsets = $columns = array();2190 $results = $this->get_results( "SHOW FULL COLUMNS FROM `$table`" );2191 if ( ! $results ) {2192 return new WP_Error( 'wpdb_get_table_charset_failure' );2193 }2194 2195 foreach ( $results as $column ) {2196 $columns[ strtolower( $column->Field ) ] = $column;2197 }2198 2199 $this->col_meta[ $tablekey ] = $columns;2200 2201 foreach ( $columns as $column ) {2202 if ( ! empty( $column->Collation ) ) {2203 list( $charset ) = explode( '_', $column->Collation );2204 $charsets[ strtolower( $charset ) ] = true;2205 }2206 2207 list( $type ) = explode( '(', $column->Type );2208 2209 // A binary/blob means the whole query gets treated like this.2210 if ( in_array( strtoupper( $type ), array( 'BINARY', 'VARBINARY', 'TINYBLOB', 'MEDIUMBLOB', 'BLOB', 'LONGBLOB' ) ) ) {2211 $this->table_charset[ $tablekey ] = 'binary';2212 return 'binary';2213 }2214 }2215 2216 // utf8mb3 is an alias for utf8.2217 if ( isset( $charsets['utf8mb3'] ) ) {2218 $charsets['utf8'] = true;2219 unset( $charsets['utf8mb3'] );2220 }2221 2222 // Check if we have more than one charset in play.2223 $count = count( $charsets );2224 if ( 1 === $count ) {2225 $charset = key( $charsets );2226 } elseif ( 0 === $count ) {2227 // No charsets, assume this table can store whatever.2228 $charset = false;2229 } else {2230 // More than one charset. Remove latin1 if present and recalculate.2231 unset( $charsets['latin1'] );2232 $count = count( $charsets );2233 if ( 1 === $count ) {2234 // Only one charset (besides latin1).2235 $charset = key( $charsets );2236 } elseif ( 2 === $count && isset( $charsets['utf8'], $charsets['utf8mb4'] ) ) {2237 // Two charsets, but they're utf8 and utf8mb4, use utf8.2238 $charset = 'utf8';2239 } else {2240 // Two mixed character sets. ascii.2241 $charset = 'ascii';2242 }2243 }2244 2245 $this->table_charset[ $tablekey ] = $charset;2246 return $charset;2247 }2248 2249 /**2250 * Retrieves the character set for the given column.2251 *2252 * @since 4.1.02253 * @access protected2254 *2255 * @param string $table Table name.2256 * @param string $column Column name.2257 * @return mixed Column character set as a string. False if the column has no2258 * character set. {@see WP_Error} object if there was an error.2259 */2260 protected function get_col_charset( $table, $column ) {2261 $tablekey = strtolower( $table );2262 $columnkey = strtolower( $column );2263 2264 /**2265 * Filter the column charset value before the DB is checked.2266 *2267 * Passing a non-null value to the filter will short-circuit2268 * checking the DB for the charset, returning that value instead.2269 *2270 * @since 4.1.02271 *2272 * @param string $charset The character set to use. Default null.2273 * @param string $table The name of the table being checked.2274 * @param string $column The name of the column being checked.2275 */2276 $charset = apply_filters( 'pre_get_col_charset', null, $table, $column );2277 if ( null !== $charset ) {2278 return $charset;2279 }2280 2281 // Skip this entirely if this isn't a MySQL database.2282 if ( false === $this->is_mysql ) {2283 return false;2284 }2285 2286 if ( empty( $this->table_charset[ $tablekey ] ) ) {2287 // This primes column information for us.2288 $table_charset = $this->get_table_charset( $table );2289 if ( is_wp_error( $table_charset ) ) {2290 return $table_charset;2291 }2292 }2293 2294 // If still no column information, return the table charset.2295 if ( empty( $this->col_meta[ $tablekey ] ) ) {2296 return $this->table_charset[ $tablekey ];2297 }2298 2299 // If this column doesn't exist, return the table charset.2300 if ( empty( $this->col_meta[ $tablekey ][ $columnkey ] ) ) {2301 return $this->table_charset[ $tablekey ];2302 }2303 2304 // Return false when it's not a string column.2305 if ( empty( $this->col_meta[ $tablekey ][ $columnkey ]->Collation ) ) {2306 return false;2307 }2308 2309 list( $charset ) = explode( '_', $this->col_meta[ $tablekey ][ $columnkey ]->Collation );2310 return $charset;2311 }2312 2313 /**2314 * Check if a string is ASCII.2315 *2316 * The negative regex is faster for non-ASCII strings, as it allows2317 * the search to finish as soon as it encounters a non-ASCII character.2318 *2319 * @since 4.1.02320 * @access protected2321 *2322 * @param string $string String to check.2323 * @return bool True if ASCII, false if not.2324 */2325 protected function check_ascii( $string ) {2326 if ( function_exists( 'mb_check_encoding' ) ) {2327 if ( mb_check_encoding( $string, 'ASCII' ) ) {2328 return true;2329 }2330 } elseif ( ! preg_match( '/[^\x00-\x7F]/', $string ) ) {2331 return true;2332 }2333 2334 return false;2335 }2336 2337 /**2338 * Strips any invalid characters based on value/charset pairs.2339 *2340 * @since 4.1.02341 * @access protected2342 *2343 * @param array $data Array of value arrays. Each value array has the keys2344 * 'value' and 'charset'. An optional 'ascii' key can be2345 * set to false to avoid redundant ASCII checks.2346 * @return array|WP_Error The $data parameter, with invalid characters removed from2347 * each value. This works as a passthrough: any additional keys2348 * such as 'field' are retained in each value array. If we cannot2349 * remove invalid characters, a {@see WP_Error} object is returned.2350 */2351 protected function strip_invalid_text( $data ) {2352 // Some multibyte character sets that we can check in PHP.2353 $mb_charsets = array(2354 'ascii' => 'ASCII',2355 'big5' => 'BIG-5',2356 'eucjpms' => 'eucJP-win',2357 'gb2312' => 'EUC-CN',2358 'ujis' => 'EUC-JP',2359 'utf32' => 'UTF-32',2360 'utf8mb4' => 'UTF-8',2361 );2362 2363 $supported_charsets = array();2364 if ( function_exists( 'mb_list_encodings' ) ) {2365 $supported_charsets = mb_list_encodings();2366 }2367 2368 $db_check_string = false;2369 2370 foreach ( $data as &$value ) {2371 $charset = $value['charset'];2372 2373 // Column isn't a string, or is latin1, which will will happily store anything.2374 if ( false === $charset || 'latin1' === $charset ) {2375 continue;2376 }2377 2378 if ( ! is_string( $value['value'] ) ) {2379 continue;2380 }2381 2382 // ASCII is always OK.2383 if ( ! isset( $value['ascii'] ) && $this->check_ascii( $value['value'] ) ) {2384 continue;2385 }2386 2387 // Convert the text locally.2388 if ( $supported_charsets ) {2389 if ( isset( $mb_charsets[ $charset ] ) && in_array( $mb_charsets[ $charset ], $supported_charsets ) ) {2390 $value['value'] = mb_convert_encoding( $value['value'], $mb_charsets[ $charset ], $mb_charsets[ $charset ] );2391 continue;2392 }2393 }2394 2395 // utf8(mb3) can be handled by regex, which is a bunch faster than a DB lookup.2396 if ( 'utf8' === $charset || 'utf8mb3' === $charset ) {2397 $regex = '/2398 (2399 (?: [\x00-\x7F] # single-byte sequences 0xxxxxxx2400 | [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx2401 | \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 22402 | [\xE1-\xEC][\x80-\xBF]{2}2403 | \xED[\x80-\x9F][\x80-\xBF]2404 | [\xEE-\xEF][\x80-\xBF]{2}2405 ){1,50} # ...one or more times2406 )2407 | . # anything else2408 /x';2409 $value['value'] = preg_replace( $regex, '$1', $value['value'] );2410 continue;2411 }2412 2413 // We couldn't use any local conversions, send it to the DB.2414 $value['db'] = $db_check_string = true;2415 }2416 unset( $value ); // Remove by reference.2417 2418 if ( $db_check_string ) {2419 $queries = array();2420 foreach ( $data as $col => $value ) {2421 if ( ! empty( $value['db'] ) ) {2422 if ( ! isset( $queries[ $value['charset'] ] ) ) {2423 $queries[ $value['charset'] ] = array();2424 }2425 2426 // Split the CONVERT() calls by charset, so we can make sure the connection is right2427 $queries[ $value['charset'] ][ $col ] = $this->prepare( "CONVERT( %s USING {$value['charset']} )", $value['value'] );2428 }2429 }2430 2431 $connection_charset = $this->charset;2432 foreach ( $queries as $charset => $query ) {2433 if ( ! $query ) {2434 continue;2435 }2436 2437 // Change the charset to match the string(s) we're converting2438 if ( $charset !== $this->charset ) {2439 $this->set_charset( $this->dbh, $charset );2440 }2441 2442 $row = $this->get_row( "SELECT " . implode( ', ', $query ), ARRAY_N );2443 if ( ! $row ) {2444 $this->set_charset( $this->dbh, $connection_charset );2445 return new WP_Error( 'wpdb_strip_invalid_text_failure' );2446 }2447 2448 $cols = array_keys( $query );2449 $col_count = count( $cols );2450 for ( $ii = 0; $ii < $col_count; $ii++ ) {2451 $data[ $cols[ $ii ] ]['value'] = $row[ $ii ];2452 }2453 }2454 2455 // Don't forget to change the charset back!2456 if ( $connection_charset !== $this->charset ) {2457 $this->set_charset( $this->dbh, $connection_charset );2458 }2459 }2460 2461 return $data;2462 }2463 2464 /**2465 * Strips any invalid characters from the query.2466 *2467 * @since 4.1.02468 * @access protected2469 *2470 * @param string $query Query to convert.2471 * @return string|WP_Error The converted query, or a {@see WP_Error} object if the conversion fails.2472 */2473 protected function strip_invalid_text_from_query( $query ) {2474 $table = $this->get_table_from_query( $query );2475 if ( $table ) {2476 $charset = $this->get_table_charset( $table );2477 if ( is_wp_error( $charset ) ) {2478 return $charset;2479 }2480 2481 // We can't reliably strip text from tables containing binary/blob columns2482 if ( 'binary' === $charset ) {2483 return $query;2484 }2485 } else {2486 $charset = $this->charset;2487 }2488 2489 $data = array(2490 'value' => $query,2491 'charset' => $charset,2492 'ascii' => false,2493 );2494 2495 $data = $this->strip_invalid_text( array( $data ) );2496 if ( is_wp_error( $data ) ) {2497 return $data;2498 }2499 2500 return $data[0]['value'];2501 }2502 2503 /**2504 * Strips any invalid characters from the string for a given table and column.2505 *2506 * @since 4.1.02507 * @access public2508 *2509 * @param string $table Table name.2510 * @param string $column Column name.2511 * @param string $value The text to check.2512 * @return string|WP_Error The converted string, or a `WP_Error` object if the conversion fails.2513 */2514 public function strip_invalid_text_for_column( $table, $column, $value ) {2515 if ( $this->check_ascii( $value ) || ! is_string( $value ) ) {2516 return $value;2517 }2518 2519 $charset = $this->get_col_charset( $table, $column );2520 if ( ! $charset ) {2521 // Not a string column.2522 return $value;2523 } elseif ( is_wp_error( $charset ) ) {2524 // Bail on real errors.2525 return $charset;2526 }2527 2528 $data = array(2529 $column => array(2530 'value' => $value,2531 'charset' => $charset,2532 'ascii' => false,2533 )2534 );2535 2536 $data = $this->strip_invalid_text( $data );2537 if ( is_wp_error( $data ) ) {2538 return $data;2539 }2540 2541 return $data[ $column ]['value'];2542 }2543 2544 /**2545 * Find the first table name referenced in a query.2546 *2547 * @since 4.1.02548 * @access protected2549 *2550 * @param string $query The query to search.2551 * @return string|false $table The table name found, or false if a table couldn't be found.2552 */2553 protected function get_table_from_query( $query ) {2554 // Remove characters that can legally trail the table name.2555 $query = rtrim( $query, ';/-#' );2556 2557 // Allow (select...) union [...] style queries. Use the first query's table name.2558 $query = ltrim( $query, "\r\n\t (" );2559 2560 /*2561 * Strip everything between parentheses except nested selects and use only 1,0002562 * chars of the query.2563 */2564 $query = preg_replace( '/\((?!\s*select)[^(]*?\)/is', '()', substr( $query, 0, 1000 ) );2565 2566 // Quickly match most common queries.2567 if ( preg_match( '/^\s*(?:'2568 . 'SELECT.*?\s+FROM'2569 . '|INSERT(?:\s+LOW_PRIORITY|\s+DELAYED|\s+HIGH_PRIORITY)?(?:\s+IGNORE)?(?:\s+INTO)?'2570 . '|REPLACE(?:\s+LOW_PRIORITY|\s+DELAYED)?(?:\s+INTO)?'2571 . '|UPDATE(?:\s+LOW_PRIORITY)?(?:\s+IGNORE)?'2572 . '|DELETE(?:\s+LOW_PRIORITY|\s+QUICK|\s+IGNORE)*(?:\s+FROM)?'2573 . ')\s+`?([\w-]+)`?/is', $query, $maybe ) ) {2574 return $maybe[1];2575 }2576 2577 // SHOW TABLE STATUS and SHOW TABLES2578 if ( preg_match( '/^\s*(?:'2579 . 'SHOW\s+TABLE\s+STATUS.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)'2580 . '|SHOW\s+(?:FULL\s+)?TABLES.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)'2581 . ')\W([\w-]+)\W/is', $query, $maybe ) ) {2582 return $maybe[1];2583 }2584 2585 // Big pattern for the rest of the table-related queries.2586 if ( preg_match( '/^\s*(?:'2587 . '(?:EXPLAIN\s+(?:EXTENDED\s+)?)?SELECT.*?\s+FROM'2588 . '|DESCRIBE|DESC|EXPLAIN|HANDLER'2589 . '|(?:LOCK|UNLOCK)\s+TABLE(?:S)?'2590 . '|(?:RENAME|OPTIMIZE|BACKUP|RESTORE|CHECK|CHECKSUM|ANALYZE|REPAIR).*\s+TABLE'2591 . '|TRUNCATE(?:\s+TABLE)?'2592 . '|CREATE(?:\s+TEMPORARY)?\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?'2593 . '|ALTER(?:\s+IGNORE)?\s+TABLE'2594 . '|DROP\s+TABLE(?:\s+IF\s+EXISTS)?'2595 . '|CREATE(?:\s+\w+)?\s+INDEX.*\s+ON'2596 . '|DROP\s+INDEX.*\s+ON'2597 . '|LOAD\s+DATA.*INFILE.*INTO\s+TABLE'2598 . '|(?:GRANT|REVOKE).*ON\s+TABLE'2599 . '|SHOW\s+(?:.*FROM|.*TABLE)'2600 . ')\s+\(*\s*`?([\w-]+)`?\s*\)*/is', $query, $maybe ) ) {2601 return $maybe[1];2602 }2603 2604 return false;2605 }2606 2607 /**2608 1994 * Load the column metadata from the last query. 2609 1995 * -
branches/4.1/tests/phpunit/tests/db.php
r30704 r30807 233 233 $this->assertNotEmpty( $wpdb->dbh ); 234 234 } 235 236 /**237 * @ticket 21212238 */239 function test_wpdb_actually_protected_properties() {240 global $wpdb;241 242 $new_meta = "HAHA I HOPE THIS DOESN'T WORK";243 244 $col_meta = $wpdb->col_meta;245 $wpdb->col_meta = $new_meta;246 247 $this->assertNotEquals( $col_meta, $new_meta );248 $this->assertEquals( $col_meta, $wpdb->col_meta );249 }250 251 235 /** 252 236 * @ticket 18510 … … 521 505 $wpdb->suppress_errors( $suppress ); 522 506 } 523 524 /**525 * @ticket 21212526 */527 function data_get_table_from_query() {528 $table = 'a_test_table_name';529 530 $queries = array(531 // Basic532 "SELECT * FROM $table",533 "SELECT * FROM `$table`",534 535 "INSERT $table",536 "INSERT IGNORE $table",537 "INSERT IGNORE INTO $table",538 "INSERT INTO $table",539 "INSERT LOW_PRIORITY $table",540 "INSERT DELAYED $table",541 "INSERT HIGH_PRIORITY $table",542 "INSERT LOW_PRIORITY IGNORE $table",543 "INSERT LOW_PRIORITY INTO $table",544 "INSERT LOW_PRIORITY IGNORE INTO $table",545 546 "REPLACE $table",547 "REPLACE INTO $table",548 "REPLACE LOW_PRIORITY $table",549 "REPLACE DELAYED $table",550 "REPLACE LOW_PRIORITY INTO $table",551 552 "UPDATE LOW_PRIORITY $table",553 "UPDATE LOW_PRIORITY IGNORE $table",554 555 "DELETE $table",556 "DELETE IGNORE $table",557 "DELETE IGNORE FROM $table",558 "DELETE FROM $table",559 "DELETE LOW_PRIORITY $table",560 "DELETE QUICK $table",561 "DELETE IGNORE $table",562 "DELETE LOW_PRIORITY FROM $table",563 564 // STATUS565 "SHOW TABLE STATUS LIKE '$table'",566 "SHOW TABLE STATUS WHERE NAME='$table'",567 568 "SHOW TABLES LIKE '$table'",569 "SHOW FULL TABLES LIKE '$table'",570 "SHOW TABLES WHERE NAME='$table'",571 572 // Extended573 "EXPLAIN SELECT * FROM $table",574 "EXPLAIN EXTENDED SELECT * FROM $table",575 "EXPLAIN EXTENDED SELECT * FROM `$table`",576 577 "DESCRIBE $table",578 "DESC $table",579 "EXPLAIN $table",580 "HANDLER $table",581 582 "LOCK TABLE $table",583 "LOCK TABLES $table",584 "UNLOCK TABLE $table",585 586 "RENAME TABLE $table",587 "OPTIMIZE TABLE $table",588 "BACKUP TABLE $table",589 "RESTORE TABLE $table",590 "CHECK TABLE $table",591 "CHECKSUM TABLE $table",592 "ANALYZE TABLE $table",593 "REPAIR TABLE $table",594 595 "TRUNCATE $table",596 "TRUNCATE TABLE $table",597 598 "CREATE TABLE $table",599 "CREATE TEMPORARY TABLE $table",600 "CREATE TABLE IF NOT EXISTS $table",601 602 "ALTER TABLE $table",603 "ALTER IGNORE TABLE $table",604 605 "DROP TABLE $table",606 "DROP TABLE IF EXISTS $table",607 608 "CREATE INDEX foo(bar(20)) ON $table",609 "CREATE UNIQUE INDEX foo(bar(20)) ON $table",610 "CREATE FULLTEXT INDEX foo(bar(20)) ON $table",611 "CREATE SPATIAL INDEX foo(bar(20)) ON $table",612 613 "DROP INDEX foo ON $table",614 615 "LOAD DATA INFILE 'wp.txt' INTO TABLE $table",616 "LOAD DATA LOW_PRIORITY INFILE 'wp.txt' INTO TABLE $table",617 "LOAD DATA CONCURRENT INFILE 'wp.txt' INTO TABLE $table",618 "LOAD DATA LOW_PRIORITY LOCAL INFILE 'wp.txt' INTO TABLE $table",619 "LOAD DATA INFILE 'wp.txt' REPLACE INTO TABLE $table",620 "LOAD DATA INFILE 'wp.txt' IGNORE INTO TABLE $table",621 622 "GRANT ALL ON TABLE $table",623 "REVOKE ALL ON TABLE $table",624 625 "SHOW COLUMNS FROM $table",626 "SHOW FULL COLUMNS FROM $table",627 "SHOW CREATE TABLE $table",628 "SHOW INDEX FROM $table",629 );630 631 foreach ( $queries as &$query ) {632 $query = array( $query, $table );633 }634 return $queries;635 }636 637 /**638 * @dataProvider data_get_table_from_query639 * @ticket 21212640 */641 function test_get_table_from_query( $query, $table ) {642 $this->assertEquals( $table, self::$_wpdb->get_table_from_query( $query ) );643 }644 645 function data_get_table_from_query_false() {646 $table = 'a_test_table_name';647 return array(648 array( "LOL THIS ISN'T EVEN A QUERY $table" ),649 );650 }651 652 /**653 * @dataProvider data_get_table_from_query_false654 * @ticket 21212655 */656 function test_get_table_from_query_false( $query ) {657 $this->assertFalse( self::$_wpdb->get_table_from_query( $query ) );658 }659 660 /**661 * @ticket 21212662 */663 function data_process_field_formats() {664 $core_db_fields_no_format_specified = array(665 array( 'post_content' => 'foo', 'post_parent' => 0 ),666 null,667 array(668 'post_content' => array( 'value' => 'foo', 'format' => '%s' ),669 'post_parent' => array( 'value' => 0, 'format' => '%d' ),670 )671 );672 673 $core_db_fields_formats_specified = array(674 array( 'post_content' => 'foo', 'post_parent' => 0 ),675 array( '%d', '%s' ), // These override core field_types676 array(677 'post_content' => array( 'value' => 'foo', 'format' => '%d' ),678 'post_parent' => array( 'value' => 0, 'format' => '%s' ),679 )680 );681 682 $misc_fields_no_format_specified = array(683 array( 'this_is_not_a_core_field' => 'foo', 'this_is_not_either' => 0 ),684 null,685 array(686 'this_is_not_a_core_field' => array( 'value' => 'foo', 'format' => '%s' ),687 'this_is_not_either' => array( 'value' => 0, 'format' => '%s' ),688 )689 );690 691 $misc_fields_formats_specified = array(692 array( 'this_is_not_a_core_field' => 0, 'this_is_not_either' => 1.2 ),693 array( '%d', '%f' ),694 array(695 'this_is_not_a_core_field' => array( 'value' => 0, 'format' => '%d' ),696 'this_is_not_either' => array( 'value' => 1.2, 'format' => '%f' ),697 )698 );699 700 $misc_fields_insufficient_formats_specified = array(701 array( 'this_is_not_a_core_field' => 0, 'this_is_not_either' => 's', 'nor_this' => 1 ),702 array( '%d', '%s' ), // The first format is used for the third703 array(704 'this_is_not_a_core_field' => array( 'value' => 0, 'format' => '%d' ),705 'this_is_not_either' => array( 'value' => 's', 'format' => '%s' ),706 'nor_this' => array( 'value' => 1, 'format' => '%d' ),707 )708 );709 710 $vars = get_defined_vars();711 // Push the variable name onto the end for assertSame $message712 foreach ( $vars as $var_name => $var ) {713 $vars[ $var_name ][] = $var_name;714 }715 return array_values( $vars );716 }717 718 /**719 * @dataProvider data_process_field_formats720 * @ticket 21212721 */722 function test_process_field_formats( $data, $format, $expected, $message ) {723 $actual = self::$_wpdb->process_field_formats( $data, $format );724 $this->assertSame( $expected, $actual, $message );725 }726 727 /**728 * @ticket 21212729 */730 function test_process_fields() {731 global $wpdb;732 $data = array( 'post_content' => '¡foo foo foo!' );733 $expected = array(734 'post_content' => array(735 'value' => '¡foo foo foo!',736 'format' => '%s',737 'charset' => $wpdb->charset,738 'ascii' => false,739 )740 );741 742 $this->assertSame( $expected, self::$_wpdb->process_fields( $wpdb->posts, $data, null ) );743 }744 745 /**746 * @ticket 21212747 * @depends test_process_fields748 */749 function test_process_fields_on_nonexistent_table( $data ) {750 self::$_wpdb->suppress_errors( true );751 $data = array( 'post_content' => '¡foo foo foo!' );752 $this->assertFalse( self::$_wpdb->process_fields( 'nonexistent_table', $data, null ) );753 self::$_wpdb->suppress_errors( false );754 }755 756 /**757 * @ticket 21212758 */759 function test_pre_get_table_charset_filter() {760 add_filter( 'pre_get_table_charset', array( $this, 'filter_pre_get_table_charset' ), 10, 2 );761 $charset = self::$_wpdb->get_table_charset( 'some_table' );762 remove_filter( 'pre_get_table_charset', array( $this, 'filter_pre_get_table_charset' ), 10 );763 764 $this->assertEquals( $charset, 'fake_charset' );765 }766 function filter_pre_get_table_charset( $charset, $table ) {767 return 'fake_charset';768 }769 770 /**771 * @ticket 21212772 */773 function test_pre_get_col_charset_filter() {774 add_filter( 'pre_get_col_charset', array( $this, 'filter_pre_get_col_charset' ), 10, 3 );775 $charset = self::$_wpdb->get_col_charset( 'some_table', 'some_col' );776 remove_filter( 'pre_get_col_charset', array( $this, 'filter_pre_get_col_charset' ), 10 );777 778 $this->assertEquals( $charset, 'fake_col_charset' );779 }780 function filter_pre_get_col_charset( $charset, $table, $column ) {781 return 'fake_col_charset';782 }783 507 } 784 508 -
branches/4.1/tests/phpunit/tests/db/charset.php
r30699 r30807 1 <?php2 3 require_once dirname( dirname( __FILE__ ) ) . '/db.php';4 5 /**6 * Test WPDB methods7 *8 * @group wpdb9 */10 class Tests_DB_Charset extends WP_UnitTestCase {11 12 /**13 * Our special WPDB14 * @var resource15 */16 protected static $_wpdb;17 18 public static function setUpBeforeClass() {19 self::$_wpdb = new wpdb_exposed_methods_for_testing();20 }21 22 /**23 * @ticket 2121224 */25 function data_strip_invalid_text() {26 $fields = array(27 'latin1' => array(28 // latin1. latin1 never changes.29 'charset' => 'latin1',30 'value' => "\xf0\x9f\x8e\xb7",31 'expected' => "\xf0\x9f\x8e\xb7"32 ),33 'ascii' => array(34 // ascii gets special treatment, make sure it's covered35 'charset' => 'ascii',36 'value' => 'Hello World',37 'expected' => 'Hello World'38 ),39 'utf8' => array(40 // utf8 only allows <= 3-byte chars41 'charset' => 'utf8',42 'value' => "H€llo\xf0\x9f\x98\x88World¢",43 'expected' => 'H€lloWorld¢'44 ),45 'utf8mb3' => array(46 // utf8mb3 should behave the same an utf847 'charset' => 'utf8mb3',48 'value' => "H€llo\xf0\x9f\x98\x88World¢",49 'expected' => 'H€lloWorld¢'50 ),51 'utf8mb4' => array(52 // utf8mb4 allows 4-byte characters, too53 'charset' => 'utf8mb4',54 'value' => "H€llo\xf0\x9f\x98\x88World¢",55 'expected' => "H€llo\xf0\x9f\x98\x88World¢"56 ),57 'koi8r' => array(58 // koi8r is a character set that needs to be checked in MySQL59 'charset' => 'koi8r',60 'value' => "\xfdord\xf2ress",61 'expected' => "\xfdord\xf2ress",62 'db' => true63 ),64 'hebrew' => array(65 // hebrew needs to be checked in MySQL, too66 'charset' => 'hebrew',67 'value' => "\xf9ord\xf7ress",68 'expected' => "\xf9ord\xf7ress",69 'db' => true70 ),71 'false' => array(72 // false is a column with no character set (ie, a number column)73 'charset' => false,74 'value' => 100,75 'expected' => 10076 ),77 );78 79 if ( function_exists( 'mb_convert_encoding' ) ) {80 // big5 is a non-Unicode multibyte charset81 $utf8 = "a\xe5\x85\xb1b"; // UTF-8 Character 2084982 $big5 = mb_convert_encoding( $utf8, 'BIG-5', 'UTF-8' );83 $conv_utf8 = mb_convert_encoding( $big5, 'UTF-8', 'BIG-5' );84 // Make sure PHP's multibyte conversions are working correctly85 $this->assertNotEquals( $utf8, $big5 );86 $this->assertEquals( $utf8, $conv_utf8 );87 88 $fields['big5'] = array(89 'charset' => 'big5',90 'value' => $big5,91 'expected' => $big592 );93 }94 95 // The data above is easy to edit. Now, prepare it for the data provider.96 $data_provider = $multiple = $multiple_expected = array();97 foreach ( $fields as $test_case => $field ) {98 $expected = $field;99 $expected['value'] = $expected['expected'];100 unset( $expected['expected'], $field['expected'] );101 102 // We're keeping track of these for our multiple-field test.103 $multiple[] = $field;104 $multiple_expected[] = $expected;105 106 // strip_invalid_text() expects an array of fields. We're testing one field at a time.107 $data = array( $field );108 $expected = array( $expected );109 110 // First argument is field data. Second is expected. Third is the message.111 $data_provider[] = array( $data, $expected, $test_case );112 }113 114 // Time for our test of multiple fields at once.115 $data_provider[] = array( $multiple, $multiple_expected, 'multiple fields/charsets' );116 117 return $data_provider;118 }119 120 /**121 * @dataProvider data_strip_invalid_text122 * @ticket 21212123 */124 function test_strip_invalid_text( $data, $expected, $message ) {125 if ( $data[0]['charset'] === 'koi8r' ) {126 self::$_wpdb->query( 'SET NAMES koi8r' );127 }128 $actual = self::$_wpdb->strip_invalid_text( $data );129 $this->assertSame( $expected, $actual, $message );130 }131 132 /**133 * @ ticket 21212134 */135 function test_process_fields_failure() {136 global $wpdb;137 $data = array( 'post_content' => "H€llo\xf0\x9f\x98\x88World¢" );138 $this->assertFalse( self::$_wpdb->process_fields( $wpdb->posts, $data, null ) );139 }140 141 /**142 * @ticket 21212143 */144 function data_process_field_charsets() {145 $charset = $GLOBALS['wpdb']->charset; // This is how all tables were installed146 // 'value' and 'format' are $data, 'charset' ends up as part of $expected147 148 $no_string_fields = array(149 'post_parent' => array( 'value' => 10, 'format' => '%d', 'charset' => false ),150 'comment_count' => array( 'value' => 0, 'format' => '%d', 'charset' => false ),151 );152 153 $all_ascii_fields = array(154 'post_content' => array( 'value' => 'foo foo foo!', 'format' => '%s', 'charset' => false ),155 'post_excerpt' => array( 'value' => 'bar bar bar!', 'format' => '%s', 'charset' => false ),156 );157 158 // This is the same data used in process_field_charsets_for_nonexistent_table()159 $non_ascii_string_fields = array(160 'post_content' => array( 'value' => '¡foo foo foo!', 'format' => '%s', 'charset' => $charset, 'ascii' => false ),161 'post_excerpt' => array( 'value' => '¡bar bar bar!', 'format' => '%s', 'charset' => $charset, 'ascii' => false ),162 );163 164 $vars = get_defined_vars();165 unset( $vars['charset'] );166 foreach ( $vars as $var_name => $var ) {167 $data = $expected = $var;168 foreach ( $data as &$datum ) {169 // 'charset' and 'ascii' are part of the expected return only.170 unset( $datum['charset'], $datum['ascii'] );171 }172 173 $vars[ $var_name ] = array( $data, $expected, $var_name );174 }175 176 return array_values( $vars );177 }178 179 /**180 * @dataProvider data_process_field_charsets181 * @ticket 21212182 */183 function test_process_field_charsets( $data, $expected, $message ) {184 $actual = self::$_wpdb->process_field_charsets( $data, $GLOBALS['wpdb']->posts );185 $this->assertSame( $expected, $actual, $message );186 }187 188 /**189 * The test this test depends on first verifies that this190 * would normally work against the posts table.191 *192 * @ticket 21212193 * @depends test_process_field_charsets194 */195 function test_process_field_charsets_on_nonexistent_table() {196 $data = array( 'post_content' => array( 'value' => '¡foo foo foo!', 'format' => '%s' ) );197 self::$_wpdb->suppress_errors( true );198 $this->assertFalse( self::$_wpdb->process_field_charsets( $data, 'nonexistent_table' ) );199 self::$_wpdb->suppress_errors( false );200 }201 202 /**203 * @ticket 21212204 */205 function test_check_ascii() {206 $ascii = "\0\t\n\r '" . '!"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';207 $this->assertTrue( self::$_wpdb->check_ascii( $ascii ) );208 }209 210 /**211 * @ticket 21212212 */213 function test_check_ascii_false() {214 $this->assertFalse( self::$_wpdb->check_ascii( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ¡©«' ) );215 }216 217 /**218 * @ticket 21212219 */220 function test_strip_invalid_text_for_column() {221 global $wpdb;222 // Invalid 3-byte and 4-byte sequences223 $value = "H€llo\xe0\x80\x80World\xf0\xff\xff\xff¢";224 $expected = "H€lloWorld¢";225 $actual = $wpdb->strip_invalid_text_for_column( $wpdb->posts, 'post_content', $value );226 $this->assertEquals( $expected, $actual );227 }228 229 /**230 * Set of table definitions for testing wpdb::get_table_charset and wpdb::get_column_charset231 * @var array232 */233 protected $table_and_column_defs = array(234 array(235 'definition' => '( a INT, b FLOAT )',236 'table_expected' => false,237 'column_expected' => array( 'a' => false, 'b' => false )238 ),239 array(240 'definition' => '( a VARCHAR(50) CHARACTER SET big5, b TEXT CHARACTER SET big5 )',241 'table_expected' => 'big5',242 'column_expected' => array( 'a' => 'big5', 'b' => 'big5' )243 ),244 array(245 'definition' => '( a VARCHAR(50) CHARACTER SET big5, b BINARY )',246 'table_expected' => 'binary',247 'column_expected' => array( 'a' => 'big5', 'b' => false )248 ),249 array(250 'definition' => '( a VARCHAR(50) CHARACTER SET latin1, b BLOB )',251 'table_expected' => 'binary',252 'column_expected' => array( 'a' => 'latin1', 'b' => false )253 ),254 array(255 'definition' => '( a VARCHAR(50) CHARACTER SET latin1, b TEXT CHARACTER SET koi8r )',256 'table_expected' => 'koi8r',257 'column_expected' => array( 'a' => 'latin1', 'b' => 'koi8r' )258 ),259 array(260 'definition' => '( a VARCHAR(50) CHARACTER SET utf8mb3, b TEXT CHARACTER SET utf8mb3 )',261 'table_expected' => 'utf8',262 'column_expected' => array( 'a' => 'utf8', 'b' => 'utf8' )263 ),264 array(265 'definition' => '( a VARCHAR(50) CHARACTER SET utf8, b TEXT CHARACTER SET utf8mb4 )',266 'table_expected' => 'utf8',267 'column_expected' => array( 'a' => 'utf8', 'b' => 'utf8mb4' )268 ),269 array(270 'definition' => '( a VARCHAR(50) CHARACTER SET big5, b TEXT CHARACTER SET koi8r )',271 'table_expected' => 'ascii',272 'column_expected' => array( 'a' => 'big5', 'b' => 'koi8r' )273 ),274 );275 276 /**277 * @ticket 21212278 */279 function data_test_get_table_charset() {280 $table_name = 'test_get_table_charset';281 282 $vars = array();283 foreach( $this->table_and_column_defs as $value ) {284 $this_table_name = $table_name . '_' . rand_str( 5 );285 $drop = "DROP TABLE IF EXISTS $this_table_name";286 $create = "CREATE TABLE $this_table_name {$value['definition']}";287 $vars[] = array( $drop, $create, $this_table_name, $value['table_expected'] );288 }289 290 return $vars;291 }292 293 /**294 * @dataProvider data_test_get_table_charset295 * @ticket 21212296 */297 function test_get_table_charset( $drop, $create, $table, $expected_charset ) {298 self::$_wpdb->query( $drop );299 300 if ( ! self::$_wpdb->has_cap( 'utf8mb4' ) && preg_match( '/utf8mb[34]/i', $create ) ) {301 $this->markTestSkipped( "This version of MySQL doesn't support utf8mb4." );302 return;303 }304 305 self::$_wpdb->query( $create );306 307 $charset = self::$_wpdb->get_table_charset( $table );308 $this->assertEquals( $charset, $expected_charset );309 310 $charset = self::$_wpdb->get_table_charset( strtoupper( $table ) );311 $this->assertEquals( $charset, $expected_charset );312 313 self::$_wpdb->query( $drop );314 }315 316 /**317 * @ticket 21212318 */319 function data_test_get_column_charset() {320 $table_name = 'test_get_column_charset';321 322 $vars = array();323 foreach( $this->table_and_column_defs as $value ) {324 $this_table_name = $table_name . '_' . rand_str( 5 );325 $drop = "DROP TABLE IF EXISTS $this_table_name";326 $create = "CREATE TABLE $this_table_name {$value['definition']}";327 $vars[] = array( $drop, $create, $this_table_name, $value['column_expected'] );328 }329 330 return $vars;331 }332 333 /**334 * @dataProvider data_test_get_column_charset335 * @ticket 21212336 */337 function test_get_column_charset( $drop, $create, $table, $expected_charset ) {338 self::$_wpdb->query( $drop );339 340 if ( ! self::$_wpdb->has_cap( 'utf8mb4' ) && preg_match( '/utf8mb[34]/i', $create ) ) {341 $this->markTestSkipped( "This version of MySQL doesn't support utf8mb4." );342 return;343 }344 345 self::$_wpdb->query( $create );346 347 foreach ( $expected_charset as $column => $charset ) {348 $this->assertEquals( $charset, self::$_wpdb->get_col_charset( $table, $column ) );349 $this->assertEquals( $charset, self::$_wpdb->get_col_charset( strtoupper( $table ), strtoupper( $column ) ) );350 }351 352 self::$_wpdb->query( $drop );353 }354 355 /**356 * @dataProvider data_test_get_column_charset357 * @ticket 21212358 */359 function test_get_column_charset_non_mysql( $drop, $create, $table, $columns ) {360 self::$_wpdb->query( $drop );361 362 if ( ! self::$_wpdb->has_cap( 'utf8mb4' ) && preg_match( '/utf8mb[34]/i', $create ) ) {363 $this->markTestSkipped( "This version of MySQL doesn't support utf8mb4." );364 return;365 }366 367 self::$_wpdb->is_mysql = false;368 369 self::$_wpdb->query( $create );370 371 $columns = array_keys( $columns );372 foreach ( $columns as $column => $charset ) {373 $this->assertEquals( false, self::$_wpdb->get_col_charset( $table, $column ) );374 }375 376 self::$_wpdb->query( $drop );377 378 self::$_wpdb->is_mysql = true;379 }380 381 /**382 * @ticket 21212383 */384 function data_strip_invalid_text_from_query() {385 $table_name = 'strip_invalid_text_from_query_table';386 $data = array(387 array(388 // binary tables don't get stripped389 "( a VARCHAR(50) CHARACTER SET utf8, b BINARY )", // create390 "('foo\xf0\x9f\x98\x88bar', 'foo')", // query391 "('foo\xf0\x9f\x98\x88bar', 'foo')" // expected result392 ),393 array(394 // utf8/utf8mb4 tables default to utf8395 "( a VARCHAR(50) CHARACTER SET utf8, b VARCHAR(50) CHARACTER SET utf8mb4 )",396 "('foo\xf0\x9f\x98\x88bar', 'foo')",397 "('foobar', 'foo')"398 ),399 );400 401 foreach( $data as &$value ) {402 $this_table_name = $table_name . '_' . rand_str( 5 );403 404 $value[0] = "CREATE TABLE $this_table_name {$value[0]}";405 $value[1] = "INSERT INTO $this_table_name VALUES {$value[1]}";406 $value[2] = "INSERT INTO $this_table_name VALUES {$value[2]}";407 $value[3] = "DROP TABLE IF EXISTS $this_table_name";408 }409 unset( $value );410 411 return $data;412 }413 414 /**415 * @dataProvider data_strip_invalid_text_from_query416 * @ticket 21212417 */418 function test_strip_invalid_text_from_query( $create, $query, $expected, $drop ) {419 self::$_wpdb->query( $drop );420 421 if ( ! self::$_wpdb->has_cap( 'utf8mb4' ) && preg_match( '/utf8mb[34]/i', $create ) ) {422 $this->markTestSkipped( "This version of MySQL doesn't support utf8mb4." );423 return;424 }425 426 self::$_wpdb->query( $create );427 428 $return = self::$_wpdb->strip_invalid_text_from_query( $query );429 $this->assertEquals( $expected, $return );430 431 self::$_wpdb->query( $drop );432 }433 434 /**435 * @ticket 21212436 */437 function test_invalid_characters_in_query() {438 global $wpdb;439 $this->assertFalse( $wpdb->query( "INSERT INTO {$wpdb->posts} (post_content) VALUES ('foo\xf0\x9f\x98\x88bar')" ) );440 }441 } -
branches/4.1/tests/phpunit/tests/post.php
r30629 r30807 1032 1032 _unregister_post_type( 'post-type-1' ); 1033 1033 } 1034 1035 /**1036 * @ticket 212121037 */1038 function test_utf8mb3_post_saves_with_emoji() {1039 global $wpdb;1040 $_wpdb = new wpdb_exposed_methods_for_testing();1041 1042 if ( 'utf8' !== $_wpdb->get_col_charset( $wpdb->posts, 'post_title' ) ) {1043 $this->markTestSkipped( 'This test is only useful with the utf8 character set' );1044 }1045 1046 require_once( ABSPATH . '/wp-admin/includes/post.php' );1047 1048 $post_id = $this->factory->post->create();1049 1050 $data = array(1051 'post_ID' => $post_id,1052 'post_title' => "foo\xf0\x9f\x98\x88bar",1053 'post_content' => "foo\xf0\x9f\x98\x8ebaz",1054 'post_excerpt' => "foo\xf0\x9f\x98\x90bat"1055 );1056 1057 $expected = array(1058 'post_title' => "foobar",1059 'post_content' => "foobaz",1060 'post_excerpt' => "foobat"1061 );1062 1063 edit_post( $data );1064 1065 $post = get_post( $post_id );1066 1067 foreach( $expected as $field => $value ) {1068 $this->assertEquals( $post->$field, $value );1069 }1070 }1071 1034 }
Note: See TracChangeset
for help on using the changeset viewer.