请注意,如果您正在寻找用于在函数(或方法)内部声明静态变量的静态关键字用法,那么您应该阅读“变量/变量范围”。我个人直到最近才发现我的 PHP 知识中的这个空白,不得不去谷歌搜索才找到它。我认为此页面应该有一个指向静态函数变量的“另请参见”链接。
https://php.net/manual/en/language.variables.scope.php
将类属性或方法声明为静态,使它们无需实例化类即可访问。这些也可以在实例化的类对象中静态访问。
由于静态方法可以在没有创建对象实例的情况下调用,因此在声明为静态的方法内部,伪变量 $this 不可使用。
静态地调用非静态方法会抛出 Error。
在 PHP 8.0.0 之前,静态地调用非静态方法已被弃用,并会生成 E_DEPRECATED
警告。
示例 #1 静态方法示例
<?php
class Foo {
public static function aStaticMethod() {
// ...
}
}
Foo::aStaticMethod();
$classname = 'Foo';
$classname::aStaticMethod();
?>
静态属性使用 范围解析运算符 (::
) 访问,不能通过对象运算符 (->
) 访问。
可以使用变量来引用类。变量的值不能是关键字(例如 self
、parent
和 static
)。
示例 #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"; // Undefined "Property" 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
请注意,如果您正在寻找用于在函数(或方法)内部声明静态变量的静态关键字用法,那么您应该阅读“变量/变量范围”。我个人直到最近才发现我的 PHP 知识中的这个空白,不得不去谷歌搜索才找到它。我认为此页面应该有一个指向静态函数变量的“另请参见”链接。
https://php.net/manual/en/language.variables.scope.php
这里静态访问的属性优先于调用它的类的属性。而 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();
?>
静态关键字仍然可以在函数内部使用(以非 OOP 方式)。因此,如果您需要一个与您的类一起存储的值,但它非常特定于函数,您可以使用它
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; // 致命错误
要检查类中声明的方法是否是静态的,您可以使用以下代码。PHP5 具有反射类,非常有用。
try {
$method = new ReflectionMethod( 'className::methodName );
if ( $method->isStatic() )
{
// 方法是静态的。
}
}
catch ( ReflectionException $e )
{
// 方法不存在
echo $e->getMessage();
}
*您可以在 https://php.net/manual/en/class.reflectionclass.php 上阅读有关反射类的更多信息
这也可以
class Foo {
public static $bar = 'a static property';
}
$baz = (new Foo)::$bar;
echo $baz;
您误解了继承的含义:从基类继承时,成员不会被复制。成员通过继承共享,并且派生类可以根据可见性(公用、受保护、私有)访问它们。
静态成员和非静态成员之间的区别在于,非静态成员与类的实例绑定,而静态成员与类绑定,而不是与特定实例绑定。
也就是说,静态成员由类的所有实例共享,而非静态成员对于类的每个实例都存在。
因此,在您的示例中,静态属性根据面向对象概念的原则具有正确的值。
类 Base
{
public $a;
public static $b;
}
类 Derived 扩展 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
需要注意的是,在“示例 #2”中,您也可以像这样调用可变定义的静态方法
<?php
class Foo {
public static function aStaticMethod() {
// ...
}
}
$classname = 'Foo';
$methodname = 'aStaticMethod';
$classname::{$methodname}(); // 从 PHP 5.3.0 开始,我相信
?>
了解类继承上下文中静态属性的行为非常重要
- 在父类和子类中定义的静态属性将为每个类保存不同的值。在子方法内部正确使用 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
静态变量在子类之间共享
<?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
?>
<?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();
输出
static:a // self:t
static:b // self:t
要检查函数是静态调用还是非静态调用,您需要执行以下操作
<?php
function foo () {
$isStatic = !(isset($this) && get_class($this) == __CLASS__);
}
?>
更多信息请访问 (http://blog.phpdoc.info/archives/4-Schizophrenic-Methods.html).
(我将很快将其添加到手册中)。
在 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(),从而产生冗余代码。
解决方案 1
将所有内容转换为非静态。但:这将在类的每个对象上产生冗余数据。
解决方案 2
将类 A 上的静态 $a 转换为数组,使用子类的类名作为索引。通过这样做,您也不必为子类重新定义 $a,并且超类的 $a 可以是私有的。
DataRecord 类上的简短示例,没有错误检查
<?php
抽象类 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 服务器上运行的人有所帮助。当然,延迟静态绑定使这种解决方法过时了。
从 PHP 5.3 开始,您可以使用 static 关键字的新功能。这是一个抽象单例类的示例
<?php
抽象类 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;
}
}
?>
<?php
class foo {
private static $getInitial;
public static function getInitial() {
if (self::$getInitial == null)
self::$getInitial = new foo();
return self::$getInitial;
}
}
foo::getInitial();
/*
这是使用具有静态方法的新类的示例。..
希望对您有所帮助
*/
?>
我注意到您不能在 HEREDOC 字符串中使用静态成员。以下代码
class A
{
public static $BLAH = "user";
function __construct()
{
echo <<<EOD
<h1>Hello {self::$BLAH}</h1>
EOD;
}
}
$blah = new A();
在源代码中产生以下内容
<h1>Hello {self::}</h1>
解决方法
在使用静态成员之前,将其存储在本地变量中,如下所示
class B
{
public static $BLAH = "user";
function __construct()
{
$blah = self::$BLAH;
echo <<<EOD
<h1>Hello {$blah}</h1>
EOD;
}
}
输出的源代码将是
<h1>Hello user</h1>
关于在类中初始化复杂的静态变量,您可以通过创建名为 init() 的静态函数并在类定义后立即调用它来模拟静态构造函数。
<?php
class Example {
private static $a = "Hello";
private static $b;
public static function init() {
self::$b = self::$a . " World!";
}
}
Example::init();
?>
在现实世界中,可以说当我们不想创建对象实例时,我们会使用静态方法。
例如...
validateEmail($email) {
if(T) return true;
return false;
}
// 这没有多大意义
$obj = new Validate();
$result = $obj->validateEmail($email);
// 这更有意义
$result = Validate::validateEmail($email);
使用静态元素的继承在 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
您好,以下是我简单的 Singleton 示例,我认为它对某些人可能有用。例如,您可以使用此模式连接到数据库。
<?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!
?>
最简单的静态构造函数。
因为 php 没有静态构造函数,并且您可能希望初始化静态类变量,所以有一个简单的方法,只需在类定义后立即调用您自己的函数。
例如。
<?php
function Demonstration()
{
return 'This is the result of demonstration()';
}
class MyStaticClass
{
//public static $MyStaticVar = Demonstration(); //!!! FAILS: syntax error
public static $MyStaticVar = null;
public static function MyStaticInit()
{
//这是一个静态构造函数
//因为在函数中,所有操作都是允许的,包括使用其他函数初始化
self::$MyStaticVar = Demonstration();
}
} MyStaticClass::MyStaticInit(); //调用静态构造函数
echo MyStaticClass::$MyStaticVar;
//This is the result of demonstration()
?>
值得一提的是,静态变量(以下意义)会持久存在
<?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 是完全不同的对象。
我使用实例化来直接访问静态属性。
一个简单的技巧,你可以应用(使用对象访问类中的静态属性)使用范围解析运算符
<?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;
如果你试图编写这样的类
<?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 参数。
如何实现基于静态属性的单一存储位置。
<?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();
?>
在尝试实现单例类时,你可能还想:
a) 通过将其设为私有来禁用 __clone
b) 通过定义 __clone 来抛出异常,来教训尝试克隆的用户。
<?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();(注意 $ 符号)。
我花了整整一个小时才搞清楚。
选择为正确答案的答案解决了问题。有一个有效的用例(设计模式),其中具有静态成员函数的类需要调用非静态成员函数,并且在此之前,这些静态成员也应该使用构造函数来实例化单例。
**案例:**
例如,我正在实现 Swoole HTTP 请求事件,为其提供一个回调作为具有静态成员的类。静态成员执行两件事;它通过在类构造函数中进行初始化来创建类的单例对象,第二件事是静态成员调用非静态方法 'run()' 来处理请求(通过与 Phalcon 桥接)。因此,没有构造函数的静态类和非静态调用对我来说不起作用。