Skip to content
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

Handle unsupported types without failing #5

Merged
merged 1 commit into from
Nov 27, 2024
Merged
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
11 changes: 4 additions & 7 deletions src/Guesser/StubAsGuesser/StubAsBackedEnumGuesser.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,20 @@
use Ingenerator\StubObjects\Attribute\StubAs;
use Ingenerator\StubObjects\Attribute\StubAs\StubAsBackedEnum;
use Ingenerator\StubObjects\Guesser\StubAsGuesser;
use Ingenerator\StubObjects\ReflectionUtils;
use Ingenerator\StubObjects\StubbingContext;
use ReflectionProperty;

class StubAsBackedEnumGuesser implements StubAsGuesser
{
public function guessCaster(ReflectionProperty $property, StubbingContext $context): false|StubAs
{
if ( ! $property->getType()) {
if (ReflectionUtils::isBuiltinType($property)) {
return FALSE;
}

if ($property->getType()->isBuiltin()) {
return FALSE;
}

$type = $property->getType()->getName();
if (is_a($type, BackedEnum::class, TRUE)) {
$type = ReflectionUtils::getTypeNameIfAvailable($property);
if ($type && is_a($type, BackedEnum::class, TRUE)) {
return new StubAsBackedEnum($type);
}

Expand Down
4 changes: 2 additions & 2 deletions src/Guesser/StubAsGuesser/StubAsCollectionGuesser.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
use Ingenerator\StubObjects\Attribute\StubAs;
use Ingenerator\StubObjects\Attribute\StubAs\StubAsCollection;
use Ingenerator\StubObjects\Guesser\StubAsGuesser;
use Ingenerator\StubObjects\ReflectionUtils;
use Ingenerator\StubObjects\StubbingContext;
use ReflectionProperty;

class StubAsCollectionGuesser implements StubAsGuesser
{
public function guessCaster(ReflectionProperty $property, StubbingContext $context): false|StubAs
{
// @todo: test with untyped props
if ($property->getType()?->getName() === Collection::class) {
if (ReflectionUtils::getTypeNameIfAvailable($property) === Collection::class) {
$collected_class = $this->parseItemClass($property);

return new StubAsCollection($property->getType()->getName(), $collected_class);
Expand Down
4 changes: 2 additions & 2 deletions src/Guesser/StubAsGuesser/StubAsDateTimeGuesser.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
use Ingenerator\StubObjects\Attribute\StubAs;
use Ingenerator\StubObjects\Attribute\StubAs\StubAsDateTime;
use Ingenerator\StubObjects\Guesser\StubAsGuesser;
use Ingenerator\StubObjects\ReflectionUtils;
use Ingenerator\StubObjects\StubbingContext;
use ReflectionProperty;

class StubAsDateTimeGuesser implements StubAsGuesser
{
public function guessCaster(ReflectionProperty $property, StubbingContext $context): false|StubAs
{
// @todo test with untyped props
if ($property->getType()?->getName() === DateTimeImmutable::class) {
if (ReflectionUtils::getTypeNameIfAvailable($property) === DateTimeImmutable::class) {
return new StubAsDateTime();
}

Expand Down
6 changes: 3 additions & 3 deletions src/Guesser/StubAsGuesser/StubAsStubObjectGuesser.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@
use Ingenerator\StubObjects\Attribute\StubAs;
use Ingenerator\StubObjects\Attribute\StubAs\StubAsStubObject;
use Ingenerator\StubObjects\Guesser\StubAsGuesser;
use Ingenerator\StubObjects\ReflectionUtils;
use Ingenerator\StubObjects\StubbingContext;
use ReflectionProperty;

class StubAsStubObjectGuesser implements StubAsGuesser
{
public function guessCaster(ReflectionProperty $property, StubbingContext $context): false|StubAs
{
// @todo: test with untyped props
if ($property->getType()?->isBuiltin()) {
if (ReflectionUtils::isBuiltinType($property)) {
return FALSE;
}

$class = $property->getType()?->getName();
$class = ReflectionUtils::getTypeNameIfAvailable($property);
if ($class && $context->isStubbable($class)) {
return new StubAsStubObject($class);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
use Ingenerator\StubObjects\Attribute\StubDefault;
use Ingenerator\StubObjects\Attribute\StubDefault\StubDefaultValue;
use Ingenerator\StubObjects\Guesser\StubDefaultGuesser;
use Ingenerator\StubObjects\ReflectionUtils;
use Ingenerator\StubObjects\StubbingContext;
use ReflectionProperty;

class StubDefualtDateTimeGuesser implements StubDefaultGuesser
class StubDefaultDateTimeGuesser implements StubDefaultGuesser
{
public function guessProvider(ReflectionProperty $property, StubbingContext $context): false|StubDefault
{
if ($property->getType()->getName() === DateTimeImmutable::class) {
if (ReflectionUtils::getTypeNameIfAvailable($property) === DateTimeImmutable::class) {
// Note that we're guessing this in string form, converting back to a DateTime happens at the
// point of hydrating values because we anyway need to do it there for values that came in overrides
return new StubDefaultValue('now');
Expand Down
3 changes: 2 additions & 1 deletion src/Guesser/StubDefaultGuesser/StubDefaultStringGuesser.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Ingenerator\StubObjects\Attribute\StubDefault;
use Ingenerator\StubObjects\Attribute\StubDefault\StubDefaultRandomString;
use Ingenerator\StubObjects\Guesser\StubDefaultGuesser;
use Ingenerator\StubObjects\ReflectionUtils;
use Ingenerator\StubObjects\StubbingContext;
use Random\Randomizer;
use ReflectionProperty;
Expand All @@ -19,7 +20,7 @@ public function __construct(

public function guessProvider(ReflectionProperty $property, StubbingContext $context): false|StubDefault
{
if ($property->getType()->getName() === 'string') {
if (ReflectionUtils::getTypeNameIfAvailable($property) === 'string') {
// @todo: guess email addresses for props with `email` in the name
// @todo: guess URLs for props with `url` in the name
return new StubDefaultRandomString(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
use Ingenerator\StubObjects\Attribute\StubDefault;
use Ingenerator\StubObjects\Attribute\StubDefault\StubDefaultValue;
use Ingenerator\StubObjects\Guesser\StubDefaultGuesser;
use Ingenerator\StubObjects\ReflectionUtils;
use Ingenerator\StubObjects\StubbingContext;
use ReflectionProperty;

class StubDefaultStubbableObjectGuesser implements StubDefaultGuesser
{
public function guessProvider(ReflectionProperty $property, StubbingContext $context): false|StubDefault
{
if ($property->getType()->isBuiltin()) {
if (ReflectionUtils::isBuiltinType($property)) {
return FALSE;
}

Expand All @@ -29,18 +30,19 @@ public function guessProvider(ReflectionProperty $property, StubbingContext $con

private function isStubbable(StubbingContext $context, ReflectionProperty $property): bool
{
$type = $property->getType();
if ($type->isBuiltin()) {
$name = ReflectionUtils::getTypeNameIfAvailable($property);

if ($name === FALSE) {
return FALSE;
}

if ($type->getName() === Collection::class) {
if ($name === Collection::class) {
// Treat collections as a special case, we'll recursively attempt to cast arrays to collections
return TRUE;
}

// Otherwise it depends if they've configured recursion
return $context->isStubbable($property->getType()->getName());
return $context->isStubbable($name);
}

}
27 changes: 27 additions & 0 deletions src/ReflectionUtils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Ingenerator\StubObjects;

use ReflectionNamedType;
use ReflectionProperty;

class ReflectionUtils
{

public static function isBuiltinType(ReflectionProperty $property): bool
{
$type = $property->getType();

return ($type instanceof ReflectionNamedType) && $type->isBuiltin();
}

public static function getTypeNameIfAvailable(ReflectionProperty $property): false|string
{
$type = $property->getType();
if ($type instanceof \ReflectionNamedType) {
return $type->getName();
}

return FALSE;
}
}
4 changes: 2 additions & 2 deletions src/StandardConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
use Ingenerator\StubObjects\Guesser\StubAsGuesser\StubAsDateTimeGuesser;
use Ingenerator\StubObjects\Guesser\StubAsGuesser\StubAsStubObjectGuesser;
use Ingenerator\StubObjects\Guesser\StubDefaultGuesser;
use Ingenerator\StubObjects\Guesser\StubDefaultGuesser\StubDefaultDateTimeGuesser;
use Ingenerator\StubObjects\Guesser\StubDefaultGuesser\StubDefaultNullGuesser;
use Ingenerator\StubObjects\Guesser\StubDefaultGuesser\StubDefaultStringGuesser;
use Ingenerator\StubObjects\Guesser\StubDefaultGuesser\StubDefaultStubbableObjectGuesser;
use Ingenerator\StubObjects\Guesser\StubDefaultGuesser\StubDefualtDateTimeGuesser;

class StandardConfig
{
Expand All @@ -36,7 +36,7 @@ public static function loadDefaultValueGuessers(): array
{
return [
new StubDefaultNullGuesser(),
new StubDefualtDateTimeGuesser(),
new StubDefaultDateTimeGuesser(),
new StubDefaultStringGuesser(),
new StubDefaultStubbableObjectGuesser(),
];
Expand Down
46 changes: 46 additions & 0 deletions test/integration/HandlesUnsupportedTypesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace test\integration;

use Ingenerator\StubObjects\Attribute\StubDefault\StubDefaultValue;
use Ingenerator\StubObjects\StubObjects;
use PHPUnit\Framework\Attributes\TestWith;
use PHPUnit\Framework\TestCase;

class HandlesUnsupportedTypesTest extends TestCase
{

#[TestWith([[], ['something' => 'default']])]
#[TestWith([['something' => 'whatever'], ['something' => 'whatever']])]
public function test_it_can_stub_objects_with_union_types(array $data, array $expect)
{
$class = new class {
#[StubDefaultValue('default')]
public bool|string $something;
};

$result = $this->newSubject()->stub($class::class, $data);

$this->assertSame($expect, (array) $result);
}

#[TestWith([[], ['something' => null]])]
#[TestWith([['something' => 'whatever'], ['something' => 'whatever']])]
public function test_it_can_stub_objects_with_untyped_properties(array $data, array $expect)
{
$class = new class {
// Note: this will be auto-detected as able to be stubbed with `null`, which is technically correct.
public $something;
};

$result = $this->newSubject()->stub($class::class, $data);

$this->assertSame($expect, (array) $result);
}


private function newSubject(array $stubbable_class_patterns = ['*']): StubObjects
{
return new StubObjects(...get_defined_vars());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

use DateTimeImmutable;
use Ingenerator\StubObjects\Attribute\StubDefault\StubDefaultValue;
use Ingenerator\StubObjects\Guesser\StubDefaultGuesser\StubDefualtDateTimeGuesser;
use Ingenerator\StubObjects\Guesser\StubDefaultGuesser\StubDefaultDateTimeGuesser;

class StubDefaultDateTimeGuesserTest extends BaseStubDefaultGuesserTestCase
{
Expand All @@ -22,7 +22,7 @@ public function test_it_guesses_current_time()
'string' => FALSE,
],
$class::class,
new StubDefualtDateTimeGuesser,
new StubDefaultDateTimeGuesser,
);
}
}
Loading