Profil | Mitglieder | Registrieren | Start | Suche


PHP-Support.de » Programmierung » PHP & MySQL » Codeschnipsel » Ein Gästebuch mit OOP    » Hallo Gast [Login | Registrieren]

Neues Thema | Antworten   

Autor Beitrag
B.C.
Mitglied
Sehr guter User


Dabei seit: 04.02.2009
Herkunft: Niedersachsen
Posts: 797
     Ein Gästebuch mit OOP Zitat | Bearbeiten

Oft besteht die Frage, wozu man OOP benutzen soll. Ich werde hier jetzt mal eine Antwort darauf geben!
Dieses fortgeschrittene Tutorial setzt allerdings einige Grundkenntnisse vorraus und ist daher für Anfänger ungeeignet.
Erstmal will ich jedoch einige Begriffe im Voraus klären:

OOP => Objektorientertes Programmieren
Methode => Nennt man die Funktion in einer Klasse
Eigenschaft => Nennt man die Variablen einer Klasse, weil diese später auf das Objekt übertragen werden, nennt man es Eigenschaften des Objekts
Objekt => Eine Varibale, auf die eine Instanz einer Klasse abgelegt ist
Instanz => der new-Operator erzeugt eine Referenz der Klasse auf das Objekt, diese nennt man Instanz

Vorraussetzungen
Du musst mit der Sprache PHP schon etwas besser vertraut sein und eventuell einige Grundkenntnisse in OOP mitbringen (Was sind Klassen, wie werden sie benutzt)
Einen kleinen Einstieg bietet diese Seite:
http://www.php-kurs.com/objektorientierte-programmierung-in-php.htm

Nun zu den Vorrausetzungen, die euer Server mitbringen muss:

  • PHP 5.1 oder höher
  • Die PDO-Klasse

    Zum 2ten Punkt komme ich später, testen könnt ihr euer Server darauf so:
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    
    <?php
    if (version_compare(phpversion(), '5.1.0''<'))
       echo 
    'PHP5.1 oder aufw&auml;rts ist nicht vorhanden';
    else
       echo 
    'PHP5.1 oder h&ouml;her ist vorhanden';

    echo 
    '<br />';

    print (!
    class_exists('PDO')) ? 'Die PDO-Klasse ist nicht vorhanden' 'Die PDO-Klasse ist vorhanden';
    ?>


    Es müssen beide Punkte erfüllt sein, denn andersfalls macht dieses Tutorial weiter keinen Sinn, wenn dein localhost dieses also nicht unterstützt, rate ich auf http://www.bplaced.de/ da ist alles vorhanden.
    Im Vorraus: Jede Klasse und Interface bekommt seine eigene Datei, zB NameDerKlasse.class.php,
    was aber vorrausgesetzt und nicht weiter erwähnt wird. Außerdem wird eine Datenbank mit der Tabelle 'gbook' benötigt, wie folgenden Aufbau hat:
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    
    CREATE TABLE `gbook` (
    `gbook_id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
    `name` VARCHAR( 50 ) NOT NULL ,
    `text` TEXT NOT NULL ,
    `timestamp` INT( 11 ) NOT NULL
    ) ENGINE = MYISAM


    PDO
    PDO (PHP Data Object) ist eine Schnittstelle, mit der man versch. Datenbanken ansprechen und über SQL (Structured Query Language) Daten verarbeiten kann. Dafür müssen wir erstmal eine Instanz dieser Klasse bekommen, zB so:
     PHP 
    1:
    2:
    3:
    
    <?php
    $obj 
    = new PDO;
    ?>

    Allerdings benätigt PDO die Verbindungsdaten,
    weshalb der Code so noch nicht funktioniert. Wir wollen aber auch verhindern, dass man nicht 2 oder mehr PDO-Objekte erstellen kann, 1. wäre das überflüsig und 2. wollen wir uns nicht 2 oder mehrmals verbinden, sondern nur einmal. Deshalb benutzen wir eine Klasse namens 'db_sql', mit der wir uns eine Instanz von PDO holen. So siehts sie aus:
    db_sql.class.php
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    11:
    12:
    13:
    14:
    15:
    16:
    17:
    18:
    19:
    20:
    21:
    22:
    23:
    24:
    25:
    26:
    27:
    28:
    29:
    30:
    31:
    32:
    33:
    34:
    35:
    36:
    37:
    38:
    39:
    40:
    41:
    42:
    43:
    44:
    45:
    46:
    47:
    48:
    49:
    50:
    51:
    
    <?php
    class db_sql
       
    {
          
    //per Komma mehrere Variablen definieren
          
    private $host 'localhost',  //Host
                  
    $user '',           //Username
                  
    $pw   '',           //DBpasswort
                  
    $name 'gbook';      //Datenbankname

          //Unser PDO-Objekt
          
    static public $db_obj null;
          static private 
    $objekt;

          
    //Der Konstruktor
          
    private function __construct ()
             {
                 
    //MySQL verbindungsdaten
                 
    $con 'mysql:dbname=' $this->name ';host=' $this->host;

                    try
                 {
                    
    self::$db_obj = new PDO ($con$this->user$this->pw, array
                                                (
                                                   
    PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING
                                                
    )
                                            );
                 }
                 catch(
    PDOException $e)
                 {
                    
    printf('Fehler beim &Ouml;ffnen der Datenbank.<br><br>%s',
                    
    $e->getMessage); exit();
                 }
             }

          
    //Hiermit holen wir uns die PDO Instanz und
          //verhindern eine doppelte Verbindung
          
    public static function getInstance ()
             {
                
    //Wenn das Objekt noch keine PDO-Instanz hat
                
    if(self::$db_obj === null)
                   
    //Dann eine erzeugen
                   
    self::$objekt = new db_sql;

                
    //Und das PDO-Objekt zurückgeben
                
    return self::$db_obj;
             }

          
    //klonen von dieser Instanz verhindern
          
    private final function __clone () { }
       }
    ?>


    PDO erwartet 4 Parameter, 1. die Art der Verbindung, also Infos über die Datenbank 2. Der Benutzername 3. das Passwort und 4. können wir optional Einstellungen vornehmen. mit PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING setzen wir den Fehlermodus auf Warning, womit wir bei feherhaften SQL einen Fehler bekommen. Wie könnten auch PDO::ERMODE_SILENT benutzen, welches und keine Fehler mehr anzeigen würde.


    1. Interface
    Nun kommen wir zu unseren Grundüberlegungen, was soll unser Skript können? Oder anders gesagt, welche Funktionen soll es haben. Per Interface (=Schnittstelle) geben wir vor, welche Methoden eine Klasse haben muss, damit alles richtig funktioniert. Sind nicht alle Vorderungen des Interfaces erfüllt, erhalten wir eine Fehlermeldung. Unser Gästebuch soll also folgendes können:

  • Einträge anzeigen
  • Einträge hinzufügen

    Also sieht demnach unser Gästebuch-Interface so aus:
    gbookInterface.class.php
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    11:
    12:
    13:
    14:
    15:
    
    <?php
    //Was soll alles gemacht werden
    //Als Hauptanwendungen
    interface gbookInterface
       
    {
          
    //Alle Eintraege anzeigen
          
    public function show_entries();

          
    //Eintragfunktion
          
    public function eintragen();

          
    //Bei einen Fehlaufruf richtig handeln
          
    public function __call ($name$param);
       }
    ?>


    Die Methode __call wird dann aufgerufen, wenn das Objekt versucht, eine Methode aufzurufen, die es gar nicht gibt. Im Normalfall kommt bloß eine Fehlermeldungen, aber wir könnten auch etwas anderes machen ... Diese gute Fehlerbehandlung ist auch ein großer Vorteil von OOP.

    Unsere Gästebuch Klasse
    Diese Klasse muss nun alle Vorrausetzungen des Interfaces erfüllen, Interface werden mit dem Schlüsselwort implements in die Klasse mit implementiert. Wir benutzen auch die Methode __construct, welche aufgerufen wird, sobald ein Objekt dieser Klasse erstellt wird. Wir wollen per Konstruktor und mit der Datenbank verbinden, also holen wir uns eine Instanz von PDO. Dafür müssen wir bloß die Methode der Klasse db_sql aufrufen, da wir diese als static deklariert haben, geht dies, ohne ein Objekt der Klasse zu besitzen. Solch einen Aufruf ist einstatischer Aufruf. Dieser sieht so aus: Klasse::Methode() So sieht die Klasse bis jetzt nun aus, mit einigen Eigenschafften:
    gbook.class.php
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    11:
    12:
    13:
    14:
    15:
    16:
    17:
    18:
    19:
    20:
    21:
    22:
    23:
    24:
    25:
    
    <?php
    //Nun die eigentliche Gbook-Klasse, die unser Interface mit implementiert
    class gbook implements gbookInterface
       
    {
          private 
    $pro_seite 5,  //Anzahl der Eintraege pro Seite
                  
    $l 500;        //Maximale Laenge der Texte
          
    protected $db;           //Das DB-Objekt, mit dem wir MySQl ansteuern


          //Konstruktor
          
    public function __construct ()
             {
                
    //Die Instanz der PDO-Klasse auf
                //unsere Variable uebertragen
                
    $this->db db_sql::getInstance();
             }


          public function 
    __call ($name$param)
             {
               
    //Bei einem Fehlaufruf sollen unsere Eintraege angezeigt werden,
               //Diese Methode kommt aber erst spaeter
             
    }
       }
    ?>


    show_entries
    Bis jetzt tut sie nichts, außer sich mit der Datenbank zu verbinden.Also erstellen wir nun die nächste Methode (in Interface gucken), ok, also kommt jetz show_entries(), um die Einträge ausgeben zu lassen. Die Funktion von PDO wird in der Methode erklärt...
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    11:
    12:
    13:
    14:
    15:
    16:
    17:
    18:
    19:
    20:
    21:
    22:
    23:
    24:
    25:
    26:
    27:
    28:
    29:
    30:
    31:
    32:
    33:
    34:
    35:
    36:
    37:
    38:
    39:
    40:
    41:
    42:
    43:
    44:
    45:
    46:
    47:
    
    <?php
          
    //Die Ausgabe der Eintraege
          
    public function show_entries ()
             {
                
    //Ueber unser PDO-Objekt, welches der Kontruktor erstellt hat,
                //bereiten wir nun eine Abfrage vor (prepare = vorbereiten)
                
    $stmt $this->db->prepare('SELECT gbook_id, name, text, timestamp
                                            FROM gbook
                                            ORDER BY gbook_id DESC'
    );


                
    //Das gesamte Statement abschicken (execute = ausfuehren)
                
    $stmt->execute();


                
    //Objekt aus der Template-Klasse erstellen
                //und an den Konstruktor den Namen des Templates geben
                
    $tpl = new template ('gbook');


                
    //Unser SQL-Result nun ausgeben, als Attribut setzen
                //wir FETCH_ASSOC, dadurch erhalten wir ein assoziatives
                //Array, wie mysql_fetch_assoc()
                
    while($row $stmt->fetch(PDO::FETCH_ASSOC))
                   {
                      
    //Text formatieren
                      
    $row['text'] = nl2br(
                                           
    trim(
                                                
    htmlentities(
                                                             
    preg_replace('/\S{40}/''\0 '$row['text']))));
                       
    $row['name'] = htmlentities($row['name']);
                      
    //Die Ausgabe in den Loop setzen, da $row ein
                      //asso. Array ist, koennen wir es so benutzen
                      //und haben im Template {Spaltenname} ersetzt
                      
    $tpl->loop('ausgabe'$row );
                   }

                
    //Wenn keine Schleife existiert, so wurde auch nix ausgegeben
                
    if(!$tpl->loop_exists('ausgabe'))
                   
    $tpl->loop('ausgabe', array('text' => '<i><b>Kein Eintrag vorhanden</b></i>') );


                
    //Ausgeben, die 0, da das Template bei {EPLODE} geteilt ist
                //und wir Part 1 (im Array 0) ausgeben wollen
                
    $tpl->show(0);
             }
    ?>


    Diese Methode funktioniert noch ohne Blätterfunktion, aber das soll und vorerst nicht weiter stören. Aufällig ist die Klasse template, dadurch wird das Template unseres Gästebuch ausgegeben. Die Funktionsweise dieser Klasse finden Sie im Laufe des Tutorials schon noch raus Die Template-Klasse gibt es anschließend zum Download. Das Template sieht nun wie folgt aus:
    gbook.htm
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    11:
    
    <table border="0" width="500px">
    {loop=ausgabe}
    <tr>
    <td><b>{name}</b> <i>vom {datum}</i></td>
    </tr>

    <tr>
      <td><p>{text}</p></td>
    </tr>
    {loop=ausgabe end}
    </table>


    Die Template Klasse, auf die wird nicht weiter eingegangen, die ist einfach nur zum Verwenden da
    template.class.php
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    11:
    12:
    13:
    14:
    15:
    16:
    17:
    18:
    19:
    20:
    21:
    22:
    23:
    24:
    25:
    26:
    27:
    28:
    29:
    30:
    31:
    32:
    33:
    34:
    35:
    36:
    37:
    38:
    39:
    40:
    41:
    42:
    43:
    44:
    45:
    46:
    47:
    48:
    49:
    50:
    51:
    52:
    53:
    54:
    55:
    56:
    57:
    58:
    59:
    60:
    61:
    62:
    63:
    64:
    65:
    66:
    67:
    68:
    69:
    70:
    71:
    72:
    73:
    74:
    75:
    76:
    77:
    78:
    79:
    80:
    81:
    82:
    83:
    84:
    85:
    86:
    87:
    88:
    89:
    90:
    91:
    92:
    93:
    94:
    95:
    96:
    97:
    98:
    99:
    100:
    101:
    102:
    103:
    104:
    105:
    106:
    107:
    108:
    109:
    110:
    111:
    112:
    113:
    114:
    115:
    116:
    117:
    118:
    119:
    120:
    121:
    122:
    123:
    124:
    125:
    126:
    127:
    128:
    129:
    130:
    131:
    132:
    133:
    134:
    135:
    136:
    137:
    138:
    139:
    140:
    141:
    142:
    143:
    144:
    145:
    146:
    147:
    148:
    149:
    150:
    151:
    152:
    153:
    154:
    155:
    156:
    157:
    158:
    159:
    160:
    161:
    162:
    163:
    164:
    165:
    166:
    167:
    168:
    169:
    170:
    171:
    172:
    173:
    174:
    175:
    176:
    177:
    178:
    179:
    180:
    181:
    182:
    183:
    184:
    185:
    186:
    187:
    188:
    189:
    190:
    191:
    192:
    193:
    194:
    195:
    196:
    197:
    198:
    199:
    200:
    201:
    202:
    203:
    204:
    205:
    206:
    207:
    208:
    209:
    
    <?php
    /**
    * Template Klasse
    * @author Sebastian Clemens
    * @package short-version
    * @version 1.0
    * @file template.class.php
    *
    * Hier werden die Templates verarbeitet,
    * Platzhalter & Loops ersetzt etc...
    */

    class template
    {
      private 
    $dir "./templates/";  //Pfad zu den Templates
      
    private $keys//Namen des Platzhalters + Wert
      
    private $parts//Teile des Templates, bei {EXPLODE} geteilt
      
    private $loop//Schleifen
      
    private $inc//Includes


    //Konstruktor
    //Lädt die Template Datei
      
    function __construct ($file$ort 1)
      {
        
    $this->keys = array();
        
    $this->parts = array();
        
    $this->loop = array();
        
    $this->include = array();


        
    //Endung hinzufuegen (kann auch ohne angegeben werden)
        
    if (substr($file, -4) != ".htm")
          
    $file .= ".htm";


          try
        {

          
    //Den Pfad vervollstaendigen
          
    if($ort == 1) {
            if(
    file_exists($this->dir.$file)) {
              
    $file $this->dir.$file;
            }
            else {
              throw new 
    Exception ();
            }
          }
          else if(
    $ort == 2) {
            if(
    file_exists($file)) {
              
    $file $file;
            }
            else {
              throw new 
    Exception ();
            }
          }

        }
        catch(
    Exception $e)
        {
          die(
    "Templatedatei nicht gefunden: <b>$file</b><br>in Datei: "
          
    .$e->getFile()."<br>in Zeile: ".$e->getLine());
        }


      
    #$inhalt = implode("", file($file));
      
    $inhalt file_get_contents($file);

      
    $this->parts explode("{EXPLODE}"$inhalt);
      }


      
    //Einen Platzhalter definieren
      
    function __set($key$wert)
      {
        
    $this->keys[$key] = $wert;
      }


      
    //Platzhalter aus einem assoziativen Array
      //herauslesen und in $this->keys hinzufügen
      
    function set_array ($array)
      {
        if(!
    is_array($array))
          return 
    false;

        foreach (
    $array as $keys => $wert)
        {
          
    $this->keys[$keys] = $wert;
        }
      }


      
    //Speichert die Schleife erstma zwischen
      
    function loop ($name$array)
      {
        if(
    is_array($name) or !is_array($array))
          return 
    false;

        
    $this->loop[$name][] = $array;
      }


      
    //prueft Loop auf Existenz
      
    function loop_exists ($name)
      {
        if(
    array_key_exists($name$this->loop))
          return 
    true;
        else
          return 
    false;
      }


      
    //Gibt den Part ersetzt aus
      
    function show ($pos)
      {
        echo 
    $this->replace($pos);
      }


      
    //Ersetzt alle Platzhalter
      
    protected function replace($pos)
      {
        
    $show $this->parts[$pos];

        foreach (
    $this->keys as $keys => $wert)
        {
          
    $show str_replace('{'.$keys.'}'$wert$show);
        }

        if(
    count($this->loop) != 0)
          
    $show $this->loop_replace($show);

        
    $show $this->include_replace($show);

        
    //Unbesetzte Platzhalter entfernen
        
    while(preg_match_all("/{(.*)}/"$show$datei))
        {
          
    $show str_replace($datei[0][0], ""$show);
        }

        return 
    $show;
      }


      
    //Ersetzt alle Schleifen
      
    protected function loop_replace $show)
      {
        
    $loop "";

        foreach (
    $this->loop as $name => $array)
        {

          
    /**
          * TODO: mit preg_match loesen, explode() ist unschoen
          */
          
    $loop_inhalt explode("{loop=$name}"$show);
          
    $loop_inhalt explode("{loop=$name end}"$loop_inhalt[1]);
          
    $loope $loop_inhalt[0];

          for(
    $i 0$i count($array); $i++)
          {
            foreach(
    $array[$i] as $key => $wert)
            {
              
    $loope str_replace("{".$key."}"$wert$loope);
            }
          
    $loop .= $loope;
          
    $loope $loop_inhalt[0];
          }

        
    $show preg_replace("/{loop=$name}.*{loop=$name end}/Usi"$loop$show);
        
    $loop "";
        }

        return 
    $show;
      }


      
    //Ersetzt alle includes
      
    protected function include_replace ($show)
      {

       while(
    preg_match_all("/{include=\"(.*)\"}/Usi"$show$datei))
       {
          foreach(
    $datei[1] as $file)
          {

            if(
    $file != "") {
            if(!
    file_exists($file))
              exit(
    "Die Templatedatei <b>$file</b> existiert nicht.");


            
    ob_start();
            require_once(
    $file);
            
    $content ob_get_contents();
            
    ob_end_clean();

            
    $show str_replace("{include=\"$file\"}"$content$show);
            }
            else
              
    $show str_replace("{include=\"\"}"""$show);
          }
        }

        return 
    $show;
      }

    }
    ?>


    Nun ist schon diese Methode schon voll funktionsbereit, legen Sie nun alle Klassen in extra Dateien mit NameDerKlasse.class.php in den Ordner "class/" ab. Die Templates kommen in den Ordner "templates/". Die Tempates haben die Endung .htm. Wenn wir nun alle Klassen und Interface includieren, ein Objekt erzeugen und anschließend die Methode aufrufen, sehen wir schon, dass es funktioniert .
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    
    <?php
    //Alle Dateien Einbinden

    //Objekt erzeugen
    $obj = new gbook;

    //Methode aufrufen
    $obj->show_entries();
    ?>


    eintragen
    Nun die nächste Methode oder auch "Funktion unseres Skriptes" (ins Interface gucken), ok, also einen Eintrag hinzufügen. Die Kommentare sollen alles weitere eigentlich ausreichend genug erklären:
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    11:
    12:
    13:
    14:
    15:
    16:
    17:
    18:
    19:
    20:
    21:
    22:
    23:
    24:
    25:
    26:
    27:
    28:
    29:
    30:
    31:
    32:
    33:
    34:
    35:
    36:
    37:
    38:
    39:
    40:
    41:
    42:
    43:
    44:
    45:
    46:
    47:
    48:
    49:
    50:
    51:
    52:
    53:
    54:
    55:
    56:
    57:
    58:
    59:
    
    <?php
          
    public function eintragen ()
             {

                
    //Wenn nichts abgeschickt wurde, das Formular anzeigen
                
    if(!$_POST)
                   {
                      
    $tpl = new template ('formular');
                      
    $tpl->show(0);
                      
    //Damit der restliche Code nicht unnoetig geparst wird, abbrechen
                      
    exit();
                   }


                else
                   {
                      
    $fehler '';

                      
    //Falls ein Fehler auftritt, an die Variable dranhaengen
                      
    if(empty($_POST['name']))
                         
    $fehler.= '<li>Der Name ist nicht ausgef&uuml;llt</li>';

                      if(empty(
    $_POST['text']))
                         
    $fehler.= '<li>Der Eintragungstext ist nicht ausgef&uuml;llt</li>';


                      
    //Falls es Fehler gab, diese ausgeben
                      
    if($fehler != '')
                         {
                            
    $tpl = new template ('formular');
                            
    $fehler 'Es ist einer der folgenden Fehler aufgetreten:<br /><ul>' $fehler '</ul>';
                            
    $tpl->fehler $fehler '<br />';
                            
    $tpl->show(0);
                         }
                      else
                         {
                             
    //Wieder unser Query vorbereiten
                             
    $stmt $this->db->prepare('INSERT INTO gbook (gbook_id, name, text, timestamp)
                                                                    VALUES (\'\', :name, :text, :time)'
    );


                             
    //Alle Platzhalter ersetzen und auf Typ + Laenge pruefen
                             //Mit binParam ersetzen wir die :var im prepared Statement
                             //3.Parameter: Auf was geprüft werden soll, 4. Auf Laenge kuerzen
                             
    $stmt->bindParam('name'$_POST['name'], PDO::PARAM_STR50);
                             
    $stmt->bindParam('text'$_POST['text'], PDO::PARAM_STR$this->l);
                             
    $stmt->bindParam('time'time(), PDO::PARAM_INT);


                             
    //Nun das ganze abschicken
                             
    $stmt->execute();


                             
    //und die Methode show() aufrufen, um alle Eintraege zu zeigen
                             
    $this->show_entries(false$this->isAdmin());
                         }
                   } 
    //else !$_POST
             
    //eintrag() ENDE
    ?>


    Das dazugehörige Template:
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    11:
    12:
    13:
    14:
    15:
    16:
    17:
    18:
    19:
    
    <form method="post" action="">

    <table border="0" cellpadding="5">

    <tr>
      <td>Dein Name: </td>
      <td><input type="text" name="name" value="{name}" size="25" /></td>
    </tr>

    <tr>
      <td>Dein Eintrag: </td>
      <td><textarea cols="50" rows="10" name="text">{text}</textarea></td>
    </tr>

    <tr>
      <td colspan="2"><input type="submit" value="  Absenden  " /></td>
    </tr>

    </table>



    Klasse um Admin erweitern
    Nun müssen wir nur noch diese Methode aufrufen, wenn es POST-Werte gibt und unser Gästebuch ist fertig! Und was nun? Ich will aba ne Admin-Funktion haben ... hm, dann schreib ich den Code mal um... Das muss nicht sein! Jetzt kommt der große Vorteil von OOP, wir können unsere Klasse nun erweitern. Darauf hin brauchen wir natürlich wieder ein Interface, dass die neuen Methoden der neuen Klasse auflistet. Was soll unser Admin denn alles können? Das Interface verrät es uns!
    gbookAdmin.class.php
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    11:
    12:
    13:
    14:
    15:
    16:
    17:
    18:
    19:
    20:
    21:
    22:
    
    <?php
    //Was der Admin alles darf
    //Zusatzanwendungen
    interface gbookAdmin
       
    {
          
    //Eintraege loeschen per id
          
    public function delete_entry ($id);

          
    //Eintraege editieren koennen
          
    public function edit_entry ($id);

          
    //Natuerlich muss er sich einloggen koennen
          
    public function einloggen ();

          
    //Admin wieder ausloggen
          
    public function ausloggen ();

          
    //Der Admin muss innerhalb der Klasse
          //identifiziert werden koennen
          
    public function isAdmin ();
       }
    ?>



    Neue Admin Klasse
    Unsere neue Klasse muss nun noch mehr können. Aber dann haben wir ja 2 Objekte, die alte und neue Klasse. Um das zu verhindern, gibt es in OOP die vererbungen. Damit erbt unsere neue Klasse alle Methoden und Eigenschafften der Elterklasse (hier gbook). Vererbung funktioniert über das Keyword extends. Aber Achtung! Eine Klasse kann nur eine weitere Klasse erben, mehr nicht, jedoch kann eine Klasse mehrere Interface implementieren. Ich gebe die neue Klasse mal sofort komplett, damit dieses Tutorial nicht noch länger wird. Die Kommentare sollten alles ausreichend erklären.
    gbookErweitert.class.php
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    11:
    12:
    13:
    14:
    15:
    16:
    17:
    18:
    19:
    20:
    21:
    22:
    23:
    24:
    25:
    26:
    27:
    28:
    29:
    30:
    31:
    32:
    33:
    34:
    35:
    36:
    37:
    38:
    39:
    40:
    41:
    42:
    43:
    44:
    45:
    46:
    47:
    48:
    49:
    50:
    51:
    52:
    53:
    54:
    55:
    56:
    57:
    58:
    59:
    60:
    61:
    62:
    63:
    64:
    65:
    66:
    67:
    68:
    69:
    70:
    71:
    72:
    73:
    74:
    75:
    76:
    77:
    78:
    79:
    80:
    81:
    82:
    83:
    84:
    85:
    86:
    87:
    88:
    89:
    90:
    91:
    92:
    93:
    94:
    95:
    96:
    97:
    98:
    99:
    100:
    101:
    102:
    103:
    104:
    105:
    106:
    107:
    108:
    109:
    110:
    111:
    112:
    113:
    114:
    115:
    116:
    117:
    118:
    119:
    120:
    121:
    122:
    123:
    124:
    125:
    126:
    127:
    128:
    129:
    130:
    131:
    132:
    133:
    134:
    135:
    136:
    137:
    138:
    139:
    140:
    141:
    142:
    143:
    144:
    145:
    146:
    147:
    148:
    149:
    150:
    151:
    152:
    153:
    154:
    155:
    156:
    157:
    158:
    159:
    160:
    161:
    162:
    163:
    164:
    
    <?php
    class gbookErweitert extends gbook
                         
    implements gbookInterfacegbookAdmin
       
    {
          private 
    $session null//Damit man den Admin weiterhin identifizieren kann


          //Nun ueberschreiben wir den Konstruktor der Elternklasse
          
    public function __construct ()
             {
                
    //Jedoch fuehren wir trotzdem den Konstruktor
                //aus, da wir ihn brauchen (db-Objekt erstellen)
                
    parent::__construct();

                
    //Ausserdem muss neuerdings die Sessions gestartet werden
                
    session_start();


                
    //Wenn der Administrator eingeloggt ist, Eigenschafft setzen
                
    if(isset($_SESSION['admin']))
                   
    $this->session $_SESSION['admin'];
             }


          
    //Ist der User der Admin?
          
    public function isAdmin ()
             {
                
    //Wenn die Session gesetzt ist, ist er es
                
    if($this->session !== null)
                   return 
    true;

                
    //Wenn nicht, dann nicht
                
    return false;
             }


          
    //Die Loeschfunktion
          
    public function delete_entry ($id)
             {
                
    //Ist er ueberhaupt der Admin?
                
    if($this->isAdmin())
                   {
                      
    //Abfrage vorbereiten
                      
    $stmt $this->db->prepare('DELETE FROM gbook WHERE gbook_id = :id');

                      
    //Platzhalter ersetzen und pruefen, eig unnoetig, da
                      //Bei der Funktionsuebergabe schon auf int geprueft wird
                      
    $stmt->bindParam('id'$idPDO::PARAM_INT11);

                      
    //Und abschicken
                      
    $stmt->execute();

                      
    //Eintraege einzeigen, 1.Parameter: $fehler, 2. $admin
                      //Der 2te soll aktiviert werden
                      
    $this->show_entries(falsetrue);
                   }


                
    //Wenn nicht...
                
    else
                   {
                      
    $fehler '<b>Du bist nicht berechtigt, diese Funktion auszuf&uuml;hren!</b><br /><br />';

                      
    $this->show_entries($fehler);
                   }
             }

          public function 
    edit_entry ($id)
             {
                if(
    $this->isAdmin())
                   {
                      if(
    $_POST)
                        {
                           
    $stmt $this->db->prepare('UPDATE gbook SET name=:name, text=:text WHERE gbook_id LIKE :id');

                           
    //Und abschicken
                           
    $stmt->bindParam('name'$_POST['name'], PDO::PARAM_STR50);
                           
    $stmt->bindParam('text'$_POST['text'], PDO::PARAM_STR$this->l);
                           
    $stmt->bindParam('id'$idPDO::PARAM_INT11);
                           
    $stmt->execute();
                        }


                         
    //Ausgabe des zu editierenden Eintrages
                         
    $stmt $this->db->prepare('SELECT name, text FROM gbook WHERE gbook_id LIKE :id');

                         
    $stmt->bindParam('id'$idPDO::PARAM_INT11);
                         
    $stmt->execute();
                         
    $row $stmt->fetch(PDO::FETCH_ASSOC);

                         
    $tpl = new template ('formular');
                         
    $tpl->set_array($row);
                         
    $tpl->show(0);

                   } 
    //isAdmin() ENDE

                   
    else
                   {
                      
    $fehler '<b>Du bist nicht berechtigt, diese Funktion auszuf&uuml;hren!</b><br /><br />';

                      
    $this->show_entries($fehler);
                   }
             }
    //edit_entry() ENDE


          //Nun die Einloggfunktion
          
    public function einloggen ()
             {
                
    $username 'test';
                
    $pw       'geheim';

                if(
    $_POST)
                   {
                      
    //Siehe Methode: eintragen(); ist fast das gleiche ;)
                      
    $fehler '';

                      if(empty(
    $_POST['username']))
                         
    $fehler.= '<li>Username ist leer</li>';

                      if(empty(
    $_POST['pw']))
                         
    $fehler.= '<li>Passwort leer</li>';

                      if(
    $_POST['username'] != $username)
                         
    $fehler.= '<li>Username ist falsch</li>';

                      if(
    $_POST['pw'] != $pw)
                         
    $fehler.= '<li>Passwort ist falsch</li>';


                      if(
    $fehler != '')
                         {
                            echo 
    'Einer der folgenden Fehler ist aufgetreten:<br /><br /><ul>' $fehler '</ul>';
                            echo 
    '<br /><br /><a href="' $_SERVER['PHP_SELF'] . '">Zur&uuml;ck</a>';
                            exit();
                         }

                      else
                         {
                            
    //Alles geklappt, also Session setzen
                            
    $_SESSION['admin'] = true;

                            
    //Und Eigenschaft
                            
    $this->session true;

                            
    //Wieder zur Hauptseite navigieren
                            
    header('Location: ' $_SERVER['PHP_SELF']);
                         }
                   } 
    //$_POST
             
    //einloggen()


          //Administrator ausloggen
          
    public function ausloggen ()
             {
                if(
    $this->isAdmin())
                   {
                      
    $this->session null;
                      
    session_destroy();
                   }
                
    header('Location: ' $_SERVER['PHP_SELF']);
             }

       }
    ?>


    Zugriffsrechte
    Nun müssen wir bloß ein Objekt dieser Klasse erzeugen und wir haben zugriff auf alle mit public gekennzeichneten Methoden und Eigenschafften. Protected bedeutet, Zugriff nur für die eigene- und Kindklassen. Bei private hat nur die Eigene Klasse Zugriff. Dadurch kann man genau definieren, was der User und/oder Anwender unserer Klasse darf, und was nicht. Daher ist sinnvoll, die Zugangsdaten der Datenbank beispielsweise auf private zu setzen.

    Die __autoload Funktion
    Soweit so gut, aber eines fehlt noch: Die Klasse und ihre Methoden zum richtigen Zeitpunkt benutzen. Aber wir wollen nicht in unsere index.php alle Klassen einzelnd per require_once() einbinden, und genau dafür gibt es seit php5 die __autoload Funktion. Diese Funktion wird immer aufgerufen, wenn der new-Operator eingesetzt wird und die Instanz einer Klasse auf ein Objekt ablegt. Der Übergabe Parameter der __autoload Funktion ist der Name der Klasse, man könnte es also ganz simpel so machen:
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    
    <?php
    function __autoload ($classname)
       {
          require_once(
    'Pfad/zu/den/Klassen/' $classname '.php');
       }
    ?>


    Aber das ist nicht so gut, da ein Objekt der gleichen Klasse mehrmals erzeugt werden kann (Beispiel template-Klasse), wird die Datei auch mehrmals eingebunden (obwohl once das eig schon verhindern). Schöner wäre es zu Prüfen, ob es die Klasse oder Interface noch nicht gibt und erst dann includieren, deshalb hier eine besser Version der __autoload Funktion:
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    11:
    12:
    13:
    14:
    15:
    16:
    
    <?php
    //Autoload der Klassen
    function __autoload ($class)
       {
          
    //Wenn die Klasse oder Interface noch nicht existieren
          
    if(!class_exists($class) or !interface_exists($class))
             {
                
    $file './class/' $class '.class.php';
                
    //dann Datei auf Existenz prufen
                
    if(!file_exists($file))
                   die (
    '__autoload Fehler!<br /> Die Klasse ' $class ' und die Datei ' $file ' existieren nicht.');

                require_once(
    $file);
             }
       }
    ?>


    Nun brauchen wir uns nie wieder um das Einbinden der Klassen kümmern, sondern müssen sie bloß in den richtigen Ordner abspeichern und können sie sofort verweden

    Anwendung der Klassen
    Aber eines fehlt noch: Wann wende ich zum richtigen Zeitpunkt die richtigen Methoden an? Sowas wird meist über die URL und GET-Parameter gelöst. Ist der und der GET-Parameter gesetzt, wird die und die Methode ausgeführt. Man könnte es mit einer riesigen switch-Anweisung lösen oder so ganz einfach:
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    11:
    12:
    13:
    14:
    15:
    16:
    17:
    18:
    19:
    20:
    21:
    22:
    23:
    24:
    25:
    26:
    
    <?php
    $obj 
    = new gbookErweitert;

    //Standardmaessig alle Eintraege anzeigen + Adminpruefung
    if(!$_GET)
       
    $obj->show_entries(false$obj->isAdmin());


    //Bei einer Get-Variable die entsprechende
    //Methode dazu ausfuehren
    foreach($_GET as $methode => $parameter)
       {
          if(
    $parameter != '')
             
    $obj->$methode($parameter);
          else
             
    $obj->$methode();

          
    //Nur ein Durchlauf
          
    break;
       }


    //Wenn auf Einloggen geklickt wurde, einloggen
    if(isset($_POST['login']))
       
    $obj->einloggen();
    ?>


    Keine Angst: Sollte etwas falsches in der URL stehen, kümmert sie __call darum. Aber nun kann er per GET alle Methoden einfach so aufrufen! Auch das ist kein Problem, da nur die Methoden aufgerufen werden können, die auf public gestellt sind, und das sind nur die Methoden, bei denen der öffentliche Zugriff erlaubt ist

    Die index.php einmal insgesamt:
     PHP 
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    10:
    11:
    12:
    13:
    14:
    15:
    16:
    17:
    18:
    19:
    20:
    21:
    22:
    23:
    24:
    25:
    26:
    27:
    28:
    29:
    30:
    31:
    32:
    33:
    34:
    35:
    36:
    37:
    38:
    39:
    40:
    41:
    42:
    43:
    44:
    45:
    46:
    47:
    48:
    49:
    
    <?php
    if (version_compare(phpversion(), '5.1.0''<') == true) {
       die (
    'Benötigt PHP5.1 oder aufw&auml;rts');
    }

    if(!
    class_exists('PDO'))
       die (
    'PDO wird ben&ouml;tigt');


    //Autoload der Klassen
    function __autoload ($class)
       {
          
    //Wenn die Klasse oder Interface noch nicht existieren
          
    if(!class_exists($class) or !interface_exists($class))
             {
                
    $file './class/' $class '.class.php';
                
    //dann Datei auf Existenz prufen
                
    if(!file_exists($file))
                   die (
    '__autoload Fehler!<br /> Die Klasse ' $class ' und die Datei ' $file ' existieren nicht.');

                require_once(
    $file);
             }
       }

    $obj = new gbookErweitert;

    //Standardmaessig alle Eintraege anzeigen + Adminpruefung
    if(!$_GET)
       
    $obj->show_entries(false$obj->isAdmin());


    //Bei einer Get-Variable die entsprechende
    //Methode dazu ausfuehren
    foreach($_GET as $methode => $parameter)
       {
          if(
    $parameter != '')
             
    $obj->$methode($parameter);
          else
             
    $obj->$methode();

          
    //Nur ein Durchlauf
          
    break;
       }


    //Wenn auf Einloggen geklickt wurde, einloggen
    if(isset($_POST['login']))
       
    $obj->einloggen();
    ?>


    Nur noch die Templates etwas bearbeiten und die Links setzen, zB: <a href="?eintragen">Ins Gästebuch eintragen</a>, um einen Eintrag zu erlauben.

    Fertig
    Ich hoffe, die Vorteile von OOP sind jetzt mal deutlich geworden, wenn man jetzt zB noch eine Kommentarfunktioneinbaun will, einfach im Template den entsprechenden Link setzen und die Methode dazu schreiben und fertig.

    Demo
    Auf bplaced habe ich eine Demo zu diesem Tutorial hochgeladen, ich habe bloß die Templates bearbeitet und etwas CSS hinzugefügt (PS: Habe zum Spaß FF-Spezifische CSS-Befehle benutzt, das GB sieht also nur dort schön aus )


    Man könnte jetzt auch andere Templates schreiben und das GB nochmal anders aussehen lassen, das auch wieder ein Vorteil von Templates, der Grafiker kann sich dort ohne lästigen PHP-Code austoben.


    Danke schonmal für das Lesen dieses langem Tutorials,
    sollten sich Fehler eingeschlichen haben, bitte bescheid sagen
    Freue mich wie immer sehr über euer Feedback, ich stelle auch alle Dateien
    (wie sie aufm Server bei bplaced sind) noch einmal zum Download bereit.

    Für die Gäste
    Download

    Wenn ihr noch wissen wollt, weshalb ich require anstatt include, single qoutes (') statt double (") oder echo statt print verwende, fragt einfach extra oder ich werde es auf Wunsch allen erklären, denn das hat schon seinen Grund.

    Gruß,
    Basti

    Dateianhang:
     Gbook_in_OOP.zip  (18 KB, 3488 mal herruntergeladen)



  • Post wurde schon 14x editiert, das letzte mal am 04.07.2021 um 21:22 von Htaccess
    06.09.2009, 17:26 Profil | PM | E-Mail  
    stex
    Mitglied
    Sehr guter User


    Dabei seit: 14.04.2009
    Herkunft: Hannover
    Posts: 650
          Zitat | Bearbeiten

    Hey Basti

    Schönes Tutorial zum Anfang. Erklärt ganz gut die Grundlegenden Dinge in Sachen OOP. Aber ich habe auch 2 Punkte.

    Der erste wäre , dass ein gutes Gästebuch auch deutlich einfacher zu programmieren ist. Dies ist wirklich eine sehr aufwendige Variant aber wie gesagt gut zum OOP lernen.

    So nun der andere Punkt ist es das OOP nicht wirklich notwendig ist sowie in C oder C++ , da Webprogrammierung im generellen nicht die Vorteile von OOP so starkt nutzt das es ein MUSS ist.

    Jedem das seine es kann auf keinen Fall Schaden OOP zu lernen. Muss jeder selbst entscheiden


    06.09.2009, 18:01 Profil | PM | E-Mail  
    B.C.
    Mitglied
    Sehr guter User


    Dabei seit: 04.02.2009
    Herkunft: Niedersachsen
    Posts: 797
          Zitat | Bearbeiten

    Hi,

    wie im Tutorial gesagt, der große Vorteil liegt in der einfachen Erweiterung.

    Stell dir ein CMS vor: die Hauptklasse bastelt alles zusammen, und wird mit news, umfragen, user etc erweitert.
    Dann ist dort OOP sehr wohl Vorteilhaft.
    Das Gästebuch lässt sich auch leicht erweitern, aber
    du hast natürlich Recht, fürn Gästebuch viel zu aufwendig,
    jedoch bei großem Projekten (Browsergames, Community etc.) ist das sehr Vorteilhaft.

    Gruß,
    Basti


    06.09.2009, 18:05 Profil | PM | E-Mail  
    RobertG
    Mitglied
    Aktiver User


    Dabei seit: 24.03.2009
    Herkunft: keine Angabe
    Posts: 128
          Zitat | Bearbeiten

    Sehr schönes Tutorial Basti. Du hast die Vorteile der Objektorientierten Programmierung gut und einfach erklärt. ( Obwohl, das Tutorial erfordert schon eine bisschen tiefere Kenntniss der Materie, so sind z.B. Interfaces am Anfang recht verwirrend... )
    Alles in Allem gute Arbeit!



    NCM, CMS based on NodeJS
    http://graczykr.ncm.jit.su


    Post wurde schon 1x editiert, das letzte mal am 06.09.2009 um 21:18 von RobertG
    06.09.2009, 21:17 Profil | PM | E-Mail  
    Gast


          Zitat | Bearbeiten

    Geniales Tutorial nur in der Praxis gerade bei einem Browsergame wird das gästebuch wohl leider versagen ich sag nur spambot gefahr...

    deshalb wäre n captcha noch ganz sinnvoll wobei man hier wieder den vorteil der OOP sieht denn jetzt kann man den ganz leicht einbauen


    19.09.2009, 15:40  
    Gast


          Zitat | Bearbeiten

    Sorry, aber dein tutorial ist einfach nicht gut. Deine Interfaces machen überhaupt keinen Sinn. Ausserdem sind die Methoden sehr schlecht gekapselt. Zuviel Funktionalität. Du vermischt viel zu viel die Business-Logik mit der Ausgabe. Das ist einfach kein guter Stil. Du programmierst unnötig komplex und am Ende kommt dennoch Spaghetti raus..


    21.04.2010, 11:16  
    ProCoder
    Mitglied
    Neuling


    Dabei seit: 20.04.2010
    Herkunft: keine Angabe
    Posts: 6
          Zitat | Bearbeiten

    Seh ich genau so


    21.04.2010, 19:16 Profil | PM | E-Mail  
    B.C.
    Mitglied
    Sehr guter User


    Dabei seit: 04.02.2009
    Herkunft: Niedersachsen
    Posts: 797
          Zitat | Bearbeiten

    Jap, inzwischen hab ich schon einiges dazu gelernt. Der Code ist nicht wirklich gut durchdacht ...
    Könnte ich ja nachholen und das Tutorial komplett überarbeiten ... mal sehn wann ich Zeit finde.
    Da da oben sind so meine Anfänge in OOP




    Post wurde schon 1x editiert, das letzte mal am 21.04.2010 um 22:16 von B.C.
    21.04.2010, 22:16 Profil | PM | E-Mail  
    asdf
    Mitglied
    Guter User


    Dabei seit: 26.10.2009
    Herkunft: keine Angabe
    Posts: 449
          Zitat | Bearbeiten

    demolink = 404


    23.04.2010, 17:28 Profil | PM | E-Mail  
    floppy
    Mitglied
    Neuling


    Dabei seit: 05.07.2010
    Herkunft: keine Angabe
    Posts: 1
          Zitat | Bearbeiten

    Ja, @ proCoder und den Typ da drüber:

    Ihr könntet ja mal erklären wie man es richtig macht. Für die Leute, die was lernen wollen.


    05.07.2010, 14:13 Profil | PM | E-Mail  
    Gast


          Zitat | Bearbeiten

    Zitat:
    Orginal von stex
    So nun der andere Punkt ist es das OOP nicht wirklich notwendig ist sowie in C oder C++ , da Webprogrammierung im generellen nicht die Vorteile von OOP so starkt nutzt das es ein MUSS ist.


    C hat gar keine OOP-Möglichkeiten. C ist Prozedual.


    18.07.2010, 11:07  
    coderD
    Mitglied
    Neuling


    Dabei seit: 25.05.2020
    Herkunft: keine Angabe
    Posts: 1
         Danke ;) hat mir sehr weiter geholfen Zitat | Bearbeiten

    Dein Beitrag hat mir sehr geholfen um das OOP richtig zu verstehen! ps: für alle die es lesen __autoload ist Seit PHP 7.2 veraltet ich habe es so gelöst !

    function my_autoloader($class) {
    if(!class_exists($class) or !interface_exists($class))
    {
    $file = 'class/' . $class . '.class.php';
    //dann Datei auf Existenz prufen
    if(!file_exists($file))
    die ('__autoload Fehler!<br /> Die Klasse ' . $class . ' und die Datei ' . $file . ' existieren nicht.');

    require_once($file);
    }
    }

    spl_autoload_register('my_autoloader');

    VIELEN Dank für denn tollen Beitrag


    25.05.2020, 20:01 Profil | PM | E-Mail  
    Seiten (1):  1 
    PHP-Support.de » Programmierung » PHP & MySQL » Codeschnipsel » Ein Gästebuch mit OOP   

    Neues Thema | Antworten   


    Powered by Command Board 1.0 - Beta 2.0 © 2004-08 PHP-Einfach | Impressum | Datenschutz