PHP动态类扩展

4
我知道你可以在构造类时像以下方式一样扩展它:
class b extends a {
}

但是,是否可能从脚本中动态扩展类呢?例如:
$b = new b($input) extends a;

我的目标是在管理页面和公共页面使用同一个模块,但实现方式不同。我知道可以创建两个具有相同名称的父类,然后只在管理页面或公共页面中包含其中一个。但我的问题是,在PHP中是否可以动态地实现这一点?


类“a”是内置的吗?您对类“a”的更改有控制权吗? - Cory Carson
不是没有很多注意事项。这两者之间有什么区别,为什么你会更喜欢第二个? - deceze
我考虑的目的是为了扩展模块,一方面用于管理员/控制目的,另一方面用于公共服务页面。管理员方式会更加复杂,而我喜欢它轻量级。 - tim
那么,再次问一下,这两种写法之间有什么区别呢? - deceze
无需评估。在此处检查我的答案 https://dev59.com/V3I_5IYBdhLWcg3wEOzq#39179213 - Krishna Torque
7个回答

3

不行,除非使用类似RunKit这样的扩展程序。

你可以考虑另一种方法。如果你想让B承担A的功能,也许下面的方法可以提供一种“混入”方法。总体思路是,B不是A的子级,而是将任务委托给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;

只是一个疯狂的想法。希望我正确理解了这个问题。

2

1

或者,如果您熟悉JavaScript风格的继承并且不介意失去类型检查:

<? //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'
?>

尽管关闭了Traits和Interfaces之门,但它打开了其他有趣的补偿之门:

<? //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'

?>

1

但是在对象创建时不能使用extendsextends仅用于类定义,并定义哪个其他类是我们新类的“父类”。


是的,这就是发帖的原因。 - tim
@tim:原因应该按照实际情况描述,以原始方式呈现。你目前想要的没有任何意义。 - zerkms

0

是的,正如Cory所提到的那样,这个功能以前已经被请求过了。但在此之前,您可以创建一个解决方法。以下是我旧学校的技巧

创建两个不同的类,像这样:

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

然后,也创建一个扩展版本

class bextendeda extends a {
}

在类b的构造方法中,放置一些函数,如果请求,则重定向到扩展对象。
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);
    }

}

那么,就是这样啦,如果你想要扩展版,只需要这样做

$b = new b(true);

如果不是

$b = new b();

祝您愉快 :)


@Tadeck,不完全是这样,但如果$extend为真,则它会像工厂一样运作。 - Starx
很遗憾,你不能从构造函数中“返回”任何东西。new b将始终导致类型为b的对象,你无法改变这一点。 - deceze
@deceze,我确信我是这样使用的。但我用另一种方式修复了它。请检查并重新审查您的投票。 - Starx
我猜技术上讲这能工作,但是... 真的吗?这是非常丑陋的hack,因为你总是需要引用到 ->object 来做任何事情... :) - deceze
呃,是啊,不。像正常人一样该死的用extend扩展你的类吧,好吗?;) - deceze
显示剩余4条评论

0
你可以使用类型转换。如果a扩展b,那么你可以这样做:
$a=(a)(new b($input));

这并不完全相同,但是类似。


1
PHP没有这样的控制结构。也从来没有过。 - zerkms

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接