2012-04-06 26 views
16

tengo una página donde hago un sondeo largo que he de utilizar en el inicio de esta página estaPHP Save Session cuando se usa session_write_close();

session_start(); 
session_write_close(); 

Porque:

para evitar escrituras concurrentes sólo un script puede operar en una sesión en cualquier momento

Así que si no lo hago y el sondeo largo se está ejecutando, 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 sondeo, pero en algún punto de mi script debo guardar mi sesión nuevamente en el servidor porque realicé algunos cambios en ella.

¿Cuál es la manera de hacerlo?

que será muy agradable que va a ser una manera de hacer algo como

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

Pero no existe la session_write_open()!

Gracias

+0

Con retraso, para los futuros lectores, estoy sugiriendo que utilice 'session_set_save_handler()' como más de una de las mejores prácticas, ya que no implica ningún soluciones temporales, pero modifica la sesión ya que los autores de PHP parece haber querido. He publicado un ejemplo de cómo hacer esto a continuación. –

Respuesta

13

Antes de hacer algún cambio a la sesión, llame session_start nuevo. Realice los cambios y, si aún no desea salir, llame de nuevo al session_write_close. Puedes hacer esto tantas veces como quieras.

+0

¿Por qué me funciona sin 'session_start'? Acabo de hacer session_write_close al comienzo de mi script, que contiene mucha lógica, incluidas las actualizaciones de la sesión, y todo funciona bien, la sesión se actualizó correctamente –

+0

@VictorBredihin sin mirar el código real. No tengo idea de qué podría estar pasando.Tal vez podrías publicar una nueva pregunta. – Jon

10

La solución anterior se creará un ID de sesión y cookies ... yo no lo uso como es:

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

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

Busco a una solución en este momento demasiado y no puedo encontrar uno. Estoy de acuerdo con aquellos que dicen que esto es un "error". Debería poder volver a abrir una sesión 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 la identificación de sesión después de procesar la solicitud. Afortunadamente, estoy trabajando con un Controlador Frontal fabricado en casa que funciona para que ningún subcontrolador alguna vez envíe datos por sí mismo. En pocas palabras, funciona perfectamente en mi caso. Para usar esto, simplemente debe 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?

+0

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 del que soy consciente;) ¡Gracias por la información sobre el error! –

+0

Puede ser solo una característica que falta a través de ... –

+1

¡Encontró una solución! respuesta editada! –

3

Después de probar el trabajo de Armel Larcier.Aquí está mi solución propuesta para 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.

4

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

Como lo menciona @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 desbordamiento de pila, 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 impide PHP desde el intento de enviar las cabeceras de nuevo. Incluso se podría escribir una función auxiliar para envolver el 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. 
} 

relacionado tan original pregunta/respuesta: https://stackoverflow.com/a/12315542/114558

+1

Este es un buen descubrimiento. La * mejor * solución sería simplemente nunca generar contenido en fragmentos (siempre funcionó para mí), en cuyo caso obtendrá los múltiples encabezados, 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

+0

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

3

Todas las respuestas aquí parecen estar diciendo utilizar los métodos de sesión de forma que claramente no estaban destinados a ser utilizados ... es decir, llamar al session_start() más de una vez.

El sitio web de PHP ofrece una implementación SessionHandlerInterface de ejemplo que funcionará igual que las sesiones existentes pero sin bloquear el archivo. La implementación de su interfaz de ejemplo resolvió mi problema de bloqueo para permitir conexiones simultáneas en la misma sesión sin limitar mi capacidad para agregar vars a la sesión. Para evitar algunas condiciones de carrera, dado que la sesión de la aplicación no es totalmente apátrida, tuve que hacer 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 sesiones menos importantes pudieran salvar al final de la solicitud. Véase el siguiente ejemplo para el 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 reemplaza con Session::start() que session_start() y Session::save() con session_write_close(), se dará cuenta de que las solicitudes posteriores no se imprimirá la tercera variable ... se perderá. Sin embargo, al usar SessionHandler (abajo), no se pierden datos.

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. See docs.

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