diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index eb75edf..21d65d8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -36,6 +36,7 @@ jobs: with: php-version: ${{ matrix.php }} extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo + coverage: xdebug - name: Setup problem matchers run: | diff --git a/.github/workflows/type-coverage.yml b/.github/workflows/type-coverage.yml new file mode 100644 index 0000000..f03fa81 --- /dev/null +++ b/.github/workflows/type-coverage.yml @@ -0,0 +1,21 @@ +name: Type Coverage + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + type-coverage: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.2 + coverage: xdebug + - name: Install dependencies + run: composer install --prefer-dist --no-progress --dev + - run: composer test:type-coverage diff --git a/composer.json b/composer.json index 5c0914a..f0a4a82 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,8 @@ "roave/security-advisories": "dev-latest", "larastan/larastan": "^2.9.2", "laravel/pint": "^1.13", - "pestphp/pest": "^2" + "pestphp/pest": "^2", + "pestphp/pest-plugin-type-coverage": "2.x-dev" }, "license": "MIT", "authors": [ @@ -36,15 +37,19 @@ ], "scripts": { "pint": "./vendor/bin/pint --test -v", - "test": "./vendor/bin/pest --parallel -v", + "test": "./vendor/bin/pest --parallel", "stan": "./vendor/bin/phpstan --memory-limit=2G", "test:coverage": [ "@putenv XDEBUG_MODE=coverage", - "@test --coverage-clover build/clover.xml" + "@test --coverage --min=90 --coverage-clover build/clover.xml" ], "test:coverage-html": [ "@putenv XDEBUG_MODE=coverage", - "@test --coverage-html build/coverage" + "@test --coverage --min=90 --coverage-html build/coverage" + ], + "test:type-coverage": [ + "@putenv XDEBUG_MODE=coverage", + "@test -- --type-coverage --min=100" ] }, "autoload": { diff --git a/src/Builder.php b/src/Builder.php index 8040f48..cd1256d 100644 --- a/src/Builder.php +++ b/src/Builder.php @@ -77,7 +77,7 @@ public function select(mixed $fields): self $collection->push('*'); } - $collection = $collection->filter(fn ($field) => !strpos($field, '.'))->flatten(); + $collection = $collection->filter(fn (string $field) => !strpos($field, '.'))->flatten(); if ($collection->isEmpty()) { $collection->push('*'); @@ -186,6 +186,7 @@ public function search(string $query): self * * @throws ReflectionException * @throws InvalidParamsException + * @throws JsonException */ public function fuzzySearch( mixed $key, @@ -196,7 +197,7 @@ public function fuzzySearch( $tokenizedQuery = explode(' ', $query); $keys = collect($key)->crossJoin($tokenizedQuery)->toArray(); - return $this->whereNested(function ($query) use ($keys, $caseSensitive): void { + return $this->whereNested(function (Builder $query) use ($keys, $caseSensitive): void { foreach ($keys as $v) { if (is_array($v)) { $query->whereLike($v[0], $v[1], $caseSensitive, '|'); @@ -458,7 +459,7 @@ protected function addArrayOfWheres( string $boolean, string $method = 'where', ): self { - return $this->whereNested(function ($query) use ( + return $this->whereNested(function (Builder $query) use ( $arrayOfWheres, $method, $boolean @@ -526,7 +527,7 @@ public function whereIn( $where = $this->query->get('where', new Collection()); - $valuesString = collect($values)->map(fn ($value) => !is_numeric($value) ? '"' . $value . '"' : $value)->implode(','); + $valuesString = collect($values)->map(fn (mixed $value) => !is_numeric($value) ? '"' . $value . '"' : $value)->implode(','); $where->push(($where->count() ? $boolean . ' ' : '') . $key . ' ' . $operator . ' ' . $prefix . $valuesString . $suffix); @@ -694,6 +695,7 @@ public function orWhereNotInExact( * * @throws ReflectionException * @throws InvalidParamsException + * @throws JsonException */ public function whereBetween( string $key, @@ -707,7 +709,7 @@ public function whereBetween( $second = $this->castDate($second); } - $this->whereNested(function ($query) use ( + $this->whereNested(function (Builder $query) use ( $key, $first, $second, @@ -741,6 +743,7 @@ public function orWhereBetween( * * @throws ReflectionException * @throws InvalidParamsException + * @throws JsonException */ public function whereNotBetween( string $key, @@ -754,7 +757,7 @@ public function whereNotBetween( $second = $this->castDate($second); } - $this->whereNested(function ($query) use ( + $this->whereNested(function (Builder $query) use ( $key, $first, $second, @@ -1046,20 +1049,20 @@ public function orderByDesc(string $key): self public function with(array $relationships): self { $relationships = collect($relationships)->mapWithKeys(function ( - $fields, - $relationship, + mixed $fields, + mixed $relationship, ) { if (is_numeric($relationship)) { return [$fields => ['*']]; } return [$relationship => $fields]; - })->map(function ($fields, $relationship) { + })->map(function (mixed $fields, mixed $relationship) { if (collect($fields)->count() === 0) { $fields = ['*']; } - return collect($fields)->map(fn ($field) => $relationship . '.' . $field)->implode(','); + return collect($fields)->map(fn (mixed $field) => $relationship . '.' . $field)->implode(','); }) ->values() ->toArray(); @@ -1088,16 +1091,16 @@ public function cache(int $seconds): self */ public function getQuery(): string { - return $this->query->map(function ($value, $key) { + return $this->query->map(function (mixed $value, string $key) { if ($key === 'where') { return collect($value)->unique()->implode(' '); } if ($key === 'fields') { - return collect($value)->unique()->sortBy(fn ($field) => count(explode('.', $field)))->implode(','); + return collect($value)->unique()->sortBy(fn (mixed $field) => count(explode('.', $field)))->implode(','); } return $value; - })->map(fn ($value, $key) => Str::finish($key . ' ' . $value, ';'))->unique()->sort()->implode("\n"); + })->map(fn (mixed $value, string $key) => Str::finish($key . ' ' . $value, ';'))->unique()->sort()->implode("\n"); } /** @@ -1176,7 +1179,7 @@ public function get(): mixed $data = $this->fetchApi(); if (isset($this->class) && $this->class) { - $data = collect($data)->map(fn ($result) => $this->mapToModel($result)); + $data = collect($data)->map(fn (mixed $result) => $this->mapToModel($result)); } $this->init(); diff --git a/src/Console/CreateWebhook.php b/src/Console/CreateWebhook.php index 4999a5c..9535fbb 100644 --- a/src/Console/CreateWebhook.php +++ b/src/Console/CreateWebhook.php @@ -95,19 +95,19 @@ private function getModels(): array $grep = preg_grep($pattern, $glob, PREG_GREP_INVERT); return collect($grep ?: []) - ->map(fn ($path) => basename($path, '.php')) + ->map(fn (string $path): string => basename($path, '.php')) ->toArray(); } private function getClosestModel(string $model): ?string { - return collect($this->getModels())->map(fn ($m) => [ + return collect($this->getModels())->map(fn (string $m): array => [ 'model' => $m, 'levenshtein' => levenshtein($m, $model), ]) - ->filter(fn ($m) => $m['levenshtein'] <= 5) - ->sortBy(fn ($m) => $m['levenshtein']) - ->map(fn ($m) => $m['model']) + ->filter(fn (array $m) => $m['levenshtein'] <= 5) + ->sortBy(fn (array $m) => $m['levenshtein']) + ->map(fn (array $m) => $m['model']) ->first(); } } diff --git a/src/Models/Model.php b/src/Models/Model.php index a933f88..588900b 100644 --- a/src/Models/Model.php +++ b/src/Models/Model.php @@ -9,7 +9,7 @@ use Carbon\Carbon; use Error; use Exception; -use Illuminate\Contracts\Support\{Arrayable, Jsonable}; +use Illuminate\Contracts\Support\Arrayable; use Illuminate\Http\Client\RequestException; use Illuminate\Pagination\Paginator; use Illuminate\Support\Collection; @@ -81,7 +81,7 @@ * @method static \Illuminate\Support\Collection all() * @method static Paginator paginate(int $limit = 10) */ -abstract class Model implements Arrayable, ArrayAccess, Jsonable +abstract class Model implements Arrayable, ArrayAccess { public ?string $identifier; public Builder $builder; @@ -175,13 +175,13 @@ protected function setIdentifier(): void protected function setAttributes(array $attributes): void { - $this->attributes = collect($attributes)->filter(function ($value) { + $this->attributes = collect($attributes)->filter(function (mixed $value) { if (is_array($value)) { - return collect($value)->filter(fn ($value) => is_object($value))->isEmpty(); + return collect($value)->filter(fn (mixed $value) => is_object($value))->isEmpty(); } return !is_object($value); - })->map(function ($value, $key) { + })->map(function (mixed $value, string $key) { $dates = collect($this->dates); if ($dates->contains($key)) { return Carbon::createFromTimestamp($value); @@ -194,10 +194,10 @@ protected function setAttributes(array $attributes): void protected function setRelations(array $attributes): void { $this->relations = collect($attributes) - ->filter(fn ($value, $key) => array_key_exists($key, $this->casts)) - ->map(function ($value, $key) { + ->filter(fn (mixed $value, string $key) => array_key_exists($key, $this->casts)) + ->map(function (mixed $value, string $key) { if (is_array($value) && array_is_list($value)) { - return collect($value)->map(fn ($value) => $this->mapToModel($key, $value))->filter(); + return collect($value)->map(fn (mixed $value) => $this->mapToModel($key, $value))->filter(); } return $this->mapToModel($key, $value); @@ -243,7 +243,7 @@ private function mapToModel(string $property, mixed $value): mixed } if (is_array($value)) { - return collect($value)->map(function ($single) use ($class) { + return collect($value)->map(function (mixed $single) use ($class) { if (!is_object($single)) { return $single; } @@ -305,7 +305,7 @@ public function getEndpoint(): string public function toArray(): array { $attributes = collect($this->attributes); - $relations = $this->relations->map(function ($relation) { + $relations = $this->relations->map(function (mixed $relation) { if (!$relation instanceof Arrayable) { return $relation; } @@ -318,10 +318,8 @@ public function toArray(): array /** * Convert the object to its JSON representation. - * - * @param int $options */ - public function toJson($options = 0): string + public function toJson(int $options = 0): string { return collect($this->toArray())->toJson($options); } diff --git a/src/Models/Webhook.php b/src/Models/Webhook.php index 077a6b1..07a8c7f 100644 --- a/src/Models/Webhook.php +++ b/src/Models/Webhook.php @@ -204,7 +204,7 @@ private function fill(mixed ...$parameters): void private function mapToModel(\Illuminate\Support\Collection $collection): \Illuminate\Support\Collection { - return $collection->map(function ($item) { + return $collection->map(function (array $item) { $webhook = new self(...$item); unset($webhook->client);