laminas / laminas-modulemanager

Modular application system for laminas-mvc applications
https://docs.laminas.dev/laminas-modulemanager/
BSD 3-Clause "New" or "Revised" License
30 stars 18 forks source link

Module (xxx) could not be initialized #7

Open weierophinney opened 4 years ago

weierophinney commented 4 years ago

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.

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


Originally posted by @ThaDafinser at https://github.com/zendframework/zend-modulemanager/issues/3

weierophinney commented 4 years ago

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 https://github.com/zendframework/zend-modulemanager/issues/3#issuecomment-111986650

weierophinney commented 4 years ago

@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 https://github.com/zendframework/zend-modulemanager/issues/3#issuecomment-112007242

weierophinney commented 4 years ago

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


Originally posted by @Maks3w at https://github.com/zendframework/zend-modulemanager/issues/3#issuecomment-140125916

weierophinney commented 4 years ago

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 https://github.com/zendframework/zend-modulemanager/issues/3#issuecomment-142307142

weierophinney commented 4 years ago

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 https://github.com/zendframework/zend-modulemanager/issues/3#issuecomment-142316469

weierophinney commented 4 years ago

@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 https://github.com/zendframework/zend-modulemanager/issues/3#issuecomment-142597998

weierophinney commented 4 years ago

@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() ...

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 https://github.com/zendframework/zend-modulemanager/issues/3#issuecomment-160910413

weierophinney commented 4 years ago

@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 https://github.com/zendframework/zend-modulemanager/issues/3#issuecomment-345628405

weierophinney commented 4 years ago

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

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:

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 https://github.com/zendframework/zend-modulemanager/issues/3#issuecomment-345718448

afilina commented 2 years ago

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 commented 1 year ago

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

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