File tree Expand file tree Collapse file tree 3 files changed +61
-3
lines changed
tests/Functional/Driver/Mysqli Expand file tree Collapse file tree 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
3939 /**
4040 * @internal The result can be only instantiated by its driver connection or statement.
4141 *
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+ *
4247 * @throws Exception
4348 */
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+ ) {
4653 $ meta = $ statement ->result_metadata ();
4754 $ this ->hasColumns = $ meta !== false ;
4855 $ 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)
4949 $ this ->boundValues = array_fill (1 , $ paramCount , null );
5050 }
5151
52+ public function __destruct ()
53+ {
54+ @$ this ->stmt ->close ();
55+ }
56+
5257 public function bindValue (int |string $ param , mixed $ value , ParameterType $ type ): void
5358 {
5459 assert (is_int ($ param ));
@@ -72,7 +77,7 @@ public function execute(): Result
7277 throw StatementError::upcast ($ e );
7378 }
7479
75- return new Result ($ this ->stmt );
80+ return new Result ($ this ->stmt , $ this );
7681 }
7782
7883 /**
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