diff --git a/tests/WP_SQLite_Translator_Tests.php b/tests/WP_SQLite_Translator_Tests.php index 7a334eeb..71070eca 100644 --- a/tests/WP_SQLite_Translator_Tests.php +++ b/tests/WP_SQLite_Translator_Tests.php @@ -118,6 +118,36 @@ public static function regexpOperators() { ); } + public function testRegexpReplace() { + /* Testing if an actual replacment works correctly */ + $this->assertQuery( + "INSERT INTO _options (option_name, option_value) VALUES ('test-ignore', '1');" + ); + $this->assertQuery( + "INSERT INTO _options (option_name, option_value) VALUES ('test-remove', '2');" + ); + $this->assertQuery( "SELECT * FROM _options WHERE REGEXP_REPLACE(option_name, '(-ignore|-remove)', '') = 'test'" ); + $this->assertCount( 2, $this->engine->get_query_results() ); + + /* If one of the required parameters is null, the return value is null, copying the MYSQL/MariaDB behavior */ + $this->assertQuery( "SELECT REGEXP_REPLACE( null, 'a', 'x') as result" ); + $results = $this->engine->get_query_results(); + $this->assertEquals( null, $results[0]->result ); + + $this->assertQuery( "SELECT REGEXP_REPLACE( 'abc', null, 'x') as result" ); + $results = $this->engine->get_query_results(); + $this->assertEquals( null, $results[0]->result ); + + $this->assertQuery( "SELECT REGEXP_REPLACE( 'abc', 'a', null) as result" ); + $results = $this->engine->get_query_results(); + $this->assertEquals( null, $results[0]->result ); + + /* Providing an empty pattern should produce an error - but we changed that to null to avoid breaking things */ + $this->assertQuery( "SELECT REGEXP_REPLACE( 'abc', '', 'x') as result" ); + $results = $this->engine->get_query_results(); + $this->assertEquals( null, $results[0]->result ); + } + public function testInsertDateNow() { $this->assertQuery( "INSERT INTO _dates (option_name, option_value) VALUES ('first', now());" diff --git a/wp-includes/sqlite/class-wp-sqlite-pdo-user-defined-functions.php b/wp-includes/sqlite/class-wp-sqlite-pdo-user-defined-functions.php index 6f0d83df..816ccb86 100644 --- a/wp-includes/sqlite/class-wp-sqlite-pdo-user-defined-functions.php +++ b/wp-includes/sqlite/class-wp-sqlite-pdo-user-defined-functions.php @@ -68,6 +68,7 @@ public function __construct( $pdo ) { 'isnull' => 'isnull', 'if' => '_if', 'regexp' => 'regexp', + 'regexp_replace' => 'regexp_replace', 'field' => 'field', 'log' => 'log', 'least' => 'least', @@ -493,6 +494,54 @@ public function regexp( $pattern, $field ) { return preg_match( $pattern, $field ); } + /** + * Method to emulate MySQL REGEXP_REPLACE() function. + * + * @param string|array $pattern Regular expression to search for (or array of strings). + * @param string|array $replacement The string or an array with strings to replace. + * @param string|array $field Haystack. + * + * @return Array if the field parameter is an array, or a string otherwise. + */ + public function regexp_replace( $field, $pattern, $replacement ) { + /* + * If the original query says REGEXP BINARY + * the comparison is byte-by-byte and letter casing now + * matters since lower- and upper-case letters have different + * byte codes. + * + * The REGEXP function can't be easily made to accept two + * parameters, so we'll have to use a hack to get around this. + * + * If the first character of the pattern is a null byte, we'll + * remove it and make the comparison case-sensitive. This should + * be reasonably safe since PHP does not allow null bytes in + * regular expressions anyway. + */ + + /* Return null if one of the required parameter is null */ + if ( is_null( $field ) || is_null( $pattern ) || is_null( $replacement ) ) { + return null; + } + + /* Return null if the pattern is empty - this changes MySQL/MariaDB behavior! */ + if ( empty( $pattern ) ) { + return null; + } + + if ( "\x00" === $pattern[0] ) { + $pattern = substr( $pattern, 1 ); + $flags = ''; + } else { + // Otherwise, the search is case-insensitive. + $flags = 'i'; + } + $pattern = str_replace( '/', '\/', $pattern ); + $pattern = '/' . $pattern . '/' . $flags; + + return preg_replace( $pattern, $replacement, $field ); + } + /** * Method to emulate MySQL FIELD() function. *