本篇内容主要讲解“构建可配置PHP应用程序的方式有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“构建可配置PHP应用程序的方式有哪些”吧!
使用INI文件进行配置
PHP内建了对配置文件的支持。这是通过php.ini文件这样的初始化文件(INI)机制实现的,在php.ini文件中定义了数据库连接超时或会话如何存储等常量。如果愿意的话,可以在这个php.ini文件中为应用程序定制配置。为了说明,我将下列代码行添加到php.ini文件中。
myapptempdir=foo
然后,我编写了一个小PHP脚本来读取这个配置项,如清单1所示。
清单1.ini1.php
functionget_template_directory()
{
$v=get_cfg_var("myapptempdir");
return($v==null)?"tempdir":$v;
}
echo(get_template_directory()."\n");
?>
当在命令行中运行这段代码时,得到如下结果:
%phpini1.php
foo
%
太棒了。但为什么不能用标准的INI函数来获取myapptempdir配置项的值呢?我研究了一下,发现在大多数情况下,定制配置项不能使用这些方法来获取。然而,使用get_cfg_var函数却是可以访问的。
为使这个方法更加简单,将对变量的访问封装在第二个函数中,该函数使用配置键名及一个缺省值作为参数,如下所示。
清单2.ini2.php
functionget_ini_value($n,$dv)
{
$c=get_cfg_var($n);
return($c==null)?$dv:$c;
}
functionget_template_directory()
{
returnget_ini_value("myapptempdir","tempdir");
}
这是对如何访问INI文件的一个很好的概括,所以,如果要使用一个不同的机制或将这个INI文件存储到其他位置,就不需要为更改大量的函数而大费周折。
我不推荐使用INI文件作为应用程序的配置,这有两个理由。首先,虽然这样做较容易读取INI文件,但却几乎不可能安全地写INI文件。所以这样做只适合于只读配置项。第二,php.ini文件在服务器的所有应用程序上共享,所以我认为特定于应用程序的配置项不应该写在该文件中。
需要对INI文件了解什么呢?最重要的是如何重置include路径来添加配置项,如下所示。
清单3.ini3.php
echo(ini_get("include_path")."\n");
ini_set("include_path",
ini_get("include_path").":./mylib");
echo(ini_get("include_path")."\n");
?>
在本例中,我将我的本地mylib目录添加到了include路径中,所以能够从该目录中requirePHP文件,而不需要将该路径添加到require语句中。
PHP中的配置
通常对于在INI文件中存储配置条目的一个替代办法是使用一个简单的PHP脚本来保持数据。如下是一个样例。
清单4.config.php
#Specifythelocationofthetemporarydirectory
#
$TEMPLATE_DIRECTORY="tempdir";
?>
使用该常量的代码如下所示。
清单5.php.php
require_once'config.php';
functionget_template_directory()
{
global$TEMPLATE_DIRECTORY;
return$TEMPLATE_DIRECTORY;
}
echo(get_template_directory()."\n");
?>
该代码首先包含配置文件(config.php),接着就可以直接使用这些常量了。
使用这项技术有很多优势。首先,如果某些人仅仅浏览config.php文件,该页面是空白的。所以可以将config.php放到相同的文件中,并作为Web应用程序的根。第二,在任何编辑器中都可编辑,并且在一些编辑器中甚至具备语法着色及语法检查功能。
这项技术的缺点是,这是一个像INI文件一样的只读技术。将数据从此文件中提取出来是轻而易举的,但在该PHP文件中调整数据却很困难,在一些情况下甚至是不可能的。
下面的替代方法显示了如何编写在本质上既可读又可写的配置系统。
文本文件
前面的两个例子对于只读配置条目都是合适的,但对于既读又写的配置参数来说又如何呢?首先,看看清单6中的文本配置文件。
清单6.config.txt
#Myapplication'sconfigurationfile
Title=MyApp
TemplateDirectory=tempdir
这是同INI文件相同的文件格式,但我自己编写了辅助工具。为此,我创建了自己的Configuration类,如下所示。
清单7.text1.php
classConfiguration
{
private$configFile='config.txt';
private$items=array();
function__construct(){$this->parse();}
function__get($id){return$this->items[$id];}
functionparse()
{
$fh=fopen($this->configFile,'r');
while($l=fgets($fh))
{
if(preg_match('/^#/',$l)==false)
{
preg_match('/^(.*?)=(.*?)$/',$l,$found);
$this->items[$found[1]]=$found[2];
}
}
fclose($fh);
}
}
$c=newConfiguration();
echo($c->TemplateDirectory."\n");
?>
该代码首先创建了一个Configuration对象。该构造函数接下来读取config.txt并用解析过的文件内容来设置局部变量$items。
该脚本随后寻找TemplateDirectory,这并没有在对象中直接定义。因此,使用设置成'TemplateDirectory'的$id来调用神奇的__get方法,__get方法针对该键返回$items数组中的值。
这个__get方法特定于PHPV5环境,所以此脚本必须在PHPV5下运行。实际上,本文中所有的脚本都需要在PHPV5下运行。
当在命令行运行此脚本时,能看到下列结果:
%phptext1.php
tempdir
%
一切都在预料之中,该对象读取config.txt文件,然后为TemplateDirectory配置项获得正确的值。
但对于设置一个配置值,应该怎么做呢?在此类中建立一个新方法及一些新的测试代码,就能够得到这个功能,如下所示。
清单8.text2.php
classConfiguration
{
...
function__get($id){return$this->items[$id];}
function__set($id,$v){$this->items[$id]=$v;}
functionparse(){...}
}
$c=newConfiguration();
echo($c->TemplateDirectory."\n");
$c->TemplateDirectory='foobar';
echo($c->TemplateDirectory."\n");
?>
现在,有了一个__set函数,它是__get函数的“堂兄弟”。该函数并不为一个成员变量获取值,当要设置一个成员变量时,才调用这个函数。底部的测试代码设置值并打印出新值。
下面是在命令行中运行此代码时出现的结果:
%phptext2.php
tempdir
foobar
%
太好了!但如何能将它存储到文件中,从而将使这个改动固定下来呢?为此,需要写文件并读取它。用于写文件的新函数,如下所示。
清单9.text3.php
classConfiguration
{
...
functionsave()
{
$nf='';
$fh=fopen($this->configFile,'r');
while($l=fgets($fh))
{
if(preg_match('/^#/',$l)==false)
{
preg_match('/^(.*?)=(.*?)$/',$l,$found);
$nf.=$found[1]."=".$this->items[$found[1]]."\n";
}
else
{
$nf.=$l;
}
}
fclose($fh);
copy($this->configFile,$this->configFile.'.bak');
$fh=fopen($this->configFile,'w');
fwrite($fh,$nf);
fclose($fh);
}
}
$c=newConfiguration();
echo($c->TemplateDirectory."\n");
$c->TemplateDirectory='foobar';
echo($c->TemplateDirectory."\n");
$c->save();
?>
新的save函数巧妙地操作config.txt。我并没有仅用更新过的配置项重写文件(这样会移除掉注释),而是读取了这个文件并灵活地重写了$items数组中的内容。这样的话,就保留了文件中的注释。
在命令行运行该脚本并输出文本配置文件中的内容,能够看到下列输出。
清单10.保存函数输出
%phptext3.php
tempdir
foobar
%catconfig.txt
#Myapplication'sconfigurationfile
Title=MyApp
TemplateDirectory=foobar
%
构建可配置PHP应用程序有哪些方式
原始的config.txt文件现在被新值更新了。
XML配置文件
尽管文本文件易于阅读及编辑,但却不如XML文件流行。另外,XML有众多适用的编辑器,这些编辑器能够理解标记、特殊符号转义等等。所以配置文件的XML版本会是什么样的呢?清单11显示了XML格式的配置文件。
清单11.config.xml
tempdir
清单12显示了使用XML来装载配置设置的Configuration类的更新版。
清单12.xml1.php
classConfiguration
{
private$configFile='config.xml';
private$items=array();
function__construct(){$this->parse();}
function__get($id){return$this->items[$id];}
functionparse()
{
$doc=newDOMDocument();
$doc->load($this->configFile);
$cn=$doc->getElementsByTagName("config");
$nodes=$cn->item(0)->getElementsByTagName("*");
foreach($nodesas$node)
$this->items[$node->nodeName]=$node->nodeValue;
}
}
$c=newConfiguration();
echo($c->TemplateDirectory."\n");
?>
看起来XML还有另一个好处:代码比文本版的代码更为简洁、容易。为保存这个XML,需要另一个版本的save函数,将结果保存为XML格式,而不是文本格式。
清单13.xml2.php
...
functionsave()
{
$doc=newDOMDocument();
$doc->formatOutput=true;
$r=$doc->createElement("config");
$doc->appendChild($r);
foreach($this->itemsas$k=>$v)
{
$kn=$doc->createElement($k);
$kn->appendChild($doc->createTextNode($v));
$r->appendChild($kn);
}
copy($this->configFile,$this->configFile.'.bak');
$doc->save($this->configFile);
}
...
这段代码创建了一个新的XML文档对象模型(DocumentObjectModel,DOM),然后将$items数组中的所有数据都保存到这个模型中。完成这些以后,使用save方法将XML保存为一个文件。
使用数据库
最后的替代方式是使用一个数据库保存配置元素的值。那首先要用一个简单的模式来存储配置数据。下面是一个简单的模式。
清单14.schema.sql
DROPTABLEIFEXISTSsettings;
CREATETABLEsettings(
idMEDIUMINTNOTNULLAUTO_INCREMENT,
nameTEXT,
valueTEXT,
PRIMARYKEY(id)
);
这要求进行一些基于应用程序需求的调整。例如,如果想让配置元素按照每个用户进行存储,就需要添加用户ID作为额外的一列。
为了读取及写入数据,我编写了如图15所示的更新过的Configuration类。
清单15.db1.php
require_once('DB.php');
$dsn='mysql://root:password@localhost/config';
$db=&DB::Connect($dsn,array());
if(PEAR::isError($db)){die($db->getMessage());}
classConfiguration
{
private$configFile='config.xml';
private$items=array();
function__construct(){$this->parse();}
function__get($id){return$this->items[$id];}
function__set($id,$v)
{
global$db;
$this->items[$id]=$v;
$sth2=$db->prepare('DELETEFROMsettingsWHEREname=?');
$db->execute($sth2,$id);
if(PEAR::isError($db)){die($db->getMessage());}
$sth3=$db->prepare('INSERTINTOsettings(id,name,value)VALUES(0,?,?)');
$db->execute($sth3,array($id,$v));
if(PEAR::isError($db)){die($db->getMessage());}
}
functionparse()
{
global$db;
$doc=newDOMDocument();
$doc->load($this->configFile);
$cn=$doc->getElementsByTagName("config");
$nodes=$cn->item(0)->getElementsByTagName("*");
foreach($nodesas$node)
$this->items[$node->nodeName]=$node->nodeValue;
$res=$db->query('SELECTname,valueFROMsettings');
if(PEAR::isError($db)){die($db->getMessage());}
while($res->fetchInto($row)){
$this->items[$row[0]]=$row[1];
}
}
}
$c=newConfiguration();
echo($c->TemplateDirectory."\n");
$c->TemplateDirectory='newfoo';
echo($c->TemplateDirectory."\n");
?>
这实际上是一个混合的文本/数据库解决方案。请仔细观察parse方法。该类首先读取文本文件来获取初始值,然后读取数据库,进而将键更新为最新的值。在设置一个值后,键就从数据库中移除掉,并添加一条具有更新过的值的新记录。
观察Configuration类如何通过本文的多个版本来发挥作用是一件有趣的事,该类能从文本文件、XML及数据库中读取数据,并一直保持相同的接口。我鼓励您在开发中也使用具有相同稳定性的接口。对于对象的客户机来说,这项工作具体是如何运行的是不明确的。关键的是对象与客户机之间的契约。
到此,相信大家对“构建可配置PHP应用程序的方式有哪些”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。