bmewburn / vscode-intelephense

PHP intellisense for Visual Studio Code
https://intelephense.com
Other
1.63k stars 94 forks source link

Go to Definition for Magic Methods #243

Open TheColorRed opened 7 years ago

TheColorRed commented 7 years ago

I know that this may be very difficult to do, so I guess that this is more of a feedback post.

In laravel and other frameworks (and non-frameworks), magic methods are used for certain features, so I would suggest that it could be possible to define a method/property using PHPDoc.

I don't believe that there is a doc comment for this, so I will make one up called: @called in this example.

It would look something like this:

class A extends ClassWithMagic {
    /**
     * @called MyMethod
     */
    public function scopeMyMethod() {}
}

Example of what the extended class might look like:

class ClassWithMagic {
    public function __call($name, $args){
        $func = 'scope' . $name;
        $this->$func($args);
    }

    public function __get($name) {
        return $this->$name;
    }
}

Then it could be used something like this:

A::MyMethod();
// or
$a = new A;
$a->MyMethod();
// or
(new $a)->MyMethod();

It would know about the method due to the doc comment.

hworld commented 7 years ago

There is support for this in phpdoc. It's called @method. Documentation: https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md#711-method

Looks like intelephense already supports it and works fine.

Would look like this:

/**
 * @method string getString()
 * @method void setInteger(int $integer)
 * @method setString(int $integer)
 */
class Child
{
    <...>
}
TheColorRed commented 7 years ago

That somewhat works, maybe I am doing it wrong, but it doesn't go to the function, just where the comment is. Is there a way to use it so it goes to the method?

/**
 * @method void DefaultSelect()
 */
class GameCache extends BaseGameModel{
    public function scopeDefaultSelect($query, $filters){
        // Do stuff
    }
}
hworld commented 7 years ago

Oh, I see. So in the __call you end up funneling it to the scopeDefaultSelect (as an example)? Yeah, I don't think that phpdoc allows you to mark that at least...

TheColorRed commented 7 years ago

Yeah, that is a Laravel feature for doing database queries: https://laravel.com/docs/5.5/eloquent#local-scopes

Other frameworks may do something differently

artrz commented 7 years ago

I don't think this could be really helpful as code should not depend on an editor. Adding random (and non-sense for others) phpdoc tags will 'couple' the code to one editor plugin, not even an editor, but a plugin feature, ending on dirty code. I'm in a project where some people use Atom, others Sublime or even PhpStorm. I see no reason to have code with custom stuff so each ones editor works, and I cannot imagine my self tagging a scope with @called for me, tagging whatever method with @forwarded so it works on some random Atom plugin and @scopedMethod for my mate using a Sublime plugin. However, suggesting this tag to the phpDoc team may be helpful, some kind of mix between @method and @see

TheColorRed commented 7 years ago

I did submit something after. No reply yet though

artrz commented 7 years ago

I did submit something after.

Cheers mate!

I'd love to have this feature supported on phpDoc blocks

bmewburn commented 7 years ago

Good discussion, I don't think introducing a new phpdoc tag is appropriate, I'd rather stick with current standards. @arturock mentions @see. I wonder if this would be suitable in itself. Document _call with @see tags and then go to definition can provide multiple locations. It wouldn't distinguish between scopeMethodA and scopeMethodB but would at least provide useful information to the user to make a decision rather than just going to the @method in the class docblock.

bmewburn commented 5 years ago

I think the way to go here would be to use inline @see in the @method description.

/**
 * @method void DefaultSelect() {@see GameCache::scopeDefaultSelect}
 */
class GameCache extends BaseGameModel{
    public function scopeDefaultSelect($query, $filters){
        // Do stuff
    }
}
scott-davidjones commented 5 years ago

would this also work with, as an example, laravel facades: https://github.com/laravel/framework/blob/5.5/src/Illuminate/Support/Facades/DB.php

the @see directive is class level and not method level. Currently when using a facade no intellisense is provided.

dmitrach commented 3 years ago

A part of the implementation works with "barryvdh/laravel-ide-helper". It generates descriptions for classes like as @method void DefaultSelect() {@see GameCache::scopeDefaultSelect}. But it doesn't work to go to the definitionm only to the class definition. Also it needs to duplicate a description of the method.

The @called tag doesn't work with other editors. May be better to use known cases.

Laravel works with scopes as static and class methods: MyModel::myScope($var). But @called and :alias: don't change a type of the method.

I could be implemented with :alias: as @see number_of() :alias: to except duplicated descriptions. https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc-tags.md#512-see

In the other case it could be implemented with the class definition and jump to the original definition (optional) though the description with ClassName::method() :alias: or @see ClassName::method() or @uses ClassName::method() (they can have priorities). Also for class properties: @see self::$variable, @uses self::$variable.

/**
 * @method static SomeType someMethod() My description. @uses ClassName::originalMethod()
 * @method void someMethod2() My description. @see ClassName::originalMethod2()
 * @property mixed $someProperty My description. @uses self::getProperty()
 * @property mixed $someProperty2 My description. @see self::$originalProperty @see http://somedescription.com 
 */
class ClassName
{
    protected $originalProperty;

    public function originalMethod(): SomeType
    {
        // 
    }

   public function originalMethod2(): void
    {
        // 
    }

   public function getProperty()
   {
       return someChanges($this->originalProperty);
   }

    /**
     * @see self::getName() :alias:
     */
    public function getTitle(): string
    {
        return $this->getName();
    }

    /**
     * Returns a name.
     */
    public function getName(): string
    {
        return $this->name;
    }
}