How do I get an object's unqualified (short) c

2019-01-30 07:15发布

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.

20条回答
聊天终结者
2楼-- · 2019-01-30 07:38

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.

查看更多
SAY GOODBYE
3楼-- · 2019-01-30 07:40

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.

查看更多
爷的心禁止访问
4楼-- · 2019-01-30 07:41

I use this:

basename(str_replace('\\', '/', get_class($object)));
查看更多
Explosion°爆炸
5楼-- · 2019-01-30 07:42

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%  |
+-----------------+--------+
查看更多
Explosion°爆炸
6楼-- · 2019-01-30 07:42

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.

查看更多
可以哭但决不认输i
7楼-- · 2019-01-30 07:45

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

$ex = explode("\\", get_class($object));
$className = end($ex);
查看更多
登录 后发表回答