Heute bin ich auf ein fehlendes OOP Feature von PHP gestoßen: „Down-Casten“ funktioniert nicht.
Ich weiß nicht, ob down-casten der korrekte Begriff ist, aber so hab ich ihn in einem PHP Forum gefunden.
Es geht darum, ein Objekt einer abgeleiteten Klasse in ein Objekt der Übergeordneten (parent) Klasse zu casten. So ungefähr sollte das eigentlich aussehen:

class ReadRecord {
  protected $value;
  private function __construct(){}
  public function getValue(){
    return $this->value;
  }
}

class WriteRecord extends ReadRecord {
  public function __construct(){}
  public function setValue($v){
    $this->value = $v;
  }
}

$wr = new WriteRecord();
$wr->setValue("hello world");
$rr = (ReadRecord) $wr;

Geht aber nicht! Man bekommt einen parse error: „syntax error, unexpected T_VARIABLE“. Doof.
Also hab ich drüber nachgedacht, wie man das trotzdem hin bekommt und bin dank der seit PHP5 vorhanden Reflection API auf folgende Lösung gekommen:

class ReadRecord {
  protected $value;
  protected function __construct(){}
  public function getValue(){
    return $this->value;
  }
}

class WriteRecord extends ReadRecord {
  public function __construct(){}
  public function setValue($v){
    $this->value = $v;
  }
  public function toReadRecord(){
    $rr = new ReadRecord();
    
    $reflectedReadRecordClass = new ReflectionClass("ReadRecord");
    $props = $reflectedReadRecordClass->getProperties();
    foreach($props AS $prop){
       $propName = $prop->getName();
       $rr->$propName= $this->$propName; 
    }
    return $rr;
  }
}

$wr = new WriteRecord();
$wr->setValue("foo");
$rr = $wr->toReadRecord();
//$rr->setValue("bar"); //Fatal error: Call to undefined method ReadRecord::setValue() in ...
echo $rr->getValue(); //prints "foo"

Das ganze ist vielleicht etwas unperformant, aber es geht!
Theoretisch könnte man auch eine abstrakte Klasse schreiben welche die „downcast“ Methode über Reflection allgemein implementiert, aber das finde ich unschön. So wie im oberen Beispiel kann man kontrolliert down-casten.