作用域解析运算符 (::)

作用域解析运算符(也称为 Paamayim Nekudotayim)或更简单的说法,双冒号,是一个标记,允许访问类的 常量静态 属性或 静态 方法,或其父类之一。此外,静态属性或方法可以通过 延迟静态绑定 覆盖。

在类定义之外引用这些项时,请使用类的名称。

可以使用变量引用类。变量的值不能是关键字(例如 selfparentstatic)。

Paamayim Nekudotayim 乍一看似乎是为双冒号命名一个奇怪的选择。然而,在编写 Zend 引擎 0.5(为 PHP 3 提供支持)时,Zend 团队决定将其命名为 Paamayim Nekudotayim。它实际上确实意味着双冒号——用希伯来语!

示例 #1 :: 从类定义之外

<?php
class MyClass {
const
CONST_VALUE = 'A constant value';
}

$classname = 'MyClass';
echo
$classname::CONST_VALUE;

echo
MyClass::CONST_VALUE;
?>

三个特殊关键字 selfparentstatic 用于从类定义内部访问属性或方法。

示例 #2 :: 从类定义内部

<?php
class OtherClass extends MyClass
{
public static
$my_static = 'static var';

public static function
doubleColon() {
echo
parent::CONST_VALUE . "\n";
echo
self::$my_static . "\n";
}
}

$classname = 'OtherClass';
$classname::doubleColon();

OtherClass::doubleColon();
?>

当扩展类覆盖父类的某个方法的定义时,PHP 不会调用父类的方法。是否调用父类方法取决于扩展类。这也适用于 构造函数和析构函数重载魔术 方法定义。

示例 #3 调用父类的方法

<?php
class MyClass
{
protected function
myFunc() {
echo
"MyClass::myFunc()\n";
}
}

class
OtherClass extends MyClass
{
// 覆盖父类的定义
public function myFunc()
{
// 但仍然调用父函数
parent::myFunc();
echo
"OtherClass::myFunc()\n";
}
}

$class = new OtherClass();
$class->myFunc();
?>

另请参见 有关静态调用技巧的一些示例

添加说明

用户贡献说明 11 个说明

237
Theriault
14 年前
类常量、类属性(静态)和类函数(静态)都可以共享同一个名称,并且可以使用双冒号访问。

<?php

class A {

public static
$B = '1'; # 静态类变量。

const B = '2'; # 类常量。

public static function B() { # 静态类函数。
return '3';
}

}

echo
A::$B . A::B . A::B(); # 输出:123
?>
105
1naveengiri at gmail dot com
7 年前
在 PHP 中,您使用 self 关键字来访问静态属性和方法。

问题是,无论 method() 是静态声明还是非静态声明,您都可以用 self::method() 替换 $this->method(),无论在何处。那么您应该使用哪一个呢?

请考虑以下代码

class ParentClass {
function test() {
self::who(); // 将输出 'parent'
$this->who(); // 将输出 'child'
}

function who() {
echo 'parent';
}
}

class ChildClass extends ParentClass {
function who() {
echo 'child';
}
}

$obj = new ChildClass();
$obj->test();
在这个示例中,self::who() 将始终输出 ‘parent’,而 $this->who() 将取决于对象的类。

现在我们可以看到,self 指的是调用它的类,而 $this 指的是当前对象的类。

因此,您应该仅在 $this 不可用时,或当您不想允许子类覆盖当前方法时使用 self。
35
guy at syntheticwebapps dot com
10 年前
似乎您可以使用比类名更多的内容来引用类定义之外的类定义的静态变量、常量和静态函数,使用 :: 。该语言似乎允许您使用对象本身。

例如
class horse
{
static $props = {'order'=>'mammal'};
}
$animal = new horse();
echo $animal::$props['order'];

// 返回 'mammal'

这似乎没有记录,但我认为这在语言中是一个重要的便利性。我希望看到它被记录并作为有效的支持。

如果没有正式支持,另一种方法似乎会很混乱,类似于这样

$animalClass = get_class($animal);
echo $animalClass::$props['order'];
20
jasverix at NOSPAM dot gmail dot com
10 年前
刚刚发现使用类名也可以调用祖先类的类似函数。

<?php

Anchestor {

public
$Prefix = '';

private
$_string = 'Bar';
public function
Foo() {
return
$this->Prefix.$this->_string;
}
}

MyParent extends Anchestor {
public function
Foo() {
$this->Prefix = null;
return
parent::Foo().'Baz';
}
}

Child extends MyParent {
public function
Foo() {
$this->Prefix = 'Foo';
return
Anchestor::Foo();
}
}

$c = new Child();
echo
$c->Foo(); //return FooBar, 因为 Prefix 在 Anchestor::Foo() 中被使用,所以 MyParent::Foo() 不会执行

?>

Child 类调用了 Anchestor::Foo(),因此 MyParent::Foo() 永远不会被执行。
12
giovanni at gargani dot it
15 年前
嗯,几行代码像瑞士军刀一样调用父类方法。唯一的限制是不能用在 "按引用" 的参数上。
主要优势是您不需要知道父类的 "实际" 签名,您只需要知道需要哪些参数。

<?php
someclass extends some superclass {
// 可用于构造函数
函数 __construct($ineedthisone) {
$args=func_get_args();
/* $args 将包含传递给 __construct 的任何参数。
* 您的形式参数不会影响 func_get_args() 的工作方式
*/
call_user_func_array(array('parent',__FUNCTION__),$args);
}
// 但这不仅仅用于 __construct
函数 anyMethod() {
$args=func_get_args();
call_user_func_array(array('parent',__FUNCTION__),$args);
}
// 注意:php 5.3.0 甚至允许您执行
函数 anyMethod() {
// 需要 php >=5.3.x
call_user_func_array(array('parent',__FUNCTION__),func_get_args());
}

}
?>
10
remy dot damour at ----no-spam---laposte dot net
14 年前
从 php 5.3.0 开始,您可以使用 'static' 作为作用域值,如下例所示(与 'self' 关键字相比,为继承机制增加了灵活性...)。

<?php

A {
const
C = 'constA';
public function
m() {
echo static::
C;
}
}

B extends A {
const
C = 'constB';
}

$b = new B();
$b->m();

// 输出:constB
?>
2
luka8088 at gmail dot com
15 年前
一个小技巧,可以绕过 php 严格标准...
函数调用者会找到调用它的对象,以便静态方法可以修改它,在静态函数中用它替换 $this,但不会出现严格警告 :)

<?php

error_reporting
(E_ALL + E_STRICT);

函数
caller () {
$backtrace = debug_backtrace();
$object = isset($backtrace[0]['object']) ? $backtrace[0]['object'] : null;
$k = 1;

while (isset(
$backtrace[$k]) && (!isset($backtrace[$k]['object']) || $object === $backtrace[$k]['object']))
$k++;

return isset(
$backtrace[$k]['object']) ? $backtrace[$k]['object'] : null;
}

a {

public
$data = 'Empty';

函数
set_data () {
b::set();
}

}

b {

static 函数
set () {
// $this->data = 'Data from B !';
// 在静态函数中使用 this 会抛出警告...
caller()->data = 'Data from B !';
}

}

$a = new a();
$a->set_data();
echo
$a->data;

?>

输出:Data from B !

没有警告或错误!
0
csaba dot dobai at php-sparcle dot com
15 年前
对于 '后期静态绑定' 主题,我发布了以下代码,它演示了一种技巧,说明如何在后期类中设置变量值,并在父类(或父类的父类等)中打印该值。

<?php

class cA
{
/**
* 使用直接默认值的测试属性
*/
protected static $item = 'Foo';

/**
* 使用间接默认值的测试属性
*/
protected static $other = 'cA';

public static function
method()
{
print
self::$item."\r\n"; // 它总是打印 'Foo' ... :(
print self::$other."\r\n"; // 我们认为,这个只会打印 'cA',但... :)
}

public static function
setOther($val)
{
self::$other = $val; // 在此作用域内设置一个值。
}
}

class
cB extends cA
{
/**
* 重新定义默认值的测试属性
*/
protected static $item = 'Bar';

public static function
setOther($val)
{
self::$other = $val;
}
}

class
cC extends cA
{
/**
* 重新定义默认值的测试属性
*/
protected static $item = 'Tango';

public static function
method()
{
print
self::$item."\r\n"; // 它总是打印 'Foo' ... :(
print self::$other."\r\n"; // 我们认为,这个只会打印 'cA',但... :)
}

/**
* 现在我们不再重新声明 setOther() 方法,只是为了好玩,使用带有 'self::' 的 cA。
*/
}

class
cD extends cA
{
/**
* 重新定义默认值的测试属性
*/
protected static $item = 'Foxtrot';

/**
* 现在我们不再重新声明所有方法来完成这个问题。
*/
}

cB::setOther('cB'); // 这是 cB::method()!
cB::method(); // 这是 cA::method()!
cC::setOther('cC'); // 这是 cA::method()!
cC::method(); // 这是 cC::method()!
cD::setOther('cD'); // 这是 cA::method()!
cD::method(); // 这是 cA::method()!

/**
* 结果: ->
* Foo
* cB
* Tango
* cC
* Foo
* cD
*
* 这是什么鬼? :)
*/

?>
-1
wouter at interpotential dot com
14 年前
值得注意的是,提到的变量也可以是对象实例。这似乎是从实例的角度来看,引用尽可能高的继承层次结构中的静态函数的最简单方法。我在非静态方法中使用 static::something() 时遇到了一些奇怪的行为。

请看下面的示例代码

<?php
class FooClass {
public function
testSelf() {
return
self::t();
}

public function
testThis() {
return
$this::t();
}

public static function
t() {
return
'FooClass';
}

function
__toString() {
return
'FooClass';
}
}

class
BarClass extends FooClass {
public static function
t() {
return
'BarClass';
}

}

$obj = new BarClass();
print_r(Array(
$obj->testSelf(), $obj->testThis(),
));
?>

输出结果

<pre>
Array
(
[0] => FooClass
[1] => BarClass
)
</pre>

正如你所见,__toString 对此没有任何影响。以防你好奇这是否是实现方式。
-1
developit at mail dot ru
18 年前
你使用 'self' 来访问当前类,使用 'parent' 来访问父类,那么如何访问父类的父类呢?或者如何访问深层类层次结构的根类?答案是使用类名。这将像 'parent' 一样工作。以下代码示例解释了我的意思。以下代码

<?php
class A
{
protected
$x = 'A';
public function
f()
{
return
'['.$this->x.']';
}
}

class
B extends A
{
protected
$x = 'B';
public function
f()
{
return
'{'.$this->x.'}';
}
}

class
C extends B
{
protected
$x = 'C';
public function
f()
{
return
'('.$this->x.')'.parent::f().B::f().A::f();
}
}

$a = new A();
$b = new B();
$c = new C();

print
$a->f().'<br/>';
print
$b->f().'<br/>';
print
$c->f().'<br/>';
?>

将输出

[A] -- {B} -- (C){C}{C}[C]
-1
gazianis2200 at gmail dot com
6 个月前
<?php
/**
* 从类外部访问常量
*/
class Foo{
public const
A = "Constant A";
}
echo
Foo::A;
echo
"\n";

/**
* 在其自身类中访问常量
*/

class Bar{
public const
A = "Constant A";
public function
abc(){
echo
self::A;
echo
"\n";
}
}

$obj = new Bar;
$obj->abc();

/**
* 在其子类中访问常量
*/

class Baz extends Bar{
public function
abc(){
echo
parent::A;
}
}
$obj = new Baz;
$obj->abc();

// 静态属性和静态方法也遵循此原则。
To Top