6

Is something like the following possible in PHP?

$blah = 'foo1';

class foo2 extends $blah {
    //...
}

class foo1 {
    //...
}

This gives an error.

I want to dynamically set $blah so I can extend whatever class I want.

Edit: The reason for wanting to do this because I wanted to use a function out of another class in a related class. In the end it would have been something like:

Final extends foo1 extends foo2 extends foo3 extends foo4 extends parent { ... }

In the end I decided to instantiate the other class within the class and use it. Not the best options because they both you 2 of the same classes, but this won't be used that often, so it will work for now.

Darryl Hein
  • 142,451
  • 95
  • 218
  • 261

8 Answers8

16

You're assuming here php executes top to bottom, but it doesn't quite work like that:

<?php
foo();  # works

function foo(){
  print "bar";
}

<?php

foo();  #dies

if( $i == 1 )
{
  function foo(){
    print "bar";
  }
}

<?php
$i = 1;
if( $i == 1 )
{
  function foo(){
    print "bar";
  }
}

foo(); #works

Now, although you can conditionally create classes:

<?php

class A { }
class B { }
if( false ){ 
  class C extends B { 
    public static function bar(){
      print "baz"; 
    }
  }
}
C::bar(); # dies

You cant instantiate one at runtime from a variable:

<?php
class A { }
class B { }
$x = 'B'; 
if( false ){ 
  class C extends $x { 
    public static function bar(){
      print "baz"; 
    }
  }
}
C::bar();
---> Parse error: syntax error, unexpected T_VARIABLE, expecting T_STRING in /tmp/eg.php on line 7

There is a way to do it with Eval, but you really don't want to go there:

<?php

class A { }
class B { }
$x = 'B'; 
if( true ){ 
 $code =<<<EOF
  class C extends $x { 
    public static function bar(){
      print "baz"; 
    }
  }
EOF;

  eval( $code );
}
C::bar();
$o = new C; 
if ( $o instanceof $x )
{
  print "WIN!\n";
}
--->barWIN!

However, there is a more important question here:

Why the hell would you want to extend a different class at runtime

Anybody using your code will want to hold you down and whip you for that.

( Alternatively, if you're into whipping, do that eval trick )

Kent Fredric
  • 56,416
  • 14
  • 107
  • 150
  • 2
    In an ideal world people wouldn't want to do it. But it's not an ideal world. I looked this up because I'm stuck with Joomla changing the parent class for defining new "elements" between version 1.5.x and later versions. My subclass doesn't need to be different and I don't want different code bases, so I want to be able to inherit from different classes according to the version of Joomla. That's why. – user185631 Jul 26 '12 at 16:35
  • It seems as though Generics not possible with PHP, disappointing, I thought of hacking it this way! – Ziyan Junaideen May 05 '13 at 02:10
  • @user185631 Came here for the exact same reason. – mavrosxristoforos Oct 15 '13 at 17:32
  • Why the hell would you want to extend a different class at runtime? Testing, mock a static method :( – Abdillah Jun 27 '18 at 09:43
  • @Abdillah PHP is the wrong tool for the job there. The reason being throwing eval into the mix presents unfathomable levels of risks for security exploits. That said, you can still do this, but don't fool yourself that what you're doing is "nice" in any way, and make sure you're not tempted to employ that code in your production stack, just as a precaution. – Kent Fredric May 16 '19 at 01:13
  • Need it for testing. Need to inject a protected variable into the a class to mock being an authed user to test a bugfix. – Austin Burk Jun 16 '21 at 23:21
4

I know this question was asked a long time ago, but the answer is relatively simple.

Assuming you want to extend class foo if class foo exists, or class bar if it doesn't, you'd use:

if(!class_exists('foo')) {
    class foo extends bar {
        function __construct() {
            parent::__construct();
        }
    }
}

class myclass extends foo{
    //YOUR CLASS HERE
}
whizzkid
  • 324
  • 2
  • 6
  • That's an idea that may work...kinda ugly if there are say 5 possible classes that you may want to extend from. – Darryl Hein Oct 04 '12 at 06:02
  • Agreed, but from one of your comments it looks like you're developing for Joomla. I assume the class you're trying to extend is either available or not, depending on the version of Joomla, so I'm guessing the possible classes would be limited to two or so. Unfortunately there's no easy way (that I know of) to use a variable in lieu of a class name, i.e. you can't write class myclass extends $classvar {} so this is the best alternative. You're right though... 5+ possible classes it'll get ugly! – whizzkid Oct 04 '12 at 19:29
  • There is no need to add __construct to foo. – Kjeld Jun 15 '18 at 19:42
3

Using PHP overloading you can accomplish this to a certain extent.

class variable_class {
    public $orginalBaseClass;
    public $orginalArgs;

    public function __construct() {
        // Get constructor parameters.
        $this->orginalArgs = func_get_args();
        // Get class name from args or 3rd party source.
        $classname = 'stdClass';

        // Pass along args to new class.
        $this->orginalBaseClass = new $classname($this->orginalArgs);
    }

    public function __call($name, $arguments) {
        // Pass all method calls to the orginalBaseClass.
        return call_user_func_array(array($this->orginalBaseClass, $name), $arguments);
    }
}

I'm using this pattern inside a Drupal module for prefetching data from the cache.

Manoj Sharma
  • 1,467
  • 2
  • 13
  • 20
mikeytown2
  • 1,744
  • 24
  • 37
2

I don't see how this would be particularly useful, but to answer your question... no. There's no way to dynamically do that because the generated class has to be instantiated before the variable is evaluated (if that makes sense).

To put it simply: The class must exist before the code can be properly executed.

Jeff Hubbard
  • 9,822
  • 3
  • 30
  • 28
2

I assume that this is for ease-of-maintenance, right? Extending a class at run time really is pretty crazy.

class SuperClassOne { /* code */ }
class SuperClassTwo { /* code */ }

class IntermediateClass extends SuperClassOne { /* empty! */ }

class DescendantClassFoo extends IntermediateClass{ }
class DescendantClassBar extends IntermediateClass{ }
class DescendantClassBaz extends IntermediateClass{ }

Then, when you want to change all your DescendantClass* classes, you just have to change what the IntermediateClass extends:

class IntermediateClass extends SuperClassTwo { }
Darren Felton
  • 2,299
  • 1
  • 18
  • 18
nickf
  • 537,072
  • 198
  • 649
  • 721
2

If you don't have too many values for $blah, you could extend each one in a different file then require_once "classes/foo_$blah.php"

Otherwise, you're stuck with the eval() solution... good luck with that... :)

Greg
  • 316,276
  • 54
  • 369
  • 333
1

I tested something with defines and barking:

<?php
    define("INHERIT",A);

    class A{
        public function bark(){
            return "I'm A";
        }
    }
    class B{
        public function bark(){
            return "I'm B";
        }
    }

    class C extends INHERIT{}

    //main?
    $dog = new C();
    echo $dog->bark();
?>

the output is:

Fatal error: Class 'INHERIT' not found in D:\sites\inherit.php on line 15

so the answer for "are variable class extensions possible?" is: No.

SparK
  • 5,181
  • 2
  • 23
  • 32
  • you can however create a class that can inherit from anything by inheriting a MultipleInheriter class that has a list of classes in an array and then it calls methods with __call in a static context so the $this inside the methods gets translated to the current class. Only public METHODS would work, so it's a really fake inheritance. You would then call the extend() method and add a class to the list. – SparK Nov 03 '11 at 17:35
-2

you should have tried $$

$blah = 'foo1';
class foo2 extends $$blah {
    //...
}

class foo1 {
    //...
}
Darryl Hein
  • 142,451
  • 95
  • 218
  • 261