Skip to content

Fix MoCache double-applying load_textdomain_mofile filter#27

Open
superdav42 wants to merge 1 commit intonawawi:masterfrom
superdav42:fix/mocache-double-filter
Open

Fix MoCache double-applying load_textdomain_mofile filter#27
superdav42 wants to merge 1 commit intonawawi:masterfrom
superdav42:fix/mocache-double-filter

Conversation

@superdav42
Copy link

Summary

  • Stop re-applying load_textdomain_mofile filter in MoCache::__construct() — WordPress core already applied it before calling override_load_textdomain
  • Add empty-path guard before import_from_file() to prevent PHP 8+ ValueError

Fixes #26

Problem

MoCache::__construct() calls apply_filters('load_textdomain_mofile', $mofile, $domain), but $mofile was already filtered by WordPress core in load_textdomain() before reaching the override_load_textdomain hook. This double-filtering breaks plugins with stateful filter callbacks.

Specifically, Polylang Pro's PLL_Locale_Fallback::load_file returns an empty string on the second pass, causing $this->mofile to be ''. Later, import_from_file('') throws ValueError: Path cannot be empty on PHP 8+.

Reproduction

Drop this mu-plugin into any WordPress site with Docket Cache (MOCACHE enabled), PHP 8+, and any plugin that has a .mo translation file:

<?php
/**
 * Plugin Name: Reproduce Docket Cache MoCache Double-Filter Bug
 */
add_filter('load_textdomain_mofile', function ($mofile, $domain) {
    static $seen = [];
    $key = $domain . '|' . $mofile;
    if (isset($seen[$key])) {
        // Second call — MoCache is re-applying the filter. Return empty to
        // simulate what Polylang Pro's PLL_Locale_Fallback does in practice.
        return '';
    }
    $seen[$key] = true;
    return $mofile;
}, 10, 2);

Any page load that triggers a translation will crash with:

PHP Fatal error: Uncaught ValueError: Path cannot be empty
  in wp-includes/pomo/streams.php

Testing

Tested on a production WordPress 6.9 multisite with PHP 8.2, Polylang Pro, and the affiliate-links plugin:

Scenario Result
Original MoCache, no workaround 500 (Fatal ValueError)
This fix applied 200
Original MoCache restored, 500 returns 500 (confirms fix was the cause)
Fix re-applied 200

Test plan

  • Verify translations still load correctly with MOCACHE enabled
  • Verify no crash on PHP 8+ with Polylang Pro active
  • Verify cached translations are still served from object cache

WordPress core already applies the `load_textdomain_mofile` filter in
`load_textdomain()` before calling `override_load_textdomain`. The
`$mofile` parameter passed to MoCache is already filtered.

Re-applying the filter in the MoCache constructor causes it to run
twice. This breaks plugins with stateful filter callbacks — notably
Polylang Pro's `PLL_Locale_Fallback::load_file`, which returns an
empty string on the second pass.

On PHP 8+, the empty path causes `fopen('')` to throw a ValueError
in `POMO_FileReader::__construct()`, resulting in a fatal error that
crashes the entire request (HTTP 500).

Changes:
- Use `$mofile` directly instead of re-filtering it
- Add empty-path guard before `import_from_file()` for robustness

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MoCache: double-applies load_textdomain_mofile filter causing empty path crash on PHP 8+

1 participant