RFC Proposal : Allows calling non-static public methods through the __callStatic magic method instead of throwing an error.

php.internals

하늘아부지

2 years ago
Hi. I'd like to propose an RFC, but I don't have the authority. Below is my suggestion. If you think this makes sense, please write an RFC for me. Sincerely. ---------------------------------------------------------- ===== Introduction ===== Allows calling non-static public methods through the __callStatic magic method instead of throwing an error. ===== Proposal ===== From a conceptual perspective: It goes without saying that calling a non-static method statically will result in an error. However, a confusing situation occurred when the __callStatic magic method exists. Non-public methods can be called, but public methods cannot. This is the opposite of the method visibility policy. From a practical point of view: If you can call Non-static public methods through the __callStatic magic method, you can write code like Laravel ORM more simply and tidy. <code> User::foo()->bar(); </code> ==== Before ==== <code> class Foo { protected static ?Foo $instance = null; public static function __callStatic($method, $args) { $instance = self::$instance ?? self::$instance = new static(); return $instance->__call($method, $args); } public function __call($method, $args) { if (method_exists($this, $method)) { return $instance->$method(...$args); } return $this; } protected function bar() { echo __METHOD__ . '<br />'; return $this; } protected function baz() { echo __METHOD__ . '<br />'; return $this; } } Foo::bar()->baz(); (new Foo())->bar()->baz(); </code> There is no error, but the concept of method visibility is broken. All Non-public methods can be called at instance scope. ==== After ==== <code> class Foo { protected static ?Foo $instance = null; public static function __callStatic($method, $args) { $instance = self::$instance ?? self::$instance = new static(); if (method_exists($instance, $method)) { return $instance->$method(...$args); } return $instance; } public function bar() { echo __METHOD__ . '<br />'; return $this; } public function baz() { echo __METHOD__ . '<br />'; return $this; } } Foo::bar()->baz(); (new Foo())->bar()->baz(); </code> This is more tidy. Only public methods are callable at instance scope.

Larry Garfield

2 years ago
On Fri, Feb 16, 2024, at 7:56 PM, 하늘아부지 wrote:
> Hi. > I'd like to propose an RFC, but I don't have the authority. > Below is my suggestion. > If you think this makes sense, please write an RFC for me. > Sincerely. > > ---------------------------------------------------------- > > ===== Introduction ===== > Allows calling non-static public methods through the __callStatic magic > method instead of throwing an error. > > ===== Proposal ===== > > From a conceptual perspective: > It goes without saying that calling a non-static method statically will > result in an error. > However, a confusing situation occurred when the __callStatic magic > method exists. > Non-public methods can be called, but public methods cannot. > This is the opposite of the method visibility policy. > > From a practical point of view: > If you can call Non-static public methods through the __callStatic > magic method, you can write code like Laravel ORM more simply and tidy. > > <code> > User::foo()->bar(); > </code> > > ==== Before ==== > > <code> > class Foo > { > protected static ?Foo $instance = null; > > public static function __callStatic($method, $args) > { > $instance = self::$instance ?? self::$instance = new static(); > return $instance->__call($method, $args); > } > > public function __call($method, $args) > { > if (method_exists($this, $method)) { > return $instance->$method(...$args); > } > > return $this; > } > > protected function bar() > { > echo __METHOD__ . '<br />'; > > return $this; > } > > protected function baz() > { > echo __METHOD__ . '<br />'; > > return $this; > } > } > > Foo::bar()->baz(); > (new Foo())->bar()->baz(); > </code> > > There is no error, but the concept of method visibility is broken. > All Non-public methods can be called at instance scope. > > ==== After ==== > > <code> > class Foo > { > protected static ?Foo $instance = null; > > public static function __callStatic($method, $args) > { > $instance = self::$instance ?? self::$instance = new static(); > > if (method_exists($instance, $method)) { > return $instance->$method(...$args); > } > > return $instance; > } > > public function bar() > { > echo __METHOD__ . '<br />'; > > return $this; > } > > public function baz() > { > echo __METHOD__ . '<br />'; > > return $this; > } > } > > Foo::bar()->baz(); > (new Foo())->bar()->baz(); > </code> > > This is more tidy. > Only public methods are callable at instance scope.
That calling bar() works at all in this example is rather accidental. The whole point of a non-static method is that it has an implicit required $this argument to it. Without a required argument, a method cannot be called, because it is supposed to fail. In fact, even your "before" example fails: https://3v4l.org/moH0s I don't think what you describe is even possible, nor would it be a good idea. See also: https://peakd.com/hive-168588/@crell/cutting-through-the-static --Larry Garfield

Claude Pache

2 years ago
> Le 17 févr. 2024 à 16:51, Larry Garfield <larry@garfieldtech.com> a écrit : > > On Fri, Feb 16, 2024, at 7:56 PM, 하늘아부지 wrote: >> Hi. >> I'd like to propose an RFC, but I don't have the authority. >> Below is my suggestion. >> If you think this makes sense, please write an RFC for me. >> Sincerely. >> >> ---------------------------------------------------------- >> >> ===== Introduction ===== >> Allows calling non-static public methods through the __callStatic magic >> method instead of throwing an error. >> >> ===== Proposal ===== >> >> From a conceptual perspective: >> It goes without saying that calling a non-static method statically will >> result in an error. >> However, a confusing situation occurred when the __callStatic magic >> method exists. >> Non-public methods can be called, but public methods cannot. >> This is the opposite of the method visibility policy. >> >> From a practical point of view: >> If you can call Non-static public methods through the __callStatic >> magic method, you can write code like Laravel ORM more simply and tidy. >> >> <code> >> User::foo()->bar(); >> </code> >> >> ==== Before ==== >> >> <code> >> class Foo >> { >> protected static ?Foo $instance = null; >> >> public static function __callStatic($method, $args) >> { >> $instance = self::$instance ?? self::$instance = new static(); >> return $instance->__call($method, $args); >> } >> >> public function __call($method, $args) >> { >> if (method_exists($this, $method)) { >> return $instance->$method(...$args); >> } >> >> return $this; >> } >> >> protected function bar() >> { >> echo __METHOD__ . '<br />'; >> >> return $this; >> } >> >> protected function baz() >> { >> echo __METHOD__ . '<br />'; >> >> return $this; >> } >> } >> >> Foo::bar()->baz(); >> (new Foo())->bar()->baz(); >> </code> >> >> There is no error, but the concept of method visibility is broken. >> All Non-public methods can be called at instance scope. >> >> ==== After ==== >> >> <code> >> class Foo >> { >> protected static ?Foo $instance = null; >> >> public static function __callStatic($method, $args) >> { >> $instance = self::$instance ?? self::$instance = new static(); >> >> if (method_exists($instance, $method)) { >> return $instance->$method(...$args); >> } >> >> return $instance; >> } >> >> public function bar() >> { >> echo __METHOD__ . '<br />'; >> >> return $this; >> } >> >> public function baz() >> { >> echo __METHOD__ . '<br />'; >> >> return $this; >> } >> } >> >> Foo::bar()->baz(); >> (new Foo())->bar()->baz(); >> </code> >> >> This is more tidy. >> Only public methods are callable at instance scope. > > That calling bar() works at all in this example is rather accidental. The whole point of a non-static method is that it has an implicit required $this argument to it. Without a required argument, a method cannot be called, because it is supposed to fail. > > In fact, even your "before" example fails: https://3v4l.org/moH0s
It does work after having corrected the obvious bug in the `bar()` method.
> > I don't think what you describe is even possible, nor would it be a good idea.
Why wouldn’t it possible? Couldn’t the engine invoke __callStatic() instead of complaining that the targeted method is not static? (I don’t have opinion on whether it is a good idea, though) —Claude

Joerg Sowa

2 years ago
For the reference to the others: https://3v4l.org/UmPVo Kind regards, Jorg