This is not immediately possible in PHP right now. Here are some alternatives, in order of ease of implementation.
First, if you know all of the possible transformations between related classes, you can easily create a method that takes the current object, populates a clean instance of the new class and returns it. This is the idea you mentioned in your original question. It's the most straightforward and safe thing you could do.
Second, if you're using a modern enough version of PHP, you can use the Serializable interface for a neat trick. If you implement that interface, __sleep
/__wakeup
are never called, and neither is the constructor. This means that you can use those methods for a cheap trick. Here's some silly demo code without the interface to demonstrate:
[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
If you didn't follow, this code replaces the class name in the serialized data. By doing this, I've just transformed a Bar
into a Baz
, with all of the same properties. This only really works if the properties are identical. I'm not sure what PHP would do if the properties don't match, and if you implement Serializable, your serialize and unserialize methods will need to handle the transformation.
This is also a tremendous hack that may cause future maintainers of your code to want to track you down and hurt you. There's also probably a minor performance penalty. If you end up using it, be sure to benchmark. And hire bodyguards. Or at least make sure the future maintainers won't be murderous psychopaths that can find your address.
Third, going back to class-based construction instead of object-based construction: If you can wait for PHP 5.4 (or whatever the current trunk will end up being), you will be able to place anonymous functions in properties and call them as if they were methods. While you can place anonymous functions in properties in earlier versions, at least as of PHP 5.3, those functions can not reference $this
, and are therefore quite useless as part of an object. This limitation is also what prevents you from using the __call
magic method to achieve the same thing right now.
Fourth, and this is a total non-answer, consider Ruby or Perl if you want these kind of class-bending gymnastics. I think Python can do similar things, but I haven't worked in it and can't be sure. PHP is not a flexible language when it comes to OO, and the people on the internals list have no interest in bringing OO up to the more interesting standards of other languages.
With regard to your related problem, it sounds like you really want instance-level traits. PHP 5.4 will also have traits, but at the class level, not the instance level. See #4 for my commentary about that.