File tree 3 files changed +61
-3
lines changed
tests/Functional/Driver/Mysqli
3 files changed +61
-3
lines changed Original file line number Diff line number Diff line change @@ -39,10 +39,17 @@ final class Result implements ResultInterface
39
39
/**
40
40
* @internal The result can be only instantiated by its driver connection or statement.
41
41
*
42
+ * @param Statement|null $statementReference Maintains a reference to the Statement that generated this result. This
43
+ * ensures that the lifetime of the Statement is managed in conjunction
44
+ * with its associated results, so they are destroyed together at the
45
+ * appropriate time, see {@see Statement::__destruct()}.
46
+ *
42
47
* @throws Exception
43
48
*/
44
- public function __construct (private readonly mysqli_stmt $ statement )
45
- {
49
+ public function __construct (
50
+ private readonly mysqli_stmt $ statement ,
51
+ private ?Statement $ statementReference = null , // @phpstan-ignore property.onlyWritten
52
+ ) {
46
53
$ meta = $ statement ->result_metadata ();
47
54
$ this ->hasColumns = $ meta !== false ;
48
55
$ this ->columnNames = $ meta !== false ? array_column ($ meta ->fetch_fields (), 'name ' ) : [];
Original file line number Diff line number Diff line change @@ -49,6 +49,11 @@ public function __construct(private readonly mysqli_stmt $stmt)
49
49
$ this ->boundValues = array_fill (1 , $ paramCount , null );
50
50
}
51
51
52
+ public function __destruct ()
53
+ {
54
+ @$ this ->stmt ->close ();
55
+ }
56
+
52
57
public function bindValue (int |string $ param , mixed $ value , ParameterType $ type ): void
53
58
{
54
59
assert (is_int ($ param ));
@@ -72,7 +77,7 @@ public function execute(): Result
72
77
throw StatementError::upcast ($ e );
73
78
}
74
79
75
- return new Result ($ this ->stmt );
80
+ return new Result ($ this ->stmt , $ this );
76
81
}
77
82
78
83
/**
Original file line number Diff line number Diff line change
1
+ <?php
2
+
3
+ declare (strict_types=1 );
4
+
5
+ namespace Doctrine \DBAL \Tests \Functional \Driver \Mysqli ;
6
+
7
+ use Doctrine \DBAL \Driver \Mysqli \Statement ;
8
+ use Doctrine \DBAL \Statement as WrapperStatement ;
9
+ use Doctrine \DBAL \Tests \FunctionalTestCase ;
10
+ use Doctrine \DBAL \Tests \TestUtil ;
11
+ use Error ;
12
+ use PHPUnit \Framework \Attributes \RequiresPhpExtension ;
13
+ use ReflectionProperty ;
14
+
15
+ #[RequiresPhpExtension('mysqli ' )]
16
+ class StatementTest extends FunctionalTestCase
17
+ {
18
+ protected function setUp (): void
19
+ {
20
+ parent ::setUp ();
21
+
22
+ if (TestUtil::isDriverOneOf ('mysqli ' )) {
23
+ return ;
24
+ }
25
+
26
+ self ::markTestSkipped ('This test requires the mysqli driver. ' );
27
+ }
28
+
29
+ public function testStatementsAreDeallocatedProperly (): void
30
+ {
31
+ $ statement = $ this ->connection ->prepare ('SELECT 1 ' );
32
+
33
+ $ property = new ReflectionProperty (WrapperStatement::class, 'stmt ' );
34
+ $ driverStatement = $ property ->getValue ($ statement );
35
+
36
+ $ mysqliProperty = new ReflectionProperty (Statement::class, 'stmt ' );
37
+ $ mysqliStatement = $ mysqliProperty ->getValue ($ driverStatement );
38
+
39
+ unset($ statement , $ driverStatement );
40
+
41
+ $ this ->expectException (Error::class);
42
+ $ this ->expectExceptionMessage ('mysqli_stmt object is already closed ' );
43
+
44
+ $ mysqliStatement ->execute ();
45
+ }
46
+ }
You can’t perform that action at this time.
0 commit comments