diff --git a/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php b/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php index 6d68c1e4bc20affae374141e86e8a0f1bdcda8f1..2569ee4adbaba186634f2a542674a1cd40806c9e 100644 --- a/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php +++ b/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php @@ -2,6 +2,7 @@ namespace Drupal\Core\Utility; +use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\UrlHelper; use Drupal\Core\GeneratedUrl; use Drupal\Core\PathProcessor\OutboundPathProcessorInterface; @@ -69,13 +70,18 @@ public function assemble($uri, array $options = [], $collect_bubbleable_metadata */ protected function buildExternalUrl($uri, array $options = [], $collect_bubbleable_metadata = FALSE) { $this->addOptionDefaults($options); - // Split off the fragment. - if (strpos($uri, '#') !== FALSE) { - list($uri, $old_fragment) = explode('#', $uri, 2); - // If $options contains no fragment, take it over from the path. - if (isset($old_fragment) && !$options['fragment']) { - $options['fragment'] = '#' . $old_fragment; - } + // Split off the query & fragment. + $parsed = UrlHelper::parse($uri); + $uri = $parsed['path']; + + $parsed += ['query' => []]; + $options += ['query' => []]; + + $options['query'] = NestedArray::mergeDeep($parsed['query'], $options['query']); + ksort($options['query']); + + if ($parsed['fragment'] && !$options['fragment']) { + $options['fragment'] = '#' . $parsed['fragment']; } if (isset($options['https'])) { @@ -88,7 +94,7 @@ protected function buildExternalUrl($uri, array $options = [], $collect_bubbleab } // Append the query. if ($options['query']) { - $uri .= (strpos($uri, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($options['query']); + $uri .= '?' . UrlHelper::buildQuery($options['query']); } // Reassemble. $url = $uri . $options['fragment']; diff --git a/core/modules/rdf/tests/src/Kernel/Field/LinkFieldRdfaTest.php b/core/modules/rdf/tests/src/Kernel/Field/LinkFieldRdfaTest.php index 30735e3a8b6fd358a18e473b8893fdd8a29f4107..aac9b75591f861b4d4265f8dad834f99214ebfcf 100644 --- a/core/modules/rdf/tests/src/Kernel/Field/LinkFieldRdfaTest.php +++ b/core/modules/rdf/tests/src/Kernel/Field/LinkFieldRdfaTest.php @@ -42,7 +42,7 @@ protected function setUp() { */ public function testAllFormattersExternal() { // Set up test values. - $this->testValue = 'http://test.me/foo/bar/neque/porro/quisquam/est/qui-dolorem?foo/bar/neque/porro/quisquam/est/qui-dolorem'; + $this->testValue = 'http://test.me/foo/bar/neque/porro/quisquam/est/qui-dolorem?path=foo/bar/neque/porro/quisquam/est/qui-dolorem'; $this->entity = EntityTest::create([]); $this->entity->{$this->fieldName}->uri = $this->testValue; diff --git a/core/modules/system/tests/src/Functional/Common/UrlTest.php b/core/modules/system/tests/src/Functional/Common/UrlTest.php index cd27ca79a5d9caf1c540e01b22979f09ececd665..8e4d7b121c5852a7d7ca65ebf85d2350f7734d9e 100644 --- a/core/modules/system/tests/src/Functional/Common/UrlTest.php +++ b/core/modules/system/tests/src/Functional/Common/UrlTest.php @@ -306,15 +306,15 @@ public function testExternalUrls() { // Verify external URL can be extended with a query string. $url = $test_url; - $query = [$this->randomMachineName(5) => $this->randomMachineName(5)]; + $query = ['awesome' => 'drupal']; $result = Url::fromUri($url, ['query' => $query])->toString(); - $this->assertEqual($url . '?' . http_build_query($query, '', '&'), $result, 'External URL can be extended with a query string in $options.'); + $this->assertSame('https://www.drupal.org/?awesome=drupal', $result); // Verify query string can be extended in an external URL. $url = $test_url . '?drupal=awesome'; - $query = [$this->randomMachineName(5) => $this->randomMachineName(5)]; + $query = ['awesome' => 'drupal']; $result = Url::fromUri($url, ['query' => $query])->toString(); - $this->assertEqual($url . '&' . http_build_query($query, '', '&'), $result); + $this->assertEqual('https://www.drupal.org/?awesome=drupal&drupal=awesome', $result); } } diff --git a/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php b/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php index a47d2062ea76c4f39e53aa6757ae91ac7ae1cb28..70b3880f4c1df8c7eebf4d5d3b1a6ddbb48ba34c 100644 --- a/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php +++ b/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php @@ -96,6 +96,10 @@ public function providerTestAssembleWithExternalUrl() { ['http://example.com/test', ['https' => TRUE], 'https://example.com/test'], ['https://example.com/test', ['https' => FALSE], 'http://example.com/test'], ['https://example.com/test?foo=1#bar', [], 'https://example.com/test?foo=1#bar'], + 'override-query' => ['https://example.com/test?foo=1#bar', ['query' => ['foo' => 2]], 'https://example.com/test?foo=2#bar'], + 'override-query-merge' => ['https://example.com/test?foo=1#bar', ['query' => ['bar' => 2]], 'https://example.com/test?bar=2&foo=1#bar'], + 'override-deep-query-merge' => ['https://example.com/test?foo=1#bar', ['query' => ['bar' => ['baz' => 'foo']]], 'https://example.com/test?bar%5Bbaz%5D=foo&foo=1#bar'], + 'override-fragment' => ['https://example.com/test?foo=1#bar', ['fragment' => 'baz'], 'https://example.com/test?foo=1#baz'], ['//www.drupal.org', [], '//www.drupal.org'], ]; }