Skip to content

Conversation

N1ebieski
Copy link
Contributor

@N1ebieski N1ebieski commented Mar 15, 2025

This PR adds support for VariableParser and VariableContext.

For example, currently, this command:

php-parser detect "<?php

/** @var \\App\\Models\\User \$user */
Gate::authorize('edit', \$user);" --debug

returns:

================================================================================
================================================================================
                              STARTING TO PARSE
================================================================================
================================================================================

Microsoft\PhpParser\Node\SourceFileNode <?php Gate::authorize('edit', $user);
+ Context: App\Contexts\Base
* Parsing: App\Parsers\SourceFileNodeParser

 Microsoft\PhpParser\Node\Statement\InlineHtml <?php 
 + Context: App\Contexts\Base
 * Parsing: App\Parsers\InlineHtmlParser

 Microsoft\PhpParser\Node\Statement\ExpressionStatement Gate::authorize('edit', $user);
 + Context: App\Contexts\Base
 * Parsing: App\Parsers\ExpressionStatementParser

  Microsoft\PhpParser\Node\Expression\CallExpression Gate::authorize('edit', $user)
  + Context: App\Contexts\MethodCall
  * Parsing: App\Parsers\CallExpressionParser

   Microsoft\PhpParser\Node\Expression\ScopedPropertyAccessExpression Gate::authorize
   + Context: App\Contexts\MethodCall
   * Parsing: App\Parsers\ScopedPropertyAccessExpressionParser

    Microsoft\PhpParser\Node\QualifiedName Gate
   Microsoft\PhpParser\Node\DelimitedList\ArgumentExpressionList 'edit', $user
   + Context: App\Contexts\MethodCall
   * Parsing: App\Parsers\ArgumentExpressionListParser

    Microsoft\PhpParser\Node\Expression\ArgumentExpression 'edit'
    + Context: App\Contexts\Argument
    * Parsing: App\Parsers\ArgumentExpressionParser

     Microsoft\PhpParser\Node\StringLiteral 'edit'
     + Context: App\Contexts\StringValue
     * Parsing: App\Parsers\StringLiteralParser

    Microsoft\PhpParser\Node\Expression\ArgumentExpression $user
    + Context: App\Contexts\Argument
    * Parsing: App\Parsers\ArgumentExpressionParser

     Microsoft\PhpParser\Node\Expression\Variable $user
[
    {
        "type": "methodCall",
        "methodName": "authorize",
        "className": "Gate",
        "arguments": {
            "type": "arguments",
            "autocompletingIndex": 2,
            "children": [
                {
                    "type": "argument",
                    "name": null,
                    "children": [
                        {
                            "type": "string",
                            "value": "edit",
                            "start": {
                                "line": 2,
                                "column": 16
                            },
                            "end": {
                                "line": 2,
                                "column": 20
                            }
                        }
                    ]
                },
                {
                    "type": "argument",
                    "name": null,
                    "children": []
                }
            ]
        }
    }
]

With VariableParser and VariableContext:

================================================================================
================================================================================
                              STARTING TO PARSE
================================================================================
================================================================================

Microsoft\PhpParser\Node\SourceFileNode <?php /** @var \App\Models\User $user */ Gate::aut
+ Context: App\Contexts\Base
* Parsing: App\Parsers\SourceFileNodeParser

 Microsoft\PhpParser\Node\Statement\InlineHtml <?php 
 + Context: App\Contexts\Base
 * Parsing: App\Parsers\InlineHtmlParser

 Microsoft\PhpParser\Node\Statement\ExpressionStatement Gate::authorize('edit', $user);
 + Context: App\Contexts\Base
 * Parsing: App\Parsers\ExpressionStatementParser

  Microsoft\PhpParser\Node\Expression\CallExpression Gate::authorize('edit', $user)
  + Context: App\Contexts\MethodCall
  * Parsing: App\Parsers\CallExpressionParser

   Microsoft\PhpParser\Node\Expression\ScopedPropertyAccessExpression Gate::authorize
   + Context: App\Contexts\MethodCall
   * Parsing: App\Parsers\ScopedPropertyAccessExpressionParser

    Microsoft\PhpParser\Node\QualifiedName Gate
   Microsoft\PhpParser\Node\DelimitedList\ArgumentExpressionList 'edit', $user
   + Context: App\Contexts\MethodCall
   * Parsing: App\Parsers\ArgumentExpressionListParser

    Microsoft\PhpParser\Node\Expression\ArgumentExpression 'edit'
    + Context: App\Contexts\Argument
    * Parsing: App\Parsers\ArgumentExpressionParser

     Microsoft\PhpParser\Node\StringLiteral 'edit'
     + Context: App\Contexts\StringValue
     * Parsing: App\Parsers\StringLiteralParser

    Microsoft\PhpParser\Node\Expression\ArgumentExpression $user
    + Context: App\Contexts\Argument
    * Parsing: App\Parsers\ArgumentExpressionParser

     Microsoft\PhpParser\Node\Expression\Variable $user
     + Context: App\Contexts\Variable
     * Parsing: App\Parsers\VariableParser

[
    {
        "type": "methodCall",
        "methodName": "authorize",
        "className": "Gate",
        "arguments": {
            "type": "arguments",
            "autocompletingIndex": 2,
            "children": [
                {
                    "type": "argument",
                    "name": null,
                    "children": [
                        {
                            "type": "string",
                            "value": "edit",
                            "start": {
                                "line": 3,
                                "column": 16
                            },
                            "end": {
                                "line": 3,
                                "column": 20
                            }
                        }
                    ]
                },
                {
                    "type": "argument",
                    "name": null,
                    "children": [
                        {
                            "type": "variable",
                            "name": "user",
                            "className": "App\\Models\\User",
                            "start": {
                                "line": 3,
                                "column": 24
                            },
                            "end": {
                                "line": 3,
                                "column": 29
                            }
                        }
                    ]
                }
            ]
        }
    }
]

@N1ebieski
Copy link
Contributor Author

I've added support for searchForVar method, so the parser can find className if the variable is a parameter:

obraz

Still have no idea how to get className from docblocks :/

obraz

@ghabriel25
Copy link

ghabriel25 commented Sep 9, 2025

@N1ebieski how about

// doc blocks
fileContent.match(/\/\*\*[\s\S]*?\*\//g)

// and each doc block
docBlock.match(/@var\s+(\w+)/)[1] // this need null safety

For php maybe something like this

// Extract all doc blocks (multi-line comments starting with /**)
preg_match_all('/\/\*\*[\s\S]*?\*\//', $fileContent, $docBlocksMatches);
$docBlocks = $docBlocksMatches[0];

// For each doc block, extract the @var type
foreach ($docBlocks as $docBlock) {
    if (preg_match('/@var\s+(\w+)/', $docBlock, $matches)) {
        $varType = $matches[1];
        // Use $varType as needed
        echo $varType . "\n";
    }
}

@N1ebieski
Copy link
Contributor Author

N1ebieski commented Sep 9, 2025

@ghabriel25 it's not that simple, but thanks :)

I've implemented experimental support for variable docblocks via https://github.com/phpstan/phpdoc-parser

Now this PR supports something like that:

obraz

Or something like that:

obraz

Short names are supported as well:

obraz

Aliases also:

obraz

@ghabriel25
Copy link

@N1ebieski Awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants