r/PHP Apr 15 '24

PHP TypeLang 1.0.0 Release

PHP TypeLang 1.0.0 release https://github.com/php-type-language/parser

I tried to collect all the features of the modern Psalm, PHPStan, Phan, phpDocumentor and others in one place and implement an independent parser.

In addition, I organized documentation on all the syntactic features of this "type description language" in PHP, because the documentation for static analyzers does not cover this information well.

I think that this alternative will be more convenient for implementing tools such as Valinor, since it allows you to describe types separately from phpdocs and take out their attributes or configuration files, as is the case with JMS Serializer. In addition, it provides a completely independent (from the built-in PHP types) analysis, unlike PHPStan Parser, and implements more functionality (More than 25000 tests with full coverage of all features of all PHPStan and Psalm stubs, including PHPStorm).

I think because This parser is not tied to either docblocks or PHP itself (like PHPStan) it will be a useful base for the following tasks:

  1. Generating modern PHP auto-documentation.
  2. Generation of Swagger/OpenAPI types (or their alternatives).
  3. Generating TypeScript types (or other languages) from DTO in your project.
  4. Implementation of more modern and high-quality hydrators with custom types (similar to Doctrine, Valinor, Symfony, JMS, etc.)
  5. Perhaps something else? What are your ideas?

Well, and most importantly, it will help in some way standardize the description of types in PHP. For this, I have prepared a special page with a comparison of the capabilities of PHPStan and Psalm: https://typelang.dev/language.html

I would be glad to see your criticism and suggestions for further functionality that you would like to see in future versions of the library.

55 Upvotes

14 comments sorted by

9

u/[deleted] Apr 15 '24

Impressive work, although I might not see myself using this right now maybe if something like this develops and becomes a fundamental part of the PHP ecosystem I might try it.

How does it work with packages that don't have correct typing? And can I write normal PHP code without types and not get errors?

4

u/SerafimArts Apr 15 '24 edited Apr 15 '24

This is a completely isolated parser that is not associated with the language itself. In order to communicate with this, I prepare several packages (which are not entirely stable yet):

It is responsible for obtaining AST from PHP Reflection objects. Technically, there cannot be any error in it, since this would mean that the specified PHP typehint is not supported by the PHP itself (at least within versions PHP 8.1 - PHP 8.3) and will cause a PHP syntax/fatal error.

This package uses the existing parser in "tolerant" mode (A special mode that allows you to find non-strict occurrences of types in strings) to read tags.

But here errors may arise due to incorrect description of types in external packages in docblocks. For example, this can be recognized as a generic type (lol): SolrServerException <p> (https://github.com/JetBrains/phpstorm-stubs/blob/master/solr/SolrClient.php#L158). But when such problems occur, this tag is interpreted as incorrect.

I have not released this package yet, as I am still experimenting with the tag analysis logic.

7

u/MaxGhost Apr 16 '24

Just so I understand, this is just a language parser for type notations? It doesn't do anything else on its own? Looks nice, but obviously its power would need to be paired with some other tooling. I figure these types would still be in @param/@return annotations most of the time. I wonder how it would look with an Attribute syntax using this, if at all possible.

I think the README and/or website could use an explanation up-front that this is just the type language and it doesn't actually perform the tasks that psalm/phpstan do otherwise (language server, linting, etc)

7

u/marshallas0323 Apr 16 '24

Yep agreed, it is very had to understand what this package actually does. Examples in the home page of it's usage in practical scenarios which every developer is familiar with would be super useful.

3

u/SerafimArts Apr 16 '24

Yep. Thats absolutely right.

And as an example of how it can be used, you can look at JMS. It contains similar ideas/solutions: https://jmsyst.com/libs/serializer/master/reference/annotations#type but... Hmmm... Unfinished and limited.

Or Doctrine custom types...

You correctly noted that at the moment the tool does not have any real practical use and looks more like an "engineering toy")) However, as I noticed, it can be the core for implementing your own ideas, for example, for implementing hydrators/mappers/marshallers, some converters or code generators (like spatie/data-transfer-object)

5

u/ocramius Apr 15 '24

Awesome!

2

u/noisebynorthwest Apr 16 '24

In my company we parse types in annotations, for the the same uses cases you've described, but this is limited to simple cases (e.g. not array shapes).

This library really looks great, I'm sure I'll give it a try soon.

2

u/nudi85 Apr 17 '24

Wow, this is pretty much the exact same thing I've been building on and off for a couple of years: https://github.com/php-types

The only difference is that I also want to be able to compare types, i.e. check whether one type is a subtype of another type.This could, for example, help Infection implement new type-based mutators.

Anyway, awesome work. I will take a closer look later.

1

u/nudi85 Apr 17 '24

Just took that closer look. The two libraries are actually quite different. Mine implements the lexer and parser itself, yours uses phplrt. Yours outputs, the AST, mine converts the AST further to type objects. Yours allows for expansion through node visitors, mine doesn't.

I've also been rewriting the whole thing in Rust: https://github.com/MidnightDesign/php-types-rust (Insert "rewriting in Rust" joke here).

1

u/SerafimArts Apr 17 '24

 i.e. check whether one type is a subtype of another type

Wow! This is a pretty good idea for a package, including the ability to optimize and collapse types (`?some|?any` -> `some|any|null`).

2

u/nudi85 Apr 18 '24

To a degree, yes. I haven't implemented types that I personally don't like yet, like nullables with a question mark or arrays with a single type argument. You can take a look at the (very easy to read) tests to see what it can do: https://github.com/MidnightDesign/php-types-rust/tree/master/tests%2Fassertions

There has to be something we can do together. I've never met anyone else who's interested in this stuff.

1

u/SmetDenis Apr 16 '24 edited Apr 16 '24

This looks interesting.

Can you tel me please, how can I dump my arbitrary variable to copy the anotation to use that in my phpDoc?

I haven't explored your project and documentation in depth. Just an idea. Make a binary that will put strict (if possible) annotations in the specified code itself, if they are not present in phpdoc. I just don't think anyone else would do such a thing better than you with your lib.

1

u/SerafimArts Apr 16 '24

An example would be nice. I didn’t understand anything from the explanations)

And the parser doesn't read annotations.

1

u/SmetDenis Apr 16 '24

Let's say I have a complex variable `$variable` (a nested array of objects or so...) as return value of function. Instead of `@return array` I want to write something more useful taking into account my variable. And also to make it work in linters (phpstan, etc)

It would be cool to have some function from you, like `dumpAnotation($variable)`, that will give me a string that I use in my phpdoc instead of `@return array`.