开发者

PHP: how to get a list of classes that implement certain interface?

I've got an interface

interface IModule {
    public function Install();
}

and some classes that implement this interface

class Module1 implements IModule {
    public function Install() {
        return true;
    }
}

class Module2 implements IModule {
    public function Install() {
        return true;
    }
}

...

class ModuleN implements IModule {
    public function Install() {
        return true;
    }
}

How to get a list of all 开发者_如何转开发classes that implement this interface? I'd like to get this list with PHP.


You dont need Reflection for this. You can simply use

  • class_implements — Return the interfaces which are implemented by the given class

Usage

in_array('InterfaceName', class_implements('className'));

Example 1 - Echo all classes implementing the Iterator Interface

foreach (get_declared_classes() as $className) {
    if (in_array('Iterator', class_implements($className))) {
        echo $className, PHP_EOL;
    }
}

Example 2 - Return array of all classes implementing the Iterator Interface

print_r(
    array_filter(
        get_declared_classes(), 
        function ($className) {
            return in_array('Iterator', class_implements($className));
        }
    )
);

The second example requires PHP5.3 due to the callback being an anonymous function.


You can use PHP's ReflectionClass::implementsInterface and get_declared_classes functions to accomplish this:

$classes = get_declared_classes();
$implementsIModule = array();
foreach($classes as $klass) {
   $reflect = new ReflectionClass($klass);
   if($reflect->implementsInterface('IModule')) 
      $implementsIModule[] = $klass;
}


Generic solution:

function getImplementingClasses( $interfaceName ) {
    return array_filter(
        get_declared_classes(),
        function( $className ) use ( $interfaceName ) {
            return in_array( $interfaceName, class_implements( $className ) );
        }
    );
}


To check who implements a particular interface, you can write a function like below:

<?php
/**
 * Get classes which implement a given interface 
 * @param string $interface_name Name of the interface
 * @return array Array of names of classes. Empty array means input is a valid interface which no class is implementing. NULL means input is not even a valid interface name.
 */
function whoImplements($interface_name) {
    if (interface_exists($interface_name)) {
        return array_filter(get_declared_classes(), create_function('$className', "return in_array(\"$interface_name\", class_implements(\"\$className\"));"));
    }
    else {
        return null;
    }
}

Output of an example call var_export(whoImplements('ArrayAccess')); will be as follows:

[sandbox]$ php whoimplementswhat.php
Array
(
    [29] => CachingIterator
    [30] => RecursiveCachingIterator
    [38] => ArrayObject
    [39] => ArrayIterator
    [40] => RecursiveArrayIterator
    [48] => SplDoublyLinkedList
    [49] => SplQueue
    [50] => SplStack
    [55] => SplFixedArray
    [56] => SplObjectStorage
    [111] => Phar
    [112] => PharData
)

This way, you don't use loops and you can run your code on lower versions of PHP. Function array_filter loops internally, but inside PHP execution engine (hence more performant than loops written in PHP code).


The answers here don't actually suggest any reliable way to achieve this... while they do work, they are unreliable in the sense that they're only returning classes that have been loaded, which is not what anyone really wants and is redundant to instanceof in the scenarios that these methods are potentially useful for.

get_declared_classes()

Returns an array of the names of the declared classes in the current script.

The keyword here is current script, this function will not return classes that have not been loaded yet.

interface_exists

Similar to the above, this method will not know if an interface exists, if the interface has not been loaded yet.

It will also autoload the interface (note the second parameter of interface_exists)

class_implements

Similar to above, this will also autoload your files regardless of your intention to use them.

So, how can I reliably retrieve this information, in an efficient way...

Simply put, construct a manifest with all this information and store it in cache.

Manifests help to cache information which is too expensive to generate on each request. Some manifests generate maps, e.g. class names to filesystem locations. Others store aggregate information like nested configuration graphs.

Here is a great example of a manifest being constructed in an MVC which serves purely to address the issues in the points above.

There is quite a bit of code to consider in implementing a class manifest accurately, and the complete picture would be a little difficult to post in this answer, however here are a few reference points to get you started:

  1. ClassManifest
  2. ClassManifestVisitor

The implementation above uses the nikic/php-parser package, rather than directly using reflection.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜