停止在PHP中使用`global` [英] Stop using `global` in PHP

查看:66
本文介绍了停止在PHP中使用`global`的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每个页面都包含一个config.php.在配置中,我创建一个看起来像这样的数组:

I have a config.php that is included to each page. In config I create an array that looks something like:

$config = array();
$config['site_name']      = 'Site Name';
$config['base_path']      = '/home/docs/public_html/';
$config['libraries_path'] = $config['base_path'] . '/libraries';
//etc...

然后我有function.php,几乎每个页面都包含该内容,在这里我必须使用global $config来访问它-而 this 是我想要摆脱的内容的!

Then I have function.php, that is also included to almost each page, where I have to use global $config to get access to it - and this is what I would like to get rid of!

如何在不使用global的情况下访问代码其他部分的$config?

How do I access $config in the other parts of my code without using global?

任何人都可以解释,为什么,我不应在示例中使用global?有人说这是一个不好的语气,有人说这是不安全的?

Could anyone explain, WHY I shouldn't use global in my example? Some say it's a bad tone, others say it's not secure?

在何处以及如何使用它的示例:

Example of where and how I use it:

function conversion($Exec, $Param = array(), $Log = '') {
        global $config;
        $cmd = $config['phppath'] . ' ' . $config['base_path'] . '/' . $Exec;
                foreach ($Param as $s)
                {
                    $cmd .= ' ' . $s;
                }
 }

按照 Vilx 的建议,将所有这些内容都放在类中是很酷的,但是在这种情况下,如何我将其与从数据库中提取config keyvalue的以下循环绑定.
我简化了分配$config数组的想法,这是一个示例:

EDIT 2:

Putting all of this in the class, as suggested by Vilx, would be cool but in this case, how would I tie it with the following loop that is extracting config key and value from database.
I oversimplified the idea of assigning $config array, here is an example:

$sql = "SELECT * from settings";
$rsc = $db->Execute($sql);
if ( $rsc ) {
    while(!$rsc->EOF) {
        $field = $rsc->fields['setting_options'];
        $config[$field] = $rsc->fields['setting_values'];
        @$rsc->MoveNext();
    }
}

此外,我还必须从config中设置的功能访问其他vars,这些功能很少,例如:$db$language等.

EDIT 3:

Besides, I have to access other vars from functions that are set in config and it's few of them, e.g.:$db, $language and etc.

如果我把它们放在课堂上,它将真的解决任何问题吗?如果我使用global,它将真正改变什么?

If I put them in the class will it really solve anything? If I use global what does it really change?

我阅读了 PHP全局函数,其中

I read PHP global in functions where Gordon explains in the very nice way why you shouldn't use global. I agree on everything but I don't use global in my case to reassign the variables, which will result in, like he said, <-- WTF!!, ;)) yeah agree, it's crazy. But if I just need to access database from a function just by using global $db where is the problem in this case? How do you do this otherwise, without using global?

在同一PHP全局函数中, deceze 说:反对global的一个重要原因是它表示该函数依赖于另一个范围.这将很快变得混乱."

In the same PHP global in functions deceze says: "The one big reason against global is that it means the function is dependent on another scope. This will get messy very quickly."

但是我在这里谈论的是基本的"INIT".我基本上设置了define,但使用了vars-嗯,这在技术上是错误的.但是您的函数不依赖任何内容,而是您可以记住的一个var $db的名称?实际上,在全球范围内都需要使用$db,这里的DEPENDENCY在哪里,否则如何使用它?

But I'm talking here about basic 'INIT'. I basically set define but use vars - well that is wrong in technical way. But your function is not depended on anything - but the name of one var $db that you could keep in mind? It's really global need to use $db, where is the DEPENDENCY here and how to use it otherwise?

PS 我只是有一个想法,我们在这里面临着两种不同思想的冲突,例如: mine (但还不太了解面向对象的编程)以及那些在OOP中可以被称为大师的人-对我来说对他们来说显而易见的问题引起了新的疑问.我认为这就是为什么一遍又一遍地问这个问题的原因.就我个人而言,它毕竟已经变得更加清晰,但是仍然有一些事情需要澄清.

P.S. I just had a thought, that we're facing the conflict here of two different minds, e.g.: mine (yet NOT well understanding object-oriented programming) and those who could be called gurus (from my current point of view) in OOP - what looks obvious for them for me arises new questions. I think that's why this question is being asked over and over again. Personally for me it's gotten more clear after all but still there are things to clarify.

推荐答案

反对global变量的要点是它们非常紧密地耦合代码.您的整个代码库取决于a)变量 name $config和b)该变量的存在.如果要重命名变量(无论出于何种原因),则必须在整个代码库中的任何地方.您也不能再使用任何依赖于变量的代码了.

The point against global variables is that they couple code very tightly. Your entire codebase is dependent on a) the variable name $config and b) the existence of that variable. If you want to rename the variable (for whatever reason), you have to do so everywhere throughout your codebase. You can also not use any piece of code that depends on the variable independently of it anymore.

带有global变量的示例:

require 'SomeClass.php';

$class = new SomeClass;
$class->doSomething();

在上面几行中的任何地方,您可能会遇到错误,因为SomeClass.php中的类或某些代码隐式依赖于全局变量$config.尽管只看了课,却没有任何迹象表明这一点.要解决此问题,您必须执行以下操作:

Anywhere in the above lines you may get an error because the class or some code in SomeClass.php implicitly depends on a global variable $config. There's no indication of this whatsoever though just looking at the class. To solve this, you have to do this:

$config = array(...);

require 'SomeClass.php';

$class = new SomeClass;
$class->doSomething();

如果您未在$config中设置正确的键,则该代码可能在某些地方 still 失败.由于不清楚配置数组SomeClass的哪些部分需要或不需要以及何时需要它们,因此很难为其创建正确的环境以使其正常运行.如果您碰巧已经将变量$config用于其他任何要使用SomeClass的地方,也会造成冲突.

This code may still fail somewhere if you do not set the correct keys inside $config. Since it's not obvious what parts of the config array SomeClass needs or doesn't need and when it needs them, it's hard to recreate the correct environment for it to run correctly. It also creates conflicts if you happened to already have a variable $config used for something else wherever you want to use SomeClass.

注入所有依赖项,而不是创建隐式,不可见的依赖项:

So instead of creating implicit, invisible dependencies, inject all dependencies:

require 'SomeClass.php';

$arbitraryConfigVariableName = array(...);

$class = new SomeClass($arbitraryConfigVariableName);
$class->doSomething();

通过显式传递config数组作为参数,解决了所有上述问题.就像在应用程序内处理所需信息一样简单.它还使应用程序的结构和流程以及讨论的内容变得更加清晰.要达到这种状态(如果您的应用程序当前是一个泥泞的球),可能需要进行一些重组.

By passing the config array explicitly as a parameter, all the above problems are solved. It's as simple as handing the required information around inside your app. It also makes the structure and flow of the application and what talks to what much clearer. To get to this state if your application is currently a big ball of mud may take some restructuring.

您的代码库越大,则彼此之间越解耦越多.如果每个部分都依赖于代码库中的每个其他部分,则您根本无法单独测试,使用或重用它的任何部分.那简直变成了混乱.若要将各个部分彼此分开,请将它们编码为类或函数,这些类或函数将其所需的所有数据作为参数.这样会在代码的不同部分之间创建清晰的接缝(接口).

The bigger your codebase gets, the more you have to decouple the individual parts from each other. If every part is dependent on every other part in your codebase, you simply cannot test, use or reuse any part of it individually. That simply devolves into chaos. To separate parts from each other, code them as classes or functions which take all their required data as parameters. That creates clean seams (interfaces) between different parts of your code.

尝试将您的问题归纳为一个示例:

Trying to tie your question together into one example:

require_once 'Database.php';
require_once 'ConfigManager.php';
require_once 'Log.php';
require_once 'Foo.php';

// establishes a database connection
$db = new Database('localhost', 'user', 'pass');

// loads the configuration from the database,
// the dependency on the database is explicit without `global`
$configManager = new ConfigManager;
$config = $configManager->loadConfigurationFromDatabase($db);

// creates a new logger which logs to the database,
// note that it reuses the same $db as earlier
$log = new Log($db);

// creates a new Foo instance with explicit configuration passed,
// which was loaded from the database (or anywhere else) earlier
$foo = new Foo($config);

// executes the conversion function, which has access to the configuration
// passed at instantiation time, and also the logger which we created earlier
$foo->conversion('foo', array('bar', 'baz'), $log);

我将把各个类的实现留给读者练习.当您尝试实现它们时,您会注意到它们非常容易实现且清晰,不需要单个global.每个函数和类都以函数参数的形式传递其所有必要的数据.还应该显而易见的是,上述组件可以以任何其他组合的形式插入在一起,或者可以轻松地将依赖项替换为其他组件.例如,配置完全不需要来自数据库,或者记录器可以登录到文件而不是数据库,而无需Foo::conversion知道其中任何一个.

I'll leave to implementation of the individual classes up as an exercise for the reader. When you try to implement them, you'll notice that they're very easy and clear to implement and do not require a single global. Every function and class gets all its necessary data passed in the form of function arguments. It should also be obvious that the above components can be plugged together in any other combination or that dependencies can easily be substituted for others. For example, the configuration does not need to come from the database at all, or the logger can log to a file instead of the database without Foo::conversion having to know about any of this.

ConfigManager的示例实现:

class ConfigManager {

    public function loadConfigurationFromDatabase(Database $db) {
        $result = $db->query('SELECT ...');

        $config = array();
        while ($row = $result->fetchRow()) {
            $config[$row['name']] = $row['value'];
        }

        return $config;
    }

}

这是一个非常简单的代码,甚至没有做很多事情.您可能会问为什么要将此作为面向对象的代码.关键是,这使该代码的使用极为灵活,因为它可以将其与其他所有代码完美地隔离开来.输入一个数据库连接,返回一个具有特定语法的数组.输入→输出.清晰的接缝,清晰的界面,最少的,明确定义的职责.您可以使用一个简单的功能来做同样的事情.

It's a very simple piece of code that doesn't even do much. You may ask why you'd want this as object oriented code. The point is that this makes using this code extremely flexible, since it isolates it perfectly from everything else. You give one database connection in, you get one array with a certain syntax back. Input → Output. Clear seams, clear interfaces, minimal, well defined responsibilities. You can do the same with a simple function.

对象具有的另一个优点是,它甚至可以将调用loadConfigurationFromDatabase的代码与该函数的任何特定实现进一步分离.如果仅使用全局function loadConfigurationFromDatabase(),则基本上还会遇到相同的问题:尝试调用该函数时需要定义该函数,如果要用其他替换它,则存在命名冲突.通过使用对象,代码的关键部分移至此处:

The extra advantage an object has is that it even further decouples the code that calls loadConfigurationFromDatabase from any particular implementation of that function. If you'd just use a global function loadConfigurationFromDatabase(), you basically have the same problem again: that function needs to be defined when you try to call it and there are naming conflicts if you want to replace it with something else. By using an object, the critical part of the code moves here:

$config = $configManager->loadConfigurationFromDatabase($db);

您可以在此处用$configManager替换任何其他也具有方法loadConfigurationFromDatabase的对象.那就是鸭子打字".只要它具有方法loadConfigurationFromDatabase,您就不必关心$configManager的确切含义.如果它走路像鸭子,而嘎嘎叫鸭子,那它就是鸭子.或者更确切地说,如果它具有loadConfigurationFromDatabase方法并返回有效的配置数组,则它是某种ConfigManager.您已将代码与一个特定的变量$config,一个特定的loadConfigurationFromDatabase函数甚至某个特定的ConfigManager解耦.所有代码都可以在任何地方动态更改,更换,替换和加载,因为代码不依赖于任何其他特定的部分.

You can substitute $configManager here for any other object that also has a method loadConfigurationFromDatabase. That's "duck typing". You don't care what exactly $configManager is, as long as it has a method loadConfigurationFromDatabase. If it walks like a duck and quacks like a duck, it is a duck. Or rather, if it has a loadConfigurationFromDatabase method and gives back a valid config array, it's some sort of ConfigManager. You have decoupled your code from one particular variable $config, from one particular loadConfigurationFromDatabase function and even from one particular ConfigManager. All parts can be changed and swapped out and replaced and loaded dynamically from anywhere, because the code does not depend on any one particular other piece.

loadConfigurationFromDatabase方法本身也不依赖于任何特定的数据库连接,只要它可以在其上调用query并获取结果即可.传递给它的$db对象可能完全是伪造的,并且只要它的 interface 行为相同,就可以从XML文件或其他任何地方读取其数据.

The loadConfigurationFromDatabase method itself also does not depend on any one particular database connection, as long as it can call query on it and fetch results. The $db object being passed into it could be entirely fake and read its data from an XML file or anywhere else instead, as long as its interface still behaves the same.

这篇关于停止在PHP中使用`global`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆