现在这在 PHP 中是不可能的。为了便于实施,这里有一些替代方案。
首先,如果您知道相关类之间所有可能的转换,您可以轻松地创建一个方法来获取当前对象,填充新类的干净实例并返回它。这是您在原始问题中提到的想法。这是你能做的最直接、最安全的事情。
其次,如果您使用的是足够现代的 PHP 版本,则可以使用Serializable 接口来获得巧妙的技巧。如果您实现该接口,则永远不会调用__sleep
/ __wakeup
,构造函数也不会。这意味着您可以使用这些方法来获得便宜的技巧。下面是一些愚蠢的演示代码,没有要演示的界面:
[mcg@mcg-workstation ~]$ php -a
Interactive shell
php > class Foo { public $a; public $b; }
php > class Bar extends Foo { public $c; }
php > class Baz extends Foo { public $c; }
php > $one = new Bar();
php > $one_s = serialize($one);
php > echo $one_s;
O:3:"Bar":3:{s:1:"c";N;s:1:"a";N;s:1:"b";N;}
php > $one_s = explode(':', $one_s, 4);
php > print_r($one_s);
Array
(
[0] => O
[1] => 3
[2] => "Bar"
[3] => 3:{s:1:"c";N;s:1:"a";N;s:1:"b";N;}
)
php > $two_s = $one_s;
php > $two_s[1] = strlen('Baz'); $two_s[2] = '"Baz"';
php > $two_s = join(':', $two_s);
php > echo $two_s;
O:3:"Baz":3:{s:1:"c";N;s:1:"a";N;s:1:"b";N;}
php > $two = unserialize($two_s);
php > echo get_class($two);
Baz
如果您没有遵循,此代码将替换序列化数据中的类名。通过这样做,我刚刚将 aBar
转换为 a Baz
,具有所有相同的属性。这只有在属性相同时才真正有效。如果属性不匹配,我不确定 PHP 会做什么,并且如果您实现 Serializable,您的序列化和反序列化方法将需要处理转换。
这也是一个巨大的黑客攻击,可能会导致您的代码的未来维护者想要追踪您并伤害您。也可能会有轻微的性能损失。如果您最终使用它,请务必进行基准测试。并聘请保镖。或者至少确保未来的维护者不会是可以找到您地址的凶残精神病患者。
第三,回到基于类的构造而不是基于对象的构造:如果您可以等待 PHP 5.4(或当前主干将成为的任何东西),您将能够将匿名函数放在属性中并像调用它们一样调用它们他们是方法。虽然您可以在早期版本中将匿名函数放在属性中,但至少从 PHP 5.3 开始,这些函数不能引用$this
,因此作为对象的一部分毫无用处。这个限制也是阻止您现在使用魔术__call
方法来实现相同目标的原因。
第四,这是完全没有答案的,如果你想要这种类型的弯曲体操,可以考虑使用 Ruby 或 Perl。我认为 Python 可以做类似的事情,但我没有在其中工作过,也不能确定。PHP 在 OO 方面不是一门灵活的语言,内部列表中的人对将 OO 提升到其他语言更有趣的标准没有兴趣。
关于您的相关问题,听起来您确实想要实例级特征。PHP 5.4 也将具有特征,但在类级别,而不是实例级别。有关我的评论,请参见#4。