Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:84675 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 98715 invoked from network); 13 Mar 2015 11:32:31 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 13 Mar 2015 11:32:31 -0000 Authentication-Results: pb1.pair.com header.from=rowan.collins@gmail.com; sender-id=pass Authentication-Results: pb1.pair.com smtp.mail=rowan.collins@gmail.com; spf=pass; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 74.125.82.54 as permitted sender) X-PHP-List-Original-Sender: rowan.collins@gmail.com X-Host-Fingerprint: 74.125.82.54 mail-wg0-f54.google.com Received: from [74.125.82.54] ([74.125.82.54:39708] helo=mail-wg0-f54.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id F6/00-32208-ECAC2055 for ; Fri, 13 Mar 2015 06:32:31 -0500 Received: by wghn12 with SMTP id n12so22611416wgh.6 for ; Fri, 13 Mar 2015 04:32:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=message-id:date:from:user-agent:mime-version:to:subject:references :in-reply-to:content-type:content-transfer-encoding; bh=DWccFYtALcl3p8sKMI7KuQWjSd0066YDgm0PJbpzTZQ=; b=jpYCOZNignta0UIFa3bDurU47DuqadFFtmgGJHdoNC4JN1NtPZRn//1aIbqxGV3Tz2 snkQF8suyQ9odssldj377YnpkSxwgBdT5eqUFtF0PTnO8CyeiMQ+Wdz94oD+Vmu1O9wq cQ+2aD/7iZKsJOER1JGqkD5l17U6/w5taDfXNLGBhEt7vsZZUcFYSgZKF1llxVHEw30k h2VDxvW+Lwh4XGTJGOD4iumZaIdjyzdMHEmSIk38ZPOcdxijV4snEU+zDrdkgbGZkrYz C5NDJDPVZv8aXepOLrba3NZjbZQ0IizeMza72vt3ZSKXI01kkGkhcoYpOmluiJn69CEH mfpQ== X-Received: by 10.194.108.137 with SMTP id hk9mr54883571wjb.112.1426246346817; Fri, 13 Mar 2015 04:32:26 -0700 (PDT) Received: from [192.168.0.159] ([62.189.198.114]) by mx.google.com with ESMTPSA id w3sm2421796wiz.5.2015.03.13.04.32.25 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 13 Mar 2015 04:32:25 -0700 (PDT) Message-ID: <5502CAA1.6090507@gmail.com> Date: Fri, 13 Mar 2015 11:31:45 +0000 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Thunderbird/31.5.0 MIME-Version: 1.0 To: internals@lists.php.net References: <6D.2C.32765.10EC0055@pb1.pair.com> <5500D967.5040800@gmail.com> <13.69.64353.73451055@pb1.pair.com> <5501876C.3020107@gmail.com> <3D.85.42021.3E7A1055@pb1.pair.com> <5501B77D.5010800@gmail.com> <85.D1.24603.247C1055@pb1.pair.com> <5501D328.9040800@gmail.com> <98.26.24603.C9DE1055@pb1.pair.com> <5501F823.3060805@gmail.com> In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [PHP-DEV] static constructor From: rowan.collins@gmail.com (Rowan Collins) 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]