PHP Conference Japan 2024

get_class

(PHP 4, PHP 5, PHP 7, PHP 8)

get_class返回对象的类名

描述

get_class(对象 $object = ?): 字符串

获取给定object的类名。

参数

object

被测试的对象。

注意: 从 PHP 7.2.0 开始,不再允许显式地将null作为object传递,并会发出E_WARNING。从 PHP 8.0.0 开始,当使用null时,会发出TypeError

返回值

返回object所属类的名称。

如果object是存在于命名空间中的类的实例,则返回该类的限定命名空间名称。

错误/异常

如果get_class()被调用时传入的参数不是对象,则会抛出TypeError。在 PHP 8.0.0 之前,会引发E_WARNING级别的错误。

如果get_class()在类外部被调用且没有参数,则会抛出Error。在 PHP 8.0.0 之前,会引发E_WARNING级别的错误。

变更日志

版本 描述
8.3.0 在没有参数的情况下调用get_class()现在会发出E_DEPRECATED警告;以前,在类内部调用此函数会返回该类的名称。
8.0.0 从类外部调用此函数,且没有任何参数,现在将抛出Error。以前,会引发E_WARNING,并且函数返回false
7.2.0 在此版本之前,object的默认值为null,并且它与不传递任何值的效果相同。现在,null已从object的默认值中移除,并且不再是有效的输入。

示例

示例 #1 使用get_class()

<?php

foo {
函数
name()
{
echo
"我的名字是 " , get_class($this) , "\n";
}
}

// 创建一个对象
$bar = new foo();

// 外部调用
echo "它的名字是 " , get_class($bar) , "\n";

// 内部调用
$bar->name();

?>

以上示例将输出

Its name is foo
My name is foo

示例 #2 在超类中使用get_class()

<?php

抽象类 bar {
public function
__construct()
{
var_dump(get_class($this));
var_dump(get_class());
}
}

foo extends bar {
}

new
foo;

?>

以上示例将输出

string(3) "foo"
string(3) "bar"

示例 #3 使用get_class()和命名空间类

<?php

命名空间 Foo\Bar;

Baz {
public function
__construct()
{

}
}

$baz = new \Foo\Bar\Baz;

var_dump(get_class($baz));
?>

以上示例将输出

string(11) "Foo\Bar\Baz"

参见

添加注释

用户贡献的笔记 36 条笔记

jjanak at webperfection dot net
10 年前
>= 5.5

::class
完全限定类名,而不是 get_class

<?php
命名空间 my\library\mvc;

Dispatcher {}

print
Dispatcher::class; // FQN == my\library\mvc\Dispatcher

$disp = new Dispatcher;

print
$disp::class; // 解析错误
dave at shax dot com
10 年前
许多其他评论中的人希望获取没有命名空间的类名。一些关于如何做到这一点的奇怪的代码建议 - 这不是我想要编写的!所以想添加我自己的方法。

<?php
function get_class_name($classname)
{
if (
$pos = strrpos($classname, '\\')) return substr($classname, $pos + 1);
return
$pos;
}
?>

此外,我还进行了一些快速基准测试,发现 strrpos() 也是最快的。微优化 = 宏优化!

39.0954 毫秒 - preg_match()
28.6305 毫秒 - explode() + end()
20.3314 毫秒 - strrpos()

(参考,这是使用的调试代码。c() 是一个基准测试函数,每个闭包运行 10,000 次。)

<?php
c
(
function(
$class = 'a\b\C') {
if (
preg_match('/\\\\([\w]+)$/', $class, $matches)) return $matches[1];
return
$class;
},
function(
$class = 'a\b\C') {
$bits = explode('\\', $class);
return
end($bits);
},
function(
$class = 'a\b\C') {
if (
$pos = strrpos($class, '\\')) return substr($class, $pos + 1);
return
$pos;
}
);
?>
mail dot temc at gmail dot com
12 年前
人们似乎混淆了 __METHOD__、get_class($obj) 和 get_class() 在类继承方面的作用。

这是一个很好的例子,应该可以永远解决这个问题

<?php

class Foo {
function
doMethod(){
echo
__METHOD__ . "\n";
}
function
doGetClassThis(){
echo
get_class($this).'::doThat' . "\n";
}
function
doGetClass(){
echo
get_class().'::doThat' . "\n";
}
}

class
Bar extends Foo {

}

class
Quux extends Bar {
function
doMethod(){
echo
__METHOD__ . "\n";
}
function
doGetClassThis(){
echo
get_class($this).'::doThat' . "\n";
}
function
doGetClass(){
echo
get_class().'::doThat' . "\n";
}
}

$foo = new Foo();
$bar = new Bar();
$quux = new Quux();

echo
"\n--doMethod--\n";

$foo->doMethod();
$bar->doMethod();
$quux->doMethod();

echo
"\n--doGetClassThis--\n";

$foo->doGetClassThis();
$bar->doGetClassThis();
$quux->doGetClassThis();

echo
"\n--doGetClass--\n";

$foo->doGetClass();
$bar->doGetClass();
$quux->doGetClass();

?>

输出

--doMethod--
Foo::doMethod
Foo::doMethod
Quux::doMethod

--doGetClassThis--
Foo::doThat
Bar::doThat
Quux::doThat

--doGetClass--
Foo::doThat
Foo::doThat
Quux::doThat
ovidiu.bute [at] gmail.com
14 年前
如果您使用命名空间,此函数将返回包含命名空间的类名,因此请注意您的代码是否对此进行了任何检查。例如

命名空间 Shop;

<?php
class Foo
{
public function
__construct()
{
echo
"Foo";
}
}

//不同的文件

include('inc/Shop.class.php');

$test = new Shop\Foo();
echo
get_class($test);//返回 Shop\Foo
?>
匿名用户
10 年前
如果您希望获取文件路径,并且您的文件结构如下所示

project -> system -> libs -> controller.php
project -> system -> modules -> foo -> foo.php

并且 foo.php 中的 foo() 扩展了 controller.php 中的 controller(),如下所示

<?PHP
namespace system\modules\foo;

class
foo extends \system\libs\controller {
public function
__construct() {
parent::__construct();
}
}
?>

并且您希望在 controller() 中知道 foo.php 的路径,这可能对您有所帮助

<?PHP
namespace system\libs;

class
controller {
protected function
__construct() {
$this->getChildPath();
}
protected function
getChildPath() {
echo
dirname(get_class($this));
}
}
?>

<?PHP
$f
= new foo(); // system\modules\foo
?>
ozana at omdesign dot cz
13 年前
获取不带命名空间的类的最简单方法

<?php
namespace a\b\c\d\e\f;

class
Foo {

public function
__toString() {
$class = explode('\\', __CLASS__);
return
end($class);
}
}

echo new
Foo(); // 输出 Foo
?>
macnimble at gmail dot com
12 年前
需要一种快速的方法来解析命名空间类的名称?试试这个

<?php
namespace Engine;
function
parse_classname ($name)
{
return array(
'namespace' => array_slice(explode('\\', $name), 0, -1),
'classname' => join('', array_slice(explode('\\', $name), -1)),
);
}
final class
Kernel
{
final public function
__construct ()
{
echo
'<pre>', print_r(parse_classname(__CLASS__),1), '</pre>';
// 或者使用此方法获取类名的一行代码:
// echo join('', array_slice(explode('\\', __CLASS__), -1));
}
}
new
Kernel();
?>

输出
数组
(
[namespace] => 数组
(
[0] => Engine
)

[classname] => Kernel
)
匿名用户
16 年前
在 Perl(和其他一些语言)中,您可以以对象和类(也称为静态)上下文调用某些方法。我在 PHP5 中为我的一个类创建了这样的方法,但发现 PHP5 中的静态方法“不知道”调用子类的名称,因此我使用回溯来确定它。我不喜欢这样的技巧,但只要 PHP 没有替代方案,就必须这样做

public function table_name() {
$result = null;
if (isset($this)) { // 对象上下文
$result = get_class($this);
}
else { // 类上下文
$result = get_class();
$trace = debug_backtrace();
foreach ($trace as &$frame) {
if (!isset($frame['class'])) {
break;
}
if ($frame['class'] != $result) {
if (!is_subclass_of($frame['class'], $result)) {
break;
}
$result = $frame['class'];
}
}
}
return $result;
}
emmanuel dot antico at gmail dot com
11 年前
/**
* 获取不带命名空间的对象类名
*/
function get_real_class($obj) {
$classname = get_class($obj);

if (preg_match('@\\\\([\w]+)$@', $classname, $matches)) {
$classname = $matches[1];
}

return $classname;
}
Edward
16 年前
我之前评论中的代码并不完全正确。我认为这个是对的。

<?
抽象类 Singleton {
protected static $__CLASS__ = __CLASS__;

protected function __construct() {


}

抽象受保护函数 init();

/**
* 获取此单例的实例。如果不存在实例,则创建一个新实例并返回。
* 如果存在实例,则返回现有实例。
*/
公共静态函数 getInstance() {
静态 $instance;

$class = self::getClass();

如果 ($instance === null) {
$instance = new $class();
$instance->init();
}

返回 $instance;
}

/**
* 返回扩展此类的子类的类名
*
* @return string 类名
*/
私有静态函数 getClass() {
$implementing_class = static::$__CLASS__;
$original_class = __CLASS__;

如果 ($implementing_class === $original_class) {
die("您必须在您的 Singleton 类中提供一个 <code>protected static \$__CLASS__ = __CLASS__;</code> 语句!");
}

返回 $implementing_class;
}
}
?>
RQuadling at GMail dot com
9 年前
关于从命名空间类名获取类名,然后使用 basename() 似乎可以很好地解决问题。

<?php
命名空间 Foo\Bar;

抽象类
Baz
{
公共函数
report()
{
echo
'__CLASS__ ', __CLASS__, ' ', basename(__CLASS__), PHP_EOL,
'get_called_class ', get_called_class(), ' ', basename(get_called_class()), PHP_EOL;
}
}

Snafu 扩展 Baz
{
}

(新
Snafu)->report();
?>

产生以下输出...

__CLASS__ Foo\Bar\Baz Baz
get_called_class Foo\Bar\Snafu Snafu
Hayley Watson
7 年前
尽管您可以从类的实例中调用类的静态方法,就好像它们是对象实例方法一样,但很高兴知道,由于类在 PHP 代码中由其名称(字符串)表示,因此 get_class() 的返回值也是如此。

<?php
$t
->Faculty();
SomeClass::Faculty(); // $t instanceof SomeClass
"SomeClass"::Faculty();
get_class($t)::Faculty();
?>

第一个是合法的,但最后一个清楚地向阅读它的人表明 Faculty() 是一个静态方法(因为方法的名称肯定不是)。
dense
7 年前
好吧,如果您对别名类调用 get_class(),您将获得原始类名。

<?php

Person {}

class_alias('Person', 'User');

$me = new User;

var_dump( get_class($me) ); // 'Person'

?>
davidsch
3 年前
下面讨论了如何创建一个允许子类的单例。似乎现在使用 get_called_class 有了一个比下面讨论的更简洁的解决方案,它不需要为每个子类重写方法。

例如

<?php
抽象类 MySuperclass {
静态私有
$instances = 数组();

静态公共函数
getInstance(): ACFBlock {
$className = get_called_class();
如果 (!isset(
self::$instances[$className])) {
self::$instances[$className] = new static();
}
返回
self::$instances[$className];
}

私有函数
__construct() {
// 单例
}
}

MySubclass 扩展 MySuperclass {
}

MySubclass::getInstance();
Nanhe Kumar
10 年前
<?php
Parent{
}
Child 扩展 Parent{
}
$c = new Child();
echo
get_class($c) //Child
?>
<?php
Parent{
公共函数
getClass(){
echo
get_class();
}
}
Child 扩展 Parent{
}
$obj = new Child();
$obj->getClass(); //输出 Parent
?>
<?php
Parent{
公共函数
getClass(){
echo
get_class($this);
}
}
Child 扩展 Parent{
}
$obj = new Child();
$obj->getClass(); // Parent
?>
Aaron
14 年前
这有时可以用来代替 get_called_class()。我在父类中使用了此函数来获取扩展它的类的名称。
luke at liveoakinteractive dot com
17 年前
此注释是对 davidc at php dot net 之前帖子的回复。不幸的是,为从静态方法获取类名而发布的解决方案不适用于继承类。

观察以下内容
<?php
BooBoof {
公共静态函数
getclass() {
返回
__CLASS__;
}

公共函数
retrieve_class() {
返回
get_class($this);
}
}

CooCoof 扩展 BooBoof {
}

echo
CooCoof::getclass();
// 输出 BooBoof

$coocoof = new CooCoof;
echo
$coocoof->retrieve_class();
// 输出 CooCoof
?>

__CLASS__ 和 get_class($this) 在继承类中的工作方式不同。到目前为止,我无法确定一种可靠的方法来从静态方法获取实际的类。
Michael Richey
13 年前
尝试此页面上描述的各种单例基类方法,我创建了一个基类和桥接函数,允许它在没有 get_called_class() 的情况下工作,如果它不可用。与这里列出的其他方法不同,我选择不阻止使用 __construct() 或 __clone()。



<?php
抽象类 Singleton {
protected static
$m_pInstance;
final public static function
getInstance(){
$class = static::getClass();
if(!isset(static::
$m_pInstance[$class])) {
static::
$m_pInstance[$class] = new $class;
}
return static::
$m_pInstance[$class];
}
final public static function
getClass(){
return
get_called_class();
}
}

// 我不记得我在哪里找到这个了,但这是为了允许 php < 5.3 使用此方法。
if (!function_exists('get_called_class')) {
function
get_called_class($bt = false, $l = 1) {
if (!
$bt)
$bt = debug_backtrace();
if (!isset(
$bt[$l]))
throw new
Exception("无法找到调用的类 -> 堆栈级别过深。");
if (!isset(
$bt[$l]['type'])) {
throw new
Exception('type 未设置');
}
else
switch (
$bt[$l]['type']) {
case
'::':
$lines = file($bt[$l]['file']);
$i = 0;
$callerLine = '';
do {
$i++;
$callerLine = $lines[$bt[$l]['line'] - $i] . $callerLine;
} while (
stripos($callerLine, $bt[$l]['function']) === false);
preg_match('/([a-zA-Z0-9\_]+)::' . $bt[$l]['function'] . '/', $callerLine, $matches);
if (!isset(
$matches[1])) {
// 必须是极端情况。
throw new Exception("找不到调用者类:源方法调用被隐藏。");
}
switch (
$matches[1]) {
case
'self':
case
'parent':
return
get_called_class($bt, $l + 1);
default:
return
$matches[1];
}
// 不会到达这里。
case '->': switch ($bt[$l]['function']) {
case
'__get':
// 极端情况 -> 获取调用对象的类
if (!is_object($bt[$l]['object']))
throw new
Exception("极端情况失败。__get 在非对象上调用。");
return
get_class($bt[$l]['object']);
default: return
$bt[$l]['class'];
}

default: throw new
Exception("未知的回溯方法类型");
}
}
}

class
B extends Singleton {
}

class
C extends Singleton {
}

$b = B::getInstance();
echo
'class: '.get_class($b);

echo
'<br />';

$c = C::getInstance();
echo echo
'class: '.get_class($c);
?>
这将返回
class: b
class: c
me at nwhiting dot com
13 年前
用于提取名称空间前缀已去除的类名称的方法。

<?php
/**
* 使用 get_class 返回类的名称,并去除名称空间。
* 这在类作用域内不起作用,因为 get_class() 是一种解决方法,
* 它使用 get_class_name(get_class());
*
* @param object|string $object 要检索名称的对象或类名称

* @return string 去除名称空间后的类名称
*/
function get_class_name($object = null)
{
if (!
is_object($object) && !is_string($object)) {
return
false;
}

$class = explode('\\', (is_string($object) ? $object : get_class($object)));
return
$class[count($class) - 1];
}
?>

以及供所有人进行单元测试的良好体验!

<?php
命名空间 testme\here;

class
TestClass {

public function
test()
{
return
get_class_name(get_class());
}
}

class
GetClassNameTest extends \PHPUnit_Framework_TestCase
{
public function
testGetClassName()
{
$class = new TestClass();
$std = new \stdClass();
$this->assertEquals('TestClass', get_class_name($class));
$this->assertEquals('stdClass', get_class_name($std));
$this->assertEquals('Test', get_class_name('Test'));
$this->assertFalse(get_class_name(null));
$this->assertFalse(get_class_name(array()));
$this->assertEquals('TestClass', $class->test());
}
}
?>
dodgie74 at NOSPAM dot yahoo dot NOSPAM dot co dot uk
17 年前
如错误 #30934 中所述(实际上不是错误,而是设计决策的结果),“self” 关键字在编译时绑定。其中一个含义是,在基类方法中,任何使用“self”关键字的地方都将引用该基类,而不管实际调用该方法的(派生)类是什么。当尝试从派生类中继承的方法内部调用重写后的静态方法时,这就会成为问题。例如

<?php
class Base
{
protected
$m_instanceName = '';

public static function
classDisplayName()
{
return
'Base Class';
}

public function
instanceDisplayName()
{
//此处,我们希望“self”引用实际的类,这可能是继承此方法的派生类,不一定是此基类
return $this->m_instanceName . ' - ' . self::classDisplayName();
}
}

class
Derived extends Base
{
public function
Derived( $name )
{
$this->m_instanceName = $name;
}

public static function
classDisplayName()
{
return
'Derived Class';
}
}

$o = new Derived('My Instance');
echo
$o->instanceDisplayName();
?>

在上面的示例中,假设运行时绑定(其中关键字“self”引用调用方法的实际类,而不是定义方法的类)将产生以下输出

My Instance - Derived Class

但是,假设编译时绑定(其中关键字“self”引用定义方法的类),这是 php 的工作方式,输出将是

My Instance - Base Class

这里奇怪的是,“$this”在运行时绑定到对象的实际类(显然),但“self”在编译时绑定,这在我看来似乎违反直觉。“self”始终是定义它的类名的同义词,程序员知道这一点,因此他/她可以使用类名;程序员无法知道调用方法的实际类名(因为该方法可以在派生类上调用),这在我看来是“self”应该有用的东西。

但是,撇开关于设计决策的问题不谈,仍然存在如何实现类似于“self”在运行时绑定的行为的问题,以便在派生类上或从派生类内部调用的静态和非静态方法都作用于该派生类。get_class() 函数可用于模拟“self”关键字对静态方法的运行时绑定功能

<?php
class Base
{
protected
$m_instanceName = '';

public static function
classDisplayName()
{
return
'Base Class';
}

public function
instanceDisplayName()
{
$realClass = get_class($this);
return
$this->m_instanceName . ' - ' . call_user_func(array($realClass, 'classDisplayName'));
}
}

class
Derived extends Base
{
public function
Derived( $name )
{
$this->m_instanceName = $name;
}

public static function
classDisplayName()
{
return
'Derived Class';
}
}

$o = new Derived('My Instance');
echo
$o->instanceDisplayName();
?>

输出
My Instance - Derived Class

我意识到有些人可能会回应“为什么不只使用附加了“Class”的类名而不是 classDisplayName() 方法”,这忽略了重点。重点不是返回的实际字符串,而是希望从继承的非静态方法内部对重写后的静态方法使用真实类的概念。以上只是一个简化版本的现实世界问题,该问题过于复杂而无法用作示例。

如果之前已经提到过,我表示歉意。
bramus at bram dot us
17 年前
@ Frederik

<?
$class = $bt[count($bt) - 1]['class'];
?>

应为

<?
$class = $bt[count($bt) - 2]['class'];
?>

;-)
yicheng zero-four at gmail dot com
17 年前
这是对 luke at liveoakinteractive dot com 和 davidc at php dot net 的回复。静态方法和变量,根据定义,绑定到类类型而不是对象实例。您不应该需要动态地找出静态方法属于哪个类,因为您的代码上下文应该使其非常明显。您的问题表明您可能还没有完全理解 OOP(我也花了一段时间才理解)。

Luke,从您的特定代码片段中观察到的行为在您思考时是完全合理的。getclass() 方法定义在 BooBoof 中,因此 __CLASS__ 宏将绑定到 BooBoof 并相对于 BooBoof 类定义。CooCoof 是 BooBoof 的子类这一事实仅仅意味着它获得了对 BooBoof::getclass() 的快捷方式。因此,实际上,您实际上是在以一种复杂的方式询问:“属于方法调用 BooBoof::getclass() 的类是什么?”正确的解决方案(如果您确实想要/需要这样做)是在 CooCoof 定义内部简单地实现 CooCoof::getclass() { return __CLASS__; },以及您希望模仿此行为的任何子类。CooCoof::getclass() 将具有预期的行为。
benjaminhill at gmail dot com
18 年前
更多怪异之处

class Parent {
function displayTableName() {
echo get_class($this);
echo get_class();
}
}

class Child {
function __construct() {
$this->displayTableName();
}
}

将返回
- Child
- Parent

因此,当他们说“PHP5 中不需要对象”时,他们并不是真的这样意思。
refrozen dot com
19 年前
philip at cornado dot com,它返回调用它的类的值,而不是实例的名称……导致继承产生意外的返回值
Edward
16 年前
使用从 PHP 5.3.0 开始可用的延迟静态绑定,现在可以以最小的开销在子类中实现抽象单例类。此处解释了延迟静态绑定:http://nl2.php.net/manual/en/language.oop5.late-static-bindings.php

简而言之,它引入了一个新的“static::”关键字,该关键字在运行时进行评估。在以下代码中,我使用它来确定子单例类的类名。

<?
抽象类 Singleton {
protected static $__CLASS__ = __CLASS__;
protected static $instance;

protected function __construct() {


static::$instance = $this;
$this->init();
}

抽象受保护函数 init();

protected function getInstance() {
$class = static::getClass();

if (static::$instance===null) {
static::$instance = new $class;
}

return static::$instance;
}

私有静态函数 getClass() {
if (static::$__CLASS__ == __CLASS__) {
die("您必须在您的 Singleton 类中提供一个 <code>protected static \$__CLASS__ = __CLASS__;</code> 语句!");
}

return static::$__CLASS__;
}
}
?>

然后可以按如下方式实现一个示例单例类

<?
class A extends Singleton {
protected static $__CLASS__ = __CLASS__; // 在每个单例类中提供此项。

protected function someFunction() {
$instance = static::getInstance();
// ...
}
}
?>

希望这对您节省一些时间:)
andregs at NOSPAM dot gmail dot NOSPAM dot com
16 年前
在阅读了前面的评论后,这是我为获得子类的最终类名所做的最佳尝试

<?php

class Singleton
{
private static
$_instances = array();
protected final function
__construct(){}

/**
* @param string $classname
* @return Singleton
*/
protected static function getInstance()
{
$classname = func_get_arg(0);
if (! isset(
self::$_instances[$classname]))
{
self::$_instances[$classname] = new $classname();
}
return
self::$_instances[$classname];
}

}

class
Child extends Singleton
{
/**
* @return Child
*/
public static function getInstance()
{
return
parent::getInstance(get_class());
}
}

?>

子类必须重写“getInstance”并且不能重写“__construct”。
Frederik Krautwald
17 年前
由于 PHP 5 引擎允许在静态调用的函数中获取最终类,这是一个对下面发布的示例的修改版本。

<?php
抽象类 Singleton {
受保护且静态的
$_instances = array();

受保护的函数
__construct() {}

受保护且静态的函数
getInstance() {
$bt = debug_backtrace();
$class = $bt[count($bt) - 1]['class'];
if (!isset(
self::$_instances[$class])) {
self::$_instances[$class] = new $class();
}
return
self::$_instances[$class];
}
}
A 扩展 Singleton {
公共且静态的函数
getInstance() {
return
parent::getInstance();
}
}
B 扩展 Singleton {
公共且静态的函数
getInstance() {
return
parent::getInstance();
}
}
C 扩展 A {
公共且静态的函数
getInstance() {
return
parent::getInstance();
}
}

$a = A::getInstance();
$b = B::getInstance();
$c = C::getInstance();

echo
"\$a is a " . get_class($a) . "<br />";
echo
"\$b is a " . get_class($b) . "<br />";
echo
"\$c is a " . get_class($c) . "<br />";
?>

我不知道如果跳过 `debug_backtrace()` 并且改为让 `getInstance()` 接受一个通过 `get_class()` 方法获取的类作为参数,就像下面帖子中描述的那样,性能是否会提高。

通过在 `Singleton` 类中将 `getInstance()` 设置为受保护的,该函数需要被重写(良好的 OOP 实践)。

需要说明的是,如果 `$class` 为空或未定义,则没有错误检查,这将导致致命错误。目前,虽然我看不出当 `getInstance()` 受保护时(即必须在子类中重写)它如何发生——但是通过良好的编码实践,你应该始终进行错误检查。
mightye at gmail dot com
17 年前
致:Bryan

在这个模型中,如果你的单例变量实际上是一个数组,它仍然是可行的。考虑
<?php
抽象类 Singleton {
受保护且最终静态的
$instances = array();

受保护的
__construct(){}

受保护的函数
getInstance() {
$class = get_real_class(); // 虚构函数返回最终类名,而不是代码从中执行的类
if (!isset(self::$instances[$class])) {
self::$instances[$class] = new $class();
}
return
self::$instances[$class];
}
}
A 扩展 Singleton {
}
B 扩展 Singleton {
}

$a = A::getInstance();
$b = B::getInstance();

echo
"\$a is a " . get_class($a) . "<br />";
echo
"\$b is a " . get_class($b) . "<br />";
?>
这将输出
$a is a A
$b is a B

如其他地方所述,唯一的替代方法是将 `getInstance()` 设为受保护的抽象方法,接受类名作为参数,并为每个使用其本地对象范围中的 `get_class()` 并将其传递给超类的子类扩展此调用。

或者创建像这样的单例工厂
<?php
最终类 SingletonFactory {
受保护且静态的
$instances = array();

受保护的
getInstance($class) {
if (!isset(
self::$instances[$class])) {
self::$instances[$class] = new $class();
}
return
self::$instances[$class];
}
}
?>
当然,这种后一种模型的缺点是类本身无法决定它是否为单例,而是调用代码决定这一点,并且真正想要或*需要*成为单例的类无法强制执行这一点,即使是通过使其构造函数受保护也不行。

基本上,这些设计模式以及各种其他元操作(在对象本质上而不是对象保存的数据上运行的事物)可以从准确了解该对象的最终类型中获益匪浅,而无法访问此信息则需要变通方案。
janci
17 年前
致 yicheng zero-four at gmail dot com:另一个可能更好的示例,其中在静态方法中找出真实类(而不是我们所在的类)应该非常有用,就是单例模式。

目前还没有办法创建一个抽象的单例类,只需扩展它而无需更改扩展的类即可使用。考虑以下示例
<?php
抽象类 Singleton
{
受保护且静态的
$__instance = false;

公共且静态的函数
getInstance()
{
if (
self::$__instance == false)
{
// 这实际上不是我们想要的,$class 将始终为 'Singleton' :(
$class = get_class();
self::$__instance = new $class();
}
return
self::$__instance;
}
}

Foo 扩展 Singleton
{
// ...
}

$single_foo = Foo::getInstance();
?>
这段代码将导致一个致命错误,提示:无法实例化抽象类 Singleton in ... 在第 11 行

我发现避免这种情况的最佳方法需要对扩展的(Foo)类进行简单的更改。
<?php
抽象类 Singleton
{
受保护且静态的
$__instance = false;

受保护且静态的函数
getInstance($class)
{
if (
self::$__instance == false)
{
if (
class_exists($class))
{
self::$__instance = new $class();
}
else
{
throw new
Exception('Cannot instantiate undefined class [' . $class . ']', 1);
}
}
return
self::$__instance;
}
}

Foo 扩展 Singleton
{
// 你必须在每个扩展的类中重载 getInstance 方法:
公共且静态的函数 getInstance()
{
return
parent::getInstance(get_class());
}
}

$single_foo = Foo::getInstance();
?>


当然,这没什么可怕的,你可能无论如何都需要在扩展类中更改某些内容(至少是构造函数的访问权限),但仍然……它不像它可能的那样好;)
Lanselot
15年前
如果您在继承的类中省略了参数,请注意。
它将返回调用该方法的类名。

<?php
class A {
function
foo() {
return
get_class();
}
}
class
B extends A {
function
bar() {
return
get_class();
}
}
$instance = new B();
echo
$instance->bar(); //输出 'B';
echo $instance->foo(); //输出 'A';
?>
XyQrTw
8年前
要获取没有命名空间的类名,您可以轻松使用此技巧
<?php
namespace My\Long\Namespace;

class
MyClass {

static function
getClassName() {
return
basename(__CLASS__);
// 或者使用 get_class();
return basename(get_class());
}

}

echo
\My\Long\Namespace\MyClass::getClassName(); // 显示:MyClass
?>
var23rav at gmail dot com
10 年前
类 A
{
函数 __construct(){
//parent::__construct();
echo $this->m = '来自构造函数 A: '.get_class();
echo $this->m = '来自构造函数 A:- 参数 = $this: '.get_class($this);

echo $this->m = '来自构造函数 A-父类: '.get_parent_class();
echo $this->m = '来自构造函数 A-父类:- 参数 = $this: '.get_parent_class($this);
}
}
类 B 扩展 A
{
函数 __construct(){
parent::__construct();
echo $this->m = '来自构造函数 B: '.get_class();
echo $this->m = '来自构造函数 B:- 参数 = $this: '.get_class($this);

echo $this->m = '来自构造函数 B-父类: '.get_parent_class();
echo $this->m = '来自构造函数 B-父类:- 参数 = $this: '.get_parent_class($this);
}
}
$b = new B();
//----------------输出--------------------

来自构造函数 A: A
来自构造函数 A:- 参数 = $this: B
来自构造函数 A-父类
来自构造函数 A-父类:- 参数 = $this: A
来自构造函数 B: B
来自构造函数 B:- 参数 = $this: B
来自构造函数 B-父类: A
来自构造函数 B-父类:- 参数 = $this: A

使用 get_class() 获取类名,它将帮助您获取类名,如果您使用另一个类扩展该类并希望获取对象实例所属的类名,请使用 get_class($object)

当您创建一个类 {$b B 的对象} 的对象时,它有一个父类 {类 A}。
在父类 {A} 中使用这些代码
--------------------------------------------
获取类名 B {对象实例}: get_class($this)
获取类名 A {父类}: get_class() 或 get_parent_class($this)
kiril (AT) aternus networks
7 年前
如果您想要没有命名空间的类名,或者您因为 basename() 对 FQCN(完全限定类名)返回一个点 (.) 而来到这里,以下为解决方案

<?php
// FQCN: App\Http\Controllers\CustomerReportController

substr(self::class, (int)strrpos(self::class, '\\') + 1)

// 返回:CustomerReportController
?>
danbettles at yahoo dot co dot uk
16 年前
可以使用新的 get_called_class 函数在 PHP 5.3 中编写一个完全自包含的单例基类。当在静态方法中调用时,此函数将返回调用该函数的类的名称。

<?php

abstract class Singleton {

protected function
__construct() {
}

final public static function
getInstance() {
static
$aoInstance = array();

$calledClassName = get_called_class();

if (! isset (
$aoInstance[$calledClassName])) {
$aoInstance[$calledClassName] = new $calledClassName();
}

return
$aoInstance[$calledClassName];
}

final private function
__clone() {
}
}

class
DatabaseConnection extends Singleton {

protected
$connection;

protected function
__construct() {
// @todo 连接数据库
}

public function
__destruct() {
// @todo 断开数据库连接
}
}

$oDbConn = new DatabaseConnection(); // 严重错误

$oDbConn = DatabaseConnection::getInstance(); // 返回单个实例
?>

2008 年 10 月的完整文章: http://danbettles.blogspot.com
MagicalTux at FF.ST
20年前
请注意,常量 __CLASS__ 与 get_class($this) 不同
<?
类 test {
函数 whoami() {
echo "Hello, I'm whoami 1 !\r\n";
echo "Value of __CLASS__ : ".__CLASS__."\r\n";
echo "Value of get_class() : ".get_class($this)."\r\n\r\n";
}
}
类 test2 扩展 test {
函数 whoami2() {
echo "Hello, I'm whoami 2 !\r\n";
echo "Value of __CLASS__ : ".__CLASS__."\r\n";
echo "Value of get_class() : ".get_class($this)."\r\n\r\n";
parent::whoami(); // 调用父级 whoami() 函数
}
}
$test=new test;
$test->whoami();
$test2=new test2;
$test2->whoami();
$test2->whoami2();
?>

输出为
Hello, I'm whoami 1 !
Value of __CLASS__ : test
Value of get_class() : test

Hello, I'm whoami 1 !
Value of __CLASS__ : test
Value of get_class() : test2

Hello, I'm whoami 2 !
Value of __CLASS__ : test2
Value of get_class() : test2

Hello, I'm whoami 1 !
Value of __CLASS__ : test
Value of get_class() : test2

事实上,__CLASS__ 返回函数所在的类的名称,而 get_class($this) 返回创建的类的名称。
dave dot zap at gmail dot com
16 年前
小心使用回溯方法查找静态方法的调用类,您应该向后遍历数组并找到与您的 getInstance() 函数匹配的项。在回溯中,您想要的类名并不总是数组中的最后一项。

我不会在这里发布整个单例类,但对 Frederik Krautwald 的方法(如下所示)进行了一些修改

<?php
$bt
= debug_backtrace();
// 此方法由 Frederik 计算 count($bt)-1) 将在从包含文件中调用 getInstance 时失败。
//$class = $bt[count($bt) - 1]['class'];

for( $i=count($bt)-1 ; $i > 0 ; $i--)
{
if(
$bt[$i]['function'] == 'getInstance')
{
$class = $bt[$i]['class'];
break;
}
}
?>
To Top