177

完全な名前空間クラスを指定せずに、PHP 名前空間環境内のオブジェクトのクラスを確認するにはどうすればよいですか。

たとえば、オブジェクト library/Entity/Contract/Name があるとします。

get_class は完全な名前空間を持つクラスを返すため、次のコードは機能しません。

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

名前空間のマジック キーワードは、現在の名前空間を返します。これは、テスト対象のオブジェクトに別の名前空間がある場合は役に立ちません。

名前空間を使用して完全なクラス名を指定することもできますが、これはコードの構造にロックされているようです。名前空間を動的に変更したい場合にもあまり役に立ちません。

誰でもこれを行う効率的な方法を考えることができますか。1つのオプションは正規表現だと思います。

4

22 に答える 22

165

(new \ReflectionClass($obj))->getShortName();パフォーマンスに関しては、最適なソリューションです。

提供されているソリューションのどれが最速なのか興味があったので、簡単なテストをまとめました。

結果

Reflection: 1.967512512207 s ClassA
Basename:   2.6840535163879 s ClassA
Explode:    2.6507515668869 s ClassA

コード

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

結果は実際に私を驚かせました。爆発ソリューションが最速の方法だと思いました...

于 2014-08-24T14:36:09.983 に答える
97

https://stackoverflow.com/a/25472778/2386943のテストに substr を追加しました 。これは、両方とも i5 でテストできる最速の方法です (CentOS PHP 5.3.3、Ubuntu PHP 5.5.9)。

$classNameWithNamespace=get_class($this);
return substr($classNameWithNamespace, strrpos($classNameWithNamespace, '\\')+1);

結果

Reflection: 0.068084406852722 s ClassA
Basename: 0.12301609516144 s ClassA
Explode: 0.14073524475098 s ClassA
Substring: 0.059865570068359 s ClassA 

コード

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

==更新==

@MrBandersnatch のコメントで述べたように、これを行うにはさらに高速な方法があります。

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

「SubstringStrChr」を使用した更新されたテスト結果は次のとおりです (最大約 0.001 秒まで節約できます)。

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
于 2014-12-13T10:10:47.743 に答える
6

これは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()));
        }
    }
}

リターンはどうなりますか?

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
}

拡張されたクラス名と名前空間は、次の場合にうまく機能します。

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
}

グローバル名前空間のクラスはどうですか?

namespace {

    class ClassWithoutNamespace {
        use \Names;
    }

    echo ClassWithoutNamespace::getNamespace() . PHP_EOL; // empty string
    echo ClassWithoutNamespace::getBaseClassName() . PHP_EOL; // ClassWithoutNamespace
}
于 2014-02-28T12:35:58.863 に答える
2

@MaBi の答えに基づいて、私はこれを作りました:

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

あなたはそのように使うことができます:

namespace Foo\Bar\Baz;

class A
{
    use ClassShortNameTrait;
}

A::class戻りますFoo\Bar\Baz\Aが、A::getClassShortName()戻りますA

PHP >= 5.5 で動作します。

于 2016-05-31T16:08:20.347 に答える
2

クラスに名前空間がない場合、予期しない結果が生じる場合があります。つまり、get_classが返されます。Foo$baseClassoo

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

get_classこれは、バックスラッシュを前に付けることで簡単に修正できます。

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

名前空間のないクラスも正しい値を返すようになりました。

于 2016-02-05T09:51:18.637 に答える
0

php.netを引用:

Windows では、スラッシュ (/) とバックスラッシュ () の両方がディレクトリ区切り文字として使用されます。他の環境では、スラッシュ (/) です。

この情報に基づいて、arzzzen の回答から拡張すると、これは Windows と Nix* システムの両方で機能するはずです。

<?php

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

注:ReflectionClass反対のベンチマークを行いbasename+str_replace+get_classました。リフレクションを使用すると、ベース名アプローチを使用するよりも約20%高速ですが、YMMV.

于 2014-03-18T15:26:36.480 に答える
0

PHP 7.2ここで見つけた中で最速Ububntu 18.04

preg_replace('/^(\w+\\\)*/', '', static::class)
于 2020-05-18T22:36:43.260 に答える
0

名前空間を削除するだけで、名前空間を持つクラス名 (または「\」がない場合は名前だけ) の最後の \ の後に何かが必要な場合は、次のようにすることができます。

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

基本的に、文字またはバックスラッシュの任意の組み合わせを取得し、最後のバックスラッシュまで、バックスラッシュ以外の文字のみを文字列の最後まで返すのは正規表現です。? の追加 最初のグループ化の後、パターン一致が存在しない場合は、完全な文字列を返すだけです。

于 2017-03-13T21:29:56.467 に答える