Instance vs. static method. Calling it statically

2019-05-31 02:30发布

问题:

In PHP there is instance methods and static methods (just these two types)? Then we can call either of these statically or non-statically (is the name "dynamically"?)?

So we can:

  1. Call an instance method statically;
  2. Call an instance method non-statically;
  3. Call a static method statically;
  4. Call a static method non-statically (all four correct?)

How would the code for these four look? Are there any good websites explaining this? I am currently reading the following url:

http://php.net/manual/en/language.oop5.basic.php

...and I am not understanding this:

"$this is a reference to the calling object (usually the object to which the method belongs, but possibly another object, if the method is called statically from the context of a secondary object)."

How would the code look for calling a method statically from a secondary object? I mean, staticall calling vs. non-statical calling, what this is?

回答1:

You are not supposed to call non static methods statically.

Some examples

class foo{
    public static function bar(){
        echo __METHOD__;
    }
    public function biz(){
        echo __METHOD__;
    }
}

Tests

//instance call
(new foo)->biz(); //echos  foo::biz (don't worry about the :: here, its part of __METHOD__)

//static call, statically
foo::bar() // echos  foo::bar

//call non-static statically
foo::biz() //in PHP7.1
    <b>Deprecated</b>:  Non-static method foo::biz() should not be called statically in <b>[...][...]</b> on line <b>18</b><br />

//call static non-statically
 (new foo)->bar(); // echos  foo::bar

The idea behind calling static non-statically is that its permissible to use static properties inside non static methods. For example:

class foo{
    protected static $boo = "hello";

    public static function bar(){
        echo __METHOD__;
    }
    public function biz(){
        echo __METHOD__;
        echo self::$boo;
        echo static::$boo; //late static binding
    }
}

So this is fine, now the flip side is calling non-static methods inside of a static methods.

class foo{
    protected $boo = "hello";

    public static function bar(){
        echo __METHOD__;
        $this->boo; // this is a no no, because no instance exists and therefor $this does not work
    }
    public function biz(){
        echo __METHOD__;
    }
}

A few other things to point out

  • When calling static $this is not usable, it's assumed you will use $this in a non-static method, so calling it statically can cause issue.
  • When calling non static $this exists, and there is no problem making a static call, so calling static methods non statically is not an issue. ie. self and static is available no mater the context.
  • Static properties are shared with all instances of the class
  • Static properties accessed in parent cannot be changed by children (when using self)
  • Static properties accessed in parent can be changed by children (when using static, late static binding)

Now if you want exact answers:

  1. Call an instance method statically;
    • you can but shouldn't because $this is not exists
  2. Call an instance method non-statically;
    • this is normal
  3. Call a static method statically;
    • this too is normal
  4. Call a static method non-statically (all four correct?)
    • sure static is available no matter the scope

We can show this by example using the above class

class foo{
    public static function bar(){
        echo __METHOD__;
    }
    public function biz(){
        echo __METHOD__;
        print_r($this);
    }
}
//call non-static statically
foo::biz();

Result (PHP7+)

 <br />
 <b>Deprecated</b>:  Non-static method foo::biz() should not be called statically in <b>[...][...]</b> on line <b>15</b><br />

foo::biz //output of __METHOD__

<br />
 <b>Fatal error</b>:  Uncaught Error: Using $this when not in object context in [...][...]:11
 Stack trace:
 #0 [...][...](15): foo::biz()
 #1 {main}
 thrown in <b>[...][...]</b> on line <b>11</b><br />

AS you can see we get a Fatal error when trying to access $this

Result (PHP5 something)

<br />
<b>Strict Standards</b>:  Non-static method foo::biz() should not be called statically in <b>[...][...]</b> on line <b>16</b><br />
foo::biz<br />
<b>Notice</b>:  Undefined variable: this in <b>[...][...]</b> on line  <b>11</b><br />

Now we don't get the Fatal error in pr PHP7 (something), and at first glance this may seem ok. Like its saying it's fine to run this way. However if you look closer Undefined variable: this this is actually worse then the Fatal error, because now you class can produce unexpected results.

If we had called this normal:

(new foo)->biz();

Result

foo::biz //output of __METHOD__
foo Object //output of $this
(
)

So I want to give you one quick example on self vs static, it can be really confusing.

class foo{
    protected static $test = 'true';

    public function boo(){
        echo self::$test."\n";
        echo static::$test."\n";
    }
}


class bar extends foo{
    protected static $test = 'false';

    public function biz(){
        echo self::$test."\n";
        echo static::$test."\n";
    }
}


$B = new bar;

$B->boo();
echo "--------------\n";
$B->biz();

Result

-------------- defined in parent ----
true //call boo() self
false //call boo() static
-------------- defined in child ----
false //call biz() self
false //call biz() static

When you use static it's called late static binding. What this means is that the static value is bound late. So what doe that really mean? It means the value is resolved at run time, not when the class is parsed by PHP.

  • bar is a child of foo.
  • We instantiate the child foo, all our calls go through foo.
  • the method boo only exists in the parent, ie. it's not overwritten by a child method.
  • foo's value is 'true'
  • bar's value is 'false'

For the fist one, we get the value of foo because we are using self, so it only knows about itself.

For the second one, we get the value of bar because we are using static, it's bound late and can use the child's value which is set in it's declaration of the property $test. So even though the parent doesn't know anything about the child (typical) it can use it's value because it's resolved at run time.

for the third one, we get the value of bar because it knows about itself, and the method is defined in itself. foo knows nothing about this method even if it did it would be overwritten by the deceleration of it in the child.

for the fourth one, again we get the value of bar this is because even with late static binding we pull the same data, the value of bar because bar is the class we instantiated, so at run time the value defined in's property is the value.

So in the last 2 the value is the same, it's because self and static resolve the same information regardless of when they are called.

This can be very confusing so hopefully it makes sense. Also as I have shown don't be afraid to make some simple classes like this and test the values you get. That's how I learned.

UPDATE

You mentioned using static calls was considered bad.

I think most of that comes from Dependency issues. This is tight coupling between the name of the class and your code. When using an instance, you assign it to variable and use the name 1 time when calling new. When calling static you use the class name every time. The problem this causes is if you decide to rename the class. With instance calls you only need to replace the name where you call new, with static you have to replace it everywhere.

For example consider this.

$foo = new foo;
$foo->one();
$foo->two();
//say you inject this into another class
$foo->three();

And compare it to this.

foo::one();
foo::two();
//say you inject this into another class
foo::three();

Now say you change the class name. For the first one you have to replace it in one place. For the second one you have to replace it evey where you used it. You can get around this somewhat by using a string variable.

$class = 'foo';
$class::one();
$class::two();
//say you inject this into another class
$class::three();

But with this you can get into a lot of trouble too, because most IDE's wont be able to resolve the class and do auto-completion.

Also if you do type hinting on inputs to other classes

 class other{ 
    public method inject(foo $foo){}
 }

This doesn't work very well on static classes, because you are passing in a string then (the class name).

Namespaces can be an issue. With instantiation, you only need to put the use statement in the file you instantiate the class in. With static, you have to put it everywhere, or include it in every call.

 \mystuff\foo::bar();

 $foo = '\\mystuff\\foo';
 $foo::bar();

I am sure there are other reasons, but these are the main ones for me.



回答2:

Let's look the following code:

<?php

class A
{
    public $property = "property A";

    public function testA()
    {
        echo "class A ";
        echo $this->property;
    }
}

class B
{
    public $property = "property B";

    public function testB()
    {
        A::testA();
    }
}

$b = new B;
$b->testB();

It will display class A property B

You are accessing a property from B, in A class, with $this. This will not work on PHP 7+, and you will get the following warning on PHP 5.6:

WARNING Non-static method A::testA() should not be called statically, assuming $this from incompatible context on line number 16

It "works", but you are not supposed to call non-static methods from static context.