development

객체의 정규화되지 않은 (짧은) 클래스 이름을 어떻게 얻습니까?

big-blog 2020. 6. 25. 07:31
반응형

객체의 정규화되지 않은 (짧은) 클래스 이름을 어떻게 얻습니까?


전체 네임 스페이스 클래스를 지정하지 않고 PHP 네임 스페이스 환경 내에서 오브젝트 클래스를 확인하는 방법

예를 들어 객체 라이브러리 / 엔티티 / 계약 / 이름이 있다고 가정합니다.

get_class가 전체 네임 스페이스 클래스를 리턴하므로 다음 코드는 작동하지 않습니다.

If(get_class($object) == 'Name') {
... do this ...
}

네임 스페이스 magic 키워드는 현재 네임 스페이스를 반환하며, 테스트 된 객체에 다른 네임 스페이스가 있으면 사용되지 않습니다.

네임 스페이스로 전체 클래스 이름을 간단히 지정할 수는 있지만 코드 구조에 잠겨있는 것 같습니다. 네임 스페이스를 동적으로 변경하려는 경우에도 많이 사용되지 않습니다.

누구나이 작업을 수행하는 효율적인 방법을 생각할 수 있습니까? 하나의 옵션이 정규식이라고 생각합니다.


리플렉션으로이 작업을 수행 할 수 있습니다. 특히 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(){
        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";

결과는 실제로 저를 놀라게했습니다. 폭발 솔루션이 가장 빠른 방법이라고 생각했습니다.


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);

결과

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 초 절약).

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

Laravel PHP 프레임 워크를 사용하는 경우 더 쉬운 방법은 다음과 같습니다.

<?php

// usage anywhere
// returns HelloWorld
$name = class_basename('Path\To\YourClass\HelloWorld');

// usage inside a class
// returns HelloWorld
$name = class_basename(__CLASS__);

나는 이것을 사용한다 :

basename(str_replace('\\', '/', get_class($object)));

짧은 이름을 단일 라이너로 얻으려면 ( PHP 5.4 부터 ) :

echo (new ReflectionClass($obj))->getShortName();

깔끔한 접근 방식과 합리적인 속도 입니다.


나는 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%  |
+-----------------+--------+

explode네임 스페이스를 분리 end하고 클래스 이름을 얻는 데 사용할 수 있습니다 .

$ex = explode("\\", get_class($object));
$className = end($ex);

다음은 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\helpers\StringHelper::basename(get_class($model));

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


클래스 내부에서 호출 된 클래스 이름을 알아야하고 네임 스페이스를 원하지 않는 경우이 이름을 사용할 수 있습니다.

$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)

@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];
}

그러나 네임 스페이스의 아이디어는 코드를 구성하는 것입니다. 또한 여러 네임 스페이스에서 같은 이름의 클래스를 가질 수 있습니다. 따라서 이론적으로 전달하는 객체의 이름은 클래스 이름을 가질 수 있지만 예상과는 완전히 다른 객체 일 수 있습니다.

Besides that, you might want to check for a specific base class, in which case get_class doesn't do the trick at all. You might want to check out the operator instanceof.


You may get an unexpected result when the class doesn't have a namespace. I.e. get_class returns Foo, then $baseClass would be oo.

$baseClass = substr(strrchr(get_class($this), '\\'), 1);

This can easily be fixed by prefixing get_class with a backslash:

$baseClass = substr(strrchr('\\'.get_class($this), '\\'), 1);

Now also classes without a namespace will return the right value.


Quoting php.net:

On Windows, both slash (/) and backslash () are used as directory separator character. In other environments, it is the forward slash (/).

Based on this info and expanding from arzzzen answer this should work on both Windows and Nix* systems:

<?php

if (basename(str_replace('\\', '/', get_class($object))) == 'Name') {
    // ... do this ...
}

Note: I did a benchmark of ReflectionClass against basename+str_replace+get_class and using reflection is roughly 20% faster than using the basename approach, but YMMV.


The fastest and imho easiest solution that works in any environment is:

<?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"

?>

$shortClassName = join('',array_slice(explode('\\', $longClassName), -1));

If you're just stripping name spaces and want anything after the last \ in a class name with namespace (or just the name if there's no '\') you can do something like this:

$base_class = preg_replace('/^([\w\\\\]+\\\\)?([^\\\\]+)$/', '$2', get_class($myobject));

Basically it's regex to get any combination of characters or backslashes up and until the last backslash then to return only the non-backslash characters up and until the end of the string. Adding the ? after the first grouping means if the pattern match doesn't exist, it just returns the full string.


A good old regex seems to be faster than the most of the previous shown methods:

// both of the below calls will output: ShortClassName

echo preg_replace('/.*\\\\/', '', 'ShortClassName');
echo preg_replace('/.*\\\\/', '', 'SomeNamespace\SomePath\ShortClassName');

So this works even when you provide a short class name or a fully qualified (canonical) class name.

What the regex does is that it consumes all previous chars until the last separator is found (which is also consumed). So the remaining string will be the short class name.

If you want to use a different separator (eg. / ) then just use that separator instead. Remember to escape the backslash (ie. \) and also the pattern char (ie. /) in the input pattern.


I know this is an old post but this is what i use - Faster than all posted above just call this method from your class, a lot quicker than using Reflection

namespace Foo\Bar\Baz;

class Test {
    public function getClass() {
        return str_replace(__NAMESPACE__.'\\', '', static::class);
    }
}

참고URL : https://stackoverflow.com/questions/19901850/how-do-i-get-an-objects-unqualified-short-class-name

반응형