Search code examples
phptype-hintingphp-8

Is it possible to type hint more than one type?


Can I allow two different types using type hinting?

E.g. parameter $requester could be either of User or File:

function log (User|File $requester) {

}

Solution

  • Academically, this is called a type union.

    Union types in PHP

    You can cheat by creating interfaces, parent types, etc, as mentioned in other answers, but what's the point, apart for adding complexity and LoCs to your project? Plus, that can't work for scalar types as you can't extend/implement a scalar type.

    Instead of making the code more readable, you'll get the opposite. Except if those classes/interfaces already existed and they are here because of OOP, not to solve a type hinting problem.

    Workarounds

    The canonical answer in PHP is... well, just don't put a type hint. The language was not thought to have a complex and powerful type system, and trying to workaround the flaws of the language is not a good answer.

    Instead, document your function properly:

    /**
     * Description of what the function does.
     *
     * @param User|File $multiTypeArgument Description of the argument.
     *
     * @return string[] Description of the function's return value.
     */
    function myFunction($multiTypeArgument)
    {
    

    This will at least bring IDE support for autocompletion and static code analysis. Well enough when working on a private project, website, etc.

    When designing a public API (a PHP library, etc), sometimes you may want to be more defensive about API consumers' inputs.

    Then @tilz0R answer is the way to go:

    function log($message) {
        if (!is_string($message) && !$message instanceof Message) {
            throw new \InvalidArgumentException('$message must be a string or a Message object.');
        }
    
        // code ...
    }
    

    The day PHP (almost) had union types

    The 14th of February 2015, the Union Types PHP RFC was proposed for PHP 7.1. After discussion and vote, it's been rejected, 18 "no" against 11 "yes".

    If the RFC had been accepted, PHP would have had union types exactly the way you've shown (User|File).

    The RFC had some flaws, but the main reason for why it's been rejected is that the mainteners voters are quite resistive to change especially when it's about type strictness and other programming paradigms (ex. "why would we need type unions when the default takes all types of values" and "that's not good for performance").