Closed (fixed)
Project:
Field Encryption
Version:
4.x-dev
Component:
Code
Priority:
Major
Category:
Task
Assigned:
Unassigned
Reporter:
Created:
9 Nov 2024 at 03:58 UTC
Updated:
26 Mar 2025 at 00:29 UTC
Jump to comment: Most recent
Drupal 11.1 has support for OOP hooks.
This module should convert its code to such hooks, and we need to provide a workaround for Drupal 11.1 because hooks can no longer be eval'd into existence.
The maintainers have decided that there will be a new 4.x branch that supports 11.1+.
This MR will add support for OOP hooks and remove the module's reliance on eval() for Drupal 11.1+.
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
ptmkenny commentedComment #4
ptmkenny commentedI don't get why we're getting the container not initialized error:
Comment #5
ptmkenny commentedComment #6
ptmkenny commentedTests are broken on minor, so postponing this on #3486538: Support Drupal 11.1.
Comment #7
nicxvan commentedI don't think this actually needs to be postponed, I'll let you change that though, but just transferring comments here.
Unfortunately dynamically created hooks are unsupported in the new OOP system. It is listed under breaking changes.
You'll want to implement one hook for update and insert like this:
Then you can order that using HMIA normally.
Once you've done that you can remove the eval code and the function checking for eval availability.
You'll also be able to remove the phpcs ignore rule you have to ignore complaints about eval.
Let me know if you have any other questions!
Comment #8
nicxvan commentedOne more note: I think once you do this, you WILL need the patch here: #3484754: Ordering of hooks needs some attention since you will be reordering using extra types.
Comment #9
ghost of drupal pastnicxvan is right: the new system can't support such eval'd code and it's perhaps easiest to decide runtime instead. But what if someone really, really, really wants to define hooks dynamically? It's doable: HookCollectorPass merely automates the following three things when it finds a method marked with the Hook attribute but it's totally doable manually.
1. Register the class as an autowired service (with the classname as the service id):
2. Add a kernel.event_listener tag on the definition:
3. Record which module this listener belongs to in the container argument
hook_implementations_map:Note you can add as many hooks as you want on the same class/service and even the same method.
Also note the Hook attribute is not used after this.
So if you do not want to decide run time which entity type should fire your service then you could use a service provider to iterate your entity types and add the right tag directly on the field_encrypt.process_entities service, method decryptEntity and record it in the map:
The code is untested but should be close.
Yes this is not easy but this was not among the many goals the hook-as-OOP system needed to solve. It's enough it's doable as it should be very rare this is necessary.
You could keep the current code for pre-Hook codebases and use this for Drupal 11.1 and eventually drop the old one. You could class_exists the HookCollectorPass class in your .module and bail out early if it exists. The service provider doesn't do anything on pre-Hook codebases and also requires nothing from the new codebase so that's safe to keep around. This way one branch will work for all.
Comment #11
ptmkenny commented@nicxvan and @ghost of drupal past: Thank you for all that info; it's very helpful. The eval hooks will need to be rethought for the 11.1 versino.
However, there may be another issue still. The "container not initialized" failure appears to be coming from calling
\Drupal::state()inhook_module_implements_alter(), not the eval hooks. I made an MR that drops all the eval hooks (disable_eval MR on this issue), and I'm still getting a "container not initialized" error in Drupal 11.1, but not in 11.0 (ignore the other test failures, because the eval hooks test obviously fails when they are not present).It's caused by this part of the module_implements_alter():
The change record says that dynamic hooks don't work, but it doesn't say anything about new restrictions on accessing services in hook_module_implements_alter(). To confirm this, I made a third mr (disable_eval_state on this issue) that also removes the Drupal::state() call, and it does not have a "container not initialized" error. Could you please share your thoughts on this?
Comment #13
nicxvan commentedYes sorry if it was not clear.
The explicit reason is because hmia is called during build.
But the question is why are you calling drupal state there.
It is so you can then dynamically generate hook implementations and order them.
If you take one of the two solutions recommended you can just order the specific hooks you create and you don't need the Drupal state call.
Comment #14
ptmkenny commentedHmm. Actually this hook calls state for two reasons. One is to reorder the dynamic hooks that are eval'd into existence; this implementation definitely has to be changed.
However, not all servers allow the use of eval(). For those servers, the Field Encrypt module allows developers to define the hooks that would otherwise be eval'd in a custom module. So this hook is also calling state to reorder hooks implemented in custom modules. So I guess we have to change the way we handle that case as well.
Comment #15
alexpottFWIW we cannot use the generic hook_entity_insert() here. We need to decrypt the entity at the very beginning of the calls to the hooks and hook_ENTITY_TYPE_insert is called before the generic hooks. That's why all this complexity exists in the first place. Using the eval method increases this module's compatibility with other modules massively as no module expects encrypted data :)
Comment #16
alexpottI think #9 might offer a way forward that removes the eval usage and is potentially very interesting.
Comment #17
ghost of drupal pastIn that case use priority PHP_INT_MAX.
Comment #18
nicxvan commentedIf you don't mind continuing to document here we'll likely want to clean this up and post it as a guide for advanced usage for any other contrib maintainers that have similar complex needs.
Comment #24
ptmkenny commentedComment #25
ptmkenny commented@nicxvan Yes, once we get this working, I'd be happy to write up a guide.
Comment #26
ptmkenny commentedComment #28
ptmkenny commentedComment #31
ptmkenny commentedThis MR now basically works; I am running my behat tests on my site locally with the MR on 11.1.x-dev and there seem to be no errors related to field_encrypt.
Suggested release notes/change record snippet:
Field Encrypt 4.0 no longer uses eval(). If you used Field Encrypt 3.x and your server did not allow the use of eval(), you had to define hook_entity_type_insert() and hook_entity_type_update() in a custom module to allow entities to be decrypted. This code is no longer necessary in 4.x and should be removed prior to upgrading.
@alexpott FieldEncryptUpdatePathTest is failing; do you have any idea why that might be?
I'm leaving this issue "Postponed" for now because I think we should commit the hook conversion first and then commit this ServiceProvider change separately instead of committing everything at once.
Comment #32
elkaro commentedI am using commit
ef79e5b425cofoop_hooks_workaround4And
drush updbreturns this error:No idea what is going on, but poking around through intuition and git bisect I found that
src/FieldEncryptServiceProvider.phpcauses the error, and deleting that file makes the error go away, although likely causes other problems because I imagine it is there for a reason.Comment #33
ptmkenny commentedComment #34
ptmkenny commentedComment #37
ptmkenny commented@elkaro If you delete src/FieldEncryptServiceProvider.php, field encryption and decryption will no longer work. It'd be great if you could provide more info about what is going wrong.
Comment #38
elkaro commented@ptmkenny unfortunately I don't know any more than I put in my comment. When I ran drush updb and src/FieldEncryptServiceProvider.php was present, I got the error, if I temporarily removed the file I could run drush updb successfully.
On Drupal 11.1.4.
Comment #39
ptmkenny commented@elkaro Thanks for the response. Did you have Field Encrypt enabled for any entities?
Comment #40
elkaro commented@ptmkenny Yes I am using it to encrypt some node and paragraph fields.
Comment #41
ptmkenny commented@elkaro Well, the good news is that the error "Call to a member function insert() on null" is the same error identified in the Unit tests, so we already have test coverage for this:
Now to fix the tests...
Comment #42
ptmkenny commentedOk, the problem is caused when $container->get('state') gets called in alter() during update:
Not sure how to fix this; will ask in Slack.
Comment #43
alexpottIt looks like we have to go one of two ways here. Either to somehow fix state in Drupal core so it can be accessing during an alter implementation. Or we need to stop using state.
If we go for not using state we need to do something like:
I think ideally state would work in this situation so might try to find sometime to fix this in core but I feel that we might have to do new table approach for now so that we can be compatible with 11.1 because any fix for state is not going to happen there.
Comment #44
alexpottUsing a separate key value collection is a good idea that might solve a lot of problems. We will need to change all the calls to state to use the new key value collection and we will need to provide an upgrade path.
Comment #45
ptmkenny commentedAnother option is the one outlined by nicxvan in #7; I started this long series of MRs with the approach described in #9 instead, because at the time it seemed cleaner to me, and it was more like the old approach (we would dynamically create hooks for each entity type). However, given the trouble I have had making it work, maybe it would be better to start over with #7 and see where that leads.
Comment #46
ptmkenny commentedOk, the keyValue approach seems to be the right one, as all tests are passing (except 11.2 phpstan, but I think that's due to deprecations-- 11.1 is completely passing).
Although I set this to "Needs review," there is currently no upgrade path (moving from state to keyValue) and we need one.
Comment #47
alexpottComment #48
alexpottThis is good to go now. We have test coverage and an update path. We should add some docs to the release notes about removing any manual implementations if you didn't use the eval() way of doing things in 3.x but this approach shows how OOP hooks are great and let's us do interesting things like this.
Comment #50
alexpott