czwartek, 31 stycznia 2013

Refleksje - rzutowanie obiektu do klasy

Java pozwala na tytułową operacją w bardzo prosty sposób
Object1.cast( Object2 );
Object1 jest rzutowany do klasy reprezentowanej przez Object2. Ostatnio naszła mnie ochota na coś podobnego w PHP, żeby rzutować obiekt klasy rozszerzający rodzica do innej klasy rozszerzającej rodzica. W PHP nie ma na to prostej metody, ale można ją samemu napisać bardzo elegancko
// rozszerzenie klasy jest jak najbardziej na miejscu bo będziemy pracować na jej instancji
class ReflectionObjectExtended extends ReflectionObject
 {
 
 private $Obj;
 
 public function __construct( $Object )
  {
  
  parent::__construct( $Object );
  
  $this->Obj = $Object; // przyda też się oryginalny obiekt
  
  }

 // nowa metoda jako argumenty przyjmuje refleksję klasy do której obiekt ma być rzutowany, oraz tablicę z argumentami dla konstruktora
 public function cast( ReflectionClass $ReflectionClass, array $arg = array() )
  {
  
  $New = $ReflectionClass->newInstanceArgs( $arg ); // tworzymy instancję obiektu który zostanie zwrócony
  
  foreach( $this->getProperties() as $ReflectionProperty ) // pobieramy refleksje parametrów aktualnego obiektu
   {
    
   $ReflectionProperty->setAccessible( TRUE ); // dzięki temu mamy dostęp do chronionych i prywatnych właściwości
   
   $name = $ReflectionProperty->getName(); // pobieramy nazwę...
   $value = $ReflectionProperty->getValue( $this->Obj ); // ...i wartość aktualnego obiektu

   if( $ReflectionClass->hasProperty( $name ) ) // jeśli klasa do której rzutujemy ma taką właściwość...
    {
    $Property = $ReflectionClass->getProperty( $name ); //...bierzemy jej refleksję...
    $Property->setAccessible( TRUE ); // ...udostępniamy j.w...
    $Property->setValue( $New, $value ); // ...i przypisujemy nową wartość...
    }
   else // ...w przeciwnym razie...
    {
    $New->$name = $value; // ...tworzymy właściwość
    }
    
   }
  
  return $New;
  
  }
 
 }
A oto zastosowanie
$DOMDocument = new DOMDocument();
$ReflectionObjectExtended = new ReflectionObjectExtended( $DOMDocument );
$ReflectionObjectExtended->cast( new ReflectionClass( 'DOMElement' ), array( 'strong' ) );
Bardziej praktycznym przykładem jest przeniesienie danych z obiektu dla użytkownika niezalogowanego do obiektu dla zalogowanego podczas logowania.