前三篇簡單的總結了下會話控制和文件操作,這一篇說說會話控制的自定義處理方式。既然知道了文件的基本讀寫,而且在會話控制中,也有人提到,session數據可以保存到緩存或數據庫中,實際上當然不會是直接利用php的session處理機制,將所有用戶的session信息保存報一個文件中,訪問量大、信息數據多、無法共享等等問題可能會出現,因此,我們需要自定義會話控制。在實現自定義會話控制前,就要知道php本身是如何來做會話管理的,。這里,就簡單的實現它被保存到自定義目錄下的文件里邊,那么緩存或數據庫就很明顯了。
首先看幾個命令再說,這將有利于我們理解它的機制。打開php配置文件,找到一些session.打頭的命令項:
session.save_handler:它的值默認是files,看它的英文解釋可知,如果它的值是files,那么就是使用php本身的機制來處理,它的信息會保存到session.save_path所指定的地方,如果是想自定義session處理的話,需要將users賦給它。
session.save_path:在win版的php配置文件中,它默認是被注釋掉的,即在配置文件中并沒有說它的值是多少,但總得有個地方放session數據是不,win下,這個地方是C:/Windows/Temp,進去即可找到(如下,如果你練習過session存儲的程序的話)以sess_為前綴、后面跟一長串長度一樣的字符串、沒有擴展名的文件,它就是默認的session數據了,里面存放的是序列化(也有的說是串行化)后的數據(如user|s:4:"Jack";,user是變量名,"Jack"是變量值,s表示變量類型string,冒號后邊的4表示長度,最后的分號以結束這個變量,這只是簡單的數值型,還有對象等等也可序列化)。關于save_path,他還有一些配置方式,比如按數字分級,更方便大數量的數據文件存放,具體不細說。
弄清楚它的處理及存放后,其實就應該冒出這樣一個問題:我們平時又沒動session數據,如果它就這么一直放著存著,那不會把磁盤占滿嗎?難道要專門寫一個腳本定時來刪除它們?幸運的是php提供了session的自動回收機制,即垃圾回收。了解回收機制還是需穴ky"http://www.it165.net/qq/" target="_blank" html' target='_blank'>class="keylink">qq/tMP8we7P7qGjPC9wPgo8cD4mbmJzcDsgJm5ic3A7ICZuYnNwOyBzZXNzaW9uLmdjX3Byb2JhYmlsaXR5o7pnY8rHZ2FyYmFnZSBjb2xsZWN0aW9uu9jK1bzy0LSjrNLiy7zJz8rHuMXCyqGiv8nE3NDUPC9wPgo8cD4mbmJzcDsgJm5ic3A7ICZuYnNwOyBzZXNzaW9uLmdjX2Rpdmlzb3KjutLiy7zJz8rHs/3K/aOs1eLBvc/u0qrX27rPxvDAtL+0o6zU2tLR09C1xHNlc3Npb27OxLz+wO+x36OsttTT2rn9xtq1xKOs0tRnY19wcm9iYWJpbGl0eS9nY19kaXZpc29ytcS4xcLKwLTRodTxxuTW0NK7uPbJvrP9o6zU2sO/tM5zZXNzaW9us/XKvLuvyrG2vLvh1eLR+df2oaPL+dLUvNnJ6GdjX2Rpdmlzb3LKxzEwMDCjrGdjX3Byb2JhYmlsaXR5yscxo6y2+LTLyrHHobrD09AxMDAwuPa5/cbatcRzZXNzaW9uzsS8/qOsxMfDtNKysrvKx8irsr/JvrP9o6y2+MrHy+a7+sz0xuTW0NK7uPbJvrP9o6y8tDEvMTAwMLXEuMXCyqGjPC9wPgo8cD4mbmJzcDsgJm5ic3A7ICZuYnNwOyBzZXNzaW9uLmdjX21heGxpZmV0aW1lo7pzZXNzaW9utcTT0NCnyrG85KOs1rvT0LOsuf3By9XiuPbT0NCnyrG85LXEc2Vzc2lvbs7EvP6yxbvhsbvB0MjrcGhwwKy7+LvYytW1xLe2zqfE2qGjPC9wPgo8cD4mbmJzcDsgJm5ic3A7ICZuYnNwOyDBy73iwcvV4tCp17yxuNaqyra686OsvdPPwsC0vs29+Mjrtb1zZXNzaW9utcTX1Lao0uW0psDtuf2zzMHLoaM8L3A+CjxwPiZuYnNwOyAmbmJzcDsgJm5ic3A7ILK7udzKx7GjtObU2m1lbWNhY2hlu7nKx8r9vt2/4qOsu7nKx8bky/u3vcq9o6zX7rP1tcTUrcDttrzSu9H5o6xwaHDU2rSmwO1zZXNzaW9uyv2+3cqxyrnTw7XEysfSu7j2uq/K/aO6c2Vzc2lvbl9zZXRfc2F2ZV9oYW5kbGVyoaPG5Nfu0MK1xNSt0M3Kx6O6PC9wPgo8cD4mbmJzcDsgJm5ic3A7ICZuYnNwOyBib29sIHNlc3Npb25fc2V0X3NhdmVfaGFuZGxlciAoIGNhbGxhYmxlICRvcGVuICwgY2FsbGFibGUgJGNsb3NlICwgY2FsbGFibGUmbmJzcDskcmVhZCAsIGNhbGxhYmxlICR3cml0ZSAsIGNhbGxhYmxlICRkZXN0cm95ICwgY2FsbGFibGUgJGdjIFssIGNhbGxhYmxlICRjcmVhdGVfaWRdKTxlbSBpZD0="__mceDel" style="line-height: 1.5;">
可以看到這個函數又會去調用回調函數。首先,我們知道的是,從session的開始到掛掉,要session_start一個會話,要往$_SESSION里邊寫入數據保存session信息,或者讀取該數組中的信息,用完時又會去destroy它的數據,這么幾個步驟,對應這幾個函數名一看,就八九不離十了。
open:它有兩個參數---$save_path和$session_id,傳遞給它的參數已經表明,它是處理存放session數據的路徑的,實際上就是通過它來改變文件的存放目錄,就可以不是C:Windows/Temp。它在創建一個全新的會話session_start,或者start一個已經存在的會話時會被php內部去自動調用(session_start()),只有返回true才可以進行下一步處理。
close:它沒有參數,作用是關閉當前會話,當你關閉一個會話時會自動調用,或者當你顯式調用另一個php的函數session_write_close時。
read:它需要一個參數$session_id,作用是從存儲session數據的地方讀取id為$session_id的用戶的session信息,并返回,以便后續的內部處理。當session_start()開啟一個會話時,或者在內部調用open函數后會繼續調用它。返回當前用戶會話數據寫入$_SESSION。
write:它需要兩個參數---$session_id和$session_data,很明顯,寫入用戶$session_id的對應的會話信息$session_data到指定文件。這個session_data是序列化后的數據,返回true可以進行下一步操作。它可以是正常的腳本被關閉時執行,也可是內部函數session_write_close()調用或者內部函數session_register_shutdown調用失敗時執行。最明顯的,當我們給$_SESSION數組賦值時它就會被執行。
destroy:它有一個參數$session_id,在session_destroy時會調用它,刪除session_id對應的會話信息。
gc:參數是$maxlifetime,對于啟動垃圾回收程序時(開始會話或者session_start被調用)執行,貌似啟動垃圾回收時也是be randomly called,任性!
create_sid:這個回調函數是可選的,可以不寫。它沒有參數,當需要一個新的會話session id時可以寫寫,比如php還有一個重新生成會話id的函數session_regenerate_id。
總結下就是,session_start()時,要執行open、read、gc,在對$_SESSION數組賦值時,以后再操作$_SESSION不會調用它們,調用session_write_close內部函數時就會去調用write和close。
======================================================================
啰嗦了這么多,就是說,在start一個會話之前,我們要做的事是:
1、session.save_handler改為user,重啟Apache,或者,使用ini_set臨時修改命令項;
2、編寫session_set_save_handler函數里對應的必選的回調函數
3、重新定義session_set_save_handler函數,使用上面的回調函數的名字;
4、code一個會話程序,在每一次調用session_start()之前,確保本次運行腳本時有上面的重定義函數可被調用到。
修改命令的工作已做,先編寫一個腳本custom_session.php,自定義session_set_save_handler函數。
<?php ini_set('save_handler', 'users'); // 使用自定義處理 $session_save_path = 'G:/sessionFiles/'; // 自定義一個存放目錄 // session_start()會來調用它 function open($save_path, $session_id){ echo '<br>call open.<br>'; global $session_save_path; $save_path = $session_save_path; //修改存放session數據的目錄 return true; } // 銷毀session或調用session_write_close時調用 function close(){ echo '<br>call close.<br>'; return true; } // 讀取session數據并返回 function read($session_id){ echo '<br>call read.<br>'; global $session_save_path; $filename = $session_save_path.'sessionId_'.$session_id.'.txt'; $content = @file_get_contents($filename); return $content; } // 將對應用戶(以會話id判斷)的數據寫入文件 function write($session_id, $session_data){ echo '<br>call write.<br>'; global $session_save_path; $filename = $session_save_path.'sessionId_'.$session_id.'.txt'; // 自定義一個文件名,最好有一定規則 if(($handle = @fopen($filename, 'w+')) == true){ $bytes = @file_put_contents($filename, $session_data); return $bytes; } else{ return false; } return false; } // 調用session_destroy時執行,刪除對應 function destroy($session_id){ echo '<br>call destroy.<br>'; global $session_save_path; $filename = $session_save_path.'sessionId_'.$session_id.'.txt'; return @unlink($filename); // 刪除文件/數據 } // 垃圾回收 function gc(int $maxlifetime){ echo '<br>call gc.<br>'; global $session_save_path; $files = glob($session_save_path.'sessionId_*'); foreach($files as $filename){ if(filemtime($filename) + $maxlifetime < time()){ @unlink($filename); } } return true; } session_set_save_handler('open', 'close', 'read', 'write', 'destroy', 'gc');
然后,寫了個簡單的用戶登錄驗證的腳本來試試,前辦部分是php驗證操作,后邊是頁面,還要include前面的自定處理方式腳本
<?php // 改變session為自定義處理方式 include 'custom_session.php'; echo '<br>before call session_start fun.<br>'; session_start(); //開啟session會話,若已開啟則返回已存在session id echo 'session id: '.session_id().'<br>'; //輸出session id if(!empty($_POST["sub"])) //如果點擊登錄 { include "conn.inc.php"; //連接數據庫 $sql = "select id from sessionuser where username='".$_POST["user"]."' and password='".md5($_POST["pass"])."'"; //echo 'sql=> '.$sql.'<br>'; $result = $mysqli->query($sql); if($result->num_rows > 0) { $assRow = $result->fetch_assoc(); $_SESSION["user"] = $_POST["user"]; // 存儲session數據 $_SESSION["uid"] = $assRow["id"]; $_SESSION["islogin"] = 1; }else{ echo "<br>wrong username or password, please relogin.";
} } ?><html> <head> <title>Session Test</title> </head> <body> <form action="login.php" method="post"> <table align="center" > <caption><h3>ID login</h3></caption> <tr> <td>username</td> <td><input type="text" name="user" /></td> </tr> <tr> <td>password</td> <td><input type="password" name="pass" /></td> </tr> <tr> <td colspan="2" align="center"> <input type="submit" name="sub" value="login" /> </td> </tr> </table> </form> </body></html><?php echo '<br>script end.<br>'; ?>
結果如下:
在session_start()前做了標記“before call session_start fun.”,在腳本最后做了標記“script end”,很清楚的看到,在session_start()調用隨后就調了open和read,在腳本自動結束前會自動去調用write和close,這里并沒有去銷毀session信息,但是腳本結束時自動會調write寫入session信息,close關閉當前會話。
然后是登出,銷毀session信息:
<?php // 自定義session處理方式 include 'custom_session.php'; echo '<br>before call session_start fun.<br>'; session_start(); $_SESSION = array(); //一次性刪除存于$_SESSION中所有變量 if(isset( $_COOKIE[session_name()] ) ) { setcookie(session_name(), '', time()-3600, '/'); } echo '<br>before call session_destroy fun.<br>'; session_destroy();?><br><a href="login.php">relogin</a><br><?php echo '<br>script end.<br>'; ?>
同樣,在session_start處做了標記,在調用session_destroy出標記了“before call session_destroy fun.”,在腳本結束時標記“script end.”,看看效果:
可以看到,在調用session_start后緊接著是open和read函數,然后走銷毀程序,在調用session_destroy后緊接著會去調destroy和close函數,然后才是腳本的結束script end,進一步證明了這些函數的調用情形。
最后,session數據是不是保存到了我們指定的地方呢?look↓ 沒跑
當然了,你也可以標記得更細致,來看看其中的道理~ note end
PHP編程
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。
新聞熱點
疑難解答