it-swarm.com.de

Wie erhalte ich den unqualifizierten (kurzen) Klassennamen eines Objekts?

Wie überprüfe ich die Klasse eines Objekts in der Umgebung mit PHP Namen, ohne die vollständige Namespaced-Klasse anzugeben. 

Angenommen, ich hatte eine Objektbibliothek/Entität/Vertrag/Name. 

Der folgende Code funktioniert nicht, da get_class die vollständige Namensraumklasse zurückgibt. 

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

Das magische Schlüsselwort des Namespaces gibt den aktuellen Namespace zurück, was nicht verwendet wird, wenn das getestete Objekt einen anderen Namespace hat. 

Ich könnte einfach den vollständigen Klassennamen mit Namespaces angeben, aber dies scheint die Struktur des Codes zu blockieren. Auch nicht sehr nützlich, wenn ich den Namespace dynamisch ändern wollte. 

Kann sich jemand eine effiziente Möglichkeit vorstellen, dies zu tun. Ich denke, eine Option ist Regex. 

125
Greg.Forbes

Sie können dies mit Reflektion tun. Insbesondere können Sie die Methode ReflectionClass::getShortName verwenden, die den Namen der Klasse ohne ihren Namensraum abruft.

Zuerst müssen Sie eine ReflectionClass-Instanz erstellen und dann die getShortName-Methode dieser Instanz aufrufen:

$reflect = new ReflectionClass($object);
if ($reflect->getShortName() === 'Name') {
    // do this
}

Ich kann mir jedoch nicht viele Umstände vorstellen, unter denen dies wünschenswert wäre. Wenn Sie möchten, dass das Objekt Mitglied einer bestimmten Klasse ist, können Sie es mit instanceof testen. Wenn Sie eine flexiblere Methode zum Signalisieren bestimmter Einschränkungen wünschen, können Sie dazu eine Schnittstelle schreiben und verlangen, dass der Code diese Schnittstelle implementiert. Der richtige Weg dazu ist instanceof. (Sie können dies mit ReflectionClass tun, aber die Leistung wäre erheblich schlechter.)

155
lonesomeday

(new \ReflectionClass($obj))->getShortName(); ist die beste Lösung in Bezug auf die Leistung.

Ich war gespannt, welche der gelieferten Lösungen am schnellsten ist, deshalb habe ich einen kleinen Test zusammengestellt.

Ergebnisse

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

Die Ergebnisse haben mich tatsächlich überrascht. Ich dachte, die Explosionslösung wäre der schnellste Weg ...

111
Hirnhamster

Ich habe substr zum Test von https://stackoverflow.com/a/25472778/2386943 Hinzugefügt. Dies ist der schnellste Weg, den ich testen könnte (CentOS PHP 5.3.3, Ubuntu PHP 5.5.9) beide mit einem i5.

$classNameWithNamespace=get_class($this);
return substr($classNameWithNamespace, strrpos($classNameWithNamespace, '\\')+1);

Ergebnisse

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 ==

Wie in den Kommentaren von @MrBandersnatch erwähnt, gibt es einen noch schnelleren Weg, dies zu tun:

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

Hier sind die aktualisierten Testergebnisse mit "SubstringStrChr" (spart bis zu 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
73
MaBi

Dies ist einfacher, wenn Sie das Laravel PHP - Framework verwenden:

<?php

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

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

So erhalten Sie den Kurznamen als Einzeiler (seit PHP 5.4 ):

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

Es ist ein sauberer Ansatz und vernünftig schnell .

14
flori

Ich benutze das:

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

Ich befand mich in einer einzigartigen Situation, in der instanceof nicht verwendet werden konnte (speziell namengebundene Merkmale) und ich benötigt den Kurznamen so effizient wie möglich machte, also habe ich a kleiner Benchmark meiner eigenen. Es enthält alle verschiedenen Methoden und Variationen der Antworten in dieser Frage.

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

Eine Liste des gesamten Ergebnisses ist hier aber hier sind die Highlights:

  • Wenn Sie trotzdem Reflektion verwenden, ist die Verwendung von $obj->getShortName() die schnellste Methode jedoch ; Die Verwendung von Reflection nur , um den Kurznamen zu erhalten, ist fast die langsamste Methode.
  • 'strrpos' Kann einen falschen Wert zurückgeben, wenn sich das Objekt nicht in einem Namespace befindet. Wenn 'safe strrpos' Also etwas langsamer ist, würde ich sagen, dass dies der Gewinner ist.
  • Um 'basename' Zwischen Linux und Windows kompatibel zu machen, müssen Sie str_replace() verwenden, wodurch diese Methode die langsamste von allen ist.

In einer vereinfachten Ergebnistabelle wird die Geschwindigkeit im Vergleich zur langsamsten Methode gemessen:

+-----------------+--------+
| 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%  |
+-----------------+--------+
12
Xorifelse

Hier ist eine einfache Lösung für 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()));
        }
    }
}

Was wird wiederkommen?

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
}

Der erweiterte Klassenname und Namespace eignet sich gut für:

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
}

Was ist mit der Klasse im globalen Namensraum?

namespace {

    class ClassWithoutNamespace {
        use \Names;
    }

    echo ClassWithoutNamespace::getNamespace() . PHP_EOL; // empty string
    echo ClassWithoutNamespace::getBaseClassName() . PHP_EOL; // ClassWithoutNamespace
}
6
OzzyCzech

Yii Weg

\yii\helpers\StringHelper::basename(get_class($model));

Yii verwendet diese Methode in seinem Gii-Code-Generator

Methodendokumentation

Diese Methode ähnelt der PHP-Funktion basename (), mit der Ausnahme, dass sowohl\als auch/als Verzeichnistrennzeichen behandelt werden, unabhängig vom Betriebssystem. Diese Methode wurde hauptsächlich für PHP-Namespaces entwickelt. Wenn Sie mit echten Dateipfaden arbeiten, sollte der Basisname () von php für Sie funktionieren. Hinweis: Diese Methode kennt nicht das tatsächliche Dateisystem oder die Pfadkomponenten wie "..".

Mehr Informationen:

https://github.com/yiisoft/yii2/blob/master/framework/helpers/BaseStringHelper.phphttp://www.yiiframework.com/doc-2.0/yii-helpers- basestringhelper.html # basename () - detail

6
mariovials

Sie können explode zum Trennen des Namespaces verwenden und end, um den Klassennamen zu erhalten:

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

Wenn Sie den Klassennamen kennen müssen, der innerhalb einer Klasse aufgerufen wurde, und den Namespace nicht verwenden möchten, können Sie diesen verwenden 

$calledClass = get_called_class();
$name = strpos($calledClass, '\\') === false ?
    $calledClass : substr($calledClass, strrpos($calledClass, '\\') + 1);

Dies ist großartig, wenn Sie eine Methode innerhalb einer Klasse haben, die von anderen Klassen erweitert wird. Darüber hinaus funktioniert dies auch, wenn Namespaces überhaupt nicht verwendet werden.

Beispiel:

<?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)
3
Nino Škopac

Basierend auf @MaBis Antwort habe ich Folgendes gemacht:

trait ClassShortNameTrait
{
    public static function getClassShortName()
    {
        if ($pos = strrchr(static::class, '\\')) {
            return substr($pos, 1);
        } else {
            return static::class;
        }
    }
}

Welches können Sie so verwenden:

namespace Foo\Bar\Baz;

class A
{
    use ClassShortNameTrait;
}

A::class gibt Foo\Bar\Baz\A zurück, A::getClassShortName() gibt A zurück.

Funktioniert für PHP> = 5.5.

2
Tristan Jahier

Gefunden auf der Dokumentationsseite von get_class , wo es von me unter nwhiting dot com gepostet wurde.

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

Bei Namespaces geht es jedoch darum, Ihren Code zu strukturieren. Das bedeutet auch, dass Sie in mehreren Namespaces Klassen mit demselben Namen haben können. Theoretisch könnte das übergebene Objekt den Namen der Klassennamen (Stripped) haben, während es sich dabei immer noch um ein völlig anderes Objekt handelt als erwartet.

Außerdem möchten Sie vielleicht nach einer bestimmten Basisklasse suchen. In diesem Fall führt get_class den Trick überhaupt nicht aus. Vielleicht möchten Sie den Operator instanceof überprüfen.

1
GolezTrol

Sie erhalten möglicherweise ein unerwartetes Ergebnis, wenn die Klasse keinen Namespace hat. Das heißt get_class gibt Foo zurück, dann wäre $baseClassoo.

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

Dies kann leicht behoben werden, indem get_class ein Backslash vorangestellt wird:

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

Jetzt geben auch Klassen ohne Namensraum den richtigen Wert zurück.

1
Tim

Wenn Sie nur Namensräume entfernen und nach dem letzten\in einem Klassennamen mit Namespace (oder nur dem Namen, wenn es kein '\' gibt) etwas suchen, können Sie Folgendes tun:

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

Im Grunde ist es Regex, eine beliebige Kombination von Zeichen oder Backslashes aufzurufen und bis zum letzten Backslash dann nur die Nicht-Backslash-Zeichen und bis zum Ende der Zeichenfolge zurückzugeben. Hinzufügen der? Wenn nach der ersten Gruppierung die Musterübereinstimmung nicht vorhanden ist, wird nur die vollständige Zeichenfolge zurückgegeben.

0
Scott

Php.net zitiert:

Unter Windows werden sowohl der Schrägstrich (/) als auch der umgekehrte Schrägstrich () als Verzeichnistrennzeichen verwendet. In anderen Umgebungen ist es der Schrägstrich (/).

Basierend auf diesen Informationen und der Erweiterung der Antwort von arzzzen sollte dies auf Windows- und Nix * -Systemen funktionieren:

<?php

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

Hinweis: Ich habe einen Benchmark von ReflectionClass gegen basename+str_replace+get_class durchgeführt und die Verwendung von Reflektion ist etwa 20% schneller als mit dem Basename-Ansatz, jedoch mit YMMV.

0
noisebleed

Die schnellste und einfachste Lösung, die in jeder Umgebung funktioniert, ist:

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

?>
0
Fleshgrinder

Ich weiß, dass dies ein alter Beitrag ist, aber das ist, was ich verwende - Schneller als alle oben genannten, rufen Sie einfach diese Methode aus Ihrer Klasse an, viel schneller als mit Reflection

namespace Foo\Bar\Baz;

class Test {
    public function getClass() {
        return str_replace(__NAMESPACE__.'\\', '', static::class);
    }
}
0
Seth
$shortClassName = join('',array_slice(explode('\\', $longClassName), -1));
0
malhal

Ein guter alter Regex scheint schneller zu sein als die meisten der zuvor gezeigten Methoden:

// both of the below calls will output: ShortClassName

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

Dies funktioniert auch, wenn Sie einen kurzen Klassennamen oder einen vollständig qualifizierten (kanonischen) Klassennamen angeben.

Was der Regex tut, ist, dass er alle vorherigen Zeichen verbraucht, bis das letzte Trennzeichen gefunden wurde (das ebenfalls verwendet wird). Die verbleibende Zeichenfolge ist also der kurze Klassenname. 

Wenn Sie ein anderes Trennzeichen verwenden möchten (z. B. /), verwenden Sie stattdessen dieses Trennzeichen. Denken Sie daran, den umgekehrten Schrägstrich (dh. \) Und auch das Musterzeichen (dh. /) Im Eingabemuster zu umgehen.

0