Skip to content

Commit 6311581

Browse files
committed
Fix bug #73948
If PREG_UNMATCHED_AS_NULL is used, make sure that unmatched capturing groups at the end are also set to null, rather than just those in the middle.
1 parent c1095cf commit 6311581

File tree

6 files changed

+136
-9
lines changed

6 files changed

+136
-9
lines changed

NEWS

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,11 @@ PHP NEWS
6767
(Sammy Kaye Powers)
6868

6969
- PCRE:
70+
. Implemented FR #77094 (Support flags in preg_replace_callback). (Nikita)
7071
. Fixed bug #72685 (Repeated UTF-8 validation of same string in UTF-8 mode).
7172
(Nikita)
72-
. Implemented FR #77094 (Support flags in preg_replace_callback). (Nikita)
73+
. Fixed bug #73948 (Preg_match_all should return NULLs on trailing optional
74+
capture groups).
7375

7476
- PDO_OCI:
7577
. Support Oracle Database tracing attributes ACTION, MODULE,

UPGRADING

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ PHP 7.4 UPGRADE NOTES
5050
function does not throw, so explicitly checking it is not necessary.
5151
RFC: http://php.net/manual/de/function.openssl-random-pseudo-bytes.php
5252

53+
- PCRE:
54+
. When PREG_UNMATCHED_AS_NULL mode is used, trailing unmatched capturing
55+
groups will now also be set to null (or [null, -1] if offset capture is
56+
enabled). This means that the size of the $matches will always be the same.
57+
5358
- PEAR:
5459
. Installation of PEAR (including PECL) is no longer enabled by default. It
5560
can be explicitly enabled using --with-pear. This option is deprecated and

ext/pcre/php_pcre.c

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ static inline void populate_match_value(
10281028

10291029
static void populate_subpat_array(
10301030
zval *subpats, char *subject, PCRE2_SIZE *offsets, zend_string **subpat_names,
1031-
int count, const PCRE2_SPTR mark, zend_long flags) {
1031+
uint32_t num_subpats, int count, const PCRE2_SPTR mark, zend_long flags) {
10321032
zend_bool offset_capture = (flags & PREG_OFFSET_CAPTURE) != 0;
10331033
zend_bool unmatched_as_null = (flags & PREG_UNMATCHED_AS_NULL) != 0;
10341034
zval val;
@@ -1040,6 +1040,11 @@ static void populate_subpat_array(
10401040
offsets[(i<<1)+1] - offsets[i<<1],
10411041
offsets[i<<1], subpat_names[i], unmatched_as_null);
10421042
}
1043+
if (unmatched_as_null) {
1044+
for (i = count; i < num_subpats; i++) {
1045+
add_offset_pair(subpats, NULL, 0, PCRE2_UNSET, subpat_names[i], 1);
1046+
}
1047+
}
10431048
} else {
10441049
for (i = 0; i < count; i++) {
10451050
populate_match_value(
@@ -1050,6 +1055,15 @@ static void populate_subpat_array(
10501055
}
10511056
zend_hash_next_index_insert(Z_ARRVAL_P(subpats), &val);
10521057
}
1058+
if (unmatched_as_null) {
1059+
for (i = count; i < num_subpats; i++) {
1060+
ZVAL_NULL(&val);
1061+
if (subpat_names[i]) {
1062+
zend_hash_update(Z_ARRVAL_P(subpats), subpat_names[i], &val);
1063+
}
1064+
zend_hash_next_index_insert(Z_ARRVAL_P(subpats), &val);
1065+
}
1066+
}
10531067
}
10541068
} else {
10551069
if (offset_capture) {
@@ -1058,12 +1072,22 @@ static void populate_subpat_array(
10581072
offsets[(i<<1)+1] - offsets[i<<1],
10591073
offsets[i<<1], NULL, unmatched_as_null);
10601074
}
1075+
if (unmatched_as_null) {
1076+
for (i = count; i < num_subpats; i++) {
1077+
add_offset_pair(subpats, NULL, 0, PCRE2_UNSET, NULL, 1);
1078+
}
1079+
}
10611080
} else {
10621081
for (i = 0; i < count; i++) {
10631082
populate_match_value(
10641083
&val, subject, offsets[2*i], offsets[2*i+1], unmatched_as_null);
10651084
zend_hash_next_index_insert(Z_ARRVAL_P(subpats), &val);
10661085
}
1086+
if (unmatched_as_null) {
1087+
for (i = count; i < num_subpats; i++) {
1088+
add_next_index_null(subpats);
1089+
}
1090+
}
10671091
}
10681092
}
10691093
/* Add MARK, if available */
@@ -1306,15 +1330,16 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str,
13061330
array_init_size(&result_set, count + (mark ? 1 : 0));
13071331
mark = pcre2_get_mark(match_data);
13081332
populate_subpat_array(
1309-
&result_set, subject, offsets, subpat_names, count, mark, flags);
1333+
&result_set, subject, offsets, subpat_names,
1334+
num_subpats, count, mark, flags);
13101335
/* And add it to the output array */
13111336
zend_hash_next_index_insert(Z_ARRVAL_P(subpats), &result_set);
13121337
}
13131338
} else { /* single pattern matching */
13141339
/* For each subpattern, insert it into the subpatterns array. */
13151340
mark = pcre2_get_mark(match_data);
13161341
populate_subpat_array(
1317-
subpats, subject, offsets, subpat_names, count, mark, flags);
1342+
subpats, subject, offsets, subpat_names, num_subpats, count, mark, flags);
13181343
break;
13191344
}
13201345
}
@@ -1473,14 +1498,14 @@ static int preg_get_backref(char **str, int *backref)
14731498

14741499
/* {{{ preg_do_repl_func
14751500
*/
1476-
static zend_string *preg_do_repl_func(zend_fcall_info *fci, zend_fcall_info_cache *fcc, char *subject, PCRE2_SIZE *offsets, zend_string **subpat_names, int count, const PCRE2_SPTR mark, zend_long flags)
1501+
static zend_string *preg_do_repl_func(zend_fcall_info *fci, zend_fcall_info_cache *fcc, char *subject, PCRE2_SIZE *offsets, zend_string **subpat_names, uint32_t num_subpats, int count, const PCRE2_SPTR mark, zend_long flags)
14771502
{
14781503
zend_string *result_str;
14791504
zval retval; /* Function return value */
14801505
zval arg; /* Argument to pass to function */
14811506

14821507
array_init_size(&arg, count + (mark ? 1 : 0));
1483-
populate_subpat_array(&arg, subject, offsets, subpat_names, count, mark, flags);
1508+
populate_subpat_array(&arg, subject, offsets, subpat_names, num_subpats, count, mark, flags);
14841509

14851510
fci->retval = &retval;
14861511
fci->param_count = 1;
@@ -1878,7 +1903,8 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin
18781903
new_len = result_len + offsets[0] - start_offset; /* part before the match */
18791904

18801905
/* Use custom function to get replacement string and its length. */
1881-
eval_result = preg_do_repl_func(fci, fcc, subject, offsets, subpat_names, count,
1906+
eval_result = preg_do_repl_func(
1907+
fci, fcc, subject, offsets, subpat_names, num_subpats, count,
18821908
pcre2_get_mark(match_data), flags);
18831909

18841910
ZEND_ASSERT(eval_result);

ext/pcre/tests/bug61780_1.phpt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ array (
145145
0 =>
146146
array (
147147
0 => '1',
148+
1 => NULL,
149+
2 => NULL,
148150
),
149151
1 =>
150152
array (
@@ -156,10 +158,13 @@ array (
156158
array (
157159
0 => '45',
158160
1 => '4',
161+
2 => NULL,
159162
),
160163
3 =>
161164
array (
162165
0 => '6',
166+
1 => NULL,
167+
2 => NULL,
163168
),
164169
)
165170

@@ -171,6 +176,16 @@ array (
171176
0 => '1',
172177
1 => 0,
173178
),
179+
1 =>
180+
array (
181+
0 => NULL,
182+
1 => -1,
183+
),
184+
2 =>
185+
array (
186+
0 => NULL,
187+
1 => -1,
188+
),
174189
),
175190
1 =>
176191
array (
@@ -202,6 +217,11 @@ array (
202217
0 => '4',
203218
1 => 3,
204219
),
220+
2 =>
221+
array (
222+
0 => NULL,
223+
1 => -1,
224+
),
205225
),
206226
3 =>
207227
array (
@@ -210,5 +230,15 @@ array (
210230
0 => '6',
211231
1 => 5,
212232
),
233+
1 =>
234+
array (
235+
0 => NULL,
236+
1 => -1,
237+
),
238+
2 =>
239+
array (
240+
0 => NULL,
241+
1 => -1,
242+
),
213243
),
214244
)

ext/pcre/tests/bug61780_2.phpt

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ array (
217217
0 =>
218218
array (
219219
0 => '1',
220+
'a' => NULL,
221+
1 => NULL,
222+
'b' => NULL,
223+
2 => NULL,
220224
),
221225
1 =>
222226
array (
@@ -231,10 +235,16 @@ array (
231235
0 => '45',
232236
'a' => '4',
233237
1 => '4',
238+
'b' => NULL,
239+
2 => NULL,
234240
),
235241
3 =>
236242
array (
237243
0 => '6',
244+
'a' => NULL,
245+
1 => NULL,
246+
'b' => NULL,
247+
2 => NULL,
238248
),
239249
)
240250

@@ -246,6 +256,26 @@ array (
246256
0 => '1',
247257
1 => 0,
248258
),
259+
'a' =>
260+
array (
261+
0 => NULL,
262+
1 => -1,
263+
),
264+
1 =>
265+
array (
266+
0 => NULL,
267+
1 => -1,
268+
),
269+
'b' =>
270+
array (
271+
0 => NULL,
272+
1 => -1,
273+
),
274+
2 =>
275+
array (
276+
0 => NULL,
277+
1 => -1,
278+
),
249279
),
250280
1 =>
251281
array (
@@ -292,6 +322,16 @@ array (
292322
0 => '4',
293323
1 => 3,
294324
),
325+
'b' =>
326+
array (
327+
0 => NULL,
328+
1 => -1,
329+
),
330+
2 =>
331+
array (
332+
0 => NULL,
333+
1 => -1,
334+
),
295335
),
296336
3 =>
297337
array (
@@ -300,5 +340,25 @@ array (
300340
0 => '6',
301341
1 => 5,
302342
),
343+
'a' =>
344+
array (
345+
0 => NULL,
346+
1 => -1,
347+
),
348+
1 =>
349+
array (
350+
0 => NULL,
351+
1 => -1,
352+
),
353+
'b' =>
354+
array (
355+
0 => NULL,
356+
1 => -1,
357+
),
358+
2 =>
359+
array (
360+
0 => NULL,
361+
1 => -1,
362+
),
303363
),
304364
)

ext/pcre/tests/preg_replace_callback_flags.phpt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,13 @@ array(1) {
9393
}
9494
string(3) "abc"
9595

96-
array(2) {
96+
array(3) {
9797
[0]=>
9898
string(1) "a"
9999
[1]=>
100100
string(1) "a"
101+
[2]=>
102+
NULL
101103
}
102104
array(3) {
103105
[0]=>
@@ -109,11 +111,13 @@ array(3) {
109111
}
110112
string(3) "abc"
111113

112-
array(2) {
114+
array(3) {
113115
[0]=>
114116
string(1) "a"
115117
[1]=>
116118
string(1) "a"
119+
[2]=>
120+
NULL
117121
}
118122
array(3) {
119123
[0]=>

0 commit comments

Comments
 (0)