本身動手寫PHP框架(二)

做者:Terry Gaophp

上一篇提到了類的自動加載和Session,今天就來逐一說說。git

1. 類的自動加載

在使用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

2. Session

默認狀況下,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架構],與我交流互動。
PHP架構師微信

相關文章
相關標籤/搜索