summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-02-11 17:30:59 +0100
committerNikita Popov <nikita.ppv@gmail.com>2020-02-11 17:31:48 +0100
commit3a515309631fcacd80ee1f6e247965a0c4626786 (patch)
treeadddb23e8f7c2d9032648c2f084cdf51658bb893
parent93b183ed551999e8c3f80cff1cc40c2be5f33033 (diff)
downloadphp-git-3a515309631fcacd80ee1f6e247965a0c4626786.tar.gz
Fixed bug #79257
Replace an existing entry for a given name only if we have a match.
-rw-r--r--NEWS2
-rw-r--r--ext/pcre/php_pcre.c22
-rw-r--r--ext/pcre/tests/bug79257.phpt218
3 files changed, 237 insertions, 5 deletions
diff --git a/NEWS b/NEWS
index 1c1b11382a..d8467ac018 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,8 @@ PHP NEWS
. Fixed bug #79188 (Memory corruption in preg_replace/preg_replace_callback
and unicode). (Nikita)
. Fixed bug #79241 (Segmentation fault on preg_match()). (Nikita)
+ . Fixed bug #79257 (Duplicate named groups (?J) prefer last alternative even
+ if not matched). (Nikita)
- Standard:
. Fixed bug #79254 (getenv() w/o arguments not showing changes). (cmb)
diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c
index c50bd2fba2..39896bb07b 100644
--- a/ext/pcre/php_pcre.c
+++ b/ext/pcre/php_pcre.c
@@ -995,6 +995,20 @@ static inline void populate_match_value(
}
}
+static inline void add_named(
+ zval *subpats, zend_string *name, zval *val, zend_bool unmatched) {
+ /* If the DUPNAMES option is used, multiple subpatterns might have the same name.
+ * In this case we want to preserve the one that actually has a value. */
+ if (!unmatched) {
+ zend_hash_update(Z_ARRVAL_P(subpats), name, val);
+ } else {
+ if (!zend_hash_add(Z_ARRVAL_P(subpats), name, val)) {
+ return;
+ }
+ }
+ Z_TRY_ADDREF_P(val);
+}
+
/* {{{ add_offset_pair */
static inline void add_offset_pair(
zval *result, const char *subject, PCRE2_SIZE start_offset, PCRE2_SIZE end_offset,
@@ -1023,8 +1037,7 @@ static inline void add_offset_pair(
}
if (name) {
- Z_ADDREF(match_pair);
- zend_hash_update(Z_ARRVAL_P(result), name, &match_pair);
+ add_named(result, name, &match_pair, start_offset == PCRE2_UNSET);
}
zend_hash_next_index_insert(Z_ARRVAL_P(result), &match_pair);
}
@@ -1054,8 +1067,7 @@ static void populate_subpat_array(
populate_match_value(
&val, subject, offsets[2*i], offsets[2*i+1], unmatched_as_null);
if (subpat_names[i]) {
- Z_TRY_ADDREF(val);
- zend_hash_update(Z_ARRVAL_P(subpats), subpat_names[i], &val);
+ add_named(subpats, subpat_names[i], &val, offsets[2*i] == PCRE2_UNSET);
}
zend_hash_next_index_insert(Z_ARRVAL_P(subpats), &val);
}
@@ -1063,7 +1075,7 @@ static void populate_subpat_array(
for (i = count; i < num_subpats; i++) {
ZVAL_NULL(&val);
if (subpat_names[i]) {
- zend_hash_update(Z_ARRVAL_P(subpats), subpat_names[i], &val);
+ zend_hash_add(Z_ARRVAL_P(subpats), subpat_names[i], &val);
}
zend_hash_next_index_insert(Z_ARRVAL_P(subpats), &val);
}
diff --git a/ext/pcre/tests/bug79257.phpt b/ext/pcre/tests/bug79257.phpt
new file mode 100644
index 0000000000..b52a0d6574
--- /dev/null
+++ b/ext/pcre/tests/bug79257.phpt
@@ -0,0 +1,218 @@
+--TEST--
+Bug #79257: Duplicate named groups (?J) prefer last alternative even if not matched
+--FILE--
+<?php
+
+preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))/', 'foo', $matches);
+var_dump($matches);
+preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))/', 'foo', $matches,
+ PREG_UNMATCHED_AS_NULL);
+var_dump($matches);
+preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))/', 'foo', $matches,
+ PREG_OFFSET_CAPTURE);
+var_dump($matches);
+preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))/', 'foo', $matches,
+ PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE);
+var_dump($matches);
+
+preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))(?<h>baz)/', 'foobaz', $matches);
+var_dump($matches);
+preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))(?<h>baz)/', 'foobaz', $matches,
+ PREG_UNMATCHED_AS_NULL);
+var_dump($matches);
+preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))(?<h>baz)/', 'foobaz', $matches,
+ PREG_OFFSET_CAPTURE);
+var_dump($matches);
+preg_match('/(?J)(?:(?<g>foo)|(?<g>bar))(?<h>baz)/', 'foobaz', $matches,
+ PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE);
+var_dump($matches);
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ string(3) "foo"
+ ["g"]=>
+ string(3) "foo"
+ [1]=>
+ string(3) "foo"
+}
+array(4) {
+ [0]=>
+ string(3) "foo"
+ ["g"]=>
+ string(3) "foo"
+ [1]=>
+ string(3) "foo"
+ [2]=>
+ NULL
+}
+array(3) {
+ [0]=>
+ array(2) {
+ [0]=>
+ string(3) "foo"
+ [1]=>
+ int(0)
+ }
+ ["g"]=>
+ array(2) {
+ [0]=>
+ string(3) "foo"
+ [1]=>
+ int(0)
+ }
+ [1]=>
+ array(2) {
+ [0]=>
+ string(3) "foo"
+ [1]=>
+ int(0)
+ }
+}
+array(4) {
+ [0]=>
+ array(2) {
+ [0]=>
+ string(3) "foo"
+ [1]=>
+ int(0)
+ }
+ ["g"]=>
+ array(2) {
+ [0]=>
+ string(3) "foo"
+ [1]=>
+ int(0)
+ }
+ [1]=>
+ array(2) {
+ [0]=>
+ string(3) "foo"
+ [1]=>
+ int(0)
+ }
+ [2]=>
+ array(2) {
+ [0]=>
+ NULL
+ [1]=>
+ int(-1)
+ }
+}
+array(6) {
+ [0]=>
+ string(6) "foobaz"
+ ["g"]=>
+ string(3) "foo"
+ [1]=>
+ string(3) "foo"
+ [2]=>
+ string(0) ""
+ ["h"]=>
+ string(3) "baz"
+ [3]=>
+ string(3) "baz"
+}
+array(6) {
+ [0]=>
+ string(6) "foobaz"
+ ["g"]=>
+ string(3) "foo"
+ [1]=>
+ string(3) "foo"
+ [2]=>
+ NULL
+ ["h"]=>
+ string(3) "baz"
+ [3]=>
+ string(3) "baz"
+}
+array(6) {
+ [0]=>
+ array(2) {
+ [0]=>
+ string(6) "foobaz"
+ [1]=>
+ int(0)
+ }
+ ["g"]=>
+ array(2) {
+ [0]=>
+ string(3) "foo"
+ [1]=>
+ int(0)
+ }
+ [1]=>
+ array(2) {
+ [0]=>
+ string(3) "foo"
+ [1]=>
+ int(0)
+ }
+ [2]=>
+ array(2) {
+ [0]=>
+ string(0) ""
+ [1]=>
+ int(-1)
+ }
+ ["h"]=>
+ array(2) {
+ [0]=>
+ string(3) "baz"
+ [1]=>
+ int(3)
+ }
+ [3]=>
+ array(2) {
+ [0]=>
+ string(3) "baz"
+ [1]=>
+ int(3)
+ }
+}
+array(6) {
+ [0]=>
+ array(2) {
+ [0]=>
+ string(6) "foobaz"
+ [1]=>
+ int(0)
+ }
+ ["g"]=>
+ array(2) {
+ [0]=>
+ string(3) "foo"
+ [1]=>
+ int(0)
+ }
+ [1]=>
+ array(2) {
+ [0]=>
+ string(3) "foo"
+ [1]=>
+ int(0)
+ }
+ [2]=>
+ array(2) {
+ [0]=>
+ NULL
+ [1]=>
+ int(-1)
+ }
+ ["h"]=>
+ array(2) {
+ [0]=>
+ string(3) "baz"
+ [1]=>
+ int(3)
+ }
+ [3]=>
+ array(2) {
+ [0]=>
+ string(3) "baz"
+ [1]=>
+ int(3)
+ }
+}