First, some background: several versions of PHP ago authors of functions and methods could only restrict types to the array type or a class/interface type. We slowly added some other types such as callable and primitive types. These tools have been invaluable to those who care about restricting the types of their inputs and outputs. This type information reduces the code each author has to write if they want to restrict to working with certain types and it also provides a form of documentation. Overall these features have been well received and are considered a good thing to do. However, as we have added these new restrictions we still cannot express exactly what types are permitted in some common cases. 1. Return types cannot specify the function will return only type T or Null. 2. Parameter types cannot express that an iterable type is required. It's common for functions to work a stream of data and it is irrelevant if it is an array or a Traversable object. 3. Parameter types cannot express that that the parameter must implement two different interfaces. For example, requiring a parameter to implement both Countable and Traversable cannot be done. There are some common work-arounds to these issues: - Omit the type information and rely on documentation. - Check the parameter type inside the function (eg `assert(is_array($x) || $x instanceof Traversable)`) - Introduce a new type that embodies the restrictions. If this in an interface it must be implemented by every object you hope to use the restriction with. In some cases these work-arounds are tolerable. However, some of them are really painful. - Requiring a new supertype is intrusive. Code cannot always be changed to use the new supertype. For example: - Upstream projects that would not benefit from your changes. - Primitives cannot be extended except by altering the engine. In these cases a new type has to be introduced that proxies behavior to the underlying object/primitive. - Relying on documentation can lead to subtle and hard to diagnose issues when it is used incorrectly. Erroring immediately is sometimes preferable. All of these issues I've outlined can be nicely resolved by adding two new kinds of types to our system: union and intersection types. A union type requires the variable to match at least one of the types. An intersection type requires the variable to match all of the types. The vertical par symbol (OR) is used for unions and ampersand (AND) is used for intersections. For example: function (A | B $var); would require $var to be either type A or type B. function (A & B $var); would require $var to be type A and type B. To accommodate the common use-case of returning some type or Null we would need to formally allow `Null` as an explicit type: function (): T | Null; Since this is a common use case some languages have a short-hand notation to represent a union with Null:



