Follow-up to #2570285: Make sure TranslatableMarkup accepts string values only

Follow-up to #2557113: Make t() return a TranslationWrapper object to remove reliance on a static, unpredictable safe list

Problem/Motivation

Empty strings are not valid input for the TranslatableMarkup constructor.

Proposed solution

Fail when input doesn't make sense. Test for empty strings before creating new TranslatableMarkup objects, and leave them as plain strings if they are empty.

Comments

mr.baileys created an issue. See original summary.

mr.baileys’s picture

Initial patch to get an idea of failures.

dawehner’s picture

+++ b/core/lib/Drupal/Core/StringTranslation/TranslatableString.php
@@ -79,6 +79,7 @@ class TranslatableString implements SafeStringInterface {
+    assert($string != '', '$string must not be empty.');
     $this->string = $string;

Ideally we would put it into an eval string, just curious ...

dawehner’s picture

On the other hand its basically for free here.

Status: Needs review » Needs work

The last submitted patch, 2: 2571677-2-translatable-string-not-empty.patch, failed testing.

mr.baileys’s picture

Status: Needs work » Needs review
StatusFileSize
new4.09 KB
new2.58 KB

This should fix a number of the failures.

Ideally we would put it into an eval string, just curious ...

Whether or not to use the eval string is being discussed in the parent issue #2570285: Make sure TranslatableMarkup accepts string values only, no agreement yet I think.

Status: Needs review » Needs work

The last submitted patch, 6: 2571677-6-translatable-string-not-empty.patch, failed testing.

martin_q’s picture

I am looking at this at DC Barcelona Sprint.
EDIT: ...and will continue to do so for a while, as I didn't get very far at the Sprint but I did understand what I need to do.

martin_q’s picture

Title: Ensure no empty strings are fed to TranslatableString » Ensure no empty strings are fed to TranslatableMarkup
Assigned: Unassigned » martin_q
Issue summary: View changes
Status: Needs work » Needs review
StatusFileSize
new6.27 KB

Issue renamed in view of the TranslatableString class having been renamed.

Building on what mr.baileys started, here's a patch which I think fixes all the places where empty strings may get fed to a TranslatableMarkup constructor, at least as far as the simpletests in core are concerned. Does there need to be a more thorough change to all the code and a test added whenever TranslatableMarkup objects are created, or was the aim just to make all the tests pass?

By the way, I don't know if this is the best way to fix the getTranslatableData method, but it seems to be a sound way to do it. That method seems to be designed to look for the valid translatable things and only translate them, so this change is just an extension of that behaviour.

dawehner’s picture

  1. +++ b/core/lib/Drupal/Core/Plugin/Discovery/YamlDiscovery.php
    @@ -93,7 +93,9 @@ public function getDefinitions() {
    +            if (!empty($definition[$property])) {
    +              $definition[$property] = new TranslatableMarkup($definition[$property], [], $options);
    +            }
    

    Should we define the property as '' instead?

  2. +++ b/core/lib/Drupal/Core/StringTranslation/TranslatableMarkup.php
    @@ -75,6 +75,7 @@ class TranslatableMarkup extends FormattableMarkup {
    +    assert($string != '', '$string must not be empty.');
    

    Let's use !== so we don't cause a cast of objects to an empty string. You never know.

Status: Needs review » Needs work

The last submitted patch, 9: 2571677-9-translatablemarkup-not-empty.patch, failed testing.

The last submitted patch, 9: 2571677-9-translatablemarkup-not-empty.patch, failed testing.

martin_q’s picture

Patch rerolled to fix the fatal error caused by the use of the now obsolete TranslationWrapper instead of TranslatableMarkup, as well as to include @dawehner's second suggestion.

@dawehner
1. Sorry, I don't understand your suggestion. Instead of what? Do you mean there should be an else statement?
2. Sure. Somewhere else I saw that we are requiring strings (not just "no empty strings"), but I guess checking for that that is outside the scope of this issue.

martin_q’s picture

Status: Needs work » Needs review
dawehner’s picture

1. Sorry, I don't understand your suggestion. Instead of what? Do you mean there should be an else statement?

Yeah there could be an else to ensure we have the property set all the time. Not sure you how can actually end up in that situation but I think its not worth to cause the notice.

martin_q’s picture

@dawehner Looking at the code again, there is already a check that isset($definition[$property]) so it would not make sense to have an else {$definition[$property] = '';} statement connected to the if in the patch.

Conceivably this else could be added to the if (isset($definition[$property])) statement, but IMHO that is beyond the scope of this issue.

The last submitted patch, 6: 2571677-6-translatable-string-not-empty.patch, failed testing.

plach’s picture

Version: 8.0.x-dev » 8.1.x-dev
Assigned: martin_q » Unassigned
Priority: Major » Normal
Status: Needs review » Reviewed & tested by the community

The assertion makes me think this is for 8.1.x now, but we could back-port a version without it to 8.0.x since then it would be a straight bug fix.

Also, this does not look major to me, feel free to restore the priority if I'm missing something.

catch’s picture

Status: Reviewed & tested by the community » Needs review

Hmm it might be easier to split the assertion into a new patch, then the bug fix can go into both versions at the same time without a backport.

dawehner’s picture

Hmm it might be easier to split the assertion into a new patch, then the bug fix can go into both versions at the same time without a backport.

I'm a bit confused about this particular statement to be honest. The goal of that issue is to avoid putting empty strings into the TranslatableMarkup.
All the other fixes are just symptoms, the assertion is the actual bugfix.

xano’s picture

+++ b/core/lib/Drupal/Core/StringTranslation/TranslatableMarkup.php
@@ -75,6 +75,7 @@ class TranslatableMarkup extends FormattableMarkup {
+    assert($string !== '', '$string must not be empty.');

If this condition is executed regardless of whether assertions are enabled, why don't we just throw an \InvalidArgumentException instead?

catch’s picture

@dawehner

I'm a bit confused about this particular statement to be honest. The goal of that issue is to avoid putting empty strings into the TranslatableMarkup.
All the other fixes are just symptoms, the assertion is the actual bugfix.

The assertion if committed by itself would break core, and potentially contrib/custom tests, we don't want to break people's tests in patch releases (unless we absolutely have to for some reason).

The other fixes are a pre-requisite for adding the assertion, but there's nothing about them which could break any other code, so they can go in a patch release easily.

I'm not sure what's confusing about that?

@Xano - if we throw an exception, a module that's not upgraded yet will throw exceptions on production, which will mean fatal errors for end users. We could do that in 9.x, not in a minor release. I'll admit I still have trouble between assertions and trigger_error() though particulary in this case where we're adding it after the fact - should we be using E_USER_DEPRECATED instead here?

xano’s picture

@catch: Thanks for the clarification. Assertions are still new to me, but you helped me understand their use case here.

dawehner’s picture

Status: Needs review » Needs work

I haven't been rational until jan 6, so yeah I agree, let's fix the issues in core.

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

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.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.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.

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.

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

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.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.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.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.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

smustgrave’s picture

Status: Needs work » Postponed (maintainer needs more info)
Issue tags: +Needs issue summary update, +Bug Smash Initiative

Could the issue summary be updated for what's needed here? We looking to throw another exception if empty?

adaragao’s picture

I'm getting a InvalidArgumentException: $string ("") must be a string. in Drupal\Core\StringTranslation\TranslatableMarkup->__construct() (line 132 of /opt/drupal/web/core/lib/Drupal/Core/StringTranslation/TranslatableMarkup.php).

And I was hoping to go around it with patch #13, but it doesn't work on Drupal 10.3.2.

dqd’s picture

@smustgrave: Well, Cannot access offset can be symptom-ish for empty string - however I wonder if this new issues on Drupal 11 are related:

Cannot access offset of type Drupal\Core\StringTranslation\TranslatableMarkup in isset or empty in Drupal\Core\Plugin\DefaultPluginManager->doGetDefinition() (line 45 of core/lib/Drupal/Component/Plugin/Discovery/DiscoveryTrait.php

When users try to enter paths like admin/structure/types/manage/article/fields/add-field

acbramley’s picture

Status: Postponed (maintainer needs more info) » Needs work

Raised this in slack with @catch, posting response below:

I still think #20 is what we should do tbh. Use the assertion to find the errors, fix the errors in their own issue, then add the assertion to core itself later.

Back to NW to get this into an MR.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.