-
-
Notifications
You must be signed in to change notification settings - Fork 812
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
civix#175 - Add support for shim scripts #17832
Conversation
(Standard links)
|
test this please |
@artfulrobot you look like you have gotten your head around this. I'm trying to resist the temptation not to just trust @totten on this & get some review but it makes my brain hurt I don't think this needs very deep review in that it is adding code & won't break something & to the extent it does we'll make Tim fix it anyway and it only affects developers. But I think it at least needs someone to +1 the approach & this seems to have emerged from a discussion you were in & are hopefully following |
@eileenmcnaughton I've been discussing this with @totten - me bringing crude ideas, Tim patiently applying and explaining genious! Like you, it's a head scratcher, but I'm onboard with the theory now. It'll take me a while to look at this specific implementation (I have very limited time at the mo), and if y'all are ok with that there might be some value to me asking some more questions here. Along the same lines, I've added needs-documentation because I think this sort of PR could be a problem if it went in without docs and then few people used it and we all forgot what it was supposed to do. I'd be happy to help write those, which would belong in the writing extensions chapter of the dev guide. |
Thanks @artfulrobot - I have tried a few times but my head kept hurting |
@eileenmcnaughton I think it will become much simpler once we have some/more practical examples. |
For sure - bluntly I think the goal is to use core code rather than copy & paste for repeated functions. I suppose what I don't know is how tricky that is to keep version compatibility on |
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 handier than core's and 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 ("a shim file") which allows boilerplate to be maintained more easily. A "shim" is like an adapter or polyfill that translates a new API (e.g. `*.mgd.php`) to an older API (e.g. `hook_civicrm_managed`). The "shim" may start out as a file in civix (or even as a bespoke file in some module) - and then be migrated into `civicrm-core`. 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 needs an update to its `hook_civicrm_foo`, then the author must regenerate `mymod.civix.php`. * If civix's spin on `hook_civicrm_xmlMenu` becomes widespread, then the `xmlMenu` boilerplate is duplicated across many extensions. After ----- When loading an extension, Civi will look for files named `shims/*.shim.php`. Each file follows this pattern: ```php return function(\CRM_Extension_ShimApi $shimApi) { // echo "This is " . $shimApi->longName . "!\n"; \Civi::dispatcher()->addListener("hook_civicrm_xmlMenu", function($e) use ($shimApi) { ... }); } ``` The shim-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`. There is a presumption that all shims will be loaded. However, if the equivalent functionality has been migrated into `civicrm-core`, then `civicrm-core` may veto the shim-file. Comments -------- After this is merged, we can add some actual shims. If it works out after the first RC, then I'd be fairly tempted to backport (eg to an ESR), but that's not a deal-breaker.
Thanks and agree with the Made some updates to the PR description. Rebased - since the related PR is now merged, this one now looks smaller than it did (1 commit vs 3 commits). A few notes-to-self/review:
|
test this please |
Closing for now - @totten to re-open once above addressed |
Overview
The
civix
code-generator provides a large set of boilerplate code (egmymod.civix.php
andmymod.php
). Its purpose is to makecivix
code-conventions (e.g.*.mgd.php
,xml/Menu/*.xml
) work withcivicrm-core
APIs (e.g.hook_managed
,hook_xmlMenu
). In this way, it serves as a bridge, polyfill, or shim between a civix extension and core.However, the boilerplate technique has various challenges. (See comments.)
This PR enables an alternative technique using "shim-files". A shim-file is a PHP file. As input, it receives some information about the extension (e.g. the extension-name and directory-path). It can analyze the extension. As output, it can register for hooks and call other APIs.
Compared to the boilerplate, shim-files aim to be gentler to work with at each stage of the lifecycle -- e.g.
civix.git
, you can edit a regular PHP file in a regular extension.civicrm-core
. This will automatically deactivate any obsolete shim-files bundled into extensions (without needing to republish the extensions).See: totten/civix#175
Depends: #17831
Before
The civix templates generate two files, such as
mymod.php
(src:module.php.php) andmymod.civix.php
(src:module.civix.php.php).A typical example looks like this:
and
The actual files (
mymod.php
andmymod.civix.php
) are much longer than the example - because they contain about a dozen shims.These two files are managed differently:
mymod.php
is owned by the downstream developer, and they may add/remove/manage the hooks in this file. If any changes are required in this boilerplate, then the developer must make them manually. (See: https://github.com/totten/civix/blob/master/UPGRADE.md - "Special Tasks")mymod.civix.php
is owned bycivix
and must be autogenerated. If any changes are required in this boilerplate, then the developer must regenerate. (See: https://github.com/totten/civix/blob/master/UPGRADE.md - "General Tasks")After
When loading an extension, Civi will look for files named
shims/*.shim.php
. These are basic PHP files and do not require meta-PHP templating. Each file has an init function like:The shim-file has a few distinctive properties:
$shimApi
). This allows it to examine the extension and register its artifacts.civicrm-core
provides the same functionality, thencivicrm-core
can disable/ignore/override the shim-file.class
,interface
, etc). (Analogy: The shim-file closure is similar to the Javascript closure used in a jQuery plugin.)Depending on circumstance/taste, the shim-files may be written in a highly-coupled or loosely-coupled style:
civix-convention-v1.shim.php
with 10 different event listeners.civix-menu-loader.shim.php
,civix-mgd-loader.shim.php
, and so on. Each file has only 1 or 2 listeners. These files are only created if they're needed.Comments
Both approaches -- boilerplate/meta-PHP-template and shim-files -- can serve the role of bridge/polyfill/shim. You can write an extension using new code-conventions immediately (before they've been approved in core).
However, the boilerplate comes with a few pain-points:
_mymod_civix_civicrm_xmlMenu
, the dev-test-loop requires several steps.hook_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.hook_civicrm_foo
, then it must add it to the boilerplate of all extensions... even for the many extensions which never usehook_civicrm_foo
.hook_civicrm_foo
, then the author must regeneratemymod.civix.php
.hook_civicrm_xmlMenu
becomes widespread, then thexmlMenu
boilerplate is duplicated across many extensions.civicrm-core
. But this will cause a problem because all the previously published extensions still have their own copy of the scanner. Each*.xml
file will be picked up twice, and there is no way to conditionally disable the old boilerplate.With shim-files, one can still take advantage of new coding-conventions immediately. However, several of those pain-points are improved because shims are plain PHP files that can be debugged/copied/edited verbatim, and they can register their own hooks (without dynamic code generation). The code is no longer a "template", and it doesn't need to be interwoven between
mymod.php
andmymod.civix.php
.There is a presumption that all shims will be loaded. However, if the equivalent functionality has been migrated into
civicrm-core
, thencivicrm-core
may veto the shim-file.Here's a slightly longer example of shim code: https://gist.github.com/totten/5fcd4835194981a6d667202d9e720cd5
Bike-shedding: The word "shim" mostly captures the intent, though other names could also describe this functionality - e.g. "mini module", "micro extension", "meta module"?