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

Add rule for detecting expensive calls on a Collection #538

Merged
merged 13 commits into from Apr 24, 2020
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -71,6 +71,8 @@ If you are getting the error `Allowed memory size exhausted`, then you can use t
./vendor/bin/phpstan analyse --memory-limit=2G
```

## Rules
A list of configurable rules specific to Laravel can be found [here](docs/rules.md).
## 👊🏻 Contributing

Thank you for considering contributing to Larastan. All the contribution guidelines are mentioned [here](CONTRIBUTING.md).
Expand Down
53 changes: 53 additions & 0 deletions docs/rules.md
@@ -0,0 +1,53 @@
# Rules

All rules that are specific to Laravel applications
are listed here with their configurable options.


## NoUnnecessaryCollectionCall

Checks for method calls on instances of `Illuminate\Support\Collection` and their
subclasses. If the same result could have been determined
directly with a query then this rule will produce an error.
This rule exists to reduce unnecessarily heavy queries on the database
and to prevent unneeded loops over Collections.

#### Examples
```php
User::all()->count();
$user->roles()->pluck('name')->contains('a role name');
```

Will result in the following errors:
```
[NoUnnecessaryCollectionCallRule] Called 'count' on collection, but could have been retrieved as a query.
[NoUnnecessaryCollectionCallRule] Called 'contains' on collection, but could have been retrieved as a query.
```

To fix the errors, the code in the previous example could be changed to:
```php
User::count();
$user->roles()->where('name', 'a role name')->exists();
```

#### Configuration
This rule is enabled by default. To disable it completely, add:
```
noUnnecessaryCollectionCall: false
```
to your `phpstan.neon` file.

You can also configure the collection methods which this rule
checks for. By default, all collection methods are checked.
To only enable a specific set of methods, you could set the
`noUnnecessaryCollectionCallOnly` configuration key. For example:
```
noUnnecessaryCollectionCallOnly: ['count', 'first']
```
will only throw errors on the `count` and `first` methods.
The inverse is also configurable, to not throw an exception
on the `contains` method, one could set the following value:
```
noUnnecessaryCollectionCallExcept: ['contains']
```

18 changes: 18 additions & 0 deletions extension.neon
Expand Up @@ -33,6 +33,18 @@ parameters:
bootstrap: %rootDir%/../../nunomaduro/larastan/bootstrap.php
reportUnmatchedIgnoredErrors: false
checkGenericClassInNonGenericObjectType: false
noUnnecessaryCollectionCall: true
noUnnecessaryCollectionCallOnly: []
noUnnecessaryCollectionCallExcept: []

parametersSchema:
noUnnecessaryCollectionCall: bool()
noUnnecessaryCollectionCallOnly: listOf(string())
noUnnecessaryCollectionCallExcept: listOf(string())

conditionalTags:
NunoMaduro\Larastan\Rules\NoUnnecessaryCollectionCallRule:
phpstan.rules.rule: %noUnnecessaryCollectionCall%

services:
-
Expand Down Expand Up @@ -194,6 +206,12 @@ services:
tags:
- phpstan.phpDoc.typeNodeResolverExtension

-
class: NunoMaduro\Larastan\Rules\NoUnnecessaryCollectionCallRule
canvural marked this conversation as resolved.
Show resolved Hide resolved
arguments:
onlyMethods: %noUnnecessaryCollectionCallOnly%
excludeMethods: %noUnnecessaryCollectionCallExcept%

-
class: NunoMaduro\Larastan\Types\GenericEloquentBuilderTypeNodeResolverExtension
tags:
Expand Down