PHP世界

PHP爱好者乐园

  PHP博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  11 随笔 :: 0 文章 :: 8 评论 :: 0 Trackbacks

2008年4月1日 #

1.  PHP 中的正则表达式
2.  八个实用的PHP正则表达式
3.  如何书写更易阅读的PHP正则表达式
4.  半小时精通正则表达式
5.  正则在文章采集系统中的应用及常见问题答疑
6.  ...更多策划中

文章内容来源方面,有本站旧文重新整理而来,也有翻译英文文献而来(感谢在加拿大的!oEL同学),还有些是个人心得.
个人能力有限,肯定有一些错误,发现的朋友请给予提醒以及时更正. 不至误导新手. 如果文章能给大家一些参考,就很满足了. 也欢迎PHP爱好者投稿,完善本系列,让新手少走一些弯路.



PHP与正则表达系列之: PHP 中的正则表达式


正则表达式简介及正则表达式在PHP中的作用

正则表达式是一种表示规则的途经,在 PHP 中利用这些规则可以让你灵活变通地匹配检验替换以及修改 字符串。本文涉及到 PCRE 的基础,以及如何使用 preg_match(),preg_replace(),and preg_split() 函数。

接下来就让我们从实例循序渐进地开始学习如何使用这些函数。

规则匹配 preg_match

利用 preg_match(),我们可以完成字符串的规则匹配。如果找到一个匹配,preg_match() 函数返回 1,否则返回 0。还有一个可选的第三参数可以让你把匹配的部分存在一个数组中。在验证数据时这个功能可以变得非常有用 SUN JAVA Certification
$string = "football";
if (preg_match('/foo/', $string)) {
// 匹配正确
}

上面的例子将成功匹配,因为单词 football 里面包含 foo。现在我们来试一个更复杂的,例如验证一个 Email 地址。
$string = "first.last@domain.uno.dos";
if (preg_match(
'/^[^0-9][a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)*[@][a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)*[.][a-zA-Z]{2,4}$/',
$string)) {
// 验证Email地址
}

这个例子将验证出此 Email 地址为正确格式。现在让我们来看看这段正则表达式所代表的各种规则。


PCRE 顾名思义,与在 Perl 中的正则表达式有相同的语法,所以每段正则表达式必须要有一对定界符。我们一般使用 / 为定界符。

开头的 ^ 和结尾的 $ 让PHP从字符串开头检查到结尾。假使没有 $,程序仍会匹配到 Email 的末尾。

[] 被用来限制许可输入类型。例如 a-z 允许所有的小写字母,A-Z 允许所有的大写字母,0-9 所有数字,等等,以及更多其他类型。

{} 被用来限制期望的字符数。例如 {2,4} 表示字符串的每一节可以有 2-4 字符长度,像是 .com.cn 或 .info。在这里, "." 并不算一个字符,因为 {2,4} 之前定义的许可输入类型只有大小写字母,故此段只匹配大小写字母

() 被用来合并小节,并定义字符串中必须存在的字符。(a|b|c) 能够匹配 a 或 b 或 c。

(.) 将匹配所有字符,而 [.] 只匹配 "." 本身。

要使用一些符号本身,必须在前增加一个 。这些字符有:( ) [ ] . * ? + ^ | $

规则替换 preg_replace

preg_replace 允许你替换字符串中匹配到你定义的正则表达式。一个简单的注释移除功能:

preg_replace('[(/*)+.+(*/)]', '', $val);

这段代码可以移除在 PHP 和 CSS 中使用 /* 注释 */ 格式的多多行注释。其中的三个参数分别为正则表达式,要替换成的字符串和要替换的目标字符串(这里要做移除功能,所以是个空白字符串 -> '')。如果你想匹配次级规则,可以使用 $0 代表全部匹配,$1、$2 等,以此类推代表分别的次级规则。

规则分割preg_split SeeBeyond

preg_split 可以将整段字符串按匹配到的正则表达式分割成 1、2或更多字符的多段。比如获取标签,无论是用空格还是逗号分隔的:
$tags = preg_split('/[,]/', 'my,tags,unevenly,spaced');
print_r($tags);

正则表达式是一个很实用的技巧,让你可以专注于所期望得到的内容。

但有时候一段正则表达式并不能让你如愿以偿得到预期的结果是件很烦人的事,所以我将在本系列的第二篇文章附上一些简单的语法指南希望能够帮到大家。

如果你想跳过痛苦的练习过程而直接获得一些成功的喜悦感,请期待PHP5研究室推出的 "PHP与正则表达系列之二: 八个实用PHP正则表达式"。


附:PCRE 语法指南

/ 定界符
^ 字符串头
$ 字符串尾
[a-z] 所有小写字母
[A-Z] 所有大写字母
[0-9] 所有数字
? 零或一个紧接前的字符
* 零或多个紧接前的字符
+ 一或多个紧接前的字符
{4} 4个紧接前的字符
{4,8} 4-8个紧接前的字符
. 任意字符
(red|green|blue) Red 或 green 或 blue(红 或 绿 或 蓝)
s 空格

特殊字符(需要在前加 )

( ) [ ] . * ? + ^ | $
posted @ 2008-04-01 16:27 loveairnike 阅读(1901) | 评论 (1)编辑 收藏

每个人都告诉你备份是如何的重要,但是目前却缺乏如何进行备份的详细的教程来讨论需要备份哪些文件,备份间隔应该是多少?这篇文章将为你回答这些问题,通过该文章你可以定义适合自己的备份策略。

  总的来说,可以将备份分为两类:系统备份,其实现对操作系统和应用程序的备份(只要系统管理员的才能进行这项工作);用户备份,其实现对用户文件的备份(我不知道是否别人也使用这些术语,但是本文中都这样使用)。我们会看到,系统备份和用户备份应该区别对待。

  系统备份

  进行系统备份的原因是尽量在系统崩溃以后能快速简单完全地恢复系统的运行。然而当然你不会希望花费几个月来备份你的系统。进行有效备份的最有效的方法是仅仅备份那些对于系统崩溃恢复所必需的数据。

  想想下面这种情况,你的系统大部分都非常稳定-/usr/bin的内容并不是那么经常变动,为了使事情简化,你也许已经有了系统的粗糙拷贝。大多数人都是使用某种发布安装Linux系统,然后对系统进行自己的定制。原始的发布就是我们进行系统恢复的出发点。

  Linux区别于其他大多数操作系统的一个方面是操作系统和大多数应用程序一次被安装,而Windows或者其他Unix系统则是应用程序与操作系统是分开来安装的,首先是安装操作系统,然后才逐渐安装各个应用程序,对于这些系统,备份整个系统才是必要的,这些操作系统在初次安装时需要花费大量的时间和精力。而对于Linux来说,初次或再次安装一个基本系统(包括绝大多数应用程序)是非常简单和快速的。

  上面说到系统的大部分都非常稳定,下面我们来考虑哪部分比较容易变动。定制自己的系统的一个方面是安装新的应用程序(没有随发布安装的应用程序)。当安装新的软件时,你应该严格要求自己,将新安装的软件和初始安装的内容分隔开来,最恰当的目录位置是/usr/local之下。/usr/local目录的目的就是包含那些相对系统来说"本地化"的内容。这样做的一个优点就是你可以容易的察看哪些程序可以从发布光盘中恢复,哪些程序需要从备份中恢复。

  另外一个你会需要修改的地方是标准程序使用的配置文件。大多数Linux应用程序的的配置文件都是纯文本格式,你可以根据需要编辑配置文件来定制软件的运行。有时候发布将自动基于你回答的问题答案来编辑某些配置文件,但是大多数情况下你需要自己编辑它们。

  很多重要的文件位于/etc目录之下,如:

  /etc/printcap---配置如何与打印机通信
  /etc/fstab--- SUN JAVA Certification  配置系统有哪些文件系统
  /etc/passwd---包含所有的用户信息,如密码,登录shell等
  /etc/inittab---配置init在不同运行级别下分别如何启动系统
  /etc/XF86Config---包含XFree86的初始配置

  不同的系统可能有很多其他的的重要配置文件,可以看到/etc目录非常重要,包含在该目录下的文件可能是数小时的工作成果。我就曾经花费了好长时间才正确的配置了XF86Config文件。想到如果重新再做一遍,我就害怕。当然,某些程序使用了其他目录下的一些文件,但是Linux的基本配置文件绝大多数都位于/etc目录下。

  当你修改了某个程序所使用的配置文件,你不能将其搬移到其他地方,因为程序通常在固定的地方寻找配置文件。因此记录对系统所做的修改就非常重要,无论所做的修改如何的微小,这样当系统崩溃时才能轻松的恢复系统。

  记录对系统的修改的最好的工具是一杆笔和一张纸。记录下对系统进行了哪些修改的详细描述及为什么要进行修改是非常必要的。不要自以为是的认为经过六个月你仍然能记得你如何编译安装了某个应用,或你为什么你要修改某个配置,而实际情况是你往往会忘记。即使你在一个单独的目录下安装了一个新的软件(比较容易查找修改),也应详细的记录如何安装的程序,什么时候安装的及是否有任何事情还不是非常清楚。

  现在我们已经讨论了哪些系统文件需要进行备份,下面我们考虑多长时间应该备份一次。每次你对系统进行了修改可能是进行备份的时刻,但是不要忘记在修改系统以前对要修改的部分备份,以防止在出现问题时可以恢复。关键点是系统只有在你修改了它以后才会变动,而修改往往不是经常的事情,因此对系统的备份应该对应于这个情况。


  用户备份

  用户备份不同于系统备份,因为用户的数据变动更加频繁一些,因此几乎不可能建立某个用户的精确到每分钟的备份。当备份用户数据,你只是为用户提供一个虚拟的安全网络-合理的最近它数据文件的备份,当出现任何问题如误删除某些文件或者硬盘发生故障时,使用户可以恢复自己的数据。

  用户备份应该比系统备份更加频繁,也许是每天都进行备份(cron程序提供了自动定期运行某个程序的方法)。而几乎所有的备份程序(包括tar)都提供了一个有用的特性:仅仅备份特定时间(如上次备份)以后被修改的文件。这可以显著的减少进行用户备份所需要花费的时间,因为一个用户在特定时间段内一般仅仅是修改了很少量的文件。你可以对用户数据进行完全备份和增量备份的手段结合起来使用。

  虽然可以是用软盘作为备份的存储介质,但是每张软盘只能存储少量的数据。很多备份程序允许一个备份跨越若干个磁盘,但是这也意味着在进行备份时必须自行更换软盘。若你仅仅是管理具有若干个用户的小型系统,这也许非常简单,但是情况往往不是这样。数字或磁记录带也许是一个更好的选择,因为它们具有更大的存储容量。Linux支持很多不同类型的磁带设备,或者通过ftape模块或者通过对SCSI的支持(数字记录带往往是SCSI设备)。而且从性价比来说磁带设备的成本现在非常低廉,因此它非常实用。可选的,你的Linux可能和其他带有磁带机的计算机在同一个局域网之中,Linux可以访问其他远程机器的磁带设备。

  无论选择什么介质进行备份,都需要管理这些介质设备。你的备份是在发生了问题时才会被使用的,这时你会依赖于备份,因此你必须经常验证你的备份。一个没有验证的备份甚至比没有备份更糟。

  你同样应该保持多个备份集合,一个流行的策略是基于"祖父-父亲-儿子"思想。这里你拥有三套备份,最新的一份(儿子),中间的一份(父亲),最老的一份(祖父)。当你进行下一次备份,你应该覆盖掉最老的那一份,使其变为最新的一份。这种策略的优点是即使其中的一份失效,你仍然可以根据其他的备份进行恢复,但是你不必每次进行两次备份。

  最后一个忠告也许有些奇怪:保持至少一个备份远离源机器,最好完全放在另外一栋大楼里。为什么呢?这是为了防止源机器所在地发生灾难如火灾等等,你可以重新实用一台新的机器,使用新的Linux发布,但是你不能得到一个新的备份磁带。计算机中的数据是最珍贵的,不可替代的,因此应该小心的保护它。

  如何进行备份

  OK,理论部分已经足够了,下面我们来看一些例子。有很多的备份程序可供选择,包括自由软件和商业软件,每种都有其优点。但是我们这里使用tar(GNU version 1.11.2)来作为示例说明。

  这里假定系统中刚刚在/usr/local中安装了很多新软件,并且需要备份系统中/usr/local下的所有内容。这里你没有磁带设备,因此你使用了软盘,使用如下的命令:

   $ tar -cWMf /dev/fd0 /usr/local

  c选项标识创建一个存档,W表示在写入备份内容到备份设备以后再读出来进行验证以提高可靠性,M选项指示tar若需要时可以跨越多张磁盘,f选项通知将存档写入到哪里,在这里是/dev/df0。在很多系统中,你必需具有root的身份才能访问/dev/df0。
$ tar -tMf /dev/fd0 SAS Institute

  将显示该磁盘备份的文件的列表。依赖于/usr/local目录下内容的大小,也许会需要多张软盘来备份该目录下的内容,当然可以是用tar命令的压缩选项-z来减少需要的磁盘数目,该选项指定tar将存档使用gzip进行压缩处理以减小存档大小。这个特性有好处也有坏处,好处就是可以减少存档大小,坏处是压缩存档的任何一处的错误就会导致整个存档不可恢复。某些程序在备份文件以前将这些文件一个个单独压缩,这时一个好主意。

  前面我提到可以仅仅备份某个时间以后被修改的文件,使用tar,你能使用N选项来进行这样的存档,例如:

   $ tar -cf /dev/ftape -N yesterday /home

  将备份/home目录下昨天以后被修改的所有文件。这一次数据被备份到软磁带设备/dev/ftape中。实现该功能的另外一个可选的方法是联合使用find和tar命令:

  $ find /home -cnewer /etc/last_backup -type f i-print > back_these_up

  $ tar -cf /dev/ftape -T back_these_up

  $ touch /etc/last_backup

  find命令用来查找所有在/etc/last_backup被修改以后内容发生了变化的文件,并将查找结果存放在文件back_these_up之中,T选项指示tar命令备份文件back_these_up中指定的的所有文件;随后touch文件/etc/last_backup,其将作为下次备份的时间点。将若干个这样的命令连接在一起使用是非常有用的。作为一个副产品我们的到了我们备份的文件的名字的列表文件,及最新一次备份的时间。(文件etc/last_backup的时间戳)。

  我们可以做的另外一件事情就是过滤文件列表,从而使特定的文件不被备份。例如,也许会希望不备份对象文件或者DVI文件,因为它们可以由源文件生成。若仅仅希望忽略一种类型的文件则一个简单的grep -v将完成这个功能,egrep则可以被用来忽略多种类型的文件,如下例所示我们只要修改上例的第一行即可:

   $ find /home -cnewer /etc/last_backup -type f i-print | egrep

    -v '<<<>.o$|<<<>.dvi$' > back_these_up

  这个功能也可以使用find来实现,当然可能不如egrep那么强大:

   $ find /home -cnewer /etc/last_backup -type f ! ( -name *.o -o -name

    *.dvi ) -print > back_these_up

  常常会出现你的备份要求不能仅用一个简单的tar命令完成的情况,这个时候应该自己写脚本来完成需要的功能。这个脚本可能简单到上面的示例那样只有几行程序,也可能非常复杂。一个非常简单的脚本,定期使用cron来运行,能使备份变为非常简单的过程。

  备份不应该成为一个长时间的痛苦的事情,但它是作为系统管理必不可少的工作。在着手之前进行详细的考虑和计划是应该的。做出对系统进行完全备份的决定是非常容易的,同样做出六个月以前的几个文件的老备份也是可以满足需要的决定也是非常容易的,而实际是最好的策略是在两者之间。
posted @ 2008-04-01 16:24 loveairnike 阅读(699) | 评论 (1)编辑 收藏

2008年3月14日 #

如果你学过Asp.net,你一定知道它里面有一个叫datalist之类自带的分页功能十分强大,这里的这个类就模拟了一部份这个功能,我敢用“强大”来定义它,是因为作为一个通用的页类,这个类真正做到了“通用”。
废话少说,马上介绍一下是如何使用的。
1、先新建一个用于测试的表
CREATE TABLE `test` (
`aa` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`bb` VARCHAR(50) NOT NULL,
`cc` VARCHAR(50) NOT NULL,
`tt` INT DEFAULT '0' NOT NULL
);
2、制作一个模板文件,保存为:test.htm
{dede:page pagesize=15/}
<table width='100%' border='0' cellpadding='1' cellspacing='1' bgcolor='#CCCCCC'>
<tr bgcolor='#F8FFEE' align='center'>
  <td width='10%'>aa</td>
  <td width='30%'>bb</td>
  <td width='30%'>cc</td>
  <td width='30%'>tt</td>
</tr>
{dede:datalist}
<tr bgcolor='#FFFFFF' align='center'>
  <td>[field:aa/]</td>
  <td>[field:bb/]</td>
  <td>[field:cc/]</td>
  <td>[field:tt function='date("Y-m-d H-i-s","@me")'/]</td>
</tr>
{/dede}
<tr bgcolor='#F8FFEE'>
  <td colspan='4'>
  {dede:pagelist listsize=3/}
  </td>
  </tr>
</table>
3、编写调用这个类的代码
数据库的连接信息均在config_base.php这个文件是设定
showtable.php
<?
require("inc_datalist.php");
$dlist = new DataList();
$dlist->Init();
$dlist->SetTemplet("./test.htm");
$dlist->SetSource("select * from ttt");
$liststring = $dlist->Display();
$dlist->Close();
?>
看看效果,做一个分文件就这么简单,还完全实现了页面与逻辑分离
假如我要增加一个GET字符串传递给查询应该怎么做呢?
Easy
假如增加的查询串为 keyword
<?
require("inc_datalist.php");
if(!isset($keyword)) $keyword="";
$dlist = new DataList();
$dlist->Init();
$dlist->SetParameter("keyword",$keyword);
$dlist->SetTemplet("./test.htm");
$dlist->SetSource("select * from ttt where bb like '%$keyword%'");
$liststring = $dlist->Display();
$dlist->Close();
?>
还有什么不能解决吗?
假如有一个字段是布尔值,我想输出时按不同情况输出不同内容,其实不难实现
Dede模板引擎支持使用自定义函数
<?
require("inc_datalist.php");
if(!isset($keyword)) $keyword="";
function GetMyName($mname)
{
    if($mname=="dede") return "My Name";
    else return $mname;
}
$dlist = new DataList();
$dlist->Init();
$dlist->SetParameter("keyword",$keyword);
$dlist->SetTemplet("./test.htm");
$dlist->SetSource("select * from ttt where bb like '%$keyword%'");
$liststring = $dlist->Display();
$dlist->Close();
?>
在程序里不用做什么,需要做的事情是更改一下模板
[field:aa function="GetMyName('@me')"/]
这样返回的值就是函数返回的值。

这样几乎达到尽善尽美的境界,唯一的是分页列表的链接是固定的,不过你可以对它进行改进。
( from tiffany jewelry )
posted @ 2008-03-14 10:14 loveairnike 阅读(2282) | 评论 (1)编辑 收藏

2008年1月11日 #

Output Control 函数可以让你自由控制脚本中数据的输出。它非常地有用,特别是对于:当你想在数据已经输出后,再输出文件头的情况。输出控制函数不对使用 header() 或 setcookie(), 发送的文件头信息产生影响,只对那些类似于 echo() 和 PHP 代码的数据块有作用。

我们先举一个简单的例子,让大家对Output Control有一个大致的印象:
Example 1.

CODE<?php
ob_start(); //打开缓冲区
echo \"Hellon\"; //输出
header("location:index.php"); //把浏览器重定向到index.php
ob_end_flush();//输出全部内容到浏览器
?>

所有对header()函数有了解的人都知道,这个函数会发送一段文件头给浏览器,但是如果在使用这个函数之前已经有了任何输出(包括空输出,比如空格,回车和换行)就会提示出错。如果我们去掉第一行的ob_start(),再执行此程序,我们会发现得到了一条错误提示:"Header had all ready send by"!但是加上ob_start,就不会提示出错,原因是当打开了缓冲区,echo后面的字符不会输出到浏览器,而是保留在服务器,直到你使用flush或者ob_end_flush才会输出,所以并不会有任何文件头输出的错误!


一、 相关函数简介:
1、Flush:刷新缓冲区的内容,输出。
函数格式:flush()
说明:这个函数经常使用,效率很高。
2、ob_start :打开输出缓冲区
函数格式:void ob_start(void)
说明:当缓冲区激活时,所有来自PHP程序的非文件头信息均不会发送,而是保存在内部缓冲区。为了输出缓冲区的内容,可以使用ob_end_flush()或flush()输出缓冲区的内容。
3 、ob_get_contents :返回内部缓冲区的内容。
使用方法:string ob_get_contents(void)
说明:这个函数会返回当前缓冲区中的内容,如果输出缓冲区没有激活,则返回 FALSE 。
4、ob_get_length:返回内部缓冲区的长度。
使用方法:int ob_get_length(void)
说明:这个函数会返回当前缓冲区中的长度;和ob_get_contents一样,如果输出缓冲区没有激活。则返回 FALSE。
5、ob_end_flush :发送内部缓冲区的内容到浏览器,并且关闭输出缓冲区。
使用方法:void ob_end_flush(void)
说明:这个函数发送输出缓冲区的内容(如果有的话)。
6、ob_end_clean:删除内部缓冲区的内容,并且关闭内部缓冲区
使用方法:void ob_end_clean(void)
说明:这个函数不会输出内部缓冲区的内容而是把它删除!
7、ob_implicit_flush:打开或关闭绝对刷新
使用方法:void ob_implicit_flush ([int flag])
说明:使用过Perl的人都知道$|=x的意义,这个字符串可以打开/关闭缓冲区,而ob_implicit_flush函数也和那个一样,默认为关闭缓冲区,打开绝对输出后,每个脚本输出都直接发送到浏览器,不再需要调用 flush()


二、深入了解:

1. 关于Flush函数:
这个函数在PHP3中就出现了,是一个效率很高的函数,他有一个非常有用的功能就是刷新browser的cache.我们举一个运行效果非常明显的例子来说明flush.
Example 2.

CODE<?php
for($i = 1; $i <= 300; $i++ ) print(" ");
// 这一句话非常关键,cache的结构使得它的内容只有达到一定的大小才能从浏览器里输出
// 换言之,如果cache的内容不达到一定的大小,它是不会在程序执行完毕前输出的。经
// 过测试,我发现这个大小的底限是256个字符长。这意味着cache以后接收的内容都会
// 源源不断的被发送出去。
For($j = 1; $j <= 20; $j++) {
echo $j."
";
flush(); //这一部会使cache新增的内容被挤出去,显示到浏览器上
sleep(1); //让程序"睡"一秒钟,会让你把效果看得更清楚
}
?>


具体效果你可以到这里看看[url]http://www.php2000.com/~uchinaboy/out.php[/url]
PHP2000的最新的PHP聊天室就是用的这个技术,可惜的是源代码未公开 L
注:如果在程序的首部加入ob_implicit_flush()打开绝对刷新,就可以在程序中不再使用flush(),这样做的好处是:提高效率!

2. 关于ob系列函数:
我想先引用我的好朋友y10k的一个例子:
Example 3.

比如你用得到服务器和客户端的设置信息,但是这个信息会因为客户端的不同而不同,如果想要保存phpinfo()函数的输出怎么办呢?在没有缓冲区控制之前,可以说一点办法也没有,但是有了缓冲区的控制,我们可以轻松的解决:
CODE<?php
ob_start(); //打开缓冲区
phpinfo(); //使用phpinfo函数
$info=ob_get_contents(); //得到缓冲区的内容并且赋值给$info
$file=fopen(\'info.txt\',\'w\'); //打开文件info.txt
fwrite($file,$info); //写入信息到info.txt
fclose($file); //关闭文件info.txt
?>


用以上的方法,就可以把不同用户的phpinfo信息保存下来,这在以前恐怕没有办法办到!其实上面就是将一些"过程"转化为"函数"的方法!
或许有人会问:"难道就这个样子吗?还有没有其他用途?"当然有了,比如笔者论坛的PHP 语法加亮显示就和这个有关(PHP默认的语法加亮显示函数会直接输出,不能保存结果,如果在每次调用都显示恐怕会很浪费CPU,笔者的论坛就把语法加亮函数显示的结果用控制缓冲区的方法保留了),大家如果感兴趣的话可以来看看[url]http://www.zphp.com/bbs/[/url]!

可能现在大家对ob_start()的功能有了一定的了解,上面的一个例子看似简单,但实际上已经掌握了使用ob_start()的要点。
<1>.使用ob_start打开browser的cache,这样可以保证cache的内容在你调用flush(),ob_end_flush()(或程序执行完毕)之前不会被输出。
<2>.现在的你应该知道你所拥有的优势:可以在任何输出内容后面使用header,setcookie以及session,这是ob_start一个很大的特点;也可以使用ob_start的参数,在cache被写入后,然后自动运行命令,比如ob_start(\"ob_gzhandler\");而我们最常用的做法是用ob_get_contents()得到cache中的内容,然后再进行处理……
<3>.当处理完毕后,我们可以使用各种方法输出,flush(),ob_end_flush(),以及等到程序执行完毕后的自动输出。当然,如果你用的是ob_get_contents(),那么就要你自己控制输出方式了。

来,让我们看看能用ob系列函数做些什么……

一、 静态模版技术

简介:所谓静态模版技术就是通过某种方式,使得用户在client端得到的是由PHP产生的html页面。如果这个html页面不会再被更新,那么当另外的用户再次浏览此页面时,程序将不会再调用PHP以及相关的数据库,对于某些信息量比较大的网站,例如sina,163,sohu。类似这种的技术带来的好处是非常巨大的。

我所知道的实现静态输出的有两种办法:
<1>.通过y10k修改的phplib的一个叫template.inc.php类实现。
<2>.使用ob系列函数实现。
对于第一种方法,因为不是这篇文章所要研究的问题,所以不再赘述。
我们现在来看一看第二种方法的具体实现:
Example 4.


CODE<?php
ob_start();//打开缓冲区
?>
php页面的全部输出
<?
$content = ob_get_contents();//取得php页面输出的全部内容
$fp = fopen("output00001.html", "w"); //创建一个文件,并打开,准备写入
fwrite($fp, $content); //把php页面的内容全部写入output00001.html,然后……
fclose($fp);
?>

这样,所谓的静态模版就很容易的被实现了……

二、 捕捉输出

以上的Example 4.是一种最简单的情况,你还可以在写入前对$content进行操作……
你可以设法捕捉一些关键字,然后去对它进行再处理,比如Example 3.所述的PHP语法高亮显示。个人认为,这个功能是此函数最大的精华所在,它可以解决各种各样的问题,但需要你有足够的想象力……
Example 5.

CODE<?
Function run_code($code) {
If($code) {
ob_start();
eval($code);
$contents = ob_get_contents();
ob_end_clean();
}else {
echo "错误!没有输出";
exit();
}
return $contents;
}


以上这个例子的用途不是很大,不过很典型$code的本身就是一个含有变量的输出页面,而这个例子用eval把$code中的变量替换,然后对输出结果再进行输出捕捉,再一次的进行处理……

Example 6. 加快传输


CODE<?
/*
** Title.........: PHP4 HTTP Compression Speeds up the Web
** Version.......: 1.20
** Author........: catoc <[email]catoc@163.net[/email]>
** Filename......: gzdoc.php
** Last changed..: 18/10/2000
** Requirments...: PHP4 >= 4.0.1
** PHP was configured with --with-zlib[=DIR]
** Notes.........: Dynamic Content Acceleration compresses
** the data transmission data on the fly
** code by sun jin hu (catoc) <[email]catoc@163.net[/email]>
** Most newer browsers since 1998/1999 have
** been equipped to support the HTTP 1.1
** standard known as \"content-encoding.\"
** Essentially the browser indicates to the
** server that it can accept \"content encoding\"
** and if the server is capable it will then
** compress the data and transmit it. The
** browser decompresses it and then renders
** the page.
**
** Modified by John Lim ([email]jlim@natsoft.com.my[/email])
** based on ideas by Sandy McArthur, Jr
** Usage........:
** No space before the beginning of the first \'<?\' tag.
** ------------Start of file----------
** |<?
** | include(\'gzdoc.php\');
** |? >
** |<HTML>
** |... the page ...
** |</HTML>
** |<?
** | gzdocout();
** |? >
** -------------End of file-----------
*/
ob_start();
ob_implicit_flush(0);
function CheckCanGzip(){
global $HTTP_ACCEPT_ENCODING;
if (headers_sent() || connection_timeout() || connection_aborted()){
return 0;
}
if (strpos($HTTP_ACCEPT_ENCODING, \'x-gzip\') !== false) return \"x-gzip\";
if (strpos($HTTP_ACCEPT_ENCODING,\'gzip\') !== false) return \"gzip\";
return 0;
}
/* $level = compression level 0-9, 0=none, 9=max */
function GzDocOut($level=1,$debug=0){
$ENCODING = CheckCanGzip();
if ($ENCODING){
print \"n<!-- Use compress $ENCODING -->n\";
$Contents = ob_get_contents();
ob_end_clean();
if ($debug){
$s = \"<p>Not compress length: \".strlen($Contents);
$s .= \"
Compressed length: \".strlen(gzcompress($Contents,$level));
$Contents .= $s;
}
header(\"Content-Encoding: $ENCODING\");
print \"x1fx8bx08x00x00x00x00x00\";
$Size = strlen($Contents);
$Crc = crc32($Contents);
$Contents = gzcompress($Contents,$level);
$Contents = substr($Contents, 0, strlen($Contents) - 4);
print $Contents;
print pack(\'V\',$Crc);
print pack(\'V\',$Size);
exit;
}else{
ob_end_flush();
exit;
}
}
?>

这是catoc的一段很早以前的代码,是在weblogs.com看到的,他利用了zlib的函数,对传输的内容进行了压缩,测试表明,对于10k以上的页面,会产生效果,而且页面越大,效果越明显……

posted @ 2008-01-11 16:04 loveairnike 阅读(908) | 评论 (0)编辑 收藏

2008年1月3日 #

引言: 
关于PERL与PHP中的包含路径一直是一个比较难解的问题,主要是与操作系统和WEB服务器有关,不可能非常智能化的解决这个路径问题。相对于 PERL,PHP的路径好得多,解决起来也容易得多,因为PHP的相对路径在PHP程序中的任何场合都可以使用,而不象PERL在某些语句中必须用绝对路径而导致移植的极其复杂。 

基于此,在PHP中,我设计了一个绝对稳妥的解决方案,如下所述。 

原则: 
使用相对路径,但在相对路径中用绝对路径(有点绕,待会儿详解),一是可以保证可移植性,二是可以方便地修改,三是公式化且结构清晰明了,易于扩展。 

步骤详解: 
1、先确定好一个程序的根目录,注意是文件系统下的,不是WEB服务器下的虚拟目录,不过一般情况下该目录下的子目录的相对路径与URL下该目录的虚拟子目录是相同的。 
2、在定义好的程序根目录下的每个子目录下(其实不一定是每个,根据需要)建立一个settings.php,里面定义一个变量或是常数(常数比较好,因为作用域比较大),如APPROOT,但这个APPROOT却不是绝对路径,而是该目录相对于你指定的程序根目录的相对路径。 
3、在此目录下的所有程序入口文件(也即第一个包含其它文件的文件,或是允许直接在浏览器中浏览的文件)中第一句写上require_once ('settings.php');,但要注意,所有被包含文件最好不要加此句——其实加上也可以,因为你可以在settings.php中写上 if (!defined(APPROOT)) define(APPROOT, '../..');这类的语句以防重定义。 
4、如果你要包含其它文件,无论是直接还是间接地包含,都可以写成 include(APPROOT.$path);,这里$path为被包含文件相对于你所指定的程序根目录的绝对路径。 

原理: 
定下的程序根目录是相对路径,但具体的目录位置是相对于那个根目录的绝对路径,两者组合起来就是具体文件相对于程序根目录的相对路径了。例如目录c:\ wwwroot\app为你指定的程序根目录,然后有这么两个文件c:\wwwroot\app\a\index.php和c:\wwwroot\app \b\inc.php。对子目录a来说,APPROOT是'..',而对程序根目录来说,inc.php的绝对路径是$path= '/b/inc.php',两者组合为'../b/inc.php'。如果要在index.php中包含inc.php就要写成include('.. /b/inc.php');,而这个路径不就正好是刚才组合而成的APPROOT.$path吗? 


结论: 
经过以上处理,各个路径绝对整齐划一,唯一罗嗦一点的就是每个目录下要定义一下这个APPROOT,但每个目录下只需在本目录的settings.php 中定义一次就足够了。如果你整个程序只有一个入口文件,如index.php,而其它文件全部都是直接或是间接地被包含进这个唯一的入口文件的话,就只需在index.php的所在目录下的settings.php中定义一次就OK了。如果有朋友做过Delphi的工程并对工程文件研究过的话,就会发现我刚才说的一个程序只有一个主入口文件的情况与Delphi的工程十分相似,因为Delphi除了一个主程序文件(dpr文件),其余的全部是单元文件或是资源文件,都不能独立执行。在PHP中,如果这种情况出现,只需定义一次APPROOT,并在主程序文件中第一句话写成require_once ('settings.php');,而以后所有的包含全都可以用include(APPROOT.$path);,就保证不会有任何问题,除非你不会写这个“包含文件相对于程序根目录的绝对路径”$path。 

这个方法我用了不止一次,收效很好 190-802 000-834 000-861 117-102  。另外还可以参考JSP的WEB-INFO中路径的定义方式。 

我这个是以不变应万变的公式化的方案,如果有朋友有更好的方案,欢迎提出讨论!如有不明白的也欢迎提出。
posted @ 2008-01-03 11:24 loveairnike 阅读(828) | 评论 (1)编辑 收藏

     摘要: 听起来有些晕吧?!!使用php来操作php页面。有一个小的用途,在一个系统当中可能会有个别的小项不便存入数据库,但是我们又要经常在其他页面当中调用,而且还需要更新,那么我们就可以用这种方式来解决。其中遇到几个小问题,搞了俺半天时间才搞定(小生驽钝! 大笑 )

比如说:用户需要更改某一个标题,但是单独为这个标题在建立一个表,显然不太合适。所以就想到,把这个变量写入php文件当中,供其它页面调用。name.php文件,需要的内容如下  阅读全文
posted @ 2008-01-03 11:22 loveairnike 阅读(900) | 评论 (1)编辑 收藏

2008年1月2日 #


介绍了在PHP中的面向对象编程(OOP,Object Oriented Programming)。我将向你演示如何通过使用一些OOP的概念和PHP的技巧来减少编码和提高质量。 这篇文章介绍了在PHP中的面向对象编程(OOP,Object Oriented Programming)。我将向你演示如何通过使用一些OOP的概念和PHP的技巧来减少编码和提高质量。祝你好运!

面向对象编程的概念:
不同的作者之间说法可能不一样,但是一个OOP语言必须有以下几方面:

抽象数据类型和信息封装
继承
多态

在PHP中是通过类来完成封装的:

---------------------------------------------------
<?php
class Something {
// 在OOP类中,通常第一个字符为大写
var $x;
function setX($v) {
// 方法开始为小写单词,然后使用大写字母来分隔单词,例如getValueOfArea()
$this->x=$v;
}
function getX() {
return $this->x;
}
}
?>---------------------------------------------------
  当然你可以按自已的喜好进行定义,但最好保持一种标准,这样会更有效。

  数据成员在类中使用"var"声明来定义,在给数据成员赋值之前,它们是没有类型的。一个数据成员可
以是一个整数,一个数组,一个相关数组(associative array)或者是一个对象。

  方法在类中被定义成函数形式,在方法中访问类成员变量时,你应该使用$this->name,否则对一个方
法来说,它只能是局部变量。

  使用new操作符来创建一个对象:

  $obj=new Something;

  然后你可以使用成员函数通过:

  $obj->setX(5);
  $see=$obj->getX();

  在这个例子中,setX成员函数将5赋值给对象的成员变量x(不是类的),然后getX返回它的值5。

  你可以象:$obj->x=6那样通过类引用方式来存取数据成员,这不是一个很好的OOP习惯。我强烈建议通
过方法来存取成员变量。如果你把成员变量看成是不可处理的,并且只通过对象句柄来使用方法,你将是一
个好的OOP程序员。不幸的是,PHP不支持声明私有成员变量,所以不良代码在PHP中也是允许的。

  继承在PHP中很容易实现,只要使用extend关键字。



-----------------------------------------------------
<?php

class Another extends Something {
var $y;
function setY($v) {
$this->y=$v;
}
function getY() {
return $this->y;
}
}

?>---------------------------------------------------
"Another"类的对象现在拥有了父类(Something)的全部的数据成员及方法,而且还加上了自已的数据成 员和方法。

你可以使用
$obj2=new Something;
$obj2->setX(6);
$obj2->setY(7);

PHP现在还不支持多重继承,所以你不能从两个或两个以上类派生出新的类来。

你可以在派生类中重定义一个方法,如果我们在"Another"类中重定义了getX方法,我们就不能使 用"Something"中的getX方法了。如果你在派生类中声明了一个与基派同名的数据成员,那么当你处理它时, 它将“隐藏”基类的数据成员。

你可以在你的类中定义构造函数。构造函数是一个与类名同名的方法,当你创建一个类的对象时会被调 用,例如:
-----------------------------------------------------
<?php

class Something {
var $x;

function Something($y) {
$this->x=$y;
}

function setX($v) {
$this->x=$v;
}

function getX() {
return $this->x;
}
}

?>---------------------------------------------------
  所以你可以创建一个对象,通过:

  $obj=new Something(6);

  构造函数会自动地把6赋值给数据变量x。构造函数和方法都是普通的PHP函数,所以你可以使用缺省参数。

  function Something($x="3",$y="5")

  接着:

  $obj=new Something(); // x=3 and y=5
  $obj=new Something(8); // x=8 and y=5
  $obj=new Something(8,9); // x=8 and y=9

  缺省参数使用C++的方式,所以你不能忽略Y的值,而给X一个缺省参数,参数是从左到右赋值的,如果
传入的参数少于要求的参数时,其作的将使用缺省参数。

  当一个派生类的对象被创建时,只有它的构造函数被调用,父类的构造函数没被调用,如果你想调用基
类的构造函数,你必须要在派生类的构造函数中显示调用。可以这样做是因为在派生类中所有父类的方法都
是可用的。

-----------------------------------------------------
<?php

function Another() {
$this->y=5;
$this->Something();
//显示调用基类构造函数
}

?>---------------------------------------------------
  OOP的一个很好的机制是使用抽象类。抽象类是不能实例化,只能提供给派生类一个接口。设计者通常
使用抽象类来强迫程序员从基类派生,这样可以确保新的类包含一些期待的功能。在PHP中没有标准的方法,
但是:

  如果你需要这个特性,可以通过定义基类,并在它的构造函数后加上"die" 的调用,这样就可以保证基
类是不可实例化的,现在在每一个方法(接口)后面加上"die" 语句,所以,如果一个程序员在派生类中没有
覆盖方法,将引发一个错误。而且因为PHP 是无类型的,你可能需要确认一个对象是来自于你的基类的派生
类,那么在基类中增加一个方法来实义类的身份(返回某种标识id),并且在你接收到一个对象参数时校验
这个值。当然,如果一个邪恶不好的程序员在派生类中覆盖了这个方法,这种方法就不起作用了,不过一般
问题多发现在懒惰的程序员身上,而不是邪恶的程序员。

  当然,能够让基类对程序员无法看到是很好的,只要将接口打印出来做他们的工作就可以了。

  在PHP中没有析构函数。

  重载(与覆盖不同)在PHP中不支持。在OOP中,你可以重载一个方法来实现两个或重多的方法具有相同
的名字,但是有不同数量或类型的参数(这要看语言)。PHP 是一种松散类型的语言,所以通过类型重载不
起作用,然而通过参数的个数不同来重载也不起作用。

  有时在OOP中重载构造函数非常好,这样你可以通过不同的方法创建对象(传递不同数量的参数)。在PHP
中实现它的技巧是:

-----------------------------------------------------
<?php

class Myclass {
function Myclass() {
$name="Myclass".func_num_args();
$this->$name();

//注意$this->name()一般是错误的,但是在这里$name是一个将被调用方法的名字
}
function Myclass1($x) {
code;
}
function Myclass2($x,$y) {
code;
}
}

?>---------------------------------------------------
  通过在类中的额外的处理,使用这个类对用户是透明的:

  $obj1=new Myclass('1'); //将调用Myclass1

  $obj2=new Myclass('1','2'); //将调用Myclass2

  有时这个非常好用。

多态
  多态是对象的一种能力,它可以在运行时刻根据传递的对象参数,决定调用哪一个对象的方法。例如,
如果你有一个figure的类,它定义了一个draw的方法。并且派生了circle和rectangle 类,在派生类中你覆
盖了draw方法,你可能还有一个函数,它希望使用一个参数x,并且可以调用$x->draw() 。如果你有多态性,
调用哪个draw方法就依赖于你传递给这个函数的对象类型。

  多态性在象PHP这样的解释语言(想象一下一个C++编译器生成这样的代码,你应该调用哪一个方法?你
也不知道你拥有的对象是什么类型的,好,这不是重点)是非常容易和自然的。所以PHP当然支持多态性。

-----------------------------------------------------
<?php

function niceDrawing($x) {

//假设这是Board类的一个方法
$x->draw();
}

$obj=new Circle(3,187);
$obj2=new Rectangle(4,5);

$board->niceDrawing($obj);
//将调用Circle的draw方法

$board->niceDrawing($obj2);
//将调用Rectangle的draw方法

?>---------------------------------------------------

用PHP进行面向对象编程
  一些"纯化论者(purists)"可能会说PHP不是一个真正的面向对象的语言,这是事实。PHP 是一个混合型
语言,你可以使用OOP,也可以使用传统的过程化编程。然而,对于大型项目,你可能想/需要在PHP 中使用
纯的OOP去声明类,而且在你的项目只用对象和类。

  随着项目越来越大,使用OOP可能会有帮助,OOP代码很容易维护,容易理解和重用。这些就是软件工程
的基础。在基于web的项目中应用这些概念就成为将来网站成功的关键。

PHP的高级OOP技术
  在看过基本的OOP概念后,我就可以向你展示更高级的技术:

序列化(Serializing)
  PHP不支持永久对象,在OOP中永久对象是可以在多个应用的引用中保持状态和功能的对象,这意味着拥
有将对象保存到一个文件或数据库中的能力,而且可以在以后装入对象。这就是所谓的序列化机制。PHP 拥
有序列化方法,它可以通过对象进行调用,序列化方法可以返回对象的字符串表示。然而,序列化只保存了
对象的成员数据而不包话方法。

  在PHP4中,如果你将对象序列化到字符串$s中,然后释放对象,接着反序列化对象到$obj,你可以继续
使用对象的方法!我不建议这样去做,因为(a)文档中没有保证这种行为在以后的版本中仍然可以使用。(b)
这个可能导致一种误解,在你把一个序列化后的版本保存到磁盘并退出脚本时。当以后运行这个脚本时,你
不能期待着在反序列化一个对象时,对象的方法也会在那里,因为字符串表示根本就不包括方法。

  总而言之,PHP 进行序列化对于保存对象的成员变量非常有用。(你也可以将相关数组和数组序列化到
一个文件中)。

例子 :

-----------------------------------------------------
<?php

$obj=new Classfoo();
$str=serialize($obj);

//保存$str到磁盘上


//几个月以后


//从磁盘中装入str

$obj2=unserialize($str)

?>---------------------------------------------------
  你恢复了成员数据,但是不包括方法(根据文档所说)。这导致了只能通过类似于使用$obj2->x来存取
成员变量(你没有别的方法!)的唯一办法,所以不要在家里试它。

  有一些办法可以解决这个问题,我把它留着,因为对这篇简洁的文章来说,他们太不好。

  我会很高兴地欢迎在PHP的后续版本中有全序列化的特性。

使用类进行数据存储
  对于PHP和OOP一件非常好的事情就是,你可以很容易地定义一个类来操作某件事情,并且无论何时你想
用的时候都可以调用相应的类。假设你有一个HTML表单,用户可以通过选择产品ID号来选择一个产品。在数
据库中有产品的信息,你想把产品显示出来,显示它的价格等等。你拥有不同类型的产品,并且同一个动作
可能对不同的产品具有不同的意思。例如,显示一个声音可能意味着播放它,但是对于其它种类的产品可能
意味着显示一个存在数据库中的图片。你可以使用OOP或PHP来减少编码并提高质量:

  定义一个产品的类,定义它应该有的方法(例如:显示),然后定义对每一种类型的产品的类,从产品
类派后出来(SoundItem类,ViewableItem类,等等),覆盖在产品类中的方法,使它们按你的想法动作。

  根据数据库中每一种产品的类型(type)字段给类命名,一个典型的产品表可能有(id, type, price,
description, 等等字段)...然后在处理脚本中,你可以从数据库中取出type值,然后实例化一个名为type
的对象:

-----------------------------------------------------
<?php

$obj=new $type();
$obj->action();

?>---------------------------------------------------
  这是PHP的一个非常好的特性,你可以不用考虑对象的类型,调用$obj的显示方法或其它的方法。使用
这个技术,你不需要修改脚本去增加一个新类型的对象,只是增加一个处理它的类。

  这个功能很强大,只要定义方法,而不去考虑所有对象的类型,在不同的类中按不同的方法实现它们,然后在主脚本中对任意对象使用它们,没有if...else,也不需要两个程序员,只有高兴。

  现在你同意编程是容易的,维护是便宜的,可重用是真的吗?

  如果你管理一组程序员,分配工作就是很简单的了,每个人可能负责一个类型的对象和处理它的类。

  可以通过这个技术实现国际化,根据用户所选的语言字段应用相应的类就可以了,等等。


拷贝和克隆
  当你创建一个$obj的对象时,你可以通过$obj2=$obj来拷贝对象,新的对象是$obj的一个拷贝(不是一
个引用),所以它具有$obj在当时的状态。有时候,你不想这样,你只是想生成一个象obj类一样的一个新
的对象,可以通过使用new语句来调用类的构造函数。在PHP中也可以通过序列化,和一个基类来实现,但所
有的其它类都要从基类派生出来。



进入危险区域
  当你序列化一个对象,你会得到某种格式的字符串,如果你感兴趣,你可以调究它,其中,字符串中有
类的名字(太好了!),你可以把它取出来,象:

-----------------------------------------------------
<?php

$herring=serialize($obj);
$vec=explode(':',$herring);
$nam=str_replace("\"",'',$vec[2]);

?>---------------------------------------------------
  所以假设你创建了一个"Universe"的类,并且强制所有的类都必须从universe扩展,你可以在universe
中定义一个clone的方法,如下:
-----------------------------------------------------
<?php

class Universe {
function clone() {
$herring=serialize($this);
$vec=explode(':',$herring);
$nam=str_replace("\"",'',$vec[2]);
$ret=new $nam;
return $ret;
}
}

//然后
$obj=new Something();

//从Universe扩展
$other=$obj->clone();

?>---------------------------------------------------
  你所得到的是一个新的Something类的对象,它同使用new方法,调用构造函数创建出的对象一样。我不
知道这个对你是否有用,但是Universe类可以知道派生类的名字是一个好的经验。想象是唯一的限制。
posted @ 2008-01-02 10:03 loveairnike 阅读(213) | 评论 (0)编辑 收藏

     摘要: 1、RewriteCond %{HTTP_REFERER} !^$ [NC]

允许空“HTTP_REFERER”的访问,即允许用户在浏览器地址栏中直接输入图片地址时图片文件的显示。一般而言,这是可选的,不过,建议这么设置,如果强迫必须具有“HTTP_REFERER”才能访问,可能会带来某些问题,比如说在用户通过代理服务器访问时。  阅读全文
posted @ 2008-01-02 09:44 loveairnike 阅读(916) | 评论 (1)编辑 收藏

2007年12月17日 #

如果你的PHP.ini中register_globals = On, 所有post, get, cookie, session的同名变量就会搅和在一起, 可以用$HTTP_*_VARS["username"]来判断你想要的那个变量.

但是即使同名, 在php.ini中variables_order = "GPCS"也会按照优先级别来判断, 等级低的值没法冲掉等级高的. 所以, 如果一开始就用session_register("username")是明智的,也可以用session_is_registered来判断变量是否已经注册.

这是一个例子:

if (!session_is_registered("username")) {
$user_name= "";
session_register("username");
}
同时保证你的php.ini中, variables_order = "GPCS" (缺省) S即session要放在最后, 优先.

register_globals = On有些浪费系统资源, 在优化配置中被关掉, 这样也避免了出现所谓漏洞



posted @ 2007-12-17 15:18 loveairnike 阅读(773) | 评论 (1)编辑 收藏

     摘要: PHP 5 添加了类似于其它语言的异常处理模块。在 PHP 代码中所产生的异常可被 throw 语句抛出并被 catch 语句捕获。需要进行异常处理的代码都必须放入 try 代码块内  阅读全文
posted @ 2007-12-17 14:18 loveairnike 阅读(941) | 评论 (0)编辑 收藏

仅列出标题  下一页