How do I get an object's unqualified (short) class name?
Solution 1
You can do this with reflection. Specifically, you can use the ReflectionClass::getShortName
method, which gets the name of the class without its namespace.
First, you need to build a ReflectionClass
instance, and then call the getShortName
method of that instance:
$reflect = new ReflectionClass($object);
if ($reflect->getShortName() === 'Name') {
// do this
}
However, I can't imagine many circumstances where this would be desirable. If you want to require that the object is a member of a certain class, the way to test it is with instanceof
. If you want a more flexible way to signal certain constraints, the way to do that is to write an interface and require that the code implement that interface. Again, the correct way to do this is with instanceof
. (You can do it with ReflectionClass
, but it would have much worse performance.)
Solution 2
(new \ReflectionClass($obj))->getShortName();
is the best solution with regards to performance.
I was curious which of the provided solutions is the fastest, so I've put together a little test.
Results
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";
The results actually surprised me. I thought the explode solution would be the fastest way to go...
Solution 3
I added substr to the test of https://stackoverflow.com/a/25472778/2386943 and that's the fastet way I could test (CentOS PHP 5.3.3, Ubuntu PHP 5.5.9) both with an i5.
$classNameWithNamespace=get_class($this);
return substr($classNameWithNamespace, strrpos($classNameWithNamespace, '\\')+1);
Results
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==
As mentioned in the comments by @MrBandersnatch there is even a faster way to do this:
return substr(strrchr(get_class($this), '\\'), 1);
Here are the updated test results with "SubstringStrChr" (saves up to about 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
Solution 4
Here is a more easier way of doing this if you are using Laravel PHP framework :
<?php
// usage anywhere
// returns HelloWorld
$name = class_basename('Path\To\YourClass\HelloWorld');
// usage inside a class
// returns HelloWorld
$name = class_basename(__CLASS__);
/**
* Get the class "basename" of the given object / class.
*
* @param string|object $class
* @return string
*/
function class_basename($class)
{
$class = is_object($class) ? get_class($class) : $class;
return basename(str_replace('\\', '/', $class));
}
Solution 5
I use this:
basename(str_replace('\\', '/', get_class($object)));
Related videos on Youtube
Greg.Forbes
Updated on July 08, 2022Comments
-
Greg.Forbes almost 2 years
How do I check the class of an object within the PHP name spaced environment without specifying the full namespaced class.
For example suppose I had an object library/Entity/Contract/Name.
The following code does not work as get_class returns the full namespaced class.
If(get_class($object) == 'Name') { ... do this ... }
The namespace magic keyword returns the current namespace, which is no use if the tested object has another namespace.
I could simply specify the full classname with namespaces, but this seems to lock in the structure of the code. Also not of much use if I wanted to change the namespace dynamically.
Can anyone think of an efficient way to do this. I guess one option is regex.
-
Alma Do over 10 yearsIt seems near pointless because different namespaces could have same class names defined inside them, so how will you handle that? And that is because full qualified class name is returned in your sample
-
lonesomeday over 10 yearsI'm on a mobile device, so I can't submit a decent answer, but the solution is reflection, specifically ReflectionClass::getShortName - php.net/manual/en/reflectionclass.getshortname.php
-
Darren Cook over 10 yearsFor people looking for a reason to want this: it might be useful in a helper function in a common base class (i.e. multiple namespaces is never an issue in this situation).
-
-
Greg.Forbes over 10 yearsinstanceof is exactly what I am looking for, but I get a strange result on one hierarchy. I enter echo get_class($tenant) and get Library\Entity\People\Tenant. Then enter var_dump($tenant instanceof Tenant) and get Bool(false) - any thoughts? Maybe I need to log as a separate question.
-
lonesomeday over 10 years@Greg.Forbes Because
Tenant
doesn't exist in the current namespace. Tryvar_dump($tenant instanceof \Library\Entity\People\Tenant)
instead. Also, investigate how to use theuse
operator, and the general concept behind PHP namespaces! -
dompie about 10 yearsYou can also try: $className = explode('\\', basename(get_class($this))); $className = array_pop($className); to get the plain classname. Or use substr.
-
OzzyCzech about 10 yearsWorks only on Windows On Windows, both slash (/) and backslash () are used as directory separator character. In other environments, it is the forward slash (/) php.net/manual/en/function.basename.php
-
prograhammer about 10 yearsI had to add a slash in front like this
$reflect = new \ReflectionClass($object);
-
AgmLauncher about 10 yearsThis is why I wish PHP had internal class information operators. Instantiating an external reflector to do what should be as simple as
$Object->__class->getShortName()
really pisses me off about PHP. Your approach works, but now you're putting concrete methods in your classes just to expose what should be a language construct. -
Fleshgrinder about 10 yearsPHP without “concrete” (or should we call them procedural) functions is impossible. Let's wait for PHP 6 (well, if it ever comes).
-
Tobias Nyholm over 9 yearsGreat answer. I was running the very same code but I got a different result (Macbook Pro i7, 16 GB ram). Reflection:0.382, Basename:0.380, Explode:0.399. I think it depends on your system what is best...
-
LeMike over 9 yearsRun PHP 10 000 times with that code and you get a better result. The above might fetch the reflection from some pool, but this is not the usual behaviour of the applications out there. They only need it once or twice.
-
Joe Green over 9 yearsI wonder does this test hold true when instantiating a ReflectionClass on a more substantial object than the small object of Class A in your test...
-
Theodore R. Smith over 9 yearsI have fixed it now. Thanks, @OzzyCzech.
-
ctatro85 over 8 yearsJust because we listing for efficiency I found this to be the fastest, comparison from the test provided in this solution substr(strrchr(get_class($obj), '\\'), 1); Reflection: 0.084223914146423 s ClassA -- Basename: 0.13206427097321 s ClassA -- Explode: 0.15331919193268 s ClassA -- Substring: 0.068068099021912 s ClassA -- Strrchar: 0.06472008228302 s ClassA --
-
Franklin P Strube over 8 yearsI just came across this thread and added an additional benchmark to test
str_replace(__NAMESPACE__ . '\\', '', __CLASS__);
. The results on a weak virtual machine showed it to be almost twice as fast as all of these.php -f bench.php Reflection: 0.44037771224976 s ClassA Basename: 0.48089025020599 s ClassA Explode: 0.54955270290375 s ClassA Substring: 0.38200764656067 s ClassA Frank's Custom Benchmark: 0.22782742977142 s ClassA
-
Franklin P Strube over 8 yearsI generally don't like to do a lot of ReflectionClass voodoo in my application because it can lead to unexpected results if mis-used (protected methods becoming public, etc.). You can use simple string replacement on PHP magic constants instead:
str_replace(__NAMESPACE__ . '\\', '', __CLASS__);
. It's also much faster, performance-wise. -
MaBi over 8 years@FranklinPStrube you didn't use an object as mentioned in the question (you used
__CLASS__
and also__NAMESPACE__
which are pointing to the current file). Therefore it's faster. To be fair, I added your suggestion to the test and ran it. Keep in mindstr_replace
is slower thansubstr
. Here are the results:Reflection: 0.070202589035034 s TestController Basename: 0.12093763881259 s TestController Explode: 0.12268789609273 s TestController Substring: 0.061004797617594 s TestController Consts: 0.057392239570618 s TestController
Your function is the "Consts". -
MaBi over 8 years@MrBandersnatch you are correct. I tested your solution and it saved me about 0.001 s. I updated my answer with yours!
-
lonesomeday over 8 years@FranklinPStrube Unless I'm missing something, that gets the short name of the current class, rather than the class of the object. I agree that use of reflection usually means you're Doing It Wrong.
-
Chris Baker over 8 years@OzzyCzech I just ran into this while moving from Windows to Ubuntu.... maddening. Wound up using the solution mentioned in MaBi's update.
-
mkungla over 8 yearsInstead of get_class($this) you might want to use some cases
substr(strrchr(get_called_class(), '\\'), 1)
for instance when you want to get Class name in parent class, traits etc. -
Heroselohim about 8 yearsThanks !! I've only required this once. My enumeration clases are using language files for the description. The name of the class is ideal for the filename of the language file.
-
Tristan Jahier almost 8 yearsWarning: this code does not work with classes in the global namespace (i.e.: their full name equals their short name)! I advice to test something like:
if ($pos = strrchr(static::class, '\\')) { .. } else { ... }
. -
Chay22 almost 8 yearsThe latter code really suits my need, perfect!
strtolower(substr(strrchr(MyClass::class, '\\'), 1));
-
Unverified Contact over 7 yearsI wonder how this compares against a string extraction in benchmarks. It seems like this would be much slower.
-
Steve Buzonas over 7 yearsThis isn't a builtin php function, it looks like a helper function provided by laravel.
-
m47730 over 7 yearsaccording to doc php.net/manual/en/function.strrchr.php only the first character is considered in strrchr. So:
return substr(strrchr(get_class($this), '\'), 1);
-
Scott about 7 yearsI think he said that
-
Jens about 7 yearsWelcome to Stack Overflow. Please provide more information for your answer. What does this do and how can one use it.
-
Jeremy Wadhams about 7 yearsThanks, I'm using Laravel and this answer saved me a bunch of time.
-
jgmjgm over 6 yearsarray_pop may be expensive. Try with end instead, at least then you aren't altering the array contents (although the difference might be trivial). For me reflection is 20% slower than explode, once I fix it so that the result of explode goes to a variable. Otherwise you get notice spam. A suppressed notice might impact performance more than you think.
-
jgmjgm over 6 yearsYou can also cache get_class, use CLASS, use self/static::class, might only be dealing with a string from elsewhere, etc. strrpos with substr is by far the fastest of all if you want speed. About twice to three times as fast in php7.1. In php 5.6 it's still strrpos > explode > reflection, however the difference is less pronounced.
-
FantomX1 over 6 yearsThis worked for me on Windows but not on Linux, maybe because namespaces are in a form of Windows directories backslashes '\' , whereas linux basename considers directory separators forward slashes '/'. So I worked it around with strtr.' basename(strtr($class,'\\','/'))
-
Vanja D. about 6 yearsMany people use Reflections for member visibility overrides, which is BAD. Do not do that! But stating that use of Reflections in general is Voodoo and Doing It Wrong gives people the wrong impression. You shouldn't avoid them, you should understand them and know when they are beneficial and at which level of abstraction.
-
Kirby almost 6 yearsReflection is too slow for so simple task. Why not to use just
explode("\\", get_class($object));
? -
lonesomeday almost 6 years@Kirby Slow? Possibly not, actually. But the relevant question is surely "why do you need the class name as a string?" It's not often the right solution to a problem.
-
Kirby almost 6 years@lonesomeday, thanks for the answer. Do you have some example when it wouldn't work? It'd be good to know...
-
lonesomeday almost 6 years@Kirby The question is "Why do you want the class name as a string?"
-
Kirby almost 6 yearsHm... I don't know. I just want only short class name. :)
-
mcmurphy almost 6 yearsrunning just one iteration instead of 100000 gives a much different result: Reflection: 1.0967254638672 100000th/s ClassA Basename: 0.81062316894531 100000th/s ClassA Explode: 0.50067901611328 100000th/s ClassA
-
rosell.dk about 5 yearsTo make it work in the global namespace too, simply prepend the classname with a backslash :) - ie:
$classNameShort = substr(strrchr('\\' . get_class($this), '\\'), 1);
-
2oppin over 4 yearsexplode('\\', static::class)[0] ? doesn't it returns the first part of the namespace? should return last part, not first
-
jezmck over 4 years
-
MikeSchinkel over 4 years@VanjaD. "Many people use Reflections for member visibility overrides, which is BAD" — It is not "BAD" when you are doing it for the right reasons. Such as implementing features that PHP is missing. One such missing feature is the ability to update the properties of an instance with the properties of a previously serialized instance. #moral_judgementalism_is_so_tedious
-
lonesomeday over 4 years@MikeSchinkel I've provided the code to do this, while noting that I personally can't think of a good use from it. That is a million miles from saying that there is no good use for it. If you're extrapolating from my answer to the general problems afflicting the human condition, you may just possibly be finding stuff that isn't there.
-
MikeSchinkel over 4 years@lonesomeday — Suffice it to say that your commentary hit on one of my pet peeves; developers who "moralize" about programming techniques simple because they are unable to imagine appropriate use-cases. To me it speaks more about a lack of vision of the person standing in judgement than it does about inappropriateness of the technique. Hey, but don't worry; the vast majority of developers opine in this manner so it is not like you are unique.
-
kaan_a over 4 yearsIf you need to retrieve not just the unqualified class name but also the namespace then I'm sure that storing the index from strrpos in a variable and then using it to get both the base name and the namespace will be fastest even though I've not benchmarked it. Perhaps reflections can compete. In my code I was using parts of the namespace to find file paths.
-
jurchiks about 4 yearsUnfortunately that only works if you're calling it in the class whose name you want, not on just any class name as a string.
-
FantomX1 about 4 years@OzzyCzech How come works only on Windows? the question was regarding the fully qualified namespace name if I am not wrong also years ago, and namespaces are not OS specific, and always with a backslash like windows directory separator.
-
Vasilii Suricov almost 4 years
getClassExplode
returnsfoo
.PHP Version 7.2.24-0ubuntu0.18.04.4
-
Vasilii Suricov almost 4 years1M iterations: 1st
preg_replace('/^(\w+\\\)*/', '', static::class)
= 0.185, 2nd(new \ReflectionClass($this))->getShortName()
= 0.248, 3rdbasename(str_replace('\\', '/', static::class))
= 0.558 -
Vasilii Suricov almost 4 years10 iterations: the same range with 3.790, 5.317, 9.918 respectively
-
lonesomeday almost 4 years@VasiliiSuricov Can you clarify what you mean?
-
Vasilii Suricov almost 4 years@lonesomeday i'll remove it, sorry. this comment was for next answer "is the best solution with regards to performance." - is not. how i've explained there
preg_replace
is fastest way -
aross over 3 yearsI think it's a micro optimization, but it looks like preg_replace is faster than all 5 of your end results. I've tested with a static string to not measure get_class performance as well, but that way preg_replace takes about 20% off the time of
SubstringStrChr
as you named it:preg_replace('#^.+\\\\#', '', $fqcn);
-
FallDi over 3 yearsclass_basename is not standard php function, you need to install laravel (illuminate/helpers) to use it. Such solution is too heavy due to additional package requirements.