Gre*_*bes 138 php namespaces class
如何在PHP名称间隔环境中检查对象的类,而不指定完整的命名空间类.
例如,假设我有一个对象库/实体/合同/名称.
以下代码不起作用,因为get_class返回完整的命名空间类.
If(get_class($object) == 'Name') {
... do this ...
}
Run Code Online (Sandbox Code Playgroud)
namespace magic关键字返回当前命名空间,如果测试对象具有另一个命名空间,则该命名空间无效.
我可以简单地用命名空间指定完整的类名,但这似乎锁定了代码的结构.如果我想动态更改名称空间,也没什么用处.
任何人都可以想到一个有效的方法来做到这一点.我猜一个选项是正则表达式.
lon*_*day 167
你可以用反射做到这一点.具体来说,您可以使用该ReflectionClass::getShortName方法,该方法获取没有名称空间的类的名称.
首先,您需要构建一个ReflectionClass实例,然后调用该getShortName实例的方法:
$reflect = new ReflectionClass($object);
if ($reflect->getShortName() === 'Name') {
// do this
}
Run Code Online (Sandbox Code Playgroud)
但是,我无法想象许多情况下这是可取的.如果您想要求对象是某个类的成员,那么测试它的方法就是instanceof.如果您希望以更灵活的方式发出某些约束信号,那么执行此操作的方法是编写接口并要求代码实现该接口.同样,正确的方法是使用instanceof.(你可以这样做ReflectionClass,但它会有更糟糕的表现.)
Hir*_*ter 119
(new \ReflectionClass($obj))->getShortName(); 是性能方面的最佳解决方案.
我很好奇哪个提供的解决方案是最快的,所以我已经进行了一些测试.
结果
Reflection: 1.967512512207 s ClassA
Basename: 2.6840535163879 s ClassA
Explode: 2.6507515668869 s ClassA
Run Code Online (Sandbox Code Playgroud)
码
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";
Run Code Online (Sandbox Code Playgroud)
结果实际上让我感到惊讶.我认为爆炸解决方案将是最快的方式......
MaB*_*aBi 77
我在/sf/answers/1783094491/的测试中添加了substr ,这是我用i5测试(CentOS PHP 5.3.3,Ubuntu PHP 5.5.9)的紧固方式.
$classNameWithNamespace=get_class($this);
return substr($classNameWithNamespace, strrpos($classNameWithNamespace, '\\')+1);
Run Code Online (Sandbox Code Playgroud)
结果
Reflection: 0.068084406852722 s ClassA
Basename: 0.12301609516144 s ClassA
Explode: 0.14073524475098 s ClassA
Substring: 0.059865570068359 s ClassA
Run Code Online (Sandbox Code Playgroud)
码
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";
Run Code Online (Sandbox Code Playgroud)
== ==更新
正如@MrBandersnatch的评论中所提到的,甚至有更快的方法:
return substr(strrchr(get_class($this), '\\'), 1);
Run Code Online (Sandbox Code Playgroud)
以下是使用"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
Run Code Online (Sandbox Code Playgroud)
spe*_*naz 22
如果您使用Laravel PHP框架,这是一种更简单的方法:
<?php
// usage anywhere
// returns HelloWorld
$name = class_basename('Path\To\YourClass\HelloWorld');
// usage inside a class
// returns HelloWorld
$name = class_basename(__CLASS__);
Run Code Online (Sandbox Code Playgroud)
小智 17
我用这个:
basename(str_replace('\\', '/', get_class($object)));
Run Code Online (Sandbox Code Playgroud)
flo*_*ori 15
要将短名称作为单行(从PHP 5.4开始):
echo (new ReflectionClass($obj))->getShortName();
Run Code Online (Sandbox Code Playgroud)
这是一个干净的方法,合理的快速.
Xor*_*lse 12
我发现自己处于一种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());
Run Code Online (Sandbox Code Playgroud)
整个结果的列表在这里,但这里是亮点:
$obj->getShortName()是最快的方法,但是,使用反射只获得短名称它几乎是最慢的方法.'strrpos'如果对象不在命名空间中,则可以返回错误的值,因此虽然'safe strrpos'速度稍慢,但我会说这是赢家.'basename'在Linux和Windows之间进行兼容,您需要使用str_replace()这种方法使这种方法最慢.一个简化的结果表,与最慢的方法相比,测量速度:
+-----------------+--------+
| 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% |
+-----------------+--------+
Run Code Online (Sandbox Code Playgroud)
您可以使用explode分隔命名空间并end获取类名:
$ex = explode("\\", get_class($object));
$className = end($ex);
Run Code Online (Sandbox Code Playgroud)
小智 7
Yii方式
\yii\helpers\StringHelper::basename(get_class($model));
Run Code Online (Sandbox Code Playgroud)
Yii在其Gii代码生成器中使用此方法
方法文档
此方法类似于php函数basename(),除了它将\和/视为目录分隔符,与操作系统无关.此方法主要用于处理php命名空间.使用真实文件路径时,php的basename()应该可以正常工作.注意:此方法不知道实际的文件系统或路径组件,如"..".
更多信息:
https://github.com/yiisoft/yii2/blob/master/framework/helpers/BaseStringHelper.php http://www.yiiframework.com/doc-2.0/yii-helpers-basestringhelper.html#basename()-detail
这是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()));
}
}
}
Run Code Online (Sandbox Code Playgroud)
会有什么回报?
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
}
Run Code Online (Sandbox Code Playgroud)
扩展类名和命名空间适用于:
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
}
Run Code Online (Sandbox Code Playgroud)
全局命名空间中的类怎么样?
namespace {
class ClassWithoutNamespace {
use \Names;
}
echo ClassWithoutNamespace::getNamespace() . PHP_EOL; // empty string
echo ClassWithoutNamespace::getBaseClassName() . PHP_EOL; // ClassWithoutNamespace
}
Run Code Online (Sandbox Code Playgroud)
我知道这是一篇旧文章,但这就是我使用的 - 比上面发布的所有内容更快,只需从您的类中调用此方法,比使用反射快得多
namespace Foo\Bar\Baz;
class Test {
public function getClass() {
return str_replace(__NAMESPACE__.'\\', '', static::class);
}
}
Run Code Online (Sandbox Code Playgroud)