Problem/Motivation
We're trying to deprecate some hooks in #2234479: Deprecate hook_test_* hooks in simpletest and #2460521: Deprecate hook_simpletest_alter()
It would be nice to be able to trigger E_USER_DEPRECATED
for these hooks when they are invoked.
Note that one is for hooks and the other is for _alter hooks.
There are also at least two deprecated alter hooks in core already, in rest.api.php
: hook_rest_type_uri_alter()
and hook_rest_relation_uri_alter()
.
Proposed resolution
Add invokeDeprecated()
, invokeAllDeprecated()
, and alterDeprecated()
to ModuleHandlerInterface
.
These will act as facades for invoke()
, invokeAll()
, and alter()
, which will behave normally, but will also check for implementations and trigger the deprecation error if there are any.
Remaining tasks
User interface changes
Adds explicit ways to deprecate hooks and alter hooks.
API changes
Adds methods to ModulerHandlerInterface.
Data model changes
Comment | File | Size | Author |
---|---|---|---|
#49 | interdiff.txt | 1011 bytes | Mile23 |
#49 | 2866779_49.patch | 12.33 KB | Mile23 |
#45 | interdiff.txt | 2.57 KB | Mile23 |
#45 | 2866779_45.patch | 12.3 KB | Mile23 |
#44 | interdiff.txt | 13.62 KB | Mile23 |
Comments
Comment #2
Mile23This patch illustrates the idea and is WIP. It does not handle alter hooks.
Comment #3
Mile23Comment #4
Mile23Comment #5
dawehnerWe could avoid changing the interface by having a module_handler.deprecated which is like a regular module handler but marks every function call as deprecated. Any thoughts about that?
Comment #6
Mile23Having a separate service means that you have to grab two different services if you invoke hooks that are both deprecated and non-deprecated. So for some services that might mean changing the service definition and constructor signature just to do deprecations.
Comment #7
joachim CreditAttribution: joachim commentedThis looks good, though I am wondering whether there is a legitimate way in the documentation standards to say 'see moduleInvokeAll() for the parameters' rather than repeat them here. I think this would improve clarity that the method signature is identical, and that it's a drop-in replacement.
Comment #8
Mile23Postponing on #2488860: Bring phpunit bridge into drupal and use it for unit tests and simpletest to handle Deprecation because there's currently no way to get the triggered error into a human-useful form.
Comment #9
dawehnerAgreed ... we should ensure we add test coverage for that, and well, for that we first need actual capabilities for that.
Comment #10
Mile23This issue has a plan now: #2870058: [policy, no patch] Document how to deprecate hooks
Comment #11
dawehnerHere is an alternative implementation using
hook_hook_info()
.Comment #12
Mile23It seems like we'd only want to trigger the error if there are implementations.
We'd definitely want a test case where hooks defined in hook_hook_info() and hooks defined in *.module both trigger errors that are found by the symfony phpunit bridge.
Comment #13
dawehnerI think you are absolutely right here :)
Comment #14
Mile23Leaving this postponed.
This patch makes alterDeprecated() work.
Note that there's a kernel test class. This test will be more meaningful after #2488860: Bring phpunit bridge into drupal and use it for unit tests and simpletest to handle Deprecation
But... Applying the patch from that issue, re-composer installing, and then running the test results in a fail. So it may be that kernel tests have some magic error handling that we haven't accounted for.
Comment #15
dawehnerFor better BC maybe add a new interface for that? To be honest I don't expect thought that anyone extends this particular class to be honest.
It is a bit weird that the first message is about the concrete function name and the second is just containing the module name. I think using the full function makes it easier to find the particular implementation.
Comment #16
Mile23module_handler
is a service, so it needs one interface that all consumers can use. Since we caninvoke()
etc onModuleHandlerInterface
, the deprecated invoke has to be on the same interface.Easiest way to code it. :-)
getImplementationInfo()
doesn't return function names, but I guess we can assemble them.Comment #17
Mile23Unpostponing since it looks like we've got some consensus in #2870058: [policy, no patch] Document how to deprecate hooks
Comment #18
dawehnerFair, we can add new methods in minor releases ...
Comment #20
Mile23Both error messages now show the method names instead of just the modules.
Moved the test to be a unit test, because kernel tests can't test deprecation errors at the moment.
Comment #21
Mile23Added a change notice: https://www.drupal.org/node/2881531
The change notice will need to be updated when the docs are updated, and when this issue is finalized.
Comment #23
Mile23Fixed CS errors. No idea about the segmentation fault in the last test.
Comment #24
dawehnerUbernitpick: Once the deprecation is before and once after the actual hook calls.
Given this loads code we should IMHO run the test in isolation/in its own separate process.
Comment #25
Mile23Argh. :-)
So yah, we can't test this yet.
Comment #26
catch@runTestsInSeparateProcesses
should work for that I think.Comment #27
dawehnerYeah totally, this is totally possible.
Comment #28
Mile23Hmm. I've been poking around the deprecation error problem with kernel and browser tests for #2870194: Ensure that process-isolated tests can use Symfony's PHPunit bridge to catch usages of deprecated code thinking that the reason the phpunit bridge doesn't work with them is because they're in isolated processes. That's why I was exasperated about this one.
Trying it now it seems to work locally, so yah.
Update: It passed locally because I had the 8.3.x dependencies set up in the repo.
Also, the other tests in core/tests/Drupal/Tests/Core/Extension have the same problem: They need to be isolated because they load modules.
Comment #30
Mile23Follow-up for the other adjacent tests: #2881788: Process-isolate Drupal\Tests\Core\Extension\ModuleHandlerTest
Comment #31
Mile23Zeroing in on the fact that
@runTestsInSeparateProcesses
kills our ability to test deprecations: #2884530: Mark process-isolated test with @expectedDeprecation as riskyComment #32
Mile23Changed tests to mocked unit tests so we can proceed without blockers.
Comment #33
Mile23Updated deprecation documentation: https://www.drupal.org/core/deprecation#how-hook
Updated change record: https://www.drupal.org/node/2881531
Re-running tests.
It would be really nice to get this in the 8.4.0 release so we can start deprecating stuff now.
Comment #34
dawehnerFrom my perspective this looks great!
I'm wondering whether we should put @see statements to the parallel methods && and then maybe get rid of the 1to1 copied documentation
Comment #36
Mile23Moving back to 8.4.x because it would be super-duper awesome to be able to fully deprecate the simpletest module before 8.5.0.
alterDeprecated()
makes sense to chop out because it's pretty long.invokeDeprecated()
andinvokeAllDeprecated()
are actually longer than the originals, explaining how the deprecation works.The implementation just has {@inheritdoc} which is right and good.
Added @see to all the new methods, pointing back to the non-deprecated versions.
Comment #37
dawehnerThank you! I think we have a solid plan now.
@Mile23
Do you think we need an actual module providing it to have some sort of complete test coverage?
Comment #38
Mile23@dawehner: No, we can't do that because we can't use
@expectedDeprecation
in process-isolated tests. See the interdiff in #32, and comments here: #2870194-22: Ensure that process-isolated tests can use Symfony's PHPunit bridge to catch usages of deprecated codeComment #39
dawehnerWell, we could write a test which ensures that the hooks are actually triggered ...
Comment #40
Mile23Like this? Because if we make an actual module and then invoke with deprecation, there will be a deprecation error which will fail the test. We can't prevent the deprecation error because
@expectedDeprecation
doesn't work for process-isolated tests, as pointed out above. It's the reason for the failing test in #28.So until we've fixed #2870194: Ensure that process-isolated tests can use Symfony's PHPunit bridge to catch usages of deprecated code we can't test this deprecation feature using kernel or functional tests. And that issue is blocked because phpunit-bridge is already a kludge. https://github.com/symfony/symfony/issues/23003
So the solution is to unit test it, which is the only way to test a deprecation with
@expectedDeprecation
.Comment #41
Mile23#2870194: Ensure that process-isolated tests can use Symfony's PHPunit bridge to catch usages of deprecated code is getting close...
Comment #42
Mile23Woot! #2870194: Ensure that process-isolated tests can use Symfony's PHPunit bridge to catch usages of deprecated code is in.
So here's a patch with a kernel test.
Comment #43
dawehnerIt would be kind of nice to say: Read more on hook_$hook() what you should do instead. As alternative maybe invokeDeprecated etc. could take an additional message parameter?
We could remove this comment now. Maybe though testing this using a unit test isn't a good idea in the first place. Any opinion about that?
Comment #44
Mile23Good idea adding specific deprecation messages.
Removed the unit test.
Added a test case for unimplemented hooks to make sure they don't trigger errors.
Edit: The unimplemented hooks test will need to be in a different class, since that one says @group legacy.
Comment #45
Mile23Re-arranged the tests for no false positives.
Comment #46
larowlannit: i'd use an array_map here, but that's just a personal preference
This looks ready to me, would be great to ship 8.5.0 with this.
Comment #48
larowlanUnable to allocate pool apcu issue again
Comment #49
Mile23Fixes nits from #46.
Comment #50
larowlanthanks
Comment #51
Wim LeersNice!
Comment #52
catchCommitted 5485720 and pushed to 8.5.x. Thanks!