Skip to content
This repository has been archived by the owner on Jul 28, 2023. It is now read-only.
/ type Public archive
generated from spaceonfire/skeleton

Commit

Permalink
Merge pull request #5 from spaceonfire/develop
Browse files Browse the repository at this point in the history
1.3.0
  • Loading branch information
hustlahusky authored Oct 17, 2020
2 parents aec1598 + 7ca45ae commit 95a2cab
Show file tree
Hide file tree
Showing 30 changed files with 439 additions and 119 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@ Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) princip
- Nothing
-->

## [1.3.0] - 2020-10-17
### Added
- Lot of improvements for `DisjunctionType` and `ConjunctionType`:
- General code moved to `AbstractAggregatedType` class;
- They can be iterated over now;
- They can be merged with other aggregated types using late static binding;
- Duplicates auto removed from given subtypes;
- At least 2 subtypes required.
- `DisjunctionTypeFactory` and `ConjunctionTypeFactory` also improved:
- General code moved to `AbstractAggregatedTypeFactory` class;
- They can now parse complex type strings, such as: `int|string|array<bool|int>|string|null`.
- `GroupTypeFactory` allows parsing grouped with `()` type strings like: `(string|int)[]`
- `MemoizedTypeFactory` allows cache results of calls to `supports()` and `make()`.
Recommend wrap type factory with memoized decorator in production.
### Fixed
- Fixed string representation of collection type with complex value type.

## [1.2.0] - 2020-10-13
### Removed
- `Type` interface doesn't declare static methods `support()` and `create()` no more.
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ You can also use factory to create type object from string

```php
use spaceonfire\Type\Factory\CompositeTypeFactory;
use spaceonfire\Type\Factory\MemoizedTypeFactory;

$factory = CompositeTypeFactory::makeWithDefaultFactories();
$factory = new MemoizedTypeFactory(CompositeTypeFactory::makeWithDefaultFactories());
$factory->make('int');
$factory->make('string[]');
$factory->make('array<string,object>');
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.2-dev"
"dev-master": "1.3-dev"
}
},
"config": {
Expand Down
61 changes: 61 additions & 0 deletions src/AbstractAggregatedType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace spaceonfire\Type;

use ArrayIterator;
use IteratorAggregate;
use Webmozart\Assert\Assert;

abstract class AbstractAggregatedType implements Type, IteratorAggregate
{
/**
* @var Type[]
*/
protected $types;
/**
* @var string
*/
protected $delimiter;

public function __construct(iterable $types, string $delimiter)
{
$types = array_values($this->prepareTypes($types));

Assert::allIsInstanceOf($types, Type::class);
Assert::minCount($types, 2);

$this->types = $types;
$this->delimiter = $delimiter;
}

private function prepareTypes(iterable $types): array
{
$output = [];

foreach ($types as $type) {
if ($type instanceof static) {
$output += $this->prepareTypes($type);
continue;
}

$output[(string)$type] = $type;
}

return $output;
}

public function getIterator(): ArrayIterator
{
return new ArrayIterator($this->types);
}

/**
* @inheritDoc
*/
public function __toString(): string
{
return implode($this->delimiter, $this->types);
}
}
6 changes: 5 additions & 1 deletion src/CollectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ public function check($value): bool
*/
public function __toString(): string
{
if ($this->iterableType instanceof InstanceOfType || $this->keyType !== null) {
if (
$this->iterableType instanceof InstanceOfType ||
$this->valueType instanceof AbstractAggregatedType ||
$this->keyType !== null
) {
return $this->iterableType . '<' . implode(',', array_filter([$this->keyType, $this->valueType])) . '>';
}

Expand Down
25 changes: 4 additions & 21 deletions src/ConjunctionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,26 @@

use spaceonfire\Type\Factory\CompositeTypeFactory;
use spaceonfire\Type\Factory\ConjunctionTypeFactory;
use Webmozart\Assert\Assert;

final class ConjunctionType implements Type
final class ConjunctionType extends AbstractAggregatedType
{
public const DELIMITER = '&';

/**
* @var Type[]
*/
private $conjuncts;

/**
* ConjunctionType constructor.
* @param Type[] $conjuncts
*/
public function __construct(array $conjuncts)
public function __construct(iterable $conjuncts)
{
Assert::allIsInstanceOf($conjuncts, Type::class);
$this->conjuncts = $conjuncts;
parent::__construct($conjuncts, self::DELIMITER);
}

/**
* @inheritDoc
*/
public function check($value): bool
{
foreach ($this->conjuncts as $type) {
foreach ($this->types as $type) {
if (!$type->check($value)) {
return false;
}
Expand All @@ -41,16 +34,6 @@ public function check($value): bool
return true;
}

/**
* @inheritDoc
*/
public function __toString(): string
{
return implode(self::DELIMITER, array_map(static function (Type $type): string {
return (string)$type;
}, $this->conjuncts));
}

/**
* @param string $type
* @return bool
Expand Down
25 changes: 4 additions & 21 deletions src/DisjunctionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,26 @@

use spaceonfire\Type\Factory\CompositeTypeFactory;
use spaceonfire\Type\Factory\DisjunctionTypeFactory;
use Webmozart\Assert\Assert;

final class DisjunctionType implements Type
final class DisjunctionType extends AbstractAggregatedType
{
public const DELIMITER = '|';

/**
* @var Type[]
*/
private $disjuncts;

/**
* DisjunctionType constructor.
* @param Type[] $disjuncts
*/
public function __construct(array $disjuncts)
public function __construct(iterable $disjuncts)
{
Assert::allIsInstanceOf($disjuncts, Type::class);
$this->disjuncts = $disjuncts;
parent::__construct($disjuncts, self::DELIMITER);
}

/**
* @inheritDoc
*/
public function check($value): bool
{
foreach ($this->disjuncts as $type) {
foreach ($this->types as $type) {
if ($type->check($value)) {
return true;
}
Expand All @@ -41,16 +34,6 @@ public function check($value): bool
return false;
}

/**
* @inheritDoc
*/
public function __toString(): string
{
return implode(self::DELIMITER, array_map(static function (Type $type): string {
return (string)$type;
}, $this->disjuncts));
}

/**
* @param string $type
* @return bool
Expand Down
66 changes: 66 additions & 0 deletions src/Factory/AbstractAggregatedTypeFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

namespace spaceonfire\Type\Factory;

abstract class AbstractAggregatedTypeFactory implements TypeFactoryInterface
{
use TypeFactoryTrait;

/**
* @var string
*/
protected $delimiter;

public function __construct(string $delimiter)
{
$this->delimiter = $delimiter;
}

private function split(string $string): array
{
return (explode($this->delimiter, $string, 2) ?: []) + ['', ''];
}

final protected function parse(string $type): ?array
{
if ($this->parent === null) {
return null;
}

$type = $this->removeWhitespaces($type);

[$left, $right] = $this->split($type);

if ($left === '' || $right === '') {
return null;
}

while (!$this->parent->supports($left)) {
[$appendLeft, $right] = $this->split($right);

if ($appendLeft !== '') {
$left .= $this->delimiter . $appendLeft;
}

if ($right === '') {
break;
}
}

if (!$this->parent->supports($right) || !$this->parent->supports($left)) {
return null;
}

return [$left, $right];
}

/**
* @inheritDoc
*/
final public function supports(string $type): bool
{
return $this->parse($type) !== null;
}
}
8 changes: 4 additions & 4 deletions src/Factory/BuiltinTypeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function __construct(bool $strictByDefault = true)
*/
public function supports(string $type): bool
{
$type = self::prepareType($type);
$type = $this->prepareType($type);

return in_array($type, BuiltinType::ALL, true);
}
Expand All @@ -41,7 +41,7 @@ public function supports(string $type): bool
*/
public function make(string $type): Type
{
$type = self::prepareType($type);
$type = $this->prepareType($type);

if (!$this->supports($type)) {
throw new TypeNotSupportedException($type, BuiltinType::class);
Expand All @@ -50,9 +50,9 @@ public function make(string $type): Type
return new BuiltinType($type, $this->prepareStrictArgument($type));
}

private static function prepareType(string $type): string
private function prepareType(string $type): string
{
$type = strtolower($type);
$type = strtolower($this->removeWhitespaces($type));

if (strpos($type, 'resource') === 0) {
$type = BuiltinType::RESOURCE;
Expand Down
10 changes: 6 additions & 4 deletions src/Factory/CollectionTypeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public function supports(string $type): bool

$this->iterableTypeFactory->setParent($this->parent);

$typeParts = self::parseType($type);
$typeParts = $this->parseType($type);

if ($typeParts === null) {
return false;
Expand Down Expand Up @@ -84,7 +84,7 @@ public function make(string $type): Type
}

/** @var array $parsed */
$parsed = self::parseType($type);
$parsed = $this->parseType($type);

$parsed['value'] = $this->parent->make($parsed['value']);
$parsed['key'] = $parsed['key'] ? $this->parent->make($parsed['key']) : null;
Expand All @@ -97,8 +97,10 @@ public function make(string $type): Type
* @param string $type
* @return array<string,string|null>|null
*/
private static function parseType(string $type): ?array
private function parseType(string $type): ?array
{
$type = $this->removeWhitespaces($type);

$result = [
'iterable' => null,
'key' => null,
Expand All @@ -115,7 +117,7 @@ private static function parseType(string $type): ?array
(strpos($type, '>') === strlen($type) - 1)
) {
$result['iterable'] = substr($type, 0, $openPos);
[$key, $value] = array_map('trim', explode(',', substr($type, $openPos + 1, -1))) + [null, null];
[$key, $value] = explode(',', substr($type, $openPos + 1, -1)) + [null, null];

if (!$value && !$key) {
return null;
Expand Down
1 change: 1 addition & 0 deletions src/Factory/CompositeTypeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public static function makeWithDefaultFactories(): self
public static function makeDefaultFactories(): iterable
{
yield new CollectionTypeFactory();
yield new GroupTypeFactory();
yield new ConjunctionTypeFactory();
yield new DisjunctionTypeFactory();
yield new InstanceOfTypeFactory();
Expand Down
Loading

0 comments on commit 95a2cab

Please sign in to comment.