Advertisement:

Author Topic: Rethinking hooks - thoughts from other places  (Read 1142 times)

Offline Arantor

  • Resident Overthinker
  • SMF Friend
  • SMF Legend
  • *
  • Posts: 71,388
    • StoryBB/StoryBB on GitHub
Rethinking hooks - thoughts from other places
« on: August 18, 2019, 02:12:03 PM »
I'll be honest, I have never been *entirely* comfortable with SMF's idea of hooks, even though I've been championing them since basically 2.0 RC4 when they got upgraded and expanded. (And the implementation was nicer than my first draft inside SimpleDesk.)

And since I'm at a point where StoryBB has established its direction in my head about things I want to do and some of the stuff I want to be able to manage going forward, it's let me to rethink the position of hooks in the universe and what I plan on doing - the reason I'm posting here is so whatever SMF 2.2 or 3.0 or whatever does, it can at least have the results of what I did both past and present and what I learned from it. All of what follows stems from what SMF did and I want to open the discussion with it. None of this should be taken as 'SMF should do this' but at most it should be 'these are things future SMF could do if it made sense'.

First up, Wedge hooks. Wedge hooks were mostly the same as SMF 2.0 hooks but with some additional functionality:

1. Hook types
Firstly there were two kinds of hooks - functions and language. (Technically there were 3 but we'll come back to that.)

This meant that you could hook a function onto whatever was going on, like in SMF 2.0/2.1, but you could also just merely say 'load a language file at this point'. The main use I remember for it was the Help, if you wanted to expand something for the popup help you needed to load a new file. Now, you *can* do this in 2.1 (not 100% sure about 2.0) but you have to add a regular hook whose contents were basically a loadLanguage call. Wedge bypassed this to just be able to load the language directly.

2. Priorities
Hooks in SMF occur in a somewhat arbitrary order, namely whichever order they had been installed, which normally would be fine but sometimes there were hooks that had to go out of their way to remain last (the old SimpleSEF mod comes to mind, for legitimate technical reasons). Wedge hooks got a priority when installed, from 1 to 100 (lower = earlier), defaulting to 50, so plugins could set up their hooks in any setup they wanted including knowing about other plugins and making sure that they reordered themselves appropriately.

3. Safety
In SMF, when hooks are registered by a plugin, this is done forcibly in the database at install time. While performant and convenient, this has some issues if a mod gets removed especially if cleanup wasn't properly carried out for whatever reason or if the plugin is buggy in some fashion. What Wedge did was to defer attaching hooks until just after the list was loaded from the database when settings were loaded - it would then look up which plugins were marked as installed, looked up the hooks they declared and attached them for that one run.

While this is slower, the fact that it is done this way means you can rename a plugin folder and the plugin will effectively become disabled - you don't need to add any additional files, or change the DB, it's handled automatically. Rename the folder, delete the folder, plugin hooks all disengage. (Or at least did when I was still a dev, the latter changes that pseudocached file edits too... not so much)

4. Hook registration
Before installing a plugin, the list of hooks that a plugin uses are checked against a list of known hooks before trying to install - limiting the amount of need to do explicit version checking if you can do feature detection. But additionally, a plugin could declare its own hooks and these get added to the list of hooks the system knows about before trying to install a plugin. It was a neat idea but the manual list of hooks was a maintenance trouble since every time a new hook was made, we had to remember to update the master list of hooks.


The above mostly made a lot of sense and was generally fairly serviceable - the worst mistake I made was writing the plugin was continuing to use XML (only compounding it with SimpleXML). The other hurdle is that there's no convenient way to hack up the templates, Nao 'fixed' that by allowing plugins to make editing again, but that's something I'd never encourage.

So what the hell did I do in StoryBB that was different? And why? What benefits? Well, I took the principles behind what I'd done in Wedge - priorities were always a good idea, and so is the safety stuff. I haven't rethought what I'm doing with the language problem yet.

But what I did differently this time was build hooks as classes. Every hook is a class, and it either extends a class called Observable or a class called Mutatable - partly so it's super clear whether the hook is read-only or read-write, partly so that it's trivial to detect if a hook is present (class_exists on the hook's name and you're done) and if you wire up something like phpDocumentor, it's even documented automatically as to what parameters it accepts and the class comments can tell you where it's used.

The only difference really is that for most cases what used to be something like:

Code: [Select]
call_integration_hook('integrate_after_create_post', array($msgOptions, $topicOptions, $posterOptions, $message_columns, $message_parameters));
now becomes something like:
Code: [Select]
(new \StoryBB\Hook\Observable\Post\Created($msgOptions, $topicOptions, $posterOptions, $message_columns, $message_parameters))->execute();
(I like namespaces. In the current code Observable is an alias so in reality the call becomes new Observable\Post\Created. Either way, you pass all the parameters into the object which is an instance of Observable or Mutatable, which both extend a base class called Hookable, which handles some other stuff. The hook object then deals with giving you access to the parameters either way.)

The called functions are always any callable, though in practice this is static methods in autoloadable classes - I see 2.1 has added the # parameter to call_integration_hook which means you get an instance of the class but this mostly seems to me to be about a different type of syntactic sugar rather than anything externally changeable, you just end up with using static:: or self:: instead of $this-> which is a change I don't really get and I've never really sat down and looked at whether call_helper would end up preserving the one instance of the class or making new ones, which is potentially expensive depending on what's going on.

But when I started looking at the various hooks in place, I began to realise that there are really 3 broad types used throughout SMF, and I'm thinking about how I want to fix that because I find it inelegant and I'm at a place where I'm free to tear it all apart.

Specifically, as I see it in SMF, you have hooks that deal with routing (integrate_actions, integrate_XMLhttpMain_subActions for example), hooks that deal with what amounts to sideloading API-able functionality (integrate_autosuggest, things like loadCacheAPIs and loadPaymentGateways are contenders for upgrades to this style of thinking) and hooks that change behaviour either in local or global states (everything else).

The first one essentially is a question about gutting the entire routing system - right now it's two tiered, between top level actions and everything else, where some actions need no additional routing (e.g. the pseudo-action that is board index), some need very minimal and light routing (the aforementioned XMLhttpMain) and some need hugely complex routing (profile, admin).

Exactly what I think could/should happen is a little bit murky but ultimately it revolves around having a routing table that delineates all the known overarching routes, specifying parameters that make up the route itself - e.g. /board/myboard.1/?sortby=author has the board/myboard.1 part as the route itself with the sortby as an additional option that isn't really part of the route if that makes sense. (This also encourages pretty URLs by default, not because it boosts SEO but because it simplifies routing in practice)

Part of me wants to go to what XenForo does, where a route specifies a class and subroutes specify methods in that class, which can be overloaded by way of their class proxy system, which is very neat and generally not *that* hard to get your head around for building extensions to existing things. It also pretty exclusively means you don't have to have hooks everywhere just to bolster routing, you can let the routing component do that and just have plugins say 'I want this route to go to this class/method'.

Then we have the hooks that exist to offer functionality choices for things - this is actually really nice to solve, you can do things like make the autosuggest handler look for any class that implements a specific interface, and make those the known autosuggest options. The only time you'd then want a hook is if you wanted to completely replace a given autosuggest handler for any reason (which is, interestingly enough, surprisingly rare). Ditto for payment gateways, or components for the CAPTCHA system, make it all essentially pluggable and give it the power to look stuff up in the code for classes that implement the right interfaces. The goal here is to have as little time and energy spent on writing connectors between things when there's no reason the system can't do that for you. It also forces a more elegant separation of concerns especially for something like the CAPTCHA system.

Lastly the 'everything else' hooks, these all pretty much have to stay as-is, but that's OK, they're either being event emitters (Observables) or allowing you to modify state in-flight (Mutatables), and these are perfectly understandable, just it would be nice to get away a bit from using hooks as all-purpose tools when they really shouldn't be.

Phew. And please before any angry replies come in, this isn't meant to be a lecture on what SMF should do or could do. This is merely a collection of thoughts based on what I've seen and done and where my started-out-as-SMF setup is currently looking to go. If nothing else, the hope is merely to inspire the question of 'is there a better way' based on what I'm experimenting with, and to generate some discussion about how we can do things going forward in the ecosystem.
Don’t try to tell me that some power can corrupt a person. You haven’t had enough to know what it’s like.

No good deed goes unpunished / No act of charity goes unresented.

Online Aleksi "Lex" Kilpinen

  • A Peculiar Finn
  • Lead Support Specialist
  • SMF Super Hero
  • *
  • Posts: 18,540
  • Gender: Male
  • Don't worry, I'm n00b friendly
    • Aleksi.Kilpinen on Facebook
    • LexArma on GitHub
    • aleksi-kilpinen on LinkedIn
    • There's No Place Like 127.0.0.1
Re: Rethinking hooks - thoughts from other places
« Reply #1 on: August 18, 2019, 11:47:52 PM »
I'm not really familiar with the code behind hooks, and not really all too experienced in even using them, but I see a lot of good food for thought in this. So, let me be the first to say thank you for sharing.
A Finnish Support Specialist
 Happily running multiple SMF 2.0 installations.
  Fooling around with an i7 990X @ 3,47Ghz / 12Gb / Win 10 x64 / 3840x2160


How you can help SMF

"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

Offline lurkalot

  • Support Specialist
  • Sophist Member
  • *
  • Posts: 1,380
  • Gender: Male
  • Tinyportal Support
    • guitaristguild on Facebook
    • Tinyportal on GitHub
    • @GuitaristGuild on Twitter
    • Guitarist Guild
Re: Rethinking hooks - thoughts from other places
« Reply #2 on: August 19, 2019, 03:09:42 AM »
I'm not really familiar with the code behind hooks, and not really all too experienced in even using them, but I see a lot of good food for thought in this. So, let me be the first to say thank you for sharing.

Me neither.  I can't get my head around them at all, I have tried, but fear I'm too old to take it in.  ???

I would however like to know if there's a list of Hooks 2.1 vs 2.0.15 for comparison anywhere.  I have a mod that uses just hooks in 2.1 and would like to do the same for 2.0.15

Offline Arantor

  • Resident Overthinker
  • SMF Friend
  • SMF Legend
  • *
  • Posts: 71,388
    • StoryBB/StoryBB on GitHub
Re: Rethinking hooks - thoughts from other places
« Reply #3 on: August 19, 2019, 03:50:36 AM »
There is no such list, bu doing a search on “call_integration_hook” in the two would be a useful starting point. 2.1 has many many more hooks but you can usually improvise with a bit of creativity.
Don’t try to tell me that some power can corrupt a person. You haven’t had enough to know what it’s like.

No good deed goes unpunished / No act of charity goes unresented.

Offline SychO

  • Lead Customizer
  • SMF Hero
  • *
  • Posts: 1,466
  • Gender: Male
    • SychO9 on GitHub
    • SychO
Re: Rethinking hooks - thoughts from other places
« Reply #4 on: August 19, 2019, 04:09:49 AM »
https://gist.github.com/SychO9/e534681410c546a34c2daba16dff3831

Thanks for sharing Arantor,

Do you mean to create a class for every hook though? Wouldn't that be way too much ?

Imazighen

Offline Arantor

  • Resident Overthinker
  • SMF Friend
  • SMF Legend
  • *
  • Posts: 71,388
    • StoryBB/StoryBB on GitHub
Re: Rethinking hooks - thoughts from other places
« Reply #5 on: August 19, 2019, 05:12:37 AM »
Firstly, no, not really because autoloading is a thing in my world (as is opcache, and especially the new stuff in PHP 7.4)

Secondly, a number of those hooks will be going away because of better ways of doing it, e.g. integrate_actions will be replaced by a global routing tables (which also fixes several of the subactions that need routing too)

Thirdly, a number of those hooks will also go away by being replaced by API patterns, where plugins don’t need to explicitly deal with hooks because the plugin manager deals with the classes that need to be connected in other ways, such as auto suggest (when it sees a plugin, it looks at the classes it provides, and looks at whether any of those match interfaces it knows to look for, e.g. PaymentProvider or the cache API, and connects them up removing the hooks)
Don’t try to tell me that some power can corrupt a person. You haven’t had enough to know what it’s like.

No good deed goes unpunished / No act of charity goes unresented.

Offline d3vcho();

  • Sempiterno
  • Lead Localizer
  • SMF Hero
  • *
  • Posts: 3,752
  • Gender: Male
    • frandominguez03 on GitHub
Re: Rethinking hooks - thoughts from other places
« Reply #6 on: August 19, 2019, 06:53:19 AM »
This is indeed a very interesting topic Arantor, and before going further, I would like to comment this:

Phew. And please before any angry replies come in, this isn't meant to be a lecture on what SMF should do or could do. This is merely a collection of thoughts based on what I've seen and done and where my started-out-as-SMF setup is currently looking to go. If nothing else, the hope is merely to inspire the question of 'is there a better way' based on what I'm experimenting with, and to generate some discussion about how we can do things going forward in the ecosystem.

Your opinions and thoughts are very valued among SMF, you know that already. Angry replies should not come basically because we're not perfect and as time passes by, new ways of doing things appear. I mean it when I saw it's really fascinant because in college I'm being taught rather old stuff that's indeed the base for everything else, but we're always looking for the new ways in what we can solve problems.

I do know I'm still a newbie of programming, in general, and even more if we're talking about SMF's codebase. I've never understood hooks itself. I know what do they do and why they're good, but never completely understood the logic behind them. I've been playing with SMF 2.1 recently, and with that huge amount of hooks I was like: "Woah, almost no code edits needed. This is really good for the core itself". But then someone with high knowledge of programming and SMF's codebase comes and tells you that everything is good so far, but we can make it even better using more modern technologies and solutions.

Now, a few questions from my side. First of all, I don't understand the priorities thing. Can you put an example on why is it important to implement? I just can't visualize it in my mind.

Another thing. Why it would be better to have an instance for every hook created than a mere hook as we know them? What about the classes itself? Will the codebase become simpler if we make a class for every hook and an instance for every hook we need?

"Greeting Death as an old friend, they departed this life as equals."

Offline Arantor

  • Resident Overthinker
  • SMF Friend
  • SMF Legend
  • *
  • Posts: 71,388
    • StoryBB/StoryBB on GitHub
Re: Rethinking hooks - thoughts from other places
« Reply #7 on: August 19, 2019, 08:12:18 AM »
Ok, priorities. You have SimpleColorizer and SimpleSEF installed. Both of these use integrate_buffer but if the hook for SinpleSEF runs first, SimpleColorizer can’t find the links it needs because SimpleSEF already rewrote them.

If you happened to install SimpleSEF first then SimpleColorizer, the latter wouldn’t work without some kind of help. SimpleSEF itself knew about this and went to some effort to make sure that it was always the last hook called for integrate_buffer specifically so it wouldn’t break anything else.

Having a priority at setup time explicitly enables you to solve that problem by just letting a given plugin operate last without having to manually check and reset it regularly.

The use of classes does, for me, make it simpler. Partially it means that you can have a very clear and distinctive way of documenting what the hooks are, what parameters they accept and what hooks exist, just by having them be explicit classes. And if you’re set up to autogenerate docs with phpDocumentor, this is even more clear.

As a bonus you can then use IDE typehinting in all the relevant places because you’re passing around an actual class in all the cases.
Don’t try to tell me that some power can corrupt a person. You haven’t had enough to know what it’s like.

No good deed goes unpunished / No act of charity goes unresented.

Offline SychO

  • Lead Customizer
  • SMF Hero
  • *
  • Posts: 1,466
  • Gender: Male
    • SychO9 on GitHub
    • SychO
Re: Rethinking hooks - thoughts from other places
« Reply #8 on: August 19, 2019, 08:34:24 AM »
Sweet, once you start making use of the OOP concepts and what Php7 has to offer, much and more can be taken to a different level.

Too bad I can't offer more to the discussion, I am still learning about all of that.

Imazighen

Offline d3vcho();

  • Sempiterno
  • Lead Localizer
  • SMF Hero
  • *
  • Posts: 3,752
  • Gender: Male
    • frandominguez03 on GitHub
Re: Rethinking hooks - thoughts from other places
« Reply #9 on: August 19, 2019, 08:35:15 AM »
And how would you deal with different instances of the same classes for the particular case of hooks?

"Greeting Death as an old friend, they departed this life as equals."

Offline Arantor

  • Resident Overthinker
  • SMF Friend
  • SMF Legend
  • *
  • Posts: 71,388
    • StoryBB/StoryBB on GitHub
Re: Rethinking hooks - thoughts from other places
« Reply #10 on: August 19, 2019, 09:41:25 AM »
Sweet, once you start making use of the OOP concepts and what Php7 has to offer, much and more can be taken to a different level.

Too bad I can't offer more to the discussion, I am still learning about all of that.

That was what I was hoping to do, stimulate discussion on the subject :)

And how would you deal with different instances of the same classes for the particular case of hooks?

You instance the class once and pass that object to each hook in turn, giving you much the same functionality you have today (since the values are passed to each in turn, modified in place if they want to modify it).
Don’t try to tell me that some power can corrupt a person. You haven’t had enough to know what it’s like.

No good deed goes unpunished / No act of charity goes unresented.

Offline d3vcho();

  • Sempiterno
  • Lead Localizer
  • SMF Hero
  • *
  • Posts: 3,752
  • Gender: Male
    • frandominguez03 on GitHub
Re: Rethinking hooks - thoughts from other places
« Reply #11 on: August 19, 2019, 09:56:06 AM »
Sounds great actually. So you're planning to implement this in StoryBB right? No drafts yet?

"Greeting Death as an old friend, they departed this life as equals."

Offline Arantor

  • Resident Overthinker
  • SMF Friend
  • SMF Legend
  • *
  • Posts: 71,388
    • StoryBB/StoryBB on GitHub
Re: Rethinking hooks - thoughts from other places
« Reply #12 on: August 19, 2019, 10:17:47 AM »
Some of this is already done (the actual hook implementation is done), I’m just in the process of retooling the plugin manager from Wedge to use the new hook stuff.  Check the plugin-core branch if you’re curious about the hook implementation, there’s a couple of examples there.
Don’t try to tell me that some power can corrupt a person. You haven’t had enough to know what it’s like.

No good deed goes unpunished / No act of charity goes unresented.

Online albertlast

  • Development Contributor
  • Full Member
  • *
  • Posts: 587
Re: Rethinking hooks - thoughts from other places
« Reply #13 on: August 19, 2019, 11:15:07 AM »
Could you not convert the existing system without change anything on hookside and on addon side?
So only in the middle part between them.

Offline Arantor

  • Resident Overthinker
  • SMF Friend
  • SMF Legend
  • *
  • Posts: 71,388
    • StoryBB/StoryBB on GitHub
Re: Rethinking hooks - thoughts from other places
« Reply #14 on: August 19, 2019, 11:53:37 AM »
Could you not convert the existing system without change anything on hookside and on addon side?
So only in the middle part between them.

I could but that would mean I get the worst of both worlds. Remember, the first half of this post talks about Wedge where I was not nearly so radical and I decided I didn’t like the way it worked, plus it didn’t do anything nice for IDEs.
Don’t try to tell me that some power can corrupt a person. You haven’t had enough to know what it’s like.

No good deed goes unpunished / No act of charity goes unresented.

Offline rocknroller

  • SMF Hero
  • ******
  • Posts: 1,606
  • Gender: Male
Re: Rethinking hooks - thoughts from other places
« Reply #15 on: August 20, 2019, 06:09:30 PM »
Doesn't should be? As base concept., backend - API -frontend.

Maybe:
PHP MYSQL / API / so FE could be at least available for more programming languages.

Good luck.

Offline SpacePhoenix

  • Semi-Newbie
  • *
  • Posts: 90
Re: Rethinking hooks - thoughts from other places
« Reply #16 on: August 20, 2019, 07:30:27 PM »
Sweet, once you start making use of the OOP concepts and what Php7 has to offer, much and more can be taken to a different level.

Too bad I can't offer more to the discussion, I am still learning about all of that.

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?

Online vbgamer45

  • Customizer
  • SMF Super Hero
  • *
  • Posts: 21,496
    • smfhacks on Facebook
    • VBGAMER45 on GitHub
    • @createaforum on Twitter
    • SMF For Free
Re: Rethinking hooks - thoughts from other places
« Reply #17 on: August 20, 2019, 08:24:58 PM »
I am glad SMF 2.1 is not OOP. One of the reasons why I stick with it. A mixture is fine like what MyBB does. But when I look at code like Xenforo it makes it unworkable for me. I dislike having to trace each and every class to figure out what is going on. And it raises the barrier of entry of users who are are looking to modify the platform.
Community Suite for SMF - Take your forum to the next level built for SMF, Gallery,Store,Classifieds,Downloads,more!

SMFHacks.com -  Paid Modifications for SMF

Mods:
EzPortal - Portal System for SMF
SMF Gallery Pro
SMF Store SMF Classifieds Ad Seller Pro

Offline SpacePhoenix

  • Semi-Newbie
  • *
  • Posts: 90
Re: Rethinking hooks - thoughts from other places
« Reply #18 on: August 21, 2019, 06:49:57 AM »
Roughly what % of the backend was changed between 2.0.15 and the current RC of 2.1?

Offline Arantor

  • Resident Overthinker
  • SMF Friend
  • SMF Legend
  • *
  • Posts: 71,388
    • StoryBB/StoryBB on GitHub
Re: Rethinking hooks - thoughts from other places
« Reply #19 on: August 21, 2019, 07:24:06 AM »
Not nearly as much as it might seem, actually.

It’s hard to give a figure but I can’t imagine it would top 25%.
Don’t try to tell me that some power can corrupt a person. You haven’t had enough to know what it’s like.

No good deed goes unpunished / No act of charity goes unresented.

Offline Suki

  • Kaizoku Jotei
  • Developer
  • SMF Super Hero
  • *
  • Posts: 15,515
  • Oh, wouldn't it be great if I *was* crazy?
    • MissAllSunday on GitHub
    • SMF mods
Re: Rethinking hooks - thoughts from other places
« Reply #20 on: August 21, 2019, 11:31:38 AM »
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.


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.

Look at them. They're just asking for it. Maybe the human race deserves to be wiped out.

Offline Arantor

  • Resident Overthinker
  • SMF Friend
  • SMF Legend
  • *
  • Posts: 71,388
    • StoryBB/StoryBB on GitHub
Re: Rethinking hooks - thoughts from other places
« Reply #21 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...
Don’t try to tell me that some power can corrupt a person. You haven’t had enough to know what it’s like.

No good deed goes unpunished / No act of charity goes unresented.

Offline SpacePhoenix

  • Semi-Newbie
  • *
  • Posts: 90
Re: Rethinking hooks - thoughts from other places
« Reply #22 on: August 21, 2019, 12:12:38 PM »

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?

Online Aleksi "Lex" Kilpinen

  • A Peculiar Finn
  • Lead Support Specialist
  • SMF Super Hero
  • *
  • Posts: 18,540
  • Gender: Male
  • Don't worry, I'm n00b friendly
    • Aleksi.Kilpinen on Facebook
    • LexArma on GitHub
    • aleksi-kilpinen on LinkedIn
    • There's No Place Like 127.0.0.1
Re: Rethinking hooks - thoughts from other places
« Reply #23 on: August 21, 2019, 12:15:53 PM »

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.
A Finnish Support Specialist
 Happily running multiple SMF 2.0 installations.
  Fooling around with an i7 990X @ 3,47Ghz / 12Gb / Win 10 x64 / 3840x2160


How you can help SMF

"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

Offline Suki

  • Kaizoku Jotei
  • Developer
  • SMF Super Hero
  • *
  • Posts: 15,515
  • Oh, wouldn't it be great if I *was* crazy?
    • MissAllSunday on GitHub
    • SMF mods
Re: Rethinking hooks - thoughts from other places
« Reply #24 on: August 21, 2019, 12:22:04 PM »
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.

Look at them. They're just asking for it. Maybe the human race deserves to be wiped out.

Offline Arantor

  • Resident Overthinker
  • SMF Friend
  • SMF Legend
  • *
  • Posts: 71,388
    • StoryBB/StoryBB on GitHub
Re: Rethinking hooks - thoughts from other places
« Reply #25 on: September 02, 2019, 02:02:01 PM »
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:
Code: [Select]
        $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.
Don’t try to tell me that some power can corrupt a person. You haven’t had enough to know what it’s like.

No good deed goes unpunished / No act of charity goes unresented.

Offline Mick.

  • SMF Friend
  • SMF Hero
  • *
  • Posts: 4,991
  • Gender: Male
    • idesignSMF on Facebook
    • @idesign360 on Twitter
    • idesignSMF.com
Re: Rethinking hooks - thoughts from other places
« Reply #26 on: September 02, 2019, 02:30:36 PM »
@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.

Offline Gwenwyfar

  • Great Wizard's familiar
  • SMF Friend
  • SMF Hero
  • *
  • Posts: 2,263
  • Gender: Female
Re: Rethinking hooks - thoughts from other places
« Reply #27 on: September 02, 2019, 07:40:28 PM »
Quote
Y'all might just think I'm a bit crazy but crazy is good right?? ;) Anyway, I hope this has been interesting
Always is :)