在PHP中,双冒号(::)和箭头(->)有什么区别?

233

PHP有两种不同的访问方法,但它们之间有什么区别?

$response->setParameter('foo', 'bar');

sfConfig::set('foo', 'bar');

我假设 -> (带有大于符号或尖括号的破折号)用于变量的函数,而::(双冒号)用于类的函数。这样正确吗?

=> 赋值运算符是否仅用于在数组中分配数据?与用于实例化或修改变量的=赋值运算符相比如何?


6个回答

205
当左边部分是对象实例时,使用->。否则,使用::
这意味着->主要用于访问实例成员(虽然它也可以用于访问静态成员,但这样的用法是不鼓励的),而::通常用于访问静态成员(虽然在一些特殊情况下,它也被用于访问实例成员)。
一般来说,::用于scope resolution,它可以左侧有类名、parentself或(在PHP 5.3中)staticparent指的是其使用的超类的范围;self指的是其使用的类的范围;static指的是“调用作用域”(请参阅late static bindings)。
规则是使用::进行调用仅在以下情况下才是实例调用:
  • 目标方法未声明为静态的
  • 在调用时有兼容的对象上下文,这意味着以下条件必须为真:
    1. 调用是从存在 $this 的上下文中进行的,并且
    2. $this 的类是被调用方法的类或其子类之一。

示例:

class A {
    public function func_instance() {
        echo "in ", __METHOD__, "\n";
    }
    public function callDynamic() {
        echo "in ", __METHOD__, "\n";
        B::dyn();
    }

}

class B extends A {
    public static $prop_static = 'B::$prop_static value';
    public $prop_instance = 'B::$prop_instance value';

    public function func_instance() {
        echo "in ", __METHOD__, "\n";
        /* this is one exception where :: is required to access an
         * instance member.
         * The super implementation of func_instance is being
         * accessed here */
        parent::func_instance();
        A::func_instance(); //same as the statement above
    }

    public static function func_static() {
        echo "in ", __METHOD__, "\n";
    }

    public function __call($name, $arguments) {
        echo "in dynamic $name (__call)", "\n";
    }

    public static function __callStatic($name, $arguments) {
        echo "in dynamic $name (__callStatic)", "\n";
    }

}

echo 'B::$prop_static: ', B::$prop_static, "\n";
echo 'B::func_static(): ', B::func_static(), "\n";
$a = new A;
$b = new B;
echo '$b->prop_instance: ', $b->prop_instance, "\n";
//not recommended (static method called as instance method):
echo '$b->func_static(): ', $b->func_static(), "\n";

echo '$b->func_instance():', "\n", $b->func_instance(), "\n";

/* This is more tricky
 * in the first case, a static call is made because $this is an
 * instance of A, so B::dyn() is a method of an incompatible class
 */
echo '$a->dyn():', "\n", $a->callDynamic(), "\n";
/* in this case, an instance call is made because $this is an
 * instance of B (despite the fact we are in a method of A), so
 * B::dyn() is a method of a compatible class (namely, it's the
 * same class as the object's)
 */
echo '$b->dyn():', "\n", $b->callDynamic(), "\n";

输出:

B::$prop_static: B::$prop_static的值
B::func_static(): 在B::func_static中

$b->prop_instance: B::$prop_instance的值
$b->func_static(): 在B::func_static中

$b->func_instance():
在B::func_instance中
在A::func_instance中
在A::func_instance中

$a->dyn():
在A::callDynamic中
在dynamic dyn (__callStatic)中

$b->dyn():
在A::callDynamic中
在dynamic dyn (__call)中

4
“->” 符号主要用于访问实例成员(虽然也可以用于访问静态成员,但不建议这样使用)。我不知道它也可以被用来访问静态成员。如果错误地像这样使用它,那么预期会有什么行为上的差异呢?只是出于好奇。 - lucideer
4
对于静态方法而言,这是一个良好实践的问题(方法属于类本身),但在PHP中,如果使用->调用静态方法,它不会抱怨。当然,您可能需要实例化该类才能调用静态方法,所以性能有所下降。然而,对于属性来说,存在更多问题。严格模式将引发警告,并且它可能或可能不起作用。请注意,反过来也是正确的-您可以静态地调用实例方法,但这甚至更糟糕(并且无法在此类方法实现中使用$this)。 - Artefacto

65

:: 用于静态上下文,即当某个方法或属性被声明为静态时使用:

class Math {
    public static function sin($angle) {
        return ...;
    }
}

$result = Math::sin(123);

同时,:: 运算符(作用域解析运算符,也称为 Paamayim Nekudotayim)在动态上下文中被用于调用父类的方法/属性:

class Rectangle {
     protected $x, $y;

     public function __construct($x, $y) {
         $this->x = $x;
         $this->y = $y;
     }
}

class Square extends Rectangle {
    public function __construct($x) {
        parent::__construct($x, $x);
    }
}

->动态上下文中使用,即当你处理某个类的实例时:

class Hello {
    public function say() {
       echo 'hello!';
    }
}

$h = new Hello();
$h->say();

顺便说一下:如果你没有任何面向对象编程经验,我认为不建议使用Symfony。


25

实际上,通过这个符号,我们可以调用一个静态的类方法,而且不依赖于其他初始化...

class Test {

    public $name;

    public function __construct() {
        $this->name = 'Mrinmoy Ghoshal';
    }

    public static function doWrite($name) {
        print 'Hello '.$name;
    }

    public function write() {
        print $this->name;
    }
}

在这里,doWrite()函数不依赖于任何其他方法或变量,并且它是一个静态方法。因此,我们可以通过该操作符调用此方法,而无需初始化该类的对象。

Test::doWrite('Mrinmoy'); // 输出:Hello Mrinmoy.

但如果您想以这种方式调用write方法,它将生成一个错误,因为它依赖于初始化。


17

=>运算符用于在关联数组中分配键值对。例如:

$fruits = array(
  'Apple'  => 'Red',
  'Banana' => 'Yellow'
);

它在foreach语句中的意思类似:

foreach ($fruits as $fruit => $color)
  echo "$fruit is $color in color.";

16

对于那些刚开始学习PHP 5面向对象编程的人来说,静态和实例化方法以及属性之间的区别似乎是最大的障碍之一。

双冒号运算符(在希伯来语中被称为Paamayim Nekudotayim - 幸存者)用于从静态上下文中调用对象或属性。这意味着尚未创建对象的实例。

相反地,箭头运算符调用从对象实例的引用中调用的方法或属性。

在链接到数据库的对象模型中使用静态方法可以特别有用,用于创建和删除方法,因为您可以将返回值设置为插入表ID,然后使用构造函数按行ID实例化对象。


2

是的,我刚刚遇到了我的第一个'PHP Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM'。我的错,我有一个$instance::method()应该是$instance->method()。我真傻。

奇怪的是,在我的本地机器上(运行PHP 5.3.8),这仍然可以正常工作 - 没有任何警告,即使error_reporting = E_ALL也没有 - 但在测试服务器上却完全不行,会在浏览器中出现语法错误和白屏。由于测试机器上关闭了PHP日志记录,并且托管公司太忙无法打开它,因此这不太明显。

因此,请注意:显然,某些PHP安装程序允许您使用$instance::method(),而其他安装程序不允许。

如果有人能够解释为什么会这样,请分享一下。


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