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

전체 네임 스페이스 클래스를 지정하지 않고 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%  |
+-----------------+--------+