diff --git a/core/lib/Drupal/Component/Utility/UrlHelper.php b/core/lib/Drupal/Component/Utility/UrlHelper.php index 1395d81..9543b7b 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,16 @@ 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; - } - else { - // For better readability of paths in query strings, we decode slashes. - $params[] = $key . '=' . str_replace('%2F', '/', rawurlencode($value)); - } - } - - return implode('&', $params); + 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. It also throws away objects. + // As a convenience and backward compatibility we convert all values to a + // string. + $value = (string) $value; + }); + 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&args[0]&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' => '
', + '#markup' => '
', '#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&args[0]&token=a8c34b5e' => $status_messages->placeholderRenderArray, + $encoded_placeholder_id => $status_messages->placeholderRenderArray, ], ], ]; - $status_messages->bigPipeNoJsPlaceholder = '
'; + + $status_messages->bigPipeNoJsPlaceholder = '
'; $status_messages->bigPipeNoJsPlaceholderRenderArray = [ - '#markup' => '
', + '#markup' => '
', '#cache' => $cacheability_depends_on_session_and_nojs_cookie, '#attached' => [ 'big_pipe_nojs_placeholders' => [ - '
' => $status_messages->placeholderRenderArray, + '
' => $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" . ' ' . "\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&args[0]=llamas&args[1]=suck&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' => '
', + '#markup' => '
', '#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&args[0]=llamas&args[1]=suck&token=68a75f1a' => $exception->placeholderRenderArray, + $encoded_placeholder_id => $exception->placeholderRenderArray, ], ], ]; $exception->embeddedAjaxResponseCommands = NULL; - $exception->bigPipeNoJsPlaceholder = '
'; + $exception->bigPipeNoJsPlaceholder = '
'; $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&&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' => '
', + '#markup' => '
', '#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&&token=2a9bd022' => $embedded_response_exception->placeholderRenderArray, + $encoded_placeholder_id => $embedded_response_exception->placeholderRenderArray, ], ], ]; $embedded_response_exception->embeddedAjaxResponseCommands = NULL; - $embedded_response_exception->bigPipeNoJsPlaceholder = '
'; + $embedded_response_exception->bigPipeNoJsPlaceholder = '
'; $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.'); } /**