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