Hi Guilherme,
I read your updated RFC:
https://wiki.php.net/rfc/abstract_final_class
IMO the RFC as such makes sense now (abstract final confusion is eliminated. In addition to turn methods implicitly into static methods, the __construct of a static class should be implicitly private as well.
I am not sure what makes more sense for PHP, but just as thought-provoking impulse, wouldn't it be better just to check that all methods are static and __construct private instead of turning methods magically into static methods and __construct private?
And then there is one bit which I do not understand. What do you mean with:
"There's no way of adding static variables to a namespace. This would address this issue too."
How does this RFC address this problem?
Cheers,
Robert
Hi all!
I read your updated RFC:
https://wiki.php.net/rfc/abstract_final_class
Hmm, I don’t like this new RFC either. Static classes in languages like C# exist because the language designers made the dogmatic decision to require everything to be a class. Thus, to make collections of functions, your only choice is to make a “utility class" with a bunch of static methods. To make this easier, the static
keyword was added to make this easier in C# - Java doesn’t have this keyword, but it has the same pattern of static method-only classes. But PHP is not one of these dogmatic “everything must be a class” languages: it has true global functions which can be namespaced.
So, why, then, does PHP need static classes? As I see it, there are two reasons:
(1) Functions currently cannot be autoloaded
(2) Encapsulation of state (private/protected static properties)
I think both of these can be solved without perpetuating what I feel is an anti-pattern, the “utility class”. If they are solved, the need for utility classes goes away.
To implement (1), someone just needs to go and finally implement function autoloading. I think Joe Watkins (krakjoe) might’ve said he’d write a patch for that, but I may be wrong. I’d certainly like to help with this effort.
Implementing (2) is more difficult. We currently don’t have file-local variables like C has, we don’t even have namespaced variables. We do have static variables within functions, however. I can think of a few possible approaches to this:
(1) Say that global state is evil and refuse to implement it. Some people (myself included, to an extent) would argue that global state is “spooky action at a distance” and we shouldn’t be encouraging it. In this case we encourage users to simply make normal, non-static/abstract classes and to pass an instance around. This is generally good programming practise anyway.
(2) Add lexically-scoped variables, and allow normal global functions to be closures. By this I mean we add something like JavaScript’s let
keyword that would make a variable that is unset when it falls out of scope (end of a {} block, end of a file etc.). Then, we allow normal functions to use the use ($var)
syntax and close over variables. That’d look something like this:
<?php
let $x = 0;
function getCounter() use(&$x) {
return $x;
}
function incrementCounter() use(&$x) {
return $x++;
}
// since this is the end of the file, $x falls out of scope
I’d quite like this, as the introduction of lexically-scoped variables would have other advantages, too. They would make foreach() by-reference much less error-prone, for example:
<?php
foreach ($array as let &$item) {
// do stuff with $item
}
// $item is unset here, since it fell out of scope when we left the {} block
(3) Add file-local variables using the static
keyword (à la C). That’d work something like this:
<?php
static $x = 0;
function getCounter() {
global $x;
return $x;
}
// etc.
This would work similarly to `let`, I suppose, it’d just only be useful for files.
(4) Add namespace-local variables. No idea quite how this’d work.
I’m not sure which of these is the best, though I’d leans towards (1) or (2). But I definitely feel that adding a static
modifier for classes is solving the wrong problem. We don’t need to make utility classes easier to create. Rather, we need to solve the long-standing problems which force people to create utility classes in the first place.
Thanks!
Andrea Faulds
http://ajf.me/
Hi Andrea,
Thanks a lot for putting efforts thinking through this problem.
Indeed we have 3 problems that leads to "static classes". You detailed 2 of
them.
The third is around encapsulation. I may want functions that are reused by
other functions at the namespace level, but that shouldn't be used outside
of it. By purely supporting (1), you still didn't address this issue. That
would be easily addressed by having private static functions inside of the
static class.
So, let me recap the 3 motivations:
(1) Function/Namespaced function autoloading
(2) State encapsulation
(3) Function scoping
Heading back to your drill down in (2), I feel that we're adding more
complexity to something that can and it is at some extent be easily
supported through classes.
I actually thought about having "let" originally and using as "global", but
I gave up on the idea due to patch's complexity and also because it didn't
matter how far I was thinking through this, something was popping up my
head telling it was wrong. I decided to share my idea with a friend which
pointed me to HHVM's support and then I decided to work on that original
patch.
So, as you walk through your options, didn't it feel wrong to you? Static
classes are an easy addiction to the language and would have a negligent
performance impact, since it just adds 2 conditions in zend_vm and zend_api.
Again, I wouldn't necessarily use this concept I'm proposing right out the
bet, but I do see there are use cases.
I plan to redo my patch and get back for review once I finalize.
Cheers,
Hi all!
I read your updated RFC:
https://wiki.php.net/rfc/abstract_final_classHmm, I don’t like this new RFC either. Static classes in languages like C#
exist because the language designers made the dogmatic decision to require
everything to be a class. Thus, to make collections of functions, your only
choice is to make a “utility class" with a bunch of static methods. To make
this easier, thestatic
keyword was added to make this easier in C# -
Java doesn’t have this keyword, but it has the same pattern of static
method-only classes. But PHP is not one of these dogmatic “everything must
be a class” languages: it has true global functions which can be namespaced.So, why, then, does PHP need static classes? As I see it, there are two
reasons:(1) Functions currently cannot be autoloaded
(2) Encapsulation of state (private/protected static properties)
I think both of these can be solved without perpetuating what I feel is an
anti-pattern, the “utility class”. If they are solved, the need for utility
classes goes away.To implement (1), someone just needs to go and finally implement function
autoloading. I think Joe Watkins (krakjoe) might’ve said he’d write a patch
for that, but I may be wrong. I’d certainly like to help with this effort.Implementing (2) is more difficult. We currently don’t have file-local
variables like C has, we don’t even have namespaced variables. We do have
static variables within functions, however. I can think of a few possible
approaches to this:(1) Say that global state is evil and refuse to implement it. Some
people (myself included, to an extent) would argue that global state is
“spooky action at a distance” and we shouldn’t be encouraging it. In this
case we encourage users to simply make normal, non-static/abstract classes
and to pass an instance around. This is generally good programming practise
anyway.(2) Add lexically-scoped variables, and allow normal global functions to
be closures. By this I mean we add something like JavaScript’slet
keyword that would make a variable that is unset when it falls out of scope
(end of a {} block, end of a file etc.). Then, we allow normal functions to
use theuse ($var)
syntax and close over variables. That’d look something
like this:<?php let $x = 0; function getCounter() use(&$x) { return $x; } function incrementCounter() use(&$x) { return $x++; } // since this is the end of the file, $x falls out of scope I’d quite like this, as the introduction of lexically-scoped
variables would have other advantages, too. They would make foreach()
by-reference much less error-prone, for example:<?php foreach ($array as let &$item) { // do stuff with $item } // $item is unset here, since it fell out of scope when we left
the {} block
(3) Add file-local variables using the
static
keyword (à la C). That’d
work something like this:<?php static $x = 0; function getCounter() { global $x; return $x; } // etc. This would work similarly to `let`, I suppose, it’d just only be
useful for files.
(4) Add namespace-local variables. No idea quite how this’d work.
I’m not sure which of these is the best, though I’d leans towards (1) or
(2). But I definitely feel that adding astatic
modifier for classes is
solving the wrong problem. We don’t need to make utility classes easier to
create. Rather, we need to solve the long-standing problems which force
people to create utility classes in the first place.Thanks!
Andrea Faulds
http://ajf.me/
--
Guilherme Blanco
MSN: guilhermeblanco@hotmail.com
GTalk: guilhermeblanco
Toronto - ON/Canada
Hi!
Hi Andrea,
Thanks a lot for putting efforts thinking through this problem.
Indeed we have 3 problems that leads to "static classes". You detailed 2 of them.
The third is around encapsulation. I may want functions that are reused by other functions at the namespace level, but that shouldn't be used outside of it. By purely supporting (1), you still didn't address this issue. That would be easily addressed by having private static functions inside of the static class.
Hmm. Well, having functions not to be used outside of the namespace/file isn’t a problem unique to classes. That would be useful for people writing normal functions. Having something like static functions would be great.
So, as you walk through your options, didn't it feel wrong to you? Static classes are an easy addiction to the language and would have a negligent performance impact, since it just adds 2 conditions in zend_vm and zend_api.
Yes, they’re a simple addition, but I still think they’re the wrong solution to the problem. We shouldn’t force people to use classes for simple collections of functions. People have wanted function autoloading for years. I say it’s high time we add that.
Thanks!
Andrea Faulds
http://ajf.me/
Andrea Faulds wrote:
Yes, they’re a simple addition, but I still think they’re the wrong
solution to the problem. We shouldn’t force people to use classes for
simple collections of functions. People have wanted function
autoloading for years. I say it’s high time we add that.
Maybe https://wiki.php.net/rfc/function_autoloading should be reopened?
--
Christoph M. Becker
Andrea Faulds wrote:
Yes, they’re a simple addition, but I still think they’re the wrong
solution to the problem. We shouldn’t force people to use classes for
simple collections of functions. People have wanted function
autoloading for years. I say it’s high time we add that.Maybe https://wiki.php.net/rfc/function_autoloading should be reopened?
Perhaps. I did want to revive that RFC, but I disliked Anthony’s approach to implementing it (new set of functions, namespaced) and the patch wasn’t for phpng.
Andrea Faulds
http://ajf.me/
guilhermeblanco@gmail.com wrote on 01/12/2014 15:27:
(1) Function/Namespaced function autoloading
(2) State encapsulation
(3) Function scoping
I would add (4) static polymorphism, which Late Static Binding
explicitly supports.
But PHP is not one of these dogmatic “everything must be a class” languages: it has true global functions which can be namespaced.
By the same token, PHP is not one of those dogmatic languages which says
that every class must have at least one instance. There may be
advantages to giving your Factory class a constructor, but there is
nothing in the language itself to stop you making it entirely out of
static methods, and even sub-classing it from an abstract base using LSB.
The only thing missing is a standardised, enforced, annotation on the
class to declare that you are using this ability and instances will
never be created.
Regards,
Rowan Collins
[IMSoP]
The only thing missing is a standardised, enforced, annotation on the class
to declare that you are using this ability and instances will never be
created.
Let's say a class has no members: what's the harm in instantiating it?
Sure, it logically doesn't do anything useful, but is this user error
such a big deal that we alter the language to disallow it?
Levi Morrison wrote on 01/12/2014 17:58:
The only thing missing is a standardised, enforced, annotation on the class
to declare that you are using this ability and instances will never be
created.
Let's say a class has no members: what's the harm in instantiating it?
Sure, it logically doesn't do anything useful, but is this user error
such a big deal that we alter the language to disallow it?
The same could be said of many function and class modifiers - they exist
as self-documenting, mostly compile-time, assertions. There may not be a
compelling reason to add one, but "the user's self-discipline should be
good enough" isn't a very compelling reason not to.
I do think the keyword should trigger simple enforcement, though, not
automagically change the behaviour.
static class Foo {
function bar() {
echo do_something_with($this);
}
}
Much nicer (and just as easy) for that to give a compile-time error of
"Static class Foo cannot contain non-static function bar" than for it to
autoload fine, and then give a less obvious error when Foo::bar() is run
(probably to do with a non-existent variable, since $this is always
undefined inside a static function).
Programmers, and automated tools, won't know about the "static class"
annotation immediately, but hopefully already know what "static
function" means.
Note that this is the same as with "abstract function" vs "abstract
class", only the other way around - adding an abstract function doesn't
implicitly convert a class to be abstract, it refuses to compile if you
don't explicitly do so.
Regards,
Rowan Collins
[IMSoP]
Hi!
static class Foo {
function bar() {
echo do_something_with($this);
}
}Much nicer (and just as easy) for that to give a compile-time error of
"Static class Foo cannot contain non-static function bar" than for it to
autoload fine, and then give a less obvious error when Foo::bar() is run
(probably to do with a non-existent variable, since $this is always
undefined inside a static function).
This example is fit the narrow purpose of being the example, but you
could have as easily written "static function bar" and be back to square
one. I'm not sure how language-enforced "all methods must be static" is
really useful - if you don't want to instantiate it, put a private ctor
or make it abstract. If you want static methods, just make them static.
It won't help you anyway if you use $this in static methods.
Stas Malyshev
smalyshev@gmail.com
Am 01.12.2014 17:37 schrieb "Rowan Collins" rowan.collins@gmail.com:
guilhermeblanco@gmail.com wrote on 01/12/2014 15:27:
(1) Function/Namespaced function autoloading
(2) State encapsulation
(3) Function scoping
I would add (4) static polymorphism, which Late Static Binding explicitly
supports.
Amen!
In several places in our codebase, I make use of per-request constants by
having an autoloaded baseclass file which uses class_alias for
specialization, like this:
// in some/class.inc
class some_class__base {
... common methods and default implementation
}
if (class_exists('some_class_'.SOME_CONSTANT)) {
class_alias('some_class', 'some_class_'.SOME_CONSTANT);
} else {
class_alias('some_class', 'some_class__base');
}
// in some/class/special.inc
class some_class_special extends some_class_base {
.... specialization ...
}
Calling code simply uses some_class::foo() without caring whether the
concrete script run then uses the specialized class, or the base class.
The only thing missing is a standardised, enforced, annotation on the
class to declare that you are using this ability and instances will never
be created.
I that regard, I'm quite happy with private function __construct, where
desired.
best regards
Patrick
> (2) Add lexically-scoped variables, and allow normal global functions
> to be closures. By this I mean we add something like JavaScript’s
> `let` keyword that would make a variable that is unset when it falls
> out of scope (end of a {} block, end of a file etc.). Then, we allow
> normal functions to use the `use ($var)` syntax and close over
> variables. That’d look something like this:
>
> <?php
> let $x = 0;
> function getCounter() use(&$x) {
> return $x;
> }
> function incrementCounter() use(&$x) {
> return $x++;
> }
> // since this is the end of the file, $x falls out of scope
>
This looks like you're trying to create a class without calling it a
class. Now, I agree that not everything should be a class. But what
you're doing - set of functions linked by common behavior and sharing a
common state - is a perfect fit for something classes and objects are
used in PHP. So why not just use them instead of inventing creative ways
of doing the same but in more complicated way?
Moreover, since your example is clearly not a singleton - it is hard to
imagine any program that would have use for just one counter but never
any more - it is a classic example of something that should be an
object. It might be a wrong example, but I have hard time seeing any
example of something that has a group of functions, bound by common
functionality, has a shared state and still should not and can not be
expressed as a class/object. That's literally what they are made for.
Now, utility classes do suck, but they suck not because they do what you
described - on the contrary, they suck exactly because they are usually
a bag of random, unrelated functions bound by nothing but the common
trait of "we don't have other places to put them in". But for that,
plain old functions work just fine in PHP.
--
Stas Malyshev
smalyshev@gmail.com
>
>
> Hi!
>
>> (2) Add lexically-scoped variables, and allow normal global functions
>> to be closures. By this I mean we add something like JavaScript’s
>> `let` keyword that would make a variable that is unset when it falls
>> out of scope (end of a {} block, end of a file etc.). Then, we allow
>> normal functions to use the `use ($var)` syntax and close over
>> variables. That’d look something like this:
>>
>> <?php
>> let $x = 0;
>> function getCounter() use(&$x) {
>> return $x;
>> }
>> function incrementCounter() use(&$x) {
>> return $x++;
>> }
>> // since this is the end of the file, $x falls out of scope
>>
> Moreover, since your example is clearly not a singleton - it is hard to
> imagine any program that would have use for just one counter but never
> any more - it is a classic example of something that should be an
> object.
That was just a frivolous example to show how the syntax works, it’s not a sensible example of why you’d want this functionality.
> This looks like you're trying to create a class without calling it a
> class. Now, I agree that not everything should be a class. But what
> you're doing - set of functions linked by common behavior and sharing a
> common state - is a perfect fit for something classes and objects are
> used in PHP. So why not just use them instead of inventing creative ways
> of doing the same but in more complicated way?
> It might be a wrong example, but I have hard time seeing any
> example of something that has a group of functions, bound by common
> functionality, has a shared state and still should not and can not be
> expressed as a class/object. That's literally what they are made for.
> Now, utility classes do suck, but they suck not because they do what you
> described - on the contrary, they suck exactly because they are usually
> a bag of random, unrelated functions bound by nothing but the common
> trait of "we don't have other places to put them in". But for that,
> plain old functions work just fine in PHP.
Yeah, I see your point. For encapsulated state with associated functions to work with that state, a class is the way to go. For miscellaneous functions with no shared state, they should just be functions, not in a class.
However, I still don’t think `static` is a good idea. Adding function autoloading eliminates one use case - a bag of random, unrelated functions. But it leaves the other: a set of functions to operate on global state. The problem is that, well, global state is rarely a good thing, I don’t think we should be encouraging it. If people really want to make a singleton or a final class with a private constructor, be my guest, but I don’t think we should make it any easier for them.
--
Andrea Faulds
http://ajf.me/
The problem is that, well, global state is rarely a good thing, I don’t think we should be encouraging it.
To get a bit philosophical: Is it really the language's job to make that decision? Is there a difference between "encouraging", "allowing", and "enabling"?
Purely static classes are a reality whether this feature is added or not. They do things namespaces can't (and probably shouldn't), but are simpler to work with than singletons (which they closely resemble) or full-blown Dependency Injection. Declaring a static class is just a way of acknowledging that this is a deliberate decision, and instance members should not be added.
Regards,
Rowan Collins
[IMSoP]
PR with this feature request: https://github.com/php/php-src/pull/929
On Mon, Dec 1, 2014 at 4:56 PM, Rowan Collins rowan.collins@gmail.com
wrote:
The problem is that, well, global state is rarely a good thing, I don’t
think we should be encouraging it.To get a bit philosophical: Is it really the language's job to make that
decision? Is there a difference between "encouraging", "allowing", and
"enabling"?Purely static classes are a reality whether this feature is added or not.
They do things namespaces can't (and probably shouldn't), but are simpler
to work with than singletons (which they closely resemble) or full-blown
Dependency Injection. Declaring a static class is just a way of
acknowledging that this is a deliberate decision, and instance members
should not be added.Regards,
Rowan Collins
[IMSoP]--
--
Guilherme Blanco
MSN: guilhermeblanco@hotmail.com
GTalk: guilhermeblanco
Toronto - ON/Canada
Perhaps I have missed it in the noise, but have any other mainstream
languages (or new hipster ones; I don't care) have something
equivalent to the proposed static classes?
Yes, C# documented here:
http://msdn.microsoft.com/en-us/library/79b3xss3.aspx
Perhaps I have missed it in the noise, but have any other mainstream
languages (or new hipster ones; I don't care) have something
equivalent to the proposed static classes?--
--
Guilherme Blanco
MSN: guilhermeblanco@hotmail.com
GTalk: guilhermeblanco
Toronto - ON/Canada
Hi!
To get a bit philosophical: Is it really the language's job to make
that decision? Is there a difference between "encouraging",
"allowing", and "enabling"?
I think, yes on both. The PHP tradition is to allow doing stuff that may
look weird (most languages won't allow you to do $$foo) but still there
are practices that we encourage, especially in OO space, and some even
that we enforce quite strictly (such as inheritance rules for methods).
Purely static classes are a reality whether this feature is added or
not. They do things namespaces can't (and probably shouldn't), but
Of course, you can create class containing of only static members. The
question is whether we need a language construct for ensuring that
certain class has only static members. I'm not sure this requires a
language-level construct.
Stas Malyshev
smalyshev@gmail.com
Hi Guilherme, hi Robert.
I am not sure what makes more sense for PHP, but just as thought-provoking impulse, wouldn't it be better just to check that all methods are static and __construct private instead of turning methods magically into static methods and __construct private?
I very much like the RFC in its current state except for the implicit static. While we can preserve the behaviour of automatically turning methods into static methods, this should indeed trigger a warning.
cu,
Lars
I'm working on a patch right now and I removed the implicit configuration
of static to methods.
It's only missing reflection magic. Should be out of the oven in one hour
or less.
Hi Guilherme, hi Robert.
I am not sure what makes more sense for PHP, but just as
thought-provoking impulse, wouldn't it be better just to check that all
methods are static and __construct private instead of turning methods
magically into static methods and __construct private?I very much like the RFC in its current state except for the implicit
static. While we can preserve the behaviour of automatically turning
methods into static methods, this should indeed trigger a warning.cu,
Lars
--
Guilherme Blanco
MSN: guilhermeblanco@hotmail.com
GTalk: guilhermeblanco
Toronto - ON/Canada