rfc:consistent_callables
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
rfc:consistent_callables [2015/09/30 13:37] – clarified __call and __callStatic danack | rfc:consistent_callables [2018/01/22 00:33] – danack | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Consistent Callables ====== | ====== PHP RFC: Consistent Callables ====== | ||
- | * Version: 0.1 | + | * Version: 0.9 |
- | * Date: 2015-09-28 | + | * Date: 2017-05-28 |
* Author: Dan Ackroyd | * Author: Dan Ackroyd | ||
- | * Status: | + | * Status: |
* First Published at: https:// | * First Published at: https:// | ||
Line 215: | Line 215: | ||
The following would be the complete list of valid values for the callable type: | The following would be the complete list of valid values for the callable type: | ||
- | i. A string that is the name of a function. | + | - A string that is the name of a function. |
+ | - An array consisting of two elements; a string at index 0 which is a valid fully qualified class name, and a string at index 1 which must meet the conditions: | ||
+ | * either be the name of a public static function of the class or the class must have a magic %%__callStatic%% method. | ||
+ | * the name must not be that of an instance method. | ||
+ | - An array consisting of two elements; an object at index 0, and a string at index 1 where either the string is the name of a public method of the object, or the object has a magic %%__call%% method. | ||
+ | - An instance of a class (an object) where the class has a public __invoke() method. | ||
+ | - Closures, which includes anonymous functions. | ||
- | ii. An array consisting of two elements; a string at index 0 which is the first string is a valid class name, and a string at index 1, which is either the name of a public static function of the class, or the class has a magic __callStatic method. | ||
- | iii. An array consisting of two elements; an object at index 0, and a string at index 1 where either the string is the name of a public method of the object, or the object has a magic __call method. | + | ==== Note - removal of colon separated string |
- | + | ||
- | iv. An instance of a class (an object) where the class has a public __invoke() method. | + | |
- | + | ||
- | v. Closures, which includes anonymous functions. | + | |
- | + | ||
- | + | ||
- | === Note - removal of colon separated string === | + | |
This removes the ability to define a callable as a single string composing a class-name and a method name separated by double-colons. The reasons for this are that: | This removes the ability to define a callable as a single string composing a class-name and a method name separated by double-colons. The reasons for this are that: | ||
- | * It is duplication of ii. The duplication of code is not just in PHP core, but for all userland code and libraries that analyze callables must duplicate the handling. | + | |
- | * For things like routing libraries it is useful for users to be able to specify not a valid callable, but instead a class that needs to be instantiated and the method that should be called on the instantiated object. Having ' | + | |
- | * It was introduced without much discussion to address a problem in the SPL iterator. | + | |
https:// | https:// | ||
- | * It is easier (in the sense of fewer CPU operations) to validate [' | + | |
- | === Note - Does not affect calling private/ | + | ==== Note - Does not affect calling private/ |
While they would no longer pass the type checker for the callable type, private and protected methods could still be executed through call_user_func and direct invocation. | While they would no longer pass the type checker for the callable type, private and protected methods could still be executed through call_user_func and direct invocation. | ||
Line 260: | Line 258: | ||
</ | </ | ||
+ | In this example, although `$fn` is not a callable that can be passed around to arbitrary scopes, it is valid to call it in the scope that it's in. call_user_func and $fn() will continue to check whether the variable passed in is callable in the current scope. i.e. with is_callable($fn, | ||
- | ==== Add Scope Resolution Operator for self and parent ==== | ||
- | To allow users to resolve class-names in the compile stage of running a PHP script, the scope resolution operator will support `self` and `parent`. | ||
- | * self::class - represents the current class name. Gives a compilation error if used outside | + | ==== The strings 'self', ' |
- | * parent::class - represent | + | Currently in PHP a callable can be defined using one of these words in place of a classname in a colon separated string like "self::methodName" |
- | Question are there any other cases where it should be allowed or forbidden? | + | By replacing the run time evaluation of these with the compile time scope resolution, the variable meaning of the values is removed and replaced with a consistent meaning. |
- | <code php> | + | To be clear, self::class, parent:: |
- | class Foo { | + | |
- | function getCallback() { | ||
- | return [self:: | ||
- | } | ||
- | public static function fooCallback() { | + | ==== Add a is_callable_type() function ==== |
- | + | ||
- | } | + | |
- | } | + | |
- | class SubFoo extends Foo { | + | A previous version of the RFC proposed modifying the parameters and meaning of the is_callable() |
- | | + | |
- | return [parent:: | + | |
- | } | + | |
- | } | + | |
- | $foo = new Foo(); | + | To be clear the meaning of the two functions will be: |
- | $subFoo = new SubFoo(); | + | |
- | $fn1 = $foo-> | + | |
- | $fn2 = $subFoo-> | + | |
- | //$fn1 and $fn2 will callables that have the same value | + | is_callable() - returns true if a the first parameter is callable in the current scope. |
- | </ | + | is_callable_type(mixed $var) : bool - returns true if the parameter can be passed as a callable type, and is callable in any scope, otherwise returns false. |
+ | <code php> | ||
+ | class Foo { | ||
+ | |||
+ | private function bar() {} | ||
+ | |||
+ | public function test($param) { | ||
+ | var_dump(is_callable($param)); | ||
+ | var_dump(is_callable_type($param)); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | $foo = new Foo(); | ||
+ | $param = [$foo, ' | ||
+ | var_dump(is_callable($param)); | ||
+ | $foo-> | ||
- | ==== The strings ' | + | output will be: |
- | Currently in PHP a callable can be defined using one of these keyword in place of a classname in a colon separated string like " | + | false // as the private method cannot |
- | + | true // as the private method can be called from within | |
- | By replacing | + | false // as the private method cannot be passed as a parameter |
+ | </ | ||
- | ## Instance methods are no longer reported as callable for class names: | + | ==== Instance methods are no longer reported as callable for class names ==== |
<code php> | <code php> | ||
Line 332: | Line 331: | ||
</ | </ | ||
- | ==== Private and protected functions no longer report as callable for is_callable ==== | ||
- | As they are not callable from all scopes, private and protected functions will not be valid methods for the is_callable | + | ==== Any additional |
- | It is currently possible using the reflection methods | + | Any other errors in is_callable() will be fixed so that if is_callable($fn) returns true, trying |
<code php> | <code php> | ||
- | class Foo { | + | if (is_callable($fn) === true) { |
- | + | $fn(); | |
- | //This generates a closure to the private method with ugly syntax. | + | |
- | public function getCallbackWithCurrentReflection() { | + | // given a zero argument, both of these will be guaranteed |
- | $reflection = new ReflectionMethod($this, ' | + | |
- | | + | |
- | + | ||
- | return $callback; | + | |
- | } | + | |
- | | + | |
- | //This will generate a closure | + | |
- | //RFC passes | + | |
- | public function getCallbackWithCallableRFC() { | + | |
- | $callback = callable($this, | + | |
- | + | ||
- | return $callback; | + | |
- | } | + | |
- | + | ||
- | private function somePrivateMethod() { | + | |
- | echo "This is private."; | + | |
- | } | + | |
} | } | ||
- | |||
- | $foo = new Foo(); | ||
- | $callback = $foo-> | ||
- | $callback(); | ||
</ | </ | ||
- | |||
- | This will allow a class to return a private method bound to an instance as a callback, without having to have the private method be exposed to the public API of the class. | ||
- | |||
- | |||
- | ==== is_callable function change ==== | ||
- | |||
- | Currently the function is_callable has the signature: | ||
- | |||
- | <code php> | ||
- | bool is_callable ( callable $name [, bool $syntax_only = false [, string & | ||
- | </ | ||
- | |||
- | which allows users to do this: | ||
- | |||
- | <code php> | ||
- | class Foo { | ||
- | function bar() { | ||
- | |||
- | } | ||
- | } | ||
- | |||
- | |||
- | $obj = new Foo(); | ||
- | is_callable([$obj, | ||
- | echo $name; //output is Foo::bar | ||
- | </ | ||
- | |||
- | The signature will be changed to be: | ||
- | |||
- | <code php> | ||
- | bool is_callable ( callable $name [, bool $syntax_only = false [, bool $current_scope = false]]) | ||
- | </ | ||
- | |||
- | The `$current_scope` flag will allow users to test whether a parameter is actually invokable in the current scope, even if it is not a parameter that is universally callable. e.g. for private functions. | ||
- | |||
- | <code php> | ||
- | class Foo { | ||
- | |||
- | private function bar() {} | ||
- | |||
- | public function test($param) { | ||
- | var_dump(is_callable($param)); | ||
- | $syntaxOnly = false; | ||
- | var_dump(is_callable($param, | ||
- | } | ||
- | } | ||
- | |||
- | $foo = new Foo(); | ||
- | $param = [$foo, ' | ||
- | |||
- | $foo-> | ||
- | //output will be: | ||
- | //false | ||
- | //true | ||
- | |||
- | </ | ||
- | |||
- | |||
- | To allow detection of code that uses the 3rd parameter to indicate the callable_name should be set, in the last PHP 7.x release we can deprecate (with a warning notice) that using the 3rd parameter is going to be removed in PHP 8. | ||
Line 436: | Line 354: | ||
The various things that need to be done to implement this RFC do not need to be all in the same release. There are advantages to having the changes implemented in separate versions. Below is this list of all the changes needed and the target version for them. | The various things that need to be done to implement this RFC do not need to be all in the same release. There are advantages to having the changes implemented in separate versions. Below is this list of all the changes needed and the target version for them. | ||
- | ==== Add Scope Resolution Operator for self and parent - 7.1==== | + | ==== Soft-deprecate colon separated string callables - 7.3 ==== |
- | + | ||
- | This is a useful thing to have (in a small set of circumstances) and there is no reason not to introduce it sooner rather than late. It will allow people to start migrating any code that currently uses " | + | |
- | + | ||
- | + | ||
- | ==== Soft-deprecate colon separated string callables - 7.1 ==== | + | |
Soft-deprecate colon separated string callables (i.e. things like " | Soft-deprecate colon separated string callables (i.e. things like " | ||
Line 459: | Line 372: | ||
==== Remove support for " | ==== Remove support for " | ||
- | Although this is covered by " | + | Although this is covered by " |
==== Change behaviour of is_callable - 8 ==== | ==== Change behaviour of is_callable - 8 ==== | ||
Line 481: | Line 394: | ||
===== BC breaks ===== | ===== BC breaks ===== | ||
- | All of the BC breaks are targetted | + | All of the BC breaks are targeted |
1. Any calls to is_callable that use the `callable_name` parameter would at least need to be wrapped in a version check against the PHP_MAJOR_VERSION number. I believe the amount of code that uses this parameter is minimal. | 1. Any calls to is_callable that use the `callable_name` parameter would at least need to be wrapped in a version check against the PHP_MAJOR_VERSION number. I believe the amount of code that uses this parameter is minimal. | ||
Line 495: | Line 408: | ||
None yet. This could be a large amount of work, so it is appropriate to get feed back on the mailing list before starting this work. The actual change of definition of callable would be a relatively small amount of work. However the SPL libraries make use of colon separated strings (including " | None yet. This could be a large amount of work, so it is appropriate to get feed back on the mailing list before starting this work. The actual change of definition of callable would be a relatively small amount of work. However the SPL libraries make use of colon separated strings (including " | ||
+ | |||
+ | |||
+ | ===== TODO ===== | ||
+ | |||
+ | Investigate weird stuff like: | ||
+ | |||
+ | $callable = [FooParent:: | ||
+ | |||
+ | assert(\is_callable($callable)); | ||
+ | https:// |
rfc/consistent_callables.txt · Last modified: 2021/10/20 13:18 by danack