86

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.

Rob Forrest
  • 6,682
  • 7
  • 48
  • 67
  • 1
    WHY WOULD _ANYONE_ THINK THAT OVERRIDING `_call` IS A GOOD IDEA?!! – Brian Gordon Dec 12 '14 at 00:45
  • 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). – Luke A. Leber Aug 03 '16 at 00:26
  • 18
    -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. – MikeSchinkel Nov 04 '17 at 03:05
  • 5
    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. – Steve Bauman Mar 05 '18 at 17:44

2 Answers2

150

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):

LazyOne
  • 137,403
  • 37
  • 338
  • 342
  • 2
    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? – Rob Forrest Mar 26 '13 at 10:57
  • 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. – LazyOne Mar 26 '13 at 11:30
  • https://github.com/phpDocumentor/phpDocumentor2/issues -- but please check if similar ticket does not exist before posting yours. – LazyOne Mar 26 '13 at 11:32
  • 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. – Rob Forrest Mar 26 '13 at 11:58
  • 2
    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): https://github.com/phpDocumentor/phpDocumentor2/issues/689 – LazyOne Mar 26 '13 at 12:14
  • It looks like I've openned a can of worms there!! This could get interesting. – Rob Forrest Mar 26 '13 at 16:28
  • The [documentation](https://docs.phpdoc.org/latest/references/phpdoc/tags/method.html) 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](https://docs.phpdoc.org/latest/references/phpdoc/tags/uses.html) and it works well. – Guillaume Renoult Feb 09 '20 at 23:43
4

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.

Yauheni Prakopchyk
  • 7,903
  • 3
  • 28
  • 34
  • 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. – jgmjgm Feb 07 '18 at 11:48