-
-
Notifications
You must be signed in to change notification settings - Fork 825
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
(WIP) civix#175 - Add support for mixins #19865
Conversation
(Standard links)
|
There's something that I have trouble wrapping my head around. I don't think it's unique to the stuff in this PR, but mixin happens to lean into the affected case more heavily. Suppose you have an extension which does some dynamic-ish registration, eg // FILE: myext.php
Civi::dispatcher()->addListener('hook_civicrm_alterMenu', function($e) {
$e->items['foo'] = [...];
}); Now, suppose you uninstall this extension. It seems this would be the sequence of events:
The thing is that we don't do (I suspect there may be some parallel issues around the management of singleton-ish data-structures during extension-lifecycle. Possibly (a) when manipulating the container definitions and/or (b) when the lifecycle action is install/enable.) (Why isn't this a concern for traditional hook-listeners? Because they're filtered on the list of active extensions, which is modified in step 2.a.) My current draft mixin in
|
Overview -------- (NOTE: For this description, I reference the term "API" in the general sense of a programmatic interface -- such as a hook or file-naming convention. It is not specifically about CRUD/DB APIs.) The `civix` code-generator provides support for additional coding-conventions -- ones which are more amenable to code-generation. For example, it autoloads files from `xml/Menu/*.xml` and `**/*.mgd.php`. The technique for implementing this traditionally relies on generating a lot of boilerplate. This patch introduces a new construct ("mixin") which allows boilerplate to be maintained more easily. A mixin inspects an extension programmatically, registering new hooks as needed. A mixin may start out as a file in `civix` (or even as a bespoke file in some module) - and then be migrated into `civicrm-core`. Each mixin has a name and version, which means that (at runtime) it will only load the mixin once (ie the best-available version). See: totten/civix#175 Before ------ The civix templates generate a few files, such as `mymod.php` and `mymod.civix.php`. A typical example looks like this: ```php // mymod.php - Implement hook_civicrm_xmlMenu require_once 'mymod.civix.php'; function mymod_civicrm_xmlMenu(&$all, $the, $params) { _mymod_civix_civicrm_xmlMenu($all, $the, $params); } ``` and ```php // mymod.civix.php - Implement hook_civicrm_xmlMenu function _mymod_civix_civicrm_xmlMenu(&$all, $the, $params) { foreach (_mosaico_civix_glob(__DIR__ . '/xml/Menu/*.xml') as $file) { $files[] = $file; } } ``` These two files are managed differently: `mymod.php` is owned by the developer, and they may add/remove/manage the hooks in this file. `mymod.civix.php` is owned by `civix` and must be autogenerated. This structure allows `civix` (and any `civix`-based extension) to take advantage of new coding-convention immediately. However, it comes with a few pain-points: * If you want to write a patch for `_mymod_civix_civicrm_xmlMenu`, the dev-test-loop requires several steps. * If `civix` needs to add a new `hook_civicrm_foo`, then the author must manually create the stub function in `mymod.php`. `civix` has documentation (`UPGRADE.md`) which keeps a long list of stubs that must be manually added. * If `civix` has an update for `_mymod_civix_civicrm_xmlMenu`, then the author must regenerate `mymod.civix.php`. * If `mymod_civix_xmlMenu` needs a change, then the author must apply it manually. * If `civix`'s spin on `hook_civicrm_xmlMenu` becomes widespread, then the `xmlMenu` boilerplate is duplicated across many extensions. After ----- An extension may enable a mixin in `info.xml`, eg: ```xml <mixins> <mixin>civix-register-files@2.0</mixin> </mixins> ``` Civi will look for a file `mixin/civicrm-register-files@2.0.0.mixin.php` (either in the extension or core). The file follows this pattern: ```php return function(\CRM_Extension_MixInfo $mixInfo, \CRM_Extension_BootCache $bootCache) { // echo "This is " . $mixInfo->longName . "!\n"; \Civi::dispatcher()->addListener("hook_civicrm_xmlMenu", function($e) use ($mixInfo) { ... }); } ``` The mixin file is a plain PHP file that can be debugged/copied/edited verbatim, and it can register for hooks on its own. The code is no longer a "template", and it doesn't need to be interwoven between `mymod.php` and `mymod.civix.php`. It is expected that a system may have multiple copies of a mixin. It will choose the newest compatible copy. Hypothetically, if there were a security update or internal API change, core might ship a newer version to supplant the old copy in any extensions. Technical Details ----------------- Mixins may define internal classes/interfaces/functions. However, each major-version must have a distinct prefix (e.g. `\V2\Mymixin\FooInterface`). Minor-versions may be provide incremental revisions over the same symbol (but it's imperative for newer increments to provide the backward-compatibility).
@totten is this still WIP? |
@totten needs rebase |
Going thu some old inactive PRs and am going to close this but feel free to reopen. Patch is stale. |
(This is an update of #17832 which addresses several of the issues from my last comment on that PR. However, there's still an issue which I haven't fully got my head around - which will discuss in a comment. But for the moment, it'd be nice to see how the tests run.)
Overview
(NOTE: For this description, I reference the term "API" in the general sense of a programmatic interface -- such as a hook or file-naming convention. It is not specifically about CRUD/DB APIs.)
The
civix
code-generator provides support for additional coding-conventions -- ones which are more amenable to code-generation. For example, it autoloads files fromxml/Menu/*.xml
and**/*.mgd.php
. The technique for implementing this traditionally relies on generating a lot of boilerplate.This patch introduces a new construct ("mixin") which allows boilerplate to be maintained more easily. A mixin inspects an extension programmatically, registering new hooks as needed. A mixin may start out as a file in
civix
(or even as a bespoke file in some module) - and then be migrated intocivicrm-core
. Each mixin has a name and version, which means that (at runtime) it will only load the mixin once (ie the best-available version).See: totten/civix#175
Before
The civix templates generate a few files, such as
mymod.php
andmymod.civix.php
. A typical example looks like this:and
These two files are managed differently:
mymod.php
is owned by the developer, and they may add/remove/manage the hooks in this file.mymod.civix.php
is owned bycivix
and must be autogenerated.This structure allows
civix
(and anycivix
-based extension) to take advantage of new coding-convention immediately. However, it comes with a few pain-points:_mymod_civix_civicrm_xmlMenu
, the dev-test-loop requires several steps.civix
needs to add a newhook_civicrm_foo
, then the author must manually create the stub function inmymod.php
.civix
has documentation (UPGRADE.md
) which keeps a long list of stubs that must be manually added.civix
has an update for_mymod_civix_civicrm_xmlMenu
, then the author must regeneratemymod.civix.php
.mymod_civix_xmlMenu
needs a change, then the author must apply it manually.civix
's spin onhook_civicrm_xmlMenu
becomes widespread, then thexmlMenu
boilerplate is duplicated across many extensions.After
An extension may enable a mixin in
info.xml
, eg:Civi will look for a file
mixin/civicrm-register-files@2.0.0.mixin.php
(either in the extension or core). The file follows this pattern:The mixin file is a plain PHP file that can be debugged/copied/edited verbatim, and it can register for hooks on its own. The code is no longer a "template", and it doesn't need to be interwoven between
mymod.php
andmymod.civix.php
.It is expected that a system may have multiple copies of a mixin. It will choose the newest compatible copy. Hypothetically, if there were a security update or internal API change, core might ship a newer version to supplant the old copy in any extensions.
Comments
https://github.com/totten/shimmy/commits/master is an extension that a sample extension with an example mixin and significantly reduced boilerplate (
shimmy.civix.php
,shimmy.php
).