Skip to content

Commit

Permalink
Fix psalm errors + Improve type hints + Add support of object arrays …
Browse files Browse the repository at this point in the history
…to `IterableDataReader` (#113)
  • Loading branch information
vjik committed Dec 27, 2022
1 parent d7acb58 commit d458436
Show file tree
Hide file tree
Showing 33 changed files with 141 additions and 127 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/static.yml
Expand Up @@ -28,4 +28,12 @@ jobs:
os: >-
['ubuntu-latest']
php: >-
['8.0', '8.1']
['8.1']
psalm-php80:
uses: yiisoft/actions/.github/workflows/psalm.yml@master
with:
psalm-config: psalm-php80.xml
os: >-
['ubuntu-latest']
php: >-
['8.0']
2 changes: 1 addition & 1 deletion composer.json
Expand Up @@ -38,7 +38,7 @@
"rector/rector": "^0.15.0",
"roave/infection-static-analysis-plugin": "^1.25",
"spatie/phpunit-watcher": "^1.23",
"vimeo/psalm": "^4.26|^5.0"
"vimeo/psalm": "^4.26|^5.4"
},
"autoload": {
"psr-4": {
Expand Down
16 changes: 16 additions & 0 deletions psalm-php80.xml
@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<psalm
errorLevel="1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor"/>
<file name="src/Paginator/KeysetPaginator.php"/>
<file name="src/Paginator/OffsetPaginator.php"/>
</ignoreFiles>
</projectFiles>
</psalm>
43 changes: 12 additions & 31 deletions src/Paginator/KeysetPaginator.php
Expand Up @@ -16,8 +16,8 @@
use Yiisoft\Data\Reader\ReadableDataInterface;
use Yiisoft\Data\Reader\Sort;
use Yiisoft\Data\Reader\SortableDataInterface;

use Yiisoft\Strings\Inflector;

use function array_reverse;
use function count;
use function is_callable;
Expand All @@ -35,17 +35,15 @@
*
* @link https://use-the-index-luke.com/no-offset
*
* @psalm-template DataReaderType = ReadableDataInterface<TKey, TValue>&FilterableDataInterface&SortableDataInterface
*
* @template TKey as array-key
* @template TValue
* @template TValue as array|object
*
* @implements PaginatorInterface<TKey, TValue>
*/
final class KeysetPaginator implements PaginatorInterface
{
/**
* @psalm-var DataReaderType
* @psalm-var ReadableDataInterface<TKey, TValue>&FilterableDataInterface&SortableDataInterface
*/
private ReadableDataInterface $dataReader;
private int $pageSize = self::DEFAULT_PAGE_SIZE;
Expand Down Expand Up @@ -73,7 +71,8 @@ final class KeysetPaginator implements PaginatorInterface
private ?array $readCache = null;

/**
* @psalm-param DataReaderType $dataReader
* @psalm-param ReadableDataInterface<TKey, TValue>&FilterableDataInterface&SortableDataInterface $dataReader
* @psalm-suppress DocblockTypeContradiction
*/
public function __construct(ReadableDataInterface $dataReader)
{
Expand All @@ -91,14 +90,13 @@ public function __construct(ReadableDataInterface $dataReader)
));
}

if ($dataReader->getSort() === null) {
$sort = $dataReader->getSort();

if ($sort === null) {
throw new RuntimeException('Data sorting should be configured to work with keyset pagination.');
}

/** @psalm-suppress PossiblyNullReference */
if ($dataReader
->getSort()
->getOrder() === []) {
if (empty($sort->getOrder())) {
throw new RuntimeException('Data should be always sorted to work with keyset pagination.');
}

Expand Down Expand Up @@ -145,8 +143,6 @@ public function withPageSize(int $pageSize): static
* Reads items of the page.
*
* This method uses the read cache to prevent duplicate reads from the data source. See more {@see resetInternal()}.
*
* @psalm-suppress MixedMethodCall
*/
public function read(): iterable
{
Expand All @@ -156,30 +152,24 @@ public function read(): iterable

/** @var Sort $sort */
$sort = $this->dataReader->getSort();
/** @psalm-var DataReaderType $dataReader */
$dataReader = $this->dataReader->withLimit($this->pageSize + 1);

if ($this->isGoingToPreviousPage()) {
$sort = $this->reverseSort($sort);
/** @psalm-var DataReaderType $dataReader */
$dataReader = $dataReader->withSort($sort);
}

if ($this->isGoingSomewhere()) {
/** @psalm-var FilterableDataInterface $dataReader */
$dataReader = $dataReader->withFilter($this->getFilter($sort));
/** @psalm-var DataReaderType $dataReader */
$this->hasPreviousPageItem = $this->previousPageExist($dataReader, $sort);
}

/** @psalm-var ReadableDataInterface<TKey, TValue> $dataReader */
$data = $this->readData($dataReader, $sort);

if ($this->isGoingToPreviousPage()) {
$data = $this->reverseData($data);
}

/** @psalm-var array<TKey, TValue> $data */
return $this->readCache = $data;
}

Expand Down Expand Up @@ -287,32 +277,23 @@ private function reverseData(array $data): array
}

/**
* @psalm-param DataReaderType $dataReader
* @psalm-suppress MixedAssignment, MixedMethodCall, UnusedForeachValue
* @psalm-param ReadableDataInterface<TKey, TValue>&FilterableDataInterface&SortableDataInterface $dataReader
*/
private function previousPageExist(ReadableDataInterface $dataReader, Sort $sort): bool
{
$reverseFilter = $this->getReverseFilter($sort);

foreach ($dataReader->withFilter($reverseFilter)->withLimit(1)->read() as $void) {
return true;
}

return false;
return !empty($dataReader->withFilter($reverseFilter)->withLimit(1)->read());
}

/**
* @return mixed
*/
private function getValueFromItem(mixed $item, string $field)
private function getValueFromItem(array|object $item, string $field): mixed
{
$methodName = 'get' . (new Inflector())->toPascalCase($field);

if (is_object($item) && is_callable([$item, $methodName])) {
return $item->$methodName();
}

/** @psalm-suppress MixedArgument */
return ArrayHelper::getValue($item, $field);
}

Expand Down
16 changes: 7 additions & 9 deletions src/Paginator/OffsetPaginator.php
Expand Up @@ -17,10 +17,8 @@
use function sprintf;

/**
* @psalm-template DataReaderType = ReadableDataInterface<TKey, TValue>&OffsetableDataInterface&CountableDataInterface
*
* @template TKey as array-key
* @template TValue
* @template TValue as array|object
*
* @implements PaginatorInterface<TKey, TValue>
*/
Expand All @@ -30,17 +28,18 @@ final class OffsetPaginator implements PaginatorInterface
private int $pageSize = self::DEFAULT_PAGE_SIZE;

/**
* @psalm-var DataReaderType
* @psalm-var ReadableDataInterface<TKey, TValue>&OffsetableDataInterface&CountableDataInterface
*/
private ReadableDataInterface $dataReader;

/**
* @psalm-var DataReaderType|null
* @psalm-var ReadableDataInterface<TKey, TValue>&OffsetableDataInterface&CountableDataInterface|null
*/
private ?ReadableDataInterface $cachedReader = null;

/**
* @psalm-param DataReaderType $dataReader
* @psalm-param ReadableDataInterface<TKey, TValue>&OffsetableDataInterface&CountableDataInterface $dataReader
* @psalm-suppress DocblockTypeContradiction
*/
public function __construct(ReadableDataInterface $dataReader)
{
Expand Down Expand Up @@ -141,7 +140,6 @@ public function getOffset(): int

public function getTotalItems(): int
{
/** @psalm-var CountableDataInterface $this->dataReader */
return $this->dataReader->count();
}

Expand All @@ -156,8 +154,7 @@ public function getSort(): ?Sort
}

/**
* @psalm-return Generator<TKey, TValue, mixed, void>
* @psalm-suppress MixedAssignment, MixedMethodCall, MixedReturnTypeCoercion
* @psalm-return Generator<TKey, TValue, mixed, null>
*/
public function read(): iterable
{
Expand All @@ -173,6 +170,7 @@ public function read(): iterable
$this->cachedReader = $this->dataReader
->withLimit($this->pageSize)
->withOffset($this->getOffset());

yield from $this->cachedReader->read();
}

Expand Down
2 changes: 1 addition & 1 deletion src/Paginator/PaginatorInterface.php
Expand Up @@ -8,7 +8,7 @@

/**
* @template TKey as array-key
* @template TValue
* @template TValue as array|object
*/
interface PaginatorInterface
{
Expand Down
2 changes: 1 addition & 1 deletion src/Reader/DataReaderInterface.php
Expand Up @@ -8,7 +8,7 @@

/**
* @template TKey as array-key
* @template TValue
* @template TValue as array|object
*
* @extends ReadableDataInterface<TKey, TValue>
* @extends IteratorAggregate<TKey, TValue>
Expand Down
3 changes: 2 additions & 1 deletion src/Reader/Filter/GroupFilter.php
Expand Up @@ -56,6 +56,8 @@ public function toArray(): array
* @param array[]|FilterInterface[] $filtersArray
*
* @return static
*
* @psalm-suppress DocblockTypeContradiction
*/
public function withFiltersArray(array $filtersArray): self
{
Expand All @@ -64,7 +66,6 @@ public function withFiltersArray(array $filtersArray): self
continue;
}

/** @psalm-suppress DocblockTypeContradiction */
if (!is_array($filter)) {
throw new InvalidArgumentException(sprintf('Invalid filter on "%s" key.', $key));
}
Expand Down
4 changes: 2 additions & 2 deletions src/Reader/FilterableDataInterface.php
Expand Up @@ -9,7 +9,7 @@

interface FilterableDataInterface
{
public function withFilter(FilterInterface $filter): self;
public function withFilter(FilterInterface $filter): static;

public function withFilterProcessors(FilterProcessorInterface ...$filterProcessors): self;
public function withFilterProcessors(FilterProcessorInterface ...$filterProcessors): static;
}

0 comments on commit d458436

Please sign in to comment.