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

PhpClassNamespaces

Php Problem Overview


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.

Php Solutions


Solution 1 - Php

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

(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 - Php

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

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

I use this:

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

Solution 6 - Php

To get the short name as an one-liner (since PHP 5.4):

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

It is a clean approach and reasonable fast.

Solution 7 - Php

I found myself in a unique situation where instanceof could not be used (specifically namespaced traits) and I needed the short name in the most efficient way possible so I've done a little benchmark of my own. It includes all the different methods & variations from the answers in this question.

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

A list of the of the entire result is here but here are the highlights:

  • If you're going to use reflection anyways, using $obj->getShortName() is the fastest method however; using reflection only to get the short name it is almost the slowest method.
  • 'strrpos' can return a wrong value if the object is not in a namespace so while 'safe strrpos' is a tiny bit slower I would say this is the winner.
  • To make 'basename' compatible between Linux and Windows you need to use str_replace() which makes this method the slowest of them all.

A simplified table of results, speed is measured compared to the slowest method:

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

Solution 8 - Php

You can use explode for separating the namespace and end to get the class name:

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

Solution 9 - Php

Yii way

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

Yii uses this method in its Gii code generator

Method documentation

> This method is similar to the php function basename() except that it will treat both \ and / as directory separators, independent of the operating system. This method was mainly created to work on php namespaces. When working with real file paths, php's basename() should work fine for you. Note: this method is not aware of the actual filesystem, or path components such as "..".

More information:

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

Solution 10 - Php

Here is simple solution for 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()));
		}
	}
}

What will be return?

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
}

Extended class name and namespace works well to:

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
}

What about class in global namespace?

namespace {

	class ClassWithoutNamespace {
		use \Names;
	}

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

Solution 11 - Php

If you need to know the class name that was called from inside a class, and don't want the namespace, you can use this one

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

This is great when you have a method inside a class which is extended by other classes. Furthermore, this also works if namespaces aren't used at all.

Example:

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

Solution 12 - Php

You may get an unexpected result when the class doesn't have a namespace. I.e. get_class returns Foo, then $baseClass would be oo.

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

This can easily be fixed by prefixing get_class with a backslash:

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

Now also classes without a namespace will return the right value.

Solution 13 - Php

Based on @MaBi 's answer, I made this:

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

Which you may use like that:

namespace Foo\Bar\Baz;

class A
{
    use ClassShortNameTrait;
}

A::class returns Foo\Bar\Baz\A, but A::getClassShortName() returns A.

Works for PHP >= 5.5.

Solution 14 - Php

I know this is an old post but this is what i use - Faster than all posted above just call this method from your class, a lot quicker than using Reflection

namespace Foo\Bar\Baz;

class Test {
    public function getClass() {
        return str_replace(__NAMESPACE__.'\\', '', static::class);
    }
}

Solution 15 - Php

Found on the documentation page of get_class, where it was posted by me at 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];
}

But the idea of namespaces is to structure your code. That also means that you can have classes with the same name in multiple namespaces. So theoretically, the object you pass could have the name (stripped) class name, while still being a totally different object than you expect.

Besides that, you might want to check for a specific base class, in which case get_class doesn't do the trick at all. You might want to check out the operator instanceof.

Solution 16 - Php

A good old regex seems to be faster than the most of the previous shown methods:

// both of the below calls will output: ShortClassName

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

So this works even when you provide a short class name or a fully qualified (canonical) class name.

What the regex does is that it consumes all previous chars until the last separator is found (which is also consumed). So the remaining string will be the short class name.

If you want to use a different separator (eg. / ) then just use that separator instead. Remember to escape the backslash (ie. \) and also the pattern char (ie. /) in the input pattern.

Solution 17 - Php

Quoting php.net:

> On Windows, both slash (/) and backslash () are used as directory separator character. In other environments, it is the forward slash (/).

Based on this info and expanding from arzzzen answer this should work on both Windows and Nix* systems:

<?php

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

Note: I did a benchmark of ReflectionClass against basename+str_replace+get_class and using reflection is roughly 20% faster than using the basename approach, but YMMV.

Solution 18 - Php

The fastest and imho easiest solution that works in any environment is:

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

?>

Solution 19 - Php

$shortClassName = join('',array_slice(explode('\\', $longClassName), -1));

Solution 20 - Php

If you're just stripping name spaces and want anything after the last \ in a class name with namespace (or just the name if there's no '') you can do something like this:

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

Basically it's regex to get any combination of characters or backslashes up and until the last backslash then to return only the non-backslash characters up and until the end of the string. Adding the ? after the first grouping means if the pattern match doesn't exist, it just returns the full string.

Solution 21 - Php

Because "ReflectionClass" can be version depend just use the follow:

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

or even clear

if(class_basename(ClassName::class) == 'ClassName') {
... do this ...
}

Solution 22 - Php

Fastest that I found here for PHP 7.2 on Ububntu 18.04

preg_replace('/^(\w+\\\)*/', '', static::class)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGreg.ForbesView Question on Stackoverflow
Solution 1 - PhplonesomedayView Answer on Stackoverflow
Solution 2 - PhpHirnhamsterView Answer on Stackoverflow
Solution 3 - PhpMaBiView Answer on Stackoverflow
Solution 4 - PhpspetsnazView Answer on Stackoverflow
Solution 5 - PhparzzzenView Answer on Stackoverflow
Solution 6 - PhpfloriView Answer on Stackoverflow
Solution 7 - PhpXorifelseView Answer on Stackoverflow
Solution 8 - Phpm13rView Answer on Stackoverflow
Solution 9 - PhpmariovialsView Answer on Stackoverflow
Solution 10 - PhpOzzyCzechView Answer on Stackoverflow
Solution 11 - PhpThe OninView Answer on Stackoverflow
Solution 12 - PhpTimView Answer on Stackoverflow
Solution 13 - PhpTristan JahierView Answer on Stackoverflow
Solution 14 - PhpSethView Answer on Stackoverflow
Solution 15 - PhpGolezTrolView Answer on Stackoverflow
Solution 16 - PhpEugen MihailescuView Answer on Stackoverflow
Solution 17 - PhpnoisebleedView Answer on Stackoverflow
Solution 18 - PhpFleshgrinderView Answer on Stackoverflow
Solution 19 - PhpmalhalView Answer on Stackoverflow
Solution 20 - PhpScottView Answer on Stackoverflow
Solution 21 - PhpMartin TonevView Answer on Stackoverflow
Solution 22 - PhpVasilii SuricovView Answer on Stackoverflow