Skip to content

[RFC] Union types #1887

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Zend/tests/multi/001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
union test param array or Traversable
--FILE--
<?php
$cb = function(array | Traversable $thing) {
return $thing;
};

var_dump($cb([]), $cb(new ArrayObject));
?>
--EXPECT--
array(0) {
}
object(ArrayObject)#2 (1) {
["storage":"ArrayObject":private]=>
array(0) {
}
}
18 changes: 18 additions & 0 deletions Zend/tests/multi/002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
union test return array or Traversable
--FILE--
<?php
$cb = function($i) : array | Traversable {
return $i == 0 ? [] : new ArrayObject;
};

var_dump($cb(0), $cb(1));
?>
--EXPECT--
array(0) {
}
object(ArrayObject)#2 (1) {
["storage":"ArrayObject":private]=>
array(0) {
}
}
39 changes: 39 additions & 0 deletions Zend/tests/multi/003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
--TEST--
intersection test
--FILE--
<?php
interface IFoo {
public function iFoo();
}

interface IBar {
public function iBar();
}

class Foo implements IFoo {
public function iFoo() {}
}

class Bar implements IFoo, IBar {
public function iFoo() {}
public function iBar() {}
}

/* not sure if string should be check first? */
$cb = function(IFoo & IBar $arg) {
return $arg;
};

var_dump($cb(new Bar));

$cb(new Foo);
?>
--EXPECTF--
object(Bar)#2 (0) {
}

Fatal error: Uncaught TypeError: Argument 1 passed to {closure}() must be IFoo and IBar, instance of Foo given, called in %s on line 26 and defined in %s:20
Stack trace:
#0 %s(26): {closure}(Object(Foo))
#1 {main}
thrown in %s on line 20
22 changes: 22 additions & 0 deletions Zend/tests/multi/004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
union of simple types
--FILE--
<?php
$cb = function(callable | array $arg) {
return $arg;
};

var_dump($cb($cb), $cb(["just an array"]));
?>
--EXPECT--
object(Closure)#1 (1) {
["parameter"]=>
array(1) {
["$arg"]=>
string(10) "<required>"
}
}
array(1) {
[0]=>
string(13) "just an array"
}
8 changes: 8 additions & 0 deletions Zend/tests/multi/005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
multi type mix
--FILE--
<?php
function(callable | array & Foo $arg) {};
?>
--EXPECTF--
Fatal error: Cannot use intersection when creating union type in %s on line 2
8 changes: 8 additions & 0 deletions Zend/tests/multi/006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
multi type repeat
--FILE--
<?php
function(callable | callable $arg) {};
?>
--EXPECTF--
Fatal error: callable is already present in union in %s on line 2
8 changes: 8 additions & 0 deletions Zend/tests/multi/007.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
multi type repeat complex
--FILE--
<?php
function(Foo | Foo $arg) {};
?>
--EXPECTF--
Fatal error: Foo is already present in union in %s on line 2
14 changes: 14 additions & 0 deletions Zend/tests/multi/008.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
basic union test inheritance
--FILE--
<?php
class Foo {
public function method(array | Traversable $arg) {}
}

class Bar extends Foo {
public function method($arg) {}
}
?>
--EXPECTF--
Warning: Declaration of Bar::method($arg) should be compatible with Foo::method(Traversable | array $arg) in %s on line 8
14 changes: 14 additions & 0 deletions Zend/tests/multi/009.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
basic union test inheritance with class
--FILE--
<?php
class Foo {
public function method(IFoo & IBar $arg) {}
}

class Bar extends Foo {
public function method($arg) {}
}
?>
--EXPECTF--
Warning: Declaration of Bar::method($arg) should be compatible with Foo::method(IFoo & IBar $arg) in %s on line 8
14 changes: 14 additions & 0 deletions Zend/tests/multi/010.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
basic union test inheritance with class
--FILE--
<?php
class Foo {
public function method() : IFoo & IBar {}
}

class Bar extends Foo {
public function method() {}
}
?>
--EXPECTF--
Fatal error: Declaration of Bar::method() must be compatible with Foo::method(): IFoo & IBar in %s on line 8
11 changes: 11 additions & 0 deletions Zend/tests/multi/011.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
basic union implicit nullability allowed on params
--FILE--
<?php
$foo = function(Foo|Bar $foo = null) {
var_dump($foo);
};
$foo(null);
?>
--EXPECT--
NULL
10 changes: 10 additions & 0 deletions Zend/tests/multi/012.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
basic union disallow void
--FILE--
<?php
function(Foo | void $throw) {};
?>
--EXPECTF--
Fatal error: Void is not a valid parameter type in %s on line 2


25 changes: 25 additions & 0 deletions Zend/tests/multi/013.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
basic reflection
--FILE--
<?php
$function = function(IFoo | IBar $thing) : IFoo & IBar {

};

$reflector = new ReflectionFunction($function);
$param =
$reflector->getParameters()[0]->getType();
$return =
$reflector->getReturnType();

var_dump((string)$param,
(string)$return);

var_dump($param->isUnion(),
$return->isIntersection());
?>
--EXPECT--
string(12) "IFoo or IBar"
string(13) "IFoo and IBar"
bool(true)
bool(true)
8 changes: 8 additions & 0 deletions Zend/tests/multi/014.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
union default values (fail)
--FILE--
<?php
function (IFoo | IBar $thing = 1) {};
?>
--EXPECTF--
Fatal error: Default type integer does not match allowed types null for parameter 1 in %s on line 2
10 changes: 10 additions & 0 deletions Zend/tests/multi/015.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
union default values (pass)
--FILE--
<?php
function (IFoo | IBar $thing = null) {};

echo "ok";
?>
--EXPECT--
ok
8 changes: 8 additions & 0 deletions Zend/tests/multi/016.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
intersection default values (fail)
--FILE--
<?php
function (IFoo & IBar $thing = 1) {};
?>
--EXPECTF--
Fatal error: Default value for intersection types can only be NULL in %s on line 2
10 changes: 10 additions & 0 deletions Zend/tests/multi/017.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
intersection default values (pass)
--FILE--
<?php
function (IFoo & IBar $thing = null) {};

echo "ok";
?>
--EXPECT--
ok
10 changes: 10 additions & 0 deletions Zend/tests/multi/018.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
basic union disallow void
--FILE--
<?php
function() : Foo | void {};
?>
--EXPECTF--
Fatal error: Void is not a valid parameter type in %s on line 2


10 changes: 10 additions & 0 deletions Zend/tests/multi/019.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
union disallow generator
--FILE--
<?php
function () : Generator | stdClass {
yield $thing;
};
?>
--EXPECTF--
Fatal error: Generators may only declare a return type of Generator, Iterator or Traversable, unions are not permitted in %s on line 3
10 changes: 10 additions & 0 deletions Zend/tests/multi/020.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
intersection disallow generator
--FILE--
<?php
function () : Generator & stdClass {
yield $thing;
};
?>
--EXPECTF--
Fatal error: Generators may only declare a return type of Generator, Iterator or Traversable, intersections are not permitted in %s on line 3
35 changes: 35 additions & 0 deletions Zend/tests/multi/021.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--TEST--
union test with classes
--FILE--
<?php

class A {}
class B {}
class C {}
class CC extends C {}

function foo(B|C $foo): B|C {
var_dump($foo);
return $foo;
}

foo(new CC);
foo(new C);
foo(new B);
foo(new A);

?>
--EXPECTF--
object(CC)#1 (0) {
}
object(C)#1 (0) {
}
object(B)#1 (0) {
}

Fatal error: Uncaught TypeError: Argument 1 passed to foo() must be B or C, instance of A given, called in %s on line %d and defined in %s:%d
Stack trace:
#0 %s(%d): foo(Object(A))
#1 {main}
thrown in %s on line %d

10 changes: 10 additions & 0 deletions Zend/tests/multi/022.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
Unions between array, objects and strings must fail
--FILE--
<?php

function foo(array & string & stdClass $foo) {}

?>
--EXPECTF--
Fatal error: Cannot require parameters to be stdClass, string and array at the same time in intersection types in %s on line %d
34 changes: 34 additions & 0 deletions Zend/tests/multi/scalar_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--TEST--
Scalar type conversion: int|float|string
--FILE--
<?php

function foo(int|float|string $foo) {
var_dump($foo);
}

foo(1);
foo(1.0);
foo(1.1);
foo("abc");
foo(true);
foo(false);
foo(new class { function __toString() { return "1"; } });
foo(null);

?>
--EXPECTF--
int(1)
float(1)
float(1.1)
string(3) "abc"
int(1)
int(0)
string(1) "1"

Fatal error: Uncaught TypeError: Argument 1 passed to foo() must be integer, float or string, null given, called in %s on line %d and defined in %s:%d
Stack trace:
#0 %s(%d): foo(NULL)
#1 {main}
thrown in %s on line %d

Loading