Common causes
- Two plugins or a plugin and theme bundle the same library (PHPMailer, Guzzle, Carbon) without namespacing it
- A class file is loaded with require instead of require_once, or both manually and by Composer
- A duplicate copy of a file (backup like User.php.bak or User-old.php) in an autoloaded folder
- A class with the same name as an imported alias in the same file (use App\Foo; class Foo {})
- A stale Composer classmap pointing two names at the same file
How to fix it
- Find the first declaration. Use (new ReflectionClass('PHPMailer'))->getFileName() just before the failing include, or search the codebase for 'class PHPMailer' to find both copies.
- Use require_once or the autoloader. Replace require/include of class files with require_once, or remove the manual include when Composer already autoloads the class.
- Guard optional bundled libraries. When shipping a plugin, wrap includes in if (!class_exists('PHPMailer\PHPMailer\PHPMailer', false)) or, better, prefix vendor namespaces with a tool like PHP-Scoper.
- Identify conflicting plugins. In WordPress, the plugin path in the error is the second loader. Deactivate plugins one by one (or rename folders via FTP) to find which pair conflicts, then update or replace one.
- Remove duplicate files. Delete backup copies of class files from autoloaded directories and run composer dump-autoload.
- Fix alias clashes. If a use statement imports a name that the file then declares, rename the import with use Vendor\Foo as VendorFoo.
PHP
if (!class_exists(\PHPMailer\PHPMailer\PHPMailer::class, false)) {
require_once __DIR__ . '/lib/PHPMailer.php';
}
// find where the existing class came from
// echo (new ReflectionClass('PHPMailer'))->getFileName(); How to stop it happening again
- Load classes only through Composer's autoloader
- Namespace and scope vendor libraries bundled in plugins
- Keep backup files out of the project tree