Skip to content

Commit eda8ce7

Browse files
committed
Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3: Add NEWS entries Fix crash in firebird statement dtor ext/pdo: Fix memory leak if GC needs to free PDO Statement Fix GHA config yml error
2 parents d35904a + e6d917e commit eda8ce7

File tree

5 files changed

+165
-13
lines changed

5 files changed

+165
-13
lines changed

.github/workflows/nightly.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ jobs:
427427
-d zend_extension=opcache.so
428428
-d opcache.enable_cli=1
429429
- uses: codecov/codecov-action@v4
430-
if: !cancelled()
430+
if: ${{ !cancelled() }}
431431
with:
432432
fail_ci_if_error: true
433433
token: ${{ secrets.CODECOV_TOKEN }}
@@ -500,7 +500,7 @@ jobs:
500500
echo opcache.jit_hot_side_exit=1 >> /etc/php.d/opcache.ini
501501
php -v
502502
- name: Test AMPHP
503-
if: !cancelled()
503+
if: ${{ !cancelled() }}
504504
run: |
505505
repositories="amp cache dns file http parallel parser pipeline process serialization socket sync websocket-client websocket-server"
506506
X=0
@@ -518,7 +518,7 @@ jobs:
518518
done
519519
exit $X
520520
- name: Test Laravel
521-
if: !cancelled()
521+
if: ${{ !cancelled() }}
522522
run: |
523523
git clone https://github.com/laravel/framework.git --branch=master --depth=1
524524
cd framework
@@ -531,7 +531,7 @@ jobs:
531531
exit 1
532532
fi
533533
- name: Test ReactPHP
534-
if: !cancelled()
534+
if: ${{ !cancelled() }}
535535
run: |
536536
repositories="async cache child-process datagram dns event-loop promise promise-stream promise-timer stream"
537537
X=0
@@ -549,7 +549,7 @@ jobs:
549549
done
550550
exit $X
551551
- name: Test Revolt PHP
552-
if: !cancelled()
552+
if: ${{ !cancelled() }}
553553
run: |
554554
git clone https://github.com/revoltphp/event-loop.git --depth=1
555555
cd event-loop
@@ -560,7 +560,7 @@ jobs:
560560
exit 1
561561
fi
562562
- name: Test Symfony
563-
if: !cancelled() && !inputs.skip_symfony
563+
if: ${{ !cancelled() && !inputs.skip_symfony }}
564564
run: |
565565
git clone https://github.com/symfony/symfony.git --depth=1
566566
cd symfony
@@ -581,7 +581,7 @@ jobs:
581581
done
582582
exit $X
583583
- name: Test PHPUnit
584-
if: !cancelled()
584+
if: ${{ !cancelled() }}
585585
run: |
586586
git clone https://github.com/sebastianbergmann/phpunit.git --branch=main --depth=1
587587
cd phpunit
@@ -592,15 +592,15 @@ jobs:
592592
exit 1
593593
fi
594594
- name: 'Symfony Preloading'
595-
if: !cancelled() && !inputs.skip_symfony
595+
if: ${{ !cancelled() && !inputs.skip_symfony }}
596596
run: |
597597
php /usr/bin/composer create-project symfony/symfony-demo symfony_demo --no-progress --ignore-platform-reqs
598598
cd symfony_demo
599599
git rev-parse HEAD
600600
sed -i 's/PHP_SAPI/"cli-server"/g' var/cache/dev/App_KernelDevDebugContainer.preload.php
601601
php -d opcache.preload=var/cache/dev/App_KernelDevDebugContainer.preload.php public/index.php
602602
- name: Test Wordpress
603-
if: !cancelled() && !inputs.skip_wordpress
603+
if: ${{ !cancelled() && !inputs.skip_wordpress }}
604604
run: |
605605
git clone https://github.com/WordPress/wordpress-develop.git wordpress --depth=1
606606
cd wordpress

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ PHP NEWS
6565
the cpu mask argument with entries type different than int/string.
6666
(David Carlier)
6767

68+
- PDO:
69+
. Fixed a memory leak when the GC is used to free a PDOStatment. (Girgias)
70+
. Fixed a crash in the PDO Firebird Statement destructor. (nielsdos)
71+
6872
- PgSql:
6973
. Fixed build failure when the constant PGRES_TUPLES_CHUNK is not present
7074
in the system. (chschneider)

ext/pdo/pdo_stmt.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2074,8 +2074,11 @@ static zend_function *dbstmt_method_get(zend_object **object_pp, zend_string *me
20742074
static HashTable *dbstmt_get_gc(zend_object *object, zval **gc_data, int *gc_count)
20752075
{
20762076
pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(object);
2077-
*gc_data = &stmt->fetch.into;
2078-
*gc_count = 1;
2077+
2078+
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
2079+
zend_get_gc_buffer_add_zval(gc_buffer, &stmt->database_object_handle);
2080+
zend_get_gc_buffer_add_zval(gc_buffer, &stmt->fetch.into);
2081+
zend_get_gc_buffer_use(gc_buffer, gc_data, gc_count);
20792082

20802083
/**
20812084
* If there are no dynamic properties and the default property is 1 (that is, there is only one property
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
--TEST--
2+
PDO Common: Cyclic PDOStatement child class
3+
--EXTENSIONS--
4+
pdo
5+
--SKIPIF--
6+
<?php
7+
$dir = getenv('REDIR_TEST_DIR');
8+
if (false == $dir) die('skip no driver');
9+
require_once $dir . 'pdo_test.inc';
10+
PDOTest::skip();
11+
?>
12+
--FILE--
13+
<?php
14+
if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.__DIR__ . '/../../pdo/tests/');
15+
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
16+
17+
class Ref {
18+
public CyclicStatement $stmt;
19+
}
20+
21+
class CyclicStatement extends PDOStatement {
22+
protected function __construct(public Ref $ref) {}
23+
}
24+
25+
class TestRow {
26+
public $id;
27+
public $val;
28+
public $val2;
29+
30+
public function __construct(public string $arg) {}
31+
}
32+
33+
$db = PDOTest::factory();
34+
$db->exec('CREATE TABLE pdo_stmt_cyclic_ref(id INT NOT NULL PRIMARY KEY, val VARCHAR(10), val2 VARCHAR(10))');
35+
$db->exec("INSERT INTO pdo_stmt_cyclic_ref VALUES(1, 'A', 'AA')");
36+
$db->exec("INSERT INTO pdo_stmt_cyclic_ref VALUES(2, 'B', 'BB')");
37+
$db->exec("INSERT INTO pdo_stmt_cyclic_ref VALUES(3, 'C', 'CC')");
38+
39+
$db->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['CyclicStatement', [new Ref]]);
40+
41+
echo "Column fetch:\n";
42+
$stmt = $db->query('SELECT id, val2, val FROM pdo_stmt_cyclic_ref');
43+
$stmt->ref->stmt = $stmt;
44+
$stmt->setFetchMode(PDO::FETCH_COLUMN, 2);
45+
foreach($stmt as $obj) {
46+
var_dump($obj);
47+
}
48+
49+
echo "Class fetch:\n";
50+
$stmt = $db->query('SELECT id, val2, val FROM pdo_stmt_cyclic_ref');
51+
$stmt->ref->stmt = $stmt;
52+
$stmt->setFetchMode(PDO::FETCH_CLASS, 'TestRow', ['Hello world']);
53+
foreach($stmt as $obj) {
54+
var_dump($obj);
55+
}
56+
57+
echo "Fetch into:\n";
58+
$stmt = $db->query('SELECT id, val2, val FROM pdo_stmt_cyclic_ref');
59+
$stmt->ref->stmt = $stmt;
60+
$stmt->setFetchMode(PDO::FETCH_INTO, new TestRow('I am being fetch into'));
61+
foreach($stmt as $obj) {
62+
var_dump($obj);
63+
}
64+
65+
?>
66+
--CLEAN--
67+
<?php
68+
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
69+
$db = PDOTest::factory();
70+
PDOTest::dropTableIfExists($db, "pdo_stmt_cyclic_ref");
71+
?>
72+
--EXPECTF--
73+
Column fetch:
74+
string(1) "A"
75+
string(1) "B"
76+
string(1) "C"
77+
Class fetch:
78+
object(TestRow)#%d (4) {
79+
["id"]=>
80+
string(1) "1"
81+
["val"]=>
82+
string(1) "A"
83+
["val2"]=>
84+
string(2) "AA"
85+
["arg"]=>
86+
string(11) "Hello world"
87+
}
88+
object(TestRow)#%d (4) {
89+
["id"]=>
90+
string(1) "2"
91+
["val"]=>
92+
string(1) "B"
93+
["val2"]=>
94+
string(2) "BB"
95+
["arg"]=>
96+
string(11) "Hello world"
97+
}
98+
object(TestRow)#%d (4) {
99+
["id"]=>
100+
string(1) "3"
101+
["val"]=>
102+
string(1) "C"
103+
["val2"]=>
104+
string(2) "CC"
105+
["arg"]=>
106+
string(11) "Hello world"
107+
}
108+
Fetch into:
109+
object(TestRow)#4 (4) {
110+
["id"]=>
111+
string(1) "1"
112+
["val"]=>
113+
string(1) "A"
114+
["val2"]=>
115+
string(2) "AA"
116+
["arg"]=>
117+
string(21) "I am being fetch into"
118+
}
119+
object(TestRow)#4 (4) {
120+
["id"]=>
121+
string(1) "2"
122+
["val"]=>
123+
string(1) "B"
124+
["val2"]=>
125+
string(2) "BB"
126+
["arg"]=>
127+
string(21) "I am being fetch into"
128+
}
129+
object(TestRow)#4 (4) {
130+
["id"]=>
131+
string(1) "3"
132+
["val"]=>
133+
string(1) "C"
134+
["val2"]=>
135+
string(2) "CC"
136+
["arg"]=>
137+
string(21) "I am being fetch into"
138+
}

ext/pdo_firebird/firebird_statement.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,15 @@ static int pdo_firebird_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */
158158
pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;
159159
int result = 1;
160160

161-
/* release the statement */
162-
if (isc_dsql_free_statement(S->H->isc_status, &S->stmt, DSQL_drop)) {
161+
/* TODO: for master, move this check to a separate function shared between pdo drivers.
162+
* pdo_pgsql and pdo_mysql do this exact same thing */
163+
bool server_obj_usable = !Z_ISUNDEF(stmt->database_object_handle)
164+
&& IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)])
165+
&& !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED);
166+
167+
/* release the statement.
168+
* Note: if the server object is already gone then the statement was closed already as well. */
169+
if (server_obj_usable && isc_dsql_free_statement(S->H->isc_status, &S->stmt, DSQL_drop)) {
163170
php_firebird_error_stmt(stmt);
164171
result = 0;
165172
}

0 commit comments

Comments
 (0)