generated from yiisoft/package-template
-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Alexander Makarov <sam@rmcreative.ru>
- Loading branch information
Showing
4 changed files
with
224 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
# Yii Hydrator Change Log | ||
|
||
## 1.0.1 under development | ||
## 1.1.0 under development | ||
|
||
- no changes in this release. | ||
- New #74: Add `NullTypeCaster` (@vjik) | ||
|
||
## 1.0.0 January 29, 2024 | ||
|
||
- Initial release. | ||
- Initial release. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Yiisoft\Hydrator\TypeCaster; | ||
|
||
use ReflectionNamedType; | ||
use ReflectionType; | ||
use ReflectionUnionType; | ||
use Yiisoft\Hydrator\Result; | ||
|
||
/** | ||
* Configurable type caster for casting value to `null`. | ||
*/ | ||
final class NullTypeCaster implements TypeCasterInterface | ||
{ | ||
public function __construct( | ||
private bool $null = true, | ||
private bool $emptyString = false, | ||
private bool $emptyArray = false, | ||
) { | ||
} | ||
|
||
public function cast(mixed $value, TypeCastContext $context): Result | ||
{ | ||
if (!$this->isAllowNull($context->getReflectionType())) { | ||
return Result::fail(); | ||
} | ||
|
||
if ( | ||
($this->null && $value === null) | ||
|| ($this->emptyString && $value === '') | ||
|| ($this->emptyArray && $value === []) | ||
) { | ||
return Result::success(null); | ||
} | ||
|
||
return Result::fail(); | ||
} | ||
|
||
private function isAllowNull(?ReflectionType $type): bool | ||
{ | ||
if ($type === null) { | ||
return true; | ||
} | ||
|
||
if ($type instanceof ReflectionNamedType) { | ||
return $type->allowsNull(); | ||
} | ||
|
||
if ($type instanceof ReflectionUnionType) { | ||
/** @psalm-suppress RedundantConditionGivenDocblockType Needed for PHP less than 8.2 */ | ||
foreach ($type->getTypes() as $subtype) { | ||
if ($subtype instanceof ReflectionNamedType && $type->allowsNull()) { | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace TypeCaster; | ||
|
||
use Closure; | ||
use PHPUnit\Framework\TestCase; | ||
use Yiisoft\Hydrator\Tests\Support\TestHelper; | ||
use Yiisoft\Hydrator\TypeCaster\NullTypeCaster; | ||
|
||
final class NullTypeCasterTest extends TestCase | ||
{ | ||
public function dataBase(): array | ||
{ | ||
return [ | ||
'default, null to non-type' => [ | ||
true, | ||
new NullTypeCaster(), | ||
null, | ||
fn($a) => null, | ||
], | ||
'default, null to ?int' => [ | ||
true, | ||
new NullTypeCaster(), | ||
null, | ||
fn(?int $a) => null, | ||
], | ||
'default, null to int' => [ | ||
false, | ||
new NullTypeCaster(), | ||
null, | ||
fn(int $a) => null, | ||
], | ||
'default, empty string to ?string' => [ | ||
false, | ||
new NullTypeCaster(), | ||
'', | ||
fn(?string $a) => null, | ||
], | ||
'default, empty array to ?array' => [ | ||
false, | ||
new NullTypeCaster(), | ||
[], | ||
fn(?array $a) => null, | ||
], | ||
'default, empty array to array|string|null' => [ | ||
false, | ||
new NullTypeCaster(), | ||
[], | ||
fn(array|string|null $a) => null, | ||
], | ||
'null=false, null to non-type' => [ | ||
false, | ||
new NullTypeCaster(null: false), | ||
null, | ||
fn($a) => null, | ||
], | ||
'null=false, null to ?int' => [ | ||
false, | ||
new NullTypeCaster(null: false), | ||
null, | ||
fn(?int $a) => null, | ||
], | ||
'null=false, null to int|string|null' => [ | ||
false, | ||
new NullTypeCaster(null: false), | ||
null, | ||
fn(int|string|null $a) => null, | ||
], | ||
'emptyString=true, empty string to non-type' => [ | ||
true, | ||
new NullTypeCaster(emptyString: true), | ||
'', | ||
fn($a) => null, | ||
], | ||
'emptyString=true, empty string to ?string' => [ | ||
true, | ||
new NullTypeCaster(emptyString: true), | ||
'', | ||
fn(?string $a) => null, | ||
], | ||
'emptyString=true, empty string to string|int|null' => [ | ||
true, | ||
new NullTypeCaster(emptyString: true), | ||
'', | ||
fn(string|int|null $a) => null, | ||
], | ||
'emptyString=true, empty string to string' => [ | ||
false, | ||
new NullTypeCaster(emptyString: true), | ||
'', | ||
fn(string $a) => null, | ||
], | ||
'emptyString=true, empty string to string|int' => [ | ||
false, | ||
new NullTypeCaster(emptyString: true), | ||
'', | ||
fn(string|int $a) => null, | ||
], | ||
'emptyArray=true, empty array to non-type' => [ | ||
true, | ||
new NullTypeCaster(emptyArray: true), | ||
[], | ||
fn($a) => null, | ||
], | ||
'emptyArray=true, empty array to ?array' => [ | ||
true, | ||
new NullTypeCaster(emptyArray: true), | ||
[], | ||
fn(?array $a) => null, | ||
], | ||
'emptyArray=true, empty array to array|string|null' => [ | ||
true, | ||
new NullTypeCaster(emptyArray: true), | ||
[], | ||
fn(array|string|null $a) => null, | ||
], | ||
'emptyArray=true, empty array to array' => [ | ||
false, | ||
new NullTypeCaster(emptyArray: true), | ||
[], | ||
fn(array $a) => null, | ||
], | ||
'emptyArray=true, empty array to array|string' => [ | ||
false, | ||
new NullTypeCaster(emptyArray: true), | ||
[], | ||
fn(array|string $a) => null, | ||
], | ||
]; | ||
} | ||
|
||
/** | ||
* @dataProvider dataBase | ||
*/ | ||
public function testBase(bool $success, NullTypeCaster $typeCaster, mixed $value, Closure $closure): void | ||
{ | ||
$context = TestHelper::createTypeCastContext($closure); | ||
|
||
$result = $typeCaster->cast($value, $context); | ||
|
||
$this->assertSame($success, $result->isResolved()); | ||
if ($success) { | ||
$this->assertNull($result->getValue()); | ||
} | ||
} | ||
|
||
public function testConstructor(): void | ||
{ | ||
$typeCaster = new NullTypeCaster(); | ||
$context = TestHelper::createTypeCastContext(fn($a) => null); | ||
|
||
$result = $typeCaster->cast('hello', $context); | ||
|
||
$this->assertSame(false, $result->isResolved()); | ||
} | ||
} |