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.
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_
ormod_
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 foldercomponents/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 folderadministrator/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 folderapi/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 foldermodules/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 folderadministrator/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 folderplugins/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 thesrc
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 prefixAcme\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 ofAcme\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
andfoobar
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 apath
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 namespaceAcme\Component\Example
) the PSR-4 autoloader will end up looking into the wrong folder for your files.