Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Composer packages do provide their own autoload.php. But it can be more efficient to stick all namespaces into one shared class loader.
Two modules exist for D7 which provide PSR-0 class loading: classloader and xautoload.
xautoload-7.x-3.x already has support for sites/*/vendor as composer vendor directory.
#1923724: Composer support for sites/all/vendor and sites/$sitename/vendor
I am not really sure about the future of this. I don't know if sites/all/vendor is the correct place, and I don't know if xautoload should do this by itself or if another module should register those namespaces.
Comments
Comment #0.0
donquixote CreditAttribution: donquixote commentedUpdated issue summary.
Comment #1
cpliakas CreditAttribution: cpliakas commentedThanks for pointing out the other projects.
I'd love to explore a soft dependency with existing class loaders. It is only a few lines of code to require the autoloader generated by Composer so I don't think a hard dependency would be warranted, but it would be nice to give people the option of using a single autoloader if they wanted to.
As a side note, I think all of the current solutions (including this module) have benefits and drawbacks, and I am also unsure if
sites/*/vendor
is the best place as well. The goal of Composer Manager is to ease people into Composer and Packagist by defaulting to common Drupal workflows, although they probably don't make sense in the long run. We have also been experimenting with another structure that I illustrated in #1929626: Composer, Composer Autoload, Composer Vendor, Composer Manager?. There are a lot of different possibilities, and I would love to hear what people thing are best practices in doing all this stuff.Thanks for the post!
Chris
Comment #2
Grayside CreditAttribution: Grayside commentedComment #3
donquixote CreditAttribution: donquixote commentedAnother option:
- Always register composer class loader (I think the registration itself is not too expensive)
- Provide a reliable way for other modules to know about the composer directory (which contains the autoload_classmap.php), or to know directly about the map of classes / namespaces.
- Provide a reliable way for other modules to know if new namespaces need to be registered
- Let the other module do the rest.
E.g. for xautoload I could imagine
- A setting in xautoload to let it handle composer namespaces (configured on performance settings page).
- Let xautoload replace the registered composer loader, if the setting is enabled.
- xautoload will directly check autoload_classmap.php to get the namespaces to register.
Motivation:
I want to absolutely avoid that xautoload becomes or is perceived as a possible risk to the system.
E.g. if someone has the wrong version of xautoload, etc.
Comment #4
donquixote CreditAttribution: donquixote commentedWhat kind of "swappable" are you thinking of? The native spl_autoload_unregister() ?
Comment #5
cpliakas CreditAttribution: cpliakas commentedLet's start with Xautoload since it seems to be the most popular D7 solution.
Any guidance as to how we can start using this?
Comment #6
donquixote CreditAttribution: donquixote commentedI think it would be a good idea to wait until D8 gets PSR-4, and xautoload gets a new branch which supports that.
Maybe that new branch will have simplifications compared to the existing branch.
Comment #7
donquixote CreditAttribution: donquixote commentedSee #2083547: PSR-4: Putting it all together
Comment #8
cpliakas CreditAttribution: cpliakas commentedWasn't aware of PSR-4. Thanks for pointing it out.
As per recommendation, postponing until progress is made on the issue mentioned above.
Comment #9
donquixote CreditAttribution: donquixote commentedI once had the idea to provide this convenient method when implementing.
But apparently this never made it into the module.
Would you say this is useful?
Comment #10
cpliakas CreditAttribution: cpliakas commentedDefinitely. To me if someone chooses to use xautoload, then I am fine implementing a hook in this module that leverages that system. I just want to make sure that only one autoloader is registered at a time to prevent any weirdness, so I would be interested to hear your thoughts on how to handle if it even is an issue.
Comment #10.0
cpliakas CreditAttribution: cpliakas commentedUpdated issue summary.
Comment #11
donquixote CreditAttribution: donquixote commentedI want to mention that xautoload has a new branch 7.x-4.x with PSR-4 support, and an improved API.
This is going to be in alpha until Dries makes a decision on #1971198: [policy] Drupal and PSR-0/PSR-4 Class Loading, but this should not be an obstacle for 3rd party integration.
Also some interface names should be fine-tuned, and compatibility with the 7.x-3.x branch needs to be tested.
The new xautoload API can do this, similar to what i already proposed in #9:
xautoload is designed to do everything pretty early in the request. This particular stuff happens on hook_init() or hook_custom_theme() whichever is first, and/or when new modules are enabled.
On the other hand, if APC cache or another cache is enabled, then all the registration stuff will only happen if there is a cache miss. This means, composer_manager_xautoload() will not fire at all on an average request. I think this is pretty awesome :)
There could even be ways to do this in hook_boot() or even earlier..
The real question is, how early is desirable?
#2157583: Fatal errors thrown when a module requires a dependency in composer.json that other modules include in their code base this one is a bit confusing to me, I think we need to get to a conclusion there before we talk about xautoload integration.
Comment #12
cpliakas CreditAttribution: cpliakas commentedComment #13
tobiasbWith this fix.
I can use classes via composer in rules hooks (hook_rules_data_info()) again. \o/ Because _rules_rebuild_cache() is called before composer_manager_init(). I do not why, but it happens.
Comment #14
heddn[#2246699] is in now. Removing postponed tag. I'd love to see something in the contrib space go in for composer so I can use it for htmlpurifier on D8
Comment #15
cpliakas CreditAttribution: cpliakas commentedThanks for your interest.
Re-setting the status just so we are consistent with the statuses in https://drupal.org/node/156119. If anyone wants to move this issue forward I would love to get a patch, however re-marking as postponed since I do not indent to work on it in the near term (reiterating that I would be happy to commit a patch it someone did want to actively work on this.
Thanks,
Chris
Comment #16
donquixote CreditAttribution: donquixote commentedIf you look at a stack trace, often such hooks are called as a side effect of things other modules do in hook_init(), hook_custom_theme() or hook_boot(). xautoload "cheats" by implementing hook_custom_theme() in addition to hook_init(), because hook_custom_theme() fires earlier in the request.
Can you confirm this still works with xautoload 7.x-5.x?
Comment #17
donquixote CreditAttribution: donquixote commentedOne problem with xautoload is that someone might have the wrong version of xautoload installed, and gets a WSOD for some features not being supported yet.
Not sure how we can solve this, if we don't want to make it an explicit dependency..
Comment #18
donquixote CreditAttribution: donquixote commentedSome kind of feature detection would be nice.
Maybe xautoload should define constants such as
Or define a class, so you can do class_exists()..
Or something with class constants?
Also consider that the version in xautoload.info is not available if you have it from git.
Comment #19
cpliakas CreditAttribution: cpliakas commented@donquixote,
Not to derail the thread, but would you suggest Composer Manager do the same thing in terms of registering its autoloader?
Comment #20
donquixote CreditAttribution: donquixote commentedYou could even consider to use hook_boot().
The question is, which side effects can this have? E.g. if at some point this triggers another hook to fire, then all kind of nonsense can happen. Some module might call watchdog(), and this might trigger theme initialization.
Or even if that does not happen, a hook to fire would cause premature inclusion of all *.module files.
For hook_custom_theme(): I am not sure this happens in every request, e.g. maybe it does not happen in Drush. So, the safe bet would be to implement both hook_init() and hook_theme(), and then whichever goes first.
You should also consider what to do when new modules are enabled.
Comment #21
donquixote CreditAttribution: donquixote commentedAfter a lot of talking and some silence:
There is no fundamental problem in having both Composer's autoload.php and XAutoload working alongside.
However, the XAutoload cache (APC or other) works better if there are no other class loaders in play.
Especially, the "postpone initialization until first cache miss" stuff does not really work, if other modules/libraries want to use classes that xautoload doesn't know. This is because the cache does not store classes that failed to load (e.g. if you call
class_exists('NonexisitingClass')
). So every first unknown class in a request triggers a "first cache miss" behavior.This could be mitigated by registering other class loaders (Composer) before xautoload's class loader. However, then we waste some microseconds in Composer's class loader, for classes that are only known by xautoload.
Still, all of this is about a few milliseconds or less. (just a wild guess, it's been a while that I benchmarked it)
There is also a risk involved: There might be a bug in xautoload, or someone might have the wrong xautoload version, or stuff is happening in the wrong time of the request. Or something might go wrong that is not caused by xautoload at all, but people still think it is.
Even if this risk is low, I still don't want to be blamed for "why this crap, when autoload.php works just fine".
The conclusion is to make this opt-in.
Even with both modules enabled, the admin should have to actively do something to enable this behavior.
And, there should be a reliable way out, if Drupal does not boot anymore.
The opt-in could be a setting, or an integration module to be enabled.
If we go for a setting, it would be nice if you can override it from settings.php.
Implementation:
1. Register Composer manager stuff in xautoload.
2. Stop the regular Composer class loader.
1. can be done like in #13.
(I hope that composer_manager_vendor_dir() does not have any unpleasant side effects and can be called at any time in the request)
2. How?
Comment #22
B-Prod CreditAttribution: B-Prod commentedFor the 2. question, I would suggest to modify the
composer_manager_register_autoloader()
function, so it does not use a static variable butdrupal_static()
. That allows to modify the static variable elsewhere.So the code suggested in the comment #1943536-13: Integrate with contributed class loader modules could be:
The purpose of the test
if (!$composer_init) {
is to avoid adding the mapping to X-Autoload if the composer had already included the composer aulotoad file. This does not seem possible as now, but if the composer_manager module code evolves, there could be a potential issue here.I assumed in my proposal, by the name of the configuration variable, that the override choice was managed by the Composer Manager module (
composer_use_xautoload_autoloader
). This means that there should be a extra settings (checkbox) in the administration page that allows to choose X-Autoload autoloader instead of the native autoload file (with amodule_exists()
).The
FALSE
default value means that this settings would be disabled by default. This way, this is the responsibility of the Drupal developer/integrator to enable the X-Autoload override. In edge cases he could disable this feature.Comment #23
markhalliwell