andreas: HEADER manipulieren

Hallo!
Ich überlege schon länger wie ich von einem Script zum anderen Daten senden kann, komme immer ein Stückchen weiter, bin aber noch lange nicht ans Ziel.
Ich weiß zwar wie ich einen einfachen Header von einem PHP-Script aus sende, sowas wie

header ("Location: http://www.php.net");

Aber damit sende ich ja einen HEADER an den Browser. Was ich möchte ist einen Header an ein anderes Script senden, also an den Server. Also so eine Art eigener Browser. In letzter Konsequenz geht es darum Daten per POST an ein anderes Script zur Verarbeitung zu schicken, ohne Browser.
Jetzt meine Frage, wie baue ich so einen HEADER in PHP auf? Ich fange mal so an:

header ("
POST /sript.php HTTP/1.1\n
.
.
.
var1=$wert1&var2=$wert2...\n
");

Was brauche ich alles dazwischen, content-type, content-length? Wie muß ich in script.php darauf reagieren, da es ja keine Ausgabe an einen Browser geben soll? Einfach selbst die Statuscodes ausgeben?:

header ("HTTP/1.1 200 OK");

?

Vielleicht kann ja jemand helfen, oder sehe ich hier irgendwas grundsätzlich falsch? Wie sorge ich dafür dass keine weiteren Header-DAten gesendet werden, oder wirden die automatisch überschrieben?

  1. holla,

    Keine Ahnung ob das mit PHP alles so funzt. Aber es sollte eigentlich auch in PHP eine Möglichkeit geben eine Art Socket zu öffnen auf Port 80.

    Was du glaub ich erreichen willst ist ein eigener HTTP Client in PHP um Daten mit anderen Servern und Scripten auszutauschen. Oder so ähnlich.

    Da würd ich mir auf alle Fälle mal die RFC zu HTTP anschauen.(Search RFC+HTTP). Dort findest du alle Informationen zum HTTP Protokoll.
    Und somit auch wie Server und Client in HTTP so schnacken.

    Ob das in der Form aber sinvoll ist weiß i nit. Wenn es ne Socket Option in PHP gibt, musst du ja nicht unbedingt mit einem HTTP Protokoll arbeiten. Aber dazu wissen andere bestimmt mehr.

    cu

    1. Hi!

      Keine Ahnung ob das mit PHP alles so funzt. Aber es sollte eigentlich auch in PHP eine Möglichkeit geben eine Art Socket zu öffnen auf Port 80.

      Ja, das geht auch, aber bei der Socket-Function steht im Manual:

      » Warning
      » This module is EXPERIMENTAL. That means, that the behaviour of
      » these functions, these function names, in concreto ANYTHING
      » documented here can change in a future release of PHP WITHOUT
      » NOTICE. Be warned, and use this module at your own risk.

      außerdem scheint mein Server die Funktione socket() gar nicht zu kennen, ist auch nicht standardmäßig implementiert, habe da leider keinen Einfluß drauf. fsockopen() funktioniert grundsätzlich schon, nur wenn ich damit eine TCP Verbindung herstelle, was habe ich davon? Wie kann ich denn daten übermitteln, ist doch nur für Dateien, oder?

      Daher dachte ich mir das mir die Daten im POST Header zu übertragen wäre eine einfache und gute Methode. Das würde (leider nur theoretisch, da es nicht implementiert ist) ja mit socket() funktionieren, wie ich dem Manual entnehmen konnte.

      Aber einfach mit header() kann ich das wohl nicht machen, oder? Was gibt es noch für Alternativen, wenn ich socket() nicht nutzen kann?

      Was du glaub ich erreichen willst ist ein eigener HTTP Client in PHP um Daten mit anderen Servern und Scripten auszutauschen. Oder so ähnlich.

      Da würd ich mir auf alle Fälle mal die RFC zu HTTP anschauen.(Search RFC+HTTP). Dort findest du alle Informationen zum HTTP Protokoll.
      Und somit auch wie Server und Client in HTTP so schnacken.

      Das weiß ich, aber was ich nicht weiß ist zum einen wie ich http-HEADER von einem Script zum anderen schicke(nicht zum Browser) und das mit PHP! Außerdem weiß ich nicht wie ich die gesendeten HEADER-Informationen sichtbar machen kann, es gibt zwar die ganzen Umgebungsvariablen, nur sind die für den Server ja immer gleich, die genau Antwort des Servers, kann ich die oin PHP irgendwie sichtbar machen? Ich weiß zwar theoretisch was da kommen muß, nur wenn ich das sehen könnte wäre mir schon sehr geholfen, denn so sehe ich was ich da alles veranstaltet habe, nur vorher sollte ich übehaupt mal in der Lage sein, von einem, wie Du richtig sagst "PHP--http-client"Header-Informationen zu verschicken.

      Ob das in der Form aber sinvoll ist weiß i nit. Wenn es ne Socket Option in PHP gibt, musst du ja nicht unbedingt mit einem HTTP Protokoll arbeiten. Aber dazu wissen andere bestimmt mehr.

      Womit denn sonst? Es geht um MySQL "Transaktionen", evtl Newsletterversand... also alles Dinge die PHP über HTTP erledigt, oder?

      Grüße
        Andreas

  2. Moin

    Jetzt meine Frage, wie baue ich so einen HEADER in PHP auf? Ich fange mal so an:

    header ("
    POST /sript.php HTTP/1.1\n
    .
    .
    .
    var1=$wert1&var2=$wert2...\n
    ");

    Das ist erstens Falsch (pro Header-Zeile sollte man einen eigenen header()-Aufruf verwenden, und keine Zeilenumbrüche drin haben) und wird zweitens nicht funktionieren, da die Dinger ja wie du bereits angemerkt hast, an den Browser gehen.

    Dir bleibt wohl nichts anderes übrig einen eigenen kleinen HTTP-Client zu schreiben. Das ist im Prinzip nicht schwer (für GET) aber POST würde ich mir selbst momentan noch nicht zutrauen. Alles was du wissen willst, findest du in RFC 2616 (HTTP/1.1) bzw. RFC 1945 (HTTP/1.0).

    Hier ein kleiner Rahmen für den Anfang (ähnliches benutze ich immer häufiger):

    <?php
     $fh = fsockopen("www.zielhost.de", 80, $errno, $errstr, 15); // Socket zu www.zielhost.de Port 80 aufmachen, fehlermeldungen landen in $errno und $errstr, Verbindungstimeout 15 Sekunden

    if(!$fh) // Verbindung fehlgeschlagen
      if($errno==0) die("Verbindung fehlgeschlagen. Unbekannter Fehler. Wahrscheinlich konnte der Hostname nicht aufgelöst werden");
      else die("Verbindung fehlgeschlagen. Fehler ($errno): $errstr");

    // Verbindung OK
     // Request zusammenbauen
     $req  = "GET / HTTP/1.0\x0d\x0a"; // hole / mit HTTP/1.0
     $req .= "Host: www.zielhost.de\x0d\x0a"; // Host-Angabe für die häufig verwendeten virtual hosts
     $req .= "User-Agent: Henryk war hier\x0d\x0a"; // Nur so; sonstige Header nach dem gleichen Prinzip hinzufügen
     $req .= "\x0d\x0a"; // Eine Leerzeile als krönenden Abschluss des Requests

    fwrite($fh, $req); // Request senden

    $puffer = ""; $answ_hdrs=Array(); $last_hdr = ""; $status_line = "";// ein paar Variablen leer machen

    // Nun die Ergebnisse einlesen
     do{ // Dazu nehmen wir eine Schleife die alles nach Zeilen trennt
      $puffer .= fread($fh, 512); // ein kleines bisschen lesen, wir wollen ja nicht unnötig blockieren
      $zpos1 = strpos($puffer, "\x0d\x0a");  // Es gibt die merkwürdigsten Zeilenendezeichen zwischen Himmel und Erde
      $zpos2 = strpos($puffer, "\x0a"); // Wir suchen mal nach den gebräuchlichsten.
      $zpos3 = strpos($puffer, "\x0d");  // In einer perfekten Welt, wären alle Zeilen der Header mit \0xd\0xa abgetrennt
      if($zpos1 !== FALSE) {  // Ein schönes Zeilenendezeichen
       $zeile = substr($puffer, 0, $zpos1); $puffer = substr($puffer, $zpos1 + 2);
      } else if($zpos2 !== FALSE) { // Ein Unix-Zeilendezeichen
       $zeile = substr($puffer, 0, $zpos2); $puffer = substr($puffer, $zpos2 + 1);
      } else if($zpos3 !== FALSE && ($zpos3 != strlen($puffer)-1 || feof($fh)) ) { // Entweder ein Mac-Zeilenende oder der Anfang eines schönen Zeilenendes, bei letzterem wird es ignoriert
       $zeile = substr($puffer, 0, $zpos3); $puffer = substr($puffer, $zpos3 + 1);
      } else { // Noch keine Zeile vollständig, oder aber ein \x0d am Ende des Puffers gefunden, einfach noch ein bisschen mehr einlesen
       continue;
      }
      // In $zeile liegt jetzt - oh wunder - eine Zeile, also schmeissen wir mit ein paar regulären Ausdrücken nach ihr
      $zeile = trim($zeile);  // Vorne und hinten überflüssige Whitespaces wegmachen. RFC 2616 sagt dass wir das dürfen
      if(preg_match('!^HTTP/(\d+).(\d+)\s+(\d+)\s+(.*)$!', $zeile, $matches)) {
       $status_line = $zeile;
       // Das ist eine wunderhübsche Statuszeile. In $matches[1] und $matches[2] liegt die HTTP-Version, in $matches[3] der Statuscode und in $matches[4] die Meldung
       // Damit kann man jetzt ein bisschen Spaß haben
       if($matches[3] > 399) die("Naja, das war wohl nichts: $zeile");
      } else if(preg_match('!^([^:]+)\s*:\s*(.*)$!', $zeile, $matches)) {
       // Das ist ein sonstiger Header, in $matches[1] liegt die Header-Bezeichnung, in $matches[2] liegt der Wert
       // Wir machen jetzt ein bisschen Magie. $answ_hdrs soll ein assoziatives Array mit den Header-Werten werden. Wenn der andere sich aber entscheidet einen Header
       //  mehr als einmal zu senden (ist sein gutes Recht), dann sollen alle Werte als weiteres Array vorliegen (ist für deine Zwecke vielleicht ein bisschen Overkill)
       $vo = trim($matches[1]); $hi = trim($matches[2]);  // Ein paar Hilfsvariablen
       if($answ_hdrs[$vo] == "") $answ_hdrs[$vo] = $hi; // Dieser Header kam bis jetzt noch nicht (der Normalfall)
       else { // Der Header wahr schonmal da, lasst uns ein Array aufmachen
        if(is_array($answ_hdrs[$vo])) { // Hmm, wir haben schonmal ein Array aufgemacht, also nehmen wir doch gleich das
         $answ_hdrs[$vo][] = $hi;
        } else {
         $answ_hdrs[$vo]=Array($answ_hdrs[$vo]);
         $answ_hdrs[$vo][] = $hi;
        }
       }
       // OK, der Header ist verzeichnet, jetzt noch merken was für ein Header das wahr, wirst gleich sehen warum
       $last_hdr = $vo;
      } else if($zeile == "") { // Eine Leerzeile verkündet das Ende der Header
       break;
      } else { // Die Zeile hat die Form "     blabla", laut Standard kann das bedeuten dass der zuletzt gesendeten Header fortgesetz werden soll, jedoch nicht alles in eine Zeile passt
       if(is_array($answd_hdrs[$last_hdr])) $answ_hdrs[$last_hdr][ count($answ_hdrs[$last_hdr])-1 ] .= " ".$zeile; // Ans Ende des letzten Array-Eintrag setzen
       else $answ_hdrs[$last_hdr] .= " ".$zeile;
      }
     } while(!feof($fh));  // Solange wie noch Zeichen kommen

    // <sing> Alle Header sind schon da, alle Header, alle </sing> Jetzt fehlt nur noch Body

    if(!feof($fh))
      if(isset($answ_hdrs["Content-Length"]))
       $puffer .= fread($fh, $answ_hdrs["Content-Length"] - strln($puffer)); // Alle angekündigten Daten empfangen
      else
       while(!feof($fh)) $puffer .= fread($fh, 2048); // Den Rest der Verbindung empfangen

    // Fertsch. In $answ_hdrs hast du jetzt alle Header, in $status_line die Statuszeile und in $puffer den empfangenen Body

    // Kleine Beispielausgabe
     echo "<h1>".htmlentities($status_line)."</h1><h2>Response Header</h2><pre>"; print_r($answ_hdr); echo "</pre><h2>Response Body</h2><pre>".htmlentities($puffer)."</pre>";
    ?>

    --
    Henryk Plötz
    Grüße aus Berlin

    1. Hi!

      Das ist erstens Falsch (pro Header-Zeile sollte man einen eigenen header()-Aufruf verwenden, und keine Zeilenumbrüche drin haben) und wird zweitens nicht funktionieren, da die Dinger ja wie du bereits angemerkt hast, an den Browser gehen.

      Nun ja, mit den Zeilen war absicht damit sich wenn man das nicht darf jemand beschwert:-) Also kann ich das vergessen, schade! Aber was ist an POST so viel schwieriger??? So wie ich den Header verstehe wird doch bei POST anstatt oben in der Zeile wo GET steht die Daten an den Link  zuhängen die Daten einfach ans Ende geschrieben und sonst genau so, oder nicht? Naja, ich werd dann mal ein wenig lesen.....

      Dir bleibt wohl nichts anderes übrig einen eigenen kleinen HTTP-Client zu schreiben. Das ist im Prinzip nicht schwer (für GET) aber POST würde ich mir selbst momentan noch nicht zutrauen. Alles was du wissen willst, findest du in RFC 2616 (HTTP/1.1) bzw. RFC 1945 (HTTP/1.0).

      Also wenn Du Dir das nicht zutraust, dann sollte ich es wohl erstmal lassen ;)

      Hier ein kleiner Rahmen für den Anfang (ähnliches benutze ich immer häufiger):

      "klein" ist gut, was ist denn bei Dir "GROSS"?
      Jedenfalls vielen Dank!

      Grüße
        Andreas

    2. Hi Henryk,

      Dir bleibt wohl nichts anderes übrig einen eigenen kleinen HTTP-
      Client zu schreiben. Das ist im Prinzip nicht schwer (für GET)
      aber POST würde ich mir selbst momentan noch nicht zutrauen.

      Was ist denn so unterschiedlich schwierig zwischen GET und POST?

      Mein HTTP-Tracer (http://www.schroepl.net/cgi-bin/http_trace.pl)
      macht zwischen beiden Methoden fast alles gleich - nur trennt er bei
      POST den im Eingabefeld enthaltenen Query-String ab und sendet ihn als
      Bestandteil des Body.

      Viele Grüße
            Michael

      1. Moin

        Mein HTTP-Tracer (http://www.schroepl.net/cgi-bin/http_trace.pl)
        macht zwischen beiden Methoden fast alles gleich - nur trennt er bei
        POST den im Eingabefeld enthaltenen Query-String ab und sendet ihn als
        Bestandteil des Body.

        Ja, weiterleiten ist einfach, selber generieren vielleicht auch noch, aber verstehen schon nicht mehr so. Vielleicht ist meine Abneigung auch nur von meinen Problemen beim basteln eines eigenen Webservers getrübt. ;-)

        Mir sind mindestens 2 Enctypes bekannt: application/x-www-form-urlencoded, ok das ist einfach das selbe wie GET in grün und multipart/form-data, das sieht allerdings fast wie eine MIME-Mail aus und gefällt mir gar nicht.

        andreas:
        Mein Tipp: Besorg dir ethereal (gibts auch für Windows) und schau deinem Browser und Webserver bei der Arbeit zu.
               ^^ Igitt, doppel-p
        --
        Henryk Plötz
        Grüße aus Berlin

        1. Hi!
          Also ich kann wohl ein wenig stolz von mir behaupten, dass ich mit einem Anfänger-fsocketopen-Script Daten per Post übertragen habe!

          Auch wenn das auf diese anspruchslose weise geschehen ist, das was ich wollte hat geklappt. Ich will je keinen Browser simulieren(wobei ich merke dass es auch interessant ist:) sondern ich will Daten(Variablen mit Text) per POST  durch netz schicken, und so Daten von der einen MySQL DB in eine andere eintragen, synchronisieren.....
          Zur Zeit mache ich das direkt von einem Script aus, ich öffne beide Datenbanken, auf verschiedenen Servern un Domains, da wurde mir aber dringend von angeraten. "Mein" Script sieht erstmal nur wie folgt aus:

          <?php
          $fp = fsockopen ("www.knet-systems.de", 80, $errno, $errstr, 30);
          if (!$fp) {
          echo "$errstr ($errno)<br>\n";
          } else {
          fputs ($fp, "POST /script.php HTTP/1.0\r\nHost: www.knet-systems.de\r\nContent-type: application/x-www-form-urlencoded\r\nContent-length: 7\r\n\r\nvar=333\r\n\r\n");
          while (!feof($fp)) {
          echo fgets ($fp,128);
          }
          fclose ($fp);
          }
          ?>

          Wenn ich dieses aufrufe wird ein anderes Script "script.php" mit der entsprechenden Variable und dem Header angezeigt.
          Erstmal die Frage ob das so OK ist, oder schon jetzt irgendwelche Fatalen Fehler eingebaut sind, jedenfalls möchte ich vorher eine DB-Abfrage machen, Daten in den Body-Bereich schreiben und so abschicken. Aber kann ich das so stehen lassen und wo "var=333" steht einfach "$daten" schreiben und da rein die ganzen Variablen mit Werten, teilweise sogar Blobs, also ne ganze Menge, womöglich ein paar mehr KB.

          Auf der Seite "script.php" kommen dann Inserts, vielleicht auch was zurückschicken, mal schaun.

          Was sagt Ihr dazu, kann man das so machen oder lieber doch Euren komplizierteren Weg gehen?

          Aber mehr will ich ja fürs erste nicht!

          Viele Grüße
            Andreas

          1. Moin

            Erstmal die Frage ob das so OK ist, oder schon jetzt irgendwelche Fatalen Fehler eingebaut sind, jedenfalls möchte ich vorher eine DB-Abfrage machen, Daten in den Body-Bereich schreiben und so abschicken. Aber kann ich das so stehen lassen und wo "var=333" steht einfach "$daten" schreiben und da rein die ganzen Variablen mit Werten, teilweise sogar Blobs, also ne ganze Menge, womöglich ein paar mehr KB.

            Wenn du POST nimmst, sind die paar mehr KB schon OK. Aber denk dran alle Daten durch urlencode() zu schicken, damit das nicht schief geht.

            Als Zeilendezeichen würde ich dir aber nicht \r\n empfehlen, sondern dass was ich benutzt habe (meinetwegen auch \015\012, ist das selbe bloß in einer anderen Notation). \r\n geht ganz gut, solange du dich auf einem Unix-System bewegst, aber unter anderen OS sind in \n die schauerlichsten Sachen drin, und bei \015\015\012 (== \r\n unter Windows) oder \015\015 (== \r\n unter Mac) könnte der Webserver schon ins Grübeln kommen, was du denn damit wohl gemeint hast. Nebenbei: Das habe ich noch nicht ausprobiert, sondern bloß gelesen (in perldoc glaubich), es _könnte_ also auch funktionieren.

            Was sagt Ihr dazu, kann man das so machen oder lieber doch Euren komplizierteren Weg gehen?

            Der ist nicht 'kompliziert', nur allgemein :)

            --
            Henryk Plötz
            Grüße aus Berlin

            1. Hi!
              OK. Aber wie ist das denn mit der Ausgabe, wenn ich alle Echos wegmache(muß ich noch nichtmal, oder?) könnte das doch durchaus für sich alleine laufen, oder? Das braucht ja keine Ausgabe, also kann ich es durch einen Cronjob starten, wodurch das sript erst ein paar DB-Abfrage durchführt, und dann ist noch die Frage wie ich die Daten jetzt genau schicke. Erst dachte ich, z.B. bei 100 neuen Einträgen, die also per POST an das 2. Script übertragen, und da ind die DB eingetragen werden, ich dachte ich würde irgendwie mit einem Riesen-Array alles übertragen, aber bei 100 Datensätzen und 40 Spalten, 2 davon BLOB weiß ich nicht ob das so  gut ist!
              Also dacahte ich mir ich forme aus den Datensätzen direkt die entsprechenden SQL-Statements, also einfach ein String, an den ich für jeden Datensatz \nINSERT INTO table.....(a,b,c) VALUES (xyz)
              anhänge.
              Und diesen String dann wie oben beschrieben in den Body-Bereich des HEADERS. Muß ich eigentlich Content-length ermitteln(also einfach die Länge meines Strings, oder?)? Und content-type: application/x-www-form-urlencode sollte ich auch lassen?

              Dann wäre da noch die Frage ob ich dann einfach alle SQL-Abfragen auuf einmal ausführen lassen kann, oder ob ich eine Schleife brauche, die z.B. jeden Instert erst ausführt, und dann den nächsten, oder ob ich einfach ein paar Inserts auf einmal, so wie

              $string = INSERT INTO sync VALUES (239, 20011229125928, 'c1', 0, 0, 0, '');
              $string .= INSERT INTO sync VALUES (240, 20011229144505, 'C1', 0, 0, 0, '');
              $string .= INSERT INTO sync VALUES (241, 20011229144515, 'C1', 0, 0, 0, '');
              ...

              also meinen String, und dann

              mysql_query($string, $link);

              Oder wäre das nicht zu empfehlen? Aber ein Schleife ist doch schon nbe Menge Arbeit, oder? Da lohnt sich ja langsam ein pconnect, oder?

              Grüße
                Andreas

              1. Moin

                [sehr viel sehr wirres Zeug gesnippt]

                Also (tieflufthol), jadukannstesohneproblemevoncronausführenlassenundwenndudieausgabedrinlässtdannmailtcronsiedirglaubichsogarzuwasfürdebugzweckesehrnützlichseinkannundneindukanntnichtmehrerequeriesmitsemikolongetrenntasuführenundmitpermanentenverbindungenhatdasüberhuptreingarnichtszutun.

                Und nun noch mal ganz langsam und verständlich:

                Du kannst das Skript völlig problemlos von cron ausführen lassen, wenn du ab- und zu mal Daten abgleichen willst. Und nein, du musst die echos nicht unbedingt rausmachen, soweit ich mich erinnern kann, mailt Cron dir dann alle Ausgaben deiner Skripte zu. Für den Anfang ist das Recht hilfreich, aber später nervt es.

                Nein, du kannst nicht mehrere Queries in einem mysql_query-Aufruf absenden (dein neuer Thread dazu ist übrigens unnötig da du die Frage hier schon mal gestellt hast). Das ist übrigens gut so! Es sorgt dafür dass mysql_query("SELECT FROM foo WHERE bla = $unvalidiertvomuserübergebenevariable"); nicht so einfach auszunutzen ist (nämlich wenn der User $unvalidiertevomuserübergebenevariable auf   lala; DELETE FROM foo   setzt). Eine Schleife ist keine schlechte Idee, ich würde dazu aber die einzelnen Queries in Arrays eintragen, dann musst du nicht aufwändig nach einem Separator (wie dem ;) suchen und hast darüberhinaus keine Probleme mit einem vom User eingefügten Separator (was den eben angesprochenen Exploit evt. wieder möglich machen würde). Was die Größe angeht: Das sollte kein Problem sein, solange du alles schön durch urlencode() jagst. Das mit dem pconnect hat damit überhaupt nichts zu tun. Das sorgt dafür dass die Verbindung auch nach Beendigung des Skripts aufgehalten wird, hat also keinerlei Einfluss auf irgendetwas, während das Skript noch läuft. pconnect ist aber eigentlich immer eine gute Idee. Denn ein Skript welches die Verbindung wieder braucht, wird bestimmt kommen, und dann spart man sich beim weggefallenen Verbindungsaufbau ein bisschen Zeit.

                Ja, application/x-www-form-urlencode solltest du lassen, da multipart/form-data wesentlich komplizierter ist (darauf beruht ja auch der weiter oben beschriebene PHP-Bug).

                Was Content-Length angeht: Ich bin mir nicht ganz sicher ob das erforderlich ist, aber mach es einfach, es kostet ja nichts.

                BTW: Header ist keine Abkürzung und darf daher ruhig mit den gängigen Groß-/Kleinschreibregeln bearbeitet werden. Und "Body-Bereich des Headers" ist ein Widerspruch in sich :)

                --
                Henryk Plötz
                Grüße aus Berlin

                1. Hi!

                  [sehr viel sehr wirres Zeug gesnippt]

                  sorry!

                  pconnect ist aber eigentlich immer eine gute Idee. Denn ein Skript welches die Verbindung wieder braucht, wird bestimmt kommen, und dann spart man sich beim weggefallenen Verbindungsaufbau ein bisschen Zeit.

                  Nun ja, aber wenn das jeder macht wird der DB-Server doch recht lahm, doer macht das nichts? Außerdem sind die Anzahl der Verbindungen doch begrenzt, oder?

                  Den Thread oben habe ich eröffnet, da es um eine spezielle Frage ging, und um ein ganz anderes Thema als in diesem Thread(HTTP-header!)
                  Und es hat ja auch einen guten Tipp:) gebracht, nämlich das ganze mit dem Shell-befehl zu machen, also die Inserts, Updates... ine eine Datei schreiben, wenn auf dem Server angekommen, und diese dann mit

                  mysql ....

                  in die DB einfügen - wenn das dann mal so geht, nicht dass hinterher die DB überschrieben wird oder sowas, da sich ja Daten ändern, die in der ursprünglich DB gar nicht voerhanden sind und so überschrieben würden.

                  Nur mit mysqldump sehe ich da schwarz, da ich wie gesagt nur die Änderungen brauche, oder bibt es dafür auch eine spezielle Funktion, wenn ich in jedr Tabelle Timestamps habe, und ein Dateum mit der letzten Synchronisation?

                  Viele Grüße
                    Andreas

                  1. Moin

                    Nun ja, aber wenn das jeder macht wird der DB-Server doch recht lahm, doer macht das nichts? Außerdem sind die Anzahl der Verbindungen doch begrenzt, oder?

                    Noe, da gibt es irgendwo ein Limit für. Ausserdem werden die Verbindungen von dem jeweiligen Apache-Prozeß aufgehalten und die werden hin und wieder recycelt, zumal es davon oft nur eine kleine Anzahl gibt (bei dir zu Hause ungefähr im Durschnitt 1 bis 4, bei deinem Provider vielleicht etwas mehr).

                    Nur mit mysqldump sehe ich da schwarz, da ich wie gesagt nur die Änderungen brauche, oder bibt es dafür auch eine spezielle Funktion, wenn ich in jedr Tabelle Timestamps habe, und ein Dateum mit der letzten Synchronisation?

                    Am Einfachsten ist es, alle Änderungen in deiner Applikation mitzuloggen. Ich hab sowas ähnliches mal gemacht. Du hast doch sicher sowieso einen Wrapper um mysql_query gelegt, oder? Das ist immer ganz nützlich weil man dann den Rückgabecode überprüfen und eine Fehlerseite ausgeben kann und den PHP-Code der den Rückgabewert prüft nicht x-hundert mal im Skript stehen hat.
                    Und dort habe ich einfach überprüft, ob die Anfage ein INSERT, UPDATE, DELETE etc. ist, und wenn ja, habe ich sie in eine Datei geschrieben. Vorrausgesetzt die Datenbanken sind bereits synchron (das kannst du ja einmal mit mysqldump sicherstellen) kannst du danach diese Datei einfach mal in regelmäßigen Abständen auf dem anderen Host einspielen.

                    Alternative: Wenn du sowieso bei jedem Datensatz eine TIMESTAMP-Spalte hast, die bei INSERT und UPDATE automagisch aktualisiert wird, kannst du die auch nehmen und dann die Queries in PHP nachträglich zusammenbauen.

                    Ausserdem: MySQL unterstützt das was ich zuerst beschrieben habe auch von Haus aus. Wenn du also Zugriff auf die MySQL-Konfiguration hast, solltest du dir http://www.mysql.com/documentation/mysql/bychapter/manual_MySQL_Database_Administration.html#Update_log ansehen.

                    BTW: Ich hoffe du hast das Synchronisationsskript irgendwie geschützt. Nicht dass da jeder einfach so kommen und SQL-Kommandos ausführen kann.

                    --
                    Henryk Plötz
                    Grüße aus Berlin

                    1. Hi

                      Am Einfachsten ist es, alle Änderungen in deiner Applikation mitzuloggen.

                      Jede Änderung - in einer extra Tabelle? Ist das nicht aufwendiger als einfach mit Timestamp und neu einer Tabelle wo bei jeder Synchronisation der Zeitpunkt festgehalten wird, udn alles was danach geändert wurde auswählen und daraus SQL-Statementsmachen.

                      Ich hab sowas ähnliches mal gemacht. Du hast doch sicher sowieso einen Wrapper um mysql_query gelegt, oder?

                      Was bitte ist ein Wrapper?

                      Das ist immer ganz nützlich weil man dann den Rückgabecode überprüfen und eine Fehlerseite ausgeben kann und den PHP-Code der den Rückgabewert prüft nicht x-hundert mal im Skript stehen hat.
                      Und dort habe ich einfach überprüft, ob die Anfage ein INSERT, UPDATE, DELETE etc. ist, und wenn ja, habe ich sie in eine Datei geschrieben. Vorrausgesetzt die Datenbanken sind bereits synchron (das kannst du ja einmal mit mysqldump sicherstellen) kannst du danach diese Datei einfach mal in regelmäßigen Abständen auf dem anderen Host einspielen.

                      Nur wie mache ich das einspielen am besten, geht das mit den paar Inserts und Updates, wenn ich die übertragen und auf dem Server in eine Datei geschrieben habe mit

                      mysql -uUSERNAME -p DATENBANKNAME < filename.sql

                      ???
                      Oder die Lösung daraus einen Array zu machen den ich dann Statement für Statement eintrage?

                      Ausserdem: MySQL unterstützt das was ich zuerst beschrieben habe auch von Haus aus. Wenn du also Zugriff auf die MySQL-Konfiguration hast, solltest du dir http://www.mysql.com/documentation/mysql/bychapter/manual_MySQL_Database_Administration.html#Update_log ansehen.

                      ich habe leider keinen Zugriff auf den MySQL Server direkt.

                      BTW: Ich hoffe du hast das Synchronisationsskript irgendwie geschützt. Nicht dass da jeder einfach so kommen und SQL-Kommandos ausführen kann.

                      Ist nicht über www erreichbar und Passwort-geschützt.

                      Grüße
                        Andreas

                      1. Moin

                        Jede Änderung - in einer extra Tabelle? Ist das nicht aufwendiger als einfach mit Timestamp und neu einer Tabelle wo bei jeder Synchronisation der Zeitpunkt festgehalten wird, udn alles was danach geändert wurde auswählen und daraus SQL-Statementsmachen.

                        Wo hab ich was von Tabelle geschrieben? Und nein, wenn du sowieso schon einen Wrapper hast ist das mitloggen einfacher (imho).

                        Was bitte ist ein Wrapper?

                        Na einfach eine Funktion um die eigentliche Funktion drumherum.

                        Ich hab beispielsweise häufig eine Funktion der Art

                        function querydb($sqlquery)
                        {
                         $ret = mysql_query($sqlquery);
                         if(!$ret) {
                          // Eine mehr oder minder melodramatische Fehlermeldung ausgeben und das Skript beenden
                         }
                         return $ret;
                        }

                        Und rufe in meinem Skript sonst nirgendwo mehr mysql_query() direkt auf, sondern nur noch querydb(). Dann kann ich mir im Skript nämlich die Fehlerüberprüfungen sparen.

                        Und da jetzt noch ein
                        if(preg_match('!^(INSERT|UPDATE|DELETE)!i', trim($sqlquery))) {
                         $fh = fopen("/eine/lustige/logdatei", "a");
                         if(!$fh) die("Konnte logdatei nicht öffnen"); else {
                          fwrite($fh, $sqlquery.";\n");
                          fclose($fh);
                         }
                        }

                        reinzufummeln ist vergleichsweise trivial im Gegensatz dazu eine SQL-Query auszubrüten die alle geänderten Datensätze sucht und sich dann auch noch zu überlegen, wie man daraus dann wieder eine Query baut.

                        mysql -uUSERNAME -p DATENBANKNAME < filename.sql

                        Wenn du so eine Datei gebaut hast, dann kannst du es mit diesem Kommando wieder einspielen. (Vorsicht: wenn du -p ohne Passwort dahinter angibst, wird er dich nach dem PW fragen)

                        Oder die Lösung daraus einen Array zu machen den ich dann Statement für Statement eintrage?

                        Genau, das mit dem Array würde ich dir empfehlen wenn du die Datensätze mit etwas in PHP selbstgeschriebenem wieder einspielen willst. Das sollte nicht sehr viel langsamer sein als der Aufruf des mysql-Client auf der Kommandozeile.

                        --
                        Henryk Plötz
                        Grüße aus Berlin

                        1. Hi!

                          Wo hab ich was von Tabelle geschrieben? Und nein, wenn du sowieso schon einen Wrapper hast ist das mitloggen einfacher (imho).

                          Ist nur die Frage was besser ist, wenn eh eine Verbindung zur DB besteht, warum das nicht in einer DB-Tabelle loggen? Da hätte ich dann den Vorteil, wieder mit nem Timestamp viel einfacher die neuen Abfragen zu bekommen! In der Logdatei ist das nicht ganz so einfach!

                          function querydb($sqlquery)
                          {
                          $ret = mysql_query($sqlquery);
                          if(!$ret) {
                            // Eine mehr oder minder melodramatische Fehlermeldung ausgeben und das Skript beenden
                          }
                          return $ret;
                          }

                          OK, das sieht vernünftig aus, hatte mir noch nie Gedanken drum gemacht aber das werde ich auch demnächst verwenden!

                          Und da jetzt noch ein
                          if(preg_match('!^(INSERT|UPDATE|DELETE)!i', trim($sqlquery))) {
                          $fh = fopen("/eine/lustige/logdatei", "a");
                          if(!$fh) die("Konnte logdatei nicht öffnen"); else {
                            fwrite($fh, $sqlquery.";\n");
                            fclose($fh);
                          }
                          }

                          Hatte ich oben schon gefragt, warum nicht in die DB mit einer Tabelle

                          ID | query | changed
                          ---+-------+--------
                             |       |

                          Da halt die Abfragen loggen und dann ggfs. nur abfragen

                          "SELECT query FROM log WHERE changed > $letztes_sync_datum ";

                          Aber das eigentliche Problem an dieser Lösung stellt die Nutzung der DB über andere Frontends dar. Zur Zeit werden Änderungen und neue Einträge nämlich teilweise von "Access" aus gemacht, und da ist sowas (auch möglich, aber) nicht ganz so leicht wie in PHP. Daher meine Überlegung, das ganze wie folgt zu machen:

                          Ich füge wie gesagt den Timestamp ein und logge lediglich das Synchronisationsdatum. So kann ich aus jeder Tabelle abfragen welche Datensätze nach diesem Zeitpunkt geändert wurden, und jedesmal zuerst die Felder einer Tabelle ermitteln, dann in einer Schleife mit den Feldnamen die Query zusammenbauen, je nachdem ob ID in Master existiert Update oder Insert.
                          Das Thema ist recht kompliziert, da sowohl Einträge in die Master als auch in die Client DB erfolgen können. Bei der Master(liegt im Internet) kommen aber nur Einträge über die Homepage(PHP, da wäre die obige Lösung mit loggen die Query das beste) , aber bei der Client können Datensätze auch über Access eingegeben und bearbeitet werden. Also brauche ich eine andere Lösung, sowas wie von mir beschrieben.

                          Ich hatte das aber schonmal hgroß und breit mit Sven Rautenberg diskutiertm und das war dann die Lösung am Ende, nur damals konnte ich das mit POST verschicken noch nicht. Jetzt will ich das makl richtig vernünftig machen, Problem ist das in master und Client gleichzeitig Einträge erfolgen können, das macht die Sache nochmal komplizierter. Ich löse das über verschiedene IDs in den gleichen Tabellen 2er Datenbanken, aber dadurch muß´ich die geloggte Query noch ändern.

                          Naja, ist wirklich Komplex die Sache, hab das auch nur geschrieben, damit Du verstehst das ich in die andere Richtung die Log-Funktion leider nicht verwenden kann, ist aber wirklich sehr viel einfacher!

                          Genau, das mit dem Array würde ich dir empfehlen wenn du die Datensätze mit etwas in PHP selbstgeschriebenem wieder einspielen willst. Das sollte nicht sehr viel langsamer sein als der Aufruf des mysql-Client auf der Kommandozeile.

                          Wirst recht haben, nur ist die erste Variante sehr viel einfacher!

                          Viele Grüße
                            Andreas