Enrico: Unerklärliches Problem mit Verarbeiten eingelesener Textdatei

Guten Abend,

erstmal Entschuldigung für den Fall, dass mein Beitrag, wie es die Vorschau befürchten lässt, trotz Angabe von "~~~" zerrissen ausgegeben werden sollte.

Ich habe folgendes Problem und ich komme einfach nicht dahinter, worin dessen Ursache liegt:

Ich lese unterschiedliche Textdateien ein, deren Namen der anzuzeigenden Seite (Variable $seite) entspricht, und verarbeite anschließend den Inhalt:

<?php
if ($seite != "media" && $seite != "community")
{
   require ("dateiEinlesen.php");

   $inhalt = dateiEinlesen ($seite);

   // [1]

   if ($inhalt != -1) // Datei wurde gefunden
   {
      // [2]

      if ($inhalt != 0) // Datei ist nicht leer
      {
         $datensaetze = explode ("[ENDE]", $inhalt);
         $anzahlEintraege = count ($datensaetze);
      }
      else
         echo 'Datei "' . $seite . '.txt" hat keinen Inhalt<br>';
   }
   else
      echo 'Datei "' . $seite . '.txt" nicht gefunden<br>';
}

switch ($seite)
{
   case "home":
   {
      if ($anzahlEintraege > 1) // count nach explode bei leerem String ergibt 1
      {

      }

      break;
   }

   case "band":
   {
      // [3]

      if ($anzahlEintraege > 1)
      {

      }

      break;
   }

   ...weitere "case"-Fälle...
}
?>

Die Datei "dateiEinlesen.php" beinhaltet lediglich folgende Funktion, die sich um das Einlesen der jeweiligen Textdatei kümmert und -1 zurückliefert, wenn die Datei nicht gefunden wurde, 0 bei leerem Inhalt bzw. den eingelesenen Inhalt:

<?php
function dateiEinlesen ($datei)
{
   clearstatcache();

   $datei = "txt/" . $datei . ".txt";

   if (!!@filemtime ($datei))
   {
      if (0 != filesize ($datei))
         return file_get_contents ($datei);
      else
         return 0;
   }
   else
      return -1;
}
?>

Das Einlesen und Verarbeiten der Textdatei "home.txt" klappt einwandfrei, auch kann ich den unbearbeiteten Inhalt der Textdatei "band.txt" bei den Marken [1], [2] und seltsamerweise sogar bei [3] ausgeben lassen, aber es greift, warum auch immer, der "else"-Zweig der Abfrage nach Marke [2] und demnach wirft er mir bei der Abfrage nach Marke [3] einen Fehler.

Die Textdateien liegen alle im selben Verzeichnis, auch die Benennungen stimmen.

Wo liegt der Fehler?

Vielen lieben Dank für eure Hilfe und Gruß Enrico

akzeptierte Antworten

  1. Liebe Mitdenker, liebe Wissende, liebe Neugierige,

    Ich habe folgendes Problem und ich komme einfach nicht dahinter, worin dessen Ursache liegt:

    Ich lese unterschiedliche Textdateien ein, deren Namen der anzuzeigenden Seite (Variable $seite) entspricht, und verarbeite anschließend den Inhalt:

    <?php
    if ($seite != "media" && $seite != "community")
    {
       require ("dateiEinlesen.php");
    
       $inhalt = dateiEinlesen ($seite);
    
       // [1]
    
       if ($inhalt != -1) // Datei wurde gefunden
       {
          // [2]
    
          if ($inhalt != 0) // Datei ist nicht leer
          {
             $datensaetze = explode ("[ENDE]", $inhalt);
             $anzahlEintraege = count ($datensaetze);
          }
          else
             echo 'Datei "' . $seite . '.txt" hat keinen Inhalt<br>';
       }
       else
          echo 'Datei "' . $seite . '.txt" nicht gefunden<br>';
    }
    
    switch ($seite)
    {
       case "home":
       {
          if ($anzahlEintraege > 1) // count nach explode bei leerem String ergibt 1
          {
    
          }
    
          break;
       }
    
       case "band":
       {
          // [3]
    
          if ($anzahlEintraege > 1)
          {
    
          }
    
          break;
       }
    
       ...weitere "case"-Fälle...
    }
    ?>
    

    Die Datei "dateiEinlesen.php" beinhaltet lediglich folgende Funktion, die sich um das Einlesen der jeweiligen Textdatei kümmert und -1 zurückliefert, wenn die Datei nicht gefunden wurde, 0 bei leerem Inhalt bzw. den eingelesenen Inhalt:

    <?php
    function dateiEinlesen ($datei)
    {
       clearstatcache();
    
       $datei = "txt/" . $datei . ".txt";
    
       if (!!@filemtime ($datei))
       {
          if (0 != filesize ($datei))
             return file_get_contents ($datei);
          else
             return 0;
       }
       else
          return -1;
    }
    ?>
    

    Das Einlesen und Verarbeiten der Textdatei "home.txt" klappt einwandfrei, auch kann ich den unbearbeiteten Inhalt der Textdatei "band.txt" bei den Marken [1], [2] und seltsamerweise sogar bei [3] ausgeben lassen, aber es greift, warum auch immer, der "else"-Zweig der Abfrage nach Marke [2] und demnach wirft er mir bei der Abfrage nach Marke [3] einen Fehler.

    und wie lautet dieser Fehler?

    was bedeutet "!!@filetime"?

    Die Textdateien liegen alle im selben Verzeichnis, auch die Benennungen stimmen.

    Wo liegt der Fehler?

    Spirituelle Grüße
    Euer Robert
    robert.r@online.de

    --
    Möge der wahre Forumsgeist ewig leben!
    1. Hi,

      <?php
      function dateiEinlesen ($datei)
      {
         clearstatcache();
      
         $datei = "txt/" . $datei . ".txt";
      
         if (!!@filemtime ($datei))
      

      was bedeutet "!!@filemtime"?

      filemtime liefert die Zeit der Veränderung der Datei (oder false bei Fehler).
      @ unterdrückt die Fehlermeldungsausgabe.
      ! negiert den booleschen Wert, !! negiert also doppelt (ist ein kleiner Trick, um auf jeden Fall einen booleschen Wert zu haben).

      cu,
      Andreas a/k/a MudGuard

      1. Hallo und Guten Morgen,

        jetzt wolle ich meiner Verwunderung Ausdruck verleihen, dass DU dem Robert diese Frage beantwortest. Die ging doch wohl aus bestimmtem Grunde an Enrico ;-P.

        Nu habe ich einen falschen Knopf gedrückt.

        Grüße TS

        1. Liebe Mitdenker, liebe Wissende, liebe Neugierige,

          jetzt wolle ich meiner Verwunderung Ausdruck verleihen, dass DU dem Robert diese Frage beantwortest. Die ging doch wohl aus bestimmtem Grunde an Enrico

          das stimmt zwar, aber lass ihn doch ruhig. Der Enrico kommt doch sowieso nicht wieder...

          Spirituelle Grüße
          Euer Robert
          robert.r@online.de

          --
          Möge der wahre Forumsgeist ewig leben!
  2. Tach!

    erstmal Entschuldigung für den Fall, dass mein Beitrag, wie es die Vorschau befürchten lässt, trotz Angabe von "~~~" zerrissen ausgegeben werden sollte.

    Leerzeilen vor und nach Blöcken sind der Trick. Lösch sie nicht, dann wird alles gut.

    dedlfix.

  3. Tach!

    Ich habe folgendes Problem und ich komme einfach nicht dahinter, worin dessen Ursache liegt:

    Was hast du bisher getan, um dem Fehler auf die Spur zu kommen? Wenn Kontrollausgaben mit var_dump() nicht dazugehört haben, dann hol das mal nach. Lass dir ausgeben, was deine verwendeten Funktionen und deine verwendeten Ausdrücke liefern. Und auch das Unterdrücken von Fehlern (@) ist beim Fehlersuchen nicht von Vorteil.

    dedlfix.

  4. Lieber Enrico,

    der Wert in der Variablen $seite ist wohl einem URL-Parameter entnommen, oder?

    $inhalt = dateiEinlesen ($seite);

    Man sieht es hier noch nicht, aber spätestens in der Funktion selbst sollte das eventuelle Sicherheitsproblem klar werden:

    <?php
    function dateiEinlesen ($datei)
    {
       clearstatcache();
    
       $datei = "txt/" . $datei . ".txt";
    
       if (!!@filemtime ($datei))
       {
          if (0 != filesize ($datei))
             return file_get_contents ($datei);
          else
             return 0;
       }
       else
          return -1;
    }
    ?>
    

    Du prüfst lediglich, ob die Datei eine Dateigröße ungleich null Bytes hat, nicht aber, ob die Datei auch wirklich in Deinen Verzeichnissen, bzw. in den beabsichtigten Verzeichnissen liegt. Du stellst lediglich ein "txt/" vor den Pfad und eine Dateiendung ".txt" hinten an.

    Genügt das wirklich, um unberlaubte Dateipfade zu vermeiden? Möchtest Du zur Sicherheit nicht lieber alle Slashes aus dem Wert der Variablen $seite vorher entfernen?

    $inhalt = dateiEinlesen ( str_replace ('/', '', $seite) );
    

    Desweiteren gefällt mir die Prüfung auf Vorhandensein der Textdatei so noch nicht. Warum nutzt Du nicht die Funktion is_file() anstelle von filemtime? Das mag jetzt eher ein akademisches Problem sein, aber der Code liest sich später tatsächlich einfacher, wenn man anhand des Funktionsnamens schon sieht, was diese Prüfung hier soll.

    Liebe Grüße,

    Felix Riesterer.

    1. Tach!

      Du prüfst lediglich, ob die Datei eine Dateigröße ungleich null Bytes hat, nicht aber, ob die Datei auch wirklich in Deinen Verzeichnissen, bzw. in den beabsichtigten Verzeichnissen liegt. Du stellst lediglich ein "txt/" vor den Pfad und eine Dateiendung ".txt" hinten an.

      Genügt das wirklich, um unberlaubte Dateipfade zu vermeiden? Möchtest Du zur Sicherheit nicht lieber alle Slashes aus dem Wert der Variablen $seite vorher entfernen?

      realpath() ist mein Favorit in solchen Fällen. Und dann den Anfang des Ergebnisses mit dem absoluten Pfad zum Verzeichnis der Dateien vergleichen.

      Desweiteren gefällt mir die Prüfung auf Vorhandensein der Textdatei so noch nicht. Warum nutzt Du nicht die Funktion is_file() anstelle von filemtime? Das mag jetzt eher ein akademisches Problem sein, aber der Code liest sich später tatsächlich einfacher, wenn man anhand des Funktionsnamens schon sieht, was diese Prüfung hier soll.

      realpath() testet auch schon auf Vorhandensein. Abgesehen davon und dem Fehler im Code ist die ganze Vorgehensweise sehr umständlich. file_get_contents() und die Sache ist in einem Einzeiler gegessen. Ob die Datei nicht vorhanden ist, sieht man am zurückgegebenen false (mit typsicherem Vergleich testen!). Ob die Dateigröße 0 oder in ihr was drin ist, sieht man ebenfalls am Ergebnis von file_get_contents() - entweder ein Leerstring oder ein gefüllter.

      dedlfix.

      1. Hallo dedlfix,

        file_get_contents() und die Sache ist in einem Einzeiler gegessen

        Stimmt :-)

        Warum es vorher nicht ging muss ich mir in einer ruhigen Minute noch einmal Schritt für Schritt durchschauen.

        Danke an alle für eure weiterführenden Tipps.

        Gruß Enrico

  5. <?php
    function dateiEinlesen ($datei)
    {
    

    if (!!@filemtime ($datei)) { if (0 != filesize ($datei)) return file_get_contents ($datei); else return 0; } else return -1; } ?>

    Wenn du unterschiedliche Datentypen als Rückgabewert nutzt, dann solltest du bei PHP tunlichst den Typvergleich nutzen, !== und ===.

    Genau genommen sollte man bei PHP niemals die einfachen Vergleiche != und == nutzen. Diese Ausgeburt der Hölle haut einem ohne Sinn und Verstand die Werte um die Ohren, bis man nicht mehr weiss, wo rinks und lechts ist, siehe http://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/#operators

    $inhalt = dateiEinlesen ($seite);

    // [1]

    if ($inhalt != -1) // Datei wurde gefunden if ($inhalt != 0) // Datei ist nicht leer

    Prüfe doch mal, ob, diese Vergleiche mit den drei bis fünftrillionen Möglichkeiten (deine Rückgabewerte 0, -1 sowie Text) das ergeben, was du erwartest.

    $a = ($inhalt != -1); var_dump($a); $a = ($inhalt != 0); var_dump($a);

    1. Nachtrag:

         if (!!@filemtime($datei))
         {
            if (0 != filesize($datei))
               return file_get_contents($datei);
            else
               return 0;
         }
         else
            return -1;
      }
      

      Es ist unnötig, den Fall "Leere Datei" separat zu behandeln. Ist die Datei leer, kann die Funktion einen leeren Text zurückgeben. Es entfällt die Prüfung der Dateigröße und der etwas unlogische Rückgabewert -1 für den Dateifehler (-1 entspricht true, ein Fehler sollte eher false, 0, null sein).

      Die Prüfung im Hauptteil ändert sich wie folgt:

         $inhalt = dateiEinlesen($seite);
      
         if ($inhalt !== false) // Datei wurde gefunden
         {
            if ($inhalt !== "") // Datei ist nicht leer
            {
               $datensaetze = explode("[ENDE]", $inhalt);
      
      1. Hallo und guten Tag,

        [...] ein Fehler sollte eher false, 0, null sein).

        Das kommt doch ganz auf die Definition an.

        Eine Funktion kann auch dedizierte Fehlernummern erzeugen, wie es vernünftige Konzepte meistens vorsehen. Da ist dann 0 der Wert für "kein Fehler". Allerdings gibt es dann auch einen separaten Fehlerkanal. Und der Rückgabewert der Funktion ist der Nutzwert, der Gültigkeit hat, wenn der Fehlerkanal 0 liefert. Und wenn die Funktion als Rückgabewert im Fehlerfall dann auch noch das "neutrale Element" liefert, dann ist es noch besser. Ob es eines gibt, hängt aber vom Datentyp und vom Anwendungsfall ab.

        Da PHP aber vermeintlich typfrei arbeitet, bzw. einen das durch die automatische Typumwandlung glauben machen kann, entstehen eben ungeheuer viele Entscheidungs-, Ausführungs- und Sicherheitsfehler.

        Grüße
        TS

        1. Lieber TS,

          [...] ein Fehler sollte eher false, 0, null sein).

          Das kommt doch ganz auf die Definition an.

          na, dann eben so:

          • <string> = Datei vorhanden und nicht leer
          • <false> = Datei nicht vorhanden
          • <true> = Datei zwar vorhanden, aber leer

          Ich persönlich fände eine solche Definition grauenvoll. Lieber wäre mir das hier

          • <string> = Dateiinhalt
          • <false> = Dateifehler

          Da PHP aber vermeintlich typfrei arbeitet, bzw. einen das durch die automatische Typumwandlung glauben machen kann, entstehen eben ungeheuer viele Entscheidungs-, Ausführungs- und Sicherheitsfehler.

          Wohl wahr. Aber damit kann man ziemlich gut umgehen, wenn die Möglichkeiten stark reduziert werden, sodass einem die Typumwandlung sogar Arbeit abnimmt. Im Obigen Beispiel wäre das so:

          if ($inhalt = dateiEinlesen ($datei)) {
              // zeige Seite
          } else {
              // zeige 404 Fehlerseite
          }
          

          Liebe Grüße,

          Felix Riesterer.

          1. Hallo und guten Morgen,

            Lieber TS,

            [...] ein Fehler sollte eher false, 0, null sein).

            Das kommt doch ganz auf die Definition an.

            Aber bitte nicht meine eigentliche Aussage klittern!
            Eine vernünftige Fehlermeldung liefert eindeutige Fehlernummern und nicht irgendein Gemisch aus Typen, false, null, Werten und Müll.

            na, dann eben so:

            • <string> = Datei vorhanden und nicht leer
            • <false> = Datei nicht vorhanden
            • <true> = Datei zwar vorhanden, aber leer

            Das ist doch noch schlimmer!

            Dann hast Du beim Unbedarften Uder nachher immer eine 1 auf dem Bildschirm stehen, weil der dachte, das sei der Dateiinhalt. Dabei ist es die bei PHP für TRUE übliche Repräsentanz.

            Grüße
            TS