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:
- ClassManifest
- ClassManifestVisitor
The implementation above uses the nikic/php-parser
package, rather than directly using reflection.
精彩评论