2012-04-06 16 views
16

Ho una pagina in cui faccio una lunga interrogazione che ho da utilizzare al inizio di questa pagina QuestoPHP Salva sessione quando si utilizza session_write_close();

session_start(); 
session_write_close(); 

Perché:

per prevenire scritture contemporanee solo uno script può operare su una sessione in qualsiasi momento

Quindi se non lo faccio e il polling lungo è in esecuzione, l'utente non sarà in grado di caricare un'altra pagina.

Quindi l'accesso ai miei dati in sessione da questa pagina di polling è possibile ma a un certo punto del mio script devo salvare la mia sessione sul server perché ho apportato qualche modifica in esso.

Qual è il modo per farlo?

che sarà molto bello sarà un modo per fare qualcosa di simile

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

Ma la session_write_open() non esiste!

Grazie

+0

ritardo, per i lettori futuri, sto suggerendo che si utilizza 'session_set_save_handler()' come più di un best-practice in quanto non comporta alcun work-around, ma modifica la sessione come gli autori di PHP sembra aver voluto. Ho pubblicato un esempio su come farlo in seguito. –

risposta

13

Prima di effettuare qualche cambiamento alla sessione, chiamare session_start di nuovo. Apporta le modifiche e, se ancora non vuoi uscire, chiama ancora session_write_close. Puoi farlo tutte le volte che vuoi.

+0

perché funziona per me senza 'session_start'? Ho appena creato session_write_close all'inizio del mio script, contenente un sacco di logica inclusi gli aggiornamenti della sessione, e tutto funziona bene, la sessione è stata aggiornata correttamente –

+0

@VictorBredihin senza guardare il codice reale Non ho idea di cosa potrebbe accadere.Forse potresti pubblicare una nuova domanda. – Jon

10

La soluzione precedente creerà un ID di sessione e biscotti ... io non lo uso come è:

sessione viene creato ogni volta che si chiama session_start(). Se si desidera per evitare più cookie, scrivere codice migliore. Multiple session_start() in particolare per gli stessi nomi nello stesso script sembra davvero una pessima idea di .

vedere qui: https://bugs.php.net/bug.php?id=38104

Sto cercando una soluzione in questo momento troppo e non riesco a trovare uno. Sono d'accordo con chi dice che questo è un "bug". Dovresti essere in grado di riaprire una sessione php, ma come hai detto session_write_open() non esiste ...

Ho trovato una soluzione alternativa nel thread precedente. Implica l'invio di un'intestazione che specifica manualmente il cookie dell'ID di sessione dopo l'elaborazione della richiesta. Fortunatamente sto lavorando con un Front Controller preparato in casa che funziona in modo che nessun sub-controller invii mai dati da solo. In poche parole, funziona perfettamente nel mio caso. Per utilizzarlo potresti dover semplicemente usare ob_start() e ob_get_clean(). Ecco la linea magica:

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

MODIFICA: vedere la risposta di CMCDragonkai qui sotto, sembra buono !?

+0

Ok non sapevo nulla dei cookies, si chiama session_start(); non ha molto senso ma non ho altra soluzione per risolvere il problema di cui sono a conoscenza;) Grazie per le informazioni sull'errore! –

+0

Potrebbe essere solo una funzionalità che manca attraverso ... –

+1

Trovato un workaround! risposta modificata! –

3

Dopo aver testato il lavoro di Armel Larcier.Ecco la mia soluzione proposta a questo 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(); 

Ciò manterrà qualsiasi cookie creato prima di lavorare con le sessioni.

BTW, è possibile che si desideri registrare il codice sopra come funzione per register_shutdown_function. Assicurati di eseguire ob_start() prima della funzione e ob_flush() all'interno della funzione.

4

Le altre risposte qui presentano soluzioni piuttosto buone. Come accennato da @Jon, il trucco è richiamare session_start() nuovamente prima di voler apportare modifiche. Quindi, quando hai terminato le modifiche, chiama di nuovo session_write_close().

Come menzionato da @Armel Larcier, il problema con questo è che PHP tenta di generare nuove intestazioni e probabilmente genererà avvisi (ad esempio se hai già scritto dati non di intestazione sul client). Certo, puoi semplicemente aggiungere il prefisso session_start() con "@" (@session_start()), ma c'è un approccio migliore.

Un'altra domanda Stack Overflow, fornito dal @VolkerK rivela la migliore risposta:

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 

questo modo si evita di PHP di tentare di inviare nuovamente le intestazioni. Si potrebbe anche scrivere una funzione di supporto per avvolgere l'ini_set() per rendere questo un po 'più comodo:

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

relative SO originale domanda/risposta: https://stackoverflow.com/a/12315542/114558

+1

Questa è una buona scoperta. La soluzione migliore sarebbe semplicemente non pubblicare mai il contenuto in blocchi (ha sempre funzionato per me), nel qual caso ottieni le intestazioni multiple ma sono innocue. Tuttavia ciò potrebbe non essere possibile se ad es. hai ereditato del codice, quindi questa soluzione alternativa ha un caso d'uso valido. – Jon

+0

Questo sembra molto complicato quando puoi usare session_set_save_handler ed evitare del tutto il problema e fargli fare quello che vuoi. –

3

Tutte le risposte qui sembrano voler dire utilizzare i metodi di sessione in modi che chiaramente non erano destinati ad essere utilizzati ... ovvero chiamando lo session_start() più di una volta.

Il sito Web PHP offre un'implementazione di SessionHandlerInterface di esempio che funzionerà esattamente come le sessioni esistenti ma senza bloccare il file. L'implementazione dell'interfaccia di esempio ha risolto il problema di blocco per consentire connessioni simultanee nella stessa sessione senza limitare la possibilità di aggiungere vars alla sessione. Per evitare alcune condizioni di gara, dal momento che la sessione dell'app non è completamente stateless, ho dovuto fare in modo di salvare la sessione a metà richiesta senza chiuderla, in modo che le modifiche importanti potessero salvare immediatamente dopo la modifica e meno importanti vars delle sessioni potevano semplicemente salvare alla fine della richiesta. Vedere l'esempio di seguito per l'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'; 

Se si sostituisce che Session::start() con session_start() e Session::save() con session_write_close(), si noterà che le richieste successive saranno mai stampare la terza variabile ... sarà persa. Tuttavia, utilizzando SessionHandler (sotto), nessun dato viene perso.

L'implementazione OOP richiede PHP 5.4+. Tuttavia, puoi fornire metodi di callback individuali nelle versioni precedenti di 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; 
     } 
    } 
Problemi correlati