2024 年 PHP 日本大会

静态关键字

提示

此页描述了使用static关键字定义静态方法和属性。 static还可以用于定义静态变量定义静态匿名函数以及进行后期静态绑定。有关static的这些含义,请参考这些页面。

将类属性或方法声明为静态,使其无需实例化类即可访问。这些也可以在实例化的类对象中静态访问。

静态方法

由于静态方法无需创建对象的实例即可调用,因此在声明为静态的方法内部无法使用伪变量$this

警告

静态调用非静态方法会抛出Error异常。

在 PHP 8.0.0 之前,静态调用非静态方法已被弃用,并会生成E_DEPRECATED警告。

示例 #1 静态方法示例

<?php
class Foo {
public static function
aStaticMethod() {
// ...
}
}

Foo::aStaticMethod();
$classname = 'Foo';
$classname::aStaticMethod();
?>

静态属性

静态属性使用作用域解析运算符 (::) 访问,不能通过对象运算符 (->) 访问。

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

示例 #2 静态属性示例

<?php
class Foo
{
public static
$my_static = 'foo';

public function
staticValue() {
return
self::$my_static;
}
}

class
Bar extends Foo
{
public function
fooStatic() {
return
parent::$my_static;
}
}


print
Foo::$my_static . "\n";

$foo = new Foo();
print
$foo->staticValue() . "\n";
print
$foo->my_static . "\n"; // 未定义的“属性”my_static

print $foo::$my_static . "\n";
$classname = 'Foo';
print
$classname::$my_static . "\n";

print
Bar::$my_static . "\n";
$bar = new Bar();
print
$bar->fooStatic() . "\n";
?>

PHP 8 中上述示例的输出类似于

foo
foo

Notice: Accessing static property Foo::$my_static as non static in /in/V0Rvv on line 23

Warning: Undefined property: Foo::$my_static in /in/V0Rvv on line 23

foo
foo
foo
foo
添加注释

用户贡献的注释 28 条注释

inkredibl
16 年前
请注意,如果您正在寻找在函数(或方法)内声明静态变量时使用的静态关键字,则应阅读“变量/变量作用域”。我自己直到最近才发现我的 PHP 知识中存在这个差距,不得不谷歌搜索才能找到这个信息。我认为此页面应该有一个指向静态函数变量的“另请参见”链接。
https://php.net/manual/en/language.variables.scope.php
payal001 at gmail dot com
13 年前
这里静态访问的属性优先使用调用它的类的属性。而 self 关键字仅强制使用当前类。请参考以下示例

<?php
class a{

static protected
$test="class a";

public function
static_test(){

echo static::
$test; // 结果为 class b
echo self::$test; // 结果为 class a

}

}

class
b extends a{

static protected
$test="class b";

}

$obj = new b();
$obj->static_test();
?>
artekpuck at gmail dot com
6 年前
值得一提的是,每个静态变量只有一个值,对所有实例都相同。
[email protected]
9年前
静态关键字仍然可以在函数内部使用(以非面向对象的方式)。因此,如果您需要一个与您的类一起存储的值,但它非常特定于函数,您可以使用它。

class aclass {
public static function b(){
static $d=12; // 仅在第一次函数调用时设置为 12
$d+=12;
return "$d\n";
}
}

echo aclass::b(); //24
echo aclass::b(); //36
echo aclass::b(); //48
echo aclass::$d; //致命错误
[email protected]
9年前
要检查类中声明的方法是静态的还是非静态的,可以使用以下代码。PHP5 有一个 Reflection 类,非常有用。

try {
$method = new ReflectionMethod( 'className::methodName' );
if ( $method->isStatic() )
{
// 方法是静态的。
}
}
catch ( ReflectionException $e )
{
// 方法不存在
echo $e->getMessage();
}

您可以阅读更多关于 Reflection 类的信息:https://php.net/manual/en/class.reflectionclass.php
匿名用户
19年前
您误解了继承的含义:当您从基类继承时,成员不会被复制。成员通过继承共享,并且派生类可以根据可见性(public、protected、private)访问它们。

静态成员和非静态成员之间的区别仅仅在于:非静态成员与类的实例绑定,而静态成员与类绑定,而不是与特定实例绑定。
也就是说,静态成员由类的所有实例共享,而非静态成员对于类的每个实例都存在。

因此,在您的示例中,静态属性具有正确的数值,符合面向对象的概念原则。
class Base
{
public $a;
public static $b;
}

class Derived extends Base
{
public function __construct()
{
$this->a = 0;
parent::$b = 0;
}
public function f()
{
$this->a++;
parent::$b++;
}
}

$i1 = new Derived;
$i2 = new Derived;

$i1->f();
echo $i1->a, ' ', Derived::$b, "\n";
$i2->f();
echo $i2->a, ' ', Derived::$b, "\n";

输出
1 1
1 2
[email protected]
6 年前
这也是可能的

class Foo {
public static $bar = 'a static property';
}

$baz = (new Foo)::$bar;
echo $baz;
匿名用户
11年前
需要注意的是,在“示例 #2”中,您还可以按如下方式调用可变定义的静态方法:

<?php
class Foo {
public static function
aStaticMethod() {
// ...
}
}

$classname = 'Foo';
$methodname = 'aStaticMethod';
$classname::{$methodname}(); // 我相信从 PHP 5.3.0 开始
?>
[email protected]
6 年前
理解类继承环境下静态属性的行为非常重要。

- 在父类和子类中定义的静态属性将为每个类保存不同的值。在子类方法内部正确使用 self:: 与 static:: 至关重要,以便引用预期的静态属性。

- 仅在父类中定义的静态属性将共享一个公共值。

<?php
declare(strict_types=1);

class
staticparent {
static
$parent_only;
static
$both_distinct;

function
__construct() {
static::
$parent_only = 'fromparent';
static::
$both_distinct = 'fromparent';
}
}

class
staticchild extends staticparent {
static
$child_only;
static
$both_distinct;

function
__construct() {
static::
$parent_only = 'fromchild';
static::
$both_distinct = 'fromchild';
static::
$child_only = 'fromchild';
}
}

$a = new staticparent;
$a = new staticchild;

echo
'Parent: parent_only=', staticparent::$parent_only, ', both_distinct=', staticparent::$both_distinct, "<br/>\r\n";
echo
'Child: parent_only=', staticchild::$parent_only, ', both_distinct=', staticchild::$both_distinct, ', child_only=', staticchild::$child_only, "<br/>\r\n";
?>

将输出
Parent: parent_only=fromchild, both_distinct=fromparent
Child: parent_only=fromchild, both_distinct=fromchild, child_only=fromchild
b1tchcakes
8年前
<?php

trait t {
protected
$p;
public function
testMe() {echo 'static:'.static::class. ' // self:'.self::class ."\n";}
}

class
a { use t; }
class
b extends a {}

echo (new
a)->testMe();
echo (new
b)->testMe();

outputs
static:a // self:t
static:b // self:t
[email protected]
15年前
静态变量在子类之间共享。

<?php
class MyParent {

protected static
$variable;
}

class
Child1 extends MyParent {

function
set() {

self::$variable = 2;
}
}

class
Child2 extends MyParent {

function
show() {

echo(
self::$variable);
}
}

$c1 = new Child1();
$c1->set();
$c2 = new Child2();
$c2->show(); // 输出 2
?>
[email protected]
14年前
在 PHP 5.2.x 或更早版本中,由于缺乏后期静态绑定,您可能会在子类中初始化静态变量时遇到问题。

<?php
class A {
protected static
$a;

public static function
init($value) { self::$a = $value; }
public static function
getA() { return self::$a; }
}

class
B extends A {
protected static
$a; // 重新定义 $a 供自身使用

// 继承 init() 方法
public static function getA() { return self::$a; }
}

B::init('lala');
echo
'A::$a = '.A::getA().'; B::$a = '.B::getA();
?>

这将输出
A::$a = lala; B::$a =

如果 init() 方法对于(几乎)所有子类看起来都一样,则无需在每个子类中实现 init(),从而避免产生冗余代码。

方案一
将所有内容转换为非静态。但是:这会在类的每个对象上产生冗余数据。

方案二
将 A 类中的静态 $a 转换为数组,使用子类的类名作为索引。这样,您也不必为子类重新定义 $a,并且超类的 $a 可以是私有的。

一个没有错误检查的 DataRecord 类的简短示例

<?php
abstract class DataRecord {
private static
$db; // MySQLi 连接,对所有子类都相同
private static $table = array(); // 子类的表数组

public static function init($classname, $table, $db = false) {
if (!(
$db === false)) self::$db = $db;
self::$table[$classname] = $table;
}

public static function
getDB() { return self::$db; }
public static function
getTable($classname) { return self::$table[$classname]; }
}

class
UserDataRecord extends DataRecord {
public static function
fetchFromDB() {
$result = parent::getDB()->query('select * from '.parent::getTable('UserDataRecord').';');

// 等等 ...
return $result; // UserDataRecord 对象数组
}
}

$db = new MySQLi(...);
UserDataRecord::init('UserDataRecord', 'users', $db);
$users = UserDataRecord::fetchFromDB();
?>

希望这对一些出于某种原因需要在 PHP 5.2.x 服务器上操作的人有所帮助。当然,后期静态绑定使这种变通方法变得多余。
aidan at php dot net
19年前
要检查函数是静态调用还是非静态调用,您需要执行以下操作

<?php
function foo () {
$isStatic = !(isset($this) && get_class($this) == __CLASS__);
}
?>

更多信息请访问 (http://blog.phpdoc.info/archives/4-Schizophrenic-Methods.html).

(我很快就会将其添加到手册中)。
tolean_dj at yahoo dot com
13 年前
从 php 5.3 开始,您可以使用 static 关键字的新特性。这是一个抽象单例类的示例

<?php

abstract class Singleton {

protected static
$_instance = NULL;

/**
* 防止直接创建对象
*/
final private function __construct() { }

/**
* 防止对象克隆
*/
final private function __clone() { }

/**
* 返回新的或现有的 Singleton 实例
* @return Singleton
*/
final public static function getInstance(){
if(
null !== static::$_instance){
return static::
$_instance;
}
static::
$_instance = new static();
return static::
$_instance;
}

}
?>
gratcypalma at gmail dot om
13 年前
<?php
class foo {
private static
$getInitial;

public static function
getInitial() {
if (
self::$getInitial == null)
self::$getInitial = new foo();
return
self::$getInitial;
}
}

foo::getInitial();

/*
这是使用带有静态方法的新类的示例。
我希望它有帮助
*/

?>
ssj dot narutovash at gmail dot com
16 年前
我注意到你不能在 HEREDOC 字符串中使用静态成员。以下代码

类 A
{
public static $BLAH = "user";

function __construct()
{
echo <<<EOD
<h1>Hello {self::$BLAH}</h1>
EOD;
}
}

$blah = new A();

在源代码中产生以下内容

<h1>Hello {self::}</h1>

解决方案

在使用静态成员之前,将其存储在局部变量中,如下所示

类 B
{
public static $BLAH = "user";

function __construct()
{
$blah = self::$BLAH;
echo <<<EOD
<h1>Hello {$blah}</h1>
EOD;
}
}

输出的源代码将是

<h1>Hello user</h1>
Jay Cain
14年前
关于在类中初始化复杂的静态变量,您可以通过创建一个名为 init() 的静态函数并在类定义后立即调用它来模拟静态构造函数。

<?php
class Example {
private static
$a = "Hello";
private static
$b;

public static function
init() {
self::$b = self::$a . " World!";
}
}
Example::init();
?>
manishpatel2280 at gmail dot com
11年前
在现实世界中,我们可以说当我们不想创建对象实例时会使用静态方法。

例如……

validateEmail($email) {
if(T) return true;
return false;
}

//这没有多大意义
$obj = new Validate();
$result = $obj->validateEmail($email);

//这更有意义
$result = Validate::validateEmail($email);
zerocool at gameinsde dot ru
16 年前
您好,这是我的简单单例示例,我认为它对某些人可能有用。例如,您可以使用此模式连接到数据库。

<?php

class MySingleton
{
private static
$instance = null;

private function
__construct()
{
$this-> name = 'Freddy';

}

public static function
getInstance()
{
if(
self::$instance == null)
{
print
"Object created!<br>";
self::$instance = new self;

}

return
self::$instance;

}

public function
sayHello()
{
print
"Hello my name is {$this-> name}!<br>";

}

public function
setName($name)
{
$this-> name = $name;

}

}

//

$objA = MySingleton::getInstance(); // Object created!

$objA-> sayHello(); // Hello my name is Freddy!

$objA-> setName("Alex");

$objA-> sayHello(); // Hello my name is Alex!

$objB = MySingleton::getInstance();

$objB-> sayHello(); // Hello my name is Alex!

$objB-> setName("Bob");

$objA-> sayHello(); // Hello my name is Bob!

?>
michalf at ncac dot torun dot pl
19年前
PHP 中静态元素的继承是一场噩梦。考虑以下代码

<?php
class BaseClass{
public static
$property;
}

class
DerivedClassOne extends BaseClass{
}

class
DerivedClassTwo extends BaseClass{
}

DerivedClassOne::$property = "foo";
DerivedClassTwo::$property = "bar";

echo
DerivedClassOne::$property; //你可能会天真地以为输出是 "foo"...
?>

你预期输出是什么?"foo"?错!是"bar"!!!静态变量不会被继承,它们指向BaseClass::$property。

就这一点而言,我认为静态变量/方法在继承方面不起作用,实在是很可惜。记住这一点,调试时可以节省时间。

此致敬礼 - michal
Mirco
14年前
最简单的静态构造函数。

因为PHP没有静态构造函数,而你可能希望初始化静态类变量,所以有一种简单的方法,只需在类定义之后直接调用你自己的函数。

例如。

<?php
function Demonstration()
{
return
'This is the result of demonstration()';
}

class
MyStaticClass
{
//public static $MyStaticVar = Demonstration(); //!!! 错误:语法错误
public static $MyStaticVar = null;

public static function
MyStaticInit()
{
//这是静态构造函数
//因为在函数中,所有操作都是允许的,包括使用其他函数进行初始化

self::$MyStaticVar = Demonstration();
}
}
MyStaticClass::MyStaticInit(); //调用静态构造函数

echo MyStaticClass::$MyStaticVar;
//This is the result of demonstration()
?>
valentin at balt dot name
14年前
如何基于静态属性实现一个单一存储位置。

<?php
class a {

public function
get () {
echo
$this->connect();
}
}
class
b extends a {
private static
$a;

public function
connect() {
return
self::$a = 'b';
}
}
class
c extends a {
private static
$a;

public function
connect() {
return
self::$a = 'c';
}
}
$b = new b ();
$c = new c ();

$b->get();
$c->get();
?>
jkenigso at utk dot edu
10年前
值得一提的是,静态变量(以下意义上的)会持久化

<?php
class StaticVars
{
public static
$a=1;
}
$b=new StaticVars;
$c=new StaticVars;

echo
$b::$a; //输出 1
$c::$a=2;
echo
$b::$a; //输出 2!
?>

请注意,$c::$a=2 改变了 $b::$a 的值,即使 $b 和 $c 是完全不同的对象。
admin at shopinson dot com
4年前
我使用实例化来直接访问静态属性。

一个简单的技巧,你可以应用(使用对象访问类中的静态属性)范围解析运算符
<?php
class Shopinson {
const
MY_CONSTANT = 'the value of MY_CONSTANT ';
}

class
Godwin extends Shopinson
{
public static
$myconstant = ' The Paamayim Nekudotayim or double-colon.';
public function
SaySomething(){
echo
parent::MY_CONSTANT .PHP_EOL; // 输出:the value of MY_CONSTANT
echo self::$myconstant; // 输出:The Paamayim Nekudotayim or double-colon.
}
}

$my_class = new Godwin();
print
$my_class::$myconstant;
$my_class::SaySomething();
echo
Godwin::$myconstant;
Godwin::SaySomething();

?>

print $my_class::$myconstant;
michael at digitalgnosis dot removethis dot com
19年前
如果你试图编写这样的类

<?php

class Base
{
static function
Foo ()
{
self::Bar();
}
}

class
Derived extends Base
{
function
Bar ()
{
echo
"Derived::Bar()";
}
}

Derived::Foo(); // 我们希望这输出 "Derived::Bar()"

?>

你会发现PHP做不到(除非有人知道正确的方法?),因为'self::'指的是拥有代码的类,而不是运行时实际调用的类。(__CLASS__ 也不起作用,因为:A. 它不能出现在 :: 之前,B. 它的行为类似于 'self')

但如果你必须这样做,这里有一个(稍微有点糟糕的)解决方法

<?php

class Base
{
function
Foo ( $class = __CLASS__ )
{
call_user_func(array($class,'Bar'));
}
}

class
Derived extends Base
{
function
Foo ( $class = __CLASS__ )
{
parent::Foo($class);
}

function
Bar ()
{
echo
"Derived::Bar()";
}
}

Derived::Foo(); // 这次有效。

?>

请注意,Base::Foo() 可能不再声明为 'static',因为静态方法不能被重写(这意味着如果错误级别包括 E_STRICT,它将触发错误。)

如果 Foo() 接受参数,则将它们列在 $class=__CLASS__ 之前,在大多数情况下,你可以在整个代码中忘记该参数。

主要需要注意的是,你必须在每个子类中重写 Foo(),并且在调用 parent::Foo() 时必须始终包含 $class 参数。
vvikramraj at yahoo dot com
16 年前
在尝试实现单例类时,你可能还想
a) 通过将其设为私有来禁用 __clone
b) 通过定义 __clone 来抛出异常,从而惩罚试图克隆的用户
Mathijs Vos
16 年前
<?php
class foo
{
public static
$myStaticClass;

public function
__construct()
{
self::myStaticClass = new bar();
}
}

class
bar
{
public function
__construct(){}
}
?>

请注意,这行不通。
使用 self::$myStaticClass = new bar(); 代替 self::myStaticClass = new bar();(注意 $ 符号)。
我花了1个小时才弄明白这一点。
fakhar_anwar123 at hotmail dot com
4年前
选为正确答案的回答解决了问题。存在一个有效的用例(设计模式),其中具有静态成员函数的类需要调用非静态成员函数,并且在此之前,这些静态成员也应该使用构造函数实例化单例。

**案例:**
例如,我正在实现 Swoole HTTP 请求事件,并为其提供一个作为类的回调函数,该类具有静态成员。静态成员执行两件事;它通过在类构造函数中进行初始化来创建类的单例对象,第二,这个静态成员的作用是调用非静态方法“run()”来处理请求(通过与 Phalcon 桥接)。因此,没有构造函数和非静态调用的静态类对我来说不起作用。
To Top