Work In Progress

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

Library packages

When you are writing a lot of software for Joomla you will find yourself having a lot of common code between your extensions. When the amount of common code surpasses a certain (low) threshold and / or is used on more than a couple of extensions it makes sense to share it among all of your extensions.

This is what a library package is designed to do. It creates a subdirectory inside the site's libraries directory and optionally assigns it a namespace.

The XML manifest of a files package is quite straightforward:

<?xml version="1.0" encoding="UTF-8"?>
  <extension type="library" method="upgrade">
  <author>Acme Corp.</author>
  <copyright>(C)2022 Acme Corp</copyright>
  <license>GNU GPLv2 or later</license>
  <packager>Acme Corp</packager>
  <namespace path="src">Acme\MyLibrary</namespace>

  <languages folder="language">
    <language tag="en-GB">en-GB/lib_mylibrary.sys.ini</language>
    <language tag="en-GB">en-GB/lib_mylibrary.ini</language>




    <server type="extension" priority="1" name="Acme Corp My Library updates"></server>

This is a typical Joomla extension XML manifest with two tags you need to pay attention to:

  • libraryname tells Joomla the name of the subdirectory under libraries where the files will be copied. In this case we have told Joomla to install our library files in the libraries/mylibrary folder of the site. This is our library root folder.

  • namespace tells Joomla the common namespace prefix of your library files —assuming they follow the PSR-4 standard— and the subdirectory inside your library's root which is the PSR-4 root. In this case we tell Joomla that the common namespace prefix is Acme\MyLibrary and the PSR-4 root for this namespace is libraries/mylibrary/src.

    This tag is optional. However, if you do not provide this tag Joomla will NOT be able to autoload your library files. You will have to provide your own autoloader or include .php files manually.

The files tag tells Joomla where to copy files from the package. If you have a subdirectory in your package where files are located remember to use the folder attribute. For example, if your package puts everything under a dist folder change the files tag to read:

<files folder="dist">

It is a good idea to include a license and Read Me file with your library. The former can be used to fulfil the legal requirement of most Open Source licenses to distribute a copy of the license with the software. The latter helps explaining to a user what exactly is in this folder, what is it used by, and instructions regarding the update and uninstallation of the library (users are well-known for going medieval on folders on their site, renaming or deleting them without much thought when they do not understand what they are for).

While providing a language file is not mandatory, I strongly recommend providing a .sys.ini language file so that you can use language strings in the name and description tags.

If you provide a regular .ini language file, e.g. for error messages and prompts, please keep in mind that it will not be autoloaded. If you really need to provide translated messages you need to make sure that all code paths leading to such a message will first go through Joomla's Language object to load the translation file:

    ->load('lib_mylibrary', JPATH_SITE);

Note that the extension you are loading is lib_mylibrary where mylibrary is the content of your library's XML manifest's libraryname tag.

Another thing you need to keep in mind when writing a library is that libraries are not bootable extensions in Joomla. As a result, they do not have a service provider (services/provider.php) file. If you need to use Joomla services you have two and a half options:

  • The library consumers (your components, plugins, or modules) need to push the services to your library objects. This is the preferable way —it makes your library code easily testable with PHPUnit— but it may lead to a very convoluted implementation in your extensions. This is the optimal way for experienced developers.

  • Pull services directly from the Joomla\CMS\Factory using its getApplication and getContainer methods. This is the simplest way to go about it BUT it makes your code untestable. In other words, it's easy to use in your extensions but really difficult to test with.

  • Hybrid! Use your own DI container and have your library's public interface objects require it to be passed in the constructor. The library consumers can set up their own DI container… or just use a default implementation. A custom container can be used when testing or when an experienced developer is developing their own application, thus emulating the first option we described above. The default DI container implementation —which simply goes through the global CMS Factory— requires no setup so novice developers, or trivial extensions, can quickly use the library without having to worry about passing around services and setting up a DI container, thus emulating the second option described above.

While library extensions can have media files it is generally not recommended to ship media files with your library. If you have media files your library is not a library, it's more of a prototype extension. Sure, you can do that —that's pretty much what we were doing with Akeeba Live Update long before Joomla supported library packages— but the use cases are so limited in number and so advanced in nature that if you need to read this documentation you are extremely unlikely to need to use this trick.

Finally, pay attention to how you are going to go about updating your library extension. Joomla has absolutely no dependency management across installed extensions. If a site has Extension A which requires version 1.x of your library and Extension B which requires version 2.x of your library you'll inadvertently break the site if version 2.x of your library is not 100% backwards compatible to version 1.x of your library. This was one of the many headaches we had to deal with when we shipped our own MVC framework (Akeeba FOF) with our extensions. Short of checking for the installed library version in your extension and refusing to execute if it's too low or too high there's no other practical way to address this conundrum.