Skip to content
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

Module (xxx) could not be initialized #7

Open
weierophinney opened this issue Dec 31, 2019 · 11 comments
Open

Module (xxx) could not be initialized #7

weierophinney opened this issue Dec 31, 2019 · 11 comments

Comments

@weierophinney
Copy link
Member

There is currently only a general Exception, when a module cannot be loaded.

I think there should be more explicit messages and a better help message, what is possibly wrong.

  • display the searched paths for Module
  • Module.php was found but is wrong?
  • ...

http://stackoverflow.com/search?q=Module+could+not+be+initialized.


Originally posted by @ThaDafinser at zendframework/zend-modulemanager#3

@weierophinney
Copy link
Member Author

Module.php was found but is wrong? What is this case? The syntax error is alrady trigger by PHP..

The problem of Modules'path is that default there are only two paths vendor and modules but you can set a lot of them and print all will be a problem..

IMO the message error is very simple..


Originally posted by @gianarb at zendframework/zend-modulemanager#3 (comment)

@weierophinney
Copy link
Member Author

@gianarb i know that, but when u touch the first time ZF2 and you dont know much about it, you just see "Module (xxx) could not be initilaized" which is pretty bad IMO.

BTW: The module must not be in vendor or modules ...it can also be anywhere if you use composer autoloading classmap or do a manual optimize


Originally posted by @ThaDafinser at zendframework/zend-modulemanager#3 (comment)

@weierophinney
Copy link
Member Author

Module could not be instantiated because it could implement a constructor with required arguments.


Originally posted by @Maks3w at zendframework/zend-modulemanager#3 (comment)

@weierophinney
Copy link
Member Author

There are two conditions that can lead to an exception.

The first is when looping through the modules, if the module name is not a string, we'll raise an exception indicating that we're missing a module name.

The next is the one you're actually seeing: failure to initialize. There is exactly one reason this happens by default: the ModuleResolverListener could not resolve a named module to a Module class under that namespace.

That said, that is one listener on the event. We allow you to attach your own listeners on that event, and the understanding is that they should act as a stack, to allow falling through to another listener when unable to resolve. If they threw exceptions, they would break that paradigm.

So, that means that the code triggering the event (EVENT_LOAD_MODULE_RESOLVE) is going to look for the first to return an object instance, and if none does, raise an exception.

The question is: what should that message be? As I noted previously, we cannot have more specific messages, because an application has multiple listeners, it could be any number of reasons. Thus, it has to be a single message.

Right now, that message is "Module (%s) could not be initialized". The only better suggestion I can make is "Module (%s) could not be resolved to a Module class". If that works for you, we can change it; I'm not 100% convinced it gives any more useful information to a new user; I'm also not convinced that the current message isn't useful enough.


Originally posted by @weierophinney at zendframework/zend-modulemanager#3 (comment)

@weierophinney
Copy link
Member Author

A quick & dirty showcase what i mean:

    protected function loadModuleByName($event)
    {
        $result = $this->getEventManager()->trigger(ModuleEvent::EVENT_LOAD_MODULE_RESOLVE, $this, $event, function ($r) {
            return (is_object($r));
        });

        $module = $result->last();
        if (! is_object($module)) {
            /* @var $event \Zend\ModuleManager\ModuleEvent */

            $addMsg = '';
            // get all the paths and show it!
            if ($event instanceof \Zend\ModuleManager\ModuleEvent) {
                /* @var $config \Zend\Config\Config */
                $config = $event->getConfigListener()->getMergedConfig(true);

                if (isset($event->getParams()['configListener'])) {
                    /* @var $configListener \Zend\ModuleManager\Listener\ConfigListener */
                    $configListener = $event->getParams()['configListener'];

                    if(count($configListener->getOptions()->getModulePaths()) > 0){
                        $possibleModuleFiles = [];
                        foreach ($configListener->getOptions()->getModulePaths() as $path) {
                            $possibleModuleFiles[] = realpath($path) . DIRECTORY_SEPARATOR . $event->getModuleName() . DIRECTORY_SEPARATOR . 'Module.php';
                        }

                        $addMsg .= ' Is your Module.php is available in one of those paths: "' . implode('", "', $possibleModuleFiles) . '"';
                    }
                }
            }

            if (class_exists('Composer\Autoload\ClassLoader')) {
                // composer is active! ... check the path their!
                $addMsg .= ' Did you maybe defined your module in composer.json, but not used the command `composer install -o` ? (then the autoloading will not work)';
            }

            throw new Exception\RuntimeException(sprintf('Module (%s) could not be initialized.' . $addMsg, $event->getModuleName()));
        }

        return $module;
    }

Originally posted by @ThaDafinser at zendframework/zend-modulemanager#3 (comment)

@weierophinney
Copy link
Member Author

@ThaDafinser — You're missing my point. The code you put above makes an invalid assumption: that it knows what listeners were attached and executed in order to resolve the Module class for a given module.

About the only way to be generically informative is to indicate that the user should check the module_listener_options.module_paths setting to ensure that the module specified exists in one of those paths, and that it contains a Module class under the namespace that can be autoloaded. Getting the value of that configuration is not something we should attempt, as the configuration listener may vary between applications.


Originally posted by @weierophinney at zendframework/zend-modulemanager#3 (comment)

@weierophinney
Copy link
Member Author

@weierophinney

A simple message could be: sprintf('Could not resolve the %s module using the %s class name', $moduleName, $className). Then here we at least know that the resolver cannot found our module class using the X classname. Initialization term don't really depends on the resolution process. An initialization exception has a better meaning in the module methods such as getConfig(), onBootstrap() ...

  • We resolve a module
  • Once we have resolved the module, we initialize the module

The problem is that the resolver currently return a module instance when it should only return the resolved classname and deletate instantiation to an object constructor. Object construction could be done through another event, allowing to provide our own object constructor. Having an dedicated object constructor (which could be overriden) would add possibilities on module initialization.

Of course, this is only my own thinking.


Originally posted by @nuxwin at zendframework/zend-modulemanager#3 (comment)

@weierophinney
Copy link
Member Author

@weierophinney
Its very annoying (and costs a lot of time) to debug (and extend) the debug message to find the real problem.

Right know I update my software from ZF2 to ZF3 and 1 of the thousands tests failed because of Module (Application) could not be initialized..


Originally posted by @dpauli at zendframework/zend-modulemanager#3 (comment)

@weierophinney
Copy link
Member Author

@dpauli Do you have a concrete suggestion? As noted above, the current suggestions are:

  • Improve the exception message generically; so far, no accepted verbiage has been provided.
  • Improve the exception message by more specifically determining the causes for failure; this, however, makes assumptions about the application structure and how module classes are resolved that are often not true.

As noted above, generally speaking, the exception occurs because we could not autoload a module class corresponding to the module name. How autoloading occurs varies a ton:

  • We default to using Composer for autoloading with v3, which means there's a high liklihood you did not add a rule to Composer, or did not run composer dump-autoload. In this case, we cannot determine the path to a module with any accuracy unless we introspect the composer.json, and, that's quite error-prone, too.
  • The module class associated with the module can be named after the module itself (e.g., a module named Foo\Bar\Baz could resolve to that class), OR to a Module class under the module name (e.g., Foo\Bar\Baz\Module). Since we try both, there's no way to know which one was intended, so we can only indicate that we cannot initialize the module, and not provide the name of the module class to load.
  • If you are sill using the ModuleAutoloader (which you shouldn't!), then autoloading may be path based. We cannot know for certain if you're relying on this facility, however.

My point with this is: we cannot get more specific in our error messages because there are too many possible ways to load a module class. The best I think we can do is improve the exception message. I could propose some alternatives, but I think the verbiage should come from those who have had issues, and who can suggest wording that might have helped them better diagnose the problem.


Originally posted by @weierophinney at zendframework/zend-modulemanager#3 (comment)

@afilina
Copy link

afilina commented Oct 20, 2021

In case this helps someone, I had the same problem when copying skeleton code to an existing project. I solved it by adding this to composer.json:

"autoload": {
    "psr-4": {
      "Application\\": "module/Application/src/"
    }
  }

@emedeirox
Copy link

I solve this problem using this...
\config\application.config.php

return [
    'module_listener_options' => [
        'use_laminas_loader' => true,
        'module_paths' => [
            './module',
            './vendor',
        ], ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants