1

I am facing a serious design problem that is driving me crazy. I think it can only be solved with multiple inheritance or something. So here is what I want to do:

Say I have a basic user class called OrdinaryUser defined in this way:

class OrdinaryUser
{
 private $id,$name;

 public function __construct($id)
 {
  $this->id = $id;
  $this->name = fictionalDB::getUserNameById($id);
 }

 public function getName()
 {
  return $this->name;
 }

 public function getId()
 {
  return $this->id;
 }

}

And I have a subclass called AdminUser with additional functionality:

class AdminUser extends OrdinaryUser
{
 public function deleteUser($id)
 {
  echo "Deleting user where id=$id";
 }
}

The problem: What if I have already instantiated an object of type "OrdinaryUser" and want to make it into an AdminUser object on-the-fly? Is there a way of "extending objects" so to avoid instantiating a subclass and having to re-populate the new object's fields with the same data?

Another related problem: I might have many other categories of users defined later, each having their own unique bahaviour, but always a basic one, and it wouldn't make sense to create a hierarchy in this case because most times one type of object should not be inheriting methods from the other type, although it might be desirable to have additional functionality from one type being "imported" into the other dinamically.

fabio
  • 2,269
  • 5
  • 22
  • 34

4 Answers4

3

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.

Charles
  • 50,943
  • 13
  • 104
  • 142
  • +1 - your answer was very instructive for me. Of course i'm not going to use that serialize/unserialize trickery, but it is very engineous indeed and I liked to follow that. And thank you for the additional info about possibilities in new versions of php (I had read stuff about traits already and like the idea ;). Even though it is hard for me to decide which answer is the best, I'm definitely staying with the decorator pattern suggested by meouw here, as it seems to be a somewhat "clean" solution. – fabio Mar 01 '11 at 17:19
1

I think your scenario might call for a Factory Pattern

Perhaps you shouldn't have instantiated a OrdinaryUser in the first place~

Shad
  • 15,134
  • 2
  • 22
  • 34
1

Sounds like the Decorator Pattern may help you

<?php
/*
   an interface to ensure we only decorate Users
   and possibly some of the most common methods that all users have
   so that we don't always suffer the overhead of the magic __call method
*/
interface User
{
    public function getId();
    public function getName();
}

class OrdinaryUser implements User
{
    private $id,$name;

    public function __construct($id)
    {
        $this->id = $id;
        $this->name = fictionalDB::getUserNameById($id);
    }

    public function getName()
    {
        return $this->name;
    }

    public function getId()
    {
        return $this->id;
    }

}

/*
   There aren't any abstract methods in this class
   but it is declared abstract because there is no point in instantiating one
*/
abstract class UserDecorator implements User
{
    protected $user;

    public function __construct( User $user )
    {
        $this->user = $user;
    }

    public function getId()
    {
        return $this->user->getId();
    }

    public function getName()
    {
        return $this->user->getName();
    }

    /*
       Any methods that aren't implemented by this type of
       user are dealt with by the decorated user
    */
    public function __call( $method, $arguments )
    {
        return call_user_func_array( array( $this->user, $method ), $arguments );
    }
}

class AdminUser extends UserDecorator
{
    /*
       Add any methods that are particular to this type of user
    */
    public function addedMethod()
    {
        // do AdminUser type stuff
        return "doing added method stuff\n";
    }

}

class FooUser extends UserDecorator
{
    public function foo()
    {
        // do some foo
        return "doing fooness\n";
    }
}

// just for testing
class fictionalDB
{
    public static function getUserNameById( $id )
    {
        $db = array(
            1 => 'Peter',
            2 => 'Paul'
        );
        return $db[$id];
    }
}


$user = new OrdinaryUser( 1 );
echo $user->getName();    // Peter

// make Peter into an AdminUser
$user = new AdminUser( $user );

// and also add some fooness
$user = new FooUser( $user );
echo $user->addedMethod(); // doing added method stuff
echo $user->foo();         // doing fooness
meouw
  • 41,754
  • 10
  • 52
  • 69
  • This pattern is really awesome! I had heard about it before but never thought it would work to solve this kind of problem. This solution is exactly what I was in need of, thanks! I see that UserDecorator as some kind of adapter, right? Moreover, I personally don't like using magic methods a lot, but think the __call magic method suits well here so I'm definitely accepting your answer. So it seems possible to add methods on-the-fly and extend an object by means of this so called decorator pattern. Charles' answer was pretty good too, and it was hard for me to chose between yours and his. – fabio Mar 01 '11 at 17:27
  • I'm also not a fan of using magic methods but in this implementation `__call` adds something that is not really part of the 'classic' decorator pattern. In the classic pattern each decorator must implement the same interface and forward a method call to the object it wraps, optionally performing its own manipulation on the arguments. By using a bit of magic we allow a decorator add new methods to the object it wraps and these methods are available from the outside layer of the construct. This kind of flexibility is very handy but has its downsides i.e. it weakens the OO contract. – meouw Mar 01 '11 at 20:31
0

What you describe is not possible, have you considered a different approach? What if your User class 'contained' various privilege implementations in an array.

Consider it as an 'Is A' and 'Has A' problem; while an AdminUser 'is' an admin, it 'has' privileges, so said priv's should be stored as member variables.

Lewis
  • 41
  • 4