Skip to content

Commit

Permalink
feat: introduce composite types
Browse files Browse the repository at this point in the history
Composite types are composed of other types and must now implement a
method to recursively traverse all sub-types.
  • Loading branch information
romm committed May 9, 2022
1 parent 5f9d41c commit 892f383
Show file tree
Hide file tree
Showing 24 changed files with 451 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/Type/CombiningType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace CuyZ\Valinor\Type;

/** @api */
interface CombiningType extends Type
interface CombiningType extends CompositeType
{
public function isMatchedBy(Type $other): bool;

Expand Down
2 changes: 1 addition & 1 deletion src/Type/CompositeTraversableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use CuyZ\Valinor\Type\Types\ArrayKeyType;

/** @api */
interface CompositeTraversableType extends Type
interface CompositeTraversableType extends CompositeType
{
public function keyType(): ArrayKeyType;

Expand Down
14 changes: 14 additions & 0 deletions src/Type/CompositeType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace CuyZ\Valinor\Type;

/** @api */
interface CompositeType extends Type
{
/**
* @return iterable<Type>
*/
public function traverse(): iterable;
}
10 changes: 0 additions & 10 deletions src/Type/TraversableType.php

This file was deleted.

10 changes: 10 additions & 0 deletions src/Type/Types/ArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace CuyZ\Valinor\Type\Types;

use CuyZ\Valinor\Type\CompositeTraversableType;
use CuyZ\Valinor\Type\CompositeType;
use CuyZ\Valinor\Type\Type;

use function is_array;
Expand Down Expand Up @@ -98,6 +99,15 @@ public function subType(): Type
return $this->subType;
}

public function traverse(): iterable
{
yield $this->subType;

if ($this->subType instanceof CompositeType) {
yield from $this->subType->traverse();
}
}

public function __toString(): string
{
return $this->signature;
Expand Down
14 changes: 13 additions & 1 deletion src/Type/Types/ClassType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
namespace CuyZ\Valinor\Type\Types;

use CuyZ\Valinor\Type\ObjectType;
use CuyZ\Valinor\Type\CompositeType;
use CuyZ\Valinor\Type\Type;

use function is_a;

/** @api */
final class ClassType implements ObjectType
final class ClassType implements ObjectType, CompositeType
{
/** @var class-string */
private string $className;
Expand Down Expand Up @@ -64,6 +65,17 @@ public function matches(Type $other): bool
return is_a($this->className, $other->className(), true);
}

public function traverse(): iterable
{
foreach ($this->generics as $type) {
yield $type;

if ($type instanceof CompositeType) {
yield from $type->traverse();
}
}
}

public function __toString(): string
{
return empty($this->generics)
Expand Down
12 changes: 12 additions & 0 deletions src/Type/Types/IntersectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use CuyZ\Valinor\Type\CombiningType;
use CuyZ\Valinor\Type\ObjectType;
use CuyZ\Valinor\Type\CompositeType;
use CuyZ\Valinor\Type\Type;

use function implode;
Expand Down Expand Up @@ -65,6 +66,17 @@ public function isMatchedBy(Type $other): bool
return true;
}

public function traverse(): iterable
{
foreach ($this->types as $type) {
yield $type;

if ($type instanceof CompositeType) {
yield from $type->traverse();
}
}
}

/**
* @return ObjectType[]
*/
Expand Down
10 changes: 10 additions & 0 deletions src/Type/Types/IterableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace CuyZ\Valinor\Type\Types;

use CuyZ\Valinor\Type\CompositeTraversableType;
use CuyZ\Valinor\Type\CompositeType;
use CuyZ\Valinor\Type\Type;

use function is_iterable;
Expand Down Expand Up @@ -84,6 +85,15 @@ public function subType(): Type
return $this->subType;
}

public function traverse(): iterable
{
yield $this->subType;

if ($this->subType instanceof CompositeType) {
yield from $this->subType->traverse();
}
}

public function __toString(): string
{
return $this->signature;
Expand Down
10 changes: 10 additions & 0 deletions src/Type/Types/ListType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace CuyZ\Valinor\Type\Types;

use CuyZ\Valinor\Type\CompositeTraversableType;
use CuyZ\Valinor\Type\CompositeType;
use CuyZ\Valinor\Type\Type;

use function is_array;
Expand Down Expand Up @@ -91,6 +92,15 @@ public function subType(): Type
return $this->subType;
}

public function traverse(): iterable
{
yield $this->subType;

if ($this->subType instanceof CompositeType) {
yield from $this->subType->traverse();
}
}

public function __toString(): string
{
return $this->signature;
Expand Down
10 changes: 10 additions & 0 deletions src/Type/Types/NonEmptyArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace CuyZ\Valinor\Type\Types;

use CuyZ\Valinor\Type\CompositeTraversableType;
use CuyZ\Valinor\Type\CompositeType;
use CuyZ\Valinor\Type\Type;

use function is_array;
Expand Down Expand Up @@ -94,6 +95,15 @@ public function subType(): Type
return $this->subType;
}

public function traverse(): iterable
{
yield $this->subType;

if ($this->subType instanceof CompositeType) {
yield from $this->subType->traverse();
}
}

public function __toString(): string
{
return $this->signature;
Expand Down
10 changes: 10 additions & 0 deletions src/Type/Types/NonEmptyListType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace CuyZ\Valinor\Type\Types;

use CuyZ\Valinor\Type\CompositeTraversableType;
use CuyZ\Valinor\Type\CompositeType;
use CuyZ\Valinor\Type\Type;

use function count;
Expand Down Expand Up @@ -99,6 +100,15 @@ public function subType(): Type
return $this->subType;
}

public function traverse(): iterable
{
yield $this->subType;

if ($this->subType instanceof CompositeType) {
yield from $this->subType->traverse();
}
}

public function __toString(): string
{
return $this->signature;
Expand Down
15 changes: 13 additions & 2 deletions src/Type/Types/ShapedArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use CuyZ\Valinor\Type\CompositeTraversableType;
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayElementDuplicatedKey;
use CuyZ\Valinor\Type\TraversableType;
use CuyZ\Valinor\Type\CompositeType;
use CuyZ\Valinor\Type\Type;

use function array_diff;
Expand All @@ -18,7 +18,7 @@
use function is_array;

/** @api */
final class ShapedArrayType implements TraversableType
final class ShapedArrayType implements CompositeType
{
/** @var ShapedArrayElement[] */
private array $elements;
Expand Down Expand Up @@ -120,6 +120,17 @@ public function matches(Type $other): bool
return true;
}

public function traverse(): iterable
{
foreach ($this->elements as $element) {
yield $type = $element->type();

if ($type instanceof CompositeType) {
yield from $type->traverse();
}
}
}

/**
* @return ShapedArrayElement[]
*/
Expand Down
12 changes: 12 additions & 0 deletions src/Type/Types/UnionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace CuyZ\Valinor\Type\Types;

use CuyZ\Valinor\Type\CombiningType;
use CuyZ\Valinor\Type\CompositeType;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\Exception\ForbiddenMixedType;

Expand Down Expand Up @@ -82,6 +83,17 @@ public function isMatchedBy(Type $other): bool
return false;
}

public function traverse(): iterable
{
foreach ($this->types as $type) {
yield $type;

if ($type instanceof CompositeType) {
yield from $type->traverse();
}
}
}

public function types(): array
{
return $this->types;
Expand Down
39 changes: 39 additions & 0 deletions tests/Fake/Type/FakeCompositeType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace CuyZ\Valinor\Tests\Fake\Type;

use CuyZ\Valinor\Type\CompositeType;
use CuyZ\Valinor\Type\Type;

final class FakeCompositeType implements CompositeType
{
/** @var Type[] */
private array $types;

public function __construct(Type ...$types)
{
$this->types = $types;
}

public function traverse(): iterable
{
yield from $this->types;
}

public function accepts($value): bool
{
return true;
}

public function matches(Type $other): bool
{
return true;
}

public function __toString(): string
{
return 'FakeCompositeType';
}
}
59 changes: 59 additions & 0 deletions tests/Fake/Type/FakeObjectCompositeType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

namespace CuyZ\Valinor\Tests\Fake\Type;

use CuyZ\Valinor\Type\CompositeType;
use CuyZ\Valinor\Type\ObjectType;
use CuyZ\Valinor\Type\Type;
use stdClass;

final class FakeObjectCompositeType implements ObjectType, CompositeType
{
/** @var class-string */
private string $className;

/** @var array<string, Type> */
private array $generics;

/**
* @param class-string $className
* @param array<string, Type> $generics
*/
public function __construct(string $className = stdClass::class, array $generics = [])
{
$this->className = $className;
$this->generics = $generics;
}

public function className(): string
{
return $this->className;
}

public function generics(): array
{
return $this->generics;
}

public function accepts($value): bool
{
return true;
}

public function matches(Type $other): bool
{
return true;
}

public function traverse(): iterable
{
yield from $this->generics;
}

public function __toString(): string
{
return $this->className;
}
}
Loading

0 comments on commit 892f383

Please sign in to comment.