How do I get an object's unqualified (short) class name?

134,597

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)));
Share:
134,597

Related videos on Youtube

Greg.Forbes
Author by

Greg.Forbes

Updated on July 08, 2022

Comments

  • Greg.Forbes
    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
      Alma Do over 10 years
      It 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
      lonesomeday over 10 years
      I'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
      Darren Cook over 10 years
      For 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
    Greg.Forbes over 10 years
    instanceof 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
    lonesomeday over 10 years
    @Greg.Forbes Because Tenant doesn't exist in the current namespace. Try var_dump($tenant instanceof \Library\Entity\People\Tenant) instead. Also, investigate how to use the use operator, and the general concept behind PHP namespaces!
  • dompie
    dompie about 10 years
    You can also try: $className = explode('\\', basename(get_class($this))); $className = array_pop($className); to get the plain classname. Or use substr.
  • OzzyCzech
    OzzyCzech about 10 years
    Works 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
    prograhammer about 10 years
    I had to add a slash in front like this $reflect = new \ReflectionClass($object);
  • AgmLauncher
    AgmLauncher about 10 years
    This 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
    Fleshgrinder about 10 years
    PHP without “concrete” (or should we call them procedural) functions is impossible. Let's wait for PHP 6 (well, if it ever comes).
  • Tobias Nyholm
    Tobias Nyholm over 9 years
    Great 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
    LeMike over 9 years
    Run 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
    Joe Green over 9 years
    I 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
    Theodore R. Smith over 9 years
    I have fixed it now. Thanks, @OzzyCzech.
  • ctatro85
    ctatro85 over 8 years
    Just 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
    Franklin P Strube over 8 years
    I 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
    Franklin P Strube over 8 years
    I 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
    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 mind str_replace is slower than substr. 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
    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
    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
    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
    mkungla over 8 years
    Instead 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
    Heroselohim about 8 years
    Thanks !! 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
    Tristan Jahier almost 8 years
    Warning: 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
    Chay22 almost 8 years
    The latter code really suits my need, perfect! strtolower(substr(strrchr(MyClass::class, '\\'), 1));
  • Unverified Contact
    Unverified Contact over 7 years
    I wonder how this compares against a string extraction in benchmarks. It seems like this would be much slower.
  • Steve Buzonas
    Steve Buzonas over 7 years
    This isn't a builtin php function, it looks like a helper function provided by laravel.
  • m47730
    m47730 over 7 years
    according 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
    Scott about 7 years
    I think he said that
  • Jens
    Jens about 7 years
    Welcome to Stack Overflow. Please provide more information for your answer. What does this do and how can one use it.
  • Jeremy Wadhams
    Jeremy Wadhams about 7 years
    Thanks, I'm using Laravel and this answer saved me a bunch of time.
  • jgmjgm
    jgmjgm over 6 years
    array_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
    jgmjgm over 6 years
    You 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
    FantomX1 over 6 years
    This 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.
    Vanja D. about 6 years
    Many 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
    Kirby almost 6 years
    Reflection is too slow for so simple task. Why not to use just explode("\\", get_class($object));?
  • lonesomeday
    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
    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
    lonesomeday almost 6 years
    @Kirby The question is "Why do you want the class name as a string?"
  • Kirby
    Kirby almost 6 years
    Hm... I don't know. I just want only short class name. :)
  • mcmurphy
    mcmurphy almost 6 years
    running 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
    rosell.dk about 5 years
    To make it work in the global namespace too, simply prepend the classname with a backslash :) - ie: $classNameShort = substr(strrchr('\\' . get_class($this), '\\'), 1);
  • 2oppin
    2oppin over 4 years
    explode('\\', static::class)[0] ? doesn't it returns the first part of the namespace? should return last part, not first
  • jezmck
    jezmck over 4 years
  • MikeSchinkel
    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
    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
    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
    kaan_a over 4 years
    If 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
    jurchiks about 4 years
    Unfortunately 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
    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
    Vasilii Suricov almost 4 years
    getClassExplode returns foo. PHP Version 7.2.24-0ubuntu0.18.04.4
  • Vasilii Suricov
    Vasilii Suricov almost 4 years
    1M iterations: 1st preg_replace('/^(\w+\\\)*/', '', static::class) = 0.185, 2nd (new \ReflectionClass($this))->getShortName() = 0.248, 3rd basename(str_replace('\\', '/', static::class)) = 0.558
  • Vasilii Suricov
    Vasilii Suricov almost 4 years
    10 iterations: the same range with 3.790, 5.317, 9.918 respectively
  • lonesomeday
    lonesomeday almost 4 years
    @VasiliiSuricov Can you clarify what you mean?
  • Vasilii Suricov
    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
    aross over 3 years
    I 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
    FallDi over 3 years
    class_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.