Rumah  >  Artikel  >  php教程  >  PHP命令行下模拟Session机制

PHP命令行下模拟Session机制

WBOY
WBOYasal
2016-06-06 20:12:171895semak imbas

— 自动化测试过程中常规策略 一.背景 Session称为会话,是指一个终端用户与交互系统进行通信的时间间隔,通常指从注册进入系统到注销退出系统之间所经过的时间,如果需要的话,可能还有一定的操作空间。通常情况下Session用于存储需要在整个用户会话过程中

— 自动化测试过程中常规策略

一.背景

Session称为会话,是指一个终端用户与交互系统进行通信的时间间隔,通常指从注册进入系统到注销退出系统之间所经过的时间,如果需要的话,可能还有一定的操作空间。通常情况下Session用于存储需要在整个用户会话过程中保持其状态的信息,例如登录信息或用户浏览 Web应用程序时需要的其它信息。

PHP的 $_SESSION 的功能之所以如此强大是因为有WebServer的支持,试想一下如果在命令行下读取一个 $_SESSION 变量,会是什么结果?

必然是null,因为PHP的session_start() 函数在命令行下是无法使用的,假若一段逻辑结果中含有这个Session会话变量,该如何去测试它的有效性?

二.Session的原理

为了探究WebServer下的Session原理,我们做一个简单的测试:

  1. session.php的文件,内容很简单:
    <?php session_start();

    通过浏览器访问该文件,同时观察Request Header中的cookie信息以及服务器下的/tmp/目录:

    Cookie中存在一个PHPSESSID的值,而 /tmp/ 下存在一个对应的值,同时还可以知道这个 /tmp/sess_87bufd4ogid71e1gr6dtcbphi0是刚刚建立的,并且文件大小是0。

  2. 接着我们给SESSION赋点值:
    session_start();
    $_SESSION['login']  = 1;
    $_SESSION['name']   = 'Lancer He';
    $_SESSION['uid']    = 72;
    $_SESSION['groups'] = array(
        "dev" => 2,
        "loc" => 4,
    );

    再观察浏览器的Request Header中的cookie信息依旧不变,但是却可以发现服务器下 /tmp/sess_87bufd4ogid71e1gr6dtcbphi0文件的大小更变,打开发现类似序列化(非序列化)的字符串,信息内容是之前$_SESSION的值:

  3. 我们开启一个新的浏览器,比如IE,再查看/tmp/下的文件:

    观察新出现一个以sess为前缀的文件,同时IE的Cookie下出现了这个PHPSESSID的值。

  4. 因此我们可以基本理解Session的工作原理:
    • 当session被启用的时候,一个唯一的标识被储存于本地的cookie中。
    • 首先使用 session_start() 函数,PHP从session仓库中加载已经存储的session变量,如果这个仓库不存在,会被创建。
    • 当操作 $_SESSION变量时,通过使用PHP内置Session函数处理session变量。
    • 当PHP脚本执行结束时,未被销毁的session变量会被自动保存在本地一定路径下的session仓库中,这个路径可以通过php.ini文件中的session.save_path指定,默认在/tmp/目录。

三.Session策略设计

既然Session的默认机制是存放在文件中,因此我们是不是可以为了命令行模式做一个假的Session机制,因此不妨设计一个策略模式:

  • 当通过浏览器请求,使用一个真的Session操作类来操作 $_SESSION全局变量。
  • 当通过CLI模式请求php文件时,默认使用一个假的Session操作类。

让我们做这样简单的操作,无论在CLI模式或是Http模式都能正常运行:

\Cores\Session::getInstance()->set('name', "Lancer");
\Cores\Session::getInstance()->set('age',  "28");
\Cores\Session::getInstance()->del("age");
\Cores\Session::getInstance()->has("name")
\Cores\Session::getInstance()->has("groups", array(
    "dev" => 2,
    "loc" => 4,
));

我们可以猜想到:

\Cores\Session对象的 getInstance() 方法必然是一个自动选择策略的过程,返回的是一个对象:

  • 在CLI模式下返回的是 \Cores\Session_CLI 对象;
  • 在普通模式下返回的是 \Cores\Session_Http 对象。

既然是一种策略模式, \Cores\Session_CLI 与 \Cores\Session_Http 必须拥有同样的方法来操作Session,所以需要提供一个接口 \Cores\Session_Interface 。

根据我们的想法,设计出简单的UML图,Session具有基本的五个方法:

start(开始), set(赋值), has(存在), get(获取), del(删除)

由于Session启动后在整个应用中必然是唯一实例,因此上图 \Cores\Session_CLI 与 \Cores\Session_Http都使用了单例模式,但 \Cores\Session_CLI 必须具有一些特殊的操作,比如写入session记录,创建session_id等伪操作,因此添加部分方法:

四.程序实现

根据Session策略设计,开始编写对应的类:

  1. 接口类Session_Interface (不可否认写接口是最没难度的):
    /**
     * Session接口
     * @author Lancer He <lancer.he>
     * @since  2014-04-23
     * @copyright http://www.crackedzone.com
     */
    interface Session_Interface {
        // 开启
        public function start();
        // 是否存在某个Session
        public function has($name);
        // 获取某个Session
        public function get($name='');
        // 给某个Session赋值
        public function set($name, $value);
        // 删除某个Session值
        public function del($name);
    }</lancer.he>
  2. Session_Http类,用于管理Http请求过来的Session策略:
    /**
     * Http模式下管理$_SESSION类
     * @author Lancer He <lancer.he>
     * @since  2014-04-23
     * @copyright http://www.crackedzone.com
     */
    class Session_Http {
        protected static $_instance = null;
        /**
         * session是否已经开启
         * @var boolean
         */
        protected $_started = false;
        /**
         * 单例模式禁止Clone
         */
        private function __clone() {}
        /**
         * 单例模式禁止外部初始化
         */
        private function __construct() {}
        /**
         * 返回单例模式
         */
        public static function getInstance() {
            if ( ! is_null( self::$_instance ) ) {
                return self::$_instance;
            }
            $instance = new self();
            $instance->start();
            self::$_instance = $instance;
            return $instance;
        }
        /**
         * 开启Session
         * @return void
         */
        public function start() {
            session_start();
            $this->_started      = true;
        }
        /**
         * 通过name查看Session是否存在
         * @param  string $name
         * @return boolean
         */
        public function has($name) {
            return isset($_SESSION[$name]);
        }
        /**
         * 通过name从Session中获取一个值
         * @param  string $name 为空时返回整个sessino
         * @return mixed
         */
        public function get($name='') {
            if ( ! $name )
                return $_SESSION;
            return isset($_SESSION[$name]) ? $_SESSION[$name] : null;
        }
        /**
         * 给指定的name设置一个session值,返回连缀对象
         * @param  string $name
         * @param  mixed  $value
         * @return object
         */
        public function set($name, $value) {
            $_SESSION[$name] = $value;
            return $this;
        }
        /**
         * 从session中删除一个值,失败返回false,成功返回连缀对象
         * @param  string $name
         * @return false|object
         */
        public function del($name) {
            if ( ! $this->has($name) ) return false;
            unset($_SESSION[$name]);
            return $this;
        }
    }</lancer.he>
  3. Session_Cli类,用于命令行下模拟Session效果:
    /**
     * CLI模式下会模拟一个session_id,同时在/tmp/下产生一个sesscli文件用来保存session信息
     * @author Lancer He <lancer.he>
     * @since  2014-04-23
     * @copyright http://www.crackedzone.com
     */
    class Session_Cli {
        protected static $_instance = null;
        /**
         * session_id
         * @var string
         */
        protected $_session_id = null;
        /**
         * session file
         * @var string
         */
        protected $_session_file = null;
        /**
         * session数组
         * @var array
         */
        protected $_session = array();
        /**
         * session是否已经开启
         * @var boolean
         */
        protected $_started = false;
        /**
         * 单例模式禁止Clone
         */
        private function __clone() {}
        /**
         * 单例模式禁止外部初始化
         */
        private function __construct() {}
        /**
         * 返回单例模式
         */
        public static function getInstance() {
            if ( ! is_null( self::$_instance ) ) {
                return self::$_instance;
            }
            $instance = new self();
            $instance->start();
            self::$_instance = $instance;
            return $instance;
        }
        /**
         * 开启Session
         * @return void
         */
        public function start() {
            $this->_init();
            $this->_started      = true;
        }
        /**
         * 初始session
         * @return void
         */
        protected function _init() {
            $this->_session_id   = md5(uniqid());
            $this->_session_file = '/tmp/' . APPLICATION_CLI_SESSION_FILE_PREFIX . $this->_session_id;
            if ( file_exists($this->_session_file) ) {
                $this->_session = unserialize( file_get_contents($this->_session_file) );
                return;
            }
            file_put_contents($this->_session_file, null);
        }
        /**
         * 通过name查看Session是否存在
         * @param  string $name
         * @return boolean
         */
        public function has($name) {
            return isset($this->_session[$name]);
        }
        /**
         * 通过name从Session中获取一个值
         * @param  string $name 为空时返回整个sessino
         * @return mixed
         */
        public function get($name='') {
            if ( ! $name )
                return $this->_session;
            return isset($this->_session[$name]) ? $this->_session[$name] : null;
        }
        /**
         * 给指定的name设置一个session值,返回连缀对象
         * @param  string $name
         * @param  mixed  $value
         * @return object
         */
        public function set($name, $value) {
            $this->_session[$name] = $value;
            return $this;
        }
        /**
         * 从session中删除一个值,失败返回false,成功返回连缀对象
         * @param  string $name
         * @return false|object
         */
        public function del($name) {
            if ( ! $this->has($name) ) return false;
            unset($this->_session[$name]);
            return $this;
        }
        /**
         * 将session存放到tmp文件中
         * @return void
         */
        public function __destruct() {
            file_put_contents($this->_session_file, serialize($this->_session) );
        }
    }</lancer.he>
  4. 环境使用角色类 Session:

    由于具体策略类已经完成,所以我们只需要定义一个常量用于区分是否是CLI请求,同样使用单例模式自动装载对应的具体策略。

    class Session {
        public static function getInstance() {
            return APPLICATION_IS_CLI ? Session_Cli::getInstance() : Session_Http::getInstance();
        }
    }
  5. 测试过程:将设计的程序,通过Http和Cli方式分别测试:

    Cli测试结果:

    Http测试结果:

    虽然保存在 /tmp/ 目录下的内容格式不一致,但已经模拟出一个Session仓库的功能,实现了对这个仓库的增删改查功能。

五.小结

通过策略模式模拟一个虚拟的Session功能,保证Session在命令行下能够正常工作,为项目的自动化测试提供了基本支持。

策略模式其用意在于封装了一组新的算法,基于不同的策略下能够互相替换,为此我们能够在自动化测试中模拟出更多的功能,如请求的Request功能,渲染的View功能等。


原创文章,转载请注明:

原文标题:PHP命令行下模拟Session机制

原文地址:http://www.crackedzone.com/php-cli-using-session-strategy.html

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn