Match the start of the string up to the first ?
and use a (*SKIP)(*F)
to omit the first match:
$str = 'www.domain.com?adult=2&airport=40-48?destination=recko';
echo preg_replace('/^[^?]*\?(*SKIP)(*F)|[?]/', '&', $str );
// => www.domain.com?adult=2&airport=40-48&destination=recko
See the IDEONE demo
Pattern details:
^
- start of a string
[^?]*
- 0+ characters other than ?
as many as possible
\?
- a literal ?
(*SKIP)(*F)
- two PCRE verbs making the regex engine omit the text matched so far in the current iteration
|
- or
[?]
- a literal ?
Alternative to (*SKIP)(*FAIL)
is to use preg_replace_callback
and the former pattern with the first alternative branch inside capturing parentheses:
$str = 'www.domain.com?adult=2&airport=40-48?destination=recko';
echo preg_replace_callback('/^([^?]*[?])|[?]/', function($m) {
return !empty($m[1]) ? $m[1] : "&";
}, $str );
See this IDEONE demo
The ^([^?]*[?])
part matches the string part from the start will the first ?
and places into Group 1. Inside the anonymous method where we pass the match object ($m
), we can check if the group matched ("participated in the match") with the help of !empty($m[1])
. If it is, we just put it back. If not, the [?]
, the second branch matched, so, we replace it.