Plugin lifecycle and events
What the panel does when a FeatherPanel plugin is installed, updated or removed, which hooks your entry class gets, and how to listen to panel events from processEvents.
On this page
The entry class
The entry class implements App\Plugins\AppPlugin. It has three required static methods and one optional one.
| Method | When it runs |
|---|---|
processEvents(PluginEvents $event) | When the backend boots: once per worker in the Docker image, once per request in classic mode. Register your listeners here. |
pluginInstall() | Once, after a fresh install. |
pluginUpdate(?string $oldVersion, ?string $newVersion) | Optional. After an update. A one-argument version also works. If the method does not exist, pluginInstall() runs instead. |
pluginUninstall() | Before the plugin’s files are removed. |
Install
When you install a package from the marketplace, an upload or a URL, the panel:
- extracts the
.fpaarchive and checksconf.yml, - on an update, saves the current plugin settings,
- copies the files to
storage/addons/<identifier>, - links
Public/topublic/addons/<identifier>andFrontend/Components/topublic/components/<identifier>(copying them if a symlink is not possible), - runs the SQL files in
Migrations/; if one fails, the install stops withADDON_MIGRATION_FAILEDand shows the migration output, - restores the saved settings on an update,
- calls
pluginInstall()orpluginUpdate().
Uninstall
Uninstall in the admin area calls pluginUninstall(), removes the plugin folder and its public links, and marks
the plugin as uninstalled. The panel does not drop your tables for you. If your plugin created tables or settings
that should go away, remove them in pluginUninstall().
Listening to events
Panel code emits named events at fixed points: a user logs in, a server is created, a backup finishes and so on.
Each event has a static method on a class in App\Plugins\Events\Events that returns its name.
<?php
namespace App\Addons\myplugin;
use App\App;
use App\Plugins\AppPlugin;
use App\Plugins\PluginEvents;
use App\Plugins\Events\Events\AppEvent;
use App\Plugins\Events\Events\AuthEvent;
class MyPlugin implements AppPlugin
{
public static function processEvents(PluginEvents $event): void
{
$event->on(AuthEvent::onAuthLoginSuccess(), function (array $user): void {
App::getInstance(true)->getLogger()->info('Login: ' . $user['username']);
});
$event->on(AppEvent::onRouterReady(), function ($router): void {
// The route collection is ready.
});
}
public static function pluginInstall(): void {}
public static function pluginUninstall(): void {}
}
How arguments are passed: the panel emits an associative payload, for example ['user' => $userInfo], and calls
each listener with its values in order. A payload with two keys means a listener with two parameters.
PluginEvents also has removeListener(), removeAllListeners() and listeners().
There are more than 400 events, grouped by area (auth, servers, files, backups, nodes, users, tickets, VDS, web hosting and others). The event reference lists each one with its name and payload.
Things to keep in mind
processEvents()runs when the backend boots, which is once per worker in the Docker image. Register listeners there, keep it fast and do not keep per-request state in it. See worker mode.- Listeners run inside the request that emitted the event. Long work belongs in a cron job.
- A plugin whose entry class cannot be resolved is skipped and logged as
Skipping plugin "<id>": entry class could not be resolved.
Related
Frequently asked questions
The values of the payload the panel emits, in order, as positional arguments. The event reference lists the payload keys for each event.
No. Errors thrown by pluginInstall or pluginUpdate are logged and the install still completes. A failing migration does stop it.