web-dev-qa-db-fra.com

Comment obtenir le nom de classe non qualifié (court) d'un objet?

Comment vérifier la classe d'un objet dans l'environnement d'espacement de noms PHP sans spécifier la classe de noms d'espaces complète. 

Par exemple, supposons que je dispose d’une bibliothèque d’objets/entité/contrat/nom. 

Le code suivant ne fonctionne pas car get_class renvoie la classe namespaced complète. 

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

Le mot clé magique d'espace de nom renvoie l'espace de nom actuel, ce qui ne sert à rien si l'objet testé possède un autre espace de nom. 

Je pourrais simplement spécifier le nom de classe complet avec des espaces de noms, mais cela semble verrouiller la structure du code. Aussi pas très utile si je voulais changer l'espace de noms dynamiquement. 

Quelqu'un peut-il penser à un moyen efficace de le faire? Je suppose qu'une option est regex. 

125
Greg.Forbes

Vous pouvez le faire avec réflexion. Plus précisément, vous pouvez utiliser la méthode ReflectionClass::getShortName , qui obtient le nom de la classe sans son espace de nom.

Tout d'abord, vous devez créer une instance ReflectionClass, puis appeler la méthode getShortName de cette instance:

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

Cependant, je ne peux imaginer de nombreuses circonstances dans lesquelles cela serait souhaitable. Si vous voulez exiger que l'objet soit membre d'une certaine classe, vous pouvez le tester à l'aide de instanceof. Si vous voulez un moyen plus flexible de signaler certaines contraintes, vous pouvez écrire une interface et demander que le code implémente cette interface. Encore une fois, la bonne façon de faire est d'utiliser instanceof. (Vous pouvez le faire avec ReflectionClass, mais les performances seraient bien pires.)

155
lonesomeday

(new \ReflectionClass($obj))->getShortName(); est la meilleure solution en termes de performances.

J'étais curieux de savoir laquelle des solutions fournies est la plus rapide, alors j'ai préparé un petit test.

Résultats

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

Les résultats m'ont réellement surpris. Je pensais que la solution d’explosion serait le moyen le plus rapide de partir….

111
Hirnhamster

J'ai ajouté soustr au test de https://stackoverflow.com/a/25472778/2386943 Et c'est la méthode rapide que j'ai pu tester (CentOS PHP 5.3.3, Ubuntu PHP 5.5.9) les deux avec un i5.

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

Résultats

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

Comme mentionné dans les commentaires de @MrBandersnatch, il existe même un moyen plus rapide de procéder:

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

Voici les résultats de test mis à jour avec "SubstringStrChr" (enregistre jusqu'à environ 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

Voici un moyen plus simple de procéder si vous utilisez le framework Laravel PHP:

<?php

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

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

Pour obtenir le nom abrégé sous la forme d'une ligne (depuis PHP 5.4 ):

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

C'est une approche propre et rapide rapide .

14
flori

J'utilise ceci:

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

Voici une solution simple pour 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()));
        }
    }
}

Quel sera le retour?

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
}

Le nom de classe étendu et l'espace de noms fonctionnent bien pour:

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
}

Qu'en est-il de la classe dans un espace de noms global?

namespace {

    class ClassWithoutNamespace {
        use \Names;
    }

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

Yii way

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

Yii utilise cette méthode dans son générateur de code Gii

Documentation de la méthode

Cette méthode est similaire à la fonction php basename () sauf qu’elle traitera les séparateurs de répertoires\et/as, indépendamment du système d’exploitation. Cette méthode a été principalement créée pour travailler sur les espaces de noms php. Lorsque vous travaillez avec de vrais chemins de fichiers, basename () de PHP devrait fonctionner correctement pour vous. Remarque: cette méthode ne connaît pas le système de fichiers ni les composants de chemin tels que "..".

Plus d'information:

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

Vous pouvez utiliser explode pour séparer l'espace de nom et end pour obtenir le nom de la classe:

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

Si vous avez besoin de connaître le nom de la classe appelée depuis l'intérieur d'une classe et que vous ne voulez pas d'espace de nom, vous pouvez utiliser celui-ci. 

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

C'est très bien quand vous avez une méthode dans une classe qui est étendue par d'autres classes. De plus, cela fonctionne aussi si les espaces de noms ne sont pas utilisés du tout.

Exemple:

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

Basé sur la réponse de @MaBi, j'ai écrit ceci:

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

Que vous pouvez utiliser comme ça:

namespace Foo\Bar\Baz;

class A
{
    use ClassShortNameTrait;
}

A::class renvoie Foo\Bar\Baz\A, mais A::getClassShortName() renvoie A.

Fonctionne pour PHP> = 5.5.

2
Tristan Jahier

Trouvé sur la page de documentation de get_class , où il a été posté par moi à 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];
}

Mais l'idée des espaces de noms est de structurer votre code. Cela signifie également que vous pouvez avoir des classes avec le même nom dans plusieurs espaces de noms. Donc théoriquement, l'objet que vous transmettez pourrait porter le nom de la classe (stripped), tout en restant un objet totalement différent de celui attendu.

En plus de cela, vous voudrez peut-être vérifier une classe de base particulière, auquel cas get_class ne fait pas l'affaire du tout. Vous voudrez peut-être vérifier l'opérateur instanceof .

1
GolezTrol

Vous pouvez obtenir un résultat inattendu lorsque la classe n'a pas d'espace de nom. C'est à dire. get_class renvoie Foo, alors $baseClass serait oo.

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

Cela peut facilement être corrigé en préfixant get_class avec une barre oblique inverse:

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

Désormais, les classes sans espace de noms renverront la bonne valeur.

1
Tim

Si vous supprimez simplement des espaces de noms et que vous voulez quoi que ce soit après le dernier\dans un nom de classe avec un espace de noms (ou simplement le nom s'il n'y a pas de '\'), vous pouvez procéder de la manière suivante:

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

Fondamentalement, c'est regex pour obtenir n'importe quelle combinaison de caractères ou de barres obliques inverses et jusqu'à la dernière barre oblique inverse, puis pour renvoyer uniquement les caractères non barres obliques inverses jusqu'à la fin de la chaîne. Ajout du? après que le premier groupe signifie que si la correspondance de modèle n'existe pas, elle renvoie simplement la chaîne complète.

0
Scott

Citant php.net:

Sous Windows, la barre oblique (/) et la barre oblique inversée () sont utilisées comme séparateur de répertoire. Dans d'autres environnements, il s'agit de la barre oblique (/).

Sur la base de ces informations et du développement d’arzzzen answer, cela devrait fonctionner à la fois sur les systèmes Windows et Nix *:

<?php

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

Remarque: J'ai effectué un test d'évaluation de ReflectionClass contre basename+str_replace+get_class et l'utilisation de la réflexion est environ 20% plus rapide que celle de l'approche de nom de base, mais YMMV.

0
noisebleed

La solution la plus rapide et la plus simple qui fonctionne dans tous les environnements est la suivante:

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

Je sais que c’est un vieux message, mais c’est ce que j’utilise: plus rapide que tous les messages ci-dessus, appelez simplement cette méthode dans votre classe, bien plus rapidement que l’utilisation de 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

Une bonne vieille expression régulière semble être plus rapide que la plupart des méthodes montrées précédemment:

// both of the below calls will output: ShortClassName

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

Donc, cela fonctionne même lorsque vous fournissez un nom de classe court ou un nom de classe pleinement qualifié (canonique).

Ce que regex fait, c'est qu'il consomme tous les caractères précédents jusqu'à ce que le dernier séparateur soit trouvé (ce qui est également consommé). Donc, la chaîne restante sera le nom abrégé de la classe. 

Si vous souhaitez utiliser un séparateur différent (par exemple, /), utilisez-le à la place. N'oubliez pas d'échapper à la barre oblique inverse (c.-à-d. \) Ainsi qu'au caractère de motif (c.-à-d. /) Du modèle d'entrée.

0
Eugen Mihailescu