Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The phpdoc array keys format supported by the plugin. #63

Closed
klesun opened this issue Jan 26, 2019 · 33 comments
Closed

The phpdoc array keys format supported by the plugin. #63

klesun opened this issue Jan 26, 2019 · 33 comments
Assignees
Labels
type: documentation Not an issue, but rather a document describing how something works type: feature request

Comments

@klesun
Copy link
Owner

klesun commented Jan 26, 2019

The README briefly mentions an ability to specify array keys in a doc. I'll describe it a bit more detailed here...

So, basically, in the @param, @var, @property, @method and @return phpdoc tags, after the variable name, you can type an = followed by any valid php expression - it will be parsed by the plugin and used in completion on further references to this variable.

  • Example: (specify that var contains the value returned by a function)

    /**
     * @param $sale = self::createSale() // a sale confirmed by bookeeping
     */
    public function processSale($sale) {}

    image

  • Another example: (specify associative array keys)

    /** @var array $clientData = [
     *     'id' => 1234, // unique id in DB
     *     'activityDt' => new DateTime,
     *     'sales' => [SaleApp::createSale()], // array of one or more sales client had with us over the years
     *     'agentReviews' => [
     *          ['agent' => 'Steeve', 'rating' => 9.5, 'comment' => 'Does his job well'],
     *          ['agent' => 'Tiffany', 'rating' => 6.3, 'comment' => 'Could try harder'],
     *     ],
     *     'soapData' => (object)[ // the data we got from external system
     *         'LoanPast' => 'GOOD',
     *         'Job' => 'WEB DESIGNER',
     *     ],
     * ] */
    $clientData = json_decode($text, true);

    image

  • Note that these type annotation will also work for argument key completion when you are calling the function:

    image

  • Another example: (specify return type keys)

    /** @return array [
     *     'address' => 'Novokuzneckaya Oblastj, Dom 427, Moscow, Russia',
     *     'family' => 'single',
     *     'name' => 'Vasya Pupkin',
     * ] */
    private function getExternalData() {}

    image
    (an = between @return and the expression is allowed, but not required)

  • As discussed in Enumeration #50, you can specify "enum" string value options in phpdoc:

    /** @param $parameters = ['keyword1', 'keyword2', 'keyword3'][$any] */
    function my_test($parameters) {}

    image

  • As mentioned below, you can specify magic method return type like that:

     * @method array getData() = ['name' => 'John', 'age' => 27]
    
  • As described in centralize configure as similar as .phpstorm.meta.php of phpstorm #75, you can now specify function return type and it's argument types in .phpstorm.meta.php

    image

  • As described in Support PSALM/phan phpdoc notation #77, you can now use psalm/phan array{key1: int, key2: string} notation to specify key types (with @template generics, yay)

  • As described in Implement WordPress-like documentations // Magic::argType() #152, you can now use WordPress format notation to specify key types

    image

  • As described in Twig type hints don't seem to work with completion for me #165 and Allow to specify php data reference function FQN in twig comment #119, deep-assoc also allows you to reference functions by fully qualified name in Twig templates to use them as type information for vars completion:

    Considering we have such php class somewhere in the project:

     namespace SomeNamespace;
     class SomeClass {
     	private function makeTemplateData() {
     		return [
     			'userName' => 'Vasya', 
     			'paymentMethods' => [
     				['name' => 'Paypal', 'countries' => ['US', 'LV']],
     				['name' => 'Swedbank', 'countries' => ['ES', 'LV']],
     			],
     		];
     	}
     }

    You can reference it in a twig file with {# data-source = ... #}:

     {# data-source = \SomeNamespace\SomeClass::makeTemplateData() #}
    
     <div>
     	<span>Hello, {{  }}</span> <!-- should suggest: "userName", "paymentMethods" -->
     </div>
  • There is a set of magic function names you can use in doc comments to reference plugin-specific resolution tools:

    • As described in use table name instead of get_object_var(static) #123 you can use @param $row = Magic::dbRow('my_table_name') to tell IDE that $row is an associative array with keys matching columns in the my_table_name. For that to work, you need Data Source with your database credentials configured. Note, you can use any expression instead of 'my_table_name' string, like $this->tableName or static::getTableName()

If there are any questions concerning the format, you are welcome to file an issue - I'll answer there, and if it involves adding more formats, will reference your issue here.

@klesun klesun added the type: documentation Not an issue, but rather a document describing how something works label Jan 26, 2019
@klesun klesun closed this as completed Jan 26, 2019
@bravoman
Copy link

Very nice feature!

Does this also allow for specification of a ('virtual') @method phpdoc tag it's return value? If so, what would be the syntax for this?

@bravoman
Copy link

Also I'm curious... If I could add non-static PHP code like this, would that in turn also be type inferred?

Or would that just be asking for too much? 😉

@klesun
Copy link
Owner Author

klesun commented Feb 28, 2019

Hi.
@method is currently not supported. I can add it, though, I guess it won't be hard.
I guess some Scala-ish syntax like that will look good:

/** 
 * @method array getData() = [
 *     'name' => 'John',
 *     'age' => 27,
 *     'children' => ['Tiffany', 'Bob', 'Marta'],
 * ]
 */

I'll write back when this is done (in few days maybe).


By non-static code you mean something like this?

$product = ['name' => 'insurance', 'quantity' => 3, 'price' => '80.00'];
/** @var $sale = ['sellingPrice' => '300.00', 'product' => $product] */
$sale = $this->sellProduct($product);

If so, there is no such feature yet either. Though I guess it could be added as well, if this is what you meant...

@klesun klesun reopened this Feb 28, 2019
@klesun klesun self-assigned this Feb 28, 2019
@klesun
Copy link
Owner Author

klesun commented Feb 28, 2019

2019.02.28.001 - added support for @method doc return type.
image

@bravoman, please, confirm that I understood you correctly about the non-static php code before I start implementing it.

@klesun klesun closed this as completed Feb 28, 2019
@bravoman
Copy link

bravoman commented Mar 7, 2019

Sweet! Yes, that is what I meant!

With regards to the non-static code I meant something like this:

/**
 * @method getById(int $id) = [
 *     'id' => 1,
 *     'username' => 'user_name'
 * ]
 * @method getAll() = [
 *     0 => self::getById(1)
 * ]
 */
class users_table {
}

Edit: it seems this already works if I use users_table::getById() instead of self::getById(1) 🙇 👍

😅

@bravoman
Copy link

bravoman commented Mar 7, 2019

I see this even works (assuming database is setup correctly in PHPStorm):

/**
 * @method getById(int $id) = (new \PDO('mysql:host=localhost;db=my_db_name;', 'random', 'random'))
 *                                ->query('SELECT * FROM domain_comments LIMIT 1')
 *                                ->fetch(\PDO::FETCH_ASSOC)
 * @method getAll() = [
 *     0 => users_table::getById()
 * ]
 */
class users_table {
}

You're my new hero ... 🏆 😉

@klesun
Copy link
Owner Author

klesun commented Mar 7, 2019

Glad you liked it.
I'll make sure references like self::getById() and $this->someField will work as well in a while.

@klesun klesun reopened this Mar 7, 2019
klesun added a commit that referenced this issue Mar 9, 2019
@klesun
Copy link
Owner Author

klesun commented Mar 9, 2019

Added support for $this-> and self:: in the doc in 2019.03.09.001
image

@klesun klesun closed this as completed Mar 9, 2019
@klesun klesun mentioned this issue May 18, 2019
@voku
Copy link
Contributor

voku commented May 19, 2019

@param $sale = self::createSale() works very good here, but "{@inheritdoc}" is not supported. Any plan to support it?

PS: thanks for this plugin, I really miss auto-completion in legacy projects :)

@klesun
Copy link
Owner Author

klesun commented May 20, 2019

@voku, thanks for noticing, the type info from the doc is supposed to be inherited (even without the @inheritdoc), but apparently this does not work when you extend the class as you say, that's a bug, re-filed it in #85

@AustinGrey
Copy link

Thank you for an awesome plugin, I can finally document my options arrays and avoid all the mess of trying to keep track of all the options. However I'm curious how far can I take the documentation. Is it possible to document each key like I would a fully fledged parameter (specifying type and default value)? Right now I'm looking at this example:
image
I see that the parameter 'filter_active' was suggested, but I would like to type hint that it should be an integer. It's easy enough to specify a default value like 0 which would also correctly type hint integer, but for this specific instance there is no default value, the lack of the option is the default! How can I specify the type without specifying a default?

@klesun
Copy link
Owner Author

klesun commented Jun 13, 2019

@AustinGrey, maybe psalm notation is what you are looking for? It's also mostly supported by the plugin:
image

Click to expand code

/** 
 * @param $person_id
 * @param array{
 *     filter_active: int, 
 *     filter_pokemon: int|string, 
 *     filter_min_date: string, 
 *     filter_max_date: string, 
 *     ignored_pokemon_ownership_ids: array<int>,
 * } $options 
 */
public function get_owned_pokemons($person_id, $options=[])
{
    $options[''];
}

The downside is that currently this format does not allow typing a comment next to a key, but I'm going to add such ability in future releases - #94

I also accidentally found out that phpstorm syntax tree parser chews such writing pretty well:
image

Click to expand code

/**
 * @param $person_id
 * @param $options = [
 *     'filter_active' => (int), // if true, only active pokemon shown
 *     'filter_pokemon' => (int) ?: (string), // only shows those pokemons whose name or id appear in the array
 *     'filter_min_date' => (string),
 *     'filter_max_date' => (string),
 *     'ignored_pokemon_ownership_ids' => [(int)], // an array of pokemon_ownership_ids that will not be included
 * ]
 */
public function get_owned_pokemons($person_id, $options=[])
{
    $options[''];
}

@AustinGrey
Copy link

Oh man this is great! Yeah looks like psalm/phan notation would be the ideal but since there's no comments allowed yet I'll use the casting format you found second until comments come. Thanks!

@klesun
Copy link
Owner Author

klesun commented Jun 13, 2019

@AustinGrey, I just added basic comments support in psalm format in 2019.06.13.001:
image

Click to expand code
/**
 * @param $person_id
 * @param array{
 *     filter_active: int, // comment 1
 *     filter_pokemon: int|string,
 *     filter_min_date: string, // comment 2
 *     filter_max_date: string,
 *     ignored_pokemon_ownership_ids: array<int>, // comment 3.1
 *                                                // comment 3.2
 * } $options
 */
public function provide_psalmKeyComments($person_id, $options=[])
{
    $options[''];
    return [
        [$options, ['filter_active', 'filter_pokemon', 'filter_min_date', 'filter_max_date', 'ignored_pokemon_ownership_ids']],
    ];
}

@miquelbrazil
Copy link

Hey @klesun, in reference to the following:

@AustinGrey, I just added basic comments support in psalm format in [2019.06.13.001]

Just thought I'd double check (and I may be doing something wrong) but does this newer psalm commenting support only work for the @param tag? I tried using it with the @var tag with no luck. I can definitely use the parenthetical type + comment format you discovered above if the new format doesn't work for the @var tag yet.

Dope plugin BTW.

@klesun
Copy link
Owner Author

klesun commented Jun 27, 2019

Hi, @miquelbrazil.
From what I observe, @var does seem to work: (at least in v2019.06.25.001)
image
I would guess that there is a mistake somewhere in your type expression (missing coma, or => instead of : or quotes around key names, or something...). It's also possible that some of constructs you are using are not supported by the plugin yet (like class-of, key-of, or something more trivial that I could have missed). If you post an example code, I could debug it.

I'm also opening a ticket to make parser still provide at least some completion even if it fails at some point to make it clear where you have the mistake if any - #99

@miquelbrazil
Copy link

Thanks for the quick reply @klesun

So a couple of things I realized in my noobish-ness with trying to get better at commenting:

  1. I was attempting to make a docBlock on an array that already presented a defined structure (which is probably really silly to do)
  2. I was using a @var tag over an object field when I should have been using a @Property tag in a class-level docBlock.

Nonetheless, my original version looked like the following:

Screen Shot 2019-06-27 at 7 52 30 PM

You can see the parser accurately detected the current value of the element after the = and the possible types are present on the far right. But it doesn't have any of the comments in the auto-suggest dropdown. This is actually similar to your screenshots you've attached. The PHP Tree Parser version includes the comments after the equal sign in your screenshots and I think I mistakingly believed that the psalm notation would do the same thing. But now I'm assuming your feature addition to add comments to the psalm format was about creating readability/commenting in the docBlock while still detecting the types – not to make those comments show up in the deep-association drop down menu as well (I could be wrong though).

All that said, I reformatted the docBlock to a class-level block and changed the tag to @Property. In this variation, I got the following:

Screen Shot 2019-06-27 at 7 54 20 PM

Correct current value, but no type detections and no comments. I guess it's safe to say that @Property might not be a supported tag (but when I think about it, why would it need to be since you generally could define the structure of the property within the class anyway).

Which led me to my next iteration – psalm notation in the docBlock and comments following each array element:

Screen Shot 2019-06-27 at 7 56 29 PM

Now we have comments but no type detection. Which still supports my previous theory about psalm notation possibly being unsupported for the @Property tag.

So my final attempt was to see how well the tree parser format worked with comments on the @Property tag:

Screen Shot 2019-06-27 at 8 03 52 PM

Now we have current value, comments and type detection (I realize mixed isn't an actual type so I'm not surprised that didn't work correctly).

So I think I will go with the final format, but I figured writing all these observations up might be helpful as you work on the plugin. So sorry it's so long. A lot of this is my desire to get better at writing phpDocumentation (which is how I stumbled upon your plugin in the first place).

Thanks so much again for looking at this and being so responsive.

@klesun
Copy link
Owner Author

klesun commented Jun 28, 2019

But it doesn't have any of the comments in the auto-suggest dropdown

It's in my TODO list - #94 (comment) (will implement it this weekend I hope)

Which still supports my previous theory about psalm notation possibly being unsupported for the @Property tag

Yeah, plugin does not support psalm format in the @property yet. Filed an (anniversary) issue for that - #100

Thanks for the detailed explanation!

@ktomk
Copy link

ktomk commented Jan 21, 2020

Do you have a hint for hinting the return value of a function/method that is a generator / returning a Generator which generates an array (list) each is an array (list) which would map what I've read from the Psalm syntax like so:

array{0: JobType, 1: int, 2: TaskType}

so to say many of them. The exemplary code of the generator would look like:

function generator(): Generator
{
    foreach(range(1, 3) as $number) {
        yield [new JobType(), $number, new TaskType()];
    }
}

And here is the usage example:

foreach (generator() as list($job, $number, $task)) {
    $job->execute();
    $task->executed($job);
}

(simplified example, point in case is here that $job is being recognized of JobType)

I tried with * @return Generator|[array{0: JobType, 1: int, 2: TaskType}] and other things but didn't come to something working.

I also dropped the Generator| in front. Basically I don't know if it would work with | here at all.

Any feedback really appreciated.

/E: I could make some progress with a line like this (note the [] at the end):

* @return Generator|array{0: JobType, 1: int, 2: TaskType}[]

I can ctrl+click in Phpstorm the method ->execute() and it then finds the correct one (incl. type hierarchy). However $job is not identified as the type but some obscure bool|float|int|null|string (PHP skalar?). Therefore Phpstorm says "Method 'execute' not found in bool|float|int|null|string".

Any idea?

@klesun
Copy link
Owner Author

klesun commented Jan 21, 2020

@ktomk I have a strong feeling that what you are looking for is PSALM's templates syntax:

 * @return Generator<array{0: JobType, 1: int, 2: TaskType}>

image

@ktomk
Copy link

ktomk commented Jan 25, 2020

Thanks for this example, it does work as well and the result looks the same: Your Phpstrom extension provides the correct hints, it's just that Phpstorm can't infer the correct type for $job. So probably it would also good to learn about what Phpstorm can actually parse for "typed" array return values for me to make this gap smaller.

@klesun
Copy link
Owner Author

klesun commented Jan 25, 2020

@ktomk note, there is an open issue with typing of list() in foreach(), it's likely related to what you are experiencing. You can upvote it - there will be 4 of us ;)

Upd.: oh, lol, they're going to finally fix it in 2020.1

@tminich
Copy link

tminich commented Jan 29, 2020

I've noticed that dac is doing some inference for non-array parameters as well, like in this example
2020-01-29 094014

Is there a way to achieve this effect via phpdoc? Basically annotating 'this parameter should only be/ususally is one of these'.

@klesun
Copy link
Owner Author

klesun commented Jan 29, 2020

@tminich, you can do it like this:

/** @param $parameters = ['keyword1', 'keyword2', 'keyword3'][$any] */
function my_test($parameters) {}

image

(added to the description)

@tminich
Copy link

tminich commented Jan 29, 2020

Awesome, thanks a lot

@JustACatRu
Copy link

Hello!
Can't understand how to make this work for any numeric key of array?
It suggests only for the first (0) key:
image
But not for any other:
image
I think I'm missging something...
PS: IDE is 2020.1.1
Thanks!

@ktomk
Copy link

ktomk commented Jul 21, 2021

It suggests only for the first (0) key [...] But not for any other

most likely b/c there is only the first (0) key in the @var annotation? (not reproduced as the information in the image is without text-form).

@JustACatRu
Copy link

JustACatRu commented Jul 21, 2021

most likely b/c there is only the first (0) key in the @var annotation?

That is the question: How to make it think that there is not only first (0) key but over 9000 keys? :)

without text-form

Sorry for that, here is the text form of this exaple:

/**
 * @var $employees = [[
 *                  'salary' => 800.00,
 *                  'position' => 'frontend developer',
 *                  'fullName' => 'Pupkin Vasja',
 *                  ], ...]
 */
$employees = json_decode('dummy');
$employees[1]['']

@ktomk
Copy link

ktomk commented Jul 22, 2021

That is the question: How to make it think that there is not only first (0) key but over 9000 keys? :)

hard to answer honestly (for myself) as I don't know and would need to guess (never tried before)

which brings me to the idea if not already

/**   @var $employees = [
 *                  'salary' => 800.00,
 *                  'position' => 'frontend developer',
 *                  'fullName' => 'Pupkin Vasja',
 *                  ][]
 */

perhaps works? would offer the "typed" array as an array (similar to what Phpstorm allows to annotate, albeit it has types as names, not the [...] kind of definition IIRC.

@JustACatRu
Copy link

perhaps works?

Nope :(
image
And even:
image

@ktomk
Copy link

ktomk commented Jul 22, 2021

Nope :(

As there weren't three guesses yet, maybe a psalm type [1] ?

/**
 * @psalm-type PhoneType = array{phone: string}
 * @var $employees PhoneType[]
 */

1: https://psalm.dev/docs/annotating_code/supported_annotations/#psalm-type

@klesun
Copy link
Owner Author

klesun commented Jul 22, 2021

Hi. @JustACatRu, you probably want:

/**
 * @var $employees = [
 *     $index => [
 *         'salary' => 800.00,
 *         'position' => 'frontend developer',
 *         'fullName' => 'Pupkin Vasja',
 *     ]
 * ]
 */
$employees = json_decode('dummy');
$employees[1]['']

image
image

You can also express that with inline PSALM notation:

/**
 * @var array<array{
 *     salary: float,
 *     position: string,
 *     fullName: string,
 * }> $employees
 */
$employees = json_decode('dummy');
$employees[5]['']

@psalm-type does not work currently sadly, but there is a task, PRs welcome: #158

@JustACatRu
Copy link

maybe a psalm type [1] ?

Neither... But thanks for trying!

you probably want:

Yep! This one is what I want! Works like a charm. Thanks!
And, @klesun, I think this example should go strait to this page:
https://github.com/klesun/deep-assoc-completion/blob/master/docs/deep-keys-overview.md
(Yes, I looked there before asking :) with no luck...)

PS: Thank you for the great plugin!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: documentation Not an issue, but rather a document describing how something works type: feature request
Projects
None yet
Development

No branches or pull requests

8 participants