So now I want to do my first own proposal for a new function in PHP and
I hope doing it right with starting a discussion here first.
The purpose of this suggestion is to introduce a static constructor,
which is called before the first call to class either static or
non-static to initialize some static properties which are needed by the
class.
I think about two different possible syntax to realize that purpose:
Version 1 (similar to Java):
class A {
private static $var;
static {
//Do some code here to initialize self::$var;
}
}
Version 2 (a new magic method):
class B {
private static $var;
private static function __static() {
//Do some code here to initialize self::$var;
}
}
My prefered code would be version 2 at the moment.
Looking forward to your feedback,
--
DerOetzi
The purpose of this suggestion is to introduce a static constructor,
which is called before the first call to class either static or
non-static to initialize some static properties which are needed by the
class.
Hey Johannes,
Why can't this be done at autoloading time?
Marco Pivetta
Am 12.03.2015 um 00:30 schrieb Marco Pivetta:
Hey Johannes,
Why can't this be done at autoloading time?
In my opinion this should not be done on autoloading time, but as a own
method inside the class for two reasons.
-
Not every class is loaded with autoload-functions, but although
directly with include_once or required_once functions -
The initialization of needed things inside the class should be in the
domain of the class itself and not of someone outside. Thats why I
proposed to make it a private magic method not a public one. To do by
autoload or by calling it inside the included file after
class-definition like
class B {
...
} B::__static();
would require to make it public and means to give the responsibility to
do the necessary initialization to someone else.
Marco Pivetta
Regards,
DerOetzi
So now I want to do my first own proposal for a new function in PHP and
I hope doing it right with starting a discussion here first.The purpose of this suggestion is to introduce a static constructor,
which is called before the first call to class either static or
non-static to initialize some static properties which are needed by the
class.
Can you give an example use case for when this would be useful? I'm
struggling to think of one for which there isn't already an established
coding pattern...
Regards,
Rowan Collins
[IMSoP]
So now I want to do my first own proposal for a new function in PHP and
I hope doing it right with starting a discussion here first.The purpose of this suggestion is to introduce a static constructor,
which is called before the first call to class either static or
non-static to initialize some static properties which are needed by the
class.Can you give an example use case for when this would be useful? I'm
struggling to think of one for which there isn't already an established
coding pattern...
Notably, user-land enums. You don't want public constructors because
you don't want it instantiated except for each enum property. You also
need it to run on class creation, not afterwards.
I think we'd be better off adding language-level enums than static
constructors though.
Am 12.03.2015 um 05:17 schrieb Levi Morrison:
So now I want to do my first own proposal for a new function in PHP and
I hope doing it right with starting a discussion here first.The purpose of this suggestion is to introduce a static constructor,
which is called before the first call to class either static or
non-static to initialize some static properties which are needed by the
class.Can you give an example use case for when this would be useful? I'm
struggling to think of one for which there isn't already an established
coding pattern...Notably, user-land enums. You don't want public constructors because
you don't want it instantiated except for each enum property. You also
need it to run on class creation, not afterwards.I think we'd be better off adding language-level enums than static
constructors though.
Yes indeed user-land enums are one of the use cases I use this feature
at the moment. But there some other use cases as well:
- Nearly all of my classes have a static LogAdapter $LOG which has to
be intialized with Classname once.
class A {
private static $LOG;
public static function __static() {
self::$LOG = LogAdapter::getLogger(self::class);
}
} A::__static();
The LogAdapter by it selfs although have a __static() method to prepare
the Log4PHP-Framework I'm using.
- Some Config classes which are intelligent wrapper-classes to some
ini-files and database have to read in some things first.
class Config {
private static $ini;
public static function __static() {
self::$ini = parse_ini_file(...);
// or
// Read from Database
}
} Config::__static();
- For a multilanguage system I determine the user language by useragent
and fallback to default-language if user language ist not supported.
class Text {
private static $defaultLang;
private static $userLang;
public static function __static() {
self::$defaultLang = //read from Database
self::setUserLanguageOrFallback();
}
private static function setUserLanguageOrFallback() {
...
}
} Text::__static();
- Already prepare some PDO-statements which needed all over the class
class Example {
private static $stmts = [];
public static function __static() {
self::$stmts['select1'] = new DBSelect('SELECT * FROM table WHERE
col1
=:clause');
}
} Example::__static();
That are the examples I can found on a quick search over my code.
Regards,
--
DerOetzi
Johannes Ott wrote on 12/03/2015 08:54:
Am 12.03.2015 um 05:17 schrieb Levi Morrison:
So now I want to do my first own proposal for a new function in PHP and
I hope doing it right with starting a discussion here first.The purpose of this suggestion is to introduce a static constructor,
which is called before the first call to class either static or
non-static to initialize some static properties which are needed by the
class.Can you give an example use case for when this would be useful? I'm
struggling to think of one for which there isn't already an established
coding pattern...
Notably, user-land enums. You don't want public constructors because
you don't want it instantiated except for each enum property. You also
need it to run on class creation, not afterwards.I think we'd be better off adding language-level enums than static
constructors though.Yes indeed user-land enums are one of the use cases I use this feature
at the moment. But there some other use cases as well:
Most of these examples are just crying out to be real objects, not
static classes. You might not want to be creating them every time you
use them, but that's what patterns like Singletons and Dependency
Injection are for.
- Nearly all of my classes have a static LogAdapter $LOG which has to
be intialized with Classname once.class A {
private static $LOG;public static function __static() { self::$LOG = LogAdapter::getLogger(self::class); }
} A::__static();
The LogAdapter by it selfs although have a __static() method to prepare
the Log4PHP-Framework I'm using.
This particular example could be achieved with a static getLogger()
method, which does the initialisation check the first time the logger is
needed, rather than the first time class A is needed.
To my mind, the creation of a class in memory should not have
side-effects - you should be able to assume that all your classes exist
at the same time, before any of your code runs. The only justification
for not acting that way would be Python-style metaclasses, where the
creation of a class definition was the responsibility of some other
object, and that's a power to be wielded very carefully.
Regards,
Rowan Collins
[IMSoP]
Most of these examples are just crying out to be real objects, not
static classes. You might not want to be creating them every time you
use them, but that's what patterns like Singletons and Dependency
Injection are for.
I really disagree to this. Singletons are a typical FactoryPattern, none
of the examples except the LogAdapter itself are doing implementation of
a factory. They are just initialize some computed static variables once
in the live time of the class mostly for internal usage of the class.
That is nearly like initializing a class constant, but in my opinion a
constant should not have a "complex" algorithm (For example conditions
or read from filesystem). That should be encapsulated inside a proper
method body.
In use case 1 Dependency Injection maybe another solution but does not
exactly do what I want to do.
- Nearly all of my classes have a static LogAdapter $LOG which has to
be intialized with Classname once.class A {
private static $LOG;public static function __static() { self::$LOG = LogAdapter::getLogger(self::class); }
} A::__static();
The LogAdapter by it selfs although have a __static() method to prepare
the Log4PHP-Framework I'm using.This particular example could be achieved with a static getLogger()
method, which does the initialisation check the first time the logger is
needed, rather than the first time class A is needed.
yes that would be another valid pattern. And would be prefered for me if
I use the Logger in 1 or 2 out of 10 class methods, which are not used
very often. But mostly I'm using it in 9 or 10 of 10 methods, which are
invoked several times through lifetime. So doing a null check each time
is a overhead of calculation which can be avoided with this static
constructor pattern.
To my mind, the creation of a class in memory should not have
side-effects - you should be able to assume that all your classes exist
at the same time, before any of your code runs.
I agree the real creation(/parsing) of the class should have no
side-effects. But the static constructor is not executed at the
creation-time of the class but directly before first access on the
class. That are two totally different moments in the lifecycle of the
class and does not block the possibility to first read all classes
without any side-effects.
The only justification
for not acting that way would be Python-style metaclasses, where the
creation of a class definition was the responsibility of some other
object, and that's a power to be wielded very carefully.
The static constructor or sometimes although called class constructor is
a well known OOP-pattern implemented by different languages like C#
or Java.
Regards,
--
DerOetzi
Johannes Ott wrote on 12/03/2015 14:51:
That is nearly like initializing a class constant, but in my opinion a
constant should not have a "complex" algorithm (For example conditions
or read from filesystem). That should be encapsulated inside a proper
method body.
I agree, but as such, I think that method should be called somewhere by
the code, even if only by a DI container, not happen automagically and
slurp in data from global state.
Consider your "prepare some SQL queries" example - it has a dependency
on a database connection, so that now has to be global state; if that in
turn is lazily initialised, it needs to get the connection string from
yet more global state, and so on. By using the class constructor, you
are forced to hard-code those dependencies - there's no parameters to
pass them in, and you can't pre-initialise them from outside the class,
because nothing on the class can run before the class constructor.
So doing a null check each time
is a overhead of calculation which can be avoided with this static
constructor pattern.
Presumably the engine would need to perform some implicit equivalent of
"if ( ! self::$initialised )" on each call to decide if the static
constructor needs to be called or not, so the overhead is not completely
eliminated.
I agree the real creation(/parsing) of the class should have no
side-effects. But the static constructor is not executed at the
creation-time of the class but directly before first access on the
class. That are two totally different moments in the lifecycle of the
class and does not block the possibility to first read all classes
without any side-effects.
OK, I misunderstood this part. So this is like the private constructor
of a Singleton, a lazy initialisation the first time you use it. (Sorry
to bang on about Singletons; they're what I know, so I'm trying to
understand the similarities and differences.)
Incidentally, note that a recent RFC to add the ability to declare a
class as static failed by 12 votes to 5 -
https://wiki.php.net/rfc/abstract_final_class - and much of the
discussion was around static implementations being generally inferior to
instances, so I'm not alone in challenging designs that rely on them.
Regards,
Rowan Collins
[IMSoP]
Johannes Ott wrote on 12/03/2015 14:51:
That is nearly like initializing a class constant, but in my opinion a
constant should not have a "complex" algorithm (For example conditions
or read from filesystem). That should be encapsulated inside a proper
method body.I agree, but as such, I think that method should be called somewhere by
the code, even if only by a DI container, not happen automagically and
slurp in data from global state.Consider your "prepare some SQL queries" example - it has a dependency
on a database connection, so that now has to be global state; if that in
turn is lazily initialised, it needs to get the connection string from
yet more global state, and so on. By using the class constructor, you
are forced to hard-code those dependencies - there's no parameters to
pass them in, and you can't pre-initialise them from outside the class,
because nothing on the class can run before the class constructor.So doing a null check each time
is a overhead of calculation which can be avoided with this static
constructor pattern.Presumably the engine would need to perform some implicit equivalent of
"if ( ! self::$initialised )" on each call to decide if the static
constructor needs to be called or not, so the overhead is not completely
eliminated.I agree the real creation(/parsing) of the class should have no
side-effects. But the static constructor is not executed at the
creation-time of the class but directly before first access on the
class. That are two totally different moments in the lifecycle of the
class and does not block the possibility to first read all classes
without any side-effects.OK, I misunderstood this part. So this is like the private constructor
of a Singleton, a lazy initialisation the first time you use it. (Sorry
to bang on about Singletons; they're what I know, so I'm trying to
understand the similarities and differences.)Incidentally, note that a recent RFC to add the ability to declare a
class as static failed by 12 votes to 5 -
https://wiki.php.net/rfc/abstract_final_class - and much of the
discussion was around static implementations being generally inferior to
instances, so I'm not alone in challenging designs that rely on them.Regards,
I thought it sounded familiar. Also check the list archive for "A
modest proposal: __constructStatic" from a month ago. It was rejected
then, too.
Really, I cannot think of any cases where I want to have a static class
self-initialize with global data (because all statics are just globals
with a fancy dress) where I wouldn't slap myself for being stupid and
not just making a proper object, factory, DI, or any number of other
options are are 10x more testable and reusable and verifiable. Sure
there's places you could use it; there's just much better options
already available in the language and have been for a decade.
--Larry Garfield
Am 12.03.2015 17:28 schrieb "Larry Garfield" larry@garfieldtech.com:
I thought it sounded familiar. Also check the list archive for "A modest
proposal: __constructStatic" from a month ago. It was rejected then, too.
That proposal was about a completely different issue.
But you are right, it was dismissed using the same "I don't like static,
use objects" nonargument.
best regards
Patrick
I thought it sounded familiar. Also check the list archive for "A modest proposal: __constructStatic" from a month ago. It was rejected then, too.
Really, I cannot think of any cases where I want to have a static class self-initialize with global data (because all statics are just globals with a fancy dress) where I wouldn't slap myself for being stupid and not just making a proper object, factory, DI, or any number of other options are are 10x more testable and reusable and verifiable. Sure there's places you could use it; there's just much better options already available in the language and have been for a decade.
I guess it’s just “dreaming” about classes, which are first-class citizens, like in smalltalk/ruby/python :-)
But this is just an approximation, anyway.
Real class-objects probably would never happen in PHP.
--
Alexey Zakhlestin
https://github.com/indeyets
PGP key: http://indeyets.ru/alexey.zakhlestin.pgp.asc
Am 12.03.2015 um 16:57 schrieb Rowan Collins:
Johannes Ott wrote on 12/03/2015 14:51:
That is nearly like initializing a class constant, but in my opinion a
constant should not have a "complex" algorithm (For example conditions
or read from filesystem). That should be encapsulated inside a proper
method body.I agree, but as such, I think that method should be called somewhere by
the code, even if only by a DI container, not happen automagically and
slurp in data from global state.
It is called somewhere in the code namely inside the static constructor :D
Consider your "prepare some SQL queries" example - it has a dependency
on a database connection, so that now has to be global state; if that in
turn is lazily initialised, it needs to get the connection string from
yet more global state, and so on. By using the class constructor, you
are forced to hard-code those dependencies - there's no parameters to
pass them in, and you can't pre-initialise them from outside the class,
because nothing on the class can run before the class constructor.
Okay that is a point each developer should be aware and decide by his self.
So doing a null check each time
is a overhead of calculation which can be avoided with this static
constructor pattern.Presumably the engine would need to perform some implicit equivalent of
"if ( ! self::$initialised )" on each call to decide if the static
constructor needs to be called or not, so the overhead is not completely
eliminated.
Yes you are right but I think it can be done more efficiently inside the
interpreter using some struct flags then have to parse each time inside
a coded part in the application.
Incidentally, note that a recent RFC to add the ability to declare a
class as static failed by 12 votes to 5 -
https://wiki.php.net/rfc/abstract_final_class - and much of the
discussion was around static implementations being generally inferior to
instances, so I'm not alone in challenging designs that rely on them.
I will check the rfc later but "static class" sounds strange.
Regards,
Regards,
DerOetzi
Johannes Ott wrote on 12/03/2015 17:05:
Am 12.03.2015 um 16:57 schrieb Rowan Collins:
Johannes Ott wrote on 12/03/2015 14:51:
That is nearly like initializing a class constant, but in my opinion a
constant should not have a "complex" algorithm (For example conditions
or read from filesystem). That should be encapsulated inside a proper
method body.
I agree, but as such, I think that method should be called somewhere by
the code, even if only by a DI container, not happen automagically and
slurp in data from global state.It is called somewhere in the code namely inside the static constructor :D
What I meant is that it is not executed by any piece of code the user
writes, but directly by the engine based on a magic hook.
So doing a null check each time
is a overhead of calculation which can be avoided with this static
constructor pattern.
Presumably the engine would need to perform some implicit equivalent of
"if ( ! self::$initialised )" on each call to decide if the static
constructor needs to be called or not, so the overhead is not completely
eliminated.Yes you are right but I think it can be done more efficiently inside the
interpreter using some struct flags then have to parse each time inside
a coded part in the application.
Yes, point taken.
Incidentally, note that a recent RFC to add the ability to declare a
class as static failed by 12 votes to 5 -
https://wiki.php.net/rfc/abstract_final_class - and much of the
discussion was around static implementations being generally inferior to
instances, so I'm not alone in challenging designs that rely on them.I will check the rfc later but "static class" sounds strange.
Basically, all your examples imply classes that consist only of static
members - they're not using static helpers to create instances, they're
using them instead of instances. This was what was mean by "static
class" in that proposal. Personally, I was in favour of that, since I
think such classes do exist, and a syntax for declaring them would be
useful, but the most common argument against was that we should be
finding ways for people to not need such classes, rather than supporting
them better. This proposal sounds like it's adding facilities to classes
that belong on objects, or ... somewhere else.
Regards,
Rowan Collins
[IMSoP]
Am 12.03.2015 18:56 schrieb "Rowan Collins" rowan.collins@gmail.com:
Johannes Ott wrote on 12/03/2015 17:05:
So doing a null check each time
is a overhead of calculation which can be avoided with this static
constructor pattern.Presumably the engine would need to perform some implicit equivalent of
"if ( ! self::$initialised )" on each call to decide if the static
constructor needs to be called or not, so the overhead is not completely
eliminated.Yes you are right but I think it can be done more efficiently inside the
interpreter using some struct flags then have to parse each time inside
a coded part in the application.Yes, point taken.
I don't think such a flag is neccessary at all. Any class. at the moment,
comes from one or another file that is included / required. And all of the
code in these files outside the class definition, is then immediately
executed. The only thing neccessary would be to check, just before that
execution begins, which of the new classes have such an initializer method,
and then call that, before the execution of the file itself begins.
Incidentally that is something that cannot be emulated with the
autoloader-does-it approach, because the autoloader can only do that after
the include/require is complete - i.e. code within the file will not yet
see the class as initialized (in that approach).
(I wrote "at the moment" above, because the currently proposed anonymous
class thing would need extra thought when the anonymous class has such an
initializer method...)
best regards
Patrick
Patrick Schaaf wrote on 12/03/2015 18:40:
Am 12.03.2015 18:56 schrieb "Rowan Collins" <rowan.collins@gmail.com
mailto:rowan.collins@gmail.com>:Johannes Ott wrote on 12/03/2015 17:05:
So doing a null check each time
is a overhead of calculation which can be avoided with this static
constructor pattern.Presumably the engine would need to perform some implicit
equivalent of
"if ( ! self::$initialised )" on each call to decide if the static
constructor needs to be called or not, so the overhead is not
completely
eliminated.Yes you are right but I think it can be done more efficiently
inside the
interpreter using some struct flags then have to parse each time inside
a coded part in the application.Yes, point taken.
I don't think such a flag is neccessary at all. Any class. at the
moment, comes from one or another file that is included / required.
And all of the code in these files outside the class definition, is
then immediately executed. The only thing neccessary would be to
check, just before that execution begins, which of the new classes
have such an initializer method, and then call that, before the
execution of the file itself begins.
This was my initial interpretation, but Johannes has explained that that
is not the intention of this proposal. Instead, it is intended to be
called on first use of the class; a subtle difference, but given this
code:
class A { public static $foo; private function __static() { echo 'A'; } }
class B { public static $foo; private function __static() { echo 'B'; } }
B::$foo = 1;
A::$foo = 2;
Running the magic at definition time will echo 'A' then 'B'; running it
on first use will echo 'B' then 'A'.
Incidentally that is something that cannot be emulated with the
autoloader-does-it approach, because the autoloader can only do that
after the include/require is complete - i.e. code within the file will
not yet see the class as initialized (in that approach).
For that scenario, the autoloader can immediately call
$class_name::__static() or whatever. The only edge-case is when you try
to also put loose code in your class definition files, but why would you
need to?
Regards,
Rowan Collins
[IMSoP]
Am 12.03.2015 um 20:34 schrieb Rowan Collins:
Patrick Schaaf wrote on 12/03/2015 18:40:
Am 12.03.2015 18:56 schrieb "Rowan Collins" <rowan.collins@gmail.com
mailto:rowan.collins@gmail.com>:Johannes Ott wrote on 12/03/2015 17:05:
So doing a null check each time
is a overhead of calculation which can be avoided with this static
constructor pattern.Presumably the engine would need to perform some implicit
equivalent of
"if ( ! self::$initialised )" on each call to decide if the static
constructor needs to be called or not, so the overhead is not
completely
eliminated.Yes you are right but I think it can be done more efficiently
inside the
interpreter using some struct flags then have to parse each time
inside
a coded part in the application.Yes, point taken.
I don't think such a flag is neccessary at all. Any class. at the
moment, comes from one or another file that is included / required.
And all of the code in these files outside the class definition, is
then immediately executed. The only thing neccessary would be to
check, just before that execution begins, which of the new classes
have such an initializer method, and then call that, before the
execution of the file itself begins.This was my initial interpretation, but Johannes has explained that that
is not the intention of this proposal. Instead, it is intended to be
called on first use of the class; a subtle difference, but given this
code:
Correct! On that point I agree with Rowan, for the fact that there a lot
of of libraries and application in user-land which don't use autoload
function to include the classes when required, but include all classes
at the startup, but on different paths through the application only use
a few of them. Or in future the interpreter or a maybe application
server will have some optimization which loads the classes it may need
already in memory, but never use it then.
The static constructor pattern should avoid overhead in all of this
current and possible future cases.
Instead the static constructor should behave like the following user-code:
class A {
private static $bInitialized = false;
...
private static function init() {
if (self::$bInitialized) {
return;
}
...
self::$bInitialized = true;
}
public static function a() {
self::init();
...
}
public function b() {
self::init();
...
}
...
}
So we need the "$bInitialized" flag in the struct.
Another point already mentioned for the example where the static
constructor uses database connection or something there can be a lot of
unnecassary db-connections opened on creation time.
Regards,
--
DerOetzi
Am 12.03.2015 um 18:55 schrieb Rowan Collins:
Johannes Ott wrote on 12/03/2015 17:05:
Am 12.03.2015 um 16:57 schrieb Rowan Collins:
Johannes Ott wrote on 12/03/2015 14:51:
That is nearly like initializing a class constant, but in my opinion a
constant should not have a "complex" algorithm (For example conditions
or read from filesystem). That should be encapsulated inside a proper
method body.
I agree, but as such, I think that method should be called somewhere by
the code, even if only by a DI container, not happen automagically and
slurp in data from global state.It is called somewhere in the code namely inside the static
constructor :DWhat I meant is that it is not executed by any piece of code the user
writes, but directly by the engine based on a magic hook.
Yes but thats why it is called magic method, isn't it ;) No serious if
it is clearly defined what the magic method __static() does and when it
is invoked, it should be okay. All of the magic methods are doing like this.
Incidentally, note that a recent RFC to add the ability to declare a
class as static failed by 12 votes to 5 -
https://wiki.php.net/rfc/abstract_final_class - and much of the
discussion was around static implementations being generally inferior to
instances, so I'm not alone in challenging designs that rely on them.I will check the rfc later but "static class" sounds strange.
I had time to read the linked rfc now. If I understand it right that rfc
Means a Util-class-pattern for util-classes which should not be
extendable. I don't see the sense of that now, why I util class with
only static methods which typically is defined as abstract should not be
extendable by other, I cannot remember to have this use cased in my 15
years of OOP-programming but okay maybe there are such use cases. But my
proposal is another one see my next comment on your comment below.
Basically, all your examples imply classes that consist only of static
members - they're not using static helpers to create instances, they're
using them instead of instances. This was what was mean by "static
class" in that proposal. Personally, I was in favour of that, since I
think such classes do exist, and a syntax for declaring them would be
useful, but the most common argument against was that we should be
finding ways for people to not need such classes, rather than supporting
them better. This proposal sounds like it's adding facilities to classes
that belong on objects, or ... somewhere else.
Okay thats a point I have to clearify I see, maybe my examples where to
much shorten by me. In my examples I'm showing only the initialize parts
of the classes, the initialized properties are used all over the class
in static as well in non-static context.
For use case 1 the LogAdapter for example is a singleton instance per
class, that's why in my understanding of dependcy Injection it is not
working with it (correct me please if I'm wrong). And this singleton is
used to log in all methods of the class/object.
Surely I would be able to get the specializied Singleton instance with
doing some code like the following two examples:
Example 1 (As I already would be prefered solution for me if I use the
Logger only in a few of the methods and not in the majority of it):
class A {
private static $LOG;
private static function initLogger() {
if (self::$LOG = null) {
self::$LOG = LogAdapter::getLogger(self::class);
}
}
public static function a() {
self::initLogger();
self::$LOG->debug();
....
self::$LOG->error();
}
public function b() {
self::initLogger();
...
self::$LOG->error();
}
...
}
or Example 2: (which is in my opinion really hard to read and to much to
write)
class A {
public static function a() {
LogAdapter::getLogger(self::class)->debug();
....
LogAdapter::getLogger(self::class)->error();
}
public function b() {
...
LogAdapter::getLogger(self::class)->error();
}
...
}
With my solution of a static constructor it would look like this.
class A {
private static $LOG;
private static function __static() {
self::$LOG = LogAdapter::getLogger(self::class);
}
public static function a() {
self::$LOG->debug();
....
self::$LOG->error();
}
public function b() {
...
self::$LOG->error();
}
...
}
On huge classes with 10 or more methods all using the LOG reference this
is in my opinion the cleanest and shortest solution.
I hope I could make the example more clear?! If you need more examples
please let me know.
Regards
DerOetzi
Johannes Ott wrote on 12/03/2015 19:45:
All of the magic methods are doing like this.
I thought you might say that, but the only thing remotely similar I can
think of is a destructor, which gets called when an object goes out of
scope; the others are all the implementation of, or instead of, some
specific piece of code:
__construct() runs every time for new Foo
__get() and __set() runs every time for property access with -> on an
undefined property
__call() and __callStatic() runs every time for method access on an
undefined property
The difference with this is that you never know which part of your code
will actually trigger it, because it only runs some of the time.
Okay thats a point I have to clearify I see, maybe my examples where to
much shorten by me. In my examples I'm showing only the initialize parts
of the classes, the initialized properties are used all over the class
in static as well in non-static context.
OK, so these are classes which share certain resources between all
instances, represented by these static properties. In that case, would
it be reasonable to say that the initialization of these properties
needs to happen the first time an instance is created? If so, the
if(self::$initialized) check needs to run only as often as
__construct(), not every time any method is called.
If the static methods are public, and used for something other than
managing instances (Factory / Singleton / etc), then are they really the
responsibility of the same class? i.e. do you have a Utility class
hiding in there pretending to be part of the instance/factory class?
For use case 1 the LogAdapter for example is a singleton instance per
class, that's why in my understanding of dependcy Injection it is not
working with it (correct me please if I'm wrong). And this singleton is
used to log in all methods of the class/object.
In a system using Dependency Injection, all dependencies have to be
passed in somewhere; a static dependency could be injected into a static
property by a static method, but it still has to come from somewhere.
Your implementation is pulling it from a global variable (or a public
static, which is effectively the same thing) rather than waiting for it
to be provided.
Note also that you don't need to create 5 loggers just because you have
5 objects needing a logger - you pass in (or acquire from a global
variable) references to the same instance each time - so the fact that
it's an instance property rather than a static one is really no hardship.
public function __construct() {
$this->logger = LogAdapter::getLogger(self::class);
}
Surely I would be able to get the specializied Singleton instance with
doing some code like the following two examples:
[examples trimmed]
If not using any injection, I would implement it as follows:
class A {
private static $LOG;
private static function getLogger() {
if (self::$LOG = null) {
self::$LOG = LogAdapter::getLogger(self::class);
}
return self::$LOG;
}
public static function a() {
self::getLogger()->debug();
....
self::getLogger()->error();
}
public function b() {
...
self::getLogger()->error();
}
...
}
Not much more verbose than your version with __static, but a lot less
magic, and therefore easier to debug. Slightly worse performance, with
the extra method call, but I doubt if it would add up to enough to worry
about.
Thinking about it, you could even leave the way open for a different
strategy by using $this->getLogger() instead of self::getLogger() -
right now, that's 100% equivalent, but it makes it easier to change
later to use injection, or be able to override the logger on particular
instances, etc.
This is often the problem with magic methods, be they at the language
level or in a framework: they tend to lock you into working in a
particular way, where more explicit code can be more readily refactored.
Regards,
Rowan Collins
[IMSoP]
Am 12.03.2015 um 21:33 schrieb Rowan Collins:
Johannes Ott wrote on 12/03/2015 19:45:
All of the magic methods are doing like this.
I thought you might say that, but the only thing remotely similar I can
think of is a destructor, which gets called when an object goes out of
scope; the others are all the implementation of, or instead of, some
specific piece of code:
__construct() runs every time for new Foo
__get() and __set() runs every time for property access with -> on an
undefined property
__call() and __callStatic() runs every time for method access on an
undefined propertyThe difference with this is that you never know which part of your code
will actually trigger it, because it only runs some of the time.
I cannot see any difference because the trigger is defined really clear
for each of the functions
For Example Class Foo implements all of those functions the triggers
would be
__construct -> triggered each time I write "new Foo()"
__destruct -> triggered each time a instance loses his last reference
and is removed by garbage collection.
__get() and __set() -> triggered each time I try to access a property
of my foo instance which is not accessible (not defined or private) from
outside Foo writing $foo->notfromoutside
__call() -> triggered each time I try to call a non-static method on a
instance of Foo which is not accessible from outside writing
$foo->notfromoutside()
__callStatic() -> triggered each time I try to access static method of
the class which is not accessible from outside writing Foo::notfromoutside()
And finally the proposed new one
__static() -> triggered on the first time I write either "new Foo()" or
"Foo::eachStaticPublicOrProtectedMethodOrProperty()"
I really don't see what __static() has more "magic" or unclear trigger
then each of the other so called magic methods?!
Do keep away any doubt, in my opinion none of the so called magic
methods have any kind of magic. They are just language patterns you
should understand how they work before you start to use them in your
code! So "private static function __static() {...}" is just another
pattern you have to understand before you use. If you don't understand
how it works you should not use. That is the same for all language
patterns and elements, for example: loops, conditions, scalar-operators,
etc.
And as I already told a static class constructor is a kind of well-known
standard function in mostly all modern OOP-languages. So professional
OOP-developer should be or become very fast familiar with this pattern.
Okay thats a point I have to clearify I see, maybe my examples where to
much shorten by me. In my examples I'm showing only the initialize parts
of the classes, the initialized properties are used all over the class
in static as well in non-static context.OK, so these are classes which share certain resources between all
instances, represented by these static properties. In that case, would
it be reasonable to say that the initialization of these properties
needs to happen the first time an instance is created? If so, the
if(self::$initialized) check needs to run only as often as
__construct(), not every time any method is called.
If you look closer at my example you will see, that the property is not
just shared between instances (then the normal constructor Could be
indeed be the place to initialize), but it is shared although with
different static methods inside the class.
If the static methods are public, and used for something other than
managing instances (Factory / Singleton / etc), then are they really the
responsibility of the same class? i.e. do you have a Utility class
hiding in there pretending to be part of the instance/factory class?
See my example 2 for a Config Wrapper class:
abstract class Config {
private static $arIni;
private static function __static() {
self::$arIni = parse_ini_file('config.ini');
//For example check whether all required fields are in the file
or throw Exception if not.
}
public static function `getHostname()` {
return self::$arIni['hostname'];
}
public static function getDBConnectionString() {
return ' self::$arIni['dbdriver'] . '://' .
self::$arIni['dbuser'] . '@' .self::$arIni['dbhost'] ...;
}
...
}
The Config class is in response for all config Parameters inside
config.ini and needs to read in this file before it can to it's work
correctly. No hiding of Factory/Singleton/instance in this.
And i although see no DI or Singleton pattern to use here to get the
same functionality, if you want to use like Config::getHostname() and
not like Config::getInstance()->getHostname() which is really
unnecessary abstraction level for nothing in my opinion!
For use case 1 the LogAdapter for example is a singleton instance per
class, that's why in my understanding of dependcy Injection it is not
working with it (correct me please if I'm wrong). And this singleton is
used to log in all methods of the class/object.In a system using Dependency Injection, all dependencies have to be
passed in somewhere; a static dependency could be injected into a static
property by a static method, but it still has to come from somewhere.
Your implementation is pulling it from a global variable (or a public
static, which is effectively the same thing) rather than waiting for it
to be provided.
Note also that you don't need to create 5 loggers just because you have
5 objects needing a logger - you pass in (or acquire from a global
variable) references to the same instance each time - so the fact that
it's an instance property rather than a static one is really no hardship.public function __construct() {
$this->logger = LogAdapter::getLogger(self::class);
}
As I said above, if I only use in instance context I agree with the
normal constructor.
Surely I would be able to get the specializied Singleton instance with
doing some code like the following two examples:
[examples trimmed]If not using any injection, I would implement it as follows:
class A {
private static $LOG;
private static function getLogger() {
if (self::$LOG = null) {
self::$LOG = LogAdapter::getLogger(self::class);
}
return self::$LOG;
}public static function a() {
self::getLogger()->debug();
....
self::getLogger()->error();
}public function b() {
...
self::getLogger()->error();
}...
}
Not much more verbose than your version with __static, but a lot less
magic, and therefore easier to debug. Slightly worse performance, with
the extra method call, but I doubt if it would add up to enough to worry
about.
It is not only about the extra method-call but although the additional
Null check inside this method call.
Let's do some calculation for that: in average I have 5 calls of the
logger per method the message is used 10 times during the programm.
Now we already have 49 unnecessary method calls (with all needed to do
for the interpreter like preparing stack, copying the returns etc.) and
40 unnecassary null checks inside (as we agreed before that is not
counted fully, because the evaluation of the flag will although take
some time but can be more efficient inside the interpreter). Let's now
think only about 10 such methods we are already at a count of 490
unnecessary method calls. Maybe only slightly worse performance but it
is a performance issue! And there is this very old performance rule: a
lot of small performance issues can become quickly to a huge one.
I have counted the calls in code of self::$LOG-> inside one small
private webproject of mine with less logging implemented yet. But there
are already 128 calls. If you multiply by 10 you already have 1280 calls
on runtime, I think that is a performance issue.
Thinking about it, you could even leave the way open for a different
strategy by using $this->getLogger() instead of self::getLogger() -
right now, that's 100% equivalent, but it makes it easier to change
later to use injection, or be able to override the logger on particular
instances, etc.
Sorry I'm confused now after reading this sentence and I have to ask
now, because I'm not sure anymore, whether you really have understand
the difference between static and non-static context and the inheritance
of classes inside PHP, especially the difference between $this, static,
self?
How can $this->getLogger() be equivalent to self::getLogger();
First contra Example:
class A {
private function getLoggerNonStatic() {
echo 'A->getLoggerNonStatic';
}
private static function getLoggerStatic() {
echo 'A::getLoggerStatic';
}
public static function call() {
$this->getLogger();
}
public static function call2() {
self::getLogger();
}
}
A::call() //Fatal error $this in static context?
A::call2() //Output A::getLoggerStatic;
Second contra Example:
class A {
protected function getLoggerNonStatic() {
echo 'A->getLoggerNonStatic()';
}
protected function getLoggerStatic() {
echo 'A::getLoggerStatic';
}
public function call() {
$this->getLoggerNonStatic();
self::getLoggerStatic();
static::getLoggerStatic();
}
}
class B extends A {
protected function getLoggerNonStatic() {
echo 'B->getLoggerNonStatic()';
}
protected function getLoggerStatic() {
echo 'B::getLoggerStatic';
}
}
$b = new B();
$b->call();
Output:
B->getLoggerNonStatic();
A::getLoggerStatic();
B::getLoggerStatic();
As you can see line 1 and 2 of the output not equivalent even by class,
but I want to log functionality of A as functionality of A not of B!
This is often the problem with magic methods, be they at the language
level or in a framework: they tend to lock you into working in a
particular way, where more explicit code can be more readily refactored.
Beside the performance for me and for each clean coder I know (btw as a
Software Architect I'm in charge of code quality of a huge Java project
at the moment, so I think I know what I'm talking about), to read 100
times self::getLogger()->... and have to think about whether the
getLogger() method (which should be by "standard" oop-naming conventions
ONLY a three lines of code getter and nothing else) is doing the magic
of getting a instance first, is much more worse then understanding once
what a static class constructor does and then just use self::$LOG->... A
"magic" getter identified by prefix get doing anything beside giving
back a class member is really far away from clean code!
Regards,
DerOetzi
Johannes Ott wrote:
And i although see no DI or Singleton pattern to use here to get the
same functionality, if you want to use like Config::getHostname() and
not like Config::getInstance()->getHostname() which is really
unnecessary abstraction level for nothing in my opinion!
It is possible, however, to add static wrapper methods to access the
singleton's methods, like
public static function getHostname()
{
return self::getInstance()->_getHostname();
}
It is not only about the extra method-call but although the additional
Null check inside this method call.Let's do some calculation for that: in average I have 5 calls of the
logger per method the message is used 10 times during the programm.
Now we already have 49 unnecessary method calls (with all needed to do
for the interpreter like preparing stack, copying the returns etc.) and
40 unnecassary null checks inside (as we agreed before that is not
counted fully, because the evaluation of the flag will although take
some time but can be more efficient inside the interpreter). Let's now
think only about 10 such methods we are already at a count of 490
unnecessary method calls. Maybe only slightly worse performance but it
is a performance issue! And there is this very old performance rule: a
lot of small performance issues can become quickly to a huge one.I have counted the calls in code of self::$LOG-> inside one small
private webproject of mine with less logging implemented yet. But there
are already 128 calls. If you multiply by 10 you already have 1280 calls
on runtime, I think that is a performance issue.
It seems to me that the logger example is not appropriate to show
performance penalties due to unnecessary method calls and null checks,
because the actual task of the logger (write to a file/database, or even
send an email) is easily much more demanding performance-wise.
Anyhow, in my humble opionion, there had been enough initial discussion
on this idea, and it would be reasonable to proceed with the actual RFC.
See "How To Create an RFC"[1] and especially "The Mysterious PHP RFC
Process and How You Can Change the Web"[2]. :)
[1] https://wiki.php.net/rfc/howto
[2] https://blogs.oracle.com/opal/entry/the_mysterious_php_rfc_process
--
Christoph M. Becker
Am 13.03.2015 um 01:33 schrieb Christoph Becker:
Johannes Ott wrote:
And i although see no DI or Singleton pattern to use here to get the
same functionality, if you want to use like Config::getHostname() and
not like Config::getInstance()->getHostname() which is really
unnecessary abstraction level for nothing in my opinion!It is possible, however, to add static wrapper methods to access the
singleton's methods, likepublic static function
getHostname()
{
return self::getInstance()->_getHostname();
}
I really do not understand why everybody here seems to try to use
Singleton where ever it is possible. Indeed Singletons are a nice Design
Pattern, but they are no universal remedy for every problem just for the
fact you can use it there as well.
Why are in your opinion static members are not allowed to hold more
complexe datastructures then simple scalars? Please give me one valid
argument why I should double all of the functions in my code for a
static and non-static access beside the argument that I "have to" use a
Singleton. As I already said this is a abstraction level that makes
absolutly no sense.
It is not only about the extra method-call but although the additional
Null check inside this method call.Let's do some calculation for that: in average I have 5 calls of the
logger per method the message is used 10 times during the programm.
Now we already have 49 unnecessary method calls (with all needed to do
for the interpreter like preparing stack, copying the returns etc.) and
40 unnecassary null checks inside (as we agreed before that is not
counted fully, because the evaluation of the flag will although take
some time but can be more efficient inside the interpreter). Let's now
think only about 10 such methods we are already at a count of 490
unnecessary method calls. Maybe only slightly worse performance but it
is a performance issue! And there is this very old performance rule: a
lot of small performance issues can become quickly to a huge one.I have counted the calls in code of self::$LOG-> inside one small
private webproject of mine with less logging implemented yet. But there
are already 128 calls. If you multiply by 10 you already have 1280 calls
on runtime, I think that is a performance issue.It seems to me that the logger example is not appropriate to show
performance penalties due to unnecessary method calls and null checks,
because the actual task of the logger (write to a file/database, or even
send an email) is easily much more demanding performance-wise.
This depends on the log-level lot of the calls are debug and traces
which are not active in production enviroment. Only a few of them are
info, warn or error. But you are right it isn't a perfect example for
benchmark the current idea, but as a whole I'm sure to have a slightly
performance improvment with this static constructor as well.
But anywhere performance was not the main matter of concern of this
proposal, but to make the possibility to write cleaner code with less
lines.
Anyhow, in my humble opionion, there had been enough initial discussion
on this idea, and it would be reasonable to proceed with the actual RFC.
See "How To Create an RFC"[1] and especially "The Mysterious PHP RFC
Process and How You Can Change the Web"[2]. :)[1] https://wiki.php.net/rfc/howto
[2] https://blogs.oracle.com/opal/entry/the_mysterious_php_rfc_process
Okay thanks for the hint I will prepare the rfc on weekend then. BTW for
me haven't done c- programming for a long time it would be a joy if
someone who has mor experience with PHP-core would help to implement
the RFC if it will be voted positive. Or even give me some hints to the
structure of the core where to start this. Already did some research on
that and I'm thinking somewhere zend_object_handler.c but not sure about
this.
Thanks in advantage to all for the in my opinion good discussion so far
about this issue. Gave me some good hints, where I have to have a closer
look, when writing the RFC now.
Regards
DerOetzi
Johannes Ott wrote on 13/03/2015 09:53:
Am 13.03.2015 um 01:33 schrieb Christoph Becker:
Johannes Ott wrote:
And i although see no DI or Singleton pattern to use here to get the
same functionality, if you want to use like Config::getHostname() and
not like Config::getInstance()->getHostname() which is really
unnecessary abstraction level for nothing in my opinion!
It is possible, however, to add static wrapper methods to access the
singleton's methods, likepublic static function
getHostname()
{
return self::getInstance()->_getHostname();
}I really do not understand why everybody here seems to try to use
Singleton where ever it is possible. Indeed Singletons are a nice Design
Pattern, but they are no universal remedy for every problem just for the
fact you can use it there as well.Why are in your opinion static members are not allowed to hold more
complexe datastructures then simple scalars?
Complex data structures, yes (and I see the use of __static in such
cases). Architectural logic gluing one class to another (as in your
Logger example), no.
Please give me one valid
argument why I should double all of the functions in my code for a
static and non-static access beside the argument that I "have to" use a
Singleton.
You don't need to double all the calls. In fact, the majority of your
code should not even know the Singleton is there, because they should be
treating it as though they have an instance. At certain key points in
your code (constructors, the top of methods) you add $foo =
Foo::getInstance();, then all your calls look like $foo->doBar();
That's the way I do it, anyway - treat getting the instance for a
singleton like importing a variable from global scope, which is
basically what it's doing.
Regards,
Rowan Collins
[IMSoP]
Sorry, replying to myself to add a couple of thoughts / clarifications:
Rowan Collins wrote on 13/03/2015 11:53:
Johannes Ott wrote on 13/03/2015 09:53:
Why are in your opinion static members are not allowed to hold more
complexe datastructures then simple scalars?Complex data structures, yes (and I see the use of __static in such
cases). Architectural logic gluing one class to another (as in your
Logger example), no.
I think I've come up with a better distinction: state belongs in
instances, whereas data can exist globally. A logger object
encapsulates state, so a pointer to one is stateful information; storing
that state statically is what feels wrong.
A singleton in this case says "there is a bunch of stateful information
here, but everywhere in the current code needs the same instance of it".
Please give me one valid
argument why I should double all of the functions in my code for a
static and non-static access beside the argument that I "have to" use a
Singleton.You don't need to double all the calls. In fact, the majority of your
code should not even know the Singleton is there, because they should
be treating it as though they have an instance. At certain key points
in your code (constructors, the top of methods) you add $foo =
Foo::getInstance();, then all your calls look like $foo->doBar();That's the way I do it, anyway - treat getting the instance for a
singleton like importing a variable from global scope, which is
basically what it's doing.
In other words, saying "Foo::getInstance()->bar();" is like using
"$GLOBALS['foo']->bar();" saying "$foo = Foo::getInstance();
$foo->bar();" is like saying "global $foo; $foo->bar();"
The act of retrieving the singleton is separate from the act of using it.
Regards,
Rowan Collins
[IMSoP]
Am 13.03.2015 um 14:36 schrieb Rowan Collins:
Sorry, replying to myself to add a couple of thoughts / clarifications:
Rowan Collins wrote on 13/03/2015 11:53:
Johannes Ott wrote on 13/03/2015 09:53:
Why are in your opinion static members are not allowed to hold more
complexe datastructures then simple scalars?Complex data structures, yes (and I see the use of __static in such
cases). Architectural logic gluing one class to another (as in your
Logger example), no.
I think I've come up with a better distinction: state belongs in
instances, whereas data can exist globally. A logger object
encapsulates state, so a pointer to one is stateful information; storing
that state statically is what feels wrong.
I agree with this statement only a bit because it makes static context
obsolete: real states belong to instances, no doubt about this. The
logger state for example as LogAdapter is a Singleton-Factory is still
inside the singleton instance and in this case only a new reference to
the class specific instance is stored as kind of "shortcut" for less
code and maybe little bit of performance to the class itself. That is
although the DI-pattern on runtime, but in static class context.
But anyway a class having any kind of static property inside is stateful
by definition of static. The best example is already each
singleton-class, it has the state initialized or not intialized. That is
the basic concept of static-context at all, the only stateless things
inside class context are class-constants.
Or another example the posted ConfigFile-Wrapper, what is the sense of
wrapping another level arround by doing it with a singleton instance.
The class itself by its definition already represents the state of
config.ini-File that will not change during the current runtime because
it is global data. In my opinion there is absolutely no sense beside
the effect that I have more code to write and especially to read.
As conclusion as long as there is this static, stateful class context
each coder can decide at everytime which static properties he sets to
define the state of the class, as a good programmer you should be aware
to use this careful, but that has absolutley nothing todo with the
proposed RFC.
But while you have already for instances a defined language method
called __construct to set the inital state of an instance, there is no
such language method to set the initial state of the class, which would
be the __static(); In my opinion this would even help beginners to
understand how careful they have to be with static context, because they
are doing something inside a special "magic" method.
BTW even the singleton pattern could have a slightly better performance
with this feature, if you do not have the need of parameters to the
instance constructor code can look like this:
abstract class Singleton() {
private static $instance;
private static function __static() {
self::$instance = new Object();
}
public static function getInstance() {
return self::$instance;
}
}
A singleton in this case says "there is a bunch of stateful information
here, but everywhere in the current code needs the same instance of it".Please give me one valid
argument why I should double all of the functions in my code for a
static and non-static access beside the argument that I "have to" use a
Singleton.You don't need to double all the calls. In fact, the majority of your
code should not even know the Singleton is there, because they should
be treating it as though they have an instance. At certain key points
in your code (constructors, the top of methods) you add $foo =
Foo::getInstance();, then all your calls look like $foo->doBar();That's the way I do it, anyway - treat getting the instance for a
singleton like importing a variable from global scope, which is
basically what it's doing.In other words, saying "Foo::getInstance()->bar();" is like using
"$GLOBALS['foo']->bar();" saying "$foo = Foo::getInstance();
$foo->bar();" is like saying "global $foo; $foo->bar();"The act of retrieving the singleton is separate from the act of using it.
Regards,
I think as Christoph wrote we should now do a cut here for the inital
discussion, because we are in a circle now. I will now get on at the RFC
process, and will prepare the RFC-draft asap.
I will try to summarize as good as possible all discussion points we had
the last days here. And I will try to figure out as clearly as possible
how each point really touchs the purpose of the new requested language
feature or is a general problem if you use static context a "wrong" way,
although outside the static constructor.
I want to say thank you especially to you Rowan, for the so far very
good discussion, which gives a lot of input to me for writting the RFC.
Regards,
DerOetzi
Johannes Ott wrote on 13/03/2015 15:35:
I think as Christoph wrote we should now do a cut here for the inital
discussion, because we are in a circle now. I will now get on at the RFC
process, and will prepare the RFC-draft asap.I will try to summarize as good as possible all discussion points we had
the last days here. And I will try to figure out as clearly as possible
how each point really touchs the purpose of the new requested language
feature or is a general problem if you use static context a "wrong" way,
although outside the static constructor.I want to say thank you especially to you Rowan, for the so far very
good discussion, which gives a lot of input to me for writting the RFC.
Yes, you are probably right. Thank you for putting up with my relative
ignorance of the concepts, and I'm glad you found the discussion
constructive.
I do now see that there are some where this would be useful,
particularly in lieu of enum or metaclass logic for pre-processing the
pure data in a class definition. Where I vary is in the validity of some
of the use cases presented, which feel like they're making static
methods do things that belong in an object, but that's largely a matter
of opinion and coding style rather than functionality.
I think the most similar thing we have at the moment is destructors -
you can't know exactly when they will be called, only that they will be,
so have to be very careful not to put too much logic into them which can
have odd side effects, generate errors at unexpected moments, etc.
Regards,
Rowan Collins
[IMSoP]
Am 13.03.2015 um 01:33 schrieb Christoph Becker:
Johannes Ott wrote:
And i although see no DI or Singleton pattern to use here to get the
same functionality, if you want to use like Config::getHostname() and
not like Config::getInstance()->getHostname() which is really
unnecessary abstraction level for nothing in my opinion!It is possible, however, to add static wrapper methods to access the
singleton's methods, likepublic static function
getHostname()
{
return self::getInstance()->_getHostname();
}It is not only about the extra method-call but although the additional
Null check inside this method call.Let's do some calculation for that: in average I have 5 calls of the
logger per method the message is used 10 times during the programm.
Now we already have 49 unnecessary method calls (with all needed to do
for the interpreter like preparing stack, copying the returns etc.) and
40 unnecassary null checks inside (as we agreed before that is not
counted fully, because the evaluation of the flag will although take
some time but can be more efficient inside the interpreter). Let's now
think only about 10 such methods we are already at a count of 490
unnecessary method calls. Maybe only slightly worse performance but it
is a performance issue! And there is this very old performance rule: a
lot of small performance issues can become quickly to a huge one.I have counted the calls in code of self::$LOG-> inside one small
private webproject of mine with less logging implemented yet. But there
are already 128 calls. If you multiply by 10 you already have 1280 calls
on runtime, I think that is a performance issue.It seems to me that the logger example is not appropriate to show
performance penalties due to unnecessary method calls and null checks,
because the actual task of the logger (write to a file/database, or even
send an email) is easily much more demanding performance-wise.Anyhow, in my humble opionion, there had been enough initial discussion
on this idea, and it would be reasonable to proceed with the actual RFC.
See "How To Create an RFC"[1] and especially "The Mysterious PHP RFC
Process and How You Can Change the Web"[2]. :)[1] https://wiki.php.net/rfc/howto
[2] https://blogs.oracle.com/opal/entry/the_mysterious_php_rfc_process
I tried to get some RFC karma for my wiki account, following those lines:
Email internals@lists.php.net requesting RFC karma for your wiki
account. In the email, remind people about the RFC you plan to create.
Note that RFC karma does not automatically give you karma to vote. See
https://wiki.php.net/rfc/voting#rfc_proposer
I didn't get any reaction on my mail for three days, is this normal or
have I done something wrong?
Thanks for your help
Regards,
DerOetzi
Johannes Ott wrote:
I tried to get some RFC karma for my wiki account, following those lines:
Email internals@lists.php.net requesting RFC karma for your wiki
account. In the email, remind people about the RFC you plan to create.
Note that RFC karma does not automatically give you karma to vote. See
https://wiki.php.net/rfc/voting#rfc_proposerI didn't get any reaction on my mail for three days, is this normal or
have I done something wrong?
As far as I can tell (I do not have a @php.net account) everything is
fine regarding your request. However, you've requested RFC karma on
Friday afternoon (UTC), and it's still weekend for most, so it may take
a while to be answered. As I understand it, any new RFC is too late for
PHP 7 anyway, so the delay shouldn't be a problem.
--
Christoph M. Becker
Hello Johannes,
in other mails you argue with Rowan about global state. I think it's
better to focus on innovation of "class context" in global scope, as
it's impossible to reason the disadvantages of global state away.
(Discussions on variable scope are painful too.)
And two questions:
- By definition cctor is guaranteed to be executed only once. Right?
- In your examples you employ some external resources in cctor (e.g.
logger, db, ini-files). Do you intend to propose a cdtor to release this
resources?
Thank you!
Am 13.03.2015 um 07:45 schrieb Crypto Compress:
Hello Johannes,
in other mails you argue with Rowan about global state. I think it's
better to focus on innovation of "class context" in global scope, as
it's impossible to reason the disadvantages of global state away.
(Discussions on variable scope are painful too.)And two questions:
- By definition cctor is guaranteed to be executed only once. Right?
Yes as the __static should be as well. See discussion with Rowan and
Patrick for details.
As I already told, I still have to clearify howto react if an exception
occurs inside the static class constructor. I prefer at the moment the
variant that a second try will raise a fatal error. But I will think
about this before I'll prepare the RFC.
- In your examples you employ some external resources in cctor (e.g.
logger, db, ini-files). Do you intend to propose a cdtor to release this
resources?
For my understanding the lifecycle of a class is from the loading of the
class definition to the end of the application or until some explicit
class unload (don't know whether php has such feature). If loading a
instance to a static member you must be aware that this instance will
live till the end of lifetime of the class (that is the basic of
static). But at the end I hope PHP is unsetting all static members as
well, which should trigger the destructor of this instance as well.
If PHP is not reacting like this (but I'm sure it is because without all
suggested singletons here would be a huge memory and connection leak as
well), I know what my second rfc would be :D
Second point if you open a resource to read some data inside a method
and do not assure that it is closed again either explicit inside the
method or on a global pattern (for example my DBAdapter I use to read,
holds a single connection to database which is closed at the very end of
run), this would be poor code inside the static constructor as well as
it would be inside every other method.
So I do not see the need of a explicit class deconstructor, because the
language should already react correctly on this issues as I can see so far
Regards,
DerOetzi
Am 13.03.2015 um 11:30 schrieb Johannes Ott:
Am 13.03.2015 um 07:45 schrieb Crypto Compress:
Hello Johannes,
in other mails you argue with Rowan about global state. I think it's
better to focus on innovation of "class context" in global scope, as
it's impossible to reason the disadvantages of global state away.
(Discussions on variable scope are painful too.)And two questions:
- By definition cctor is guaranteed to be executed only once. Right?
Yes as the __static should be as well. See discussion with Rowan and
Patrick for details.As I already told, I still have to clearify howto react if an exception
occurs inside the static class constructor. I prefer at the moment the
variant that a second try will raise a fatal error. But I will think
about this before I'll prepare the RFC.
- In your examples you employ some external resources in cctor (e.g.
logger, db, ini-files). Do you intend to propose a cdtor to release this
resources?
For my understanding the lifecycle of a class is from the loading of the
class definition to the end of the application or until some explicit
class unload (don't know whether php has such feature).
PHP has no explicit class domain unload feature and you do not plan to
implement this. Okay.
If loading a
instance to a static member you must be aware that this instance will
live till the end of lifetime of the class (that is the basic of
static). But at the end I hope PHP is unsetting all static members as
well, which should trigger the destructor of this instance as well.If PHP is not reacting like this (but I'm sure it is because without all
suggested singletons here would be a huge memory and connection leak as
well), I know what my second rfc would be :DSecond point if you open a resource to read some data inside a method
and do not assure that it is closed again either explicit inside the
method or on a global pattern (for example my DBAdapter I use to read,
holds a single connection to database which is closed at the very end of
run), this would be poor code inside the static constructor as well as
it would be inside every other method.
This "poor code inside the static constructor" seems to be a crucial
point of your RFC.
So I do not see the need of a explicit class deconstructor, because the
language should already react correctly on this issues as I can see so far
The language cannot know the order of dependencies and how to destruct them.
Regards,
Thank you!
Am 14.03.2015 um 07:49 schrieb Crypto Compress:
Am 13.03.2015 um 11:30 schrieb Johannes Ott:
Am 13.03.2015 um 07:45 schrieb Crypto Compress:
Hello Johannes,
in other mails you argue with Rowan about global state. I think it's
better to focus on innovation of "class context" in global scope, as
it's impossible to reason the disadvantages of global state away.
(Discussions on variable scope are painful too.)And two questions:
- By definition cctor is guaranteed to be executed only once. Right?
Yes as the __static should be as well. See discussion with Rowan and
Patrick for details.As I already told, I still have to clearify howto react if an exception
occurs inside the static class constructor. I prefer at the moment the
variant that a second try will raise a fatal error. But I will think
about this before I'll prepare the RFC.
- In your examples you employ some external resources in cctor (e.g.
logger, db, ini-files). Do you intend to propose a cdtor to release this
resources?
For my understanding the lifecycle of a class is from the loading of the
class definition to the end of the application or until some explicit
class unload (don't know whether php has such feature).PHP has no explicit class domain unload feature and you do not plan to
implement this. Okay.
Yes, this could be another RFC in future but not yet.
If loading a
instance to a static member you must be aware that this instance will
live till the end of lifetime of the class (that is the basic of
static). But at the end I hope PHP is unsetting all static members as
well, which should trigger the destructor of this instance as well.If PHP is not reacting like this (but I'm sure it is because without all
suggested singletons here would be a huge memory and connection leak as
well), I know what my second rfc would be :DSecond point if you open a resource to read some data inside a method
and do not assure that it is closed again either explicit inside the
method or on a global pattern (for example my DBAdapter I use to read,
holds a single connection to database which is closed at the very end of
run), this would be poor code inside the static constructor as well as
it would be inside every other method.This "poor code inside the static constructor" seems to be a crucial
point of your RFC.
As I already told several times, this poor code does NOT depend on a
static constructor method.
On the one hand the proposed static constructor is only a shorter
replacement of several valid user-land patterns doing the same feature
already, as you can see on the examples I or Patrick posted in this thread.
On the other hand I force the opinion, that a RFC should not be declined
for the reason someone, who is not understanding the feature and all of
if side-effects, maybe can do some crucial things with it, while it is
useful for programmers who know how to use it. Because in my opinion,
as I already tried to figure out, you can do crucial things with every
single language feature, which is already implemented yet, if you're not
knowing how it works and use it anyway.
So we should prohibit PHP and all other languages ;) (Just fun!)
So I do not see the need of a explicit class deconstructor, because the
language should already react correctly on this issues as I can see so
farThe language cannot know the order of dependencies and how to destruct
them.
A dependcy between destructors of instances, which the language have to
know by itself that sounds horrific indeed.
If one instance has destruction dependcies to other instances, you
should have used any kind of linking pattern between this instances so
the __destruct of the first instance destructed can handle the
destruction dependcies. At the moment I cannot think about any use case,
where this should be done another way. Maybe you have a concret example
for me, so I can think about this?
On destruction/unload of a class the language should be able to unset
static properties in random order for this reason, without any need of a
class destructor?!
Regards,
Thank you!
Regards,
DerOetzi
So I do not see the need of a explicit class deconstructor, because the
language should already react correctly on this issues as I can see so
far
The language cannot know the order of dependencies and how to destruct
them.
A dependcy between destructors of instances, which the language have to
know by itself that sounds horrific indeed.If one instance has destruction dependcies to other instances, you
should have used any kind of linking pattern between this instances so
the __destruct of the first instance destructed can handle the
destruction dependcies. At the moment I cannot think about any use case,
where this should be done another way. Maybe you have a concret example
for me, so I can think about this?
a) Implicit order of dependencies:
Logger should be the last destructed
DatabaseLogger before FileLogger before error_log-Logger...
b) How to destruct:
Write "Bye!" before closing socket.
On destruction/unload of a class the language should be able to unset
static properties in random order for this reason, without any need of a
class destructor?!
Reverse creation order.
Am 14.03.2015 um 18:34 schrieb Crypto Compress:
So I do not see the need of a explicit class deconstructor, because the
language should already react correctly on this issues as I can see so
far
The language cannot know the order of dependencies and how to destruct
them.
A dependcy between destructors of instances, which the language have to
know by itself that sounds horrific indeed.If one instance has destruction dependcies to other instances, you
should have used any kind of linking pattern between this instances so
the __destruct of the first instance destructed can handle the
destruction dependcies. At the moment I cannot think about any use case,
where this should be done another way. Maybe you have a concret example
for me, so I can think about this?a) Implicit order of dependencies:
Logger should be the last destructed
DatabaseLogger before FileLogger before error_log-Logger...
Okay the Implicit order for class unload should be safe, if you use the
Logger classes inside class A and class B, PHP should unload the class
Logger as last one after A and B. The unload order of DatabaseLogger
before FileLogger is a internal of the Logger Instances as Logger itself
is a singleton Factory:
For example:
class LogAdapter {
private static $instances = [];
public static getLoggerInstance($classname) {
if (!array_key_exists($classname, self::$instances)) {
self::$instances[$classname] = new Logger($classname);
}
return self::$instances[$classname];
}
}
class Logger() {
...
public __destruct() {
//Unload whatever you need.
}
}
b) How to destruct:
Write "Bye!" before closing socket.
To manage a socket connection is not a typical use case for a "global"
static class context because normal use case of a Socket class is to
manage different Sockets. So this is typically a instance pattern to
use, where the __destruct() can say bye.
On destruction/unload of a class the language should be able to unset
static properties in random order for this reason, without any need of a
class destructor?!Reverse creation order.
The random was just meant as placeholder for the fact that for static
properties it should not matter in which order they are unset by the
language. Can be reverse creation order or something else.
Regards
--
DerOetzi
Am 14.03.2015 um 18:34 schrieb Crypto Compress:
So I do not see the need of a explicit class deconstructor, because the
language should already react correctly on this issues as I can see so
far
The language cannot know the order of dependencies and how to destruct
them.
A dependcy between destructors of instances, which the language have to
know by itself that sounds horrific indeed.If one instance has destruction dependcies to other instances, you
should have used any kind of linking pattern between this instances so
the __destruct of the first instance destructed can handle the
destruction dependcies. At the moment I cannot think about any use case,
where this should be done another way. Maybe you have a concret example
for me, so I can think about this?a) Implicit order of dependencies:
Logger should be the last destructed
DatabaseLogger before FileLogger before error_log-Logger...b) How to destruct:
Write "Bye!" before closing socket.On destruction/unload of a class the language should be able to unset
static properties in random order for this reason, without any need of a
class destructor?!Reverse creation order.
Sorry for replying to myself, but I'm just preparing the RFC and while
reading the complete thread again. I think I now get the
misunderstanding I had on your destructor question and want to clear that:
In your examples you employ some external resources in cctor (e.g.
logger, db, ini-files). Do you intend to propose a cdtor to release
this resources?
I never ever would store any kind of resources (opening any kind of
connections to db, file, socket etc.) directly to the static context
without wrapping in a instance, because those are really dynamic handles
which need a proper constructor and destructor for the special connection.
If I use inside the static constructor only for reading purpose and have
to ensure that they are closed again. This can be done directly inside
the static constructor or in case of my DBAdapter holding the
DBConnection instance at the very end of my application.
For example
class Example {
private static $dataFromFile;
private static $dataFromDB;
private static $localReferenceToLogger;
private static function __static() {
//Read from file
$fHandle = fopen('example.ini', 'r');
self::$dataFromFile = fread($fHandle, 1024);
fclose($fHandle);
//Shorted DB Example
self::$dataFromDB = DBAdapter::readFromDB();
//Get local Reference to logger instance;
self::$localReferenceToLogger =
LogAdapter::getLoggerInstance(self::class);
}
}
//Somewhere at the end of my application
DBAdapter::closeAllConnections();
Hope I could get some light to the problem you meant?
Regards,
DerOetzi
I think I now get the misunderstanding I had on your destructor question
Sorry for confusion. My points are agnostic about implementation details
and concrete code. It's up to ppl to use this feature as they like.
- first point is a logical conclusion: If there is a cctor, there should
be a cdtor. - second point is about implicit order: Shutdown process will free in
reversed creation order. Classes don't have guaranteed creation order.
Am 15.03.2015 um 11:02 schrieb Crypto Compress:
I think I now get the misunderstanding I had on your destructor question
Sorry for confusion. My points are agnostic about implementation details
and concrete code. It's up to ppl to use this feature as they like.
Okay get your point, but as already discussed several times, the rfc
should not be declined for the reason a ppl, who doesn't understand when
to use static context or when not to use at all, can do crucial things.
Because he although can do without the static constructor.
For a horiffic example:
class Example {
private static $handler;
public function open() {
self::$handler = fopen('example.txt');
}
...
}
Example::open();
Indeed I have the opinion some beginner who is doing such horiffic code
maybe think more about what he is doing and especially about the side
effects inside a so called "magic" method, then outside.
- first point is a logical conclusion: If there is a cctor, there should
be a cdtor.
Okay the logical conclusion I can take in count. But doing 15 years of
OOP-programming now, I never had the need to have a cdtor, for a "valid"
usage of static context. And still after I have seen your examples,
which should all be done, as I explained, with instances instead of
direct static binding, I don't see the use case for a cdtor.
- second point is about implicit order: Shutdown process will free in
reversed creation order. Classes don't have guaranteed creation order.
But I hope shutdown process of PHP is as intelligent not do unload a
class which is needed in another class before this class?!
Regards,
DerOetzi
Okay get your point, but as already discussed several times, the rfc
should not be declined for the reason a ppl, who doesn't understand when
to use static context or when not to use at all, can do crucial things.
Because he although can do without the static constructor.For a horiffic example:
class Example {
private static $handler; public function open() { self::$handler = fopen('example.txt'); } ...
}
Example::open();
Indeed I have the opinion some beginner who is doing such horiffic code
maybe think more about what he is doing and especially about the side
effects inside a so called "magic" method, then outside.
I'm not clear what's so different between this and your logger example.
Here you have a file handle, which in PHP happens to be a special type
rather than an object, and are storing it in a static property; closing
the file handle has to be managed somehow, and this code is letting PHP
do this implicitly with what amounts to a destructor on the file handle
resource.
I never ever would store any kind of resources (opening any kind of
connections to db, file, socket etc.) directly to the static context
without wrapping in a instance, because those are really dynamic handles
which need a proper constructor and destructor for the special connection.
However many intermediate instances you create, at some point the
destructor has to run, and that will only happen once the static
reference is unset. Luckily, the Zend Engine will go through and garbage
collect all global and static variables at the end of a request, so you
can cheat that way.
Or, you can invalidate the objects, rather than destructing them, as
implied by your "DBAdapter::closeAllConnections()" example - there will
still be references to those connections live in your code if, for
instance, you have Example::$logger, and $logger->dbConnection. You
can't have an Example::cleanupResources() method, because it would fire
the static constructor on pages which hadn't used the class yet,
destroying the lazy-loading.
What you're really relying on is that the implicit static destructor
provided by the engine is good enough for the kinds of resource which a
static constructor should be dealing with. This is also true of many
objects, which don't technically need a custom destructor to close file
or DB handles, because Zend will reference count the resource, but more
advanced objects can do more advanced cleanup.
Regards,
--
Rowan Collins
[IMSoP]
Am 15.03.2015 um 19:47 schrieb Rowan Collins:
Okay get your point, but as already discussed several times, the rfc
should not be declined for the reason a ppl, who doesn't understand when
to use static context or when not to use at all, can do crucial things.
Because he although can do without the static constructor.For a horiffic example:
class Example {
private static $handler; public function open() { self::$handler = fopen('example.txt'); } ...
}
Example::open();
Indeed I have the opinion some beginner who is doing such horiffic code
maybe think more about what he is doing and especially about the side
effects inside a so called "magic" method, then outside.I'm not clear what's so different between this and your logger example.
Here you have a file handle, which in PHP happens to be a special type
rather than an object, and are storing it in a static property; closing
the file handle has to be managed somehow, and this code is letting PHP
do this implicitly with what amounts to a destructor on the file handle
resource.
The difference is as I told the Resource Handler is wrapped inside a
Singleton Instance with a destructor!
I never ever would store any kind of resources (opening any kind of
connections to db, file, socket etc.) directly to the static context
without wrapping in a instance, because those are really dynamic handles
which need a proper constructor and destructor for the special
connection.However many intermediate instances you create, at some point the
destructor has to run, and that will only happen once the static
reference is unset. Luckily, the Zend Engine will go through and garbage
collect all global and static variables at the end of a request, so you
can cheat that way.
I think that is no kind of a cheat, in my opinion this is the normal
behavior how static stored properties and instances, stored at them,
should be cleanup in every oop-language.
Or, you can invalidate the objects, rather than destructing them, as
implied by your "DBAdapter::closeAllConnections()" example - there will
still be references to those connections live in your code if, for
instance, you have Example::$logger, and $logger->dbConnection. You
can't have an Example::cleanupResources() method, because it would fire
the static constructor on pages which hadn't used the class yet,
destroying the lazy-loading.
You always talking about the singleton-pattern, as I although told
different times now, I have no resources directly stored in static
context but in singleton instances inside.
for example the DBAdapter::closeAllConnections():
public static closeAllConnections() {
while(count(self::$connections) > 0) {
self::$connections[0]->close();
unset(self::$connections[0]);
}
}
the same for LogAdapter.
What you're really relying on is that the implicit static destructor
provided by the engine is good enough for the kinds of resource which a
static constructor should be dealing with. This is also true of many
objects, which don't technically need a custom destructor to close file
or DB handles, because Zend will reference count the resource, but more
advanced objects can do more advanced cleanup.
Yes indeed this is true, and again same sentence: resources -> inside
instances not directly inside static property.
Regards,
--
DerOetzi
Am 15.03.2015 um 19:47 schrieb Rowan Collins:
Okay get your point, but as already discussed several times, the rfc
should not be declined for the reason a ppl, who doesn't understand when
to use static context or when not to use at all, can do crucial things.
Because he although can do without the static constructor.For a horiffic example:
class Example {
private static $handler; public function open() { self::$handler = fopen('example.txt'); } ...
}
Example::open();
Indeed I have the opinion some beginner who is doing such horiffic code
maybe think more about what he is doing and especially about the side
effects inside a so called "magic" method, then outside.
I'm not clear what's so different between this and your logger example.
Here you have a file handle, which in PHP happens to be a special type
rather than an object, and are storing it in a static property; closing
the file handle has to be managed somehow, and this code is letting PHP
do this implicitly with what amounts to a destructor on the file handle
resource.The difference is as I told the Resource Handler is wrapped inside a
Singleton Instance with a destructor!
Whether the instance is a singleton, or has a non-default destructor,
makes no difference - the cleanup happens during PHP's shutdown
procedure. As it happens, resources have the equivalent of destructors
internally, so even the object instance vs resource doesn't actually
matter here.
Or, you can invalidate the objects, rather than destructing them, as
implied by your "DBAdapter::closeAllConnections()" example - there will
still be references to those connections live in your code if, for
instance, you have Example::$logger, and $logger->dbConnection. You
can't have an Example::cleanupResources() method, because it would fire
the static constructor on pages which hadn't used the class yet,
destroying the lazy-loading.
You always talking about the singleton-pattern, as I although told
different times now, I have no resources directly stored in static
context but in singleton instances inside.for example the DBAdapter::closeAllConnections():
public static closeAllConnections() {
while(count(self::$connections) > 0) {
self::$connections[0]->close();
unset(self::$connections[0]);
}
}the same for LogAdapter.
This doesn't destroy those objects - PHP is a purely garbage collected
language (in the sense of no manually managed memory / resources), so
you can't destroy an object something still points to. If you gave out a
reference to a DB connection and stored it in a static variable
somewhere, that reference still ponts to the object, and you will have
to detect that it is a closed connection and handle it appropriately.
This isn't to do with public constructors vs singletons, or even objects
vs resources, it's about the handle being shared amongst multiple users,
no one of which can unilaterally "unshare" it.
My point about lazy-loading is that if you have a class like this...
class LazyThing {
private static $foo;
private function __static() { self::$foo =
Something::GetSomeKindOfWrappedResource(); }
public function cleanup() { self::$foo->destroy(); }
}
...and then don't need it on a particular page, when you call
LazyThing::cleanup(), it will actually trigger the static constructor,
potentially triggering a whole chain of resources to be opened just in
order to close them again. If that's the case, then you might as well
have a matching pair of Foo::initialise() and Foo::cleanup() calls which
are both called explicitly.
So explicit cleanup routines can't usefully be used in the same class as
implicit initialisation, which kind of makes sense, but rules them out
as an alternative to static destructors.
Regards,
--
Rowan Collins
[IMSoP]
Johannes Ott wrote on 12/03/2015 23:36:
Am 12.03.2015 um 21:33 schrieb Rowan Collins:
Johannes Ott wrote on 12/03/2015 19:45:
All of the magic methods are doing like this.
I thought you might say that, but the only thing remotely similar I can
think of is a destructor, which gets called when an object goes out of
scope; the others are all the implementation of, or instead of, some
specific piece of code:
__construct() runs every time for new Foo
__get() and __set() runs every time for property access with -> on an
undefined property
__call() and __callStatic() runs every time for method access on an
undefined propertyThe difference with this is that you never know which part of your code
will actually trigger it, because it only runs some of the time.I cannot see any difference because the trigger is defined really clear
for each of the functionsFor Example Class Foo implements all of those functions the triggers
would be__construct -> triggered each time I write "new Foo()"
__destruct -> triggered each time a instance loses his last reference
and is removed by garbage collection.__get() and __set() -> triggered each time I try to access a property
of my foo instance which is not accessible (not defined or private) from
outside Foo writing $foo->notfromoutside__call() -> triggered each time I try to call a non-static method on a
instance of Foo which is not accessible from outside writing
$foo->notfromoutside()__callStatic() -> triggered each time I try to access static method of
the class which is not accessible from outside writing Foo::notfromoutside()And finally the proposed new one
__static() -> triggered on the first time I write either "new Foo()" or
"Foo::eachStaticPublicOrProtectedMethodOrProperty()"I really don't see what __static() has more "magic" or unclear trigger
then each of the other so called magic methods?!
Note all except two of these descriptions start "each time I". You can
look at code, and know that the function will be called at that specific
point in the program. You can't look at code, and know that this will,
on a particular execution path, be the first time a particular
condition is met, and will therefore trigger __static().
The other exception is __destruct(), which is a good reason to use
__destruct() sparingly.
Do keep away any doubt, in my opinion none of the so called magic
methods have any kind of magic. They are just language patterns you
should understand how they work before you start to use them in your
code! So "private static function __static() {...}" is just another
pattern you have to understand before you use. If you don't understand
how it works you should not use. That is the same for all language
patterns and elements, for example: loops, conditions, scalar-operators,
etc.And as I already told a static class constructor is a kind of well-known
standard function in mostly all modern OOP-languages. So professional
OOP-developer should be or become very fast familiar with this pattern.
Yes, to be fair, I can definitely see their use for "constant" values. I
think a lot of your examples are doing too much, though.
Okay thats a point I have to clearify I see, maybe my examples where to
much shorten by me. In my examples I'm showing only the initialize parts
of the classes, the initialized properties are used all over the class
in static as well in non-static context.
OK, so these are classes which share certain resources between all
instances, represented by these static properties. In that case, would
it be reasonable to say that the initialization of these properties
needs to happen the first time an instance is created? If so, the
if(self::$initialized) check needs to run only as often as
__construct(), not every time any method is called.If you look closer at my example you will see, that the property is not
just shared between instances (then the normal constructor Could be
indeed be the place to initialize), but it is shared although with
different static methods inside the class.If the static methods are public, and used for something other than
managing instances (Factory / Singleton / etc), then are they really the
responsibility of the same class? i.e. do you have a Utility class
hiding in there pretending to be part of the instance/factory class?See my example 2 for a Config Wrapper class:
abstract class Config {
private static $arIni;private static function __static() { self::$arIni = parse_ini_file('config.ini'); //For example check whether all required fields are in the file
or throw Exception if not.
}public static function `getHostname()` { return self::$arIni['hostname']; } public static function getDBConnectionString() { return ' self::$arIni['dbdriver'] . '://' .
self::$arIni['dbuser'] . '@' .self::$arIni['dbhost'] ...;
}...
}
The Config class is in response for all config Parameters inside
config.ini and needs to read in this file before it can to it's work
correctly. No hiding of Factory/Singleton/instance in this.And i although see no DI or Singleton pattern to use here to get the
same functionality, if you want to use like Config::getHostname() and
not like Config::getInstance()->getHostname() which is really
unnecessary abstraction level for nothing in my opinion!
It doesn't seem at all odd to me to say "new Config('config.ini');", and
pass around an instance. This would be the Dependency Injection solution.
As for Singleton, I see it as a stepping-stone between global state
(static methods only) and encapsulated state (instances). You can write
$this->config = Config::getInstance() in a constructor, and write the
rest of the class as though you had an instance injected.
There's certainly an argument for keeping it simple until you need
something cleverer, but I'm not sure it's a good use of this proposed
language feature.
Thinking about it, you could even leave the way open for a different
strategy by using $this->getLogger() instead of self::getLogger() -
right now, that's 100% equivalent, but it makes it easier to change
later to use injection, or be able to override the logger on particular
instances, etc.
Sorry I'm confused now after reading this sentence and I have to ask
now, because I'm not sure anymore, whether you really have understand
the difference between static and non-static context and the inheritance
of classes inside PHP, especially the difference between $this, static,
self?How can $this->getLogger() be equivalent to self::getLogger();
It's a bit of a dirty trick: if you access a static method as
$this->methodName(), it runs fine, as though you'd written
static::methodName(). Looking back at the example, I see that one of the
methods using the logger is itself static, so obviously it wouldn't work
there, but it would in the non-static one. Again, I was confused by your
class acting both as a static utility and an instance.
Output:
B->getLoggerNonStatic();
A::getLoggerStatic();
B::getLoggerStatic();As you can see line 1 and 2 of the output not equivalent even by class,
but I want to log functionality of A as functionality of A not of B!
I'm not sure I follow you here. If you don't want class B to over-ride
the getLogger(), can't you just declare it final? Or are you saying that
you want your logs to show where in the inheritance hierarchy everything
was defined, rather than the actual classes that were used? Seems like
an odd requirement.
Beside the performance for me and for each clean coder I know (btw as
a Software Architect I'm in charge of code quality of a huge Java
project at the moment, so I think I know what I'm talking about), to
read 100 times self::getLogger()->... and have to think about whether
the getLogger() method (which should be by "standard" oop-naming
conventions ONLY a three lines of code getter and nothing else) is
doing the magic of getting a instance first, is much more worse then
understanding once what a static class constructor does and then just
use self::$LOG->... A "magic" getter identified by prefix get doing
anything beside giving back a class member is really far away from
clean code!
I don't really see the difference - either I have to know that what
appears to be a scope lookup (self::$LOG) is actually a magic
initialiser, or I have to know that a method call (which you can call
whatever you like) has some code in it that initialises on first use.
Either way, what you're basically saying is "pretend this value will
always be initialised, and just access it".
I can see the argument that once you're used to it, they're equivalent,
but I certainly don't see how "::" would ever be more of a clue than
"->get" that initialisation might happen.
Regards,
Rowan Collins
[IMSoP]
On Thursday 12 March 2015 00:10:15 Rowan Collins wrote:
The purpose of this suggestion is to introduce a static constructor,
which is called before the first call to class either static or
non-static to initialize some static properties which are needed by the
class.Can you give an example use case for when this would be useful? I'm
struggling to think of one for which there isn't already an established
coding pattern...
It's useful everywhere you now have more than one method starting
if (!static::$is_initialized) static::initialize_me();
Some examples from our codebase:
-
a session wrapper class, hiding $_SESSION behind setter/getter methods,
where the static class initialization determines which cookie to use,
depending on some global feature flags, and which session backend to use,
depending on current availability. (main purpose, apart from clean calling
side code just using the setters/getters, is to get the lazy_write
functionality Yasuo tried to introduce recently) -
computation of some class properties from others, like doing an array_flip
on one to get a reverse mapping. -
definition of computed constants, in various places. Partly obsolete now
that class constants support constant expressions, but needed as soon as these
are not really constant. -
setting up some class properties used by various methods, that should depend
on some global feature flags (defined by the users of the class, usually
toplevel scripts) -
invariant checks on subclasses, in various places, where concrete subclasses
set up some static properties of a configuration nature, and I want to make
sure in the base class, as early as possible, that the values are consistent
and make sense, avoiding checks spread all over the place in various methods.
Of course, most of this could be done by code outside the class definition, in
the same file. But that has proven, in the past, a fountain of joy wrt.
placement, with variations needed for APC and opcache, and general frustration
all around.
best regards
Patrick
Patrick Schaaf wrote on 12/03/2015 08:40:
On Thursday 12 March 2015 00:10:15 Rowan Collins wrote:
The purpose of this suggestion is to introduce a static constructor,
which is called before the first call to class either static or
non-static to initialize some static properties which are needed by the
class.
Can you give an example use case for when this would be useful? I'm
struggling to think of one for which there isn't already an established
coding pattern...
It's useful everywhere you now have more than one method startingif (!static::$is_initialized) static::initialize_me();
I suspect that most places where this occurs, there are other ways to
eliminate it than adding magic pre-initialisation. If a class has lots
of static methods all depending on some state being initialised, maybe
they just shouldn't be static - they should be instance members of a
Singleton or Dependency Injected object. Or maybe they're accessing
members directly which would be better hidden behind an accessor method
which loads some data and "caches" its result.
Some examples from our codebase:
- a session wrapper class, hiding $_SESSION behind setter/getter methods,
where the static class initialization determines which cookie to use,
depending on some global feature flags, and which session backend to use,
depending on current availability. (main purpose, apart from clean calling
side code just using the setters/getters, is to get the lazy_write
functionality Yasuo tried to introduce recently)
This sounds like a job for a Singleton or Dependency-Injected object.
The initialisation is a specific action with its own parameters (which
you're currently grabbing from global configuration).
computation of some class properties from others, like doing an array_flip
on one to get a reverse mapping.definition of computed constants, in various places. Partly obsolete now
that class constants support constant expressions, but needed as soon as these
are not really constant.
This is the only case that seems valid to me: enums and other types of
"computed constant", where the class is just there as a container.
- invariant checks on subclasses, in various places, where concrete subclasses
set up some static properties of a configuration nature, and I want to make
sure in the base class, as early as possible, that the values are consistent
and make sense, avoiding checks spread all over the place in various methods.
The question is, why is this whole hierarchy static? If these were
instances not definitions, the initialisation is a well-defined event
(the constructor), and you can check validity there.
Regards,
Rowan Collins
[IMSoP]
Patrick Schaaf wrote:
But that has proven, in the past, a fountain of joy wrt.
placement, with variations needed for APC and opcache, and general frustration
all around.
Is there a bug report for the problems? OPCache shouldn't have
side-effects on the code.
cheers
Dan
On Thursday 12 March 2015 00:10:15 Rowan Collins wrote:
The purpose of this suggestion is to introduce a static constructor,
which is called before the first call to class either static or
non-static to initialize some static properties which are needed by the
class.Can you give an example use case for when this would be useful? I'm
struggling to think of one for which there isn't already an established
coding pattern...It's useful everywhere you now have more than one method starting
if (!static::$is_initialized) static::initialize_me();
Some examples from our codebase:
a session wrapper class, hiding $_SESSION behind setter/getter methods,
where the static class initialization determines which cookie to use,
depending on some global feature flags, and which session backend to use,
depending on current availability. (main purpose, apart from clean calling
side code just using the setters/getters, is to get the lazy_write
functionality Yasuo tried to introduce recently)computation of some class properties from others, like doing an array_flip
on one to get a reverse mapping.definition of computed constants, in various places. Partly obsolete now
that class constants support constant expressions, but needed as soon as these
are not really constant.setting up some class properties used by various methods, that should depend
on some global feature flags (defined by the users of the class, usually
toplevel scripts)invariant checks on subclasses, in various places, where concrete subclasses
set up some static properties of a configuration nature, and I want to make
sure in the base class, as early as possible, that the values are consistent
and make sense, avoiding checks spread all over the place in various methods.Of course, most of this could be done by code outside the class definition, in
the same file. But that has proven, in the past, a fountain of joy wrt.
placement, with variations needed for APC and opcache, and general frustration
all around.best regards
Patrick
Am 12.03.2015 20:12 schrieb "Dan Ackroyd" danack@basereality.com:
Patrick Schaaf wrote:
But that has proven, in the past, a fountain of joy wrt.
placement, with variations needed for APC and opcache, and general
frustration
all around.Is there a bug report for the problems? OPCache shouldn't have
side-effects on the code.
The issues we had were never reproducible in any controlled way, so no bug
reports.
Back in that time there was also an inordinate amount of mixed autoloading
(being phased in) and lots of include/require spaghetti (being phased out).
I don't remember any details, just that I don't want to remember them.
best regards
Patrick
On Thursday 12 March 2015 00:21:34 Johannes Ott wrote:
The purpose of this suggestion is to introduce a static constructor,
which is called before the first call to class either static or
non-static to initialize some static properties which are needed by the
class.
We are doing this in our private codebase, implemented as a feature of our
autoloader: after having loaded a class, check whether it has a static method
called 'init§class()', and call that.
At the moment, 63 of our 397 classes have that, so it's definitely a success
for us.
One issue to think about, is whether to call that function when it's just
inherited. We do, and in several places, then check if (get_called_class() ==
CLASS). It's a bit tedious in places, but in other places it permits
setting up of subclass static::$properties from a shared baseclass initializer
method.
In principle none of that is neccessary, because one can put code outside the
class definition. One more line to write. In practise, it's a bit annoying, as
it must go after the whole class definition, or it breaks for subclasses in
funny ways when the parent class is not already defined...
Something like your version 2 proposal, a new __magic method, would be my
prefered choice, too.
best regards
Patrick
So now I want to do my first own proposal for a new function in PHP and
I hope doing it right with starting a discussion here first.The purpose of this suggestion is to introduce a static constructor,
which is called before the first call to class either static or
non-static to initialize some static properties which are needed by the
class.I think about two different possible syntax to realize that purpose:
Version 1 (similar to Java):
class A {
private static $var;static {
//Do some code here to initialize self::$var;
}}
Version 2 (a new magic method):
class B {
private static $var;private static function __static() {
//Do some code here to initialize self::$var;
}
}My prefered code would be version 2 at the moment.
Looking forward to your feedback,
What about inheritance?
I think dynamic class-constructor would make much more sense.
A function which can analyse real class and do initialisation.
class A
{
protected static function __class_construct()
{
echo get_called_class()
.” class is defined\n";
}
}
class B extends A
{
}
output
A class is defined
B class is defined
--
Alexey Zakhlestin
https://github.com/indeyets
PGP key: http://indeyets.ru/alexey.zakhlestin.pgp.asc
What about inheritance?
I think dynamic class-constructor would make much more sense.
A function which can analyse real class and do initialisation.class A
{
protected static function __class_construct()
{
echoget_called_class()
.” class is defined\n";
}
}class B extends A
{
}output
A class is defined
B class is defined
I think class-constructor and static-constructor are two different
things. I even think that a inherited class-constructor is a dangerous
thing because you have huge depencies between all subclasses.
For a quick and dirty example:
abstract class A {
protected static $value;
protected static function __class_construct() {
switch (get_called_class()):
case 'B':
self::$value = 1;
break;
case 'C':
self::$value = 2;
break;
default:
self::$value = 3;
}
}
class B extends A {
public static function isOne() {
return self::$value == 1;
}
}
class C extends A {
public static function isTwo() {
return self::$value == 2;
}
}
That not wellformed code for three reasons:
-
Class A must know about all of it's subclasses, so you can not easily
reuse this class A in other projects because you have to refactor the
complete __class_constructor. -
Huge dependcies between all subclasses. If you want to implement a
class D although extending class A you have to look in each other class
for sideeffects of the the changes you have todo in the
__class_construct for this class D -
As you can see in the quick example you have a depency of class
loading order as well the result of B::isOne() and C::isTwo() depends in
which order you have loaded your classfiles. Even worst for example
You are using only class B for the beginn B::isOne() giving you true,
later you load class C for another reason and suddenly B::isOne() gives
you false.
A static constructor however encapsulate the initialization to each
class and should only initialize private members which can be accessed
by protected getters by the subclass.
For the example:
abstract class A {
private static $one;
private static function __static() {
self::$one = 1;
}
protected static function getOne() {
return self::$one;
}
}
class B extends A {
public static function isOne() {
return self::getOne() == 1; //Now always true
}
}
class C extends A {
public static function isTwo() {
return self::getOne() == 2; //Now always false
}
}
Regards,
--
DerOetzi
Hello Johannes,
class Foo {
private static function __static() {
throw new Exception("boom");
}
}
while(true) {
try {
$foo = new Foo;
} catch (Exception ex) {}
}
Would this code be valid?
Am 12.03.2015 um 12:16 schrieb Crypto Compress:
Hello Johannes,
class Foo {
private static function __static() {
throw new Exception("boom");
}
}while(true) {
try {
$foo = new Foo;
} catch (Exception ex) {}
}Would this code be valid?
Have to think about this issue, but on the first look I would say yes!
Because the meaning of the static constructor is to do some necassary
initialize for the class it should be able to throw an Exception if it
cannot do it's work correctly. For example if it needs to read some
configuration from a Database and is not able to connect.
For the caller I although would say, that the error inside the static
constructor should be catchable, for the simple fact that every Error
should be catchable for user-errorhandling.
What is your point of view for this? I'm open for discussion about this.
Regards
DerOetzi
2015-03-12 12:33 GMT+01:00 Johannes Ott mail@deroetzi.de:
Am 12.03.2015 um 12:16 schrieb Crypto Compress:
Hello Johannes,
class Foo {
private static function __static() {
throw new Exception("boom");
}
}while(true) {
try {
$foo = new Foo;
} catch (Exception ex) {}
}Would this code be valid?
Have to think about this issue, but on the first look I would say yes!
Because the meaning of the static constructor is to do some necassary
initialize for the class it should be able to throw an Exception if it
cannot do it's work correctly. For example if it needs to read some
configuration from a Database and is not able to connect.
How would it behave for the second call? If the first initialize fails due
to some exception, should that static constructor be executed again?
For the caller I although would say, that the error inside the static
constructor should be catchable, for the simple fact that every Error
should be catchable for user-errorhandling.What is your point of view for this? I'm open for discussion about this.
Regards
DerOetzi
--
Regards, Niklas
Am 12.03.2015 um 12:40 schrieb Niklas Keller:
How would it behave for the second call? If the first initialize fails due
to some exception, should that static constructor be executed again?
I think there a two different solutions and I do not know which one I
prefer at the moment:
-
No second call can raise an fatal.
-
Yes should be executed again
As an internal implementation, we can do a flag which is at creation
time of class false and set after sucessful run of the static
constructor to true.
Do avoid deadlocks in loops like in the example maybe we can implement a
combination of both solutions. We can implement the flag not as a
boolean but as an integer counter. Raising a FATAL after X retries.
Regards,
DerOetzi
Am 12.03.2015 um 12:33 schrieb Johannes Ott:
Am 12.03.2015 um 12:16 schrieb Crypto Compress:
Hello Johannes,
class Foo {
private static function __static() {
throw new Exception("boom");
}
}while(true) {
try {
$foo = new Foo;
} catch (Exception ex) {}
}Would this code be valid?
Have to think about this issue, but on the first look I would say yes!Because the meaning of the static constructor is to do some necassary
initialize for the class it should be able to throw an Exception if it
cannot do it's work correctly. For example if it needs to read some
configuration from a Database and is not able to connect.For the caller I although would say, that the error inside the static
constructor should be catchable, for the simple fact that every Error
should be catchable for user-errorhandling.What is your point of view for this? I'm open for discussion about this.
Regards
If it's valid code then caching thrown exception and rethrow as
wrapped-inner-exception on each subsequent access on Foo, seems
feasible. I like the idea but cannot wrap my head around this.