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?!

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

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to