Problem/Motivation
From the originating issue #1002080: [Policy, no patch] Normalize on usage of is_callable() instead of function_exists()
Instead of using function_exists() to verify if a callback exists, Drupal could use is_callable(); this would allow to use object and class methods as callbacks.
Steps to reproduce
N/A
Proposed resolution
Review instances of function_exists.
There are several options:
- If it is called on a string we know it's a function and we can address it when we convert the function itself.
- If it is called on a generated string from files other than .module (e.g. $module . '_install') we can convert that when we handle .install, .theme, .engine, .post_update files
- If it is accepting something like a form callback we can safely convert that to is_callable here.
- If it is a generated string inside .module files (e.g. $function = $entity->getEntityTypeId() . '_last_viewed';) address here or in follow up
- If it is something else (e.g. EntityResolverManager: if (function_exists($controller)) {) address here or in follow up.
HandlerBase #3539917: Deprecate access callback in HandlerBase -> already is defined in an array as access callback, function_exists only needs to be changed to CallbackResolver
form.inc #3539919: Convert batch callbacks to CallableResolver -> already is defined in an array key as batch redirect, function_exists only needs to be changed to CallbackResolver
CssOptimizerUnitTest #3539921: Clean up CSSOptimizer unit test. remove extra namespace.
EntityResolverManager #3539922: Deprecate EntityResolverManager procedural controllers deprecate this option - remove procedural callbacks from provider since only one provider option has a deprecation.
CommentManager #3536209: Deprecate magic ENTITY_TYPE_last_viewed functions -> Needs a way to define the class and method. This was deprecated and moved to history, which was removed from core.
ViewsUI #3536204: Deprecate magic cancel submit handlers in Views UI -> Needs a way to define the class and method.
Remaining tasks
Confirm evaluation
User interface changes
N/A
Introduced terminology
N/A
API changes
N/A
Data model changes
N/A
Release notes snippet
N/A
| Comment | File | Size | Author |
|---|---|---|---|
| #18 | functionExistsCallsLine.txt | 11.68 KB | nicxvan |
Issue fork drupal-3520416
Show commands
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
annmarysruthy commentedComment #3
annmarysruthy commentedComment #4
annmarysruthy commented@quietone Should we replace all existences of function_exists() with is_callable() ?
Comment #5
quietone commented@annmarysruthy, I think each usage needs to be examined. For example, the usages in tests should probably not change, In may turn out that there are no places where making this change is an improvement. I have not looked closely at all the instances.
Comment #7
annmarysruthy commentedChecked all occurrences of function_exists() and changed 6 files. In my opinion, Rest of the occurrences are better kept as function_exists() itself. Kindly review MR
Comment #8
annmarysruthy commentedComment #9
nicxvan commentedThis looks great! I have to review more closely, but this already covers the three I was reviewing.
Comment #10
andypostcurious to compare performance of both methods
Comment #11
nicxvan commentedI'd take another look at EntityResolverManager.
Comment #12
nicxvan commentedFound some more hooks to exclude.
Comment #13
nicxvan commentedFor 11.
Comment #14
nicxvan commentedOk I am going through this and here are the criteria
Comment #15
nicxvan commentedComment #16
alexpottI don't think we should be changing the string ones here. It's pointless and I think should be address in the issue that does the change.
Comment #17
nicxvan commentedI agree, that is the first bullet point, sorry if that wasn't clear.
Comment #18
nicxvan commentedOk I did the evaluation, it could use confirmation, I've attached it.
I ran this command to get a list of function_exists calls
grep -rn "function_exists" core > functionExistsCallsLine.txtI then removed comments and broke it into three categories:
is_fileHere is the list of actionable ones, I wasn't sure about the css test.
Category 1 Actionable
core/modules/views/src/Plugin/views/HandlerBase.php:572: if (isset($this->definition['access callback']) && function_exists($this->definition['access callback'])) {
core/includes/form.inc:955: if (($function = $batch['redirect_callback']) && function_exists($function)) {
core/modules/comment/src/CommentManager.php:210: if (function_exists($function)) {
core/modules/views_ui/src/ViewUI.php:341: $cancel_submit = function_exists($form_id . '_cancel') ? $form_id . '_cancel' : [$this, 'standardCancel'];
core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php:298:if (!function_exists('Drupal\Core\Asset\file_uri_scheme')) {
core/lib/Drupal/Core/Entity/EntityResolverManager.php:93: if (function_exists($controller)) {
Comment #19
nicxvan commentedI'm going to work on this.
Comment #20
nicxvan commentedComment #21
nicxvan commentedComment #22
nicxvan commentedComment #23
nicxvan commentedReducing scope here to just functions that have to be in a .module file.
Comment #24
nicxvan commentedComment #25
nicxvan commentedComment #28
nicxvan commentedI created a new MR removing the function_exists call in EntityManagerResolver so we can see what fails.
I accidentally pushed to the other branch so I force pushed that branch to reset it to the state annmarysruthy left it in.
Comment #29
mstrelan commented#3536209: Deprecate magic ENTITY_TYPE_last_viewed functions
#3536204: Deprecate magic cancel submit handlers in Views UI
We can remove that whole extra namespace at the bottom of the test, since #3034072: Move file uri/scheme functions from file.inc and FileSystem to StreamWrapperManager. Do you want another follow up or should it happen here?
I think the failing test explains this one. I think it can stay, or update to
is_callable.Comment #30
berdirMixed feelings on this. I think almost all registered callbacks should use CallableResolver, for example the batch stuff, because then we could actually use services. I'm also not sure why we even check if for example the redirect_callback. If that's incorrectly defined then it just silently ignores it. As a developer, I'd much prefer to just get an exception/error on that.
For me the question is whether it's really worth changing to is_callable(), only to then either deprecate the whole thing or having it change it again. If we're going to deprecate something, we could just do it straight away? And for batch I'd prefer a dedicated issue to make everything use CallableResolver.
> I think the failing test explains this one. I think it can stay, or update to is_callable.
Well, it's a unit test testing the code, it was most likely written to explicitly cover every line of code. It's existence isn't necessarily proof that it's actually needed. I'd try removing the unit test to see if there's anything else. Unlike others, this is an explicit fallback. It can't be an array, and at this point, it can't really be anything else but a function. So an is_callable() check there seems pointless, either we keep it or we remove it..
Comment #31
kim.pepperAgree with @berdir. Using
CallableResolvergives us more options and proper validation of callables.Comment #32
nicxvan commentedThank you everyone, that is exactly what I was looking for.
We should probably rename this since it's more about handling the last edge cases of functions required to be in .module files.
I'm ok with CallableResolver!
So in summary I think the form and handler we use CallableResolver.
In the test we just remove that namespace.
In the other cases we outright deprecate them in children issues.
This might need framework manager review.
Comment #33
nicxvan commentedI'm going to close @annmarysruthy's branch and hide it since we have a slightly different strategy now.
In my new branch I will use CallableResolver for form, and Handler.
I will look at updating the test and I will deprecate the function option for EntityResolver.
@berdir I not quite sure I follow this:
Did you mean something like this?
Comment #38
nicxvan commentedOk I added CallbackResolver to form.inc, removed the extra namespace and deprecated that path in EntityResolverManager.
I'm sure I'll need to clean up tests.
Is there anything special to consider for injecting CallbackResolver in HandlerBase? I see it's using getters and setters for almost everything else, I'm not sure if there is a reason for that.
Also need to handle deprecations being hit in EntityResolverManagerTest
Comment #39
nicxvan commentedComment #40
nicxvan commentedComment #41
nicxvan commentedComment #42
nicxvan commentedGoing to convert this to a meta and open an issue for each.
Comment #43
nicxvan commentedComment #44
nicxvan commentedComment #45
nicxvan commentedComment #46
nicxvan commentedComment #47
nicxvan commentedComment #48
quietone commented#3522207: [meta] Road to deprecating .module files seems like is should be a parent of this.
Comment #49
nicxvan commentedCouple more issues in needs review, and one other needs some deprecations in tests tracked down.
Comment #52
nicxvan commentedComment #53
nicxvan commentedThis is nearly there, just the one issue!
I know usually we close metas once there is only one issue remaining, but I'd like to keep this open a bit.