diff --git a/core/lib/Drupal/Component/Utility/UrlHelper.php b/core/lib/Drupal/Component/Utility/UrlHelper.php
index 1395d81..3c29d40 100644
--- a/core/lib/Drupal/Component/Utility/UrlHelper.php
+++ b/core/lib/Drupal/Component/Utility/UrlHelper.php
@@ -26,15 +26,9 @@ class UrlHelper {
    * RFC3986 and as a consequence non compliant to RFC3987 and as a consequence
    * not valid as a "URL" in HTML5.
    *
-   * @todo Remove this function once PHP 5.4 is required as we can use just
-   *   http_build_query() directly.
-   *
    * @param array $query
    *   The query parameter array to be processed; for instance,
    *   \Drupal::request()->query->all().
-   * @param string $parent
-   *   (optional) Internal use only. Used to build the $query array key for
-   *   nested items. Defaults to an empty string.
    *
    * @return string
    *   A rawurlencoded string which can be used as or appended to the URL query
@@ -42,27 +36,20 @@ class UrlHelper {
    *
    * @ingroup php_wrappers
    */
-  public static function buildQuery(array $query, $parent = '') {
-    $params = array();
-
-    foreach ($query as $key => $value) {
-      $key = ($parent ? $parent . '[' . rawurlencode($key) . ']' : rawurlencode($key));
-
-      // Recurse into children.
-      if (is_array($value)) {
-        $params[] = static::buildQuery($value, $key);
-      }
-      // If a query parameter value is NULL, only append its key.
-      elseif (!isset($value)) {
-        $params[] = $key;
+  public static function buildQuery(array $query) {
+    // Do some conversions to match previous implementations.
+    array_walk_recursive($query, function (&$value, $key) {
+      // Previous versions of Drupal created query arguments with NULL values
+      // but http_build_query throws them away. Convert to empty string.
+      if (!isset($value)) {
+        $value = '';
       }
-      else {
-        // For better readability of paths in query strings, we decode slashes.
-        $params[] = $key . '=' . str_replace('%2F', '/', rawurlencode($value));
+      // Previous versions of Drupal cast objects to strings.
+      if (!is_scalar($value)) {
+        $value = (string) $value;
       }
-    }
-
-    return implode('&', $params);
+    });
+    return http_build_query($query, '', '&', PHP_QUERY_RFC3986);
   }
 
   /**
diff --git a/core/modules/big_pipe/src/Tests/BigPipePlaceholderTestCases.php b/core/modules/big_pipe/src/Tests/BigPipePlaceholderTestCases.php
index 1cf10d4..5ea7bc2 100644
--- a/core/modules/big_pipe/src/Tests/BigPipePlaceholderTestCases.php
+++ b/core/modules/big_pipe/src/Tests/BigPipePlaceholderTestCases.php
@@ -8,6 +8,8 @@
 namespace Drupal\big_pipe\Tests;
 
 use Drupal\big_pipe\Render\BigPipeMarkup;
+use Drupal\Component\Utility\Html;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
 use Drupal\Core\Url;
@@ -59,29 +61,32 @@ public static function cases(ContainerInterface $container = NULL, AccountInterf
         ],
       ]
     );
-    $status_messages->bigPipePlaceholderId = 'callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;token=a8c34b5e';
+    $placeholder_id = UrlHelper::buildQuery(['callback' => 'Drupal\Core\Render\Element\StatusMessages::renderMessages', 'args' => [NULL], 'token' => 'a8c34b5e']);
+    $encoded_placeholder_id = Html::escape($placeholder_id);
+    $status_messages->bigPipePlaceholderId = $encoded_placeholder_id;
     $status_messages->bigPipePlaceholderRenderArray = [
-      '#markup' => '<div data-big-pipe-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;token=a8c34b5e"></div>',
+      '#markup' => '<div data-big-pipe-placeholder-id="' . $encoded_placeholder_id . '"></div>',
       '#cache' => $cacheability_depends_on_session_and_nojs_cookie,
       '#attached' => [
         'library' => ['big_pipe/big_pipe'],
         'drupalSettings' => [
           'bigPipePlaceholderIds' => [
-            'callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&args[0]&token=a8c34b5e' => TRUE,
+            $placeholder_id => TRUE,
           ],
         ],
         'big_pipe_placeholders' => [
-          'callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;token=a8c34b5e' => $status_messages->placeholderRenderArray,
+          $encoded_placeholder_id => $status_messages->placeholderRenderArray,
         ],
       ],
     ];
-    $status_messages->bigPipeNoJsPlaceholder = '<div data-big-pipe-nojs-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;token=a8c34b5e"></div>';
+
+    $status_messages->bigPipeNoJsPlaceholder = '<div data-big-pipe-nojs-placeholder-id="' . $encoded_placeholder_id . '"></div>';
     $status_messages->bigPipeNoJsPlaceholderRenderArray = [
-      '#markup' => '<div data-big-pipe-nojs-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;token=a8c34b5e"></div>',
+      '#markup' => '<div data-big-pipe-nojs-placeholder-id="' . $encoded_placeholder_id . '"></div>',
       '#cache' => $cacheability_depends_on_session_and_nojs_cookie,
       '#attached' => [
         'big_pipe_nojs_placeholders' => [
-          '<div data-big-pipe-nojs-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;token=a8c34b5e"></div>' => $status_messages->placeholderRenderArray,
+          '<div data-big-pipe-nojs-placeholder-id="' . $encoded_placeholder_id . '"></div>' => $status_messages->placeholderRenderArray,
         ],
       ],
     ];
@@ -109,7 +114,7 @@ public static function cases(ContainerInterface $container = NULL, AccountInterf
         [
           'command' => 'insert',
           'method' => 'replaceWith',
-          'selector' => '[data-big-pipe-placeholder-id="callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&args[0]&token=a8c34b5e"]',
+          'selector' => '[data-big-pipe-placeholder-id="' . $placeholder_id . '"]',
           'data' => "\n" . '    <div role="contentinfo" aria-label="Status message" class="messages messages--status">' . "\n" . '                  <h2 class="visually-hidden">Status message</h2>' . "\n" . '                    Hello from BigPipe!' . "\n" . '            </div>' . "\n    \n",
           'settings' => NULL,
         ],
@@ -272,24 +277,26 @@ public static function cases(ContainerInterface $container = NULL, AccountInterf
         '#lazy_builder' => ['\Drupal\big_pipe_test\BigPipeTestController::exception', ['llamas', 'suck']],
       ]
     );
-    $exception->bigPipePlaceholderId = 'callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3Aexception&amp;args[0]=llamas&amp;args[1]=suck&amp;token=68a75f1a';
+    $placeholder_id = UrlHelper::buildQuery(['callback' => '\Drupal\big_pipe_test\BigPipeTestController::exception', 'args' => ['llamas', 'suck'], 'token' => '68a75f1a']);
+    $encoded_placeholder_id = Html::escape($placeholder_id);
+    $exception->bigPipePlaceholderId = $encoded_placeholder_id;
     $exception->bigPipePlaceholderRenderArray = [
-      '#markup' => '<div data-big-pipe-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3Aexception&amp;args[0]=llamas&amp;args[1]=suck&amp;token=68a75f1a"></div>',
+      '#markup' => '<div data-big-pipe-placeholder-id="' . $encoded_placeholder_id . '"></div>',
       '#cache' => $cacheability_depends_on_session_and_nojs_cookie,
       '#attached' => [
         'library' => ['big_pipe/big_pipe'],
         'drupalSettings' => [
           'bigPipePlaceholderIds' => [
-            'callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3Aexception&args[0]=llamas&args[1]=suck&token=68a75f1a' => TRUE,
+            $placeholder_id => TRUE,
           ],
         ],
         'big_pipe_placeholders' => [
-          'callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3Aexception&amp;args[0]=llamas&amp;args[1]=suck&amp;token=68a75f1a' => $exception->placeholderRenderArray,
+          $encoded_placeholder_id => $exception->placeholderRenderArray,
         ],
       ],
     ];
     $exception->embeddedAjaxResponseCommands = NULL;
-    $exception->bigPipeNoJsPlaceholder = '<div data-big-pipe-nojs-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3Aexception&amp;args[0]=llamas&amp;args[1]=suck&amp;token=68a75f1a"></div>';
+    $exception->bigPipeNoJsPlaceholder = '<div data-big-pipe-nojs-placeholder-id="' . $encoded_placeholder_id . '"></div>';
     $exception->bigPipeNoJsPlaceholderRenderArray = [
       '#markup' => $exception->bigPipeNoJsPlaceholder,
       '#cache' => $cacheability_depends_on_session_and_nojs_cookie,
@@ -312,24 +319,26 @@ public static function cases(ContainerInterface $container = NULL, AccountInterf
         '#lazy_builder' => ['\Drupal\big_pipe_test\BigPipeTestController::responseException', []],
       ]
     );
-    $embedded_response_exception->bigPipePlaceholderId = 'callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3AresponseException&amp;&amp;token=2a9bd022';
+    $placeholder_id = UrlHelper::buildQuery(['callback' => '\Drupal\big_pipe_test\BigPipeTestController::responseException', 'args' => [], 'token' => '2a9bd022']);
+    $encoded_placeholder_id = Html::escape($placeholder_id);
+    $embedded_response_exception->bigPipePlaceholderId = $encoded_placeholder_id;
     $embedded_response_exception->bigPipePlaceholderRenderArray = [
-      '#markup' => '<div data-big-pipe-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3AresponseException&amp;&amp;token=2a9bd022"></div>',
+      '#markup' => '<div data-big-pipe-placeholder-id="' . $encoded_placeholder_id . '"></div>',
       '#cache' => $cacheability_depends_on_session_and_nojs_cookie,
       '#attached' => [
         'library' => ['big_pipe/big_pipe'],
         'drupalSettings' => [
           'bigPipePlaceholderIds' => [
-            'callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3AresponseException&&token=2a9bd022' => TRUE,
+            $placeholder_id => TRUE,
           ],
         ],
         'big_pipe_placeholders' => [
-          'callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3AresponseException&amp;&amp;token=2a9bd022' => $embedded_response_exception->placeholderRenderArray,
+          $encoded_placeholder_id => $embedded_response_exception->placeholderRenderArray,
         ],
       ],
     ];
     $embedded_response_exception->embeddedAjaxResponseCommands = NULL;
-    $embedded_response_exception->bigPipeNoJsPlaceholder = '<div data-big-pipe-nojs-placeholder-id="callback=%5CDrupal%5Cbig_pipe_test%5CBigPipeTestController%3A%3AresponseException&amp;&amp;token=2a9bd022"></div>';
+    $embedded_response_exception->bigPipeNoJsPlaceholder = '<div data-big-pipe-nojs-placeholder-id="' . $encoded_placeholder_id . '"></div>';
     $embedded_response_exception->bigPipeNoJsPlaceholderRenderArray = [
       '#markup' => $embedded_response_exception->bigPipeNoJsPlaceholder,
       '#cache' => $cacheability_depends_on_session_and_nojs_cookie,
diff --git a/core/modules/system/src/Tests/Database/SelectTableSortDefaultTest.php b/core/modules/system/src/Tests/Database/SelectTableSortDefaultTest.php
index 7ccbbb5..f1505c8 100644
--- a/core/modules/system/src/Tests/Database/SelectTableSortDefaultTest.php
+++ b/core/modules/system/src/Tests/Database/SelectTableSortDefaultTest.php
@@ -32,8 +32,8 @@ function testTableSortQuery() {
       $first = array_shift($data->tasks);
       $last = array_pop($data->tasks);
 
-      $this->assertEqual($first->task, $sort['first'], 'Items appear in the correct order.');
-      $this->assertEqual($last->task, $sort['last'], 'Items appear in the correct order.');
+      $this->assertEqual($first->task, $sort['first'], format_string('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
+      $this->assertEqual($last->task, $sort['last'], format_string('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
     }
   }
 
diff --git a/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php b/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
index 87ab75f..93fe7dd 100644
--- a/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\Component\Utility;
 
+use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Tests\UnitTestCase;
 
@@ -18,13 +19,12 @@ class UrlHelperTest extends UnitTestCase {
    * @return array
    */
   public function providerTestBuildQuery() {
-    return array(
-      array(array('a' => ' &#//+%20@۞'), 'a=%20%26%23//%2B%2520%40%DB%9E', 'Value was properly encoded.'),
-      array(array(' &#//+%20@۞' => 'a'), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', 'Key was properly encoded.'),
-      array(array('a' => '1', 'b' => '2', 'c' => '3'), 'a=1&b=2&c=3', 'Multiple values were properly concatenated.'),
-      array(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo'), 'a[b]=2&a[c]=3&d=foo', 'Nested array was properly encoded.'),
-      array(array('foo' => NULL), 'foo', 'Simple parameters are properly added.'),
-    );
+    return [
+      [['a' => ' &#//+%20@۞'], 'Value was properly encoded.'],
+      [['a' => '1', 'b' => '2', 'c' => '3'], 'Multiple values were properly concatenated.'],
+      [['a' => ['b' => '2', 'c' => '3'], 'd' => 'foo'], 'Nested array was properly encoded.'],
+      [['foo' => NULL], 'Simple parameters are properly added.'],
+    ];
   }
 
   /**
@@ -35,13 +35,25 @@ public function providerTestBuildQuery() {
    *
    * @param array $query
    *   The array of query parameters.
-   * @param string $expected
-   *   The expected query string.
    * @param string $message
    *   The assertion message.
    */
-  public function testBuildQuery($query, $expected, $message) {
-    $this->assertEquals(UrlHelper::buildQuery($query), $expected, $message);
+  public function testBuildQuery($query, $message) {
+    parse_str(UrlHelper::buildQuery($query), $result);
+    $this->assertEquals($query, $result, $message);
+  }
+
+  /**
+   * Test query building of query's that can't be generalized.
+   */
+  public function testBuildQuerySpecial() {
+    // Parse string throws away the leading space in the key breaking our
+    // generalized test.
+    $this->assertEquals('%20%26%23%2F%2F%2B%2520%40%DB%9E=a', UrlHelper::buildQuery([' &#//+%20@۞' => 'a']), 'Key was properly encoded.');
+
+    // Ensure as a convenience we can put translatable and formatted markup
+    // objects into queries.
+    $this->assertEquals('a=foo', UrlHelper::buildQuery(['a' => new FormattableMarkup('foo', [])]), 'Objects cast as a string.');
   }
 
   /**
