Work In Progress

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

Chapter 1. Basic concepts

As mentioned in the introduction, Joomla 4 introduced a lot of new features. Some of these features introduce new fundamental concepts or refine existing ones. Before delving deeper into Joomla extension development let's take a look at these basic concepts to make sure we are all on the same page.

Namespaces

The single most defining change in Joomla 4 is that Joomla embraces PHP namespaces everywhere: core, core extensions, third party extensions and everything in between.

The core

The work on namespacing core Joomla API classes had started since Joomla 3.3. If you have not updated your code the last few years you may have found that it no longer works on Joomla 4. Do not despair! The vast majority of issues comes from your using outdated, non-namespaced versions of core classes in your code. Replacing them with their namespaced versions will let most Joomla 3–only extensions to run well enough in Joomla 4 for a migration to be practical.

I have collected the old, non-namespaced core classes and the new, namespaced names of them along with the Joomla version the namespaced versions appeared and the Joomla version the non-namespaced versions are or will be removed in my Joomla Type Hints repository. The repository also includes Rector configurations which allow you to do most of the heavy lifting using an automated tool. For everything else you need some good old search and replace across your source trees.

The extensions

The story of Joomla 4 began on the last day of May 2015 in Prague, Czech Republic at a round table discussion about the future of Joomla. A dozen or so leadership members, core contributors and third party developers sat down to figure out why Joomla “sucks” and how to improve it.

Beyond the user-facing issues, we found out that the core MVC and architecture were extremely dated. The core MVC started being developed in 2005 and made its first appearance in 2007, with Joomla 1.5. It had not changed much, despite some moderate improvements in Joomla 1.6 which was released in 2010 (e.g. XML Forms) and a few disparate things added here and there (e.g. Tags in 3.2, Layouts in 3.4 and so on). You could not reference frontend code from the backend or vice-versa. For third party extensions this was an annoyance which led to duplicated code and bugs. For using core code, like creating articles the right way using com_content's Article model, it was nearly impossible: if you tried doing that on a page which had already loaded the front-end ContentModelArticle class you could no longer load the same-named but entirely differently operating backend class.

The way to solve that would be to namespace the extensions' code. A com_example extension could have the namespace prefix \MyCompany\Component\Example\Administrator for its backend classes and \MyCompany\Company\Example\Site for its frontend classes. This way you could have something like:

namespace \MyCompany\Component\Example\Site\Model;

class ItemModel extends \MyCompany\Component\Example\Administrator\Model\ItemModel
{
  // ...
}

By being able to extend classes across application sides you would finally be able to get rid of duplicate code and obliterate bugs coming from that bad practice.

By following the PSR-4 standard we could also have a very efficient autoloader which allows us to load any extension's classes from anywhere, without having to know where on the filesystem the class file lives and without having to use the ugly static methods of the core MVC classes (which, by the way, would NOT work predictably across extensions because of their dependence on the JPATH_COMPONENT_ADMINISTRATOR and JPATH_COMPONENT constants which cannot, of course, be redefined).

Each extension's namespace is declared in the XML manifest of the extension using a new XML element under the <extension> root element:

<namespace path="src">MyCompany\Component\Example</namespace>

The path attribute tells Joomla which subdirectory of your extension holds the PSR-4 of your extension's PHP files. It is best practice to name it src but you don't have to. In the rest of this section I assume you are using src.

The text inside the XML element, MyCompany\Component\Example in our example, is the namespace prefix you will be using.

Here is how namespace prefixes work for Joomla extensions:

  • All extensions start with a vendor namespace prefix. For Joomla's core extensions that is "\Joomla". For your custom extension you can use your own namespace, like your company name. This vendor namespace prefix can have multiple segments, for instance \Acme\Development\Utilities.

  • After the vendor namespace prefix there is a namespace segment to indicate the extension type: \Component, \Module or \Plugin.

  • When the extension type is a component or a module it is followed by:

    • a namespace segment with the extension name (without the com_ or mod_ prefix)

    • and after that a segment with the application type: \Administrator, \Site or \Api.

  • When the extension type is a plugin it is followed by:

    • a namespace segment with the plugin type, for instance \Content, \Finder or \System

    • and after that a segment with the extension name (without the plg_ prefix)

Joomla's extension namespace prefixes as a railroad diagram:

Examples across different types of extensions:

  • Component, frontend. The component com_example by Acme, Inc could have a namespace prefix of \Acme\Component\Example set up in its XML manifest. In this case its frontend classes MUST be under the namespace \Acme\Component\Example\Site. The folder components/com_example/src is the root of the \Acme\Component\Example\Site namespace.

  • Component, backend. The same component com_example by Acme, Inc which has a namespace prefix of \Acme\Component\Example. In this case its backend classes MUST be under the namespace \Acme\Component\Example\Administrator. The folder administrator/components/com_example/src is the root of the \Acme\Component\Example\Administrator namespace.

  • Component, API application. The API application is the third type of Joomla application (the other two being Site the frontend, and Administrator the backend). We'll talk about it later in this book. Let's say we have the component com_example by Acme, Inc which has a namespace prefix of \Acme\Component\Example. In this case its API application classes MUST be under the namespace \Acme\Component\Example\Api. The folder api/components/com_example/src is the root of the \Acme\Component\Example\Api namespace.

  • Module, frontend. The module mod_example by Acme, Inc could have a namespace prefix of \Acme\Module\Example set up in its XML manifest. If it is a frontend module the namespace will then be \Acme\Module\Example\Site and the folder modules/mod_example/src is the root of that namespace.

  • Module, backend. If that module mod_example by Acme, Inc would be a backend module, with the same namespace prefix \Acme\Module\Example declared in its XML manifest, the namespace will then be \Acme\Module\Example\Administrator. The folder administrator/modules/mod_example/src is the root of the \Acme\Module\Example\Administrator namespace.

  • Plugin. The plugin plg_system_example by Acme, Inc could have a namespace prefix of \Acme\Plugin\System\Example set up in its XML manifest. The folder plugins/system/example/src is the root of that \Acme\Plugin\System\Example namespace.

  • Template. Well, Joomla does NOT register a namespace for templates. You are encouraged to use namespaces for any template-specific code (you certainly have some helpers, don't you?) BUT Joomla won't facilitate you with any autoloading. You will have to register your template's namespace directly with the JLoader::registerPrefix method. It is a good practice putting your namespaced PHP files in the src directory and use the namespace \MyCompany\Template\Site\Something for frontend templates and \MyCompany\Template\Administrator\Something for backend templates. Please note that there is no real guidance for the template names BUT we can infer what any future change adding namespace support to templates will most likely be based on how modules are namespaced and Joomla trying to have a modicum of consistency.

How to choose a namespace for your extension

Namespaces have for two primary reasons of existence:

  • Uniqueness. Each of the extensions installed on the same site in different directories must have a unique namespace prefix which is not a subset or a superset of the namespace prefix of another extension and does not clash (be identical to) the namespace prefix of another extension.

  • Identifiability. Namespaces of Joomla extensions should make it fairly obvious which extension they belong to when read by people with little to no PHP coding experience.

Let's see some bad examples first.

  • A component with the namespace prefix Acme\Component\Example and a plugin with the prefix Acme\Component\Example\Plugin\System. This is not correctly the rules set out above and violates both reasons for having a namespace: the namespace prefixes are not unique (the plugin's prefix is a subset of the component's prefix) and it's hard to understand which extension they refer to.

  • A component released by Yoyodyne Corporation with the namespace prefix Acme\Component\Example. This violates the second rule as a casual bystander would assume that the component was written by a company called Acme, not Yoyodyne.

  • A system plugin called foobar with the namespace Acme\Plugin\System\Foo. Again, it violates both reasons as it's neither unique (plg_system_foo could use the same namespace!) and does not help people identify the plugin in question.

Based on the bad examples it's easy to understand what a good namespace is.

A good namespace is generally constructed as follows:

  • The company name, as commonly referred to in marketing and documentation. For example Joomla (the project name commonly used), not OpenSourceMattersInc (the full name of the non-profit legal entity which owns the Joomla trademark).

  • The type of the extension the namespace refers to, e.g. Component, Plugin, or Module.

  • (for plugins) The plugin type i.e. the folder it's installed into, e.g. System, Content, and so on.

  • The name of the extension, either as installed (e.g. ARS for com_ars) or as commonly referred to in the extensions manager (e.g. ReleaseSystem for com_ars).

Thus, a namespace like Akeeba\Component\ReleaseSystem gives us a pretty good idea that it's an extension written by a company called Akeeba, it's a component and it's name is something with Release System in it. Hm, it must be Akeeba Release System!

It is a very good idea that parts of your namespace when searched in the extensions manager end up listing your extension. This will help a user faced with an exception error page to figure out which extension the exception comes from, even though they have no idea about PHP or what an exception is.

If you get Class Not Found while developing extensions

Joomla automatically creates a map of installed extensions, their location and their namespace prefixes as noted in their XML manifests. It uses this information to register a PSR-4 autoloader which handles loading the various classes based on their name, without having to manually include their files yourself.

The information is cached in the file administrator/cache/autoload_psr4.php which is automatically generated on every page load if it's missing. It is also forcibly regenerated when you install, update, remove, publish, or unpublish an extension from your site.

It is possible that while developing your site you will end up with that file becoming out of sync with the XML manifests on your filesystem — for example, if you changed the namespace of an extension you are developing or if you moved files around. If that happens to you, simply delete the administrator/cache/autoload_psr4.php file and reload the frontend home page of your site. The file will be regenerated.

If something is missing from that file or if you keep getting problems despite that file appearing to be correct, ask yourself these questions:

  • Am I using the correct class name? Writing this book I've typed Acme\Component\Examlpe instead of Acme\Component\Example more times than I care to admit. Always start with the assumption that you typed the class name wrong.

  • Are my folders and files the correct letter case? Attention macOS and Windows users! While on macOS and Windows the folders FooBar, FOOBAR and foobar will all work this is not true on Linux! Also remember that if you commit a folder / file in the wrong case on Git you can't just rename it and commit it; it will not work when checking out the file. Instead rename the folder / file to its correct case with a -bak suffix, commit, remove the suffix, commit again.

  • Is the extension installed? You'd be surprised at how wrapped up you can get writing the code of the extension without remembering to actually install it on your development site. I do that every second plugin I write. At least I know that I'm both the detective and the killer in this code murder mystery, having gone through it so often.

  • Is the extension published? When you install a plugin it is disabled by default. Disabled extensions don't get autoloaded.

  • Am I using OPcache? If you're using OPcache —or any other PHP cache— your server might “remember” the older version of the autoload_psr4.php file. If unsure, restart the web server and the PHP-FPM service.

  • Have I cleared Joomla's cache? If you toggled the publish status of an extension in the database Joomla might not be aware that you did so (especially salient for plugins and modules). Clear Joomla's cache, administrator and site.

  • Am I using another cache, CDN or reverse proxy between my browser and the server? Don't do that during development!

  • Does the XML manifest exist in the location Joomla expects it and up-to-date? Components' XML manifests must exist in the component's backend root (e.g. administrator/components/com_example/example.xml). Plugins and modules expect the XML manifest in their respective folder. Make sure the manifest file you have there actually has the <namespace> tag, the namespace is spelled correctly and it has a path attribute which is also spelled correctly, both itself and the folder name which is your PSR-4 root (usually set to "src").

  • Is it possible that my namespace violates the two rules described above, especially the one about it not being a subset of another extension's namespace? If your namespace is a subset of another extension (e.g. my namespace is Acme\Component\Example\Foo and there's another extension with the namespace Acme\Component\Example) the PSR-4 autoloader will end up looking into the wrong folder for your files.