views:

183

answers:

5

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"
+2  A: 

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
A: 

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
This is actually perfect; I have a wp blog set up on the site, so the function's already there. Thanks!
Cam
Glad I could help!
Kerry
A: 

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
+3  A: 

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['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;
Charles
+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
@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
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
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
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
Thank you artlung for the typo fixes!
Charles
A: 

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