- ·上一篇文章:在PHP中全面阻止SQL注入式攻击之一
- ·下一篇文章:跟我来学PHP5:session会话的使用和分析
构建可配置PHP应用程序的正确方式
下面是在命令行中运行此代码时出现的结果:
%phptext2.php tempdir foobar % |
太好了!但如何能将它存储到文件中,从而将使这个改动固定下来呢?为此,需要写文件并读取它。用于写文件的新函数,如下所示。
清单9.text3.php
<?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 % |
原始的config.txt文件现在被新值更新了。
XML配置文件
尽管文本文件易于阅读及编辑,但却不如XML文件流行。另外,XML有众多适用的编辑器,这些编辑器能够理解标记、特殊符号转义等等。所以配置文件的XML版本会是什么样的呢?清单11显示了XML格式的配置文件。
清单11.config.xml
|
|
清单12显示了使用XML来装载配置设置的Configuration类的更新版。
清单12.xml1.php
<?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
<?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; $sth1=$db->prepare('DELETEFROMsettingsWHEREname=?'); $db->execute($sth1,$id); if(PEAR::isError($db)){die($db->getMessage());} $sth2=$db->prepare('INSERTINTOsettings(id,name,value)VALUES(0,?,?)'); $db->execute($sth2,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及数据库中读取数据,并一直保持相同的接口。我鼓励您在开发中也使用具有相同稳定性的接口。对于对象的客户机来说,这项工作具体是如何运行的是不
