Hi internals,
While some people are trying to make PHP a stricter language I'm also interested in making it a more flexible language by allowing to opt-in to advanced features for people who want it.
Please don't shoot this down just because you are not the target audience of such a feature ;-)
Toying around with PHP 8 I noticed that it is now impossible to have methods which can be called both statically and in an object.
This proposal brings back mixed mode methods both for extensions and a new syntax for user-land functions to explicitly allow it.
Motivation:
- Make migration of tried-and-tested mixed mode methods easier while having to explicitly declare it. So yes, unchanged code gets the new stricter semantics but updating code to the new (and from now on well-defined) behaviour can be done by simply extending the function definition. Having to rewrite such code to two different method names can be tedious and error-prone without big benefit.
- Allowing mixed mode methods as a form of name-overload in cases having to have two separate names for static/non-static is a WTF.
- Reviving things like
$elements = DOMDocument::loadXML($html)->childNodes;
instead of having to rewrite them to something like
($dom = new DOMDocument)->loadXML($html);
$elements = $dom->childNodes;
by adding ZEND_ACC_ALLOW_STATIC to the method signature (ext/dom/document.c currently still contains code to handle both cases).
A first shot at an implementation was done using the syntax ?static and using isset($this) to determine the current mode:
class A {
var $dynamic = 'dynamic';
?static function mixedmodefn() { return isset($this) ? $this->dynamic : 'static'; }
}
allowing both (new A)->mixedmodefn() and A::mixedmodefn().
The implementation (including a test) can be found at
https://github.com/php/php-src/compare/master...chschneider:optional_static
If people are interested I could create an RFC for this.
Regards,
- Chris
On Mon, Sep 2, 2019 at 4:03 PM Christian Schneider cschneid@cschneid.com
wrote:
Hi internals,
While some people are trying to make PHP a stricter language I'm also
interested in making it a more flexible language by allowing to opt-in to
advanced features for people who want it.
Please don't shoot this down just because you are not the target audience
of such a feature ;-)Toying around with PHP 8 I noticed that it is now impossible to have
methods which can be called both statically and in an object.
This proposal brings back mixed mode methods both for extensions and a new
syntax for user-land functions to explicitly allow it.Motivation:
- Make migration of tried-and-tested mixed mode methods easier while
having to explicitly declare it. So yes, unchanged code gets the new
stricter semantics but updating code to the new (and from now on
well-defined) behaviour can be done by simply extending the function
definition. Having to rewrite such code to two different method names can
be tedious and error-prone without big benefit.- Allowing mixed mode methods as a form of name-overload in cases having
to have two separate names for static/non-static is a WTF.- Reviving things like
$elements = DOMDocument::loadXML($html)->childNodes;
instead of having to rewrite them to something like
($dom = new DOMDocument)->loadXML($html);
$elements = $dom->childNodes;
by adding ZEND_ACC_ALLOW_STATIC to the method signature
(ext/dom/document.c currently still contains code to handle both cases).A first shot at an implementation was done using the syntax ?static and
using isset($this) to determine the current mode:
class A {
var $dynamic = 'dynamic';
?static function mixedmodefn() { return isset($this) ?
$this->dynamic : 'static'; }
}
allowing both (new A)->mixedmodefn() and A::mixedmodefn().The implementation (including a test) can be found at
https://github.com/php/php-src/compare/master...chschneider:optional_static
If people are interested I could create an RFC for this.
I'm not in favor of this, but I'll also say that I don't hate it either.
The big problem with what we had before was that any random instance method
could also be used as a static method, which in 99% of the cases was not
intended and would not work. Additionally, the engine had to always account
for this edge-case possibility and perform extra checks that only rarely
did something useful. Your proposal to use an explicit ?static annotation
leaves this as a questionable coding pattern, but I don't see any big
technical problems with it.
Regards,
Nikita
Please don't shoot this down just because you are not the target audience of such a feature
That is always good advice.
When you draft the RFC, I strongly recommend coming up with a line of
reasoning of why creating static versions of functions like this:
class Foo {
static function createFromXml($html) {
$instance = new static();
$instance>loadXml();
return $instance;
}
function loadXml($html) {
//...
}
}
Are such a terrible burden, that keeping the mixed mode calling is a
desirable thing. I say that as I think it would need a strong
justification rather than just "it's something that could be done".
In particular, there are tools like
https://github.com/rectorphp/rector that I understand could be used to
do this automatically, at least for userland code.
Also(, without checking to see if it's feasible,) to me a less
surprising approach would be to allow static and instance methods to
be declared separately with the same method name.
class Foo {
static function loadXml() {
echo "I am static method\n";
}
function loadXml() {
echo "I am instance method\n";
}
}
Foo::loadXml();
(new Foo())->loadXml();
// output is
// I am static method
// I am instance method
Although that doesn't meet your goal of allowing seamless upgrades, it
seems like a better approach to allowing that sort of thing in
general. At least in the sense of, if I had to explain this capability
to a junior programmer, explaining that the static method is used when
called statically, and the instance method is used when called on an
instance, would be easier to explain that '$this' might or not be
there.
cheers
Dan
Ack
Am 02.09.2019 um 17:01 schrieb Dan Ackroyd danack@basereality.com:
Also(, without checking to see if it's feasible,) to me a less
surprising approach would be to allow static and instance methods to
be declared separately with the same method name.class Foo {
static function loadXml() {
echo "I am static method\n";
}function loadXml() {
echo "I am instance method\n";
}
}
I was considering this approach but that is a special case of Ad hoc polymorphism (https://en.wikipedia.org/wiki/Ad_hoc_polymorphism).
And that's a path I don't want to go down, I don't think that's a good fit for PHP.
It felt more natural to model it similarly to nullable return types.
- Chris
In my experience, things that have different roles or behavior should
be differently named.
I have seen a lot of functions with dynamic return types based on a
mode set in the parameter (e.g. the return type could be string,
string[] or string[][] depending on whether some parameter is NULL
or
not). In all cases, the confusion and ambiguity caused by this was far
greater than the supposed / perceived convenience.
Often enough, the increased complexity of control flow branches inside
the function would lead to bugs, which could remain undiscovered for
years.
Maybe the only benefits were to not pollute the global namespace as
much, and to give access to the same static variables for caching.
None of these benefits apply in an object-oriented scenario.
A method body which has to handle and distinguish static and
non-static calls would easily suffer from the same problems.
Am 02.09.2019 um 17:01 schrieb Dan Ackroyd danack@basereality.com:
Also(, without checking to see if it's feasible,) to me a less
surprising approach would be to allow static and instance methods to
be declared separately with the same method name.
This would at least solve the problem of added complexity in the method body.
Still, as Dan Ackroyd points out, there needs to be a clear benefit.
I was considering this approach but that is a special case of Ad hoc polymorphism (https://en.wikipedia.org/wiki/Ad_hoc_polymorphism).
And that's a path I don't want to go down, I don't think that's a good fit for PHP.
It felt more natural to model it similarly to nullable return types.
The value of parameter-based polymorphism is for cases where the
calling code wants to handle all parameter types in one call, whereas
the callee has different implementations for different parameter
types.
This is not the case here: The calling code does always know whether a
call should be static or not, so it can choose the applicable method
name.
Am 02.09.2019 um 17:01 schrieb Dan Ackroyd danack@basereality.com:
Also(, without checking to see if it's feasible,) to me a less
surprising approach would be to allow static and instance methods to
be declared separately with the same method name.class Foo {
static function loadXml() {
echo "I am static method\n";
}function loadXml() {
echo "I am instance method\n";
}
}I was considering this approach but that is a special case of Ad hoc polymorphism (https://en.wikipedia.org/wiki/Ad_hoc_polymorphism).
And that's a path I don't want to go down, I don't think that's a good fit for PHP.
It felt more natural to model it similarly to nullable return types.
- Chris
The value of parameter-based polymorphism is for cases where the
calling code wants to handle all parameter types in one call, whereas
the callee has different implementations for different parameter
types.This is not the case here: The calling code does always know whether a
call should be static or not, so it can choose the applicable method
name.
This is what I don't get. How can it not?
If a method is static-safe, and you want to call it from both static and object contexts... Make it a static method and call it statically from both contexts. That's fine. If it's not safe to call from a static context (because it reads $this), you couldn't call it statically anyway.
If the method has 2 different logic paths, depending on if it's a static or object context, then it should be two different methods. Folding them into one just pushes more conditionals down deeper into the code where the cyclomatic complexity impact is greater. That's not a win.
--Larry Garfield