PHP Conference Japan 2024

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

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

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

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

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

示例 #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 条注释

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
?>
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。
guy at syntheticwebapps dot com
11 年前
似乎可以使用不止类名来引用类定义的静态变量、常量和静态函数,从使用 :: 的类外部引用。该语言似乎允许您使用对象本身。

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

// 产生 'mammal'

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



如果它没有得到官方支持,替代方案似乎会很混乱,就像这样

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

<?php

class Anchestor {

public
$Prefix = '';

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

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

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

$c = new Child();
echo
$c->Foo(); //return FooBar, 因为 Prefix,就像在 Anchestor::Foo() 中

?>

Child 类调用 Anchestor::Foo(),因此 MyParent::Foo() 从未运行。
giovanni at gargani dot it
15 年前
好吧,几行“瑞士军刀”代码来调用父方法。唯一的限制是你不能将其用于“按引用”参数。
主要优点是您不需要知道超类的“实际”签名,您只需要知道需要哪些参数。

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

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

<?php

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

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

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

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

<?php

error_reporting
(E_ALL + E_STRICT);

function
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;
}

class
a {

public
$data = 'Empty';

function
set_data () {
b::set();
}

}

class
b {

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

}

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

?>

输出:Data from B !

没有警告或错误!
csaba dot dobai at php-sparcle dot com
15 年前
对于“延迟静态绑定”主题,我在下面发布了一段代码,演示了如何在延迟类中设置变量值,并在父类(或父类的父类等)中打印该值。



<?php

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; // 在此作用域内设置一个值。
}
}

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

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

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

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

/**
* 现在我们放弃重新声明 setOther() 方法,仅为乐趣使用 cA 和 'self::'。
*/
}

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
*
* 到底怎么回事?! :)
*/

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

请参阅以下示例代码

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

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

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

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

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

}

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

输出结果为

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

如您所见,__toString 对此没有任何影响。以防您想知道这是否就是这样做的。
developit at mail dot ru
18 年前
您使用 'self' 来访问此类,'parent' 来访问父类,那么如何访问父类的父类呢?或者访问深度类层次结构的最根类?答案是使用类名。这将像 'parent' 一样工作。以下是一个解释我的意思的例子。以下代码

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

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

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]
gazianis2200 at gmail dot com
10 个月前
<?php
/**
* 从类外部访问常量
*/
class Foo{
public const
A = "常量 A";
}
echo
Foo::A;
echo
"\n";

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

class Bar{
public const
A = "常量 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