【问题标题】:Why PHP uses static methods in object context?为什么 PHP 在对象上下文中使用静态方法?
【发布时间】:2013-01-01 04:14:18
【问题描述】:

我有以下代码(真的,这是我的真实代码):

<?php
class Foobar
{
  public static function foo()
  {
    exit('foo');
  }
}

当我运行$foobar = new FooBar; $foobar-&gt;foo() 时,它会显示foo

为什么 PHP 会尝试在对象上下文中使用静态方法?有没有办法避免这种情况?


好吧,你们没有明白我的问题:我知道静态和非静态方法之间的区别以及如何调用它们。这就是我的重点,如果我调用$foobar-&gt;foo(),为什么 PHP 会尝试运行静态方法?


注意:我运行 PHP 5.4.4,错误报告给E_ALL

【问题讨论】:

  • 可能相关:__call and __callStatic
  • @Daniel 不过应该叫Foobar::foo
  • 澄清一下:我确实希望在我使用它时找到这个静态方法,这对我来说没有意义。
  • @MaximeFabre 去接受一些答案......
  • 该方法将始终被静态调用。恰好有两种不同的语法来调用它。尽量避免使用对您没有意义的语法,这样也可以避免与以后阅读您的代码的人混淆。但是,有时在注入到另一个对象中的实例化对象上调用静态方法可能是有意义的,因为您不一定知道传入的对象类的完全限定名。这只是在该实例中的一种方便。

标签: php static-methods magic-methods


【解决方案1】:

要调用静态方法,请不要使用:

$foobar = new FooBar;
$foobar->foo()

你打电话

FooBar::foo();

PHP 手册说...

将类属性或方法声明为静态使它们可以访问 不需要类的实例化。声明为的属性 static 不能用实例化的类对象访问(尽管 静态方法可以)。

这就是为什么您可以在实例上调用该方法的原因,即使这不是您想要做的。

无论您是静态调用静态方法还是在实例上调用静态方法,都无法在静态方法中访问$this

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

您可以检查您是否处于静态上下文中,尽管我会质疑这是否是矫枉过正...

class Foobar
{
  public static function foo()
  {
    $backtrace = debug_backtrace();
    if ($backtrace[1]['type'] == '::') {
      exit('foo');
    }
  }
}

另外一点 - 我相信该方法总是在静态上下文中执行,即使它是在实例上调用的。如果我错了,我很高兴得到纠正。

【讨论】:

  • 他在对象上下文中调用静态函数时可能会出现错误,所以他的问题是为什么没有错误,而不是怎么做。
  • Is there a way to avoid this ? 这也是问题的一部分,没有回答。
  • shadyyx - 本着公平的精神,我在编辑问题后编辑了答案,因此最初并没有完全回答问题。 Valdars - 在我的回答中,它在 PHP 中是允许的(如果我没记错的话,还有 Java)。 phant0m 我会将其添加到我的答案中。
  • @EM-Creations 哪个位?请记住编辑历史记录:stackoverflow.com/posts/14382206/revisions
  • @SteveFenton OP 似乎已经掌握了静态和非静态之间的区别以及你“不应该”能够做他们所做的事情这一事实。
【解决方案2】:

由于 PHP 是一种非常宽容的语言,您可以通过重载 __callStatic 来防止这种默认行为,也可以使用反射来验证方法范围。

http://php.net/manual/en/language.oop5.overloading.php#object.call

http://php.net/ReflectionClass

【讨论】:

    【解决方案3】:

    我们可能需要更多关于 FooBar 声明的信息。 显然,你不能声明两个方法 foo() ,即使一个是静态方法,另一个是实例方法:

    class FooBar
    {
      public static function foo()
      {
        return 'I am FooBar::foo().';
      }
      public function foo()
      {
        return 'I am FooBar->foo().';
      }
    }
    // result to Fatal error: Cannot redeclare FooBar::foo()
    

    所以,我想你想达到一个神奇的__call() 方法,就像这样:

    class FooBar
    {
      public $foo = 'I am FooBar->foo().'; 
      // yes we can have a property with the same name than a method
    
      // this is the static method that we want to avoid
      public static function foo()
      {
        return 'I am FooBar::foo().';
      }
    
      // this is the method that should be call
      public function __call( $method , $arguments = array() )
      {
        if( isset( $this->$method ) ) // or anything else
        {
          return $this->$method; // or anything else
        }
        else
        {
          // restore fatal error
          trigger_error( sprintf( 'Call to undefined method %s::%s()' , get_class( $this ) , $method ) , E_USER_ERROR );
        }
      }
    
    }
    

    要实现这一点,请查看以下代码:

    $foobar = new FooBar;
    
    try
    {
      // safe way to detect if a method is static
      // @see http://php.net/manual/en/class.reflectionmethod.php
      $rfx = new ReflectionMethod( get_class( $foobar ).'::foo' );
      if( $rfx->isStatic() )
      {
        // the method exists and is static
        // but we do not want to call it directly
        // why not involving the magic public method `__call()`?
        // sounds like a plan...
        echo $foobar->__call( 'foo' );
      }
      else
      {
        // the method exists and is not static
        echo $foobar->foo();
      }
    }
    catch( ReflectionException $e )
    {
      // the method does not exist, let's do some kind of magic
      echo $foobar->foo();
    }
    

    【讨论】:

      猜你喜欢
      • 2011-01-21
      • 2012-07-02
      • 2020-05-24
      • 1970-01-01
      • 1970-01-01
      • 2016-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多