Make WordPress Core

Changeset 42066


Ignore:
Timestamp:
10/31/2017 12:56:57 PM (9 years ago)
Author:
pento
Message:

Database: Restore numbered placeholders in wpdb::prepare().

[41496] removed support for numbered placeholders in queries send through wpdb::prepare(), which, despite being undocumented, were quite commonly used.

This change restores support for numbered placeholders (as well as a subset of placeholder formatting), while also adding extra checks to ensure the correct number of arguments are being passed to wpdb::prepare(), given the number of placeholders.

Merges [41662], [42056] to the 3.9 branch.
See #41925.

Location:
branches/3.9
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • branches/3.9

  • branches/3.9/src/wp-includes/post.php

    r33559 r42066  
    39043904        $page_path = str_replace('%20', ' ', $page_path);
    39053905        $parts = explode( '/', trim( $page_path, '/' ) );
    3906         $parts = esc_sql( $parts );
    39073906        $parts = array_map( 'sanitize_title_for_query', $parts );
    3908 
    3909         $in_string = "'" . implode( "','", $parts ) . "'";
     3907        $escaped_parts = esc_sql( $parts );
     3908
     3909        $in_string = "'" . implode( "','", $escaped_parts ) . "'";
    39103910
    39113911        if ( is_array( $post_type ) ) {
  • branches/3.9/src/wp-includes/wp-db.php

    r41506 r42066  
    10741074                if ( $this->dbh ) {
    10751075                        if ( $this->use_mysqli ) {
    1076                                 return mysqli_real_escape_string( $this->dbh, $string );
     1076                                $escaped = mysqli_real_escape_string( $this->dbh, $string );
    10771077                        } else {
    1078                                 return mysql_real_escape_string( $string, $this->dbh );
    1079                         }
    1080                 }
    1081 
    1082                 $class = get_class( $this );
    1083                 _doing_it_wrong( $class, "$class must set a database connection for use with escaping.", E_USER_NOTICE );
    1084                 return addslashes( $string );
     1078                                $escaped = mysql_real_escape_string( $string, $this->dbh );
     1079                        }
     1080                } else {
     1081                        $class = get_class( $this );
     1082                        if ( function_exists( '__' ) ) {
     1083                                /* translators: %s: database access abstraction class, usually wpdb or a class extending wpdb */
     1084                                _doing_it_wrong( $class, sprintf( __( '%s must set a database connection for use with escaping.' ), $class ), '3.6.0' );
     1085                        } else {
     1086                                _doing_it_wrong( $class, sprintf( '%s must set a database connection for use with escaping.', $class ), '3.6.0' );
     1087                        }
     1088                        $escaped = addslashes( $string );
     1089                }
     1090
     1091                return $this->add_placeholder_escape( $escaped );
    10851092        }
    10861093
     
    11561163         * Prepares a SQL query for safe execution. Uses sprintf()-like syntax.
    11571164         *
    1158          * The following directives can be used in the query format string:
     1165         * The following placeholders can be used in the query string:
    11591166         *   %d (integer)
    11601167         *   %f (float)
    11611168         *   %s (string)
    1162          *   %% (literal percentage sign - no argument needed)
    1163          *
    1164          * All of %d, %f, and %s are to be left unquoted in the query string and they need an argument passed for them.
    1165          * Literals (%) as parts of the query must be properly written as %%.
    1166          *
    1167          * This function only supports a small subset of the sprintf syntax; it only supports %d (integer), %f (float), and %s (string).
    1168          * Does not support sign, padding, alignment, width or precision specifiers.
    1169          * Does not support argument numbering/swapping.
    1170          *
    1171          * May be called like {@link http://php.net/sprintf sprintf()} or like {@link http://php.net/vsprintf vsprintf()}.
    1172          *
    1173          * Both %d and %s should be left unquoted in the query string.
    1174          *
    1175          * <code>
    1176          * wpdb::prepare( "SELECT * FROM `table` WHERE `column` = %s AND `field` = %d", 'foo', 1337 )
    1177          * wpdb::prepare( "SELECT DATE_FORMAT(`field`, '%%c') FROM `table` WHERE `column` = %s", 'foo' );
    1178          * </code>
    1179          *
    1180          * @link http://php.net/sprintf Description of syntax.
     1169         *
     1170         * All placeholders MUST be left unquoted in the query string. A corresponding argument MUST be passed for each placeholder.
     1171         *
     1172         * For compatibility with old behavior, numbered or formatted string placeholders (eg, %1$s, %5s) will not have quotes
     1173         * added by this function, so should be passed with appropriate quotes around them for your usage.
     1174         *
     1175         * Literal percentage signs (%) in the query string must be written as %%. Percentage wildcards (for example,
     1176         * to use in LIKE syntax) must be passed via a substitution argument containing the complete LIKE string, these
     1177         * cannot be inserted directly in the query string. Also see {@see esc_like()}.
     1178         *
     1179         * Arguments may be passed as individual arguments to the method, or as a single array containing all arguments. A combination
     1180         * of the two is not supported.
     1181         *
     1182         * Examples:
     1183         *     $wpdb->prepare( "SELECT * FROM `table` WHERE `column` = %s AND `field` = %d OR `other_field` LIKE %s", array( 'foo', 1337, '%bar' ) );
     1184         *     $wpdb->prepare( "SELECT DATE_FORMAT(`field`, '%%c') FROM `table` WHERE `column` = %s", 'foo' );
     1185         *
     1186         * @link https://secure.php.net/sprintf Description of syntax.
    11811187         * @since 2.3.0
    11821188         *
    1183          * @param string $query Query statement with sprintf()-like placeholders
    1184          * @param array|mixed $args The array of variables to substitute into the query's placeholders if being called like
    1185          *      {@link http://php.net/vsprintf vsprintf()}, or the first variable to substitute into the query's placeholders if
    1186          *      being called like {@link http://php.net/sprintf sprintf()}.
    1187          * @param mixed $args,... further variables to substitute into the query's placeholders if being called like
    1188          *      {@link http://php.net/sprintf sprintf()}.
    1189          * @return null|false|string Sanitized query string, null if there is no query, false if there is an error and string
    1190          *      if there was something to prepare
    1191          */
    1192         function prepare( $query, $args ) {
    1193                 if ( is_null( $query ) )
     1189         * @param string      $query    Query statement with sprintf()-like placeholders
     1190         * @param array|mixed $args     The array of variables to substitute into the query's placeholders if being called with an array of arguments,
     1191         *                              or the first variable to substitute into the query's placeholders if being called with individual arguments.
     1192         * @param mixed       $args,... further variables to substitute into the query's placeholders if being called wih individual arguments.
     1193         * @return string|void Sanitized query string, if there is a query to prepare.
     1194         */
     1195        public function prepare( $query, $args ) {
     1196                if ( is_null( $query ) ) {
    11941197                        return;
     1198                }
    11951199
    11961200                // This is not meant to be foolproof -- but it will catch obviously incorrect usage.
    11971201                if ( strpos( $query, '%' ) === false ) {
    1198                         _doing_it_wrong( 'wpdb::prepare', sprintf( __( 'The query argument of %s must have a placeholder.' ), 'wpdb::prepare()' ), '3.9' );
     1202                        wp_load_translations_early();
     1203                        _doing_it_wrong( 'wpdb::prepare', sprintf( __( 'The query argument of %s must have a placeholder.' ), 'wpdb::prepare()' ), '3.9.0' );
    11991204                }
    12001205
     
    12021207                array_shift( $args );
    12031208
    1204                 // If args were passed as an array (as in vsprintf), move them up
     1209                // If args were passed as an array (as in vsprintf), move them up.
     1210                $passed_as_array = false;
    12051211                if ( is_array( $args[0] ) && count( $args ) == 1 ) {
     1212                        $passed_as_array = true;
    12061213                        $args = $args[0];
    12071214                }
     
    12091216                foreach ( $args as $arg ) {
    12101217                        if ( ! is_scalar( $arg ) && ! is_null( $arg ) ) {
    1211                                 _doing_it_wrong( 'wpdb::prepare', sprintf( 'Unsupported value type (%s).', gettype( $arg ) ), '3.9.20' );
    1212                         }
    1213                 }
    1214 
    1215                 $query = str_replace( "'%s'", '%s', $query ); // in case someone mistakenly already singlequoted it
    1216                 $query = str_replace( '"%s"', '%s', $query ); // doublequote unquoting
    1217                 $query = preg_replace( '|(?<!%)%f|' , '%F', $query ); // Force floats to be locale unaware
    1218                 $query = preg_replace( '|(?<!%)%s|', "'%s'", $query ); // quote the strings, avoiding escaped strings like %%s
    1219                 $query = preg_replace( '/%(?:%|$|([^dsF]))/', '%%\\1', $query ); // escape any unescaped percents
     1218                                wp_load_translations_early();
     1219                                _doing_it_wrong( 'wpdb::prepare', sprintf( __( 'Unsupported value type (%s).' ), gettype( $arg ) ), '4.8.2' );
     1220                        }
     1221                }
     1222
     1223                /*
     1224                 * Specify the formatting allowed in a placeholder. The following are allowed:
     1225                 *
     1226                 * - Sign specifier. eg, $+d
     1227                 * - Numbered placeholders. eg, %1$s
     1228                 * - Padding specifier, including custom padding characters. eg, %05s, %'#5s
     1229                 * - Alignment specifier. eg, %05-s
     1230                 * - Precision specifier. eg, %.2f
     1231                 */
     1232                $allowed_format = '(?:[1-9][0-9]*[$])?[-+0-9]*(?: |0|\'.)?[-+0-9]*(?:\.[0-9]+)?';
     1233
     1234                /*
     1235                 * If a %s placeholder already has quotes around it, removing the existing quotes and re-inserting them
     1236                 * ensures the quotes are consistent.
     1237                 *
     1238                 * For backwards compatibility, this is only applied to %s, and not to placeholders like %1$s, which are frequently
     1239                 * used in the middle of longer strings, or as table name placeholders.
     1240                 */
     1241                $query = str_replace( "'%s'", '%s', $query ); // Strip any existing single quotes.
     1242                $query = str_replace( '"%s"', '%s', $query ); // Strip any existing double quotes.
     1243                $query = preg_replace( '/(?<!%)%s/', "'%s'", $query ); // Quote the strings, avoiding escaped strings like %%s.
     1244
     1245                $query = preg_replace( "/(?<!%)(%($allowed_format)?f)/" , '%\\2F', $query ); // Force floats to be locale unaware.
     1246
     1247                $query = preg_replace( "/%(?:%|$|(?!($allowed_format)?[sdF]))/", '%%\\1', $query ); // Escape any unescaped percents.
     1248
     1249                // Count the number of valid placeholders in the query.
     1250                $placeholders = preg_match_all( "/(^|[^%]|(%%)+)%($allowed_format)?[sdF]/", $query, $matches );
     1251
     1252                if ( count( $args ) !== $placeholders ) {
     1253                        if ( 1 === $placeholders && $passed_as_array ) {
     1254                                // If the passed query only expected one argument, but the wrong number of arguments were sent as an array, bail.
     1255                                wp_load_translations_early();
     1256                                _doing_it_wrong( 'wpdb::prepare', __( 'The query only expected one placeholder, but an array of multiple placeholders was sent.' ), '4.9.0' );
     1257
     1258                                return;
     1259                        } else {
     1260                                /*
     1261                                 * If we don't have the right number of placeholders, but they were passed as individual arguments,
     1262                                 * or we were expecting multiple arguments in an array, throw a warning.
     1263                                 */
     1264                                wp_load_translations_early();
     1265                                _doing_it_wrong( 'wpdb::prepare',
     1266                                        /* translators: 1: number of placeholders, 2: number of arguments passed */
     1267                                        sprintf( __( 'The query does not contain the correct number of placeholders (%1$d) for the number of arguments passed (%2$d).' ),
     1268                                                $placeholders,
     1269                                                count( $args ) ),
     1270                                        '4.8.3'
     1271                                );
     1272                        }
     1273                }
     1274
    12201275                array_walk( $args, array( $this, 'escape_by_ref' ) );
    1221                 return @vsprintf( $query, $args );
     1276                $query = @vsprintf( $query, $args );
     1277
     1278                return $this->add_placeholder_escape( $query );
    12221279        }
    12231280
     
    16951752                        $this->queries[] = array( $query, $this->timer_stop(), $this->get_caller() );
    16961753                }
     1754        }
     1755
     1756        /**
     1757         * Generates and returns a placeholder escape string for use in queries returned by ::prepare().
     1758         *
     1759         * @since 4.8.3
     1760         *
     1761         * @return string String to escape placeholders.
     1762         */
     1763        public function placeholder_escape() {
     1764                static $placeholder;
     1765
     1766                if ( ! $placeholder ) {
     1767                        // If ext/hash is not present, compat.php's hash_hmac() does not support sha256.
     1768                        $algo = function_exists( 'hash' ) ? 'sha256' : 'sha1';
     1769                        // Old WP installs may not have AUTH_SALT defined.
     1770                        $salt = defined( 'AUTH_SALT' ) ? AUTH_SALT : rand();
     1771
     1772                        $placeholder = '{' . hash_hmac( $algo, uniqid( $salt, true ), $salt ) . '}';
     1773                }
     1774
     1775                /*
     1776                 * Add the filter to remove the placeholder escaper. Uses priority 0, so that anything
     1777                 * else attached to this filter will recieve the query with the placeholder string removed.
     1778                 */
     1779                if ( ! has_filter( 'query', array( $this, 'remove_placeholder_escape' ) ) ) {
     1780                        add_filter( 'query', array( $this, 'remove_placeholder_escape' ), 0 );
     1781                }
     1782
     1783                return $placeholder;
     1784        }
     1785
     1786        /**
     1787         * Adds a placeholder escape string, to escape anything that resembles a printf() placeholder.
     1788         *
     1789         * @since 4.8.3
     1790         *
     1791         * @param string $query The query to escape.
     1792         * @return string The query with the placeholder escape string inserted where necessary.
     1793         */
     1794        public function add_placeholder_escape( $query ) {
     1795                /*
     1796                 * To prevent returning anything that even vaguely resembles a placeholder,
     1797                 * we clobber every % we can find.
     1798                 */
     1799                return str_replace( '%', $this->placeholder_escape(), $query );
     1800        }
     1801
     1802        /**
     1803         * Removes the placeholder escape strings from a query.
     1804         *
     1805         * @since 4.8.3
     1806         *
     1807         * @param string $query The query from which the placeholder will be removed.
     1808         * @return string The query with the placeholder removed.
     1809         */
     1810        public function remove_placeholder_escape( $query ) {
     1811                return str_replace( $this->placeholder_escape(), '%', $query );
    16971812        }
    16981813
  • branches/3.9/tests/phpunit/includes/testcase.php

    r40248 r42066  
    149149                        $this->fail( "Unexpected incorrect usage notice for $unexpected" );
    150150                }
     151        }
     152
     153        /**
     154         * Declare an expected `_doing_it_wrong()` call from within a test.
     155         *
     156         * @since 4.2.0
     157         *
     158         * @param string $deprecated Name of the function, method, or class that appears in the first argument of the
     159         *                           source `_doing_it_wrong()` call.
     160         */
     161        public function setExpectedIncorrectUsage( $doing_it_wrong ) {
     162                array_push( $this->expected_doing_it_wrong, $doing_it_wrong );
    151163        }
    152164
  • branches/3.9/tests/phpunit/tests/db.php

    r41506 r42066  
    171171                global $wpdb;
    172172                $sql = $wpdb->prepare( "UPDATE test_table SET string_column = '%%f is a float, %%d is an int %d, %%s is a string', field = %s", 3, '4' );
     173                $this->assertContains( $wpdb->placeholder_escape(), $sql );
     174
     175                $sql = $wpdb->remove_placeholder_escape( $sql );
    173176                $this->assertEquals( "UPDATE test_table SET string_column = '%f is a float, %d is an int 3, %s is a string', field = '4'", $sql );
    174177        }
     
    275278        }
    276279
    277         function test_prepare_vsprintf() {
    278                 global $wpdb;
     280    function test_prepare_vsprintf() {
     281                global $wpdb;
    279282
    280283                $prepared = $wpdb->prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s", array( 1, "admin" ) );
     
    293296                $prepared = @$wpdb->prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s", array( array( 1 ), "admin" ) );
    294297                $this->assertEquals( "SELECT * FROM $wpdb->users WHERE id = 0 AND user_login = 'admin'", $prepared );
    295         }
     298        }
     299
     300        /**
     301         * @ticket 42040
     302         * @dataProvider data_prepare_incorrect_arg_count
     303         * @expectedIncorrectUsage wpdb::prepare
     304         */
     305        public function test_prepare_incorrect_arg_count( $query, $args, $expected ) {
     306                global $wpdb;
     307
     308                // $query is the first argument to be passed to wpdb::prepare()
     309                array_unshift( $args, $query );
     310
     311                $prepared = @call_user_func_array( array( $wpdb, 'prepare' ), $args );
     312                $this->assertEquals( $expected, $prepared );
     313        }
     314
     315        public function data_prepare_incorrect_arg_count() {
     316                global $wpdb;
     317
     318                return array(
     319                        array(
     320                                "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s",     // Query
     321                                array( 1, "admin", "extra-arg" ),                                   // ::prepare() args, to be passed via call_user_func_array
     322                                "SELECT * FROM $wpdb->users WHERE id = 1 AND user_login = 'admin'", // Expected output
     323                        ),
     324                        array(
     325                                "SELECT * FROM $wpdb->users WHERE id = %%%d AND user_login = %s",
     326                                array( 1 ),
     327                                false,
     328                        ),
     329                        array(
     330                                "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s",
     331                                array( array( 1, "admin", "extra-arg" ) ),
     332                                "SELECT * FROM $wpdb->users WHERE id = 1 AND user_login = 'admin'",
     333                        ),
     334                        array(
     335                                "SELECT * FROM $wpdb->users WHERE id = %d AND %% AND user_login = %s",
     336                                array( 1, "admin", "extra-arg" ),
     337                                "SELECT * FROM $wpdb->users WHERE id = 1 AND {$wpdb->placeholder_escape()} AND user_login = 'admin'",
     338                        ),
     339                        array(
     340                                "SELECT * FROM $wpdb->users WHERE id = %%%d AND %F AND %f AND user_login = %s",
     341                                array( 1, 2.3, "4.5", "admin", "extra-arg" ),
     342                                "SELECT * FROM $wpdb->users WHERE id = {$wpdb->placeholder_escape()}1 AND 2.300000 AND 4.500000 AND user_login = 'admin'",
     343                        ),
     344                        array(
     345                                "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s",
     346                                array( array( 1 ), "admin", "extra-arg" ),
     347                                "SELECT * FROM $wpdb->users WHERE id = 0 AND user_login = 'admin'",
     348                        ),
     349                        array(
     350                                "SELECT * FROM $wpdb->users WHERE id = %d and user_nicename = %s and user_status = %d and user_login = %s",
     351                                array( 1, "admin", 0 ),
     352                                '',
     353                        ),
     354                        array(
     355                                "SELECT * FROM $wpdb->users WHERE id = %d and user_nicename = %s and user_status = %d and user_login = %s",
     356                                array( array( 1, "admin", 0 ) ),
     357                                '',
     358                        ),
     359                        array(
     360                                "SELECT * FROM $wpdb->users WHERE id = %d and %% and user_login = %s and user_status = %d and user_login = %s",
     361                                array( 1, "admin", "extra-arg" ),
     362                                '',
     363                        ),
     364                );
     365        }
    296366
    297367        function test_db_version() {
     
    711781
    712782        /**
    713          *
    714          */
    715         function test_prepare_with_unescaped_percents() {
    716                 global $wpdb;
    717 
    718                 $sql = $wpdb->prepare( '%d %1$d %%% %', 1 );
    719                 $this->assertEquals( '1 %1$d %% %', $sql );
     783         * @dataProvider data_prepare_with_placeholders
     784         */
     785        function test_prepare_with_placeholders_and_individual_args( $sql, $values, $incorrect_usage, $expected) {
     786                global $wpdb;
     787
     788                if ( $incorrect_usage ) {
     789                        $this->setExpectedIncorrectUsage( 'wpdb::prepare' );
     790                }
     791
     792                if ( ! is_array( $values ) ) {
     793                        $values = array( $values );
     794                }
     795
     796                array_unshift( $values, $sql );
     797
     798                $sql = call_user_func_array( array( $wpdb, 'prepare' ), $values );
     799                $this->assertEquals( $expected, $sql );
     800        }
     801
     802        /**
     803         * @dataProvider data_prepare_with_placeholders
     804         */
     805        function test_prepare_with_placeholders_and_array_args( $sql, $values, $incorrect_usage, $expected) {
     806                global $wpdb;
     807
     808                if ( $incorrect_usage ) {
     809                        $this->setExpectedIncorrectUsage( 'wpdb::prepare' );
     810                }
     811
     812                if ( ! is_array( $values ) ) {
     813                        $values = array( $values );
     814                }
     815
     816                $sql = call_user_func_array( array( $wpdb, 'prepare' ), array( $sql, $values ) );
     817                $this->assertEquals( $expected, $sql );
     818        }
     819
     820        function data_prepare_with_placeholders() {
     821                global $wpdb;
     822
     823                return array(
     824                        array(
     825                                '%5s',   // SQL to prepare
     826                                'foo',   // Value to insert in the SQL
     827                                false,   // Whether to expect an incorrect usage error or not
     828                                '  foo', // Expected output
     829                        ),
     830                        array(
     831                                '%1$d %%% % %%1$d%% %%%1$d%%',
     832                                1,
     833                                true,
     834                                "1 {$wpdb->placeholder_escape()}{$wpdb->placeholder_escape()} {$wpdb->placeholder_escape()} {$wpdb->placeholder_escape()}1\$d{$wpdb->placeholder_escape()} {$wpdb->placeholder_escape()}1{$wpdb->placeholder_escape()}",
     835                        ),
     836                        array(
     837                                '%-5s',
     838                                'foo',
     839                                false,
     840                                'foo  ',
     841                        ),
     842                        array(
     843                                '%05s',
     844                                'foo',
     845                                false,
     846                                '00foo',
     847                        ),
     848                        array(
     849                                "%'#5s",
     850                                'foo',
     851                                false,
     852                                '##foo',
     853                        ),
     854                        array(
     855                                '%.3s',
     856                                'foobar',
     857                                false,
     858                                'foo',
     859                        ),
     860                        array(
     861                                '%.3f',
     862                                5.123456,
     863                                false,
     864                                '5.123',
     865                        ),
     866                        array(
     867                                '%.3f',
     868                                5.12,
     869                                false,
     870                                '5.120',
     871                        ),
     872                        array(
     873                                '%s',
     874                                ' %s ',
     875                                false,
     876                                "' {$wpdb->placeholder_escape()}s '",
     877                        ),
     878                        array(
     879                                '%1$s',
     880                                ' %s ',
     881                                false,
     882                                " {$wpdb->placeholder_escape()}s ",
     883                        ),
     884                        array(
     885                                '%1$s',
     886                                ' %1$s ',
     887                                false,
     888                                " {$wpdb->placeholder_escape()}1\$s ",
     889                        ),
     890                        array(
     891                                '%d %1$d %%% %',
     892                                1,
     893                                true,
     894                                "1 1 {$wpdb->placeholder_escape()}{$wpdb->placeholder_escape()} {$wpdb->placeholder_escape()}",
     895                        ),
     896                        array(
     897                                '%d %2$s',
     898                                array( 1, 'hello' ),
     899                                false,
     900                                "1 hello",
     901                        ),
     902                        array(
     903                                "'%s'",
     904                                'hello',
     905                                false,
     906                                "'hello'",
     907                        ),
     908                        array(
     909                                '"%s"',
     910                                'hello',
     911                                false,
     912                                "'hello'",
     913                        ),
     914                        array(
     915                                "%s '%1\$s'",
     916                                'hello',
     917                                true,
     918                                "'hello' 'hello'",
     919                        ),
     920                        array(
     921                                "%s '%1\$s'",
     922                                'hello',
     923                                true,
     924                                "'hello' 'hello'",
     925                        ),
     926                        array(
     927                                '%s "%1$s"',
     928                                'hello',
     929                                true,
     930                                "'hello' \"hello\"",
     931                        ),
     932                        array(
     933                                "%%s %%'%1\$s'",
     934                                'hello',
     935                                false,
     936                                "{$wpdb->placeholder_escape()}s {$wpdb->placeholder_escape()}'hello'",
     937                        ),
     938                        array(
     939                                '%%s %%"%1$s"',
     940                                'hello',
     941                                false,
     942                                "{$wpdb->placeholder_escape()}s {$wpdb->placeholder_escape()}\"hello\"",
     943                        ),
     944                        array(
     945                                '%s',
     946                                ' %  s ',
     947                                false,
     948                                "' {$wpdb->placeholder_escape()}  s '",
     949                        ),
     950                        array(
     951                                '%%f %%"%1$f"',
     952                                3,
     953                                false,
     954                                "{$wpdb->placeholder_escape()}f {$wpdb->placeholder_escape()}\"3.000000\"",
     955                        ),
     956                        array(
     957                                'WHERE second=\'%2$s\' AND first=\'%1$s\'',
     958                                array( 'first arg', 'second arg' ),
     959                                false,
     960                                "WHERE second='second arg' AND first='first arg'",
     961                        ),
     962                        array(
     963                                'WHERE second=%2$d AND first=%1$d',
     964                                array( 1, 2 ),
     965                                false,
     966                                "WHERE second=2 AND first=1",
     967                        ),
     968                        array(
     969                                "'%'%%s",
     970                                'hello',
     971                                true,
     972                                "'{$wpdb->placeholder_escape()}'{$wpdb->placeholder_escape()}s",
     973                        ),
     974                        array(
     975                                "'%'%%s%s",
     976                                'hello',
     977                                false,
     978                                "'{$wpdb->placeholder_escape()}'{$wpdb->placeholder_escape()}s'hello'",
     979                        ),
     980                        array(
     981                                "'%'%%s %s",
     982                                'hello',
     983                                false,
     984                                "'{$wpdb->placeholder_escape()}'{$wpdb->placeholder_escape()}s 'hello'",
     985                        ),
     986                        array(
     987                                "'%-'#5s' '%'#-+-5s'",
     988                                array( 'hello', 'foo' ),
     989                                false,
     990                                "'hello' 'foo##'",
     991                        ),
     992                );
     993        }
     994
     995        /**
     996         * @dataProvider data_escape_and_prepare
     997         */
     998        function test_escape_and_prepare( $escape, $sql, $values, $incorrect_usage, $expected ) {
     999                global $wpdb;
     1000
     1001                if ( $incorrect_usage ) {
     1002                        $this->setExpectedIncorrectUsage( 'wpdb::prepare' );
     1003                }
     1004
     1005                $escape = esc_sql( $escape );
     1006
     1007                $sql = str_replace( '{ESCAPE}', $escape, $sql );
     1008
     1009                $actual = $wpdb->prepare( $sql, $values );
     1010
     1011                $this->assertEquals( $expected, $actual );
     1012        }
     1013
     1014        function data_escape_and_prepare() {
     1015                global $wpdb;
     1016                return array(
     1017                        array(
     1018                                '%s',                                  // String to pass through esc_url()
     1019                                ' {ESCAPE} ',                          // Query to insert the output of esc_url() into, replacing "{ESCAPE}"
     1020                                'foo',                                 // Data to send to prepare()
     1021                                true,                                  // Whether to expect an incorrect usage error or not
     1022                                " {$wpdb->placeholder_escape()}s ",    // Expected output
     1023                        ),
     1024                        array(
     1025                                'foo%sbar',
     1026                                "SELECT * FROM bar WHERE foo='{ESCAPE}' OR baz=%s",
     1027                                array( ' SQLi -- -', 'pewpewpew' ),
     1028                                true,
     1029                                null,
     1030                        ),
     1031                        array(
     1032                                '%s',
     1033                                ' %s {ESCAPE} ',
     1034                                'foo',
     1035                                false,
     1036                                " 'foo' {$wpdb->placeholder_escape()}s ",
     1037                        ),
     1038                );
     1039        }
     1040
     1041        /**
     1042         * @expectedIncorrectUsage wpdb::prepare
     1043         */
     1044        function test_double_prepare() {
     1045                global $wpdb;
     1046
     1047                $part = $wpdb->prepare( ' AND meta_value = %s', ' %s ' );
     1048                $this->assertNotContains( '%s', $part );
     1049                $query = $wpdb->prepare( 'SELECT * FROM {$wpdb->postmeta} WHERE meta_key = %s $part', array( 'foo', 'bar' ) );
     1050                $this->assertNull( $query );
     1051        }
     1052
     1053        function test_prepare_numeric_placeholders_float_args() {
     1054                global $wpdb;
     1055
     1056                $actual = $wpdb->prepare(
     1057                        'WHERE second=%2$f AND first=%1$f',
     1058                        1.1,
     1059                        2.2
     1060                );
     1061
     1062                /* Floats can be right padded, need to assert differently */
     1063                $this->assertContains( ' first=1.1', $actual );
     1064                $this->assertContains( ' second=2.2', $actual );
     1065        }
     1066
     1067        function test_prepare_numeric_placeholders_float_array() {
     1068                global $wpdb;
     1069
     1070                $actual = $wpdb->prepare(
     1071                        'WHERE second=%2$f AND first=%1$f',
     1072                        array( 1.1, 2.2 )
     1073                );
     1074
     1075                /* Floats can be right padded, need to assert differently */
     1076                $this->assertContains( ' first=1.1', $actual );
     1077                $this->assertContains( ' second=2.2', $actual );
     1078        }
     1079
     1080        function test_query_unescapes_placeholders() {
     1081                global $wpdb;
     1082
     1083                $value = ' %s ';
     1084
     1085                $wpdb->query( "CREATE TABLE {$wpdb->prefix}test_placeholder( a VARCHAR(100) );" );
     1086                $sql = $wpdb->prepare( "INSERT INTO {$wpdb->prefix}test_placeholder VALUES(%s)", $value );
     1087                $wpdb->query( $sql );
     1088
     1089                $actual = $wpdb->get_var( "SELECT a FROM {$wpdb->prefix}test_placeholder" );
     1090
     1091                $wpdb->query( "DROP TABLE {$wpdb->prefix}test_placeholder" );
     1092
     1093                $this->assertNotContains( '%s', $sql );
     1094                $this->assertEquals( $value, $actual );
     1095        }
     1096
     1097        function test_esc_sql_with_unsupported_placeholder_type() {
     1098                global $wpdb;
     1099
     1100                $sql = $wpdb->prepare( ' %s %1$c ', 'foo' );
     1101                $sql = $wpdb->prepare( " $sql %s ", 'foo' );
     1102
     1103                $this->assertEquals( "  'foo' {$wpdb->placeholder_escape()}1\$c  'foo' ", $sql );
    7201104        }
    7211105}
  • branches/3.9/wp-tests-config-sample.php

    r25571 r42066  
    3131define( 'DB_COLLATE', '' );
    3232
     33/**#@+
     34 * Authentication Unique Keys and Salts.
     35 *
     36 * Change these to different unique phrases!
     37 * You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
     38 */
     39define('AUTH_KEY',         'put your unique phrase here');
     40define('SECURE_AUTH_KEY',  'put your unique phrase here');
     41define('LOGGED_IN_KEY',    'put your unique phrase here');
     42define('NONCE_KEY',        'put your unique phrase here');
     43define('AUTH_SALT',        'put your unique phrase here');
     44define('SECURE_AUTH_SALT', 'put your unique phrase here');
     45define('LOGGED_IN_SALT',   'put your unique phrase here');
     46define('NONCE_SALT',       'put your unique phrase here');
     47
    3348$table_prefix  = 'wptests_';   // Only numbers, letters, and underscores please!
    3449
Note: See TracChangeset for help on using the changeset viewer.