Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions ext/mysqli/mysqli.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,11 @@ public function real_connect(
*/
public function real_escape_string(string $string): string {}

/**
* @alias mysqli_quote_string
*/
public function quote_string(string $string): string {}

/**
* @tentative-return-type
* @alias mysqli_reap_async_query
Expand Down Expand Up @@ -1547,6 +1552,8 @@ function mysqli_real_escape_string(mysqli $mysql, string $string): string {}
/** @alias mysqli_real_escape_string */
function mysqli_escape_string(mysqli $mysql, string $string): string {}

function mysqli_quote_string(mysqli $mysql, string $string): string {}

function mysqli_real_query(mysqli $mysql, string $query): bool {}

/** @refcount 1 */
Expand Down
25 changes: 24 additions & 1 deletion ext/mysqli/mysqli_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -1198,7 +1198,7 @@ PHP_FUNCTION(mysqli_options)
zend_argument_value_error(ERROR_ARG_POS(2), "must be MYSQLI_INIT_COMMAND, MYSQLI_SET_CHARSET_NAME, MYSQLI_SERVER_PUBLIC_KEY, or one of the MYSQLI_OPT_* constants");
RETURN_THROWS();
}

if (expected_type != Z_TYPE_P(mysql_value)) {
switch (expected_type) {
case IS_STRING:
Expand Down Expand Up @@ -1363,6 +1363,29 @@ PHP_FUNCTION(mysqli_real_escape_string) {
RETURN_NEW_STR(newstr);
}

PHP_FUNCTION(mysqli_quote_string) {
MY_MYSQL *mysql;
zval *mysql_link = NULL;
char *escapestr;
size_t escapestr_len;
zend_string *newstr;

if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &mysql_link, mysqli_link_class_entry, &escapestr, &escapestr_len) == FAILURE) {
RETURN_THROWS();
}
MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);

newstr = zend_string_safe_alloc(2, escapestr_len, 2, 0);
char *out = ZSTR_VAL(newstr);
*out++ = '\'';
out += mysql_real_escape_string(mysql->mysql, out, escapestr, escapestr_len);
*out++ = '\'';
*out = '\0';
newstr = zend_string_truncate(newstr, out - ZSTR_VAL(newstr), 0);

RETURN_NEW_STR(newstr);
}

/* {{{ Undo actions from current transaction */
PHP_FUNCTION(mysqli_rollback)
{
Expand Down
11 changes: 10 additions & 1 deletion ext/mysqli/mysqli_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ext/mysqli/tests/mysqli_class_mysqli_interface.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ require_once 'skipifconnectfailure.inc';
'ping' => true,
'prepare' => true,
'query' => true,
'quote_string' => true,
'real_connect' => true,
'real_escape_string' => true,
'real_query' => true,
Expand Down
86 changes: 86 additions & 0 deletions ext/mysqli/tests/mysqli_quote_string.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
--TEST--
mysqli_quote_string()
--EXTENSIONS--
mysqli
--SKIPIF--
<?php
require_once 'skipifconnectfailure.inc';
?>
--FILE--
<?php

require_once 'connect.inc';
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket);

echo mysqli_quote_string($link, '\\') . "\n";
echo mysqli_quote_string($link, '"') . "\n";
echo mysqli_quote_string($link, "'") . "\n";

$escaped = mysqli_quote_string($link, "\' \ \"");
echo $escaped . "\n";
$result = $link->query("SELECT $escaped AS test");
$value = $result->fetch_column();
echo $value . "\n";

$escaped = mysqli_quote_string($link, '" OR 1=1 -- foo');
echo $escaped . "\n";
$result = $link->query("SELECT $escaped AS test");
$value = $result->fetch_column();
echo $value . "\n";

$escaped = mysqli_quote_string($link, "\n");
if ($escaped !== "'\\n'") {
printf("[001] Expected '\\n', got %s\n", $escaped);
}

$escaped = mysqli_quote_string($link, "\r");
if ($escaped !== "'\\r'") {
printf("[002] Expected '\\r', got %s\n", $escaped);
}

$escaped = mysqli_quote_string($link, "foo" . chr(0) . "bar");
if ($escaped !== "'foo\\0bar'") {
printf("[003] Expected 'foo\\0bar', got %s\n", $escaped);
}

Comment thread
TimWolla marked this conversation as resolved.
echo "=====================\n";

// Test that the SQL injection is impossible with NO_BACKSLASH_ESCAPES mode
$link->query('SET @@sql_mode="NO_BACKSLASH_ESCAPES"');

echo $link->quote_string('\\') . "\n";
echo $link->quote_string('"') . "\n";
echo $link->quote_string("'") . "\n";

$escaped = $link->quote_string("\' \ \"");
echo $escaped . "\n";
$result = $link->query("SELECT $escaped AS test");
$value = $result->fetch_column();
echo $value . "\n";

$escaped = $link->quote_string('" OR 1=1 -- foo');
echo $escaped . "\n";
$result = $link->query("SELECT $escaped AS test");
$value = $result->fetch_column();
echo $value . "\n";

echo "done!";
?>
--EXPECT--
'\\'
'\"'
'\''
'\\\' \\ \"'
\' \ "
'\" OR 1=1 -- foo'
" OR 1=1 -- foo
=====================
'\'
'"'
''''
'\'' \ "'
\' \ "
'" OR 1=1 -- foo'
" OR 1=1 -- foo
done!
Loading