Skip to content

Commit 3a51530

Browse files
committed
Fixed bug #79257
Replace an existing entry for a given name only if we have a match.
1 parent 93b183e commit 3a51530

File tree

3 files changed

+237
-5
lines changed

3 files changed

+237
-5
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ PHP NEWS
2121
. Fixed bug #79188 (Memory corruption in preg_replace/preg_replace_callback
2222
and unicode). (Nikita)
2323
. Fixed bug #79241 (Segmentation fault on preg_match()). (Nikita)
24+
. Fixed bug #79257 (Duplicate named groups (?J) prefer last alternative even
25+
if not matched). (Nikita)
2426

2527
- Standard:
2628
. Fixed bug #79254 (getenv() w/o arguments not showing changes). (cmb)

ext/pcre/php_pcre.c

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,20 @@ static inline void populate_match_value(
995995
}
996996
}
997997

998+
static inline void add_named(
999+
zval *subpats, zend_string *name, zval *val, zend_bool unmatched) {
1000+
/* If the DUPNAMES option is used, multiple subpatterns might have the same name.
1001+
* In this case we want to preserve the one that actually has a value. */
1002+
if (!unmatched) {
1003+
zend_hash_update(Z_ARRVAL_P(subpats), name, val);
1004+
} else {
1005+
if (!zend_hash_add(Z_ARRVAL_P(subpats), name, val)) {
1006+
return;
1007+
}
1008+
}
1009+
Z_TRY_ADDREF_P(val);
1010+
}
1011+
9981012
/* {{{ add_offset_pair */
9991013
static inline void add_offset_pair(
10001014
zval *result, const char *subject, PCRE2_SIZE start_offset, PCRE2_SIZE end_offset,
@@ -1023,8 +1037,7 @@ static inline void add_offset_pair(
10231037
}
10241038

10251039
if (name) {
1026-
Z_ADDREF(match_pair);
1027-
zend_hash_update(Z_ARRVAL_P(result), name, &match_pair);
1040+
add_named(result, name, &match_pair, start_offset == PCRE2_UNSET);
10281041
}
10291042
zend_hash_next_index_insert(Z_ARRVAL_P(result), &match_pair);
10301043
}
@@ -1054,16 +1067,15 @@ static void populate_subpat_array(
10541067
populate_match_value(
10551068
&val, subject, offsets[2*i], offsets[2*i+1], unmatched_as_null);
10561069
if (subpat_names[i]) {
1057-
Z_TRY_ADDREF(val);
1058-
zend_hash_update(Z_ARRVAL_P(subpats), subpat_names[i], &val);
1070+
add_named(subpats, subpat_names[i], &val, offsets[2*i] == PCRE2_UNSET);
10591071
}
10601072
zend_hash_next_index_insert(Z_ARRVAL_P(subpats), &val);
10611073
}
10621074
if (unmatched_as_null) {
10631075
for (i = count; i < num_subpats; i++) {
10641076
ZVAL_NULL(&val);
10651077
if (subpat_names[i]) {
1066-
zend_hash_update(Z_ARRVAL_P(subpats), subpat_names[i], &val);
1078+
zend_hash_add(Z_ARRVAL_P(subpats), subpat_names[i], &val);
10671079
}
10681080
zend_hash_next_index_insert(Z_ARRVAL_P(subpats), &val);
10691081
}

ext/pcre/tests/bug79257.phpt

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
--TEST--
2+
Bug #79257: Duplicate named groups (?J) prefer last alternative even if not matched
3+
--FILE--
4+
<?php
5+
6+
preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))/', 'foo', $matches);
7+
var_dump($matches);
8+
preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))/', 'foo', $matches,
9+
PREG_UNMATCHED_AS_NULL);
10+
var_dump($matches);
11+
preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))/', 'foo', $matches,
12+
PREG_OFFSET_CAPTURE);
13+
var_dump($matches);
14+
preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))/', 'foo', $matches,
15+
PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE);
16+
var_dump($matches);
17+
18+
preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))(?<h>baz)/', 'foobaz', $matches);
19+
var_dump($matches);
20+
preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))(?<h>baz)/', 'foobaz', $matches,
21+
PREG_UNMATCHED_AS_NULL);
22+
var_dump($matches);
23+
preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))(?<h>baz)/', 'foobaz', $matches,
24+
PREG_OFFSET_CAPTURE);
25+
var_dump($matches);
26+
preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))(?<h>baz)/', 'foobaz', $matches,
27+
PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE);
28+
var_dump($matches);
29+
30+
?>
31+
--EXPECT--
32+
array(3) {
33+
[0]=>
34+
string(3) "foo"
35+
["g"]=>
36+
string(3) "foo"
37+
[1]=>
38+
string(3) "foo"
39+
}
40+
array(4) {
41+
[0]=>
42+
string(3) "foo"
43+
["g"]=>
44+
string(3) "foo"
45+
[1]=>
46+
string(3) "foo"
47+
[2]=>
48+
NULL
49+
}
50+
array(3) {
51+
[0]=>
52+
array(2) {
53+
[0]=>
54+
string(3) "foo"
55+
[1]=>
56+
int(0)
57+
}
58+
["g"]=>
59+
array(2) {
60+
[0]=>
61+
string(3) "foo"
62+
[1]=>
63+
int(0)
64+
}
65+
[1]=>
66+
array(2) {
67+
[0]=>
68+
string(3) "foo"
69+
[1]=>
70+
int(0)
71+
}
72+
}
73+
array(4) {
74+
[0]=>
75+
array(2) {
76+
[0]=>
77+
string(3) "foo"
78+
[1]=>
79+
int(0)
80+
}
81+
["g"]=>
82+
array(2) {
83+
[0]=>
84+
string(3) "foo"
85+
[1]=>
86+
int(0)
87+
}
88+
[1]=>
89+
array(2) {
90+
[0]=>
91+
string(3) "foo"
92+
[1]=>
93+
int(0)
94+
}
95+
[2]=>
96+
array(2) {
97+
[0]=>
98+
NULL
99+
[1]=>
100+
int(-1)
101+
}
102+
}
103+
array(6) {
104+
[0]=>
105+
string(6) "foobaz"
106+
["g"]=>
107+
string(3) "foo"
108+
[1]=>
109+
string(3) "foo"
110+
[2]=>
111+
string(0) ""
112+
["h"]=>
113+
string(3) "baz"
114+
[3]=>
115+
string(3) "baz"
116+
}
117+
array(6) {
118+
[0]=>
119+
string(6) "foobaz"
120+
["g"]=>
121+
string(3) "foo"
122+
[1]=>
123+
string(3) "foo"
124+
[2]=>
125+
NULL
126+
["h"]=>
127+
string(3) "baz"
128+
[3]=>
129+
string(3) "baz"
130+
}
131+
array(6) {
132+
[0]=>
133+
array(2) {
134+
[0]=>
135+
string(6) "foobaz"
136+
[1]=>
137+
int(0)
138+
}
139+
["g"]=>
140+
array(2) {
141+
[0]=>
142+
string(3) "foo"
143+
[1]=>
144+
int(0)
145+
}
146+
[1]=>
147+
array(2) {
148+
[0]=>
149+
string(3) "foo"
150+
[1]=>
151+
int(0)
152+
}
153+
[2]=>
154+
array(2) {
155+
[0]=>
156+
string(0) ""
157+
[1]=>
158+
int(-1)
159+
}
160+
["h"]=>
161+
array(2) {
162+
[0]=>
163+
string(3) "baz"
164+
[1]=>
165+
int(3)
166+
}
167+
[3]=>
168+
array(2) {
169+
[0]=>
170+
string(3) "baz"
171+
[1]=>
172+
int(3)
173+
}
174+
}
175+
array(6) {
176+
[0]=>
177+
array(2) {
178+
[0]=>
179+
string(6) "foobaz"
180+
[1]=>
181+
int(0)
182+
}
183+
["g"]=>
184+
array(2) {
185+
[0]=>
186+
string(3) "foo"
187+
[1]=>
188+
int(0)
189+
}
190+
[1]=>
191+
array(2) {
192+
[0]=>
193+
string(3) "foo"
194+
[1]=>
195+
int(0)
196+
}
197+
[2]=>
198+
array(2) {
199+
[0]=>
200+
NULL
201+
[1]=>
202+
int(-1)
203+
}
204+
["h"]=>
205+
array(2) {
206+
[0]=>
207+
string(3) "baz"
208+
[1]=>
209+
int(3)
210+
}
211+
[3]=>
212+
array(2) {
213+
[0]=>
214+
string(3) "baz"
215+
[1]=>
216+
int(3)
217+
}
218+
}

0 commit comments

Comments
 (0)