在PHP中使用全局变量的几种方法
即使开发一个新的大型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);
$registry->set ('settings', $settings);
$registry->set ('user', $user);
function test() {
$registry =& Registry::getInstance();
$db =& $registry->get('db');
$settings =& $registry->get('settings');
$user =& $registry->get('user');
// Do something with the objects
}
?>
正如你看到的,我们不需要把私有的东西都传递到一个函数,也不需要使用“global”关键字。所以注册器模式是这个问题的理想解决方案,而且它非常的灵活。
请求封装器
虽然我们的注册器已经使“global”关键字完全多余了,在我们的代码中还是存在一种类型的全局变量:超级全局变量,比如变量$_POST,$_GET。虽然这些变量都非常标准,而且在你使用中也不会出什么问题,但是在某些情况下,你可能同样需要使用注册器来封装它们。
一个简单的解决方法就是写一个类来提供获取这些变量的接口。这通常被称为“请求封装器”,下面是一个简单的例子:
复制代码 代码如下:
<?php
Class Request {
var $_request = array();
function Request() {
// Get request variables
$this->_request = $_REQUEST;
}
function get($name) {
return $this->_request[$name];
}
}
?>
上面的例子是一个简单的演示,当然在请求封装器(request wrapper)里面你还可以做很多其他的事情(比如:自动过滤数据,提供默认值等等)。
下面的代码演示了如何调用一个请求封装器:
复制代码 代码如下:
<?php
$request = new Request;
// Register object
$registry =& Registry::getInstance();
$registry->set ('request', &$request);
test();
function test() {
$registry =& Registry::getInstance();
$request =& $registry->get ('request');
// Print the 'name' querystring, normally it'd be $_GET['name']
echo htmlentities($request->get('name'));
}
?>
正如你看到的,现在我们不再依靠任何全局变量了,而且我们完全让这些函数远离了全局变量。
结论
在本文中,我们演示了如何从根本上移除代码中的全局变量,而相应的用合适的函数和变量来替代。注册模式是我最喜欢的设计模式之一,因为它是非常的灵活,而且它能够防止你的代码变得一塌糊涂。
另外,我推荐使用函数参数而不是单件模式来传递注册器对象。虽然使用单件更加轻松,但是它可能会在以后出现一些问题,而且使用函数参数来传递也更加容易被人理解。
转载于:https://www.cnblogs.com/moqiang02/p/4061547.html
相关文章:

python处在哪个阶段_python 基础复习
1、简述cpu、内存、硬盘的作用cpu(1)cpu:处理逻辑运算、算术运算(2)cpu:接受指令传给电脑硬件,让其运行内存:(1)内存:从硬盘中读取数据,供其cpu调取指令运行,短暂的存贮数据;运行速度…

android用户界面之WebView教程实例汇总
一、WebView教程1.Android---UI篇---WebView(网络视图)http://www.apkbus.com/android-14259-1-1.html2.webview学习记录http://www.apkbus.com/android-44567-1-1.html3.Android中使用WebView, WebChromeClient和WebViewClient加载网页http://www.apkbu…

java下输出中文的一点研究
网上或者大部分书上都说Java中输出中文使用FileReader类就可以了,但是当你读取一个中文文档时,你会发现,除了乱码,还是乱码。究其原因,这其实是文件流读取时使用的编码方式和文件本身编码方式不同,造成读取出来文件乱码…

C++智能指针:unique_ptr详解
文章目录unique_ptr描述声明作用函数指针描述总结unique_ptr描述 声明 头文件:<memory> 模版类: 默认类型template <class T, class D default_delete<T>> class unique_ptr数组类型template <class T, class D> class uniq…

川崎机器人示教盒维修_专业维修丹阳市KUKA库卡KRC2库卡C4主板维修{苏州罗韦维修}...
发那科机器人故障分析 发那科伺服放大器上LED指示灯故障维修大全_发那科机器人维修,FANUC机器人保养,伺服电机示教器减速器维修,驱动器维修,苏州发那科机器人维修,本文主要介绍了发那科伺服放大器上因故障而出现的各种…

js 文本反向排列显示
一次面试遇到这样的题目 反向输出“how are you” 解决方法 <script language"JavaScript">var message1"how are you";var message2"";for (countmessage1.length; count > 0; count--)message2message1.substring(count,count-1);doc…

2012年12月4期手机网页开发
最近主要做手机上页面的开发,主要框架是,手机安装客户端,加载主站手机应用页面,手机客户端配合主站功能实现本地扫描或重力感应的效果。 针对安卓系统,在开发和调试时发现如下问题:1路径错误&#…

zabbix4.0构建实录
【Nginx】 #wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo [rootcentos ~]# yum -y install zlib pcre pcre-devel openssl openssl-devel[rootcentos ~]# useradd -s /sbin/nologin nginx [rootzabbix-server ~]# yum install -y nginx 【M…

ceph-kvstore-tool 工具使用详解
文章目录简介使用总结简介 ceph-kvstore-tool工具是用来获取存放在leveldb或者rocksdb数据库中的键值元数据。并且该工具能够对kvstore中的数据进行配置,就像是对离线后的osd操作osd map一样 使用该工具,需要安装ceph-test-12.2.1.06-0.el7.centos.x86_…

springboot 订单重复提交_Spring Boot (一) 校验表单重复提交
一、前言在某些情况下,由于网速慢,用户操作有误(连续点击两下提交按钮),页面卡顿等原因,可能会出现表单数据重复提交造成数据库保存多条重复数据。存在如上问题可以交给前端解决,判断多长时间内不能再次点击保存按钮&a…

智能会议白板系统每日开发记录
智能会议白板系统,在开发过程中,整个项目期限内,每月,每周,每天要做的事情,作为组长的记录,多有不足之处,望指点。 转载于:https://www.cnblogs.com/mayijun/p/3458039.html

java.lang.OutOfMemoryError: PermGen space及其解决方法
PermGen space的全称是Permanent Generation space,是指内存的永久保存区域OutOfMemoryError: PermGen space从表面上看就是内存益出,解决方法也一定是加大内存。说说为什么会内存益出:这一部分用于存放Class和Meta的信息,Class在被 Load的时候被放入Per…

ceph-dencoder工具使用详解
文章目录简介使用decode命令用法encode简介 ceph-dencoder工具是一个序列化编码、解码并且打印ceph数据结构的工具。它主要用来调试和测试ceph不同版本之间的兼容性问题 该工具是由 ceph-common-12.2.1.06-0.el7.centos.x86_64 rpm包生成 本文章是根据ceph-12.2.1版本来描述改…

EBS fnd_global.apps_initialize
原型:fnd_global.apps_initialize(user_ID, Responsibility_id,Responsibility_application_id);作用:在数据库的会话中设置全局变量,和用户概要信息。参数获得:参数一,用户ID select user_idfrom fnd_userwhere user_…

js判断鼠标靠近屏幕最侧面的监听_threejs按鼠标位置缩放场景
threejs的orbitcontrol,默认的缩放模式为整体以target为中心进行缩放。有时候,我们想让场景按照鼠标位置进行缩放,体验起来就和地图的缩放一样,最直观的感觉就是整个场景会越来越靠近鼠标点的位置,而不是整体的缩放大小…

hibernate中多对多分解成一对多,
1,参考:http://blog.csdn.net/yaerfeng/article/details/6969632

C++ 函数参数 值传递与引用传递
以前我们在C语言中函数参数传递过程中,如果我们想要让当A函数作用域中的变量经过B函数处理之后的数值仍然在A函数中生效,这个时候函数参数的传递时需要引用方式去传递,方式如下: #include <stdio.h> //函数参数为指针&…

SharePoint 2013 图文开发系列之代码定义列表
在SharePoint的开发中,用Visual Studio自定义列表是经常会用到的,因为很多时候,我们并不会手动创建列表,而手动创建列表在测试服务器和正式机之间同步字段,也很麻烦,所以我们经常用代码来定义列表或者文档库…

arduino下载库出错_【arduino】DIY音乐播放器,arduino播放wav音乐,TRMpcm库测试及使用...
微信关注 “DLGG创客DIY”设为“星标”,重磅干货,第一时间送达。arduino特点库超多,想必大家都领教了,今天来分享一下之前玩过的TRMpcm库。这个库是干嘛用的?简单粗暴用arduino(这里特指arduino官方那几个板子uno、nan…

vim替换技巧4
、 转自:http://www.confay.com/2008/03/vim4.html [技巧一] 第一个是在VIM邮件列表中看到的,给出了一个如何统计文章字数的方法。 统计一个完整文件的字数,可以使用Unix下的wc工具,它能够统计一个文件的行数、单词数和字符数。 如…

spark1.x和2.xIterable和iterator兼容问题
1. spark 1.x 升级到spark 2.x 对于普通的spark来说,变动不大 : 1 举一个最简单的实例:spark1.x public static JavaRDD<String> workJob(JavaRDD<String> spark1Rdd) {JavaPairRDD<String, Integer> testRdd spark1Rdd.flatMapToPair(new PairFlatMapFunct…

C++ 拷贝构造函数和重载赋值运算符的区别
文章目录拷贝构造函数重载赋值运算符赋值运算符和拷贝构造函数最大区别是赋值运算符没有新的对象生成,而拷贝构造函数会生成新的对象。 为了更加形象 准确得描述 赋值运算符和拷贝构造函数得区别,将详细通过代码展示两者之间得差异。 拷贝构造函数 首先…

单元格内多个姓名拆分成一列_EXCEL拆分单元格中的姓名,这都不叫事儿
作者:祝洪忠 转自:Excel之家ExcelHome小伙伴们好啊,今天老祝和大家来分享一个数据整理的技巧。下面的表格形式,想必大家不会陌生吧:在这个表格内,同一个部门的人员名单都挤到一个单元格内。现在问题来了&am…
3.1 A Historical Perspective 历史观点
1.从1978年的8086到现在的2008年core i7 ,从29K个晶体管到781M个晶体管,地址线(也叫地址位长(bit long))8086只有20个地址线,1982年,MS-windows 使用80286平台开发了自己的windows。直到1985年,i386正式扩展…

idea中 maven打包时时报错User setting file does not exist C:\Users\lenevo\.m2\setting.xml,
第一种错误 :idea中 maven打包时时报错User setting file does not exist C:\Users\lenevo\.m2\setting.xml, 解决方案如下:将maven的安装目录\conf目录下的setting.xml拷贝到C:\Users\lenevo\.m2目录下即可。 第二种错误: This a…

关于部署osd过程中:Device is in use by a device-mapper mapping问题解决
ceph环境:12.2.1 使用古老的ceph-disk工具部署osd,仅仅prepare过程中就出现如上所示问题 Device is in use by a device-mapper mapping md127 解决方法如下: 由于device-mapper为系统自己的磁盘映射器,此时检查系统是否有逻辑卷 pvs lvs vg…

spyder一打开就卡了_欧姆龙plc 用 SD 卡上传/下载程序
以Nx102为例,NJ类似。使用 SD 卡将 Sysmac Studio 编写的程序传入 NX1P2 内; 使用 SD 卡对NX1P2 的程序进行备份, 查看备份的程序, 并把备份的程序传入另一台对应型号的 NX1P2 内。一、 使用 SD 卡将 Sysmac Studio 写的程序传入 …

Unity3D 中 2D_Toolkit插件下载 和 导入方法
Unity3D 中 2D_Toolkit插件下载 和 导入方法 1.你把下载来的包放到 安装目录:Editor\Standard Packages里面。 2.然后按ctrl9,进入asset store,等页面加载。 3.页面加载成功后,如果有账号,就登录,没有账号先…

Emacs 使用YASnippet
<?xml version"1.0" encoding"utf-8"?> Emacs 使用YASnippetUP | HOME Emacs 使用YASnippet Table of Contents 1 安装YASnippent2 安装 org-mode字典3 org-mode中使用教程4 YASnippet增加模板1 安装YASnippent $ cd ~/.emacs.d/plugins $ git c…

ruoyi后台管理系统分析(三)---admin包
三、admin包 --web包 -----controller包 -----------common包 CommonController.java------通用请求处理 package com.ruoyi.web.controller.common;import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import javax.servlet.http.HttpServletRequest;…