Work In Progress

This book is currently work in progress. Some sections are not yet written. Thank you for your understanding!

The installation script

We touched a bit on the installation script when talking about Dashboards. But what is an installation script?

When Joomla is installing an extension it gives us, the developers of the extension, the opportunity to run some custom PHP code at different phases of the installer execution using the different methods provided in our installer script (you can see all available methods in the \Joomla\CMS\Installer\InstallerScriptInterface interface):

public function preflight(string $type, InstallerAdapter $adapter): bool;

Runs before the component is installed, updated or uninstalled.

This is where you make basic environment checks and make sure that the installer can proceed. Return boolean false to stop the installer, causing to display an error like “Extension something: Custom install routine failure.”

public function install(InstallerAdapter $adapter): bool;

Runs after your extension has been installed. This only happens on a clean installation, i.e. your extension was not previously installed.

This is where you can run code which only applies to a clean installation, e.g. publishing modules and plugins necessary for your component to work.

public function update(InstallerAdapter $adapter): bool;

Runs after your extension has been updated.

[Warning]Warning

“Updated” is a term used very loosely by Joomla. It merely means that a different version than the one which was previously installed on the site has been installed. It might be the same version (refresh) or even an earlier version (downgrade).

public function uninstall(InstallerAdapter $adapter): bool;

Runs after your extension has been successfully uninstalled.

Use this to perform any necessary cleanup which could not be performed by Joomla removing the files and folders of your extension and running the uninstallation SQL files.

public function postflight(string $type, InstallerAdapter $adapter): bool;

This runs at the very last end, before Joomla cleans up. It runs after the extension has been installed, updated or uninstalled.

In the above methods you can see one or two parameters:

string $type

Whenever present it tells us which kind of operation is taking place:

  • install. The extension is being installed from a package uploaded via the browser, a package downloaded from an HTTP/HTTPS source, the Install via Web feature or from a directory. This is a normal installation from scratch. The extension had not been previously installed.

  • discover_install. The extension is being installed using the Discover feature. This means that the files are already placed on the site but there's no guarantee that some files are not missing! Also note that you cannot install a package extension like that. If your component is part of a package it should return false in the preflight method to prevent a broken, non-updatable installation of your component to persist on the site.

  • update. Another version of your extension (NOT necessarily older!) was already installed on the site and the user is trying to install a different version.

  • uninstall. Your extension is being uninstalled.

InstallerAdapter $adapter

This is an instance of the \Joomla\CMS\Installer\InstallerAdapter subclass handling the installation, update or uninstallation of your extension. For components, that's actually an instance of the \Joomla\CMS\Installer\Adapter\ComponentAdapter class.

To use an installation script you have to declare it in your extension's XML manifest. For example:

<scriptfile>script.example.php</scriptfile>

The file must be included with your extension's XML manifest at the archive's root.

The script.example.php file will contain our extension installation script. You are familiar with that; it's been around since Joomla 1.0.

Since Joomla 3.6 there's a superclass our installation script can extend from. In Joomla 3 it was called JInstallerScript, in Joomla 4 it's Joomla\CMS\Installer\InstallerScript (the old name will work up to and including Joomla 5.3). When your script extends from this class you can unlock a lot of useful functionality.

[Note]Note

This class is actually a fork of the F0FUtilsInstallscript class I had introduced in FOF since 2014 and maintained until 2021, when FOF officially became obsolete and I moved all my development to Joomla core MVC. The fork was contributed by George Wilson in 2016 — he had already asked me for permission to fork some useful bits of FOF 2 as it was going to be EOL in June that year. Just an aside, so that you know that Joomla and 3PDs have a symbiotic relationship; we help each other survive.

Minimum Joomla and PHP versions

The installation script can automatically enforce a minimum Joomla and / or PHP version for your extension. If the minimum requirements are not met the installation or upgrade will abort with the aforementioned error. You can do that by setting the relevant properties in your class:

// The extension will only install on PHP 8.0.0 or later
protected $minimumPhp = '8.0.0';

// The extension will only install on Joomla 4.2.0 or later
protected $minimumJoomla = '4.2.0';

Handling downgrades

As mentioned earlier, Joomla uses the term “upgrade” very loosely. If you install any version of your extension while another (or the same!) version is already installed Joomla will happily oblige and call it an “upgrade”. However, most extensions cannot downgrade cleanly, i.e. going from version 2.0.0 to version 1.9.3 may not work at all. This makes perfect sense. You cannot possibly travel back in time to update the installation script of your extension's older versions to make a downgrade possible — if it's even at all possible. Therefore, by default, the installation script will prevent downgrades. Users can only install the same or a newer version of your software.

[Warning]Warning

Preventing downgrades will only work properly if your extension uses Semantic Versioning (SemVer). Your development / nightly releases must additionally follow PHP's versioning rules. That is to say, a development / nightly release coming after the official release of version 1.2.3 must be named something like 1.2.4-dev-20220914.

There are cases where you might not want to do that. Simpler extensions may support downgrades, or you may have implemented some solution which allows you to perform downgrades (e.g. you may have added a custom file in every new release to handle downgrades to a previous version). Or you may just not use semantic versioning and you'd rather implement your custom logic to determine when something is a downgrade or not. In this case set this in your installer class:

protected $allowDowngrades = true;

Removing obsolete folders and files

As we progress through versions of our software we have to contend with the fact that some code becomes obsolete and needs to be removed, Joomla has changed leading to moved files, or we might even need to refactor code and move things around. This means that we have files and folders which need to be removed on upgrade.

Joomla will automatically remove folders and files explicitly listed in the XML manifest of the installed version but not on the new version's XML manifest. This means that only, for example, top level component folders are removed on update. If you have a few specific files, e.g. some obsolete view template files, or folders deeper inside the directory structure they will not be automatically removed.

The $deleteFiles and $deleteFolders properties allow you to specify which files and folders need to be removed, if they exist, on update.

Please remember that you need to call the removeFiles method directly from your postflight method.

Example for setting these variables:

protected $deleteFiles = [
  'components/com_example/tmpl/welcome/default_donate.php',
  'administrator/components/com_example/tmpl/item/default_phpwarning.php',
];

protected $deleteFolders = [
  'media/com_example/icons',
];