实例与静态方法。静态或动态调用

5 php oop methods static non-static

PHP中有实例方法和静态方法(就这两种类型)?那么我们可以静态或非静态地调用其中任何一个(名称是“动态”吗?)?

所以我们可以:

  1. 静态调用实例方法;
  2. 非静态调用实例方法;
  3. 静态调用静态方法;
  4. 非静态调用静态方法(四个都正确吗?)

这四个的代码看起来怎么样?有没有什么好的网站可以解释这个?我目前正在阅读以下网址:

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

...我不明白这一点:

“$this 是对调用对象的引用(通常是该方法所属的对象,但也可能是另一个对象,如果该方法是从辅助对象的上下文中静态调用的)。”

代码如何从辅助对象静态调用方法?我的意思是,静态所有调用与非静态调用,这是什么?

Art*_*nix 4

您不应该静态调用非静态方法。

一些例子

class foo{
    public static function bar(){
        echo __METHOD__;
    }
    public function biz(){
        echo __METHOD__;
    }
}
Run Code Online (Sandbox Code Playgroud)

测试

//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
Run Code Online (Sandbox Code Playgroud)

非静态调用 static 背后的想法是允许在非静态方法中使用静态属性。例如:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

所以这很好,现在的另一面是在静态方法内部调用非静态方法。

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__;
    }
}
Run Code Online (Sandbox Code Playgroud)

其他一些需要指出的事情

  • 当调用 static $this 不可用时,假设您将在非静态方法中使用 $this,因此静态调用它可能会导致问题。
  • 调用非静态时$this存在,静态调用没有问题,所以非静态调用静态方法也没有问题。IE。无论上下文如何, self 和 static 都可用。
  • 静态属性与类的所有实例共享
  • 子级无法更改父级中访问的静态属性(使用 self 时)
  • 子级可以更改父级中访问的静态属性(使用静态、后期静态绑定时)

现在,如果您想要确切的答案:

  1. 静态调用实例方法;
    • 你可以但不应该因为 $this 不存在
  2. 非静态调用实例方法;
    • 这个是正常的
  3. 静态调用静态方法;
    • 这也很正常
  4. 非静态调用静态方法(四个都正确吗?)
    • 确保静态可用,无论范围如何

我们可以使用上面的类通过示例来展示这一点

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

结果 (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 />
Run Code Online (Sandbox Code Playgroud)

如您所见,我们在尝试访问时遇到致命错误$this

结果(PHP5的东西)

<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 />
Run Code Online (Sandbox Code Playgroud)

现在我们在 PHP7 中没有遇到致命错误(某事),乍一看这似乎没问题。就像它说的那样,这样跑就好了。然而,如果你仔细观察,Undefined variable: this这实际上比致命错误更糟糕,因为现在你的类可能会产生意想不到的结果。

如果我们称这为正常:

(new foo)->biz();
Run Code Online (Sandbox Code Playgroud)

结果

foo::biz //output of __METHOD__
foo Object //output of $this
(
)
Run Code Online (Sandbox Code Playgroud)

所以我想给你一个关于selfvs的简单例子static,它可能真的很令人困惑。

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();
Run Code Online (Sandbox Code Playgroud)

结果

-------------- defined in parent ----
true //call boo() self
false //call boo() static
-------------- defined in child ----
false //call biz() self
false //call biz() static
Run Code Online (Sandbox Code Playgroud)

当您使用static它时,称为后期静态绑定。这意味着静态值被延迟绑定。那么这到底是什么意思呢?这意味着该值是在运行时解析的,而不是在 PHP 解析类时解析的。

  • bar是 的孩子foo
  • 我们实例化这个孩子foo,我们所有的调用都会通过foo
  • 该方法boo仅存在于父级中,即。它不会被子方法覆盖。
  • foo 的值为 'true'
  • 条的值为“假”

对于第一个,我们得到 的值,foo因为我们使用的是 self,所以它只知道它自己。

对于第二个,我们得到 的值,bar因为我们使用的是 static,它是后期绑定的,并且可以使用在属性声明中设置的子项的值$test。因此,即使父级对子级一无所知(典型),它也可以使用它的值,因为它是在运行时解析的。

对于第三个,我们得到 的值,bar因为它了解自身,并且该方法是在其自身中定义的。foo对这个方法一无所知,即使它确实存在,它也会被子级中的减速覆盖。

对于第四个,我们再次得到barthis 的值,因为即使使用后期静态绑定,我们也会提取相同的数据,bar因为它的值bar是我们实例化的类,所以在运行时,属性中定义的值就是该值。

所以最后两个值是相同的,这是因为 self 和 static 解析相同的信息,无论它们何时被调用。

这可能会让人非常困惑,所以希望它是有道理的。另外,正如我所展示的,不要害怕创建一些像这样的简单类并测试您获得的值。我就是这样学习的。

更新

您提到使用静态调用被认为是不好的。

我认为其中大部分来自依赖性问题。这是类名和代码之间的紧密耦合。使用实例时,将其分配给变量并在调用 new 时使用该名称 1 次。调用 static 时,每次都使用类名。这导致的问题是如果您决定重命名该类。对于实例调用,您只需替换调用 new 的名称,而对于静态调用,则必须在所有地方替换它。

例如考虑这个。

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

并将其与此进行比较。

foo::one();
foo::two();
//say you inject this into another class
foo::three();
Run Code Online (Sandbox Code Playgroud)

现在假设您更改了类名。对于第一个,您必须将其替换在一个地方。对于第二个,您必须在每个使用它的地方更换它。您可以通过使用字符串变量来解决这个问题。

$class = 'foo';
$class::one();
$class::two();
//say you inject this into another class
$class::three();
Run Code Online (Sandbox Code Playgroud)

但这样你也会遇到很多麻烦,因为大多数 IDE 无法解析类并进行自动完成。

另外,如果您对其他类的输入进行类型提示

 class other{ 
    public method inject(foo $foo){}
 }
Run Code Online (Sandbox Code Playgroud)

这在静态类上效果不太好,因为你要传入一个字符串(类名)。

命名空间可能是一个问题。使用实例化,您只需将 use 语句放在实例化类的文件中。使用静态,您必须将它放在任何地方,或者将其包含在每次调用中。

 \mystuff\foo::bar();

 $foo = '\\mystuff\\foo';
 $foo::bar();
Run Code Online (Sandbox Code Playgroud)

我确信还有其他原因,但对我来说这些是主要原因。