Ein Builder sollte normalerweise nur einmalig verwendet werden. Es kann aber Fälle geben, wo die Wiederverwendbarkeit ziemlich bequem ist. Hier ein Beispiel, wie es funktioniert.
<?php class Builder { private $propA; public function withPropA($a) { $this->propA = $a; return $this; } } $builder1 = new Builder(); $builder1->withPropA("foo"); // this won't work as expected, since builder1 is modified before cloned // $builder2 = clone $builder1->withPropA("bar"); // syntax error :( // $builder2 = (clone $builder1)->withPropA("bar"); $builder2 = clone $builder1; $builder2->withPropA("bar"); print_r($builder1); // foo print_r($builder2); // bar
Soweit das Beispiel, mit „einfachen“ (skalaren) Klassen Variablen (inkl. Arrays – diese werden ebenfalls kopiert). Wird jedoch ein inneres Objekt verwendet (oder ein „`&$pointer„`), muss man aufpassen. Folgendes Beispiel geht „schief“:
// als Fortsetzung zum oberen Code class SubClass { public $propB; } $sub = new SubClass(); $sub->propB = "boo"; $builder1->withPropA($sub); $builder3 = clone $builder1; $sub->propB = "far"; // changes the state of both builder objects!! print_r($builder1); // far!! print_r($builder3); // far!!
Um solch einen Fall korrekt zu handhaben, kann man im Builder die „__clone“ Methode wie folgt implementieren:
class Builder2 { private $propA; public function withPropA($a){ $this->propA = $a; return $this; } public function __clone() { if (is_object($this->propA)) { $this->propA = clone $this->propA; } } } $builder1 = new Builder2(); $builder1->withPropA($sub); $builder3 = clone $builder1; $sub->propB = "yes"; // changes the state of $builder1 since this is the object set to builder1 print_r($builder1); // yes print_r($builder3); // far (value before it was cloned)
Auch wenn hier jetzt das Klonen richtig funktioniert, ist der Code trotzdem etwas verwirrend, da $builder1 verändert wird, obwohl $sub erst nach dem clonen modifiziert wird. Daher sollte solcher Code aufgrund der Leserlichkeit trotzdem vermieden werden.
Trotzdem sollte das Klonen bei Objekt-Variablen auf jeden Fall wie im letzten Beispiel umgesetzt werden!