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) { }
Python functions can return multiple values. These values can be stored in variables directly. A function is not restricted to return a variable, it can return zero, one, two or more values.
Type hints improve IDEs and linters. They make it much easier to statically reason about your code. Type hints help you build and maintain a cleaner architecture. The act of writing type hints forces you to think about the types in your program.
Unlike how types work in most other statically typed languages, type hints by themselves don't cause Python to enforce types.
Python's type hints provide you with optional static typing to leverage the best of both static and dynamic typing. Besides the str type, you can use other built-in types such as int , float , bool , and bytes for type hintings. To check the syntax for type hints, you need to use a static type checker tool.
The proposal has been voted 61 in favour to 5 against, and the implementation is ready to go.
There was a previous RFC proposing this, mentioned in another answer, but that one ended up being rejected.
It will work exactly as the example in your question:
class F { public function foo (File|Resource $f) : int|float { /** implement this**// } }
Which means that F::foo()
expects either a File
or a resource, and will return an int
or a float
.
A couple additional points:
Additionally, you can declare an union with null
. A|null
is equivalent to ?A
, but a more complex declaration as A|B|null
is also possible.
It's also possible use the false
type as part of a union-type declaration. E.g. int|false
. This is included mostly because of historical reasons, since some internal functions return false
on some types of error conditions. See strpos() as an example.
More modern functions should probably return null
or throw an exception in these situations, but this alternative is included to account for legacy code.
It is legal to add union types for parameter type hints (thus, making the function less restrictive), and to remove union types for return type hints (making the return type more specific).
Given the class F
from above, this is legal:
class G extends F { public function foo(File|Resource|string $f) : int { /** **/ } }
But this is not:
class H extends F { public function foo(File $f) : int|float|bool { /** **/ } }
Academically, this is called a type union.
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.
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 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").
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With