1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Merge branch 'PHP-8.3'

* PHP-8.3:
  Fix GH-13177: PHP 8.3.2: final private constructor not allowed when used in trait
  Fix GH-12107: When running a stored procedure (that returns a result set) twice, PHP crashes
This commit is contained in:
Niels Dossche
2024-01-19 23:41:01 +01:00
4 changed files with 141 additions and 5 deletions

View File

@@ -0,0 +1,63 @@
--TEST--
GH-13177 (PHP 8.3.2: final private constructor not allowed when used in trait)
--FILE--
<?php
trait Bar {
final private function __construct() {}
}
final class Foo1 {
use Bar;
}
final class Foo2 {
use Bar {
__construct as final;
}
}
class Foo3 {
use Bar {
__construct as final;
}
}
trait TraitNonConstructor {
private final function test() {}
}
class Foo4 {
use TraitNonConstructor { test as __construct; }
}
for ($i = 1; $i <= 4; $i++) {
$rc = new ReflectionClass("Foo$i");
echo $rc->getMethod("__construct"), "\n";
}
class Foo5 extends Foo3 {
private function __construct() {}
}
?>
--EXPECTF--
Warning: Private methods cannot be final as they are never overridden by other classes in %s on line %d
Method [ <user, ctor> final private method __construct ] {
@@ %sgh13177.php 4 - 4
}
Method [ <user, ctor> final private method __construct ] {
@@ %sgh13177.php 4 - 4
}
Method [ <user, ctor> final private method __construct ] {
@@ %sgh13177.php 4 - 4
}
Method [ <user, ctor> final private method __construct ] {
@@ %sgh13177.php 24 - 24
}
Fatal error: Cannot override final method Foo3::__construct() in %s on line %d

View File

@@ -1952,10 +1952,6 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_
zend_function *new_fn;
bool check_inheritance = false;
if ((fn->common.fn_flags & (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)) == (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)) {
zend_error(E_COMPILE_WARNING, "Private methods cannot be final as they are never overridden by other classes");
}
if ((existing_fn = zend_hash_find_ptr(&ce->function_table, key)) != NULL) {
/* if it is the same function with the same visibility and has not been assigned a class scope yet, regardless
* of where it is coming from there is no conflict and we do not need to add it again */
@@ -2036,6 +2032,17 @@ static void zend_fixup_trait_method(zend_function *fn, zend_class_entry *ce) /*
}
/* }}} */
static void zend_traits_check_private_final_inheritance(uint32_t original_fn_flags, zend_function *fn_copy, zend_string *name)
{
/* If the function was originally already private+final, then it will have already been warned about.
* If the function became private+final only after applying modifiers, we need to emit the same warning. */
if ((original_fn_flags & (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)) != (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)
&& (fn_copy->common.fn_flags & (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)) == (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)
&& !zend_string_equals_literal_ci(name, ZEND_CONSTRUCTOR_FUNC_NAME)) {
zend_error(E_COMPILE_WARNING, "Private methods cannot be final as they are never overridden by other classes");
}
}
static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, zend_class_entry *ce, HashTable *exclude_table, zend_class_entry **aliases) /* {{{ */
{
zend_trait_alias *alias, **alias_ptr;
@@ -2061,6 +2068,8 @@ static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, z
fn_copy.common.fn_flags = alias->modifiers | fn->common.fn_flags;
}
zend_traits_check_private_final_inheritance(fn->common.fn_flags, &fn_copy, alias->alias);
lcname = zend_string_tolower(alias->alias);
zend_add_trait_method(ce, alias->alias, lcname, &fn_copy);
zend_string_release_ex(lcname, 0);
@@ -2098,6 +2107,8 @@ static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, z
}
}
zend_traits_check_private_final_inheritance(fn->common.fn_flags, &fn_copy, fnname);
zend_add_trait_method(ce, fn->common.function_name, fnname, &fn_copy);
}
}

View File

@@ -0,0 +1,59 @@
--TEST--
GH-12107 (When running a stored procedure (that returns a result set) twice, PHP crashes)
--EXTENSIONS--
mysqli
--SKIPIF--
<?php
require_once 'skipifconnectfailure.inc';
?>
--FILE--
<?php
require_once 'connect.inc';
$mysqli = new mysqli("$host:$port", $user, $passwd, $db);
$sql = <<<SQL
CREATE PROCEDURE `gh12107`()
BEGIN
SELECT "hello world";
END;
SQL;
$mysqli->query($sql);
echo "Start or run 1\n";
$stmt = $mysqli->prepare("call `gh12107`()");
$stmt->execute();
$stmt->bind_result($output);
var_dump($stmt->fetch());
var_dump($output);
unset($output);
echo "End of run 1\n";
echo "Start or run 2\n";
$stmt->execute();
$stmt->bind_result($output);
var_dump($stmt->fetch());
var_dump($output);
echo "End of run 2\n";
?>
--CLEAN--
<?php
require_once 'connect.inc';
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))
printf("[c001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
if (!mysqli_query($link, "DROP PROCEDURE IF EXISTS gh12107"))
printf("[c002] Cannot drop procedure, [%d] %s\n", mysqli_errno($link), mysqli_error($link));
mysqli_close($link);
?>
--EXPECT--
Start or run 1
bool(true)
string(11) "hello world"
End of run 1
Start or run 2
bool(true)
string(11) "hello world"
End of run 2

View File

@@ -652,8 +652,11 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, const enum_my
Executed, but the user hasn't started to fetch
This will clean also the metadata, but after the EXECUTE call we will
have it again.
stmt->result may be freed and nullified by free_stmt_result, transitively called from flush.
*/
stmt->result->m.free_result_buffers(stmt->result);
if (stmt->result) {
stmt->result->m.free_result_buffers(stmt->result);
}
stmt->state = MYSQLND_STMT_PREPARED;
} else if (stmt->state < MYSQLND_STMT_PREPARED) {