我想为 Session 和 Request 创建一个包装器,这样我就不必直接访问 PHP superglobals。我意识到,如果我为超全局变量创建一个包装器并使用它们,那么对我的应用程序进行单元测试会更容易,因为可以模拟包装器类。
在尝试创建包装器类时,我研究了一些示例包装器类。其中一些在初始化时将超全局存储为类属性:
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,既然包装类无论如何都会被模拟,为什么还要费心将超全局变量存储为类属性呢?这么多人这样做有什么特别的原因吗?我应该将超全局变量作为属性存储在它们的包装器中而不是直接访问它吗?
感谢任何输入。
最佳答案
Session 是一个非常特殊的案例。但是你问是否有任何理由包装 super 全局变量。以下是一些可能的原因(不按顺序,也不完整):
减少代码对全局状态的依赖,从而更易于测试。您可以测试依赖于全局状态的代码。但它比被告知其状态的测试代码更难、更脆弱。
让代码更加灵活,因为您可以伪造请求和子请求来做一些有趣的事情,而这些事情在真正的全局状态下是不可能的。
使代码更具可移植性。通过将其包装在包装器中,您可以在中央位置处理依赖于平台的事情,例如去除引号、处理字符集转换等。这可以更轻松地处理平台之间或多个平台之间的转换。
对变量施加额外的约束。由于 $_SESSION 允许你在其内部设置任何你想要的东西,你可能会得到一个不可序列化的状态,这可能会给你带来问题。使用包装器,您有一个集中点,您可以在其中检查状态以确定它是否符合必要的约束条件。
使您的代码更具可读性。当然,如果您在某个方法中访问 $_POST,几乎每个 php 开发人员都知道您在做什么。但是他们需要知道那个实现细节吗?还是 $request->getFromPostData('foo'); 更冗长?
为了使您的代码更易于调试,因为您可以在请求类中设置断点并立即找到所有访问请求变量的事件(只要您从不直接访问它们)。
使类的依赖关系更容易理解。如果我给你一个使用 super 全局变量的类的 API,你无法判断该类是否访问了它,因此也无法判断该类究竟需要操作什么。但是如果你需要注入(inject)一个请求类,你可以快速地看一眼就知道这个类实际上确实需要请求中的一些东西来操作。从而提高可读性并进一步阐明您的 API。
现实中还有更多原因,但这些是我能想到的。
关于php - 我应该将 superglobals 存储为包装类的属性还是应该直接访问它?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5795754/
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2
我有这个html标记:我想得到这个:我如何使用Nokogiri做到这一点? 最佳答案 require'nokogiri'doc=Nokogiri::HTML('')您可以通过xpath删除所有属性:doc.xpath('//@*').remove或者,如果您需要做一些更复杂的事情,有时使用以下方法遍历所有元素会更容易:doc.traversedo|node|node.keys.eachdo|attribute|node.deleteattributeendend 关于ruby-Nokog
对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs
只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
我正在编写一个简单的静态Rack应用程序。查看下面的config.ru代码:useRack::Static,:urls=>["/elements","/img","/pages","/users","/css","/js"],:root=>"archive"map'/'dorunProc.new{|env|[200,{'Content-Type'=>'text/html','Cache-Control'=>'public,max-age=6400'},File.open('archive/splash.html',File::RDONLY)]}endmap'/pages/search.