I have seen a handful of modules over the past few years that have implemented this kind of check somewhere within the module code, but it would be a nice addition for .info files to be able to declare specific php extensions a module requires in order to properly function.
If those extensions are not available, the module should not be allowed to be enabled.
Follow up issues:
#1728314: Move module/extension compatibility/requirements checking from System module into module.inc
Comments
Comment #1
willvincent CreditAttribution: willvincent commentedHere is a patch to the system.admin.inc file that implements this functionality.
It allows for a new parameter php_ext to be added to .info files for modules, and if any required extensions are not available, disallows that module from being enabled.
Format for this new parameter follows the same spec as dependencies:
In this example, if either the xml or bcmath extension is not enabled, the module won't be available to enable.
The patch also will only check against loaded extensions if a module explicitly defines this parameter, if no modules implement it, there will be no performance hit on the module page of the site admin.
Comment #2
wjaspers CreditAttribution: wjaspers commentedComment #4
wjaspers CreditAttribution: wjaspers commentedI think this was originally drawn up against the 7.x branch. Adjusting for the moment to see how it does. Can switch back later if it continues to fail.
Comment #5
wjaspers CreditAttribution: wjaspers commented#1: add-php_ext-param-to-dot-info-file-properties-1322890-1.patch queued for re-testing.
Comment #7
wjaspers CreditAttribution: wjaspers commentedPlease credit @willvincent, not I, if this is rolled up.
Comment #8
wjaspers CreditAttribution: wjaspers commentedComment #9
wjaspers CreditAttribution: wjaspers commentedTagging for contributor experience, since this isn't necessarily a DX only problem.
Adding this functionality will help prevent someone from starting the installation or executing a module's code that requires a specific PHP extension.
Comment #10
willvincent CreditAttribution: willvincent commentedThanks wjaspers.. it's been so long I'd almost completely forgotten about this. Thanks for rerolling it against D8 :)
Comment #11
sunThis makes sense and seems to be beneficial addition.
The proper name "php_ext" looks and reads a bit strange. It's also an abbreviation, which we generally try to avoid. Additionally, it is a multi-value property, so the name should actually be plural.
Can we think of a better name? E.g., "php extensions"? Or why not simply "extensions"? Or would that be too vague?
Second, I wonder whether this location is really the only place to adjust. Isn't that limited to the Modules page output? I may be mistaken, but isn't there some other code somewhere else that runs along the lines of hook_requirements()?
Comment #12
wjaspers CreditAttribution: wjaspers commented@sun,
hook_requirements() is useful, but only takes effect when you're actively trying to install something. Noting an incomplete requirement beforehand is IMHO a better experience.
"extensions" seems a bit vague, so "php extensions" doesn't seem all that bad. Natural language is typically easier to remember.
Comment #13
willvincent CreditAttribution: willvincent commentedAs wjaspers notes, having this included in the .info file allows the details to show up on the module list page. While hook_requirements would prevent installation, it doesn't allow for updating the info on the module list page (not that I'm aware of anyway) and having information as to why a module can't be enabled on that page makes a lot of sense to me.
Rerolled with the name changes to php_extensions
Comment #14
willvincent CreditAttribution: willvincent commentedstatus changed to needs review
Comment #16
willvincent CreditAttribution: willvincent commentedOk, _actually_ rerolled this time, rather than just manually changing the text in the patch file. Looks like the previous patch was rolled before things moved into core/
Comment #17
sunre: #12: hook_requirements():
Well, the problem is that just outputting the lack of a PHP extension is insufficient. A module needs to be actively prevented from being installed, as with any other requirement.
I see that the code being touched here results in an appropriate UI change on the Modules page (Red X instead of checkbox).
Looking some further into the module system and System module, it actually looks like we're performing these checks only within the Modules page/form (OMG):
http://api.drupal.org/api/drupal/core!modules!system!system.admin.inc/fu...
http://api.drupal.org/api/drupal/core!modules!system!system.admin.inc/fu...
Which in turn means that this patch should actually be complete. I hope someone else can double-check and confirm that.
That said, there's one last detail left to bikeshed:
AFAIK, we're using spaces instead of underscores in .info files (e.g., "base theme"), so the property name would actually have to be "php extensions".
Comment #18
willvincent CreditAttribution: willvincent commentedYou're right. I checked to be sure it would still work with a space instead of an underscore, and it does. Here's another reroll with that change.
If I remember correctly, that was my finding as well when I initially looked into this October of last year.
So there may be one more issue to bike shed here:
Given the proliferation of install profiles and use of drush for enabling modules, etc. It would probably be worth doing some extra testing to ensure that there aren't cases where modules that have unmet requirements cannot be installed. I'm not sure if there are any instances where that is the case currently, but given that these checks only seem to be run within the modules page/form, I wouldn't be surprised if there are cases that could allow installation of modules with unmet requirements.
Comment #19
willvincent CreditAttribution: willvincent commentedForgot the attachment.
Comment #20
sunAFAICS, tools like Drush will have to (re-)implement this check on their own currently, so I guess there's not much that can be double-checked.
I'd highly appreciate if we'd create a follow-up issue to properly untangle that compatibility/requirements checking from System module (e.g., by moving the non-form related code into module.inc). That would be lovely.
Comment #21
cosmicdreams CreditAttribution: cosmicdreams commentedDone #1728314: Move module/extension compatibility/requirements checking from System module into module.inc
Comment #22
sunHm. I just wanted to RTBC this, but actually -- it is possible to test the expected behavior and thus we should add tests for it.
Comment #23
cosmicdreams CreditAttribution: cosmicdreams commentedCool, I'll try writing the tests for these in the next couple of days if Will doesn't beat me to it. Can anyone identify exactly what should be tested?
Comment #24
willvincent CreditAttribution: willvincent commentedNeed to test that if a missing php extension is in $info['php extensions'][] the module cannot be enabled.
I think using a non-existent php_extension (ie: foobarbaz) would probably be a sufficient test that if a required extension is not present the module is not able to install.
Whereas, if a required extension is present (should test for something common, like 'session' maybe) the module is able to install.
I think that would cover the tests. @sun were you thinking of anything else needing testing here?
It seems to me module_enable() should probably also do these checks, not just the form, however I don't think it does, if that's the case there's probably more work needed here.
Comment #25
catchIf we add this, what's the point of hook_requirements($phase = 'install')?
Also, does Composer have anything like this?
Comment #26
sunhook_requirements() is still used for more complex requirements checks.
Composer has support for specifying dependencies on PHP extensions (via 'requires', I think), but I'm not sure how that is relevant here?
(Apparently, support for dependencies on PHP extensions is not in the official documentation, and was only tacked on through a hack recently here: https://github.com/composer/composer/issues/312 -- alas, they special-cased the
ext-*
package namespace...)That said, would it be too crazy if we'd change this property into a sub-keyed notation, so as to allow to differentiate between required/supported dependencies?
E.g., something along the lines of:
Just raising the idea. That might allow (but not 100% sure) to simplify some of the hook_requirements() implementations. — Not really sure, because the optionally supported extensions are usually amended with a description that clarifies how they're helpful and better, so if we'd replace those checks with simple properties, there'd be no way to do that. Perhaps the separation by requires/supports is not a good idea after all...
Comment #27
catchThe composer question was in relation to #1398772: Replace .info.yml with composer.json for extensions if we ever do that for the package management stuff in modules.
Things like supports are generally handled by runtime status messages as well, so I don't think it'd necessarily help to have that in .info files.
Wondering a bit about stuff like gd vs. imagemagick (and bcmath vs. whatever the other one is that OpenID uses), but maybe that's what hook_requirements() stays useful for.
Comment #28
Fabianx CreditAttribution: Fabianx commentedSetting status to NW and adding needs tests tag.
Comment #29
willvincent CreditAttribution: willvincent commentedSo the only thing keeping this from inclusion in core at this point is tests?
I would be happy to write them if I had any idea how.
Someone willing to at least point me in the right direction on that?
Comment #30
sunThis would be very very very helpful for #1447736: Adopt Guzzle library to replace drupal_http_request(), since that issue is essentially only blocked on adding the cURL requirement to a couple of core modules.
Comment #31
Sylvain Lecoy CreditAttribution: Sylvain Lecoy commentedAlso, we should add a version check, like modules dependencies:
Comment #32
willvincent CreditAttribution: willvincent commentedextensions[] = curl (>1.0)
perhaps... not:
Comment #33
willvincent CreditAttribution: willvincent commentedActually.. supporting version numbers would require a complete rework of how this is currently implemented.
Since this currently relies on the extension_loaded() function, which does not provide access to version numbers.
In fact, I question whether version number details are cleanly accessible from within php at all. Aside, maybe, from parsing the markup spit out by a call to phpinfo(), I'm not aware of any way to access that information.
I can understand your reasoning for wanting versions included, but I think the big first step is getting this into core AT ALL. This request has been sitting out here for over a year, with a patch the entire time.. and there has been no real movement on it.
SO, now that we're staring down the barrel of the feature freeze gun, what else MUST be done to get this into core?
Let's not try adding features to it -- unless you all want to do that. I just want to see the basic functionality of one of the items I've submit for inclusion in core (both of which were well received at least initially) actually make it in.
Comment #34
sunThe version of an installed extension is retrieved through PHP's phpversion().
Comment #35
willvincent CreditAttribution: willvincent commentedPerfect. I'll rewrite it to use that now.
Comment #36
willvincent CreditAttribution: willvincent commentedHmm.. the existing drupal_parse_dependency() doesn't seem to work for parsing out version numbers for this..
Comment #37
sunLet's handle versions in a separate follow-up issue.
Comment #38
willvincent CreditAttribution: willvincent commentedAgreed... it's looking to be a pretty big can of worms, not all extensions return version numbers, and even when they do,
they aren't necessarily in an easily handled format.
So, back to my previous question. Aside from the patch that applies cleanly in #19 above, what else is needed for this to be included in core?
Comment #39
Fabianx CreditAttribution: Fabianx commentedYou should provide a test that the functionality is working:
I think it could work like this:
a) Add a new test module and add a non-existing extension
b) Add an always required extension to another module
Change or Copy:
core/modules/system/lib/Drupal/system/Tests/Module/RequiredTest.php
And just make sure for a) the checkbox is disabled and for b) it is enabled.
That should be all that is needed.
Thanks!
Comment #40
willvincent CreditAttribution: willvincent commentedHere's my attempt at those tests... It comes back as a pass, but I think it's because the conditions required to actually test it aren't ever met.
The new test modules never show up in the list. As far as I can tell this is because they are 'hidden', but then so are all of the other test modules.
I'm afraid someone else is going to have to assist with this, automated testing is all greek to me.
Comment #41
Fabianx CreditAttribution: Fabianx commentedI think it should be more like:
But the hidden makes that really problematic.
Suggestion:
Unhide the modules for now, make the test pass (locally), then merge your work into:
#1447736: Adopt Guzzle library to replace drupal_http_request()
and implement the test with
"aggregator needs curl" && extension_loaded('curl')
So that in either case:
curl present -> aggregator enabled
andcurl not present -> aggregator disabled
the test succeeds.Comment #42
willvincent CreditAttribution: willvincent commentedI'm going to leave this as is. I don't really understand what all is being asked for here. I've provided a patch that adds additional functionality, I've confirmed it works. If this needs tests built with the automated test suite someone who understands how all of that works will have to build them.
If that means the patch never makes it into core, honestly, at this point I just don't care anymore.
I can always manually apply the patch to sites I build if I need it. To be completely honest though, the lack of real help that's been offered toward this issue indicates to me that the want for more developers to contribute to core development is mostly lip service, as the requirements to having a patch accepted involve a much steeper learning curve than I have time allowed to invest.
My experiences thus far with involvement in core development, and interaction with several core developers are leaving a very sour taste in my mouth.
So, if this is ever deemed worthwhile, I hope it makes it into core, but at this point, my effort on it is done.
Comment #43
Fabianx CreditAttribution: Fabianx commented#42: Uh, hi! :-)
First of all I am no "real" core developer, I just happen to have a little interest in core things, like you.
Second, thanks for all the work. It is a nice patch. I definitely did not want to frustrate you. I am sorry if it came across this way.
Yes, it is true that the requirements of a patch are high and testing can be very difficult to grok. ( When I wrote my first tests I was like WTF! )
But it is totally worth it, because soo often tests prevent severe failures later on.
Again, thanks a lot for a nice patch!
Comment #44
xmacinfoMarked #1859536: Testing/Aggregator module warning: Requirements check comparison output two 'strings' (cURL) as an array as duplicate of this bug.
I have not read all this issue completely, not yet.
Does this issue deal with integrating requirements information in the Status page?
Fro example, a requirement that is met may display details in the status page, like the version number of the required PHP module, or, like for cURL, display a warning in the Status page and offer help on how to install cURL.
We define the requirements (in the .info file that would be easier than in the .install file), and then we act upon the requirements (sometimes adding a row in the Status page).
Comment #45
klonos@xmacinfo: I'm pretty sure you meant to link to some other issue on your comment there. You accidentally linked to this very same here ;)
Comment #46
xmacinfoIndeed. I fixed the link above.
Thanks.
Comment #47
tstoecklerThe problem with this is that once you are missing an extension for an enabled module, you are already doomed, i.e. there is no way to prevent a fatal as that module may call a function from the extension, for example. Therefore you need to tell people that an extension is missing *before* you enable a module, which is what the current patch does: It adds that information to the Modules page.
Comment #48
xmacinfoThis not always the case. The requirements in the .install files can display a status message in the Status page or a warning. If cURL is not available, the website is not doomed. It is still fully functional. Only some file-based function will fail or emit errors.
If we display errors only on the modules page, we leave out most of the Status page information. In that sense, that would be a regression.
Comment #49
tstoecklerWell, it might not always be the case, but I would still consider it a problem if we're
*sometimes* doomed, with no reason to control when. In the example of cURL it's obviously hard to imagine a hardcoded call to curl_init() or whatever on every page, but since it's possible it's what we have to expect. Therefore we *cannot* load the module file of a module that depends on a missing extension. .install file, fine; we can document to only use native PHP functions there, that shouldn't be a problem.
What we could do is find all required extensions by all *disabled* modules and list the missing ones on the status page as a warning, such as "Module A (disabled) requires extension foo, so you currently can't enable it." Would that be a compromise? :-)
Comment #50
Fabianx CreditAttribution: Fabianx commentedWell, that will be difficult with distributions.
I think this should be done, but only for enabled modules as those make the problem.
Think of scenario:
* Move to new server
* Import DB
* Aggregator is enabled, curl is disabled
* Check status report: Warning: Aggregator needs curl enabled.
=> WIN!
Comment #51
sun@Fabianx: I've to agree with @tstoeckler here - IF a module requires PHP extension X, you have a >50% chance of completely blowing up your system if the extension is not loaded. In turn, you likely won't be able to reach that Status report page.
But anyway, friends, let's not over-engineer this. Let's use the exact same approach that we have for (missing) module dependencies right now. That should be sufficient for most cases, the rest we can figure out later.
Also bear in mind that possible tests are very limited by design here - AFAICS, the only test we can reasonably expect from this issue/patch is a test that verifies that a module that requires a non-existing PHP extension "foo" cannot be installed. More sophisticated tests than that require in-depth hackery of various subsystems.
And I'm not even sure whether it is worth to provide any more detailed tests in the first place, since the moment you install a module that requires a PHP extension that isn't required by Drupal core, you are effectively entering custom waters. Drupal cannot, should not, and will not help you to correct blatant mistakes when staging a site from A to B and blatantly forgetting that the code requires the PHP extension "foo" to exist. That's a level of validation we cannot reasonably provide. We could as well validate that Drupal runs on a "correctly configured" Apache webserver... Nope, no way, out of bounds.
Comment #52
Fabianx CreditAttribution: Fabianx commentedI am totally on board with that and don't think we need it for enabled modules, I was just opposed to showing it for disabled modules on the status page ...
Comment #53
xmacinfoAll I want to highlight is that the same function that currently checks for requirements also builds the status page.
Here the PHP requirement builds the display of the PHP version number and the link to the PHP status page.
My original concern: if we move the requirement properties to .info files, will there be provision to build at the same time the Status page?
I am not arguing about displaying warning for disabled PHP modules.
Comment #54
tstoecklerI don't understand when and how and what you want to display on the Status page. As I've tried to make clear, showing missing extensions for *enabled* modules is not on the table IMO, as that's already too late.
Comment #55
Sylvain Lecoy CreditAttribution: Sylvain Lecoy commentedI would like to work on and propose a TouchPoint functionality, but not in this issue. I'll refers to the OSGi TouchPoint concept which allows native touchpoint to integrates with a system. I would love to provides a set of touchpoints like: PECLTouchPoint, PEARTouchPoint, ComposerTouchPoint, AptitudeTouchPoint, YumTouchPoint, etc. which install, configure and load such extensions.
To see the big picture at a module installation, we could introduce a dependency resolver which would fetch the drupal module dependencies from drupal.org with their corresponding version (maybe?) and install the corresponding php extensions and/or native package that are needed from TouchPoints. This would be an addition to the module manager.
For instance a PECLTouchPoint would be roughly like /core/vendor/doctrine/common/bin/travis-setup.php:
I'll work on a sandbox for this but anyone excited by this project is welcome to work with me.
Comment #56
xmacinfoI am not arguing about displaying warning for disabled PHP modules. I do not want to show missing extensions for *enabled* modules either.
I want to know how the Status Page will be build if we move the requirement check to the info files. Will the Status page be gone?
Comment #57
willvincent CreditAttribution: willvincent commentedIs anyone actually talking about moving the requirement check out of the .install file? All this issue was initially about was adding an additional property to .info files so that modules can indicate that they depend on specific php extensions so that in the event of a module that requires an extension that might not be standard, it can be checked for before that module can be enabled on the module list page.
The behavior is identical to how module dependencies behavior is implemented currently.
There is no reason I can think of to eliminate hook_requirements, which is sounds like that's what you're arguing against.
Summing up, all the patches in this issue provide presently is additional logic for the module list form to prevent a checkbox allowing for enabling of a module on that form -- in much the same way that module dependency is handled.
Comment #58
xmacinfo@willvincent: That was my understanding. Thanks for the clarification.
Comment #59
BerdirI don't really get what people have against showing it on the status page either, *additionally* to a pre-install check.
I think it would be very useful to have an ok message giving you list of all extensions required by your installation. Because then you have a single place to look for extensions that your new server needs to support in case you move it around. And if we have that, it's easy to change it to red if something is missing. Or list the red ones separately.
If you have something that blows up before you can visit that page, bad luck, but I think most modules aren't going to do that. Looking at core, I fail to see a single example that will prevent you from visiting the status page. Also, those things are checked in update.php as well, so it's quite possible that you do a module update which has a new extension dependency that you're missing. And update.php blowing up already because of it at that step is *very* unlikely.
Also, I'd suggest to update the existing examples in core which require php extensions, shouldn't be that many and should show how much copy & paste code we can avoid with this.
Comment #60
klonosI'd like to have an overview displayed on the site status page as well. Perhaps if not within the score of this issue but can be (relatively) easily implemented, how about a follow-up issue then?
Comment #61
Dave ReidA side note that cURL now needs to be a system requirement, and not just an optional requirement for certain modules. It's used to request localization files from localize.d.org during the install process. #1991298: Fatal error when installing Drupal on servers that don't have the cURL extension
Comment #62
catchHmm that's a good point, I don't think this was accounted for with the drupal_http_request()/guzzle change since most of the other calls are very optional.
For untranslated installs we don't need to download translations and it seems a bit rigid to add a system requirement for that (or for something needed in the installer).
Also wondering if we should consider a fallback just for the installer to use something dumb.
Comment #63
Dave ReidThe real function being used is install_retrieve_file() which is an API function that any install profile can use. It's not just limited to translations.
Comment #64
Dave ReidSorry, meant to link to #1991298: Fatal error when installing Drupal on servers that don't have the cURL extension in #61. I truly believe that it is in our best interest for our end users and module developers, that I should be able to rely on being able to use Guzzle without needing to add a requirement to my module. It's provided by core, why do I have to add a dependency in my module?
Comment #64.0
Dave Reidreferencing follow up issue in summary.
Comment #65
alansaviolobo CreditAttribution: alansaviolobo commentedattempting a reroll.
Comment #67
alansaviolobo CreditAttribution: alansaviolobo commenteduploaded the wrong patch.
Comment #68
Crell CreditAttribution: Crell commentedDo we have any other yaml keys with spaces in them? That seems like a great confusion source...
Otherwise this seems reasonable.
Comment #69
alansaviolobo CreditAttribution: alansaviolobo commentedI searched the other info files using the regex ^[.*]\s[.*]: but couldn't come up with any other keys having a space.
hence I renamed the key to php_extensions
also, When I examined the test report, I couldn't find the message corresponding to the test. would appreciate some help on that.
uploading the updated patch.
Comment #71
Mile23Drupal extensions can specify PHP extensions in their composer.json file. #2494073: Prevent modules which have unmet Composer dependencies from being installed is trying to solve some of that, but it looks like the most elegant solution in that issue is to mark Drupal extensions as requiring installation through Composer, rather than solving the dependency tree.
Thus it looks like Composer might solve the problem here.