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.
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.)
(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….
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
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__);
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 .
J'utilise ceci:
basename(str_replace('\\', '/', get_class($object)));
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
}
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
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);
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)
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.
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
.
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.
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.
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.
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"
?>
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);
}
}
$shortClassName = join('',array_slice(explode('\\', $longClassName), -1));
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.