Problem/Motivation
See parent meta for motivations on introducing this component.
At the moment, in order to resolve callables with various DX features (such as service notation), this is either being implemented manually or using the controller_resolver service. The problem is this is a strange coupling, we shouldn't need a service that is primarily designed to deal with Request objects to resolve callbacks for completely unrelated subsystems.
Proposed resolution
A new service called "callback_resolver" that standardises the approach for resolving callbacks, decoupled from controllers.
Down the track: use the callback_resolver ControllerResolver in places where a callable is required from a "definition".
Remaining tasks
- #64
Make a decision on moving\Drupal\Core\Security\DoTrustedCallbackTraitto this class - #64
Make the test data return different values to ensure correct logic is called - #66
Use this in\Drupal\Core\Controller\ControllerResolveras an example
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|
Issue fork drupal-2982949
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:
- 2982949-introduce-callableresolver
changes, plain diff MR !338
Comments
Comment #2
sam152 commentedComment #3
sam152 commented@joachim suggested this be called
CallbackResolverin #2975503: allow FieldConfigInterface::setDefaultValueCallback() to accept a callback in service notation.Comment #4
sam152 commentedInitial pass at this.
Comment #5
sam152 commentedResponding to the comments from @joachim in #2982950: [meta] Standardize the approach for capturing and invoking callables across various subsystems.
Currently ControllerResolver automatically understands the difference between static notation and ones resolved using the class resolver, see the following (uncommitted) test case:
So I think we should do the same thing here and that two methods is not necessary. Once we support all the things that the current
ControllerResolverdoes, we can pull that logic out of that class and make it use this new service.Comment #6
sam152 commentedComment #7
sam152 commentedAnother thought, perhaps this should be
CallableResolver. When it comes to controllers for example, it's not technically a callback, but in all cases we want to be returning a callable that can be used for various purposes.Comment #8
sam152 commentedAdding support for callables with classes resolved by the class resolver. At this point, I think it'd be able to totally resolve the custom callables stuff built into
CallbackResolver.Comment #9
sam152 commentedMoving to "callable resolver", fixing some docs and exception messages.
Comment #10
sam152 commentedComment #11
sam152 commentedWas missing part of the service definition.
Comment #14
joachim commentedI would put bullet points here to explain exactly how a string is resolved into a callback, and in what order things are tried.
But doing that should probably wait until we've actually got the code stable.
Do we need this?
call_user_func_array() is notoriously slow, and this method would be stuck using it until we can use variadics, which is when we drop PHP 5 support, which is something like at least a year away.
Also, I wrote the following by mistake on the parent issue:
One of the problems we face in unifying this is that different places use 'ThisClass::thisMethod' to mean different things.
In FieldConfigInterface::setDefaultValueCallback(), a value of 'ThisClass::thisMethod' simply is a PHP callable, representing a static call to thisMethod() on the class ThisClass.
However, in the routing system, putting 'ThisClass::thisMethod' as the controller for a route will cause ControllerResolver to use the DI ClassResolver to instantiate ThisClass, and call thisMethod() on the instance:
So given that, I think our new CallbackResolver will need two public methods, one for each behaviour: resolveCallbackWithStatic(), and resolveCallbackWithInstantiation().
Then I'd have a single protected method resolveCallback(bool $instantiate_static_notation), where the parameter controls the behaviour.
The two public methods are then just thin wrappers that pass the parameter value, which means we get code re-use, but also we don't require developers to know what a boolean parameter means.
Comment #15
sam152 commented1. Good point, few better docs would be good here.
2. Hm, it would be a handy convenience method based on the usages I can see. Callables are often invoked as soon as they are resolved. We can always deprecate methods and add new ones there is something new we can utilise in php7.
3. I think we're already supporting both of these:
Comment #16
joachim commented> Hm, it would be a handy convenience method based on the usages I can see. Callables are often invoked as soon as they are resolved.
True, but without variadics, I don't think it's that nice DX, as you have to wrap up your parameters into an array:
With variadics, it becomes:
There's the performance issue too, so really think we should leave that to a follow-up to be postponed until we can use variadics.
> 3. I think we're already supporting both of these:
I can't see that in the code.
And those two cases in the test look the same to me:
> + static::class . '::staticMethod',
The code will receive a string that looks like \MyNamespace\MyClass::staticMethod
> + '\Drupal\Tests\Core\Utility\MockContainerInjection::getResult',
Same here.
And the service will instatiate the class with the class resolver service and call the method on the instantiated object.
But for baseFieldDefinitions() we actually want a static call. The callable string '\MyNamespace\MyClass::staticMethod' should be called as \MyNamespace\MyClass::staticMethod().
Comment #17
sam152 commentedWe could always use func_get_args to support
($callable, $param1, $param2)syntax in the meantime, then file a follow-up for variadics, but I'm not too opinionated on this. I think worrying about the performance too much would be a bit premature.Ah, right you are! We should test that the static method is definitely being called in static context. I'd like to investigate doing that automatically, a flag for static methods would be quite ugly, in a lot of cases if we're using the resolver in a context where the callable has been supplied by a user, maybe in the context for a
BaseFieldDefinition::setDefaultValueCallback, we might not know if it's static or not and we don't necessarily want to ask the user either.So remaining tasks are:
Comment #18
sam152 commentedUpdating to reflect #17. Another reason to avoid the flag is: if we are aware that our input is a static method in a particular notation, we can always avoid the callable resolver altogether and invoke it straight away.
Comment #20
joachim commentedJust a thought that occurs to me as I write tests for #2975503: allow FieldConfigInterface::setDefaultValueCallback() to accept a callback in service notation -- tests here should check that the service method default callback is not called statically. Stack overflow says the way to do this is:
Comment #21
sam152 commentedGood point. Adding that coverage now. This further validates that the following test cases are invoked non-statically:
Comment #22
joachim commentedI don't think we should do it like this. Reflection is expensive, and we want the callable resolver to be something fast, that other components can use without worrying about performance.
I still think we should handle this the way I suggested, with two methods, as I suggested previously:
> So given that, I think our new CallbackResolver will need two public methods, one for each behaviour: resolveCallbackWithStatic(), and resolveCallbackWithInstantiation().
Users of this service will know whether they want 'MyClass::myMethod' to mean an instantiation, or a static call. It'll be up to them to document what they mean in their own APIs.
Is there anywhere in core that does this? If not, I would say this is YAGNI, and we should remove it until we do find we need it.
I still think this is surplus to what we need here, and until we can use variadics, it has a performance cost.
I think for legibility, it would be better for all the callables in the test to be explicit, so I would add another class for these methods to be on.
Comment #23
sam152 commentedI think I could agree with everything except
resolveCallbackWithStaticandresolveCallbackWithInstantiation.If we take #2975503: allow FieldConfigInterface::setDefaultValueCallback() to accept a callback in service notation as an example, in that scenario if we're invoking a callable, are you saying fields can only accept either static or instantiated callbacks but not both? And it's up to the field system to choose one of those and document which one it supports? IMO that defeats the whole purpose. I think reflection is slow, but in super relative terms and relative to the cost of actually invoking a callable I think it's quite minor.
To be honest, I think unless this gets integrated at a very low level where it's being called thousands of times a page load, it wont matter. And if at some point that was the case, we could look at introducing such methods. Until then, I think it's an acceptable cost to bear, given the utility it provides.
Comment #24
joachim commented> in that scenario if we're invoking a callable, are you saying fields can only accept either static or instantiated callbacks but not both?
Yup. I'm saying exactly that :)
The Field system currently treats a callable for FieldConfigInterface::setDefaultValueCallback() of the form 'MyClass::myMethod' as a static call. There is no way to get an object instantiated.
Meanwhile, the routing system treats a controller declared in mymodule.routing.yml as 'MyClass::myMethod' as an object to be instantiated with \Drupal\Core\DependencyInjection\ClassResolver.
In the field case, I imagine it was designed like this because you typically want the method for the default value for a base field to be on entity class, but when the Field system calls that method, it does not have an entity to hand. So it must be a static.
For the routing case, my guess is that it's following the convention from Symfony.
So we have two systems that treat the same syntax differently, and I think the simplest thing is to let them just say to the CallbackResolver what they want to be done with it.
Comment #25
sam152 commentedI'm not really a fan of that from a DX perspective and it goes against the goal of standardisation. As someone providing an API with callables, I don't want to have to make and document this decision and as a user of an API providing callables I don't want to have to be aware of different contexts. I'm not really convinced of the performance argument, given all of the examples so far are not examples of callables being invoked thousands of times per request.
If you'd like to pursue it, please feel free. I think #21 is more or less what I had in mind, so I'm not interested in taking it further than that.
Comment #26
joachim commented> I'm not really a fan of that from a DX perspective and it goes against the goal of standardisation.
I agree that having the string 'MyClass::myMethod' not result in the same kind of call in different systems is not very good DX, but that situation currently exists. This issue is not adding to or changing this situation.
This proposed CallableResolver is a very low-level API. Most developers shouldn't encounter it. It's something for other systems to use, that then have to declare and document their own APIs.
And the Field system and routing systems *already* document (or should!) what it means when the string 'MyClass::myMethod' is given to their APIs.
And in fact, because we can't break BC, we can't go changing what the string 'MyClass::myMethod' means to either of those systems. For Field system, it must remain that it means a static call. For routing, it must remain that it means an instantiation. So we have to deal with that difference.
This issue is about sliding a new piece of common code at the bottom of existing systems, in such a way that the existing systems change to use our new code, but consumers of the existing systems don't notice or have to care about the change.
Because the existing systems have different ideas about what things mean, we need to account for that.
Comment #27
joachim commentedI think the root cause of why we are disagreeing on how to write this service is that we're approaching it from very different angles.
I think this service should be a consolidation and refactoring of code that's currently duplicated in different parts of core.
It seems to me that you're trying to write a utility class that covers all cases.
I think that approach is over-stretching, and as we can see from the use cases we have to satisfy, it's over-complicating.
Comment #28
sam152 commentedI think that's a fair analysis. Not criticising your efforts in any way, it's probably also a positive step, it's just not one I'm interested in working on. Not sure if those changes would be appropriate for this issue or a new one.
I think for the systems mentioned, it would be great if we had the same set of tools. I think the performance and BC risks are acceptable, given they can be evaluated properly when integrated with the service and static methods will continue to be called as static. For low level code, we would always have the chance to integrate callables differently. I do see this issue as a tested utility that will allow developers to create richer APIs in the future.
Maybe a review from a framework manager would help focus this discussion?
There could also be an acceptable middle ground that accounts for both use cases. We could use bitwise operators to include or exclude the types of callables that would be resolved. Maybe something like the following:
public function getCallableFromDefinition($definition, $included_formats = CallableResolverInterface::ALL);And a series of constants:
CallableResolverInterface::STATICCallableResolverInterface::INSTANTIATEDCallableResolverInterface::SERVICE_NOTATIONCallableResolverInterface::INVOKABLEThen bitwise could be used to indicate which types are checked and used:
$resolver->getCallableFromDefinition($definition, CallableResolverInterface::STATIC | CallableResolverInterface::SERVICE_NOTATION);That would allow more control in the hands of the user. But I would still hope most systems would choose to use all available methods.
Comment #29
sam152 commentedI think some outside opinions would be really helpful.
Comment #30
sam152 commented@joachim If you're going to be at Drupal Europe, maybe we could catch up and come to a resolution on this issue?
Comment #31
joachim commentedYup, good idea!
Comment #32
sam152 commentedHere is a blackfire report comparing:
\Drupal\Core\Field\BaseFieldDefinition::getDefaultValuevs what's already in core.Invoking it once failed to register on the report.
https://blackfire.io/profiles/compare/8fbaf49c-d94c-4c00-bece-1b254bf8fb...
So the way I'm interpreting the results is, on a form with 1000 fields with default values, about a 5% cost to the total page would be added. Or 0.005% change for the typical use case of one default author.
Comment #33
sam152 commentedNR based on discussions with @joachim yesterday, we agreed the only blocker to accepting an approach which covered the use case of all callables was the speed of reflection.
Comment #34
sam152 commented@joachim any chance of another review of this? :)
Comment #35
longwaveFWIW I have been tracking this issue for a while and would like to see something like this implemented, and I tend to agree with @Sam152 on the point that this is supposed to be a DX improvement, so we shouldn't make the DX overcomplicated by adding multiple methods or flags if at all possible. I understand @joachim has performance concerns but I think if we are to take that into account we need a concrete example where this will be a problem, #32 is quite a contrived example but seems to imply there won't be a real world performance hit anywhere? If it turns out there is a critical path that is affected, then we are also free just not to use this new method.
Comment #36
sam152 commentedThanks for following up on this @longwave, much appreciated. @joachim and I had a very similar conversation in Drupal Europe to that same effect.
The go-to example was integrating this into the renderer. Since the critical path heavily depends on it, we could make the call at that point in time (with profiling or otherwise) if it was an appropriate use case or not.
Comment #37
sam152 commentedComment #38
larowlandoesn't php enforce this?
Comment #39
sam152 commentedLooks like it throws a deprecated notice in 7 and a warning in 5: https://3v4l.org/MYd7M, however I don't think it failed our tests.
Comment #41
alexpottI think this makes sense +1 to the idea.
As 8.8.x is going to PHP7 (not sure of the minimum version yet but they all support this) let's use a variadic argument here...
Also the interface documentation can be updated to use variadic documentation.
Comment #42
joachim commented> The reflection kinda bothers me - can we test early for callability and avoid reflection?
The reason the reflection is in the patch is that in Drupal there is an inconsistency in what we mean by some kinds of callable strings.
Specifically, the string 'MySpace\MyClass::myMethod' does not always mean the same thing.
- In FieldConfigInterface::setDefaultValueCallback(), a value of 'MySpace\MyClass::myMethod' simply is a PHP callable, representing a static call.
- In the routing system, putting 'MySpace\MyClass::myMethod' as the controller for a route will cause ControllerResolver to use the DI ClassResolver to instantiate MySpace\MyClass, and call thisMethod() on the instance.
So the problem is how the new service can handle both techniques.
The options are basically:
1. Provide two methods on the service.
2. Provide a 2nd optional parameter that can specify exactly which kinds of callable techniques are allowed
3. Use reflection.
In light of the more recent issues about callable security, option 2 might be the best, as it could also allow users of the service to enforce their security rules.
Comment #43
sam152 commentedThanks for the review!
#41.1: Sounds like a great idea.
#41.2: I usually default to creating an interface for things that are technically swappable, but in this case I agree that a single implementation makes sense here.
#41.3: Unfortunately, we have to test for static-ness before attempting to pass the class off to
$this->classResolver->getInstanceFromDefinition, since a static method doesn't need an instance and there is no way of guaranteeing that the factory will actually be able to create an instance. #22 to #32 discuss options in some detail. There is a test class in the patch which exposes these semantics:#42: The signature of
TrustedCallbackInterface::trustedCallbacksin #2966327: Limit what can be called by a callback in render arrays to reduce the risk of RCE is static, so the same security could be applied for both static and non-static calls.Comment #44
sam152 commentedUpdating patch.
Comment #45
kim.pepperDoes the order of resolution have an impact on performance? Ie. is_callable() could come first? It all looks pretty simple, but thought I'd ask the question.
Can we have an interface? I know this has been discussed ad nauseam, but if we are creating a new service definition, then it's a public interface right?
Can we justify its usefulness by including real usage of this by replacing it in core somewhere?
Comment #46
sam152 commentedHey @kim.pepper, thanks for the review.
Yeah, unfortunately
is_callabledoesn't delineate between methods that can actually be called without any instantiation and methods which require a instance via a factory first. Here is a simple example@alexpott was in favour of no interface for this class in #41.
Re: usages, the child issues and summary of #2982950: [meta] Standardize the approach for capturing and invoking callables across various subsystems are gathering some of the use-cases. I'd be fine to add one of em in here, but was just considering scope etc.
Comment #47
joachim commentedI've not read #2966327: Limit what can be called by a callback in render arrays to reduce the risk of RCE in detail, but it sounds like the requirement there is to fine-control which types of callable are allowed.
Given that, should we revisit the plan outlined in #28, where @Sam152 proposed we allow the users of this service to specify precisely which type of callable are ok?
Comment #48
dsnopek#2966327: Limit what can be called by a callback in render arrays to reduce the risk of RCE is specifically about limiting the callbacks used in render arrays to functions that were written to be render array callbacks. This is important in that context because there are places where a user can enter Twig templates in the UI, where it's possible to build and render render arrays, ie. we're dealing with callbacks from user input.
This issue is way more low-level and seems like it's mainly about situations where only a developer who can edit code files can setup callbacks. So, I'm not sure this issue needs to dig into limiting callbacks that were setup in user input - the validation of the callbacks can be done before calling the APIs built in this issue, which probably makes sense, given that getting callbacks from user input is an edge case that hopefully won't come up very often. :-)
Comment #49
joachim commentedI've just noticed in passing that Drupal\Core\Render\Renderer uses ControllerResolver::getControllerFromDefinition() to resolve callbacks in render arrays, which doesn't look right!
Comment #50
sam152 commentedI can't see a reason why
::getCallableFromDefinitionintroduced in this issue and::doTrustedCallbackin the other issue wouldn't be completely compatible. The former resolves various "Drupal" callable formats into a PHP-style callables and the latter is able to verify all the PHP-style callable formats. So in theory you could easily resolve some callable which required a Drupal factory and then also verify it was a trusted callback.We could also expand
CallableResolver::invokeFromDefinitionto support verifying "trusted callbacks", however we have freedom to explore that with additional BC argument defaults, so I don't really see either issue as blocking each other?I don't think there was any other actionable feedback as far as I could see?
Comment #51
joachim commentedComing back to this issue as I'm working with options fields, and finding that the 'allowed_values_function' can't use service notation...
Just some nitpicks:
The catch shouldn't be coddled.
Is this exception message accurate? Are there other cases in which is_callable() would be false here?
I think we should check for service notation before we treat the $definition as either a callable or a class name.
Comment #52
joachim commentedHere's a patch with the changes from my review in #51 and a few other tweaks too.
I've changed the first line of the class docs, and also made it clearer in the class docs code example that you can use a service name with a '.' in it because I think that's useful to see.
Comment #58
fathershawnCreated an issue fork with #52 applied to investigate the test failure
Comment #60
fathershawnUpdated test class for deprecations per https://www.drupal.org/node/3056869 and https://www.drupal.org/node/3114724
Updated the exception message to match the message now returned
Comment #61
fathershawnTests pass for me locally using:
Investigating the failures
Comment #62
joachim commentedTests pass.
Patch looks good!
Comment #63
catchLet's try to add the return type hint.
Also wondering if the order of resolution is the most efficient we can do - i.e. could we move is_callable() checks in front of things checking exception and possibly hitting exceptions.
Also can't see immediately what the next step for this is, do we need to open up follow-ups under #2982950: [meta] Standardize the approach for capturing and invoking callables across various subsystems to start using this?
Comment #64
fathershawnAnd should we move the work from
\Drupal\Core\Security\DoTrustedCallbackTraitinto this class? In the course of working on #2966711: Limit what can be called by a callback in form arrays I've needed to add that Trait to FormBuilder and FormValidator, so we are now using the trait in 3 places.Comment #65
joachim commented> Also wondering if the order of resolution is the most efficient we can do - i.e. could we move is_callable() checks in front of things checking exception and possibly hitting exceptions.
Yes, definitely! I could have sworn we have all the string manipulations and is_callable() being done before wading into reflection.
FWIW I'm still not keen on the use of reflection here.
> And should we move the work from \Drupal\Core\Security\DoTrustedCallbackTrait into this class?
Yes, that's maybe a good idea.
Though I am torn between saying it should be done in a follow-up, and saying we should do it here to make sure the API is consistent.
Something like add a matching pair of methods getTrustedCallableFromDefinition() and invokeTrustedCallableFromDefinition()?
Comment #66
tim.plunkettI agree we should update DoTrustedCallbackTrait within this issue. Updating other "end-user" calls within core is one thing, but that's pretty low-level on it's own.
Also I'm not 100% clear on what the future interaction between this and ControllerResolver will be. It'd be good to make that change here too, IMO.
Hiding the patches since this issue is using MRs now.
Comment #70
andypostBtw PHP 8.2 will need it as voting mostly ++ https://wiki.php.net/rfc/partially-supported-callables-expand-deprecatio...
Comment #72
kim.pepperThis would be useful for #3353583: Allow validators passed to file_validate to use callable syntax
Comment #73
kim.pepperI made quite a few changes to address some of the feedback.
is_callable()check to the top for quick exit.invokeFromDefinition()methodOne side effect is that the error messages are a bit more generic without the extra service container lookups. Not sure whether that is a worthwhile trade off?
Comment #74
kim.pepperI think invoking the callbacks should be out of scope. Having a look at how we use them, they are pretty simple wrappers around DoTrustedCallbackTrait that are specific to Renderer etc. I think we want to keep this more generic.
Comment #75
kim.pepperUpdating IS with remaining tasks.
Comment #76
joachim commentedIt's been ages since I looked at this.
Was there a resolution to the problem of how to deal with the 'Class::method' syntax, where different systems currently handle that in different ways?
Comment #77
kim.pepperPretty sure its just
::is for static or instance method. The class resolver takes care of creating new instances for us.Comment #78
joachim commented> Pretty sure its just :: is for static or instance method.
That's the thing though -- in some systems, Class::method means a static call, and in others, it means instantiate the class and call $object->method.
Comment #79
andypostAdded more callables to test and rebased
Comment #80
kim.pepperRe: #78 Not sure it matters? Calling
is_callable()on the string'\Drupal\Tests\Core\Utility\NoInstantiationMockStaticCallable::staticMethod'returns true if it's a static method. If not, it drops down to where we instantiate an object.Comment #81
sam152 commentedPre PHP 8 is_callable would return true for public methods that required instantiation, but this changed: https://3v4l.org/d93fd - created this snippet 24h ago because I was similarly confused 😅
Hence the reflection is no longer required and false from is_callable can accurately signal the need for instantiation or the container.
Comment #82
kim.pepperChanged ControllerResolver to use CallableResolver and added a CR.
Comment #83
kim.pepperComment #84
kim.pepperWhew! 😅
Comment #85
kim.pepperSo this looks like a genuine fail for
Drupal\Tests\ckeditor5\FunctionalJavascript\MediaTest.When embedding a media item into a ckeditor5 field, I get the following error in logs:
\Drupal\media\Controller\MediaFilterController::formatUsesMediaEmbedFilter()does existIs there something we are missing to handle static methods in:
?
Comment #86
kim.pepperConfirmed! Added a check for a string static method callable with "::" syntax.
Comment #87
kim.pepperHere's a test-only patch for the ArgumentsResolver to show that callables that are static methods in string "::" format aren't currently supported.
Comment #88
joachim commented> Pre PHP 8 is_callable would return true for public methods that required instantiation, but this changed: https://3v4l.org/d93fd - created this snippet 24h ago because I was similarly confused 😅
Oh that's NICE!
It works for this too:
Comment #90
joachim commentedThe change in PHP 8 is documented here BTW: https://www.php.net/manual/en/migration80.incompatible.php
Comment #91
kim.pepperTests passed so needs review.
Comment #92
larowlanLeft some comments on the MR - looking great - some solid test coverage too
Re #64 I don't think moving the trait into this service is needed. Traits should be reused, so I don't see the harm in there being N classes that use it.
However I do think we should be using this in
\Drupal\Core\Security\DoTrustedCallbackTrait::doTrustedCallbackI think to enable that we'd need a $callableResolver property in that trait and ::getCallableResolver method that first checked the property, and if it wasn't set reached out to the \Drupal singleton. That way classes using the trait can use DI to the property eventually, but if they don't, the class will be fetched from the container. We'd have to update the usages of the trait in core to set that property.
Comment #93
andypostAddressed mostly all feedback in MR, leaving NW for #92 and
- https://git.drupalcode.org/project/drupal/-/merge_requests/338#note_166660
- https://git.drupalcode.org/project/drupal/-/merge_requests/338#note_166667
Comment #94
kim.pepperAll feedback has been addressed or at least commented on.
Comment #95
kim.pepperShould we split static methods in strings '::' into arrays first? That we we can do the ArgumentResolver change in a follow up.
Comment #96
joachim commentedAh, the change to ArgumentResolver is so that it can handle getting the method reflector for a method given in 'Class::method' form? That makes sense now :)
I'd keep it in.
Comment #97
andypostRe #95 there's interesting numbers - #3274867-21: Add TrustedCallback attribute
TL'DR callable as array faster ~50% vs string
PS only first-class callable faster, see #3259716: Replace usages of static::class . '::methodName' to first-class callable syntax static::method(...)
Comment #98
kim.pepper#97 are you saying we should split by "::" first up?
Comment #99
kim.pepper@andypost I'm not sure how much of a micro-optimisation that is? Currently static methods in "::" string format will be returned by the first line:
Comment #100
andypost@kim.pepper no need to change it as PHP internals will do that on call, I see no reason in changing callable as the most performant case is first-class callable but it's just preemptive optimization ATM
Looks ready to go
Comment #101
dieterholvoet commentedI noticed service notation without specifying a method name (with an
__invokemethod) is missing from the documentation/tests. Is this supported yet? If not, do you think it should be supported? It seems potentially useful to me.Comment #102
larowlanYes I think we should support #101
Comment #103
andypostAdded test and docs for service with
__invoke()PS: rebased and merge commits are gone, /cc @kim.pepper
Comment #104
smustgrave commentedSeems this was already previously reviewed.
Point #101 appears to have been addressed so remarking this for ya.
Comment #106
larowlanThere's an unresolved comment on the MR about whether we should be using this in a few places
Can we get follow up issues created for that?
Comment #107
kim.pepperCreated follow up issues:
Comment #109
larowlanNeeds reroll
Comment #110
kim.pepperRebased on 11.x
Comment #111
kim.pepperHiding patch as it makes it look like the MR is failing when its not.
Comment #112
andypostBack to RTBC, thanks
Comment #113
andypostThere's some measurements of different types of callables https://gist.github.com/donquixote/85efcca90056111e967dd14cb1f9de9c
Which means we should try re-use a callable as possible
Comment #114
andypostComment #115
kim.pepperThe last MR comment can be resolved.
Comment #116
larowlanIssue credits
Comment #118
larowlanCommitted to 11.x and published the change record.
The child issues are now good to go.
Comment #119
larowlanI also added an extra CR announcing the new feature - https://www.drupal.org/node/3368504 please review and amend if appropriate
Comment #120
fathershawnGrateful and excited to see this committed. I'll rebase the work I started on #2966711: Limit what can be called by a callback in form arrays onto 11.x-dev and update it in light of this work.