4

I know you can extend a class when constructing it like the following:

class b extends a {
}

But is it possible to dynamically extend classes from the scripts? Such as:

$b = new b($input) extends a;

What I wish to accomplish is to extend the module differnetly wheither it's used in admin rather than the public pages. I know I can create two different parent classes by the same name and only include one per admin or public. But my question is, is it possible to do it dynamically in PHP?

5
  • Is class "a" built-in? Do you have control over changes to class "a"? Commented May 6, 2012 at 5:18
  • Not without a ton of caveats. What's the difference between the two and why would you prefer the second? Commented May 6, 2012 at 5:18
  • The purpose I had in mind was to extend the module in one way for admin/controlling purposes. And another for public served pages. The admin way would be a lot more complex and I like it lightweight. Commented May 6, 2012 at 5:23
  • So again, what's the difference between these two forms of writing the same thing? Commented May 6, 2012 at 5:26
  • no need eval. check my answer here stackoverflow.com/a/39179213/2456323 Commented Aug 27, 2016 at 8:51

7 Answers 7

3

No, not without an extension like RunKit.

You might consider an alternative approach. If you want B to assume the functionality of A, perhaps something like the following could provide a sort of "mixin" approach. The general picture is that, instead of B being a child of A, B delegates to A.

<?php

class MixMeIn
{
  public $favouriteNumber = 7;

  public function sayHi() {
    echo "Hello\n";
  }
}

class BoringClass
{
  private $mixins = array();

  public function mixin($object)
  {
    $this->mixins[] = $object;
  }

  public function doNothing() {
    echo "Zzz\n";
  }

  public function __call($method, $args)
  {
    foreach ($this->mixins as $mixin)
    {
      if (method_exists($mixin, $method))
      {
        return call_user_func_array(array($mixin, $method), $args);
      }
    }
    throw new Exception(__CLASS__ + " has no method " + $method);
  }

  public function __get($attr)
  {
    foreach ($this->mixins as $mixin)
    {
      if (property_exists($mixin, $attr))
      {
        return $mixin->$attr;
      }
    }
    throw new Exception(__CLASS__ + " has no property " + $attr);
  }

  public function __set($attr, $value)
  {
    foreach ($this->mixins as $mixin)
    {
      if (property_exists($mixin, $attr))
      {
        return $mixin->$attr = $value;
      }
    }
    throw new Exception(__CLASS__ + " has no property " + $attr);
  }

}

// testing

$boring = new BoringClass();
$boring->doNothing();
try {
  $boring->sayHi(); // not available :-(
}
catch (Exception $e) {
  echo "sayHi didn't work: ", $e->getMessage(), "\n";
}
// now we mixin the fun stuff!
$boring->mixin(new MixMeIn());
$boring->sayHi(); // works! :-)
echo $boring->favouriteNumber;

Just a zany idea. I hope I understood the question correctly.

Sign up to request clarification or add additional context in comments.

Comments

2

You can't, but this has been requested for a few years: https://bugs.php.net/bug.php?id=41856&edit=1

You can define the classes within an eval, but it's more trouble than declaring the class normally.

Comments

1

But you cannot use extends while object creation. extends is used in class definition only and defines which other class is "parent" for our new class.

2 Comments

Yes, and that's the reason for posting the question.
@tim: the reason should be described as-is, in the original way. What you currently want makes no sense
1

Alternatively, if you are comfortable with javascript-style inheritance and don't mind losing typechecking:

<? //PHP 5.4+
final class ExpandoLookalike {
    //Allow callable properties to be executed
    public function __call($name, $arguments) {
        \call_user_func_array($this->$name, $arguments);
    }
}

$newBaseModule = static function(){
  $base = new ExpandoLookalike();
  //Common base functions get assigned here.
  $basePrivateVar = 42;

  $base->commonFunction = static function($params1, $params2) use ($basePrivateVar){
      echo "common function\n";
  };
  $base->comment = static function() use ($basePrivateVar){
    echo "Doing base comment with $basePrivateVar\n";
  };
  return $base;
};

//Javascript-style extends
$newAdminModule = static function($param) use ($newBaseModule){
  $base = $newBaseModule();

  $privateVar = 5;


  $base->adminProperty = 60;
  $base->suspendSite = static function() use ($param, $privateVar){
    echo 'Doing admin only function ';
    echo "with $param, $privateVar\n";

  };

  return $base;
};


$newPublicModule = static function() use ($newBaseModule){
  $base = $newBaseModule();

  $privateVar = 3;

  //Javascript-style overloading
  $oldComment = $base->comment;
  $base->comment = static function($data) use ($oldComment, $privateVar){
    $oldComment();
    echo 'Doing public function ';
    echo "with $data\n";
  };

  return $base;
};

$baseModule = $newBaseModule();
$adminModule = $newAdminModule('P');
$publicModule = $newPublicModule();

$adminModule->suspendSite(); //echos 'Doing admin only function with P, 5'
echo "{$adminModule->adminProperty}\n"; //echos '60'
$publicModule->comment('com'); //echos 'Doing base comment with 42'
                                     //'Doing public function with com'
?>

Despite closing the door to traits and interfaces, it opens up other interesting doors to compensate:

<? //PHP 5.4+

$inheritAllTheThings = static function(){
  $base = new ExpandoLookalike();
  foreach(\func_get_args() as $object){
    foreach($object as $key => $value){
        //Properties from later objects overwrite properties from earlier ones.
        $base->$key = $value;
    }
  }
  return $base;
};

$allOfEm = $inheritAllTheThings(
  $newPublicModule(),
  $newAdminModule('Q'),
  ['anotherProp' => 69,]
);

$allOfEm->comment('f'); //echos 'Doing base comment with 42'
//Because AdminModule came after PublicModule, the function that echos 'f'
//from PublicModule was overridden by the function from AdminModule.
//Hence, order denotes resolutions for multiple inheritance collisions.
$allOfEm->suspendSite(); //echos 'Doing admin only function with Q, 5'
echo $allOfEm->anotherProp . "\n"; //echos '69'

?>

Comments

0

You can with typecasting. If a extends b then you could do

$a=(a)(new b($input));

Which isn't exactly the same, but similar.

1 Comment

PHP doesn't have such control structure. And never had.
0

You can look: https://github.com/ptrofimov/jslikeobject

Author implemented JS-like objects with support of inheritance.

But perhaps it is not so good to use such objects instead of usual ones.

Comments

0

Yes, as cory mentioned, this feature has been requested before. But before that, you can create a workaround. Here is my old school trick for this

Create two separate classes like these:

class a {
}
class b {
   public $object;
}

Then, create an extended version too

class bextendeda extends a {
}

In the constructor method of class b, place few functions which redirects to the extended object if requested.

class b {
    public $object;

    public function __contruct($extend = false) {
        if($extend) $this -> object = new bextendeda(); 
        else $this -> object = $this;
    }

    function __get($prop) {
        return $this-> object -> $prop;
    }

    function __set($prop, $val) {
       $this-> object -> $prop = $val;
    }
    function __call($name, $arguments)
    {
        return call_user_func_array(array($this -> object, $name), $arguments);
    }

}

And there you have it, IF you want the extended version just do this

$b = new b(true);

If not

$b = new b();

Enjoy :)

9 Comments

It is in fact a factory design pattern.
@Tadeck, Not exactly, but if $extend is true, it does act like a factory.
Too bad you cannot return anything from a constructor. new b will always result in an object of type b, you cannot change that.
@deceze, I was sure, I used it like that. But I fixed it another way. Please check it out and review your vote.
I guess it technically works, but... Really? Pretty ugly hack, since you always need to refer to ->object for doing anything... :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.