Die magischen Funktionen bei PHP sind wie sicher vieles in der Programmierung eine Geschmacks- oder sogar fast schon eine Glaubensfrage. Die einen lieben sie, die anderen hassen sie. Je nach Verwendung können sie schonmal einen Entwickler in den Wahnsinn treiben, wenn irgendwelche Werte, irgendwie aus dem Nichts kommen und auch wieder dahin verschwinden.
Es gibt aber auch Fälle, in denen die magische Methode __call() den Code lesbarer machen kann und gleichzeitig eine Menge Schreibarbeit einspart.
Stellen wir uns einfach mal vor, wir hätten ein Model mit 20 Werten und wollen es anhand eines bestimmten Feldes laden.
<?php $model->load($id); // lädt das Model mit Defaultkey = $id, oder $model->load('Daniel', 'name'); // Feld 'name' = 'Daniel'
Letzteres ist durchaus gut zu lesen und lädt bestenfalls auch die richtigen Daten. Mit __call() lässt sich das ganze ohne viel Aufwand noch etwas dekorieren, so dass wir das Model dann so laden können:
<?php $model->loadByName('Daniel');
Nun könnte man entweder für alle 20 Felder die jeweiligen Methoden schreiben, oder eine Art Template mit __call() und eines RegEx erstellen und den Aufruf einfach deligieren.
<?php class Custom_Model { public function __call($method, $args) { if (preg_match('/^loadBy([a-zA-Z]*?)$/', $method, $matches)) return $this->load($args[0], $matches[1]); } // ... } }
Der Vorteil ist ganz klar die eingesparte Tipparbeit und eine übersichtlichere Klasse, da wir alleine für diese Funktionalität 20 Methoden weniger im Code haben. Jedoch ist auch der Nachteil nicht außer Acht zu lassen. Weil die Methoden nur virtuell existieren, sind sie z.B. für die IDE unsichtbar und sind daher auch für neue Entwickler nicht sofort erkennbar.
Es bleibt also wieder dem eigenen Geschmack überlassen, für welchen Weg man sich entscheidet.
27.09.2010 um 11:42 Uhr
Habe mich damit noch nich beschafft, aber sollten phpdoc kommentare nich auch in der Lage sein, virtuelle Methoden und Werte zu beschreiben?
Falls nich, wäre das auf jeden Fall ein Sinnvoller Featurerequest.
27.09.2010 um 12:37 Uhr
Also bei Zend, denke auch das das bei pdt geht (beides Eclipse), kann man mit phpdoc am klassenkopf “@method string funcname” und “@property string name” Methoden und Eigenschaften hinzufügen.
/**
* @property string name
*/
class …
27.09.2010 um 13:52 Uhr
Ich finde das solche late binding Mechanismen die Lesbarkeit des Codes stark einschränken. Die Kopplungen sind sehr schwer zu erkennen. Vor allem findet sie kein automatisiertes Tool. Die selbe Technik habe ich in diversen CMS Systemen gesehen mit dicken Regexblöcken in __call. Meiner Meinung nach ist das einfach nicht strikt genug und erhöht die Fehlerquellen für einen zu geringen Nutzen.
27.09.2010 um 16:47 Uhr
Nie, Nie, Nie, so was in einem größeren Projekt benutzen. Es gibt IMHO keinen einzigen use-case der so etwas rechtfertigt! Der Verlust an Wartbarkeit ist einfach zu groß. Ich sag dann immer zu meinen Azubis: Faules Mädchen langes Fädchen.
27.09.2010 um 18:16 Uhr
Never Say Never Again.
Richtig ist, für die IDE’s sind zusätzliche Annotationen notwendig. Richtig ist auch, dass jeder Magic-Call Performance frisst. Hinzu kommt noch der Performanceverlust durch das Namens-Matching mittels Regular-Expressions.
Die Frage lautet also, wie kann ich Methoden für Models haben, die ich nicht kenne. Antwort: _call.
Haufenweise Kommentare/Aufregung…. ,)
Aber wie kann ich die Methodenerstellung und die Annotationserstellung automatisieren und die Wartbarkeit wiederherstellen?
Sowohl die Annotationen, aber auch die eigentlichen Methoden die über _call aufgerufen werden, kann man automatisch erstellen lassen. Und zwar bei einer erfolgreichen Delegation, denn man kennt den Methodennamen und die Parameter. Diese Angaben kann man verwenden, um mittels Scaffolding (hat auch was mit Methoden-Templates zu tun ,) die Methode selbst zu schreiben!
Günstig ist folgendes Verfahren: _call() Methode in der Parentklasse unterbringen. Per Scaffolding eine Kindklasse zur Abfrage des Models schreiben und bei erfolgreichen _calls die Methoden in diese Klasse. Dadurch reduzieren sich die Erstellungkosten, die Performancenbremsen werden rausgeworfen, die Wartbarkeit steigt automatisch an und die IDE erkennt nach und nach alle verfügbaren Methoden.
Gruß Jens
27.09.2010 um 20:03 Uhr
Hehe, hätte mich gewundert, wenn der Artikel nicht derartige Reaktionen hervorgebracht hätte.
@Jens-André: Dein Ansatz klingt zwar erstmal gut und logisch, dennoch würde ich dann doch lieber vollständige Codegeneration vorziehen, als zur Laufzeit an den Klassen rumzumachen.
@Harald: Sag das mal den Leuten von Varien/Magento…
27.09.2010 um 22:14 Uhr
Ich seh das wie Harald. Es ist Teufelszeug! Es kostet Dich Performance, Wartbarkeit und Debuggen macht auch kein Spaß. Doctrine1 verwendet es zahlreich und wenn man wirklich mal, weil etwas schief gegangen ist, tracken will, ob es vielleicht einfach nur ein Fehler in Doctrine steckt, dann brauch mal reichlich Geduld.
@Daniel: von Magento habe ich nicht viel gutes gehört und Deine Aussage unterstützt dieses schlechte Bild, was ich habe, nur.
OFFTOPIC: Übrigens bin ich mir auch noch nicht wirklich sicher, ob Closures eine gute Erfindung haben. Vermisst habe ich sie vor 5.3 auch nicht wirklich.
28.09.2010 um 19:23 Uhr
man mag ja über doctrine sagen was man will .. aber ich würde doch stark davon ausgehen, dass die nicht einfach nur der laune halber die ganzen magic getter & setter beim umstieg von doctrine1 auf doctrine2 entfernt haben ..
01.10.2010 um 17:51 Uhr
Wir hatten intern gerade vor kurzem bei uns im Geschäft die Diskussion, was die Vorteile/Nachteile von Magic Methoden sind. Danke für die interessanten Kommentare. Ich bin auch eher der Meinung, dass Magic Methoden mehr Probleme schaffen, als das sie helfen.