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:
63
Zend/tests/traits/bugs/gh13177.phpt
Normal file
63
Zend/tests/traits/bugs/gh13177.phpt
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
59
ext/mysqli/tests/gh12107.phpt
Normal file
59
ext/mysqli/tests/gh12107.phpt
Normal 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
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user