Skip to content

Commit

Permalink
feat: add support for DNF types on PHP 8.2
Browse files Browse the repository at this point in the history
  • Loading branch information
nhedger committed Sep 16, 2022
1 parent caf9315 commit 6c421c4
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 8 deletions.
20 changes: 12 additions & 8 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ function _checkTypehint(callable $callback, $object)

// Extract the type of the argument and handle different possibilities
$type = $expectedException->getType();

$isTypeUnion = true;
$types = [];

Expand All @@ -379,14 +379,18 @@ function _checkTypehint(callable $callback, $object)
}

foreach ($types as $type) {
if (!$type instanceof \ReflectionNamedType) {
throw new \LogicException('This implementation does not support groups of intersection or union types');
}

// A named-type can be either a class-name or a built-in type like string, int, array, etc.
$matches = ($type->isBuiltin() && \gettype($object) === $type->getName())
|| (new \ReflectionClass($type->getName()))->isInstance($object);

if ($type instanceof \ReflectionIntersectionType) {
foreach ($type->getTypes() as $typeToMatch) {
if (!($matches = ($typeToMatch->isBuiltin() && \gettype($object) === $typeToMatch->getName())
|| (new \ReflectionClass($typeToMatch->getName()))->isInstance($object))) {
break;
}
}
} else {
$matches = ($type->isBuiltin() && \gettype($object) === $type->getName())
|| (new \ReflectionClass($type->getName()))->isInstance($object);
}

// If we look for a single match (union), we can return early on match
// If we look for a full match (intersection), we can return early on mismatch
Expand Down
33 changes: 33 additions & 0 deletions tests/FunctionCheckTypehintTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,39 @@ public function shouldAcceptStaticClassCallbackWithIntersectionTypehint()
self::assertTrue(_checkTypehint(['React\Promise\CallbackWithIntersectionTypehintClass', 'testCallbackStatic'], new CountableException()));
}

/**
* @test
* @requires PHP 8.2
*/
public function shouldAcceptInvokableObjectCallbackWithDNFTypehint()
{
self::assertFalse(_checkTypehint(new CallbackWithDNFTypehintClass(), new \RuntimeException()));
self::assertTrue(_checkTypehint(new CallbackWithDNFTypehintClass(), new ArrayAccessibleException()));
self::assertTrue(_checkTypehint(new CallbackWithDNFTypehintClass(), new CountableException()));
}

/**
* @test
* @requires PHP 8.2
*/
public function shouldAcceptObjectMethodCallbackWithDNFTypehint()
{
self::assertFalse(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new \RuntimeException()));
self::assertTrue(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new CountableException()));
self::assertTrue(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new ArrayAccessibleException()));
}

/**
* @test
* @requires PHP 8.2
*/
public function shouldAcceptStaticClassCallbackWithDNFTypehint()
{
self::assertFalse(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new \RuntimeException()));
self::assertTrue(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new CountableException()));
self::assertTrue(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new ArrayAccessibleException()));
}

/** @test */
public function shouldAcceptClosureCallbackWithoutTypehint()
{
Expand Down
22 changes: 22 additions & 0 deletions tests/fixtures/ArrayAccessibleException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace React\Promise;

use RuntimeException;

class ArrayAccessibleException extends RuntimeException implements \ArrayAccess
{
public function offsetExists(mixed $offset): bool
{
return true;
}

public function offsetGet(mixed $offset): mixed
{
return $offset;
}

public function offsetSet(mixed $offset, mixed $value): void {}

public function offsetUnset(mixed $offset): void {}
}
21 changes: 21 additions & 0 deletions tests/fixtures/CallbackWithDNFTypehintClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace React\Promise;

use Countable;
use RuntimeException;

class CallbackWithDNFTypehintClass
{
public function __invoke((RuntimeException&Countable)|(RuntimeException&\ArrayAccess) $e)
{
}

public function testCallback((RuntimeException&Countable)|(RuntimeException&\ArrayAccess) $e)
{
}

public static function testCallbackStatic((RuntimeException&Countable)|(RuntimeException&\ArrayAccess) $e)
{
}
}

0 comments on commit 6c421c4

Please sign in to comment.