Using #plain_text, these render arrays will not display the expected values (they render as empty string):

$render_arrays = array(
  array('#plain_text' => "0"),
  array('#plain_text' => 0)
);

But this works:

$render_arrays = array(
  array('#markup' => "0"),
  array('#markup' => 0)
);

I want to use the #plain_text attribute since I want the value to be escaped (e.g. user input).

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

weboide created an issue. See original summary.

weboide’s picture

Title: #plain_text doesn't allow displaying certain empty-like values (e.g. 0, 0.0) » #plain_text doesn't allow displaying certain empty-like values (e.g. 0 and "0")
weboide’s picture

Title: #plain_text doesn't allow displaying certain empty-like values (e.g. 0 and "0") » #plain_text doesn't render empty-like values (e.g. 0 and "0")
hgoto’s picture

Thank you for sharing the issue. I tested and confirmed that this bug exists.

This seems to be caused as Drupal\Core\Render\Renderer::doRender() doesn't check the value of #plain_text properly.

    // All render elements support #markup and #plain_text.
    if (!empty($elements['#markup']) || !empty($elements['#plain_text'])) {
      $elements = $this->ensureMarkupIsSafe($elements);
    }

Here empty() is not proper and we should use isset() instead, I think.

I created a patch. I thought it's better to add tests for the following cases and added such tests, too.

$render_arrays = array(
  array('#markup' => "0"),
  array('#markup' => 0)
);

I'd like someone to review this. Thank you.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

jrockowitz’s picture

This patch and test make complete sense to me. I just ran into this issue while working on #2922896: Write dedicated rendering tests. I am rerunning the tests and then I am going mark this RTBC. Hopefully, it will get committed or someone will ask for more test coverage.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

gnuget’s picture

Today I ran into this issue as well.

So, here a rebased version of the patch.

The last submitted patch, 11: 2765609-11-test_only.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

gnuget’s picture

Status: Needs review » Needs work
Issue tags: +Novice

It seems that the patch needs to fix some coding standard problems.

I'm going to add the novice tag in case someone wants to do it.

yogeshmpawar’s picture

Assigned: Unassigned » yogeshmpawar
yogeshmpawar’s picture

Assigned: yogeshmpawar » Unassigned
Status: Needs work » Needs review
FileSize
2.42 KB
1.8 KB

Changes addressed in comment #13 & also added an interdiff.

gnuget’s picture

Status: Needs review » Reviewed & tested by the community

Looks great, thanks!

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

Nice find. I think we should try to keep the #markup and #plain_text logic as similar as possible.

  1. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -380,7 +380,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
    -    if (!empty($elements['#markup']) || !empty($elements['#plain_text'])) {
    +    if (!empty($elements['#markup']) || isset($elements['#plain_text'])) {
    

    Let's change both !empty()<code> to <code>isset()

  2. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -744,11 +744,11 @@ protected function xssFilterAdminIfUnsafe($string) {
    -    if (empty($elements['#markup']) && empty($elements['#plain_text'])) {
    +    if (empty($elements['#markup']) && !isset($elements['#plain_text'])) {
           return $elements;
         }
    

    This whole check seems pointless.

  3. Then the whole function becomes:
      protected function ensureMarkupIsSafe(array $elements) {
        if (isset($elements['#plain_text'])) {
          $elements['#markup'] = Markup::create(Html::escape($elements['#plain_text']));
        }
        elseif (!($elements['#markup'] instanceof MarkupInterface)) {
          // The default behaviour is to XSS filter using the admin tag list.
          $tags = isset($elements['#allowed_tags']) ? $elements['#allowed_tags'] : Xss::getAdminTagList();
          $elements['#markup'] = Markup::create(Xss::filter($elements['#markup'], $tags));
        }
    
        return $elements;
      }
    

    Note that we don't need to check the isset($elements['#markup'] in the second condition because it must be set to have got here. I think reviewing this code we should make this fix but also open a follow-up issue to inline ensureMarkupIsSafe into the main render function because it is not needed as a separate function. It just makes it most complex.

    If we inlined the function it would look like:

        if (isset($elements['#plain_text'])) {
          $elements['#markup'] = Markup::create(Html::escape($elements['#plain_text']));
        }
        elseif (isset($elements['#markup'] && !($elements['#markup'] instanceof MarkupInterface)) {
          // The default behaviour is to XSS filter using the admin tag list.
          $tags = isset($elements['#allowed_tags']) ? $elements['#allowed_tags'] : Xss::getAdminTagList();
          $elements['#markup'] = Markup::create(Xss::filter($elements['#markup'], $tags));
        }
    

    which in my opinion is easier to grok than having the misdirection of the method call inside the if(). But that is all followup material.

  4. I think we need to expand the test coverage to ensure sensible things happen with nulls and empty strings
c31ck’s picture

Attached a patch that addresses remarks 1, 2 and 4. I've added a check for '' and NULL to the tests. Also attached a patch containing only the test and an interdiff.

gnuget’s picture

Status: Needs review » Reviewed & tested by the community

Looks great.

Thanks!

alexpott’s picture

Crediting @weboide for discovering and filing the issue. Crediting myself for a review that directly influenced the patch.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed 018bd7c5e2 to 8.6.x and 1675b7b81c to 8.5.x. Thanks!

  • alexpott committed 018bd7c on 8.6.x
    Issue #2765609 by c31ck, gnuget, hgoto, Yogesh Pawar, weboide, alexpott...

  • alexpott committed 1675b7b on 8.5.x
    Issue #2765609 by c31ck, gnuget, hgoto, Yogesh Pawar, weboide, alexpott...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.