Hank: php-Script alle 5 Minuten

Hallo,

wenn ich ein Script alle 5 Minuten ausführen will, kann ich das so machen?

if($date->format('i') %5 == 0) {
    //ausführen
}

Hank

  1. Deine Idee, das im PHP-Skript selbst zu regeln, ist ein Irrweg. Selbst wenn man Dich auf sleep() hinweist.

    Linux? Ein

    man 5 crontab 
    

    und

    crontab -e
    

    hilft Dir weiter.

    Windows? Zu Linux wechseln oder jemand anders fragen.

    Oder, besser, beschreib erst mal, was Du EIGENTLICH machen willst.

  2. Hello Hank,

    Antwort von Jörg zum OS hast Du ja schon.

    Cronjob wäre die schmerzloseste Lösung. Der funktioniert dann (voraussichtlich) auch nach einem Reboot des Linux-Hosts wieder.

    Alternativ könntest Du tatsächlich (auf einem Linux-Host) den PHP-Prozess auch in den Hintergrund stellen.

    Suche dazu einfach mal im Archiv nach "Dauerlauf".
    dauerlauf php

    Diese Variante würde aber die ständige Überwachung durch eine Frontend-App erfordern. Du müsstest also öfter schauen, ob der Auftrag noch aktiv ist.

    Glück Auf
    Tom vom Berg

    --
    Es gibt soviel Sonne, nutzen wir sie.
    www.Solar-Harz.de
    S☼nnige Grüße aus dem Oberharz
  3. Hallo Hank,

    das Folgende macht man dann und nur dann, wenn man keinen Dienst hat, der zeitgesteuert ein PHP Script aufrufen kann. Auf einem Linux-Server wäre das ein cron Job, auf einem Windows-Gerät kann man dafür den Aufgabenplaner bemühen. Bei reinen Web-Hosting Paketen ist das nicht verfügbar, man braucht schon einen Server mit Root-Zugang oder ein Hosting-Paket, wo ein Timer-Dienst angeboten wird (keine Ahnung ob es die gibt).

    Ich hab vor ewigen Jahren mal ein MMORPG gespielt, wo man Völker regieren durfte und jede Spielfigur vom Server separat verwaltet wurde. Jede halbe Stunde wurde geprüft, ob Spielfiguren zu alt wurden, ob neue geboren wurden, und einiges mehr. Dieser Updatezyklus fand in einem normalen Webrequest desjenigen Spielers statt, der das Pech hatte, genau im passenden Moment hereinzukommen. Man tat gut daran, um xx:00 oder xx:30 Uhr keinen Angriff zu starten, wenn man nämlich erstmal den Update-Zyklus reingedrückt bekam, war man mitten im Angriff etliche Sekunden gelähmt.

    Dieses Spiel lief auf einem Billigserver ohne cron-Jobs und hat den Ansatz verwendet, den Du Dir überlegst. Da reicht aber keine Prüfung, ob die Minuten durch 5 teilbar sind. Denn

    • Wenn in dieser Minute mehr als ein Webrequest hereinkommt, soll die Spezialaktion trotzdem nur einmal ablaufen
    • Wenn in einer durch 5 teilbaren Minute gar kein Request hereinkommt, muss die Spezialaktion trotzdem irgendwann ablaufen.

    Wenn Du diese Fünfminutenaktion also als Teil der normalen Webrequests einbauen willst, brauchst also eine Steuerung, die dafür sorgt, dass sie immer nur einmal läuft. Wie diese Steuerung aussieht, hängt auch davon ab, wie genau Du an der durch 5 teilbaren Minute liegen musst. Bei dem MMORPG, von dem ich sprach, war es wichtig, dass sie jeweils um xx:00 und xx:30 Uhr lief, so dass es insgesamt 48 Updatezyklen pro Tag gab.

    Du könntest z.B. in einer DB-Tabelle oder eine Datei speichern, wann der nächste Fälligkeitszeitpunkt ist - sagen wir: 13:20 Uhr. Wenn dann um 13:22 ein Webrequest kommt, ist der Fälligkeitszeitpunkt erreicht und die Spezialaktion kann laufen. In der Steuertabelle addierst Du dann einfach 5 Minuten auf den Fälligkeitszeitpunkt auf - 13:25. Damit führst Du die Spezialaktion zwar nicht mit 5 Minuten Abstand durch, aber stellst sicher, dass es 12 pro Stunde werden.

    Es kann auch sein, dass 13:20 Uhr die Fälligkeit ist, der nächste Request aber erst um 13:33 kommt. Dann hast Du 2 Fälligkeiten verpasst - und nun hast Du je nach fachlichem Hintergrund die Entscheidungsfreiheit, die beiden verpassen Fälligkeiten nachzuberechnen, oder sie wegzulassen.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf,

      Da reicht aber keine Prüfung, ob die Minuten durch 5 teilbar sind. Denn

      • Wenn in dieser Minute mehr als ein Webrequest hereinkommt, soll die Spezialaktion trotzdem nur einmal ablaufen
      • Wenn in einer durch 5 teilbaren Minute gar kein Request hereinkommt, muss die Spezialaktion trotzdem irgendwann ablaufen.

      Das ist schon berücksichtigt.
      Es geht wirklich nur um die Bedingung, die ich formuliert habe.

      Hank

      1. Es geht wirklich nur um die Bedingung, die ich formuliert habe.

        Das scheint Dir nur so.

        Tatsächlich ist die Wahl der Lösung davon abhängig, was Du WIRKLICH (letztendlich) erreichen willst und natürlich davon, welche Lösungsmöglichkeit Dir zur Verfügung steht.

        Also: Was willst Du (warum alle 5 Minuten?) und was hast Du (z.B. welches Hostingpaket bei welchem Anbieter?)

        1. Also: Was willst Du (warum alle 5 Minuten?) und was hast Du (z.B. welches Hostingpaket bei welchem Anbieter?)

          Habe einen Rootserver inkl. Crons, aber die sind hier wirklich nicht nötig.

          Hank

  4. Sorry.
    Ich habe die Frage falsch formuliert, eigentlich sollte sie heißen

    Wenn ich eine Funktion nur ausführen will, wenn das php-Script selber zur vollen 5. Minute einer Stunde läuft.

    Deshalb frage ich nochmal, ob meine Bedingung das ausdrückt.

    Hank

    1. Sorry.
      Ich habe die Frage falsch formuliert, eigentlich sollte sie heißen

      Wenn ich eine Funktion nur ausführen will, wenn das php-Script selber zur vollen 5. Minute einer Stunde läuft.

      Deshalb frage ich nochmal, ob meine Bedingung das ausdrückt.

      Im Kern schon. Aber der bedingte Teil Deines Skriptes wird halt auch ausgeführt wenn das Skript um 11:55:01.000 und um 11:55:01.003, ..., 11:55:59.9999997 auf das

      if($date->format('i') %5 == 0) {
      

      stößt. Das heisst, das Zeug wird womöglich hunderte Mal ausgeführt, statt nur einmal. Kommt im letzteren Falle noch ein

      if($date->format('i') %5 == 0) {
          ...
          if($date->format('i') %10 == 0) {
      

      hinzu, dann kann es passieren, dass um 11:50:59.9999997 auf volle 5 Minuten erkannt wird, die zweite Abfrage dann - wegen des Zeitablaufes - aber um 11:51:00.000001 negativ beantwortet wird.

      Und es ist, neben den obigen und regelmäßig unerwünschten Unwägbarkeiten, gerade Deine Frage, die vermuten lässt, dass Du ein insgesamt ungeeignetes Vorgehen gewählt hast.

      Die Frage beleibt stehen: Was willst du also erreichen, was kann Dein System? Oder ist das geheim? (Ich frage das jetzt zum dritten Male...)

      1. Im Kern schon. Aber der bedingte Teil Deines Skriptes wird halt auch ausgeführt wenn das Skript um 11:55:01.000 und um 11:55:01.003, ..., 11:55:59.9999997 auf

        Ja, genau das will ich.

        stößt. Das heisst, das Zeug wird womöglich hunderte Mal ausgeführt, statt nur einmal. Kommt im letzteren Falle noch ein

        Nein, da ich das Script im Fall vom Eintreten der Bedingung abbrechen will.

        Die Frage beleibt stehen: Was willst du also erreichen, was kann Dein System? Oder ist das geheim? (Ich frage das jetzt zum dritten Male...)

        Das Script soll unter einer bestimmten Bedingung einfach nicht durchlaufen. Ich will die Bedingung aber nicht jedes mal prüfen, weil deren Vorkommen sehr sehr unwahrscheinlich ist, die Prüfung mir dafür zuviel Serverlast erzeugt. Deshalb will ich alle 5 Minuten prüfen.
        Zusätzlich sei erwähnt, dass nichts Dramatisches passiert, wenn das Script durchläuft, aber ich möchte es stoppen dann lieber stoppen.

        Hank

        1. Das Script soll unter einer bestimmten Bedingung einfach nicht durchlaufen. Ich will die Bedingung aber nicht jedes mal prüfen, weil deren Vorkommen sehr sehr unwahrscheinlich ist, die Prüfung mir dafür zuviel Serverlast erzeugt. Deshalb will ich alle 5 Minuten prüfen.

          Klingt so als wölltest Du viel mehr über touch() und Flagfiles nachlesen und die Aktion anhand der Existenz bzw. der Last-Modified-Eigenschaft der Datei (des Flagfiles) steuern.

    2. @@Hank

      Wenn ich eine Funktion nur ausführen will, wenn das php-Script selber zur vollen 5. Minute einer Stunde läuft.

      Deshalb frage ich nochmal, ob meine Bedingung das ausdrückt.

      Musste ich auch erstmal in die Doku schauen: $date->format('i') gibt einen String zurück. Darf man Strings mit % verknüpfen? Arithmetische Operatoren sagt ja; Strings werden automatisch in Zahlen umgewandelt.

      Wenn auch nicht schön, es sollte passen.

      🖖 Живіть довго і процвітайте

      --
      „Im Vergleich mit Elon Musk bei Twitter ist ein Elefant im Porzellanladen eine Ballerina.“
      — @Grantscheam auf Twitter
      1. Musste ich auch erstmal in die Doku schauen: $date->format('i') gibt einen String zurück. Darf man Strings mit % verknüpfen? Arithmetische Operatoren sagt ja; Strings werden automatisch in Zahlen umgewandelt.

        Wenn auch nicht schön, es sollte passen.

        Danke fürs nachgucken.

        Hank

        1. Was hast Du eigentlich gegen time()?

          Das Du die Unix-Zeit (aktuell: 1677937915) dann mit

          $t = time()
          if ( 0 === $t % 300 ) 
          

          date->format() ist „teuer“, liefert, wie schon gezeigt einen String, der dann wieder „teuer“ als Zahl interpretiert wird...

          1. $t = time()
            if ( 0 === $t % 300 ) 
            

            Nein, das reicht nicht, weil ich nicht garantieren kann, einen Treffer zu erzeugen. Es ist schon schön, dass meine Lösung einen Zeitraum von 1 Minute umfasst.

            1. $t = time()
              if ( 0 === $t % 300 ) 
              

              Nein, das reicht nicht, weil ich nicht garantieren kann, einen Treffer zu erzeugen. Es ist schon schön, dass meine Lösung einen Zeitraum von 1 Minute umfasst.

              Kannst Du eh nicht. Das kann nur der Trick mit dem Flagfile. Wenn das älter als 5 Minuten ist setzt Du SOFORT die Zeit neu und machst danach den anderen Krempel.

              (Ansonsten:)

              $m = floor( time()/60 );
              if ( 0 === $m % 5 ) 
              
              1. @@Raketenwilli

                $m = floor( time()/60 );
                if ( 0 === $m % 5 ) 
                

                Da sollte zwar dasselbe rauskommen wie bei mir; aber warum mit Floats rechnen, wenn es Integers auch tun?

                🖖 Живіть довго і процвітайте

                --
                „Im Vergleich mit Elon Musk bei Twitter ist ein Elefant im Porzellanladen eine Ballerina.“
                — @Grantscheam auf Twitter
          2. @@Raketenwilli

            Was hast Du eigentlich gegen time()?

            Das Du die Unix-Zeit (aktuell: 1677937915) dann mit

            $t = time()
            if ( 0 === $t % 300 ) 
            

            Geht der Satz noch weiter?

            Ich vollende ihn mal: … verknüpfen kannst, ist schön und gut, liefert aber nicht das gewünschte Ergebnis.

            Du musst die Sekunden erstmal wegschmeißen: intdiv($t, 60) und das dann auf Teilbarkeit durch 5 prüfen:

            $t = time();
            if (intdiv($t, 60) % 5 === 0) {}
            

            🖖 Живіть довго і процвітайте

            --
            „Im Vergleich mit Elon Musk bei Twitter ist ein Elefant im Porzellanladen eine Ballerina.“
            — @Grantscheam auf Twitter
            1. Geht der Satz noch weiter?

              Ja. Aber mit der Frage hast Du das Absenden des Edits unmöglich gemacht. Und jetzt hab ich keinen Bock mehr.

              1. @@Raketenwilli

                Aber mit der Frage hast Du das Absenden des Edits unmöglich gemacht.

                Als registrierter Nutzer wär dir das wohl nicht passiert. 😉

                🖖 Живіть довго і процвітайте

                --
                „Im Vergleich mit Elon Musk bei Twitter ist ein Elefant im Porzellanladen eine Ballerina.“
                — @Grantscheam auf Twitter
            2. Du musst die Sekunden erstmal wegschmeißen: intdiv($t, 60) und das dann auf Teilbarkeit durch 5 prüfen:

              $t = time();
              if (intdiv($t, 60) % 5 === 0) {}
              

              Gefällt mir auch sehr gut. 👍

        2. Hallo Hank,

          dass es Dir auf die Frage ankam, ob dein Code überhaupt geeignet ist, ein Vielfaches von 5 Minuten zu erkennen, war mir nicht so ganz klar.

          Dass es unschön ist, einen String zu erzeugen und den dann als Zahl umdeuten zu lassen, erwähnte Gunnar schon. Eine schönere Variante wäre:

          $loctime = localtime(null, true);
          if ($locTime['tm_min'] % 5 == 0) {
             ...
          }
          

          bzw. das geht sogar in einem Rutsch, ist dann nur schwerer lesbar:

          if (localtime(null, true)['tm_min'] % 5 == 0) {
             ...
          }
          

          Wenn Du unbedingt ein date-Objekt als Grundlage brauchst, dann geht auch

          if (localtime($date->getTimestamp(), true)['tm_min'] % 5 == 0) {
             ...
          }
          

          Rolf

          --
          sumpsi - posui - obstruxi
          1. Hallo Rolf,

            $loctime = localtime(null, true);
            if ($locTime['tm_min'] % 5 == 0) {
               ...
            }
            

            Diese Lösung würde tatsächlich auch die komplette Minute (5-6, 10-11, 15-16, usw.) nutzen, richtig?

            Hank

            1. Hallo Hank,

              ja.

              Ich verstehe allerdings immer noch nicht, was das soll. Da ist eine unwahrscheinliche Bedingung, die zu prüfen recht teuer ist, und deshalb prüfst Du in einem 5-Minuten Intervall nur in einer dieser 5 Minuten, ob sie erfüllt ist? In den anderen 4 Minuten ignorierst Du den Sachverhalt einfach?

              Rolf

              --
              sumpsi - posui - obstruxi
              1. Ich verstehe allerdings immer noch nicht, was das soll. Da ist eine unwahrscheinliche Bedingung, die zu prüfen recht teuer ist, und deshalb prüfst Du in einem 5-Minuten Intervall nur in einer dieser 5 Minuten, ob sie erfüllt ist? In den anderen 4 Minuten ignorierst Du den Sachverhalt einfach?

                Tatsächlich, so ist es.

            2. Was soll denn die Umrechnung in die lokale Zeit bringen? Ich denke, Dir geht es um die Vermeidung von Performanceleaks?

              $flagFile = '/path/to/file.flag';
              $tBetween = 300; # Sekunden
              
              
              $ft = filemtime( $flagFile );
              if ( false === $ft  || $ft + $tBetween < time() ) {
              
                  if ( ! touch( $flagFile ) ) {
                      trigger_error( "Konnte Flagfile $flagFile nicht anlegen oder ändern.", E_USER_ERROR );
                  }
                  
                  # was auch immer Du tun willt
              }
              

              → Wenn seit dem letzten Durchlauf mehr als 5 Minuten rum sind wird Dein Zeug gemacht.

              • Von dem anderen Zeug lass die Finger.
              • filemtime ist „billig“, weil es allenfalls nur den inode lesen muss, nicht die Datei selbst.
              • touch ist billig, weil es nur den Inode neu schreibt, nicht die Datei selbst. Den Rest besorgt (hoffentlich) der Dateisystemcache.
                • Von dem anderen Zeug lass die Finger.
                • filemtime ist „billig“, weil es allenfalls nur den inode lesen muss, nicht die Datei selbst.
                • touch ist billig, weil es nur den Inode neu schreibt, nicht die Datei selbst. Den Rest besorgt (hoffentlich) der Dateisystemcache.

                Ist deine Lösung "billiger" als die von Gunnar?

                  • Von dem anderen Zeug lass die Finger.
                  • filemtime ist „billig“, weil es allenfalls nur den inode lesen muss, nicht die Datei selbst.
                  • touch ist billig, weil es nur den Inode neu schreibt, nicht die Datei selbst. Den Rest besorgt (hoffentlich) der Dateisystemcache.

                  Ist deine Lösung "billiger" als die von Gunnar?

                  Auf jeden Fall viel besser und in der Gesamtstatistik viel schneller als Dein Ansatz, zu dem immer mehr Unsinn (wie gerade die localtime) hinzukommt. Schon mal im Hinblick auf die garantierte Ausführung und auf die - nach Deiner eigenen Beschreibung - n-1 mal notlose (sinnlose) Ausführung, wenn das Skript in den betreffenden Minuten n-mal aufgerufen wird.

                  n kann sehr groß werden...

                  Wobei ich mich wirklich frage, warum Du den Aufwand nicht an der Stelle und zu dem Zeitpunkt betreibst, zu dem der auch anfällt. Da ist doch mit Sicherheit ein anderes Programm am Wirken und Du kannst - ebenso sicher - den Programmablauf an dieser Stelle vervollständigen statt das „ondemand“ (bei einem Webseitenaufruf) zu tun.

                  So richtig „Richtig“ ist also auch diese „Lösung“ nicht, aber um Klassen näher dran.

                  1. So richtig „Richtig“ ist also auch diese „Lösung“ nicht, aber um Klassen näher dran.

                    Ich habe nun gemerkt, dass ich an dieser Stelle auch auf Sessions zugreifen kann, wo ich die "teure Anfrage" vermute, daher kann ich mir nun komplett ersparen, erst alle 5 Minuten danach zu sehen.

                    Jetzt kann ich die Bedingung sooft prüfen, wie ich will und verzichte komplett auf den 5-Minuten Abstand.

                    Danke für Eure Anregungen,

                    Hank