User tests: Successful: Unsuccessful:
As with the move to namespaces in J4, we should reconsider class loading. It would be nice when class path management is done automatically in the background.
With this pr an extension can provide an autoload.php file which registers its own classpath on the class loader. Like that there is no need anymore during runtime to manually mess with classloading in extensions. This means an extension must not write anymore require
, include
or JLoader::import()
. All is set up already when the application is executed.
Additionally an extension can define alias when some classes got moved around to keep backwards compatibility. The same procedure as we do with the core classes.
This pr extends JLoader the way that it can automatically load classes solely based on the namespace when they follow a certain convention.
Play around with the articles manager and none namespaced extensions.
All is working as expected.
All is working as expected.
Classloading needs to be documented.
Status | New | ⇒ | Pending |
Category | ⇒ | Administration com_content Modules Libraries |
Labels |
Added:
?
|
Yes the idea is that there are no more require, includes or JLoader::import calls needed. Also adding lookup paths to Controller, Model, View and Table are then obsolete as well.
General namespace conventions in the PHP ecosystem are <Vendor>\<PackageName>
(i.e. Joomla\Registry
) or <Vendor>\<PackageType>\<PackageName>
(i.e. Symfony\Component\Yaml
). So the client shouldn't be the second namespace segment.
Title |
|
I more or less like the idea of flexibility. However, what would be the need for a Joomla extension developer to use a different autoloader than the default Joomla autoloader and/or PSR-4 autoloading? I can imagine that some developers will still want to keep their old code intact, but is that healthy. With a major bump, I would find it normal to have developers clean up their code and update it to current PHP standards, using namespaces and PSR-4 autoloading.
@jissereitsma encourage - yes - force - no
We decided to support during the Joomla 4 lifecicle the not namespaced (legacy) extensions, so we don't want to force them to use namespaces in J4.
With this pr, the extension developer can decide by himself which class loader to use. If his extension is fully managed by composer then this will not be an issue. The important part is that during runtime it would be nice when we don't have to deal with class loading, they are just there.
Here my feed-back to this RFC:
I love autoloading, but not with many auto-loaders. Any component can already now add his own auto-loader as needed, which gets initialized only when the component is executed.
Adding 150-250 file_exists() (number of entries in #_extensions
table is a major performance hit imho.
Then if each extension has its own auto-loader then each new class use will add another 150 autoloader.php files includes, which will be another major performance hit.
Thus, I suggest that we rather define a PSR-4 namespace scheme which is standard for Joomla extensions, and have the Joomla core loader auto-load from extensions space.
@laoneo @brianteeman Thanks for explaining. I think @beat is spot on with his remark on performance. However, I don't think any extension would need the ability for autoloading. I'm not sure what the current plans are on dumping modules and plugins, but thinking along the line of the current J3 extension types, only components perhaps need this ability?
@jissereitsma If it is only the current component executed to render the current page, then you don't need to loop to load the auto-loader of each extension. In that case, I would prefer a as early as possible component onbefore event that allows the component to load the auto-loader than having a systematic file_exists that is not useful for most components (if they follow a "Joomla component PSR-4" class namespace naming rule.
Really i don't think that extensions require a standard autoloading system included in the CMS.
I already do autoloading for classes in my extensions since J2.5, i think that it must be up to the extension developer to choose witch method to use to load classes and files in their own components.
Always think to a component as a separate application running in the CMS eco system. What happens inside the component must be almost at the sole discretion of the extension developer.
I could be used to import a composer autoloader in my extension, or for example a prefix autoloading, etc
A developer will still be able to do what they want. But in essence the changes to the MVC layer classes will eventually force developers using those classes to have to migrate at some point when it's determined the compatibility layer will no longer be maintained (i.e. if we were to fully deprecate file path lookups for model, table, controller, and view classes in favor of using a class autoloader which therefore requires unique class names for all entities versus the logic we have now of duplicated class names and our factories in essence handling the responsibility of loading classes and files into PHP).
IMO there are some conventions we need to break in Joomla. The "one active component per request" logic needs to go away, especially as we build more tooling where more components are involved in a request (on the frontend for a single article you could easily have com_content, com_tags, com_fields, and com_contact all involved in the handling of things). And that doesn't include modules, and aside from the feed and custom HTML modules, most of the core modules are in some way tied to a component). That assumption must stay true when it relates to routing, but for system internals, we break it.
@joeforjoomla that's what this pr is about. Now we give the extension devs a standard way to do autoloading when they want. This is absolute optional.
What happens inside the component must be almost at the sole discretion of the extension developer.
This was true a couple of years ago, very extension was a silo for itself. Times have changed. Nowadays more and more extensions do have dependencies/relationships to other extensions. So it would be good if extensions do not have to care about class loading at all.
@beat Performance can be improved. I'm thinking about a flag in the extension table if a component supports autoloading, so we do only load the extensions which do actually support it. But honestly, I don't think this is really an issue as we can eliminate all the JLoader::register and JLoader::import calls in core which balance it out.
Then if each extension has its own auto-loader then each new class use will add another 150 autoloader.php files includes, which will be another major performance hit.
Don't understand that, with PSR-4 you define one filesystem path per namespace. It is one autoload file per extension.
Category | Administration com_content Modules Libraries | ⇒ | Administration com_content com_postinstall Modules Templates (admin) Libraries |
I think the real benefit of having all extensions auto-loaded is that it allows outside code (a module, a plugin, a different component) to use code of a component classes (in most cases, it's component model classes) without having to worry about registering auto-loader. It results in less, clean code.
With that said, I think for now, we can start with auto-loading all components first as I could not see the benefit of auto-loading modules or plugins classes yet. If you worry about the performance issue of file_exists check, I wrote some code to calculation the execute time in case we just auto-load components (default Joomla installation) and the execute time on my local computer is about 0.0095059871673584 seconds. Is it acceptable?
I think we don't have to worry at the current state about performance as this is solveable. I think the best way would be a flag in the database if the extension is autoloaded.
@joomdonation in my latest commit I'v changed the mod latest to namespace and autoload. It looks now cleaner. So it would make sense to autoload all extensions.
@joomdonation @laoneo Performance in the web is important and adding 9.5 ms to each http access is a big performance hit. Look at sucessful projects: They have a performance metric monitor that is part of automated test-suites and they are becoming faster at each release. Autoloading's main goal should be to speed things up, not to slow them down. Adding and loading an additional column from extension is not going to help drastically either. If you do that, then instead of doing that for auto-loading, you should do that at a higher level, for dependencies handling and auto-load at that level.
I still don't understand why each extension needs a specific auto-loader "baby-sitted" by Joomla. Make the namespaces standard, and have the generic Joomla auto-loader handle them. Rest should remain specific to each extension.
So I still don't understand the benefit for the end-user or the developer of a specific auto-loader per extension that gets loaded by joomla instead of the extension.
@laoneo The code in the module looks cleaner than before because it was messed up when we were trying to use dispatcher to get model class and the component is not autoload yet. Assume that that component is autoloaded, the code of the module would become:
use Joomla\Component\Content\Administrator\Model\Articles;
JLoader::register('ModLatestHelper', __DIR__ . '/helper.php');
$list = ModLatestHelper::getList($params, new Articles(array('ignore_request' => true)));
if ($params->get('automatic_title', 0))
{
$module->title = ModLatestHelper::getTitle($params);
}
require JModuleHelper::getLayoutPath('mod_latest', $params->get('layout', 'default'));
The same as the code you are having now, except that we don't need to create an extra autoload.php file and save performance which others concern about. I still don't see the benefit of namespace just a helper file of a module like how you did in this PR.
@beat The reason I think auto-load components is good is because it allows modules and plugins (even a different component) to use a component code without having to know about and register component autoloader
Look at this module for example https://github.com/Digital-Peak/joomla-cms/blob/j4/feature/autoloading/administrator/modules/mod_latest/mod_latest.php, assume that component autoloader has not been registered yet, we would have to register autoloader like this https://github.com/Digital-Peak/joomla-cms/blob/j4/feature/autoloading/administrator/components/com_content/autoload.php in every modules which want to use com_content model classes . And if the model extends frontend model (which is possible with namespace MVC which we are having right now), we even have to register frontend namespace, too.
@Fedik Just saw your comment. The code you want to write in your comment is already possible with current namespace MVC - mean even without this PR.
Performance in the web is important
I don't say that it is not. I think there is room for optimisation and we will be able to solve it.
Make the namespaces standard
How should a namespace then look like in your eyes for an extension?
and have the generic Joomla auto-loader handle them
The problem is then that there is no way to add class alias or starting up the extension specific composer loader.
@laoneo How the namespaces should look for specific extensions is a bit challenging indeed. But we can analyse it a bit. The first part is reserved for a company or personal name (example JisseReitsma
, that's me!), second part for the extension name (MyFirstExtension
). The challenge will be the third part I guess. This could be identifying the extension type that you are offering - for instance a Component
or Template
. This would lead to the ability to have multiple extension types bundled in the same extension suite (JisseReitsma\MyFirstExtension\Component\Model\Item
and JisseReitsma\MyFirstExtension\Module\ItemList
). I'm not sure how advanced the talks of a standardized namespaced naming already has, but perhaps my 50 cents is useful ;)
The challenge will be the third part I guess. This could be identifying the extension type that you are offering
@jissereitsma that can be bound to the directory structure (some scaffolding that will be required)
I'm not sure how advanced the talks of a standardized namespaced naming already has,
Imho, that should have been the very first step. I as well think ideally components shouldn't need an own autoloader and the Joomla loader can figure it out itself as long as the components follow that standard. If Joomla can't load it, it could fall back and check for an autoloader in the component so they can do their own thing if needed.
The problem is then that there is no way to add class alias or starting up the extension specific composer loader.
That isn't necessary a problem for 3rd party extensions. They have different requirements than Joomla itself.
What we are orienting us is the doc from Odense. There the attendees decided to use the following pattern:
If we really want to go the path of some magic lookup, then 3rd party needs to be Akeeba\Component\Backup\Site. I guess then we can do some magic autoloading for all extensions as the second segment will always be the extension type and the rest the folders. Honestly I think this can work.
I guess then we can do some magic autoloading for all extensions as the second segment will always be the extension type and the rest the folders.
Simple solutions tend to be the most efficient ones!
The only problem with that is our loader basically has to do a wildcard on the first segment of the namespace (the vendor). So if a *\Component
prefix can work with an autoloader and the third segment can directly map to the component's name in the components directory (so Joomla\Component\Content
is com_content
, Akeeba\Component\Backup
is com_backup
), then we're in business.
Category | Administration com_content Modules Libraries com_postinstall Templates (admin) | ⇒ | Administration com_content com_postinstall Modules Templates (admin) Libraries Unit Tests |
Labels |
Added:
?
|
I'v reverted the autoload.php code and extended JLoader the way that it is capable to autoload extensions based solely on the classname. As soon as one class is loaded from an extension which does support the scheme it is added to the registered namespaces and on the second call the autoload lookup is not done. Guess we are on the good side now.
a heretic (and maybe stupid) question: Why do we even need the vendor? I mean I know it is "best practice" and it makes sense for distributed libraries to avoid namespace conflicts in PHP applications. However in our context we will have an issue way before the namespace becomes an issue. If two extensions share the same name (and thus the same namespace), they will overwrite eachother during installation. Thus eliminating the namespaced classes from the other one and removing the potential conflict.
Also if we have to ignore the vendor for the autoloading anyway, it makes even less sense to me since similar namespaces (except from vendor) wouldn't be resolved properly anyway :)
Also, I wonder why Site and Admin has to come after Component | Module | Template. Imho it would be more intuitive to have it before so it would follow more or less the folder structure. But I guess that's a minor thing and the autoloader can figure that out.
We are already dealing with distributed code in the context of Joomla, except in our case it is more constrained to the Joomla ecosystem than the greater PHP ecosystem as a whole. Naming conflicts can and will crop up no matter what our structure is.
Namespace prefixes shouldn't be manipulated to fit the Joomla filesystem structure or extension concept unless there is a very technically strong reason to do so.
Do realize that we already have namespaced code and extensions floating around the Joomla ecosystem (i.e. Akeeba products and our Patch Tester component). So with that said...
And one more comment. Generally the way things have worked in the PHP ecosystem is your filesystem essentially follows your class naming convention since most autoloaders map class name structures to filesystem paths. So namespaces aren't intended to actually represent your filesystem structure by design, it just ends up following that pattern because of how most code is written.
My idea would be that I can use a standard namespace pattern like Joomla/Site/Component/Sermonspeaker
(or Joomla/Component/Sermonspeaker/Site
if you prefer that) which implies that I intend to use the Joomla autoloader for my component. If Akeeba uses Akeeba/Foo/Bar
then I would expect an own autoloader which is either registered by the extension into JLoader and/or JLoader has a default place where it would look for an autoloader file (eg what Allon initially proposed here).
That way we would allow 3rd parties the flexibility to have their own autoloader and we can still have a "simple" mode for extensions which just want to use the default behavior.
It may of course be a stupid idea due to my very basic understanding of namespacing. It's just what would be the most intuitive to me with a very low barrier as entry point.
Generally with namespaced code the first couple of segments describe who released the package and what it is (i.e. Joomla Content Component from Joomla\Component\Content
or Joomla! Framework Website Controller from Joomla\FrameworkWebsite\Controller
. The only "issue" with your examples is it would actually make it easier to look like "this is code released by Joomla", hence the reason using a "proper" vendor is beneficial.
If you really wanted to force something though, JoomlaComponent\SermonSpeaker
(remember namespaces don't have to be only the first character as uppercase or anything goofy, with components as long as we can take any piece of it and basically turn it into $component = 'com_' . strtolower($segment);
we're good) which would somewhat make clear that it's a Joomla component, but it still potentially identifies the code as being "owned" by Joomla.
The only "issue" with your examples is it would actually make it easier to look like "this is code released by Joomla", hence the reason using a "proper" vendor is beneficial.
Yeah, if you look at it as the vendor, then that's the case. So far I look at it just as "this extensions operates within the Joomla namespace", which would be the case.
JoomlaComponent\SermonSpeaker
Looks a bit ugly but could work as well.
Whatever we do, imho core extensions should use a similar namespace than what 3rd party should use. Eg if 3rd party should use JoomlaComponent/Foo
then core should use that as well and not Joomla/Component/Foo
. If we have to use Bakual/Component/Sermonspeaker
then of course core should use eg Joomla/Component/Content
. After all, core extensions serve as example code in many cases.
I don't think it is important to distinguish core extensions from 3rd party through the namespace thought. Imho that's what all those doc blocks and XML attributes are for. It's simple enough to find out if it is core or not when someone wonders when looking at the code.
I'm here with Michael, the namespace should be obvious and a 3rd party extension should not have Joomla as vendor IMO. But with the actual code it is totally up to the extension dev what for a vendor is chosen, if she/he likes Joomla as vendor then its ok. I really don't understand why we need to discus about that. I really think that we should follow here the PSR 4 recommendations which do state
The fully qualified class name MUST have a top-level namespace name, also known as a “vendor namespace”.
I really think that we should follow here the PSR 4 recommendations which do state
PSR-4 states that there must be a top-level namespace name, which we have. So we absolutely follow the spec here.
Remember PSR-4 describes the autoloading functionality, it isn't primary a namespacing definition. The top-level requirement here comes from the requirement to have a namespace "prefix" (which defines the base directory) and a class name. So the absolut minimum classname would be Toplevel/Class
. Otherwise PSR-4 will not work.
If we're going to ignore the "vendor" namespace in our autoloader, then it absolutely doesn't matter what we enter there. Could be Joomla
or StupidNameSpace
, it will have no impact.
I think it doesn't make sense to just ignore a part of the namespace.
Better would be to have it act as the "switch" for the autoloader so it only runs our lookup path for the "Joomla" namespace. And I think that was also the intention of the PSR-4 definition.
Keep in mind that the extension routine comes into action when there is not already a registered namespace fof the class.
You're absolutely right in the way the autoloader implementation is set up right now the first segment of the namespace doesn't matter. That doesn't mean we drop it completely (otherwise our "vendor" namespace segment is going to become the extension type, that's bad too). Honestly we're just wasting massive time fighting over something that really doesn't matter, let's follow a best practice for once in Joomla's development lifetime here and use a "proper" namespace setup instead of defining our own rules and continue to be the ass end of jokes in the PHP ecosystem because we're so short sighted we keep doing our own thing and ignoring everyone and everything else.
@mbabker I didn't say to drop it. I'm thinking about ways to make it useful for for 3rd parties who want to use their own autoloader.
I'm all for following "best practice", but I see them as recommendations and not as hard rules. I usually want to know the "why". Sometimes, best practices aren't best practices depending on context.
Here I think the toplevel namespace (aka vendor) is meant to prevent conflicts between different vendors which have otherwise implemented the same subnamespaces/classes (eg a fork from a library).
If we can use it to autoload classes following the "Joomla" namespace (and thus folder/class structure) and for extensions with an own vendor namespace we instead look for an autoloader file to include (in a standard place), then I think that would be an interesting thing. As it is now, 3rd parties need to register the namespace manually within their code if they don't follow the conventions. Which isn't a big deal either imho. It's a simple call (especially if the $type argument defaults to PSR-4).
if you think that makes no sense, then the current proposal looks fine to me as it is very easy for 3rd parties as long as they follow our conventions. and it should also be good from a performance perspective.
I still think that my initial peoposal makes sense as well. If we can agree on this one, then we can add support for an autoload.pho file on a second step. For me was interesting to hear if the community is open for such new ideas. Looks like it is.
For me was interesting to hear if the community is open for such new ideas.
Definitvely. It will be a huge plus for namespacing extensions if you don't have to care about class loading at all.
For me was interesting to hear if the community is open for such new ideas.
Yes there is definitely a lot of interest from the community. If things like this would have happened years ago, extension developers wouldn't have implemented their own autoloaders so we would not have this discussion now...
I would also be in favor for a common autoloader instead of setting up a custom scenario for every extension.
I don't mean to complicate things but IMHO one of the first pieces required to support namespaces is renaming the extensions top folder like this:
/components/com_foobar/
/components/Foobar/
I believe this would solve some problems with namespacing and prevent quirky code to support namespacing the "Joomla! Way"™
Oh and BTW Thanks @laoneo for posting this link:
https://volunteers.joomla.org/teams/joomla-4-architecture/reports/115-joomla-4-architecture-sprint-odense-dk
I actually clicked it and red it from the top to the the very end getting more and more excited. Then I realized that it is actually almost two year old
Not very much has been implemented since in J!4
So, @laoneo please go on with your effort here - BIG
Not very much has been implemented since in J!4
This was for the group since named as Joomla X. But there's things for Joomla 4 we can take out of it :)
Umm, looking both at the URL and the post - there are lots of 4s and very few Xs - but sorry for misunderstanding then
I hope that you will take a lot of things out of this post - especially the Doctrine part...
That's because back then Joomla X was going to be Joomla 4 :P i know it's confusing as hell :) We aren't doing the doctrine part - sorry :(
I don't mean to complicate things but IMHO one of the first pieces required to support namespaces is renaming the extensions top folder like this:
/components/com_foobar/
/components/Foobar/
I certainly wouldn't start renaming the component folder (or extension folders in general) as that would have an impact on many other places in the CMS, plus it will make upgrading an extension much more complicate.
I also don't think it complicates things a lot when I look at the code proposed in this PR. It looks quite straight forward.
Status | Pending | ⇒ | Fixed in Code Base |
Closed_Date | 0000-00-00 00:00:00 | ⇒ | 2017-04-25 23:54:04 |
Closed_By | ⇒ | wilsonge |
I like the idea.
is it will allow to do something like:
without any extra includes by hands?
btw, why the
Administrator
at middle? can it be after the Vendor:Blabla\Administrator\Component
..Blabla\Site\Component
..just curious