-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Fix issue with ModuleDirReader not finding files in vendor modules #1490
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Woah, that's fast Vinai... awesome! 👍 |
You're a machine! Thanks for doing this. I'm wondering if the solution could be simpler though, as this PR changes the way the filesystem driver works which is obviously quite a fundamental change. Assuming this change is intended to resolve the issues in TL;DR I think Long version Ignoring for a moment Does that make sense? What do you think, @Vinai? Edit: It looks like |
Without checking the details, what you are saying looks sound, @joshdifabio. But I think it is a bigger architectural change then the small fix this PR contains. I think this PR is valid even outside of the scope of this issue. The implementation of The change you suggest is more invasive from my point of view. It makes sense, but it is a bigger change since removing the I'm not saying the |
Pushed an update because the integration test did not clean up after itself. |
Sounds good, @Vinai. I'll make a separate PR for Thanks for your hard work! |
Rebased onto current develop. I don't know why the two integration tests keep failing in travis. If I run the tests locally they are green. o_O |
@Vinai thanks for contribution.
Output:
This approach works for most of configuration files. Only concern is that it feels unnatural from the framework perspective: it declares set of constants with the base paths first, then disregards what was declared adding relative part "../../". What if instead we refactor the constants which defines paths, so that vendor directory is supported natively without patches? Would like to clarify this before accepting pull request. |
@@ -0,0 +1,114 @@ | |||
<?php | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all new files should have copyright header
Absolutely, having a more natural support for modules in vendor without the need for a patch would be better! However - as I've already written above - regardless of the way modules from vendor are loaded, it would be good if the method |
Just a thought that just crossed my mind: the location of the vendor dir can be specified in the project composer.json file, so it might not always be in the Magento root dir. Ideally the vendor dir location would be retrieved from the project composer.json file. |
Added copyright and license comment to new files and rebased onto current develop HEAD. |
"Ideally the vendor dir location would be retrieved from the project composer.json file." - That would be nice. Personally I would love it if the code supported multiple directories to look in, but that may be hard and I would prefer small solid progress rather than making life too difficult. Having 'vendor' work is useful progress. Use case - I have my own modules in a Git repo somewhere else. I would like to say "code is in default locations, vendor, or a set of extra directories in my personal workspace". But 'vendor' is a great step forwards and may be enough. Oh, my other personal goal is to get deployment specific configuration as separate as possible to site configuration. E.g. graphics for my site are part of my site; directory its deployed to, or host and port of MySQL are not part of my site. That is a deployment detail. env.php is a step in this direction. Goal is to make it easier to move a "site" from dev, to test, to prod as easily as possible - its harder with deployment config files mixed in with site files. (But I digress sorry.) |
@Vinai regarding your comment about vendor location, we also have this file /magento2ce/app/etc/vendor_path.php with the content
|
@alankent Of course you are right - one small step after the other is how good progress is made! |
@vrann |
@Vinai , I'd suggest to run Composer with |
After investigating why the integration tests is failing, it turns out that the module registry needs an option to unregister modules, so the global state can be reset during the tests. |
@buskamuza Thank you for your input, I'll definitely try that! |
The idea was that mocks would be used for testing purposes, not the static |
@@ -32,6 +32,11 @@ public static function registerModule($moduleName, $path) | |||
self::$modulePaths[$moduleName] = $path; | |||
} | |||
|
|||
public static function unregisterModule($moduleName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is a good idea. What happens if this method is called when the application has already been bootstrapped and the unregistered module has already been loaded in some way? I think we need to make these registry objects immutable (see #1562), and tests should use mocks if they need to make use of a module registry
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm absolutely with you that Immutability is a good thing.
However, testability trumps this in my view, and since we are working with global state here, some mutability is probably required.
It should be possible to create Integration tests that run in isolation without mocks.
I did attempt to resolve the situation without changing the registrar. At first I tried the obvious solution: to run the test in a separate process with the @runTestsInSeparateProcesses
annotation. But the \Magento\TestFramework\Isolation\DeploymentConfig
listener sees the registered module as a deployment configuration change and complains:
ERROR: deployment configuration is corrupted. The application state is no longer valid.
Further tests may fail. This test failure may be misleading, if you are re-running it on a corrupted application.
Next try, use the @magentoAppIsolation enabled
annotation. With this annotation the listener \Magento\TestFramework\Annotation\AppIsolation
tries to reset the whole application state after a test with that annotation as follows:
/**
* Reset application global state
*/
protected function _resetApp()
{
/** @var $objectManager \Magento\TestFramework\ObjectManager */
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
$objectManager->clearCache();
\Magento\Framework\Data\Form::setElementRenderer(null);
\Magento\Framework\Data\Form::setFieldsetRenderer(null);
\Magento\Framework\Data\Form::setFieldsetElementRenderer(null);
$this->_appArea = null;
}
This fails since it resets all global state and recreates the application, but it doesn't reset the module registry.
Thinking further, the addition of the unregisterModule()
method does not conflict with your #1562. In fact, it complements it nicely.
Once the registrar is instantiated by the object manager and injected into the ModuleList\Loader
and the Module\Dir
, the list of modules in immutable. However, this instance is discarded when the application is reset by $objectManager->clearCache();
.
Then, when the application is initialized again, new instances of the registrar is created. This time it will use the changed list of modules.
To make it even more safe the preference could be configured as a shared instance.
A test that requires changing the module list state could do the following:
protected function setUp()
{
\Magento\Framework\Module\Registrar::registerModule('Test_Module', BP . '/vendor/test/example');
\Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize();
}
protected function tearDown()
{
\Magento\Framework\Module\Registrar::unregisterModule('Test_Module');
\Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize();
// Instead of calling `reinitialize()` after `unregister()`, the tests
// could also use the `@magentoAppIsolation enabled` annotation.
}
I've merged your PR #1562 locally and tried this and it works as expected.
If I still didn't manage to convince you, another thought was to make the unregisterModule
method final protected. That was a test could create a subclass and modify the list of registered modules that way. The benefit would be that the method signature would not be public, and thus the public static interface of the registrar is cleaner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking further, the addition of the unregisterModule() method does not conflict with your #1562. In fact, it complements it nicely.
I agree.
However, testability trumps this in my view, and since we are working with global state here, some mutability is probably required.
It should be possible to create Integration tests without mocks that run in isolation.
I think this comes down to personal preference. I don't think you're wrong, my preference is just different. But I think the big smell comes from the mutability of instances of Registrar
(addressed in #1562) which could cause the application state to be inconsistent, and which would be exacerbated by this change. If #1562 is merged then I wouldn't have any major issues with unregisterModule()
.
@Vinai Thanks for going into so much detail!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, too, @joshdifabio. I am really enjoying this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about adding another Modul Registrar Class which does not make use of static. The static method/the adding of modules is not part of the interface. Also its easier to Test with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Flyingmana What would be the use case for the additional class?
For more information please refer to issue 1481.
@Vinai update from my side: investigating the effort of changing the architecture of the framework not to depend on app/code:
Depending on the amount of effort, current PR would be either accepted as the fast way, or rejected. We discussed approach from the PR, it looks interesting, but 2 major concerns are:
After that, what would need to be investigated are:
|
@vrann Thanks for looking into this. Great to hear that a change using an absolute path to module directories is the preferred way to go forward. The list of items to investigate further after the change is also very interesting. Regarding the change of // \Magento\Framework\Filesystem\Directory\Read
public function getRelativePath()
{
if (!$this->driver->containsPath($this->path, $path)) {
throw new Exception("Nope, sorry, try again - the path has to be contained within {$this-path}");
}
return $this->driver->getRelativePath($this->path, $path);
} Probably the method should be renamed for that purpose, too. Maybe By the way, I'm fully okay with this PR not being accepted. I'm only saying this because I would like to work with code that behaves as I would expect it to do from simply looking at class and method names. That makes working with a code base a lot easier and more pleasant. |
Just noticed the "needs update" label on this PR. I'm still eager to get full support for modules in vendor/ working before the Q4 release. |
Hi, @Vinai, sorry for being slow on this. As it was mentioned before, I was looking into what does it cost to make platform changes in the way that we don't need to calculate relative paths related to app/code. This branch is result of my investigation: Magento was able to run, having most of its components under vendor dir. The exclusion is the magento2-base, which defines the structure of the project and have some required components. What is left to be done there in order to merge to mainline is:
After that, as a separate effort, we can split magento2-base module on more meaningful components. Still looking into that, so if you have some feedback it will be very helpful. |
More specifically, these are open questions to the approach:
|
Wow, this is very cool. Thanks for the update and detailed response! Regarding the registration callback file. I don't like the idea of every module having one, since the ones under app/code don't need it. A module should be built for one location in my view and doesn't need to work in both possible locations. Regarding the xsd locations, I think copying them to var/xsd is a pragmatic solution. Not ideal, but it fits to how many other parts of Magento 2 work. Looking forward to how this progresses, |
A goal of XSD files was to allow new modules to define their own config files, and hence have their own XSD files. So rather than have a single shared directory for XSD files, I would probably prefer:
|
@Vinai thanks! @alankent yes, looks like that will work. I don't remember a reason why we didn't do it in this way in the first place. Some IDE doesn't support it? Should this be verified on all major IDEs or PHPStorm is enough to start with? |
@Vinai marked as rejected because I think removal of MODULES constant will do the same. But will keep open in case if discussion is still going here. |
@vrann We did not do it in the first place as it forces you to set up the IDE with each path to the XSD file. It works well as long as modules sit under app/code. So it got us going. With this (great) effort to allow modules in other locations, the simplistic initial approach is no longer enough. Devs will have to set up the URN to correct path name mappings, which is more work. But the benefits are worth it (in my opinion). |
@vrann That extension development process you describe is quite likely, true. But still, I think it would be cleaner to only require a module to contain the data valid for one location. @alankent @vrann For me using an URN for the xsd file location would be fine. I don't know how other IDEs handle that. One thing PHPStorm supports are URNs pointing to an external source if the URL is specified after the URN separated by a space. The resource can then be downloaded and validated against using the intention keyboard command (alt-enter by default). This would make setting up the mappings trivial. |
Hi @Vinai , @joshdifabio , @alankent , @vrann ! |
The use case I am wondering about is a developer dealing with multiple modules, possibly in different GIT repos. E.g. a local site module, a shared one a SI is using on multiple projects (not local to project, but not public either), etc. So in my mind its no so much 'vendor' that is the issue, but rather can we support developers dealing with source code and version control management. But your explicit question regarding the 'vendor' directory, I cannot think of a reason that this directory needs to be able to move. If anything, my question would be "can we move away from having the Magento root directory" - can we make a whole site completely install under vendor with no special setup require for the root directory. Maybe that is too hard. For example, long term I would personally like to see one directory tree for the files developers set up (source code etc), and a separate directory tree holding things like 'var' and 'pub/static' where content is generated. But maybe there should even be three directories - source code, static computed files (still read only), and writable files (like logs). The first is in GIT. The first two can be made read-only in production. All three are required for a site to run. |
I dont comment much about this here, as it is not really related to the PR or the Issue, only a possible tool which could help solving this issues. And as i is nearly on a stable release, maybe someone wants to look more into it. |
We have automated a Magento Contributor License Agreement verifier for contributions sent to our GitHub projects. |
MAGETWO-71893: 2.2.0-RC21: Omitting --base_url breaks site (not getting host:port from "Host" header correctly)
This PR contains:
\Magento\Framework\Filesystem\Driver\File::getRelativePath()
\Magento\Framework\Filesystem\Driver\File::getRelativePath()
\Magento\Framework\Module\Dir\Reader
finds files in modules installed intovendor/
.The integration test does not cover the check for
Controller
directories and gathering action classes, because the ModuleDirReader directly operates on the file system, which is a lot more effort to test.For more information please refer to issue #1481