I'm in the real need to know if, given a string className or object, it implements a particular interface. I'm specially interested in the string className part.
During the search, many seemingly valid options appeared: instanceof
, is_subclass_of
, is_a
, ReflectionClass::ImplementsInterface
, class_implements
… Which one to use? Let's do some testing!
The test
I coded the following test to determine the behavior of different options:
interface MyInterface { }
class NotInterfaced { }
class Interfaced implements MyInterface { }
class InterfacedDerived extends Interfaced { }
function dotest($functionName, $result)
{
echo ($result ? '☑' : '☒'), ' ', $functionName, PHP_EOL;
}
function test($object, array $functions)
{
$className = get_class($object);
echo "{$className} implements MyInterface?", PHP_EOL;
foreach ($functions as $functionName => $function) {
dotest("string {$functionName}", $function($className));
dotest("object {$functionName}", $function($object));
}
echo PHP_EOL;
}
$functions = array(
'instanceof' => function($type) {
return $type instanceof MyInterface;
},
'is_a' => function($type) {
return is_a($type, 'MyInterface');
},
'is_subclass_of' => function($type) {
return is_subclass_of($type, 'MyInterface');
},
'class_implements' => function($type) {
return in_array('MyInterface', class_implements($type));
},
'ReflectionClass::ImplementsInterface' => function($type) {
$rc = new ReflectionClass($type);
return $rc->implementsInterface('MyInterface');
},
);
test(new NotInterfaced, $functions);
test(new Interfaced, $functions);
test(new InterfacedDerived, $functions);
This checks every option with three different classes, using both the classname and a real instance of the class. And now, the results:
NotInterfaced
NotInterfaced implements MyInterface?
☒ string instanceof
☒ object instanceof
☒ string is_a
☒ object is_a
☒ string is_subclass_of
☒ object is_subclass_of
☒ string class_implements
☒ object class_implements
☒ string ReflectionClass::ImplementsInterface
☒ object ReflectionClass::ImplementsInterface
NotInterfaced
is clearly an edge case to check if the tests are doing fine. The results are all correct, since the class doesn't implement the MyInterface
interface.
Interfaced
Interfaced implements MyInterface?
☒ string instanceof
☑ object instanceof
☒ string is_a
☑ object is_a
☒ string is_subclass_of
☒ object is_subclass_of
☑ string class_implements
☑ object class_implements
☑ string ReflectionClass::ImplementsInterface
☑ object ReflectionClass::ImplementsInterface
Here we can extract some info. All methods worked well with objects, except for is_subclass_of
. Since it neither works with strings, at this point, I thought is_subclass_of
was not a valid option at all.
The other bit of info is that only class_implements
and ReflectionClass::ImplementsInterface
worked well with both string classNames and objects. Fine, as long as they're as fast as any of the other options.
InterfacedDerived
InterfacedDerived implements MyInterface?
☒ string instanceof
☑ object instanceof
☒ string is_a
☑ object is_a
☑ string is_subclass_of
☑ object is_subclass_of
☑ string class_implements
☑ object class_implements
☑ string ReflectionClass::ImplementsInterface
☑ object ReflectionClass::ImplementsInterface
Some more food for thought… And a surprise. Now, suddenly, is_subclass_of
worked well, not only for object parameters, but for string classNames too. A further check to it's name reveals the answer: InterfacedDerived
is a subclass of Interfaced
which, in turn, implements MyInterface
. So we can say the behavior was correct. My intuition, wasn't.
As with previous test, instanceof
and is_a
give good results for checking objects, as good as class_implements
and ReflectionClass::ImplementsInterface
for string classNames do.
In Conclusion
To check if an object implements a given interface, you can choose between:
instanceof
is_a
class_implements
ReflectionClass::ImplementsInterface
To check if a class implements a given interface, given only its class name, you should choose between:
class_implements
ReflectionClass::ImplementsInterface
Which are your choices? Know of any other way to do it?
No comments:
Post a Comment