0

我正在开发一组组件(有望成为一个完整的框架),目前正在开发一个以提供 PHP 会话的抽象。

我试图使代码尽可能地可测试,但是根据定义,会话类将依赖于 $_SESSION 超全局形式的全局状态。

我试图以这样一种方式实现我的会话类,即 $ SESSION 和 session * 函数只在一个地方被调用,然后我可以在 PHPUnit 中覆盖它以进行测试,但我不禁想知道是否有更好的这样做的方式。

如果您可以建议一种更好的方法来制作可测试的会话课程,那么我将不胜感激您的任何意见。

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

namespace gordian\reefknot\storage\session;

use gordian\reefknot\storage;

/**
 * Session management
 * 
 * Each instance of the Session object represents a "namespace" within the PHP
 * $_SESSION system.  This allows sessions to be easily managed and organized
 * within an application
 * 
 */
class Session implements storage\iface\Crud, iface\Session
{

    protected
        $name       = '',
        $storage    = NULL;


    /**
     * Add a new item to the session
     * 
     * @param mixed $data 
     * @param string $key
     * @return Session
     * @throws \InvalidArgumentException Thrown if no name is provided
     */
    public function createItem ($data, $key)
    {
        if (!empty ($key))
        {
            $key    = (string) $key;
            if (($this -> storage === NULL)
            || (!array_key_exists ($key, $this -> storage)))
            {
                $this -> storage [$key] = $data;
            }
        }
        else
        {
            throw new \Exception ('No valid key given');
        }
        return ($this);
    }

    /**
     * Delete the specified key
     * 
     * @param string $key 
     * @return Session
     */
    public function deleteItem ($key)
    {
        unset ($this -> storage [$key]);
        return ($this);
    }

    /**
     * Retrieve the data stored in the specified key
     * 
     * @param type $key 
     * @return mixed
     */
    public function readItem ($key)
    {
        return (array_key_exists ($key, $this -> storage)? 
            $this -> storage ['key']: 
            NULL);
    }

    /**
     * Update a previously stored data item to a new value
     * 
     * @param mixed $data 
     * @param string $key
     */
    public function updateItem ($data, $key)
    {
        if ($this -> storage === NULL)
        {
            throw new \RuntimeException ('Session contains no data');
        }

        if (array_key_exists ($key, $this -> storage))
        {
            $this -> storage [$key] = $data;
        }
        return ($this);
    }

    /**
     * Clear the session of all stored data
     * 
     * @return Session 
     */
    public function reset ()
    {
        $this -> storage = NULL;
        return ($this);
    }

    /**
     * Retrieve all data stored in the session
     * 
     * @return array 
     */
    public function getAll ()
    {
        return ($this -> storage);
    }

    /**
     * Return whether there is data stored in this session
     * 
     * @return bool 
     */
    public function hasData ()
    {
        return (!empty ($this -> storage));
    }

    /**
     * Initialize the back-end storage for the session
     * 
     * This method provides access for this class to the underlying PHP session
     * mechanism.  
     * 
     * @return bool Whether the newly initialized session contains data or not
     * @throws \RuntimeException Will be thrown if the session failed to start
     */
    protected function initStorage ()
    {
        // Check that storage hasn't already been initialized
        if ($this -> storage === NULL)
        {
            // Attempt to start the session if it hasn't already been started
            if ((session_id () === '')
            && ((headers_sent ()) 
            || ((!session_start ()))))
            {
                throw new \RuntimeException ('Unable to start session at this time');
            }
            // Alias our instance storage to the named $_SESSION variable
            $this -> storage    =& $_SESSION [$this -> name];
        }
        return ($this -> hasData ());
    }

    /**
     * Class constructor
     * 
     * @param string $sessName
     * @throws \InvalidArgumentException Thrown if no session name is provided
     */
    public function __construct ($sessName)
    {
        if (!empty ($sessName))
        {
            $this -> name   = $sessName;
            $this -> initStorage ();
        }
        else
        {
            throw new \InvalidArgumentException ('Session must have a name');
        }
    }
}

为了测试,目前的计划是用一个只设置一个内部数组的方法替换 initStorage()。如果你能提出更好的方法,我很想听听。

4

2 回答 2

1

如果我理解正确..

创建本地会话管理的抽象,以便您的会话存储助手不需要实际执行任何 session_* 调用或直接访问 $_SESSION。

它有两个实现,一个实际上做正确的事情,另一个不是伪造 session_*() 和 $_SESSION,在你的构造函数中你只需调用 SESSIONCLASS::start() 和 SESSIONCLASS::getVar(name)。然后你可以完全测试“会话”。

于 2012-02-26T08:51:22.840 回答
1

由于$_SESSION是一个常规数组,因此没有理由通过引用访问它。各种读/写方法应该直接对其进行操作。您可以在测试用例的setUp()方法中清除数组。使用参考会使课程过于复杂而没有回报。

在进行单元测试时,您不应该关心测试内置的 PHP 函数,例如session_id()headers_sent(). 创建类的部分模拟或覆盖此方法的仅测试子类。

于 2012-02-26T09:04:48.553 回答