dey: Counter terminiert regelmässig

Hallo,

wir beutzen eine auf PHP-basis der aus 2 Dateien besteht:
1. Zähler hoch setzen

  
<?php  
$data = file("count.dat");  
$text = $data[0];  
  
$text = $text + 1;  
$openfile = fopen("count.dat", 'w+');  
  flock($openfile, 2);  
  fwrite($openfile, $text);  
  fclose($openfile);  
?>  

2. Weiterleitung auf Ausgabe

  
<script type="text/javascript">  
<!--  
  location.replace('counter.php')  
//-->  
</script>  

3. Ausgabe

  
$data = file("count.dat");  
    $text = $data[0];  
    $lentext = strlen($text);  
    for ($i = 1; $i <= $lentext; $i++)  
    {  
    $num = substr($text, $i-1, 1);  
    echo "<img src='../img/" . $num . ".gif' border='0' alt='no'>";  
    }  

(Un-)regelmässig setzt sich der counter auf Null zurück. Vielleicht Count.dat gelöscht.

1. Wie kann ich den Grund dafür rausfinden?
2. Ist irgendwas in meinem Code dafür prädestiniert?

bydey

--
-- noch immer ein erfolgloser <DIV> Jünger --
  1. echo $begrüßung;

    (Un-)regelmässig setzt sich der counter auf Null zurück. Vielleicht Count.dat gelöscht.

    1. Wie kann ich den Grund dafür rausfinden?

    Du könntest ein Error-Logfile führen. Teste vor Zugriff auf die Datei, ob sie vorhanden ist und schreibe eine entsprechende Meldung in eine Datei.

    1. Ist irgendwas in meinem Code dafür prädestiniert?

    Ja, es gibt mindestens eine Schwachstelle

    $data = file("count.dat");

    Wenn bei file() ein Fehler auftritt, wird kein Array sondern false zurückgeliefert. Diesen Sonderfall berücksichtigst du nicht. Vermutlich tritt dieser Fall auf, wenn die Datei gerade mit flock() gesperrt ist.

    $text = $data[0];

    false hat kein Element 0. Die entsprechende Notice wirst du sicherlich ignorieren, weil das die Default-Einstellung für das error_reporting ist. Hier hilft nur, Fehler, die im unbeaufsichtigtem Betrieb auftreten zu loggen. Dazu könnte man error_reporting auf E_ALL, display_errors auf 0, log_errors auf 1 und error_log auf einen Dateinamen setzen. Diese Datei sollte man dann regelmäßig kontrollieren.

    $text = $text + 1;

    Hier schlägt dann die automatische Typumwandlung von PHP zu. $data[0] hat NULL ergeben, das wird, weil damit gerechnet werden soll, in 0 umgewandelt und dann dein 1 addiert. ($x = $x + 1; kann man übrigens auch als $x++; schreiben.)

    echo "$verabschiedung $name";

    1. Hallo,

      Wenn bei file() ein Fehler auftritt, wird kein Array sondern false zurückgeliefert. Diesen Sonderfall berücksichtigst du nicht. Vermutlich tritt dieser Fall auf, wenn die Datei gerade mit flock() gesperrt ist.

      Nein!
      Die flock()-Funktion ist nur advisory und muss daher in der Applikation beachtet werden. File() tut dies aber nicht.
      Das bedeutet also, dass File() immer das ausliest, was gerade
      noch vorhanden und lesbar ist.

      Anders wäre das mit den dio_-Funktionen
      http://de.php.net/manual/de/ref.dio.php
      Die sperren die Datei in der Betriebssystemschicht, also mandatory.
      Die Applikationen kommen also an dieser Sperre nicht vorbei.
      Das würde dann auch für file() zutreffen.

      Da File() aber eine namensbasierte Funktion ist und keine handlebasierte, eignet es sich nicht für Multiuser-Aufgaben, bzw. für
      Datenmanipulation. File() sollte man ausschließlich für reine Datenanzeigen verwenden, bei denen die Konsistenz nicht wesentlich ist.

      LG
      Chris

      1. Hallo,

        Anders wäre das mit den dio_-Funktionen
        http://de.php.net/manual/de/ref.dio.php
        Die sperren die Datei in der Betriebssystemschicht, also mandatory.
        Die Applikationen kommen also an dieser Sperre nicht vorbei.
        Das würde dann auch für file() zutreffen.

        leider wurden die DIO-Funktionen mit der Version 5.x aus den Standardmodulen herausgenommen und sind als PECL-Erweiterung erhältlich.
        Weiß jemand, was sich die PHP-Entwickler dabei gedacht haben?

        Gruß aus Berlin!
        eddi

      2. echo $begrüßung;

        Die flock()-Funktion ist nur advisory

        Jetzt, wo du es sagst, fällt es mir wieder ein, dass da ja sowas war. Allerdings fällt mir auch ein, dass es Unterschiede zwischen Windows und Unix gibt. Unter Windows ist flock mandatory. Dies weiß auch die Handbuchseite zu berichten:

        flock -- Portable advisory file locking
          ...
          Note:  flock() is mandatory under Windows.

        Meine Vermutung äußerte ich auch aufgrund eines Userkommentars (John 21-Jul-2003 01:32) auf der Handbuchseite zu file(). Leider schrieb er nicht, ob er Windows oder Unix verwendet hat.

        Die flock()-Vermutung ziehe ich hiermit zurück. Den Rest lasse ich aber so stehen.

        echo "$verabschiedung $name";

        1. Hallo Dedlfix,

          Die flock()-Funktion ist nur advisory

          Jetzt, wo du es sagst, fällt es mir wieder ein, dass da ja sowas war. Allerdings fällt mir auch ein, dass es Unterschiede zwischen Windows und Unix gibt. Unter Windows ist flock mandatory. Dies weiß auch die Handbuchseite zu berichten:

          flock -- Portable advisory file locking
            ...
            Note:  flock() is mandatory under Windows.

          Meine Vermutung äußerte ich auch aufgrund eines Userkommentars (John 21-Jul-2003 01:32) auf der Handbuchseite zu file(). Leider schrieb er nicht, ob er Windows oder Unix verwendet hat.

          Die flock()-Vermutung ziehe ich hiermit zurück. Den Rest lasse ich aber so stehen.

          Guter Hinweis. Den muss ich gleich weiterreichen.

          Wird meinen Partner allerdings überhaupt nicht freuen. Der hat gerade vorgestern den Feature-Artikel übers Locking wieder aufgegriffen.

          Die PHPler scheinen hier eine Menge Konfusion eingebaut zu haben. Wird ma wohl nicht drum herum kommen, in den Quellcode zu schauen, wenn alles stimmen soll später.

          LG
          Chris

  2. Hallo,

    wir beutzen eine auf PHP-basis der aus 2 Dateien besteht:

    Könntest Du den Satz bitte mal ins Deutsche übersetzen? :-)

    $openfile = fopen("count.dat", 'w+');
      flock($openfile, 2);
      fwrite($openfile, $text);
      fclose($openfile);

    Was soll das Filelocking hier noch bewirken?
    Es ist bei PHP per default "advisory", also nur beratend.
    Es hindert also nicht die funktion file(), das hier gerade
    neu angelegte und noch leere File auszulesen.

    File() ist ohnehin ungeeignet, um in einer Multiuser-Umgebung benutzt zu werden, wenn die ausgelesenen Daten konsistent bleiben müssen. Alle Krücken, die man zum "Sperren" um File() herumbauen könnte, helfen auch nicht wirklich weiter.

    Nutze also die normalen fopen(), fread() und fwrite()

    Um die Datei anzulegen, musst Du das entweder generell bei der Installation des Zählers tun und Dich nachher darauf verlassen können, dass es auch geklappt hat, oder aber Du musst es zerstörungsfrei machen.

    Dazu benutzt man entweder
      $fh = @fopen($dateiname,'+xb');
      if ($fh !== false)
      {
        # hat geklappt, weitermachen
      }
      else
      {
        # Auswerten von $phperrmsg
        # dazu muss track_errors = on sein!
      }

    Mit diesem Handle kann dann sofort weitergearbeitet werden

    oder aber einfach

    $fh = fopen($dateiname,'ab');
      fclose($fh);

    $fh = fopen($dateiname,'rb+');

    und nun _erst_ locken,
    dann auslesen
    dann erhöhen
    dann zurückspulen
    dann schreiben
    dann Dateilänge absitmmen (ftruncate())
    dann Datei schließen

    Durch das Schließen wird sie automatisch entsperrt.

    Ich hoffe, dass Du alles verstanden hast.

    LG
    Chris

    1. Hallo,

      Ich hoffe, dass Du alles verstanden hast.

      Im grossen und ganzen ja. Altes script - irgendwann mal kopiert als ich von php noch keine Ahnung hatte und nie wieder darüber nachgedacht.

      bydey

      --
      -- noch immer ein erfolgloser <DIV> Jünger --
      1. Hallo,

        Ich hoffe, dass Du alles verstanden hast.

        Im grossen und ganzen ja. Altes script - irgendwann mal kopiert als ich von php noch keine Ahnung hatte und nie wieder darüber nachgedacht.

        Viel spannender ist Deine noch fehlende Aussage, ob nun alles funktioniert...

        LG
        Chris

        1. Hallo,

          Viel spannender ist Deine noch fehlende Aussage, ob nun alles funktioniert...

          Wäre schwierig festzustellen, da
          a) der Fehler zufällig alle paar Monate auftritt und
          b) es ja nicht sicher ist, dass wir auf der richtigen Fährte sind
          und somit der Fehler nicht reproduzierbar ist.
          Was bisher geschrieben wurde macht Sinn und ich werde es umsetzten.

          bydey

          --
          -- noch immer ein erfolgloser <DIV> Jünger --
          1. Hallo,

            Viel spannender ist Deine noch fehlende Aussage, ob nun alles funktioniert...

            Wäre schwierig festzustellen, da
            a) der Fehler zufällig alle paar Monate auftritt und

            Das kann man mittels Multirequest mit dem Programm 'ab' eines Apache-Webservers sehr schnell herausfinden. meistens reichen schon wenige parallere Requests, um solche Fehler zu provozieren.

            b) es ja nicht sicher ist, dass wir auf der richtigen Fährte sind
            und somit der Fehler nicht reproduzierbar ist.

            Auf der richtigen Fährte bist Du mit den Hinweisen auf jeden Fall.
            Das wurde hier schließlich auch schon oft genug beschrieben!

            Was bisher geschrieben wurde macht Sinn und ich werde es umsetzten.

            Gut so. Und hinterher bitte Erfolgsbericht :-)

            LG
            Chris