PHP Sitzung speichern bei Verwendung von session_write_close();

Lesezeit: 7 Minuten

Ich habe eine Seite, auf der ich eine lange Umfrage mache, die ich am Anfang dieser Seite verwenden muss

session_start();
session_write_close();

Da :

Um gleichzeitige Schreibvorgänge zu verhindern, kann immer nur ein Skript in einer Sitzung ausgeführt werden

Wenn ich dies nicht tue und die lange Abfrage läuft, kann der Benutzer keine weitere Seite laden.

Der Zugriff auf meine Sitzungsdaten von dieser Abfrageseite aus ist also möglich, aber irgendwann in meinem Skript muss ich meine Sitzung wieder auf dem Server speichern, weil ich einige Änderungen daran vorgenommen habe.

Wie geht das?

Das wird sehr schön sein, es wird eine Möglichkeit sein, so etwas zu tun

session_write_open();
//do stuff
session_write_close();

Aber das session_write_open() existiert nicht!

Vielen Dank

  • Verspätet, für zukünftige Leser, schlage ich vor, dass Sie verwenden session_set_save_handler() eher als bewährte Methode, da es keine Problemumgehungen beinhaltet, sondern die Sitzung so modifiziert, wie es die PHP-Autoren anscheinend beabsichtigt haben. Ein Beispiel dafür habe ich unten gepostet.

    – Kevin Nelson

    16. Januar 2015 um 23:11 Uhr

Vor Wenn Sie eine Änderung an der Sitzung vornehmen, rufen Sie an session_start wieder. Nehmen Sie die Änderungen vor, und wenn Sie den Anruf immer noch nicht beenden möchten session_write_close einmal mehr. Sie können dies so oft tun, wie Sie möchten.

  • warum funktioniert es bei mir ohne ‘session_start’? Ich mache einfach session_write_close ganz am Anfang meines Skripts, das viel Logik enthält, einschließlich Aktualisierungen der Sitzung, und alles funktioniert gut, die Sitzung wird korrekt aktualisiert

    – Victor Bredihin

    19. Januar 2017 um 12:27 Uhr

  • @VictorBredihin ohne mir den eigentlichen Code anzusehen, habe ich keine Ahnung, was passieren könnte. Vielleicht könntest du eine neue Frage stellen.

    – Jon

    26. Januar 2017 um 11:35 Uhr

Benutzeravatar von Armel Larcier
Armel Larcier

Die vorherige Lösung erstellt Sitzungs-IDs und Cookies … Ich würde sie nicht so verwenden, wie sie ist:

Die Sitzung wird jedes Mal erstellt, wenn Sie session_start() aufrufen. Wenn Sie mehrere Cookies vermeiden möchten, schreiben Sie besseren Code. Mehrere session_start(), insbesondere für dieselben Namen im selben Skript, scheint eine wirklich schlechte Idee zu sein.

siehe hier : https://bugs.php.net/bug.php?id=38104

Ich suche auch gerade nach einer Lösung und finde keine. Ich stimme denen zu, die sagen, dass dies ein “Fehler” ist. Sie sollten in der Lage sein, eine PHP-Sitzung erneut zu öffnen, aber wie Sie sagten session_write_open() ist nicht vorhanden…

Im obigen Thread habe ich einen Workaround gefunden. Es beinhaltet das Senden eines Headers, der das Cookie der Sitzungs-ID nach der Verarbeitung der Anfrage manuell angibt. Glücklicherweise arbeite ich mit einem selbstgebauten Front Controller, der so funktioniert, dass kein Sub-Controller jemals alleine Daten senden wird. Kurz gesagt, bei mir funktioniert es einwandfrei. Um dies zu verwenden, müssen Sie möglicherweise nur verwenden ob_start() und ob_get_clean(). Hier ist die magische Linie:

if (SID) header('Set-Cookie: '.SID.'; path=/', true);

BEARBEITEN: siehe CMCDragonkais Antwort unten, scheint gut zu sein!?

  • Ok, ich wusste nichts über die Cookies, ja Aufruf von session_start(); macht nicht viel Sinn, aber ich habe keine andere Lösung, um das Problem zu beheben, das mir bekannt ist;) Danke für die Info über den Fehler!

    – Hieronymus Ansia

    5. Dezember 2012 um 15:07 Uhr

Benutzeravatar von rinogo
Rinogo

Die anderen Antworten hier präsentieren ziemlich gute Lösungen. Wie von @Jon erwähnt, besteht der Trick darin, session_start() erneut aufzurufen, bevor Sie Änderungen vornehmen möchten. Wenn Sie dann mit den Änderungen fertig sind, rufen Sie session_write_close() erneut auf.

Wie von @Armel Larcier erwähnt, besteht das Problem dabei darin, dass PHP versucht, neue Header zu generieren, und wahrscheinlich Warnungen generiert (z. B. wenn Sie bereits Nicht-Header-Daten an den Client geschrieben haben). Natürlich können Sie session_start() einfach ein „@“ voranstellen (@session_start()), aber es gibt einen besseren Ansatz.

Eine weitere Stack Overflow-Frage, bereitgestellt von @VolkerK, zeigt die beste Antwort:

session_start(); // first session_start
...
session_write_close();
...

ini_set('session.use_only_cookies', false);
ini_set('session.use_cookies', false);
//ini_set('session.use_trans_sid', false); //May be necessary in some situations
ini_set('session.cache_limiter', null);
session_start(); // second session_start

Dies verhindert, dass PHP versucht, die Header erneut zu senden. Sie könnten sogar eine Hilfsfunktion schreiben, um die ini_set () -Funktionen einzuschließen, um dies etwas bequemer zu machen:

function session_reopen() {
    ini_set('session.use_only_cookies', false);
    ini_set('session.use_cookies', false);
    //ini_set('session.use_trans_sid', false); //May be necessary in some situations
    ini_set('session.cache_limiter', null);
    session_start(); //Reopen the (previously closed) session for writing.
}

Ursprüngliche verwandte SO-Frage/Antwort: https://stackoverflow.com/a/12315542/114558

  • Das ist ein guter Fund. Das Beste Die Lösung wäre, Inhalte einfach nie in Blöcken auszugeben (hat bei mir immer funktioniert). In diesem Fall erhalten Sie zwar mehrere Header, aber sie sind harmlos. Dies ist jedoch möglicherweise nicht möglich, wenn Sie beispielsweise Code geerbt haben, sodass diese Problemumgehung einen gültigen Anwendungsfall hat.

    – Jon

    30. Juli 2013 um 9:02 Uhr

  • Dies scheint sehr kompliziert, wenn Sie einfach session_set_save_handler verwenden und das Problem vollständig vermeiden und es tun lassen können, was Sie wollen.

    – Kevin Nelson

    16. Januar 2015 um 23:08 Uhr

Benutzeravatar von Kevin Nelson
Kevin Nelson

Alle Antworten hier scheinen zu sagen, dass die Sitzungsmethoden auf eine Weise verwendet werden sollen, für die sie eindeutig nicht vorgesehen waren … nämlich anrufen session_start() mehr als einmal.

Die PHP-Website bietet ein Beispiel für eine SessionHandlerInterface-Implementierung, die genau wie bestehende Sitzungen funktioniert, jedoch ohne die Datei zu sperren. Durch die Implementierung ihrer Beispielschnittstelle wurde mein Sperrproblem behoben, um gleichzeitige Verbindungen in derselben Sitzung zu ermöglichen, ohne meine Fähigkeit einzuschränken, Variablen zur Sitzung hinzuzufügen. Da die Sitzung der App nicht vollständig zustandslos ist, musste ich eine Möglichkeit finden, um einige Race-Bedingungen zu verhindern, um die Sitzung während der Anfrage zu speichern, ohne sie zu schließen, damit wichtige Änderungen sofort nach der Änderung gespeichert werden können und weniger wichtige Sitzungsvariablen einfach gespeichert werden können am Ende der Anfrage. Siehe das folgende Beispiel für die Verwendung:

Session::start();
echo("<pre>Vars Stored in Session Were:\n");print_r($_SESSION);echo("</pre>");

$_SESSION['one']    = 'one';
$_SESSION['two']    = 'two';
//save won't close session and subsequent request will show 'three'
Session::save(); 
$_SESSION['three']  = 'three';

Wenn Sie das ersetzen Session::start() mit session_start() und Session::save() mit session_write_close(), werden Sie feststellen, dass nachfolgende Anfragen die dritte Variable niemals ausgeben … sie geht verloren. Mit dem SessionHandler (unten) gehen jedoch keine Daten verloren.

Die OOP-Implementierung erfordert PHP 5.4+. In älteren PHP-Versionen können Sie jedoch individuelle Callback-Methoden bereitstellen. Siehe Dokumente.

namespace {
    class Session implements SessionHandlerInterface {
        /** @var Session */
        private static $_instance;
        private $savePath;

        public static function start() {
            if( empty(self::$_instance) ) {
                self::$_instance = new self();
                session_set_save_handler(self::$_instance,true);
                session_start();
            }
        }
        public static function save() {
            if( empty(self::$_instance) ) {
                throw new \Exception("You cannot save a session before starting the session");
            }
            self::$_instance->write(session_id(),session_encode());
        }
        public function open($savePath, $sessionName) {
            $this->savePath = $savePath;
            if (!is_dir($this->savePath)) {
                mkdir($this->savePath, 0777);
            }

            return true;
        }
        public function close() {
            return true;
        }
        public function read($id) {
            return (string)@file_get_contents("$this->savePath/sess_$id");
        }
        public function write($id, $data) {
            return file_put_contents("$this->savePath/sess_$id", $data) === false ? false : true;
        }
        public function destroy($id) {
            $file = "$this->savePath/sess_$id";
            if (file_exists($file)) {
                unlink($file);
            }

            return true;
        }
        public function gc($maxlifetime) {
            foreach (glob("$this->savePath/sess_*") as $file) {
                if (filemtime($file) + $maxlifetime < time() && file_exists($file)) {
                    unlink($file);
                }
            }

            return true;
        }
    }

Benutzeravatar von CMCDragonkai
CMCDragonkai

Nachdem ich die Arbeit von Armel Larcier ausprobiert hatte. Hier ist mein Lösungsvorschlag für dieses Problem:

    ob_start();

    session_start();
    session_write_close();

    session_start();
    session_write_close();

    session_start();
    session_write_close();

    session_start();
    session_write_close();

    if(SID){

        $headers =  array_unique(headers_list());   

        $cookie_strings = array();

        foreach($headers as $header){
            if(preg_match('/^Set-Cookie: (.+)/', $header, $matches)){
                $cookie_strings[] = $matches[1];
            }
        }

        header_remove('Set-Cookie');

        foreach($cookie_strings as $cookie){
            header('Set-Cookie: ' . $cookie, false);
        }

    }

    ob_flush();

Dadurch werden alle Cookies beibehalten, die vor der Arbeit mit Sitzungen erstellt wurden.

Übrigens, vielleicht möchten Sie den obigen Code als Funktion für register_shutdown_function registrieren. Stellen Sie sicher, dass Sie ob_start() vor der Funktion und ob_flush() innerhalb der Funktion ausführen.

1433890cookie-checkPHP Sitzung speichern bei Verwendung von session_write_close();

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy