Giter VIP home page Giter VIP logo

phpstan-laravel-validation's Introduction

phpstan-laravel-validation

ci License: AGPL v3+ stability-experimental

Explanation

If the rules given to a laravel validator are a constant expression, then the shape of the array returned by \Illuminate\Validation\Validator::validated() is known at compile-time, and can be statically analyzed.

$request = new \Illuminate\Http\Request();

$data = \Illuminate\Support\Facades\Validator::make($request->all(), [
    'person.*.email' => 'required|email|unique:users',
    'person.*.first_name' => 'required|string',
    'person.*.age' => 'required|integer|string',
])->validated();

\PHPStan\dumpType($data);
// array{person: array<int|string, array{email: non-empty-string, first_name: string, age: numeric-string}>}

$data = $request->validate([
    'person.*.email' => 'required|email|unique:users',
    'person.*.first_name' => 'required|string',
    'person.*.age' => 'required|integer|string',
]);

\PHPStan\dumpType($data);
// array{person: array<int|string, array{email: non-empty-string, first_name: string, age: numeric-string}>}

If the input data does not match the rules array, an \Illuminate\Validation\ValidationException is thrown, thus preserving type safety.

Installation

To use this extension, require it in Composer:

composer require --dev jbboehr/phpstan-laravel-validation

If you also install phpstan/extension-installer then you're all set!

Manual installation

If you don't want to use phpstan/extension-installer, include extension.neon in your project's PHPStan config:

includes:
    - vendor/jbboehr/phpstan-laravel-validation/extension.neon

Caveats

  • Laravel's validation does not cast anything, so, for example, numeric produces the type union int|float|numeric-string. If you know it will always be a string, you can refine the type by using numeric|string and get a plain numeric-string.
  • Wildcards must be indexed by integer and can't be mixed with non-wildcard rules.
  • Custom validation rules, implicit rules, and enums are not currently supported.

License

This project is licensed under the AGPL v3+ License - see the LICENSE.md file for details.

phpstan-laravel-validation's People

Contributors

jbboehr avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

phpstan-laravel-validation's Issues

Narrow types are not accepted

Ran into a weird issue using this plugin. Not entirely sure whether it is actually caused by this library. When I attempt to run collect() on the value that resides in the request, I get an error that the array type is not accepted which makes zero sense to me.

$x = $request->validate(['values.*' => 'required|integer'])['values'];
dumpType($x); // array<int|string, int|numeric-string> 
$t = collect($x); // Parameter #1 $value of function collect expects Illuminate\Contracts\Support\Arrayable<int|string, int|string>|iterable<int|string, int|string>|null, array<int|string, int|numeric-string> given.  

The weird thing is, the inferred type should be accepted. When I instantiate a new variable and explicitly give it this type, PHPStan returns no errors:

/** @var array<int|string, int|numeric-string> $val */
$val = [1, '1'];
dumpType($val); // array<int|string, int|numeric-string>
collect($val); // No errors

I have no clue why the former code fragment displays and error but the latter does not. I figured there might be a small bug in the type inference.

Using PHPStan level 9

Wrapped or untyped response returned depending on context

Hi there! I've run into a couple of type inconsistencies when using this plugin (at 26f81b6) under Laravel 9.x (9.48.0 specifically):

When using $controller->validate($request, [rules]) (as provided by Illuminate\Foundation\Validation\ValidatesRequests), the return type ends up as simply array rather than one with the type information provided:

// in a controller
$valid = $this->validate($request, [
    'amount' => 'required|integer',
]);
\PHPStan\dumpType($valid);

installed: Dumped type: array
not installed Dumped type: array

Similarly, when using $request->validate([rules]) (as apparently provided by Illuminate/Foundation/Providers/FoundationServiceProvider calling Request::macro('validate', ...)), the actual validated array is returned, rather than Illuminate\Validation\Validator. While the validated type is correct if I unwrap it with ->validated(), this will cause a crash at runtime since the framework has already unwrapped the value!

// also in a controller
$valid = $request->validate([
    'amount' => 'required|integer',
]);
\PHPStan\dumpType($valid);

installed: Dumped type: Illuminate\Validate\Validator

\PHPStan\dumpType($valid->validated()) shows: Dumped type: array{amount: int|numeric-string}
but doing this will crash at runtime!

not installed: Dumped type: mixed

In both cases, the actual value produced is:

['amount' => '1500']

In the former case, it looks like it should be handled by the FactoryMakeExtension (though the getClass() return value may not be picking up on the Contracts part of the namespace, but I tried changing it to no avail), so I'm totally lost.

In the latter case, I see the RequestValidateExtension returning the ValidatorType, so it appears to not be reflecting the fact that the framework calls validate() automatically.

Hopefully you find this feedback useful!

Validating required|integer is turning into float|int|numeric-string

Hi - thanks for putting this together! It's helping me out a lot with typechecking Laravel validation results :)

I noticed that the following validation appears to be producing the wrong type:

        $valid = $request->validate([
            'amount' => 'required|integer',
        ])->validated();
        \PHPStan\dumpType($valid); //  array{amount: float|int|numeric-string}

Unless Laravel's integer validation is doing something really odd, it seems like the result of this should instead be int|numeric-string, without the float in the union.

I'm currently using dev-master 1fe0f3b of this extension.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.