Re: [RFC] Static Constructor

From: Date: Wed, 19 Jun 2024 18:19:48 +0000
Subject: Re: [RFC] Static Constructor
References: 1 2  Groups: php.internals 
Request: Send a blank email to [email protected] to get a copy of this message
> On Jun 19, 2024, at 8:33 AM, Erick de Azevedo Lima <[email protected] <mailto:[email protected]>> wrote:
> 
> Hello everybody.
> 
> I found myself wanting this feature (that I first encountered when programming in C#) for
> removing a workaround from a codebase I work from time to time.
> I searched internals and found a discussion from almost a decade ago. That discussion did not
> end well, mostly because of insulting accusations.
> I then decided to do some research on this subject and found out that it's a pretty common
> feature in other OOP languages.
> Also, as I started studying the php-src  (and missed the days when I used to program in C in my
> main job), I decided to do an implementation myself even before presenting the RFC.
> The implementation link can also be found at the RFC.
> 
> You can read the RFC here:
> https://wiki.php.net/rfc/static_constructor
> <https://wiki.php.net/rfc/static_constructor>
> 
> Regards,
> 
> Erick

1. I noticed you did not include an example for Go so I wrote one up for you in a Go playground.
Hopefully you can include Go's approach in your RFC?

> https://goplay.tools/snippet/6lvAQdye9P9
> <https://goplay.tools/snippet/6lvAQdye9P9>
2. Also, in the past I made frequent use of "on_load()" methods that I would call
immediately after the class code. I always wanted a static initializer — and even discussed it
with Ben Ramsay at an after-event for Atlanta PHP meetup years ago — but I never proposed in as an
RFC and cannot remember if I ever discussed here on the list.

Even though I no longer actively use or maintain the following library I wanted to provide you with
links for each of the different use-case where I had an on_load() method and would have
used a static initializer had it been available in hopes they may inspire you to enhance your RFC
with new use-cases. I am also sharing for others to see the different use-cases:

- https://github.com/wplib/wplib/blob/master/wplib.php#L142
<https://github.com/wplib/wplib/blob/master/wplib.php#L142>
- https://github.com/wplib/wplib/blob/master/modules/posts/posts.php#L43
<https://github.com/wplib/wplib/blob/master/modules/posts/posts.php#L43>

- https://github.com/wplib/wplib/blob/master/modules/theme/theme.php#L14
<https://github.com/wplib/wplib/blob/master/modules/theme/theme.php#L14>
- https://github.com/wplib/wplib/blob/master/modules/role-administrator/role-administrator.php#L35
<https://github.com/wplib/wplib/blob/master/modules/role-administrator/role-administrator.php#L35>
- https://github.com/wplib/wplib/blob/master/modules/roles/includes/class-role-module-base.php#L33
<https://github.com/wplib/wplib/blob/master/modules/roles/includes/class-role-module-base.php#L33>
- https://github.com/wplib/wplib/blob/master/modules/commit-reviser/commit-reviser.php#L13
<https://github.com/wplib/wplib/blob/master/modules/commit-reviser/commit-reviser.php#L13>
- https://github.com/wplib/wplib/blob/master/modules/helpers-html/helpers-html.php#L16
<https://github.com/wplib/wplib/blob/master/modules/helpers-html/helpers-html.php#L16>


3. One thing about "best practices" for static initializers. When using a package-level
variable in Go initializations and for those PHP static classes with on_load() methods
I found it problematic to add anything that could fail in a reasonable use-case, and I would
recommend that this would become the advice given as a best practice for using static initializers. 

In my PHP examples above I do happen to assume the database is open and exists but only because the
library is for use with WordPress as a must-load plugin, and if the database is not available my
plugin will never get loaded.

In Go I never use database access code or remote API calls, or anything that could generate an
error. I instead create "initialize" methods and call them explicitly in
main() or some other func that main() calls.

In PHP you can have a syntax error at runtime, but in that case it will error as soon as you try to
autoload the PHP file so I do not think syntax errors should be real concern here, only runtime
errors.

4. To elaborate more about "best practices" where I see static initializers being
especially valuable it when you want to initialize immutable data, and especially when that data is
in complex form such as an object vs. just a simple value. Here is one such example of this which
initializes the default labels for a WordPress post-type: https://github.com/wplib/wplib/blob/master/modules/posts/posts.php#L54-L64
<https://github.com/wplib/wplib/blob/master/modules/posts/posts.php#L54-L64>


5. Another use-case is registering "hooks" defined by a framework, such as the
wp_loaded hook in WordPress: https://github.com/wplib/wplib/blob/master/modules/posts/posts.php#L68C28-L68C37
<https://github.com/wplib/wplib/blob/master/modules/posts/posts.php#L68C28-L68C37>


6. Yes another use-case would be to make a framework more robust and to provide guidance to a user
for what they are doing wrong. Here I am actually throwing an error when the it is clear the classes
are being initialized out-of-order: https://github.com/wplib/wplib/blob/master/wplib.php#L157
<https://github.com/wplib/wplib/blob/master/wplib.php#L157>

7. Still another is loading related .PHP files that are required so as not to have to rely on the
autoloader.
https://github.com/wplib/wplib/blob/master/wplib.php#L167
<https://github.com/wplib/wplib/blob/master/wplib.php#L167>.


Here I would also suggest guidance about not including any code that can fail at load time during
the normal course of execution meaning syntax errors are okay but the loaded code should really
never generate a runtime errors. 

8. A big use-case too is allowing static classes to register themselves as being able to provide a
service to another class defined by the application or framework. This allows a class to
self-describe its behavior and capabilities such as how my library allows the registration of the
class as a "Helper" for the main class and how it allows registering a class as
representing a "Role" in the framework: https://github.com/wplib/wplib/blob/master/modules/posts/posts.php#L48
<https://github.com/wplib/wplib/blob/master/modules/posts/posts.php#L48>
https://github.com/wplib/wplib/blob/master/modules/role-administrator/role-administrator.php#L37
<https://github.com/wplib/wplib/blob/master/modules/role-administrator/role-administrator.php#L37>


This is much like how a pub-sub system allows others to subscribe to the publisher and the publisher
does not need to know in advance all the subscribers to the system. 

9. Regarding Larry's concern for test mocking, if the only things that static initialize
functions are doing is initializing immutable data, registering hooks, pre-loading non-error
generating .PHP files, and/or registering classes for special use-cases then there is #fyi (little
or?) no need to mock them for testing purposes. 

For developers following said guidance I do not see static initializers as creating a challenge for
testing.

-Mike

P.S. I think it would be great if static initializers threw a runtime error if they attempted to do
anything that could throw an error — at least not without a try-catch to absorb the error — but
as I cannot envision how that would be possible and still be acceptably performant I think the only
approach should be provide the community with guidance on how and how not to use static
initializers. #fwiw




Thread (16 messages)

« previous php.internals (#123684) next »