自己动手写PHP框架(二)

来源:互联网 发布:js下载图片保存到本地 编辑:程序博客网 时间:2024/05/21 08:46

作者:沙袋

上一篇提到了类的自动加载和Session,今天就来逐一说说。

1. 类的自动加载

在使用PHP的OO模式开发系统时,通常大家习惯将每个类的实现都存放在一个单独的文件里,这样会很容易实现对类进行复用,同时将来维护时也很便利,这也是OO设计的基本思想之一。如果需要使用一个类,只需要直接使用include/require将其包含进来即可。但随着项目规模的不断扩大,使用这种方式会带来一些隐含的问题:如果一个PHP文件需要使用很多其它类,那么就需要很多的require/include语句,这样有可能会造成遗漏或者包含进不必要的类文件。如果大量的文件都需要使用其它的类,那么要保证每个文件都包含正确的类文件肯定是一个噩梦。
PHP5为这个问题提供了一个解决方案,这就是类的自动装载(autoload)机制。

/* Nova\Framework\Autoloader.php */<?phpnamespace 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(),它通过解析传入进来的类名(由于我们使用了命名空间,所以类名基本上都是“Nova\Framework\Autoloader这样的形式”),从项目根目录开始,按照类名本身指定的路径来定位相应类的源码文件,如果存在该文件,则将其引入。

更多关于自动加载类的机制和原理,可以参考PHP autoload原理

2. Session

默认情况下,PHP是将session以文件的形式存在服务器上,具体可以在php.ini中配置。但是实际生产环境中,稍大些的站点都不会采用这种形式,一般都会借助sql数据库、或者nosql类型的如memcached、redis等缓存服务器来存储session,这样做可以有效缓解PHP服务器的压力和处理速度,提高并发能力。

在Nova中我们使用redis服务器来存取Session。

/* Nova\Framework\Session.php<?phpnamespace 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方法也重写了。

<?phpnamespace 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架构师],与我交流互动。
phpjiagoushier

0 0
原创粉丝点击