4

In PHP it is possible to get a full class name via class name resolution like this:

Example:

namespace Name\Space;
class ClassName {}

echo ClassName::class;

Output: Name\Space\ClassName

This is better than using the string Name\Space\ClassName directly in the code because code introspection especially in IDEs can find an error directly.

I wonder if there is something similar for methods of a class - this would be specifically useful for callback functions.

This is how you can basically can pass a callback:

$a = function($callback,$arg) { return $callback($arg); }

$a('getInfo',5);

Instead of passing a string here (which might change), I would prefer to do something like this:

$a(MyClass::class::getInfo,5);

With I "go to declaration" click in the IDE I could go directly to getInfo plus I see errors in case with method does not exist anymore. Is there a way to achieve what I want to do here?

Maksym Fedorov
  • 6,383
  • 2
  • 11
  • 31
Blackbam
  • 17,496
  • 26
  • 97
  • 150
  • 1
    [Possible duplicate](https://stackoverflow.com/questions/20301865/php-how-to-get-a-method-name-with-a-class-and-namespace-path-as-a-string). Some useful information can be found in the comments under the question, not just in the answer. – El_Vanja Feb 19 '21 at 13:47
  • @El_Vanja Thank your very much this is essentially the same question and this is a nice discussion. For being a duplicate I think the stackoverflow guidelines say "already has an answer here". But it seems like there is no adequate answer so it might be a PHP feature request? Or does somebody come up with a modern solution? – Blackbam Feb 19 '21 at 15:49
  • I only linked manually as a possible duplicate because, while essentially asking about the same thing, that question's author was very specific about looking for a shorthand expression and I wasn't sure if information there would provide a satisfactory answer for your case. – El_Vanja Feb 19 '21 at 16:00
  • @El_Vanja Yeah - thank you. My question is a little bit broader but chances are that the answer will be the same. – Blackbam Feb 19 '21 at 16:02

3 Answers3

3

In fact, you work with callable type. And PHP allows setting method/function name only as a string. But if you use classes and objects you will have a different way to set callback. For example:

$a = function($callback, $arg) { 
   return call_user_func($callback, $arg)); 
}

// call a static method of the class with 
// using fullname space and method name as a string
$a('Name\Space\MyClass::getInfo',5); 

// call a static method of the class 
// with using ::class 
$a([MyClass::class, 'getInfo'], 5);

// call a method of an object 
$myObject = new MyClass();
$a([$myOject, 'getInfo'], 5);
Maksym Fedorov
  • 6,383
  • 2
  • 11
  • 31
  • Thanks for the explanation. With `is_callable` you could even check if call_user_func will work, still this is a dynamic check at runtime which IDEs can not help you with - e.g. spelling mistakes? – Blackbam Feb 19 '21 at 15:51
  • 1
    @Blackbam Yep, you can't use autocomplete from IDE in this case, but for example, in PHPStorm you can navigate these methods with the help CTRL+ Left Click combination. And it can be used as a sort of checking of correctness – Maksym Fedorov Feb 22 '21 at 11:15
0

Three possibilities.

(1)

echo `__CLASS__`;

...returns namespace\classname as a string.

(2)

If you're trying to get the namespace\classname from another class, i.e., not the one where you're currently executing code, then I would suggest setting a public property inside each class such as:

public static $classname = __CLASS__;

which you could then access from anywhere as:

ClassName::$classname

Put it in each of your classes. Always use the same property name.

(3)

Have you considered the PHP function debug_backtrace() which returns a call stack with the most recent call at index = 0;

So, if:

$caller = debug_backtrace();

Then, $caller[0]['class'] contains the fully qualified class name, including any namespace, at the point where you called debug_backtrace().

I'm guessing that #2 is the solution that will work for you.

Just thought of a 4th possibility that doesn't depend on you adding any code to each class. Might add some overhead though, but so does my 3rd solution above.

(4)

$declared_classes = get_declared_classes();

This lists all of the classes currently declared within the PHP scope as fully qualified namespace\classname. You could search the returned array for partial string matches within the array and return the whole namespace\classname string.

One thing to keep in mind. You might have duplicates if different namespaces have same-named classes.

  • Good answer for classes but how to get a class method without a hardcoded string? – Blackbam Feb 25 '21 at 23:43
  • I don't understand your question @Blackbam. At some point, you have to know what you're searching for, whether its a variable that contains the method name, or a hard coded method. There is another PHP call: get_class_methods() that returns all of the public methods in a class, and get_class_vars() which returns all of the public properties in a class. Once you know the class, get_declared_classes() will return the list of fully qualified (namespace\class) within PHP's scope. – Paul Swarthout Feb 27 '21 at 01:31
  • The point why we use `mock(MyClass::class)` instead of `mock('MyClass')` is that modern IDEs will rename the argument when the class name is renamed. That's why it'd be nice to have something similar for methods. One could argue that methods are renamed more often than classes. – s3c Apr 15 '22 at 15:10
  • 1
    @s3c, as a workaround I've taken to using `[MyClass::class, 'myMethod'][1]` wherever I want to reference `myMethod`. PHPStorm, at least, has code completion on it, allows me to jump to `myMethod` from it, and will include it in the refactoring if I rename `myMethod`. – Elte Hupkes Jan 16 '23 at 09:41
  • Thanks. Tried `$test = Mockery::mock([Product::class, 'dateOfArrivalUtc'][1]);` but neither auto-complete, nor global rename, worked in VSCode. Glad it works in PHPStorm. – s3c Jan 17 '23 at 11:08
0

I've added this as a comment somewhere else but figured it might warrant an actual answer to this question. If you use:

$callback = [MyClass::class, 'myMethod'];

Then at least one IDE (PhpStorm) will recognize this as the callable that it is, allow you to navigate to it, mention it in "show usages" and automatically change it when it is renamed through a refactor. I use this in my code if, for instance, I reference a method in a test:

$this->mock(MyClass::class, function(MockInterface $mock) {
    $mock->shouldReceive([MyClass:class, 'myMethod'][1])->andReturn(10);
});

Not the cleanest syntax, but it's workable.

Elte Hupkes
  • 2,773
  • 2
  • 23
  • 18