Skip to content

Prefix Composer dependencies with wp-scoper to prevent conflicts with other plugins #334

Description

@mostafasoufi

🚨 Problem

The plugin ships its Composer dependencies in vendor/ without any namespace prefixing:

  • enshrined/svg-sanitize
  • symfony/polyfill-php80, symfony/polyfill-php81, symfony/polyfill-php83

PHP can only load one class with a given name per request. WordPress loads every active plugin into the same process, so if another plugin ships the same library, whichever plugin loads first "wins" and the other one gets that version, whether it is compatible or not.

This is not theoretical for WP-Parsidate:

  • 🧨 enshrined\svgSanitize\Sanitizer is also bundled by Safe SVG (1M+ active installs) and by several theme frameworks. If Safe SVG loads an older/newer release first, WPParsidate\Helper\Sanitizing::svg() runs against a class with a different API and different allow-lists. Depending on the version mix this is a wrong result (SVG not cleaned the way we expect) or a fatal error ("Call to undefined method").
  • 🧨 Symfony\Polyfill\Php80\Php80 (and Php81, Php83) are bundled by a large number of plugins. The polyfill functions (str_contains, array_is_list, json_validate, ...) are guarded with function_exists(), but the helper classes are not: an older polyfill loaded by another plugin can be missing a method that our bootstrap file calls.
  • 🔇 These conflicts are hard to reproduce and hard to diagnose because they depend on which other plugins are active and in what order. Issues like تداخل Parsi Date با افزونه Booked در WordPress Customizer و ایجاد Fatal Error #329 (fatal error together with another plugin) are the typical symptom.

✅ Proposal

Use wp-scoper, the same tool wp-sms and its add-ons use. It is a Composer plugin, so it needs no global tool or PHAR:

  1. composer require --dev veronalabs/wp-scoper
  2. Add an extra.wp-scoper block to composer.json:
    "extra": {
      "wp-scoper": {
        "namespace_prefix": "WPParsidate\\Dependencies",
        "packages": ["enshrined/svg-sanitize", "symfony/polyfill-php80", "symfony/polyfill-php81", "symfony/polyfill-php83"],
        "target_directory": "packages",
        "delete_vendor_packages": true,
        "update_call_sites": ["inc"]
      }
    }
  3. On every composer install, wp-scoper copies the dependencies to packages/, rewrites their namespaces, generates packages/autoload.php and updates our own use statements:
    // before
    use enshrined\svgSanitize\Sanitizer;
    // after
    use WPParsidate\Dependencies\enshrined\svgSanitize\Sanitizer;
  4. The main plugin file loads packages/autoload.php instead of vendor/autoload.php. packages/ is committed, vendor/ becomes dev-only and leaves the release ZIP.

🎁 Benefits

  • 🛡️ No more class conflicts. WPParsidate\Dependencies\enshrined\svgSanitize\Sanitizer can never collide with anyone else's enshrined\svgSanitize\Sanitizer.
  • 📌 We always run the exact library version we tested, regardless of what other plugins ship.
  • 📦 Smaller release. Only the PHP files we need are copied (tests, docs and config files of the libraries are skipped): 77 KB instead of 107 KB for the current dependency set.
  • 🤖 Zero manual work. It runs as part of composer install; there is no separate build step to remember. Call sites in inc/ are updated automatically.
  • 🔍 Verifiable in CI. A workflow job can re-run composer install and fail if packages/ is out of date.

🔍 How it works in wp-sms (demo)

I have a pull request ready for this.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions