{"record":{"id":"38d5fe355929859a","repo":"passbolt/passbolt_api","slug":"the-class-name-should-implement-plugininterface-class","errorCode":null,"errorMessage":"The class {$name} should implement PluginInterface::class.","messagePattern":"The class (.+?) should implement PluginInterface::class\\.","errorType":"exception","errorClass":"InternalErrorException","httpStatus":500,"severity":"error","filePath":"src/Utility/Application/FeaturePluginAwareTrait.php","lineNumber":62,"sourceCode":"    /**\n     * @param string $name Plugin class name or plugin name, either upper case or lower case first (without the \"Passbolt/\" prefix)\n     * @return void\n     */\n    public function disableFeaturePlugin(string $name): void\n    {\n        Configure::write($this->getPluginEnabledConfigurationKey($name), false);\n    }\n\n    /**\n     * @param string $name Plugin class name or plugin name, either upper case or lower case first (without the \"Passbolt/\" prefix)\n     * @return string\n     * @throws \\Cake\\Http\\Exception\\InternalErrorException if the plugin name is a class and not a plugin interface\n     */\n    protected function getPluginEnabledConfigurationKey(string $name): string\n    {\n        if (class_exists($name)) {\n            if (!is_subclass_of($name, PluginInterface::class)) {\n                throw new InternalErrorException(\"The class {$name} should implement PluginInterface::class.\");\n            }\n\n            $extractedName = substr(substr(strrchr($name, '\\\\'), 1), 0, -6);\n            if (empty($extractedName)) {\n                throw new InternalErrorException(\"The class {$name} is not a valid plugin.\");\n            }\n\n            $name = $extractedName;\n        }\n\n        $name = lcfirst($name);\n\n        return \"passbolt.plugins.{$name}.enabled\";\n    }\n}\n","sourceCodeStart":44,"sourceCodeEnd":78,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/src/Utility/Application/FeaturePluginAwareTrait.php#L44-L78","documentation":"FeaturePluginAwareTrait::getPluginEnabledConfigurationKey() resolves the config key that controls whether a feature plugin is enabled. If the given $name is an existing PHP class, it must implement Cake\\Core\\PluginInterface; otherwise an InternalErrorException is thrown, because the trait can only derive the plugin name from valid plugin classes or plain plugin names.","triggerScenarios":"Passing a fully-qualified class name (e.g. in enableFeaturePlugin/disableFeaturePlugin or isFeaturePluginEnabled) that exists but does not implement PluginInterface — such as a random class in the same namespace, a typo'd class, or a class name with a truncated/wrong suffix.","commonSituations":"Configuring feature plugins in config/passbolt.php with a class string that points at the wrong class; refactoring renamed the plugin class so it no longer implements PluginInterface; copy-pasted a namespace that resolves to a non-plugin class.","solutions":["Ensure the class passed implements Cake\\Core\\PluginInterface (e.g. PassboltCe/Folders/src/FoldersPlugin.php).","Alternatively pass the short plugin name string ('Folders') instead of the FQCN.","Check for typos or wrong namespaces in the feature-plugin configuration values.","Verify plugin class naming convention: class name must end with 'Plugin' so the short name can be extracted."],"exampleFix":"// before\n$this->isFeaturePluginEnabled('\\App\\Some\\NotAPluginClass');\n// after\n$this->isFeaturePluginEnabled('\\PassboltCe\\Folders\\FoldersPlugin'); // implements PluginInterface\n// or simply:\n$this->isFeaturePluginEnabled('Folders');","handlingStrategy":"validation","validationCode":"if (class_exists($plugin)) {\n    if (!is_subclass_of($plugin, \\Cake\\Core\\PluginInterface::class)) {\n        throw new \\InvalidArgumentException(\"$plugin must implement PluginInterface\");\n    }\n}","typeGuard":"function isPluginClass(string $name): bool {\n    return !class_exists($name) || is_subclass_of($name, \\Cake\\Core\\PluginInterface::class);\n}","tryCatchPattern":"try {\n    $this->isFeaturePluginEnabled($plugin);\n} catch (\\Cake\\Http\\Exception\\InternalErrorException $e) {\n    Log::error('Invalid plugin class: ' . $e->getMessage());\n    $this->isFeaturePluginEnabled(substr($plugin, strrpos($plugin, '\\\\') + 1));\n}","preventionTips":["Always pass either a short plugin name or a class FQCN that implements PluginInterface.","Centralize plugin FQCN references in a constants class to avoid typos.","Add a unit test asserting every configured feature plugin class implements PluginInterface.","Review config/passbolt.php feature plugin entries after refactors/renames."],"tags":["plugin-system","configuration","internal-error"],"backgroundTag":"invalid-argument-value","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}