New RFC draft "static class constructor"

php.internals

Johannes Ott

11 years ago
Hi, finally I managed to do my first RFC draft. https://wiki.php.net/rfc/static_class_constructor I hope I have done everything correct so far and I'm looking forward to your feedback on it. As I already mentioned in the prediscussion thread here: For being my first change to the PHP core, I would be very happy, if someone of the "old stager" would help me with the implementation of this RFC if it is accepted. Regards
-- DerOetzi

Johannes Schlueter

11 years ago
Hi, On Mon, 2015-04-13 at 15:37 +0200, Johannes Ott wrote:
> finally I managed to do my first RFC draft. > > https://wiki.php.net/rfc/static_class_constructor > > I hope I have done everything correct so far and I'm looking forward to > your feedback on it.
In my opinion this makes the language way more complex as there are more places which "suddenly" execute code but solves a small problem compared to that. (Which actually is an issue many people would suggest to avoid completely instead of ennobling this with a language feature. Why am I saying it makes the language more complex? - Your proposal seems to miss mentioning when exactly the method is executed. what is the output of a.php: <?php echo 'A: '.__FILE__.':'.__LINE__."\n"; class A { static function __static() { echo __CLASS__.'::'.__METHOD__."\n"; } } echo 'B: '.__FILE__.':'.__LINE__."\n"; class B { static function __static() { echo __CLASS__.'::'.__METHOD__."\n"; } } echo 'C: '.__FILE__.':'.__LINE__."\n"; ?> b.php: <?php echo 'D: '.__FILE__.':'.__LINE__."\n"; C::$foo = 23; echo 'E: '.__FILE__.':'.__LINE__."\n"; include 'a.php'; echo 'F: '.__FILE__.':'.__LINE__."\n"; class C { static $foo = 0; static function __static() { echo __CLASS__.'::'.__METHOD__."\n"; } } echo 'G: '.__FILE__.':'.__LINE__."\n"; class D extends B { static function __static() { echo __CLASS__.'::'.__METHOD__."\n"; } } echo 'H: '.__FILE__.':'.__LINE__."\n"; ?> Mind that in b.php we make use of class C above the declaration, which we can do as C is a simple class and can be bound early during compilation. Class D however can only be bound during run-time, after including a.php, which happens after C was already used. johannes

Benjamin Eberlei

11 years ago
What would happen if you "call the parent constructor": class A extends B { static public function __static() { B::__static(); } } On Mon, Apr 13, 2015 at 5:23 PM, Johannes Schlüter <johannes@schlueters.de> wrote:

Johannes Ott

11 years ago
Am 13.04.2015 um 17:29 schrieb Benjamin Eberlei:
> What would happen if you "call the parent constructor": > > class A extends B { > static public function __static() { > B::__static(); > } > } >
Please have a closer look to the definition of the function: https://wiki.php.net/rfc/static_class_constructor#proposal it should be declared "private" to prohibit any access from outside the of the class itself. Although see the the answer 2 in discussion section: https://wiki.php.net/rfc/static_class_constructor#inheritance_of_the_class_constructor Thanks for feedback
> On Mon, Apr 13, 2015 at 5:23 PM, Johannes Schlüter <johannes@schlueters.de> > wrote: > >> Hi, >> >> On Mon, 2015-04-13 at 15:37 +0200, Johannes Ott wrote: >>> finally I managed to do my first RFC draft. >>> >>> https://wiki.php.net/rfc/static_class_constructor >>> >>> I hope I have done everything correct so far and I'm looking forward to >>> your feedback on it. >> >> In my opinion this makes the language way more complex as there are more >> places which "suddenly" execute code but solves a small problem compared >> to that. (Which actually is an issue many people would suggest to avoid >> completely instead of ennobling this with a language feature. >> >> Why am I saying it makes the language more complex? - Your proposal >> seems to miss mentioning when exactly the method is executed. what is >> the output of >> >> >> a.php: >> <?php >> echo 'A: '.__FILE__.':'.__LINE__."\n"; >> class A { >> static function __static() { >> echo __CLASS__.'::'.__METHOD__."\n"; >> } >> } >> echo 'B: '.__FILE__.':'.__LINE__."\n"; >> class B { >> static function __static() { >> echo __CLASS__.'::'.__METHOD__."\n"; >> } >> } >> echo 'C: '.__FILE__.':'.__LINE__."\n"; >> ?> >> >> b.php: >> <?php >> echo 'D: '.__FILE__.':'.__LINE__."\n"; >> >> C::$foo = 23; >> echo 'E: '.__FILE__.':'.__LINE__."\n"; >> >> include 'a.php'; >> echo 'F: '.__FILE__.':'.__LINE__."\n"; >> >> >> class C { >> static $foo = 0; >> static function __static() { >> echo __CLASS__.'::'.__METHOD__."\n"; >> } >> } >> >> echo 'G: '.__FILE__.':'.__LINE__."\n"; >> >> class D extends B { >> static function __static() { >> echo __CLASS__.'::'.__METHOD__."\n"; >> } >> } >> >> echo 'H: '.__FILE__.':'.__LINE__."\n"; >> ?> >> >> >> Mind that in b.php we make use of class C above the declaration, which >> we can do as C is a simple class and can be bound early during >> compilation. Class D however can only be bound during run-time, after >> including a.php, which happens after C was already used. >> >> johannes >> >> >
-- DerOetzi

Johannes Schlueter

11 years ago
On Mon, 2015-04-13 at 17:23 +0200, Johannes Schlüter wrote:
> Why am I saying it makes the language more complex? - Your proposal > seems to miss mentioning when exactly the method is executed. what is
Ah, I missed this Trigger for “magic” method call: First call to class, either first call to __construct(...) or first call to any public or protected static method or property of the class Which means adding a flag, which has to be checked on every method call or other access to *any* class and leads to completely "random" (from perspective of average user) behavior while this feature can't be used for an "obvious" task like registering itself as plugin into some registry. -1 johannes

Johannes Ott

11 years ago
Am 13.04.2015 um 18:54 schrieb Johannes Schlüter:
> On Mon, 2015-04-13 at 17:23 +0200, Johannes Schlüter wrote: >> Why am I saying it makes the language more complex? - Your proposal >> seems to miss mentioning when exactly the method is executed. what is > > Ah, I missed this > > Trigger for “magic” method call: First call to class, either > first call to __construct(...) or first call to any public or > protected static method or property of the class > > Which means adding a flag, which has to be checked on every method call > or other access to *any* class and leads to completely "random" (from > perspective of average user) behavior while this feature can't be used > for an "obvious" task like registering itself as plugin into some > registry. > > -1 > > johannes >
The obvious task is to initial the state of the class before usage as I wrote inside my introduction of the RFC. I think there is no "random" behavior (not more then other so called "magic" methods). But I see that I forgot this point in the discussion part because we already had this point on the prediscussion. Will add this tomorrow, with my exact answer to this point, Thanks for reminder. https://wiki.php.net/rfc/static_class_constructor
-- DerOetzi

Johannes Schlueter

11 years ago
On Mon, 2015-04-13 at 21:14 +0200, Johannes Ott wrote:
> The obvious task is to initial the state of the class before usage as I > wrote inside my introduction of the RFC.
That is one possible task users are going to try doing. Users are more creative.
> I think there is no "random" behavior (not more then other so called > "magic" methods). But I see that I forgot this point in the discussion > part because we already had this point on the prediscussion. Will add > this tomorrow, with my exact answer to this point,
It is "random" in the sense that a seemingly unrelated change somewhere in an application changes order in other places, too. johannes

François Laupretre

11 years ago
> De : Johannes Ott [mailto:mail@deroetzi.de] > finally I managed to do my first RFC draft. > > https://wiki.php.net/rfc/static_class_constructor > > I hope I have done everything correct so far and I'm looking forward to > your feedback on it.
Interesting. It also allows to respect PSR-1, which says 'Files SHOULD either declare symbols (classes, functions, constants, etc.) or cause side-effects (e.g. generate output, change .ini settings, etc.) but SHOULD NOT do both' (see https://github.com/flaupretre/automap/blob/develop/src/classes/Automap.php for an example). Comments : - __static is ambiguous and does not reflect the method's role. Even if longer, I definitely prefer __staticInit or, even better, __classInit. - IMO, the method should be called when the class is created, just after every parent class and implemented interfaces are created. If some load a bunch of classes they don't use, that's their problem, not ours. It is not a reason to follow a two-step process. A properly written program uses autoloading and loads the classes it needs only. Another argument is that I'm not sure it is possible to catch every case of read access to static properties. - I think you should provide a destructor method because there can be a need to cleanup/close the resources opened when initializing the class. It can be open files, sockets, or any other resource, but, IMO, they need to be explicitly closed when the request ends. Regards François

Johannes Ott

11 years ago
Am 13.04.2015 um 18:02 schrieb François Laupretre:
>> De : Johannes Ott [mailto:mail@deroetzi.de] >> finally I managed to do my first RFC draft. >> >> https://wiki.php.net/rfc/static_class_constructor >> >> I hope I have done everything correct so far and I'm looking forward to >> your feedback on it. > > Interesting. It also allows to respect PSR-1, which says 'Files SHOULD either declare symbols (classes, functions, constants, etc.) or cause side-effects (e.g. generate output, change .ini settings, etc.) but SHOULD NOT do both' (see https://github.com/flaupretre/automap/blob/develop/src/classes/Automap.php for an example). > > Comments : > > - __static is ambiguous and does not reflect the method's role. Even if longer, I definitely prefer __staticInit or, even better, __classInit. >
Okay I will think about the alternative names, I somehow like __classInit you proposed or __classConstruct
> - IMO, the method should be called when the class is created, just after every parent class and implemented interfaces are created. If some load a bunch of classes they don't use, that's their problem, not ours. It is not a reason to follow a two-step process. A properly written program uses autoloading and loads the classes it needs only. Another argument is that I'm not sure it is possible to catch every case of read access to static properties.
On the one hand I think we should respect most of the user-land code as far as possible and on the other hand I think there should be the possibility to handle the being initialized or not initialized inside the class action.
> > - I think you should provide a destructor method because there can be a need to cleanup/close the resources opened when initializing the class. It can be open files, sockets, or any other resource, but, IMO, they need to be explicitly closed when the request ends. >
Please see my answer 4 in discussion section for this: https://wiki.php.net/rfc/static_class_constructor#need_of_a_class_destructor_to_handle_unload_order_dependencies
> Regards > > François > >
-- DerOetzi

Alex Sky

11 years ago
> Okay I will think about the alternative names, I somehow like > __classInit you proposed or __classConstruct
I like simple: __init()

Levi Morrison

11 years ago
> - IMO, the method should be called when the class is created, just after every parent class and implemented interfaces are created.
In general I think static class data and static class constructors are a sign of poorly designed code, which means I am against this feature. If it does make it though I agree with this statement above. The behavior proposed in the RFC makes it non-obvious when the class constructor is run and would much prefer it happening immediately after definition.

Johannes Ott

11 years ago
Am 14.04.2015 um 00:16 schrieb Levi Morrison:
>> - IMO, the method should be called when the class is created, just after every parent class and implemented interfaces are created. > > In general I think static class data and static class constructors are > a sign of poorly designed code, which means I am against this feature.
Thanks for the flowers as we would say in Germany! I didn't hear I'm doing poor code for a long time now :(
-- DerOetzi

Johannes Schlueter

11 years ago
On Tue, 2015-04-14 at 11:49 +0200, Johannes Ott wrote:
> Am 14.04.2015 um 00:16 schrieb Levi Morrison: > >> - IMO, the method should be called when the class is created, just after every parent class and implemented interfaces are created. > > > > In general I think static class data and static class constructors are > > a sign of poorly designed code, which means I am against this feature. > > Thanks for the flowers as we would say in Germany! I didn't hear I'm > doing poor code for a long time now :(
If static elements need initialisation they apparently have state. If they have state they in 95% of the cases shouldn't be static but bound to an object instance. Yes there might be cases where this is a good design, but the language should provide the syntax sugar to lead the 95% to write good code instead of leading them to write bad code which can be tested harder and where changes might have unexpected side-effects. So even if you're code is good and your own use-case is good design, which I won't question, I don't think advertising it to all users by providing a language feature is good. johannes

Johannes Ott

11 years ago
Am 13.04.2015 um 18:48 schrieb Cesar Rodas:
> Instead of having a method, why don't we allow body expr to be
evaluated as we see things in the Class definition?
> > <?php > > class foobar > { > static protected $conn = new mysqli('localhost', 'my_user',
'my_password', 'my_db');
> } > > It's cleaner but it breaks compatibility with everything :-( > > -- > César D. Rodas > Open Source developer > +595-983-161124 > PGP: F9ED A265 A3AB C8A1 D145 7368 158A 0336 C707 0AA6
Hello César, you accidently wrote this answer only to me, so I forward this to the mailing list together with my answer. First of all I would not store any resource handle, like a mysql connection you did in your example, directly to a static member. (Will add another point on this to the discussion-section the next days) https://wiki.php.net/rfc/static_class_constructor#discussions Second point is that with your suggested syntax only single methods can be used to initialize the static member. With my proposal you will be able to do more complex initialization algorithms. For example open a config-file with fopen, read the content to a static member and close the config-file again with fclose. Regards
-- DerOetzi

Levi Morrison

11 years ago
On Mon, Apr 13, 2015 at 7:37 AM, Johannes Ott <mail@deroetzi.de> wrote:
> Hi, > > finally I managed to do my first RFC draft. > > https://wiki.php.net/rfc/static_class_constructor > > I hope I have done everything correct so far and I'm looking forward to > your feedback on it.
The static constructor cannot be called by the user, which means it shouldn't even register as a function. Additionally, it is always private. Given these constraints I would prefer to change this syntax: class Config { private static $arIni; private static function __static() { $self::$arIni = parse_ini_file('config.ini'); } } To this (borrowed from Java): class Config { private static $arIni; static { $self::$arIni = parse_ini_file('config.ini'); } } I do not see any reason to do the former unless the latter has conflicts in the parser, which I don't think it would.

Marcio Almada

11 years ago
Hi, 2015-04-13 10:37 GMT-03:00 Johannes Ott <mail@deroetzi.de>:
> Hi, > > finally I managed to do my first RFC draft. > > https://wiki.php.net/rfc/static_class_constructor > > I hope I have done everything correct so far and I'm looking forward to > your feedback on it. > > As I already mentioned in the prediscussion thread here: > > For being my first change to the PHP core, I would be very happy, if > someone of the "old stager" would help me with the implementation of > this RFC if it is accepted. > > Regards > > -- > DerOetzi > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php >
I know this is still a draft, but conceptually I don't like this feature. I always avoid static state on classes as much as possible and I think this should be avoided. But this is my personal opinion. Some suggestions: In case the RFC passes, I'd prefer to have a `static{ ... }` block instead of yet another static __ method. The reason is that this kind of construct is already known on other languages as `static{...}` and that feels more organized IMMO. In case you decide to proceed with the magic method approach, I'd prefer the more verbose `__staticConstruct` name because it seems clearer. Thanks, Márcio

Johannes Ott

11 years ago
As the RFC is still in draft status, I will stop for now answering any issues on the purpose of this RFC. I will now rewrite some things at the draft, and come back to the discussion in this threads after those changes I will focus on the following open issues I have identified during the last two days discussion here. I identified the follwing issues to clearify more: - "Magic" method vs. static block (as it is done by JAVA) - Naming of the method - Why the trigger should be like it is proposed by the RFC And I will focus again on the contra arguments during last days discussion and the pre-draft discussion some weeks ago. In my point of view all of those arguments I read so far can be summarized to the single argument: You should avoid static class state and for this a initialization method, because this is "poor" designed code and "crucial" things can be done with it. Please feel free to correct me, if I got anyone's argument against the RFC wrong! I hope I can finish the changes to the drafts during the weekend, afterwards I will answer your comments just by linking to the specific RFC-Section. Thanks and regards
-- DerOetzi

Dan Ackroyd

11 years ago
Hi Johannes, On 13 April 2015 at 13:37, Johannes Ott <mail@deroetzi.de> wrote:
> Hi, > > finally I managed to do my first RFC draft. > > https://wiki.php.net/rfc/static_class_constructor > > I hope I have done everything correct so far and I'm looking forward to > your feedback on it.
Here is some feedback then: From the RFC:
> - Trigger for “magic” method call: First call to class, either first call to __construct(...) or first call to any public or protected static method or property of the class
I don't think this doesn't cover all cases that it needs to. For example calling unserialze on some data that results in a class that has this static initializer should mean that it would be invoked? Similarly ReflectionClass::newInstanceWithoutConstructor also creates a class in a way that isn't covered in the RFC, but presumably should be. From the RFC.
> the static class constructor should be able to throw a catchable Exception > for user error handling, if it is not possible to initialize. The open issue for > me is, what the behavior on recalling the static class constructor afterward is. > There are two possible behaviors I do not know which one I really would prefer so far.
You missed the third and in my opinion correct one; the static initializer would only ever be called once. If the programmer catches the exception and forces the program to continue, that is their own problem. Adding complexity to a language to try to work around programmers ignoring exceptions is not a good idea. I think the RFC needs to be clearer in the specification of the order that static initialization is called in. e.g. class B extends class A. Both A and B have static initialization blocks. When a user calls `new B()` which static initialization block is called first? cheers Dan

Johannes Ott

11 years ago
Am 14.04.2015 um 16:33 schrieb Dan Ackroyd:
> Here is some feedback then: > > From the RFC: >> - Trigger for “magic” method call: First call to class, either first call to __construct(...) or first call to any public or protected static method or property of the class > > I don't think this doesn't cover all cases that it needs to. For > example calling unserialze on some data that results in a class that > has this static initializer should mean that it would be invoked? > Similarly ReflectionClass::newInstanceWithoutConstructor also creates > a class in a way that isn't covered in the RFC, but presumably should > be. >
Your correct I forgot this two scenarios of first calls to a class. Thanks for the hint! But in the new draft (v0.4) I'm preparing at the moment I'll try to formulate the trigger "first call" more common by comparing it to the same trigger for __autoload function. Then these two scenarios should be included as well.
> From the RFC. >> the static class constructor should be able to throw a catchable Exception >> for user error handling, if it is not possible to initialize. The open issue for >> me is, what the behavior on recalling the static class constructor afterward is. >> There are two possible behaviors I do not know which one I really would prefer so far. > > You missed the third and in my opinion correct one; the static > initializer would only ever be called once.
I think this is suggested behaviour 1, isn't it? "On recall of the static class constructor a fatal error is raised to ensure it is only called once..."
>If the programmer catches > the exception and forces the program to continue, that is their own > problem. Adding complexity to a language to try to work around > programmers ignoring exceptions is not a good idea. >
I agree to this point that ignoring an exception is a bad idea. I think the example-code is a bit confusing, because the empty catch-block gives the impression that the exception is ignored. But what is really meant by behaviour 2 is that the programmer has the opportunity to handle the exception not by exiting his programm, but by cleaning up the situation and afterwards retry the code which failed before. I will try to clearify this in one of the next draft versions.
> I think the RFC needs to be clearer in the specification of the order > that static initialization is called in. e.g. class B extends class A. > Both A and B have static initialization blocks. When a user calls `new > B()` which static initialization block is called first? >
While rewriting the draft for v0.4 I although noticed this gap inside specification and came to the conclusion that this is the third "first call" scenario I haved missed. Because inheritance by using keyword extends is already a first call to class A.
> cheers > Dan >
Thanks for really constructive feedback, that one really helps me for the next draft of rfc Regards
-- DerOetzi

Dan Ackroyd

11 years ago
On 14 April 2015 at 17:39, Johannes Ott <mail@deroetzi.de> wrote:
> Am 14.04.2015 um 16:33 schrieb Dan Ackroyd: > >> Here is some feedback then: >>
Johannes Ott wrote:
> But in the new draft (v0.4) I'm preparing at the moment I'll try to > formulate the trigger "first call" more common by comparing it to the > same trigger for __autoload function.
Hmm. The language will need to be quite clear then - sometimes classes are autoloaded when they aren't instantiated. e.g. for just normal reflection of classes, the class is autoloaded if it isn't already present, or some people may autoload all the things ahead of time, to avoid any delay when responding to a request. Johannes Ott wrote:
>Danack wrote: >> You missed the third and in my opinion correct one; the static >> initializer would only ever be called once. > I think this is suggested behaviour 1, isn't it?
I read that section very differntly. "On recall of the static class constructor a fatal error is raised to ensure it is only called once and initialized before exception was thrown properties will not be reinitialized." I read that as it implies that it would be possible to call the static initializer again, and in that case a fatal error would be thrown. If the initializer is only ever called once, it would be clearer to say that as a 'positive' statement, as opposed to saying a 'negative' of what wouldn't be allowed - i.e. single statements are clearer than any kind of double-negative statements. Johannes Ott wrote:
>Danack wrote: >> I think the RFC needs to be clearer in the specification of the order >> that static initialization is called in. > Because inheritance by using keyword > extends is already a first call to class A.
tbh, it's quite likely that looking at Java or some other language that has static initializers and copying that behaviour for precedence could be the best choice. However the behaviour might not be mappable directly, due to differences in PHP's inheritance and class model, in particular the late static binding. class A { static function __staticInit() { static::foo(); } static function foo() { echo "This is A::foo\n"; } } class B extends A { static function foo() { echo "This is B::foo\n"; } } B::__staticInit(); //Output is 'This is B::foo' It's pretty easy to imagine some cases where there are circular dependencies....so the RFC would need to be precise about exactly what the rules are. cheers Dan

Jakub Kubíček

11 years ago
I would rather appreciate run-time class properties initialization. Best regards, Kubo2 2015-04-13 15:37 GMT+02:00 Johannes Ott <mail@deroetzi.de>:

Johannes Ott

11 years ago
Am 13.04.2015 um 15:37 schrieb Johannes Ott:
> Hi, > > finally I managed to do my first RFC draft. > > https://wiki.php.net/rfc/static_class_constructor > > I hope I have done everything correct so far and I'm looking forward to > your feedback on it. > > As I already mentioned in the prediscussion thread here: > > For being my first change to the PHP core, I would be very happy, if > someone of the "old stager" would help me with the implementation of > this RFC if it is accepted. > > Regards >
Hi there, there was a really bad situation in my life the last week, so I'm not able to do anything on this Draft for some days, I will pause it for some weeks now Regards
-- DerOetzi

Alex Sky

10 years ago
2015-04-28 11:29 GMT+03:00 Johannes Ott <mail@deroetzi.de>:
> Am 13.04.2015 um 15:37 schrieb Johannes Ott: >> Hi, >> >> finally I managed to do my first RFC draft. >> >> https://wiki.php.net/rfc/static_class_constructor >> >> I hope I have done everything correct so far and I'm looking forward to >> your feedback on it. >> >> As I already mentioned in the prediscussion thread here: >> >> For being my first change to the PHP core, I would be very happy, if >> someone of the "old stager" would help me with the implementation of >> this RFC if it is accepted. >> >> Regards >> > > Hi there, > > there was a really bad situation in my life the last week, so I'm not > able to do anything on this Draft for some days, I will pause it for > some weeks now > > Regards > > -- > DerOetzi > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php >
Hi. Is any progress on the implementation? Thank.