Как получить неквалифицированное (короткое) имя класса объекта?
Как проверить класс объекта в среде PHP name spaced без указания полного класса пространства имен.
например, предположим, что у меня есть библиотека объектов/сущность/контракт/имя.
следующий код не работает, так как get_class возвращает полный класс пространства имен.
If(get_class($object) == 'Name') {
... do this ...
}
ключевое слово namespace magic возвращает текущее пространство имен, которое бесполезно, если тестируемый объект имеет другое пространство имен.
Я мог бы просто укажите полное имя класса с пространствами имен, но это, похоже, блокируется в структуре кода. Также не очень полезно, если я хотел изменить пространство имен динамически.
кто-нибудь может придумать эффективный способ сделать это. Я думаю, один из вариантов-регулярное выражение.
19 ответов:
вы можете сделать это с отражением. В частности, вы можете использовать
ReflectionClass::getShortNameметод, который возвращает имя класса без пространства имен.во-первых, вам нужно построить
ReflectionClassэкземпляр, а затем вызватьgetShortNameметод этого экземпляра:$reflect = new ReflectionClass($object); if ($reflect->getShortName() === 'Name') { // do this }однако я не могу представить себе много обстоятельств, где это было бы желательно. Если вы хотите потребовать, чтобы объект был членом определенного класса, способ его проверки-с помощью
instanceof. Если вы требуется более гибкий способ сигнализации определенных ограничений, способ сделать это-написать интерфейс и потребовать, чтобы код реализовал этот интерфейс. Опять же, правильный способ сделать это сinstanceof. (Вы можете сделать это с помощьюReflectionClass, но это будет иметь гораздо худшую производительность.)
(new \ReflectionClass($obj))->getShortName();является лучшим решением в отношении производительности.мне было любопытно, какое из предлагаемых решений является самым быстрым, поэтому я собрал небольшой тест.
результаты
Reflection: 1.967512512207 s ClassA Basename: 2.6840535163879 s ClassA Explode: 2.6507515668869 s ClassAкод
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; } } $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";результаты на самом деле удивили меня. Я думал, что взрывное решение будет самым быстрым способом...
я добавил substr к тесту https://stackoverflow.com/a/25472778/2386943 и это способ fastet, который я мог бы протестировать (CentOS PHP 5.3.3, Ubuntu PHP 5.5.9) как с i5.
$classNameWithNamespace=get_class($this); return substr($classNameWithNamespace, strrpos($classNameWithNamespace, '\')+1);результаты
Reflection: 0.068084406852722 s ClassA Basename: 0.12301609516144 s ClassA Explode: 0.14073524475098 s ClassA Substring: 0.059865570068359 s ClassAкод
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";==обновление==
как уже упоминалось в комментариях @MrBandersnatch есть еще более быстрый способ сделать это:
return substr(strrchr(get_class($this), '\'), 1);здесь обновленные результаты теста с " SubstringStrChr "(сохраняет до около 0.001 s):
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
чтобы получить короткое имя в виде однострочного (так как PHP 5.4):
echo (new ReflectionClass($obj))->getShortName();Это чистый подход и разумно быстро.
вот более простой способ сделать это, если вы используете Laravel PHP framework:
<?php // usage anywhere // returns HelloWorld $name = class_basename('Path\To\YourClass\HelloWorld'); // usage inside a class // returns HelloWorld $name = class_basename(__CLASS__);
я оказался в уникальной ситуации, где
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());список всего результата является здесь но вот основные моменты:
- если ты собираюсь использовать отражение в любом случае, используя
$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% | +-----------------+--------+
вот простое решение для 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())); } } }что будет возвращено?
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 }расширенное имя класса и пространство имен работает хорошо:
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 }насчет класса в глобальном пространстве имен?
namespace { class ClassWithoutNamespace { use \Names; } echo ClassWithoutNamespace::getNamespace() . PHP_EOL; // empty string echo ClassWithoutNamespace::getBaseClassName() . PHP_EOL; // ClassWithoutNamespace }
Yii way
\yii\helpers\StringHelper::basename(get_class($model));Yii использует этот метод в своем генераторе кода Gii
документация по методу
этот метод аналогичен функции php basename () за исключением того, что он будет обрабатывать как\, так и / как разделители каталогов, независимо от операционной системы. Этот метод был в основном создан для работы с пространствами имен php. При работе с реальными путями файлов, базовое имя php () должно работать нормально для вас. Примечание: этот метод не знает о фактическом файловая система или компоненты пути, такие как "..".
дополнительная информация:
https://github.com/yiisoft/yii2/blob/master/framework/helpers/BaseStringHelper.php http://www.yiiframework.com/doc-2.0/yii-helpers-basestringhelper.html#basename()-detail
Если вам нужно знать имя класса, который был вызван из класса, и не хотите пространство имен, вы можете использовать этот
$calledClass = get_called_class(); $name = strpos($calledClass, '\') === false ? $calledClass : substr($calledClass, strrpos($calledClass, '\') + 1);Это здорово, когда у вас есть метод внутри класса, который расширяется на другие классы. Кроме того, это также работает, если пространства имен не используются вообще.
пример:
<?php namespace One\Two { class foo { public function foo() { $calledClass = get_called_class(); $name = strpos($calledClass, '\') === false ? $calledClass : substr($calledClass, strrpos($calledClass, '\') + 1); var_dump($name); } } } namespace Three { class bar extends \One\Two\foo { public function bar() { $this->foo(); } } } namespace { (new One\Two\foo)->foo(); (new Three\bar)->bar(); } // test.php:11:string 'foo' (length=3) // test.php:11:string 'bar' (length=3)
можно использовать
explodeдля разделения пространства имен иendчтобы получить название класса:$ex = explode("\", get_class($object)); $className = end($ex);
основываясь на ответе @MaBi, я сделал это:
trait ClassShortNameTrait { public static function getClassShortName() { if ($pos = strrchr(static::class, '\')) { return substr($pos, 1); } else { return static::class; } } }, который вы можете использовать так:
namespace Foo\Bar\Baz; class A { use ClassShortNameTrait; }
A::classвозвращаетFoo\Bar\Baz\A, аA::getClassShortName()возвращаетA.работает на PHP >= 5.5.
нашли на страница документации get_class, где он был размещен Я в nwhiting dot com.
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]; }но идея пространств имен состоит в том, чтобы структурировать ваш код. Это также означает, что вы можете иметь классы с одинаковыми именами в разных пространствах имен. Таким образом, теоретически, объект, который вы передаете, может иметь имя (разделенное) имя класса, все еще будучи совершенно другим объектом, чем вы ожидаете.
кроме того, вы можете проверить для конкретного базовый класс в этом случае
get_classне делает трюк вообще. Возможно, вы захотите проверить оператораinstanceof.
вы можете получить неожиданный результат, когда класс не имеет пространства имен. То есть
get_classвозвращаетFoo, потом$baseClassбудетoo.$baseClass = substr(strrchr(get_class($this), '\'), 1);это можно легко исправить с помощью префикса
get_classС обратной косой чертой:$baseClass = substr(strrchr('\'.get_class($this), '\'), 1);теперь также классы без пространства имен будут возвращать правильное значение.
цитирование php.net:
В Windows в качестве символа разделителя каталогов используются косая черта (/) и обратная косая черта (). В других средах, это косая черта (/).
на основе этой информации и расширения от ответа arzzzen это должно работать как на Windows, так и на Nix * системах:
<?php if (basename(str_replace('\', '/', get_class($object))) == 'Name') { // ... do this ... }Примечание: Я сделал эталон
ReflectionClassпротивbasename+str_replace+get_classи использование отражения примерно на 20% быстрее, чем использование подхода basename, но МММ.
самое быстрое и простое решение imho, которое работает в любой среде:
<?php namespace \My\Awesome\Namespace; class Foo { private $shortName; public function fastShortName() { if ($this->shortName === null) { $this->shortName = explode("\", static::class); $this->shortName = end($this->shortName); } return $this->shortName; } public function shortName() { return basename(strtr(static::class, "\", "/")); } } echo (new Foo())->shortName(); // "Foo" ?>
Если вы просто удаляете пространства имен и хотите что-нибудь после последнего \ в имени класса с пространством имен (или просто имя, если нет'\'), вы можете сделать что-то вроде этого:
$base_class = preg_replace('/^([\w\\]+\\)?([^\\]+)$/', '', get_class($myobject));в основном это регулярное выражение, чтобы получить любую комбинацию символов или обратных косых черт и до последней обратной косой черты, а затем возвращать только символы без обратной косой черты и до конца строки. Добавление ? после того, как первая группировка означает, что если совпадение с шаблоном не существует, оно просто возвращает полная строка.
старое доброе выражение, кажется, быстрее, чем большинство предыдущих, показанных методов:
// both of the below calls will output: ShortClassName echo preg_replace('/.*\\/', '', 'ShortClassName'); echo preg_replace('/.*\\/', '', 'SomeNamespace\SomePath\ShortClassName');Так что это работает, даже если вы предоставляете короткое имя класса или полное (каноническое) имя класса.
что делает регулярное выражение, так это то, что оно потребляет все предыдущие символы до тех пор, пока не будет найден последний разделитель (который также потребляется). Таким образом, оставшаяся строка будет коротким именем класса.
Если вы хотите использовать другой разделитель (напр. / ) тогда просто используйте это вместо этого разделитель. Не забудьте избежать обратной косой черты (т. е. \ ) а также шаблон char (т. е. /) во входном шаблоне.
Comments