Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/test-suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ jobs:
- name: Generate coverage report
if: ${{ !cancelled() }}
run: make gcovr-xml
- uses: codecov/codecov-action@v5
- uses: codecov/codecov-action@v6
if: ${{ !cancelled() }}
with:
disable_search: true
Expand Down
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ PHP NEWS
. Fixed bug GH-21223; mb_guess_encoding no longer crashes when passed huge
list of candidate encodings (with 200,000+ entries). (Jordi Kroon)

- Mysqli:
. Added mysqli_quote_string() and mysqli::quote_string(). (Kamil Tekiela)

- Opcache:
. Fixed bug GH-20051 (apache2 shutdowns when restart is requested during
preloading). (Arnaud, welcomycozyhom)
Expand Down
4 changes: 4 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ PHP 8.6 UPGRADE NOTES
. `grapheme_strrev()` returns strrev for grapheme cluster unit.
RFC: https://wiki.php.net/rfc/grapheme_strrev

- mysqli:
. Added `mysqli::quote_string()` and `mysqli_quote_string()`.
RFC: https://wiki.php.net/rfc/mysqli_quote_string

- Standard:
. `clamp()` returns the given value if in range, else return the nearest
bound.
Expand Down
3 changes: 3 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES
. The INI_ORIG_{INT|STR|FLT|BOOL}() macros have been removed as they are
unused. If this behaviour is required fall back to the zend_ini_*
functions.
. The unused ZEND_AST_PARENT_PROPERTY_HOOK_CALL has been removed.
. ZEND_AST_METHOD_REFERENCE has been renamed to
ZEND_AST_TRAIT_METHOD_REFERENCE.

========================
2. Build system changes
Expand Down
8 changes: 1 addition & 7 deletions Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -1055,13 +1055,7 @@ static zend_always_inline bool zend_char_has_nul_byte(const char *s, size_t know
#define RETURN_ZVAL(zv, copy, dtor) do { RETVAL_ZVAL(zv, copy, dtor); return; } while (0)
#define RETURN_FALSE do { RETVAL_FALSE; return; } while (0)
#define RETURN_TRUE do { RETVAL_TRUE; return; } while (0)

#ifndef HAVE_GCOV
# define RETURN_THROWS() do { ZEND_ASSERT(EG(exception)); (void) return_value; return; } while (0)
#else
/* Drop ZEND_ASSERT() to avoid untested branch warning in gcov. */
# define RETURN_THROWS() do { (void) return_value; return; } while (0)
#endif
#define RETURN_THROWS() do { ZEND_ASSERT(EG(exception)); (void) return_value; return; } while (0)

#define HASH_OF(p) (Z_TYPE_P(p)==IS_ARRAY ? Z_ARRVAL_P(p) : ((Z_TYPE_P(p)==IS_OBJECT ? Z_OBJ_HT_P(p)->get_properties(Z_OBJ_P(p)) : NULL)))

Expand Down
8 changes: 1 addition & 7 deletions Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -2555,12 +2555,6 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
smart_str_appendc(str, ')');
break;
}
case ZEND_AST_PARENT_PROPERTY_HOOK_CALL:
smart_str_append(str, Z_STR_P(zend_ast_get_zval(ast->child[0])));
smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[1], 0, indent);
smart_str_appendc(str, ')');
break;
case ZEND_AST_CALLABLE_CONVERT: {
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast;
ast = fcc_ast->args;
Expand Down Expand Up @@ -2809,7 +2803,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
smart_str_appends(str, " insteadof ");
zend_ast_export_ex(str, ast->child[1], 0, indent);
break;
case ZEND_AST_METHOD_REFERENCE:
case ZEND_AST_TRAIT_METHOD_REFERENCE:
if (ast->child[0]) {
zend_ast_export_name(str, ast->child[0], 0, indent);
smart_str_appends(str, "::");
Expand Down
3 changes: 1 addition & 2 deletions Zend/zend_ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ enum _zend_ast_kind {
ZEND_AST_DECLARE,
ZEND_AST_USE_TRAIT,
ZEND_AST_TRAIT_PRECEDENCE,
ZEND_AST_METHOD_REFERENCE,
ZEND_AST_TRAIT_METHOD_REFERENCE,
ZEND_AST_NAMESPACE,
ZEND_AST_USE_ELEM,
ZEND_AST_TRAIT_ALIAS,
Expand All @@ -153,7 +153,6 @@ enum _zend_ast_kind {
ZEND_AST_MATCH,
ZEND_AST_MATCH_ARM,
ZEND_AST_NAMED_ARG,
ZEND_AST_PARENT_PROPERTY_HOOK_CALL,
ZEND_AST_PIPE,

/* 3 child nodes */
Expand Down
4 changes: 0 additions & 4 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -12135,7 +12135,6 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
case ZEND_AST_METHOD_CALL:
case ZEND_AST_NULLSAFE_METHOD_CALL:
case ZEND_AST_STATIC_CALL:
case ZEND_AST_PARENT_PROPERTY_HOOK_CALL:
case ZEND_AST_PIPE:
zend_compile_var(result, ast, BP_VAR_R, false);
return;
Expand Down Expand Up @@ -12292,9 +12291,6 @@ static zend_op *zend_compile_var_inner(znode *result, zend_ast *ast, uint32_t ty
case ZEND_AST_CALL:
zend_compile_call(result, ast, type);
return NULL;
case ZEND_AST_PARENT_PROPERTY_HOOK_CALL:
zend_compile_parent_property_hook_call(result, ast, type);
return NULL;
case ZEND_AST_METHOD_CALL:
case ZEND_AST_NULLSAFE_METHOD_CALL:
zend_compile_method_call(result, ast, type);
Expand Down
4 changes: 2 additions & 2 deletions Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -1059,13 +1059,13 @@ trait_alias:

trait_method_reference:
identifier
{ $$ = zend_ast_create(ZEND_AST_METHOD_REFERENCE, NULL, $1); }
{ $$ = zend_ast_create(ZEND_AST_TRAIT_METHOD_REFERENCE, NULL, $1); }
| absolute_trait_method_reference { $$ = $1; }
;

absolute_trait_method_reference:
class_name T_PAAMAYIM_NEKUDOTAYIM identifier
{ $$ = zend_ast_create(ZEND_AST_METHOD_REFERENCE, $1, $3); }
{ $$ = zend_ast_create(ZEND_AST_TRAIT_METHOD_REFERENCE, $1, $3); }
;

method_body:
Expand Down
5 changes: 4 additions & 1 deletion Zend/zend_portability.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@
# define ZEND_ASSUME(c)
#endif

#if ZEND_DEBUG
#ifdef HAVE_GCOV
/* Disable assert() when compiling with gcov to avoid untested branch warning. */
# define ZEND_ASSERT(c) ((void)sizeof(c))
#elif ZEND_DEBUG
# define ZEND_ASSERT(c) assert(c)
#else
# define ZEND_ASSERT(c) ZEND_ASSUME(c)
Expand Down
1 change: 0 additions & 1 deletion build/Makefile.gcov
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ GCOVR_EXCLUDES = \
GCOVR_EXCLUDE_LINES_BY_PATTERNS = \
'.*\b(ZEND_PARSE_PARAMETERS_(START|END|NONE)|Z_PARAM_).*' \
'\s*EMPTY_SWITCH_DEFAULT_CASE\(\)(;)?\s*' \
'\s*ZEND_ASSERT\(.*\);\s*' \
'\s*ZEND_UNREACHABLE\(\);\s*'

lcov: lcov-html
Expand Down
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);
}

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!