网站开发日志

2009年11月9日星期一

网页同时含有安全性与非安全性的项目

当网页内包含HTTP及HTTPS的内容时,在IE下会出现弹出 "…网页同时含有安全性与非安全性的项目…"的警告讯息;在Firefox下,你将看到有下角的安全锁图标裂开了。这通常不是什么大问题。

如何你在浏览别人的网站,

  1. 在IE下你可以通过:
    工具->选项->安全性->自订等级将『显示混合的内容』由"提示"修改为"启用"即可。
  2. 在Forefox下,你可以忽略那个裂开的安全锁。

如果你拥有这个网站,那么你应该考虑避免在安全页面包含非安全的元素,也就是在https页面里,把http://打头的href用https://或者相对路径代替。当然,前提是该元素可以通过https接入。

https是需要购买安全证书的,不是什么网站随随便便在前面加上https就可以访问,例如:https://www.61dh.com是不可以访问的,因为我没有买安全证书。另外SSL安全证书是和域名对应的,比如说我买了https://secure.61dh.com,它只对sercure.61dh.com生效,而https://www.61dh.com依然无法访问。

如果一个安全页面上含有非安全元素(通常是链接或者图片),并且这些链接不支持安全连接。那么应该怎么办呢?

这里要介绍两个可行解决方案。(注意:下面用到https://secure.61dh.com只是用来举例说明的,它并不存在。)

方法一:在服务器配置文件里加入安全到非安全的跳转

在Apache服务器下,当访问https://安全页面时通常要先到达ssl.conf,而访问不安全页面通常先到达http.conf。所以我们可以在ssl.conf里对某些url进行跳转。这种方法是用于同域名跳转,例如:

RewriteCond   %{REQUEST_URI}  !^/cart/checkout/.*$
RewriteRule   ^/(.*)$         http://%{HTTP_HOST}/$1  [R,L]

上述Rewrite Rule,可以实现 https://secure.61dh.com/cart/http://www.61dh.com/cart 的跳转。

方法二:使用PHP跳转代码

当安全页面含有外部链接时,这个方法很适用。例如在https://secure.61dh.com页面里含有http://www.cpzhan.com的友情链接,我们可以把这个链接用安全链接来表示:

<a href="https://secure.61dh.com/redirect.php?to=http%3a%2f%2fwww.cpzhan.com">
彩票站</a>

注意:这里我用到redirect.php,这个文件支持安全链接,而非安全链接http://www.cpzhan.com被当成变量。redirect.php代码很简单,如下:

<?php
$to    = isset($_GET['to'])      ?  $_GET['to']       : '';
if ($to){
   header("Location: $to") ;
   exit;
}
?>

标签: ,

2009年10月12日星期一

密码加密

我经常在数据库的表里或者在代码里看到未经加密的密码,这是一个很不好的习惯。PHP里提供很多种的密码加密函数,在正式的项目开发里,我们应该要重视密码的加密。

我们可以在用户注册帐号的时候,使用md5()或者sha1()加密用户输入的密码,然后保存在数据库里。下次用户登入的时候,把用户输入的密码通过md5()或者sha1()函数计算出加密值,然后用这个加密值和数据库里的保存密码进行比较,如果相等就代表密码正确,反之亦然。

实例代码如下:

<?hpp
$pass=$_POST['pass']; 
$save = md5($pass); //保存这个加密过的密码
if(strcmp(md5($pass),$save)==0)
  echo"密码正确";
else
  echo"密码不对";
?>

注意使用md5或者sha1()加密过后的值是不可以逆的,也就是说通过这个加密值你无法还原原始值。所以如果你忘记密码,你只有重新设置密码。

有时候,你可能需要一个可以还原的加密方法。这时使用上面的加密函数就行不通了,但是你完全可以自己写一个,这里就要介绍一个用base64_encode()和base64_decode()来创建自定义密码加密的方法。

首先,创建下来LIB文件,命名为pass.php, 保存在你的库文件目录下。

<?php
function myencode($str)
{
  for($i=0; $i<5;$i++)
  {
    $str=strrev(base64_encode($str));
  }
  return $str;
}
function mydecode($str){
  for($i=0; $i<5;$i++)
  {
    $str=base64_decode(strrev($str)); 
  }
  return $str;
}
?>

然后,用下面的代码创建一个加密后的密码:

<?php 
include ("LIB/pass.php");
echo myencode($argv[1]);
?>

把上面代码命名为mypass.php, 然后在命令行运行:

php mypass.php 61dh
VZlSXRVVONXTWl1daZkVaNGbKVVVB1TP

最后,在实际代码里,使用这个来代替加密过的代码来代替真实密码。

<?php
include ("LIB/pass.php");
$pass = 'VZlSXRVVONXTWl1daZkVaNGbKVVVB1TP';
$real_pass = mydeocde($pass);
?>

当然,这种方法只是在一定程度上保护了密码,如果别人可以介入你的库文件(pass.php),加密的字符串将马上就可以被还原。但是这总是比让人一目了然就看到你的密码来的好。

标签:

2009年8月11日星期二

PHP实用代码系列 - 计算页面加载时间

网页的加载时间很重要,没有人喜欢花好几分钟来等待一个页面加载完毕。影响页面加载速度的因素很多,对于动态网页,其中一个重要因素是代码的效率。不论用那种语言来写网站的人,我们都应该重视代码的效率,注重效率也是优秀程序员和新手的一个重要区别。当你给你的页面加入一些代码的后,最后要检测一下新加入的代码有没有给页面的加载造成很大的影响。下面是一个适用的PHP代码,它可以用来计算页面的加载时间 (更新: 8/12/2009)。

<!-- 把这段反正页面顶部 -->
<?php
   $mtime = explode(" ",microtime());
   $starttime = $mtime[1] + $mtime[0];
?>
 
<!-- 把实际代码放在下面 -->
 
 
<!-- 把这段代码放在页面底部-->
<?php
   $mtime = explode(" ",microtime());
   $endtime = $mtime[1] + $mtime[0];
   $totaltime = ($endtime - $starttime);
   echo "页面加载时间为: ".$totaltime."";
?>

更新:

谢谢扑街囝的回复。我想你指的是

<?php
$start_t = time() + microtime();
//do sth.
$end_t = time() + microtime();
$timer = $end_t - $start_t;
echo $timer;
?>

这确实要精简。其实在PHP5里有更加精简的写法:

<?php
$start_t = microtime(true); //加入true,microtime输出为浮点值
//do sth.
$end_t = microtime(true);
$totaltime = $end_t - $start_t;
echo "页面加载时间为: ".$totaltime."";
?>

标签: ,

2009年8月8日星期六

Amazon S3

Amazon S3 is an online storage web service offered by Amazon Web Services. My company have being used to host some images for a while, and it works great, fast and cheap.

Recently, I found a handy tool called s3up, which lets you

upload files using custom headers so they’re served back with a proper expiration date and gzipped when possible — i.e., the techniques recommended by YSlow.

And I have used it to upoad some Javascripts to be hosted on Amazon S3, which largely reduces my company server's bandwidth usage. But there is small issue when you use this command line tool in old version of PHP (<5.2). PHP has a function to return information about a file, but this function does not return PATHINFO_FILENAME constant until PHP 5.2.0. So, you have to tweak the code little bit, for example, I added a custom function (Credit: Lostindream at atlas dot cz) below to get FILENAME constant for PHP < 5.2.0.

function path_info($filePath){
   $fileParts = pathinfo($filePath);
   if(!isset($fileParts['filename'])){
    $fileParts['filename'] = substr($fileParts['basename'], 0, strrpos($fileParts['basename'], '.'));
   }
   return $fileParts;
}
So the revised version is here. The usage can be found from original post.  I doubt people is using Amazon S3 in China, so this post is in English. If you are interested in Amazon S3 and have any related questions, please leave your message.

标签:

2009年8月2日星期日

NetBean IDE for PHP

在公司写PHP代码我一般都是在Linux下用VIM, 而在家里我用的是UltraEdit。这几天才发现UltraEdit在保持PHP文件时为UTF8时会加入BOM (byte-order mark), 一般情况下这不造成什么问题,因为浏览器解析代码的时候,会识别这个特殊标识而忽略它。但是有时候它会给你带来莫名其妙的问题,就像我在Wordpress 搬家记录一文里提到的header already sent的问题,不过你可以在保存UTF-8是选择NO-BOM来解决问题。

我一直没有用IDE,突然间觉得应该学着用用,市面上这么多IDE,他们的存在总是有他们的理由,所以今天下载了NetBean IDE for PHP,决定用他来开发cpzhan项目,另外NetBean还支持FTP功能,修改代码后直接就可以上传到服务器了。虽然UltraEdit也有FTP功能,但是NetBean在项目管理,PHP语法检查方面要比UltraEidt强悍多了。

我相信NetBean的很多功能还等待我去学习,几个小时的试用最让我开心的时,NetBean帮我找到了困扰我好几天的json问题的原因。我用NetBean打开getTerm.php (这个代码用来生产一组json格式的数据), 发现在<?php前多了一个点,我想这就是所谓的BOM。多了这一点(BOM),getTerm.php运行没什么问题(可能会有warning), 用浏览器打开,照样收到json字符串,但是这个json字符串已经不是正确合法的格式了,因为这个字符前有一个用看不见的标识在那里,导致json_decode无法还原json编码前的数组。最终把这个多余的点删除后,解决了问题!!!

注意:用jQuery的getJSON它会忽略这个特殊字符,这就是为什么在json问题里我用getJSON可以的原因。

标签:

2009年7月26日星期日

json问题

今天在给我的cpzhan.com写一个很小的API,主要是在服务器端提供一组数据,如果按照往常我会使用XML,但是最近在学习json,所以就json来试试。按照我上篇日志的介绍,代码如下:

<?php
require("../lib/config.inc.php");
require("../lib/Database.class.php");
$db = new Database($config['server'], $config['user'], $config['pass'], $config['database'], $config['tablePrefix']);
// connect to the server
$db->connect();  
$sql = "SELECT * FROM results ORDER BY UID DESC LIMIT 1";  
$row = $db->query_first($sql);
$current_year = date('Y');
$year = $row['Year'];
$row['Next_Term'] = ($current_year == $year) ? ($row['Term'] + 1) : 1;
$row['Next_Year'] = $current_year;
$row['Next_DRAW_DATE'] = date('Y-m-d');
//上面的代码产生一组数组,然后用json_encode($row)产生json格式的字符串
echo json_encode($row);
?>

接下来,我在本地又写了一段代码,准备用来掉用上面写好的api,然后用json_decode, 把数组还原。

<?php
$url = 'http://www.cpzhan.com/api/getTerm.php';
$jsonfeed = file_get_contents($url);
echo $jsonfeed;
//这里我收到到了json格式的字符串
$result = json_decode($jsonfeed, true);
print_r ($result);
//!!!但是这里无法还原编码前的数组
?>

不知道什么原因,json_decode无法还原编码前的数组。很明显,我上面写的api输出的字符串无法被json_decode识别,但是API的输出看上去的确是合法的json格式。我还试着给api的输出加上header如下, 終于找到原因了, 見netbean-ide一文的介绍)

<?php
ini_set('display_errors', 0);
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT" ); 
header("Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . "GMT" ); 
header("Cache-Control: no-cache, must-revalidate" ); 
header("Pragma: no-cache" );
header("Content-type: application/json; charset=utf-8");
.....

但是还是没有起任何作用。最后我放弃了这种方法,改用我曾经介绍过的jQuery获取JSON数据的方法:

<script>
   $.getJSON("http://www.cpzhan.com/api/getTerm.php?callback=?",
   function(data){//data这里是JSON对象,它可以是由多个JSON对象组成
      $("#year").val(data.Next_Year);
      $("#term").val(data.Next_Term);
      $("#date").val(data.Next_DRAW_DATE);
   });
</script>

一开始,我收到错误信息:invalid label,上网搜索了一下终于找到了原因,原来jQuery在调用getJSON的时候会在callback后自动加上值,因此我的产生JSON数据的时候要加上callback, 并且要用大括号把encode后的JSON数据包裹。如下:

<?php
ini_set('display_errors', 0);
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT" ); 
header("Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . "GMT" ); 
header("Cache-Control: no-cache, must-revalidate" ); 
header("Pragma: no-cache" );
header("Content-type: application/json; charset=utf-8");
//$row是要传递的数组
$jsonp = $_GET['callback']; //callback的是jquery自动加入的
$myJSON = json_encode($row);
echo $jsonp. '(' . $myJSON . ')'; //注意:json_encode的结果被大括号包裹

至于为什么要这么做我还没有搞懂(可能是出于安全的考虑),但是我想这很可能是我上面用PHP获取JSON数据失败的原因,我们必须对API的输出做特殊处理后,json_decode才可以识别。如果有朋友对JSON精通,请留言告知。谢谢!

标签: ,

2009年7月22日星期三

PHP和json简介

我在日志里曾经介绍过如何使用jQuery解析JSON格式的feed。今天准备简单介绍如何使用PHP输出JSON格式的Feed以及如何使用PHP解析JSON格式的Feed。JSON是一种轻便的数据交换格式,和XML的用途显示,但是相比之下XML要臃肿许多。至于XML和JSON那个更有优势,我也说不上,但是听说JSON的处理速度要比XML快,似乎挺有道理。JSON是全称是Javascript Object Notation(Javascript对象标记法),它的结构不如XML直观,特别对初学者来说,下面就是一个最基本、最简单的JSON feed的例子,你可以看到一个对象是有一组无序的对数组成,并用大括号括起来;而对数十用逗号分隔;而对数的名称和值用冒号分隔。

{name:adam,date:2009-07-22,title:PHP and JSON}
//最基本的JSON Feed

通常JSON Feed的结构往往不是这么简单,因为他的值得类型很多,可以是string、number、object、array、true、false、null等等,如果你的值里含有控制符号(大括号,冒号,中括号等等),你还要使用转义符。在这里我不准备做详细介绍,你可以参考http://json.org

PHP5.2后PHP自带有JSON的encoder和decoder,PHP5.2以前的版本你可以使用ZEND的JSON库。

使用PHP的JSON encoder,你可以把相关数组转换成JSON格式,下面是一个例子:

<?php
 $today = date("D M j G:i:s T Y");
 $status = array(
     "text" => "大家好!".
         "最近一直都很忙,但是我会尽量经常更新我的日志;" .
         "把学到的东西记录下来、分享给大家",
     "created_at" => "$today"
 );
 $name = array("name" => "adam");
 $status['user'] = $name;
 $myJSONstatus = json_encode($status);
 echo $myJSONstatus;
 ?>
//输出结果:
{"text":"\u5927\u5bb6\u597d\uff01\u6700\u8fd1\u4e00\u76f4\u90fd\u5f88\u5fd9\uff0c\u4f46\u662f\u6211\u4f1a\u5c3d\u91cf\u7ecf\u5e38\u66f4\u65b0\u6211\u7684\u65e5\u5fd7;\u628a\u5b66\u5230\u7684\u4e1c\u897f\u8bb0\u5f55\u4e0b\u6765\u3001\u5206\u4eab\u7ed9\u5927\u5bb6","created_at":"Wed Jul 22 22:33:44 EDT 2009","user":{"name":"adam"}}

注意:以上的输出里有很多4位的十六进制代码,这是因为json_encode把中文都转换为对应的十六进制代码。

和encoder对应,我们还可以使用json decoder把JSON feed转换成object或者array。例如:

<?php
$url = 'http://feeds.delicious.com/v2/json/tags/61dh?count=100';
$jsonfeed = file_get_contents($url);
echo $jsonfeed;
$result = json_decode($jsonfeed, true); 
//把第二个变量是控制输出类型的,true为Array,默认(false)为Object
print_r ($result);
?>
输出结果:
Array
(
    [ajax] => 8
    [CSS] => 19
    [HTML] => 16
    [Javascript] => 37
    [jQuery] => 37
    [jQuery基础] => 11
    [Linux] => 5
    [mySQL] => 3
    [Perl] => 5
    [PHP] => 25
    [PHP实用代码系列] => 4
    [Smarty] => 2
    [博客] => 21
    [浏览器] => 10
    [百度知道] => 4
    [素材] => 2
    [编程开发] => 7
)

http://feeds.delicious.com/v2/json/tags/61dh?count=10 这个链接是美味书签提供的,经常光临我的日志的朋友,可能知道,我没发布一篇文章都会把链接加到美味书签,然后使用它产生本站的日志分类。使用json-decode, 我就可以从json feed里把日志分类和文章数解析出来。

如果你用的5.2以前的版本,你可以使用Zend的Json库,一般PHP服务器带有Zend。使用方法如下:

<?php
require_once 'Zend/Json.php';
//encode例子:
$today = date("D M j G:i:s T Y");
 $status = array(
     "text" => "大家好!".
         "最近一直都很忙,但是我会尽量经常更新我的日志;" .
         "把学到的东西记录下来、分享给大家",
     "created_at" => "$today"
 );
 $name = array("name" => "adam");
 $status['user'] = $name;
echo Zend_Json::encode($status);
//decode例子:
$url = 'http://feeds.delicious.com/v2/json/tags/61dh?count=100';
$jsonfeed = file_get_contents($url);
echo  Zend_Json::decode($jsonfeed);
?>

标签:

2009年7月20日星期一

reCAPTCHA - 帮你帮我

CAPTCHA 是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机和人的公共全自动程序。CAPTCHA目前广泛用于网站的留言板,许多留言板为防止有人利用计算机程式大量在留言板上张贴广告或其他垃圾讯息,因此会放置CAPTCHA要求留言者必需输入图片上所显示的文数字或是算术题才可完成留言。

虽然自己动手写一个简单的CAPTCHA程序也不是很难,但是这里要介绍的如何使用reCATPCHA。reCAPTCHA计划的主要目的是利用CAPTCHA技术来帮助典籍数字化的进行,这个计划将由书本扫描下来无法准确的被光学文字辨识技术识别的文字显示在CAPTCHA 问题中,让人类在回答CAPTCHA问题时用人脑加以识别。为了验证人类所输入的文字是正确的,而不是随意输入,每次有两个字会被显示出来;一个是光学文字辨识软件无法辨别的字,另一个是一个已经知道正确答案的字。如果使用者正确的回答出已知正确答案的字,那么就假设所输入的另一个光学辨识软件无法辨识的字是认真的检视后被输入而非随便输入。因此,每一次有效的验证也是对reCAPTCHA计划的贡献。

(以上文字大部分摘自wiki

要把reCAPTCHA加到你的网站里很简单,reCAPTCHA提供适合于多种程序语言的接口,这里要介绍的reCAPTCHA PHP库的运用。

  • 首先到reCAPTCHA网站上获取API Key,这需要注册,但是是完全免费的。
  • 然后下载reCAPTCHA PHP库
  • 最后调用库函数。下面提供示例,通常这些代码是加在表单(form)里,你需要根据实际情况修改,如有问题请留言。
<?php
require_once('recaptchalib.php');
$publickey = "注册后你将得到这个key"; 
$privatekey = "注册后你将得到这个key";
# the response from reCAPTCHA
$resp = null;
# the error code from reCAPTCHA, if any
$error = null;
$switch = 0;
# are we submitting the page?
if ($_POST["submit"]) {
  $resp = recaptcha_check_answer ($privatekey,
                                  $_SERVER["REMOTE_ADDR"],
                                  $_POST["recaptcha_challenge_field"],
                                  $_POST["recaptcha_response_field"]);
  if ($resp->is_valid) {
	$switch = 1;	
  } else {
  	$switch = 2;
    $error = $resp->error;
  }
}
?>
<form name="validation" enctype="multipart/form-data" action="" method="post">
<!--下面的javascript变量可以控制图片的主题外观,当然这是可选的-->
<script>
var RecaptchaOptions = {
   theme : 'clean',
   tabindex : 2
};
</script>
<?php
if ($switch == 0){
	echo '有空?给reCAPTCHA项目贡献几个字吧:-)';
//你可以把表单内容放在这里
}
elseif ($switch == 2){
	echo '你是机器人?不是的话,请再输一次。';
}
else{
	echo '谢谢你对reCAPTCHA的支持';
//这里加入表单提交成功后的提示信息
}
//下面是重要的recaptcha函数,调用captcha的图片
echo recaptcha_get_html($publickey, $error); 
?>
//这里可以加入其他表单信息
<input type="submit" name="submit" value="submit" />
</form>

下面我用iframe把reCAPTCHA加入这个帖子里,这个表单不传递其它信息,仅供网友测试,同时给reCAPTCHA做点贡献。:-)

reCAPTCHA - 一个帮你帮我的项目,不是吗?

标签: ,

2009年6月22日星期一

PHP发送Gmail邮件加附件

前一段,我介绍了如何用PHP调用Gmail发送邮件,此方法只适用于发送文本邮件,也就是不支持附件发送。其实,要发送附件也不难,只是需要安装一个模块:Mail-Mime。下面就给大家做过详细介绍。

1.首先安装Mail-Mime, 你可以到pear网站下载后安装,如果你的机子装有Pear的包管理器,你直接运行下面的命令即可:

mail-mime pear install -o Mail_Mime
#在unbuntu下,使用sudo
sudo mail-mime pear install -o Mail_Mime

2. 使用下面的代码,这和PHP调用Gmail发送邮件一文中介绍的代码相识,不同的是多了Mail_Mime, 它是用来构造邮件的主题部分(内容和附件):

<?php
require_once "Mail.php";
require_once "Mail/mime.php";
//请用你的Gmail帐号和密码替代
$host = "smtp.gmail.com";
$username = "your-account";
$password = "your-password";
//定义收信人,发信人,以及主题和信息等
$from = "Adam<your@gmail.com>";
$to = "Cindy<someone@any.com>";
$subject = "Hi!";
$text = "Hi,\n\nHow are you?";
$html = "<b>hi</b>; 你也可以发送HTML格式的邮件
//定义附件路径和名称
$file = "/root/test/mail.php";
//如果你对下面的代码不熟悉,那么就请保留原样
$crlf="\n";
$headers = array (
	'From' => $from,
  	'Subject' => $subject);
$mime = new Mail_mime($crlf);
$mime->setTXTBody($text);
$mime->setHTMLBody($html); //发送HTML
$mime->addAttachment($file, 'text/plain');
$body = $mime->get();
$hdrs = $mime->headers($headers);
$smtp = Mail::factory('smtp',
  array ('host' => $host,
    'auth' => true,
    'username' => $username,
    'password' => $password));
$mail = $smtp->send($to, $hdrs, $body);
if (PEAR::isError($mail)) {
  echo("<p>" . $mail->getMessage() . "</p>");
 } else {
  echo("<p>Message successfully sent!</p>");
 }
?>

注意:在上述代码里,我使用的附件内容类型是文本(content-type:text/plain)。如果你要发送PDF,图片,ZIP等其他文件类型,你可以使用下面的代码代替:

$mime->addAttachment($file, 'text/plain'); //文本
$mime->addAttachment($file, 'application/pdf'); //PDF
$mime->addAttachment($file, 'image/gif'); //图片
$mime->addAttachment($file, 'application/zip'); //ZIP
关于content_type (内容类型),请参考:w3schools的介绍

标签:

2009年6月17日星期三

PHP中的值是否为空

在百度知道看到一个网页提问,是关于PHP中的值是否为空。这个问题还是相对简单,参考PHP手册,以下几种情况在PHP里被定义为空:

  • "" (an empty string)
  • 0 (0 as an integer)
  • "0" (0 as a string)
  • NULL
  • FALSE
  • array() (an empty array)
  • var $var; (a variable declared, but without a value in a class)

掌握这些规则,我们就可以对下面的PHP变量进行是否为空的判断。

<?php
$a1 = null;
$a2 = false;
$a3 = 0;
$a4 = '';
$a5 = '0';
$a6 = 'null';
$a7 = array();
$a8 = array(array());
echo empty($a1) ? 'true' : 'false';  //true
echo "<br>";
echo empty($a2) ? 'true' : 'false';  //true
echo "<br>";
echo empty($a3) ? 'true' : 'false';  //true
echo "<br>";
echo empty($a4) ? 'true' : 'false';  //true
echo "<br>";
echo empty($a5) ? 'true' : 'false';  //true
echo "<br>";
echo empty($a6) ? 'true' : 'false';  //false, 因为$a6不是空字符串也不是'0'
echo "<br>";
echo empty($a7) ? 'true' : 'false';  //true
echo "<br>";
echo empty($a8) ? 'true' : 'false';  //false,因为$a不是空数组
?>

标签: ,

2009年6月2日星期二

PHP调用Gmail发送邮件

大家对PHP的mail()函数一定都很熟悉,它简单易用,这里就不介绍了。如果你不知道怎么用,请查看PHP的在线手册。但是mail()并不是在任何服务器上都可以用的。下面是两个使用mail()的主要先决条件:

  • 1.服务器必须安装有sendmail
  • 2.smtp服务器无需鉴权(authentication)

比如自家的服务器,我在'Ubuntu命令行发送邮件到远端'一文里介绍过这个问题。 今天我准备介绍另外一种方法: PEAR:Mail + Gmail。首先,安装PEAR Mail, 这需要3个模块:NET/SMTP, Mail,Net/Socket。

如果你用的是RedHat,具体步骤如下:

1. 下载文件:
# cd /tmp
# wget http://download.pear.php.net/package/Mail-1.1.14.tgz
# wget http://download.pear.php.net/package/Net_SMTP-1.2.10.tgz
# wget http://download.pear.php.net/package/Net_Socket-1.0.8.tgz
2. untar 所有文件:
# tar -zxvf Mail-1.1.14.tgz
# tar -zxvf Net_SMTP-1.2.10.tgz
# tar -zxvf Net_Socket-1.0.8.tgz
3.安装文件:
# cd /usr/share/pear
# mkdir Net
# cd Net
# cp /tmp/Net_SMTP-1.2.10/SMTP.php .
# cp /tmp/Net_Socket-1.0.8/Socket.php .
# cd ..
# cp -avr /tmp/Mail-1.1.14/Mail/ .
# cp -avr /tmp/Mail-1.1.14/Mail.php .

如果你用的是Ubuntu,相对要简单一些:

1.如何你还没有装PEAR:
sudo apt-get install php-pear
2.有了pear以后,直接使用下面的命令:(提示:使用 ‘--alldeps’ 可以自动安装Mail的相关模块)
sudo pear install --alldeps Mail

安装好Pear的Mail模块后,你就可以使用PHP代码调用Gmail来发送邮件了。下面的代码可供参考:

<?php
require_once "Mail.php";
 
$from = "Adam <adam@gmail.com>";
$to = "Cindy <cindy@61dh.com>";
$subject = "Hi!"; //邮件主题
$body = "Hi,\n\nHow are you?"; //邮件内容
 
$host = "smtp.gmail.com";
$username = "adam"; //gmail用户名
$password = "mima-pass"; //gmail密码
 
$headers = array ('From' => $from,
'To' => $to,
'Subject' => $subject);
$smtp = Mail::factory('smtp',
array ('host' => $host,
'auth' => true,
'username' => $username,
'password' => $password));
 
$mail = $smtp->send($to, $headers, $body);
 
if (PEAR::isError($mail)) {
echo("<p>" . $mail->getMessage() . "</p>");
} else {
echo("<p>Message successfully sent!</p>");
}
?>

参考资料:

https://help.ubuntu.com/community/PhpPear
http://www.cyberciti.biz/tips/howto-php-send-email-via-smtp-authentication.html

标签: ,

2009年4月19日星期日

PHP实用代码系列 - Ajax密码强度检测

PHP和Ajax搭配真的不是什么新的东西,Google一下,你可以找到很多介绍它们的文章。正是因为它们的流行、简单、强大,所以我觉得在PHP实用代码系列文章里一定不可以少了它们。

在jQuery没出来以前,我都是通过GetXmlHttpObject()函数来实现Ajax。现在好了,jQuery把所有这些繁琐的步骤都包含在它的库文件里,我们只要通过它的接口函数,就可以轻松实现Ajax。在这些接口函数里最简单的应该要算load()。下面我就要开始介绍,PHP+Ajax(通过jQuery的load函数)的小运用 - 密码强度检测

首先是PHP代码(pass_test.php):

<?php
$password = isset($_POST['pass']) ? $_POST['pass'] : "";
if (!$password) echo "";
else{
   $strength = 0;
   // 小写字母
   if(preg_match("/([a-z]+)/", $password)) {
    $strength++;
   }
   // 大写字母
   if(preg_match("/([A-Z]+)/", $password)) {
       $strength++;
   }
   // 数字
   if(preg_match("/([0-9]+)/", $password)) {
       $strength++;
   }
   // 非任意(文字和数字)字母和下滑线的字符
   if(preg_match("/(\W+)/", $password)) {
       $strength++;
   }
   // 密码的长度
   if (strlen($password) < 7) $strength--;
   else $strength++;
   switch($strength) {
       case 0:
       case 1:
           echo '密码强度太弱';
       break;
       case 2:
           echo '密码强度弱';
       break;
       case 3:
           echo '密码强度高';
       break;
       case 4:
       case 5:
           echo '密码强度很高';
       break;
   }
}
?>

上面的PHP代码很简单,它只是通过一些自定义的规则来判断密码的强度。通常认为高强度的密码必须包含大小写字母、数字、和特殊字符,而且密码长度应该不小于6位。

接下来是HTML代码:

<label>请输入密码:</label>
<input id="pass" type="password" name="password" />&nbsp;
<div id="passwd_strength"></div> <!--这个div用来显示提示信息-->

最后是jQuery代码:

<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript">
   $(document).ready(function(){
      $("#pass").keyup(function(){
         $("#passwd_strength").load("pass_test.php", {"pass" : $(this).val()});
      });
   });
</script>
演示

标签: ,

2009年4月17日星期五

PHP实用代码系列 - 控制CSS

许多网站都提供换肤的功能,这种动态CSS的换肤技术通常是通过Javascript来实现,比如你可以准备两个CSS样式文件(分别定义不同的颜色,背景图等等),当用户点击选择某个样式后,触发自定义Javascript函数来更改CSS。但在这篇文章里,我要介绍的不同的方法,即使用PHP来控制页面的CSS样式。和Javascript的方法相比,这种方法应该算是真正意义上的动态CSS,因为它是用PHP输出CSS样式文件。例如:通过PHP函数,我们可以在一天的不同时段或者某个特别的日子(例如圣诞节),输出不同的CSS样式。

先看看演示,接下来要介绍如何实现这一特效。

首先使用HTML的link标签引用外部CSS样式文件:

<head>
 <link rel="stylesheet" type="text/css" href="style.php" />
</head>

注意: 这里被引用的css文件不是常规的.css文件,而是.php文件。

然后在style.php文件里,根据需要定义一些变量,然后输出css文件:

<?php
header("Content-type: text/css");
$ntime=date("G"); //取得现在的时间(小时)
$ntime += 8; //因为服务器的时间不准,所以这里做了些调整
if($ntime>=0 and $ntime<11){
  $bg = '#ffc';
  $font_color = '#222';
}
elseif ($ntime>=11 and $ntime<14){
  $bg = '#c36';
  $font_color = '#fff';
}
elseif ($ntime>=14 and $ntime<18){
  $bg = '#9cc';
  $font_color = '#555';
}
else{
  $bg = '#333';
  $font_color = '#ffc';
}
?>
body {
 background:<?php echo "$bg" ?>;
 color:<?php echo "$font_color" ?>;
}
a {color:<?php echo "$font_color" ?>;}
a:hover {text-decoration:none;}

上面的PHP代码只是根据不同的时间段,定义背景和字体颜色。在实际运用中,你还可以根据不同的节日来设置CSS的属性变量。总之,你可以根据需要,使用PHP来完全控制CSS。特别注意:在PHP文件的顶部要先送出一个Header:header("Content-type: text/css"); 表明输出的类型为CSS。

代码及演示

标签: ,

2009年4月15日星期三

PHP实用代码系列 - 随机图片

在这个系列里,我将要给大家介绍很多PHP实用代码,这些代码并非很复杂,但是却非常经典实用。希望这些文章可以起到抛砖引玉的作用,并且可以把这些知识运用到你的下一个项目里。

这篇文章要介绍的是如何使用简单的PHP函数来实现随机显示图片。为了让网页更具动态效果,有时你希望用户刷新页面后,可以看到不同的标题图片。用PHP的rand()函数来实现这一特效真的是在简单不过了。

首先准备几张标题图片,把它们命名为:
img_1.jpg, img_2.jpg,img_3.jpg,img_4.jpg,img_5.jpg,img_6.jpg。
当然你还可以使用其它类型的图片,例如:gif。然后,把这些图片上传到某个文件夹,例如:"/images/random"。

接下来使用下面的代码就可以了:

<img src="/images/random/img_<?php echo rand(1,6)?>.jpg" border="0" />
<!-- 注意: "/images/random"保存随机图片的文件夹路径,你应该使用实际路径替换--> 

是不是很简单?

这种方法稍微有点麻烦的地方是你要把图片根据上面的规则重新命名。如果你不想重新命名图片,你还可以使用下面的方法,但是要多写几行PHP代码。 :-)

<?php
$dir = $_SERVER['DOCUMENT_ROOT'] . "images/random";
chdir ($dir);
$images = glob("*.{gif,png,jpg}", GLOB_BRACE);
$file = $images[array_rand($images)];
?>
<img src="<?php echo ("/images/random/$file"); ?>" border="0"  />

注意:

  • 在上面的代码里,我仍然假定图片被存放在文档根目录(document_root)下images文件夹的random文件夹里,即/images/random, 所以你要根据实际情况来改变。
  • 在使用glob函数以前,要先更改路径(chdir)到图片目录。

使用后面这种方法相对要灵活很多,你只需把随机图片上传到图片文件夹即可,无需改名。而且不限图片格式(只要浏览器支持),更强大的是你可以实时更新图片而不用更改代码。

标签: ,

2009年4月6日星期一

jQuery 制作迷你背词汇工具

jQuery提供的Load的函数可能可以说是实现ajax功能的最轻巧的方法。我在博客里介绍了很多,请参考这些文章。今天我要介绍的是load()函数的一个实际运用,希望你读完以后会觉得它很简单、而且很实用。下面是一个类似金山词霸里背单词的小工具,它和滚动文字(图片)的效果差不多,但是用到的是ajax功能,也就是涉及到服务器端的脚本的执行。

loading ...

首先我创建的一个文本文件包含有我要背诵的英文词汇,然后是下面的PHP代码,用来读取词汇,并且随机返回一个词汇。

<?php
$buffer = array();
$handle = @fopen("toefl_listen.txt", "r");
if ($handle) {
    while (!feof($handle)) {
        array_push ($buffer, fgets($handle, 4096));
    }
    fclose($handle);
}
echo $buffer[array_rand($buffer)];
?>

最后是通过下面的Javascript脚本加上一点Ajax技术,调用服务器端的PHP代码,并把返回结果在特定DIV里显示。因为是循环播放,所以我用到了setInterval()函数。此外还使用clearInterval()函数,实现鼠标滑过 - 暂定播放的功能。

<script>
  $(document).ready(function()
  {
   //没隔3秒调用服务器端的php文件
   var refreshId = setInterval(function()
   {
    $('#timeval').load('reflesh.php');
   }, 3000);
   //鼠标滑过 - 暂停播放
   $("#timeval").mouseover(function()
   {
      clearInterval(refreshId);
   });
   $("#timeval").mouseout(function(){
      refreshId = setInterval(function()
      {
      $('#timeval').load('reflesh.php');
      }, 3000);
   });
  });
</script>
我觉得上面介绍的间隔一定时间调用服务器的代码,其扩展性还是挺大的。我这里只是使用它来读取一个简单的文本文件,你还可以用它来调用数据库,来实现对某个数据的实时更新。

标签: , ,

2009年3月29日星期日

iconv 中文字符分割

我在日志介绍过PHP对中文字符分割的方法,其中我用到ord()函数把字符转换成ASCII码,然后通过判断不同字符的长度来正确分割中文字符串。前一段在研究lucene的源代码时,看到iconv()函数,发现用iconv()函数进行中文字符分割要更加简洁。代码如下:

<?php
$s="上网导航";
$b = array();
$len = iconv_strlen($s, "UTF-8"); 
for ($i=0; $i<$len; $i++){
   $a = iconv_substr($s,$i,1,"UTF-8");
   array_push($b, $a);
}
print_r($b);
?>
/* 结果如下:
Array
(
[0] => 上
[1] => 网 
[2] => 导
[3] => 航
)
*/

PHP的iconv模块是用来支持多国语言的。我偏好UTF-8,所以你看到我在iconv_strlen()iconv_substr()里UTF-8作为编码方式。除了多了编码,iconv_strlen()iconv_substr()的用法和strlen()substr()的用法相同,在这里不做详述。

另外一个注意点是,iconv_substr()函数在PHP5.2以前的版本里存在一个BUG:当字符串的长度是1的时候会报错。例如:
$s = iconv_substr('A', 0, 1, 'UTF-8'); 程序运行报错信息:Notice: iconv_substr() [function.iconv-substr.html]: Unknown error(0) ...如果你使用PHP5.2以前的版本,这里有个解决办法:在字符串后加上一个空格;如下:

<?php
$s="";//字符串的长度为1
$b = array();
$len = iconv_strlen($s, "UTF-8");
$s .= " "; //在string后加上一个空格,来解决iconv_substr的bug
for ($i=0; $i<$len; $i++){
   $a = iconv_substr($s,$i,1,"UTF-8");
   array_push($b, $a);
}
print_r($b);
?>
 

标签:

2009年3月26日星期四

cookie

cookie在网站开发里运用的很多,特别是购物网站。cookie可以分为两种,会话cookie(session cookie) 和持久cookie(presistent cookie)。在PHP里,当一个会话开始,服务器会在客户端的浏览器里设置一个cookie用来保存PHP session ID,而cookie的内容被存放在服务器端。会话cookie的有效期通常是持续到会话结束,也就是当你关闭浏览器后,但是你也可以在PHP.INI里改变会话的有效期。通常我们说的cookie是指持久cookie,它是完全保存在客户端的浏览器里,其有效期是在设置的时候定义好。在这篇文章里,我准备介绍持久cookie(以下简称cookie)。

cookie的运用涉及到信息的保存和提取,当然我们可以使用数据库来完成这项工作,但是对于一些小信息,比如email,使用cookie显得更加快捷。cookie是存在客户端的,因此首先想到的是使用Javascript来设置和提取cookie。W3Cschool上提供两个函数,使用它们基本上就可以满足我们的需要。我把它们稍作修改,如下:

// 全局变量
domain    = '.61dh.com'; //如果不设代表当前页面的域名
path    = '/'; //通常使用根目录,当然你可以可特指一个路径
secure    = 0; //对于SSL网站(https),需要设置安全cookie,这时可以把值设为‘1’
//setCookie,设置cookie,需要三个变量,cookie名称,cookie值,和有效时长
function setCookie(c_name,value,expiredays){
  var exdate=new Date();
  exdate.setDate(exdate.getDate()+expiredays);
  document.cookie=c_name+ "=" +escape(value)+
  ((domain.length > 0) ? ';domain=' + domain : '') +
  ((path) ? ';path=' + path : '') +
  ((secure) ? ';secure' : '') +
  ((expiredays==null) ? "" : ";expires="+exdate.toGMTString());
}
//getCookie,获取相关cookie的值
function getCookie(c_name){
  if (document.cookie.length>0){
    c_start=document.cookie.indexOf(c_name + "=");
    if (c_start!=-1){ 
      c_start=c_start + c_name.length+1; 
      c_end=document.cookie.indexOf(";",c_start);
      if (c_end==-1) c_end=document.cookie.length;
      return unescape(document.cookie.substring(c_start,c_end));
    } 
  }
  return "";
}

有了上面的函数,你就可以根据需要来保存一个数据(setCookie),然后再根据需要取回数据(getCookie), 例如:

setCookie('email',adamcai@live.com,365);//保存email,有效期365天
var email = getCookie('email');//获取email

另外一种方法是使用PHP,这要根据实际需要,有时你可能会觉得使用PHP更方便。PHP自带有函数:

setcookie(name,value,expire,path,domain,secure)
  • name:必须,cookie名称
  • value:必须,cookie值
  • expire:任择。指定的cookie时到期。单位是秒,例如: 3600 * 24 * 30将设置Cookie过期30天。如果此参数没有设置时,cookie将在会话结束时(当浏览器关闭)到期,
  • path:任择。指定服务器路径的Cookie如果设置为“ / ”时, cookie将可在整个域内有效。如果设置为“ /测试/ ”时, cookie将只在测试目录及所有子目录有效。默认值是当前目录。
  • domain:任择。指定域名的cookie 。为了使现有的cookie的所有子网域内有效,你应该将其设定为 ".example.com"。如果不设置,将使用当前页的域名。
  • secure:任择。指定是否cookie应用于安全的HTTPS连接。如果设为true,那么cookie将只可以在安全连接存在的情况下设定。默认值为false 。

$_COOKIE['name'] //用来获取cookie的值。

//设置cookie
setcookie("email","adamcai@live.com”, time()+3600*24); //时常为24小时
//获取cookie
if(isset($_COOKIE("email")){
  $email = $_COOKIE("email");
}

标签: ,

2009年3月9日星期一

jQuery ajax - 如何使用XML

我在日志里介绍了很多使用jQuey load的方法来实现ajax。load()应该是jQuery里ajax的最简单的运用了,你不用了解ajax是怎么一回事,就可以把某个脚本的输出(通常是text/html类型)动态的赋给页面的某个元素(例如div和p)。但是在实际的运用中,你可能会发现load()常常不能满足要求,例如:你想对一个表单的多个控件使用ajax进行更新,可是load()一次只可以对其中一个控件起作用;如果使用多个load(),那么会增加服务器的负荷,因此肯定不是一个好办法。本文要介绍jQuery的ajax()方法,它是类似传统的ajax方法。jQuery的ajax()方法涉及的知识点很多,具体可以参考jQuery中文参考文档。这里要介绍的是如何使用XML数据类型以及如何对XML进行读取。

1.使用PHP输出XML,如果你只是使用echo,那么要注意要先输出正确的header: Content-Type是text/xml。如果你要给XML的某个节点用HTML代码赋值,你必须把HTML放在<![CDATA[" 和"]]>之间。例子如下: genXML.php

<?php
echo "header("Content-Type: text/xml; charset=UTF-8");";
echo <<< XML
<?xml version="1.0" encoding="UTF-8"?>
<rsp>
  <name>Adam</name>
  <site>http://www.61dh.com</site>
  <note>
    <![CDATA[ 
      HTML代码可以放在这里
    ]]>
  </note>
</rsp>
XML;
?>

2. HTML样本:

<div id="name"></div>
<div id="note"></div>

3.接下来我们要使用jQuery的ajax()方法,读取genXML.php的输出(xml),然后把相关节点的内容赋给HTML元素:#name和#node。代码如下:

$.ajax({ 
  url: "genXML.php", //本例中,url为genXML.php
  type: 'GET', //根据实际情况,可以是'POST'或者'GET'
  //data: "name=adam&location=61dh",本例中没有用到data
  dataType: 'xml', //指定数据类型
  timeout: 1000, //设置超时1000 = 1秒
  error: function(){ 
    alert('Error loading XML document'); }, //错误提示
  success: function(xml){ //ajax请求成功后 do something with xml
    //把XML的相关节点值赋给HTML里相关ID
    $('#name').html($(xml).find('name').text()); 
    $('#note').html($(xml).find('note').text());
  }
});

我在上面的代码里已经做了比较详细地注释,值得再提的是使用jQuery提供find()的方法可以很简单的读取XML某个节点的值,例如:$(xml).find('name').text(); //读取节点name;然后使用html()的方法赋值给HTML里的某个元素。

注: 我在日志里写了很多有关jQuery的文章而且还挺受欢迎:-),所以我把它专门作为一个菜单放在上方导航条里。希望这些文章对jQuery初学者有所帮助。如果你有任何的问题和建议,请给我留言或者发Email。谢谢!

标签: , ,

2009年2月16日星期一

ajax vs iframe

前两天在百度知道看到一个有关表单验证的问题,提问的网友想知道:

...录入完张三的信息后,再录入张三的名字时会提示已经录入过该人的信息了,弹出信息框的形式提示...

这里的验证需要通过数据库的查询来验证,因此需要另一个PHP文件来执行。并且执行的结果要以弹出信息框的方式出现,因此要用到Javscript。通常的解法应该是使用ajax,具体思路如下:(我采用jQuery提供的ajax方法,这样代码会比较简单)

1. 需要验证的文本框 - HTML代码:

<input name="xm" type="text" id="xm" size="30" />

2. 使用jQuery的ajax方法调用check.php文件并且把结果:

$("#xm").onChange(function(){
  var name = $(this).val();
  $.ajax({
   type: "GET",
   url: "check.php",
   data: "name="+name,   
   success: function(repeat){
     if(repeat)
     alert( "重复!" );
   }
  });
});

3. check.php - 检测输入的名字是否已经存在于数据库,如果是返回1,如果不是返回0

<?php
  $xm=$_REQUEST['xm'];
  $sql="select count(*) from jinengchaxun where xm='$xm'";
  mysql_connect(..自己一定写正确哦..);
  $res=mysql_query($sql);
  list($cnt)=mysql_fetch_row($res);
  mysql_free_result($res);
  mysql_close();
  return ($cnt>0) ? 1 : 0; 
?>

注意:我没有测试过这种方法,有兴趣的可以试一下。

另外一种方法用到iframe,是另外一个网友提供的:

/**一、编写一个checkxinxi.php来检测指定的名字是否存在,内容如下:*/
<?php
$xm=$_REQUEST['xm'];
$sql="select count(*) from jinengchaxun where xm='$xm'";
mysql_connect(..自己一定写正确哦..);
$res=mysql_query($sql);
list($cnt)=mysql_fetch_row($res);
mysql_free_result($res);
mysql_close();
echo <<<END
<script type=text/javascript>
if ($cnt>0) {
alert('重复提示:已经录入过 $xm 了!');
parent.document.getElementById('chk_rst').innerHTML='重复';
}else parent.document.getElementById('chk_rst').innerHTML='新人';
</script>
END;
?>
/**二、addxinxi.php进行如下修改既可:*/
<input name="xm" type="text" id="xm" size="30" />
//修改为:
<input name="xm" type="text" id="xm" size="30" 
  onchange="chk_frm.location.href='chkxinxi.php?xm='+this.value;" />
<span id=chk_rst></span>
<iframe name=chk_frm style='display:none'></iframe>

我把原答案里的parent.chk_rst.innerHTML 改成 parent.document.getElementById('chk_rst').innerHTML,因为Firefox不识别parent.chk_rst

个人感觉,使用iFrame比较简单, 但是iFrame其实是把一个页面隐藏在另外一个页面里,所以执行的时候它需要刷新整个页面。而ajax可以异步更新,所以相对要平滑许多,而且使用jQuery后ajax的书写也变得简单了,所以还是建议使用ajax。

方法二的演示

标签: , , ,

2009年2月10日星期二

ionCube - PHP的加密/解密工具

公司昨天买了一套affiliate marketing的软件(用来管理通过affiliate网站带来交易的佣金计算),之前我们用的是Commision Junction,现在准备在自己的服务器上管理affilate的交易信息,这样就可以省去Commission Junction的中间抽成。

今天在安装这套软件的时候,发现它需要ionCube loader支持。开始还不知道什么是ionCube,上网Google了一下才知道ionCube是用来加密PHP的工具。ionCube Encoder可以把PHP源代码转换成ByteCode。进行加密授权处理后的PHP代码就不在开源了,必须使用ionCube loader才可以执行加密过的PHP代码。PHP本身没有带有ionCube loader模块, 必须到ionCube网站下载。ionCube loader是免费的,但是Encoder的价格就比较昂贵(起价$199)。

下面要介绍如何在Linux下安装ionCube Loader。

1. 下载ionCube http://www.ioncube.com/loader_download.php, 通过这个链接可以找到对应的版本,我使用wget从命令行获取Linux64版本。
wget -c http://downloads2.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz

2. 解压 tar -zxvf ioncube_loaders_lin_x86-64.tar.gz

3. 在解压后的文件夹了可以找到ioncube-loader-helper.php, 把它拷贝到网站的目录下,例如:
cp ioncube-loader-helper.php /var/www/html/

4. 然后在浏览器下打开ioncube-loader-helper.php,点击Server System Information链接,它会根据你的系统配置,告诉你应该使用那个ionCube-loader模块,并且告诉php.ini的路径。

5. 接下来把相关模块复制到/usr/local/(或者其他目录),根据第四步的提示在php.ini里加入一行。例如:
zend_extension = /usr/local/ioncube/ioncube_loader_lin_5.3.so
注意:如果你的php.ini里没有zend_extension,你可以把这行加在任一位置,否则,把它加在第一个zend_extension语句前。

6. 保存php.ini,重启服务器。在我们的服务器上使用到的命令是:/etc/init.d/httpd restart

另外我还在ZendChina上看到一篇关于ZendGuard 与ionCube工具比较的文章,ZendGuard也是一款对PHP源码加密的工具,ZendGuard只能用于配置了ZendOptimizer的环境中,不能独立运行。据说使用的ZendOptimizer(PHP优化引擎)可以提高20~50%的源码执行速度,如果结合ZendGuard可以把速度提高至50%以上。我们公司的网站还没有使用PHP加密和PHP优化,有机会一定要试试。

不知PHP的加密是否违背开源的理念?

标签:

清除IE7的Cookie

我们公司的合作伙伴(affiliate)要求从通过他们的网站进入我们的网站后可以显示他们公司的图标。我们提供的解决方法是这样的:在链接进来的url后加入一个参数,通过这个参数来设置Session变量,然后通过判断Session的存在与否以及Session的值来决定是否显示图片以及显示哪个affiliate的图标。

Page.inc:

//1.先判断URL
$img = (isset($_GET['img'])) ? 
       trim(stripslashes(x_site_safe($_GET['img']))) : "";
//2. 如果URL上没有img参数,查看SESSION变量
if (!$img) $img = (isset($_SESSION['img']))? $_SESSION['img'] : "";
//3. 判断affiliate的图标是否存在
if (file_exists("/var/www/production/images/ns/img_upl/aff_logo_$img.gif"))
  $this->aff_img = $img;

Sessioin.inc

//1.判断url的img参数是否存在
$this->url['img'] = (isset($_GET['img'])) ? 
                     trim(stripslashes($_GET['img']))      : "";
//2.如果是新的访问并且URL含义合法的img参数,设置SESSION变量$_SESSION['img']
if ($this->do_tracking) { 
  if($this->url['img']) $_SESSION['img'] = $this->url['img'];
}

我这里用到if ($this->do_tracking) 目的是只对新访问进行设置SESSION变量。最后根据$this->aff_img存在与否来决定是否在HTML里显示图标。 注意:以上并非完整代码,只是用于示例用。

代码完成了,接下来是在浏览器里测试代码是否正确,首先必须先清浏览器的COOKIE。SESSION变量其实也是一种COOKIE,只是它不是存在客户端的电脑里,而是存放在服务器端。浏览器提供的清空COOKIE的功能同时也可以清除SESSION COOKIE。

在Firefox下,我们使用Web Developer扩展,通过它可以快捷地清除Domain Cookie,然后就可以测试了。测试结果一切正常:如果第一次进入网站并且在URL上加入参数 /?img=1,在整个session内,都可以看到affiliate1的图标。

当时在IE7下,我们碰到了问题。我们使用IE7工具栏里删除选项清空了COOKIE,同样在URL后加入img参数,在第一个页面我们看到了affiliate的图标,但是当点击网页的其他链接进入一个新的页面(URL不在带有img参数),图标就消失了。很明显SESSION变量没有设置正确,按照逻辑,清除COOKIE后,第一次进入网站,如果URL含有img参数,应该要设置SESSION变量的。为什么不行呢?最终的答案是在IE7下清空COOKIE,要重启浏览器才生效!

标签: ,

2008年12月8日星期一

HTTP_REFERER在IE7下不工作

一般情况下在PHP里,我们可以通过HTTP_REFERER环境变量获得用户是从那个网页进入当前页面的(也有人称之为“来路”),使用方法为:

<?php
echo getenv("HTTP_REFERER");
//OR
echo $ENV{'HTTP_REFERER'};
?>

但是,HTTP_REFERER并不是万无一失的,因为有些浏览器在进入新页面时并不会送出这个变量。这几天公司的网站在增加在线咨询功能,为了更好回答用户碰到的问题,我们想知道用户是从那个页面进入在线咨询上的。当用户点击页面上的在线咨询按钮后会打开一个用户信息表单,这一步我用到HTTP_REFERFER把用户来路网址写入在表单,做为一个隐含值;当用户填好相关信息提交表单后这个隐含值被送给聊天引擎,这样客服人员就可以知道用户是从哪里进入在线咨询了。这种方法在Firefox下工作的很好,但是这IE7下却不工作。没办法,只好改用Javascript在用户点击按钮时,把来路网址作为一个参数传递给PHP文件。具体代码如下,

HTML/Javascript代码:

<a href="#" onClick="window.open(\'/chat/chat.php?di=24395&ref='+location.href+'\',\'custclient\',\'width=500,height=470,scrollbars=0\');return false;">
  <img src="images/livechattile_on_rtnav.gif" border="0">
</a>

注意:在调用chat.php时,当前网址location.href被当成一个参数。另外值得一提的是单引号内javascript变量的写法,必须使用 '+location.href+' ,而不是直接插入location.href

PHP代码:

//$ref = getenv("HTTP_REFERER");
//getenv在IE7下行不通,所以改用$_GET
$ref = ($_GET['ref'])? $_GET['ref'] : "";

标签: ,

2008年12月7日星期日

PHP开发者应该关注的6个网站

PHP语言入门不难,如果你有一些编程基础(例如C,Perl),照着PHP的在线用户手册,你可能三两天就可以入门了。但是PHP做为一门优秀的网站编程语言,它所包含的内容绝对不是三两天就可以学好。想对精通PHP需要长期的经验积累,如果你想成为是一名优秀的PHP开发者,那么你应该常常关注下面列出的6个网站。

1. Zend Developer Zone
Zend’s Developer Zone 经常会发布介绍PHP各方各面的文章和指南。

2. PHP Architect C7Y
C7Y是类似Zend的PHP公司,提供PHP构建的社区论坛。你可以在那里找到很多有关PHP开发的文章和PHP的最新报道。

3. PHPDeveloper.org
有关PHP的最新、最重要的报道都可在PHPDeveloper找到,另外它还提供PHP博客、工作信息等资源。

4. Digg.com/programming
在Digg社区里你总是可以看到当今流行的文章,而在Programming分区里,你将不会错过与网站编程相关的精彩的文章。

5. PHPClasses.org
PHPClasses.org提供丰富的PHP Class,这些前人写好的库文件往往会帮你省下许多开发的时间。

6. Planet PHP

是博客的世界,它聚集100多个主要的PHP博客文章。

7. Google Reader (推荐)

由于每个人的时间都是有限的,不可能对上述网站的每一篇文章都读过。好在这些网站大都提供RSS,我们可以通过RSS阅读器来选择自己感兴趣的文章。这里推荐使用Google Reeder,然后通过下面的RSS链接把这些网站收录到阅读器里。

标签: ,

2008年11月20日星期四

jQuery load的详解

我在日志里介绍过,jQuery Load的简单运用如何防止Load使用缓存。这篇文章将对jQuery Load做更加详细的介绍。

调用load方法的完整格式是:load( url, [data], [callback] ),其中

  • url:是指要导入文件的地址。
  • data:可选参数;因为Load不仅仅可以导入静态的html文件,还可以导入动态脚本,例如PHP文件,所以要导入的是动态文件时,我们可以把要传递的参数放在这里。
  • callback:可选参数;是指调用load方法并得到服务器响应后,再执行的另外一个函数。

一:如何使用data

1.加载一个php文件,该php文件不含传递参数
$("#myID").load("test.php");
//在id为#myID的元素里导入test.php运行后的结果

2. 加载一个php文件,该php文件含有一个传递参数

$("#myID").load("test.php",{"name" : "Adam"});
//导入的php文件含有一个传递参数,类似于:test.php?name=Adam

3. 加载一个php文件,该php文件含有多个传递参数。注:参数间用逗号分隔

$("#myID").load("test.php",{"name" : "Adam" ,"site":"61dh.com"});
//导入的php文件含有一个传递参数,类似于:test.php?name=Adam&site=61dh.com

4. 加载一个php文件,该php文件以数组作为传递参数

$("#myID").load("test.php",{'myinfo[]', ["Adam", "61dh.com"]});
//导入的php文件含有一个数组传递参数。

注意:使用load,这些参数是以POST的方式传递的,因此在test.php里,不能用GET来获取参数。

二:如何使用callback

比如我们要在load方法得到服务器响应后,慢慢地显示加载的内容,就可以使用callback函数。代码如下:

$("#go").click(function(){
  $("#myID").load("welcome.php", {"lname" : "Cai", "fname" : "Adam", function(){
    $("#myID").fadeIn('slow');}
  );
});
演示和下载:jQuery-Load

标签: , ,

2008年11月14日星期五

利用美味书签创建日志分类

经过几天的努力,终于把日志分类做好了。Blogger是一个优秀的日志播报引擎,但是和我一样在使用它的FTP功能来发布日志的朋友,一定都有这样的烦恼 - Blogger的传统模板没有日志分类(也称作标签)功能。我们都知道日志分类的功能是很有用的,它会帮助读者更好的找到需要的帖子。因此在我还没有切换到WordPress之前,我准备自己做一个。最初的想法是通过博客的Feed来创建分类标签,但是发现Blogger只给我的博客Feed(供稿)提供20个帖子。这样一来,我就需要经常的备份旧的供稿,以便得到完整的帖子分类。

前几天给本博客增加了Add this - 收藏博客帖子的快捷方式,其中有一个快速把帖子链接加入美味书签的功能。因为目前日志的帖子数不多,所以通过这个AddThis按钮,我很快就把所有帖子都加到了我的美味书签。不试不知道,美味书签真不愧是当今第一网络书签。它提供丰富的功能和接口,很容易我就把美味书签提供的“云标签”添加到我的博客。代码如下,你可以把它加入你的网站,这样就很容易接入本博客的每一个帖子。

  <!--start delicious -->  
  <script type="text/javascript" 
   src="http://feeds.delicious.com/v2/js/tags/61dh?
        title=%E7%BE%8E%E5%91%B3%E4%B9%A6%E7%AD%BE
        &count=100&sort=alpha&flow=cloud&color=999-777&size=12-30">
  </script>  
  <!-- end delicious -->

除了“云标签”,美味书签还给网站开发者提供许多有用的工具,其中有一个由第三方开发者开发的PHP美味书签API,phpdelicious,就相当的好用,我就是利用它创建了日志分类。具体的过程如下:

1. 到phpdelicious网站下载库文件

2. 通过库文件提供的方法(methods),先导出帖子标签,然后再把对应的帖子找出来。示例代码如下:

<?php
   require('/home/dcai/61dh/library/php-delicious.inc.php');
   require('py.php');//这个用来把中文字符转换成拼音的,在我的上一个帖子里有介绍
   define('DELICIOUS_USER', '你的美味书签用户名');
   define('DELICIOUS_PASS', '密码');   
   $oDelicious = new PhpDelicious(DELICIOUS_USER, DELICIOUS_PASS);
   $tags = $oDelicious->GetAllTags(); //导出所有标签
   $aPosts = $oDelicious->GetAllPosts(); //导出所有帖子
   foreach ($tags as $tag){
?>
   <h3 class="post-title"><?=$tag['tag'] ?></h3>
<?php
     foreach($aPosts as $aPost) {
       if(in_array($tag['tag'], $aPost['tags'])){
?>
     <a href="<?=$aPost['url'] ?>"><?=$aPost['desc'] ?></a>
<?php
       }
     }
   }
?>
最后,使用PHP代码在本地产生HTML文件,然后用FTP上传到网站服务器,这个过程将用Linux的Cron自动完成。另外我还应用jQuery的Popin插件制作了对应标签帖子的覆盖层(overlay)展示。具体方法和步骤将会在以后的帖子里详述。

标签: ,

2008年11月13日星期四

UTF8字符串转换拼音

Blogger的传统模板不支持边侧栏的日志分类功能,这两天我忙着写代码来实现这个一功能。目前你已经可以看到用美味书签(delicious.com)制作的“标签云”(注:有国内的朋友反映,美味书签打开的速度很慢,估计是国内对delicious.com的过滤造成的)。其他日志分类功能也快要开发完成了,我采用的方法是在Linux操作系统上用PHP产生HTML代码然后上传到服务器,这样就免除在国内用户访问滞后的问题。估计明天可以把这个功能加上,到时再做详细介绍。这个帖子主要将介绍我在开发中碰到的编码问题:因为我的日志分类标签有用中文命名的,例如“浏览器”。如果我把“浏览器”标签的帖子放在一起,命名为“浏览器.html”,然后在链接里用“/blog/category/浏览器.html”,应该也是可以的。但是如果文件用中文命名,要想把文件从Linux系统里用WinSCP下载到本地是行不通的,原因是WinSCP不支持中文文件名。我没有花太多时间去研究是否可以更改WinSCP的编码设置来修正这个问题;而且我本来就不爱用中文来命名html文件,总觉得把中文放在地址栏有点怪怪的,所以我就想用别的方法来解决这个问题。

开始想到的是用urlencode函数,例如:

$string = urlencode("浏览器");
//编码后 $string = "%e6%b5%8f%e8%a7%88%e5%99%a8"

可是这个方法行不通,虽然url编码后链接是不含中文的,但是当你点击它后,它又会被转回中文,因为文件名也已经使用了url编码,所以这时它已经找不到以中文命名的文件了。

后来想到用拼音来代替中文字符,上网Google了下,找到了一些中文字符转换拼音的PHP库,但大多数是支持GB转拼音。因为我的系统用的是UTF-8,所以需要先把字符的编码转换一下:

$string="博客"
return iconv('UTF-8', 'GB2312', $string); //得到GB2312编码

此外,我的确还找到一个支持UTF8转换拼音的PHP代码。使用方法很简单,先点击这里下载py.php(注:不知道原作者是谁,如果谁知道请来信告知,我会把出处补上。)然后调用文件里的Pinyin函数即可。值得注意的是,它不支持大写的英文字母,比如字符串含有大写英文字母,转换后大写英文字母会丢失。因此在转换之前,要先把字符串小写化。具体调用代码如下:

<?php   
  require('py.php');
  $newtag = Pinyin(strtolower($tag),1);
/*注意:
1.$tag可以是中文,也可以是英文,但是因为字母必须小写,所以用到strtolower函数
2.因为我的系统用的是UTF-8编码,所以调用Pinyin函数的是,加入了'1'为第二个参数;如果你的
系统是GB编码的,调用的时候,则不需要这个参数。*/
?>     

更新(2/23/09): 谢谢匿名网友的提示, 在py.php里,把下面这行改下,就支持大写字母了。

return preg_replace("/[^a-z0-9]*/", '', $_Res);
//这里$_Res是转换好的字符串,使用preg_replace,把不是a-z以及不是数字的字符过滤,
//这么一来把那些大写字母也过滤了。因此把替换正则表达式如下更改一下就可以保留大写字母了。
return preg_replace("/[^a-zA-Z0-9]*/", '', $_Res);
正则是我的强项:-)按道理不应该不知道的,哈哈,没有认真读代码 :-(

标签:

2008年11月6日星期四

Smarty显示页面出现乱码的探究

公司的网站使用Smarty,Smarty是一种功能强大的模板引擎(Template Engine)。使用Smarty可以让HTML代码从PHP代分离出来,这样我们就用不着在PHP代码中加入无数的echo "<html>";,只要用$smarty->display('html.tpl');就可以把相关的HTML代码显示在浏览器上,这会让PHP代码简洁而便于阅读。

HTML的编码方式通常可以在HTML模板里定义的,比如说,在html.tpl里我们定义了:

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />

该模板就会用“iso-8859-1”的编码方式来显示代码。但是当要显示的模板是一个弹出层(通常这个弹出层只包含部分的HTML代码,然后显示在当前页面上)。例如:

<div id="add_overlay" style="border-color: rgb(58, 137, 201);">
  <div style="padding: 10px 5px 5px; width: 250px;">
    <div style="text-align: center; width: 250px; float: left;">
      <div style="font-size: 16px; margin-bottom: 5px;">
        {$cart.items[0].title}has been added into your cart!
      </div>
      <a href="/cart">
        <img src="/images/checkout.gif" alt="checkout" width="68" height="25">
      </a>
      <a href="Javascript:history.go(0)">
        <img src="/images/continue.gif" alt="continue" width="123" height="25">
      </a>
    </div>
  </div>
</div>

这时Smarty会用UTF-8来显示这个HTML层(layer)。问题是数据库里用的iso-88590-1为编码方式,如果用UTF-8来显示就会造成有些字符出现乱码,像注册商标"®"。我开始以为只要给弹出层加上header来定义正确的编码就可以了,其实不然,Smarty仍旧用UTF-8来显示。最终的解决办法是,在调用Smarty的display函数前,加入PHP的header函数来定义编码,如下:

if (isset($_GET['popin'])){
   header('Content-Type:text/html;charset=iso-8859-1');
   $smarty->display('cart/popin.tpl');
}

标签: ,

2008年10月30日星期四

PHP对变量赋值(包含特殊字符)

给变量赋值应该是一个很简单的概念,这里主要介绍一下双引号和单引号的区别,以及‘三个小于号’在声明冗长字符串(通常值HTML代码)中的使用。

1. 如果要声明的变量值不含有特殊字符(例如:‘$', '\', 等),用单引号和双引号是没有区别的。

$one = "www.61dh.com";  
$one = 'www.61dh.com';
//用上两种表达式等同的。

2. 如果要声明的变量值含有特殊字符(但不包括单引号),用单引号不用加反斜杠;但用双引号需要加反斜杠。防斜杠的目的是保留特殊字符原型(也就是不做特殊字符处理)。

$mysite = "61dh";
$one = "www.$mysite.com";
$two = 'www.$mysite.com';
//$one 的值现在是:www.61dh.com ($mysite的值插入)
//$two 的值现在是:www.$mysite.com (不对$mysite进行处理)
//如果你想用双引号实现单引号的结果,你可以用'\'来保留特殊字符,如下:
$one = "www.\$mysite".com";

3. 如果特殊字符含有单引号,用单引号要加入反斜杠;而用双引号无需加反斜杠。

$mysite = "61dh";
$one = "www.'$mysite'.com";
$two = 'www.\'$mysite\'.com';
//赋值后,$one= www.'61dh'.com; $two = www.'$mysite'.com

4. 当要声明的变量值是HTML代码的时候,无论用双引号或者单引号,你通常都要加入反斜杠,来保留一些特殊字符,像单引号,双引号,斜杠,等等。

$html1 = "<a href=\"www.61dh.com\">六一导航</a>";
$html2 = '<a href="#" onClick=\'goto();\'>六一导航</a>';
//注意反斜杠的使用,赋值后的结果是:
//1. html1 = <a href="www.61dh.com">六一导航</a>
//2. html2 = <a href="#" onClick='goto();'>六一导航</a> 

5.从上的例子中,你或许已察觉当要声明的变量值是一大段HTML代码的时候,我们要用很多的反斜杠,这将带来很大的不便。幸运的是,PHP提供另外一种赋值的方法 -- 使用三个小于号,"<<<"。

$name = "Adam";
$abc = <<< EOF
<div id="sidebar">
  <h2 class="sidebar-title">关于</h2>
  <p>本博客用于记录网站开发的点点滴滴。
包括网站设计,编程指南,资源和灵感。</p>
by $name
EOF;
echo $abc;
//以上代码将把两个EOF之间的部分,直接赋给变量$abc.
//注意:EOF之间的$name仍然被当作变量处理,这里它将别解析为Adam。

标签:

2008年10月14日星期二

PHP 对中文字符窜分割

下面是百度知道里一个网友的问题: PHP - 对字符串操作

我有一个由英文字母、数字、中文字符组成的字符串
我想要往这个字符串的每两个字符中间加入一个百分号“%”,例如:

我爱PHP编程
变成:
我%爱%P%H%P%编%程

如果直接用PHP的函数,str_split, 来分割,会出现乱码,因为中文字符长度和英文字符长度是不一样的。但是我们可以建立新的函数先把支付转成ASCII 值,接着通过判断不同字符的长度来正确分割中文字符串,把结果存入数组,最后再用PHP函数,join,在字符间插入百分号。具体代码如下:

<?php
function str_split_utf8($str) {
    $split=1;
    $array = array();
    for ( $i=0; $i < strlen( $str ); ){
        $value = ord($str[$i]);
        if($value > 127){
            if($value >= 192 && $value <= 223)
                $split=2;
            elseif($value >= 224 && $value <= 239)
                $split=3;
            elseif($value >= 240 && $value <= 247)
                $split=4;
        }else{
            $split=1;
        }
            $key = NULL;
        for ( $j = 0; $j < $split; $j++, $i++ ) {
            $key .= $str[$i];
        }
        array_push( $array, $key );
    }
    return $array;
}
$string ="我爱PHP编程";
$arr1 = str_split_utf8($string);
echo join("%", $arr1);
?>

标签: ,

2008年10月4日星期六

PHP Regular Expressions

我在上一篇文章:Perl 正则表达式 中,对Perl Regrex 做了详细的介绍。在使用PHP的正则表达式时,我一直都是运用Perl的正则表达式思想来实现的,因为两者在字符串的匹配规则是相通的。在这篇文章里,我主要介绍如何把Perl的正则表达式运用于PHP中。

Perl的正则表达式表达式书写非常简洁,而PHP通过函数(function)来实现的。先看个例子:

#Perl
$string = "Clinton是美国第42届总统";
if($string =~ m/^(Clinton|Bush|Reagan).*(\d{2})/i)
  {print "$1\t$2\n"};
#在Perl里我们用$1表示第一给匹配组'()'里的内容.$2表示第二个匹配组里的内容。
#显然打印结果是: Clinton   42
//PHP
$string = "Clinton 是美国第42届总统";
if(preg_match("/^(Clinton|Bush|Reagan).*(\d{2})/", $string, $matches))
   print $matches[1]\t$matches[2]\n";
//首先PHP要用函数preg_match($pattern,$string,$matches)来实现匹配判断
//其次,匹配结果被放在数组$matches里。
//最后,如果匹配成立,数组的第一个值是整个匹配字符串,第二个值才是第一匹配组里的内容。
//所以,$matches[1]匹配'Clinton',$matches[2]匹配'42'

如果你读懂了上面的例子,你大概已经知道如何使用PHP的函数,preg_match()。preg_match这个函数主要接受三个变量输入:$pattern(匹配式样)、$string (要进行匹配的字符串)、$matches(匹配结果数组)。第一个变量参数是难点,可以完全应用Perl的正则表达式。另外一个难点,是对匹配内容的选择,在perl里用$1,$2,等等,在这里用数组$matches,$matches[1]相当于$1。

下面这里例子介绍如何用preg_match()来验证邮箱。我曾介绍过Javascript验证邮箱,原理差不多。

<?php
    $okay = preg_match(
      '/^[A-z0-9_\-]+[@][A-z0-9_\-]+([.][A-z0-9_\-]+)+[.][A-z]{2,4}$/', 
      $email
    );
    if ($okay) {
      echo "E-mail is validated";
    } else {
      echo "E-mail is incorrect";
    }
?> 
//因为我只要知道输入的email是否匹配合法的表达式,也就是不保存匹配结果,
//所以在调用preg_match()的时候只需要两个变量输入,$pattern和$email
//这里pattern = /^[A-z0-9_\-]+[@][A-z0-9_\-]+([.][A-z0-9_\-]+)+[.][A-z]{2,4}$/
//我们可以把pattern分为6段来分析:
//1. ^[A-z0-9_\-]+:以任意一个或者多个字母或者数字或者下划线或者连字号开头
//2. [@]中间是@
//3. [A-z0-9_\-]+: @后面跟着一个或者多个字母或者数字或者下划线或者连字号
//4. ([.][A-z0-9_\-]+)+: 之后跟着一个或者多个(.字符或者字符串)
//5. [.]: 之后跟一个点
//6. [A-z]{2,4}$: 最后跟2-4个英文字母。(比如说:com, net, edu, cn, 等等)

第三个例子是对百度知道一个网页的问题回答:

$str = "任意字符串1<aaa 任意字符串2>任意字符串3<aaa 任意字符串4>任意字符串5</aaa>";
//我想取出:
//$arr1 = 任意字符串1
//$arr2 = 任意字符串2
//$arr3 = 剩下的东东(任意字符串3<aaa 任意字符串4>任意字符串5</aaa>
//答案:
preg_match ('/^(.*?)<aaa\s(.*?)>(.*)$/', $str, $out);
echo $out[1] . "\n";
echo $out[2] . "\n";
echo $out[3] . "\n";
//注意:这里用到非贪婪匹配
//'^(.*?)<aaa', 匹配组匹配其实字符到碰到第一个'<aaa'前的所有字符
//如果我们用'^(.*)<aaa', 那么将贪婪匹配到最后一个'<aaa'前的所有字符

补充(11/6/08) 需要匹配多行的字符串,使用“smi”后缀修饰。例如:

$a = '<{LIST(10011002,10,0,0)}>
<tr>
<td width="100"> </td>
<td> </td>
<tr>
<{/LIST}> ';
if (preg_match("/<{[^}]+}\>(.+)\<{[^}]+}>/smi", $a)){
   print "yes";
}
补充(11/10/08) "\b"表示单词的分界线。比如说,"\bweb\b" 匹配"web", 但是不匹配"website"。

标签:

2008年9月25日星期四

PHP - 调用或者读取文件的方法

我们写程序时常常要调用已有的代码或者读取已经存在的文件,这样一来简化代码,二来挺高代码的复用率。在PHP中我们用 include 或者require来调用现有的代码。includerequire功能非常相似。唯一的区别是它们对运行错误的处理方式不同。两者都提供警告(warning),但是当出现错误时,比如说所需文件的路径不对,require将终止程序,这是PHP对致命错误(fetal error)的处理方法, 相比之下,include就宽容的多,它会跳过这个错误,让程序继续执行。另外我们还常常用到,include_oncerequire_once, once这里代表只调用一次的意思。因为如果一个文件(代码)在程序的同一个运行周期里被多次调用,有可能会造成变量重复分配,函数重复定义等问题,所以当程序由很多部分组成,我们在调用代码时采用include_oncerequire_once,这样即使同样的代码被调用了两次,在整个程序的实际运行中将只被调用一次。

includerequire 是PHP里的控制结构(Control Structures), 因此严格的讲它们不算是PHP的方法(function), 但是我们也可以用它们来读取文件,这就类似于使用PHP里其他一些方法(function), 比如file_get_contents, fpassthru, 等等。使用include、require 和其它读取文件的方法有什么不同呢?这主要表现在执行速度和内存使用上。下面是引自 SITE WITH THE LAMP 的几种方法对1M文件读取的对比表,可以看出fpassthru执行速度最快、readfile占用内存最小。相比之下,include, require 就的效率就很低。所以当我们只是对静态文件进行读取的时候,尽量不要用includerequire

Function Sample Usage Time (s) Memory (b)
file_get_contents echo file_get_contents($filename); 0.00564 1067856
fpassthru fpassthru($fp); 0.00184 20032
fgets
$fp = fopen($filename,"rb");
while(!feof($fp))
{
    echo fgets($fp);
}
0.07190 30768
file echo join("",file($filename)); 0.06464 2185624
require_once require_once($filename); 0.08065 2067696
include include($filename); 0.08202 2067696
readfile readfile($filename); 0.00191 19208

标签:

2008年9月8日星期一

PHP随机产生数组

在百度知道看到一个网友的提问:

我想在1-33随机生成6个不重复的数字,以此为一组;可以短时间内生成很多组的方法或软件,最好能用文档保存结果,请问类似的程序有吗?

刚好有空,就用PHP写了一个:

<?php
$sets = isset($_GET['sets'])?$_GET['sets']:10;
$min  = isset($_GET['min'])?$_GET['min']:1;
$max  = isset($_GET['max'])?$_GET['max']:33;
if ($min >= $max){
  echo "最小值必须比最大值小!";
  exit;
}
if ($sets >500){
 echo "只能给你500组:-) 如果你要更多,请写信给我,AdamCai@Live.com!";
 exit;
}
$myset=array();
for ($i=0; $i<$sets; $i++){
	while(count($myset)<6){
		$a = rand($min, $max);
		if (array_search($a, $myset) === FALSE){	
			$myset[] = $a;
		}
	}
	foreach ($myset as $index => $value){
		echo $value;
		if ($index < 5) echo " ";
		else echo "<br />";
	}
	$myset = array();
}
?>

演示:http://www.61dh.com/code/match.php?sets=20&min=1&max=200

这里sets代表需要的组数,min代表数组的最小值,max代表数组的最大值。

更新(09/30/2008): 我在这小段程序的基础上,做了一个彩票电脑选号器, 有兴趣的朋友可以看看。

标签: