How to document magic (_call and _callStatic) methods for IDEs
Asked Answered
D

2

95

After many happy years coding in notepad++ and sublime, I've been advised to give a PHP IDE a go. I'm trying out phpStorm and it seems nice. The code completion and documentation is a great feature but isn't working out for me when magic methods are used. Is there a work around to get phpStorm to understand what's going on in magic methods?

Our situation is something like this:

abstract class a {
    public static function __callStatic($method,$args)
    {
        if(strpos($method,"get_by_") === 0)
        {
            //do stuff
        } elseif(strpos($method,"get_first_by_") === 0) {
            //do stuff
        } elseif($method == "get_all") {
            //do stuff
        }
    }
}

class b extends a {
    // some more stuff
}

b::get_by_user_id(27);
b::get_first_by_id(156);
b::get_all();

The magic callStatic method allows us to get a collection of objects via 1 or more arguments that make up the function call.

I see that there is an @method statement for use in these cases but phpStorm is only picking up the first of these statements. Furthermore I can only set the return type to mixed where as I'd prefer to be able to set it as whatever class this was called on (b in my example).

Any ideas or suggestions would be very gratefully received, thanks.

Dastard answered 26/3, 2013 at 9:54 Comment(4)
WHY WOULD ANYONE THINK THAT OVERRIDING _call IS A GOOD IDEA?!!Goliath
Gotta say, +1'd Brian's comment in the event that any sane person is going to find this question. Magic methods are for all intents and purposes: undocumentable (try to document a(n) [parameter|precondition|postcondtion|exception] to a magic method), not IDE-friendly (try to step debug a magic method), resilient to refactoring (please, don't even consider trying to refactor a magic method in a tenured piece of software), and LAZY (ok, the last one might be construed as an opinion).Pat
-1 to the opinion in the comment by @LukeA.Leber as it evidences a lack of vision. While magic methods are not a way to write less code (if you are using them to be lazy), magic methods make architectures possible that simple would not otherwise possible or that would be so outrageously complex it would not be worth writing. And they are completely IDE friendly when using PHPDoc. Note that most of the time you do not need magic methods, but when you need them there is no substitute (in PHP.) When they are used in a very structured manner using them is a completed valid solution.Equate
Don't think overriding __call is a bad idea. It's all about implementation. The implementation shown in the question above definitely wouldn't be the best way, but for chain-able API's, it allows a lot of flexibility.Severus
N
166

Use class-level PHPDoc comment -- specifically @method tag -- works fine in PhpStorm:

/**
 * @method static someClass get_by_user_id(int $id) Bla-bla
 * @method static someClass get_first_by_id(int $id) 
 */
abstract class a {
...

In the above:

  • @method -- PHPDoc tag
  • static -- tells that this is static method
  • someClass or $this -- return type
  • get_by_user_id -- method name
  • (int $id) -- method signature: ([[type] [parameter]<, ...>])
  • Bla-bla -- some optional description

More about @method:

P.S. While @method static works fine in PhpStorm (tells IDE that method is static) it may not be (yet?) supported by actual phpDocumentor tool (sorry, have not used it for a while).


Alternatively: (in PhpStorm, of course) Settings | Inspections | PHP | Undefined | Undefined method --> Downgrade severity if __magic methods are present in class -- it will not help with code completion for such methods in any way, but will not mark those magic methods as "undefined method" errors.


phpDocumentor's ticket regarding using RegEx/partial names for @property/@method tags (how it can be useful for documentation and how little help it may bring to the actual IDE when dealing with code completion):

Nitz answered 26/3, 2013 at 10:17 Comment(8)
Thanks, this looks like a reasonable suggestion and it certainly works in phpStorm, but I'm a bit loathed to write out the potentially hundreds of lines of @method at the top of each class. You see the get_by_* method can be prepended by any of the objects parameters to get objects of that type by the specified parameter. Even excluding the possibility of get_by__and_ i'd end up with about 1500 @methods across 140 different class. Is there not a more generic way to provide documentation?Dastard
No. All magic methods must be declared specifically (that's the main point of documenting this way) - PHPDoc does not understand partial names (e.g. get_by_*(int $id)). For IDE (code inspection, not completion!) you have alt solution (disable warnings). For phpDocumentor (or alternative tool) - no solution known to me (maybe it is there, but I do not know about it). You have the link to github - file new ticket and ask for adding such "partial names" matching functionality - see what they will say (most likely will be rejected). If it will be implemented, then IDE may have it as well later.Nitz
github.com/phpDocumentor/phpDocumentor2/issues -- but please check if similar ticket does not exist before posting yours.Nitz
Thanks for all of that. There is a currently open ticket with regards to this but it all seems to have gone quiet. I've stuck a comment in there and we'll see what comes of it.Dastard
Just for the reference, the phpDocumentor's ticket (so other users know what ticket you are talking about; also added it to the answer itself): github.com/phpDocumentor/phpDocumentor2/issues/689Nitz
It looks like I've openned a can of worms there!! This could get interesting.Dastard
The documentation says "@method tags MUST NOT be used in a PHPDoc that is not associated with a class or interface.". Therefore if you want to reference a method from another class, you can specify the @uses annotation and it works well.Nakia
Here is the updated link to the @method tag documentation page : docs.phpdoc.org/latest/guide/references/phpdoc/tags/… (previous links in the answer and comments are broken/outdated).Rrhagia
A
6

Somewhat related to original question:

You can also define this in phpstorm meta file. Here's an example for factory method (v2016.3):

// Define in .phpstorm.meta.php
namespace PHPSTORM_META {
    $STATIC_METHOD_TYPES = [
        \Factory::create('') => [],
    ];
}

// Then use in code
$factory = new \Factory();
$user = $factory->create(\User::class);
// Here you get autocomplete.
$user->subscribe();

This way you don't have to docblock every possibility when magic happens.

Have some docs for details.

Aparejo answered 27/11, 2016 at 10:28 Comment(1)
This doesn't work with __call. It's also not documented and invalid PHP. PHPStorm only provides support for where you have statically defined methods that return a mixture of types depending on the input.Gist

© 2022 - 2024 — McMap. All rights reserved.