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条回答
Root(大扎)
2楼-- · 2019-01-30 07:45

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)
查看更多
Lonely孤独者°
3楼-- · 2019-01-30 07:47

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.

查看更多
虎瘦雄心在
4楼-- · 2019-01-30 07:47

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
}
查看更多
干净又极端
5楼-- · 2019-01-30 07:49

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.

查看更多
女痞
6楼-- · 2019-01-30 07:51

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.

查看更多
Summer. ? 凉城
7楼-- · 2019-01-30 07:54

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

$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...

查看更多
登录 后发表回答