php 如何获得对象的非限定(短)类名?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19901850/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I get an object's unqualified (short) class name?
提问by Greg.Forbes
How do I check the class of an object within the PHP name spaced environment without specifying the full namespaced class.
如何在不指定完整命名空间类的情况下检查 PHP 命名空间环境中对象的类。
For example suppose I had an object library/Entity/Contract/Name.
例如,假设我有一个对象库/实体/合同/名称。
The following code does not work as get_class returns the full namespaced class.
以下代码不起作用,因为 get_class 返回完整的命名空间类。
If(get_class($object) == 'Name') {
... do this ...
}
The namespace magic keyword returns the current namespace, which is no use if the tested object has another namespace.
namespace magic 关键字返回当前的命名空间,如果测试对象有另一个命名空间,则没有用。
I could simply specify the full classname with namespaces, but this seems to lock in the structure of the code. Also not of much use if I wanted to change the namespace dynamically.
我可以简单地使用命名空间指定完整的类名,但这似乎锁定了代码的结构。如果我想动态更改命名空间,也没有多大用处。
Can anyone think of an efficient way to do this. I guess one option is regex.
任何人都可以想到一种有效的方法来做到这一点。我想一种选择是正则表达式。
回答by lonesomeday
You can do this with reflection. Specifically, you can use the ReflectionClass::getShortName
method, which gets the name of the class without its namespace.
你可以通过反射来做到这一点。具体来说,您可以使用该ReflectionClass::getShortName
方法,该方法获取不带命名空间的类的名称。
First, you need to build a ReflectionClass
instance, and then call the getShortName
method of that instance:
首先,您需要构建一个ReflectionClass
实例,然后调用该getShortName
实例的方法:
$reflect = new ReflectionClass($object);
if ($reflect->getShortName() === 'Name') {
// do this
}
However, I can't imagine many circumstances where this would be desirable. If you want to require that the object is a member of a certain class, the way to test it is with instanceof
. If you want a more flexible way to signal certain constraints, the way to do that is to write an interface and require that the code implement that interface. Again, the correct way to do this is with instanceof
. (You can do it with ReflectionClass
, but it would have much worse performance.)
但是,我无法想象在很多情况下这是可取的。如果你想要求对象是某个类的成员,测试它的方法是使用instanceof
. 如果您想要一种更灵活的方式来表示某些约束,那么这样做的方法是编写一个接口并要求代码实现该接口。同样,正确的方法是使用instanceof
. (你可以用 来做ReflectionClass
,但它的性能会差很多。)
回答by Hirnhamster
(new \ReflectionClass($obj))->getShortName();
is the best solution with regards to performance.
(new \ReflectionClass($obj))->getShortName();
是性能方面的最佳解决方案。
I was curious which of the provided solutions is the fastest, so I've put together a little test.
我很好奇所提供的解决方案中哪一个是最快的,所以我做了一个小测试。
Results
结果
Reflection: 1.967512512207 s ClassA
Basename: 2.6840535163879 s ClassA
Explode: 2.6507515668869 s ClassA
Code
代码
namespace foo\bar\baz;
class ClassA{
public function getClassExplode(){
return explode('\', static::class)[0];
}
public function getClassReflection(){
return (new \ReflectionClass($this))->getShortName();
}
public function getClassBasename(){
return basename(str_replace('\', '/', static::class));
}
}
$a = new ClassA();
$num = 100000;
$rounds = 10;
$res = array(
"Reflection" => array(),
"Basename" => array(),
"Explode" => array(),
);
for($r = 0; $r < $rounds; $r++){
$start = microtime(true);
for($i = 0; $i < $num; $i++){
$a->getClassReflection();
}
$end = microtime(true);
$res["Reflection"][] = ($end-$start);
$start = microtime(true);
for($i = 0; $i < $num; $i++){
$a->getClassBasename();
}
$end = microtime(true);
$res["Basename"][] = ($end-$start);
$start = microtime(true);
for($i = 0; $i < $num; $i++){
$a->getClassExplode();
}
$end = microtime(true);
$res["Explode"][] = ($end-$start);
}
echo "Reflection: ".array_sum($res["Reflection"])/count($res["Reflection"])." s ".$a->getClassReflection()."\n";
echo "Basename: ".array_sum($res["Basename"])/count($res["Basename"])." s ".$a->getClassBasename()."\n";
echo "Explode: ".array_sum($res["Explode"])/count($res["Explode"])." s ".$a->getClassExplode()."\n";
The results actually surprised me. I thought the explode solution would be the fastest way to go...
结果实际上让我感到惊讶。我认为爆炸解决方案将是最快的方法......
回答by MaBi
I added substr to the test of https://stackoverflow.com/a/25472778/2386943and that's the fastet way I could test (CentOS PHP 5.3.3, Ubuntu PHP 5.5.9) both with an i5.
我在https://stackoverflow.com/a/25472778/2386943的测试中添加了 substr ,这是我可以用 i5 测试(CentOS PHP 5.3.3,Ubuntu PHP 5.5.9)的最快方法。
$classNameWithNamespace=get_class($this);
return substr($classNameWithNamespace, strrpos($classNameWithNamespace, '\')+1);
Results
结果
Reflection: 0.068084406852722 s ClassA
Basename: 0.12301609516144 s ClassA
Explode: 0.14073524475098 s ClassA
Substring: 0.059865570068359 s ClassA
Code
代码
namespace foo\bar\baz;
class ClassA{
public function getClassExplode(){
$c = array_pop(explode('\', get_class($this)));
return $c;
}
public function getClassReflection(){
$c = (new \ReflectionClass($this))->getShortName();
return $c;
}
public function getClassBasename(){
$c = basename(str_replace('\', '/', get_class($this)));
return $c;
}
public function getClassSubstring(){
$classNameWithNamespace = get_class($this);
return substr($classNameWithNamespace, strrpos($classNameWithNamespace, '\')+1);
}
}
$a = new ClassA();
$num = 100000;
$rounds = 10;
$res = array(
"Reflection" => array(),
"Basename" => array(),
"Explode" => array(),
"Substring" => array()
);
for($r = 0; $r < $rounds; $r++){
$start = microtime(true);
for($i = 0; $i < $num; $i++){
$a->getClassReflection();
}
$end = microtime(true);
$res["Reflection"][] = ($end-$start);
$start = microtime(true);
for($i = 0; $i < $num; $i++){
$a->getClassBasename();
}
$end = microtime(true);
$res["Basename"][] = ($end-$start);
$start = microtime(true);
for($i = 0; $i < $num; $i++){
$a->getClassExplode();
}
$end = microtime(true);
$res["Explode"][] = ($end-$start);
$start = microtime(true);
for($i = 0; $i < $num; $i++){
$a->getClassSubstring();
}
$end = microtime(true);
$res["Substring"][] = ($end-$start);
}
echo "Reflection: ".array_sum($res["Reflection"])/count($res["Reflection"])." s ".$a->getClassReflection()."\n";
echo "Basename: ".array_sum($res["Basename"])/count($res["Basename"])." s ".$a->getClassBasename()."\n";
echo "Explode: ".array_sum($res["Explode"])/count($res["Explode"])." s ".$a->getClassExplode()."\n";
echo "Substring: ".array_sum($res["Substring"])/count($res["Substring"])." s ".$a->getClassSubstring()."\n";
==UPDATE==
==更新==
As mentioned in the comments by @MrBandersnatch there is even a faster way to do this:
正如@MrBandersnatch 在评论中提到的,还有一种更快的方法可以做到这一点:
return substr(strrchr(get_class($this), '\'), 1);
Here are the updated test results with "SubstringStrChr" (saves up to about 0.001 s):
以下是使用“SubstringStrChr”更新的测试结果(最多可节省约 0.001 秒):
Reflection: 0.073065280914307 s ClassA
Basename: 0.12585079669952 s ClassA
Explode: 0.14593172073364 s ClassA
Substring: 0.060415267944336 s ClassA
SubstringStrChr: 0.059880912303925 s ClassA
回答by spetsnaz
Here is a more easier way of doing this if you are using Laravel PHP framework :
如果您使用的是 Laravel PHP 框架,这里有一种更简单的方法:
<?php
// usage anywhere
// returns HelloWorld
$name = class_basename('Path\To\YourClass\HelloWorld');
// usage inside a class
// returns HelloWorld
$name = class_basename(__CLASS__);
回答by arzzzen
I use this:
我用这个:
basename(str_replace('\', '/', get_class($object)));
回答by flori
To get the short name as an one-liner (since PHP 5.4):
将短名称作为单行(自PHP 5.4 起):
echo (new ReflectionClass($obj))->getShortName();
It is a clean approach and reasonable fast.
这是一个干净的方法和合理的快速。
回答by Xorifelse
I found myself in a unique situation where instanceof
could not be used (specifically namespaced traits) and I neededthe short name in the most efficient way possible so I've done a little benchmark of my own. It includes all the different methods & variations from the answers in this question.
我发现自己处于一种instanceof
无法使用的独特情况(特别是命名空间特征),我需要以最有效的方式使用短名称,因此我自己做了一些基准测试。它包括该问题答案的所有不同方法和变体。
$bench = new \xori\Benchmark(1000, 1000); # https://github.com/Xorifelse/php-benchmark-closure
$shell = new \my\fancy\namespace\classname(); # Just an empty class named `classname` defined in the `\my\fancy\namespace\` namespace
$bench->register('strrpos', (function(){
return substr(static::class, strrpos(static::class, '\') + 1);
})->bindTo($shell));
$bench->register('safe strrpos', (function(){
return substr(static::class, ($p = strrpos(static::class, '\')) !== false ? $p + 1 : 0);
})->bindTo($shell));
$bench->register('strrchr', (function(){
return substr(strrchr(static::class, '\'), 1);
})->bindTo($shell));
$bench->register('reflection', (function(){
return (new \ReflectionClass($this))->getShortName();
})->bindTo($shell));
$bench->register('reflection 2', (function($obj){
return $obj->getShortName();
})->bindTo($shell), new \ReflectionClass($shell));
$bench->register('basename', (function(){
return basename(str_replace('\', '/', static::class));
})->bindTo($shell));
$bench->register('explode', (function(){
$e = explode("\", static::class);
return end($e);
})->bindTo($shell));
$bench->register('slice', (function(){
return join('',array_slice(explode('\', static::class), -1));
})->bindTo($shell));
print_r($bench->start());
A list of the of the entire result is herebut here are the highlights:
整个结果的列表在这里,但这里是亮点:
- Ifyou're going to use reflection anyways, using
$obj->getShortName()
is the fastest method however; using reflection onlyto get the short name it is almost the slowest method. 'strrpos'
can return a wrong value if the object is not in a namespace so while'safe strrpos'
is a tiny bit slower I would say this is the winner.- To make
'basename'
compatible between Linux and Windows you need to usestr_replace()
which makes this method the slowest of them all.
- 如果你要使用反射反正,使用
$obj->getShortName()
是最快的方法。然而,仅使用反射来获取短名称几乎是最慢的方法。 'strrpos'
如果对象不在命名空间中,则可能会返回错误的值,因此虽然'safe strrpos'
稍微慢了一点,但我会说这是赢家。- 要
'basename'
在 Linux 和 Windows 之间兼容,您需要使用str_replace()
这使得此方法成为所有方法中最慢的。
A simplified table of results, speed is measured compared to the slowest method:
一个简化的结果表,与最慢的方法相比测量速度:
+-----------------+--------+
| registered name | speed |
+-----------------+--------+
| reflection 2 | 70.75% |
| strrpos | 60.38% |
| safe strrpos | 57.69% |
| strrchr | 54.88% |
| explode | 46.60% |
| slice | 37.02% |
| reflection | 16.75% |
| basename | 0.00% |
+-----------------+--------+
回答by m13r
You can use explode
for separating the namespace and end
to get the class name:
您可以explode
用于分隔命名空间并end
获取类名:
$ex = explode("\", get_class($object));
$className = end($ex);
回答by mariovials
Yii way
依依方式
\yii\helpers\StringHelper::basename(get_class($model));
Yii uses this method in its Gii code generator
Yii 在其 Gii 代码生成器中使用此方法
Method documentation
方法文档
This method is similar to the php function basename() except that it will treat both \ and / as directory separators, independent of the operating system. This method was mainly created to work on php namespaces. When working with real file paths, php's basename() should work fine for you. Note: this method is not aware of the actual filesystem, or path components such as "..".
此方法类似于 php 函数 basename() ,不同之处在于它将 \ 和 / 视为目录分隔符,与操作系统无关。这个方法主要是为了在 php 命名空间上工作而创建的。处理真实文件路径时,php 的 basename() 应该适合您。注意:此方法不知道实际的文件系统或路径组件,例如“..”。
More information:
更多信息:
https://github.com/yiisoft/yii2/blob/master/framework/helpers/BaseStringHelper.phphttp://www.yiiframework.com/doc-2.0/yii-helpers-basestringhelper.html#basename()-detail
https://github.com/yiisoft/yii2/blob/master/framework/helpers/BaseStringHelper.php http://www.yiiframework.com/doc-2.0/yii-helpers-basestringhelper.html#basename()-detail
回答by OzzyCzech
Here is simple solution for PHP 5.4+
这是 PHP 5.4+ 的简单解决方案
namespace {
trait Names {
public static function getNamespace() {
return implode('\', array_slice(explode('\', get_called_class()), 0, -1));
}
public static function getBaseClassName() {
return basename(str_replace('\', '/', get_called_class()));
}
}
}
What will be return?
会返回什么?
namespace x\y\z {
class SomeClass {
use \Names;
}
echo \x\y\z\SomeClass::getNamespace() . PHP_EOL; // x\y\z
echo \x\y\z\SomeClass::getBaseClassName() . PHP_EOL; // SomeClass
}
Extended class name and namespace works well to:
扩展类名和命名空间适用于:
namespace d\e\f {
class DifferentClass extends \x\y\z\SomeClass {
}
echo \d\e\f\DifferentClass::getNamespace() . PHP_EOL; // d\e\f
echo \d\e\f\DifferentClass::getBaseClassName() . PHP_EOL; // DifferentClass
}
What about class in global namespace?
全局命名空间中的类呢?
namespace {
class ClassWithoutNamespace {
use \Names;
}
echo ClassWithoutNamespace::getNamespace() . PHP_EOL; // empty string
echo ClassWithoutNamespace::getBaseClassName() . PHP_EOL; // ClassWithoutNamespace
}