Skip to content

Commit

Permalink
Merge pull request #139 from voku/int_range
Browse files Browse the repository at this point in the history
add support for "int<min,max>", "negative-int" and "numeric"
  • Loading branch information
jaapio authored Jan 4, 2022
2 parents 67665c5 + 9c0394b commit 9f13ce1
Show file tree
Hide file tree
Showing 10 changed files with 489 additions and 3 deletions.
61 changes: 61 additions & 0 deletions src/PseudoTypes/IntegerRange.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/

namespace phpDocumentor\Reflection\PseudoTypes;

use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Integer;

/**
* Value Object representing the type 'int'.
*
* @psalm-immutable
*/
final class IntegerRange extends Integer implements PseudoType
{
/** @var string */
private $minValue;

/** @var string */
private $maxValue;

public function __construct(string $minValue, string $maxValue)
{
$this->minValue = $minValue;
$this->maxValue = $maxValue;
}

public function underlyingType(): Type
{
return new Integer();
}

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

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

/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'int<' . $this->minValue . ', ' . $this->maxValue . '>';
}
}
39 changes: 39 additions & 0 deletions src/PseudoTypes/NegativeInteger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/

namespace phpDocumentor\Reflection\PseudoTypes;

use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Integer;

/**
* Value Object representing the type 'int'.
*
* @psalm-immutable
*/
final class NegativeInteger extends Integer implements PseudoType
{
public function underlyingType(): Type
{
return new Integer();
}

/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'negative-int';
}
}
47 changes: 47 additions & 0 deletions src/PseudoTypes/Numeric_.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/

namespace phpDocumentor\Reflection\PseudoTypes;

use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\AggregatedType;
use phpDocumentor\Reflection\Types\Compound;
use phpDocumentor\Reflection\Types\Float_;
use phpDocumentor\Reflection\Types\Integer;

/**
* Value Object representing the 'numeric' pseudo-type, which is either a numeric-string, integer or float.
*
* @psalm-immutable
*/
final class Numeric_ extends AggregatedType implements PseudoType
{
public function __construct()
{
AggregatedType::__construct([new NumericString(), new Integer(), new Float_()], '|');
}

public function underlyingType(): Type
{
return new Compound([new NumericString(), new Integer(), new Float_()]);
}

/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'numeric';
}
}
2 changes: 1 addition & 1 deletion src/PseudoTypes/PositiveInteger.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
use phpDocumentor\Reflection\Types\Integer;

/**
* Value Object representing the type 'string'.
* Value Object representing the type 'int'.
*
* @psalm-immutable
*/
Expand Down
75 changes: 75 additions & 0 deletions src/TypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use ArrayIterator;
use InvalidArgumentException;
use phpDocumentor\Reflection\PseudoTypes\IntegerRange;
use phpDocumentor\Reflection\PseudoTypes\List_;
use phpDocumentor\Reflection\Types\Array_;
use phpDocumentor\Reflection\Types\ArrayKey;
Expand All @@ -41,6 +42,7 @@
use function current;
use function end;
use function in_array;
use function is_numeric;
use function key;
use function preg_split;
use function strpos;
Expand Down Expand Up @@ -83,10 +85,12 @@ final class TypeResolver
'non-empty-lowercase-string' => PseudoTypes\NonEmptyLowercaseString::class,
'non-empty-string' => PseudoTypes\NonEmptyString::class,
'numeric-string' => PseudoTypes\NumericString::class,
'numeric' => PseudoTypes\Numeric_::class,
'trait-string' => PseudoTypes\TraitString::class,
'int' => Types\Integer::class,
'integer' => Types\Integer::class,
'positive-int' => PseudoTypes\PositiveInteger::class,
'negative-int' => PseudoTypes\NegativeInteger::class,
'bool' => Types\Boolean::class,
'boolean' => Types\Boolean::class,
'real' => Types\Float_::class,
Expand Down Expand Up @@ -258,6 +262,8 @@ private function parseTypes(ArrayIterator $tokens, Context $context, int $parser
if ($classType !== null) {
if ((string) $classType === 'class-string') {
$types[] = $this->resolveClassString($tokens, $context);
} elseif ((string) $classType === 'int') {
$types[] = $this->resolveIntRange($tokens);
} elseif ((string) $classType === 'interface-string') {
$types[] = $this->resolveInterfaceString($tokens, $context);
} else {
Expand Down Expand Up @@ -484,6 +490,75 @@ private function resolveClassString(ArrayIterator $tokens, Context $context): Ty
return new ClassString($classType->getFqsen());
}

/**
* Resolves integer ranges
*
* @param ArrayIterator<int, (string|null)> $tokens
*/
private function resolveIntRange(ArrayIterator $tokens): Type
{
$tokens->next();

$token = '';
$minValue = null;
$maxValue = null;
$commaFound = false;
$tokenCounter = 0;
while ($tokens->valid()) {
$tokenCounter++;
$token = $tokens->current();
if ($token === null) {
throw new RuntimeException(
'Unexpected nullable character'
);
}

$token = trim($token);

if ($token === '>') {
break;
}

if ($token === ',') {
$commaFound = true;
}

if ($commaFound === false && $minValue === null) {
if (is_numeric($token) || $token === 'max' || $token === 'min') {
$minValue = $token;
}
}

if ($commaFound === true && $maxValue === null) {
if (is_numeric($token) || $token === 'max' || $token === 'min') {
$maxValue = $token;
}
}

$tokens->next();
}

if ($token !== '>') {
if (empty($token)) {
throw new RuntimeException(
'interface-string: ">" is missing'
);
}

throw new RuntimeException(
'Unexpected character "' . $token . '", ">" is missing'
);
}

if (!$minValue || !$maxValue || $tokenCounter > 4) {
throw new RuntimeException(
'int<min,max> has not the correct format'
);
}

return new IntegerRange($minValue, $maxValue);
}

/**
* Resolves class string
*
Expand Down
10 changes: 8 additions & 2 deletions tests/unit/CollectionResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,16 @@ public function testBadArrayCollectionKey(): void
public function testGoodArrayCollectionKey(): void
{
$fixture = new TypeResolver();
$fixture->resolve('array<array-key,string>', new Context(''));
$resolvedType = $fixture->resolve('array<array-key,string>', new Context(''));

$this->assertInstanceOf(Array_::class, $resolvedType);
$this->assertSame('array<array-key,string>', (string) $resolvedType);

$fixture = new TypeResolver();
$fixture->resolve('array<class-string,string>', new Context(''));
$resolvedType = $fixture->resolve('array<class-string,string>', new Context(''));

$this->assertInstanceOf(Array_::class, $resolvedType);
$this->assertSame('array<class-string,string>', (string) $resolvedType);
}

/**
Expand Down
Loading

0 comments on commit 9f13ce1

Please sign in to comment.