PHP-Class-Problem

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • PHP-Class-Problem

    Hallo Community,

    ich habe ein kleines Problem. Ich fange gerade erst so richtig mit PHP an. Ich bin dabei folgendes zu realisieren:
    Eine Datei wird aufgerufen und diese startet eine Class. In dieser Class kann über c->addInstanz() eine neue (weitere) Klasse erzeugt werden. Hier der Code:

    Quellcode

    1. class construct {
    2. var $debug = array();
    3. public function createInstanz($class){
    4. if(file_exists('libary/class/' . $class . '.php')){
    5. self::addDebug($class.' included and started.');
    6. $this->{$class} = new $class;
    7. }else{
    8. self::addDebug($class.'-class not found.');
    9. }
    10. }
    11. private function addDebug($status){
    12. $this->debug[] = $status;
    13. var_dump($this->debug);
    14. }
    15. public function getDebug($format = 'Notice: <strong>%s</strong> <br />'){
    16. var_dump($this->debug);
    17. foreach($this->debug AS $message){
    18. printf($format, $message);
    19. }
    20. }
    21. }
    22. $c = new construct;
    Alles anzeigen


    Ich rufe also diese Class auf und weise diese an, eine neue zu erzeugen.

    Quellcode

    1. $c->addInstanz('user');


    Ich kann wunderbar auf die Objekte in der user-class zugreifen ($c->user->foobar() etc). Jedoch wenn ich innerhalb der user-class die Debug-Funktion nutzen will, klappt das nicht. Aufbau der user-class:

    Quellcode

    1. class user extends construct{
    2. function foobar(){
    3. $this->addDebug('tet');
    4. }
    5. }


    Der Aufruf: $c->user->foobar() funktioniert, aber wenn ich $c->getDebug() ausführe, erhalte ich nur Debug-Nachrichten aus der construct-class.

    Mein Problem ist also die Kommunikation der Classen untereinander. Da ich in Zukunft auch eine Datenbank-Class hinzufügen will, die eventuell auf Informationen aus der user-class zurückgreifen soll, weiß ich schon jetzt nicht, wie ich das anstelle, dass ich aus allen Classes in alle Classes kommunizieren kann.

    Ich hatte folgende Idee:

    Quellcode

    1. class construct{
    2. var $user = '';
    3. var $db = '';
    4. function addInstanz(&$class){
    5. // .. exists-check etc
    6. $this->user = &$class
    7. }
    8. }
    Alles anzeigen

    Dafür müsste ich aber schon jetzt alle Classes kennen, die ich irgendwann mal erzeugen würde. Und selbst dann bin ich mir über den Aufbau noch nicht so sicher, da ich nicht weiß, ob dann ein $this->db->foobar() in der user-class funktioniert.

    Wenn jemand nen Tipp hat, wie ich das umsetzte, wäre ich sehr dankbar.

    Danke & Grüße
    meme
  • Hi,

    vorweg ein paar Kleinigkeiten:
    -Du solltest deine Klasse nicht "construct" nennen. Es ist kein reserviertes Wort, aber leicht mit "__construct" welches bei PHP für den Konstruktor steht, zu verwechseln.
    -Dann ist es sinnvoll Klassennamen groß zu schreiben.
    -Wie du in deiner Funktion schon richtig geschrieben hast instanziert man eine Klasse (nicht starten).
    -Das Schlüsselwor "var" für Klassenvariablen gibt es in PHP5 nicht mehr. Dort kennzeichnest du die Klassenvariablen mit public,private oder protected.
    -Wenn du eine Klasse instanzierst, dann solltest du dir angewöhnen die Klammern zu setzen. ("new Foo()")


    Ohne es vll. zu wissen, hast du das Factory Pattern angewandt ;)


    Jetzt zu deiner Problematik.

    meme schrieb:

    Der Aufruf: $c->user->foobar() funktioniert, aber wenn ich $c->getDebug() ausführe, erhalte ich nur Debug-Nachrichten aus der construct-class.

    Wenn du $c->getDebug aufrufst, dann ist es klar, dass du nur die Debugnachricht der construct-Klasse erhälst, da diese Klasse nichts von Ihren Kindklassen weiß. Um das zu realisieren, musst du mit static arbeiten, so, dass die Debugvariable nicht immer für jede Klasse neu gesetzt wird, sondern alle die selbe Variable nutzen, dann funktioniert dein Vorhaben.


    Was du da vorhast ist nicht sonderlich schön.

    Exkurs:
    Das Factory Pattern macht Sinn, wenn man z.B eine Klasse mit mehreren Adaptern besitzt.
    Bei DB Klassen sieht man das häufig. Als Beispiel gibt es den Adapter "MySQL" der eine eigenständige Klasse darstellt. Dann kann man z.B so die DB Klasse mit dem Adapter MySQL instanzieren: $db = new DB("MySQL"); Wobei dann die Adapterklasse MySQL mittels dem Factorypattern instanziert wird.


    Besser ist es, wenn du deine Klassen eigenständig instanzierst und alles was zur Debuglogik gehört auch in die Debuggerklasse packst, denn alle anderen Klassen brauchen davon nichts wissen und die interessiert das auch nicht.
    Um das vernünftig umzusetzen brauchst du lediglich das Singleton Pattern einsetzen.


    So könnte die Implementierung aussehen :

    Quellcode

    1. <?php
    2. class Debugger
    3. {
    4. private $debugMessages = array();
    5. static private $instance = null;
    6. public static function getInstance(){
    7. if(self::$instance === null){
    8. self::$instance = new Debugger();
    9. }
    10. return self::$instance;
    11. }
    12. private function __construct() {}
    13. private function __clone() {}
    14. public function addDebugMessage($message) {
    15. $this->debugMessages[] = $message;
    16. }
    17. public function getDebugMessages() {
    18. return $this->debugMessages;
    19. }
    20. }
    21. class User
    22. {
    23. public function saveData($data = array()) {
    24. // la la la ... Programmlogik
    25. Debugger::getInstance()->addDebugMessage(get_class($this) . " says data saved");
    26. }
    27. }
    28. class DB
    29. {
    30. public function connect($host) {}
    31. public function persist($object) {
    32. // Daten in die DB schreiben ....
    33. Debugger::getInstance()->addDebugMessage(get_class($this) . " says data saved");
    34. }
    35. }
    36. $db = new DB();
    37. $db->connect("localhost");
    38. $user = new User();
    39. $user->saveData(array("username"=>"new-user-name"));
    40. $db->persist($user);
    41. $debugMessages = Debugger::getInstance()->getDebugMessages();
    42. foreach($debugMessages as $m) {
    43. echo "Nachricht: " . $m . "<br />";
    44. }
    45. // AUSGABE
    46. // Nachricht: User says data saved
    47. // Nachricht: DB says data saved
    Alles anzeigen
  • Hallo und vielen Dank für die Antwort.

    Und eine Abfrage aus User nach DB wäre so:

    Quellcode

    1. DB::getInstance()->query('BLA');

    Vorher muss ich in der DB-Class auch diesen Abfangmechanismus einbauen:

    Quellcode

    1. public static function getInstance(){ if(self::$instance === null){ self::$instance = new Debugger(); } return self::$instance; }


    Kann ich dann nicht das getInstance bei den aufrufen durch das __construct-Element ersetzten? Also dass der Aufruf so wäre: DB::query("Bla") und halt in __construct der Code steht, der die Anzahl der instanzen regelt?

    meme
  • Hi,

    Möchtest du deine DB Klasse auch als Singleton implementieren ?

    Wenn ja, dann benötigt diese auch die getInstance() Methode und die statische Variable $instance. In der getInstance() Methode musst du natürlich die richtige Klasse eintragen :

    Quellcode

    1. self::$instance = new DB();
    2. // oder variabel für alle
    3. self::$instance = new self();



    Zum Verständnis vom Singleton:
    Das Singleton gewährleistet, dass von einer Klasse nur ein Objekt erzeugt werden kann.
    Quelle: phpbar.de/w/Singleton

    Du hast keine Instanzen, sondern nur 1 Instanz !
    Deshalb geht man den Weg über die getInstance-Methode, da diese Sicherstellt, das es nur 1 Instanz der Klasse gibt. Daher ist __clone und __construct auch privat so, dass von der Klasse niemals eine 2. Instanz existieren kann.


    meme schrieb:

    Und eine Abfrage aus User nach DB wäre so:
    DB::getInstance()->query('BLA');

    Was meinst du mit "einer Abfrage aus User zu DB" ?
  • Hallo Vince,

    hatte nu schnell Copy+Paste gemacht, darum stand da noch die alte Class. Mit der Abfrage meinte ich eine Anfrage/Abfrage aus der User-Class in die DB-Class (also ein Query der ausgeführt wird etc). Das Problem ist aber hinfällig bzw. gelöst.

    Ich finde diesen Aufbau sehr schön. Ich frage mich aber nun noch, warum ich alles via getInstance steuern muss und nicht __construct nehmen kann. Weil es wäre, wenn ich darauf verzichten könnte, eine kürzere Schreibweise alá DB::query('FOO').

    Eine letzte Frage, dann verschwinde ich wieder (kann mich auch mit der getInstance abfinden): Folgendes von phpbar:

    Quellcode

    1. <?php
    2. class Singleton
    3. {
    4. static private $instances = array();
    5. static public function getInstance($className) {
    6. if (!isset(self::$instances[$className])) {
    7. self::$instances[$className] = new $className();
    8. }
    9. return self::$instances[$className];
    10. }
    11. }
    12. require_once 'Girl.php';
    13. $girl = Singleton::getInstance('Girl');
    14. require_once 'Boy.php';
    15. $boy = Singleton::getInstance('Boy');
    Alles anzeigen


    Ich würde nach diesem Muster dann eine Function von (class) Boy ansteuern: Singleton::instances['Boy']->foobar(); ??
  • Hi,

    nochmal zum Konstrukt & Singleton Pattern.

    Der Konstruktor wird immer dann aufgerufen, wenn man eine Klasse instanziert.
    Wenn du eine statische Methode "query" von der Klasse "DB" aufrufen würdest, dann würde der Konstruktor NICHT aufgerufen werden. Das geschieht NUR über das "new" Keyword. Also "new DB()". Mit dem Singleton möchte man aber erreichen, das es nur EINE Instanz gibt und man diese dann über die statische Methode "getInstance" erreichen kann.

    Kleines Beispiel:

    Quellcode

    1. <?php
    2. $db = new DB();
    3. $db->connect("localhost");
    4. unset($db);
    5. // die Verbindung ist nicht mehr verfügbar, da $db gelöscht wurde
    6. ////////////////////////////////////////////////////////
    7. $db = DB::getInstance();
    8. $db->connect("localhost");
    9. unset($db);
    10. // durch das Singleton Pattern, kann die Verbindung (Resource) über die statische getInstance-Methode immer wieder geholt werden
    11. $user = DB::getInstance()->query('SELECT * FROM User'); // query ist nicht static !
    Alles anzeigen


    Du solltest zwischendurch ein bisschen "rumspielen" und testen. Schreib einfach eine Klasse mit mehreren Methoden und schaue, wann der Konstruktor überhaupt aufgerufen wird.
    Meist klären sich dadurch viele Dinge von selbst.



    meme schrieb:

    Ich würde nach diesem Muster dann eine Function von (class) Boy ansteuern: Singleton::instances['Boy']->foobar(); ??


    Die beiden Klassen sehen ja so aus:

    Quellcode

    1. class Boy extends Singleton {}
    2. class Girl extends Singleton {}


    Jein, nicht du greifst im Endeffekt auf das $instances Array der Singleton Klasse zu, sondern der Interpreter von PHP.

    Um das rumspielen von vorhin aufzugreifen.
    Setz einfach mal das Instance Array auf public und schau dir den Inhalt an.

    Quellcode

    1. <?php
    2. class Singleton
    3. {
    4. static public $instances = array();
    5. [...]
    6. }
    7. class Boy extends Singleton {}
    8. class Girl extends Singleton {}
    9. echo "Pre Girl-Boy";
    10. var_dump(Singleton::$instances);
    11. $girl = Singleton::getInstance('Girl');
    12. echo "Post Girl";
    13. var_dump(Singleton::$instances);
    14. $boy = Singleton::getInstance('Boy');
    15. echo "Post Girl-Boy";
    16. var_dump(Singleton::$instances);
    Alles anzeigen





    meme schrieb:

    Eine letzte Frage, dann verschwinde ich wieder

    du bist herzlich eingeladen dich zu registrieren :)