... or somewhere else where $language hasn't been set yet.

I've noticed this on in Drupal 5 but I'm pretty sure it still exists in Drupal 7.

When a module calls the t() function when $locale hasn't been set yet (like in a define()), the locale cache gets rebuilt every page request. This can have a huge performance impact on a site with a lot of translated labels.

The function locale now does something like:

function locale($string = NULL, $langcode = NULL, $reset = FALSE) {
  global $language;
  static $locale_t;

  if ($reset) {
    // Reset in-memory cache.
    $locale_t = NULL;
  }

  if (!isset($string)) {
    // Return all cached strings if no string was specified
    return $locale_t;
  }

  $langcode = isset($langcode) ? $langcode : $language->language;

  // Store database cached translations in a static var.
  if (!isset($locale_t[$langcode])) {

I think it would be safer to check if $language is set in that latest if:

<code>
function locale($string = NULL, $langcode = NULL, $reset = FALSE) {
  global $language;
  static $locale_t;

  if ($reset) {
    // Reset in-memory cache.
    $locale_t = NULL;
  }

  if (!isset($string)) {
    // Return all cached strings if no string was specified
    return $locale_t;
  }

  $langcode = isset($langcode) ? $langcode : $language->language;

  // Store database cached translations in a static var.
  if (!isset($locale_t[$langcode]) && isset($language)) {

Okay, I admit every module should take care of calling t() only when locale is initialized, but this way we can make this a bit safer. Only the buggy t() call gets not translated and all the rest is translated well.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

davyvdb’s picture

BTW. You can find an example of this at http://drupal.org/node/320682.

This was a bug which was VERY hard to find.

Damien Tournoud’s picture

Title: Huge performance issue in case of t() in define() » Doc: don't use t() in global contexts
Component: language system » documentation

Well, the true answer is: *don't use t() in a global context*. Gabor lists this as one of the most common mistakes in his cheat sheet.

Requalifying as a documentation problem, the document of t() should be extended to include that in bold.

davyvdb’s picture

Why not put the extra check there? It solves the performance issue gracefully.

alexanderpas’s picture

can we catch this with the coder module?

davyvdb’s picture

Status: Active » Needs review
FileSize
816 bytes

To be honest. The impact on performance is so big, we need to do both imho. There are so many contributed modules available and if some novice users downloads them, he'll never notice this issue.

davyvdb’s picture

jhodgdon’s picture

Component: documentation » locale.module

If that is the patch, this is not a documentation issue.

cburschka’s picture

Title: Doc: don't use t() in global contexts » Don't rebuild locale cache when $language is not set

Title should reflect that as well. Is this accurate?

davyvdb’s picture

Yes sir!

mgifford’s picture

+1

Patch applies nicely.

Don't see any need to rebuild locale cache when $language is not set

cburschka’s picture

Status: Needs review » Reviewed & tested by the community

This gets a +1 from me too. The change in the logic makes sense and the code change is clean and simple. Tests are passing as well, so I dare RTBC this.

Even though this could be seen as baby-sitting broken code, I don't think it is. Sanity checks are quite normal, and as this one prevents a pretty nasty slow-down, it's a good idea.

Damien Tournoud’s picture

Status: Reviewed & tested by the community » Needs work

The check needs to be on $langcode, not $language.

I suggest to make this check on is own, and return in this case.

davyvdb’s picture

Status: Needs work » Needs review

It's definitely on $language. That's not initialized yet. It's not a check on input parameters.

Status: Needs review » Needs work

The last submitted patch failed testing.

alexanderpas’s picture

alexanderpas’s picture

Status: Needs work » Needs review

head broke -- resetting status

Re-test of 320686-global-t.patch from comment #5 was requested by Arancaytar.

cburschka’s picture

Last test is over a month old. Otherwise, this is probably RTBC.

Status: Needs review » Needs work
Issue tags: -Performance, -location performance improvements

The last submitted patch, 320686-global-t.patch, failed testing.

Status: Needs work » Needs review
Issue tags: +Performance, +location performance improvements

Re-test of 320686-global-t.patch from comment #5 was requested by Arancaytar.

cburschka’s picture

I cannot reproduce the fatal error on DatabaseTaggingTestCase. Let's have another go, then investigate if it happens again.

catch’s picture

Status: Needs review » Needs work

This is passing now, must have been broken test bot. I don't see any reason not to add the extra check here, IMO this doesn't fall into "dont' babysit broken code" - it makes sense in itself.

However can we get a one-line comment which explains what we're doing and why?

cburschka’s picture

Status: Needs work » Needs review
FileSize
878 bytes

Here's a comment.

cburschka’s picture

On second thought, that comment is silly. This one is hopefully a bit better.

catch’s picture

Status: Needs review » Reviewed & tested by the community

Very nice.

Dries’s picture

Status: Reviewed & tested by the community » Needs work

Instead of 'var', let's use 'variable'. Should be quick re-roll.

cburschka’s picture

Status: Needs work » Needs review
FileSize
976 bytes

Done, and patched.

catch’s picture

Status: Needs review » Reviewed & tested by the community

Yep.

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Committed to HEAD!

Status: Fixed » Closed (fixed)
Issue tags: -Performance, -location performance improvements

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