dedlfix: Typische Anfängerfehler

Beitrag lesen

echo $begrüßung;

<?php

if(isset($_POST['name']) AND isset($_POST['comments']))
      {
        $name = htmlentities($_POST["name"]);
        $comments = htmlentities($_POST["comments"]);
        $posting = '<p>' . $name . " schrieb:</p>\n\n  <p>" . $comments . "</p>\n\n  ";
        $posting = stripslashes($posting);
        $posting = str_replace("\\", "\", $posting);

$posts = fopen("posts.txt","a") or die('shit happens');
        fwrite($posts, $posting);
        fclose($posts);
      }

$give = implode("",file("posts.txt"));
    echo $give;
  ?>

  

> Würdest du noch etwas empfehlen?  
  
Viel lernen. Gerade als Anfänger macht man selbst Fehler und stolpert über diverse PHP-Eigenheiten. Eine davon hast du schon kennengelernt, die [Magic Quotes](http://de.php.net/manual/en/security.magicquotes.php). Die sind eigentlich für Ausgaben in Datenbanken gedacht, arbeiten aber an der falschen Stelle und beeinflussen so auch die Teile eines Scripts, die keine Datenbank-Ausgaben vornehmen. Dieses Feature stirbt ab PHP 6. Am besten ist es, es auch heute schon zu deaktivieren oder seine Auswirkungen zu eliminieren: [Disabling Magic Quotes](http://de.php.net/manual/en/security.magicquotes.disabling.php).  
  
Damit komme ich gleich zu einem nächsten Punkt. Der Übersicht ist es dienlich, wenn man sein Programm strukturiert und in die drei Teile Eingabe, Verarbeitung und Ausgabe separiert, das so genannte EVA-Prinzip. Magic Quotes beseitigen ist eine Eingabedatenbehandlung, sollte also zuerst erfolgen.  
  
htmlentities() solltest du zu Gunsten von htmlspecialchars() wieder vergessen. HTML verlangt eine Sonderbehandlung nur für einige wenige Zeichen. Für die anderen ist es perfekt, wenn sie direkt notiert stehen und nicht als Entity oder nummerische Zeichenreferenz. Da das eine Behandlung für ein Ausgabemedium ist, kommt sie in den Ausgabe-Teil.  
  
Es folgt meine Version des Scripts, Kommentare inklusive.  
  
  <?php  
  
Das ist der erste Teil der Eingabebehandlung.  
  
  if (get\_magic\_quotes\_gpc()) {  
    //von verlinkter Handbuchseite nehmen  
  }  
  
Prüfen der Eingabewerte gehört auch mit zum E-Teil. Davon abhängig ist aber der Verarbeitungsteil.  
  
  if (isset($\_POST['name']) and isset($\_POST['comments'])) {  
  
Umkopieren in neue Variablen sollte man sich schenken, auch wenn du sie dabei gleich behandelst. Die Behandlung ist sowieso an falschen Stelle der Verarbeitungsreihenfolge, denn das wäre Aufgabe für den A-Teil.  
  
  // $name = htmlentities($\_POST["name"]);  
  // $comments = htmlentities($\_POST["comments"]);  
  
Diese Zeilen entfallen ebenfalls, weil sie nicht (mehr) benötigt werden.  
  
  // $posting = str\_replace("\\\\", "\\", $posting);  
  // $posting = stripslashes($posting);  
  
Jetzt kommt der V-Teil, also die Verarbeitung der Werte.  
Ein »or die(...);« ist schnell eingefügt, aber in den wenigsten Fällen eine sinvolle Fehlerbehandlung. Zudem versaut es durch den sofortigen Gnadenschuss das Layout der restlichen Seite. Besser ist es, Fehler einzukalkulieren, sich Gedanken zu machen, was im Fehlerfall für die Anwendung gut wäre (Gibt es Alternativen? Sollte jemand über den Fehler benachrichtigt werden?) und dabei den Anwender nicht vergessen. Der sollte beispielsweise eine Tröstmeldung bekommen, mit einem Hinweis, wie er doch noch an sein Ziel kommen kann. Fehlermeldungen mit technischen Details interessieren ihn nicht. Und wenn doch, dann will man demjenigen solche Informationen nicht freiwillig preisgeben.  
  
  // $posts = fopen("posts.txt","a") or die('shit happens');  
  if ($posts = fopen('posts.txt', 'a')) {  
  
Rohdaten zu speichern sollte man stets einer Speicherung in Ausgabeformatierung vorziehen. Somit hat man es später einfacher, das Speichermedium zu wechseln. Wenn du erst mühsam den Inhalt aus dem HTML-Code klauben musst, wenn du später mal auf eine Datenbank umsteigen möchtest, dann weißt du warum dies  
  
    //$posting = '<p>' . $name . " schrieb:</p>\n\n  <p>" . $comments . "</p>\n\n  ";  
    //fwrite($posts, $posting);  
  
keine gute Idee war.  
  
Wenn dir PHP ab Version 5.1.0 zur Verfügung steht - das sollte, denn PHP4 ist schon eine Weile abgekündigt - dann nimm lieber [fputcsv()](http://de.php.net/manual/en/function.fputcsv.php)  
  
    if (! fputcsv($posts, array($\_POST["name"], $\_POST['comments'])) {  
      // Fehler beim Schreiben angemessen behandeln  
    }  
    fclose($posts);  
  
Ansonsten gibt es Nachbauten in den Userkommentaren der Handbuchseite. Diese Funktion kümmert sich darum, dass die Daten CSV-gerecht in der Daten zu stehen kommen und auch die zur Trennung der einzelnen Werte verwendeten Spezialzeichen entschärft werden, wenn diese als Datenbestandteil vorkommen.  
  
  } else {  
    // Fehler beim Öffnen/Anlegen der Datei angemessen behandeln  
  }  
  
Gänzlich unbeachtet gelassen habe ich das Thema [Sperren von Dateien](http://aktuell.de.selfhtml.org/artikel/programmiertechnik/dateisperren/index.htm). Diese "Verkehrsampel für Programme" braucht man, damit es keine Datenunfälle gibt. Alles weitere spar ich mir, denn das steht im verlinkten Artikel. Du solltest es dir nicht sparen, wenn dir deine Daten lieb sind. Im Labor wirst du nichts bemerken, doch in freier Wildbahn greifen gelegentlich mehrere Anwender gleichzeitig zu. Da sollte der andere warten bis der eine fertig ist.  
  
Es gibt die Funktion file\_get\_contents(), mit der man sich das unnötige Aufdröseln in Einzelzeilen durch file() und das Wiederzusammenfügen mit implode() sparen kann.  
  
    //$give = implode("",file("posts.txt"));  
  
Doch da ich das CSV-Format vorgeschlagen habe, müssen wir dazu passende Funktionen verwenden. Wir befinden uns nun außerhalb des if(isset($\_POST[...-Bereichs. Dieser Teil wird also auch ohne eine erfolgte Eingabe ausgeführt. Das ist auch der Grund, warum ich die Datei erneut öffne und nicht das Datei-Handle gleich weiterverwende, denn im Nicht-Post-Fall ist es ja nicht gültig.  
  
  $posts\_data = array();  
  if ($posts = fopen('posts.txt', 'r') {  
    while (false !== $line\_data = fgetcsv($posts, 4000)) {  
      $posts\_data[] = $line\_data;  
    }  
    fclose($posts);  
  }  
  
Auch hier fehlt wieder das Sperren von Dateien. Bitte selbst einbauen.  
  
Kommen wir nun zum A(usgabe)-Teil. $posts\_data ist nun ein Array, das weitere Arrays jeweils mit Namen und Kommentar enthält. Nun erst, beim Einfügen in die Ausgabe, werden die Werte dem Ausgabekontext gemäß angepasst.  
  
  //$posting = '<p>' . $name . " schrieb:</p>\n\n  <p>" . $comments . "</p>\n\n  ";  
  foreach ($posts\_data as $post)  
    printf("<p>%s schrieb:</p>\n\n  <p>%s</p>\n\n  ",  
      htmlspecialchars($post[0]),  
      htmlspecialchars($post[1]));  
  
  ?>  
  
printf() und die Platzhalter %s für Strings ist meiner Meinung nach der beste Kompromiss zwischen  
  echo "String mit $variable drin";  
und Rein-in-den-String-raus-aus-dem-String-und-wieder-rein-usw.  
  echo 'String, geteilt und ' . $variable . ' extra';  
  
  
  
echo "$verabschiedung $name";