请注意,如果您正在寻找在函数(或方法)内声明静态变量时使用的静态关键字,则应阅读“变量/变量作用域”。我自己直到最近才发现我的 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"; // 未定义的“属性”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();
?>
静态关键字仍然可以在函数内部使用(以非面向对象的方式)。因此,如果您需要一个与您的类一起存储的值,但它非常特定于函数,您可以使用它。
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 有一个 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
您误解了继承的含义:当您从基类继承时,成员不会被复制。成员通过继承共享,并且派生类可以根据可见性(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
这也是可能的
class Foo {
public static $bar = 'a static property';
}
$baz = (new Foo)::$bar;
echo $baz;
需要注意的是,在“示例 #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
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
静态变量在子类之间共享。
<?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 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 服务器上操作的人有所帮助。当然,后期静态绑定使这种变通方法变得多余。
要检查函数是静态调用还是非静态调用,您需要执行以下操作
<?php
function foo () {
$isStatic = !(isset($this) && get_class($this) == __CLASS__);
}
?>
更多信息请访问 (http://blog.phpdoc.info/archives/4-Schizophrenic-Methods.html).
(我很快就会将其添加到手册中)。
从 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;
}
}
?>
<?php
class foo {
private static $getInitial;
public static function getInitial() {
if (self::$getInitial == null)
self::$getInitial = new foo();
return self::$getInitial;
}
}
foo::getInitial();
/*
这是使用带有静态方法的新类的示例。
我希望它有帮助
*/
?>
我注意到你不能在 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>
关于在类中初始化复杂的静态变量,您可以通过创建一个名为 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
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
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
最简单的静态构造函数。
因为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()
?>
如何基于静态属性实现一个单一存储位置。
<?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();
?>
值得一提的是,静态变量(以下意义上的)会持久化
<?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 参数。
在尝试实现单例类时,你可能还想
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();(注意 $ 符号)。
我花了1个小时才弄明白这一点。
选为正确答案的回答解决了问题。存在一个有效的用例(设计模式),其中具有静态成员函数的类需要调用非静态成员函数,并且在此之前,这些静态成员也应该使用构造函数实例化单例。
**案例:**
例如,我正在实现 Swoole HTTP 请求事件,并为其提供一个作为类的回调函数,该类具有静态成员。静态成员执行两件事;它通过在类构造函数中进行初始化来创建类的单例对象,第二,这个静态成员的作用是调用非静态方法“run()”来处理请求(通过与 Phalcon 桥接)。因此,没有构造函数和非静态调用的静态类对我来说不起作用。