Quick answer: The hash tags / named anchors / fragment identifiers (how ever you call them) must come after the query string to be parsed properly (at least as you expect them to be). So you need to build your URLs accordingly.
The following question on superuser.com has a full answer for your question (albeit your question is a bit different, the answer is the same) :
Does an anchor tag come before the query string or after?
Although http://www.example.com/test/#test?source=blahblahblah
is a valid URL, what will be considered as the hash tag string is the whole test?source=blahblahblah
part (with no query string being detected). Also, if you need to include the #
char in the path, document or query string parts of the URL it must be endoded as %23
.
So no, your second link will never work as you intend it to be, even if you need it to work like that.
EDIT - Bonus
If you're looking for an easy way to add a query string to an existing url in PHP, try url_add_query_params()
from the following code:
function build_query($query_data, $numeric_prefix = '', $arg_separator = '&', $inner_call = 0) {
// Source: http://php.net/manual/en/function.http-build-query.php#112024
if (!is_array($query_data)) return false;
$result = array();
foreach ((array)$query_data as $key => $value) {
if ($inner_call) {
if (is_numeric($key))
$key = $numeric_prefix . "[]";
else
$key = $numeric_prefix . "[$key]";
} else {
if (is_int($key))
$key = $numeric_prefix . $key;
}
if (is_array($value) || is_object($value)) {
$result[] = build_query($value, $key, $arg_separator, 1);
continue;
}
$result[] = urlencode($key) . "=" . urlencode($value);
}
return implode($arg_separator, $result);
}
function unparse_url($parsed_url) {
// Source: http://php.net/manual/en/function.parse-url.php#106731
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
return "$scheme$user$pass$host$port$path$query$fragment";
}
function url_add_query_params($url, $new_params) {
// parse url
$url_components = parse_url($url);
// return immediately if parsing failed
if ($url_components === FALSE) { return $url; }
// if $new_params is a query string, parse it
if (!is_array($new_params)) {
$new_params_str = $new_params;
$new_params = array();
parse_str($new_params_str, $new_params);
}
// parse query string from original url
$params_str = isset($url_components['query']) ? $url_components['query'] : '';
$params = array();
parse_str($params_str, $params);
// merge the two arrays, keeping later values for identical keys
$params = array_merge($params, $new_params);
// rebuild query string
$url_components['query'] = build_query($params);
// rebuild url
return unparse_url($url_components);
}
Example:
$url = 'http://www.example.com/test/#test';
$new_url = url_add_query_params($url, 'source=blahblahblah');
echo $new_url; // output: http://www.example.com/test/?source=blahblahblah#test
echo "\n";
$url = 'http://www.example.com/test/?source=index¶m=value#test';
$new_url = url_add_query_params($url, 'source=blahblahblah');
echo $new_url; // output: http://www.example.com/test/?source=blahblahblah¶m=value#test
I only lightly tested it, but it should work as intended.