简介

即使开发一个新的大型PHP程序,你也不可避免的要使用到全局数据,因为有些数据是需要用到你的代码的不同部分的。一些常见的全局数据有:程序设定类、数据库连接类、用户资料等等。有很多方法能够使这些数据成为全局数据,其中最常用的就是使用“global”关键字申明,稍后在文章中我们会具体的讲解到。
   使用“global”关键字来申明全局数据的唯一缺点就是它事实上是一种非常差的编程方式,而且经常在其后导致程序中出现更大的问题,因为全局数据把你代码中原本单独的代码段都联系在一起了,这样的后果就是如果你改变其中的某一部分代码,可能就会导致其他部分出错。所以如果你的代码中有很多全局的变量,那么你的整个程序必然是难以维护的。
   本文将展示如何通过不同的技术或者设计模式来防止这种全局变量问题。当然,首先让我们看看如何使用“global”关键字来进行全局数据以及它是如何工作的。

使用全局变量和“global”关键字

PHP默认定义了一些“超级全局(Superglobals)”变量,这些变量自动全局化,而且能够在程序的任何地方中调用,比如$_GET和$_REQUEST等等。它们通常都来自数据或者其他外部数据,使用这些变量通常是不会产生问题的,因为他们基本上是不可写的。
但是你可以使用你自己的全局变量。使用关键字“global”你就可以把全局数据导入到一个函数的局部范围内。如果你不明白“变量使用范围”,请你自己参考PHP手册上的相关说明。

下面是一个使用“global”关键字的演示例子:

以下为引用的内容:
<?php 
$my_var = 'Hello World'; 
test_global(); 
function test_global() { 
    // Now in local scope 
    // the $my_var variable doesn't exist 
    // Produces error: "Undefined variable: my_var" 
    echo $my_var; 
    // Now let's important the variable 
    global $my_var; 
    // Works: 
    echo $my_var; 

?>

正如你在上面的例子中看到的一样,“global”关键字是用来导入全局变量的。看起来它工作的很好,而且很简单,那么为什么我们还要担心使用“global”关键字来定义全局数据呢?
下面是三个很好的理由:
1、  代码重用几乎是不可能的。
如果一个函数依赖于全局变量,那么想在不同的环境中使用这个函数几乎是不可能的。另外一个问题就是你不能提取出这个函数,然后在其他的代码中使用。
2、  调试并解决问题是非常困难的。
跟踪一个全局变量比跟踪一个非全局变量困难的多。一个全局变量可能会在一些不明显的包含文件中被重新定义,即使你有一个非常好的程序编辑器(或者IDE)来帮助你,你也得花了几个小时才能发现这个问题所在。
3、  理解这些代码将是非常难的事情。
你很难弄清楚一个全局变量是从哪里来得,它是用来做什么的。在开发的过程中,你可能会知道知道每一个全局变量,但大概一年之后,你可能会忘记其中至少一般的全局变量,这个时候你会为自己使用那么多全局变量而懊悔不已。
那么如果我们不使用全局变量,我们该使用什么呢?下面让我们看看一些解决方案。

使用函数参数

停止使用全局变量的一种方法就是简单的把变量作为函数的参数传递过去,如同下面所示:

以下为引用的内容:

<?php 
$var = 'Hello World'; 
test ($var); 
function test($var) { 
    echo $var; 

?>

如果你仅仅只需要传递一个全局变量,那么这是一种非常优秀甚至可以说是杰出的解决方案,但是如果你要传递很多个值,那该怎么办呢?
比如说,假如我们要使用一个数据库类,一个程序设置类和一个用户类。在我们代码中,这三个类在所有组件中都要用到,所以必须传递给每一个组件。如果我们使用函数参数的方法,我们不得不这样:

以下为引用的内容:
<?php 
$db = new DBConnection; 
$settings = new Settings_XML; 
$user = new User; 
test($db, $settings, $user); 
function test(&$db, &$settings, &$user) { 
    // Do something 

?>

显然,这是不值得的,而且一旦我们有新的对象需要加入,我们不得不为每一个函数增加多一个函数参数。因此我们需要用采用另外一种方式来解决。

使用单件(Singletons)

解决函数参数问题的一种方法就是采用单件(Singletons)来代替函数参数。单件是一类特殊的对象,它们只能实例化一次,而且含有一个静态方法来返回对象的接口。下面的例子演示了如何构建一个简单的单件:

以下为引用的内容:
<?php 
// Get instance of DBConnection 
$db =& DBConnection::getInstance(); 
// Set user property on object 
$db->user = 'sa'; 
// Set second variable (which points to the same instance) 
$second =& DBConnection::getInstance(); 
// Should print 'sa' 
echo $second->user; 
Class DBConnection { 
    var $user; 
    function &getInstance() { 
        static $me; 
        if (is_object($me) == true) { 
            return $me; 
        } 
        $me = new DBConnection; 
        return $me; 
    } 
    function connect() { 
        // TODO 
    } 
    function query() { 
        // TODO 
    } 

?>

上面例子中最重要的部分是函数getInstance()。这个函数通过使用一个静态变量$me来返回这个类的实例,从而确保了只有一个DBConnection类的实例。
使用单件的好处就是我们不需要明确的传递一个对象,而是简单的使用getInstance()方法来获取到这个对象,就好像下面这样:

以下为引用的内容:
<?php 
function test() { 
    $db = DBConnection::getInstance(); 
    // Do something with the object 

?>

然而使用单件也存在一系列的不足。首先,如果我们如何在一个类需要全局化多个对象呢?因为我们使用单件,所以这个不可能的(正如它的名字是单件一样)。另外一个问题,单件不能使用个体测试来测试的,而且这也是完全不可能的,除非你引入所有的堆栈,而这显然是你不想看到的。这也是为什么单件不是我们理想中的解决方法的主要原因。

注册模式

让一些对象能够被我们代码中所有的组件使用到(译者注:全局化对象或者数据)的最好的方法就是使用一个中央容器对象,用它来包含我们所有的对象。通常这种容器对象被人们称为一个注册器。它非常的灵活而且也非常的简单。一个简单的注册器对象就如下所示:

以下为引用的内容:

<?php 
Class Registry { 
    var $_objects = array(); 
    function set($name, &$object) { 
        $this->_objects[$name] =& $object; 
    } 
    function &get($name) { 
        return $this->_objects[$name]; 
    } 

?>

使用注册器对象的第一步就是使用方法set()来注册一个对象:

以下为引用的内容:
<?php 
$db = new DBConnection; 
$settings = new Settings_XML; 
$user = new User; 
// Register objects 
$registry =& new Registry; 
$registry->set ('db', $db); 
$registry->set ('settings', $settings); 
$registry->set ('user', $user); 
?>

现在我们的寄存器对象容纳了我们所有的对象,我们指需要把这个注册器对象传递给一个函数(而不是分别传递三个对象)。看下面的例子:

以下为引用的内容:
<?php 
function test(&$registry) { 
    $db =& $registry->get('db'); 
    $settings =& $registry->get('settings'); 
    $user =& $registry->get('user'); 
    // Do something with the objects 

?>

注册器相比其他的方法来说,它的一个很大的改进就是当我们需要在我们的代码中新增加一个对象的时候,我们不再需要改变所有的东西(译者注:指程序中所有用到全局对象的代码),我们只需要在注册器里面新注册一个对象,然后它(译者注:新注册的对象)就立即可以在所有的组件中调用

为了更加容易的使用注册器,我们把它的调用改成单件模式(译者注:不使用前面提到的函数传递)。因为在我们的程序中只需要使用一个注册器,所以单件模式使非常适合这种任务的。在注册器类里面增加一个新的方法,如下所示:

以下为引用的内容:

<? 
function &getInstance() { 
    static $me; 
    if (is_object($me) == true) { 
        return $me; 
    } 
    $me = new Registry; 
    return $me; 

?>

这样它就可以作为一个单件来使用,比如:

以下为引用的内容:
<?php 
$db = new DBConnection; 
$settings = new Settings_XML; 
$user = new User; 
// Register objects 
$registry =& Registry::getInstance(); 
$registry->set ('db', $db);

(转) 在PHP中使用全局变量的更多相关文章

  1. C语言中定义全局变量

    (1)在C语言的头文件中定义变量出现的问题 最好不要傻嘻嘻的在头文件里定义什么东西.比如全局变量: /*xx头文件*/ #ifndef  _XX_头文件.H #define  _XX_头文件.H in ...

  2. js中的全局变量和静态变量的使用, js 的调试?- 如果js出错, js引擎 就会停止, 这会 导致 后面的 html中 refer 该函数时, 会报错 函数为定义!!

    效果里面的函数, 如show, hide,slideDown等, 这些都叫 "效果"函数, 但是里面可以包含动画, 也可以 不包含动画. 动画,是指 元素 的内容 是 逐渐 显示/ ...

  3. c语言头文件中定义全局变量的问题

    c语言头文件中定义全局变量的问题 (转http://www.cnblogs.com/Sorean/) 先说一下,全局变量只能定义在 函数里面,任意函数,其他函数在使用的时候用extern声明.千万不要 ...

  4. 在android.app.Application中定义全局变量

    在Android应用中使用全局变量,除了public的静态变量,还有更优雅的方式是使用android.app.Application. 启动Application时,系统会创建一个PID,即进程ID, ...

  5. Yii Framework2.0开发教程(4)在yii中定义全局变量

    在yii中定义全局变量最好的地方是入口脚本处.也就是web目录中的index.php文件 比如我们在defined('YII_ENV') or define('YII_ENV', 'dev');后写上 ...

  6. python中的全局变量和局部变量

    python中,对于变量作用域的规定有些不一样. 在诸如C/C++.java等编程语言中,默认在函数的内部是能够直接訪问在函数外定义的全局变量的,可是这一点在python中就会有问题.以下是一个样例. ...

  7. java中的全局变量如何实现?ThreadLocal~

    全局变量就是不管你在哪里,都能够直接引用的变量,还不用担心各种问题.每个语言都有自己的全局变量,我想! 一般地,面向过程的语言当中,可能就是一个声明在最前面的变量,后面的代码直接引用,就成了全局变量! ...

  8. python线程中的全局变量与局部变量

    在python多线程开发中,全局变量是多个线程共享的数据,局部变量是各自线程的,非共享的. 如下几种写法都是可以的: 第一种:将列表当成参数传递给线程 from threading import Th ...

  9. Loadrunner 运行场景-场景中的全局变量与关联结果参数

    运行场景-场景中的全局变量与关联结果参数   by:授客 QQ:1033553122 A.   全局变量 实验1: globals.h #ifndef _GLOBALS_H #define _GLOB ...

  10. python中的全局变量和局部变量(转)

    python中,对于变量作用域的规定有些不一样. 在诸如C/C++.java等编程语言中,默认在函数的内部是能够直接訪问在函数外定义的全局变量的,可是这一点在python中就会有问题.以下是一个样例. ...

随机推荐

  1. iis+php(FastCGI)

    1. 安装 IIS 时选择添加 CGI 功能 2. 安装 PHP, 2.1 下载 nts 版本 (非线程安全版本) zip 压缩包,下载对应的 vc++ 运行时(php官网下载页面左侧有下载链接) 2 ...

  2. 卸载Windows,安装纯Linux

    操作步骤 下载安装一键GHOST优盘版 参考帮助文档,安装U盘启动 重启电脑,F12进入模式选择界面,选择USB device 选择DOS 工具 进入Disk Genius 删除掉除主分区(0)外的其 ...

  3. Android中调用高德导航(组件)

    btn_.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //调用 ...

  4. Python中socket经ssl加密后server开多线程

            前几天手撸Python socket代码,撸完之后经过ssl加密,确保数据的安全,外加server端开启多线程保证一个客户端连接有一个线程来服务客户端,走了不少的弯路,网上的信息啥的要 ...

  5. 与native交互时会出现的问题

    1.jsbridge:  可以用jsbridge与native交互,这属于第三方库,前端后端都需要加jsbridge 2.可以直接调用原生的方法,ios:  window.webkit.message ...

  6. Failed to start NodeManager caused by "/var/lib/hadoop-yarn/yarn-nm-recovery/yarn-nm-state/LOCK: Permission denied"

      Hadoop 安装步骤: 0. 安装前准备(节点机器,环境设置,yum源设置) 1. 配置并安装Cloudera-Manager 2. 启动 CM 服务 3. 安装CDH,并配置集群 4. 启动 ...

  7. MongoDB实战开发

    [目标]:本文将以实战的形式,向您展示如何用C#访问MongoDB,完成常见的数据库操作任务, 同时,也将介绍MongoDB的客户端(命令行工作模式)以及一些基础的命令. [说明]:MongoDB是什 ...

  8. springboot实现服务器端消息推送(H5原生支持)

    随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端的通信功 ...

  9. Fiddler使用一(Fiddler简介)

    参考文章:http://blog.csdn.net/ohmygirl/article/details/17846199 1.为什么是Fiddler? 抓包工具有很多,小到最常用的web调试工具fire ...

  10. Netflix Hystrix笔记

    maven引入 <dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hyst ...