Follow-up to #2557113: Make t() return a TranslationWrapper object to remove reliance on a static, unpredictable safe list, #1825952: Turn on twig autoescape by default

Problem

SafeMarkup::format() is the last place where we still use the safe list. Currently, use of the safe list in SafeMarkup::format() leads to bugs as well as security issues (see #2549943: [plan] Remove as much of the SafeMarkup class's methods as possible).

Proposed resolution

Instead of a string, make t() return a FormattableString object implementing SafeStringInterface. This object will return the formatted string upon casting to string using the __toString() magic method.

Doing this will allow us to get rid of the safe list entirely.

Remaining tasks

Review current patch
Update change record at https://www.drupal.org/node/2564451
Commit

User interface changes

No

API changes

This will be disruptive to contrib in that, like t(), the return value of SafeMarkup::format() will need to be explicitly cast to string in strpos() and in array keys.

CommentFileSizeAuthor
#102 2559971-3-102.patch20.93 KBalexpott
#102 100-102-interdiff.txt1.79 KBalexpott
#100 2559971-3-100.patch21.15 KBalexpott
#100 96-100-interdiff.txt15.15 KBalexpott
#96 2559971-3-96.patch9.09 KBalexpott
#95 make-2559971-95.txt10.73 KBlauriii
#85 2559971-85.patch25.87 KBplach
#85 2559971-85.interdiff.txt3.07 KBplach
#80 2559971-80.patch24.46 KBstefan.r
#75 2559971-75.patch24.46 KBstefan.r
#72 2559971-70-bare.patch8.93 KBstefan.r
#72 2559971-72.patch21.39 KBstefan.r
#61 2559971-60.patch8.71 KBstefan.r
#61 interdiff-56-60.txt4.56 KBstefan.r
#61 2559971-60.patch8.71 KBstefan.r
#56 interdiff.txt646 byteslauriii
#56 make-2559971-56.patch8.19 KBlauriii
#53 interdiff.txt4.98 KBlauriii
#53 make-2559971-53.patch8.17 KBlauriii
#46 2559971-46.patch5.44 KBstefan.r
#46 interdiff-44-46.txt603 bytesstefan.r
#44 interdiff-42-44.txt728 bytesstefan.r
#44 2559971-44.patch5.44 KBstefan.r
#42 2559971-42.patch5.24 KBalexpott
#33 make-2559971-33.patch28.67 KBnlisgo
#33 interdiff-2559971-32-33.txt4.13 KBnlisgo
#32 make-2559971-32.patch28.05 KBnlisgo
#29 2559971-29.patch58.21 KBalexpott
#29 27-29-interdiff.txt1.25 KBalexpott
#27 2559971-27.patch56.97 KBalexpott
#21 2559971-21.patch36.67 KBalexpott
#21 15-21-interdiff.txt32.88 KBalexpott
#21 15-21-the-interdiff-to-read.txt1.82 KBalexpott
#21 2559971-21.do-not-test.patch6.78 KBalexpott
#15 2559971-15.patch4.95 KBalexpott
#12 2559971-12.patch1.12 KBdawehner
#5 2559971-5.patch98.05 KBstefan.r
#5 interdiff-2-5.txt3.36 KBstefan.r
#3 2559971-2.patch96.29 KBstefan.r
#3 additions.txt10.97 KBstefan.r

Comments

stefan.r created an issue. See original summary.

Status: Needs review » Needs work

The last submitted patch, 3: 2559971-2.patch, failed testing.

stefan.r’s picture

Status: Needs work » Needs review
StatusFileSize
new3.36 KB
new98.05 KB

lauriii queued 5: 2559971-5.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 5: 2559971-5.patch, failed testing.

nlisgo’s picture

Issue tags: +Needs reroll
stefan.r’s picture

Status: Needs work » Postponed
stefan.r’s picture

Title: Make SafeMarkup::format() return a safe string object » Make SafeMarkup::format() return a safe string object to remove reliance on a static, unpredictable safe list
Parent issue: » #2506427: [meta] !placeholder causes strings to be escaped and makes the sanitization API harder to understand
Related issues: +#2557113: Make t() return a TranslationWrapper object to remove reliance on a static, unpredictable safe list
dawehner’s picture

Status: Postponed » Needs review
StatusFileSize
new1.12 KB

The patch doesn't apply at all anymore, so better start with a minimal approach again.

Status: Needs review » Needs work

The last submitted patch, 12: 2559971-12.patch, failed testing.

The last submitted patch, 12: 2559971-12.patch, failed testing.

alexpott’s picture

Status: Needs work » Needs review
Related issues: +#2509218: Ensure that SafeString objects can be used in non-HTML contexts
StatusFileSize
new4.95 KB

@dawehner once SafeMarkup::checkPlain() (or even before) we can just take over the whole SafeMarkup class :)

And now SafeMarkup::format also delays escaping to the last possible moment making it compatible with the work in #2509218: Ensure that SafeString objects can be used in non-HTML contexts

alexpott’s picture

Once this is in and #2569699: Remove SafeMarkup::checkPlain() for Drupal 9.0.x lands we can completely remove the safe list and their associated methods - yay!

Status: Needs review » Needs work

The last submitted patch, 15: 2559971-15.patch, failed testing.

The last submitted patch, 15: 2559971-15.patch, failed testing.

lauriii’s picture

Assigned: Unassigned » lauriii

Working on the test fails

lauriii’s picture

Assigned: lauriii » alexpott

alexpott is working on this instead.

alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new6.78 KB
new1.82 KB
new32.88 KB
new36.67 KB

So we cast to array in the entity query system and that is interesting for objects.

Also we need #2568977: Replace SafeMarkup::format() in the link generator - it's a bad example to everyone so the patch includes that.

stefan.r’s picture

Assigned: alexpott » Unassigned
Issue tags: -Needs reroll

Status: Needs review » Needs work

The last submitted patch, 21: 2559971-21.patch, failed testing.

alexpott’s picture

+++ b/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
@@ -135,7 +135,7 @@ public function challengeException(Request $request, \Exception $previous) {
     $challenge = SafeMarkup::format('Basic realm="@realm"', array(
       '@realm' => !empty($site_name) ? $site_name : 'Access restricted',
     ));
-    return new UnauthorizedHttpException($challenge, 'No authentication credentials provided.', $previous);
+    return new UnauthorizedHttpException((string) $challenge, 'No authentication credentials provided.', $previous);
   }
 

The interdiff to read missed this.

The last submitted patch, 21: 2559971-21.patch, failed testing.

alexpott’s picture

Status: Needs review » Needs work

The last submitted patch, 27: 2559971-27.patch, failed testing.

alexpott’s picture

The last submitted patch, 27: 2559971-27.patch, failed testing.

lauriii’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal.php
    @@ -546,16 +546,15 @@ public static function linkGenerator() {
    +  public static function l($text, Url $url) {
    

    Removing the parameter probably requires also changes for the docs also

  2. +++ b/core/lib/Drupal/Core/StringTranslation/PluralTranslatedString.php
    @@ -0,0 +1,160 @@
    +   *   themed. See \Drupal\Component\Utility\SafeMarkup::format(). Note that you do
    

    Over 80 chars

  3. +++ b/core/lib/Drupal/Core/StringTranslation/PluralTranslatedString.php
    @@ -0,0 +1,160 @@
    +   *   An associative array of additional options. See t() for allowed keys.
    +   */
    

    @return missing

  4. +++ b/core/lib/Drupal/Core/StringTranslation/PluralTranslatedString.php
    @@ -0,0 +1,160 @@
    +        // the second plural form as a fallback (which allows for most flexibility
    

    Over 80chars

  5. +++ b/core/lib/Drupal/Core/StringTranslation/PluralTranslatedString.php
    @@ -0,0 +1,160 @@
    \ No newline at end of file
    

    Missing newline

  6. +++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php
    @@ -8,10 +8,12 @@
    +use Drupal\Core\GeneratedUrl;
    

    Unused

  7. +++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
    @@ -1531,19 +1533,20 @@ protected function renderAsLink($alter, $text, $tokens) {
    +    return $this->getRenderer()->render($render);
     
    -    return $value;
    

    Space should be before the return

  8. +++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
    @@ -7,7 +7,10 @@
    +use Drupal\Core\Render\RendererInterface;
    

    Unused

  9. +++ b/core/tests/Drupal/Tests/Core/Form/FormErrorHandlerTest.php
    @@ -130,3 +130,11 @@ public function testSetElementErrorsFromFormState() {
    \ No newline at end of file
    

    Newline missing

nlisgo’s picture

Status: Needs work » Needs review
StatusFileSize
new28.05 KB

Re-roll needed before addressing feedback in #31 since the following issue was fixed: #2568977: Replace SafeMarkup::format() in the link generator - it's a bad example to everyone

nlisgo’s picture

StatusFileSize
new4.13 KB
new28.67 KB

#31.1 was addressed in #2568977: Replace SafeMarkup::format() in the link generator - it's a bad example to everyone

This patch addresses all other feedback in #31.

nlisgo’s picture

The reason why the patch is much smaller in #32 than in #29 is because the issue #2568977: Replace SafeMarkup::format() in the link generator - it's a bad example to everyone implemented some of the same changes as the patch in #29.

The last submitted patch, 32: make-2559971-32.patch, failed testing.

nlisgo queued 32: make-2559971-32.patch for re-testing.

The last submitted patch, 32: make-2559971-32.patch, failed testing.

nlisgo queued 32: make-2559971-32.patch for re-testing.

lauriii’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal/Core/StringTranslation/PluralTranslatedString.php
    @@ -0,0 +1,162 @@
    +   * Constructs a new class instance.
    

    s/new class instance/new PluralTranslatedString object

  2. +++ b/core/lib/Drupal/Core/StringTranslation/PluralTranslatedString.php
    @@ -0,0 +1,162 @@
    +   *   (optional) The string translation service.
    

    I don't think this is tied for string translation being a service

  3. +++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
    @@ -195,22 +209,57 @@ public static function checkPlain($text) {
    +    // @todo temporary hack whilst !placeholder exists.
    

    If we are going to commit this one, maybe it would be a good idea to point the exact issue which allows fixing this @todo

alexpott’s picture

alexpott’s picture

We should make #2570037: Remove the ability to return unsafe string from Translation->translate() do @todo and re-title it do be to clean up both translation and safemarkup format

alexpott’s picture

Status: Postponed » Needs review
StatusFileSize
new5.24 KB
lauriii’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -228,13 +242,48 @@ public static function checkPlain($text) {
+    // @todo temporary hack whilst !placeholder exists.

We could add a link for the issue where this should be removed to ensure it gets done. Otherwise this is RTBC for me.

stefan.r’s picture

Status: Needs work » Needs review
StatusFileSize
new5.44 KB
new728 bytes

Patch looks great to me!

It feels like a bit of a cruel joke that SafeMarkup::format() now returns a SafeMarkup object, but why not :)

The last submitted patch, 42: 2559971-42.patch, failed testing.

stefan.r’s picture

StatusFileSize
new603 bytes
new5.44 KB

The last submitted patch, 44: 2559971-44.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 46: 2559971-46.patch, failed testing.

Status: Needs work » Needs review

stefan.r queued 46: 2559971-46.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 46: 2559971-46.patch, failed testing.

stefan.r’s picture

Today at Drupalcon BCN there was a discussion about how there ought to be no valid use cases for extensive use of markup in either t() or SafeMarkup::format(), how only a limited set of tags ought to be used (a em/i strong sup sub dir pre code cite), and how the word "safe" could suggest something other than what we intend it to mean.

Considering t() outputs a TranslatableString object, and SafeMarkup::format() returns an untranslatable version of this, could we come up with a more appropriate name for the return object? @pwolanin mentioned FormattedString?

lauriii’s picture

Assigned: Unassigned » lauriii
lauriii’s picture

Assigned: lauriii » Unassigned
Status: Needs work » Needs review
StatusFileSize
new8.17 KB
new4.98 KB

Created FormattableString class. Also probably fixed the test failure.

stefan.r’s picture

Discussed this with @joelpittet and he was also +1 on the name change to FormattableString

+++ b/core/tests/Drupal/Tests/Core/Form/FormCacheTest.php
@@ -441,9 +441,7 @@ public function testSetCacheWithSafeStrings() {
-    $form_state_data['build_info']['safe_strings'] = [
-      'a_safe_string' => ['html' => TRUE],
-    ];
+    $form_state_data['build_info']['safe_strings'] = [];

Why are we removing this?

stefan.r’s picture

Issue summary: View changes

If #2557113: Make t() return a TranslationWrapper object to remove reliance on a static, unpredictable safe list was critical, this one probably should be as well?

@lauriii could we also have this use ToStringTrait and such and have this return a regular string on render if we use !placeholders? Also see TranslatableString::render()

lauriii’s picture

StatusFileSize
new8.19 KB
new646 bytes
+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -228,13 +228,24 @@ public static function checkPlain($text) {
-      static::$safeStrings[$output]['html'] = TRUE;

#54: Because this was removed2

alexpott’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
    @@ -493,8 +493,12 @@ protected function doPostSave(EntityInterface $entity, $update) {
       protected function buildPropertyQuery(QueryInterface $entity_query, array $values) {
         foreach ($values as $name => $value) {
    -      // Cast scalars to array so we can consistently use an IN condition.
    -      $entity_query->condition($name, (array) $value, 'IN');
    +      // Make scalars and objects to array so we can consistently use an IN
    +      // condition.
    +      if (!is_array($value)) {
    +        $value = [$value];
    +      }
    +      $entity_query->condition($name, $value, 'IN');
         }
    

    This is just wrong. We need to get #2571909: CommentForm selects using the user formatted name in.

  2. +++ b/core/lib/Drupal/Core/Template/Attribute.php
    @@ -112,10 +112,13 @@ protected function createAttributeValue($name, $value) {
         if (is_array($value) || $name == 'class') {
    -      // Cast the value to an array if the value was passed in as a string.
    +      // Cast the value to an array if the value was passed in is not.
    +      if (!is_array($value)) {
    +        $value = [$value];
    +      }
    

    This needs a test

  3. +++ b/core/tests/Drupal/Tests/Core/Form/FormCacheTest.php
    @@ -441,9 +441,7 @@ public function testSetCacheWithSafeStrings() {
    -    $form_state_data['build_info']['safe_strings'] = [
    -      'a_safe_string' => ['html' => TRUE],
    -    ];
    +    $form_state_data['build_info']['safe_strings'] = [];
    

    There was a better fix for this earlier - we should be using SafeMarkup::setMultiple() until we remove it. Because SafeMarkup::checkPlain() is still a thing.

stefan.r’s picture

Priority: Major » Critical

Talked to @xjm & @catch and this ought to be critical

dawehner’s picture

  1. +++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
    @@ -228,13 +228,24 @@ public static function checkPlain($text) {
    +    $string = (string) $string;
    

    assert for string

  2. +++ b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
    @@ -493,8 +493,12 @@ protected function doPostSave(EntityInterface $entity, $update) {
    +      // Make scalars and objects to array so we can consistently use an IN
    +      // condition.
    +      if (!is_array($value)) {
    +        $value = [$value];
    +      }
    

    What about using if (instance oSafeStringInterface($value)) { $value = (string) $value; }

  3. +++ b/core/lib/Drupal/Core/Template/Attribute.php
    @@ -112,10 +112,13 @@ protected function createAttributeValue($name, $value) {
    +      // Cast the value to an array if the value was passed in is not.
    +      if (!is_array($value)) {
    +        $value = [$value];
    +      }
    

    Explain why and cast to string + explicit test coverage in \Drupal\Tests\Core\Template\AttributeTest

larowlan’s picture

Over in #2571909-32: CommentForm selects using the user formatted name when hook_user_format_name_alter() returns a SafeStringInterface, Drupal\user\Tests\Views\HandlerFilterUserNameTest ends up failing so I expect Drupal\user\Plugin\views\filter\Name will also need updating here once the @todo in user_hooks_test_user_format_name_alter() (which points here) is resolved.

stefan.r’s picture

Status: Needs work » Needs review
StatusFileSize
new8.71 KB
new4.56 KB
new8.71 KB

This will fail

stefan.r’s picture

discussed with @larowlan and #60 should actually be fine after all

dawehner’s picture

+++ a/core/lib/Drupal/Core/Entity/EntityStorageBase.php
@@ -493,12 +493,8 @@
+      // Cast scalars to array so we can consistently use an IN condition.
+      $entity_query->condition($name, (array) $value, 'IN');
-      // Make scalars and objects to array so we can consistently use an IN
-      // condition.
-      if (!is_array($value)) {
-        $value = [$value];
-      }
-      $entity_query->condition($name, $value, 'IN');

Won't that cause issues with objects again?

Status: Needs review » Needs work

The last submitted patch, 61: 2559971-60.patch, failed testing.

The last submitted patch, 61: 2559971-60.patch, failed testing.

The last submitted patch, 61: 2559971-60.patch, failed testing.

The last submitted patch, 61: 2559971-60.patch, failed testing.

stefan.r’s picture

#63 yes but alex says it's wrong (#57.1) - we need to get #2571909: CommentForm selects using the user formatted name in

Status: Needs work » Needs review

stefan.r queued 61: 2559971-60.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 61: 2559971-60.patch, failed testing.

The last submitted patch, 61: 2559971-60.patch, failed testing.

stefan.r’s picture

Status: Needs work » Needs review
StatusFileSize
new21.39 KB
new8.93 KB

The last submitted patch, 72: 2559971-72.patch, failed testing.

The last submitted patch, 72: 2559971-72.patch, failed testing.

stefan.r’s picture

StatusFileSize
new24.46 KB

The last submitted patch, 72: 2559971-70-bare.patch, failed testing.

The last submitted patch, 72: 2559971-70-bare.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 75: 2559971-75.patch, failed testing.

The last submitted patch, 75: 2559971-75.patch, failed testing.

stefan.r’s picture

Status: Needs work » Needs review
StatusFileSize
new24.46 KB

Status: Needs review » Needs work

The last submitted patch, 80: 2559971-80.patch, failed testing.

The last submitted patch, 80: 2559971-80.patch, failed testing.

plach’s picture

+++ b/core/lib/Drupal/Component/Utility/FormattableString.php
@@ -0,0 +1,72 @@
+class FormattableString implements SafeStringInterface {

What about just DynamicString?

plach’s picture

Assigned: Unassigned » plach

Let me try to fix that failure...

plach’s picture

Assigned: plach » Unassigned
StatusFileSize
new3.07 KB
new25.87 KB

I didn't pay attention to the clock, sorry

stefan.r’s picture

Status: Needs work » Needs review
stefan.r’s picture

  1. +++ b/core/lib/Drupal/Component/Utility/FormattableString.php
    @@ -0,0 +1,72 @@
    +   * The safe string.
    

    with placeholders.. and only safe inside HTML fragments

  2. +++ b/core/lib/Drupal/Component/Utility/FormattableString.php
    @@ -0,0 +1,72 @@
    +   *   The string that is to be translated.
    

    to be formatted

Status: Needs review » Needs work

The last submitted patch, 85: 2559971-85.patch, failed testing.

The last submitted patch, 85: 2559971-85.patch, failed testing.

jhedstrom’s picture

Assigned: Unassigned » jhedstrom

Taking a look at the failures.

jhedstrom’s picture

Assigned: jhedstrom » Unassigned

The 2 fails here are related to this change:

+++ b/core/modules/comment/src/CommentForm.php
@@ -263,16 +257,27 @@ public function buildEntity(array $form, FormStateInterface $form_state) {
-
-    if (!$this->currentUser->isAnonymous()) {
-      // Assign the owner based on the given user name - none means anonymous.
-      $accounts = $this->entityManager->getStorage('user')
-        ->loadByProperties(array('name' => $author_name));
-      $account = reset($accounts);
-      $uid = $account ? $account->id() : 0;
-      $comment->setOwnerId($uid);
+    if ($comment->id() && $this->currentUser->hasPermission('administer comments')) {
+      // Admin can leave the author-ID blank to revert to anonymous.
+      $author_id = $author_id ?: 0;
+    }
+    if (!is_null($author_id)) {
+      $account = $this->entityManager->getStorage('user')->load($author_id);
+      if ($author_id || !$form['author']['name']['#access']) {
+        $comment->setAuthorName($account->getUserName());
+      }
+      else {
+        // Anonymous user - use the author name value.
+        $comment->setAuthorName($author_name);
+      }
+    }
+    else {
+      $author_id = $this->currentUser->id();
     }
+    $comment->setOwnerId($author_id);

since $author_id is always null unless the user has the administer comments permission.

I think #2571909: CommentForm selects using the user formatted name is attempting to address this same area of code though, so perhaps this should be postponed?

almaudoh’s picture

+++ b/core/modules/comment/src/Tests/CommentPreviewTest.php
@@ -50,13 +51,13 @@ function testCommentPreview() {
-    $this->assertEscaped('<em>' . $this->webUser->id() . '</em>');
+    $this->assertEscaped('<em>' . $this->webUser->getUsername() . '</em>');
...
-    $this->assertRaw('<em>' . $this->webUser->id() . '</em>');
+    $this->assertRaw('<em>' . $this->webUser->getUsername() . '</em>');

The verbose output from the test displays the uid and not the username on the comment form, so I'm wondering why this change was made.

stefan.r’s picture

These bits are unrelated to this current patch - i had merged in the patch from #2571909: CommentForm selects using the user formatted name so please copy any feedback to that issue.

almaudoh’s picture

Ok, thanks for the clarification @stefan.r. Then we have to wait for that issue to get fixed.

lauriii’s picture

Status: Needs work » Postponed
StatusFileSize
new10.73 KB

Postponing this on #2571909: CommentForm selects using the user formatted name. Applied also the patch that we can keep working on after this has been unblocked.

alexpott’s picture

Status: Postponed » Needs review
StatusFileSize
new9.09 KB

Rerolled using #95.

Status: Needs review » Needs work

The last submitted patch, 96: 2559971-3-96.patch, failed testing.

The last submitted patch, 96: 2559971-3-96.patch, failed testing.

dawehner’s picture

+++ b/core/lib/Drupal/Component/Utility/FormattableString.php
@@ -0,0 +1,45 @@
+/**
+ * Provides a formattable string class.
+ */
+class FormattableString implements SafeStringInterface {

That comment doens't explain anything

alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new15.15 KB
new21.15 KB

I think we should add any new assertions in followups. Also if we use SafeStringTrait we get a ::create() method we don't want so reverting that change.

I've moved all the docs so the placeholdering docs are on the PlaceholderTrait and all the standard places point to it.

pwolanin’s picture

Status: Needs review » Needs work

Some minor problems with the code docs:

  1. +++ b/core/lib/Drupal/Component/Utility/FormattableString.php
    @@ -0,0 +1,98 @@
    +   * Creates a FormattableString object if necessary.
    

    "if necessary" seems wrong here.

  2. +++ b/core/lib/Drupal/Component/Utility/FormattableString.php
    @@ -0,0 +1,98 @@
    +   * If $string is equal to a blank string then it is not necessary to create a
    +   * SafeString object.
    

    This comment doesn't make sense here

  3. +++ b/core/lib/Drupal/Component/Utility/FormattableString.php
    @@ -0,0 +1,98 @@
    +   * @return string|\Drupal\Component\Utility\SafeStringInterface
    +   *   A formattable string.
    

    This doesn't seem right - we get back an instance of this class

  4. +++ b/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
    @@ -15,18 +15,62 @@
    +   * unexpected output.
    

    unexpected or unsafe (or both)?

  5. +++ b/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
    @@ -15,18 +15,62 @@
    +   *   - !variable: Inserted as is, with no sanitization or formatting. Only
    

    should we add a note now that this is about to be removed?

alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new1.79 KB
new20.93 KB

I had a change of heart about FormattableString::create() whilst rolling the patch - oops - thanks for the review @pwolanin.

  1. Fixed
  2. Fixed
  3. Fixed
  4. Fixed
  5. I don't think so
stefan.r’s picture

We could now deprecate SafeMarkup::format() in favor of FormattableString::create()?

Could we just keep the assertions instead or changing behavior from TranslatableString by casting input to string? They were green, and I'm happy work on this and to copy-paste tests from TranslatableString.

dawehner’s picture

I'm not 100% convinved that the FormattedString is the best place for this, but well, the FormattedString will appear in many places.

+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -162,79 +162,41 @@ public static function checkPlain($text) {
+   * @see \Drupal\Component\Utility\FormattableString

but at least for have the @see here

pwolanin’s picture

Status: Needs review » Reviewed & tested by the community

Thanks for the doc fixes.

@stefan.r - it doesn't have a create() method, I think alexpott was just commenting as to why the docs seemed mismatched.

A create() method wouldn't be useful anyhow since you can't give it args according to that interface?

alexpott’s picture

+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -162,79 +162,41 @@ public static function checkPlain($text) {
+   * @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
+   *   Use \Drupal\Component\Utility\FormattableString.

@stefan.r yep hence the deprecate here... :)

I decided against the ::create() method to keep this aligned with TranslatableString...

stefan.r’s picture

Issue tags: +Needs change record

Ah so "new FormattableString('string', [args])"

If we really wanted a ::create() method we could have one on TranslatableString as well but just using the constructor is fine as well.

+++ b/core/lib/Drupal/Component/Utility/FormattableString.php
@@ -0,0 +1,92 @@
+    $this->string = (string) $string;

This bit is different to TranslatableString (which has an assert for strings). Accepting objects might be a nice feature at some point but shouldn't we stick to strings only for now and fail when input is not a string?

stefan.r’s picture

So we could either put further documentation in the existing t() change record at https://www.drupal.org/node/2564451 or create a new one?

alexpott’s picture

@stefan.r so the strtr in HEAD does the cast to string... so SafeMarkup::format() supports objects. The reason to cast to string when creating the Formattable string is so that if there is any execptions thrown then it works as expected rather then throwing the exception in FormattableString::__toString() - which would just break.

stefan.r’s picture

wait, we removed the ToStringTrait? why not use that here?

It's a code smell if we're passing in objects IMO. I don't think we do it anywhere in core anyway?

@dawehner wanted a check in TranslatableStirng as well - maybe check with him. think it ought to be the same for FormattableString (even if SafeMarkup::format() used to accept objects)

dawehner’s picture

I'm fine with dropping any kind of asserts, give the problems we have with asserts in the first place, sadly.

stefan.r’s picture

Until now "let's add any assertions in followups" has meant they don't materialize... that's a problem as well.

I think they're great and that the quote problem is solvable by forcing single quotes and a comment above asserts until we require PHP7... this is solvable in core and contrib in the testbot and in the release publishing process - that would kill the "people might copypaste into custom modules" argument.

but even an exception would be better than nothing here - it's not like an is_string is expensive.

plach’s picture

  1. +++ b/core/lib/Drupal/Component/Utility/FormattableString.php
    @@ -0,0 +1,92 @@
    + * rather than this object, since it will translate the text (on
    

    s/it/they

  2. +++ b/core/lib/Drupal/Component/Utility/FormattableString.php
    @@ -0,0 +1,92 @@
    +  public function count() {
    

    Wouldn't ::length() be more appropriate?

  3. +++ b/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
    @@ -15,18 +15,62 @@
    +   *   - @variable: Escaped to HTML using Html::escape() unless the value is
    +   *     already HTML-safe. Use this as the default choice for anything
    +   *     displayed on a page on the site, but not within HTML attributes.
    +   *   - %variable: Escaped to HTML just like @variable, but also wrapped in
    +   *     <em> tags, which makes the following HTML code:
    +   *     @code
    +   *       <em class="placeholder">text output here.</em>
    +   *     @endcode
    +   *     As with @variable, do not use this within HTML attributes.
    

    I think we are supporting @ and % into attributes with the changes introduced in #2571673: Convert Views t() usage where it is used as an attribute value, aren't we? The result will be converted to plain text and then escaped. We will need to update these docs over there if this lands before that one, as it appears likely.

stefan.r’s picture

@plach then what about #2568977: Replace SafeMarkup::format() in the link generator - it's a bad example to everyone, have those concerns been addressed now?

alexpott’s picture

@plach the count behaviour is a Twig hack

plach’s picture

@stefan.r:

URLs are still a special beast, but with the changes over there we should have built-in support for the normal attribute values, I think

xjm’s picture

xjm’s picture

Status: Reviewed & tested by the community » Needs work

Also I don't see the change record linked in the sidebar?

dawehner’s picture

Working on the CR

dawehner’s picture

Status: Needs work » Reviewed & tested by the community
Issue tags: -Needs change record +SafeMarkup

There it is, also adapted https://www.drupal.org/node/2296163

catch’s picture

Status: Reviewed & tested by the community » Fixed

Fixed #113.1 locally and committed/pushed to 8.0.x, thanks!

  • catch committed 24ed79e on 8.0.x
    Issue #2559971 by stefan.r, alexpott, lauriii, nlisgo, plach, dawehner:...

Status: Fixed » Closed (fixed)

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