The file driver for CI is to meet the following three conditions:
1, drive to implement open, read, write, close, Destory, GC six methods.
Session_Start (), The Open (), read () method is invoked. And there is a certain probability of triggering the GC () method.
Session_commit () or session_write_close (), triggers the Write (), close () method.
Session_destory () triggers the Desotry () method.
These six methods implement the following functions:
Open: Gets and creates a path to save the file
READ: Retrieves or creates a session_id corresponding file based on session_id, gets the file action pointer and locks the flock
Write: Writes the content to the session_id corresponding file when the session content changes. The write is a full volume write. The contents of the Session_data include the new session key value pairs and the existing key value pairs.
Close: Releases (flock) the file lock and closes the (fclose) file pointer.
destory: Deletes the server session_id corresponding file, and clears the client corresponding session_id cookie information
GC: Determines whether a file expires: Determines whether the session's corresponding session expires based on the file's last modification time (Filemtime ()) and the current time. This method is called at a certain probability when session_start.
2, drive to support session_regenerate_id ().
This method regenerates a new session_id and creates a file named after this session_id. Then copy the contents of the old session_id file into the new file. Finally delete the file where old session_id resides.
3, the driver to achieve session Lock: Here using File lock mode.
implementation: file-driven
Class Ci_session_files_driver extends Ci_session_driver implements Sessionhandlerinterface {//File save path protected $
_save_path;
File manipulation handle protected $_file_handle;
filename protected $_file_path;
Whether the new document identifies protected $_file_new; ------------------------------------------------------------------------//Constructor Public function __construct (&A
MP; $params) {parent::__construct ($params);
Initializes the file save path, setting the Session.save_path option in PHP.ini (isset ($this->_config[' Save_path ') according to the configuration file) {
$this->_config[' save_path '] = RTrim ($this->_config[' Save_path '], '/\\ ');
Set the Session.save_path option in php.ini according to the profile Ini_set (' Session.save_path ', $this->_config[' Save_path ')); else {//if $config[' Sess_save_path ' in the configuration file does not exist, use the default path in the current INI $this->_config['
Save_path '] = RTrim (Ini_get (' Session.save_path '), '/\\ '); }
}
// ---------------The first parameter of the---------------------------------------------------------//open method//$save_path corresponds to Ini_get (' Session.save_pa Th ')//The second parameter $name corresponds to the Ini_get (' session.name ') public function open ($save _path, $name) {//If the file diameter does not exist, try to create
Build if (! Is_dir ($save _path)) {if (! mkdir ($save _path, 0700, TRUE)) { If you cannot create a directory, throw new Exception ("Session:configured save Path". $this->_config[' Save_path ']. "' I
s not directory, doesn ' t exist or cannot is created. '} If the directory is not writable, throw an exception ElseIf (! is_writable ($save _path)) {throw new Exception ("session:configured
Save path ' ". $this->_config[' Save_path ']." ' Isn't writable by the PHP process. "
The path to the file save, where the file is saved with a bit of material to avoid conflict $this->_config[' save_path ' = $save _path; $this->_file_path = $this->_config[' Save_path ']. Directory_separator. $name//we ' ll usE The session cookie name as a prefix to avoid collisions.
($this->_config[' match_ip ']? MD5 ($_server[' REMOTE_ADDR '): ");
return $this->_success; }//------------------------------------------------------------------------//read//Parameter $session_id corresponds to Sess The value of ion_id () is public function read ($session _id) {//if session_id () the corresponding file action pointer is an empty if ($this->_file_ha Ndle = = NULL) {//Just using fopen () with ' c+b ' mode would being perfect, but it is only// Available since PHP 5.2.6 and we have to set permissions for new files,//So we ' d have to hack around this.
..
It is recommended that you use c+ mode when opening a file because the schema does not delete the original contents of the file when the file exists (w+ mode clears the original file content)//But the mode is only valid after PHP 5.2.6, so we have to do different things depending on whether the file exists. File does not exist in ' w+ ' mode, the file exists on the ' r+ ' mode//when the requested session file does not exist, the ' w+b ' read-write mode is used to create the file, get the action handle if (($this->_file_n EW =!
File_exists ($this->_file_path. $session _id)) = = = TRUE) {//use ' w+b ' mode to open the file, get the action handle if ($this->_file_handle = fopen ($this->_file_path. $sessi on_id, ' w+b ') = = FALSE) {//If success cannot be created, return failure log_message (' Error '),
"Session:file '". $this->_file_path. $session _id. "' doesn ' t exist and cannot be created.");
return $this->_failure; If the requested session file exists, open the file in ' r+b ' read-write mode to get the action handle ElseIf (($this->_file_handle = fopen ($this->_file_path. $session _id, ' r+b ')) = = FALSE) {//if failed to read successfully, return failure log_message (' Error ', "session:unable to open fi
Le ' ". $this->_file_path. $session _id.");
return $this->_failure;
The file pointer was successfully obtained and assigned to the $this->_file_handle//lock file pointer to a $this->_file_handle (LOCK_EX is an exclusive lock) Note: The release lock (Lock_un) is placed in the close () function if (Flock ($this->_file_handle, LOCK_EX)= = FALSE) {//Not locked successfully, log, release file pointer, then return failed.
Log_message (' Error ', ' session:unable to obtain lock for file '. $this->_file_path. $session _id. ");
Fclose ($this->_file_handle);
$this->_file_handle = NULL;
return $this->_failure; }//Needed by write () to detect session_regenerate_id () calls///assign $session_id to the properties of the image $this->_se SSION_ID//session_regenerate_id () after changing SessionID, in the Write method, which preserves the old SessionID $this->_session_i
D = $session _id;
If it is a newly generated file, set the file permissions to ($this->_file_new) {//Read Only permission, no Execute permissions
chmod ($this->_file_path. $session _id, 0600);
$this->_fingerprint = MD5 (');//summary is NULL character MD5 return '; }//We shouldn ' t need this, but apparently we do ...//check Https://github.com/bcit-ci/Codeigniter/issues/4039//if $this->_file_handle = = FALSE, return failure. This is git on a call Aanbar's brother found, and then fill up the $this->_file_handle = = FALSE This judgment,//Because fopen success when the file pointer, if open failure return FALSE ElseIf
($this->_file_handle = = FALSE)
{return $this->_failure; else {//If the pointer is not empty//The file internal offset pointer is again pointed to the opening rewind ($this->_file_handle
);
} $session _data = '; Read the content for ($read = 0, $length = filesize ($this->_file_path. $session _id); $read < $length; $read + = strlen ($
Buffer) {if ($buffer = fread ($this->_file_handle, $length-$read)) = = FALSE) {
Break
$session _data. = $buffer;
->_fingerprint = MD5 ($session _data) based on content generated file summary $this
return $session _data; Write Note: The writes for the session are all written, not incremental write//parameter $session_id corresponding to the value//parameter of session_id () $session_Data is not just the currently pending writes, it contains the saved data for the entire session + the data public function write that is currently being written ($session _id, $session _data) {/********* SESSION_REGENERATE_ID () processing begins *****************///If the program calls SESSION_REGENERATE_ID (), it causes the $session_id after the function call (
Parameter $session_id) is inconsistent with the $session_id ($this->_session_id) before the function call.
At this point we need to close the old file pointer, open a new file to get operation pointers//The IF condition statement here is actually a short-circuit operation, decomposition is/*if ($session _id!== $this->_session_id) { $close _flag= $this->close ()//Call Close closes the old pointer $read _flag= $this->read ($session _id);//read function parameter is new $SE
SSION_ID, which creates a new file//error in one of the two steps above, then returns a failure.
In fact, OR is also short-circuit operation, the first $close_flag=== $this->_failure, will not be followed by the implementation of $this->read ($session _id)//To explain the idea, first ignore this
if ($close _flag=== $this->_failure OR $read _flag=== $this->_failure) return $this->_failure; }*/if ($session _id!== $this->_session_id && ($this->close () = = $this->_failure OR $th Is->read ($session_id) = = = = $this->_failure)) {return $this->_failure;
}//If $this->_file_handle is not a resource type, an error is returned if (! Is_resource ($this->_file_handle)) {
return $this->_failure; ///If the session content summary of the current request is the same as $session_data, then the new SessionID file is generated successfully//and the touch function is used to test the existence of the file and detect the $this->_file_ New token (the tag is set to True if the file does not exist in read)//If the condition is satisfied, it is returned successfully elseif ($this->_fingerprint = = MD5 ($session _data
) {return (! $this->_file_new && Touch ($this->_file_path. $session _id)) ?
$this->_failure: $this->_success; /***************** session_regenerate_id () processing end *****************///If it is a ready-made file, then empty the content if (! $t
His->_file_new) {//Empty file contents Ftruncate ($this->_file_handle, 0);
Point the file internal offset pointer back to the opening rewind ($this->_file_handle);
} Session writes are written in full volume, not in incremental writes///$session_data content is written to the file. if ($length = strlen ($session _data)) > 0) {for ($written = 0; $written < $length; $written = = $result) {if ($result = fwrite ($this->_file_handle, substr ($session _data, $written)) = = =
FALSE) {break; } if (! Is_int ($result)) {$this->_fingerprint = MD5 (substr ($se
Ssion_data, 0, $written));
Log_message (' Error ', ' session:unable to write data. ');
return $this->_failure;
}//Get Session content Summary $this->_fingerprint = MD5 ($session _data);
return $this->_success;
}//Close//close () executes after the program that is currently requested executes, or performs public function close () when Session_commit (), Session_write_close () is invoked.
{if (Is_resource ($this->_file_handle)) {//Free file lock Flock ($this->_file_handle, Lock_un);
Release the file pointer fclose ($this->_file_handle);
Empty variable $this->_file_handle = $this->_file_new = $this->_session_id = NULL;
return $this->_success; }//Destroy public Function destory ($session _id) {//Call Close () method if ($this->close () = = $
this->_success) {if (File_exists ($this->_file_path. $session _id)) {
Deletes the corresponding client cookie $this->_cookie_destroy (); Delete server-side file return unlink ($this->_file_path. $session _id)?
$this->_success: $this->_failure;
return $this->_success; }//Call Close () method failed ElseIf ($this->_file_path!== NULL) {//clear PHP cached information for this file, Is_file (
), Is_dir (), file_exists () have influence Clearstatcache (); Repeat the above statement, and then delete once if (File_exists ($this->_file_path. $session _id)) {$this
; _cookie_destroy (); Return unlink ($this->_file_path $session _id)?
$this->_success: $this->_failure;
return $this->_success;
return $this->_failure; }//------------------------------------------------------------------------//gc method. When Session_Start () is called for probability, delete expired files Public Function GC ($maxlifetime) {if (! Is_dir ($this->_config[' Save_pat H ']) OR ($directory = opendir ($this->_config[' Save_path ')) = = FALSE) {log_message (' Debug ', ' Sess
Ion:garbage collector couldn ' t list files under directory '. $this->_config[' Save_path '].
return $this->_failure;
$ts = Time ()-$maxlifetime; To determine the regular session file name, so as to avoid mistakenly deleting the file $pattern = sprintf ('/^%s[0-9a-f]{%d}$/', Preg_quote ($this->_config[' cookie_name '], '/'), ($this->_config[' match_ip '] = = TRUE? 72:40)
); while ($file = Readdir ($directory))!== FALSE} {//If the filename doesn ' t match this pattern, it ' s
Either not a session file or was not ours////////. Preg_match (!, $file) based on the creation time. OR! Is_file ($this->_config[' Save_path '). Directory_separator. $file) OR ($mtime = filemtime ($this->_config[' Save_path ').
Directory_separator. $file)) = = FALSE OR $mtime > $ts) {continue; } unlink ($this->_config[' Save_path ').
Directory_separator. $file);
} closedir ($directory);
return $this->_success; }
}