Index: t/unit_core_uri_for.t =================================================================== --- t/unit_core_uri_for.t (revision 10686) +++ t/unit_core_uri_for.t (working copy) @@ -1,7 +1,7 @@ use strict; use warnings; -use Test::More tests => 15; +use Test::More tests => 16; use URI; use_ok('Catalyst'); @@ -103,3 +103,13 @@ is( $warnings, 0, "no warnings emitted" ); } +# make sure caller's query parameter hash isn't messed up +{ + my $query_params_base = {test => "one two", + bar => ["foo baz", "bar"]}; + my $query_params_test = {test => "one two", + bar => ["foo baz", "bar"]}; + Catalyst::uri_for($context, '/bar/baz', $query_params_test); + is_deeply($query_params_base, $query_params_test, + "uri_for() doesn't mess up query parameter hash in the caller"); +} Index: lib/Catalyst.pm =================================================================== --- lib/Catalyst.pm (revision 10686) +++ lib/Catalyst.pm (working copy) @@ -1118,6 +1118,20 @@ my $params = ( scalar @args && ref $args[$#args] eq 'HASH' ? pop @args : {} ); + # make a copy of the parameters hash to prevent the caller's query param + # hash from getting munged. we only really care about copying the array + # ref param values since that is the only case where the caller's data + # can be modified. + my %params_copy; + while (my ($key, $val) = each %$params) { + if (ref $val eq 'ARRAY') { + my @array_copy = @$val; + $params_copy{$key} = \@array_copy; + } else { + $params_copy{$key} = $val; + } + } + carp "uri_for called with undef argument" if grep { ! defined $_ } @args; s/([^$URI::uric])/$URI::Escape::escapes{$1}/go for @args; @@ -1141,10 +1155,10 @@ my $query = ''; - if (my @keys = keys %$params) { + if (my @keys = keys %params_copy) { # somewhat lifted from URI::_query's query_form $query = '?'.join('&', map { - my $val = $params->{$_}; + my $val = $params_copy{$_}; s/([;\/?:@&=+,\$\[\]%])/$URI::Escape::escapes{$1}/go; s/ /+/g; my $key = $_;