Needs work
Project:
Drupal core
Version:
main
Component:
asset library system
Priority:
Normal
Category:
Bug report
Assigned:
Unassigned
Reporter:
Created:
28 Sep 2016 at 15:02 UTC
Updated:
13 Jan 2026 at 11:02 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
dawehnerWhat would be the expected behaviour, maybe throw an exception? Is that a BC break? Is this a BC break we actually "care" about?
Comment #3
martin107 commentedHow about an assert instead of an exception?
that way there is no BC break for production sites
but users of dev sites get the appropriate early warning indication.
Comment #4
chi commentedI would not consider this as BC break unless someone provides a good use case of using buildByExtension() against non existing extensions.
Comment #5
martin107 commentedThis solution looks good... a minor adjustment we need a @throws
Comment #8
martin107 commentedI will fix the tests.
Comment #9
chi commented@martin107 sorry, I noticed your assignment too late. Feel free to review / fix this patch.
Comment #10
wim leersTwo problems with this:
The library '%s' could not be found because its extension '%s' is not installed.would be better, because it'd be in line with the other exceptions.InvalidArguemntException, but should add a new exception class: we already have 5 such specific exception classes, this should have one too. Perhaps aLibraryNotFoundException(this is very similar to a 404 page not found after all).Other than that, this looks splendid! Once that's fixed, this is RTBC.
Comment #11
chi commentedThis method builds information for all libraries in an extension, not just for particular library.
This is not quite accurate because we are not looking for a library but for extension. I propose something like this:
ExtensionNotFoundExceptionorExtensionMissingException.Comment #12
wim leersSounds good!
Comment #13
martin107 commented@Chi regarding #9 -- Ah no problem - a happy accident. My solution was similar to yours - and it is always good to see another person's thinking.
Your solution to the AjaxTest showed me that my solution would not work!
Regarding LibraryDiscoveryParserTest::setup()
If I may criticise constructively setting a mock that always returns TRUE is a little too permissive.
My preference is to only return true when required - That has the benefit that when other test code accidentally get past the moduleHandler->moduleExists() method call then an extra error will be triggered.
I have also added LibraryDiscoveryParserTest::testUnistalledTheme() which covers our new exception explicitly.
Regarding the name of the exception
NotFound has different meaning in terms of http requests and "is the file found on disc".
Missing has similar issue for me.
So I look at the documentation surrounding themeExists and moduleExits and saw that exists is used as another word for installed.
PS
Also I am going to file a separate issue - the documented list of @throw statements is not upto date.
Comment #14
martin107 commentedComment #15
chi commentedShall we change this to ExtensionDoesNotExistException?
The name can be more meaningful. We are not testing uninstalled theme and did not ever uninstall themes in this case. We just make sure that the exception is thrown if non existing extension was passed to buildByExtension() method. I propose testMissingExtension or test testNotExistingExtension and so on.
Comment #17
chi commentedSo in case of Bartik it would read as follows:
Sounds a bit weird to me. Also note that this exception will be thrown for missing modules as well. Actually if the extension is missing we have no idea what type it is supposed to be. I suggest we return previous wording discussed above.
Comment #18
wim leersThanks! Great teamwork here :)
s/a theme/an extension/
s/theme or module/library's extension/
You're passing only one argument:
$extension, but there are two'%s'occurrences.Plus the message does not make a ton of sense. What about
The extension '%s' is not installed.This was not yet updated, and hence the test should fail.
s/Uinstalled/Uninstalled/
+1, thank you!
Comment #19
dawehnerWhat about is not installed OR is not available
Comment #20
martin107 commentedMost of the things to fix are the results of myself being in a rush ... so it is down to me to fix.. I will sort this out tonight ( in about 4 hours )
Comment #21
martin107 commented#19
If I do for example drush en flag, without first downloading the contrib module. Then it is auto downloaded for me.
So when you say
"available" implies to me that it could be if some kind of helpful fallback mechanism were invoked. That is not what is suggested by the wording of the documentation above ThemeHandlerInterface::moduleExists() - or as far a I can see what happens in the underlying implementation. So for the moment I went with
Thank you all for the corrections - I think I got to all of them
For our new tests I went with "testMissingExtension".
Comment #22
martin107 commentedI just noticed a little flaw, which is out of scope for this issue.
Comment #23
wim leersLet's also assert the expected message.
Also, we don't use
@expectedExceptionanymore, PHPUnit is deprecating that.Use
setExpectedException()instead.After that, this is RTBC IMHO.
Comment #24
martin107 commentedIf it is ok with you I would like to fix that up in a follow up
so that we can refactor all the other @expectedException()'s in the asset library system.
Comment #25
wim leersWell, you still need to add assert the message. NW for that.
I'm fine with doing the conversion in a follow-up, but I know that committers have demanded that we add no new code that uses the deprecated mechanism.
Comment #26
martin107 commentedI am happy to be held to that line.... I will patch that here.
Comment #27
martin107 commentedComment #28
wim leersPerfect :)
Comment #29
catchComment #30
catchNot sure about changing the php warning to exception here.
This isn't really an issue with a 'bc break', but it's more about if you've already got a site with a buggy module on it, let's not make it even worse for you when you update.
We could continue to do a warning with E_USER_WARNING, but without trying to include the file. That way if someone somehow is running on production with this error they won't get a worse error than now. Or the assert that dawehner suggested could be good too. Possibly both so it's an early fail on dev but still logs on production?
Comment #31
chi commented@catch this sounds like a new Core development policy that needs to be discussed and adopted globally.
Stop creating new exceptions until Drupal 9 to keep BC.
For me this case is not BC breaking. Building library information for non-existing extension is certainly a bug. It's not like PHP notices which can just spam watchdog but still let site work.
Comment #32
Anonymous (not verified) commented@Chi, you are very harsh!) Ordinary users have enough pain from other problems of D8. Do you really want to give them a white screen for the fact that they are updated? When Drupal updated form 8.1.3 to 8.1.4, rose panic. Now it will be the same. Although your arguments and look logical, but please be tolerant)
Comment #33
Anonymous (not verified) commentedI wanted implement both variants (Exception for 'dev', and Log for 'prod'), but I don't know how to get environment value. Only found it as protected property of DrupalKernel class. Any hints?
Comment #34
catch@vaplas there's no prod/dev switch in core, but you can use assertions which achieve much the same thing, see https://www.drupal.org/node/2492225
Comment #36
Anonymous (not verified) commented@catch, many thanks!
Comment #37
Anonymous (not verified) commentedComment #39
Anonymous (not verified) commentedComment #40
Anonymous (not verified) commentedComment #41
wim leersThis line is not necessary.
This must be removed.
This can just be
return;.Comment #42
wim leers@catch: Sorry for not thinking of that!
Comment #43
Anonymous (not verified) commentedComment #44
wim leersComment #45
catchThis isn't really the right way to use asserts - normally you'd assert the actual thing you're testing against. However I'm not sure doing it differently would be any neater here.
Comment #46
wim leers#45: I went through the exact same train of thought when reviewing this.
Comment #47
catchWhat about:
Then it's clear why it's failing maybe? Or if not we should add a comment.
Comment #48
alexpottWouldn't an assert normally be:
assert('$this->themeHandler->themeExists($extension)', $message);But also I don't really get why we're doing both an assert and a trigger_error(). If we're going to trigger an error that should suffice, no?
Comment #49
alexpottI think this issue should be 'needs review' till the assert thing is decided on.
Comment #50
Anonymous (not verified) commentedComment #51
catchAn assert would be a hard-fail on dev, that's the only reason to do it - so maybe we should just skip it and leave the rest the same?
Comment #52
wim leersWell, a hard fail is what you want while developing. It's just that we can't have a hard fail for existing production sites that happen to run with assertions turned on. But that means we can't have this assert at all, period. Which means we must only have the
trigger_error(…, E_USER_WARNING)?Comment #53
catchI don't think we need to support assertions being enabled on production. The question for me is more - do we support not having assertions on for development. If we removed the trigger_error() and left the assert, the effect would be a silent fail on prod and a hard fail on dev if assertions are on, but otherwise silent again.
Comment #54
Anonymous (not verified) commentedAnyway, we can make only first step and fix "Provide a more useful error message". And then, if necessary, open new issue to improve it via assert or exception.
Comment #55
wim leersI think we don't need to support that. It's already a best practice to use
settings.local.phpfor development, and that enables assertions by default.I'm not a fan of logging this as a warning, because that is far too easy to miss. I wouldn't think to go and check the error log after modifying my
*.libraries.ymlfile…Comment #56
dawehnerI agree with @Wim Leers, let's go with just assertions. We should not treat libraries in some special way.
Comment #57
catchYeah I think that's fair enough overall, sorry for confusing things a bit.
Comment #58
wim leersSo we want #43, but with the
trigger_error()removed.Comment #59
Anonymous (not verified) commentedGuys, I think you are the best, but now I confused definitely)
Example. If I have code:
$variables['#attached']['library'][] = 'missing/extension';then when using
trigger_errorI get:Ok, user warning appears only once (and without '/extension' part), but it's better than never.
Without trigger_error i have only
'Warning: Invalid argument..'and i don't undestand what it message means.but
zend.assertions = -1by default. Maybe we need to add info to "Status report" with the recommendation to change php.ini settings, if using settings.local.php?If we have a better alarm about new entries in the journal, I would have seen them.
Hence Exception instead Assert?
What better using:
assert(false, $message);like #43assert(FALSE, $message);like line 29 in core/lib/Drupal/Core/DependencyInjection/Container.phpassert($theme_exists, $message);like #47assert($extension_exists, $message);like #47, but with general nameassert('$this->themeHandler->themeExists($extension)', $message);like #48assert(isset($extension_type), $message);like #50 (typo: addisset, because undefined variable error)Comment #60
wim leersYes, sorry for all the confusion!
When using
settings.local.php, assertions are enabled automatically through this:The default value in
php.iniis irrelevant.So let's go with either:
or:
Comment #61
Anonymous (not verified) commented@Wim Leers, thank you very much!
I forgot to say that checking on php7. With php 7
assert_options(ASSERT_ACTIVE, TRUE);not work ifzend.assertions = -1documentation (but documentation says that by default 1, although in reality -1)Comment #62
Anonymous (not verified) commentedComment #63
wim leers#61: if that PHP7 behavior were true, then any Drupal 8 unit test involving assertions would fail. Since they don't, it must be working.
I like that @catch can now choose between either approach :)
Comment #68
catchLet's go for isset(), seems more self-documenting. Committed/pushed to 8.3.x, thanks! Don't think it needs to be in 8.2.x
Comment #70
catchAnnnd that fails on PHP 7 with the assertion message.
Comment #71
wim leersEh… why?
Comment #75
wim leersI'd bet this was a PHP 7 bug that has been fixed since. Retesting and re-RTBC'ing.
It's a shame we lost track of this for the past six months… :/
Comment #76
Anonymous (not verified) commentedExactly! But .. it's devilishly frequent randomly fails with
ConfigImportInstallProfileTestandConfigImportUITest.Maybe this is the same boss of random fails (Config schema generation), which @alexpott mentioned in #2828559-127: UpdatePathTestBase tests randomly failing?
Rerolled #61 with 100 runs of ConfigImportUITest (also bit research here #17 - #22).
Comment #77
Anonymous (not verified) commentedComment #79
Anonymous (not verified) commentedLooks like in case of a fall the
themeHandler->themeExists()uses an outdated list of themes. After the list is reset, the tests begin to work more stable. (see 28-29 posts in #2879048: Ignore: patch testing issue for #2919863).Comment #81
Anonymous (not verified) commentedOpps, it was a bit ambitious to forget about the other tests, sorry.
Comment #82
dawehnerIt feels like rebuilding this information all the time is not the best approach. Is there no other way to fix it?
Comment #83
Anonymous (not verified) commented@dawehner, thanks for review. Yeah,
rebuildThemeData()is absolutely not the best approach, and it not help to stable tests forConfigImportInstallProfileTestandConfigImportUITest.$this->list = NULL;helps to stabilize these tests, but causes 216 fails in other tests :) And it also not good approach :(I'm just trying to get close to the root of the problem. But for me it's not easy, especially because I can not reproduce the problem locally.
My main assumption is that Mr. Bot works so fast (especially with php7) that the cached value of
listdoes not have time to update.Comment #86
catchBumping this to major, if you set the admin theme to 'default', it is saved as '0' in configuration, and code is trying to load that theme with drupal_get_filename(), so you get:
Comment #87
Anonymous (not verified) commentedAdded related issues to #86 point.
Reuploaded #61 with conflict tests only.
Some observations (from experiments in the ignore-issue (#2948180-45 and more), because i still cann't reproduce fails locally):
1. PHP 5.5 + Postgresql 9.1 works more stable than
PHP 7.2 + MySQL 5.5(Is the quick execution of the code causing a problem?)2. Tests work stable if we adding to
LibraryDiscoveryParser:So that issue only highlights the problem, and does not create it.
3. refresh theme list before checking
$extensionis sufficient for ConfigImportUITest, but not for ConfigImportInstallProfileTest.4. ConfigImportInstallProfileTest does not contain
\Drupal::cache('bootstrap')->get('system_list'). And 'classy' has status 0 in\Drupal::state()->get('system.theme.data')on DI (but locally everything is ok).5. Locally ConfigImportInstallProfileTest adds info about 'classy' inside of the batch procces (during parsing dependencies to 'core/html5shiv' library). On DI this does not happen, and search for dependencies in the batch process is not performed at all.
Comment #89
almaudoh commentedFound this issue trying to fix a test fail from #2347783: Deprecate drupal_get_path() and drupal_get_filename() and replace with ExtensionList::getPath() and ExtensionList::getPathname(). I don't know if we still need this issue after #2347783 is done since
ExtensionLists throw theUnknownExceptionExtensionif they are called on a non-existent extension. Some parts of the patch here may still be useful.Comment #90
andypostComment #91
vacho commentedAdding diff between 81 and 87 patches.
Comment #92
vacho commentedThis patch no need reroll.
Comment #93
rodrigoaguileraWe should look closely at
#2347783: Deprecate drupal_get_path() and drupal_get_filename() and replace with ExtensionList::getPath() and ExtensionList::getPathname()
Becuse it might be introducing more bugs about by not failing when you try to get the path but trying to invoke alter hooks on modules that are not enabled.
I will try to introduce a test in that other issue for this.
Comment #94
rodrigoaguileraProbably the following issue should be marked as duplicate
#2279551: Library discovery Parser tries to guess extension type assuming 'theme' for non-existing modules
Comment #95
rodrigoaguileraI can confirm that
#2347783: Deprecate drupal_get_path() and drupal_get_filename() and replace with ExtensionList::getPath() and ExtensionList::getPathname()
Will solve this issue by throwing an Drupal/Core/Extension/Exception/UnknownExtensionException
What still bothers me is that I confirmed that
LibraryDiscoveryParser::buildByExtension()
will return information from themes that exist in the filesystem but are not active but for modules it will only return information for those that are enabled. When you query for a module that is in the filesystem but not enabled it will assume is a theme giving a confusing exception message like:
Drupal/Core/Extension/Exception/UnknownExtensionException with message 'The theme non_enabled_module does not exist.'Should we repurpose this issue to fix that or open a new one?
Comment #97
joachim commentedI don't think this issue need to be postponed. Whichever of this and #2347783: Deprecate drupal_get_path() and drupal_get_filename() and replace with ExtensionList::getPath() and ExtensionList::getPathname() gets in first will cause a reroll of the other one, but the other issue doesn't fix this faulty logic:
Comment #102
larowlanwe're also forgetting the profile extension type here
Comment #103
larowlanClosed #2279551: Library discovery Parser tries to guess extension type assuming 'theme' for non-existing modules as a duplicate, applying credit for
Comment #108
catchThis is still valid but downgrading to normal.