Re: static constructor

From: Date: Fri, 13 Mar 2015 11:31:45 +0000
Subject: Re: static constructor
References: 1 2 3 4 5 6 7 8 9 10 11 12  Groups: php.internals 
Request: Send a blank email to internals+get-84675@lists.php.net to get a copy of this message
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 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.
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?!
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]

« previous php.internals (#84675) next »