2012-04-06 50 views
16

Tôi đã một trang mà tôi làm một điểm bỏ phiếu dài tôi đã sử dụng tại bắt đầu của trang này nàyPHP Lưu phiên khi sử dụng session_write_close();

session_start(); 
session_write_close(); 

Bởi vì:

để ngăn chặn viết đồng thời chỉ có một kịch bản có thể hoạt động trên một phiên bất cứ lúc nào

Vì vậy nếu tôi không và bỏ phiếu dài đang chạy người dùng sẽ không thể tải trang khác.

Vì vậy, truy cập vào dữ liệu của tôi trong phiên từ trang bỏ phiếu này là có thể nhưng tại một số điểm trong tập lệnh của tôi tôi đã lưu phiên của tôi trở lại máy chủ vì tôi đã thực hiện một số thay đổi trong đó.

Cách để làm điều đó là gì?

Đó sẽ là rất đẹp nó sẽ là một cách để làm điều gì đó như

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

Nhưng session_write_open() không tồn tại!

Cảm ơn

+0

Tương tự, đối với người đọc trong tương lai, tôi cho rằng bạn sử dụng 'session_set_save_handler()' để thực hành tốt nhất vì nó không liên quan đến bất kỳ công việc nào, nhưng thay đổi phiên làm tác giả PHP dường như đã có ý định. Tôi đã đăng một ví dụ về cách làm điều này dưới đây. –

Trả lời

13

Trước bạn thực hiện một số thay đổi phiên, gọi session_start một lần nữa. Thực hiện thay đổi và nếu bạn vẫn không muốn thoát khỏi cuộc gọi session_write_close một lần nữa. Bạn có thể làm điều này bao nhiêu lần tùy thích.

+0

tại sao nó làm việc cho tôi mà không có 'session_start'? Tôi chỉ thực hiện session_write_close ở phần đầu của kịch bản, chứa rất nhiều logic bao gồm cập nhật phiên, và mọi thứ hoạt động tốt, phiên cập nhật chính xác –

+0

@VictorBredihin mà không nhìn vào mã thực tế mà tôi không biết điều gì có thể xảy ra.Có lẽ bạn có thể đăng câu hỏi mới. – Jon

10

Các giải pháp trước đó sẽ tạo ra một id phiên và cookie ... Tôi sẽ không sử dụng nó như là:

Session được tạo ra mỗi khi bạn gọi session_start(). Nếu bạn muốn để tránh nhiều cookie, hãy viết mã tốt hơn. Nhiều session_start() đặc biệt là cho cùng tên trong cùng một tập lệnh có vẻ như thực sự là ý tưởng tồi của .

thấy ở đây: https://bugs.php.net/bug.php?id=38104

Tôi đang tìm kiếm một giải pháp ngay bây giờ quá và tôi không thể tìm thấy một. Tôi đồng ý với những người nói đây là một "lỗi". Bạn sẽ có thể mở lại phiên php, nhưng như bạn đã nói session_write_open() không tồn tại ...

Tôi tìm thấy cách giải quyết khác trong chuỗi trên. Nó liên quan đến việc gửi một tiêu đề xác định thủ công cookie của session id sau khi xử lý yêu cầu. May mắn là tôi đang làm việc với một bộ điều khiển mặt trước do nhà sản xuất làm việc để không có bộ điều khiển phụ nào tự gửi dữ liệu. Tóm lại, nó hoạt động hoàn hảo trong trường hợp của tôi. Để sử dụng điều này, bạn có thể chỉ cần sử dụng ob_start()ob_get_clean(). Đây là dòng ma thuật:

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

EDIT: xem câu trả lời của CMCDragonkai bên dưới, có vẻ tốt!?

+0

Ok tôi không biết về các cookie, có gọi session_start(); không có ý nghĩa nhiều nhưng tôi không có giải pháp khác để khắc phục sự cố mà tôi biết;) Cảm ơn bạn đã biết thông tin về lỗi này! –

+0

Nó có thể chỉ là một tính năng bị thiếu thông qua ... –

+1

Tìm thấy một giải pháp thay thế! câu trả lời đã chỉnh sửa! –

3

Sau khi thử nghiệm công việc của Armel Larcier.Đây là giải pháp được đề xuất của tôi cho vấn đề này:

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

Điều này sẽ giữ lại bất kỳ cookie nào được tạo trước khi làm việc với phiên.

BTW, bạn có thể muốn đăng ký mã ở trên làm chức năng cho register_shutdown_function. Đảm bảo chạy ob_start() trước hàm và ob_flush() bên trong hàm.

4

Các câu trả lời khác ở đây trình bày các giải pháp khá tốt. Như đã đề cập bởi @Jon, mẹo là gọi session_start() một lần nữa trước khi bạn muốn thực hiện thay đổi. Sau đó, khi bạn thực hiện xong các thay đổi, hãy gọi lại session_write_close().

Như đã đề cập bởi @Armel Larcier, vấn đề với điều này là PHP cố gắng tạo tiêu đề mới và có khả năng sẽ tạo cảnh báo (ví dụ: nếu bạn đã ghi dữ liệu không có tiêu đề cho ứng dụng khách). Tất nhiên, bạn có thể chỉ đơn giản là tiền tố session_start() với "@" (@session_start()), nhưng có một cách tiếp cận tốt hơn.

Một câu hỏi Stack Overflow, được cung cấp bởi @VolkerK tiết lộ câu trả lời tốt nhất:

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 

Điều này ngăn cản PHP từ cố gắng để gửi các tiêu đề một lần nữa. Bạn thậm chí có thể viết một hàm helper để quấn ini_set() chức năng để làm điều này thuận tiện hơn một chút:

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

gốc liên quan SO câu hỏi/câu trả lời: https://stackoverflow.com/a/12315542/114558

+1

Đây là một tìm kiếm tốt. Giải pháp * tốt nhất sẽ đơn giản là không bao giờ xuất nội dung theo từng khối (luôn luôn làm việc cho tôi) trong trường hợp này bạn nhận được nhiều tiêu đề nhưng chúng vô hại. Tuy nhiên, điều đó có thể không thực hiện được nếu ví dụ: bạn đã thừa hưởng một số mã, vì vậy cách giải quyết này có một trường hợp sử dụng hợp lệ. – Jon

+0

Điều này có vẻ rất phức tạp khi bạn chỉ có thể sử dụng session_set_save_handler và tránh vấn đề hoàn toàn và có nó làm những gì bạn muốn. –

3

Tất cả các câu trả lời ở đây dường như được nói để sử dụng các phương pháp phiên theo cách mà chúng rõ ràng không có ý định sử dụng ... cụ thể là gọi số session_start() nhiều lần.

Trang web PHP cung cấp ví dụ về thực thi SessionHandlerInterface sẽ hoạt động giống như các phiên hiện có nhưng không khóa tệp. Chỉ cần triển khai giao diện mẫu của họ đã khắc phục sự cố khóa của tôi để cho phép các kết nối đồng thời trên cùng một phiên mà không giới hạn khả năng thêm các vars của tôi vào phiên. Để ngăn chặn một số điều kiện chủng tộc, vì phiên của ứng dụng không hoàn toàn phi trạng thái, tôi phải thực hiện một cách để lưu phiên giữa yêu cầu mà không đóng nó để thay đổi quan trọng có thể lưu ngay lập tức sau khi thay đổi và ít phiên quan trọng hơn. ở cuối yêu cầu. Xem ví dụ dưới đây để sử dụng:

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

Nếu bạn thay thế mà Session::start() với session_start()Session::save() với session_write_close(), bạn sẽ nhận thấy rằng yêu cầu tiếp theo sẽ không bao giờ in ra biến thứ ba ... nó sẽ bị mất. Tuy nhiên, bằng cách sử dụng SessionHandler (bên dưới), không có dữ liệu bị mất.

Việc triển khai OOP yêu cầu PHP 5.4+. Tuy nhiên, bạn có thể cung cấp các phương thức gọi lại cá nhân trong các phiên bản PHP cũ hơn. 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; 
     } 
    } 
Các vấn đề liên quan