30

I need to determine, after all files have been included, which classes extend a parent class, so:

class foo{
}
class boo extends foo{
}
class bar extends foo{
}

and I'd like to be able to grab an array like:

array('boo','bar');
Trey
  • 5,480
  • 4
  • 23
  • 30
  • http://stackoverflow.com/questions/538863/getting-the-name-of-a-child-class-in-php – Darragh Enright Jul 12 '11 at 21:34
  • not quite, that requires the constructor function to be run, I'm looking for declared classes that have not necessarily been run yet – Trey Jul 12 '11 at 21:37
  • 1
    Possible duplicate of [Get all extended Classes in PHP](http://stackoverflow.com/questions/16680040/get-all-extended-classes-in-php) – jeremy Feb 10 '16 at 01:48
  • 1
    Except those was asked 2 years before that.... – Trey Feb 10 '16 at 14:28
  • it depends on what do you want to do after getting all the classes that implement and interface/extend a class – Ahmad Hajjar Mar 14 '16 at 07:24

4 Answers4

31

Taking Wrikken's answer and correcting it using Scott BonAmi's suggestion and you get:

$children = array();
foreach( get_declared_classes() as $class ){
  if( is_subclass_of( $class, 'foo' ) )
    $children[] = $class;
}

The other suggestions of is_a() and instanceof don't work for this because both of them expect an instance of an object, not a classname.

MikeSchinkel
  • 4,947
  • 4
  • 38
  • 46
27

If you need that, it really smells like bad code, the base class shouldn't need to know this.

However, if you definitions have been included (i.e. you don't need to include new files with classes you possibly have), you could run:

$children  = array();
foreach(get_declared_classes() as $class){
    if($class instanceof foo) $children[] = $class;
}
Wrikken
  • 69,272
  • 8
  • 97
  • 136
  • 3
    this is basically what I have, except I'm using `is_a` instead of `instanceof`... and I understand why you would think that, but the base class doesn't actually need to know anything, this is more like a plugin manager function:) – Trey Jul 12 '11 at 21:52
  • Ack, that could be a valid case. If that's basically what you have, does it not work / is there an issue, or are you just asking to confirm there isn't a better/faster method? – Wrikken Jul 12 '11 at 21:55
  • Check, if it's in a plugin-manager with volatile plugins, you might want to perform a syntax check on files before including them to prevent fatal parse errors, but that's about it I guess. – Wrikken Jul 12 '11 at 22:01
  • 5
    It's a bit old, but you could use `is_subclass_of` [(link)](http://www.php.net/manual/en/function.is-subclass-of.php) instead of `is_a` and `instanceof`. – sbonami Nov 07 '12 at 18:38
  • 4
    Thanks @ScottBonAmi; your answer worked but `is_a()` and `instanceof` did not for me because both expect an object, not a classname. – MikeSchinkel Aug 15 '13 at 17:28
  • 1
    Normally this should fail, because modern applications will use autoloading and in this case classes are not defined. get_declared_classes() will return a list of an undefined application state, because some classes are loaded and others not. – Matthias Kleine Nov 05 '16 at 22:31
  • @MatthiasKleine: indeed, hence the _"However, if you definitions have been included (i.e. you don't need to include new files with classes you possibly have)"_ there. If this isn't the case, it's impossible to do in a sane way. But as stated, the requested functionality itself is a bit iffy already :) – Wrikken Dec 21 '16 at 11:22
  • 3
    I'd like to add that, this does not smell like "Bad Code" -- it's more in the lines with Dynamic Class Reflection, which is why reflection functions exist and why there is a lot of benefit to dynamic class creation / loading. – phillihp Jan 29 '17 at 17:31
  • @phillihp: I disagree. If you are talking about knowing parent classes, implemented interfaces, or even traits, I'm with you. Knowing all possible classes (that are potentially not loaded or known yet) that extend a class is not. This in no way blocks you from creating dynamic classes in any way, nor is it a requirement for it. Still, if a (dynamic or not) class wants to be used by a certain instance or class, the proper way to do that is signaling, not auto-discovery, as this would limit your options to blacklist or filter this list (undefining a class is to my knowledge not possible in PHP). – Wrikken Jan 30 '17 at 19:10
  • 1
    how could `($class instanceof foo)` work of `$class` is a string and not a class. Don't you need somehow to create an instance of it? – Junior Dec 14 '17 at 21:45
  • 1
    THIS ANSWER DOESN'T WORK! `instanceof` expects an instance, not a string. Use `is_subclass_of()` (as suggested below) which does. – Alessandro Lai Dec 19 '17 at 10:47
7

Use

$allClasses = get_declared_classes();

to get a list of all classes.

Then, use PHP's Reflection feature to build the inheritance tree.

Kerem
  • 11,377
  • 5
  • 59
  • 58
SteAp
  • 11,853
  • 10
  • 53
  • 88
  • I know this is old now, but thank you for pointing me to the reflection classes, they lack the complete documentation of most of php.net, but they are extremely useful in a few different places +1 – Trey Aug 21 '11 at 19:13
3

I am pretty sure that the following solution or some thing like that would be a good fit for your problem. IMHO, you can do the following (which is kind of observer pattern):

1- Define an interface call it Fooable

interface Fooable{
    public function doSomething();
}

2- All your target classes must implement that interface:

class Fooer implements Fooable{
    public function doSomething(){
         return "doing something";
    }
}

class SpecialFooer implements Fooable{
    public function doSomething(){
         return "doing something special";
    }
}

3- Make a registrar class call it the FooRegisterar

class FooRegisterar{
    public static $listOfFooers =array();

    public static function register($name, Fooable $fooer){
         self::$listOfFooers[$name]=$fooer;
    }
    public static function getRegisterdFooers(){
         return self::$listOfFooers;
    }
}

4- Somewhere in your boot script or some script that is included in the boot script:

FooRegisterar::register("Fooer",new Fooer());
FooRegisterar::register("Special Fooer",new SpecialFooer());

5- In your main code:

class FooClient{

    public function fooSomething(){
         $fooers = FooRegisterar::getRegisterdFooers();
         foreach($fooers as $fooer){
              $fooer->doSomthing();
         }
    }
}
Ahmad Hajjar
  • 1,796
  • 3
  • 18
  • 33
  • If you look at the comments and the accepted answer, the problem is that I 1.) didn't want to have to know about the child classes ahead of time (your step 4 requires me to register each child class) and 2.) I didn't want to actually instantiate the classes, just have a list of them. – Trey Mar 14 '16 at 14:26
  • 1
    let me ask you the question in a different way, why would you need this? I mean why would you need to get all the classes that do inherit a class without knowing them ahead of the time? – Ahmad Hajjar Mar 15 '16 at 11:11
  • 1
    You marked your question as OOP that's why I am posting this answer because this is how an OOP solution looks like :) – Ahmad Hajjar Mar 15 '16 at 11:12
  • not to mention that the accepted answer may imply a "performance issue" – Ahmad Hajjar Mar 15 '16 at 11:14
  • 3
    Doesn't really matter why, when I asked this 5 years ago I was building a plugin auto loader, knowing that I could potentially have a few hundred plugins, but only a dozen on some page loads, the accepted answer allows me to cache the results and doesn't require that I know about all of the classes or instantiate them. – Trey Mar 15 '16 at 17:26
  • I understand plugins as things that can be loaded in a system by registering them into the system using some sort of registration method (that will allow you to identify what and how many plugins do you have). What I mean is, if you want to apply an object oriented solution it has to be fully OOP right? – Ahmad Hajjar Mar 16 '16 at 02:27
  • 1
    Not sure how many different ways I can say this. The plugin system itself is OOP, I was looking for a dynamic way to find and register these plugins without instantiating them, your "solution" would be a possible way to implement the registration of plugins on page load, but doesn't address the dynamic need of discovering the plugins. Where do you get "Fooer" and "Special Fooer" from if you don't already know they exist? – Trey Mar 16 '16 at 16:06
  • no need to be nervous and to talk that way I am sorry to answer your question ... never mind :) – Ahmad Hajjar Mar 17 '16 at 07:08
  • 1
    no one is nervous, you didn't answer my question, you provided a solution that would be perfectly acceptable if the requirements were different. – Trey Mar 17 '16 at 16:33