【问题标题】:Is using superglobals directly good or bad in PHP?在 PHP 中直接使用超全局变量是好是坏?
【发布时间】:2011-03-30 17:14:24
【问题描述】:

所以,我的 PHP 背景并不深厚——我想知道在格式良好的代码中是否应该直接使用“超级全局变量”,例如在某个函数的中间说$_SESSION['x'] = 'y';,或者如果像我通常对变量所做的那样,最好将它们作为可以从那里使用的参数发送,例如:

class Doer {
    private $sess;
    public function __construct(&$sess) {
        $this->sess =& $sess;
    }
} 

$doer = new Doer($_SESSION);

然后使用 Doer 等内部的Doer->sess 版本。 (这种方法的优点是明确Doer使用了$_SESSION。)

对于这个问题,公认的 PHP 设计方法是什么?

【问题讨论】:

  • 在验证后将所需的数据移动到变量中,因为那里的数据在验证之前是可疑的。
  • 您的方法并不常见,将超全局变量别名为局部属性可能会使共同开发人员感到困惑。但这当然是合法的,如果您的对象使用或过滤它(例如配置助手)可能是个好主意。

标签: php global-variables superglobals


【解决方案1】:

我确实喜欢将 $_SESSION$_POST$_GET$_COOKIE 包装到 OOP 结构中。

我使用这种方法来集中处理卫生和验证的代码、所有必要的isset () 检查、随机数、setcookie 参数等。它还允许客户端代码更具可读性(并给我一种错觉:它更易于维护)。

强制使用这种结构可能很困难,尤其是在有多个编码员的情况下。使用$_GET$_POST$_COOKIE(我相信),您的初始化代码可以复制数据,然后销毁超全局。也许一个聪明的析构函数可以通过 $_SESSION 实现这一点(在加载时擦除 $_SESSION,将其写回到析构函数中),尽管我没有尝试过。

不过,我通常不使用任何这些强制技术。习惯了之后,在 session 类之外的代码中看到$_SESSION 就觉得很奇怪,而且我大多是独自工作。

编辑
这是一些示例客户端代码,以防它对某人有所帮助。我敢肯定,查看任何主要框架都会为您提供更好的想法...

$post = Post::load ();  
$post->numeric ('member_age');  
$post->email ('member_email');
$post->match ('/regex/','member_field');
$post->required ('member_first_name','member_email');
$post->inSet ('member_status',array('unemployed','retired','part-time','full-time'));
$post->money ('member_salary');
$post->register ('member_last_name'); // no specific requirements, but we want access
if ($post->isValid())
{
  // do good stuff
  $firstName = $post->member_first_name;
}
else
{
  // do error stuff
}

Post 和它的朋友都派生自一个实现核心验证代码的基类,添加了他们自己的特定功能,如表单令牌、会话 cookie 配置等等。

在内部,该类包含一组有效数据,这些数据在调用验证方法时从$_POST 中提取,然后使用神奇的__get 方法将它们作为属性返回。无法通过这种方式访问​​失败的字段。我的验证方法(required 除外)不会在空字段上失败,其中许多使用 func_get_args 来允许它们一次对多个字段进行操作。一些方法(如money)会自动将数据转换为自定义值类型。

在错误情况下,我有办法将数据转换为可以保存在会话中的格式,并用于预填充表单并在重定向到原始表单后突出显示错误。

对此进行改进的一种方法是将验证信息存储在 Form 类中,该类用于呈现表单并支持客户端验证,以及在提交后清理数据。

【讨论】:

  • 给我! 你会为输入数组发布你的 OOP 包装器吗?我正在使用类似的方法,并希望将这种方法与可比较的方法进行对比。
  • @mario:我无法发布代码,但我可以详细说明一下,给你一个想法。我现在将编辑答案。
  • 没关系。但我的在这里是为了比较:sourceforge.net/p/php7framework/wiki/input - 只是想知道您使用哪种实用程序功能,或者您认为哪些过滤器最有用。
  • @mario:酷。我真的很喜欢链接功能。感谢分享!
  • @FélixGagnon-Grenier:这当然是一种方法,但请注意,这种方法明确地试图远离全局状态。它将全局状态的快照提取到具有可预测行为和内容的局部变量中。创建并验证$post 对象后,您可以像传递任何其他数据结构一样传递它,因此客户端代码永远不会查看全局状态。虽然很少同时使用多个,但绝对不是单例。
【解决方案2】:

修改超全局变量的内容被认为是不好的做法。虽然它并没有什么真正的问题,尤其是如果代码 100% 在您的控制之下,它可能会导致意想不到的副作用,尤其是当您考虑混合源代码时。例如,如果您执行以下操作:

$_POST['someval'] = mysql_real_escape_string($_POST['someval']);

您可能期望 PHP 使“someval”可用的任何地方也会发生变化,但事实并非如此。 $_REQUEST['someval'] 中的副本将保持不变,仍然是原始的“不安全”版本。如果您在 $_POST 上进行所有转义,这可能会导致意外注入漏洞,但后来的库使用 $_REQUEST 并假定它已经被转义。

因此,即使您可以修改它们,最好将超全局变量视为只读。如果您必须弄乱这些值,请维护自己的并行副本并执行维护该副本所需的任何包装器/访问方法。

【讨论】:

    【解决方案3】:

    我知道这个问题很老,但我想添加一个答案。

    mario 处理输入的类很棒。

    我更喜欢以某种方式包装超全局变量。它可以使您的代码更易于阅读并带来更好的可维护性。

    例如,我目前的工作中有一些我讨厌的代码!会话变量被大量使用,以至于您实际上无法在不严重影响整个站点的情况下更改实现。

    例如,

    假设您创建了一个特定于您的应用程序的 Session 类。

    class Session
    {
        //some nice code
    }
    

    你可以这样写

    $session = new Session();
    if( $session->isLoggedIn() )
    {
       //do some stuff
    }
    

    与此相反

    if( $_SESSION['logged'] == true )
    {
       //do some stuff
    }
    

    这似乎有点微不足道,但对我来说却很重要。假设将来某个时候我决定要将索引的名称从“logged”更改为“loggedIn”。

    我现在必须去应用程序中使用会话变量来更改它的每个地方。或者,我可以离开它并找到某种方式来维护这两个变量。

    或者,如果我想检查该用户是否是管理员用户并已登录,该怎么办?我最终可能会为此检查会话中的两个不同变量。但是,我可以将其封装到一种方法中并缩短我的代码。

    这有助于其他程序员查看您的代码,因为它变得更容易阅读,并且他们在查看代码时不必“思考”太多。他们可以转到该方法并看到只有一种方法可以让用户登录。它也对您有所帮助,因为如果您想让“已登录”检查更加复杂,您只需去一个地方进行更改,而不是尝试使用您的 IDE 进行全局查找并尝试以这种方式进行更改。

    同样,这是一个简单的示例,但取决于您如何使用会话,这种使用方法和类来保护访问的途径可以让您的生活更轻松。

    【讨论】:

      【解决方案4】:

      我完全不建议通过引用传递超全局。在您的课程中,不清楚您正在修改的是会话变量。另外,请记住 $_SESSION 在课堂之外的任何地方都可用。从面向对象的角度来看,能够通过修改与类无关的变量来从类外部修改类内部的变量是非常错误的。拥有公共属性被认为是一种不好的做法,这是最糟糕的。

      【讨论】:

        【解决方案5】:

        我在研究我的新 PHP 框架时找到了自己的方式。

        验证输入非常重要。但是,我确实经常发现自己回到了这样的代码:

        function get( $key, $default=FALSE ){
            return (isset($_GET[$key]) ? $_GET[$key]:$default);
        }
        function post( $key, $default=FALSE ){
            return (isset($_POST[$key]) ? $_POST[$key]:$default);
        }
        function session( $key, $default=FALSE ){
            return (isset($_SESSION[$key]) ? $_SESSION[$key]:$default);
        }
        

        然后我像这样使用它:

        $page = get('p', 'start');
        
        $first_name = post('first_name');
        $last_name = post('last_name');
        $age = post('age', -1);
        

        我发现,由于我对不同项目的验证有完全不同的要求,因此处理所有案例的任何类都必须非常庞大和复杂。所以我用普通 PHP 编写验证代码。

        【讨论】:

          【解决方案6】:

          这对PHP不好用。

          直接获取 $_SESSION 变量:

          $id   = $_SESSION['id'];
          $hash = $_SESSION['hash'];
          

          等等

          【讨论】:

          • 如果数组中不存在变量,则得到一个不错的 E_NOTICE... -> -1
          • 你是如何获得 $_SESSION 的?
          猜你喜欢
          • 2017-12-30
          • 2015-12-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多