# 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.

Source: https://featherpanel.com/docs/plugins/lifecycle-and-events/

## 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. |

> [!TIP]
> The installer calls the hooks on the first `.php` file it finds at the root of the plugin folder. Keep the entry
> class as the only PHP file at the root and put everything else in subfolders.

## Install

When you install a package from the marketplace, an upload or a URL, the panel:

1. extracts the `.fpa` archive and checks `conf.yml`,
2. on an update, saves the current plugin settings,
3. copies the files to `storage/addons/<identifier>`,
4. links `Public/` to `public/addons/<identifier>` and `Frontend/Components/` to `public/components/<identifier>`
   (copying them if a symlink is not possible),
5. runs the SQL files in `Migrations/`; if one fails, the install stops with `ADDON_MIGRATION_FAILED` and shows the
   migration output,
6. restores the saved settings on an update,
7. calls `pluginInstall()` or `pluginUpdate()`.

## 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
<?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](https://featherpanel.com/docs/plugins/events/) 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](https://featherpanel.com/docs/plugins/middleware-and-hooks/#worker-mode).
- Listeners run inside the request that emitted the event. Long work belongs in a [cron job](https://featherpanel.com/docs/plugins/cron-and-commands/).
- A plugin whose entry class cannot be resolved is skipped and logged as `Skipping plugin "<id>": entry class could not be resolved`.

## Related

- [Event reference](https://featherpanel.com/docs/plugins/events/)
- [Routes and permissions](https://featherpanel.com/docs/plugins/routes-and-permissions/)
- [Migrations and storage](https://featherpanel.com/docs/plugins/database/)

## Frequently asked questions

### What arguments does my event listener receive?

The values of the payload the panel emits, in order, as positional arguments. The event reference lists the payload keys for each event.

### Does a failing pluginInstall hook stop the install?

No. Errors thrown by pluginInstall or pluginUpdate are logged and the install still completes. A failing migration does stop it.
