From d3c33d5821fedc99775b0710c5d1abaffb6b8ab8 Mon Sep 17 00:00:00 2001 From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org> Date: Mon, 20 Jul 2015 16:15:11 +0100 Subject: [PATCH] Issue #2506581 by alexpott, Wim Leers, Fabianx, xjm, joelpittet, Cottser, dawehner: Remove SafeMarkup::set() from Renderer::doRender --- core/includes/bootstrap.inc | 2 +- .../Ajax/AjaxResponseAttachmentsProcessor.php | 6 +- .../Ajax/CommandWithAttachedAssetsTrait.php | 4 +- .../Core/Render/MainContent/AjaxRenderer.php | 4 +- core/lib/Drupal/Core/Render/RenderCache.php | 16 ++-- core/lib/Drupal/Core/Render/Renderer.php | 37 ++++---- .../Drupal/Core/Render/RendererInterface.php | 6 +- core/lib/Drupal/Core/Render/SafeString.php | 84 +++++++++++++++++++ .../StringTranslation/TranslationWrapper.php | 4 +- .../Drupal/Core/Template/TwigEnvironment.php | 9 +- .../lib/Drupal/Core/Utility/LinkGenerator.php | 8 ++ .../block/src/Tests/BlockViewBuilderTest.php | 10 +-- core/modules/contact/contact.module | 4 +- .../contextual/src/ContextualController.php | 2 +- core/modules/editor/src/EditorController.php | 2 +- core/modules/filter/filter.module | 2 +- .../filter/src/Tests/FilterAPITest.php | 8 +- .../quickedit/src/QuickEditController.php | 6 +- .../src/Tests/Views/StyleSerializerTest.php | 2 +- .../simpletest/src/AssertContentTrait.php | 4 +- .../Tests/Common/RenderElementTypesTest.php | 2 +- .../Tests/Entity/EntityTranslationTest.php | 4 +- .../views/src/Plugin/views/PluginBase.php | 2 +- .../views/src/Plugin/views/display/Feed.php | 2 +- .../Plugin/views/field/FieldPluginBase.php | 9 +- .../views/src/Plugin/views/row/OpmlFields.php | 2 +- .../Plugin/views/style/StylePluginBase.php | 9 +- .../src/Tests/Handler/FieldCounterTest.php | 12 +-- .../src/Tests/Handler/FieldFieldTest.php | 68 ++++++++------- .../views/src/Tests/QueryGroupByTest.php | 16 ++-- .../Core/Render/RendererBubblingTest.php | 12 +-- .../Core/Render/RendererPlaceholdersTest.php | 34 ++++---- .../Drupal/Tests/Core/Render/RendererTest.php | 13 +-- 33 files changed, 238 insertions(+), 167 deletions(-) create mode 100644 core/lib/Drupal/Core/Render/SafeString.php diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 7bfa55c3d286..5d4424abdb42 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -563,7 +563,7 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) $new = array( 'safe' => SafeMarkup::isSafe($message), - 'message' => $message, + 'message' => (string) $message, ); if ($repeat || !in_array($new, $_SESSION['messages'][$type])) { $_SESSION['messages'][$type][] = $new; diff --git a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php index 7d36b51c718e..bffe66619833 100644 --- a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php +++ b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php @@ -164,15 +164,15 @@ protected function buildAttachmentsCommands(AjaxResponse $response, Request $req $resource_commands = array(); if ($css_assets) { $css_render_array = $this->cssCollectionRenderer->render($css_assets); - $resource_commands[] = new AddCssCommand($this->renderer->renderPlain($css_render_array)); + $resource_commands[] = new AddCssCommand((string) $this->renderer->renderPlain($css_render_array)); } if ($js_assets_header) { $js_header_render_array = $this->jsCollectionRenderer->render($js_assets_header); - $resource_commands[] = new PrependCommand('head', $this->renderer->renderPlain($js_header_render_array)); + $resource_commands[] = new PrependCommand('head', (string) $this->renderer->renderPlain($js_header_render_array)); } if ($js_assets_footer) { $js_footer_render_array = $this->jsCollectionRenderer->render($js_assets_footer); - $resource_commands[] = new AppendCommand('body', $this->renderer->renderPlain($js_footer_render_array)); + $resource_commands[] = new AppendCommand('body', (string) $this->renderer->renderPlain($js_footer_render_array)); } foreach (array_reverse($resource_commands) as $resource_command) { $response->addCommand($resource_command, TRUE); diff --git a/core/lib/Drupal/Core/Ajax/CommandWithAttachedAssetsTrait.php b/core/lib/Drupal/Core/Ajax/CommandWithAttachedAssetsTrait.php index fe117e173083..d83db5779f2a 100644 --- a/core/lib/Drupal/Core/Ajax/CommandWithAttachedAssetsTrait.php +++ b/core/lib/Drupal/Core/Ajax/CommandWithAttachedAssetsTrait.php @@ -37,10 +37,10 @@ protected function getRenderedContent() { if (is_array($this->content)) { $html = \Drupal::service('renderer')->renderRoot($this->content); $this->attachedAssets = AttachedAssets::createFromRenderArray($this->content); - return $html; + return (string) $html; } else { - return $this->content; + return (string) $this->content; } } diff --git a/core/lib/Drupal/Core/Render/MainContent/AjaxRenderer.php b/core/lib/Drupal/Core/Render/MainContent/AjaxRenderer.php index 31192f35f999..c54799db6cf3 100644 --- a/core/lib/Drupal/Core/Render/MainContent/AjaxRenderer.php +++ b/core/lib/Drupal/Core/Render/MainContent/AjaxRenderer.php @@ -64,7 +64,7 @@ public function renderResponse(array $main_content, Request $request, RouteMatch } } - $html = $this->drupalRenderRoot($main_content); + $html = (string) $this->drupalRenderRoot($main_content); $response->setAttachments($main_content['#attached']); // The selector for the insert command is NULL as the new content will @@ -72,7 +72,7 @@ public function renderResponse(array $main_content, Request $request, RouteMatch // behavior can be changed with #ajax['method']. $response->addCommand(new InsertCommand(NULL, $html)); $status_messages = array('#type' => 'status_messages'); - $output = $this->drupalRenderRoot($status_messages); + $output = (string) $this->drupalRenderRoot($status_messages); if (!empty($output)) { $response->addCommand(new PrependCommand(NULL, $output)); } diff --git a/core/lib/Drupal/Core/Render/RenderCache.php b/core/lib/Drupal/Core/Render/RenderCache.php index 28549116e8be..880fca36f505 100644 --- a/core/lib/Drupal/Core/Render/RenderCache.php +++ b/core/lib/Drupal/Core/Render/RenderCache.php @@ -77,11 +77,6 @@ public function get(array $elements) { if (isset($cached_element['#cache_redirect'])) { return $this->get($cached_element); } - // Ensure that any safe properties are marked safe. - foreach ($cached_element['#safe_cache_properties'] as $cache_property) { - SafeMarkup::set($cached_element[$cache_property]); - } - unset($cached_element['#safe_cache_properties']); // Return the cached element. return $cached_element; } @@ -333,7 +328,6 @@ public function getCacheableRenderArray(array $elements) { 'tags' => $elements['#cache']['tags'], 'max-age' => $elements['#cache']['max-age'], ], - '#safe_cache_properties' => [] ]; // Preserve cacheable items if specified. If we are preserving any cacheable @@ -342,10 +336,10 @@ public function getCacheableRenderArray(array $elements) { // the cache entry size. if (!empty($elements['#cache_properties']) && is_array($elements['#cache_properties'])) { $data['#cache_properties'] = $elements['#cache_properties']; - // Store whether any of the cache properties are safe strings. + // Ensure that any safe strings are a SafeString object. foreach (Element::properties(array_flip($elements['#cache_properties'])) as $cache_property) { - if (isset($elements[$cache_property]) && !is_array($elements[$cache_property]) && SafeMarkup::isSafe($elements[$cache_property])) { - $data['#safe_cache_properties'][] = $cache_property; + if (isset($elements[$cache_property]) && is_scalar($elements[$cache_property]) && SafeMarkup::isSafe($elements[$cache_property])) { + $elements[$cache_property] = SafeString::create($elements[$cache_property]); } } @@ -357,12 +351,14 @@ public function getCacheableRenderArray(array $elements) { $data['#markup'] = ''; // Cache only cacheable children's markup. foreach ($cacheable_children as $key) { - $cacheable_items[$key] = ['#markup' => $cacheable_items[$key]['#markup']]; + // We can assume that #markup is safe at this point. + $cacheable_items[$key] = ['#markup' => SafeString::create($cacheable_items[$key]['#markup'])]; } } $data += $cacheable_items; } + $data['#markup'] = SafeString::create($data['#markup']); return $data; } diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php index d68282278771..cbd9ca853d98 100644 --- a/core/lib/Drupal/Core/Render/Renderer.php +++ b/core/lib/Drupal/Core/Render/Renderer.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\UrlHelper; +use Drupal\Component\Utility\Xss; use Drupal\Core\Access\AccessResultInterface; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheableMetadata; @@ -277,15 +278,9 @@ protected function doRender(&$elements, $is_root_call = FALSE) { if ($is_root_call) { $this->replacePlaceholders($elements); } - // Mark the element markup as safe. If we have cached children, we need - // to mark them as safe too. The parent markup contains the child - // markup, so if the parent markup is safe, then the markup of the - // individual children must be safe as well. - $elements['#markup'] = SafeMarkup::set($elements['#markup']); - if (!empty($elements['#cache_properties'])) { - foreach (Element::children($cached_element) as $key) { - SafeMarkup::set($cached_element[$key]['#markup']); - } + // Mark the element markup as safe if is it a string. + if (is_string($elements['#markup'])) { + $elements['#markup'] = SafeString::create($elements['#markup']); } // The render cache item contains all the bubbleable rendering metadata // for the subtree. @@ -410,7 +405,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) { $elements['#children'] = ''; } - if (isset($elements['#markup'])) { + if (!empty($elements['#markup'])) { // @todo Decide how to support non-HTML in the render API in // https://www.drupal.org/node/2501313. $elements['#markup'] = $this->xssFilterAdminIfUnsafe($elements['#markup']); @@ -452,7 +447,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) { foreach ($children as $key) { $elements['#children'] .= $this->doRender($elements[$key]); } - $elements['#children'] = SafeMarkup::set($elements['#children']); + $elements['#children'] = SafeString::create($elements['#children']); } // If #theme is not implemented and the element has raw #markup as a @@ -463,7 +458,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) { // required. Eventually #theme_wrappers will expect both #markup and // #children to be a single string as #children. if (!$theme_is_implemented && isset($elements['#markup'])) { - $elements['#children'] = SafeMarkup::set($elements['#markup'] . $elements['#children']); + $elements['#children'] = SafeString::create($elements['#markup'] . $elements['#children']); } // Let the theme functions in #theme_wrappers add markup around the rendered @@ -551,8 +546,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) { $context->bubble(); $elements['#printed'] = TRUE; - $elements['#markup'] = SafeMarkup::set($elements['#markup']); - return $elements['#markup']; + return SafeString::create($elements['#markup']); } /** @@ -710,17 +704,18 @@ public function addCacheableDependency(array &$elements, $dependency) { * Note: This method only filters if $string is not marked safe already. This * ensures that HTML intended for display is not filtered. * - * @param string $string + * @param string|\Drupal\Core\Render\SafeString $string * A string. * - * @return string - * The escaped string. If SafeMarkup::isSafe($string) returns TRUE, it won't - * be escaped again. + * @return \Drupal\Core\Render\SafeString + * The escaped string wrapped in a SafeString object. If + * SafeMarkup::isSafe($string) returns TRUE, it won't be escaped again. */ protected function xssFilterAdminIfUnsafe($string) { - // @todo https://www.drupal.org/node/2506581 replace with - // SafeMarkup::isSafe() and Xss::filterAdmin(). - return SafeMarkup::checkAdminXss($string); + if (!SafeMarkup::isSafe($string)) { + $string = Xss::filterAdmin($string); + } + return SafeString::create($string); } } diff --git a/core/lib/Drupal/Core/Render/RendererInterface.php b/core/lib/Drupal/Core/Render/RendererInterface.php index af3bcdad3c16..155eec4dbadf 100644 --- a/core/lib/Drupal/Core/Render/RendererInterface.php +++ b/core/lib/Drupal/Core/Render/RendererInterface.php @@ -27,7 +27,7 @@ interface RendererInterface { * @param array $elements * The structured array describing the data to be rendered. * - * @return string + * @return \Drupal\Component\Utility\SafeStringInterface * The rendered HTML. * * @see ::render() @@ -58,7 +58,7 @@ public function renderRoot(&$elements); * @param array $elements * The structured array describing the data to be rendered. * - * @return string + * @return \Drupal\Component\Utility\SafeStringInterface * The rendered HTML. * * @see ::renderRoot() @@ -302,7 +302,7 @@ public function renderPlain(&$elements); * (Internal use only.) Whether this is a recursive call or not. See * ::renderRoot(). * - * @return string + * @return \Drupal\Component\Utility\SafeStringInterface * The rendered HTML. * * @throws \LogicException diff --git a/core/lib/Drupal/Core/Render/SafeString.php b/core/lib/Drupal/Core/Render/SafeString.php new file mode 100644 index 000000000000..170331e18ac2 --- /dev/null +++ b/core/lib/Drupal/Core/Render/SafeString.php @@ -0,0 +1,84 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Render\SafeString. + */ + +namespace Drupal\Core\Render; + +use Drupal\Component\Utility\SafeStringInterface; +use Drupal\Component\Utility\Unicode; + +/** + * Defines an object that passes safe strings through the render system. + * + * This object should only be constructed with a known safe string. If there is + * any risk that the string contains user-entered data that has not been + * filtered first, it must not be used. + * + * @internal + * This object is marked as internal because it should only be used during + * rendering. Currently, there is no use case for this object by contrib or + * custom code. + * + * @see \Drupal\Core\Template\TwigExtension::escapeFilter + * @see \Twig_Markup + * @see \Drupal\Component\Utility\SafeMarkup + */ +class SafeString implements SafeStringInterface, \Countable { + + /** + * The safe string. + * + * @var string + */ + protected $string; + + /** + * Creates a SafeString object if necessary. + * + * If $string is equal to a blank string then it is not necessary to create a + * SafeString object. If $string is an object that implements + * SafeStringInterface it is returned unchanged. + * + * @param mixed $string + * The string to mark as safe. This value will be cast to a string. + * + * @return string|\Drupal\Component\Utility\SafeStringInterface + * A safe string. + */ + public static function create($string) { + if ($string instanceof SafeStringInterface) { + return $string; + } + $string = (string) $string; + if ($string === '') { + return ''; + } + $safe_string = new static(); + $safe_string->string = $string; + return $safe_string; + } + + /** + * Returns the string version of the SafeString object. + * + * @return string + * The safe string content. + */ + public function __toString() { + return $this->string; + } + + /** + * Returns the string length. + * + * @return int + * The length of the string. + */ + public function count() { + return Unicode::strlen($this->string); + } + +} diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php index e2c1a980d886..3a36db5b1bdb 100644 --- a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php +++ b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php @@ -7,6 +7,8 @@ namespace Drupal\Core\StringTranslation; +use Drupal\Component\Utility\SafeStringInterface; + /** * Provides a class to wrap a translatable string. * @@ -16,7 +18,7 @@ * * @see \Drupal\Core\Annotation\Translation */ -class TranslationWrapper { +class TranslationWrapper implements SafeStringInterface { use StringTranslationTrait; /** diff --git a/core/lib/Drupal/Core/Template/TwigEnvironment.php b/core/lib/Drupal/Core/Template/TwigEnvironment.php index 0af48ceaec1c..346dcc24b098 100644 --- a/core/lib/Drupal/Core/Template/TwigEnvironment.php +++ b/core/lib/Drupal/Core/Template/TwigEnvironment.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\PhpStorage\PhpStorageFactory; +use Drupal\Core\Render\SafeString; /** * A class that defines a Twig environment for Drupal. @@ -194,17 +195,15 @@ public function getTemplateClass($name, $index = NULL) { * @param array $context * An array of parameters to pass to the template. * - * @return string - * The rendered inline template. + * @return \Drupal\Component\Utility\SafeStringInterface|string + * The rendered inline template as a SafeString object. * * @see \Drupal\Core\Template\Loader\StringLoader::exists() */ public function renderInline($template_string, array $context = array()) { // Prefix all inline templates with a special comment. $template_string = '{# inline_template_start #}' . $template_string; - // @todo replace with object implementating SafeStringInterface in - // https://www.drupal.org/node/2506581. - return SafeMarkup::set($this->loadTemplate($template_string, NULL)->render($context)); + return SafeString::create($this->loadTemplate($template_string, NULL)->render($context)); } } diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php index 1f6e1f904783..a59539492d12 100644 --- a/core/lib/Drupal/Core/Utility/LinkGenerator.php +++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php @@ -9,6 +9,7 @@ use Drupal\Component\Serialization\Json; use Drupal\Component\Utility\SafeMarkup; +use Drupal\Component\Utility\SafeStringInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\GeneratedLink; use Drupal\Core\Link; @@ -106,6 +107,13 @@ public function generate($text, Url $url, $collect_bubbleable_metadata = FALSE) $variables['options']['attributes']['hreflang'] = $variables['options']['language']->getId(); } + // Ensure that query values are strings. + array_walk($variables['options']['query'], function(&$value) { + if ($value instanceof SafeStringInterface) { + $value = (string) $value; + } + }); + // Set the "active" class if the 'set_active_class' option is not empty. if (!empty($variables['options']['set_active_class']) && !$url->isExternal()) { // Add a "data-drupal-link-query" attribute to let the diff --git a/core/modules/block/src/Tests/BlockViewBuilderTest.php b/core/modules/block/src/Tests/BlockViewBuilderTest.php index 2dd2f402e19c..7e3c39a74aba 100644 --- a/core/modules/block/src/Tests/BlockViewBuilderTest.php +++ b/core/modules/block/src/Tests/BlockViewBuilderTest.php @@ -190,14 +190,14 @@ protected function verifyRenderCacheHandling() { public function testBlockViewBuilderAlter() { // Establish baseline. $build = $this->getBlockRenderArray(); - $this->assertIdentical($this->renderer->renderRoot($build), 'Llamas > unicorns!'); + $this->assertIdentical((string) $this->renderer->renderRoot($build), 'Llamas > unicorns!'); // Enable the block view alter hook that adds a suffix, for basic testing. \Drupal::state()->set('block_test_view_alter_suffix', TRUE); Cache::invalidateTags($this->block->getCacheTagsToInvalidate()); $build = $this->getBlockRenderArray(); $this->assertTrue(isset($build['#suffix']) && $build['#suffix'] === '<br>Goodbye!', 'A block with content is altered.'); - $this->assertIdentical($this->renderer->renderRoot($build), 'Llamas > unicorns!<br>Goodbye!'); + $this->assertIdentical((string) $this->renderer->renderRoot($build), 'Llamas > unicorns!<br>Goodbye!'); \Drupal::state()->set('block_test_view_alter_suffix', FALSE); // Force a request via GET so we can test the render cache. @@ -218,7 +218,7 @@ public function testBlockViewBuilderAlter() { $expected_keys = array_merge($default_keys, array($alter_add_key)); $build = $this->getBlockRenderArray(); $this->assertIdentical($expected_keys, $build['#cache']['keys'], 'An altered cacheable block has the expected cache keys.'); - $this->assertIdentical($this->renderer->renderRoot($build), ''); + $this->assertIdentical((string) $this->renderer->renderRoot($build), ''); $cache_entry = $this->container->get('cache.render')->get($cid); $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.'); $expected_tags = array_merge($default_tags, ['rendered']); @@ -233,7 +233,7 @@ public function testBlockViewBuilderAlter() { $build = $this->getBlockRenderArray(); sort($build['#cache']['tags']); $this->assertIdentical($expected_tags, $build['#cache']['tags'], 'An altered cacheable block has the expected cache tags.'); - $this->assertIdentical($this->renderer->renderRoot($build), ''); + $this->assertIdentical((string) $this->renderer->renderRoot($build), ''); $cache_entry = $this->container->get('cache.render')->get($cid); $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.'); $expected_tags = array_merge($default_tags, [$alter_add_tag, 'rendered']); @@ -246,7 +246,7 @@ public function testBlockViewBuilderAlter() { \Drupal::state()->set('block_test_view_alter_append_pre_render_prefix', TRUE); $build = $this->getBlockRenderArray(); $this->assertFalse(isset($build['#prefix']), 'The appended #pre_render callback has not yet run before rendering.'); - $this->assertIdentical($this->renderer->renderRoot($build), 'Hiya!<br>'); + $this->assertIdentical((string) $this->renderer->renderRoot($build), 'Hiya!<br>'); $this->assertTrue(isset($build['#prefix']) && $build['#prefix'] === 'Hiya!<br>', 'A cached block without content is altered.'); // Restore the previous request method. diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module index 5d4c26bc44ae..1f479f2bd602 100644 --- a/core/modules/contact/contact.module +++ b/core/modules/contact/contact.module @@ -143,7 +143,7 @@ function contact_mail($key, &$message, $params) { $message['subject'] .= t('[!form] !subject', $variables, $options); $message['body'][] = t("!sender-name (!sender-url) sent a message using the contact form at !form-url.", $variables, $options); $build = entity_view($contact_message, 'mail', $language->getId()); - $message['body'][] = \Drupal::service('renderer')->renderPlain($build); + $message['body'][] = (string) \Drupal::service('renderer')->renderPlain($build); break; case 'page_autoreply': @@ -162,7 +162,7 @@ function contact_mail($key, &$message, $params) { $message['body'][] = t("!sender-name (!sender-url) has sent you a message via your contact form at !site-name.", $variables, $options); $message['body'][] = t("If you don't want to receive such emails, you can change your settings at !recipient-edit-url.", $variables, $options); $build = entity_view($contact_message, 'mail', $language->getId()); - $message['body'][] = \Drupal::service('renderer')->renderPlain($build); + $message['body'][] = (string) \Drupal::service('renderer')->renderPlain($build); break; } } diff --git a/core/modules/contextual/src/ContextualController.php b/core/modules/contextual/src/ContextualController.php index 975113dc9d1d..ca248d196453 100644 --- a/core/modules/contextual/src/ContextualController.php +++ b/core/modules/contextual/src/ContextualController.php @@ -44,7 +44,7 @@ public function render(Request $request) { '#type' => 'contextual_links', '#contextual_links' => _contextual_id_to_links($id), ); - $rendered[$id] = $this->container->get('renderer')->renderRoot($element); + $rendered[$id] = (string) $this->container->get('renderer')->renderRoot($element); } return new JsonResponse($rendered); diff --git a/core/modules/editor/src/EditorController.php b/core/modules/editor/src/EditorController.php index 44feda30f836..192f3411e9bb 100644 --- a/core/modules/editor/src/EditorController.php +++ b/core/modules/editor/src/EditorController.php @@ -48,7 +48,7 @@ public function getUntransformedText(EntityInterface $entity, $field_name, $lang // Direct text editing is only supported for single-valued fields. $field = $entity->getTranslation($langcode)->$field_name; $editable_text = check_markup($field->value, $field->format, $langcode, array(FilterInterface::TYPE_TRANSFORM_REVERSIBLE, FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE)); - $response->addCommand(new GetUntransformedTextCommand($editable_text)); + $response->addCommand(new GetUntransformedTextCommand((string) $editable_text)); return $response; } diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module index b05144361e86..67dbd6ceb8c7 100644 --- a/core/modules/filter/filter.module +++ b/core/modules/filter/filter.module @@ -288,7 +288,7 @@ function filter_fallback_format() { * FilterInterface::TYPE_HTML_RESTRICTOR is the only type that cannot be * skipped. * - * @return string + * @return \Drupal\Component\Utility\SafeStringInterface * The filtered text. * * @see filter_process_text() diff --git a/core/modules/filter/src/Tests/FilterAPITest.php b/core/modules/filter/src/Tests/FilterAPITest.php index 4ad909b6a459..263b31ee48d6 100644 --- a/core/modules/filter/src/Tests/FilterAPITest.php +++ b/core/modules/filter/src/Tests/FilterAPITest.php @@ -60,7 +60,7 @@ function testCheckMarkupFilterOrder() { $text = "<p>Llamas are <not> awesome!</p>"; $expected_filtered_text = "<p>Llamas are awesome!</p>"; - $this->assertIdentical(check_markup($text, 'crazy'), $expected_filtered_text, 'Filters applied in correct order.'); + $this->assertEqual(check_markup($text, 'crazy'), $expected_filtered_text, 'Filters applied in correct order.'); } /** @@ -73,14 +73,14 @@ function testCheckMarkupFilterSubset() { $actual_filtered_text = check_markup($text, 'filtered_html', '', array()); $this->verbose("Actual:<pre>$actual_filtered_text</pre>Expected:<pre>$expected_filtered_text</pre>"); - $this->assertIdentical( + $this->assertEqual( $actual_filtered_text, $expected_filtered_text, 'Expected filter result.' ); $actual_filtered_text_without_html_generators = check_markup($text, 'filtered_html', '', array(FilterInterface::TYPE_MARKUP_LANGUAGE)); $this->verbose("Actual:<pre>$actual_filtered_text_without_html_generators</pre>Expected:<pre>$expected_filter_text_without_html_generators</pre>"); - $this->assertIdentical( + $this->assertEqual( $actual_filtered_text_without_html_generators, $expected_filter_text_without_html_generators, 'Expected filter result when skipping FilterInterface::TYPE_MARKUP_LANGUAGE filters.' @@ -91,7 +91,7 @@ function testCheckMarkupFilterSubset() { // most extensive test possible. $actual_filtered_text_without_html_generators = check_markup($text, 'filtered_html', '', array(FilterInterface::TYPE_HTML_RESTRICTOR, FilterInterface::TYPE_MARKUP_LANGUAGE)); $this->verbose("Actual:<pre>$actual_filtered_text_without_html_generators</pre>Expected:<pre>$expected_filter_text_without_html_generators</pre>"); - $this->assertIdentical( + $this->assertEqual( $actual_filtered_text_without_html_generators, $expected_filter_text_without_html_generators, 'Expected filter result when skipping FilterInterface::TYPE_MARKUP_LANGUAGE filters, even when trying to disable filters of the FilterInterface::TYPE_HTML_RESTRICTOR type.' diff --git a/core/modules/quickedit/src/QuickEditController.php b/core/modules/quickedit/src/QuickEditController.php index 383f61b8151b..067a73a7c322 100644 --- a/core/modules/quickedit/src/QuickEditController.php +++ b/core/modules/quickedit/src/QuickEditController.php @@ -216,7 +216,7 @@ public function fieldForm(EntityInterface $entity, $field_name, $langcode, $view $response->addCommand(new FieldFormSavedCommand($output, $other_view_modes)); } else { - $output = $this->renderer->renderRoot($form); + $output = (string) $this->renderer->renderRoot($form); // When working with a hidden form, we don't want its CSS/JS to be loaded. if ($request->request->get('nocssjs') !== 'true') { $response->setAttachments($form['#attached']); @@ -228,7 +228,7 @@ public function fieldForm(EntityInterface $entity, $field_name, $langcode, $view $status_messages = array( '#type' => 'status_messages' ); - $response->addCommand(new FieldFormValidationErrorsCommand($this->renderer->renderRoot($status_messages))); + $response->addCommand(new FieldFormValidationErrorsCommand((string) $this->renderer->renderRoot($status_messages))); } } @@ -275,7 +275,7 @@ protected function renderField(EntityInterface $entity, $field_name, $langcode, $output = $this->moduleHandler()->invoke($module, 'quickedit_render_field', $args); } - return $this->renderer->renderRoot($output); + return (string) $this->renderer->renderRoot($output); } /** diff --git a/core/modules/rest/src/Tests/Views/StyleSerializerTest.php b/core/modules/rest/src/Tests/Views/StyleSerializerTest.php index 1eca0d2e8b15..a0f482adc63d 100644 --- a/core/modules/rest/src/Tests/Views/StyleSerializerTest.php +++ b/core/modules/rest/src/Tests/Views/StyleSerializerTest.php @@ -108,7 +108,7 @@ public function testSerializerResponses() { // Mock the request content type by setting it on the display handler. $view->display_handler->setContentType('json'); $output = $view->preview(); - $this->assertIdentical($actual_json, drupal_render_root($output), 'The expected JSON preview output was found.'); + $this->assertIdentical($actual_json, (string) drupal_render_root($output), 'The expected JSON preview output was found.'); // Test a 403 callback. $this->drupalGet('test/serialize/denied'); diff --git a/core/modules/simpletest/src/AssertContentTrait.php b/core/modules/simpletest/src/AssertContentTrait.php index 99f7ca38970e..a425f4f954c6 100644 --- a/core/modules/simpletest/src/AssertContentTrait.php +++ b/core/modules/simpletest/src/AssertContentTrait.php @@ -398,7 +398,7 @@ protected function assertRaw($raw, $message = '', $group = 'Other') { if (!$message) { $message = SafeMarkup::format('Raw "@raw" found', array('@raw' => $raw)); } - return $this->assert(strpos($this->getRawContent(), $raw) !== FALSE, $message, $group); + return $this->assert(strpos($this->getRawContent(), (string) $raw) !== FALSE, $message, $group); } /** @@ -425,7 +425,7 @@ protected function assertNoRaw($raw, $message = '', $group = 'Other') { if (!$message) { $message = SafeMarkup::format('Raw "@raw" not found', array('@raw' => $raw)); } - return $this->assert(strpos($this->getRawContent(), $raw) === FALSE, $message, $group); + return $this->assert(strpos($this->getRawContent(), (string) $raw) === FALSE, $message, $group); } /** diff --git a/core/modules/system/src/Tests/Common/RenderElementTypesTest.php b/core/modules/system/src/Tests/Common/RenderElementTypesTest.php index d7c42cd10238..9c250fca7459 100644 --- a/core/modules/system/src/Tests/Common/RenderElementTypesTest.php +++ b/core/modules/system/src/Tests/Common/RenderElementTypesTest.php @@ -43,7 +43,7 @@ protected function setUp() { * Assertion message. */ protected function assertElements(array $elements, $expected_html, $message) { - $actual_html = \Drupal::service('renderer')->renderRoot($elements); + $actual_html = (string) \Drupal::service('renderer')->renderRoot($elements); $out = '<table><tr>'; $out .= '<td valign="top"><pre>' . SafeMarkup::checkPlain($expected_html) . '</pre></td>'; diff --git a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php b/core/modules/system/src/Tests/Entity/EntityTranslationTest.php index 11ec5898cca3..88f21c932e1c 100644 --- a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php +++ b/core/modules/system/src/Tests/Entity/EntityTranslationTest.php @@ -612,10 +612,10 @@ protected function doTestLanguageFallback($entity_type) { // Get an view builder. $controller = $this->entityManager->getViewBuilder($entity_type); $entity2_build = $controller->view($entity2); - $entity2_output = $renderer->renderRoot($entity2_build); + $entity2_output = (string) $renderer->renderRoot($entity2_build); $translation = $this->entityManager->getTranslationFromContext($entity2, $default_langcode); $translation_build = $controller->view($translation); - $translation_output = $renderer->renderRoot($translation_build); + $translation_output = (string) $renderer->renderRoot($translation_build); $this->assertIdentical($entity2_output, $translation_output, 'When the entity has no translation no fallback is applied.'); // Checks that entity translations are rendered properly. diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php index 69c4f19fceb7..e56dcae7c9ee 100644 --- a/core/modules/views/src/Plugin/views/PluginBase.php +++ b/core/modules/views/src/Plugin/views/PluginBase.php @@ -390,7 +390,7 @@ function ($children, $elements) { ], ); - return $this->getRenderer()->render($build); + return (string) $this->getRenderer()->render($build); } else { return $text; diff --git a/core/modules/views/src/Plugin/views/display/Feed.php b/core/modules/views/src/Plugin/views/display/Feed.php index 0798690bd995..e4da10e25b7d 100644 --- a/core/modules/views/src/Plugin/views/display/Feed.php +++ b/core/modules/views/src/Plugin/views/display/Feed.php @@ -67,7 +67,7 @@ public static function buildResponse($view_id, $display_id, array $args = []) { /** @var \Drupal\Core\Render\RendererInterface $renderer */ $renderer = \Drupal::service('renderer'); - $output = $renderer->renderRoot($build); + $output = (string) $renderer->renderRoot($build); if (empty($output)) { throw new NotFoundHttpException(); diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php index 9bd01c4ef290..c83b9c4677f0 100644 --- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php +++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php @@ -16,6 +16,7 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Renderer; +use Drupal\Core\Render\SafeString; use Drupal\Core\Url as CoreUrl; use Drupal\views\Plugin\views\HandlerBase; use Drupal\views\Plugin\views\display\DisplayPluginBase; @@ -1137,7 +1138,7 @@ public function advancedRender(ResultRow $values) { else { $value = $this->render($values); if (is_array($value)) { - $value = $this->getRenderer()->render($value); + $value = (string) $this->getRenderer()->render($value); } $this->last_render = $value; $this->original_value = $value; @@ -1150,7 +1151,7 @@ public function advancedRender(ResultRow $values) { foreach ($raw_items as $count => $item) { $value = $this->render_item($count, $item); if (is_array($value)) { - $value = $this->getRenderer()->render($value); + $value = (string) $this->getRenderer()->render($value); } $this->last_render = $value; $this->original_value = $this->last_render; @@ -1168,7 +1169,7 @@ public function advancedRender(ResultRow $values) { } if (is_array($value)) { - $value = $this->getRenderer()->render($value); + $value = (string) $this->getRenderer()->render($value); } // This happens here so that renderAsLink can get the unaltered value of // this field as a token rather than the altered value. @@ -1290,7 +1291,7 @@ public function renderText($alter) { * Render this field as user-defined altered text. */ protected function renderAltered($alter, $tokens) { - return $this->viewsTokenReplace($alter['text'], $tokens); + return SafeString::create($this->viewsTokenReplace($alter['text'], $tokens)); } /** diff --git a/core/modules/views/src/Plugin/views/row/OpmlFields.php b/core/modules/views/src/Plugin/views/row/OpmlFields.php index 2e936f58e4f5..4654bcad724f 100644 --- a/core/modules/views/src/Plugin/views/row/OpmlFields.php +++ b/core/modules/views/src/Plugin/views/row/OpmlFields.php @@ -217,7 +217,7 @@ public function getField($index, $field_id) { if (empty($this->view->style_plugin) || !is_object($this->view->style_plugin) || empty($field_id)) { return ''; } - return $this->view->style_plugin->getField($index, $field_id); + return (string) $this->view->style_plugin->getField($index, $field_id); } } diff --git a/core/modules/views/src/Plugin/views/style/StylePluginBase.php b/core/modules/views/src/Plugin/views/style/StylePluginBase.php index e1e977cf2b29..3bb472fb5bdf 100644 --- a/core/modules/views/src/Plugin/views/style/StylePluginBase.php +++ b/core/modules/views/src/Plugin/views/style/StylePluginBase.php @@ -8,10 +8,10 @@ namespace Drupal\views\Plugin\views\style; use Drupal\Component\Utility\Html; -use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\Xss; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; +use Drupal\Core\Render\SafeString; use Drupal\views\Plugin\views\display\DisplayPluginBase; use Drupal\views\Plugin\views\PluginBase; use Drupal\views\Plugin\views\wizard\WizardInterface; @@ -706,8 +706,9 @@ protected function renderFields(array $result) { $placeholders = array_keys($post_render_tokens); $values = array_values($post_render_tokens); foreach ($this->rendered_fields[$index] as &$rendered_field) { - $rendered_field = str_replace($placeholders, $values, $rendered_field); - SafeMarkup::set($rendered_field); + // Placeholders and rendered fields have been processed by the + // render system and are therefore safe. + $rendered_field = SafeString::create(str_replace($placeholders, $values, $rendered_field)); } } } @@ -744,7 +745,7 @@ public function elementPreRenderRow(array $data) { * @param string $field * The ID of the field. * - * @return string|null + * @return \Drupal\Core\Render\SafeString|null * The output of the field, or NULL if it was empty. */ public function getField($index, $field) { diff --git a/core/modules/views/src/Tests/Handler/FieldCounterTest.php b/core/modules/views/src/Tests/Handler/FieldCounterTest.php index 4d1eac1fd5cb..9fe24743cd3e 100644 --- a/core/modules/views/src/Tests/Handler/FieldCounterTest.php +++ b/core/modules/views/src/Tests/Handler/FieldCounterTest.php @@ -51,11 +51,11 @@ function testSimple() { $view->preview(); $counter = $view->style_plugin->getField(0, 'counter'); - $this->assertEqual($counter, 1, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 1, '@counter' => $counter))); + $this->assertEqual($counter, '1', format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 1, '@counter' => $counter))); $counter = $view->style_plugin->getField(1, 'counter'); - $this->assertEqual($counter, 2, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 2, '@counter' => $counter))); + $this->assertEqual($counter, '2', format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 2, '@counter' => $counter))); $counter = $view->style_plugin->getField(2, 'counter'); - $this->assertEqual($counter, 3, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 3, '@counter' => $counter))); + $this->assertEqual($counter, '3', format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 3, '@counter' => $counter))); $view->destroy(); $view->storage->invalidateCaches(); @@ -80,13 +80,13 @@ function testSimple() { $counter = $view->style_plugin->getField(0, 'counter'); $expected_number = 0 + $rand_start; - $this->assertEqual($counter, $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); + $this->assertEqual($counter, (string) $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); $counter = $view->style_plugin->getField(1, 'counter'); $expected_number = 1 + $rand_start; - $this->assertEqual($counter, $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); + $this->assertEqual($counter, (string) $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); $counter = $view->style_plugin->getField(2, 'counter'); $expected_number = 2 + $rand_start; - $this->assertEqual($counter, $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); + $this->assertEqual($counter, (string) $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); } // @TODO: Write tests for pager. diff --git a/core/modules/views/src/Tests/Handler/FieldFieldTest.php b/core/modules/views/src/Tests/Handler/FieldFieldTest.php index 8ad0bd14ba4e..599338581937 100644 --- a/core/modules/views/src/Tests/Handler/FieldFieldTest.php +++ b/core/modules/views/src/Tests/Handler/FieldFieldTest.php @@ -251,18 +251,18 @@ public function testSimpleRender() { $executable = Views::getView('test_field_field_test'); $executable->execute(); - $this->assertEqual(1, $executable->getStyle()->getField(0, 'id')); - $this->assertEqual(3, $executable->getStyle()->getField(0, 'field_test')); - $this->assertEqual(2, $executable->getStyle()->getField(1, 'id')); + $this->assertEqual('1', $executable->getStyle()->getField(0, 'id')); + $this->assertEqual('3', $executable->getStyle()->getField(0, 'field_test')); + $this->assertEqual('2', $executable->getStyle()->getField(1, 'id')); // @todo Switch this assertion to assertIdentical('', ...) when // https://www.drupal.org/node/2488006 gets fixed. - $this->assertEqual(0, $executable->getStyle()->getField(1, 'field_test')); - $this->assertEqual(3, $executable->getStyle()->getField(2, 'id')); - $this->assertEqual(8, $executable->getStyle()->getField(2, 'field_test')); - $this->assertEqual(4, $executable->getStyle()->getField(3, 'id')); - $this->assertEqual(5, $executable->getStyle()->getField(3, 'field_test')); - $this->assertEqual(5, $executable->getStyle()->getField(4, 'id')); - $this->assertEqual(6, $executable->getStyle()->getField(4, 'field_test')); + $this->assertEqual('0', $executable->getStyle()->getField(1, 'field_test')); + $this->assertEqual('3', $executable->getStyle()->getField(2, 'id')); + $this->assertEqual('8', $executable->getStyle()->getField(2, 'field_test')); + $this->assertEqual('4', $executable->getStyle()->getField(3, 'id')); + $this->assertEqual('5', $executable->getStyle()->getField(3, 'field_test')); + $this->assertEqual('5', $executable->getStyle()->getField(4, 'id')); + $this->assertEqual('6', $executable->getStyle()->getField(4, 'field_test')); } /** @@ -326,10 +326,10 @@ public function testFieldAliasRender() { $executable->execute(); for ($i = 0; $i < 5; $i++) { - $this->assertEqual($i + 1, $executable->getStyle()->getField($i, 'id')); + $this->assertEqual((string) ($i + 1), $executable->getStyle()->getField($i, 'id')); $this->assertEqual('test ' . $i, $executable->getStyle()->getField($i, 'name')); $entity = EntityTest::load($i + 1); - $this->assertEqual('<a href="' . $entity->url() . '" hreflang="' . $entity->language()->getId() . '">test ' . $i . '</a>', $executable->getStyle()->getField($i, 'name_alias')); + $this->assertEqual('<a href="' . $entity->url() . '" hreflang="' . $entity->language()->getId() . '">test ' . $i . '</a>', (string) $executable->getStyle()->getField($i, 'name_alias')); } } @@ -426,24 +426,24 @@ public function testRevisionRender() { $executable = Views::getView('test_field_field_revision_test'); $executable->execute(); - $this->assertEqual(1, $executable->getStyle()->getField(0, 'id')); - $this->assertEqual(1, $executable->getStyle()->getField(0, 'revision_id')); - $this->assertEqual(1, $executable->getStyle()->getField(0, 'field_test')); + $this->assertEqual('1', $executable->getStyle()->getField(0, 'id')); + $this->assertEqual('1', $executable->getStyle()->getField(0, 'revision_id')); + $this->assertEqual('1', $executable->getStyle()->getField(0, 'field_test')); $this->assertEqual('base value', $executable->getStyle()->getField(0, 'name')); - $this->assertEqual(1, $executable->getStyle()->getField(1, 'id')); - $this->assertEqual(2, $executable->getStyle()->getField(1, 'revision_id')); - $this->assertEqual(2, $executable->getStyle()->getField(1, 'field_test')); + $this->assertEqual('1', $executable->getStyle()->getField(1, 'id')); + $this->assertEqual('2', $executable->getStyle()->getField(1, 'revision_id')); + $this->assertEqual('2', $executable->getStyle()->getField(1, 'field_test')); $this->assertEqual('revision value1', $executable->getStyle()->getField(1, 'name')); - $this->assertEqual(1, $executable->getStyle()->getField(2, 'id')); - $this->assertEqual(3, $executable->getStyle()->getField(2, 'revision_id')); - $this->assertEqual(3, $executable->getStyle()->getField(2, 'field_test')); + $this->assertEqual('1', $executable->getStyle()->getField(2, 'id')); + $this->assertEqual('3', $executable->getStyle()->getField(2, 'revision_id')); + $this->assertEqual('3', $executable->getStyle()->getField(2, 'field_test')); $this->assertEqual('revision value2', $executable->getStyle()->getField(2, 'name')); - $this->assertEqual(2, $executable->getStyle()->getField(3, 'id')); - $this->assertEqual(4, $executable->getStyle()->getField(3, 'revision_id')); - $this->assertEqual(4, $executable->getStyle()->getField(3, 'field_test')); + $this->assertEqual('2', $executable->getStyle()->getField(3, 'id')); + $this->assertEqual('4', $executable->getStyle()->getField(3, 'revision_id')); + $this->assertEqual('4', $executable->getStyle()->getField(3, 'field_test')); $this->assertEqual('next entity value', $executable->getStyle()->getField(3, 'name')); } @@ -484,29 +484,29 @@ public function testRevisionComplexRender() { $executable = Views::getView('test_field_field_revision_complex_test'); $executable->execute(); - $this->assertEqual(1, $executable->getStyle()->getField(0, 'id')); - $this->assertEqual(1, $executable->getStyle()->getField(0, 'revision_id')); + $this->assertEqual('1', $executable->getStyle()->getField(0, 'id')); + $this->assertEqual('1', $executable->getStyle()->getField(0, 'revision_id')); $this->assertEqual($this->testUsers[0]->getTimeZone(), $executable->getStyle()->getField(0, 'timezone')); $this->assertEqual('1, 3, 7', $executable->getStyle()->getField(0, 'field_test_multiple')); $this->assertEqual('1', $executable->getStyle()->getField(0, 'field_test_multiple_1')); $this->assertEqual('3, 7', $executable->getStyle()->getField(0, 'field_test_multiple_2')); - $this->assertEqual(1, $executable->getStyle()->getField(1, 'id')); - $this->assertEqual(2, $executable->getStyle()->getField(1, 'revision_id')); + $this->assertEqual('1', $executable->getStyle()->getField(1, 'id')); + $this->assertEqual('2', $executable->getStyle()->getField(1, 'revision_id')); $this->assertEqual($this->testUsers[1]->getTimeZone(), $executable->getStyle()->getField(1, 'timezone')); $this->assertEqual('0, 3, 5', $executable->getStyle()->getField(1, 'field_test_multiple')); $this->assertEqual('0', $executable->getStyle()->getField(1, 'field_test_multiple_1')); $this->assertEqual('3, 5', $executable->getStyle()->getField(1, 'field_test_multiple_2')); - $this->assertEqual(1, $executable->getStyle()->getField(2, 'id')); - $this->assertEqual(3, $executable->getStyle()->getField(2, 'revision_id')); + $this->assertEqual('1', $executable->getStyle()->getField(2, 'id')); + $this->assertEqual('3', $executable->getStyle()->getField(2, 'revision_id')); $this->assertEqual($this->testUsers[2]->getTimeZone(), $executable->getStyle()->getField(2, 'timezone')); $this->assertEqual('9, 9, 9', $executable->getStyle()->getField(2, 'field_test_multiple')); $this->assertEqual('9', $executable->getStyle()->getField(2, 'field_test_multiple_1')); $this->assertEqual('9, 9', $executable->getStyle()->getField(2, 'field_test_multiple_2')); - $this->assertEqual(2, $executable->getStyle()->getField(3, 'id')); - $this->assertEqual(4, $executable->getStyle()->getField(3, 'revision_id')); + $this->assertEqual('2', $executable->getStyle()->getField(3, 'id')); + $this->assertEqual('4', $executable->getStyle()->getField(3, 'revision_id')); $this->assertEqual($this->testUsers[3]->getTimeZone(), $executable->getStyle()->getField(3, 'timezone')); $this->assertEqual('2, 9, 9', $executable->getStyle()->getField(3, 'field_test_multiple')); $this->assertEqual('2', $executable->getStyle()->getField(3, 'field_test_multiple_1')); @@ -531,9 +531,7 @@ public function testMissingBundleFieldRender() { $executable = Views::getView('test_field_field_test'); $executable->execute(); - // @todo Switch this assertion to assertIdentical('', ...) when - // https://www.drupal.org/node/2488006 gets fixed. - $this->assertEqual(0, $executable->getStyle()->getField(1, 'field_test')); + $this->assertEqual('', $executable->getStyle()->getField(1, 'field_test')); } } diff --git a/core/modules/views/src/Tests/QueryGroupByTest.php b/core/modules/views/src/Tests/QueryGroupByTest.php index 768aaafbf0c7..4fb2062115ff 100644 --- a/core/modules/views/src/Tests/QueryGroupByTest.php +++ b/core/modules/views/src/Tests/QueryGroupByTest.php @@ -253,9 +253,9 @@ public function testGroupByFieldWithCardinality() { $this->executeView($view); $this->assertEqual(2, count($view->result)); - $this->assertEqual(3, $view->getStyle()->getField(0, 'id')); + $this->assertEqual('3', $view->getStyle()->getField(0, 'id')); $this->assertEqual('1', $view->getStyle()->getField(0, 'field_test')); - $this->assertEqual(6, $view->getStyle()->getField(1, 'id')); + $this->assertEqual('6', $view->getStyle()->getField(1, 'id')); $this->assertEqual('2', $view->getStyle()->getField(1, 'field_test')); $entities[2]->field_test[0]->value = 3; @@ -267,15 +267,15 @@ public function testGroupByFieldWithCardinality() { $this->executeView($view); $this->assertEqual(5, count($view->result)); - $this->assertEqual(3, $view->getStyle()->getField(0, 'id')); + $this->assertEqual('3', $view->getStyle()->getField(0, 'id')); $this->assertEqual('1', $view->getStyle()->getField(0, 'field_test')); - $this->assertEqual(3, $view->getStyle()->getField(1, 'id')); + $this->assertEqual('3', $view->getStyle()->getField(1, 'id')); $this->assertEqual('2', $view->getStyle()->getField(1, 'field_test')); - $this->assertEqual(1, $view->getStyle()->getField(2, 'id')); + $this->assertEqual('1', $view->getStyle()->getField(2, 'id')); $this->assertEqual('3', $view->getStyle()->getField(2, 'field_test')); - $this->assertEqual(1, $view->getStyle()->getField(3, 'id')); + $this->assertEqual('1', $view->getStyle()->getField(3, 'id')); $this->assertEqual('4', $view->getStyle()->getField(3, 'field_test')); - $this->assertEqual(1, $view->getStyle()->getField(4, 'id')); + $this->assertEqual('1', $view->getStyle()->getField(4, 'id')); $this->assertEqual('5', $view->getStyle()->getField(4, 'field_test')); // Check that translated values are correctly retrieved and are not grouped @@ -288,7 +288,7 @@ public function testGroupByFieldWithCardinality() { $this->executeView($view); $this->assertEqual(6, count($view->result)); - $this->assertEqual(3, $view->getStyle()->getField(5, 'id')); + $this->assertEqual('3', $view->getStyle()->getField(5, 'id')); $this->assertEqual('6', $view->getStyle()->getField(5, 'field_test')); } diff --git a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php index 0f39b10dfd34..d659849f4713 100644 --- a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php +++ b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php @@ -179,7 +179,6 @@ public function providerTestContextBubblingEdgeCases() { 'max-age' => Cache::PERMANENT, ], '#markup' => 'parent', - '#safe_cache_properties' => [], ], ]; $data[] = [$test_element, [], $expected_cache_items]; @@ -203,7 +202,6 @@ public function providerTestContextBubblingEdgeCases() { 'max-age' => Cache::PERMANENT, ], '#markup' => '', - '#safe_cache_properties' => [], ], ]; $context_orders = [ @@ -245,7 +243,6 @@ public function providerTestContextBubblingEdgeCases() { 'max-age' => 3600, ], '#markup' => 'parent', - '#safe_cache_properties' => [], ], ]; $data[] = [$test_element, ['bar', 'baz', 'foo'], $expected_cache_items]; @@ -294,7 +291,6 @@ public function providerTestContextBubblingEdgeCases() { 'max-age' => Cache::PERMANENT, ], '#markup' => 'parent', - '#safe_cache_properties' => [], ], ]; $data[] = [$test_element, ['bar', 'foo'], $expected_cache_items]; @@ -374,7 +370,6 @@ public function testConditionalCacheContextBubblingSelfHealing() { 'max-age' => Cache::PERMANENT, ], '#markup' => 'parent', - '#safe_cache_properties' => [], ]); // Request 2: role B, the grandchild is accessible => bubbled cache @@ -400,7 +395,6 @@ public function testConditionalCacheContextBubblingSelfHealing() { 'max-age' => 1800, ], '#markup' => 'parent', - '#safe_cache_properties' => [], ]); // Request 3: role A again, the grandchild is inaccessible again => bubbled @@ -437,7 +431,6 @@ public function testConditionalCacheContextBubblingSelfHealing() { 'max-age' => Cache::PERMANENT, ], '#markup' => 'parent', - '#safe_cache_properties' => [], ]); // Request 4: role C, both the grandchild and the grandgrandchild are @@ -465,7 +458,6 @@ public function testConditionalCacheContextBubblingSelfHealing() { 'max-age' => 300, ], '#markup' => 'parent', - '#safe_cache_properties' => [], ]); // Request 5: role A again, verifying the merging like we did for request 3. @@ -485,7 +477,6 @@ public function testConditionalCacheContextBubblingSelfHealing() { 'max-age' => Cache::PERMANENT, ], '#markup' => 'parent', - '#safe_cache_properties' => [], ]); // Request 6: role B again, verifying the merging like we did for request 3. @@ -505,7 +496,6 @@ public function testConditionalCacheContextBubblingSelfHealing() { 'max-age' => 1800, ], '#markup' => 'parent', - '#safe_cache_properties' => [], ]); } @@ -539,7 +529,7 @@ public function testBubblingWithPrerender($test_element) { // - … is not cached DOES get called. \Drupal::state()->set('bubbling_nested_pre_render_cached', FALSE); \Drupal::state()->set('bubbling_nested_pre_render_uncached', FALSE); - $this->memoryCache->set('cached_nested', ['#markup' => 'Cached nested!', '#attached' => [], '#cache' => ['contexts' => [], 'tags' => []], '#safe_cache_properties' => ['#markup']]); + $this->memoryCache->set('cached_nested', ['#markup' => 'Cached nested!', '#attached' => [], '#cache' => ['contexts' => [], 'tags' => []]]); // Simulate the rendering of an entire response (i.e. a root call). $output = $this->renderer->renderRoot($test_element); diff --git a/core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php b/core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php index 8012a907f18b..9e3aaa76d65f 100644 --- a/core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php +++ b/core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php @@ -137,7 +137,6 @@ public function providerPlaceholders() { 'tags' => [], 'max-age' => Cache::PERMANENT, ], - '#safe_cache_properties' => [], ], ]; @@ -180,7 +179,6 @@ public function providerPlaceholders() { 'tags' => [], 'max-age' => Cache::PERMANENT, ], - '#safe_cache_properties' => [], ], ]; @@ -217,7 +215,7 @@ protected function assertPlaceholderRenderCache($cid_parts, array $expected_data if ($cid_parts !== FALSE) { // Verify render cached placeholder. $cached_element = $this->memoryCache->get(implode(':', $cid_parts))->data; - $this->assertSame($expected_data, $cached_element, 'The correct data is cached: the stored #markup and #attached properties are not affected by the placeholder being replaced.'); + $this->assertEquals($expected_data, $cached_element, 'The correct data is cached: the stored #markup and #attached properties are not affected by the placeholder being replaced.'); } } /** @@ -239,8 +237,8 @@ public function testUncacheableParent($element, $args, $placeholder_cid_keys, ar // No #cache on parent element. $element['#prefix'] = '<p>#cache disabled</p>'; $output = $this->renderer->renderRoot($element); - $this->assertSame('<p>#cache disabled</p><p>This is a rendered placeholder!</p>', $output, 'Output is overridden.'); - $this->assertSame('<p>#cache disabled</p><p>This is a rendered placeholder!</p>', $element['#markup'], '#markup is overridden.'); + $this->assertSame('<p>#cache disabled</p><p>This is a rendered placeholder!</p>', (string) $output, 'Output is overridden.'); + $this->assertSame('<p>#cache disabled</p><p>This is a rendered placeholder!</p>', (string) $element['#markup'], '#markup is overridden.'); $expected_js_settings = [ 'foo' => 'bar', 'dynamic_animal' => $args[0], @@ -285,9 +283,9 @@ public function testCacheableParent($test_element, $args, $placeholder_cid_keys, $element['#cache'] = ['keys' => ['placeholder_test_GET']]; $element['#prefix'] = '<p>#cache enabled, GET</p>'; $output = $this->renderer->renderRoot($element); - $this->assertSame('<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>', $output, 'Output is overridden.'); + $this->assertSame('<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>', (string) $output, 'Output is overridden.'); $this->assertTrue(isset($element['#printed']), 'No cache hit'); - $this->assertSame('<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>', $element['#markup'], '#markup is overridden.'); + $this->assertSame('<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>', (string) $element['#markup'], '#markup is overridden.'); $expected_js_settings = [ 'foo' => 'bar', 'dynamic_animal' => $args[0], @@ -314,7 +312,6 @@ public function testCacheableParent($test_element, $args, $placeholder_cid_keys, 'tags' => [], 'max-age' => Cache::PERMANENT, ], - '#safe_cache_properties' => [], ]; $expected_element['#attached']['placeholders'][$expected_placeholder_markup] = $expected_placeholder_render_array; $this->assertEquals($cached_element, $expected_element, 'The correct data is cached: the stored #markup and #attached properties are not affected by placeholder #lazy_builder callbacks.'); @@ -324,9 +321,9 @@ public function testCacheableParent($test_element, $args, $placeholder_cid_keys, $element['#cache'] = ['keys' => ['placeholder_test_GET']]; $element['#prefix'] = '<p>#cache enabled, GET</p>'; $output = $this->renderer->renderRoot($element); - $this->assertSame('<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>', $output, 'Output is overridden.'); + $this->assertSame('<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>', (string) $output, 'Output is overridden.'); $this->assertFalse(isset($element['#printed']), 'Cache hit'); - $this->assertSame('<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>', $element['#markup'], '#markup is overridden.'); + $this->assertSame('<p>#cache enabled, GET</p><p>This is a rendered placeholder!</p>', (string) $element['#markup'], '#markup is overridden.'); $expected_js_settings = [ 'foo' => 'bar', 'dynamic_animal' => $args[0], @@ -354,9 +351,9 @@ public function testCacheableParentWithPostRequest($test_element, $args) { $element['#cache'] = ['keys' => ['placeholder_test_POST']]; $element['#prefix'] = '<p>#cache enabled, POST</p>'; $output = $this->renderer->renderRoot($element); - $this->assertSame('<p>#cache enabled, POST</p><p>This is a rendered placeholder!</p>', $output, 'Output is overridden.'); + $this->assertSame('<p>#cache enabled, POST</p><p>This is a rendered placeholder!</p>', (string) $output, 'Output is overridden.'); $this->assertTrue(isset($element['#printed']), 'No cache hit'); - $this->assertSame('<p>#cache enabled, POST</p><p>This is a rendered placeholder!</p>', $element['#markup'], '#markup is overridden.'); + $this->assertSame('<p>#cache enabled, POST</p><p>This is a rendered placeholder!</p>', (string) $element['#markup'], '#markup is overridden.'); $expected_js_settings = [ 'foo' => 'bar', 'dynamic_animal' => $args[0], @@ -386,7 +383,7 @@ public function testRecursivePlaceholder() { $output = $this->renderer->renderRoot($element); $this->assertEquals('<p>This is a rendered placeholder!</p>', $output, 'The output has been modified by the indirect, recursive placeholder #lazy_builder callback.'); - $this->assertSame($element['#markup'], '<p>This is a rendered placeholder!</p>', '#markup is overridden by the indirect, recursive placeholder #lazy_builder callback.'); + $this->assertSame((string) $element['#markup'], '<p>This is a rendered placeholder!</p>', '#markup is overridden by the indirect, recursive placeholder #lazy_builder callback.'); $expected_js_settings = [ 'dynamic_animal' => $args[0], ]; @@ -549,9 +546,9 @@ public function testRenderChildrenPlaceholdersDifferentArguments() { </details></div> </details> HTML; - $this->assertSame($expected_output, $output, 'Output is not overridden.'); + $this->assertSame($expected_output, (string) $output, 'Output is not overridden.'); $this->assertTrue(isset($element['#printed']), 'No cache hit'); - $this->assertSame($expected_output, $output, '#markup is not overridden.'); + $this->assertSame($expected_output, (string) $element['#markup'], '#markup is not overridden.'); $expected_js_settings = [ 'foo' => 'bar', 'dynamic_animal' => [$args_1[0] => TRUE, $args_2[0] => TRUE, $args_3[0] => TRUE], @@ -582,7 +579,6 @@ public function testRenderChildrenPlaceholdersDifferentArguments() { 'tags' => [], 'max-age' => Cache::PERMANENT, ], - '#safe_cache_properties' => [], ]; $dom = Html::load($cached_element['#markup']); @@ -599,7 +595,7 @@ public function testRenderChildrenPlaceholdersDifferentArguments() { // GET request: #cache enabled, cache hit. $element = $test_element; $output = $this->renderer->renderRoot($element); - $this->assertSame($expected_output, $output, 'Output is not overridden.'); + $this->assertSame($expected_output, (string) $output, 'Output is not overridden.'); $this->assertFalse(isset($element['#printed']), 'Cache hit'); $this->assertSame($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; both the original JavaScript setting and the ones added by each placeholder #lazy_builder callback exist.'); @@ -608,8 +604,8 @@ public function testRenderChildrenPlaceholdersDifferentArguments() { unset($test_element['#cache']); $element = $test_element; $output = $this->renderer->renderRoot($element); - $this->assertSame($expected_output, $output, 'Output is not overridden.'); - $this->assertSame($expected_output, $output, '#markup is not overridden.'); + $this->assertSame($expected_output, (string) $output, 'Output is not overridden.'); + $this->assertSame($expected_output, (string) $element['#markup'], '#markup is not overridden.'); $this->assertSame($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; both the original JavaScript setting and the ones added by each #lazy_builder callback exist.'); } diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTest.php b/core/tests/Drupal/Tests/Core/Render/RendererTest.php index 8ffe61fbf7fe..c6845f560ddb 100644 --- a/core/tests/Drupal/Tests/Core/Render/RendererTest.php +++ b/core/tests/Drupal/Tests/Core/Render/RendererTest.php @@ -13,6 +13,7 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Render\Element; +use Drupal\Core\Render\SafeString; use Drupal\Core\Template\Attribute; /** @@ -46,7 +47,7 @@ public function testRenderBasic($build, $expected, callable $setup_code = NULL) $setup_code(); } - $this->assertSame($expected, $this->renderer->renderRoot($build)); + $this->assertSame($expected, (string) $this->renderer->renderRoot($build)); } /** @@ -508,10 +509,10 @@ protected function assertAccess($build, $access) { $sensitive_content = $this->randomContextValue(); $build['#markup'] = $sensitive_content; if (($access instanceof AccessResultInterface && $access->isAllowed()) || $access === TRUE) { - $this->assertSame($sensitive_content, $this->renderer->renderRoot($build)); + $this->assertSame($sensitive_content, (string) $this->renderer->renderRoot($build)); } else { - $this->assertSame('', $this->renderer->renderRoot($build)); + $this->assertSame('', (string) $this->renderer->renderRoot($build)); } } @@ -691,8 +692,8 @@ public function testRenderCacheProperties(array $expected_results) { ], // Collect expected property names. '#cache_properties' => array_keys(array_filter($expected_results)), - 'child1' => ['#markup' => '1'], - 'child2' => ['#markup' => '2'], + 'child1' => ['#markup' => SafeString::create('1')], + 'child2' => ['#markup' => SafeString::create('2')], // Mark the value as safe. '#custom_property' => SafeMarkup::checkPlain('custom_value'), '#custom_property_array' => ['custom value'], @@ -712,7 +713,7 @@ public function testRenderCacheProperties(array $expected_results) { $this->assertEquals($cached, (bool) $expected); // Check that only the #markup key is preserved for children. if ($cached) { - $this->assertSame($data[$property], $original[$property]); + $this->assertEquals($data[$property], $original[$property]); } } // #custom_property_array can not be a safe_cache_property. -- GitLab