我应该将超全局变量存储为包装类吗?属性还是我应该直接访问它?
我想为 Session
和 Request
创建一个包装器,这样我就不必直接访问 PHP 超全局变量。我意识到,如果我为超全局创建一个包装器并使用它们,对我的应用程序进行单元测试将会更容易,因为包装器类可以被模拟。
在尝试创建我的包装类时,我研究了一些示例包装类。其中一些在初始化时将超全局存储为类属性:
class Session
{
protected $vars;
public function __construct()
{
session_start();
// POINT OF INTEREST
// Store the superglobal as a class property
$this->vars = $_SESSION;
}
public function get($index)
{
// POINT OF INTEREST
// Accesses the class property instead of the superglobal
return $this->vars[$index];
}
public function write($index, $value)
{
// Writes both class property and session variable
$this->vars[$index] = $value;
$_SESSION[$index] = $value;
}
}
我的问题:为什么在创建包装类时我们将超全局存储为类的属性而不是直接访问它们,是否有任何特殊原因?将上面的代码与下面的代码进行对比:
class Session
{
public function __construct()
{
session_start();
}
public function get($index)
{
// Accesses the superglobal directly
return $_SESSION[$index];
}
public function write($index, $value)
{
// Accesses the superglobal directly
$_SESSION[$index] = $value;
}
}
IMO,既然包装类无论如何都会被模拟,为什么还要费力将超全局变量存储为类属性呢?这么多人这样做有什么特殊原因吗?我应该将超全局变量作为属性存储在其包装器中,而不是直接访问它吗?
感谢您的任何意见。
I wanted to create a wrapper for Session
and Request
so that I don't have to access the PHP superglobals directly. I realized that if I create a wrapper for the superglobals and use them, unit testing my application will be easier as the wrapper class can be mocked.
While trying to create my wrapper class, I researched some sample wrapper classes. Some of them stores the superglobal as a class property at initialization:
class Session
{
protected $vars;
public function __construct()
{
session_start();
// POINT OF INTEREST
// Store the superglobal as a class property
$this->vars = $_SESSION;
}
public function get($index)
{
// POINT OF INTEREST
// Accesses the class property instead of the superglobal
return $this->vars[$index];
}
public function write($index, $value)
{
// Writes both class property and session variable
$this->vars[$index] = $value;
$_SESSION[$index] = $value;
}
}
My question: is there any particular reason why while creating a wrapper class we store the superglobal as the class' property instead of accessing them directly? Contrast the above code with this one:
class Session
{
public function __construct()
{
session_start();
}
public function get($index)
{
// Accesses the superglobal directly
return $_SESSION[$index];
}
public function write($index, $value)
{
// Accesses the superglobal directly
$_SESSION[$index] = $value;
}
}
IMO, since the wrapper class would be mocked anyway, why bother storing the superglobals as a class property? Is there a particular reason why so many people do this? Should I store the superglobals as a property in their wrapper instead of accessing it directly?
Thanks for any input.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Session 是一个非常具体的案例。但你问是否有任何理由包装超级全局变量。以下是一些可能的原因(不按顺序排列,也不完整):
为了使代码更少依赖于全局状态,从而更容易测试。您可以测试依赖于全局状态的代码。但它比测试被告知其状态的代码更困难、更脆弱。
为了使代码更加灵活,因为您可以伪造请求和子请求来完成真正的全局状态不可能完成的有趣的事情。
使代码更加可移植。通过将其包装在包装器中,您可以在中心位置处理依赖于平台的事情,例如剥离引号、处理字符集转换等。这可以更轻松地处理平台之间或多个平台之间的转换。
对变量实施附加约束。由于 $_SESSION 允许您在其内部设置任何您想要的内容,因此您可能会遇到不可序列化的状态,这可能会导致问题。使用包装器,您可以拥有一个集中点,您可以在其中检查状态以确定它是否符合必要的约束。
使您的代码更具可读性。当然,如果您在方法中访问 $_POST,几乎每个 PHP 开发人员都知道您在做什么。但他们需要了解实施细节吗?还是
$request->getFromPostData('foo');
更冗长?为了使您的代码更易于调试,因为您可以在请求类中设置断点并立即找到所有访问请求变量的情况(只要您从未直接访问它们)。
使类的依赖关系更容易理解。如果我为您提供了一个使用超级全局变量的类的 API,您将无法判断该类是否访问了它,因此也无法判断该类需要操作什么。但是,如果您需要注入一个请求类,那么您一眼就能看出该类实际上确实需要请求中的某些内容才能进行操作。因此,可以提高可读性并进一步阐明 API。
现实中的原因还有很多,但我暂时能想到的就这些了。
Session is a pretty specific case. But you asked if there was any reason to wrap super-globals. Here are a few possible reasons (Not in order, nor complete):
To make the code less dependent upon global state and hence easier to test. You can test code that depends on a global state. But it's harder and more fragile than testing code that gets told its state.
To make code more flexible since you can then fake requests and sub-requests to do interesting things that wouldn't be possible with a true global state.
To make the code more portable. By wrapping it in a wrapper, you can handle platform dependent things such as stripping quotes, handling character set conversions, etc in a central location. This can make it easier to handle a shift between platforms or multiple platforms.
To enforce additional constraints upon the variable. Since $_SESSION allows you to set anything you want inside itself, you can wind up with a non-serializable state which could cause you problem. With a wrapper, you have one centralized point where you can check the state to determine if it matches the constraints necessary.
To make your code more readable. Sure, just about every php developer knows what you're doing if you access $_POST in a method. But do they need to know about that implementation detail? Or is
$request->getFromPostData('foo');
more verbose?To make your code easier to debug since you can set a breakpoint in the request class and immediately find all occurrences of accessing request variables (as long as you never access them directly).
To make your class's dependencies easier to understand. If I gave you an API for a class that used the super globals, you can't tell if that class accessed it, and hence exactly what the class needs to operate. But if you require a request class to be injected, you could tell at a quick glance that the class actually does need something from the request to operate. Hence improving readability and clarifying your API more.
There are more reasons in reality, but those are the ones I can think of off the top of my head.
我不认为有一些重要原因,也许只是如果您之后更改了您所指的超全局,则不必在类的整个代码,但仅在构造函数中。
从这个角度来看,我发现您提供的第一个实现的 write 方法不太明智,正如您所说,该方法同时写入类属性和会话变量。
然而,所有这些似乎有点矫枉过正(并且想得太多)。保持简单并做对你有用的事情。
I don't think there's some important reason, maybe it's just that if you change the superglobal you're referring to afterwards, you haven't got to replace it in the whole code of your class, but just in the constructor.
From this point of view, I find not very sensible the
write
method of the first implementation you provided, that writes both class property and session variable, as you remarked.All of this seems a bit overkill (and overthink), however. Just keep simple and do things that are useful to you.