当前位置: 首页 > 知识库问答 >
问题:

停止在PHP中使用'global'

韩麒
2023-03-14

我有一个config.php包含到每个页面。在配置中,我创建了一个类似于以下内容的数组:

$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来访问它-这就是我想要摆脱的!

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

谁能解释一下,为什么我不应该在我的示例中使用global?有人说这是一个坏的语气,其他人说这是不安全的?

我在哪里以及如何使用它的示例:

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

按照Vilx的建议,将所有这些都放在类中会很酷,但在这种情况下,我将如何将其与以下循环联系起来,即从数据库中提取配置分配$config数组,下面是一个示例:

$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();
    }
}

此外,我必须从配置中设置的函数中访问其他vars,其中很少,例如:$db$语言等。

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

我在函数中阅读了PHP全局函数,Gordon以非常好的方式解释了为什么不应该使用global。我同意一切,但在我的情况下,我不使用global来重新分配变量,这将导致,就像他说的那样,

在同一个PHP全局函数中,deceze说:“反对全局的一个主要原因是它意味着函数依赖于另一个作用域。这将很快变得混乱。”

但是我在这里谈论的是基本的“INIT”。我基本上设置了定义,但使用了vars-这在技术上是错误的。但是你的函数不依赖于任何东西——但是你可以记住的一个var$db的名字?它确实需要全局使用$db,这里的依赖在哪里以及如何使用它?

另外,我刚才有一个想法,我们在这里面临着两种不同思想的冲突,例如:我的(还没有很好地理解面向对象编程)和那些在OOP中可以被称为大师(从我目前的观点来看)的人——他们对我来说显而易见的东西产生了新的问题。我想这就是为什么人们一遍又一遍地问这个问题。就我个人而言,这已经变得更加清晰,但仍然有一些事情需要澄清。


共有3个答案

夏侯和韵
2023-03-14

对于您的情况,我将只创建一个文件常量。php和定义(如果您的目的是在执行时永远不要更改这些“变量”):

define('SITE_NAME','site name');

define('BASE_PATH','/home/docs/public_html/');

...

包括这个常量。php在您需要的所有文件中:

include_once('constants.php');
从渊
2023-03-14

我用一门课解决了这个问题:

class Config
{
    public static $SiteName = 'My Cool Site';
}

function SomeFunction
{
    echo 'Welcome to ' , Config::$SiteName;
}

fcortes关于使用常量的建议也是一个很好的建议。我只想建议给所有常量一个前缀,比如CFG\u SITE\u NAME,以避免与其他常量发生意外的名称冲突。

叶淇
2023-03-14

反对全局变量的一点是它们非常紧密地耦合代码。您的整个代码库依赖于a)变量名称$config和b)该变量的存在。如果您想重命名变量(无论出于什么原因),您必须在代码库中的任何地方重命名。您也不能再使用任何独立于变量的代码。

带有全局变量的示例:

require 'SomeClass.php';

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

在上述行中的任何地方,您都可能会得到一个错误,因为类或lass.php中的某些代码隐含地依赖于一个全局变量$config。尽管只是看了看班级,但没有任何迹象表明这一点。为了解决这个问题,你必须这样做:

$config = array(...);

require 'SomeClass.php';

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

如果您没有在$config中设置正确的键,此代码仍可能在某个地方失败。由于配置数组的哪些部分需要或不需要以及何时需要并不明显,因此很难重新创建正确的环境以使其正确运行。如果你碰巧已经有了一个变量$config,在你想使用某些类的任何地方,它也会产生冲突。

因此,与其创建隐式的、不可见的依赖关系,不如注入所有依赖关系:

require 'SomeClass.php';

$arbitraryConfigVariableName = array(...);

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

通过将配置数组显式地作为参数传递,上述所有问题都得到了解决。只需在应用程序中传递所需信息即可。它还使应用程序的结构和流程以及与之相关的内容更加清晰。如果您的应用程序当前是一个大泥球,要达到这种状态可能需要进行一些重构。

代码库越大,就越需要将各个部分彼此解耦。如果每个部分都依赖于代码库中的其他部分,那么就不能单独测试、使用或重用其中的任何部分。这只会演变成混乱。要将各个部分彼此分离,请将它们编码为类或函数,这些类或函数将所有必需的数据作为参数。这将在代码的不同部分之间创建干净的接缝(接口)。

尝试将您的问题结合到一个示例中:

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。每个函数和类都以函数参数的形式传递其所有必要的数据。同样显而易见的是,上述组件可以在任何其他组合中插在一起,或者依赖关系可以很容易地替代其他组件。例如,配置根本不需要来自数据库,或者记录器可以登录到一个文件而不是数据库,而不需要知道这些。

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;
    }

}

这是一段非常简单的代码,甚至做不了什么。您可能会问为什么要将其作为面向对象的代码。关键是,这使得使用这段代码非常灵活,因为它将它与其他一切完美隔离。你给一个数据库连接,你得到一个具有特定语法的数组。输入→输出。清晰的接缝,清晰的界面,最小的,明确的责任。你可以用一个简单的函数做同样的事情。

对象的额外优势在于,它甚至进一步将调用loadConfigurationFromDatabase的代码与该函数的任何特定实现分离。如果只使用全局函数loadConfigurationFromDatabase(),基本上又会遇到相同的问题:在尝试调用该函数时需要定义该函数,如果要用其他函数替换它,则会出现命名冲突。通过使用对象,代码的关键部分移动到这里:

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

您可以在此处将$configManager替换为具有方法loadConfigurationFromDatabase的任何其他对象。那是“鸭子打字”。您不在乎$configManager到底是什么,只要它有一个方法loadConfigurationFromDatabase。如果它走路像鸭子,嘎嘎叫像鸭子,那它就是鸭子。或者更确切地说,如果它有一个loadConfigurationFromDatabase方法并返回一个有效的配置数组,那么它就是某种ConfigManager。您已将代码与一个特定变量$config、一个特定的loadConfigurationFromDatabase函数甚至一个特定的ConfigManager解耦。所有部件都可以在任何地方进行更改、交换、替换和动态加载,因为代码不依赖于任何一个特定的其他部件。

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

 类似资料:
  • 问题内容: 我有一个包含在每个页面中。在配置中,我创建一个看起来像这样的数组: 然后,我有了,几乎每个页面也都包含,我必须使用它来访问它- 这 就是我要摆脱的东西! 如何在不使用代码的情况下访问代码的其他部分? 谁能解释, 为什么 我不应该在示例中使用?有人说这是一个不好的语气,有人说这是不安全的? 编辑1: 我在哪里以及如何使用它的示例: 编辑2: 按照Vilx的建议,将所有这些内容放入类中会很

  • 问题内容: 如果我在命令行中键入,则可以看到以下行: 要 启动 此cronjob,我需要使用命令编辑文件,删除该行开头的注释字符,保存已编辑的文件,然后退出编辑器。 要 停止 此cronjob,请执行相同的步骤,但在该行的开头添加注释字符。 我想使用 PHP脚本 实现完全相同的效果,而不是手动编辑文件。 问题答案: 我进行了一些研究,并在论坛中找到以下消息: 将EDITOR环境变量设置为php脚本

  • 问题内容: 在装有PHP 5.5.3的新Macbook上安装MAMP。 重新加载和刷新无济于事。依然没有。Google过了几分钟,试图找出问题所在,然后回来刷新。有用。有没有搞错? 我进入php.ini并禁用了所有新的OPcache,并将默认缓存时间设置为0。向文档添加了标头以强制不进行缓存。还是同样的问题。这里到底发生了什么? 网络标签显示的是HTTP 200请求,因此文件中的任何新HTML都可

  • 启动无限循环后,我无法关闭JFrame。 我想停止无限循环使用停止按钮。 我用开始按钮开始一个无限循环。我想用“停止”按钮关闭那个回路。 > if(stop.getModel(). isP的()){中断;}不工作 actionListener用于识别按钮单击并在循环也不起作用时使用中断语句终止 点击停止按钮,无限循环必须终止。在使用start Button启动无限循环后,我无法使用JFrame中的

  • 我有一个简单的UILabel,当你长按它时,它就会开始旋转。我使用以下代码成功地实现了这一点: 用户只需双击标签,就可以在任何时候停止这个动画。下面是代码: 问题是,下次你长按标签时,它没有反应。旋转动画不会再次启动。你必须再次点击它才能让它再次运行。 所以模式是: 1)长按-动画开始 2)双击-动画停止 3)长按-什么都没有发生 4)再次长按-动画再次开始 我该怎么解决这个问题?

  • 我有一个程序,它每秒钟响一次,直到停止。问题是,在我按下“开始”并发出嘟嘟声后,我无法单击“停止”按钮,因为窗口冻结。欢迎任何帮助。