做者:Terry Gaophp
上一篇提到了類的自動加載和Session,今天就來逐一說說。git
在使用PHP的OO模式開發系統時,一般你們習慣將每一個類的實現都存放在一個單獨的文件裏,這樣會很容易實現對類進行復用,同時未來維護時也很便利,這也是OO設計的基本思想之一。若是須要使用一個類,只須要直接使用include/require將其包含進來便可。但隨着項目規模的不斷擴大,使用這種方式會帶來一些隱含的問題:若是一個PHP文件須要使用不少其它類,那麼就須要不少的require/include語句,這樣有可能會形成遺漏或者包含進沒必要要的類文件。若是大量的文件都須要使用其它的類,那麼要保證每一個文件都包含正確的類文件確定是一個噩夢。
PHP5爲這個問題提供了一個解決方案,這就是類的自動裝載(autoload)機制。github
/* Nova\Framework\Autoloader.php */ <?php namespace Nova\Framework; class Autoloader { public static $loader; /** * Autoloader 構造函數 */ private function __construct() { //將$this->import()註冊到sql_autoload,做爲本項目中類的自動加載方法 spl_autoload_register(array( $this, 'import' )); } /** * Autoloader的入口函數 * 用於建立Autoloader的惟一實例化對象 * * @return Autoloader */ public static function init() { if (self::$loader == NULL) self::$loader = new self(); return self::$loader; } /** * 類的自動加載方法 * 根據傳入參數$className,自動引入相應類的源文件 * * @param string $className */ public function import($className) { $path = explode('\\', substr($className, strlen('Nova'))); $filePath = ROOT_DIR . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $path) . '.php'; if (is_file($filePath)) { require $filePath; } } }
這個自動加載類比較簡單,初始化後,只有一個主要的方法import(),它經過解析傳入進來的類名(因爲咱們使用了命名空間,因此類名基本上都是「NovaFrameworkAutoloader這樣的形式」),從項目根目錄開始,按照類名自己指定的路徑來定位相應類的源碼文件,若是存在該文件,則將其引入。redis
更多關於自動加載類的機制和原理,能夠參考PHP autoload原理sql
默認狀況下,PHP是將session以文件的形式存在服務器上,具體能夠在php.ini中配置。可是實際生產環境中,稍大些的站點都不會採用這種形式,通常都會藉助sql數據庫、或者nosql類型的如memcached、redis等緩存服務器來存儲session,這樣作能夠有效緩解PHP服務器的壓力和處理速度,提升併發能力。數據庫
在Nova中咱們使用redis服務器來存取Session。json
/* Nova\Framework\Session.php <?php namespace Nova\Framework; class Session { private static $sessionId, $redisCache, $userIp; public function __construct() { } public static function start() { //註冊Session的各類處理函數 session_set_save_handler( array(__CLASS__, "open"), array(__CLASS__, "close"), array(__CLASS__, "read"), array(__CLASS__, "write"), array(__CLASS__, "destory"), array(__CLASS__, "gc") ); session_start(); } /** * session_start時會調用該函數 * * @return bool */ public static function open() { //生成或獲取一個session id self::get_sid(); //獲取用於存儲Session的Redis對象實例 self::$redisCache = Redis::get_instance(); return true; } /** * 使用SessionId做爲key,從Redis中讀取相應數據,並將數據寫入Session變量 * * @return bool */ public static function read() { $sessionValue = self::$redisCache->get(self::$sessionId, SESSION_TABLE_NAME); if ($sessionValue) { $_SESSION = $sessionValue; } return true; } /** * 將Session變量的內容寫入Redis中 * * @return bool */ public static function write() { if (!empty($_SESSION)) { self::$redisCache->set(self::$sessionId, $_SESSION, SESSION_TABLE_NAME, SESSION_TIMEOUT); } return true; } /** * 經過刪除Redis中SessionId對應的數據來註銷Session * session_destory()是自動調用 * * @return bool */ public static function destory() { if (self::$redisCache->exists(self::$sessionId, SESSION_TABLE_NAME)) { self::$redisCache->delete(self::$sessionId, SESSION_TABLE_NAME); } setcookie(SESSION_NAME, self::$sessionId, 1, COOKIE_PATH, COOKIE_DOMAIN, FALSE); return true; } public static function close() { return true; } public static function gc() { return true; } /** * 返回一個SessionId * 若Cookie中已存在SessionId,則直接返回該SessionId * 若不存在,則按照規則新生成一個SessionId * * @return string Session Id */ public static function get_sid() { self::$userIp = Tools::real_ip(); $arr = $_COOKIE; //判斷Cookie中是否已經存在SessionId if (is_null(self::$sessionId) && empty($arr[SESSION_NAME])) { //使用MD5對用戶IP+隨機字符串加密後做爲新的SessionId self::$sessionId = function_exists('com_create_guid') ? md5(self::$userIp . com_create_guid()) : md5(self::$userIp . uniqid(mt_rand(), true)); //對新的SessionId再作一次crc32運算,做爲最終的SessionId self::$sessionId .= sprintf('%08x', crc32(self::$sessionId)); //將SessionId寫入Cookie中 setcookie(SESSION_NAME, self::$sessionId, time() + SESSION_TIMEOUT, COOKIE_PATH, COOKIE_DOMAIN, FALSE); $_COOKIE[SESSION_NAME] = self::$sessionId; } else { self::$sessionId = $arr[SESSION_NAME]; } //返回SessionId return self::$sessionId; } }
Nova基本上重寫了Session的一些核心處理函數。爲了方便使用自定義的全局Redis Rootkey,Nova把Redis方法也重寫了。緩存
<?php namespace Nova\Framework; class Redis extends \Redis { private static $_instanceObj; public $groupName = REDIS_ROOT; private $tempName = "temp:"; private $_redis; private $groupPath = REDIS_ROOT; public function __construct() { $this->_redis = new \Redis(); $this->_redis->connect(REDIS_HOST, REDIS_PORT); } public static function get_instance($redisKey = REDIS_ROOT) { if (!(self::$_instanceObj[$redisKey] instanceof self)) { self::$_instanceObj[$redisKey] = new self; } self::$_instanceObj[$redisKey]->redisKey = $redisKey; return self::$_instanceObj[$redisKey]; } public function set_group($groupName = "") { if (empty($groupName)) { return FLASE; } $this->groupName = $groupName; $this->groupPath = implode(":", explode("/", $groupName)) . ":"; return TRUE; } public function set($key, $data, $groupName = "", $timeout = SESSION_TIMEOUT) { if (empty($groupName)) { $groupName = $this->groupName . $this->tempName; } else { $groupName = $this->groupName . $groupName; } if (is_array($data)) { $data = json_encode($data); } $redisKey = $groupName . $key; return $this->_redis->setex($redisKey, $timeout, $data); } public function get($key, $groupName = "") { if (empty($groupName)) { $groupName = $this->groupName . $this->tempName; } else { $groupName = $this->groupName . $groupName; } $redisKey = $groupName . $key; $return = ""; $temp = $this->_redis->get($redisKey); $return = json_decode($temp, 1); return empty($return) ? $temp : $return; } public function delete($key, $groupName = "") { if (empty($groupName)) { $groupName = $this->groupName . $this->tempName; } else { $groupName = $this->groupName . $groupName; } $redisKey = $groupName . $key; return $this->_redis->delete($redisKey); } public function exists($key, $groupName = "") { if (empty($groupName)) { $groupName = $this->groupName . $this->tempName; } else { $groupName = $this->groupName . $groupName; } $redisKey = $groupName . $key; return $this->_redis->exists($redisKey); } }
你能夠在Github上查看Nova項目的源代碼。服務器
若是你有任何問題或建議,能夠掃描下方二維碼或者微信搜索[phpjiagoushier],關注個人微信公衆號[PHP架構],與我交流互動。
微信