There is a problem with the autoloader when using two classes with the same name (but a different namespace).
I get a class not found error although the class has been properly listed in the use statements.
When installing Drupal the following error occurs:
Error: Class 'Drupal\migrate\Event\MigrateEvents' not found in Drupal\mymodule\EventSubscriber\MigrationEventSubscriber::getSubscribedEvents()
I have an install profile that enables migrate, migrate_plus, migrate_tools and the following eventsubscriber:
namespace Drupal\mymodule\EventSubscriber;
use Drupal\migrate\Event\MigrateEvents as MigrateEventsCore;
use Drupal\migrate\Event\MigratePostRowSaveEvent;
use Drupal\migrate_plus\Event\MigrateEvents as MigrateEventsPlus;
use Drupal\migrate_plus\Event\MigratePrepareRowEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class MigrationEventSubscriber implements EventSubscriberInterface {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[MigrateEventsPlus::PREPARE_ROW][] = array('onPrepareRow', 0);
$events[MigrateEventsCore::POST_ROW_SAVE][] = array('onPostRowSave');
return $events;
}
...
}
Adding class_exists fixes the issue (as it will try to autoload the class):
if (class_exists('Drupal\migrate\Event\MigrateEvents')) {
$events[MigrateEventsCore::POST_ROW_SAVE][] = array('onPostRowSave');
}
But the class should be autoloaded:
use Drupal\migrate\Event\MigrateEvents as MigrateEventsCore;
use Drupal\migrate_plus\Event\MigrateEvents as MigrateEventsPlus;
| Comment | File | Size | Author |
|---|---|---|---|
| #54 | 2776235-54.patch | 36.29 KB | alexpott |
| #54 | 51-54-interdiff.txt | 1.22 KB | alexpott |
| #51 | 2776235-51.patch | 36.25 KB | alexpott |
| #51 | 46-51-interdiff.txt | 2.73 KB | alexpott |
| #46 | 2776235-46.patch | 35.84 KB | alexpott |
Comments
Comment #2
mpp commentedComment #3
mpp commentedComment #4
cilefen commentedWhy does the call to Drupal\migrate_plus\Event\MigrateEvents not produce an error?
Comment #5
alexpottThis suggests that the event is firing before both modules are actually installed - does mymodule depend on both the modules? And is it a separate module or just part of the install profile. Install profile dependencies are not real dependencies...
Comment #6
mpp commented@alexpott,
The module is a separate module and has all the dependencies listed:
The module is a dependency of the install profile:
It is likely a caching issue somewhere, I've reinstalled over and over and the class isn't found until a cache rebuild occurs.
Comment #7
alexpottTry putting migrate_plus in the install profile before your module. In fact put all the dependencies of your module in there.
Comment #8
mpp commentedI had the same idea, adding "migrate" as a dependency for the profile resolved the issue.
I closed this issue but shouldn't it be sufficient to add it as a dependency for the module implementing an EventSubscriber?
I also tried the solution in http://data.agaric.com/what-do-when-developing-drupal-8-module-and-class... but that didn't work.
Comment #9
mpp commentedRe-opening this as it seems other modules have similar issues that didn't occur before.
See https://www.drupal.org/node/2777483, https://www.drupal.org/node/2775963
Comment #10
borisson_Adding information from the search api / facets issues that @mpp linked.
In #2775437: Fix the tests we had to fix some tests because views changed
unpackArgumentValue, we also had a truckload of failures related to a class not found error.We fixed that by adding drupal:taxonomy and drupal:node as dependencies in our tests, but we didn't have to do this before, I can try figuring out which commit introduced this if that's helpful.
Because taxonomy was added as a dependency, facets will need to do the same thing and paragraphs also did that http://cgit.drupalcode.org/paragraphs/commit/?h=8.x-1.x&id=d31495d0ea308.... This is not a reasonable approach imho, an issue was added for search api to resolve this in a nice way: #2777483: Unmet dependencies.
We also see to have another issue with memory for search api that popped up recently, but that doesn't seem to be related: #2784849: Tests fail w/ out of memory error.
Comment #11
mikeryanThis problem has derailed #2485385: Move highwater field support to the source plugin, and do not expose its internals on MigrationInterface.
Moving out of migration system, since it's reported elsewhere.
Comment #12
xjmIs #2796953: [regression] Plugins extending from classes of uninstalled modules lead to fatal error during discovery related? Is this specific to plugins?
Comment #13
borisson_I think this is a duplicate, yeah.
Comment #14
mikeryanThe MigrateEvents error is unrelated to that regression (or to plugins in general, for that matter).
Comment #15
mikeryanPer phenaproxima in https://www.drupal.org/node/2485385#comment-11628073, the MigrateEvents error seems to be D7 only - are the other instances reported above also under D7?
Comment #16
mikeryans/D7/PHP7/, of course.
Comment #17
dawehnerI've seen instances before in which the module installer couldn't resolve the dependency tree properly, so it installed modules in the wrong order.
Why this only appears on PHP7 I cannot say, but for debugging purposes I would checkout in
core/lib/Drupal/Core/Extension/ModuleInstaller.php:191in which order the module installation appears.Comment #18
mikeryanHere's an attempt to reduce the migrate highwater patch which triggered the problem to a bare minimum.
Comment #19
mikeryanAnd here's a crazy idea...
Comment #20
mikeryanDoes it matter where the event subscriber class lives?
Comment #21
mikeryanThe service definition, the event subscriber, and the referenced class are all in the migrate module, so it doesn't seem to be a module dependency issue.
Here's what's really odd - the event subscriber class is obviously loaded, since its getSubscribedEvents() is being called - yet the MigrateEvents class, in the same module, is not loaded.
Comment #22
phenaproximaI have a knee-jerk suspicion, based on nothing, that it has something to do with the MigrateEvents class being final. I can't prove this, of course...
Comment #23
mikeryanEasy enough to test this. Seems unlikely, though, since all such event classes are final...
Comment #24
mikeryanSo, looking around at other event subscribers in core to try to figure out why only ours breaks - the namespace, the services definition, where the referenced event class lives - the closest thing is in locale. Comparing the relevant bits...
locale.services.yml:
migrate.services.yml:
LocaleTranslationCacheTag.php:
PluginEventSubscriber.php:
LocaleEvents.php:
MigrateEvents.php:
Unless I'm missing something, the only difference I can see is the name space of the events class (top-level in local, Event in migrate), so my next stab will be to move MigrateEvents up one directory...
Comment #25
mikeryanMoving that class...
Comment #26
mikeryanDuh - ignore that last patch, moved the file without changing the namespace...
This is my last try today.
Comment #27
mikeryanFriday afternoon, sigh... Forgot to restore final in those last two patches, but I'll let that go for now...
Comment #28
mikeryanWell, of course all the use statements need to be updated...
Comment #31
mikeryanI give up... Not sure where to go from here.
Comment #32
mikeryanWell, here's one difference between locale and migrate...
Comment #33
mikeryanAh well - given the failure is reported in InstallUninstallTest right after the experimental modules confirmation form, I was thinking being experimental was a trigger (and that this might relate to #2771363: BigPipe and Migrate module won't install ) - but, seems not.
Comment #34
mikeryanalexpott has diagnosed the issue in https://www.drupal.org/node/2485385#comment-11638119:
Alex has added a simple immediate workaround to that issue - let's keep this one open to potentially follow up with one of the other possibilities.
Comment #35
dawehnerwow
Comment #36
catchWe already have #1387438: Timeout on enabling modules: make it a batch operation open for that.
A third option is an upstream patch to not cache negative class_exists().
Comment #37
alexpottHere's a failing test case. That will fail regardless of PHP version because of module dependencies.
Comment #38
alexpottHere's a fix that adds a new class loader as required.
Comment #40
alexpottOkay and now we need to ensure that if you are mad enough to use want to discover migration source plugins during install this works too. This is tested in \Drupal\Tests\migrate\Kernel\Plugin\MigrationPluginListTest() but I'm also working on providing some tests for the new core component.
Comment #41
alexpottComment #42
alexpottHere's test for the new ClassFinder component.
Comment #43
alexpottHere's the related composer issue https://github.com/composer/composer/issues/5619
Comment #45
catchBumping to critical on the basis that:
- this blocks a migrate critical
- there's no mitigation
Comment #46
alexpottNice - run-tests.sh adds all the PHPunit tests to the autoloader... therefore we need to take a different approach on the test.
Comment #47
dawehnerThe overall fix is really nice. This removes the need to have a class loader being around just for migrate.
Nice usage of some British english
Are you sure this line really makes it easy for people to understand what is going on?
Let's update the docs
Comment #48
mikeryanTpyo.
Comment #49
xjmNegative cache : cache :: electron : positron
Clearly.
Watch out, that cache clear might release a lot of energy.
Comment #50
catchNote I have a very simple workaround to the original bug report in #2485385-165: Move highwater field support to the source plugin, and do not expose its internals on MigrationInterface. This is a real bug, but also partly we've shot ourselves in the foot the way we use Symfony events vs. the hook system.
Comment #51
alexpottI don't think we should be checking if modules are installed using class_exists() checks. But I think the real issue is that the module handler needs to be available when we are building the event list but event registration occurs too early for that. However, even saying that we still need to provide multiple class loaders because after installing a module the class should be available. Here's an updated patch to address the reviews in #47 and #48.
Comment #52
dawehnerWell, we have the list of enabled modules though, given that its part of the container building process.
Hehe, I think we found some secret energy source.
Comment #53
phenaproximaI'm probably not overly qualified to review this patch so I'm not going to RTBC...but these are the things I noticed...
Why doesn't this depend on Doctrine? ClassFinder explicitly implements a Doctrine interface, so it seems that this should perhaps have Doctrine as a dependency.
Would prefer if this were slightly more defensive -- i.e.,
if (empty($class_loader)).This violates dependency injection...but I suppose it's not a big deal right now.
Comment #54
alexpott@phenaproxima thanks for the review
1. Fixed - nice spot
2. I'm not sure this is more defensive - unfortunately there is no way to assert that we have a class loader - there is no generic interface.
3. This is not an injected dependency - the class finder is not a service - it just is - like calling
\Drupal\Component\Utility\Html::cleanCssIdentifier()Comment #55
mikeryanI've submitted a patch merging this with the highwater patch (minus the MigrateEvents-avoiding workaround) at https://www.drupal.org/node/2485385#comment-11660651 just to verify that this patch will fix the original problem.
Comment #56
mikeryanI'm not confident enough in my understanding of class loading to give a full RTBC - but, I can offer a "T" for the highwater patch passing with this (apart from 3 random Sqlite failures, two of which passed on a second run, third one is rerunning now).
Comment #57
dawehnerI believe its a clean situation for the problem. Especially the detail to not change the existing classloader but add an additional one.
Comment #60
catchYep looked at this a couple of times and no complaints from me either.
Committed/pushed to 8.3.x and cherry-picked to 8.2.x. Thanks!
Comment #62
klausiFollow-up: #2824868: MigrateSourcePluginManager::$classLoader property is unused
Comment #63
dmiric commentedSorry I have to reopen this issue tested on latest Drupal 8.3-dev and 8.2.2 - PHP 7
Installation profile:
Error:
Comment #64
geek-merlin