3

What I am trying to do is find question marks in a string and replace it with & except the first one. My code so far:

$str = 'www.domain.com?adult=2&airport=40-48?destination=recko';        
    echo preg_replace(array('/[?]/'), '&', $str );

output:

www.domain.com&adult=2&airport=40-48&destination=recko

How can I ignore the first occurence of the ??

Rasclatt
  • 12,498
  • 3
  • 25
  • 33
Roman
  • 1,118
  • 3
  • 15
  • 37

6 Answers6

5

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.

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
1

We just loop thought the whole string to find the first ? and replace every other with &. No need to use regex which is slow

<?php
$str = 'www.domain.com?adult=2&airport=40-48?destination=recko';     

$strlen = strlen($str);
$passed_first = false;

for($i = 0; $i < $strlen; $i++) {
    if($str[$i] == "?") {
        if(!$passed_first) {
            $passed_first = true;
        } else {
            $str[$i] = "&"; 
        }
    }
}

echo $str;
b0ne
  • 653
  • 3
  • 10
0

Not know if it's the cleanest way, but would work and it's easy:

echo substr($str,0,1) . preg_replace(array('/[?]/'), '&', substr($str,1));
M. Pipal
  • 734
  • 11
  • 24
0

The best way would be I think is do explode("?",$str) or explode("&",$str) and put them in a Array and do preg_replace() with small if condition where you take the first ? and rest you ignore and do array_marge().

try it and let me know

Malarivtan
  • 404
  • 1
  • 6
  • 20
0

See documentation on parse_url

With this you can easily extract parts of the url and do string replace only on a component that interests you

<?php

$str = 'www.domain.com?adult=2&airport=40-48?destination=recko';

$url = parse_url($str);
$url['query'] = str_replace('?', '&', $url['query']);

echo $url['path'] . '?' . $url['query'];
Michal Bieda
  • 954
  • 5
  • 13
0

You can use a lookbehind together with \K

$str = preg_replace('/(?<=\?)[^?]*\K\?/', "&", $str);

The lookbehind is used to require a ? before the one to be matched. [^?] matches any character, that is not a ? (negated character class). \K resets beginning of the reported match.

Demo at regex101 or PHP demo at eval.in


Or by using a capturing group instead of \K: (?<=\?)([^?]*)\? and replace with $1&

bobble bubble
  • 16,888
  • 3
  • 27
  • 46