Skip to content

Commit

Permalink
Add 'resource (closed)' type check in IsType for closed resources
Browse files Browse the repository at this point in the history
  • Loading branch information
stemis authored and sebastianbergmann committed Jul 7, 2020
1 parent f971401 commit a30201d
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 31 deletions.
34 changes: 34 additions & 0 deletions src/Framework/Assert.php
Expand Up @@ -1511,6 +1511,23 @@ public static function assertIsResource($actual, string $message = ''): void
);
}

/**
* Asserts that a variable is of type resource and is closed.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert resource $actual
*/
public static function assertIsClosedResource($actual, string $message = ''): void
{
static::assertThat(
$actual,
new IsType(IsType::TYPE_CLOSED_RESOURCE),
$message
);
}

/**
* Asserts that a variable is of type string.
*
Expand Down Expand Up @@ -1698,6 +1715,23 @@ public static function assertIsNotResource($actual, string $message = ''): void
);
}

/**
* Asserts that a variable is not of type resource.
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !resource $actual
*/
public static function assertIsNotClosedResource($actual, string $message = ''): void
{
static::assertThat(
$actual,
new LogicalNot(new IsType(IsType::TYPE_CLOSED_RESOURCE)),
$message
);
}

/**
* Asserts that a variable is not of type string.
*
Expand Down
30 changes: 30 additions & 0 deletions src/Framework/Assert/Functions.php
Expand Up @@ -1295,6 +1295,21 @@ function assertIsResource($actual, string $message = ''): void
Assert::assertIsResource(...func_get_args());
}

/**
* Asserts that a variable is of type resource (closed).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert resource $actual
*
* @see Assert::assertIsClosedResource
*/
function assertIsClosedResource($actual, string $message = ''): void
{
Assert::assertIsClosedResource(...func_get_args());
}

/**
* Asserts that a variable is of type string.
*
Expand Down Expand Up @@ -1460,6 +1475,21 @@ function assertIsNotResource($actual, string $message = ''): void
Assert::assertIsNotResource(...func_get_args());
}

/**
* Asserts that a variable is not of type resource (closed).
*
* @throws ExpectationFailedException
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
*
* @psalm-assert !resource $actual
*
* @see Assert::assertIsNotClosedResource
*/
function assertIsNotClosedResource($actual, string $message = ''): void
{
Assert::assertIsNotClosedResource(...func_get_args());
}

/**
* Asserts that a variable is not of type string.
*
Expand Down
58 changes: 27 additions & 31 deletions src/Framework/Constraint/Type/IsType.php
Expand Up @@ -9,7 +9,7 @@
*/
namespace PHPUnit\Framework\Constraint;

use function get_resource_type;
use function gettype;
use function is_array;
use function is_bool;
use function is_callable;
Expand All @@ -18,11 +18,9 @@
use function is_iterable;
use function is_numeric;
use function is_object;
use function is_resource;
use function is_scalar;
use function is_string;
use function sprintf;
use TypeError;

/**
* Constraint that asserts that the value it is evaluated for is of a
Expand Down Expand Up @@ -72,6 +70,11 @@ final class IsType extends Constraint
*/
public const TYPE_RESOURCE = 'resource';

/**
* @var string
*/
public const TYPE_CLOSED_RESOURCE = 'resource (closed)';

/**
* @var string
*/
Expand All @@ -96,22 +99,23 @@ final class IsType extends Constraint
* @var array<string,bool>
*/
private const KNOWN_TYPES = [
'array' => true,
'boolean' => true,
'bool' => true,
'double' => true,
'float' => true,
'integer' => true,
'int' => true,
'null' => true,
'numeric' => true,
'object' => true,
'real' => true,
'resource' => true,
'string' => true,
'scalar' => true,
'callable' => true,
'iterable' => true,
'array' => true,
'boolean' => true,
'bool' => true,
'double' => true,
'float' => true,
'integer' => true,
'int' => true,
'null' => true,
'numeric' => true,
'object' => true,
'real' => true,
'resource' => true,
'resource (closed)' => true,
'string' => true,
'scalar' => true,
'callable' => true,
'iterable' => true,
];

/**
Expand Down Expand Up @@ -186,20 +190,12 @@ protected function matches($other): bool
return is_object($other);

case 'resource':
if (is_resource($other)) {
return true;
}

try {
$resource = @get_resource_type($other);
$type = gettype($other);

if (is_string($resource)) {
return true;
}
} catch (TypeError $e) {
}
return $type === 'resource' || $type === 'resource (closed)';

return false;
case 'resource (closed)':
return gettype($other) === 'resource (closed)';

case 'scalar':
return is_scalar($other);
Expand Down
24 changes: 24 additions & 0 deletions tests/static-analysis/happy-path/assert-is-closed-resource.php
@@ -0,0 +1,24 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsClosedResource;

use PHPUnit\Framework\Assert;

/**
* @param mixed $value
*
* @return resource
*/
function consume($value)
{
Assert::assertIsClosedResource($value);

return $value;
}
20 changes: 20 additions & 0 deletions tests/static-analysis/happy-path/assert-is-not-closed-resource.php
@@ -0,0 +1,20 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\StaticAnalysis\HappyPath\AssertIsNotClosedResource;

use PHPUnit\Framework\Assert;

/** @param int|resource $value */
function consume($value): int
{
Assert::assertIsNotClosedResource($value);

return $value;
}
40 changes: 40 additions & 0 deletions tests/unit/Framework/AssertTest.php
Expand Up @@ -16,6 +16,7 @@
use function acos;
use function array_merge;
use function chmod;
use function fclose;
use function file_get_contents;
use function fopen;
use function json_encode;
Expand Down Expand Up @@ -1980,6 +1981,23 @@ public function testResourceTypeCanBeAsserted(): void
$this->fail();
}

public function testClosedResourceTypeCanBeAsserted(): void
{
$resource = fopen(__FILE__, 'r');
fclose($resource);

$this->assertIsClosedResource($resource);
$this->assertIsResource($resource);

try {
$this->assertIsClosedResource(null);
} catch (AssertionFailedError $e) {
return;
}

$this->fail();
}

public function testStringTypeCanBeAsserted(): void
{
$this->assertIsString('');
Expand Down Expand Up @@ -2124,6 +2142,28 @@ public function testNotResourceTypeCanBeAsserted(): void
$this->fail();
}

public function testNotClosedResourceTypeCanBeAsserted(): void
{
$this->assertIsNotClosedResource(null);

$resource = fopen(__FILE__, 'r');
fclose($resource);

try {
$this->assertIsNotClosedResource($resource);
} catch (AssertionFailedError $e) {
return;
}

try {
$this->assertIsNotResource($resource);
} catch (AssertionFailedError $e) {
return;
}

$this->fail();
}

public function testNotScalarTypeCanBeAsserted(): void
{
$this->assertIsNotScalar(new stdClass);
Expand Down

0 comments on commit a30201d

Please sign in to comment.