GithubHelp home page GithubHelp logo

phpstan / phpdoc-parser Goto Github PK

View Code? Open in Web Editor NEW
1.2K 7.0 61.0 838 KB

Next-gen phpDoc parser with support for intersection types and generics

License: MIT License

PHP 99.72% Shell 0.10% Makefile 0.14% Latte 0.03%
phpstan static-analysis php php7 testing static-code-analysis static-analyzer phpdoc

phpdoc-parser's Introduction

PHPDoc Parser for PHPStan

Build Status Latest Stable Version License PHPStan Enabled

This library phpstan/phpdoc-parser represents PHPDocs with an AST (Abstract Syntax Tree). It supports parsing and modifying PHPDocs.

For the complete list of supported PHPDoc features check out PHPStan documentation. PHPStan is the main (but not the only) user of this library.

This parser also supports parsing Doctrine Annotations. The AST nodes live in the PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine namespace. The support needs to be turned on by setting bool $parseDoctrineAnnotations to true in Lexer and PhpDocParser class constructors.

Installation

composer require phpstan/phpdoc-parser

Basic usage

<?php

require_once __DIR__ . '/vendor/autoload.php';

use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;

// basic setup

$lexer = new Lexer();
$constExprParser = new ConstExprParser();
$typeParser = new TypeParser($constExprParser);
$phpDocParser = new PhpDocParser($typeParser, $constExprParser);

// parsing and reading a PHPDoc string

$tokens = new TokenIterator($lexer->tokenize('/** @param Lorem $a */'));
$phpDocNode = $phpDocParser->parse($tokens); // PhpDocNode
$paramTags = $phpDocNode->getParamTagValues(); // ParamTagValueNode[]
echo $paramTags[0]->parameterName; // '$a'
echo $paramTags[0]->type; // IdentifierTypeNode - 'Lorem'

Format-preserving printer

This component can be used to modify the AST and print it again as close as possible to the original.

It's heavily inspired by format-preserving printer component in nikic/PHP-Parser.

<?php

require_once __DIR__ . '/vendor/autoload.php';

use PHPStan\PhpDocParser\Ast\NodeTraverser;
use PHPStan\PhpDocParser\Ast\NodeVisitor\CloningVisitor;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
use PHPStan\PhpDocParser\Printer\Printer;

// basic setup with enabled required lexer attributes

$usedAttributes = ['lines' => true, 'indexes' => true];

$lexer = new Lexer();
$constExprParser = new ConstExprParser(true, true, $usedAttributes);
$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, $usedAttributes);

$tokens = new TokenIterator($lexer->tokenize('/** @param Lorem $a */'));
$phpDocNode = $phpDocParser->parse($tokens); // PhpDocNode

$cloningTraverser = new NodeTraverser([new CloningVisitor()]);

/** @var PhpDocNode $newPhpDocNode */
[$newPhpDocNode] = $cloningTraverser->traverse([$phpDocNode]);

// change something in $newPhpDocNode
$newPhpDocNode->getParamTagValues()[0]->type = new IdentifierTypeNode('Ipsum');

// print changed PHPDoc
$printer = new Printer();
$newPhpDoc = $printer->printFormatPreserving($newPhpDocNode, $phpDocNode, $tokens);
echo $newPhpDoc; // '/** @param Ipsum $a */'

Code of Conduct

This project adheres to a Contributor Code of Conduct. By participating in this project and its community, you are expected to uphold this code.

Building

Initially you need to run composer install, or composer update in case you aren't working in a folder which was built before.

Afterwards you can either run the whole build including linting and coding standards using

make

or run only tests using

make tests

phpdoc-parser's People

Contributors

alexndlm avatar arnaud-lb avatar dependabot[bot] avatar ekisu avatar herndlm avatar jantvrdik avatar jiripudil avatar jrmajor avatar kocal avatar kubawerlos avatar kukulich avatar localheinz avatar mad-briller avatar mglaman avatar mvorisek avatar ondrejmirtes avatar phpstan-bot avatar renovate-bot avatar renovate[bot] avatar ruudk avatar rvanvelzen avatar sabbelasichon avatar schlndh avatar seldaek avatar shmax avatar staabm avatar tomasvotruba avatar villfa avatar vincentlanglet avatar zonuexe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

phpdoc-parser's Issues

nullable callable():type not supported

Here is an example:

use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
$lexer = new Lexer();
$typeParser = new TypeParser(new ConstExprParser());
$tokens = new TokenIterator($lexer->tokenize('?callable():string'));
$type = $typeParser->parse($tokens);
if($tokens->currentTokenType() !== Lexer::TOKEN_END) {
  throw new Exception('invalid syntax at position ' . $tokens->currentTokenOffset());
}

the token ends at position 9

current alternative with union type:

use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
$lexer = new Lexer();
$typeParser = new TypeParser(new ConstExprParser());
$tokens = new TokenIterator($lexer->tokenize('callable():string|null'));
$type = $typeParser->parse($tokens);
if($tokens->currentTokenType() !== Lexer::TOKEN_END) {
  throw new Exception('invalid syntax at position ' . $tokens->currentTokenOffset());
}

Annotation @TYPO3\CMS\Extbase\Annotation\Inject not tokenized correctly?

Currently i have the problem that all annotations prefixed with @TYPO3 are not tokenized correctly by the Lexer class. The end result of the tag variable is always @TYPO. The rest is cut off.
I guess the failure is located here https://github.com/phpstan/phpdoc-parser/blob/master/src/Lexer/Lexer.php#L144 because numbers are not allowed.
I am not really sure how i can test it or prove it. If you could guide me i would be happy to fix this, if it is considered a bug

resolveDeprecatedTag incorrectly grabs multiline deprecation messages

Identified in phpstan/phpstan#1983

I provided some faulty logic in \PHPStan\PhpDoc\PhpDocNodeResolver::resolveDeprecatedTag. The $key value is always 0, not the original index of the key amongst its neighbors. The goal was to take the current key and find all entries below it, rebuilding the multiple line message.

	private function resolveDeprecatedTag(PhpDocNode $phpDocNode, NameScope $nameScope): ?\PHPStan\PhpDoc\Tag\DeprecatedTag
	{
		foreach ($phpDocNode->getDeprecatedTagValues() as $key => $deprecatedTagValue) {
			$totalChildren = count($phpDocNode->children);
			/** @var \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode[] $textNodes */
			$textNodes = array_filter($phpDocNode->children, static function (PhpDocChildNode $child): bool {
				return $child instanceof PhpDocTextNode;
			});
			$deprecatedMessage = $deprecatedTagValue->description;
			if (count($textNodes) > 0) {
				for ($i = $key; $i < $totalChildren; $i++) {
					// Skip invalid children.
					if (!isset($textNodes[$i])) {
						continue;
					}
					$textNodeText = trim($textNodes[$i]->text);
					// Break at the first empty new line, as this is probably
					// leading to a new tag and no longer part of the multiline
					// deprecation message.
					if ($textNodeText === '') {
						break;
					}
					$deprecatedMessage .= ' ' . $textNodeText;
				}
			}
			$deprecatedMessage = trim($deprecatedMessage);
			return new DeprecatedTag($deprecatedMessage === '' ? null : $deprecatedMessage);
		}

		return null;
	}

Please add README.md

Hello, can you add README.md file with examples how to use your component. I already took examples from tests, but it would be more convenient, if examples will exist in README.md

larastan broken on v0.3.4

Symfony\Component\Debug\Exception\FatalThrowableError : Argument 7 passed to class@anonymous::create() must be of the type string or null, boolean given, called in /vendor/nunomaduro/larastan/src/Methods/Pipes/Macros.php on line 83

at /private/var/folders/23/25rt362577n6ck0zdpzcbz1r0000gn/T/phpstan/cache/nette.configurator/Container_5bbaf41769.php:1811
1807| $this->container = $container;
1808| }
1809|
1810|

1811| public function create(
1812| PHPStan\Reflection\ClassReflection $declaringClass,
1813| ?PHPStan\Reflection\ClassReflection $declaringTrait,
1814| PHPStan\Reflection\Php\BuiltinMethodReflection $reflection,
1815| array $phpDocParameterTypes,

Exception trace:

1 class@anonymous/private/var/folders/23/25rt362577n6ck0zdpzcbz1r0000gn/T/phpstan/cache/nette.configurator/Container_5bbaf41769.php0x1086a4166::create(Object(PHPStan\Reflection\ClassReflection), Object(NunoMaduro\Larastan\Methods\Macro))
/vendor/nunomaduro/larastan/src/Methods/Pipes/Macros.php:83

2 NunoMaduro\Larastan\Methods\Pipes\Macros::handle(Object(NunoMaduro\Larastan\Methods\Passable), Object(Closure))
/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:163

Please use the argument -v to see more details.

multiline array shape not merged?

Hi, I have a multiline array shape in a project that doesn't seem to be parsed.

When searching for an answer, I found #33 that do exactly that.

However, as though this was supposed to be merged according to
phpstan/phpstan#2497 (comment)
and
#33 (comment)

The actual commit was pretty empty:
18b0e92

and I can't find the content of the PR on master.

For example:
https://github.com/shmax/phpdoc-parser/blob/17d28cc099984a5ecbbbfb7b102daa45ce1af62f/tests/PHPStan/Parser/TypeParserTest.php#L481
https://github.com/shmax/phpdoc-parser/blob/master/tests/PHPStan/Parser/TypeParserTest.php#L481
should be the same

https://github.com/shmax/phpdoc-parser/blob/17d28cc099984a5ecbbbfb7b102daa45ce1af62f/src/Parser/TypeParser.php#L224
https://github.com/shmax/phpdoc-parser/blob/master/src/Parser/TypeParser.php#L224
too.

Was this reverted afterward?

rfc define return type by param

is type hinting where a parameter to the function is used as a fallback return value an appropriate means of documenting return types?

I'm getting errors with phpstan & vimeo every now and then to the effect that "function doessomething expects string, scalar|null given". If I'm passing a string to $default, then analysis could report string|null given instead perhaps?

/**
* @param scalar|null $default
*
* @return scalar|$default
*/
function foo(string $k, $default = null) {
 // check some global for $k or return $default
}

Method tag is missing return by reference

The MethodTagValue doesn't contain an property returnsReference. This is not a very common option to use but we found a few of them in the wild.

Full blown example will look like this:

/** @method static Type &myMethod() description here */

I will provide a PR later when ready with other things.

unresolvable type when starting @return description with html tag

Bug report

When using an html tag as the first part of the desciprtion of the @return statement, PHPStan returns the error PHPDoc tag @return contains unresolvable type.:

 * @return bool <b>TRUE</b> on success or <b>FALSE</b> on failure.

This only occurs with the characters < and & as the first char of the description. All other cases I tested worked out fine.

Code snippet that reproduces the problem

https://phpstan.org/r/6bd09036-ee66-4fd4-9e69-6d4a5b51fb83

Expected output

I found nothing against using html in the start of a PHPdoc description, so this snippet shouldn't return an error.

[Suggestion] Add support for @internal tags

Hi !
First, thanks for all the awesome work you've been doing !

Would you be interested in adding support for @internal phpdoc tags ?

I think I can make a PR in the following days, I just prefered to ask beforehand in case it is already in the making (or if you'd rather not see this implemented for any reason).

request re: shapes

Are there any plans to support the kinds of shapes that psalm supports in docblocks?

i.e. array<class-string<Foo>, array<int, class-string<Bar>>> ?

@param type can be omitted

According to https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/param.html, the type may be omitted. This is especially useful in PHP 8 applications, which can almost fully typehint all arguments. The type in the PHPdoc in this case is redundant and may only lead to out-of-sync types.

PHPstorm and Psalm already appear to support this. However, this is not yet the case with PHPstan: https://phpstan.org/r/691a2424-e087-45a0-a9ce-774a57f887ec

(fyi, we're about to remove the types in Symfony, which is why I checked support in the popular static analysis libraries)

Using `class-string<T>` in a conditional `Closure` return type results in parsing error.

Bug report

Using class-string<T> in a conditional Closure return type results in parsing error.

PHPDoc tag @param has invalid value (Closure(Container):($serviceId is class-string<TService> ? TService : mixed)) $service): Unexpected token "(", expected variable at offset 150    

Code snippet that reproduces the problem

<?php

namespace Phpactor\Container;

use Closure;

interface ContainerBuilder
{
    /**
     * @template TService
     * @param class-string<TService>|string $serviceId
     * @param Closure(Container):($serviceId is class-string<TService> ? TService : mixed) $service
     */
    public function register(string $serviceId, Closure $service): void;
}

https://phpstan.org/r/17633639-1608-478a-b439-6fc15373daf5

Did PHPStan help you today? Did it make you happy in any way?

PHPStan helped me several times today, it's awesome.

phpcs broken on 7.3

consistence/coding-standard:~2 requires a broken code_sniffer, but we cannot bump it to a higher version due to a conflict on slevomat/coding-standard.

callable(int $foo): void

callable with variable names are not parsed corretly.

$lexer = new Lexer();
$typeParser = new TypeParser(new ConstExprParser());
$tokens = new TokenIterator($lexer->tokenize($type));
$type = $typeParser->parse($tokens);
if($tokens->currentTokenType() !== Lexer::TOKEN_END) {
  throw new Exception('invalid syntax at position ' . $tokens->currentTokenOffset());
}

i get an error at offset 8 with an token 4 (TOKEN_OPEN_PARENTHESES)

Random ClassNotFoundError PhpStanExtractor.php line 67

A few times a day, I am getting following exception in symfony project. Clearing symfony cache fixes it - any ideas what could be the issue since it's random?

Uncaught Error: Class "PHPStan\PhpDocParser\Parser\PhpDocParser" not found

Uncaught PHP Exception Symfony\Component\ErrorHandler\Error\ClassNotFoundError: "Attempted to load class "PhpDocParser" from namespace "PHPStan\PhpDocParser\Parser". Did you forget a "use" statement for another namespace?" at /home/flies/workspace/sf5/homologacja/vendor/symfony/property-info/Extractor/PhpStanExtractor.php line 67

Native types shouldn't try to use the current namespace

Hi,

It seems phpdoc-parser tries to use the files' namespace even for native types. Not sure how to best explain this, let me do my best.

There this really odd issue on the Safe library where phpstan + larastan would throw an error on any library using the Safe package. At first I thought the error was on larastan but after they fixed what I thought was the root cause, the error was still there, so after some more debugging (thank $DEITY for xdebug) I found out that the problem was on the library (or on phpdoc-parser, it could be fixed on both places).

Long story short, the library is basically a bunch of files redeclaring native php functions, the issue is that whenever there's a phpdoc on one of their files using @param array or @return array, since all function files are namespaced under Safe, the parser tries to use the type Safe\array, which unfortunately has a PSR4 match on their array.php file. The bug appeared when the library was used together with larastan because larastan checks if any of the parameters is a subclass of one of Laravel's Eloquent classes on one of their extensions, triggering the second loading of the same file (doing (new ObjectType(Model::class))->isSuperTypeOf(new ObjectType($nameScope->resolveStringName('Safe\array')))->yes()). I (erroneously) thought that adding \ to the array will force it to be on the global namespace but it turns out that \array is not a valid type for phpstan.

Anyway, I believe the native types from PHP should take precedence when being parsed (same than the actual PHP behaviour), maybe with some sort of list to be compared with when parsing the types from @param and @return, although I'm not fully familiar with the implementation to make a proper suggestion.

Please, let me know if there are any doubts so I can explain further, not my best day today 😛

Add README

Please add a README.md showing how the parser can be used.

Question: max 120 chars for annototations | multi line annotations possible?

I've seen this discussion #6

is there alread a solution for multiline annotations e.g. for @return or @var, @param annotations?

i',m starting to reach the max line usage when the coding style sniffer is running.

eg.:

    * @return array<string, Common_Item_Interface[]|Common_Item_List_Interface[]|Common_Item_Type_Interface[]> Associative
    * list of domains as keys and lists of IDs/items implementing Common_Item_Interface
    ...

Cannot find custom annotations

Hi all I have a simple code like this:

use Symfony\Component\Validator\Constraints as Assert;

class ApplicationLevelDto
{
  /**
   * @Assert\NotBlank
   */
  public string|null $foo;
}

And I want to get the type @Assert\NotBlank but if I print tags I can't retrieve it, I have tried:

            $lexer = new Lexer();
            $typeParser = new TypeParser();
            $constExprParser = new ConstExprParser();
            $phpDocParser = new PhpDocParser($typeParser, $constExprParser);

            if (null === $node->getDocComment()) {
                return;
            }

            /** @var Doc $docComment */
            $docComment = $node->getDocComment();

            $tokens = $lexer->tokenize($docComment->getText());
            $tokenIterator = new TokenIterator($tokens);
            $phpDocNode = $phpDocParser->parse($tokenIterator);

            var_dump($phpDocNode->getTags());

but I get:

object(PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode)#1961 (3) {
    ["name"]=>
    string(16) "@Assert\NotBlank"
    ["value"]=>
    object(PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode)#1960 (2) {
      ["value"]=>
      string(0) ""
      ["attributes":"PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode":private]=>
      array(0) {
      }
    }
    ["attributes":"PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode":private]=>
    array(0) {
    }
  }

As you can see there isn't the type.
I tried other methods with the parser without success,

how can I get the custom annotation as a type?

Expose tokens

👋 this is might be out of scope for Phpstan, but it would be really great to be able to modify docblocks whilst preserving their formatting (this would be useful, f.e., when renaming things in phpactor).

This could be facilitated by exposing the tokens used to build each Node:

$paramTag->tokens[0]->type;    // Lexer::TOKEN_FOO
$paramTag->tokens[0]->value;  // text content of token

Would be happy to work on that if you think it makes sense here, no worries if not.

Unnamed vargs type in callable/Closure phpdoc is silently ignored

Bug report

Currently

@return \Closure(int ...$u, string): string

phpdoc works correctly, but

@return \Closure(...int, object): string

is silently ignored. No wrong phpdoc error, no error when this type does not match the type of the returned value.

Even if such type is not documented as supported - https://phpstan.org/writing-php-code/phpdoc-types#callables - I belive it should be supported as standard (non-vargs) unnamed parameters are supported.

Code snippet that reproduces the problem

https://phpstan.org/r/f3779e03-f24f-43e7-8d73-b27973bbd15c

Expected output

The 2nd phpdoc must emit a phpstan error, the type (return type of the Closure) does not match the result.

@return iterable

trying to type hint in phpstan 0.9.1 that an array is an array of strings and has array indices

/**
* @return iterable<string, string>
*/
  726    PHPDoc tag @return with type iterable<string, string> is not subtype of native type array

is there a better way that I should be marking this up ?

Allow @property without dollar sign

Some of our libraries do not format their phpdoc property so phpstan is complaining about bad property access within our app code. Example: https://github.com/twilio/twilio-php/blob/master/Twilio/Rest/Client.php:

/**
 * @property \Twilio\Rest\Api api <~ no dollar sign!
 */

Is it reasonable to make this parser support this kind of @property declaration? I think a simple fix to this line should be enough:

self::TOKEN_VARIABLE => '\\$[a-z_\\x80-\\xFF][0-9a-z_\\x80-\\xFF]*+',

No way to know what the annotation name was

This is my second attempt to use this lib, after copy pasting some lines from your unit tests I was able to start using this lib.

I am trying to replace phpdocumentor lib by yours in Doctum. (why: I still do not know, but I hope to have better type parsing with your lib)

My problem is that I need to build the method description and long description. But this lib does not give me a way to exclude non pure text nodes from the ones that are like @{something} {text}.

This gives more output that I would need to build a long method description.

Here is my function, ``getLongDescriptionFromNodes` would need to know if there was a tag on the line

    public function parse(?string $comment, ParserContext $context): DocBlockNode
    {
        /** @var PhpDocNode|null */
        $docBlock     = null;
        $errorMessage = '';
        $result       = new DocBlockNode();

        if ($comment === null) {
            return $result;
        }

        try {
	    $lexer = new Lexer();
            $constExprParser = new ConstExprParser();
            $phpDocParser = new PhpDocParser(new TypeParser($constExprParser), $constExprParser);
            $tokens = new TokenIterator($lexer->tokenize($comment));
            $docBlock = $phpDocParser->parse($tokens);
        } catch (\Exception $e) {
            $errorMessage = $e->getMessage();
        }

        if ($errorMessage) {
            $result->addError($errorMessage);

            return $result;
        }

        $textNodes = $this->getTextNodes($docBlock);
        /** @var PhpDocTextNode|null $firstTextNode */
        $firstTextNode = array_shift($textNodes);
        // First found and not empty test
        if ($firstTextNode !== null && $firstTextNode->text !== '') {
            $result->setShortDesc($firstTextNode->text);
        }
        $longDescription = $this->getLongDescriptionFromNodes($textNodes);
        if ($longDescription !== null) {
            $result->setLongDesc($longDescription);
        }

        foreach ($docBlock->getTags() as $tag) {
            $result->addTag($tag->name, $this->parseTag($tag));
        }

        return $result;
    }

	/**
	 * @param PhpDocTextNode[] $nodes
	 */
    public function getLongDescriptionFromNodes(array $nodes): ?string
    {
        $contents = '';
        foreach ($nodes as $textNode) {
            $contents .= $textNode->text;
        }

        $contents = trim($contents, ' ');

        return $contents === '' ? null : $contents;
    }

	/**
	 * @return PhpDocTextNode[]
	 */
	public function getTextNodes(PhpDocNode $node): array
	{
		return array_filter($node->children, static function (PhpDocChildNode $child): bool {
			return $child instanceof PhpDocTextNode;
		});
    }

And code in a class

    /**
     * Return a copy of this list which contains items matching any of these characteristics.
     *
     * See {@link filter()}
     * @example // only bob in the list
     *          $list = $list->filterAny('Name', 'bob');
     *          // SQL: WHERE "Name" = 'bob'
     * @example // azis or bob in the list
     *          $list = $list->filterAny('Name', array('aziz', 'bob');
     *          // SQL: WHERE ("Name" IN ('aziz','bob'))
     * @example // bob or anyone aged 21 in the list
     *          $list = $list->filterAny(array('Name'=>'bob, 'Age'=>21));
     *          // SQL: WHERE ("Name" = 'bob' OR "Age" = '21')
     * @example // bob or anyone aged 21 or 43 in the list
     *          $list = $list->filterAny(array('Name'=>'bob, 'Age'=>array(21, 43)));
     *          // SQL: WHERE ("Name" = 'bob' OR ("Age" IN ('21', '43'))
     * @example // all bobs, phils or anyone aged 21 or 43 in the list
     *          $list = $list->filterAny(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
     *          // SQL: WHERE (("Name" IN ('bob', 'phil')) OR ("Age" IN ('21', '43'))
     *
     * @see Foo
     *
     * @return Foo
     * @throws \Exception
     */
    public function zzd()
    {
        return [];
    }

[Question] Real type

Hello

Is it possible to have the real type (FQCN) when an object is used ? Actualy we only have a "string" name for an object type, without the real "use" object type ?

Thanks :)

Allow attributes to make format preserving print piece of cake

It probably out of scope of PHPStan (for now), but as we talked, it would be great if this package allowed format preserving print.

I already asked nikic how to do it, he explained me the basics. Basically, AbstractNode class with set/get attributes is the first step towards it: nikic/PHP-Parser@4d2a4d0#diff-ee2208021c6e96ff1de44281aa029630R108

After few weeks I managed to make working prototype, but it contains lot of boiler plate code and external storage of tokens positions etc., to make it work.

Having this would also make it easy to configure FQN namespaces, the same way php-parser does, without modifing the output. And other SOLID features :)

Multiline return fails to parse

Having this phpDoc:

    /**
     * Parses and builds AST for the given Query.
     *
     * @return \Doctrine\ORM\Query\AST\SelectStatement |
     *         \Doctrine\ORM\Query\AST\UpdateStatement |
     *         \Doctrine\ORM\Query\AST\DeleteStatement
     */

PHPStan 0.9.2 errors with:

  234    PHPDoc tag @return has invalid value (\Doctrine\ORM\Query\AST\SelectStatement |): Unexpected token "\n     *", expected TOKEN_IDENTIFIER at offset 117

This is currently used in Doctrine: https://github.com/doctrine/doctrine2/blob/2de6c807c8617b888bbb9da8b0908648432b3673/lib/Doctrine/ORM/Query/Parser.php#L227

Is this by design, bug or simply overseen case?

[PSR-5] param tag with optional values

The param requires a paramname, however, this could be omitted when the param tag is describing just a single argument method or all other params are adequately documented. For example:

/**
 * @param int
 * /
 public function foo($arg): void 
{}
/**
 * @param int $arg
 * @param int Optional description
 * /
 public function foo($arg, $second): void 
{}

I do see this as edge cased, but it makes makes more sense when you align this with other tags for example the @var tag. Which has the same behavior.

class Foo {
    /** @var int */
   public $bar
}
class Foo {
    /** 
     * @var int
     * @var string $foo
     */
   public $bar $foo
}

I'm working on a pr to support this.

Issues with array keys with $

Bug report

Not completely sure if this is an issue with phpstan, or underlying libraries that parse doc-blocks, but I'll start here. If you feel this should be reported in a different repo, feel free to point me in the right direction.

Code snippet that reproduces the problem

https://phpstan.org/r/fadf7cdd-6ab0-4a1a-b4b0-4534371c0680

Expected output

The issue is that I can't seem to document the array key called $ref. I have tried to escape the $, but that does not seem to help.

Undefined property: PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode::$name

Hey,

We are having some issues with phpcs due to a recent change when upgrading from phpstan/phpdoc-parser 1.4.5 to 1.5.1. When running PHPCS we get the following error.

----------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------
 1 | ERROR | An error occurred during processing; checking has been
   |       | aborted. The error message was: Undefined property:
   |       | PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode::$name
   |       | in
   |       | /.../vendor/slevomat/coding-standard/SlevomatCodingStandard/Helpers/AnnotationTypeHelper.php
   |       | on line [32](...#L32)8 (Internal.Exception)

I havn't done too much exploring what fails here, but this class was introduced very recently. Commit: 28cb38b

But my guess is that something fails because of this. I can see if I can reproducer this if requesed.

parse error: `PHPDoc tag @param-out has invalid value`

this phpdoc, taken from psalm stub file

/**
 * @psalm-pure
 * @template TFlags as int-mask<0, 256, 512>
 *
 * @param string $pattern
 * @param string $subject
 * @param mixed $matches
 * @param TFlags $flags
 * @param-out (TFlags is 256 ? array<array-key, array{string, 0|positive-int}|array{'', -1}> :
 *             TFlags is 512 ? array<array-key, string|null> :
 *             TFlags is 768 ? array<array-key, array{string, 0|positive-int}|array{null, -1}> :
 *                             array<array-key, string>
 *            ) $matches
 * @return 1|0|false
 * @psalm-ignore-falsable-return
 */
function preg_match($pattern, $subject, &$matches = [], int $flags = 0, int $offset = 0) {}

does not parse properly

Note: Using configuration file /home/runner/work/phpstan-src/phpstan-src/compiler/phpstan.neon.
 0/4 [░░░░░░░░░░░░░░░░░░░░░░░░░░░░]   0%
 4/4 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

Error: PHPDoc tag @param-out has invalid value ((TFlags is 256 ? array<array-key, array{string, 0|positive-int}|array{'', -1}> :
            TFlags is 512 ? array<array-key, string|null> :
            TFlags is 768 ? array<array-key, array{string, 0|positive-int}|array{null, -1}> :
                            array<array-key, string>
           ) $matches): Unexpected token "is", expected ')' at offset 270
 ------ ----------------------------------------------------------------------- 
  Line   ../stubs/core.stub                                                     
 ------ ----------------------------------------------------------------------- 
  212    PHPDoc tag @param-out has invalid value ((TFlags is 256 ?              
         array<array-key, array{string, 0|positive-int}|array{'', -1}> :        
                     TFlags is 512 ? array<array-key, string|null> :            
                     TFlags is 768 ? array<array-key, array{string,             
         0|positive-int}|array{null, -1}> :                                     
                                     array<array-key, string>                   
                    ) $matches): Unexpected token "is", expected ')' at offset  
         270                                                                    
 ------ ----------------------------------------------------------------------- 


Error:  [ERROR] Found 1 error                                                          

Error: Process completed with exit code 1.

when put into a phpstan stub file.

when adding parenthesis it works

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Edited/Blocked

These updates have been manually edited so Renovate will no longer make changes. To discard all commits and start over, click on a checkbox.

Detected dependencies

composer
apigen/composer.json
  • apigen/apigen ^7.0.0
composer.json
  • php ^7.2 || ^8.0
  • doctrine/annotations ^2.0
  • nikic/php-parser ^4.15
  • php-parallel-lint/php-parallel-lint ^1.2
  • phpstan/extension-installer ^1.0
  • phpstan/phpstan ^1.5
  • phpstan/phpstan-phpunit ^1.1
  • phpstan/phpstan-strict-rules ^1.0
  • phpunit/phpunit ^9.5
  • symfony/process ^5.2
github-actions
.github/workflows/apiref.yml
  • actions/checkout v4
  • shivammathur/setup-php v2
  • actions/upload-pages-artifact v3
  • actions/deploy-pages v4
.github/workflows/backward-compatibility.yml
  • actions/checkout v4
  • shivammathur/setup-php v2
.github/workflows/build.yml
  • actions/checkout v4
  • shivammathur/setup-php v2
  • actions/checkout v4
  • actions/checkout v4
  • shivammathur/setup-php v2
  • actions/checkout v4
  • shivammathur/setup-php v2
  • actions/checkout v4
  • shivammathur/setup-php v2
.github/workflows/create-tag.yml
  • actions/checkout v4
  • WyriHaximus/github-action-get-previous-tag v1
  • WyriHaximus/github-action-next-semvers v1
  • rickstaa/action-create-tag v1
  • rickstaa/action-create-tag v1
.github/workflows/lock-closed-issues.yml
  • dessant/lock-threads v5
.github/workflows/merge-maintained-branch.yml
  • actions/checkout v4
  • everlytic/branch-merge 1.1.5
.github/workflows/release-toot.yml
  • cbrgm/mastodon-github-action v2
.github/workflows/release-tweet.yml
  • Eomm/why-don-t-you-tweet v1
.github/workflows/release.yml
  • actions/checkout v4
  • metcalfc/changelog-generator v4.3.1
  • actions/create-release v1
.github/workflows/send-pr.yml
  • shivammathur/setup-php v2
  • actions/checkout v4
  • peter-evans/create-pull-request v6
.github/workflows/test-slevomat-coding-standard.yml
  • actions/checkout v4
  • actions/checkout v4
  • shivammathur/setup-php v2

  • Check this box to trigger a request for Renovate to run again on this repository

rfc flag method as having write operation side effects

I had a brief perusal of the phpdocumenter tag list, and there doesn't seem to be a tag for indicating a method has write operation amongst it list of side effects.

The situation I'm thinking of here is some theoretical static analysis for flagging up write operations taking place before some anti-csrf action, i.e. if the token is re-used, no write operations should've taken place, i.e.

  • deleting files
  • writing files to a filesystem
  • insert/update/delete sql queries

write operations in this context do not include simply calling property setters etc.

if ($request->files->has('thing')) {
    // write file to filesystem
}

$db->query('UPDATE `table` SET `foo` = ? WHERE `id` = ?', ['bar', $id]); 
/*
`$db->query() is either implicitly flagged as an ambiguous operation (or explicitly via tag),
since it has `PDOStatement::execute()` somewhere in it's AST
*/

// check token somewhere around here, halting if token not valid

$anticsrf->removeToken($token);
/*
hypothetical analyser now checks previous chunks of the AST in it's scope for write operations
*/

/*
assume docblock for `$db->update()` has the "write operation" tag
*/
$db->update('table', ['foo' => 'bar'], ['id' => $id]);

the hypthetical analysis could also flag methods that're explicitly tagged as being read-only operations that (in a later commit/ composer package update) end up with a write operation in it's AST.

tl:dr; we have tags for type safety, what about operation safety?

request for clarification re: return types for generators

What's the appropriate docblock here?

function Foo() : Generator {
    yield from [
        ['foo', 123, ['foo' => ['bar', 'baz', null]]],
        ['bar', 456, ['foo' => ['bar', 'baz', null]]],
        ['baz', 789, ['foo' => ['bar', 'baz', null]]],
    ];
}

"New in initializers" breaks in docblock

Bug report

Since PHP 8.1, it is possible to use new as default parameters ("new in initializers" RFC).

PHPStan gets it perfectly, but things stop working once we move them to docblocks, explicitly the @method docblock (see example).

Code snippet that reproduces the problem

https://phpstan.org/r/5d0cdcbb-5c11-4483-90b0-b95986f92bc2

Expected output

Since PHPStan understands the "new in initializers" in the code, I guess that this is a missed use-case instead of an actual bug, but I didn't know where to report it.

Did PHPStan help you today? Did it make you happy in any way?

Every. single. day.

Thanks for this amazing tool!

Generic<Type>[] incorrectly failing

Bug report

Code snippet that reproduces the problem

Something like

Collection<int, int>[]

Should be a valid type

No error with psalm: https://psalm.dev/r/26d249d13d
An error with phpstan (the last one): https://phpstan.org/r/a42e2d1b-d1fe-4c8b-a4df-9c6e61b422b1

PHPDoc tag @param has invalid value (Collection<int, int>[] $bars): Unexpected token "[", expected variable at offset 38

An actual workaround is (Collection<int, int>)[] but I think the phpDoc parser should still be improved to handle the syntax.

rfc recursive return type definitions

/**
* @return array<string, scalar|array<int, array<{{recursive definition is here}}>>>
*/
function DoThingWithNode(DOMNode $node) : array { /* do stuff here */ }

So I have this method that return an array with string keys whose values are either scalars or arrays with string keys whose values are either scalars or arrays with string keys whose values are either scalars with string keys..... etc.

If the definition stops at array, then one would just assume that phpstan et. al would determine that values at that level are "mixed" (or otherwise defined), which may be an undesirable effect.

One solution to the recursive @return/@param problem could be to define a class that encapsulates the recursion- i.e. class Thing extends ArrayObject & modify the return types to indicate scalar|Thing, but I'd be wanting to avoid class definitions.

A docblock-type solution would be to define a template/pattern tag so one could @thing foo array<string, scalar|array<string, {{@thing foo}}> , but I could foresee problems by virtue of it being a recursive solution to a recursive problem.

Thoughts?

p.s. the recursion here comes from the return type possibly containing arrays of the return type of the method, so maybe @return array<string, scalar|{{@method DoThingWithNode}}||{{@method DoSomeOtherThingWithNode}}> might be a solution by explicitly indicating the return values is built up from other methods ?

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.