【问题标题】:Calling non-static method with double-colon(::)使用双冒号(::) 调用非静态方法
【发布时间】:2011-04-14 20:43:36
【问题描述】:

为什么我不能使用带有方法 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

【问题讨论】:

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

标签: php static


【解决方案1】:

你可以这样做,但是如果你在名为fun1()的函数中使用$this,你的代码会出错

【讨论】:

    【解决方案2】:

    在大多数语言中,您需要有一个类的实例才能执行实例方法。当您使用范围解析运算符调用实例方法时,PHP 似乎会创建一个临时实例。

    【讨论】:

      【解决方案3】:

      这是 PHP 的一个已知“怪癖”。设计是为了防止反向传播来确定我们是否确实实例化了一个对象(请记住,PHP 是解释的,而不是编译的)。但是,如果对象未实例化,则通过范围解析运算符访问任何非静态成员将引发致命错误。

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

      【讨论】:

        【解决方案4】:

        不知道为什么 PHP 允许这样做,但您不想养成这样做的习惯。您的示例之所以有效,是因为它不会尝试访问该类的非静态属性。

        一些简单的事情:

        <?php
        class Foo {
        
            private $color;
        
            public function bar() {
                echo 'before';
                $this->color = "blue";
                echo 'after';
            }
        }
        
        Foo::bar();
        

        会导致致命错误

        【讨论】:

          【解决方案5】:

          PHP 4 没有 static 关键字(在函数声明上下文中),但仍然允许使用 :: 静态调用方法。为了向后兼容,这在 PHP 5 中继续。

          【讨论】:

            【解决方案6】:

            PHP 在静态与非静态方法方面非常松散。我在这里没有注意到的一件事是,如果您调用非静态方法,ns 静态地从类 C 的非静态方法中,$this 内部 ns 将引用您的实例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
            

            如果您有严格的错误报告,这实际上是某种错误,但不是。

            【讨论】:

            • 感谢@notJim,我已经调试这种情况好几个小时了。其实这种作业很疯狂。
            • 当 C 是 A 的子类 (example) 时,这不是错误,实际上在这种情况下非常有用,例如访问父级的父级上的方法。
            【解决方案7】:

            这是 PHP 4 的向后兼容性。在 PHP 4 中,对象方法和作为静态类方法编写的全局函数之间没有区别。因此两者都起作用了。

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

            然后,从 PHP 5.1.3 开始,您会收到关于以下内容的严格标准警告:

            严格标准:不应静态调用非静态方法 Foo::bar()

            和/或:

            严格标准:不应静态调用非静态方法 Foo::bar(),假设 $this 来自不兼容的上下文

            您应该为您的开发设置启用它。所以它只是向后兼容到一个语言不能有足够差异的时候,所以这是在运行时“定义”的。

            现在您可以在代码中定义它,但是如果您仍然称它为“错误”,代码不会中断。

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

            【讨论】:

              【解决方案8】:

              我注意到,如果你从一个类中调用非静态方法 self::test(),不会发出严格标准的警告,就像你调用 Class::test() 时一样。我相信这与 LSB 无关,因为我的课程没有扩展(在 php 5.5 上测试)?

              【讨论】:

                【解决方案9】:

                警告 在 PHP 7 中,不推荐静态调用非静态方法,并且会生成 E_DEPRECATED 警告。将来可能会删除对静态调用非静态方法的支持。

                Link

                【讨论】:

                • 如果它将来删除我们可以使用 __callStatic 魔术方法来支持它吗?我说的对吗?
                • @siddhesh 最好创建一个类的实例并调用非静态方法。
                • 是的,我同意你的观点,但是像 request 或 logger 这样的单例想要使用类名访问而不是创建对象并将其存储到容器中。例如 Log::info($message) 而不是 $container->log->info("this is message")
                • @siddhesh 在这种情况下,信息函数可能应该是静态的,不是吗?如果它不是你所说的静态__calStatic。
                【解决方案10】:

                静态和非静态调用同一方法的一种方法是使用魔术方法__call__callStatic

                FluentMath 类(下面的代码)是一个示例,您可以在其中静态或非静态调用方法 addsubtract

                $res1 = FluentMath::add(5)    // add method called statically
                    ->add(3)                  // add method called non-statically
                    ->subtract(2)
                    ->result();
                
                $res2 = FluentMath::subtract(1)->add(10)->result();
                

                FluentMath 类

                class FluentMath
                {
                    private $result = 0;
                
                    public function __call($method, $args)
                    {
                        return $this->call($method, $args);
                    }
                
                    public static function __callStatic($method, $args)
                    {
                        return (new static())->call($method, $args);
                    }
                
                    private function call($method, $args)
                    {
                        if (! method_exists($this , '_' . $method)) {
                            throw new Exception('Call undefined method ' . $method);
                        }
                
                        return $this->{'_' . $method}(...$args);
                    }
                
                    private function _add($num)
                    {
                        $this->result += $num;
                
                        return $this;
                    }
                
                    private function _subtract($num)
                    {
                        $this->result -= $num;
                
                        return $this;
                    }
                
                    public function result()
                    {
                        return $this->result;
                    }
                }
                

                如果您想了解该类如何工作的完整说明,请查看:

                【讨论】:

                  【解决方案11】:

                  从 PHP8 开始,这不再有效。

                  静态调用非静态方法的能力是finally removed in PHP 8

                  静态调用非静态方法的功能已被移除。因此is_callable() 在检查带有类名的非静态方法时会失败(必须检查对象实例)。

                  原来是deprecated on PHP 7

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

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2013-03-20
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多