pull/33/head
commit
77fc5920b6
37 changed files with 1261 additions and 23 deletions
@ -0,0 +1,17 @@ |
||||
<?php |
||||
|
||||
class PrivateConstructorParent |
||||
{ |
||||
private function __construct() |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class PrivateConstructorChild extends PrivateConstructorParent |
||||
{ |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
new PrivateConstructorChild(); |
||||
} |
||||
@ -0,0 +1,6 @@ |
||||
<?php |
||||
|
||||
function main() |
||||
{ |
||||
new Closure(); |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
<?php |
||||
|
||||
namespace ConstructorVisibility; |
||||
|
||||
class Hidden |
||||
{ |
||||
private function __construct() |
||||
{ |
||||
} |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
new Hidden(); |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
<?php |
||||
|
||||
class TestClass |
||||
{ |
||||
private function __construct(){} |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
new TestClass; |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
<?php |
||||
|
||||
class TestClass |
||||
{ |
||||
protected function __construct(){} |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
new TestClass; |
||||
} |
||||
@ -0,0 +1,19 @@ |
||||
<?php |
||||
|
||||
class Base |
||||
{ |
||||
protected function __construct(){} |
||||
} |
||||
|
||||
class Other |
||||
{ |
||||
public static function make(): Base |
||||
{ |
||||
return new Base(); |
||||
} |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
Other::make(); |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
interface GeneratorReturnContract |
||||
{ |
||||
public function values(): \Generator; |
||||
} |
||||
|
||||
class GeneratorReturnImplementation implements GeneratorReturnContract |
||||
{ |
||||
public function values(): iterable |
||||
{ |
||||
yield 1; |
||||
} |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
<?php |
||||
|
||||
interface IntersectionLeft |
||||
{ |
||||
} |
||||
|
||||
interface IntersectionRight |
||||
{ |
||||
} |
||||
|
||||
interface IntersectionReturnParent |
||||
{ |
||||
public function value(): IntersectionLeft&IntersectionRight; |
||||
} |
||||
|
||||
class IntersectionReturnChild implements IntersectionReturnParent |
||||
{ |
||||
public function value(): IntersectionLeft |
||||
{ |
||||
return new class implements IntersectionLeft { |
||||
}; |
||||
} |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
abstract class NeverReturnParent |
||||
{ |
||||
abstract public function stop(): never; |
||||
} |
||||
|
||||
abstract class NeverReturnChild extends NeverReturnParent |
||||
{ |
||||
public function stop(): void |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,17 @@ |
||||
<?php |
||||
|
||||
class StaticReturnParent |
||||
{ |
||||
public function value(): static |
||||
{ |
||||
return $this; |
||||
} |
||||
} |
||||
|
||||
class StaticReturnChild extends StaticReturnParent |
||||
{ |
||||
public function value(): self |
||||
{ |
||||
return $this; |
||||
} |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
interface UnionReturnParent |
||||
{ |
||||
public function value(): int|string; |
||||
} |
||||
|
||||
class UnionReturnChild implements UnionReturnParent |
||||
{ |
||||
public function value(): bool |
||||
{ |
||||
return true; |
||||
} |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace NamespaceEndingComment; |
||||
|
||||
const VALUE = 42; |
||||
|
||||
function value(): int |
||||
{ |
||||
return VALUE; |
||||
} |
||||
|
||||
// unbracketed namespace trailing comment |
||||
@ -0,0 +1,39 @@ |
||||
<?php |
||||
|
||||
interface CovarianceLeft |
||||
{ |
||||
} |
||||
|
||||
interface CovarianceRight |
||||
{ |
||||
} |
||||
|
||||
class CovarianceBoth implements CovarianceLeft, CovarianceRight |
||||
{ |
||||
} |
||||
|
||||
interface IntersectionNarrowingContract |
||||
{ |
||||
public function intersection(): CovarianceLeft; |
||||
} |
||||
|
||||
class IntersectionNarrowingImpl implements IntersectionNarrowingContract |
||||
{ |
||||
public function intersection(): CovarianceLeft&CovarianceRight |
||||
{ |
||||
return new CovarianceBoth(); |
||||
} |
||||
} |
||||
|
||||
interface IntersectionContract |
||||
{ |
||||
public function concrete(): CovarianceLeft&CovarianceRight; |
||||
} |
||||
|
||||
class IntersectionImpl implements IntersectionContract |
||||
{ |
||||
public function concrete(): CovarianceBoth |
||||
{ |
||||
return new CovarianceBoth(); |
||||
} |
||||
} |
||||
@ -0,0 +1,29 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
trait TraitA |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
echo "A\n"; |
||||
} |
||||
} |
||||
|
||||
trait TraitB |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
echo "B\n"; |
||||
} |
||||
} |
||||
|
||||
class TestClass |
||||
{ |
||||
use TraitA, TraitB; |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
new TestClass(); |
||||
} |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
trait TestTrait |
||||
{ |
||||
private function __construct() |
||||
{ |
||||
echo "trait ctor\n"; |
||||
} |
||||
} |
||||
|
||||
class TestClass |
||||
{ |
||||
use TestTrait; |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
new TestClass(); |
||||
} |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
trait TestTrait |
||||
{ |
||||
protected function __construct() |
||||
{ |
||||
echo "trait ctor\n"; |
||||
} |
||||
} |
||||
|
||||
class TestClass |
||||
{ |
||||
use TestTrait; |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
new TestClass(); |
||||
} |
||||
@ -0,0 +1,78 @@ |
||||
<?php |
||||
|
||||
use TypePhp\Exception\TestError; |
||||
|
||||
class ConstructorVisibilityTest extends BaseTest |
||||
{ |
||||
/** |
||||
* 编译期错误在转换阶段直接以 TestError 抛出,而在桩文件生成阶段 |
||||
* (gen_stub.php) 会被包成 RuntimeException,这里两者都要捕获。 |
||||
*/ |
||||
protected function exec(string $expected, string $file): void |
||||
{ |
||||
try { |
||||
$this->compile($file); |
||||
} catch (TestError|RuntimeException $exception) { |
||||
$this->assertStringContainsString($expected, $exception->getMessage()); |
||||
return; |
||||
} |
||||
$this->fail('Expected compile-time error was not thrown'); |
||||
} |
||||
|
||||
public function testPrivateConstructorCannotBeCalledFromOutside(): void |
||||
{ |
||||
// 私有构造器不能从类外部通过 `new` 调用 |
||||
$this->exec('Cannot call private TestClass::__construct()', 'constructor_visibility_private.php'); |
||||
} |
||||
|
||||
public function testProtectedConstructorCannotBeCalledFromGlobalScope(): void |
||||
{ |
||||
// 保护构造器不能从全局作用域调用 |
||||
$this->exec('Cannot call protected TestClass::__construct()', 'constructor_visibility_protected.php'); |
||||
} |
||||
|
||||
public function testProtectedConstructorCannotBeCalledFromNonSubclass(): void |
||||
{ |
||||
// 保护构造器不能从非子类的其它类内部调用 |
||||
$this->exec('Cannot call protected Base::__construct()', 'constructor_visibility_protected_foreign_class.php'); |
||||
} |
||||
|
||||
public function testInheritedPrivateConstructorCannotBeCalledFromChildScope(): void |
||||
{ |
||||
$this->exec( |
||||
'Cannot call private PrivateConstructorParent::__construct()', |
||||
'constructor_visibility_inherited_private.php' |
||||
); |
||||
} |
||||
|
||||
public function testInternalPrivateConstructorCannotBeCalled(): void |
||||
{ |
||||
$this->exec('Cannot call private Closure::__construct()', 'constructor_visibility_internal_private.php'); |
||||
} |
||||
|
||||
public function testNamespacedConstructorUsesPhpClassNameInDiagnostic(): void |
||||
{ |
||||
$this->exec( |
||||
'Cannot call private ConstructorVisibility\Hidden::__construct()', |
||||
'constructor_visibility_namespaced.php' |
||||
); |
||||
} |
||||
|
||||
public function testTraitPrivateConstructorCannotBeCalledFromGlobalScope(): void |
||||
{ |
||||
// trait 提供的私有构造器扁平化后等价于类的私有构造器 |
||||
$this->exec('Cannot call private TestClass::__construct()', 'trait_constructor_private.php'); |
||||
} |
||||
|
||||
public function testTraitProtectedConstructorCannotBeCalledFromGlobalScope(): void |
||||
{ |
||||
// trait 提供的保护构造器扁平化后等价于类的保护构造器 |
||||
$this->exec('Cannot call protected TestClass::__construct()', 'trait_constructor_protected.php'); |
||||
} |
||||
|
||||
public function testConflictingTraitConstructorMustBeResolved(): void |
||||
{ |
||||
// 两个 trait 各自声明 __construct 时必须显式解决冲突 |
||||
$this->exec('Trait `TraitB` method `__construct` already exists', 'trait_constructor_conflict.php'); |
||||
} |
||||
} |
||||
@ -0,0 +1,47 @@ |
||||
--TEST-- |
||||
Interface return type covariance with nullable interface and anonymous class |
||||
--FILE-- |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
interface TestInterface1 |
||||
{ |
||||
} |
||||
|
||||
interface TestInterface2 extends TestInterface1 |
||||
{ |
||||
} |
||||
|
||||
interface TestInterface3 |
||||
{ |
||||
public function test(): ?TestInterface1; |
||||
} |
||||
|
||||
class TestClass implements TestInterface3 |
||||
{ |
||||
// Covariant: ?TestInterface2 is a subtype of ?TestInterface1 because |
||||
// TestInterface2 extends TestInterface1. |
||||
public function test(): ?TestInterface2 |
||||
{ |
||||
return new class() implements TestInterface2 { |
||||
public function hello(): string { |
||||
return "anon"; |
||||
} |
||||
}; |
||||
} |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
$test = new TestClass; |
||||
$result = $test->test(); |
||||
var_dump($result instanceof TestInterface1); |
||||
var_dump($result instanceof TestInterface2); |
||||
var_dump($result === null); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
bool(true) |
||||
bool(true) |
||||
bool(false) |
||||
@ -0,0 +1,57 @@ |
||||
--TEST-- |
||||
generator re-yielding array elements via foreach with \Generator return type |
||||
--FILE-- |
||||
<?php |
||||
|
||||
function main() |
||||
{ |
||||
$g = test([1, 2, 3]); |
||||
var_dump($g); |
||||
foreach ($g as $value) |
||||
{ |
||||
var_dump($value); |
||||
} |
||||
} |
||||
|
||||
function test(array $array): \Generator |
||||
{ |
||||
foreach ($array as $value) |
||||
{ |
||||
yield $value; |
||||
} |
||||
} |
||||
|
||||
// main(); |
||||
?> |
||||
--EXPECTF-- |
||||
object(FiberGenerator)#%d (9) { |
||||
["callback":"FiberGenerator":private]=> |
||||
object(Closure)#%d (2) { |
||||
["function"]=> |
||||
string(19) "stdClass::{closure}" |
||||
["this"]=> |
||||
object(stdClass)#%d (1) { |
||||
["box"]=> |
||||
resource(%d) of type (php::box) |
||||
} |
||||
} |
||||
["fiber":"FiberGenerator":private]=> |
||||
NULL |
||||
["current":"FiberGenerator":private]=> |
||||
NULL |
||||
["key":"FiberGenerator":private]=> |
||||
NULL |
||||
["valid":"FiberGenerator":private]=> |
||||
bool(false) |
||||
["state":"FiberGenerator":private]=> |
||||
int(0) |
||||
["yield_count":"FiberGenerator":private]=> |
||||
int(0) |
||||
["next_index":"FiberGenerator":private]=> |
||||
int(0) |
||||
["return_value":"FiberGenerator":private]=> |
||||
NULL |
||||
} |
||||
int(1) |
||||
int(2) |
||||
int(3) |
||||
@ -0,0 +1,53 @@ |
||||
--TEST-- |
||||
generator return type accepts \Generator for methods, nullable and union variants |
||||
--FILE-- |
||||
<?php |
||||
|
||||
class Box |
||||
{ |
||||
public function gen(array $array): \Generator |
||||
{ |
||||
foreach ($array as $value) { |
||||
yield $value * 2; |
||||
} |
||||
} |
||||
} |
||||
|
||||
function nullableGen(array $array): ?\Generator |
||||
{ |
||||
foreach ($array as $value) { |
||||
yield $value; |
||||
} |
||||
} |
||||
|
||||
function unionGen(array $array): \Generator|\Iterator |
||||
{ |
||||
foreach ($array as $value) { |
||||
yield $value; |
||||
} |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
$b = new Box(); |
||||
foreach ($b->gen([1, 2, 3]) as $v) { |
||||
var_dump($v); |
||||
} |
||||
$g = nullableGen([4, 5]); |
||||
foreach ($g as $v) { |
||||
var_dump($v); |
||||
} |
||||
$u = unionGen([6, 7]); |
||||
foreach ($u as $v) { |
||||
var_dump($v); |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
int(2) |
||||
int(4) |
||||
int(6) |
||||
int(4) |
||||
int(5) |
||||
int(6) |
||||
int(7) |
||||
@ -0,0 +1,96 @@ |
||||
--TEST-- |
||||
generator methods implementing interfaces with iterable, nullable and union return types |
||||
--FILE-- |
||||
<?php |
||||
interface GenInterface |
||||
{ |
||||
public function gen(array $array): \Generator; |
||||
} |
||||
|
||||
interface IterableInterface |
||||
{ |
||||
public function it(array $array): iterable; |
||||
|
||||
public function narrowed(array $array): iterable; |
||||
} |
||||
|
||||
interface NullableInterface |
||||
{ |
||||
public function nullable(array $array): ?\Generator; |
||||
} |
||||
|
||||
interface UnionInterface |
||||
{ |
||||
public function union(array $array): \Generator|\Iterator; |
||||
} |
||||
|
||||
class Box implements GenInterface, IterableInterface, NullableInterface, UnionInterface |
||||
{ |
||||
public function gen(array $array): \Generator |
||||
{ |
||||
foreach ($array as $value) { |
||||
yield $value * 2; |
||||
} |
||||
} |
||||
|
||||
public function it(array $array): iterable |
||||
{ |
||||
foreach ($array as $value) { |
||||
yield $value; |
||||
} |
||||
} |
||||
|
||||
public function narrowed(array $array): \Generator |
||||
{ |
||||
foreach ($array as $value) { |
||||
yield $value; |
||||
} |
||||
} |
||||
|
||||
public function nullable(array $array): ?\Generator |
||||
{ |
||||
foreach ($array as $value) { |
||||
yield $value; |
||||
} |
||||
} |
||||
|
||||
public function union(array $array): \Generator|\Iterator |
||||
{ |
||||
foreach ($array as $value) { |
||||
yield $value; |
||||
} |
||||
} |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
$box = new Box(); |
||||
foreach ($box->gen([1, 2, 3]) as $v) { |
||||
var_dump($v); |
||||
} |
||||
foreach ($box->it([4, 5]) as $v) { |
||||
var_dump($v); |
||||
} |
||||
foreach ($box->narrowed([10, 11]) as $v) { |
||||
var_dump($v); |
||||
} |
||||
foreach ($box->nullable([6, 7]) as $v) { |
||||
var_dump($v); |
||||
} |
||||
foreach ($box->union([8, 9]) as $v) { |
||||
var_dump($v); |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
int(2) |
||||
int(4) |
||||
int(6) |
||||
int(4) |
||||
int(5) |
||||
int(10) |
||||
int(11) |
||||
int(6) |
||||
int(7) |
||||
int(8) |
||||
int(9) |
||||
@ -0,0 +1,38 @@ |
||||
--TEST-- |
||||
generator method implementing an interface that declares \Generator return type |
||||
--FILE-- |
||||
<?php |
||||
interface T |
||||
{ |
||||
public function test(array $array): \Generator; |
||||
} |
||||
|
||||
class TestClass implements T |
||||
{ |
||||
public function test(array $array): \Generator |
||||
{ |
||||
foreach ($array as $value) { |
||||
yield $value; |
||||
} |
||||
} |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
$test = new TestClass; |
||||
$g = $test->test([1, 2, 3]); |
||||
// TypePHP generators return a \FiberGenerator which implements Iterator |
||||
// but is NOT the Zend \Generator class. |
||||
var_dump($g instanceof \Generator); |
||||
var_dump($g instanceof \Iterator); |
||||
foreach ($g as $value) { |
||||
var_dump($value); |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
bool(false) |
||||
bool(true) |
||||
int(1) |
||||
int(2) |
||||
int(3) |
||||
@ -0,0 +1,22 @@ |
||||
--TEST-- |
||||
A namespace block ending with a comment must not be treated as stray code |
||||
--FILE-- |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace Test { |
||||
/* named namespace trailing block comment */ |
||||
} |
||||
|
||||
namespace { |
||||
function main() |
||||
{ |
||||
var_dump('done'); |
||||
} |
||||
|
||||
// global namespace trailing line comment |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(4) "done" |
||||
@ -0,0 +1,26 @@ |
||||
--TEST-- |
||||
Constructor visibility - protected constructor accessible from subclass |
||||
--FILE-- |
||||
<?php |
||||
|
||||
class Base |
||||
{ |
||||
protected function __construct(){} |
||||
} |
||||
|
||||
class Sub extends Base |
||||
{ |
||||
public static function make(): Base |
||||
{ |
||||
return new Base(); |
||||
} |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
$obj = Sub::make(); |
||||
var_dump($obj instanceof Base); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
bool(true) |
||||
@ -0,0 +1,27 @@ |
||||
--TEST-- |
||||
Trait __construct is used by the composing class |
||||
--FILE-- |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
trait TestTrait |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
echo "trait ctor\n"; |
||||
} |
||||
} |
||||
|
||||
class TestClass |
||||
{ |
||||
use TestTrait; |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
new TestClass(); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
trait ctor |
||||
@ -0,0 +1,32 @@ |
||||
--TEST-- |
||||
Class __construct overrides the one provided by a trait |
||||
--FILE-- |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
trait TestTrait |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
echo "trait ctor\n"; |
||||
} |
||||
} |
||||
|
||||
class TestClass |
||||
{ |
||||
use TestTrait; |
||||
|
||||
public function __construct() |
||||
{ |
||||
echo "class ctor\n"; |
||||
} |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
new TestClass(); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
class ctor |
||||
@ -0,0 +1,37 @@ |
||||
--TEST-- |
||||
Trait protected __construct is accessible from a subclass |
||||
--FILE-- |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
trait TestTrait |
||||
{ |
||||
protected function __construct() |
||||
{ |
||||
echo "base ctor\n"; |
||||
} |
||||
} |
||||
|
||||
class BaseClass |
||||
{ |
||||
use TestTrait; |
||||
} |
||||
|
||||
class SubClass extends BaseClass |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
new BaseClass(); |
||||
echo "sub ctor\n"; |
||||
} |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
new SubClass(); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
base ctor |
||||
sub ctor |
||||
@ -0,0 +1,30 @@ |
||||
--TEST-- |
||||
Trait __construct with arguments and $this property access |
||||
--FILE-- |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
trait TestTrait |
||||
{ |
||||
private int $value = 0; |
||||
|
||||
public function __construct(int $value) |
||||
{ |
||||
$this->value = $value; |
||||
echo "value=" . $this->value . "\n"; |
||||
} |
||||
} |
||||
|
||||
class TestClass |
||||
{ |
||||
use TestTrait; |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
new TestClass(42); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
value=42 |
||||
@ -0,0 +1,120 @@ |
||||
--TEST-- |
||||
Return type covariance: union narrowing and object subtype |
||||
--FILE-- |
||||
<?php |
||||
|
||||
interface UnionReturnContract |
||||
{ |
||||
public function make(): int|string; |
||||
} |
||||
|
||||
class UnionReturnImpl implements UnionReturnContract |
||||
{ |
||||
// Covariant: narrowing a union return type (int|string -> int) is allowed. |
||||
public function make(): int |
||||
{ |
||||
return 42; |
||||
} |
||||
} |
||||
|
||||
class BaseType {} |
||||
class ChildType extends BaseType {} |
||||
|
||||
interface ObjectReturnContract |
||||
{ |
||||
public function build(): BaseType; |
||||
} |
||||
|
||||
class ObjectReturnImpl implements ObjectReturnContract |
||||
{ |
||||
// Covariant: returning a subtype (ChildType) for a BaseType return is allowed. |
||||
public function build(): ChildType |
||||
{ |
||||
return new ChildType(); |
||||
} |
||||
} |
||||
|
||||
class StaticBase |
||||
{ |
||||
public function copy(): ?self |
||||
{ |
||||
return $this; |
||||
} |
||||
} |
||||
|
||||
class StaticChild extends StaticBase |
||||
{ |
||||
public function copy(): ?static |
||||
{ |
||||
return $this; |
||||
} |
||||
} |
||||
|
||||
interface IterableContract |
||||
{ |
||||
public function values(): iterable; |
||||
} |
||||
|
||||
class IterableImpl implements IterableContract |
||||
{ |
||||
public function values(): array |
||||
{ |
||||
return [1, 2]; |
||||
} |
||||
} |
||||
|
||||
interface BoolContract |
||||
{ |
||||
public function enabled(): bool; |
||||
} |
||||
|
||||
class LiteralBoolImpl implements BoolContract |
||||
{ |
||||
public function enabled(): true |
||||
{ |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
abstract class VoidContract |
||||
{ |
||||
abstract public function stop(): void; |
||||
} |
||||
|
||||
abstract class NeverImpl extends VoidContract |
||||
{ |
||||
public function stop(): never |
||||
{ |
||||
throw new RuntimeException('stop'); |
||||
} |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
$impl = new UnionReturnImpl(); |
||||
var_dump($impl->make()); |
||||
|
||||
$obj = new ObjectReturnImpl(); |
||||
$built = $obj->build(); |
||||
var_dump($built instanceof BaseType); |
||||
var_dump($built instanceof ChildType); |
||||
|
||||
$static = new StaticChild(); |
||||
var_dump($static->copy() instanceof StaticChild); |
||||
|
||||
var_dump((new IterableImpl())->values()); |
||||
var_dump((new LiteralBoolImpl())->enabled()); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
int(42) |
||||
bool(true) |
||||
bool(true) |
||||
bool(true) |
||||
array(2) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
int(2) |
||||
} |
||||
bool(true) |
||||
Loading…
Reference in new issue