Basics¶
A setup plugin is a PHP file with these characteristics:
- The file's name ends in
*.civi-setup.php. - The file has a guard at the top (
if (!defined('CIVI_SETUP'))...). - The file defines events-listeners (
\Civi\Setup::disptacher()->addListener($eventName, $callback)).
Plugins are conventionally autoloaded from civicrm/setup/plugins/**.civi-setup.php (although some installers may load custom plugins).
For example, let's create a plugin that logs a message during database installation:
<?php
// FILE: civicrm/setup/plugins/installDatabase/MySpecialLogger.civi-setup.php
use Civi\Setup\Event\InstallDatabaseEvent;
if (!defined('CIVI_SETUP')) {
exit("Installation plugins must only be loaded by the installer.\n");
}
\Civi\Setup::dispatcher()->addListener(
'civi.setup.installDatabase',
function (InstallDatabaseEvent $event) {
\Civi\Setup::log()->info("I like to run the plugin during installation.");
}
);
All events provide access to the setup data-model. For example, this will
provide the path to civicrm.settings.php:
$event->getModel()->settingsPath
Observe that the primary way for a plugin to interact with the system is to register for events (using Symfony's EventDispatcher). The events determine when the plugin runs and what data it can access.
Events: General installation¶
Most methods in the Civi\Setup API have a corresponding event name (civi.setup.{myEvent}) and event class (Civi\Setup\Event\MyEvent).
Most plugins will register for one of these events:
| \Civi\Setup Method | Event Name | Event Class |
|---|---|---|
init() |
civi.setup.init |
InitEvent |
checkAuthorized() |
civi.setup.checkAuthorized |
CheckAuthorizedEvent |
checkInstalled() |
civi.setup.checkInstalled |
CheckInstalledEvent |
checkRequirements() |
civi.setup.checkRequirements |
CheckRequirementsEvent |
installFiles() |
civi.setup.installFiles |
InstallFilesEvent |
installDatabase() |
civi.setup.installDatabase |
InstallDatabaseEvent |
uninstallFiles |
civi.setup.uninstallFiles |
UninstallFilesEvent |
uninstallDatabase |
civi.setup.uninstallDatabase |
UninstallDatabaseEvent |
Some events provide additional methods and properties. For example:
- For
civi.setup.checkRequirements, use$event->addError(...)to record an error that prevents installation. Similarly, useaddWarning(...)andaddInfo(...)to report less critical issues. - For
civi.setup.checkAuthorized, use$event->setAuthorized(bool $authorized)to indicate whether authorization is permitted, and use$event->isAuthorized()to see if authorization has been permitted.
Events: Web installation¶
For methods related to the built-in web UI, the event-names and event-classes live in a
different namespace (civi.setupui.{myEvent} and Civi\Setup\UI\Event, respectively).
| Method | Event Name | Event Class |
|---|---|---|
\Civi\Setup::createController() |
civi.setupui.construct |
Civi\Setup\UI\Event\UIConstructEvent |
\Civi\Setup\UI\SetupController::run() |
civi.setupui.run |
Civi\Setup\UI\Event\UIRunEvent |
\Civi\Setup\UI\SetupController::boot() |
civi.setupui.boot |
Civi\Setup\UI\Event\UIBootEvent |
Some events provide additional methods and properties. For example:
- For
civi.setupui.run, the list of HTTP inputs is available as$event->getFields().
What's in a file name?¶
The plugins/ folder is loosely organized based on how the plugin fits into
the system. Let's take a few example files (at time of writing):
plugins/checkRequirements/CheckBaseUrl.civi-setup.php
plugins/checkRequirements/CheckDbWellFormed.civi-setup.php
plugins/common/LogEvents.civi-setup.php
plugins/installDatabase/InstallExtensions.civi-setup.php
plugins/installDatabase/InstallSchema.civi-setup.php
plugins/installDatabase/InstallSettings.civi-setup.php
Notice a pattern?
- The files under
plugins/checkRequirementsare all plugins which listen to thecivi.setup.checkRequirementsevent. - The files under
plugins/installDatabaseare all plugins which listen to thecivi.setup.installDatabaseevent.
Most plugins only handle one event, so it's a convenient way to organize them. However, this is just a convention. It's entirely legitimate for a plugin to listen to multiple events. For example:
common/LogEvents.civi-setup.phplistens to many events.blocks/*define visible blocks for the built-in web UI. Many of these listen to 2-3 events.
Should a plugin only handle one event?
If you write a new plugin, should it handle one event or multiple? Do whatever is best (on the whole) for improving the concept/coupling/cohesion. This usually means writing a small/narrow plugin, but it doesn't necessarily.
Browsing for high-level overview
Browse the plugin folders for a high-level skim of the plugins. Try to respect this in framing new plugins, but don't assume that it's perfect. For detailed inspection or debugging, use cv core:install --debug-event.