使用双冒号(::)调用非静态方法

59
为什么我不能使用非静态方法的语法(static(class::method))?这是某种配置问题吗?
class Teste {

    public function fun1() {
        echo 'fun1';
    }
    public static function fun2() {
        echo "static fun2" ;
    }
}

Teste::fun1(); // why?
Teste::fun2(); //ok - is a static method

12
PHP 可以让你这样做,但会出现错误。是否记录/查看这些错误取决于你的配置。 - Wrikken
2
您也可以使用 $instance->staticFunc(); 调用静态函数。 - Benjamin Crouzier
11个回答

41

PHP在处理静态和非静态方法时非常宽松。这里没有提到的一件事是,如果你从类C的非静态方法中以静态方式调用非静态方法ns,那么ns内部的$this将指向C类的实例。

class A 
{
    public function test()
    {
        echo $this->name;
    }
}

class C 
{
     public function q()
     {
         $this->name = 'hello';
         A::test();
     }
}

$c = new C;
$c->q();// prints hello

如果您打开了严格的错误报告,那么实际上这是某种类型的错误,但否则不会出错。


3
谢谢@notJim,我为了解决这种情况调试了几个小时。实际上这种赋值方式相当疯狂。 - Gihan
当C是A的子类时,这并不是一个错误(例如)。实际上,在这种情况下,这可能非常有用,例如访问父级的父级上的方法。 - Tgr

26

这是PHP的已知“怪癖”。这是出于设计考虑,以防止回溯,以确定一段时间前是否实例化了对象(请记住,PHP是解释性语言,而非编译语言)。但是,如果未实例化对象,则通过作用域解析运算符访问任何非静态成员将发出致命错误。

Courtesy of PHP.net:

class User {
    const GIVEN = 1;  // class constants can't be labeled static nor assigned visibility
    public $a=2;
    public static $b=3;

    public function me(){
        echo "print me";
    }
     public static function you() {
        echo "print you";
    }
}

class myUser extends User {
}

// Are properties and methods instantiated to an object of a class, & are they accessible?
//$object1= new User();        // uncomment this line with each of the following lines individually
//echo $object1->GIVEN . "</br>";        // yields nothing
//echo $object1->GIVE . "</br>";        //  deliberately misnamed, still yields nothing
//echo $object1->User::GIVEN . "</br>";    // yields nothing
//echo $object1->a . "</br>";        // yields 2
//echo $object1->b . "</br>";        // yields nothing
//echo $object1->me() . "</br>";        // yields print me
//echo $object1->you() . "</br>";        // yields print you

// Are  properties and methods instantiated to an object of a child class,  & are accessible?
//$object2= new myUser();        // uncomment this line with each of the following lines individually
//echo $object2->GIVEN . "</br>";        // yields nothing
//echo $object2->a . "</br>";        // yields 2
//echo $object2->b . "</br>";        // yields nothing
//echo $object2->me() . "</br>";        // yields print me
//echo $object2->you() . "</br>";        // yields print you

// Are the properties and methods accessible directly in the class?
//echo User::GIVEN . "</br>";        // yields 1
//echo User::$a . "</br>";            // yields fatal error since it is not static
//echo User::$b . "</br>";            // yields 3
//echo User::me() . "</br>";        // yields print me
//echo User::you() . "</br>";        // yields print you

// Are the properties and methods copied to the child class and are they accessible?
//echo myUser::GIVEN . "</br>";        // yields 1
//echo myUser::$a . "</br>";        // yields fatal error since it is not static
//echo myUser::$b . "</br>";        // yields 3
//echo myUser::me() . "</br>";        // yields print me
//echo myUser::you() . "</br>";        // yields print you
?>


18

这是 PHP 4 的向后兼容性。在 PHP 4 中,无法区分作为静态类方法编写的全局函数和对象方法。因此两者都可以使用。

然而,随着 PHP 5 中对象模型的变化-http://php.net/oop5 - 引入了 static 关键字。

自从 PHP 5.1.3 以来,您会得到有关这些问题的适当严格警告:

Strict Standards: Non-static method Foo::bar() should not be called statically

和/或:

Strict Standards: Non-static method Foo::bar() should not be called statically, assuming $this from incompatible context

您应该在开发环境中启用它们。所以这只是向后兼容到一个语言不能够足够区分的时间,因此这是在运行时“定义”的。

现在,您已经可以在代码中定义它,但如果仍然“错误”调用它,代码也不会出现错误。

一些演示用于触发错误消息并显示不同 PHP 版本下的不同行为:http://3v4l.org/8WRQH


16

在PHP 4中,没有静态关键字(在函数声明上下文中),但仍允许使用::静态调用方法。出于向后兼容性的目的,这种做法在PHP 5中得以延续。


9

您可以这样做,但是如果在调用fun1()函数时使用$this,则会出现错误。


9

警告:在PHP 7中,静态地调用非静态方法已经被弃用,并且会产生一个E_DEPRECATED警告。未来可能会取消对静态地调用非静态方法的支持。

链接


如果它在将来被删除,我们可以使用__callStatic魔术方法来支持它,对吗? - siddhesh
1
@siddhesh 最好创建一个类的实例并调用非静态方法。 - Malus Jan
是的,我同意你的观点。但是像请求或日志记录器这样的单例对象,最好使用类名来访问,而不是创建对象并将其存储到容器中。例如,使用 Log::info($message) 而不是 $container->log->info("this is message")。 - siddhesh
1
在这种情况下,info函数可能应该是静态的,不是吗?如果它不是静态的,正如你所说__calStatic。 - Malus Jan

5

从PHP8开始,这个方法不再起作用。

在PHP 8中,调用非静态方法的能力被最终移除

调用非静态方法的能力已被移除。因此,在检查具有类名的非静态方法时,is_callable()将失败(必须使用对象实例进行检查)。

它最初是在PHP 7上弃用的

对未声明为静态的方法进行静态调用已弃用,可能会在未来被删除。


但为什么? - cachius
1
因为这从来没有意义,@cachius。它会导致混乱的代码,很容易出错。在任何情况下,静态调用非静态方法都应该失败,因为如果该方法不是静态的,则需要访问实例。如果以静态方式调用它,则没有实例,应用程序将崩溃。如果该方法不需要访问实例,则没有理由将其作为实例方法开始。 - yivi

1

不确定为什么PHP允许这样做,但你不想养成这样的习惯。你的例子之所以有效,是因为它没有尝试访问类的非静态属性。

就算是一些简单的操作:

<?php
class Foo {

    private $color;

    public function bar() {
        echo 'before';
        $this->color = "blue";
        echo 'after';
    }
}

Foo::bar();

会导致致命错误


1
在大多数编程语言中,你需要拥有一个类的实例才能执行实例方法。但是在 PHP 中,当你使用作用域解析运算符调用实例方法时,PHP 会创建一个临时实例。

0

我注意到,如果你在类内部调用非静态方法self::test(),不会像调用Class::test()那样发出严格标准的警告。我相信这与LSB无关,因为我的类没有被扩展(在php 5.5上测试过)。


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