Naps: Ladezeit messen / __destruct() ?

Hi,

ich würde gerne die Ladezeit einer Website messen und speichern.

Im PHP Manual habe ich bezüglich __destruct() folgendes gelesen:

"Die Destruktormethode wird aufgerufen, sobald es keine weiteren Referenzen auf ein bestimmtes Objekt mehr gibt, oder in beliebiger Reihenfolge am Ende des Skripts."

Bedeutet das, dass so etwas funktionieren würde:

class loadingTime{  
    public function __construct(){  
        $this->startTime= microtime(true);  
    }  
    public function __destruct(){  
        $this->endTime= microtime(true);  
        $this->loadingTime= $this->endTime- $this->startTime;  
        //  in DB speichern  
     }  
}

und dann einfach einbinden?
Wie kann ich sicher sein, dass __destruct wirklich als letztes aufgerufen wird?

Danke
MfG Naps

  1. Meine Herren!

    ich würde gerne die Ladezeit einer Website messen und speichern.

    Was verstehst du denn unter Ladezeit? Die Zeit, die der Server braucht, um die Anfrage zu bearbeiten? Die kannst du einfach mit deinen Browser-Entwicklertools messen (Strg+Shift+i oder F12).

    Bedeutet das, dass so etwas funktionieren würde:

    class loadingTime{

    public function __construct(){
            $this->startTime= microtime(true);
        }
        public function __destruct(){
            $this->endTime= microtime(true);
            $this->loadingTime= $this->endTime- $this->startTime;
            //  in DB speichern
         }
    }

      
    Damit würdest du die Lebensdauer einer Objektinstanz messen können. Also wie lange hat das Objekt der Klasse loadingTime existiert.  
      
    
    > Wie kann ich sicher sein, dass \_\_destruct wirklich als letztes aufgerufen wird?  
      
    Wenn es keine Referenzen auf das Objekt mehr gibt, gibt es keine Möglichkeit mehr eine Methoden auf diesem Objekt aufzurufen. Wenn du den einen Ring in einen Vulkan geworfen hast, kannst du ihn nicht mehr anziehen, nicht mal mehr ansehen, so gerne du auch würdest. Diese Gewährleistung verschafft dir PHP, da musst du dich um nichts mehr kümmern.  
    
    -- 
    “All right, then, I'll go to hell.” – Huck Finn
    
    1. Was verstehst du denn unter Ladezeit? Die Zeit, die der Server braucht, um die Anfrage zu bearbeiten? Die kannst du einfach mit deinen Browser-Entwicklertools messen (Strg+Shift+i oder F12).

      Das stimmt so nicht.
      Da es auch eine Übertragungszeit gibt, kann man die reine Scriptzeit so nicht messen.
      Eventuell möchte der Fragende die Scriptzeit auch mitloggen.

      Gruß
      was habt ihr alle gegen meine Mudda?
      T-Rex

      1. Meine Herren!

        Was verstehst du denn unter Ladezeit? Die Zeit, die der Server braucht, um die Anfrage zu bearbeiten? Die kannst du einfach mit deinen Browser-Entwicklertools messen (Strg+Shift+i oder F12).

        Das stimmt so nicht.
        Da es auch eine Übertragungszeit gibt, kann man die reine Scriptzeit so nicht messen.

        Die Übertragungsdauer wird von den Entwicklertools unabhängig von der Wartezeit gemessen, ich nehme aber an, dass die Pingzeit in die Wartezeit mit einfließt, so gesehen hast du wieder recht.

        --
        “All right, then, I'll go to hell.” – Huck Finn
    2. Om nah hoo pez nyeetz, 1UnitedPower!

      Wenn du den einen Ring in einen Vulkan geworfen hast, kannst du ihn nicht mehr anziehen, nicht mal mehr ansehen, so gerne du auch würdest.

      Das kommt ganz auf den Vulkan an ;-p

      Matthias

      --
      Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Verdi und Verdienstausfall.

  2. Hello,

    Ladezeit != Erzeugungsdauer
    Der Ausgabepuffer des Webservers hat da auch noch ein paar Baud mitzureden.

    Aber Du könntest das vielleicht ganz klassisch mit register_shutdown_function() lösen
    http://de3.php.net/manual/en/function.register-shutdown-function.php

    Soweit ich mich erinnere, musst Du nur das Handle für die Datei, in die Du die Zeiten schreibst, schon vorher besorgt haben. Könnte sein, dass es in der Funktion nicht mehr geht. Da war mal was ...

    Und ignore_user_abort() http://de3.php.net/manual/en/function.ignore-user-abort.php möchte ich Dir noch ans Herz legen zum Spielen.

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
     ☻_
    /▌
    / \ Nur selber lernen macht schlau
    Die ultimative Seite für Selbermacher
    1. Ladezeit != Erzeugungsdauer
      Der Ausgabepuffer des Webservers hat da auch noch ein paar Baud mitzureden.

      Stimmt, ich meinte die Erzeugungsdauert!

      Aber Du könntest das vielleicht ganz klassisch mit register_shutdown_function() lösen
      http://de3.php.net/manual/en/function.register-shutdown-function.php

      Soweit ich mich erinnere, musst Du nur das Handle für die Datei, in die Du die Zeiten schreibst, schon vorher besorgt haben. Könnte sein, dass es in der Funktion nicht mehr geht. Da war mal was ...

      Und ignore_user_abort() http://de3.php.net/manual/en/function.ignore-user-abort.php möchte ich Dir noch ans Herz legen zum Spielen.

      Perfekt! Schau ich mir mal an.
      MfG Naps

    2. Meine Herren!

      Der Ausgabepuffer des Webservers hat da auch noch ein paar Baud mitzureden.

      Und auch der opcode-Cache. Wartezeiten bei IO-Operationen (zum Beispiel Datenbank-Abfragen) möchte man wahrscheinlich auch nicht mit messen. Je klarer man benennen kann, was gemessen werden soll und was nicht, desto einfacherer wird es eine Messmethode zu entwickeln. Eine Messung Pi mal Daumen, was eben so anfällt wird später wenig hilfreich dabei sein einen potenziellen Flaschenhals zu identifizieren.

      --
      “All right, then, I'll go to hell.” – Huck Finn
      1. Und auch der opcode-Cache. Wartezeiten bei IO-Operationen (zum Beispiel Datenbank-Abfragen) möchte man wahrscheinlich auch nicht mit messen. Je klarer man benennen kann, was gemessen werden soll und was nicht, desto einfacherer wird es eine Messmethode zu entwickeln. Eine Messung Pi mal Daumen, was eben so anfällt wird später wenig hilfreich dabei sein einen potenziellen Flaschenhals zu identifizieren.

        Ich würde gerne alles von der Anfrage bis zur Ausgabe messen. Mit den Browser-Entwicklertools hast du recht, so kann ich aber nur bei mir messen, und nicht wirklich speichern.

        MfG Naps

  3. Hello,

    class loadingTime{

    public function __construct(){
            $this->startTime= microtime(true);
        }
        public function __destruct(){
            $this->endTime= microtime(true);
            $this->loadingTime= $this->endTime- $this->startTime;
            //  in DB speichern
         }
    }

      
    $\_\_t = new loadingTime();  
      
    Zum Testen (oder als lezte Zeile im Script) kannst Du auch  
      
    $\_\_t = NULL;  
      
    schreiben, um das Objekt bewusst zu zerstören.  
      
      
      
      
    Liebe Grüße aus dem schönen Oberharz  
      
      
    Tom vom Berg  
    ![](http://selfhtml.bitworks.de/Virencheck.gif)  
      
    
    -- 
     ☻\_  
    /▌  
    / \ Nur selber lernen macht schlau  
    [Die ultimative Seite für Selbermacher](http://getscript.de/)
    
    1. Mahlzeit,

      Damit misst du aber nur das Objekt, nicht das komplette Script.

      --
      42
      1. Hello,

        Damit misst du aber nur das Objekt, nicht das komplette Script.

        Die Instanzbildung machst Du ganz am Anfang, Das Zerstören ganz am Ende. Das ist dann zwar keine exacte Script-Lebensdauer, aber kommt der schon ziemlich nahe.

        Eine andere Möglichkeit wäre die Benutzung einer Funktion mit einer Static-Variablen in einem Auto-Prepend-File und einem Auto-Append-File.

        http://de2.php.net/manual/en/ini.core.php#ini.auto-prepend-file
        http://de2.php.net/manual/en/ini.core.php#ini.auto-append-file

        Einrichtung:
        PHP_INI_PERDIR Entry can be set in php.ini, .htaccess, httpd.conf or .user.ini (since PHP 5.3)
                                                     ^^^^^^^^^^

          
        #.htaccess  
        php_value auto_prepend_file prepend.php  
        php_value auto_append_file  append.php  
          
        
        
          
          
        <?php ### prepend.php utf-8 ### ÄÖÜäöü  
          
        function get_optime($mode)  
        {  
            static $start;  
            static $fp;	  
          
        	if ( $mode === 'start')  
            {  
        		$start = microtime(true);  
        		$fp = fopen('log.txt', 'ab+');  
                fwrite($fp, "startet at $start" . PHP_EOL);  
            }  
            elseif ($mode === 'stop' && is_resource($fp))  
            {  
                $stop = microtime(true);  
                fwrite ($fp, "stopped at $stop => operation time: " . ($stop - $start) . PHP_EOL);  
                fclose($fp);  ## kann mman sich wahrscheinlich schenken  
            }  
        }	  
          
        get_optime('start');  
          
        ?>  
          
        
        

        Da ist dann die Messung dann wieder ein kleines Bisschen zu lang. Die exakte Scriptlebensdauer zu messen wird man wohl nicht hinbekommen.

          
        <?php ### append.php ### utf-8 ÄÖÜäöü  
          
        get_optime('stop');  
          
        ?>  
          
        
        

        und ein Testsrcipt dazu

          
        <?php  ### testscript.php ### utf-8 ### ÄÖÜäöü  
        header('Content-Type: text/plain; CharSet=utf-8');  
        set_time_limit(0);  
        ob_end_clean();  
          
        for ($i = 0; $i < 50; $i++)  
        {  
        	echo date('H:i:s') . " - Hallo\r\n";  
        	flush();  
        	sleep(1);  
        }  
          
        ?>  
          
        
        

        Funktioniert aber nur, wenn das Hauptscript nicht mit exit() beendet wird.

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
         ☻_
        /▌
        / \ Nur selber lernen macht schlau
        Die ultimative Seite für Selbermacher
        1. Hello,

          so geht es auch. Nur mit Prepend-File.

            
          #.htaccess  
          php_value auto_prepend_file prepend.php  
            
          
          
            
            
          <?php ### prepend.php utf-8 ### ÄÖÜäöü  
            
          #-------------------------------------------------------------------------------  
          function get_optime($mode)  
          {  
              static $start;  
              static $fp;	  
            
              if ( $mode === 'start')  
              {  
                  $start = microtime(true);  
                  $fp = fopen('log.txt', 'ab+');  
                  fwrite($fp, __FILE__ . " - startet at $start" . PHP_EOL);  
              }  
              elseif ($mode === 'stop' && is_resource($fp))  
              {  
                  $stop = microtime(true);  
                  fwrite ($fp, __FILE__ . " - stopped at $stop => operation time: " . ($stop - $start) . PHP_EOL);  
                  fclose($fp);  ## kann mman sich wahrscheinlich schenken  
              }  
          }	  
          #-------------------------------------------------------------------------------  
            
          register_shutdown_function('get_optime', 'stop');  
          get_optime('start');  
            
          ?>  
            
          
          

          Das hat dann den zweiten Vorteil, dass das Logging auch bei exit() im Hauptscript noch durchgeführt wird.

          Liebe Grüße aus dem schönen Oberharz

          Tom vom Berg

          --
           ☻_
          /▌
          / \ Nur selber lernen macht schlau
          Die ultimative Seite für Selbermacher
          1. Hello,

            so geht es auch. Nur mit Prepend-File.

            #.htaccess
            php_value auto_prepend_file prepend.php

            
            >   
            >   
            > ~~~php
              
            
            >   
            > <?php ### prepend.php utf-8 ### ÄÖÜäöü  
            >   
            > #-------------------------------------------------------------------------------  
            > function get_optime($mode)  
            > {  
            >     static $start;  
            >     static $fp;	  
            >   
            >     if ( $mode === 'start')  
            >     {  
            >         $start = microtime(true);  
            >         $fp = fopen('log.txt', 'ab+');  
            
                      fwrite($fp, "startet at $start" . PHP_EOL);  
            
            >     }  
            >     elseif ($mode === 'stop' && is_resource($fp))  
            >     {  
            >         $stop = microtime(true);  
            >         fwrite ($fp, "stopped at $stop => operation time: " . ($stop - $start) . PHP_EOL);  
            >         fclose($fp);  ## kann mman sich wahrscheinlich schenken  
            >     }  
            > }	  
            > #-------------------------------------------------------------------------------  
            >   
            > register_shutdown_function('get_optime', 'stop');  
            > get_optime('start');  
            >   
            > ?>  
            >   
            > 
            
            

            Das __FILE__ kann man sich schenken, da es leider nur den Namen des Prepend-Files enthält und nicht des Programm-Files, das eigentlich aufgerufen wurde.

            Liebe Grüße aus dem schönen Oberharz

            Tom vom Berg

            --
             ☻_
            /▌
            / \ Nur selber lernen macht schlau
            Die ultimative Seite für Selbermacher
        2. Mahlzeit,

          Die Instanzbildung machst Du ganz am Anfang,

          Nein, ich nicht. Bevor ich meine Klasse instanziere, passiert erstmal ne ganze Menge.

          Das Zerstören ganz am Ende. Das ist dann zwar keine exacte Script-Lebensdauer, aber kommt der schon ziemlich nahe.

          Auch nicht zwingend. Ich zerstöre z.B. öfter mal ein Objekt zur Laufzeit, wenn ich es nicht mehr brauche.

          Es kommt immer auf die Programmierung an. Das wollte ich sagen, dass es nicht pauschal im Konstruktor und Destruktur passieren sollte.

          Wenn dein Script so aussieht:

            
          <?php  
          include 'class.php';  
          $object = new Classname();  
          ?>  
          
          

          Dann hast du natürlich recht.

          Eine andere Möglichkeit wäre die Benutzung einer Funktion mit einer Static-Variablen in einem Auto-Prepend-File und einem Auto-Append-File.

          Das macht vorallem Sinn, wenn man Scriptlaufzeiten bei der Entwicklung messen will, diese Daten aber im Produktivbetrieb nicht benötigt. Praktische Sache das :)

          Hast du mal gestestet, ob es langsamer ist, mit auto-append und auto-prepend zu arbeiten? Nur rein Interessehalber. Wäre ja grundsätzlich interessant.

          --
          42
          1. Hello,

            Wenn dein Script so aussieht:

              
            <?php  
            include 'myClass.php';  
            $object = new myClass();  
              
            blah();  
            blubb();  
              
            $object = NULL;  
            ?>  
            
            

            Dann hast du natürlich recht.

            Das wa Naps idee, es so zu machen.
            Nun könnte er/sie ja auch mal ausprobieren, ob es funktioniert.

            Hast du mal gestestet, ob es langsamer ist, mit auto-append und auto-prepend zu arbeiten? Nur rein Interessehalber. Wäre ja grundsätzlich interessant.

            Ich habe die Version prepend/append gegen die prepend/shutdown-Function getestet. Man kann nur schwer sagen, wieviele Mikrosekunden das nun Unterschied macht...

            Liebe Grüße aus dem schönen Oberharz

            Tom vom Berg

            --
             ☻_
            /▌
            / \ Nur selber lernen macht schlau
            Die ultimative Seite für Selbermacher
  4. Ola,

    die Idee hatte ich auch mal :). Hat aber leider nur eingeschränkt funktioniert.
    PHP hat einen Garbage Collector. Der räumt alle Objekte auf und ruft den Destruktor auf. Der Garbage Collector räumt aber nach dem Ausgabepuffer auf, so dass du keine Ausgabe mehr auf den Bildschirm bekommst. Ergo müsstest du das Objekt bewusst vorher zerstören. Dann kannst du aber gleich eine Funktion aufrufen ;).

    Einen Blick wert ist aber Toms Funktion die am Scriptende aufgerufen wird. Eventuell sitzt die noch vor dem Ausgabepuffer.

    Gruß
    10 * Kluger
    T-Rex

  5. Moin!

    "Die Destruktormethode wird aufgerufen, sobald es keine weiteren Referenzen auf ein bestimmtes Objekt mehr gibt, oder in beliebiger Reihenfolge am Ende des Skripts."

    Ich finde es falsch, wenn man wichtige Dinge im Destruktor versteckt.

    Bedeutet das, dass so etwas funktionieren würde:

    class loadingTime{

    public function __construct(){
            $this->startTime= microtime(true);
        }
        public function __destruct(){
            $this->endTime= microtime(true);
            $this->loadingTime= $this->endTime- $this->startTime;
            //  in DB speichern
         }
    }

    
    >   
    > und dann einfach einbinden?  
      
    Die Konsequenz wäre, dass du in deinem Code nun irgendwo ein Objekt erzeugst:  
      
    `$perform = new loadingTime();`{:.language-php}  
      
    Danach wird das Objekt nie mehr benutzt.  
      
    Jemand, der den Code liest, wird sich denken: "Warum zum Henker wird da eine ungenutzte Variable mit irgendeinem Objekt erzeugt?" Und er könnte versucht sein, diese sinnlos erscheinende Zeile zu löschen. Aus diesem Aspekt heraus klingt es nicht nach der besten Idee, den Destruktor für wichtige Arbeit zu verwenden, denn du ersetzt damit lediglich einen Aufruf einer Methode (zum Messen der Zeit) durch einen Nicht-Aufruf (es geschieht dasselbe, nur in einer Methode \_\_destruct).  
      
    Wie erwähnt, wird der Destruktor nicht garantiert sofort aufgerufen. Wenn man das irgendwie provozieren will, muss man das Objekt explizit löschen.  
      
    Eine Zeile wie die hier sagt mir aber nicht, dass da gerade eine Zeitmessung irgendwo hin gespeichert wird:  
      
    `$perform = null;`{:.language-php}  
      
    Halten wir also fest: Erstens kommst du nicht drum herum, zum Starten der Zeitmessung eine Zeile Code zu schreiben. Zweitens kommst du nicht drum herum, zum Beenden der Zeitmessung eine weitere Zeile Code zu schreiben.  
      
    Wenn du also sowieso zwei Zeilen Code schreiben musst, warum dann nicht durch den Methodennamen klar kommunizieren, was da passiert, anstatt es implizit über diese magischen Methoden zu machen.  
      
    Abgesehen davon hielte ich es für zuviel Aufgabe innerhalb einer Klasse, wenn der Timer gleichzeitig auch noch das Speichern in der Datenbank übernehmen sollte. Datenbankzugriff wird es vermutlich als Klassen schon geben. Der Timer muss also nur eine Zeit ausspucken.  
      
    Und zu guter letzt ist ja auch immer die Frage, wie genau so eine Messung sein kann, wenn DANACH noch irgendein Schreibzugriff auf eine Datenbank stattfindet.  
      
    
    > Wie kann ich sicher sein, dass \_\_destruct wirklich als letztes aufgerufen wird?  
      
    Du kannst dir bei \_\_destruct über keine garantierte Reihenfolge sicher sein. Insbesondere wenn du Objekte nutzen willst (z.B. für Datenbankzugriff), die ihrerseits eventuell schon weggeräumt wurden.  
      
    Der Destruktor ist einerseits das falsche Mittel, und andererseits hält er potentiell böse Überraschungen bereit. Vermeide ihn lieber. Mach die Dinge explizit dann, wenn du sie erledigt haben willst.  
      
     - Sven Rautenberg