News:

Wondering if this will always be free?  See why free is better.

Main Menu

Rethinking hooks - thoughts from other places

Started by Arantor, August 18, 2019, 02:12:03 PM

Previous topic - Next topic

Suki

My experience with observables/ events/listeners is that they quickly become too cumbersome (Symfony and Laravel) and invassive. Symfony for example has too much boilerplate code and the way you need to declare your listener isn't always the best experience. I like SMF's hook system because of its simplicity, its raw, straight to the point code.

I agree hooks needs to be separated and categorized and that not all categories will really be called "hooks" or even resemble them.

Indeed I thought about giving hooks a way to set their priority, heck, even separate them from the settings table to avoid having an active hook while uninstalling it for example

For 3.0 I want to port some of that hook simplicity, to be able to execute whatever you want on a certain point in the code lifecycle does have its charm, however, that level of liberty was only achievable by abusing global scope, which isn't something I plan to continue doing.

Indeed routing needs to be on its own, haven't decided how exactly tackle that but certainly a way to extend/listen the route system needs to happen.  The rest?  I think the question of what we will do will become clear once we have set a code structure and architecture.  I'm not closed to have more than one system or even more than one way to declare/use a system.

Quote from: SpacePhoenix on August 20, 2019, 07:30:27 PM

I just used Notepad++ to do a quick search for "function __construct(" and it seems like the only OOP code is 3rd party code. Why hasn't OOP been used for 2.1?

Because 2.1 wasn't meant to be re-write.  2.1 supports mods been completely OOP.
Disclaimer: unless otherwise stated, all my posts are personal and does not represent any views or opinions held by Simple Machines.

Arantor

I don't disagree that it can very quickly escalate - the boilerplate I deal with for listeners in Totara is more effort than the type of boilerplate I suggest here (and am using), though I don't have any recent Laravel or Symfony experience. Priorities are a really nice to have feature and if you're already thinking of overhauling it in future, I'd definitely suggest adding it.

Though I think that the emitter is not exactly more code and the data carrier (something SMF doesn't exactly have, it just passes everything directly, which has the side effect of listeners having to remember to declare what is a reference)... well, the data carrier by being an actual object has the advantage that it becomes pretty self documenting and easy to look up later.

You don't have to explicitly remove them from the settings table to get what you're looking to achieve, you just have to not do added-to-list-of-hooks as a default. If you add them at run time rather than install time, this whole problem is entirely solveable.

Routing is a thing I'm working on, and every framework seems to do it differently, just because they all have slightly different quirks, mostly around how far the separation between slug and id goes, e.g. some of them seem to suggest /route/slug/id versus something like XenForo's /route/slug.id/ style approach.

I just took the view that hooks really should be for event emitting or in-flight changing, and that routing isn't really something that should have a generic hook.

But I'm not trying to suggest what SMF 3 should or should not do, merely this is what I'm doing and it might be an interesting thing to look at as it might help guide thinking towards or away from certain things. I definitely think the current approach has issues, these are the ones I see and what I want to do about it inside my bubble. It may happen that it works out well, it may happen that I have drastically misjudged it, especially as I'm going to end up being the principle mod author.

I also have to realise that I have a litany of things I'd love to do but even time to actually do them...

SpacePhoenix

Quote from: Suki on August 21, 2019, 11:31:38 AM

Because 2.1 wasn't meant to be re-write.  2.1 supports mods been completely OOP.

Was 2.1 basically meant to be just fixing things that got broken with PHP version 7.0 and newer?

Aleksi "Lex" Kilpinen

Quote from: SpacePhoenix on August 21, 2019, 12:12:38 PM
Quote from: Suki on August 21, 2019, 11:31:38 AM

Because 2.1 wasn't meant to be re-write.  2.1 supports mods been completely OOP.

Was 2.1 basically meant to be just fixing things that got broken with PHP version 7.0 and newer?
No, it's supposed to be a .1 release.
Slava
Ukraini!
"Before you allow people access to your forum, especially in an administrative position, you must be aware that that person can seriously damage your forum. Therefore, you should only allow people that you trust, implicitly, to have such access." -Douglas

How you can help SMF

Suki

Quote from: Arantor on August 21, 2019, 11:46:23 AM
I don't disagree that it can very quickly escalate - the boilerplate I deal with for listeners in Totara is more effort than the type of boilerplate I suggest here (and am using), though I don't have any recent Laravel or Symfony experience. Priorities are a really nice to have feature and if you're already thinking of overhauling it in future, I'd definitely suggest adding it.

Though I think that the emitter is not exactly more code and the data carrier (something SMF doesn't exactly have, it just passes everything directly, which has the side effect of listeners having to remember to declare what is a reference)... well, the data carrier by being an actual object has the advantage that it becomes pretty self documenting and easy to look up later.

You don't have to explicitly remove them from the settings table to get what you're looking to achieve, you just have to not do added-to-list-of-hooks as a default. If you add them at run time rather than install time, this whole problem is entirely solveable.

Routing is a thing I'm working on, and every framework seems to do it differently, just because they all have slightly different quirks, mostly around how far the separation between slug and id goes, e.g. some of them seem to suggest /route/slug/id versus something like XenForo's /route/slug.id/ style approach.

I just took the view that hooks really should be for event emitting or in-flight changing, and that routing isn't really something that should have a generic hook.

But I'm not trying to suggest what SMF 3 should or should not do, merely this is what I'm doing and it might be an interesting thing to look at as it might help guide thinking towards or away from certain things. I definitely think the current approach has issues, these are the ones I see and what I want to do about it inside my bubble. It may happen that it works out well, it may happen that I have drastically misjudged it, especially as I'm going to end up being the principle mod author.

I also have to realise that I have a litany of things I'd love to do but even time to actually do them...



I agree with you.  (shock!) :D

And its basically what I want to do too. The data carrier is an interesting concept and its what I had in mind. Just need to find a balance between been really useful and been bloated. This is why having more than one way to listen/emit could be used but then again having more than one means more stuff to test, overlapping functionality and that kind of fun stuff.

Routing, that's where my headaches start. I want SMF to be as framework agnostic as possible (use but not depend), this means two things, use X framework's routing stuff or write our own, both has its pros and cons and that's where I'm stumbled.


I have a bazillion ideas for 3.0, some of them are written down, some others are just floating on my head and indeed time constraints are a pain in the posterior.
Disclaimer: unless otherwise stated, all my posts are personal and does not represent any views or opinions held by Simple Machines.

Arantor

Since this post I've had some time to reflect on a few things, namely the stuff I think about that's essentially API connectivity. Might spark a few ideas or discussions, so here we go.

Specifically, there's a bunch of places where things are primarily extensible along a single axis and while you could do much larger changes, the vast focus of such is along those axes. For example, new payment providers for paid subs, new search backends, new autosuggest handlers, new scheduled tasks.

So I thought about it and came up with a solution I think is sensible. Each of those different things in my setup is already a class - each of the autosuggest backends, each of the scheduled tasks, each of the payment providers, they're all separate classes and (now) all of them have parent interfaces to work off. (Some have abstracts to help them but they all have discrete interfaces, Payment\PaymentProvider, Search\Searchable, Helper\Autocomplete\Completable, Task\Schedulable)

Each of these now extends an interface called Discoverable, and this is where things get interesting - the system will on occasion scan everything it can find and log which interfaces extend Discoverable, and any classes that implement *that* interface can be found pretty painlessly by asking the class manager for the classes that implement a specific interface. I didn't do it for all interfaces and all classes, because that seemed overkill, but doing it for a specific subset of interfaces seemed like a sensible idea, and this extends pretty seamlessly to plugins.

This means several things I can now do:
1. I don't have to maintain a list of scheduled tasks any more during the installer. The installer could, during late stage setup, request the list of scheduled tasks, find the ones it doesn't already have and add them. (I already remodelled the scheduled tasks table to not rely on having a function name but only a class name, and classes are responsible for providing their own name/description now). This does imply that the tasks have some idea what their schedule should be as well, but that's OK, most of them run daily and it doesn't take much to create something like the stock install list if you already have a list of class names.

2. I don't need to have hooks or weird detection to implement new things. If I make a plugin that provides, I dunno... Stripe as a payment provider, I make a class that follows the PaymentProvider interface and that's it. No more looking for files of the right name structure or specific comments in their header, I just implement the right interface, run the install and I'm done. Similarly for autosuggest, I don't have to go and create the logic then add a hook, I just define the class with the right interface, run the install (or clear cache), and it's ready to go.

It's also a long-life cache so it does occasionally need to be rebuilt if you add/remove a plugin or something like that, but the price of convenience in general totally seemed worth it to me. Doing it for caching requires more thought though because of the way I do everything else with hooks.

As for overheads, the overhead is really only paid if you take a code path that actually uses one of these; autosuggest, paid subs, search etc. don't need to do this lookup and don't even load the ClassManager if they don't actually need it, and while there is a slight cost of actually having to load the cache/load the extra interface, this is offset very easily by opcache.

Example for what I mean, loading a list of the autocomplete backends where it builds a map of things like 'member' => 'StoryBB\\Helper\\Autocomplete\\Member' for the routing purposes:
        $searchTypes = [];
        foreach (ClassManager::get_classes_implementing('StoryBB\\Helper\\Autocomplete\\Completable') as $class)
        {
            $searchTypes[strtolower(substr(strrchr($class, '\\'), 1))] = $class;
        }


(where ClassManager has an appropriate use statement)

But it just returns a regular array and lets the caller decide how it wants to use that list. Payment gateways do more for example than just this (though in theory there's no reason why I couldn't boil it down more)

Y'all might just think I'm a bit crazy but crazy is good right?? ;) Anyway, I hope this has been interesting and hopefully it might spark an idea or two.

Mick.

@Arantor, bro I've been following you a long time and your work. Your ideas always impress the heck out of me. You have a gift, a talent. Keep rocking it man. -Mick.

Gwenwyfar

QuoteY'all might just think I'm a bit crazy but crazy is good right?? ;) Anyway, I hope this has been interesting
Always is :)
"It is impossible to communicate with one that does not wish to communicate"

Advertisement: