What it's all about...

Lets have a look at our user class...

  $user = Yii::app()->user;
  CVarDumper::dump( $user, 1, true );

CWebUser#1
(
    [
allowAutoLogin] => true
    
[guestName] => 'Guest'
    
[loginUrl] => array(...)
    [
identityCookie] => null
    
[authTimeout] => null
    
[autoRenewCookie] => false
    
[autoUpdateFlash] => true
    
[loginRequiredAjaxResponse] => null
    
[CWebUser:_keyPrefix] => 'd96b817b9c066963e5b670b8453eeeda'
    
[CWebUser:_access] => array(...)
    [
behaviors] => array(...)
    [
CApplicationComponent:_initialized] => true
    
[CComponent:_e] => null
    
[CComponent:_m] => null
)

Can it be used as IWebUser?

  CVarDumper::dump( $user instanceof IWebUser, 1, true );

true

But my UserManagementModule works on IUser interfaces. Can the user class also be used as IUser?

  CVarDumper::dump( $user instanceof IUser, 1, true );

false

But CWebUser is a CComponent, right?

  CVarDumper::dump( $user instanceof CComponent, 1, true );

true

So I can attach CBehaviors!

  $user->attachBehavior( 'CWebUserAdapter', array(
    'class' => 'CWebUserAdapter'
  ));

And I can use CComponents AS A attached CBehavior:

  $webUserAdapter = $user->asa( 'CWebUserAdapter' );
  CVarDumper::dump( $webUserAdapter, 1, true );

CWebUserAdapter#1
(
    [
userManagementModuleId] => 'userManager'
    
[CBehavior:_enabled] => true
    
[CBehavior:_owner] => CWebUser(...)
    [
CComponent:_e] => null
    
[CComponent:_m] => null
)

Now, if that adapter implemented the needed Interface...

  CVarDumper::dump( $webUserAdapter instanceof IUser, 1, true );

true

I could obviously work on the adapter. But I could also work on my user as if it implemented that interface itself!

  echo CHtml::image( $user->getImage() );

Too bad that I have to remember what CBehaviors are attached to which CComponent. Too bad I can't simply do:

  CVarDumper::dump( $user instanceof IUser, 1, true );

false

To get the response "Yes, that's okay, go on, use it as IUser"

Now, what if...

What if there was some "instanceof" utility, that knew about Yii's capabilities?

That's where InstanceofBehavior comes into play.

Actually, this application is configured with this Behavior.

And that's why I can do:

  if (Yii::app()->isInstanceof($user,'IUser'))
  {
    echo CHtml::image( $user->getImage() );
  }

But granted, this isn't really safe...

If in an upcomming release CWebUser got a method "getImage", the wrong method would be called.

We can only invoke methods of a CBehavior, if the behavior's owner doesn't define the method itself.

That's why it might be safer to use this version:

  if ($instanceofIUser = Yii::app()->getInstanceof($user,'IUser'))
  {
    echo CHtml::image( $instanceofIUser->getImage() );
  }

And what do you think $instanceofIUser is?

  CVarDumper::dump( $instanceofIUser, 1, true );

CWebUserAdapter#1
(
    [
userManagementModuleId] => 'userManager'
    
[CBehavior:_enabled] => true
    
[CBehavior:_owner] => CWebUser(...)
    [
CComponent:_e] => null
    
[CComponent:_m] => null
)

Correct, the behavior implementing the class/ interface we've asked for.

That's why we can even call colliding methods:

  if ($instanceofIUser = Yii::app()->getInstanceof($user,'IUser'))
  {
    CVarDumper::dump( $user->getName()           , 1, true );
    CVarDumper::dump( $instanceofIUser->getName(), 1, true );
  }

'Guest' 'UserManager User'

Of course, the methods also work for normal inheritance:

  CVarDumper::dump( Yii::app()->isInstanceof($user,'IWebUser'), 1, true );
  CVarDumper::dump( Yii::app()->getInstanceof($user,'IWebUser'), 1, true );

true CWebUser#1
(
    [
allowAutoLogin] => true
    
[guestName] => 'Guest'
    
[loginUrl] => array(...)
    [
identityCookie] => null
    
[authTimeout] => null
    
[autoRenewCookie] => false
    
[autoUpdateFlash] => true
    
[loginRequiredAjaxResponse] => null
    
[CWebUser:_keyPrefix] => 'd96b817b9c066963e5b670b8453eeeda'
    
[CWebUser:_access] => array(...)
    [
behaviors] => array(...)
    [
CApplicationComponent:_initialized] => true
    
[CComponent:_e] => null
    
[CComponent:_m] => array(...)
)

A quick negative test:

 
  if (Yii::app()->isInstanceof($user,'IAdminUser')) {
    echo "Hooray, I'm Admin!";
  } else {
    echo "(>_<)</br>";
  }
 
  if ($instanceofIAdminUser=Yii::app()->isInstanceof($user,'IAdminUser')) {
    echo "Hooray, I'm Admin!";
  } else {
    echo "(>_<)</br>";
  }

(>_<)
(>_<)

And we no longer have to remember under which name we attached the behavior.

Do you remember its name? :)