2

Given a url, and a query string, how can I get the url resulting from the combination of the query string with the url?

I'm looking for functionality similar to .htaccess's qsa. I realize this would be fairly trivial to implement completely by hand, however are there built-in functions that deal with query strings which could either simplify or completely solve this?

Example input/result sets:

Url="http://www.example.com/index.php/page?a=1"
QS ="?b=2"
Result="http://www.example.com/index.php/page?a=1&b=2"

-

Url="page.php"
QS ="?b=2"
Result="page.php?b=2"
Cam
  • 14,930
  • 16
  • 77
  • 128

6 Answers6

8

How about something that uses no PECL extensions and isn't a huge set of copied-and-pasted functions? It's still a tad complex because you're splicing together two query strings and want to do it in a way that isn't just $old .= $new;

We'll use parse_url to extract the query string from the desired url, parse_str to parse the query strings you wish to join, array_merge to join them together, and http_build_query to create the new, combined string for us.

// Parse the URL into components
$url = 'http://...';
$url_parsed = parse_url($url);
$new_qs_parsed = array();
// Grab our first query string
parse_str($url_parsed['query'], $new_qs_parsed);
// Here's the other query string
$other_query_string = 'that=this&those=these';
$other_qs_parsed = array();
parse_str($other_query_string, $other_qs_parsed);
// Stitch the two query strings together
$final_query_string_array = array_merge($new_qs_parsed, $other_qs_parsed);
$final_query_string = http_build_query($final_query_string_array);
// Now, our final URL:
$new_url = $url_parsed['scheme'] 
         . '://'
         . $url_parsed['host'] 
         . $url_parsed['path'] 
         . '?'      
         . $final_query_string;
Fluffy
  • 27,504
  • 41
  • 151
  • 234
Charles
  • 50,943
  • 13
  • 104
  • 142
  • 3
    +1 for miraculously being the only one that bothers to use the proper, readily available functions. It's maybe a pet peeve of mine, but all those people re-re-re-re-reinventing already available functionality is a big part why PHP gets a bad rep. – Wrikken Jun 09 '10 at 01:26
  • @Wrikken I actually prefer to reinvent the wheel when it comes to `http_build_query` and query string parsing in general. The way multiple values for the same query parameter are handled is incompatible with everything else but PHP (e.g. Java's ServletRequest.getParameterValues). – Artefacto Jun 09 '10 at 01:37
  • 1
    This isn't quite correct since query strings may have the same value multiple times with different values, and sometimes preserving the parameter order is important. – Duncan Beevers Jun 09 '10 at 01:42
  • 1
    Unfortunately code to properly preserve parameter order (and not automagically convert to the [] syntax) would be much larger, along the lines of wheel reinvention. There is a case for it, however. – Charles Jun 09 '10 at 02:36
  • 1
    Switched this to best answer (used to have the wordpress one selected). This one is more consise and uses built-in functions (like I asked in the question). – Cam Jun 09 '10 at 03:12
  • Thank you artlung for the typo fixes! – Charles Jun 09 '10 at 16:24
1

You can get the query string part from url using:

$_SERVER['QUERY_STRING']

and then append it to url normally.

If you want to specify your own custom variables in query string, have a look at:

http_build_query

Sarfraz
  • 377,238
  • 77
  • 533
  • 578
0

This is a series of functions taken from the WordPress "framework" that will do it, but this could quite well be too much:

add_query_arg()

/**
 * Retrieve a modified URL query string.
 *
 * You can rebuild the URL and append a new query variable to the URL query by
 * using this function. You can also retrieve the full URL with query data.
 *
 * Adding a single key & value or an associative array. Setting a key value to
 * emptystring removes the key. Omitting oldquery_or_uri uses the $_SERVER
 * value.
 *
 * @since 1.0
 *
 * @param mixed $param1 Either newkey or an associative_array
 * @param mixed $param2 Either newvalue or oldquery or uri
 * @param mixed $param3 Optional. Old query or uri
 * @return string New URL query string.
 */
public function add_query_arg() {
    $ret = '';
    if ( is_array( func_get_arg(0) ) ) {
        $uri = ( @func_num_args() < 2 || false === @func_get_arg( 1 ) ) ? $_SERVER['REQUEST_URI'] : @func_get_arg( 1 );
    } else {
        $uri = ( @func_num_args() < 3 || false === @func_get_arg( 2 ) ) ? $_SERVER['REQUEST_URI'] : @func_get_arg( 2 );
    }

    if ( $frag = strstr( $uri, '#' ) ) {
        $uri = substr( $uri, 0, -strlen( $frag ) );
    } else {
        $frag = '';
    }

    if ( preg_match( '|^https?://|i', $uri, $matches ) ) {
        $protocol = $matches[0];
        $uri = substr( $uri, strlen( $protocol ) );
    } else {
        $protocol = '';
    }

    if ( strpos( $uri, '?' ) !== false ) {
        $parts = explode( '?', $uri, 2 );
        if ( 1 == count( $parts ) ) {
            $base = '?';
            $query = $parts[0];
        } else {
            $base = $parts[0] . '?';
            $query = $parts[1];
        }
    } elseif ( !empty( $protocol ) || strpos( $uri, '=' ) === false ) {
        $base = $uri . '?';
        $query = '';
    } else {
        $base = '';
        $query = $uri;
    }

    parse_str( $query, $qs );

    if ( get_magic_quotes_gpc() )
        $qs = format::stripslashes_deep( $qs );

    $qs = format::urlencode_deep( $qs ); // this re-URL-encodes things that were already in the query string
    if ( is_array( func_get_arg( 0 ) ) ) {
        $kayvees = func_get_arg( 0 );
        $qs = array_merge( $qs, $kayvees );
    } else {
        $qs[func_get_arg( 0 )] = func_get_arg( 1 );
    }

    foreach ( ( array ) $qs as $k => $v ) {
        if ( $v === false )
            unset( $qs[$k] );
    }

    $ret = http_build_query( $qs, '', '&' );
    $ret = trim( $ret, '?' );
    $ret = preg_replace( '#=(&|$)#', '$1', $ret );
    $ret = $protocol . $base . $ret . $frag;
    $ret = rtrim( $ret, '?' );
    return $ret;
}

stripslashes_deep()

/**
 * Navigates through an array and removes slashes from the values.
 *
 * If an array is passed, the array_map() function causes a callback to pass the
 * value back to the function. The slashes from this value will removed.
 *
 * @since 1.0
 *
 * @param array|string $value The array or string to be stripped
 * @return array|string Stripped array (or string in the callback).
 */
function stripslashes_deep( $value ) {
    return is_array( $value ) ? array_map( array('self', 'stripslashes_deep'), $value ) : stripslashes( $value );
}

urlencode_deep()

/**
 * Navigates through an array and encodes the values to be used in a URL.
 *
 * Uses a callback to pass the value of the array back to the function as a
 * string.
 *
 * @since 1.0
 *
 * @param array|string $value The array or string to be encoded.
 * @return array|string $value The encoded array (or string from the callback).
 */
public function urlencode_deep( $value ) {
    return is_array($value) ? array_map( array('self', 'urlencode_deep'), $value) : urlencode($value);
}
Kerry Jones
  • 21,806
  • 12
  • 62
  • 89
  • This is actually perfect; I have a wp blog set up on the site, so the function's already there. Thanks! – Cam Jun 09 '10 at 00:53
0

THere is no built-in function to do this. However, you can use this function from http PECL extension,

http://usphp.com/manual/en/function.http-build-url.php

For example,

$url = http_build_url("http://www.example.com/index.php/page?a=1",
    array(
        "b" => "2"
    )
);
ZZ Coder
  • 74,484
  • 29
  • 137
  • 169
0

So what happens if the urls conflict? If both urls contain a b= component in the querystring? You'd need to decided which holds sway.

Here's a chunk of code that does what you want, parsing each string as a url, then extracting the query url part and implode() ing them back together.

$url="http://www.example.com/index.php/page?a=1";
$qs ="?b=2";

$url_parsed = parse_url($url);
$qs_parsed = parse_url($qs);

$args = array(
    $url_parsed['query'],
    $qs_parsed['query'],
);

$new_url = $url_parsed['scheme'];
$new_url .= '://';
$new_url .= $url_parsed['host'];
$new_url .= $url_parsed['path'];
$new_url .= '?';
$new_url .= implode('&', $args);

print $new_url;
artlung
  • 33,305
  • 16
  • 69
  • 121
0

Here is another function to do the merging of params to existing URL

public function mergeParamsToUrl(string $url, array $params)
    {
        $urlParts = parse_url($url);

        // Combine existing query string with new parameters
        $existingParams = [];
        if (isset($urlParts['query'])) {
            parse_str($urlParts['query'], $existingParams);
        }

        $mergedParams = array_merge($existingParams, $params);
        $mergedQueryString = http_build_query($mergedParams);

        // Reconstruct the URL
        $mergedUrl = "{$urlParts['scheme']}://{$urlParts['host']}{$urlParts['path']}";
        if ($mergedQueryString) {
            $mergedUrl .= '?'.$mergedQueryString;
        }

        if (isset($urlParts['fragment'])) {
            $mergedUrl .= '#'.$urlParts['fragment'];
        }

        return $mergedUrl;
    }