我正在研究一组组件(希望成为一个完整的框架),目前我正在开发一个组件来提供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()。如果你能提出一个更好的方法,我会热衷于听到它。
答案 0 :(得分:1)
如果我没有正确看待..
创建本机会话管理的抽象,以便您的会话存储帮助程序不需要实际执行任何session_ *调用或直接访问$ _SESSION。
有两个实现它,一个实际上做正确的事情,另一个是fakes session _ *()和$ _SESSION,并且在你的构造函数中你只需要调用SESSIONCLASS :: start()和SESSIONCLASS :: getVar(name) 。那么你可以完全测试“Session”。
答案 1 :(得分:1)
由于$_SESSION
是常规数组,因此没有理由通过引用访问它。各种读/写方法应该直接对它进行操作。您可以在测试用例的setUp()
方法中清除该数组。使用引用会使类过度复杂而无需支付。
进行单元测试时,您不应该考虑测试内置的PHP函数,如session_id()
和headers_sent()
。创建一个类的部分模拟或一个覆盖这个方法的仅测试子类。