14

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

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

当我运行$foobar = new FooBar; $foobar->foo()它时显示foo.

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


好的,你们没有得到我的问题:我知道静态和非静态方法之间的区别以及如何调用它们。这就是我的全部观点,如果我打电话$foobar->foo(),为什么 PHP 会尝试运行静态方法?


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

4

3 回答 3

19

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

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

你打电话

FooBar::foo();

PHP手册说...

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

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

无论您是静态调用静态方法还是在实例上调用静态方法,都无法$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');
    }
  }
}

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

于 2013-01-17T15:21:18.040 回答
9

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

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

http://php.net/ReflectionClass

于 2013-01-17T15:30:15.710 回答
2

我们可能需要更多关于 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();
}
于 2016-06-21T11:36:45.830 回答