phpstan / phpdoc-parser

Next-gen phpDoc parser with support for intersection types and generics
MIT License
1.36k stars 61 forks source link

Reading types from reflection, not from string #225

Closed zmitic closed 11 months ago

zmitic commented 11 months ago

I am sorry if this is the wrong repo but I checked others, all the issues and tests, and couldn't find an answer. Tests like this are all using strings, and better reflection doesn't read docblocks.

Here is a real use case. With my bundle, user can do this in symfony/forms:

$builder->add('name', TextType::class, [
    'update_value' => fn(string $name, Product $p) => $p->setName($name),
]);

In the background, my code does a reflection of update_value. If the first param is non-nullable (like in the example), it silently adds NotNull validation if users didn't do that themselves. It has to be on form level because entity can (and will) fail for empty_data, which means entity-level validation will not be executed. It is 100% legit behavior, I am not mixing wrong things, it is just hard to explain on simple example.

The problem is if I wanted this:

$builder->add('name', TextType::class, [
    'update_value' => /** @param non-empty-string $name */ fn(string $name, Product $p) => $p->setName($name),
]);

Neither psalm nor phpstan would complain here, but without user manually adding validation, empty string could still end as $name. Forgetting that validation is very easy, happens to me all the time so I simply don't use it. But if I could read this @non-empty-string somehow during runtime, from reflection, then it would be trivial to add such validation automatically. Not just this one, but many others like positive-int, or int<1, 42>.


I hope the above makes sense, I tried to put least complex example but the real use-cases go above this. Is this possible to do? Performance doesn't matter here.

ondrejmirtes commented 11 months ago

This isn't really pretty code, right?

/** @param non-empty-string $name */ fn(string $name, Product $p) ...

Not even PHPStan currently supports reading PHPDocs above closures and arrow functions, see: https://github.com/phpstan/phpstan/issues/3770

So if you decide to implement this you should first make sure that your users would even be willing to write code like this. To me it feels like a lot of magic.

But you're right, if we disregard PHPStan and consider that you're just using phpstan/phpdoc-parser to achieve whatever features you want, you need to implement this part yourself.

phpstan/phpdoc-parser just provides an AST representation. A lot of other useful things are done by PHPStan itself, and unfortunately it's not really separable from the rest of the tool.

These things are:

zmitic commented 11 months ago

Thanks for quick response, I will keep my eye on these. The example I put doesn't have to be param before the closure, this is totally fine too as long as I document it:

 fn(/** @param non-empty-string $name */ string $name, Product $p)

or variation of it. Sure, it might look like magic but I assure you, it is very useful in projects with big reusable forms. I guess TypeNodeResolver is what I am looking for, thanks again.

github-actions[bot] commented 10 months ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.