【PHP8.x】class_uses関数の使い方

作成日: 更新日:

class_uses関数は、指定されたクラスが直接使用しているすべてのトレイトの名前を取得するために実行される関数です。トレイトとは、PHPにおいてコードの再利用性を高めるための仕組みであり、クラスにメソッドやプロパティを注入する形で機能を提供します。

この関数は、引数としてクラス名を表す文字列、またはクラスのインスタンスであるオブジェクトを受け取ります。オプションで、第二引数にオートロードを有効にするか否かの真偽値を指定できますが、通常はデフォルトのtrueで問題ありません。

成功した場合、この関数は、使用されている各トレイトの完全修飾名をキーとし、その同じトレイト名を値とする連想配列を返します。この配列には、親クラスが使用しているトレイトは含まれません。あくまで、指定されたクラス自身がuseキーワードでインポートしているトレイトのみが対象です。ただし、もしトレイトが別のトレイトを使用している場合は、その使用されているトレイトも結果の配列に含まれます。

PHP 8.0.0以降(本バージョンであるPHP 8.4を含む)では、指定されたクラスが存在しない場合や、引数の型が不正である場合にはfalseを返すのではなく、TypeError例外をスローするようになりました。システム開発においては、クラスの振る舞いを動的に調べたり、特定の機能を持つクラスを識別したりする際に、このclass_uses関数が役立ちます。

基本的な使い方

構文(syntax)

<?php

trait MyTrait {}

class MyClass {
    use MyTrait;
}

$traits = class_uses(MyClass::class);

?>

引数(parameters)

object|string $object_or_class, bool $autoload = true

  • object|string $object_or_class: トレイトを使用しているクラスまたはオブジェクトを指定します。
  • bool $autoload = true: トレイトを自動的にロードするかどうかを指定します。デフォルトはtrueで、自動ロードされます。

戻り値(return)

array|false

指定されたクラスが使用しているトレイトのクラス名(文字列)の配列を返します。トレイトが使用されていない場合は false を返します。

サンプルコード

PHPのclass_uses_recursiveでトレイトを再帰的に取得する

<?php

/**
 * Recursively finds all traits used by a class, including traits used by its parent classes
 * and traits used within other traits.
 *
 * @param object|string $class The class name or an object instance.
 * @return array An array of fully qualified trait names.
 */
function class_uses_recursive(object|string $class): array
{
    // If an object is passed, get its class name.
    if (is_object($class)) {
        $class = get_class($class);
    }

    $allTraits = [];
    $currentTraits = [];

    // Collect traits from the current class and all its parent classes.
    // This loop iterates up the inheritance chain to gather all directly used traits.
    do {
        // class_uses returns an array of traits or false if the class doesn't exist.
        // We use the null coalescing operator (?: []) to ensure we always merge an array.
        $traitsFromClass = class_uses($class) ?: [];
        $currentTraits = array_merge($currentTraits, $traitsFromClass);
        $allTraits = array_merge($allTraits, $traitsFromClass);
    } while ($class = get_parent_class($class)); // Move to the parent class until no parent is found.

    // Now, recursively collect traits used within the traits that have been gathered.
    // This loop continues as long as new traits are discovered in the trait dependencies.
    // $currentTraits holds the traits discovered in the previous "wave" whose sub-traits need to be found.
    while (!empty($currentTraits)) {
        $newTraitsDiscovered = [];
        foreach ($currentTraits as $trait) {
            // Get traits used by the current trait.
            $newTraitsDiscovered = array_merge(class_uses($trait) ?: [], $newTraitsDiscovered);
        }
        // Identify truly new traits that haven't been processed or added to $allTraits yet.
        // These new traits will be processed in the next iteration of the while loop.
        $currentTraits = array_diff($newTraitsDiscovered, $allTraits);
        // Add these newly discovered traits to the overall collection.
        $allTraits = array_merge($allTraits, $currentTraits);
    }

    // Return the unique list of all found traits.
    // array_unique is used as a final safeguard to ensure no duplicates, although the logic aims to prevent most.
    return array_unique($allTraits);
}

// --- Example Usage and Demonstrations ---

// Define some traits for demonstration purposes.
trait TraitA {
    use TraitB; // TraitA uses TraitB
}

trait TraitB {
    // TraitB does not use any further traits.
}

trait TraitC {
    use TraitD; // TraitC uses TraitD
}

trait TraitD {
    // TraitD does not use any further traits.
}

trait TraitE {
    // TraitE is a standalone trait.
}

// Define some classes for demonstration purposes.
class BaseClass {
    use TraitE; // BaseClass uses TraitE.
}

class MyClass extends BaseClass {
    use TraitA; // MyClass uses TraitA.
    use TraitC; // MyClass uses TraitC.
}

// Create an instance of MyClass.
$myObject = new MyClass();

// Get all traits used by MyClass (including those from parent classes and nested traits).
$allUsedTraits = class_uses_recursive($myObject);

// Sort the results for consistent output in the example.
sort($allUsedTraits);

echo "All traits used by MyClass (recursively):\n";
foreach ($allUsedTraits as $traitName) {
    echo "- " . $traitName . "\n";
}

echo "\n";

// Demonstrate with a class name directly.
$baseClassTraits = class_uses_recursive(BaseClass::class);
sort($baseClassTraits);
echo "All traits used by BaseClass (recursively):\n";
foreach ($baseClassTraits as $traitName) {
    echo "- " . $traitName . "\n";
}

echo "\n";

// Demonstrate traits used within a trait itself.
$traitATraits = class_uses_recursive(TraitA::class);
sort($traitATraits);
echo "All traits used by TraitA (recursively):\n";
foreach ($traitATraits as $traitName) {
    echo "- " . $traitName . "\n";
}

class_uses_recursive関数は、指定されたクラスまたはオブジェクトが直接的、間接的に使用しているすべてのトレイト名を再帰的に収集し、そのリストを配列として返すカスタム関数です。PHPが標準で提供するclass_uses関数が、クラスが直接使用するトレイトのみを返すのに対し、この関数は、親クラスが使用するトレイトや、別のトレイト内で使用されているネストされたトレイトも網羅的に探索します。

引数としては、調査したい対象のクラス名(文字列)またはそのクラスのオブジェクトインスタンスを渡すことができます。関数内部では、まず指定されたクラスとそのすべての親クラスが直接使用するトレイトを収集します。その後、収集されたトレイト自体がさらに別のトレイトを使用している場合、それらも発見される限り再帰的に追加していきます。戻り値は、発見されたすべてのトレイトの完全修飾名を含む配列となります。

この関数は、複雑なクラス階層やトレイトの利用状況を持つシステムにおいて、あるクラスが最終的にどのような機能群(トレイト)から成り立っているかを網羅的に把握したい場合に非常に有用です。例えば、特定の機能がどのトレイトを通じて提供されているかを追跡する際などに役立ちます。

このサンプルコードのclass_uses_recursive関数は、PHPの標準機能であるclass_usesを応用し、クラスや親クラス、さらに利用されているトレイト内まで再帰的に全てのトレイトを収集します。class_uses関数は、指定されたクラスが存在しない場合などにfalseを返すことがあるため、サンプルコードのように?: [](null合体演算子)で空配列に変換する処理は、エラーを防ぎ安全に動作させるための重要な工夫です。この関数はPHPの標準関数ではなく、独自に定義されたものですので、そのまま利用可能です。PHP 8.0以降の型ヒント(object|string)と戻り値の型宣言(: array)により、コードの堅牢性と可読性が向上しています。多くのクラスやトレイトが存在する大規模なシステムで利用する際は、処理のパフォーマンスや名前空間の扱いに注意してください。

PHP class_uses でトレイトを取得する

<?php

/**
 * Trait (トレイト) は、PHPにおいて複数のクラス間でコードを再利用するための仕組みです。
 * クラスの継承とは異なり、1つのクラスが複数のトレイトを利用できます。
 */
trait Timestampable
{
    /**
     * オブジェクトが作成された時刻を返します。
     */
    public function getCreatedAt(): DateTime
    {
        return new DateTime();
    }
}

/**
 * Timestampable トレイトを利用するクラスを定義します。
 */
class User
{
    use Timestampable; // Timestampable トレイトのメソッドを User クラスで利用できるようにします。

    private string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function getName(): string
    {
        return $this->name;
    }
}

/**
 * トレイトを利用しない別のクラスも定義します。
 */
class Product
{
    private string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function getName(): string
    {
        return $this->name;
    }
}

/**
 * class_uses() 関数は、指定されたクラスまたはオブジェクトが使用している
 * 全てのトレイトの名前を配列で返します。
 * トレイトを使用していない場合は空の配列を返します。
 * クラスが存在しないなどのエラー時には false を返します。
 */

echo "--- User クラスが使用しているトレイト ---" . PHP_EOL;

// 1. クラス名(文字列)を渡す例
$userTraits = class_uses(User::class);

if ($userTraits === false) {
    echo "エラー: User クラスのトレイト取得に失敗しました。" . PHP_EOL;
} elseif (empty($userTraits)) {
    echo "User クラスはトレイトを使用していません。" . PHP_EOL;
} else {
    echo "User クラスが使用しているトレイト:" . PHP_EOL;
    foreach ($userTraits as $trait) {
        echo "- " . $trait . PHP_EOL;
    }
}
echo PHP_EOL;

echo "--- Product クラスのオブジェクトが使用しているトレイト ---" . PHP_EOL;

// 2. オブジェクトを渡す例
$product = new Product("Laptop");
$productTraits = class_uses($product);

if ($productTraits === false) {
    echo "エラー: Product オブジェクトのトレイト取得に失敗しました。" . PHP_EOL;
} elseif (empty($productTraits)) {
    echo "Product オブジェクトはトレイトを使用していません。" . PHP_EOL;
} else {
    echo "Product オブジェクトが使用しているトレイト:" . PHP_EOL;
    foreach ($productTraits as $trait) {
        echo "- " . $trait . PHP_EOL;
    }
}
echo PHP_EOL;

// 動作確認として、トレイトのメソッドが呼び出せることを示します。
$user = new User("Alice");
echo "ユーザー名: " . $user->getName() . PHP_EOL;
echo "作成日時: " . $user->getCreatedAt()->format('Y-m-d H:i:s') . PHP_EOL;

?>

class_uses関数は、指定されたクラスまたはオブジェクトがどのトレイトを使用しているかを調べ、そのトレイト名を配列で返します。トレイトは、PHPでコードを再利用するための仕組みで、useキーワードを使ってクラスに取り込まれます。

引数には、確認したい「クラスの名前(文字列)」か「そのクラスのオブジェクト」を指定します。例えば、TimestampableトレイトをuseしているUserクラスにこの関数を使うと、Timestampableというトレイト名を含む配列が返されます。一方、トレイトをuseしていないProductクラスに使うと、空の配列が戻り値となります。

もし指定したクラスが存在しないなどのエラーが発生した場合は、戻り値としてfalseが返されるため、戻り値がfalseでないか確認し、適切に処理することが大切です。この関数を使うことで、実行時にクラスが利用している機能を動的に把握し、柔軟なコード設計に役立てられます。

class_uses関数は、指定されたクラスやオブジェクトが利用しているトレイトを配列で返しますが、エラーが発生した場合はfalseを返します。そのため、戻り値がfalseかどうかを===(厳密な比較)で確認し、適切にエラー処理を行うことが非常に重要です。トレイトを使用していないクラスの場合は空の配列が返されますので、その場合の処理も考慮してください。引数には、クラス名を表す文字列(ClassName::classのように指定するのが安全です)またはオブジェクトインスタンスを渡すことができます。トレイトは、PHPにおいて複数のクラス間で共通の機能を再利用するための仕組みであり、クラスの継承とは異なる形でコードを整理・拡張するために活用されます。

【PHP8.x】class_uses関数の使い方 | いっしー@Webエンジニア