将当前对象($ this)转换为后代类

Nat*_*nes 34 php oop type-conversion downcast

我有一个类,可能需要将对象更改为后续级别的后代类.这可能吗?我知道一个选项是返回它的副本但是使用子类,但是实际修改当前对象会很好...所以:

class myClass {
  protected $var;

  function myMethod()
  {
    // function which changes the class of this object
    recast(myChildClass); 
  }
}

class myChildClass extends myClass {
}

$obj = new myClass();
$obj->myMethod();
get_class_name($obj); // => myChildClass
Run Code Online (Sandbox Code Playgroud)

irc*_*ell 35

在PHP中无法进行转换以更改对象的类型(不使用令人讨厌的扩展名).实例化对象后,您无法再更改类(或其他实现细节)...

你可以用这样的方法模拟它:

public function castAs($newClass) {
    $obj = new $newClass;
    foreach (get_object_vars($this) as $key => $name) {
        $obj->$key = $name;
    }
    return $obj;
}
Run Code Online (Sandbox Code Playgroud)

用法:

$obj = new MyClass();
$obj->foo = 'bar';
$newObj = $obj->castAs('myChildClass');
echo $newObj->foo; // bar
Run Code Online (Sandbox Code Playgroud)

但要注意它实际上并没有改变原来的类.它只是创造了一个新的.请注意,这要求属性是公共的,或者具有getter和setter魔术方法......

如果你想要更多的支票(我建议如此),我会将这一行添加castAs为防止问题的第一行:

if (!$newClass instanceof self) {
    throw new InvalidArgumentException(
        'Can\'t change class hierarchy, you must cast to a child class'
    );
}
Run Code Online (Sandbox Code Playgroud)

好吧,既然Gordon发布了一个非常黑魔法的解决方案,我会做同样的事情(使用RunKit PECL扩展(警告:这里是龙):

class myClass {}
class myChildClass extends MyClass {}

function getInstance($classname) {
    //create random classname
    $tmpclass = 'inheritableClass'.rand(0,9);
    while (class_exists($tmpclass))
        $tmpclass .= rand(0,9);
    $code = 'class '.$tmpclass.' extends '.$classname.' {}';
    eval($code);
    return new $tmpclass();
}

function castAs($obj, $class) {
    $classname = get_class($obj);
    if (stripos($classname, 'inheritableClass') !== 0)
        throw new InvalidArgumentException(
            'Class is not castable'
        );
    runkit_class_emancipate($classname);
    runkit_class_adopt($classname, $class);
}
Run Code Online (Sandbox Code Playgroud)

所以,不要这样做new Foo,你会做这样的事情:

$obj = getInstance('MyClass');
echo $obj instanceof MyChildClass; //false
castAs($obj, 'myChildClass');
echo $obj instanceof MyChildClass; //true
Run Code Online (Sandbox Code Playgroud)

从类中(只要它是创建的getInstance):

echo $this instanceof MyChildClass; //false
castAs($this, 'myChildClass');
echo $this instanceof MyChildClass; //true
Run Code Online (Sandbox Code Playgroud)

免责声明:不要这样做.真的,不要.这是可能的,但这是一个如此可怕的想法......

  • 这个技巧是制作丑陋代码的好方法.我宁愿留下语言给予的东西,并试图"赞成合成而不是继承"...... (2认同)

Gor*_*don 12

重新定义类

您可以使用runkit PECL扩展名为"地狱工具包" 来执行此操作:


重新定义实例

runkit函数不适用于对象实例.如果你想在对象实例上这​​样做,理论上你可以通过搞乱序列化的对象字符串来做到这一点.
这是黑魔法的领域.

下面的代码允许您将实例更改为其他类:

function castToObject($instance, $className)
{
    if (!is_object($instance)) {
        throw new InvalidArgumentException(
            'Argument 1 must be an Object'
        );
    }
    if (!class_exists($className)) {
        throw new InvalidArgumentException(
            'Argument 2 must be an existing Class'
        );
    }
    return unserialize(
        sprintf(
            'O:%d:"%s"%s',
            strlen($className),
            $className,
            strstr(strstr(serialize($instance), '"'), ':')
        )
    );
}
Run Code Online (Sandbox Code Playgroud)

例:

class Foo
{
    private $prop1;
    public function __construct($arg)
    {
        $this->prop1 = $arg;
    }
    public function getProp1()
    {
        return $this->prop1;
    }
}
class Bar extends Foo
{
    protected $prop2;
    public function getProp2()
    {
        return $this->prop2;
    }
}
$foo = new Foo('test');
$bar = castToObject($foo, 'Bar');
var_dump($bar);
Run Code Online (Sandbox Code Playgroud)

结果:

object(Bar)#3 (2) {
  ["prop2":protected]=>
  NULL
  ["prop1":"Foo":private]=>
  string(4) "test"
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,所产生的对象是一个Bar对象现在所有属性保持自己的知名度,但是prop2NULL.ctor不允许这样做,所以从技术上讲,当你有一个Bar孩子时Foo,它不是有效状态.你可以添加一个神奇的__wakeup方法以某种方式处理这个,但严重的是,你不想要它,它显示了为什么铸造是丑陋的业务.

免责声明:我绝对不鼓励任何人在生产中使用任何这些解决方案.

  • 嗯,我觉得这有点同情(考虑到它有多糟糕)...... (2认同)

ssi*_*ice 9

你可以像其他答案中所描述的那样,使用令人讨厌的黑魔法 PECL扩展.

虽然,你真的不想要它.您想要在OOP中解决的任何问题都有符合OOP标准的方法.

运行时类型层次结构修改不符合OOP(事实上,这是有意识地避免的).有些设计模式应该符合您的要求.

请告诉我们你为什么要这样做,我相信必须有更好的方法来做到这一点;)

  • 这是我第一次听到避免多态性而不是OOP兼容.我认为这是相反的,一个基本原则.这是不同的多态性称为"类型多态". (7认同)
  • 我不同意.多态性是OOP的关键原则之一.当然,强大的力量带来了巨大的责任. (3认同)