Hello!
I would like to suggest a feature I saw being implemented in the V8 9.4
engine called "class static initialization block".
https://v8.dev/blog/v8-release-94
In short, it is a method that is executed once when a class is imported and
loaded, allowing for some more advanced initialization process to be done.
class Test {
static readonly Carbon $now;
static () {
self::$now = now();
}
}
Currently I can only do this in a very hacky way, usually by creating a
public static method and calling it after class initialization.
class Test {
static Carbon $now;
public static function init(): void {
self::$now = now();
}
}
Test::init();
I think the way the V8 does is much more interesting.
Another alternative would be to create a magic method like __initialize().
Hey David,
Hello!
I would like to suggest a feature I saw being implemented in the V8 9.4
engine called "class static initialization block".https://v8.dev/blog/v8-release-94
In short, it is a method that is executed once when a class is imported and
loaded, allowing for some more advanced initialization process to be done.class Test {
static readonly Carbon $now;static () { self::$now = now(); }
}
Currently I can only do this in a very hacky way, usually by creating a
public static method and calling it after class initialization.class Test {
static Carbon $now;public static function init(): void { self::$now = now(); }
}
Test::init();
I think the way the V8 does is much more interesting.
Another alternative would be to create a magic method like __initialize().
This already works without any magic.
In MyClass.php
:
class MyClass {
public static DateTimeImmutable $startup;
public function horribleInitializationPractices() {
self::$startup = new DateTimeImmutable();
}
}
MyClass::horribleInitializationPractices();
That's really all there is to it.
What you define as hacky is really normal/boring, and does not need special
constructs.
Also, it's generally not a good idea to sprinkle static mutable
runtime-bound all over the place: it should be an exception, not a rule.
Hello!
I would like to suggest a feature I saw being implemented in the V8 9.4
engine called "class static initialization block".https://v8.dev/blog/v8-release-94
In short, it is a method that is executed once when a class is imported and
loaded, allowing for some more advanced initialization process to be done.class Test {
static readonly Carbon $now;static () {
self::$now = now();
}
}Currently I can only do this in a very hacky way, usually by creating a
public static method and calling it after class initialization.class Test {
static Carbon $now;public static function init(): void {
self::$now = now();
}
}Test::init();
I think the way the V8 does is much more interesting.
Another alternative would be to create a magic method like __initialize().
This already works without any magic.
Not true. Here are three (3) things you cannot currently do with your proposed solution and without said magic:
1.) Use static analysis to recognize a class has an initialization functionality and thus provide lint-specific to best practices for static initialization.
2.) Make the initialization function private.
3.) Override and/or change order of initialization of parent in child class.
I do not know of use-cases of #3, but #1 and #2 use-case should be obvious.
MyClass::horribleInitializationPractices();
You named that in a rather condescending and passive-aggressive way. Is that really helpful for respectful dialog?
That's really all there is to it.
What you define as hacky is really normal/boring, and does not need special
constructs.Also, it's generally not a good idea to sprinkle static mutable
runtime-bound all over the place: it should be an exception, not a rule.
Valid use-cases I have come across:
-
Hooking actions and filters in plugins for frameworks and CMS that use hooks as an extension mechanism.
-
Initializing what would ideally be constants but because of PHP's constraints regarding constant initializes cannot be constants.
Hello!
I would like to suggest a feature I saw being implemented in the V8 9.4
engine called "class static initialization block".Hi David,
There was a similar proposal for PHP a few years ago: https://wiki.php.net/rfc/static_class_constructor
Although it didn't go to a vote, the proposal was dropped for lack of support. There's some discussion summarised in that RFC, and you can probably find the rest by searching for its title on https://externals.io
Regards,
--
Rowan Tommins
Here is the external thread:
https://externals.io/message/84602
Rowan, you were definitely the most vocal one to argue against it with the most messages on that thread.
But all the arguments against appeared to bog down into discrediting Johannes Ott's desired use-case for static class constructors and did not really look for other reason why they would be useful.
As I mentioned above, hooking actions and filters in plugins for frameworks and CMS is where it would be useful, especially since these generally need to be hooked upon initial load of the plugin.
Also, initializing variables to act as constants because PHP does not support complex initializations on constants is another use-case.
One of your arguments against was the timing of the initialization because evidently Johannes Ott wanted them to be lazily evaluated. Given the use-case I just stated I think they should always be loaded immediately whenever the file is loaded. That makes that argument about debugging cause-and-effect becomes a moot point.
And lazy evaluation should be considered as a separate feature. I blogged about that a while back but never brought to the list:
https://mikeschinkel.org/2021/lets-add-lazy-evaluation-to-php/
The other main argument you had against was Singletons. But that was a moot point because one of the reasons to use static initialization is to instantiate a Singleton.
Java has static initialization blocks[1], C# has static constructors[2] so the idea they would be useful for PHP is not a stretch,
-Mike
[1] https://www.geeksforgeeks.org/g-fact-79/
[2] https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors
Hi Mike,
I will leave others to discuss the use cases. It would be interesting if
people could find the motivating use cases that led to support in C#,
Java, and JS.
I just wanted to comment quickly on these few points, though:
1.) Use static analysis to recognize a class has an initialization functionality and thus provide lint-specific to best practices for static initialization.
Theoretically, a convention is enough for this, since PHP has no
official static analyser. But certainly a built-in syntax is the
strongest kind of convention, so point taken.
2.) Make the initialization function private.
I'm not sure how that would work: a private constructor means that it
has to be explicitly called from within the same class, but a static
initialiser would be called by the engine, before the class scope even
existed.
3.) Override and/or change order of initialization of parent in child class.
Similarly, I'm not sure this makes sense: it would imply that static
initialisers are inherited, and would be re-run every time a new
sub-class was defined, rather than being run strictly once when the
class is loaded. If the child class wants to re-use code in its static
initialiser, it would make more sense to call a (protected) static
method than to directly repeat the parent's initialiser block.
From your link, it seems that C# restricts them much more than methods
or constructors:
A static constructor doesn't take access modifiers or have parameters.
A class or struct can only have one static constructor.
Static constructors cannot be inherited or overloaded.
A static constructor cannot be called directly and is only meant to be called by the common language runtime (CLR). It is invoked automatically.
Apparently, the JS implementation does allow multiple static initialiser
blocks in one class, which are simply processed in order. But JS classes
are weird creations, implemented as a complex sugar on top of a
classless object system, so may not be the best point of reference for
PHP, whose object system much more closely resembles Java or C#.
Regards,
--
Rowan Tommins
[IMSoP]
2.) Make the initialization function private.
I'm not sure how that would work: a private constructor means that it has to be explicitly called from within the same class, but a static initialiser would be called by the engine, before the class scope even existed.
I think we are getting in the weeds here and loosing sight of the desired outcome.
Currently it is not possible for a developer to disallow calling a class initialization method from outside of the class. However that would end up being achieved — via private methods, protected methods, or even voodoo — those details are unimportant to ultimately disallowing the calling of initialization code from outside the class.
From your link, it seems that C# restricts them much more than methods or constructors:
A static constructor doesn't take access modifiers or have parameters.
A class or struct can only have one static constructor.
Static constructors cannot be inherited or overloaded.
A static constructor cannot be called directly and is only meant
to be called by the common language runtime (CLR). It is invoked
automatically.
Those all sound like they would be appropriate restrictions for PHP too.
Apparently, the JS implementation does allow multiple static initialiser blocks in one class, which are simply processed in order. But JS classes are weird creations, implemented as a complex sugar on top of a classless object system, so may not be the best point of reference for PHP, whose object system much more closely resembles Java or C#.
I did not reference JS — I only referenced Java and C# — so JS' approach was not part of my argument.
-Mike
Hello!
I would like to suggest a feature I saw being implemented in the V8 9.4
engine called "class static initialization block".
Hi David,
There was a similar proposal for PHP a few years ago: https://wiki.php.net/rfc/static_class_constructor
Although it didn't go to a vote, the proposal was dropped for lack of support. There's some discussion summarised in that RFC, and you can probably find the rest by searching for its title on https://externals.io
Regards,
--
Rowan Tommins
[IMSoP]
On Sun, Sep 12, 2021 at 2:28 AM David Rodrigues david.proweb@gmail.com
wrote:
Hello!
I would like to suggest a feature I saw being implemented in the V8 9.4
engine called "class static initialization block".https://v8.dev/blog/v8-release-94
In short, it is a method that is executed once when a class is imported and
loaded, allowing for some more advanced initialization process to be done.class Test {
static readonly Carbon $now;static () { self::$now = now(); }
}
Currently I can only do this in a very hacky way, usually by creating a
public static method and calling it after class initialization.class Test {
static Carbon $now;public static function init(): void { self::$now = now(); }
}
Test::init();
I think the way the V8 does is much more interesting.
Another alternative would be to create a magic method like __initialize().
Hey David,
The first time I built static initializers for classes was about 15 years
ago when, in my class loader (composer was not a thing), if the class had a
static method called __init(), it would call it after loading.
I don't remember why I needed it but I'm pretty sure I was used with the
Java static initialization blocks.
Maybe also the limitations that exist on the static values not being able
to be dynamically defined could have played a role but I mainly blame it on
my lower skill as a programmer.
I have never used it in a similar way in the past 10 years, as far as I
remember.
That's because I almost never use static state as that's just global state
and it should be avoided.
If you really want to compute some static state, I think that's going to be
in PHP in a few years.
An attempt to add it with the new keyword was done in
https://wiki.php.net/rfc/new_in_initializers#unsupported_positions but was
eventually dropped for class static properties and constants. You can read
details about it there.
Once the identified problems will be sorted out and probably the option to
have static initialization blocks will be possible.
So far, the current possible approach with having a public static method
and calling it after class definition works well in most of the cases.
If it's really needed, I think we can use it and be happy that it is
supported on all past PHP versions.
There is only one place that I know where I think it will not work, if the
class is preloaded. But I guess you can just avoid it for now.
Alex