Skip to content

Commit ebb7746

Browse files
committed
WIP 3
1 parent 1aa2cb5 commit ebb7746

File tree

9 files changed

+96
-36
lines changed

9 files changed

+96
-36
lines changed

spec/Prophecy/Doubler/Generator/Node/ClassNodeSpec.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ function it_can_has_methods(MethodNode $method1, MethodNode $method2)
7676
$this->addMethod($method1);
7777
$this->addMethod($method2);
7878

79-
$this->getMethods()->shouldReturn(array(
79+
$this->getMethods()->shouldReturn([
8080
'__construct' => $method1,
8181
'getName' => $method2,
82-
));
82+
]);
8383
}
8484

8585
function its_hasMethod_returns_true_if_method_exists(MethodNode $method)

spec/Prophecy/Doubler/Generator/Node/Type/UnionTypeSpec.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace spec\Prophecy\Doubler\Generator\Node\Type;
44

55
use PhpSpec\ObjectBehavior;
6+
use Prophecy\Doubler\Generator\Node\Type\IntersectionType;
67
use Prophecy\Doubler\Generator\Node\Type\SimpleType;
78
use Prophecy\Doubler\Generator\Node\Type\TypeInterface;
89
use Prophecy\Doubler\Generator\Node\Type\UnionType;
@@ -68,4 +69,14 @@ function it_return_array_of_its_types(): void
6869
new SimpleType('string')
6970
]);
7071
}
72+
73+
function it_should_accept_simple_type_and_intersection()
74+
{
75+
$type1 = new SimpleType('string');
76+
$type2 = new IntersectionType([new SimpleType('A'), new SimpleType('B')]);
77+
$this->beConstructedWith([$type1, $type2]);
78+
79+
$this->has($type1)->shouldBe(true);
80+
$this->has($type2)->shouldBe(true);
81+
}
7182
}

src/Prophecy/Doubler/Generator/ClassMirror.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,6 @@ private function reflectArgumentToNode(ReflectionParameter $parameter, Reflectio
215215
$node->setAsPassedByReference();
216216
}
217217

218-
219218
$methodNode->addArgument($node);
220219
}
221220

@@ -278,15 +277,15 @@ private function createTypeFromReflection(ReflectionType $type, ReflectionClass
278277
// Handle nullability for named types explicitly by wrapping in a UnionType if needed
279278
if ($type->allowsNull() && $name !== 'mixed' && $name !== 'null') {
280279
// Check if SimpleType already resolved to 'null' (e.g. input was 'null')
281-
if ((string)$simpleType === 'null') {
280+
if ($simpleType->getType() === 'null') {
282281
return $simpleType; // Already null, no union needed
283282
}
284283
// Check if SimpleType already resolved to 'mixed'
285-
if ((string)$simpleType === 'mixed') {
284+
if ($simpleType->getType() === 'mixed') {
286285
return $simpleType; // mixed implies null, no union needed
287286
}
288287

289-
return new UnionType([$simpleType, new SimpleType('null')]);
288+
return new UnionType([new SimpleType('null'), $simpleType]);
290289
}
291290

292291
return $simpleType;
@@ -357,7 +356,7 @@ function (string $type) use ($class) {
357356
$types
358357
);
359358

360-
if ($types && $types != ['mixed'] && $allowsNull) {
359+
if ($types && $types != ['mixed'] && $allowsNull && !in_array('null', $types, true)) {
361360
$types[] = 'null';
362361
}
363362

src/Prophecy/Doubler/Generator/Node/ReturnTypeNode.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Prophecy\Doubler\Generator\Node;
44

5+
use Prophecy\Doubler\Generator\Node\Type\SimpleType;
56
use Prophecy\Exception\Doubler\DoubleException;
67

78
final class ReturnTypeNode extends TypeNodeAbstract
@@ -34,14 +35,22 @@ protected function guardIsValidType()
3435
*
3536
* @return bool
3637
*/
37-
public function isVoid()
38+
public function isVoid(): bool
3839
{
39-
return $this->types == ['void' => 'void'];
40+
if ($this->type === null) {
41+
return true;
42+
}
43+
44+
return $this->type->equals(new SimpleType('void'));
4045
}
4146

4247
public function hasReturnStatement(): bool
4348
{
44-
return $this->types !== ['void' => 'void']
45-
&& $this->types !== ['never' => 'never'];
49+
if ($this->type === null) {
50+
return true;
51+
}
52+
53+
return !$this->type->equals(new SimpleType('void'))
54+
&& !$this->type->equals(new SimpleType('never'));
4655
}
4756
}

src/Prophecy/Doubler/Generator/Node/Type/IntersectionType.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public function __construct(private array $types)
1515
}
1616

1717
/**
18-
* @return list<TypeInterface>
18+
* @return list<SimpleType>
1919
*/
2020
public function getTypes(): array
2121
{
@@ -48,6 +48,8 @@ public function equals(TypeInterface $givenType): bool
4848
return false;
4949
}
5050
}
51+
52+
return true;
5153
}
5254

5355
private function guard(): void

src/Prophecy/Doubler/Generator/Node/Type/UnionType.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,13 @@ private function guard(): void
4242
throw new DoubleException('Union types cannot contain other unions.');
4343
}
4444
if ($type instanceof IntersectionType) {
45+
$typeStrings[] = implode('inter-', array_map(fn(SimpleType $type) => $type->getType(), $type->getTypes()));
4546
continue; // Valid type, nothing to be checked
4647
}
4748
if (!$type instanceof SimpleType) {
4849
throw new DoubleException(sprintf('Unexpected type "%s". Only IntersectionType and SimpleType are supported in UnionType.', get_class($type)));
4950
}
50-
$typeName = (string) $type;
51+
$typeName = $type->getType();
5152
$typeStrings[] = $typeName;
5253

5354
if (in_array($typeName, ['void', 'never', 'mixed'], true)) {
@@ -65,7 +66,7 @@ private function guard(): void
6566
}
6667
}
6768

68-
private function has(SimpleType|IntersectionType $givenType): bool
69+
public function has(SimpleType|IntersectionType $givenType): bool
6970
{
7071
foreach ($this->types as $type) {
7172
if ($type->equals($givenType)) {

src/Prophecy/Doubler/Generator/Node/TypeNodeAbstract.php

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
namespace Prophecy\Doubler\Generator\Node;
44

5+
use Prophecy\Doubler\Generator\Node\Type\IntersectionType;
56
use Prophecy\Doubler\Generator\Node\Type\TypeInterface;
67
use Prophecy\Doubler\Generator\Node\Type\SimpleType;
78
use Prophecy\Doubler\Generator\Node\Type\UnionType;
89
use Prophecy\Exception\Doubler\DoubleException;
910

1011
abstract class TypeNodeAbstract
1112
{
12-
protected TypeInterface $type;
13+
protected TypeInterface|null $type;
1314

1415
/**
1516
* @param string|TypeInterface ...$types
@@ -29,23 +30,35 @@ public function __construct(string|TypeInterface ...$types)
2930
}
3031

3132
// BC Layer for usage with strings
33+
$typesNormalized = [];
34+
$union = [];
3235
foreach ($types as $index => $type) {
3336
if (is_string($type)) {
34-
$types[$index] = new SimpleType($type);
37+
$type = new SimpleType($type);
38+
if (!in_array($type->getType(), $typesNormalized, true)) {
39+
$union[] = $type;
40+
$typesNormalized[] = $type->getType();
41+
}
42+
continue;
3543
}
44+
$union[] = $type;
3645
}
3746

3847
// BC Layer for usage with many types
39-
if (count($types) > 1) {
40-
$this->type = new UnionType($types);
48+
if (count($union) > 1) {
49+
$this->type = new UnionType($union);
4150
} else {
42-
$this->type = $types[0];
51+
$this->type = $union[0] ?? null;
4352
}
4453
}
4554

4655
public function canUseNullShorthand(): bool
4756
{
48-
return isset($this->types['null']) && count($this->types) === 2;
57+
if ($this->type instanceof UnionType) {
58+
return $this->type->has(new SimpleType('null')) && count($this->type->getTypes()) === 2;
59+
}
60+
61+
return false;
4962
}
5063

5164
/**
@@ -56,16 +69,21 @@ public function getTypes(): array
5669
{
5770
// TODO: add deprecation notice
5871
if ($this->type instanceof SimpleType) {
59-
return [(string) $this->type];
72+
return [$this->type->getType()];
6073
}
6174

62-
if ($this->type instanceof UnionType && $this->type->isSimple()) {
75+
$types = [];
76+
77+
if ($this->type instanceof UnionType) {
6378
foreach ($this->type->getTypes() as $type) {
64-
$types[] = (string) $type;
79+
if ($type instanceof IntersectionType) {
80+
throw new DoubleException('getType() method is deprecated and do not support IntersectionType by design. Use getType() instead.');
81+
}
82+
$types[$type->getType()] = $type->getType();
6583
}
6684
}
6785

68-
return $types;
86+
return array_values($types);
6987
}
7088

7189
public function getType(): TypeInterface
@@ -79,11 +97,29 @@ public function getType(): TypeInterface
7997
*/
8098
public function getNonNullTypes(): array
8199
{
82-
// @fixme
83-
$nonNullTypes = $this->types;
84-
unset($nonNullTypes['null']);
100+
if ($this->type === null) {
101+
return [];
102+
}
103+
if ($this->type instanceof UnionType) {
104+
$types = [];
105+
foreach ($this->type->getTypes() as $type) {
106+
if ($type->getType() === 'null') {
107+
continue;
108+
}
109+
$types[] = $type->getType();
110+
}
111+
112+
return $types;
113+
}
114+
115+
if ($this->type instanceof SimpleType) {
116+
if ($this->type->getType() === 'null') {
117+
return [];
118+
}
119+
return [$this->type->getType()];
120+
}
85121

86-
return array_values($nonNullTypes);
122+
throw new DoubleException('getNonNullTypes() method is deprecated and do not support IntersectionType by design. Use getType() instead.');
87123
}
88124

89125
protected function prefixWithNsSeparator(string $type): string

src/Prophecy/Prophecy/ObjectProphecy.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ public function addMethodProphecy(MethodProphecy $methodProphecy)
148148
$methodName = strtolower($methodProphecy->getMethodName());
149149

150150
if (!isset($this->methodProphecies[$methodName])) {
151-
$this->methodProphecies[$methodName] = array();
151+
$this->methodProphecies[$methodName] = [];
152152
}
153153

154154
$this->methodProphecies[$methodName][] = $methodProphecy;
@@ -172,7 +172,7 @@ public function getMethodProphecies($methodName = null)
172172
$methodName = strtolower($methodName);
173173

174174
if (!isset($this->methodProphecies[$methodName])) {
175-
return array();
175+
return [];
176176
}
177177

178178
return $this->methodProphecies[$methodName];

tests/Doubler/Generator/ClassMirrorTest.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ public function it_can_double_a_class_with_union_argument_types()
564564
$classNode = (new ClassMirror())->reflect(new \ReflectionClass('Fixtures\Prophecy\UnionArgumentTypes'), []);
565565
$methodNode = $classNode->getMethods()['doSomething'];
566566

567-
$this->assertEquals(new ArgumentTypeNode('bool', '\\stdClass'), $methodNode->getArguments()[0]->getTypeNode());
567+
$this->assertEquals(new ArgumentTypeNode('\\stdClass', 'bool'), $methodNode->getArguments()[0]->getTypeNode());
568568
}
569569

570570
/**
@@ -579,7 +579,7 @@ public function it_can_double_a_class_with_union_argument_type_with_false()
579579
$classNode = (new ClassMirror())->reflect(new \ReflectionClass('Fixtures\Prophecy\UnionArgumentTypeFalse'), []);
580580
$methodNode = $classNode->getMethods()['method'];
581581

582-
$this->assertEquals(new ArgumentTypeNode('false', '\stdClass'), $methodNode->getArguments()[0]->getTypeNode());
582+
$this->assertEquals(new ArgumentTypeNode('\stdClass', 'false'), $methodNode->getArguments()[0]->getTypeNode());
583583
}
584584

585585
/** @test */
@@ -636,7 +636,8 @@ public function it_can_not_double_an_enum()
636636
}
637637

638638
/**
639-
* @test
639+
* @todo: remove this test and test it works!
640+
* test
640641
*/
641642
public function it_can_not_double_intersection_return_types()
642643
{
@@ -722,7 +723,7 @@ public function it_can_double_a_nullable_parameter_type_of_false()
722723
$method = $classNode->getMethod('method');
723724
$arguments = $method->getArguments();
724725

725-
$this->assertEquals(new ArgumentTypeNode('null', 'false'), $arguments[0]->getTypeNode());
726+
$this->assertEquals(new ArgumentTypeNode('false', 'null'), $arguments[0]->getTypeNode());
726727
}
727728

728729
/**
@@ -740,7 +741,8 @@ public function it_can_not_double_dnf_intersection_argument_types()
740741
}
741742

742743
/**
743-
* @test
744+
* @todo: remove this test and test it works!
745+
* test
744746
*/
745747
public function it_can_not_double_dnf_intersection_return_types()
746748
{
@@ -825,7 +827,7 @@ public function it_can_double_a_nullable_parameter_type_of_true()
825827
$method = $classNode->getMethod('method');
826828
$arguments = $method->getArguments();
827829

828-
$this->assertEquals(new ArgumentTypeNode('null', 'true'), $arguments[0]->getTypeNode());
830+
$this->assertEquals(new ArgumentTypeNode('true', 'null'), $arguments[0]->getTypeNode());
829831
}
830832

831833
/**

0 commit comments

Comments
 (0)