Changeset 30345 for trunk/src/wp-includes/wp-db.php
- Timestamp:
- 11/14/2014 09:27:17 PM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/wp-db.php
r30299 r30345 145 145 146 146 /** 147 * Cached column info, for sanity checking data before inserting 148 * 149 * @since 4.1.0 150 * @access protected 151 * @var array 152 */ 153 protected $col_meta = array(); 154 155 /** 156 * Calculated character sets on tables 157 * 158 * @since 4.1.0 159 * @access protected 160 * @var array 161 */ 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.0 168 * @access protected 169 * @var bool 170 */ 171 protected $check_current_query = true; 172 173 /** 147 174 * Saved info on the table column 148 175 * … … 648 675 */ 649 676 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 } 650 685 $this->$name = $value; 651 686 } … … 1545 1580 */ 1546 1581 public function query( $query ) { 1547 if ( ! $this->ready ) 1582 if ( ! $this->ready ) { 1583 $this->check_current_query = true; 1548 1584 return false; 1585 } 1549 1586 1550 1587 /** … … 1564 1601 // Log how the function was called 1565 1602 $this->func_call = "\$db->query(\"$query\")"; 1603 1604 // If we're writing to the database, make sure the query will write safely. 1605 if ( $this->check_current_query && ! $this->check_ascii( $query ) ) { 1606 $stripped_query = $this->strip_invalid_text_from_query( $query ); 1607 if ( $stripped_query !== $query ) { 1608 $this->insert_id = 0; 1609 return false; 1610 } 1611 } 1612 1613 $this->check_current_query = true; 1566 1614 1567 1615 // Keep track of the last query for debug.. … … 1738 1786 */ 1739 1787 function _insert_replace_helper( $table, $data, $format = null, $type = 'INSERT' ) { 1740 if ( ! in_array( strtoupper( $type ), array( 'REPLACE', 'INSERT' ) ) ) 1788 if ( ! in_array( strtoupper( $type ), array( 'REPLACE', 'INSERT' ) ) ) { 1741 1789 return false; 1790 } 1791 1792 $data = $this->process_fields( $table, $data, $format ); 1793 if ( false === $data ) { 1794 return false; 1795 } 1796 1797 $formats = $values = array(); 1798 foreach ( $data as $value ) { 1799 $formats[] = $value['format']; 1800 $values[] = $value['value']; 1801 } 1802 1803 $fields = '`' . implode( '`, `', array_keys( $data ) ) . '`'; 1804 $formats = implode( ', ', $formats ); 1805 1806 $sql = "$type INTO `$table` ($fields) VALUES ($formats)"; 1807 1742 1808 $this->insert_id = 0; 1743 $formats = $format = (array) $format; 1744 $fields = array_keys( $data ); 1745 $formatted_fields = array(); 1746 foreach ( $fields as $field ) { 1747 if ( !empty( $format ) ) 1748 $form = ( $form = array_shift( $formats ) ) ? $form : $format[0]; 1749 elseif ( isset( $this->field_types[$field] ) ) 1750 $form = $this->field_types[$field]; 1751 else 1752 $form = '%s'; 1753 $formatted_fields[] = $form; 1754 } 1755 $sql = "{$type} INTO `$table` (`" . implode( '`,`', $fields ) . "`) VALUES (" . implode( ",", $formatted_fields ) . ")"; 1756 return $this->query( $this->prepare( $sql, $data ) ); 1809 $this->check_current_query = false; 1810 return $this->query( $this->prepare( $sql, $values ) ); 1757 1811 } 1758 1812 … … 1779 1833 */ 1780 1834 public function update( $table, $data, $where, $format = null, $where_format = null ) { 1781 if ( ! is_array( $data ) || ! is_array( $where ) ) 1835 if ( ! is_array( $data ) || ! is_array( $where ) ) { 1782 1836 return false; 1783 1784 $formats = $format = (array) $format; 1785 $bits = $wheres = array(); 1786 foreach ( (array) array_keys( $data ) as $field ) { 1787 if ( !empty( $format ) ) 1788 $form = ( $form = array_shift( $formats ) ) ? $form : $format[0]; 1789 elseif ( isset($this->field_types[$field]) ) 1790 $form = $this->field_types[$field]; 1791 else 1792 $form = '%s'; 1793 $bits[] = "`$field` = {$form}"; 1794 } 1795 1796 $where_formats = $where_format = (array) $where_format; 1797 foreach ( (array) array_keys( $where ) as $field ) { 1798 if ( !empty( $where_format ) ) 1799 $form = ( $form = array_shift( $where_formats ) ) ? $form : $where_format[0]; 1800 elseif ( isset( $this->field_types[$field] ) ) 1801 $form = $this->field_types[$field]; 1802 else 1803 $form = '%s'; 1804 $wheres[] = "`$field` = {$form}"; 1805 } 1806 1807 $sql = "UPDATE `$table` SET " . implode( ', ', $bits ) . ' WHERE ' . implode( ' AND ', $wheres ); 1808 return $this->query( $this->prepare( $sql, array_merge( array_values( $data ), array_values( $where ) ) ) ); 1837 } 1838 1839 $data = $this->process_fields( $table, $data, $format ); 1840 if ( false === $data ) { 1841 return false; 1842 } 1843 $where = $this->process_fields( $table, $where, $where_format ); 1844 if ( false === $where ) { 1845 return false; 1846 } 1847 1848 $fields = $conditions = $values = array(); 1849 foreach ( $data as $field => $value ) { 1850 $fields[] = "`$field` = " . $value['format']; 1851 $values[] = $value['value']; 1852 } 1853 foreach ( $where as $field => $value ) { 1854 $conditions[] = "`$field` = " . $value['format']; 1855 $values[] = $value['value']; 1856 } 1857 1858 $fields = implode( ', ', $fields ); 1859 $conditions = implode( ' AND ', $conditions ); 1860 1861 $sql = "UPDATE `$table` SET $fields WHERE $conditions"; 1862 1863 $this->check_current_query = false; 1864 return $this->query( $this->prepare( $sql, $values ) ); 1809 1865 } 1810 1866 … … 1828 1884 */ 1829 1885 public function delete( $table, $where, $where_format = null ) { 1830 if ( ! is_array( $where ) ) 1886 if ( ! is_array( $where ) ) { 1831 1887 return false; 1832 1833 $wheres = array(); 1834 1835 $where_formats = $where_format = (array) $where_format; 1836 1837 foreach ( array_keys( $where ) as $field ) { 1838 if ( !empty( $where_format ) ) { 1839 $form = ( $form = array_shift( $where_formats ) ) ? $form : $where_format[0]; 1888 } 1889 1890 $where = $this->process_fields( $table, $where, $where_format ); 1891 if ( false === $where ) { 1892 return false; 1893 } 1894 1895 $conditions = $values = array(); 1896 foreach ( $where as $field => $value ) { 1897 $conditions[] = "`$field` = " . $value['format']; 1898 $values[] = $value['value']; 1899 } 1900 1901 $conditions = implode( ' AND ', $conditions ); 1902 1903 $sql = "DELETE FROM `$table` WHERE $conditions"; 1904 1905 $this->check_current_query = false; 1906 return $this->query( $this->prepare( $sql, $values ) ); 1907 } 1908 1909 /** 1910 * Processes arrays of field/value pairs and field formats. 1911 * 1912 * This is a helper method for wpdb's CRUD methods, which take field/value 1913 * pairs for inserts, updates, and where clauses. This method first pairs 1914 * each value with a format. Then it determines the charset of that field, 1915 * using that to determine if any invalid text would be stripped. If text is 1916 * stripped, then field processing is rejected and the query fails. 1917 * 1918 * @since 4.1.0 1919 * @access protected 1920 * 1921 * @param string $table Table name. 1922 * @param array $data Field/value pair. 1923 * @param mixed $format Format for each field. 1924 * @return array|bool Returns an array of fields that contain paired values 1925 * and formats. Returns false for invalid values. 1926 */ 1927 protected function process_fields( $table, $data, $format ) { 1928 $data = $this->process_field_formats( $data, $format ); 1929 $data = $this->process_field_charsets( $data, $table ); 1930 if ( false === $data ) { 1931 return false; 1932 } 1933 1934 $converted_data = $this->strip_invalid_text( $data ); 1935 1936 if ( $data !== $converted_data ) { 1937 return false; 1938 } 1939 1940 return $data; 1941 } 1942 1943 /** 1944 * Prepares arrays of value/format pairs as passed to wpdb CRUD methods. 1945 * 1946 * @since 4.1.0 1947 * @access protected 1948 * 1949 * @param array $data Array of fields to values. 1950 * @param mixed $format Formats to be mapped to the values in $data. 1951 * @return array Array, keyed by field names with values being an array 1952 * of 'value' and 'format' keys. 1953 */ 1954 protected function process_field_formats( $data, $format ) { 1955 $formats = $original_formats = (array) $format; 1956 1957 foreach ( $data as $field => $value ) { 1958 $value = array( 1959 'value' => $value, 1960 'format' => '%s', 1961 ); 1962 1963 if ( ! empty( $format ) ) { 1964 $value['format'] = array_shift( $formats ); 1965 if ( ! $value['format'] ) { 1966 $value['format'] = reset( $original_formats ); 1967 } 1840 1968 } elseif ( isset( $this->field_types[ $field ] ) ) { 1841 $form = $this->field_types[ $field ]; 1969 $value['format'] = $this->field_types[ $field ]; 1970 } 1971 1972 $data[ $field ] = $value; 1973 } 1974 1975 return $data; 1976 } 1977 1978 /** 1979 * Adds field charsets to field/value/format arrays 1980 * generated by the process_field_formats() method. 1981 * 1982 * @since 4.1.0 1983 * @access protected 1984 * 1985 * @param array $data As it comes from the process_field_formats() method. 1986 * @param string $table Table name. 1987 * @return The same array as $data with additional 'charset' keys. 1988 */ 1989 protected function process_field_charsets( $data, $table ) { 1990 foreach ( $data as $field => $value ) { 1991 if ( '%d' === $value['format'] || '%f' === $value['format'] ) { 1992 // We can skip this field if we know it isn't a string. 1993 // This checks %d/%f versus ! %s because it's sprintf() could take more. 1994 $value['charset'] = false; 1995 } elseif ( $this->check_ascii( $value['value'] ) ) { 1996 // If it's ASCII, then we don't need the charset. We can skip this field. 1997 $value['charset'] = false; 1842 1998 } else { 1843 $form = '%s'; 1844 } 1845 1846 $wheres[] = "$field = $form"; 1847 } 1848 1849 $sql = "DELETE FROM $table WHERE " . implode( ' AND ', $wheres ); 1850 return $this->query( $this->prepare( $sql, $where ) ); 1851 } 1852 1999 $value['charset'] = $this->get_col_charset( $table, $field ); 2000 if ( is_wp_error( $value['charset'] ) ) { 2001 return false; 2002 } 2003 2004 // This isn't ASCII. Don't have strip_invalid_text() re-check. 2005 $value['ascii'] = false; 2006 } 2007 2008 $data[ $field ] = $value; 2009 } 2010 2011 return $data; 2012 } 1853 2013 1854 2014 /** … … 1868 2028 public function get_var( $query = null, $x = 0, $y = 0 ) { 1869 2029 $this->func_call = "\$db->get_var(\"$query\", $x, $y)"; 1870 if ( $query ) 2030 2031 if ( $query ) { 2032 $this->check_current_query = false; 1871 2033 $this->query( $query ); 2034 } 1872 2035 1873 2036 // Extract var out of cached results based x,y vals … … 1895 2058 public function get_row( $query = null, $output = OBJECT, $y = 0 ) { 1896 2059 $this->func_call = "\$db->get_row(\"$query\",$output,$y)"; 1897 if ( $query ) 2060 if ( $query ) { 2061 $this->check_current_query = false; 1898 2062 $this->query( $query ); 1899 else2063 } else { 1900 2064 return null; 2065 } 1901 2066 1902 2067 if ( !isset( $this->last_result[$y] ) ) … … 1931 2096 */ 1932 2097 public function get_col( $query = null , $x = 0 ) { 1933 if ( $query ) 2098 if ( $query ) { 2099 $this->check_current_query = false; 1934 2100 $this->query( $query ); 2101 } 1935 2102 1936 2103 $new_array = array(); … … 1958 2125 $this->func_call = "\$db->get_results(\"$query\", $output)"; 1959 2126 1960 if ( $query ) 2127 if ( $query ) { 2128 $this->check_current_query = false; 1961 2129 $this->query( $query ); 1962 else2130 } else { 1963 2131 return null; 2132 } 1964 2133 1965 2134 $new_array = array(); … … 1999 2168 2000 2169 /** 2170 * Retrieves the character set for the given table. 2171 * 2172 * @since 4.1.0 2173 * @access protected 2174 * 2175 * @param string $table Table name. 2176 * @return string|WP_Error Table character set, `WP_Error` object if it couldn't be found. 2177 */ 2178 protected function get_table_charset( $table ) { 2179 $table = strtolower( $table ); 2180 2181 /** 2182 * Filter the table charset value before the DB is checked. 2183 * 2184 * Passing a non-null value to the filter will effectively short-circuit 2185 * checking the DB for the charset, returning that value instead. 2186 * 2187 * @since 4.1.0 2188 * 2189 * @param string $charset The character set to use. Default null. 2190 * @param string $table The name of the table being checked. 2191 */ 2192 $charset = apply_filters( 'pre_get_table_charset', null, $table ); 2193 if ( null !== $charset ) { 2194 return $charset; 2195 } 2196 2197 if ( isset( $this->table_charset[ $table ] ) ) { 2198 return $this->table_charset[ $table ]; 2199 } 2200 2201 $charsets = $columns = array(); 2202 $results = $this->get_results( "SHOW FULL COLUMNS FROM `$table`" ); 2203 if ( ! $results ) { 2204 return new WP_Error( 'wpdb_get_table_charset_failure' ); 2205 } 2206 2207 foreach ( $results as $column ) { 2208 $columns[ strtolower( $column->Field ) ] = $column; 2209 } 2210 2211 $this->col_meta[ $table ] = $columns; 2212 2213 foreach ( $columns as $column ) { 2214 if ( $column->Collation ) { 2215 list( $charset ) = explode( '_', $column->Collation ); 2216 $charsets[ strtolower( $charset ) ] = true; 2217 } 2218 2219 list( $type ) = explode( '(', $column->Type ); 2220 2221 // A binary/blob means the whole query gets treated like this. 2222 if ( in_array( strtoupper( $type ), array( 'BINARY', 'VARBINARY', 'TINYBLOB', 'MEDIUMBLOB', 'BLOB', 'LONGBLOB' ) ) ) { 2223 $this->table_charset[ $table ] = 'binary'; 2224 return 'binary'; 2225 } 2226 } 2227 2228 // utf8mb3 is an alias for utf8. 2229 if ( isset( $charsets['utf8mb3'] ) ) { 2230 $charsets['utf8'] = true; 2231 unset( $charsets['utf8mb3'] ); 2232 } 2233 2234 // Check if we have more than one charset in play. 2235 $count = count( $charsets ); 2236 if ( 1 === $count ) { 2237 $charset = key( $charsets ); 2238 } elseif ( 0 === $count ) { 2239 // No charsets, assume this table can store whatever. 2240 $charset = 'latin1'; 2241 } else { 2242 // More than one charset. Remove latin1 if present and recalculate. 2243 unset( $charsets['latin1'] ); 2244 $count = count( $charsets ); 2245 if ( 1 === $count ) { 2246 // Only one charset (besides latin1). 2247 $charset = key( $charsets ); 2248 } elseif ( 2 === $count && isset( $charsets['utf8'], $charsets['utf8mb4'] ) ) { 2249 // Two charsets, but they're utf8 and utf8mb4, use utf8. 2250 $charset = 'utf8'; 2251 } else { 2252 // Two mixed character sets. ascii. 2253 $charset = 'ascii'; 2254 } 2255 } 2256 2257 $this->table_charset[ $table ] = $charset; 2258 return $charset; 2259 } 2260 2261 /** 2262 * Retrieves the character set for the given column. 2263 * 2264 * @since 4.1.0 2265 * @access protected 2266 * 2267 * @param string $table Table name. 2268 * @param string $column Column name. 2269 * @return mixed Column character set as a string. 2270 * False if the column has no character set. 2271 * `WP_Error` object if there was an error. 2272 */ 2273 protected function get_col_charset( $table, $column ) { 2274 $table = strtolower( $table ); 2275 $column = strtolower( $column ); 2276 2277 /** 2278 * Filter the column charset value before the DB is checked. 2279 * 2280 * Passing a non-null value to the filter will short-circuit 2281 * checking the DB for the charset, returning that value instead. 2282 * 2283 * @since 4.1.0 2284 * 2285 * @param string $charset The character set to use. Default null. 2286 * @param string $table The name of the table being checked. 2287 * @param string $column The name of the column being checked. 2288 */ 2289 $charset = apply_filters( 'pre_get_col_charset', null, $table, $column ); 2290 if ( null !== $charset ) { 2291 return $charset; 2292 } 2293 2294 if ( empty( $this->table_charset[ $table ] ) ) { 2295 // This primes column information for us. 2296 $table_charset = $this->get_table_charset( $table ); 2297 if ( is_wp_error( $table_charset ) ) { 2298 return $table_charset; 2299 } 2300 } 2301 2302 // If still no column information, return the table charset. 2303 if ( empty( $this->col_meta[ $table ] ) ) { 2304 return $this->table_charset[ $table ]; 2305 } 2306 2307 // If this column doesn't exist, return the table charset. 2308 if ( empty( $this->col_meta[ $table ][ $column ] ) ) { 2309 return $this->table_charset[ $table ]; 2310 } 2311 2312 // Return false when it's not a string column. 2313 if ( empty( $this->col_meta[ $table ][ $column ]->Collation ) ) { 2314 return false; 2315 } 2316 2317 list( $charset ) = explode( '_', $this->col_meta[ $table ][ $column ]->Collation ); 2318 return $charset; 2319 } 2320 2321 /** 2322 * Check if a string is ASCII. 2323 * 2324 * The negative regex is faster for non-ASCII strings, as it allows 2325 * the search to finish as soon as it encounters a non-ASCII character. 2326 * 2327 * @since 4.1.0 2328 * @access protected 2329 * 2330 * @param string $string String to check. 2331 * @return bool True if ASCII, false if not. 2332 */ 2333 protected function check_ascii( $string ) { 2334 if ( function_exists( 'mb_check_encoding' ) ) { 2335 if ( mb_check_encoding( $string, 'ASCII' ) ) { 2336 return true; 2337 } 2338 } elseif ( ! preg_match( '/[^\x00-\x7F]/', $string ) ) { 2339 return true; 2340 } 2341 2342 return false; 2343 } 2344 2345 /** 2346 * Strips any invalid characters based on value/charset pairs. 2347 * 2348 * @since 4.1.0 2349 * @access protected 2350 * 2351 * @param array $data Array of value arrays. Each value array has the keys 2352 * 'value' and 'charset'. An optional 'ascii' key can be 2353 * set to false to avoid redundant ASCII checks. 2354 * @return array|WP_Error The $data parameter, with invalid characters removed from 2355 * each value. This works as a passthrough: any additional keys 2356 * such as 'field' are retained in each value array. If we cannot 2357 * remove invalid characters, a `WP_Error` object is returned. 2358 */ 2359 protected function strip_invalid_text( $data ) { 2360 // Some multibyte character sets that we can check in PHP. 2361 $mb_charsets = array( 2362 'ascii' => 'ASCII', 2363 'big5' => 'BIG-5', 2364 'eucjpms' => 'eucJP-win', 2365 'gb2312' => 'EUC-CN', 2366 'ujis' => 'EUC-JP', 2367 'utf32' => 'UTF-32', 2368 'utf8mb4' => 'UTF-8', 2369 ); 2370 2371 $supported_charsets = array(); 2372 if ( function_exists( 'mb_list_encodings' ) ) { 2373 $supported_charsets = mb_list_encodings(); 2374 } 2375 2376 $db_check_string = false; 2377 2378 foreach ( $data as &$value ) { 2379 $charset = $value['charset']; 2380 2381 // latin1 will happily store anything. 2382 if ( 'latin1' === $charset ) { 2383 continue; 2384 } 2385 2386 // Column or value isn't a string. 2387 if ( false === $charset || ! is_string( $value['value'] ) ) { 2388 continue; 2389 } 2390 2391 // ASCII is always OK. 2392 if ( ! isset( $value['ascii'] ) && $this->check_ascii( $value['value'] ) ) { 2393 continue; 2394 } 2395 2396 // Convert the text locally. 2397 if ( $supported_charsets ) { 2398 if ( isset( $mb_charsets[ $charset ] ) && in_array( $mb_charsets[ $charset ], $supported_charsets ) ) { 2399 $value['value'] = mb_convert_encoding( $value['value'], $mb_charsets[ $charset ], $mb_charsets[ $charset ] ); 2400 continue; 2401 } 2402 } 2403 2404 // utf8(mb3) can be handled by regex, which is a bunch faster than a DB lookup. 2405 if ( 'utf8' === $charset || 'utf8mb3' === $charset ) { 2406 $regex = '/ 2407 ( 2408 (?: [\x00-\x7F] # single-byte sequences 0xxxxxxx 2409 | [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx 2410 | \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2 2411 | [\xE1-\xEC][\x80-\xBF]{2} 2412 | \xED[\x80-\x9F][\x80-\xBF] 2413 | [\xEE-\xEF][\x80-\xBF]{2} 2414 ){1,100} # ...one or more times 2415 ) 2416 | . # anything else 2417 /x'; 2418 $value['value'] = preg_replace( $regex, '$1', $value['value'] ); 2419 continue; 2420 } 2421 2422 // We couldn't use any local conversions, send it to the DB. 2423 $value['db'] = $db_check_string = true; 2424 } 2425 unset( $value ); // Remove by reference. 2426 2427 if ( $db_check_string ) { 2428 $queries = array(); 2429 foreach ( $data as $col => $value ) { 2430 if ( ! empty( $value['db'] ) ) { 2431 if ( ! isset( $queries[ $value['charset'] ] ) ) { 2432 $queries[ $value['charset'] ] = array(); 2433 } 2434 2435 // Split the CONVERT() calls by charset, so we can make sure the connection is right 2436 $queries[ $value['charset'] ][ $col ] = $this->prepare( "CONVERT( %s USING {$value['charset']} )", $value['value'] ); 2437 } 2438 } 2439 2440 $connection_charset = $this->charset; 2441 foreach ( $queries as $charset => $query ) { 2442 if ( ! $query ) { 2443 continue; 2444 } 2445 2446 // Change the charset to match the string(s) we're converting 2447 if ( $charset !== $this->charset ) { 2448 $this->set_charset( $this->dbh, $charset ); 2449 } 2450 2451 $row = $this->get_row( "SELECT " . implode( ', ', $query ), ARRAY_N ); 2452 if ( ! $row ) { 2453 $this->set_charset( $this->dbh, $connection_charset ); 2454 return new WP_Error( 'wpdb_strip_invalid_text_failure' ); 2455 } 2456 2457 $cols = array_keys( $query ); 2458 $col_count = count( $cols ); 2459 for ( $ii = 0; $ii < $col_count; $ii++ ) { 2460 $data[ $cols[ $ii ] ]['value'] = $row[ $ii ]; 2461 } 2462 } 2463 2464 // Don't forget to change the charset back! 2465 if ( $connection_charset !== $this->charset ) { 2466 $this->set_charset( $this->dbh, $connection_charset ); 2467 } 2468 } 2469 2470 return $data; 2471 } 2472 2473 /** 2474 * Strips any invalid characters from the query. 2475 * 2476 * @since 4.1.0 2477 * @access protected 2478 * 2479 * @param string $query Query to convert. 2480 * @return string|WP_Error The converted query, or a `WP_Error` object if the conversion fails. 2481 */ 2482 protected function strip_invalid_text_from_query( $query ) { 2483 $table = $this->get_table_from_query( $query ); 2484 if ( $table ) { 2485 $charset = $this->get_table_charset( $table ); 2486 if ( is_wp_error( $charset ) ) { 2487 return $charset; 2488 } 2489 2490 // We can't reliably strip text from tables containing binary/blob columns 2491 if ( 'binary' === $charset ) { 2492 return $query; 2493 } 2494 } else { 2495 $charset = $this->charset; 2496 } 2497 2498 $data = array( 2499 'value' => $query, 2500 'charset' => $charset, 2501 'ascii' => false, 2502 ); 2503 2504 $data = $this->strip_invalid_text( array( $data ) ); 2505 if ( is_wp_error( $data ) ) { 2506 return $data; 2507 } 2508 2509 return $data[0]['value']; 2510 } 2511 2512 /** 2513 * Strips any invalid characters from the string for a given table and column. 2514 * 2515 * @since 4.1.0 2516 * @access public 2517 * 2518 * @param string $table Table name. 2519 * @param string $column Column name. 2520 * @param string $value The text to check. 2521 * @return string|WP_Error The converted string, or a `WP_Error` object if the conversion fails. 2522 */ 2523 public function strip_invalid_text_for_column( $table, $column, $value ) { 2524 if ( $this->check_ascii( $value ) || ! is_string( $value ) ) { 2525 return $value; 2526 } 2527 2528 $charset = $this->get_col_charset( $table, $column ); 2529 if ( ! $charset ) { 2530 // Not a string column. 2531 return $value; 2532 } elseif ( is_wp_error( $charset ) ) { 2533 // Bail on real errors. 2534 return $charset; 2535 } 2536 2537 $data = array( 2538 $column => array( 2539 'value' => $value, 2540 'charset' => $charset, 2541 'ascii' => false, 2542 ) 2543 ); 2544 2545 $data = $this->strip_invalid_text( $data ); 2546 if ( is_wp_error( $data ) ) { 2547 return $data; 2548 } 2549 2550 return $data[ $column ]['value']; 2551 } 2552 2553 /** 2554 * Find the first table name referenced in a query. 2555 * 2556 * @since 4.1.0 2557 * @access protected 2558 * 2559 * @param string $query The query to search. 2560 * @return string|bool $table The table name found, or false if a table couldn't be found. 2561 */ 2562 protected function get_table_from_query( $query ) { 2563 // Remove characters that can legally trail the table name. 2564 $query = rtrim( $query, ';/-#' ); 2565 2566 // Allow (select...) union [...] style queries. Use the first query's table name. 2567 $query = ltrim( $query, "\r\n\t (" ); 2568 2569 /* 2570 * Strip everything between parentheses except nested selects and use only 1,000 2571 * chars of the query. 2572 */ 2573 $query = preg_replace( '/\((?!\s*select)[^(]*?\)/is', '()', substr( $query, 0, 1000 ) ); 2574 2575 // Quickly match most common queries. 2576 if ( preg_match( '/^\s*(?:' 2577 . 'SELECT.*?\s+FROM' 2578 . '|INSERT(?:\s+LOW_PRIORITY|\s+DELAYED|\s+HIGH_PRIORITY)?(?:\s+IGNORE)?(?:\s+INTO)?' 2579 . '|REPLACE(?:\s+LOW_PRIORITY|\s+DELAYED)?(?:\s+INTO)?' 2580 . '|UPDATE(?:\s+LOW_PRIORITY)?(?:\s+IGNORE)?' 2581 . '|DELETE(?:\s+LOW_PRIORITY|\s+QUICK|\s+IGNORE)*(?:\s+FROM)?' 2582 . ')\s+`?([\w-]+)`?/is', $query, $maybe ) ) { 2583 return $maybe[1]; 2584 } 2585 2586 // SHOW TABLE STATUS and SHOW TABLES 2587 if ( preg_match( '/^\s*(?:' 2588 . 'SHOW\s+TABLE\s+STATUS.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)' 2589 . '|SHOW\s+(?:FULL\s+)?TABLES.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)' 2590 . ')\W([\w-]+)\W/is', $query, $maybe ) ) { 2591 return $maybe[1]; 2592 } 2593 2594 // Big pattern for the rest of the table-related queries. 2595 if ( preg_match( '/^\s*(?:' 2596 . '(?:EXPLAIN\s+(?:EXTENDED\s+)?)?SELECT.*?\s+FROM' 2597 . '|DESCRIBE|DESC|EXPLAIN|HANDLER' 2598 . '|(?:LOCK|UNLOCK)\s+TABLE(?:S)?' 2599 . '|(?:RENAME|OPTIMIZE|BACKUP|RESTORE|CHECK|CHECKSUM|ANALYZE|REPAIR).*\s+TABLE' 2600 . '|TRUNCATE(?:\s+TABLE)?' 2601 . '|CREATE(?:\s+TEMPORARY)?\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?' 2602 . '|ALTER(?:\s+IGNORE)?\s+TABLE' 2603 . '|DROP\s+TABLE(?:\s+IF\s+EXISTS)?' 2604 . '|CREATE(?:\s+\w+)?\s+INDEX.*\s+ON' 2605 . '|DROP\s+INDEX.*\s+ON' 2606 . '|LOAD\s+DATA.*INFILE.*INTO\s+TABLE' 2607 . '|(?:GRANT|REVOKE).*ON\s+TABLE' 2608 . '|SHOW\s+(?:.*FROM|.*TABLE)' 2609 . ')\s+\(*\s*`?([\w-]+)`?\s*\)*/is', $query, $maybe ) ) { 2610 return $maybe[1]; 2611 } 2612 2613 return false; 2614 } 2615 2616 /** 2001 2617 * Load the column metadata from the last query. 2002 2618 * … … 2161 2777 case 'set_charset' : 2162 2778 return version_compare( $version, '5.0.7', '>=' ); 2779 case 'utf8mb4' : // @since 4.1.0 2780 return version_compare( $version, '5.5.3', '>=' ); 2163 2781 } 2164 2782
Note: See TracChangeset
for help on using the changeset viewer.