Problem/Motivation

Date formats have 2 property paths that are not yet validatable:

./vendor/bin/drush config:inspect --filter-keys=core.date_format.html_date --detail --list-constraints --fields=key,validatability,constraints
➜  🤖 Analyzing…

 ----------------------- ------------- ------------------------------------------------------------------------- 
  Key                     Validatable   Validation constraints                                                   
 ----------------------- ------------- ------------------------------------------------------------------------- 
  core.date_format.html   82%           ValidKeys: '<infer>'                                                     
  _date                                                                                                          
   core.date_format.htm   Validatable   ValidKeys: '<infer>'                                                     
  l_date:                                                                                                        
   core.date_format.htm   Validatable   ValidKeys:                                                               
  l_date:_core                            - default_config_hash                                                  
   core.date_format.htm   Validatable   NotNull: {  }                                                            
  l_date:_core.default_                 Regex: '/^[a-zA-Z0-9\-_]+$/'                                             
  config_hash                           Length: 43                                                               
                                        ↣ PrimitiveType: {  }                                                    
   core.date_format.htm   Validatable   ValidKeys: '<infer>'                                                     
  l_date:dependencies                                                                                            
   core.date_format.htm   NOT           ⚠️  @todo Add validation constraints to config entity type:              
  l_date:id                             core.date_format.*                                                       
   core.date_format.htm   Validatable   Regex:                                                                   
  l_date:label                            pattern: '/([^\PC])/u'                                                 
                                          match: false                                                           
                                          message: 'Labels are not allowed to span multiple lines or contain     
                                        control characters.'                                                     
                                        NotBlank: {  }                                                           
                                        ↣ PrimitiveType: {  }                                                    
   core.date_format.htm   Validatable   NotNull: {  }                                                            
  l_date:langcode                       Choice:                                                                  
                                          callback:                                                              
                                        'Drupal\Core\TypedData\Plugin\DataType\LanguageReference::getAllValidLa  
                                        ngcodes'                                                                 
                                        ↣ PrimitiveType: {  }                                                    
   core.date_format.htm   Validatable   ↣ PrimitiveType: {  }                                                    
  l_date:locked                                                                                                  
   core.date_format.htm   NOT           ⚠️  @todo Add validation constraints to config entity type:              
  l_date:pattern                        core.date_format.*                                                       
   core.date_format.htm   Validatable   ↣ PrimitiveType: {  }                                                    
  l_date:status                                                                                                  
   core.date_format.htm   Validatable   Uuid: {  }                                                               
  l_date:uuid                           ↣ PrimitiveType: {  }                                                    
 ----------------------- ------------- ------------------------------------------------------------------------- 

On my local umami install, they are also with 11 in the top 25 of config objects the closest to 100% validatability. (See ./vendor/bin/drush config:inspect --todo=50)

Steps to reproduce

  1. Get a local git clone of Drupal core 11.x.
  2. composer require drupal/config_inspector — or manually install https://www.drupal.org/project/config_inspector/releases/2.1.5 or newer (which supports Drupal 11!)
  3. composer require drush/drush
  4. vendor/bin/drush config:inspect --filter-keys=core.date_format.html_date --detail --list-constraints

Proposed resolution

Add validation constraints to:

  1. core.date_format.*:id
  2. core.date_format.*:pattern

This requires looking at the existing code and admin UI (if any) to understand which values could be considered valid. Eventually this needs to be reviewed by the relevant subsystem maintainer.

For examples, search *.schema.yml files for the string constraints: 😊

Reach out to @borisson_ or @wimleers in the #distributions-and-recipes.

Remaining tasks

  1. core.date_format.*:id
  2. core.date_format.*:pattern

User interface changes

None.

API changes

None.

Data model changes

More validation 🚀

Release notes snippet

None.

Issue fork drupal-3397491

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

borisson_ created an issue. See original summary.

borisson_’s picture

Issue summary: View changes
borisson_’s picture

Issue summary: View changes
Status: Active » Needs review

I'm not sure how to add validation to the format, because it looks like all different kinds of things are allowed, so I've added not null, so we at least have something to validate against.
Passing null into DateTime::format is also not allowed, so it also helps that case.

borisson_’s picture

Issue summary: View changes
smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me. Surprised didn't cause more test failures besides the 1 but woo!

wim leers’s picture

One nitpicky question 😇

borisson_’s picture

Status: Reviewed & tested by the community » Needs work

Setting to needs work to apply the NotBlank to this as well. I'm not sure about the regex.

borisson_’s picture

Actually, when thinking about this again, I don't think it makes sense for people to use a date formatter without using one of the letters that are actually transforming the date into something that has to do with the input.

So the regex, while it will be a long one, does make sense.

borisson_’s picture

Status: Needs work » Needs review

This will need a test to validate the regex being applied, but I would like to get a review on the approach first.

smustgrave’s picture

Question how often could a new character be added to php date?

borisson_’s picture

Well, as soon as php updates the date formatting handling. The changelog notes a change in 8.0 and 8.2, so with following minor release we'd have to recheck this?

smustgrave’s picture

That's true guess it will just have to be remembered. Will leave in NR if you want another vote, agree though on the need for tests.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new1.25 KB

The Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

borisson_’s picture

Status: Needs work » Needs review

Updated the yaml file to add a cspell ignore line, to fix #14.

smustgrave’s picture

See there is a test in there now. Should that be expanded though?

borisson_’s picture

I already included this test change in the very first commit, because otherwise it was failing. But you are right, this should be expanded.

borisson_’s picture

Added a kernel test that covers this, per @smustgrave.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new1.9 KB

The Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

borisson_’s picture

Status: Needs work » Needs review

Merged 11.x back into this branch.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new1.9 KB

The Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

borisson_’s picture

Status: Needs work » Needs review

Merged 11.x into the MR.

borisson_’s picture

Removed the @needs-review-queue-bot files.

smustgrave’s picture

Status: Needs review » Needs work

Surprised the bot hasn't got this, but appears to have build failure.

borisson_’s picture

Status: Needs work » Needs review

Pushed phpcs fix,

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Ran the test-only job and got several test failures (which is good as it shows coverage) https://git.drupalcode.org/issue/drupal-3397491/-/jobs/463228

There is 1 thread open but I believe it has been addressed by @borisson_

Think this is good.

wim leers’s picture

Status: Reviewed & tested by the community » Needs work
borisson_’s picture

I will change the test coverage, but I'm not sure how to change the validation error message. What would you prefer?

wim leers’s picture

Yeah that's … tricky 😬 Maybe something like For example, use "Y" for the year, "m" for the month, "d" for the day. For more advanced options see .? 🤔

borisson_’s picture

Status: Needs work » Needs review

Setting to needs review to get help with the writing of the error message.

wim leers’s picture

Status: Needs review » Needs work

sourabhjain made their first commit to this issue’s fork.

wim leers’s picture

@sourabhjain Please do not ask for a review on every review thread. Also: it seems like you just blindly modified these lines, without running tests locally?

    $this->assertValidationErrors(function () use ($entity_values) {
      DateFormat::create($entity_values)->save();
    });

is nonsense 😳 If you'd run it, you'd see you introduced a PHP fatal error:

"Drupal\KernelTests\Core\Config\ConfigEntityValidationTestBase::assertValidationErrors(): Argument #1 ($expected_messages) must be of type array, Closure given, called in …

This is a contribution that slows progress down. 😔

phenaproxima made their first commit to this issue’s fork.

phenaproxima’s picture

Component: datetime.module » base system
Status: Needs work » Needs review
wim leers’s picture

Status: Needs review » Needs work

One usability nit, but other than that this looks great!

(And thanks for already adding FullyValidatable!)

phenaproxima’s picture

Assigned: Unassigned » wim leers
Status: Needs work » Needs review

Agreed and fixed.

wim leers’s picture

Status: Needs review » Reviewed & tested by the community

🚢

catch’s picture

Status: Reviewed & tested by the community » Needs review

#11 is a good question. If PHP 8.4 adds support for a new letter, then we would need to add it here, but that would then potentially allow invalid formats for sites running PHP 8.3.

Then I wondered - do we need a callback somewhere that provides a different string for the regex depending on PHP version? But that seems like overkill.

So what if we just allowed [a-z][A-Z] and it would be a bit looser than reality but we'd never have to think about the above, and it would still catch most completely invalid date formats. Someone can still make a case error with the new validation as long as something else in the string is valid, so it's not completely preventing mistakes.

borisson_’s picture

Sure, adding [a-z][A-Z] is a MUCH simpler option, but that doesn't solve the question asked by @Wim Leers originally:

🤔 Shouldn't this have a minimum length of at least one? And … can't we/shouldn't we use a RegEx constraint to ensure this is using one or more of the available letters (to print year/month/day/hour/…)?

wim leers’s picture

Assigned: wim leers » Unassigned

Interesting. 🤔

This would not catch the following test cases though:

+   * @testWith ["q", true, "This is not a valid date format."]
…
+   *   ["q", false, "This is not a valid date format."]

Alternative idea: what if we made a custom validator that checked if each individual [a-z][A-Z] character actually yielded output? Because characters that are not supported by PHP will just get printed as-is:

<?php
$date=date_create("2013-03-15");
var_dump(date_format($date,"q Y/m/d H:i:s"));
var_dump(date_format($date,"q"));

https://3v4l.org/PIAV2

borisson_’s picture

Alternative idea: what if we made a custom validator that checked if each individual [a-z][A-Z] character actually yielded output? Because characters that are not supported by PHP will just get printed as-is:

I don't think that'd be right solution, people might want to print something like: "Happened on Fri, Feb 9 2024 around 14:00". That is currently possible, and would no longer be with that solution?

wim leers’s picture

#42: I doubt that's ever been intentionally supported? 😅 See the test coverage, for example \Drupal\Tests\system\Functional\Common\FormatDateTest — none of that works that way. Or maybe I'm entirely wrong?

Also … the example you provide is also forbidden by the current MR?

borisson_’s picture

You're right - it is currently also broken, but it is what I had intended to write, to allow something like this https://3v4l.org/HIK4D. Not sure if we want to support this usecase.

wim leers’s picture

AFAICT that's supposed to be a translatable string, and the translatable string has a placeholder for this formatted date. Putting the string into the formatted date is the wrong way around IMHO.

phenaproxima’s picture

How often does PHP add new letters to date formats? If the available letters haven't changed in aeons, I'm not sure it's worth making this more complex than the regex we already have in the MR.

wim leers’s picture

#46 is also what I was thinking honestly 😅

phenaproxima’s picture

Another option here: rather than the regex only listing the characters we allow in a date format, list the characters we forbid. (In other words, just invert the logical outcome of the current regex.) If a new version of PHP comes along that (for example) adds support for the letter Q, we can just remove it from the rejectlist, and older versions of PHP will just harmlessly pass it through.

catch’s picture

I also thought #44 was allowed (if not recommended), it's what PHP allows you to do, #48 would explicitly break that behaviour then but if it's not already possible, maybe that is fine?

phenaproxima’s picture

Oh wow, I didn't know you could escape formatting characters. What a pain.

Okay, in light of that, maybe we should just go back to something simple and relatively naïve, like "forbid control characters". So just reuse type: label.

wim leers’s picture

Status: Needs review » Needs work

… which implies "yeah you'll have to check and see how this works". That's probably true already anyway. So … not unreasonable I think.

I don't like it, but considering all factors at play here, it might be the best possible trade-off indeed to just go with type: label + a comment explaining the factors that led to that decision.

OTOH

it's what PHP allows you to do

That should not be the question here. The question should be: How does Drupal intend this DateFormat config entity to be used? And then I think the conclusion would be different, because the validation that type: label gets us is virtually useless 🙈

phenaproxima’s picture

Status: Needs work » Reviewed & tested by the community

Wait a minute here. Might we not be massively overcomplicating this?

In the MR, the constraint we have for the date pattern is this:

        Regex:
          pattern: '/[aABcdDeFgGhHiIjlLmMnNoOpPrsStTuUvwWxXyYzZ]/'

In other words: "this string must contain, somewhere, at least one of the characters that we know is valid to use in a date format as of right now".

This regex has no opinion about unsupported characters, or escaping anything, or characters that might be supported in the future. All of those things would sail through it. It merely says you must have at least one of the characters that is supported now.

So, except in the highly unlikely event that PHP 8.4 drops support for all of these characters in date formats...this pattern will continue to be valid. Even if PHP adds support for a new character. And rightfully so - this is already a very permissive validation. Because of that, it's also very future-proof.

Therefore, I'm not sure #11 is looking at this correctly.

I'm restoring RTBC, but if I'm barking up the wrong tree, feel free to knock it back.

EDIT: We could beef up this regex slightly by making it say, in effect, "your string must contain at least one of these characters and it cannot be escaped". Something like: [^\\][aABcdDeFgGhHiIjlLmMnNoOpPrsStTuUvwWxXyYzZ]

catch’s picture

Even if PHP adds support for a new character.

The issue would be that someone adds a new date format using only the new character that PHP supports, and then it fails validation.

I don't know whether that will actually happen, but it seems as likely as them adding an unsupported letter by accident that would pass the type: label regex and fail the one here.

phenaproxima’s picture

My feeling is, if that happened - which already feels edge-casey to me, but who knows - it would be a perfectly legitimate reason to open an issue to add the newly-supported letter to our regex. :) That'd be a one-line fix and probably wouldn't even need test coverage.

And if the functionality was needed right away, one could easily implement hook_config_schema_alter() to change the constraint.

catch’s picture

Status: Reviewed & tested by the community » Fixed

OK I'm convinced. Thanks for talking through it.

Committed/pushed to 11.x, thanks!

  • catch committed 9df6250d on 11.x
    Issue #3397491 by borisson_, phenaproxima, Wim Leers, smustgrave: Add...
wim leers’s picture

Status: Reviewed & tested by the community » Fixed

Thanks!

(Fixing status, silly d.o and its non-handling of race conditions 😅)

Status: Fixed » Closed (fixed)

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