Search code examples
phptypesreflectionphp-7anonymous-function

Can I check if ReflectionType is instance of another type?


I want to check if a callable's return type is an instance of another type. For example, let's say I a have:

  1. A class Pigeon which extends Animal.
  2. A function addSupplier() which takes a callable argument and expects the return type to be Animal.
  3. A call to addSupplier() which passes a closure that returns a Pigeon.

Here is the example in code:

namespace Foo\Bar;

class Animal {
    // ...
}

class Pigeon extends Animal {
    // ...
}

class Suppliers {

    public static function test() {
        self::addSupplier(fn() => new Pigeon());
    }

    public static function addSupplier(callable $callable) {
        $reflection = new ReflectionFunction($callable);
        $type = $reflection->getReturnType();

        if($type->isInstance('\Foo\Bar\Animal')) { // Pseudocode
            // It's an animal!
        } else
            throw new InvalidArgumentException("Callable must return an animal!");
    }
    
}

Is there a way that addSupplier can check the the return type of the callable parameter to ensure that it will be an instance of Animal?


Solution

  • As currently written your callable will eventually return a Pigeon instance, but is not constrainted to do that. If you add a return type hint, your requirement can be implemented. Have a look at this modification:

    <?php
    
    namespace Foo\Bar;
    
    class Animal {
        // ...
    }
    
    class Pigeon extends Animal {
        // ...
    }
    
    class Suppliers {
    
        public static function test() {
            self::addSupplier(fn(): Pigeon => new Pigeon());
        }
    
        public static function addSupplier(callable $callable) {
            $reflection = new \ReflectionFunction($callable);
            $type = $reflection->getReturnType();
            if($type && (new \ReflectionClass($type->getName()))->isSubclassOf('\Foo\Bar\Animal')) {
                print("It's an animal!");
            } else
                throw new \InvalidArgumentException("Callable must return an animal!");
        }
        
    }
    
    Suppliers::test();
    

    Remove the : Pigeon hint to see the other case.