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

B#188652 AccessorPair accept all array notations #62

Merged
merged 13 commits into from
Nov 8, 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
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
"sort-packages": true,
"allow-plugins": {
"phpstan/extension-installer": true
}
},
"lock": false
},
"require": {
"php": ">=8.1",
"doctrine/inflector": "^2.0",
"phpdocumentor/type-resolver": "^1.7",
"phpdocumentor/type-resolver": "^1.9",
"phpunit/phpunit": "^10.0 || ^11.0"
},
"require-dev": {
Expand Down
12 changes: 5 additions & 7 deletions src/Constraint/Typehint/PhpDocParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ public function getParamTypehint(string $parameterName, string $docComment): ?st

preg_match('/\*\s*@(?:phpstan|psalm)-param\s+(.*?)\s*(?:\.\.\.)?' . preg_quote('$' . $parameterName, '/') . '\W/i', $docComment, $matches);
if (isset($matches[1])) {
return $this->normalizeDocblock((string)$matches[1]);
return $this->normalizeDocblock($matches[1]);
}

preg_match('/\*\s*@param\s+(.*?)\s*(?:\.\.\.)?' . preg_quote('$' . $parameterName, '/') . '\W/i', $docComment, $matches);
if (isset($matches[1])) {
return $this->normalizeDocblock((string)$matches[1]);
return $this->normalizeDocblock($matches[1]);
}

return null;
Expand All @@ -36,6 +36,7 @@ public function getParamTypehint(string $parameterName, string $docComment): ?st
public function getReturnTypehint(string $originalDocComment): ?string
{
$docComment = trim($originalDocComment);
$docComment = str_replace([', ', ': '], [',', ':'], $docComment);
// empty docblock provided, no typehint found
if ($docComment === '') {
return null;
Expand All @@ -49,12 +50,12 @@ public function getReturnTypehint(string $originalDocComment): ?string

preg_match('/\*\s*@(?:phpstan|psalm)-return\s+(.*?)(?:\s+|\*)/', $docComment, $matches);
if (isset($matches[1])) {
return $this->normalizeDocblock((string)$matches[1]);
return $this->normalizeDocblock($matches[1]);
}

preg_match('/\*\s*@return\s+(.*?)(?:\s+|\*)/', $docComment, $matches);
if (isset($matches[1])) {
return $this->normalizeDocblock((string)$matches[1]);
return $this->normalizeDocblock($matches[1]);
}

return null;
Expand All @@ -74,9 +75,6 @@ public function getTemplateTypehints(string $originalDocComment): array
}

preg_match_all('/\*\s*@template\s+(.*?)\sof\s(.*?)(?:\s+|\*)/', $docComment, $matches);
if (isset($matches[1], $matches[2]) === false) {
return []; // @codeCoverageIgnore
}

return array_combine($matches[1], $matches[2]);
}
Expand Down
6 changes: 3 additions & 3 deletions src/Constraint/Typehint/TypehintResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
*/
class TypehintResolver
{
protected PhpDocParser $phpDocParser;
protected PhpDocParser $phpDocParser;
protected ReflectionMethod $method;
protected TypeResolver $resolver;
protected Context $resolverContext;
protected TypeResolver $resolver;
protected Context $resolverContext;

public function __construct(ReflectionMethod $method)
{
Expand Down
47 changes: 47 additions & 0 deletions src/Constraint/ValueProvider/Compound/ArrayShapeProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);

namespace DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Compound;

use DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\ValueProvider;

class ArrayShapeProvider implements ValueProvider
{
/**
* @param array<string, ValueProvider> $items
*/
public function __construct(protected array $items)
{
}

/**
* @return array<array<int|string, mixed>>
* @inheritDoc
*/
public function getValues(): array
{
$testArray = [];
$keyValues = [];

$minValues = null;
foreach ($this->items as $key => $item) {
$values = $item->getValues();
if ($minValues === null || count($values) < $minValues) {
$minValues = count($values);
}
$keyValues[$key] = $item->getValues();
}

foreach ($keyValues as $key => $values) {
$keyValues[$key] = array_slice($values, 0, $minValues);
}

foreach ($keyValues as $key => $values) {
foreach ($values as $index => $value) {
$testArray[$index][$key] = $value;
}
}

return $testArray;
}
}
9 changes: 9 additions & 0 deletions src/Constraint/ValueProvider/NativeValueProviderFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider;

use DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Compound\ArrayProvider;
use DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Compound\ArrayShapeProvider;
use DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Compound\CallableProvider;
use DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Compound\IterableProvider;
use DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Compound\ObjectProvider;
Expand All @@ -16,6 +17,7 @@
use DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Special\NullProvider;
use DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Special\ResourceProvider;
use LogicException;
use phpDocumentor\Reflection\PseudoTypes\ArrayShape;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Array_;
use phpDocumentor\Reflection\Types\Boolean;
Expand Down Expand Up @@ -79,6 +81,13 @@ protected function getCompoundProvider(Type $typehint): ?ValueProvider
$this->valueProviderFactory->getProvider($typehint->getValueType()),
$this->valueProviderFactory->getProvider($typehint->getKeyType())
);
case ArrayShape::class:
$items = $typehint->getItems();
$itemProviders = [];
foreach ($items as $item) {
$itemProviders[$item->getKey()] = $this->valueProviderFactory->getProvider($item->getValue());
}
return new ArrayShapeProvider($itemProviders);
case Callable_::class:
return new CallableProvider();
case Iterable_::class:
Expand Down
1 change: 1 addition & 0 deletions tests/Integration/AccessorPairAsserterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
* @uses \DigitalRevolution\AccessorPairConstraint\Constraint\Typehint\PhpDocParser
* @uses \DigitalRevolution\AccessorPairConstraint\Constraint\Typehint\TypehintResolver
* @uses \DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Compound\ArrayProvider
* @uses \DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Compound\ArrayShapeProvider
* @uses \DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Compound\CallableProvider
* @uses \DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Compound\InstanceProvider
* @uses \DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Compound\IntersectionProvider
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);

namespace DigitalRevolution\AccessorPairConstraint\Tests\Integration\data\success\Regular\Types\CompoundTypes;

class ArrayShapeProperty
{
/** @var array{foo: int, bar: string}[] */
private array $items;

/**
* @param array{foo: int, bar: string}[] $items
*/
public function __construct(array $items)
{
$this->items = $items;
}

/**
* @param array{foo: int, bar: string}[] $items
*/
public function setItems(array $items): void
{
$this->items = $items;
}

/**
* @return array{foo: int, bar: string}[]
*/
public function getItems(): array
{
return $this->items;
}
}
2 changes: 1 addition & 1 deletion tests/Unit/Constraint/ConstraintConfigTest.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
declare(strict_types=1);

namespace Constraint;
namespace DigitalRevolution\AccessorPairConstraint\Tests\Unit\Constraint;

use DigitalRevolution\AccessorPairConstraint\Constraint\ConstraintConfig;
use DigitalRevolution\AccessorPairConstraint\Tests\TestCase;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);

namespace DigitalRevolution\AccessorPairConstraint\Tests\Unit\Constraint\MethodPair\AccessorPair\data\success;

use DigitalRevolution\AccessorPairConstraint\Tests\Unit\Constraint\MethodPair\AbstractDataClass;

class TypedArrayShapeSetGet extends AbstractDataClass
{
/** @var array{foo: int, bar: string}[] */
private $items;

/**
* @param array{foo: int, bar: string}[] $items
*/
public function __construct(array $items)
{
$this->items = $items;
}

/**
* @param array{foo: int, bar: string} $item
*/
public function addItem(array $item): void
{
$this->items[] = $item;
}

/**
* @param array{foo: int, bar: string}[] $items
*/
public function setItems(array $items): void
{
$this->items = $items;
}

/**
* @return array{foo: int, bar: string}[]
*/
public function getItems(): array
{
return $this->items;
}

public function getExpectedPairs(): array
{
return [['getItems', 'setItems', false], ['getItems', 'addItem', true]];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);

namespace Constraint\ValueProvider\Compound;

use DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Compound\ArrayShapeProvider;
use DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Scalar\IntProvider;
use DigitalRevolution\AccessorPairConstraint\Constraint\ValueProvider\Scalar\StringProvider;
use DigitalRevolution\AccessorPairConstraint\Tests\Unit\Constraint\ValueProvider\AbstractValueProviderTestCase;
use Exception;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\UsesClass;

#[CoversClass(ArrayShapeProvider::class)]
#[UsesClass(IntProvider::class)]
#[UsesClass(StringProvider::class)]
class ArrayShapeProviderTest extends AbstractValueProviderTestCase
{
/**
* @throws Exception
*/
public function testGetValues(): void
{
$valueProvider = new ArrayShapeProvider(['foo' => new IntProvider(), 'bar' => new StringProvider()]);
$values = $valueProvider->getValues();

static::assertNotCount(0, $values);
foreach ($values as $value) {
static::assertCount(2, $value);
static::assertArrayHasKey('foo', $value);
static::assertIsInt($value['foo']);
static::assertArrayHasKey('bar', $value);
static::assertIsString($value['bar']);
}
}
}