PHP中的静态方法和非静态方法有什么区别吗?

14
class t {
    public function tt()
    {
        echo 1;
    }
}
t::tt();

看到了吗?非静态函数也可以在类级别调用。那么,如果我在public之前添加static关键字,有什么不同呢?

5
哇,它真的起作用了。PHP 真的很混乱。 - Gumbo
@Gumbo:PHP4向后兼容,如果你喜欢严格的话,在严格出错时抛出异常。 - hakre
6个回答

15
除此之外,如果您尝试在方法中使用$this,就像这样:
class t {
    protected $a = 10;
    public function tt() {
        echo $this->a;
        echo 1;
    }
}
t::tt();

当静态调用非静态方法时,您将会收到一个致命错误:

Fatal error: Using $this when not in object context in ...\temp.php on line 11

例如,你的示例有点过于简单,与实际情况不太相符;-)


还要注意,你的示例应该会收到严格警告(引用):

调用非静态方法静态地生成一个E_STRICT级别的警告。

实际上确实如此(至少在PHP 5.3中)

Strict Standards: Non-static method t::tt() should not be called statically in ...\temp.php on line 12
1

所以:不太好;-)


然而,静态调用非静态方法看起来并不像是任何一种良好的实践(这可能就是为什么它会引发一个严格警告的原因),因为静态方法和非静态方法没有相同的含义:静态方法不引用任何对象,而非静态方法在类的实例上工作。


再次强调:即使PHP允许您做某些事情(也许是出于历史原因——比如与旧版本的兼容性),也并不意味着您应该这样做!


3
这是因为E_ALL不包括E_STRICT - 参见http://www.php.net/manual/en/errorfunc.constants.php,该页面说明了在PHP < 6中,E_ALL是“所有支持的错误和警告,但不包括严格级别(E_STRICT)”。 - Pascal MARTIN
2
这可能是因为您不需要使用E_ALL & E_STRICT,而是需要使用E_ALL | E_STRICT(即您需要E_ALL + E_STRICT),但我不确定在哪个版本的PHP中开始引发E_STRICT - Pascal MARTIN
“not in object context” 错误至少是显而易见的。当您在对象上下文中(某个属于完全不同类的对象的某个实例方法)发出 t::tt() 时,$this 将引用该对象,而没有任何警告。 - Tgr
非静态方法在调用它们的类实例上工作。显然并非如此,否则该线程中的示例将无法正常工作。 - robomc
-1 - 这个答案没有正确解释关键的功能差异,并错误地暗示在静态上下文中调用非静态方法会导致致命错误。关键的区别是**静态方法无法访问$this**(即使通过实例对象调用)。只有当您尝试访问未定义的$this时,才会收到致命错误。(您可以使用isset()empty()测试其可用性。) - danorton
显示剩余2条评论

4

静态关键字

由于静态方法可以在创建对象实例之前被调用,所以在声明为静态的方法内部,伪变量 $this 是不可用的。

不能使用箭头运算符 -> 通过对象访问静态属性。

以静态方式调用非静态方法会生成 E_STRICT 级别的警告。

仅仅因为你可以静态地调用非静态方法,并不意味着你应该这样做。这是一种不好的形式。


2
通常情况下,静态方法也被称为“类方法”,而非静态方法也被称为“对象方法”或“实例方法”。
类方法和对象方法之间的区别在于,类方法只能访问类属性(静态属性),而对象方法用于访问对象属性(同一类实例的属性)。
静态方法和属性用于共享特定类的所有实例之间的公共数据。
例如,您可以使用静态属性来跟踪实例数量:
class A {
    private static $counter = 0;

    public function __construct() {
        self::counter = self::counter + 1;
    }

    public function __destruct() {
        self::counter = self::counter - 1;
    }

    public static function printCounter() {
        echo "There are currently ".self::counter." instances of ".__CLASS__;
    }
}

$a1 = new A();
$a2 = new A();
A::printCounter();
unset($a2);
A::printCounter();

请注意,静态属性counter是私有的,因此只能由该类本身和该类的实例访问,而不能从外部访问。

1
一个未被提及的主要区别涉及到多态行为
非静态方法在派生类中重新声明时,会覆盖基类方法,并允许根据调用它们的实例类型进行多态行为。但是静态方法不具备这种特性

PHP 5.3 引入了延迟静态绑定的概念,可以用于在静态继承的上下文中引用被调用的类。


0

是的,关键区别在于声明为static的方法无法访问对象上下文变量$this

此外,在非对象上下文中调用非静态方法将触发E_STRICT错误事件。当启用时,该事件的默认行为是向错误日志(或STDERR)输出消息,但它将允许程序继续运行

此外,在非对象上下文中尝试引用$this将触发E_ERROR事件。该事件的行为是向错误日志(或STDERR)输出消息,并使用状态255退出程序。

例如:

<?php
error_reporting(-1);
//error_reporting(E_ALL);

class DualNature {
  public static function fnStatic() {
    if ( isset( $this ) ) {
      // never ever gets here
      $myValue = $this->_instanceValue;
    } else {
      // always gets here
      $myValue = self::$_staticValue;
    }
    return $myValue;
  }

  public function fnInstance() {
    if ( isset( $this ) ) {
      // gets here on instance (->) reference only
      $myValue = $this->_instanceValue;
    } else {
      // gets here in all other situations
      $myValue = self::$_staticValue;
    }
    return $myValue;
  }

  public static function fnStaticDeath() {
    return $this->_instanceValue;
  }

  private static $_staticValue = 'no access to $this';
  private $_instanceValue = '$this is available';

}

$thing = new DualNature();
echo "==========\n";
printf("DualNature::fnStatic(): \"%s\"\n", DualNature::fnStatic() );
echo "==========\n";
printf("\$thing::fnStatic(): \"%s\"\n", $thing::fnStatic() );
echo "==========\n";
printf("\$thing->fnStatic(): \"%s\"\n", $thing->fnStatic() );
echo "==========\n";
printf("DualNature::fnInstance(): \"%s\"\n", DualNature::fnInstance() );
echo "==========\n";
printf("\$thing::fnInstance(): \"%s\"\n", $thing::fnInstance() );
echo "==========\n";
printf("\$thing->fnInstance(): \"%s\"\n", $thing->fnInstance() );
echo "==========\n";
printf("\$thing->fnStaticDeath(): \"%s\"\n", $thing->fnStaticDeath() );
echo "==========\n";
echo "I'M ALIVE!!!\n";

以上的输出是:
==========
PHP Strict Standards:  Non-static method DualNature::fnInstance() should not be called statically in example.php on line 45
DualNature::fnStatic(): "no access to $this"
==========
$thing::fnStatic(): "no access to $this"
==========
$thing->fnStatic(): "no access to $this"
PHP Strict Standards:  Non-static method DualNature::fnInstance() should not be called statically in example.php on line 47
==========
DualNature::fnInstance(): "no access to $this"
==========
$thing::fnInstance(): "no access to $this"
==========
$thing->fnInstance(): "$this is available"
==========
PHP Fatal error:  Using $this when not in object context in example.php on line 29

将错误报告级别更改为E_ALL将抑制默认的E_STRICT警告消息(事件仍将传播),但对$this的无效引用仍将导致致命错误并退出程序。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接