diff --git a/.github/workflows/wp-tests-phpunit-run.js b/.github/workflows/wp-tests-phpunit-run.js index 658f319..f49d691 100644 --- a/.github/workflows/wp-tests-phpunit-run.js +++ b/.github/workflows/wp-tests-phpunit-run.js @@ -11,29 +11,11 @@ const fs = require( 'fs' ); const path = require( 'path' ); const expectedErrors = [ - 'Tests_Admin_wpSiteHealth::test_object_cache_default_thresholds_non_multisite', - 'Tests_Admin_wpSiteHealth::test_object_cache_thresholds with data set #0', - 'Tests_Admin_wpSiteHealth::test_object_cache_thresholds with data set #1', - 'Tests_Admin_wpSiteHealth::test_object_cache_thresholds with data set #2', - 'Tests_Admin_wpSiteHealth::test_object_cache_thresholds with data set #3', - 'Tests_Admin_wpSiteHealth::test_object_cache_thresholds with data set #4', 'Tests_Comment_WpComment::test_get_instance_should_succeed_for_float_that_is_equal_to_post_id', 'Tests_Cron_getCronArray::test_get_cron_array_output_validation with data set "null"', 'Tests_DB_Charset::test_strip_invalid_text', 'Tests_DB::test_db_reconnect', 'Tests_DB::test_get_col_info', - 'Tests_DB::test_prepare_should_respect_the_allow_unsafe_unquoted_parameters_property with data set "escaped-false-1"', - 'Tests_DB::test_prepare_should_respect_the_allow_unsafe_unquoted_parameters_property with data set "escaped-false-2"', - 'Tests_DB::test_prepare_should_respect_the_allow_unsafe_unquoted_parameters_property with data set "escaped-true-1"', - 'Tests_DB::test_prepare_should_respect_the_allow_unsafe_unquoted_parameters_property with data set "escaped-true-2"', - 'Tests_DB::test_prepare_should_respect_the_allow_unsafe_unquoted_parameters_property with data set "format-false-1"', - 'Tests_DB::test_prepare_should_respect_the_allow_unsafe_unquoted_parameters_property with data set "format-false-2"', - 'Tests_DB::test_prepare_should_respect_the_allow_unsafe_unquoted_parameters_property with data set "format-true-1"', - 'Tests_DB::test_prepare_should_respect_the_allow_unsafe_unquoted_parameters_property with data set "format-true-2"', - 'Tests_DB::test_prepare_should_respect_the_allow_unsafe_unquoted_parameters_property with data set "numbered-false-1"', - 'Tests_DB::test_prepare_should_respect_the_allow_unsafe_unquoted_parameters_property with data set "numbered-false-2"', - 'Tests_DB::test_prepare_should_respect_the_allow_unsafe_unquoted_parameters_property with data set "numbered-true-1"', - 'Tests_DB::test_prepare_should_respect_the_allow_unsafe_unquoted_parameters_property with data set "numbered-true-2"', 'Tests_DB::test_process_fields_value_too_long_for_field with data set "invalid chars"', 'Tests_DB::test_process_fields_value_too_long_for_field with data set "too long"', 'Tests_DB::test_process_fields', @@ -45,6 +27,8 @@ const expectedErrors = [ ]; const expectedFailures = [ + 'Tests_Admin_wpSiteHealth::test_object_cache_thresholds with data set #2', + 'Tests_Admin_wpSiteHealth::test_object_cache_thresholds with data set #3', 'Tests_Comment::test_wp_new_comment_respects_comment_field_lengths', 'Tests_Comment::test_wp_update_comment', 'Tests_DB_dbDelta::test_spatial_indices', diff --git a/wp-includes/sqlite-ast/class-wp-sqlite-driver.php b/wp-includes/sqlite-ast/class-wp-sqlite-driver.php index bfce1e7..110264e 100644 --- a/wp-includes/sqlite-ast/class-wp-sqlite-driver.php +++ b/wp-includes/sqlite-ast/class-wp-sqlite-driver.php @@ -489,6 +489,22 @@ public function get_insert_id() { return $last_insert_id; } + /** + * Quotes a string for use in a query. + * + * Places quotes around the input string (if required) and escapes special + * characters within the input string. See "PDO::quote()". + * + * @param string $value The string value to quote. + * @param int $type The type of the parameter. Default is PDO::PARAM_STR. + * @return string The quoted string. + */ + public function quote( string $value, int $type = PDO::PARAM_STR ): string { + $quoted = addslashes( $value ); + $quoted = $this->pdo->quote( $value, $type ); + return $quoted; + } + /** * Translate and execute a MySQL query in SQLite. * diff --git a/wp-includes/sqlite/class-wp-sqlite-crosscheck-db.php b/wp-includes/sqlite/class-wp-sqlite-crosscheck-db.php index 53a688d..7878a48 100644 --- a/wp-includes/sqlite/class-wp-sqlite-crosscheck-db.php +++ b/wp-includes/sqlite/class-wp-sqlite-crosscheck-db.php @@ -2,8 +2,8 @@ class WP_SQLite_Crosscheck_DB_ extends WP_SQLite_DB { - public function __construct() { - parent::__construct(); + public function __construct( string $dbname ) { + parent::__construct( $dbname ); $GLOBALS['sqlite'] = $this; $GLOBALS['mysql'] = new wpdb( DB_USER, diff --git a/wp-includes/sqlite/class-wp-sqlite-db.php b/wp-includes/sqlite/class-wp-sqlite-db.php index f98d350..6c87bb8 100644 --- a/wp-includes/sqlite/class-wp-sqlite-db.php +++ b/wp-includes/sqlite/class-wp-sqlite-db.php @@ -21,12 +21,24 @@ class WP_SQLite_DB extends wpdb { protected $dbh; /** - * Constructor + * Backward compatibility, see wpdb::$allow_unsafe_unquoted_parameters. * - * Unlike wpdb, no credentials are needed. + * This property is mirroring "wpdb::$allow_unsafe_unquoted_parameters", + * because some tests are accessing it externally using PHP reflection. + * + * @var + */ + private $allow_unsafe_unquoted_parameters = true; + + /** + * Connects to the SQLite database. + * + * Unlike for MySQL, no credentials and host are needed. + * + * @param string $dbname Database name. */ - public function __construct() { - parent::__construct( '', '', '', '' ); + public function __construct( $dbname ) { + parent::__construct( '', '', $dbname, '' ); $this->charset = 'utf8mb4'; } @@ -107,7 +119,13 @@ public function _real_escape( $data ) { if ( ! is_scalar( $data ) ) { return ''; } - $escaped = addslashes( $data ); + if ( $this->dbh instanceof WP_SQLite_Driver ) { + // WP_SQLite_Driver::quote() wraps the escaped string with quotes, + // while WPDB expects the string to be escaped without them. + $escaped = substr( $this->dbh->quote( $data ), 1, -1 ); + } else { + $escaped = addslashes( $data ); + } return $this->add_placeholder_escape( $escaped ); } @@ -289,6 +307,33 @@ public function check_connection( $allow_bail = true ) { return true; } + /** + * Prepares a SQL query for safe execution. + * + * See "wpdb::prepare()". This override only fixes a WPDB test issue. + * + * @param string $query Query statement with `sprintf()`-like placeholders. + * @param array|mixed $args The array of variables or the first variable to substitute. + * @param mixed ...$args Further variables to substitute when using individual arguments. + * @return string|void Sanitized query string, if there is a query to prepare. + */ + public function prepare( $query, ...$args ) { + /* + * Sync "$allow_unsafe_unquoted_parameters" with the WPDB parent property. + * This is only needed because some WPDB tests are accessing the private + * property externally via PHP reflection. This should be fixed WP tests. + */ + $wpdb_allow_unsafe_unquoted_parameters = $this->__get( 'allow_unsafe_unquoted_parameters' ); + if ( $wpdb_allow_unsafe_unquoted_parameters !== $this->allow_unsafe_unquoted_parameters ) { + $property = new ReflectionProperty( 'wpdb', 'allow_unsafe_unquoted_parameters' ); + $property->setAccessible( true ); + $property->setValue( $this, $this->allow_unsafe_unquoted_parameters ); + $property->setAccessible( false ); + } + + return parent::prepare( $query, ...$args ); + } + /** * Performs a database query. * diff --git a/wp-includes/sqlite/db.php b/wp-includes/sqlite/db.php index 13c2111..9f8ca0a 100644 --- a/wp-includes/sqlite/db.php +++ b/wp-includes/sqlite/db.php @@ -59,7 +59,7 @@ $crosscheck_tests_file_path = dirname( __DIR__, 2 ) . '/tests/class-wp-sqlite-crosscheck-db.php'; if ( defined( 'SQLITE_DEBUG_CROSSCHECK' ) && SQLITE_DEBUG_CROSSCHECK && file_exists( $crosscheck_tests_file_path ) ) { require_once $crosscheck_tests_file_path; - $GLOBALS['wpdb'] = new WP_SQLite_Crosscheck_DB(); + $GLOBALS['wpdb'] = new WP_SQLite_Crosscheck_DB( DB_NAME ); } else { - $GLOBALS['wpdb'] = new WP_SQLite_DB(); + $GLOBALS['wpdb'] = new WP_SQLite_DB( DB_NAME ); }