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]
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php