Make WordPress Core

Opened 11 months ago

Closed 11 months ago

Last modified 11 months ago

#56980 closed defect (bug) (fixed)

Check that the Normalizer class exists in remove_accents()

Reported by: sergeybiryukov's profile SergeyBiryukov Owned by: sergeybiryukov's profile SergeyBiryukov
Milestone: 6.1.1 Priority: normal
Severity: normal Version:
Component: Formatting Keywords: has-patch commit
Focuses: Cc:

Description

Background: #24661, #30130, #35951, #47763, #52654.

After the 6.1 release, seeing a few errors like this on support forums and Google search results:

Uncaught Error: Class 'Normalizer' not found in .../wp-includes/formatting.php:1605

Introduced in [53754]. There is a function_exists( 'normalizer_normalize' ) check, but apparently some plugins polyfill that function while the Normalizer class is still not available, see comment:22:ticket:35951. This can happen when the intl PHP extension is not loaded.

I believe we should add a class_exists( 'Normalizer' ) check here.

Attachments (4)

56980.diff (685 bytes) - added by SergeyBiryukov 11 months ago.
56980.2.diff (703 bytes) - added by SergeyBiryukov 11 months ago.
56980.3.diff (803 bytes) - added by desrosj 11 months ago.
56980.4.diff (816 bytes) - added by SergeyBiryukov 11 months ago.

Download all attachments as: .zip

Change History (27)

#1 follow-up: @mukesh27
11 months ago

We need both class to be exist? or any one?

This ticket was mentioned in Slack in #forums by zodiac1978. View the logs.


11 months ago

This ticket was mentioned in Slack in #forums by clorith. View the logs.


11 months ago

#4 @hellofromTonya
11 months ago

It appears that the plugin polyfills the normalizer_normalize() function, but the Normalizer class has a different namespace there, p\Normalizer, so Normalizer::FORM_C produces a fatal error.

Is there a case where Core should allow for the polyfill's version to be used?

#5 in reply to: ↑ 1 @SergeyBiryukov
11 months ago

Replying to mukesh27:

We need both class to be exist? or any one?

Both the normalizer_normalize() function and the Normalizer::FORM_C class property are used in the affected code, so I think we should check if both are available.

Replying to hellofromTonya:

Is there a case where Core should allow for the polyfill's version to be used?

Good question! Maybe, but I'm not sure it's possible.

See the UpdraftPlus plugin for example, which uses the Symfony polyfill for Normalizer, but the Normalizer class has a different namespace there, p\Normalizer, so Normalizer::FORM_C produces a fatal error.

How do we get the correct Normalizer::FORM_C value in that case? Should we also check for p\Normalizer?

Version 0, edited 11 months ago by SergeyBiryukov (next)

#6 follow-up: @SergeyBiryukov
11 months ago

On a closer look, it appears that Normalizer::FORM_C is the default value both for normalizer_is_normalized() and normalizer_normalize(), so perhaps we can just remove it. That way the polyfilled function should work.

This ticket was mentioned in Slack in #forums by zoonini. View the logs.


11 months ago

#8 in reply to: ↑ 6 ; follow-up: @hellofromTonya
11 months ago

Replying to SergeyBiryukov:

On a closer look, it appears that Normalizer::FORM_C is the default value both for normalizer_is_normalized() and normalizer_normalize(), so perhaps we can just remove it. That way the polyfilled function should work.

That's a good point! Core doesn't need to pass the default $form value since both functions include the default in their function parameters. Then if the intl package is not available, the polyfill handles it.

56980.2.diff should work.

@SergeyBiryukov how can this be tested?

This ticket was mentioned in Slack in #forums by zoonini. View the logs.


11 months ago

This ticket was mentioned in Slack in #forums by yui. View the logs.


11 months ago

#11 in reply to: ↑ 8 @zodiac1978
11 months ago

Replying to hellofromTonya:

@SergeyBiryukov how can this be tested?

I think we need to have a PHP compiled without the --enable-intl option, otherwise I'm afraid there is no way to disable this extension via php.ini.

Plugins with the polyfill are found here:
https://wpdirectory.net/search/01GGYGZXMMWWG4FDD3781EKW0W

remove_accents is for example used if you create a new user, like described here:
https://github.com/woocommerce/woocommerce/issues/35471#issuecomment-1301815362

Non-NFC content can be found in #30130 (PDF attachment).

So this could be a way to test.

#12 @jchambo
11 months ago

I'm also experiencing this on some of my client's websites, but as of right now it's only on sites using WooCommerce.

This ticket was mentioned in Slack in #core by jeffpaul. View the logs.


11 months ago

#14 follow-up: @costdev
11 months ago

  • Keywords dev-feedback added

@SergeyBiryukov The patch looks good to me generally, though it seems to assume that normalizer_is_normalized() exists if normalizer_normalize() exists. The example polyfills check both, so we might consider this too.

Last edited 11 months ago by costdev (previous) (diff)

#15 follow-up: @jchambo
11 months ago

So is the "solution" here for WordPress instances running on Ubuntu Server, or say VPS's using OpenLiteSpeed WordPress installs to run these in order?

sudo apt-get update
sudo apt install php-intl
sudo reboot

Then we can go ahead and update all plugins & update to WordPress 6.1?

#16 in reply to: ↑ 15 @zodiac1978
11 months ago

Replying to jchambo:

So is the "solution" here for WordPress instances running on Ubuntu Server, or say VPS's using OpenLiteSpeed WordPress installs to run these in order?

Yes, this should solve the problem, as then the intl extension is providing the function directly and no polyfill is necessary. Especially, the namespace problem (and therefore the fatal error) is gone then.

See also:
https://make.wordpress.org/hosting/2021/05/20/why-hosters-should-install-the-php-intl-extension/

#17 in reply to: ↑ 14 ; follow-up: @desrosj
11 months ago

  • Keywords changes-requested added; dev-feedback removed

Replying to costdev:

@SergeyBiryukov The patch looks good to me generally, though it seems to assume that normalizer_is_normalized() exists if normalizer_normalize() exists. The example polyfills check both, so we might consider this too.

This came to mind for me as well. May be worthwhile to play it safe and also check for function_exists( 'normalizer_is_normalized' ) as well.

This ticket was mentioned in Slack in #core by jeffpaul. View the logs.


11 months ago

@desrosj
11 months ago

#19 @desrosj
11 months ago

  • Keywords changes-requested removed

56980.3.diff adds an additional function_exists() check for normalizer_is_normalized.

#20 in reply to: ↑ 17 @SergeyBiryukov
11 months ago

Replying to desrosj:

Replying to costdev:

The patch looks good to me generally, though it seems to assume that normalizer_is_normalized() exists if normalizer_normalize() exists. The example polyfills check both, so we might consider this too.

This came to mind for me as well. May be worthwhile to play it safe and also check for function_exists( 'normalizer_is_normalized' ) as well.

Thanks! Yes, that makes perfect sense to me. 56980.4.diff is a minor modification to check the functions in the same order as they are called.

#21 @desrosj
11 months ago

  • Keywords commit added

Thanks all! This looks good to commit.

#22 @SergeyBiryukov
11 months ago

  • Owner set to SergeyBiryukov
  • Resolution set to fixed
  • Status changed from new to closed

In 54813:

Formatting: Check that both normalizer_* functions exist in remove_accents().

This applies to:

  • normalizer_is_normalized()
  • normalizer_normalize()

Includes removing the Normalizer::FORM_C constant as a parameter, since it is the default value for both functions and does not need to be explicitly passed. This avoids a fatal error if a plugin includes polyfill for any of the functions but the Normalizer class has a different namespace, for example when using the Symfony polyfill.

Follow-up to [53754].

Props hellofromTonya, costdev, desrosj, mukesh27, zodiac1978, jchambo, gisgeo, SergeyBiryukov.
Fixes #56980.

#23 @SergeyBiryukov
11 months ago

In 54814:

Formatting: Check that both normalizer_* functions exist in remove_accents().

This applies to:

  • normalizer_is_normalized()
  • normalizer_normalize()

Includes removing the Normalizer::FORM_C constant as a parameter, since it is the default value for both functions and does not need to be explicitly passed. This avoids a fatal error if a plugin includes polyfill for any of the functions but the Normalizer class has a different namespace, for example when using the Symfony polyfill.

Follow-up to [53754].

Props hellofromTonya, costdev, desrosj, mukesh27, zodiac1978, jchambo, gisgeo, SergeyBiryukov.
Merges [54813] to the 6.1 branch.
Fixes #56980.

Note: See TracTickets for help on using tickets.