Jump to content

Conditional module autoload


pwFoo
 Share

Recommended Posts

Hi,

I would like to build plugins with conditional autoload dependent on loaded modules.

Topic: ProcessWire Dev Branch - 3. Conditional autoload modules

3. Conditional autoload modules. In PHP 5.3+, modules may now specify an anonymous function OR a selector string, rather than a boolean for the 'autoload' property returned by getModuleInfo(). PW runs the anonymous function after determining the current $page, so your module can make autoload decisions based on the $page (or any other factor you'd like), if desired. Lets say that we have a module that we only want to autoload when the template is 'admin': 

public static function getModuleInfo() {
  return array(
    'title' => 'Module Title',
    'summary' => 'Summary text...',
    'version' => 1, 
    'autoload' => function() {
        if(wire('page')->template == 'admin') return true;
          else return false; 
    }); 
}
And the same example but using a selector for autoload: 
public static function getModuleInfo() {
  return array(
    'title' => 'Module Title',
    'summary' => 'Summary text...',
    'version' => 1, 
    'autoload' => 'template=admin'
    ); 
}

I tried that simple function code inside getModuleInfo().

[...]
'requires' => array('UserFrontendRegister'),    
'autoload'=> function() { return class_exists('UserFrontendRegister',false); },
[...]

Class_exists was suggested to check if a module is loaded. That works fine inside a template, but not with my module / autoload function.

I think the module is NOT loaded during this code is executed, but I'm not sure...

Is such a conditional autoload possible?

Link to comment
Share on other sites

Yes, should work, but isn't my needed case ;)

I need to check if the module is loaded instead of just installed.

Here a example

class_exists('UserFrontendRegister',false);    // return false

$modules->get('UserFrontendRegister')
class_exists('UserFrontendRegister',false);    // return true

As autoload condition it could be used to load a register plugin only if the register module / page is active. Use template or page as selector is possible, but not as flexible as a class_exists check.

Link to comment
Share on other sites

After ProcessWire has finished the ready-stage, none of the conditional autoloads are evaluated again (the list of them is even cleared). This means that if you load a module in a template, you are already past the ready-stage and hence no autoloading conditionals will be processed.

  • Like 3
Link to comment
Share on other sites

Hello sforsman,

please read my first post with Ryans examples ;)

I only tested the class_exists function inside a template and it works as expected. 

As an autoload function inside getModuleInfo() it doesn't work.

Link to comment
Share on other sites

please read my first post with Ryans examples  ;)

I have read it. My point is that during ProcessWire bootstrapping, the class does not exist yet, and the existence (i.e. your conditional autoloading function) won't be checked after ProcessWire becomes ready()

Edit: Just to make this clear - the only thing you have when your conditional autoloading function is called is a placeholder module, the actual class hasn't been included yet - so it doesn't exist (unless it's an autoloading module or your module cache is empty). It will be included and replaced when you request the module inside your template - but like I have said, this does not trigger any conditional autoloading for other modules.

This won't work, because in the static method getModuleInfo() there is no guarantee that the API is already available.

See: https://github.com/wanze/ProcessBatcher/issues/1

Cheers

There is guarantee for the conditional autoloads - they are triggered when ProcessWire goes ready().

Edited by sforsman
  • Like 5
Link to comment
Share on other sites

3. Conditional autoload modules. In PHP 5.3+, modules may now specify an anonymous function OR a selector string, rather than a boolean for the 'autoload' property returned by getModuleInfo(). PW runs the anonymous function after determining the current $page, so your module can make autoload decisions based on the $page (or any other factor you'd like), if desired. Lets say that we have a module that we only want to autoload when the template is 'admin': 

After ProcessWire has finished the ready-stage, none of the conditional autoloads are evaluated again (the list of them is even cleared). This means that if you load a module in a template, you are already past the ready-stage and hence no autoloading conditionals will be processed.

Got it!

I think not the conditional autoload was the problem, but the main module is loaded after conditional autoload decision...

Link to comment
Share on other sites

Got it!

I think not the conditional autoload was the problem, but the main module is loaded after conditional autoload decision...

Oh, do you mean that you are just trying to see if a module is installed? And if so, you want another module to be loaded?

Link to comment
Share on other sites

By the way the order does not matter if your main module is not an autoloading module. Its real class is not loaded until the module is requested in the template. You will only have a placeholder that is created based on the cache, without including the main module's code.

  • Like 1
Link to comment
Share on other sites

Hi Soma, I know this post and tried to use Ryans Code example as autoload condition ;)

By the way the order does not matter if your main module is not an autoloading module. Its real class is not loaded until the module is requested in the template. You will only have a placeholder that is created based on the cache, without including the main module's code.

The main module isn't an autoload module. 

I don't know how PW handles or cache this. 

Link to comment
Share on other sites

Sorry wasn't reading carefully.

Maybe if you can provide more what you want to archive, maybe there's better alternative ways.

It's also good to know that if you change the autoload setting in code you have to refresh module cache or reinstall module.

Link to comment
Share on other sites

@sforsman

Ok, thanks. So I have to find another way 

@Soma

No problem, thanks for reading and answer here :)

I have a register user module loaded inside a template and now try to (auto-)load an plugin / extension (here an example / test notification module) via conditional autoload.

    public static function getModuleInfo() {
        return array(
            'title' => 'UserFrontendRegisterNotification',
            'summary' => 'Enable Emails to Registration',
            'version' => '001',
            'requires' => array('UserFrontendRegister'),    
            'autoload'=> function() { return class_exists('UserFrontendRegister',false); },
        );
    }

The notification module hooks into the register method after a user is successful registered.

I wouldn't autoload the register notifications on every page, but if the register module is loaded.

So a conditional autoload (see above) looks like the best solution. Unfortunately that way doesn't work ;)

Here the example addHook from example module notifications

public function init()
    {
        $this->addHookAfter('UserFrontendRegister::executeRegister',$this,'sendEmail');
    }

The conditional autoload should load the notifications module only if the register module is loaded. During init() the submodule / plugin hooks into the "main module".

Any other resource friendly method to do that?

Link to comment
Share on other sites

A working solution at the moment is a autoload if the main module is installed.

'autoload'=> function() { return wire('modules')->isInstalled('UserFrontendRegister'); },

And add a hook after register process at init().

$this->addHookAfter('UserFrontendRegister::executeRegister',$this,'sendEmail');

I know this solution, but tried to find a way to reduce loading of not needed modules.

Link to comment
Share on other sites

After all I think it depends a lot on what context and how it's used. If in templates, a call to the main module (which is not autoload), you can't use the autoload condition, unless you also trigger something from the template which is not that nice. In the end I think a conditional autoload also requires some "resources" so loading a simple module that only has hooks anyway, might not be as bad you think.

As far as I know modules that hook into something need to be "autoload" anyway, if using a condition or not doesn't really matter and is a ok tradeoff? Not sure if I'm overlooking something obvious. 

Of course it's a good thinking in the first place to not waste resources, would be good to maybe have some sort of data to show what impact autoload really has when there's 100's of additonal modules? I'm also curious how Ryan would see this, I can't remember there was ever something in more depth about this. Anybody care to test? :)

Link to comment
Share on other sites

A working solution at the moment is a autoload if the main module is installed.

'autoload'=> function() { return wire('modules')->isInstalled('UserFrontendRegister'); },

And add a hook after register process at init().

$this->addHookAfter('UserFrontendRegister::executeRegister',$this,'sendEmail');

I know this solution, but tried to find a way to reduce loading of not needed modules.

The funny thing is, ProcessWire works like this for the exact same reason: to reduce loading of not needed modules :)

If you really, really want to do this yourself, then you would have to create a container inside the main module (or create a new module), that holds the relationships (this can even be a module configuration field). Then each "sub" module registers themselves as a conditionally loadable module with the main module. This would be done inside their ___install() -method. Then in your main module's init(), you fetch all the registered "sub" modules and load them. This would work - I can even write you an example. But to be honest, I'd just autoload the damn the module ;) Though just let me know if you still want an example of what I described.

  • Like 6
Link to comment
Share on other sites

@sforsman: Thanks for your great answers in the forum recently! Looks like you have a lot of knowledge about PW even with only 50 posts in the forum :) Keep on doing the good stuff :D

 

What Nico said! My thoughts exactly!  :-)  ^-^ . 

Wow ok, thanks for the encouraging comments guys :) I've worked with PW for quite a while, surfing the core silently in the background (I'm a little shy like that). But one day apeisa gave me a proper kick in the knee and told me to grow a pair - I did, ergo, here I am.

I must say you have a really good thing going on here and I'm very happy to be part of it!

  • Like 7
Link to comment
Share on other sites

@sforsman

Thank you for helping here! 

If you really, really want to do this yourself, then you would have to create a container inside the main module (or create a new module), that holds the relationships (this can even be a module configuration field). Then each "sub" module registers themselves as a conditionally loadable module with the main module. This would be done inside their ___install() -method. Then in your main module's init(), you fetch all the registered "sub" modules and load them. This would work - I can even write you an example. 

Ok, got the way it works. Interesting and nice way.

Dependent on the quantity of plugins it could be better, but I should keep it simple first... So I start with a simple conditional autoload (module is installed).

Would like to see example code, too. But wouldn't waste your time to write the code ;)

Link to comment
Share on other sites

  • 2 years later...

Just for the info this can be used to autoload only if the template of the edited page in the admin is "basic-page":

'autoload' => function() { return wire('pages')->get(wire('input')->get->id)->template == 'basic-page'; },

 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...