资讯专栏INFORMATION COLUMN

自己动手写PHP框架(二)

April / 2838人阅读

摘要:作者上一篇提到了类的自动加载和,今天就来逐一说说。为这个问题提供了一个解决方案,这就是类的自动装载机制。为了方便使用自定义的全局,把方法也重写了。

作者:Terry Gao

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

1. 类的自动加载

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

/* NovaFrameworkAutoloader.php */
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这样的形式”),从项目根目录开始,按照类名本身指定的路径来定位相应类的源码文件,如果存在该文件,则将其引入。

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

2. Session

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

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

/* NovaFrameworkSession.php
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方法也重写了。

_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架构],与我交流互动。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/22554.html

相关文章

  • 自己动手PHP框架(一)

    摘要:作者作为的入门进阶,自己动手写一个框架可以从各方面巩固和加深你对的理解,还能了解一些架构方面的基础知识。它将程序划分为三层。最上面的一层,是直接面向最终用户的视图层。最底下的一层,是核心的模型层,负责检索组织和处理程序所需的数据或信息。 作者:Terry Gao 作为PHP Coder的入门进阶,自己动手写一个MVC框架可以从各方面巩固和加深你对PHP的理解,还能了解一些架构方面的基础...

    B0B0 评论0 收藏0
  • 自己动手PHP框架(三)

    摘要:假设我们站点的域名是,用户请求地址。模型层我们可以将数据组织和处理逻辑放在模型层,这里封装了数据库操作,甚至有些大型的框架会在这一层对这个数据库进行对象化,目的都是为了组织和处理数据,然后将处理好的数据返回给控制器层。 作者:Terry Gao 1. 控制器 第一篇我们聊到路由分发会把用户请求按照规则分发到控制器层的不同类,而默认的规则中,请求会下发到控制器的index类的main方法...

    Terry_Tai 评论0 收藏0
  • 从零开始搭建论坛(一):Web服务器与Web框架

    摘要:服务器通过协议与客户端通信,因此也被称为服务器。本文标题为从零开始搭建论坛一服务器与框架本文链接为更多阅读自己动手开发网络服务器一自己动手开发网络服务器二自己动手开发网络服务器三服务器网关接口实现原理分析最佳实践指南应用浅谈框架编程简介 之前用 Django 做过一个小的站点,感觉Django太过笨重,于是就准备换一个比较轻量级的 Web 框架来玩玩。Web.py 作者已经挂掉,项目好...

    dantezhao 评论0 收藏0
  • 项目开发

    摘要:前端菜鸟项目,大佬们轻喷美团外卖项目演示地址第期分享推荐上好玩容易上手的项目,帮你找到编程的乐趣。包含支持冒泡的事件框架资源模块等组件,按需选择组件,不绑架开发者。 基于 Vue 的 WebSocket 实时聊天实战项目 一个基于vue的,从前端到后端构建的项目。能让学到如何用,用到了vue全家桶,nodejs,通过webpack打包,图灵机器人,websocket等前沿知识进行构建。...

    elva 评论0 收藏0
  • 一本关于 React.js 的小书

    摘要:因为工作中一直在使用,也一直以来想总结一下自己关于的一些知识经验。于是把一些想法慢慢整理书写下来,做成一本开源免费专业简单的入门级别的小书,提供给社区。本书的后续可能会做成视频版本,敬请期待。本作品采用署名禁止演绎国际许可协议进行许可 React.js 小书 本文作者:胡子大哈本文原文:React.js 小书 转载请注明出处,保留原文链接以及作者信息 在线阅读:http://huzi...

    Scorpion 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<