rfc:pass_scope_to_magic_accessors

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
rfc:pass_scope_to_magic_accessors [2023/01/19 16:56] nicolasgrekasrfc:pass_scope_to_magic_accessors [2023/01/19 17:39] nicolasgrekas
Line 14: Line 14:
 This RFC proposes to pass their calling scope to magic accessors: ''%%__get()%%'', ''%%__set()%%'', ''%%__isset()%%'', ''%%__unset()%%'', ''%%__call()%%'' and ''%%__callStatic()%%''. This RFC proposes to pass their calling scope to magic accessors: ''%%__get()%%'', ''%%__set()%%'', ''%%__isset()%%'', ''%%__unset()%%'', ''%%__call()%%'' and ''%%__callStatic()%%''.
  
-The calling scope defined as the name of the class that is calling a magic method. It's a string. It can also be null when a magic method is called outside of any class context, as is the case when calling them from the global scope or from the body of a named function. For closures, their scope is the class name they were created in (if any), or their rebound scope when using ''Closure::bind()''. The calling scope is also the piece of information that is found under key ''"class"'' of frames returned by ''debug_backtrace()''.+ 
 +The calling scope is defined as the name of the class that is calling a magic method. It's a string. It can also be null when a magic method is called outside of any class context, as is the case when calling them from the global scope or from the body of a named function. For closures, their scope is the class name they were created in (if any), or their rebound scope when using ''Closure::bind()''. The calling scope is also the piece of information that is found under key ''"class"'' of frames returned by ''debug_backtrace()''. 
 + 
 +Here are some examples: 
 + 
 +<PHP> 
 +class Foo { 
 +    private $abc; 
 +     
 +    public function __construct() 
 +    { 
 +        unset($this->abc); // enables magic methods when accessing the property 
 +    } 
 +     
 +    public function __get($name) { 
 +        var_dump(debug_backtrace()[1]['class'] ?? null); 
 +    } 
 +
 + 
 +$foo = new Foo(); 
 + 
 +$foo->abc; // NULL 
 + 
 +class Bar { 
 +    public function __construct($foo) { 
 +        $foo->abc; 
 +    } 
 +
 + 
 +$bar = new Bar($foo); // string(3) "Bar" 
 + 
 +$f = fn ($foo) => $foo->abc; 
 + 
 +$f($foo); // NULL 
 + 
 +$g = \Closure::bind($f, null, Bar::class); 
 + 
 +$g($foo); // string(3) "Bar" 
 +</PHP>
  
 Passing the calling scope to magic accessors would help to properly implement visibility-related logic. Right now, we have to call ''debug_backtrace()'' to write scope-sensitive logic, but it's very difficult to get it right: accounting for inheritance is hard (calling e.g. ''%%parent::__get()%%'' changes the outcome of ''debug_backtrace()'' inside this ''%%parent::__get()%%'' method.) Passing the calling scope would make this concern more evident and would solve the issue with inheritance. It would also fix the edge case of calling a magic method via ''ReflectionProperty::get/setValue()'', where looking up at ''debug_backtrace()'' needs special care to extract the calling scope. Last but not least, removing calls to ''debug_backtrace()'' would improve the performance of such magic accessors. Passing the calling scope to magic accessors would help to properly implement visibility-related logic. Right now, we have to call ''debug_backtrace()'' to write scope-sensitive logic, but it's very difficult to get it right: accounting for inheritance is hard (calling e.g. ''%%parent::__get()%%'' changes the outcome of ''debug_backtrace()'' inside this ''%%parent::__get()%%'' method.) Passing the calling scope would make this concern more evident and would solve the issue with inheritance. It would also fix the edge case of calling a magic method via ''ReflectionProperty::get/setValue()'', where looking up at ''debug_backtrace()'' needs special care to extract the calling scope. Last but not least, removing calls to ''debug_backtrace()'' would improve the performance of such magic accessors.
rfc/pass_scope_to_magic_accessors.txt · Last modified: 2023/05/09 16:25 by nicolasgrekas