Skip to content
Closed
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
45 changes: 45 additions & 0 deletions .github/workflows/types.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: types

on:
push:
pull_request:
schedule:
- cron: '0 0 * * *'

jobs:
linux_tests:
runs-on: ubuntu-20.04

strategy:
fail-fast: true
matrix:
php: ['8.0']
stability: [prefer-lowest, prefer-stable]
include:
- php: '8.1'
flags: "--ignore-platform-req=php"
stability: prefer-stable

name: PHP ${{ matrix.php }} - ${{ matrix.stability }}

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: composer:v2
coverage: none

- name: Install dependencies
uses: nick-invision/retry@v1
with:
timeout_minutes: 5
max_attempts: 5
command: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress ${{ matrix.flags }}

- name: Execute type checking
continue-on-error: ${{ matrix.php > 8 }}
run: vendor/bin/phpstan
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"mockery/mockery": "^1.4.2",
"orchestra/testbench-core": "^6.23",
"pda/pheanstalk": "^4.0",
"phpstan/phpstan": "^0.12.94",
"phpunit/phpunit": "^8.5.8|^9.3.3",
"predis/predis": "^1.1.2",
"symfony/cache": "^5.1.4"
Expand Down
4 changes: 4 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
parameters:
paths:
- types
level: max
58 changes: 40 additions & 18 deletions src/Illuminate/Collections/Enumerable.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,32 @@
use IteratorAggregate;
use JsonSerializable;

/**
* @template TKey of array-key
* @template TValue
* @template-extends \IteratorAggregate<TKey, TValue>
*/
interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable
{
/**
* Create a new collection instance if the value isn't one already.
*
* @param mixed $items
* @return static
* @template TMakeKey of array-key
* @template TMakeValue
*
* @param iterable<TMakeKey, TMakeValue> $items
* @return static<TMakeKey, TMakeValue>
*/
public static function make($items = []);

/**
* Create a new instance by invoking the callback a given amount of times.
*
* @template TTimesValue
*
* @param int $number
* @param callable|null $callback
* @return static
* @param (callable(int): TTimesValue)|null $callback
* @return static<int, TTimesValue>
*/
public static function times($number, callable $callback = null);

Expand All @@ -32,37 +42,45 @@ public static function times($number, callable $callback = null);
*
* @param int $from
* @param int $to
* @return static
* @return static<int, int>
*/
public static function range($from, $to);

/**
* Wrap the given value in a collection if applicable.
*
* @param mixed $value
* @return static
* @template TWrapValue
*
* @param TWrapValue|iterable<TWrapValue> $value
* @return static<array-key, TWrapValue>
*/
public static function wrap($value);

/**
* Get the underlying items from the given collection if applicable.
*
* @param array|static $value
* @return array
* @template TUnwrapKey of array-key
* @template TUnwrapValue
*
* @param array<TUnwrapKey, TUnwrapValue>|static<TUnwrapKey, TUnwrapValue> $value
* @return array<TUnwrapKey, TUnwrapValue>
*/
public static function unwrap($value);

/**
* Create a new instance with no items.
*
* @return static
* @template TEmptyKey of array-key
* @template TEmptyValue
*
* @return static<TEmptyKey, TEmptyValue>
*/
public static function empty();

/**
* Get all items in the enumerable.
*
* @return array
* @return array<TKey, TValue>
*/
public function all();

Expand Down Expand Up @@ -228,7 +246,7 @@ public function duplicatesStrict($callback = null);
/**
* Execute a callback over each item.
*
* @param callable $callback
* @param callable(TValue): mixed $callback
* @return $this
*/
public function each(callable $callback);
Expand Down Expand Up @@ -425,9 +443,11 @@ public function whereInstanceOf($type);
/**
* Get the first item from the enumerable passing the given truth test.
*
* @param callable|null $callback
* @param mixed $default
* @return mixed
* @template TFirstDefaultValue
*
* @param (callable(TValue): bool)|null $callback
* @param TFirstDefaultValue $default
* @return TValue|TFirstDefaultValue
*/
public function first(callable $callback = null, $default = null);

Expand Down Expand Up @@ -459,9 +479,11 @@ public function flip();
/**
* Get an item from the collection by key.
*
* @param mixed $key
* @param mixed $default
* @return mixed
* @template TGetDefaultValue
*
* @param TKey $key
* @param TGetDefaultValue $default
* @return TValue|TGetDefaultValue
*/
public function get($key, $default = null);

Expand Down
60 changes: 60 additions & 0 deletions types/Support/Enumerable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Enumerable;
use function PHPStan\Testing\assertType;

/** @var Enumerable<int, User> $enumerable */
$enumerable = collect([]);
class User extends Authenticatable
{
}

foreach ($enumerable as $int => $user) {
assertType('int', $int);
assertType('User', $user);
}

assertType('Illuminate\Support\Enumerable<int, string>', $enumerable::make(['string']));
assertType('Illuminate\Support\Enumerable<string, User>', $enumerable::make(['string' => new User]));

assertType('Illuminate\Support\Enumerable<int, User>', $enumerable::times(10, function ($int) {
// assertType('int', $int);

return new User;
}));

assertType('Illuminate\Support\Enumerable<int, User>', $enumerable->each(function ($user) {
assertType('User', $user);
}));

assertType('Illuminate\Support\Enumerable<int, int>', $enumerable->range(1, 100));

assertType('Illuminate\Support\Enumerable<(int|string), int>', $enumerable->wrap(1));
assertType('Illuminate\Support\Enumerable<(int|string), string>', $enumerable->wrap('string'));
assertType('Illuminate\Support\Enumerable<(int|string), string>', $enumerable->wrap(['string']));
assertType('Illuminate\Support\Enumerable<(int|string), User>', $enumerable->wrap(['string' => new User]));

assertType('array<int, string>', $enumerable->unwrap(['string']));
assertType('array<int, User>', $enumerable->unwrap(
$enumerable
));

assertType('Illuminate\Support\Enumerable<(int|string), mixed>', $enumerable::empty());

assertType('array<int, User>', $enumerable->all());

assertType('User|null', $enumerable->get(0));
assertType('string|User', $enumerable->get(0, 'string'));

assertType('User|null', $enumerable->first());
assertType('User|null', $enumerable->first(function ($user) {
assertType('User', $user);

return true;
}));
assertType('string|User', $enumerable->first(function ($user) {
assertType('User', $user);

return false;
}, 'string'));