PHP Guardar sesión cuando se usa session_write_close();

7 minutos de lectura

Tengo una página donde hago un sondeo largo que debo usar al comienzo de esta página este

session_start();
session_write_close();

Porque :

para evitar escrituras simultáneas, solo un script puede operar en una sesión en cualquier momento

Entonces, si no lo hago y se está ejecutando el sondeo largo, el usuario no podrá cargar otra página.

Por lo tanto, es posible acceder a mis datos en sesión desde esta página de encuestas, pero en algún momento de mi secuencia de comandos, tengo que guardar mi sesión en el servidor porque hice algunos cambios en ella.

¿Cuál es la manera de hacerlo?

Eso será muy bueno, será una forma de hacer algo como

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

¡Pero session_write_open() no existe!

Gracias

  • Tardíamente, para futuros lectores, sugiero que usen session_set_save_handler() como una mejor práctica, ya que no implica soluciones alternativas, pero modifica la sesión como parece que los autores de PHP pretendían. He publicado un ejemplo de cómo hacer esto a continuación.

    –Kevin Nelson

    16 de enero de 2015 a las 23:11

Antes haces algún cambio en la sesión, llama session_start otra vez. Haz los cambios, y si sigues sin querer salir llama session_write_close una vez más. Puedes hacer esto tantas veces como quieras.

  • ¿Por qué me funciona sin ‘session_start’? Simplemente hago session_write_close al comienzo de mi secuencia de comandos, que contiene mucha lógica, incluidas las actualizaciones de la sesión, y todo funciona bien, la sesión se actualizó correctamente.

    – Víctor Bredihin

    19 de enero de 2017 a las 12:27

  • @VictorBredihin sin mirar el código real, no tengo idea de lo que podría estar sucediendo. Quizás podrías publicar una nueva pregunta.

    – Jon

    26 de enero de 2017 a las 11:35

Avatar de usuario de Armel Larcier
armel larcier

La solución anterior creará identificadores de sesión y cookies… No lo usaría tal como está:

La sesión se crea cada vez que llamas a session_start(). Si desea evitar múltiples cookies, escriba un mejor código. Múltiples session_start() especialmente para los mismos nombres en el mismo script parece una muy mala idea.

mira aquí : https://bugs.php.net/bug.php?id=38104

Estoy buscando una solución en este momento también y no puedo encontrar una. Estoy de acuerdo con los que dicen que esto es un “error”. Debería poder reabrir una sesión de php, pero como dijo session_write_open() no existe…

Encontré una solución en el hilo anterior. Implica enviar un encabezado especificando manualmente la cookie de identificación de la sesión después de procesar la solicitud. Afortunadamente, estoy trabajando con un controlador frontal hecho en casa que funciona para que ningún subcontrolador envíe datos por sí solo. En pocas palabras, funciona perfectamente en mi caso. Para usar esto, es posible que solo tengas que usar ob_start() y ob_get_clean(). Aquí está la línea mágica:

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

EDITAR: vea la respuesta de CMCDragonkai a continuación, ¿¡parece bueno!?

  • Ok, no sabía sobre las cookies, sí llamando a session_start(); no tiene mucho sentido, pero no tengo otra solución para solucionar el problema que yo sepa;) ¡Gracias por la información sobre el error!

    – Jerónimo Ansia

    5 de diciembre de 2012 a las 15:07

avatar de usuario de rinogo
rinogo

Las otras respuestas aquí presentan soluciones bastante buenas. Como mencionó @Jon, el truco consiste en volver a llamar a session_start() antes de realizar cambios. Luego, cuando haya terminado de hacer cambios, llame a session_write_close() nuevamente.

Como mencionó @Armel Larcier, el problema con esto es que PHP intenta generar nuevos encabezados y probablemente generará advertencias (por ejemplo, si ya ha escrito datos que no son de encabezado para el cliente). Por supuesto, simplemente puede prefijar session_start() con “@” (@session_start()), pero hay un mejor enfoque.

Otra pregunta de Stack Overflow proporcionada por @VolkerK revela la mejor respuesta:

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

Esto evita que PHP intente enviar los encabezados nuevamente. Incluso podría escribir una función de ayuda para envolver las funciones ini_set() para hacer esto un poco más conveniente:

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.
}

Pregunta/respuesta original relacionada con SO: https://stackoverflow.com/a/12315542/114558

  • Este es un buen hallazgo. los mejor la solución sería simplemente nunca generar contenido en fragmentos (siempre funcionó para mí), en cuyo caso obtiene los encabezados múltiples pero son inofensivos. Sin embargo, eso puede no ser posible si, por ejemplo, ha heredado algún código, por lo que esta solución tiene un caso de uso válido.

    – Jon

    30 de julio de 2013 a las 9:02

  • Esto parece muy complicado cuando puedes usar session_set_save_handler y evitar el problema por completo y hacer que haga lo que quieras.

    –Kevin Nelson

    16 de enero de 2015 a las 23:08

Avatar de usuario de Kevin Nelson
kevin nelson

Todas las respuestas aquí parecen estar diciendo que se usen los métodos de sesión de formas en las que claramente no estaban destinados a ser usados… a saber, llamando session_start() mas de una vez.

El sitio web de PHP ofrece un ejemplo de implementación de SessionHandlerInterface que funcionará igual que las sesiones existentes pero sin bloquear el archivo. Simplemente implementar su interfaz de ejemplo solucionó mi problema de bloqueo para permitir conexiones simultáneas en la misma sesión sin limitar mi capacidad de agregar vars a la sesión. Para evitar algunas condiciones de carrera, dado que la sesión de la aplicación no es completamente apátrida, tuve que crear una forma de guardar la sesión a mitad de la solicitud sin cerrarla para que los cambios importantes pudieran guardarse inmediatamente después del cambio y las variables de sesión menos importantes pudieran simplemente guardarse. al final de la solicitud. Vea el siguiente ejemplo de uso:

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';

Si reemplazas eso Session::start() con session_start() y Session::save() con session_write_close(), notará que las solicitudes posteriores nunca imprimirán la tercera variable… se perderá. Sin embargo, al utilizar SessionHandler (a continuación), no se pierde ningún dato.

La implementación de OOP requiere PHP 5.4+. Sin embargo, puede proporcionar métodos de devolución de llamada individuales en versiones anteriores de PHP. ver documentos.

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;
        }
    }

Avatar de usuario de CMCDragonkai
CMCDragonkai

Después de probar el trabajo de Armel Larcier. Aquí está mi propuesta de solución a este problema:

    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();

Esto conservará las cookies que se crearon antes de trabajar con las sesiones.

Por cierto, es posible que desee registrar el código anterior como función para register_shutdown_function. Asegúrese de ejecutar ob_start() antes de la función y ob_flush() dentro de la función.

¿Ha sido útil esta solución?