Alexander Brock: HTTP-Cache

Hallo Freunde des gehobenen Forumsgenusses,

Ich habe eine PHP-Klasse zum Cachen von Inhalten hinter HTTP-URLs geschrieben, und in einem Thread im großen Forum verlinkt, den ich nicht mehr finde (no-archive?).

In dem Thread ging es darum, ob man fremde URLs mit file_get_contents abrufen sollte oder besser nicht und im weiteren Verlauf u.a. um den übermäßigen Traffic durch die Weblog-Feeds.

Ich habe dann diese Klasse geschrieben um Webmastern, die fremde Feeds (speziell die des Weblogs ;-)) einbinden wollen eine komfortable, traffic-sparende Möglichkeit zu geben:

http://alexanderbrock.de/temp/cache/

Verwendet wird das ganze so:

  
require_once('HTTPCacheDB.class.php');  
require_once('HTTPCache.class.php');  
  
$foo = new HTTPCache;  
$inhalt = $foo->get('http://example.org/');  

Dann hat man in $inhalt den Inhalt der URL, ohne zu wissen ob selbiger aus dem Cache kommt oder gerade heruntergeladen wurde.

Dazu legt die Software folgende Verzeichnisstruktur an:

  • data (Hier wird alles gespeichert, den Namen kann man einstellen)
     |- resources.ser (Serialisiertes Array mit Informationen über die URLs)
     |- cache (Verzeichnis mit den Cache-Dateien)

Zu dieser Software hätte ich jetzt gerne Kritik :-)

Gruß
Alexander

  1. Hallo,

    Zu dieser Software hätte ich jetzt gerne Kritik :-)

    bitte mache dazu den Inhalt von HTTPCache.class.php ebenso zugängig!

    Gruß aus Berlin!
    eddi

    1. Hallo Freunde des gehobenen Forumsgenusses,

      bitte mache dazu den Inhalt von HTTPCache.class.php ebenso zugängig!

      Was meinst du?

      Hier sind Links auf den Quellcode der beiden Klassen:

      HTTPCache.class.php
      HTTPCacheDB.class.php

      Gruß
      Alexander Brock

      1. Hallo,

        bitte mache dazu den Inhalt von HTTPCache.class.php ebenso zugängig!

        Was meinst du?

        das zu einer Kritik uns _beide_ Quellcodes zugängig sein müssen. Bitte sorge dafür!

        Gruß aus Berlin!
        eddi

      2. Hallo Freunde des gehobenen Forumsgenusses,

        bitte mache dazu den Inhalt von HTTPCache.class.php ebenso zugängig!

        Uhps, du hast ja Recht. Jetzt sollten aber beide Klassen erreichbar sein.

        Gruß
        Alexander Brock

        1. Re:

          HTTPCache.class.php:

          231d230
          <   $timeout = (int)round($timeout/2+0.00000000001);
          237a237

          // nutze die möglichkeiten der stream-Funktionen, um auch ssl-Resourcen zu saugen

          251d250
          <   socket_set_timeout($pointer, $timeout);
          271c270
          <    $head .= 'If-None-Match: '.((string)$etag)."\r\n";
          ---

          $head .= 'If-None-Match: '.$etag."\r\n";

          274c273
          <    $head .= 'If-Modified-Since: '.date('D, d M Y H:i:s', abs((int)$lastget))." GMT\r\n";
          ---

          $head .= 'If-Modified-Since: '.date('D, d M Y H:i:s', abs($lastget))." GMT\r\n";

          276a276

          // deflate vergessen => http://forum.de.selfhtml.org/archiv/2006/4/t128318/#m829037

          278,279c278
          <   $head .=
          <   'Connection: close'."\r\n"."\r\n";
          ---

          $head .= "Connection: close\r\n\r\n";

          284a284

          $header=true;

          288c288,292
          <    $response .= fgets($pointer);
          ---

          $t=fgets($pointer);
                 if($header){
                         $t=explode(': ',$t);
                         // header auseinanderpulen
                 }

          299a304

          // Warum verarbeitest Du in Form einer Auffrischung nicht HTTP-Status 304? Letzen Teil bitte überarbeiten zumal Header während des Einlesens gleich geparst werden können

          302c307
          <   $body = $ares[1];
          ---

          /*  $body = $ares[1];

          335c340
          <   return $return;
          ---

          return $return;*/

          HTTPCacheDB.class.php:

          18d17
          <    $url = (string)$url;
          54,63c53
          <   if (!file_exists($this->db)) {
          <    mkdir($this->db, 0644);
          <   }
          <   if (!file_exists($this->db)) {
          <    return false;
          <   }
          <   if (!file_exists($this->db.'cache')) {
          <    mkdir($this->db.'cache', 0644);
          <   }
          <   if (!file_exists($this->db.'cache')) {
          ---

          if ((!file_exists($this->db) && !mkdir($this->db, 0644)) || (!file_exists($this->db.'cache') && !mkdir($this->db.'cache', 0644))) {

          98a89,90

          //  völlig überflüssige CPU-Last eine URL _ist_ eindeutig und einmalig, md5 und sha1 können u.U. gleiche
          //  Werte für verschiedene URLs liefern es ist überdenkenswerte hier mit einer Headerdatei für alle gespeicherten URLs zu arbeiten

          114d105
          <   $url = (string)$url;
          119a111

          // warum prüfst Du mit is_int, um dann mit (int) zu konvertieren?

          122a115

          // warum prüfst Du mit is_string, um dann mit (string) zu konvertieren?

          125a119

          // warum prüfst Du mit is_int, um dann mit (int) zu konvertieren?

          128a123

          // warum prüfst Du mit is_int, um dann mit (int) zu konvertieren?

          138c133,134
          <   $res = serialize($this->files);
          ---

          //  $res = serialize($this->files); redundant -> es wird unnötig Array-Key "url" mit abgespeichert, der ergibt sich aus dem Namen der Datei
                 $res=serialize($this->file['url']);

          153,154d148
          <   $url = (string)$url;
          <   $content = (string)$content;
          155a150,151

          //  völlig überflüssige CPU-Last eine URL _ist_ eindeutig und einmalig, md5 und sha1 können u.U. gleiche
          //  Werte für verschiedene URLs liefern es ist überdenkenswert hier mit einer Headerdatei für alle gespeicherten URLs zu arbeiten

          Gruß aus Berlin!
          eddi

          1. Hallo Freunde des gehobenen Forumsgenusses,

            231d230
            <   $timeout = (int)round($timeout/2+0.00000000001);
            237a237

            // nutze die möglichkeiten der stream-Funktionen, um auch ssl-Resourcen zu saugen

            Ich werde das noch implementieren. Aus den Zahlen werde ich allerdings nicht schlau, was willst du mit denen ausdrücken?

            251d250
            <   socket_set_timeout($pointer, $timeout);
            271c270
            <    $head .= 'If-None-Match: '.((string)$etag)."\r\n";

            $head .= 'If-None-Match: '.$etag."\r\n";
            274c273
            <    $head .= 'If-Modified-Since: '.date('D, d M Y H:i:s', abs((int)$lastget))." GMT\r\n";


            $head .= 'If-Modified-Since: '.date('D, d M Y H:i:s', abs($lastget))." GMT\r\n";
            276a276
               // deflate vergessen => http://forum.de.selfhtml.org/archiv/2006/4/t128318/#m829037

            Sehe ich das richtig, dass ich gzinflate verwenden muss, wenn der Server als "Content-Encoding: deflate" zurückschickt?

            // Warum verarbeitest Du in Form einer Auffrischung nicht HTTP-Status 304? Letzen Teil bitte überarbeiten zumal Header während des Einlesens gleich geparst werden können

            Darf man eine Verbindung einfach so abbrechen, wenn einen das Ergebnis nicht mehr interessiert?

            98a89,90

            //  völlig überflüssige CPU-Last eine URL _ist_ eindeutig und einmalig, md5 und sha1 können u.U. gleiche
            //  Werte für verschiedene URLs liefern es ist überdenkenswerte hier mit einer Headerdatei für alle gespeicherten URLs zu arbeiten

            In der Header-Datei stehen noch ein paar andere Informationen, wie ETag und Last-Modified, außerdem können URLs Zeichen enthalten, die nicht als Dateinamen erlaubt sind und sie können zu lang sein (hatte ich alles schon, war echt doof).

            Die Wahrscheinlichkeit, dass unter n URLs zwei sind, die den gleichen MD5 _und_ den gleichen SHA1-Wert haben:

            [latex]
            1-\left(1-{1 \over {2^{128+160}}}\right)^n
            [/latex]

            Also ich halte das eindeutig für das kleinere Übel.

            114d105
            <   $url = (string)$url;
            119a111

            // warum prüfst Du mit is_int, um dann mit (int) zu konvertieren?

            Wahrscheinlich habe ich zuerst den Typcast und dann die Abfrage geschrieben.

            <   $res = serialize($this->files);

            //  $res = serialize($this->files); redundant -> es wird unnötig Array-Key "url" mit abgespeichert, der ergibt sich aus dem Namen der Datei
                   $res=serialize($this->file['url']);

            Die URL wird genau einmal gespeichert, und zwar als Schlüssel in diesem Array, da ist gar nichts redundant.

            Gruß
            Alexander Brock

            1. Hi Alexander,

              // deflate vergessen => http://forum.de.selfhtml.org/archiv/2006/4/t128318/#m829037

              Sehe ich das richtig, dass ich gzinflate verwenden muss, wenn der Server als "Content-Encoding: deflate" zurückschickt?

              Jein, im Prinzip ja, praktisch ist das etwas komplizierter - siehe http://forum.de.selfhtml.org/archiv/2006/4/t128318/#m829037 ff. ;-)

              In der Header-Datei stehen noch ein paar andere Informationen, wie ETag und Last-Modified, außerdem können URLs Zeichen enthalten, die nicht als Dateinamen erlaubt sind und sie können zu lang sein (hatte ich alles schon, war echt doof).

              Hier denke ich genauso wie du - trotzdem würden mich mal noch eddi's Gründe dagegen interessieren - ist es wirklich nur die CPU-Last? Das sollte doch auf modernen Rechnern kaum ins Gewicht fallen...

              MfG, Dennis.

              1. Hallo,

                Sehe ich das richtig, dass ich gzinflate verwenden muss, wenn der Server als "Content-Encoding: deflate" zurückschickt?

                Jein, im Prinzip ja, praktisch ist das etwas komplizierter - siehe http://forum.de.selfhtml.org/archiv/2006/4/t128318/#m829037 ff. ;-)

                das Wesentliche habe ich dabei sogar vergessen zu erwähnen, was Dennis und ich uns hier erst durch einen Einwand von Christian Seiler erarbeiten mußten: Du empfängst Daten via HTTP/1.1 hast aber keinerlei Routinen dazu geschrieben mit "Transfer-Encoding: chunked" klarzukommen.

                Dein Srcikt läuft auch Gefahr durch den mitgelieferten "User-Agent" erkannt zu werden und von einem böswilligen Admin abgeschossen zu werden. Das soll nicht heißen, daß Du den UA nicht mitsenden solltest. Mache aber eine Maximallängebeschränkung.

                In der Header-Datei stehen noch ein paar andere Informationen, wie ETag und Last-Modified, außerdem können URLs Zeichen enthalten, die nicht als Dateinamen erlaubt sind und sie können zu lang sein (hatte ich alles schon, war echt doof).

                Hier denke ich genauso wie du - trotzdem würden mich mal noch eddi's Gründe dagegen interessieren - ist es wirklich nur die CPU-Last? Das sollte doch auf modernen Rechnern kaum ins Gewicht fallen...

                Damit verfolge ich gleich mehrere Ansätze. Zum einen ist und bleibt es wirklich unnütze CPU-Last, zum anderen ist aus dem Dateinamen für den Menschen kein Schluß mehr auf die Resource möglich. Würdest Du eine Headerdatei mit den Angaben der URLs nutzen, bestünde noch die Möglichkeit der Übersicht. (Es ist aber kein MUSS ;)

                Gruß aus Berlin!
                eddi

                1. Hallo Freunde des gehobenen Forumsgenusses,

                  das Wesentliche habe ich dabei sogar vergessen zu erwähnen, was Dennis und ich uns hier erst durch einen Einwand von Christian Seiler erarbeiten mußten: Du empfängst Daten via HTTP/1.1 hast aber keinerlei Routinen dazu geschrieben mit "Transfer-Encoding: chunked" klarzukommen.

                    
                    if (isset($return['transfer-encoding'])) {  
                     if ($return['transfer-encoding'] == 'chunked') {  
                      $length = hexdec(trim(substr($body, 0, strpos($body, "\r\n"))));  
                      $body = substr($body, strpos($body, "\r\n")+2, $length);  
                     }  
                    }  
                  
                  

                  Das ist da drin, seit ich die http_get-Methode auf HTTP 1.1 umgestellt habe (und es funktioniert z.B. mit den Weblog-Feeds).

                  Dein Srcikt läuft auch Gefahr durch den mitgelieferten "User-Agent" erkannt zu werden und von einem böswilligen Admin abgeschossen zu werden. Das soll nicht heißen, daß Du den UA nicht mitsenden solltest. Mache aber eine Maximallängebeschränkung.

                  Ich habe den UA erstmal rausgeschmissen, darum kann ich mich auch später kümmern, ich halte das für ein eher unwichtiges Detail.

                  Gruß
                  Alexander Brock

                  1. Hallo Alexander,

                    if (isset($return['transfer-encoding'])) {
                       if ($return['transfer-encoding'] == 'chunked') {
                        $length = hexdec(trim(substr($body, 0, strpos($body, "\r\n"))));
                        $body = substr($body, strpos($body, "\r\n")+2, $length);
                       }
                      }

                      
                    Damit liest Du nur den ersten Chunk aus -> sobald der Server mehr als einen Chunk liefert, fliegt Deine Methode auf die Schnauze.  
                      
                    
                    > Das ist da drin, seit ich die http\_get-Methode auf HTTP 1.1 umgestellt habe (und es funktioniert z.B. mit den Weblog-Feeds).  
                      
                    Zufall, dass das funktioniert. Bzw. eigentlich will ich das sogar bezweifeln, da Chunks im Normalfall sehr klein sind. Evtl. werden die Weblog-Feeds, die Du untersucht hast, nicht als chunked gesendet und es geht deswegen.  
                      
                    Viele Grüße,  
                    Christian  
                    
                    -- 
                    "I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone." - Bjarne Stroustrup
                    
            2. Hallo,

              Ich werde das noch implementieren. Aus den Zahlen werde ich allerdings nicht schlau, was willst du mit denen ausdrücken?

              das ist der Auswurf von $ diff HTTPCache.class.php HTTPCache.class.php.1 und soll Dir die Arbeit erleichtern.

              Darf man eine Verbindung einfach so abbrechen, wenn einen das Ergebnis nicht mehr interessiert?

              Ja.

              In der Header-Datei stehen noch ein paar andere Informationen, wie ETag und Last-Modified, außerdem können URLs Zeichen enthalten, die nicht als Dateinamen erlaubt sind und sie können zu lang sein (hatte ich alles schon, war echt doof).

              Daher würde ich ja auch eine Headerdatei vorschlagen und nicht einen gleichbleibend 36-Byte-langen String.

              Wahrscheinlich habe ich zuerst den Typcast und dann die Abfrage geschrieben.

              Spiele Dir die unterschiede mit patch ein, dann siehst Du besser, was ich meine.

              Die URL wird genau einmal gespeichert, und zwar als Schlüssel in diesem Array, da ist gar nichts redundant

              Sie ist aber überflüssig.

              Gruß aus Berlin!
              eddi

              1. Hallo Freunde des gehobenen Forumsgenusses,

                das ist der Auswurf von $ diff HTTPCache.class.php HTTPCache.class.php.1 und soll Dir die Arbeit erleichtern.

                Könntest du die von dir veränderte Version der Datei mal hochladen, ich hab meine Version inzwischen so oft geändert, dass das garantiert schief geht.

                Darf man eine Verbindung einfach so abbrechen, wenn einen das Ergebnis nicht mehr interessiert?

                Ja.

                Okay, dann mach ich das, sobald ich wieder Zeit dazu habe.

                In der Header-Datei stehen noch ein paar andere Informationen, wie ETag und Last-Modified, außerdem können URLs Zeichen enthalten, die nicht als Dateinamen erlaubt sind und sie können zu lang sein (hatte ich alles schon, war echt doof).

                Daher würde ich ja auch eine Headerdatei vorschlagen und nicht einen gleichbleibend 36-Byte-langen String.

                Was verstehst du unter einer Headerdatei?

                Gruß
                Alexander Brock

                1. Hallo,

                  Daher würde ich ja auch eine Headerdatei vorschlagen und nicht einen gleichbleibend 36-Byte-langen String.

                  Was verstehst du unter einer Headerdatei?

                  Hierbei meine ich eine Datei, die den Zusammenhang zwischen den im Cache gehaltennen Dateien (und dessen Namen) herstellt.

                  CacheDatei   URL
                  -------------------------------------------------------------------------------
                  000001.txt   http://ai000.de/PHP/scripte/HTTPCacheDB.class
                  000002.txt   http://ai000.de/PHP/scripte/HTTPCache.class
                  000003.txt   http://eddi.to-grip/ich.rss
                  000004.txt   http://ich.bin.ein/ganz/doll?lange=URL&mit=wasweissich

                  Gruß aus Berlin!
                  eddi

          2. Hallo Freunde des gehobenen Forumsgenusses,

            // deflate vergessen => http://forum.de.selfhtml.org/archiv/2006/4/t128318/#m829037

            Darf ich die von dir geopstete Funktion an meine Bedürfnisse anpassen und unter GPL veröffentlichen?

            Gruß
            Alexander Brock

            1. Hallo,

              Darf ich die von dir geopstete Funktion an meine Bedürfnisse anpassen und unter GPL veröffentlichen?

              sie stehen Dir (und jedem anderen auch) lizenzlos zur freien Verfügung. Aber Vorsicht - die allermeisten enthalten einen kleinen Fehler, den der OP jeweils selbst zu lösen hatte ;)

              Gruß aus Berlin!
              eddi

  2. echo $begrüßung;

    Zu dieser Software hätte ich jetzt gerne Kritik :-)

    $foo = new HTTPCache;
    $inhalt = $foo->get('http://example.org/');

    Wenn get() die einzige öffentliche Methode ist, wäre es für den Anwender einfacher, diese statisch aufrufen zu können, statt sich eine Instanz, die man nicht weiter braucht, in seinen Variablenraum zu holen. (Um nicht bei mehrfachen Aufrufen jedes Mal ein neues Objekt zu erzeugen, kannst du dich ja intern des Singleton-Musters bedienen.) Du verwendest auch keinen Konstruktor, für dessen Aufruf eine Instantiierung nötig wäre.

    if (basename($_SERVER['PHP_SELF']) == 'HTTPCache.class.php') {
    highlight_file('HTTPCache.class.php');

    Oft haben Webserver eine Standardeinstellung, die beim Enden von Dateinamen auf .phps das macht, was du damit nachbildest. Ein Symlink auf die .php-Datei ist dafür ausreichend (falls das Verfolgen von Symlinks nicht konfigurativ unterbunden wurde).

    Die Klasse HTTPCacheDB ist im Prinzip allein nicht lebensfähig. Sie verwendet Klassenvariablen (z.B. $this->open), die aber erst in der abgeleiteten Klasse HTTPCache definiert werden. Für PHP ist das zwar kein großes Problem - es erzeugt sie sich einfach, wenn es sie nicht gibt - aber guter Stil ist das nicht. "Die Kinder versorgen die Eltern" ist nicht im Sinne des OOP-Konzepts. Besser ist es, eine Basisklasse zu erstellen, die alle Eigenschaften und Methoden enthält, die grundsätzlich vorhanden sein sollen, auch wenn diese nur abstrakt sind und in erbenden Klassen überschrieben werden müssen. Ich habe den Eindruck, dass du HTTPCacheDB nur zum Auslagern von Code verwendest, damit die Hauptklasse nicht zu groß wird. Das ist ... ähm ... unüblich. Eine Klasse sollte nach außen hin ein abgeschlossenes Gebilde sein, so dass man sie auch eigenständig woanders verwenden kann.

    Du setzt mit der Verwendung von file_put_contents() PHP5 voraus, wendest aber dessen neue Visibility-Möglichkeiten bei der Verwendung von Klassen nicht an (public, private, protected für Methoden und statt var). (Das ist nur eine Feststellung meinerseits, kein kriegsentscheidender Fehler.)

    Du verwendest trigger_error() in HTTPCacheDB. Soweit nicht schlecht. Das hat nur den Nachteil, dass man die Ausgabe von Meldungen irgendwie ausschalten muss, wenn man deine Klassen einsetzt. Ein besseres Konzept wären Exceptions. Die erzeugen immer noch eine Meldung, jedoch nur, wenn man sie nicht abfängt, was man aber im Gegensatz zu einem irgendwohin ausgegebenen Fehlermeldungsstring einfacher machen und auswerten kann.

    Die Methode open() in HTTPCacheDB liefert zwar einen Erfolgsstatus, doch der wird in HTTPCache ignoriert. Bei add|read|update|writeResource() ist das auch so.

    Soweit die Dinge, die mir beim Drüberschauen aufgefallen sind. Inhaltlich habe ich nicht geprüft.

    echo "$verabschiedung $name";

    1. Hallo Freunde des gehobenen Forumsgenusses,

      Wenn get() die einzige öffentliche Methode ist, wäre es für den Anwender einfacher, diese statisch aufrufen zu können, statt sich eine Instanz, die man nicht weiter braucht, in seinen Variablenraum zu holen.

      Du meinst so? HTTPCache::get($url);

      Die Klasse HTTPCacheDB ist im Prinzip allein nicht lebensfähig.

      Ja, das ist Absicht. Sie macht auch allein wenig Sinn.

      Ich habe den Eindruck, dass du HTTPCacheDB nur zum Auslagern von Code verwendest, damit die Hauptklasse nicht zu groß wird. Das ist ... ähm ... unüblich.

      Nein, ich wollte eigentlich Die Verarbeitung von HTTP-Anfragen von der Datenbank trennen, damit man z.B. die Datenbank durch ein DBMS ersetzen kann.,

      Du setzt mit der Verwendung von file_put_contents() PHP5 voraus, wendest aber dessen neue Visibility-Möglichkeiten bei der Verwendung von Klassen nicht an (public, private, protected für Methoden und statt var).

      Ich habe extra für die PHP 4 eine Datei, in der die notwendigen Funktionen nachgerüstet werden, weil das ganze auch unter PHP 4 laufen können soll.

        
      if (!function_exists('file_put_contents')) {  
       #[....]  
      }  
      
      

      Du verwendest trigger_error() in HTTPCacheDB. Soweit nicht schlecht. Das hat nur den Nachteil, dass man die Ausgabe von Meldungen irgendwie ausschalten muss, wenn man deine Klassen einsetzt. Ein besseres Konzept wären Exceptions.

      Die es in PHP 4 nicht gibt. Das mit dem trigger_error ist auch mehr zu Debugging-Zwecken, ich kommentiere das jetzt mal aus. Außerdem sollten in Produktiv-Systemen die Fehler geloggt und nicht ausgegeben werden.

      Die Methode open() in HTTPCacheDB liefert zwar einen Erfolgsstatus, doch der wird in HTTPCache ignoriert. Bei add|read|update|writeResource() ist das auch so.

      Das ist wahr, das änder ich noch.

      Gruß
      Alexander Brock

      1. echo $begrüßung;

        Du meinst so? HTTPCache::get($url);

        Ja.

        Die Klasse HTTPCacheDB ist im Prinzip allein nicht lebensfähig.
        Ja, das ist Absicht. Sie macht auch allein wenig Sinn.

        Vielleicht war das etwas falsch ausgedrückt. Es kommt nicht darauf an, ob eine Klasse nur eine Hilfsaufgabe oder ob sie eine Hauptaufgabe erledigen soll. Eine Klasse, die sich um das Resultat einer Datenbank-Abfrage kümmert oder einen einzelnen Datensatz aus der Abfragemenge darstellt, ist ohne die Klasse für das eigentliche Datenbankhandling nicht besonders sinnvoll. Sie soll aber möglichst selbständig die Aufgabe erledigen, für die sie zuständig ist. Wenn sie aber von etwas abhängig ist, dass erst in einem Erben bereitgestellt wird, dann ist da was nicht richtig. Der Erbe kann gern die vorhandene Funktionalität nutzen und seinerseit neue bereitstellen, darf aber nicht lebensnotwendig für den Vorfahren sein.

        PHP ist da, wie gesagt, tolerant. Compiler anderer Sprachen würden sich beim Compiliervorgang über Zugriffe auf nicht deklarierte Variablen beklagen und ihre Arbeit einstellen.

        Nein, ich wollte eigentlich Die Verarbeitung von HTTP-Anfragen von der Datenbank trennen, damit man z.B. die Datenbank durch ein DBMS ersetzen kann.,

        Soweit löblich, aber siehe oben.

        Das mit dem trigger_error ist auch mehr zu Debugging-Zwecken, ich kommentiere das jetzt mal aus. Außerdem sollten in Produktiv-Systemen die Fehler geloggt und nicht ausgegeben werden.

        Dem zweiten Satz stimme ich zu.
        Neben dem bedingungslosen Auswerfen der Meldung gibt es noch die "MySQL-Methode". Die Funktionen geben ein Ergebnis oder aber false im Fehlerfall zurück. Den Meldungstext (und auch eine eindeutige Nummer, die die Art des Fehlers auswertbar angibt), kann man sich im Bedarfsfall über spezielle Funktionen abholen. Um an die Fehlermeldung zu gelangen muss diese also im Hauptprogramm abgefragt werden. Das hat den Nebeneffekt, dass du damit auch gleich in das Hauptprogramm Code zur Auswertung des Fehlerstatus einbaust.
        Ich habe die Befürchtung, dass du mit der trigger_error-Methode zwar Lösungen für alle im Labor angezeigten Fehler implementierst, dir dann sicher bist, alles Mögliche getan zu haben, jedoch in der freien Wildbahn wegen etwas unbeachtet gelassenem ungeschützt auf die Nase fällst.

        echo "$verabschiedung $name";

        1. Hallo Freunde des gehobenen Forumsgenusses,

          Du meinst so? HTTPCache::get($url);

          Ja.

          Hm, wenn man nun ein RDBMS bemühen möchte (z.B. weil man innerhalb eines CMS alles mit einem RDBMS speichert) müsste man sowieso irgendwie Zugangsdaten übergeben (z.B. an den Konstruktor).

          Außerdem könnte man auch mehrere URLs abrufen wollen, da wäre ein Objekt meines Erachtens auch praktischer.

          Die Klasse HTTPCacheDB ist im Prinzip allein nicht lebensfähig.
          Ja, das ist Absicht. Sie macht auch allein wenig Sinn.

          Vielleicht war das etwas falsch ausgedrückt. Es kommt nicht darauf an, ob eine Klasse nur eine Hilfsaufgabe oder ob sie eine Hauptaufgabe erledigen soll. Eine Klasse, die sich um das Resultat einer Datenbank-Abfrage kümmert oder einen einzelnen Datensatz aus der Abfragemenge darstellt, ist ohne die Klasse für das eigentliche Datenbankhandling nicht besonders sinnvoll. Sie soll aber möglichst selbständig die Aufgabe erledigen, für die sie zuständig ist. Wenn sie aber von etwas abhängig ist, dass erst in einem Erben bereitgestellt wird, dann ist da was nicht richtig. Der Erbe kann gern die vorhandene Funktionalität nutzen und seinerseit neue bereitstellen, darf aber nicht lebensnotwendig für den Vorfahren sein.

          Wenn ich nun das ganze umdrehen würde, also von HTTPCache erbe könnte ich, ohne den Quellcode von HTTPCache.class.php zu verändern mehrere unterschiedliche DBMSe verwenden (HTTPCacheDB ist ein solches).

          Allerdings wäre HTTPCache dann immer noch nicht allein funktionsfähig.

          Gruß
          Alexander Brock

          1. echo $begrüßung;

            HTTPCache::get($url);
            Hm, wenn man nun ein RDBMS bemühen möchte (z.B. weil man innerhalb eines CMS alles mit einem RDBMS speichert) müsste man sowieso irgendwie Zugangsdaten übergeben (z.B. an den Konstruktor).

            Eine andere Möglichkeit wäre noch: HTTPCache::setup(...);

            Außerdem könnte man auch mehrere URLs abrufen wollen, da wäre ein Objekt meines Erachtens auch praktischer.

            Ja, wenn diese Aufrufe innerhalb _einer_ Funktion stattfinden. Wenn sie über das ganze Script verteilt sind, müsste man dann zu Methoden greifen, wie: jedes Mal ein neues Objekt erstellen, eins global ablegen, sich selbst ein Singleton erstellen, ...

            Wenn ich nun das ganze umdrehen würde, also von HTTPCache erbe könnte ich, ohne den Quellcode von HTTPCache.class.php zu verändern mehrere unterschiedliche DBMSe verwenden (HTTPCacheDB ist ein solches).

            Ich würde das vielleicht so implementieren: Die Klasse HTTPCache kümmert sich um den HTTP-Vorgang. Zum Ablegen der Seiten bedient sich HTTPCache einer "Storage-Engine" (Lass dich von dem Begriff nicht erschrecken, der klingt gefährlicher als er ist.) Die Storage-Engine weiß nichts weiter von HTTPCache. Sie stellt nur Methoden wie init() (oder Parameterübergabe im Konstruktor), get() und store() zur Verfügung. HTTPCache muss auch nicht von der Storage-Engine erben. Er erweitert nicht deren Funktionalität sondern bedient sich ihrer nur. Damit bleibt sie beliebig austauschbar. Du müsstest sonst jedes Mal von einer anderen Storage-Klasse erben.

            Nehmen wir mal an, du hättest noch nichts da. In der Anfangsphase könnte die Storage-Engine aus wenigen Zeilen Code bestehen, die einfach nur Dummy-Funktionalität haben. get() gibt immer nur false zurück (daraufhin holt HTTPCache die Seite neu aus dem Netz - es soll ja erstmal nur der HTTP-Teil entstehen). store() verwirft das Übergebene und sagt true. Andere Methoden geben auch erstmal nur irgendwas zurück. Wenn HTTPCache soweit funktioniert kann man sich der Storage-Engine zuwenden.
            Man braucht noch nicht mal HTTPCache zum Testen der Funktionalität. Ein paar Zeilen Code, der nur feste oder zufällige Werte übergibt und/oder anfordert, reicht um zu testen, ob die Storage-Engine ordentlich arbeitet.

            Ob es nötig ist, eine Basisklasse für Storage-Engines zu schreiben? ... Gemeinsame Funktionalität, die also nicht jede Storage-Engine-Implementation anders realisiert, wird es wohl nicht geben. Man kann aber immerhin noch, wenn nötig, testen (beispielsweise beim Initialisieren der Storage-Engine in HTTPCache), ob ein von der Basisklasse abgeleitetes Objekt zurückgegeben wurde (is_a()).

            echo "$verabschiedung $name";

            1. Hi dedlfix,

              Ob es nötig ist, eine Basisklasse für Storage-Engines zu schreiben? ... Gemeinsame Funktionalität, die also nicht jede Storage-Engine-Implementation anders realisiert, wird es wohl nicht geben.

              Das schreit doch förmlich nach einem Interface oder einer abstrakten Klasse.

              Man kann aber immerhin noch, wenn nötig, testen (beispielsweise beim Initialisieren der Storage-Engine in HTTPCache), ob ein von der Basisklasse abgeleitetes Objekt zurückgegeben wurde (is_a()).

              Zum Beispiel folgende abstrakte Klasse:

              abstract class DBMS {  
                
                // Diese Funktionen muss jedes DBMS auf jeden Fall implementieren  
                abstract public function get($ressource);  
                abstract public function save($ressource, $data);  
                
                // aber nicht jedes DBMS muss sich initialisieren und beenden  
                public function init() {  
                  return true;  
                }  
                public function stop() {  
                  return true;  
                }  
              }
              

              Mit get_parent_class() sollte sich dann noch prüfen lassen, ob KonkreteKlasse ein Kind von DBMS ist - Durch die Prüfung kann man dann auch noch sicherstellen, dass die obigen 4 Funktionen auf jeden Fall vorhanden sind.

              MfG, Dennis.

              1. Hallo Freunde des gehobenen Forumsgenusses,

                Das schreit doch förmlich nach einem Interface oder einer abstrakten Klasse.

                Beides erfordert PHP 5, und das ganze soll auch unter PHP 4 laufen, deshalb vertraue ich einfach darauf, dass die Leute, die eine Storage-Engine schreiben die benötigten Methoden auch schreiben.

                Gruß
                Alexander Brock

                1. echo $begrüßung;

                  Das schreit doch förmlich nach einem Interface oder einer abstrakten Klasse.

                  Beides erfordert PHP 5, und das ganze soll auch unter PHP 4 laufen, deshalb vertraue ich einfach darauf, dass die Leute, die eine Storage-Engine schreiben die benötigten Methoden auch schreiben.

                  Interface fällt also aus, Schwamm drüber. Aber die abstrakte Klasse kannst du auch ohne das Schlüsselwort abstract unter PHP4 schreiben. Dann muss ein Erbe nur selbst die Disziplin mitbringen, alle von dir in abstrakter Weise (echo "Du sollst mich überschreiben!";) notierten Methoden zu implementieren.

                  echo "$verabschiedung $name";

                  1. Hallo Freunde des gehobenen Forumsgenusses,

                    Interface fällt also aus, Schwamm drüber. Aber die abstrakte Klasse kannst du auch ohne das Schlüsselwort abstract unter PHP4 schreiben. Dann muss ein Erbe nur selbst die Disziplin mitbringen, alle von dir in abstrakter Weise (echo "Du sollst mich überschreiben!";) notierten Methoden zu implementieren.

                    Ich erbe aber schon von HTTPCache, da kann ich nicht auch noch von einer weiteren abstrakten Klasse erben (jedenfalls nicht in PHP 4).

                    Wenn jemand also eine Storage-Engine schreibt und eine notwendige Methode vergisst gibts schlicht und ergreifend einen Fatal Error, das sollte demjenigen dann schon auffallen.

                    Gruß
                    Alexander Brock

                  2. Hallo Freunde des gehobenen Forumsgenusses,

                    Interface fällt also aus, Schwamm drüber. Aber die abstrakte Klasse kannst du auch ohne das Schlüsselwort abstract unter PHP4 schreiben. Dann muss ein Erbe nur selbst die Disziplin mitbringen, alle von dir in abstrakter Weise (echo "Du sollst mich überschreiben!";) notierten Methoden zu implementieren.

                    Ich habe jetzt in der Klasse HTTPCache alle notwendigen Methoden eingetragen, die alle das Programm sterben lassen, wenn sie nicht überschrieben werden *g*.

                    Gruß
                    Alexander Brock

              2. echo $begrüßung;

                Mit get_parent_class() sollte sich dann noch prüfen lassen, ob KonkreteKlasse ein Kind von DBMS ist

                Ich finde is_a(), bzw. unter PHP5 den instanceof-Operator, angebrachter. get_parent_class() testet nur eine Hierarchiestufe weit. is_a()/instanceof geht beliebig weit und ist damit zukunftssicherer. Vielleicht gibt es ja mal neben der abstrakten Storage-Engine eine abgeleitete aber allgemeine Datenbank-S.E. die die Basis für eine MySQL-S.E., eine Postgres-S.E., usw. bildet.

                echo "$verabschiedung $name";

            2. Hallo Freunde des gehobenen Forumsgenusses,

              Ja, wenn diese Aufrufe innerhalb _einer_ Funktion stattfinden. Wenn sie über das ganze Script verteilt sind, müsste man dann zu Methoden greifen, wie: jedes Mal ein neues Objekt erstellen, eins global ablegen, sich selbst ein Singleton erstellen, ...

              Ich habe jetzt ein PHP4-kompatibles Singleton mittels deiner Methode aus https://forum.selfhtml.org/?t=129839&m=839507 gemacht.

              Ich würde das vielleicht so implementieren: Die Klasse HTTPCache kümmert sich um den HTTP-Vorgang. Zum Ablegen der Seiten bedient sich HTTPCache einer "Storage-Engine" (Lass dich von dem Begriff nicht erschrecken, der klingt gefährlicher als er ist.) Die Storage-Engine weiß nichts weiter von HTTPCache. Sie stellt nur Methoden wie init() (oder Parameterübergabe im Konstruktor), get() und store() zur Verfügung. HTTPCache muss auch nicht von der Storage-Engine erben. Er erweitert nicht deren Funktionalität sondern bedient sich ihrer nur. Damit bleibt sie beliebig austauschbar. Du müsstest sonst jedes Mal von einer anderen Storage-Klasse erben.

              Im Moment lasse ich die Storage-Engine (man, klingt das cool *g*) von HTTPCache erben, dann muss nichtmal HTTPCache beibringen, unterschiedliche Storage-Engines zu verwenden, sondern man muss nur noch die gewünschte Storage-Engine einbinden.

              Die Datei-System-Storage-Engine (die einzige, die es im Moment gibt) hat jetzt eine Methode getInstance(), die ein Objekt von sich selbst zurückgibt (gibt die eigentlich das Objekt oder eine Referenz zurück?).

              Nehmen wir mal an, du hättest noch nichts da. [...]

              Eigentlich gibt es schon eine Storage-Engine, und der HTTP-Teil funktioniert auch :-)

              Ich überlege gerade, ob eine Storage-Engine, die MySQL verwendet großartig Sinn ergeben würde, aber das Lesen und Einfügen von Blobs soll ja furchtbar lahm sein, der einzige Vorteil wäre, dass man nicht die komplette Liste aller URLs in den Speicher kopieren muss.

              Das wäre aber wahrscheinlich erst bei tausenden von URLs, von denen man immer nur ganz wenige braucht schneller.

              Ich hab das ganze mal wieder hochgeladen, damit ihr es euch angucken könnt:

              http://alexanderbrock.de/temp/cache/

              Ich habe Softlinks auf die Dateien gesetzt, aber jetzt werden sie mir nur zum Herunterladen angeboten, kann man das per .htaccess einstellen, dass die Dateien mit Syntaxhighlighting ausgegeben werden?

              Gruß
              Alexander Brock

              1. Hi Alexander,

                Ich habe Softlinks auf die Dateien gesetzt, aber jetzt werden sie mir nur zum Herunterladen angeboten, kann man das per .htaccess einstellen, dass die Dateien mit Syntaxhighlighting ausgegeben werden?

                Das liegt wohl nicht an den Symlinks, sondern an der Endung .phps - oder hast du mal eine richtige .phps Datei hochgeladen und es hat funktioniert?

                Probier mal ein AddType application/x-httpd-php-source .phps in der .htaccess, siehe http://php-faq.de/q/q-apache-parsen.html.

                MfG, Dennis.

                1. Hallo Freunde des gehobenen Forumsgenusses,

                  Das liegt wohl nicht an den Symlinks, sondern an der Endung .phps - oder hast du mal eine richtige .phps Datei hochgeladen und es hat funktioniert?

                  Erste Frage: Ja
                  Zweite Frage: Nein, hat es nicht.

                  Probier mal ein AddType application/x-httpd-php-source .phps in der .htaccess, siehe http://php-faq.de/q/q-apache-parsen.html.

                  Das funktioniert auch nicht, es führt lediglich dazu, dass ich die Dateien als application/x-httpd-php-source ausgeliefert bekomme.

                  Gruß
                  Alexander Brock

                  1. echo $begrüßung;

                    Probier mal ein AddType application/x-httpd-php-source .phps in der .htaccess, siehe http://php-faq.de/q/q-apache-parsen.html.

                    Das funktioniert auch nicht, es führt lediglich dazu, dass ich die Dateien als application/x-httpd-php-source ausgeliefert bekomme.

                    Dann ist dein Server möglicherweise nach anderen "occuren" :-) Prinzipien eingestellt als PHP es vorschlägt. Diese Konfiguration hängt von den Server-Gegebenheiten ab. CGI vs. Modul spielt eine Rolle (siehe verlinkte Seite) und die Mime-Type-Namen können auch geändert worden sein. (Ich glaube, dazu gab es hier neulich einen Faden.)

                    echo "$verabschiedung $name";

              2. echo $begrüßung;

                Im Moment lasse ich die Storage-Engine (man, klingt das cool *g*) von HTTPCache erben, dann muss nichtmal HTTPCache beibringen, unterschiedliche Storage-Engines zu verwenden, sondern man muss nur noch die gewünschte Storage-Engine einbinden.

                Ich halte das für nicht optimal, aber mach das ruhig so. Man kann auch aus durch Fehlern entstehender Erfahrung lernen :-)

                Die Datei-System-Storage-Engine (die einzige, die es im Moment gibt) hat jetzt eine Methode getInstance(), die ein Objekt von sich selbst zurückgibt (gibt die eigentlich das Objekt oder eine Referenz zurück?).

                Weder noch. PHP4 arbeitet immer mit Kopien. Möchte man eine Referenz, muss man das explizit angeben.

                function &singleton() {  
                  static $instance = null;  
                  
                  if ($instance == null)  
                    $instance = new classname();  
                  
                  return $instance;  
                }  
                  
                $instance =& singleton();
                

                Beachte die beiden &. Siehe: Returning References (Der deutschen Übersetzung fehlen nicht ganz unwichtige Textstellen.)

                (In seltenen Fällen muss auch noch mit $instance =& new classname(); eine Referenz statt einer Kopie des erzeugten classname-Objektes in $instance abgelegt werden.)

                (PHP5 hingegen arbeitet (nur) bei Objekten mit Referenzen. Möchte man eine Kopie muss man clonen.)

                echo "$verabschiedung $name";

                1. Hallo Freunde des gehobenen Forumsgenusses,

                  Im Moment lasse ich die Storage-Engine (man, klingt das cool *g*) von HTTPCache erben, dann muss nichtmal HTTPCache beibringen, unterschiedliche Storage-Engines zu verwenden, sondern man muss nur noch die gewünschte Storage-Engine einbinden.

                  Ich halte das für nicht optimal,[...]

                  Warum nicht? Was spricht denn dagegen? Wenn man z.B. einen Feedreader schreibt muss nur HTTPCache requiren, die Storage-Engine requiren und sich ein Objekt davon besorgen.

                  Ansonsten müsste man umständlich der Klasse HTTPCache erklären, welche Storage-Engine sie instanzieren soll.

                  Man kann auch aus durch Fehlern entstehender Erfahrung lernen :-)

                  Also dieser Satz ergibt auch nach längerem Nachdenken wenig Sinn.

                  Weder noch. PHP4 arbeitet immer mit Kopien. Möchte man eine Referenz, muss man das explizit angeben.

                  Also wenn ich so eine Methode habe, bekomme ich dann unter PHP 4 und PHP 5 immer nur die Referenz?

                    
                  /**  
                  * Gibt eine Instanz des Caches zurück.  
                  */  
                   function getInstance() {  
                    static $conn = null;  
                    
                    if ($conn == NULL) {  
                     $conn = &new HTTPCacheDB();  
                     $conn->open();  
                    }  
                    
                    return $conn;  
                   }  
                  
                  

                  Gruß
                  Alexander Brock

                  1. echo $begrüßung;

                    Im Moment lasse ich die Storage-Engine (man, klingt das cool *g*) von HTTPCache erben, dann muss nichtmal HTTPCache beibringen, unterschiedliche Storage-Engines zu verwenden, sondern man muss nur noch die gewünschte Storage-Engine einbinden.

                    Ich halte das für nicht optimal,[...]

                    Warum nicht? Was spricht denn dagegen? Wenn man z.B. einen Feedreader schreibt muss nur HTTPCache requiren, die Storage-Engine requiren und sich ein Objekt davon besorgen.

                    Du sparst dir vielleicht in diesem kleinen überschaubaren Fall die Instantiierung der Storage-Engine. Doch wenn deine Projekte mal größer werden, kannst du nicht einfach eine riesige Vererbungskette aufbauen, nur um irgendwas zu sparen.
                    Der Sinn von Klassen ist, einer abgeschlossene Funktionalität einen geeigneten Rahmen zu geben. Wenn du eine große Erbmasse erstellst, kannst du auch gleich die Klassen weglassen und alles im globalen Adressraum ablegen.
                    Wenn dein Lebensmittelhändler die Aufgabe hat, Lebensmittel zu besorgen, musst du als Kunde nicht von ihm erben. Als Erbe bekommst du unnötigen Zugriff auf all seine Eigenschaften. Da kann es dann schon mal zu Problemen beim Zugriff auf $this->portemonnaie geben. Das will weder der Händler, noch du. Er hat seine abgeschlossene Aufgabe, du hast deine. Wenn du seine Leistungen nutzen willst, bediene dich ihrer an den vom Händler bereitgestellten Schnittstellen, aber verleibe ihn dir nicht ein.

                    Ansonsten müsste man umständlich der Klasse HTTPCache erklären, welche Storage-Engine sie instanzieren soll.

                    Es ist kein großer Unterschied, ob du ein um "DB" erweiterten Klassennamen ansprichst, oder das "DB" als Parameter übergibst. Und ein

                    function engine_factory($engine_type)
                      $engine_name = 'Engine_' . $engine_type;
                      $engine_file = $engine_name . '.php';

                    if (!file_exists($engine_file)) {
                        fehler('Klassendatei nicht gefunden');
                        return false;
                      }

                    require_once $engine_file;

                    if (class_exists($engine_name) and is_a($engine = new $engine_name(), 'Engine'))
                        return $engine;

                    fehler('Engineerzeugungsfehler');
                      return false;
                    }

                    ist schnell geschrieben. (Wieder kommt ein Pattern zum Einsatz, diesmal das factory pattern.)

                    Also wenn ich so eine Methode habe, bekomme ich dann unter PHP 4 und PHP 5 immer nur die Referenz?

                    Nein, PHP4 und 5 arbeiten da unterschiedlich. PHP4 kopiert, PHP5 referenziert.

                    /**

                    • Gibt eine Instanz des Caches zurück.
                      */
                      function getInstance() {
                        static $conn = null;

                    if ($conn == NULL) {
                       $conn = &new HTTPCacheDB();
                       $conn->open();
                      }

                    return $conn;
                    }

                      
                    So ist das weder unter PHP4 noch unter PHP5 sinnvoll.  
                    PHP4: Du erzeugst eine neue Instanz von HTTPCacheDB, übergibst eine Referenz darauf. Das ist, wie ich schrieb nur in besonderen Ausnahmefällen notwendig. Das Handbuch empfiehlt, nicht auf Teufel komm raus zu referenzieren, sondern nur da, wo man wirklich eine Referenz braucht. "Do not use return-by-reference to increase performance, the engine is smart enough to optimize this on its own. Only return references when you have a valid technical reason to do it!" Diesen Grund sehe ich bei HTTPCacheDB nicht.  
                    Du solltest aber die Referenz so wie in meinem Vorposting-Beispiel verwenden. Ob du nun die wirklich erzeugte Instanz oder eine Kopie davon in $conn ablegst, ist technisch gesehen egal. Für den weiteren Verlauf ist es an der Stelle unwichtig, dass es nur eine Kopie ist. Das Original ist nicht mehr ansprechbar und geht in die ewigen Jagdgründe. Es gibt keinen Konflikt zwischen den beiden.  
                    Wenn du aber $conn an den Aufrufer von getInstance übergeben willst ist es schon wichtig, ob du eine Kopie oder das Original rausgibst. Wenn der Aufrufer eine Kopie ändert, bekommt der nächste Aufrufer eine neue jungfräuliche Kopie ohne die Änderungen der anderen Aufrufers. Das kann so gewollt sein oder auch nicht.  
                    PHP5: Hier gibt es generell Referenzen. In der Notation $x =& new foo(); ist das & überflüssig. Ansonsten würde dein Beispiel von sich aus Referenzen zurückgeben.  
                      
                      
                    echo "$verabschiedung $name";
                    
                    1. Hallo Freunde des gehobenen Forumsgenusses,

                      Wenn dein Lebensmittelhändler die Aufgabe hat, Lebensmittel zu besorgen, musst du als Kunde nicht von ihm erben. [...] Wenn du seine Leistungen nutzen willst, bediene dich ihrer an den vom Händler bereitgestellten Schnittstellen, aber verleibe ihn dir nicht ein.

                      Ich kann ihn aber auch aufkaufen, dann kann ich ihn kontrolieren *g*

                      (Wieder kommt ein Pattern zum Einsatz, diesmal das factory pattern.)

                      Okay, das sieht gut aus, ich probier das mal aus.

                      So ist das weder unter PHP4 noch unter PHP5 sinnvoll.
                      PHP4: Du erzeugst eine neue Instanz von HTTPCacheDB, übergibst eine Referenz darauf. Das ist, wie ich schrieb nur in besonderen Ausnahmefällen notwendig. Das Handbuch empfiehlt, nicht auf Teufel komm raus zu referenzieren, sondern nur da, wo man wirklich eine Referenz braucht. "Do not use return-by-reference to increase performance, the engine is smart enough to optimize this on its own. Only return references when you have a valid technical reason to do it!" Diesen Grund sehe ich bei HTTPCacheDB nicht.

                      Ich schon: Wenn verschiedene Teile einer größeren Software die selbe URL haben wollen und die noch nicht gecacht ist oder ihr Verfallsdatum überschritten hat, wird sie so oft heruntergeladen wie Kopien von HTTPCache erstellt werden, bei der Referenz ist das nicht so.

                      Gruß
                      Alexander Brock

                      1. echo $begrüßung;

                        Wenn verschiedene Teile einer größeren Software die selbe URL haben wollen und die noch nicht gecacht ist oder ihr Verfallsdatum überschritten hat, wird sie so oft heruntergeladen wie Kopien von HTTPCache erstellt werden, bei der Referenz ist das nicht so.

                        Ich nehme an, du hast das noch nicht verstanden. Beim Erzeugen der Instanz der HTTPCacheDB-Klasse

                        $conn = new HTTPCacheDB(); // nur ein = , kein =&

                        ist es nicht nötig, eine Referenz auf das erzeugte Objekt zu halten. Eine Kopie macht es auch.
                        Mit obigem Befehl, wird durch das new zunächst eine Instanz erzeugt. Durch die Zuweisung zu $conn wird eine Kopie des zuzuweisenden Ausdrucks erstellt und in $conn abgelegt. Die eigentlich erzeugte Instanz wird verworfen, die in $conn liegende Kopie ist nun deine einzige Instanz. (Bei dem eben beschriebenen Vorgang treffen zwei normale Verhaltensweisen von PHP aufeinander: das, was PHPs bei new macht und das, was es bei einer normalen Zuweisung macht.)

                        $conn liegt lokal in der getInstance()-Methode. Um die in $conn liegende Instanz aus getInstance heraus zu bekommen, muss sie als Rückgabewert ausgegeben werden. Dabei darf allerdings keine Kopie erstellt werden. Deshalb muss die Rückgabe per Referenz erfolgen.

                        So sieht das dann in Code aus:

                        function &getInstance() { // <-- da kommt das & hin  
                          static $conn = null;  
                          
                          if ($conn == NULL) {  
                            $conn = new HTTPCacheDB();  
                            $conn->open();  
                          }  
                          
                          return $conn;  
                        }
                        

                        Da fehlen jetzt nur noch geeignete Maßnahmen, falls das open() fehlschlägt.

                        Und beim Zugreifen ist ebenfalls das Referenzieren nicht zu vergessen:

                        $httpcachedb_instance =& getInstance();

                        echo "$verabschiedung $name";

                    2. Hallo Freunde des gehobenen Forumsgenusses,

                      function engine_factory($engine_type)
                        $engine_name = 'Engine_' . $engine_type;
                        $engine_file = $engine_name . '.php';

                      if (!file_exists($engine_file)) {

                      Damit habe ich ein Problem:

                      Was ist, wenn 'Engine_'.$engine_type.'.php' gar nicht im aktuellen Verzeichnis sondern irgendwo anders im include-pfad liegt? Ich würde es bevorzugen, auf Teufel komm raus die Engine mit require_once einzubinden und es PHP überlassen bei Nichtexistenz abzubrechen.

                      Gruß
                      Alexander Brock

                      1. echo $begrüßung;

                        Was ist, wenn 'Engine_'.$engine_type.'.php' gar nicht im aktuellen Verzeichnis sondern irgendwo anders im include-pfad liegt?

                        Dann definierst du dir halt noch eine Konstante, die den Pfad zu den Dateien enthält. Diese soll sich der Anwender dann selbst anpassen, wenn er was an der Verzeichnisstruktur ändert.

                        echo "$verabschiedung $name";