Plugin FAQ
Common questions about writing FeatherPanel plugins, from identifiers and namespaces to settings, worker mode, CLI commands and mixins.
On this page
Names and structure
Which characters can an identifier use?
Letters, digits and _ only. my-plugin is rejected. The identifier is also the folder name and the namespace
segment, App\Addons\<identifier>, and all three must match exactly.
Does the entry class have to be named after plugin.name?
Yes. The loader looks for App\Addons\<identifier>\<plugin.name>. If that class does not exist it falls back to a
single class in the plugin root that implements AppPlugin, but the package installer insists on the exact match, so
keep the class, the file name and plugin.name identical.
Where do my other classes go?
Anywhere under the plugin folder, in a namespace that matches the path. Composer maps App\Addons\ to
backend/storage/addons/. Controllers/, Chat/ (models), Services/, Middleware/ and Events/ are
conventions, not requirements. A controller is not a route by itself; a file in Routes/ has to call it.
Which sidebars can a plugin add pages to?
client (the user dashboard), admin, server, vds and webspace. Other top-level keys in sidebar.json are
ignored. See Frontend and Power SDK.
Settings and data
Why is my setting empty even though conf.yml has a default?
The default of a config field is the placeholder of the form field and is never saved. Write real defaults in
pluginInstall() and fall back in code. See Settings.
Can I create my own tables?
Yes, with SQL files in Migrations/. Prefix table names with featherpanel_<identifier>_. See
Migrations and storage.
Are my tables removed on uninstall?
No. Uninstall calls pluginUninstall(), deletes the plugin files and links, and leaves the database alone. Drop your
tables and delete your settings in pluginUninstall() if that is what users expect.
Runtime
Does processEvents() run on every request?
Only in classic mode. The Docker image serves the API in FrankenPHP worker mode, where processEvents() runs once when
each worker starts. Register listeners there and keep request-specific state out of it. See
Middleware and router hooks.
How do I reload my PHP code while developing?
Click Resync on the plugin page or save one of its settings: a successful change under /api/admin/plugins makes
every worker restart. Restarting the backend container works too. Setting FRANKENPHP_WORKER=false on the backend
container turns worker mode off, so every request loads the code again.
Can a listener stop a request?
Yes. Exceptions thrown in listeners are not caught by the event manager, so the request that emitted the event fails. Catch your own exceptions.
Can a plugin add commands to php cli?
Yes, with classes in Commands/. On v1.5.2 and earlier the CLI only finds them when it runs from the repository
root, so they do not show up through the Docker wrapper. Finding them from any directory is fixed in v1.5.3 (PR #365). See
Cron jobs and CLI commands.
Can a plugin change core PHP classes with mixins?
Not on v1.5.2 and earlier. The panel contains a mixin framework (App\Plugins\Mixins) and conf.yml accepts a
mixins map, but nothing registers mixins when the panel starts. Registration is fixed in v1.5.3 (PR #365). Even then the panel
ships no built-in mixins: a mixin is a class added to backend/app/Plugins/Mixins/Custom/, not something a plugin
uses to patch core classes. Use events, routes, middleware and UI packs instead. See
conf.yml: mixins.
Can a plugin change the look of the panel?
Yes. Theme packs change colours and CSS, UI packs and overrides.json hide or replace parts of the layout. See
Themes and UI packs.
Documentation
Where else are plugins documented?
The panel ships its own developer docs, called icanhasfeatherpanel, at /icanhasfeatherpanel/ on your panel and on
GitHub Pages. See icanhasfeatherpanel developer docs.
Is there a template I can start from?
Yes. Admin > Dev > Plugins > Create Plugin generates a plugin from the Empty, Fresh, Starter, Theme pack or UI pack template. Developer mode must be on. Build your first plugin walks through a small plugin by hand.
Related
Frequently asked questions
No. Identifiers may only contain letters, digits and underscores. Use my_plugin or myplugin instead of my-plugin.
Yes. Composer maps the App\Addons\ namespace to backend/storage/addons/, so App\Addons\myplugin\Services\Sync is loaded from storage/addons/myplugin/Services/Sync.php.
Yes. Plugins live in storage/addons, which is the featherpanel_addons volume in Docker. The backend runs php cli migrate on start, which also runs plugin migrations that have not run yet.