Skip to content

Commit da57f27

Browse files
authored
Merge pull request #158 from robbiejackson/namespaces
Namespaces
2 parents 309e67c + 881fe7b commit da57f27

File tree

8 files changed

+219
-19
lines changed

8 files changed

+219
-19
lines changed

.vscode/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"githubPullRequests.ignoredPullRequestBranches": [
3+
"main"
4+
]
5+
}

docs/general-concept/namespace.md

Lines changed: 0 additions & 19 deletions
This file was deleted.
52 KB
Loading
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
sidebar_position: 4
3+
title: Class Autoloading
4+
---
5+
A Joomla instance contains thousands of PHP source files, with a comparable number of PHP classes. it's obviously impractical to do a `require` of every source file, to make the PHP interpreter aware of all the classes. How then can Joomla avoid getting a PHP Class not found fatal error when it tries to access a class which the PHP interpreter doesn't recognise? The answer is that it uses [class autoloading](https://www.php.net/manual/en/language.oop5.autoload.php).
6+
7+
When Joomla initialises it sets up (in libraries/loader.php `setup()`) a number of autoloaders using, for example:
8+
```php
9+
spl_autoload_register(['JLoader', 'loadByPsr']);
10+
```
11+
12+
If the PHP interpreter encounters a line such as:
13+
```php
14+
if (class_exists($classname)) ...
15+
```
16+
and it doesn't recognise the name of the class then it will pass the class name to a registered autoloader and will call eg
17+
```php
18+
loadByPsr($classname)
19+
```
20+
passing the name of the class. From Joomla 4 onwards this should be a fully qualified classname.
21+
22+
The responsibility of the autoloader is to try and find the source file which contains that class, and perform an `include_once` on it, so that PHP then becomes aware of that class. For components, modules and plugins (both Joomla and third party extensions) Joomla uses the [PSR4](https://www.php-fig.org/psr/psr-4/) method of mapping class names to source files.
23+
24+
During initialisation Joomla looks through the components, modules and plugins directories for both the site and the administrator to find all the manifest XML files. It then loads the XML and looks for `<namespace>` elements, and from those elements builds a set of mappings of namespace prefixes to directories. As this involves quite a bit of work the result is cached in administrator/cache/autoload_psr4.php - go ahead and have a look at it!.
25+
26+
The `loadByPsr` function then uses this mapping and the PSR4 rules for finding source files described in [Finding classes with PSR4](./finding-classes-with-psr4.md) to determine the name of the file which should contain that class. It checks if the file exists and if so performs `include_once` on it, and returns.
27+
28+
If the autoloader `loadByPsr` function has successfully found the class, then PHP treats the `class_exists($classname)` expression as now `true`, and continues down the code. If it was unsuccessful then PHP raises the class not found exception.
29+
30+
Joomla has a number of autoloader functions - not all classes are loaded using PSR4. For example libraries classes all have a mapping of FQN to the individual source file for that class - have a look through libraries/vendor/composer/autoload_classmap.php.
31+
32+
There is also a mapping (in libraries/classmap.php) from old global library names starting with J (eg JModelAdmin) to the FQN equivalents, but this disappears in Joomla 6.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
sidebar_position: 3
3+
title: Defining your Namespace Prefix
4+
---
5+
# Defining your Namespace Prefix
6+
## Components
7+
When you develop a Joomla extension you define the namespace you want to use in your manifest file, eg for `com_example`:
8+
```xml
9+
<namespace path="src">Mycompany\Component\Example</namespace>
10+
```
11+
Let's look at the various parts of this:
12+
13+
- Mycompany – you are free to put whatever you like here – it's generally set to indicate the name of the company who developed the extension.
14+
15+
- Component – this must be set to Component if your Joomla extension is a component
16+
17+
- Example – this should match the name of the component you're developing
18+
19+
- src – this is the subfolder where you store your class files. You don't have to call it this, but it is common practice.
20+
21+
From this manifest file Joomla will create 2 namespace prefixes:
22+
23+
- 'Mycompany\Component\Example\Site\' will point to components/com_example/src
24+
- 'Mycompany\Component\Example\Administrator\' will point to administrator/components/com_example/src
25+
26+
(Assuming you have both site and administrator aspects to your component).
27+
## Modules
28+
You would do a similar thing for modules:
29+
```xml
30+
<namespace path="src">Mycompany\Module\Example</namespace>
31+
<files>
32+
<filename module="mod_example">mod_example.php</filename>
33+
<folder>src</folder>
34+
<folder>tmpl</folder>
35+
</files>
36+
```
37+
It's not essential to match the "Example" in the namespace to "mod_example" – the name of the module – but you'll probably find it easier to do that.
38+
39+
If this is a site module then Joomla will create the namespace prefix:
40+
41+
'Mycompany\Module\Example\Site\' will point to modules/mod_example/src
42+
43+
If this is an administration module then Joomla will create the namespace prefix:
44+
45+
'Mycompany\Module\Example\Administrator\' will point to administrator/modules/mod_example/src
46+
## Plugins
47+
For plugins:
48+
```xml
49+
<namespace path="src">Mycompany\Plugin\Content\Example</namespace>
50+
<files>
51+
<filename plugin="example">example.php</filename>
52+
<folder>src</folder>
53+
</files>
54+
```
55+
Here there's an extra part to the namespace, which must be set to the plugin type – 'Content' in the example above.
56+
57+
Once again the 'Example' segment in the namespace doesn't have to match exactly the plugin="example" name of the plugin.
58+
59+
For the above Joomla would create the namespace prefix:
60+
61+
'Mycompany\Plugin\Content\Example\' will point to plugins/content/example/src
62+
63+
## Capitalisation
64+
**Be careful to ensure that capitalisation within parts of the fully qualified classname matches capitalisation within the names of directories and files! If you develop on Windows but your target system is linux you may find that your extension works on your development machine but not on your target system. This is because Windows is forgiving if you have wrong capitalisation in your filenames or directory names – it will still open the file for you – but linux isn't.**
65+
66+
## Finding your Classes
67+
Once you have decided upon your namespaces then you can sort out where you class files are going to be located. Joomla components use a fairly flat directory structure under `src`, but you don't stick to that. As long as you adhere to the PSR4 recommendation for matching fully qualified class names to file paths then Joomla will find the source files for your classes. Gone are the days when you had to guess what directory to store your helper file in, and what to name to give the file!
68+
69+
However, there is one case where you need to give Joomla a helping hand - in your form XML files.
70+
- if you define a custom field then you need to tell Joomla where to find the class which defines that custom field
71+
- if you define a custom validation rule then you need to tell Joomla where to find the class which defines that rule
72+
73+
As XML files don't have PHP `<namespace>` statements you have to provide the equivalent, and it's easiest to do this by including an `addfieldprefix` or `addruleprefix` attribute within an XML element which encloses where you use them, eg:
74+
```xml
75+
<?xml version="1.0" encoding="utf-8"?>
76+
<form
77+
addruleprefix="Mycompany\Component\Example\Administrator\Rule"
78+
addfieldprefix="Mycompany\Component\Example\Administrator\Field"
79+
>
80+
```
81+
82+
## PHP Global namespace
83+
If you have a `<namespace>` statement in your PHP source file then PHP will by default assume that any classnames you refer to in your code will be under that namespace. So if you use any standard PHP classes such as `Exception`, you will have to precede them with a backslash `\Exception`. The backslash at the start indicates to PHP that this is a fully qualified classname, and as there is just one segment to this FQN it will be found in the global namespace.
84+
85+
Similarly if you use a function, then PHP will try to find that function under your namespace. But unlike classes, if it doesn't find the function under your namespace it will revert back to looking for it in the global namespace. So it's marginally more performant if you tell PHP not to bother looking in your namespace by prefixing the function name with a backslash, eg `\strlen(...)`. You will see this present at times in the Joomla PHP code.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
sidebar_position: 1
3+
title: Finding Class files with PSR4
4+
---
5+
Namespaces allow us to organise our PHP classes in a similar way that directories allow us to organise our files. In particular, the [PSR4 recommendation](https://www.php-fig.org/psr/psr-4/) which Joomla uses enables us to determine what file a certain class should be in.
6+
7+
![Namespacing](_assets/namespace.jpg "PSR4 namespacing")
8+
9+
To see this, let's take an example of the file administrator/components/com_content/src/View/Articles/HtmlView.php, which contains the code for the view which presents the Articles to the administrator. This file contains the lines
10+
```php
11+
namespace Joomla\Component\Content\Administrator\View\Articles;
12+
...
13+
class HtmlView extends BaseHtmlView { …
14+
```
15+
so the fully qualified name (FQN) of the class is `\Joomla\Component\Content\Administrator\View\Articles\HtmlView`.
16+
17+
Joomla holds a list of namespace prefixes strings, and it compares the string containing the fully qualified class name to that list, starting at the left hand side of the class name, to see if it can find a partial match. In this example the namespace prefix `'Joomla\Component\Content\Administrator'` will match.
18+
19+
This namespace prefix has an associated directory within the file system, which is the starting point for finding classes. In this case the directory is administrator/components/com_content/src. (We'll see shortly how the namespace prefixes and associated directories are defined).
20+
21+
Next it removes the matching namespace prefix from the FQN, leaving \View\Articles\HtmlView, and treats this as the path down from the above directory. So the full file path is: administrator/components/com_content/src/View/Articles/HtmlView.php. The diagram gives a pictorial view of this.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: Namespaces
3+
---
4+
Namespacing was progressively introduced for Joomla classes over a number of Joomla 3.x releases. In Joomla 4 nearly all of the Joomla classes were namespaced, and it was expected that Joomla extensions were namespaced as well.
5+
6+
If you're not familiar with PHP namespacing then you can find general information online, for example at https://www.php.net/manual/en/language.namespaces.php
7+
8+
This Joomla documentation focuses on how namespacing is implemented within Joomla.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
sidebar_position: 2
3+
title: Joomla Namespace Prefixes
4+
---
5+
# Joomla Namespace Prefixes
6+
7+
Joomla holds namespace prefixes and their mapping to the position in the file system for the following:
8+
9+
## Components
10+
`'Joomla\Component\<Component name>\Administrator\'` points to `administrator/components/com_<component name>/src`
11+
12+
`'Joomla\Component\<Component name>\Site\'` points to `components/com_<component name>/src`
13+
14+
`'Joomla\Component\<Component name>\Api\'` points to `api/components/com_<component name>/src`
15+
16+
eg 'Joomla\Component\Content\Administrator\' points to administrator/components/com_content/src
17+
18+
## Modules
19+
`'Joomla\Module\<Module name>\Administrator\'` points to `administrator/modules/mod_<module name>/src`
20+
21+
`'Joomla\Module\<Module name>\Site\'` points to `modules/mod_<module name>/src`
22+
23+
eg 'Joomla\Module\Login\Site\' points to modules/mod_login/src
24+
25+
## Plugins
26+
`'Joomla\Plugin\<Plugin type>\<Plugin name>\'` points to `plugins/<plugin type>/<plugin name>/src`
27+
28+
eg 'Joomla\Plugin\Fields\Calendar' points to plugins/fields/calendar/src
29+
30+
## Library Classes
31+
'Joomla\CMS\' points to libraries/src. Note that these are the classes which are described in the [API docs](https://api.joomla.org/) on the [Joomla CMS](https://api.joomla.org/cms-4/index.html) side.
32+
33+
'Joomla\SomethingElse\' points to libraries/vendor/somethingelse/src.
34+
35+
eg 'Joomla\Event\' points to libraries/vendor/event/src
36+
37+
Note that these are the classes which are described in the [API docs](https://api.joomla.org/) on the [Framework](https://api.joomla.org/framework-2/index.html) side.
38+
39+
(As an aside, just be aware that if a class on the CMS side inherits from a class on the framework side, then not all of the methods available might be in the API docs. For example, the class Joomla\CMS\Application\WebApplication has methods such as `setHeader` because it extends [Joomla\Application\AbstractWebApplication](https://api.joomla.org/framework-2/classes/Joomla-Application-AbstractWebApplication.html), but this function isn't listed in [WebApplication API doc](https://api.joomla.org/cms-4/classes/Joomla-CMS-Application-WebApplication.html).)
40+
41+
If a library classname doesn't start with 'Joomla' then it's going to be found in one of the other directories under libraries/vendor/.
42+
43+
(Note that all the above is the general standard for Joomla code – you might however find the odd exception).
44+
45+
If you look in administrator/cache/autoload_psr4.php you'll see all the namespace prefixes for Joomla component, modules and plugins, together with the associated position in the file system (and also the namespace prefixes of any installed extensions).
46+
47+
## Duplicate class names
48+
Before namespacing was introduced Joomla had a lot of duplicate classnames, eg. for com_example the MVC model code would be in the class ExampleModelExample for both site and administrator, with both classes in the global namespace. This presented an obstacle to sharing code - you couldn't just have your site model class inheriting from your admin model class, for instance.
49+
50+
With Joomla namespacing, the FQNs of the site and administrator model are different, as they're in different namespaces. This makes it very easy to share code between them - you just have to let one model class inherit from the other.
51+
```php
52+
<?php
53+
namespace Mycompany\Component\Example\Site\Model;
54+
55+
use Mycompany\Component\Example\Administrator\ExampleModel as AdministratorModel;
56+
57+
class ExampleModel extends AdministratorModel {
58+
```
59+
60+
A word of warning: although Joomla has unique FQNs for all the classes, **there are some Joomla library classes which share the same last segment of the FQN**, for example:
61+
62+
- Registry may refer to Joomla\Registry\Registry (a utility class for holding data structures) or Joomla\CMS\HTML\Registry (a class for holding snippets of HTML used in `HtmlHelper::_()` calls)
63+
64+
- CategoryFactory may refer to Joomla\CMS\Extension\Service\Provider\CategoryFactory or Joomla\CMS\Categories\CategoryFactory - there are several similar instances where the same last segment of the FQN may refer to the Factory class, or to the service provider class which registers the Factory class in the dependency injection container.
65+
66+
- DispatcherInterface may refer to Joomla\CMS\Dispatcher\DispatcherInterface (the DispatcherInterface for components and modules) or Joomla\Event\DispatcherInterface (the DispatcherInterface for plugins).
67+
68+
You just need to be careful to examine the `use` statement to see exactly which class is being referred to.

0 commit comments

Comments
 (0)