网站防盗链的解决方案(针对:图片、下载)

没有评论

2010 年 12 月 04 日 at 下午 5:51分类:PHP | WEB开发

盗链是指服务提供商自己不提供服务的内容,通过技术手段绕过其它有利益的最终用户界面(如广告),直接在自己的网站上向最终用户提供其它服务提供商的服务内容,骗取最终用户的浏览和点击率。受益者不提供资源或提供很少的资源,而真正的服务提供商却得不到任何的收益。

针对图片的防盗链方法: 在 Apache 的 .htaccess 文件中添加下列代码:

RewriteCond Host: (.+)
#排除以下网站
RewriteCond Referer: (?!http://(?:php95\.com|(.*)\.php95\.com|php100\.com|(.*)\.php100\.com|baidu\.com|(.*)\.baidu\.com|google\.com|(.*).\google.com)).*
#其他网站的图片、软件下载请求都转向到 block.png 中
RewriteRule (.*\.(?:jpg|jpeg|gif|png|bmp|rar|zip|exe)) /block.png [I,O,N]

针对下载的防盗链方法: download.php 文件相当于一个“守城兵”,负责保护数据、传递数据。它会判断 $_SERVER['HTTP_REFERER'] 与本站域名是否有匹配,如果否,则说明该下载请求来自他站,此时拒绝下载,显示“请不要盗链本站资源”。如果是本站的下载请求,则调用下载类—-读取文件并输出下载。($_SERVER['HTTP_REFERER'] 这个服务器变量的意思是:获取当前链接的上一个连接的来源地址,即链接到当前页面的前一页面的 URL 地址)

主要有两个文件:download.php 、Download.class.php

现在开始看代码:

假设我提供下载的地址是:http://www.php95.com/download.php?fn=文件名.rar

当此链接被点击后,download.php 开始工作——过滤非本站的下载请求,如请求通过,则返回数据给用户。

download.php

<?php
include_once 'Download.class.php';
$referer = $_SERVER['HTTP_REFERER'];
$host = 'php95.com';
if(!strpos($referer,$host)){
echo '请不要盗链本站资源!';
}else{
// 压缩文件自由放置,在这里加上路径就可以了。比如 $fn = '\a\b\c\\'.$_GET[fn];
$fn = $_GET[fn];
$dl=new Download();
$dl->_dl_($fn);
}
?>

Download.class.php

/**
* 下载类
* 示例: $dl = new Download(); $dl->_dl_('a.rar');
**/
class Download{
var $cache_size=1024;
function Download(){}
//文件的相对路径
function _dl_($path)
{
$full_path=getcwd().$path;
//echo $full_path;
if(!file_exists($full_path)){
echo "File Not Find.";
exit;
}else{
$handle=fopen($full_path,"rb");
Header("Content-type:".$this->getmimetype($full_path));
Header("Accept-Ranges:bytes");
Header("Accept-Length:".filesize($full_path));
$infos=pathinfo($full_path);
Header("Content-Disposition:attachment;filename=".$infos["basename"]);
while(!feof($handle)){
echo fread($handle,$this->cache_size);
}
fclose($handle);
exit;
}
}
function getmimetype($path){
$mimeArray=array();
$mimeArray["zip"]="application/zip";
$mimeArray["wav"]="audio/x-wav";
$mimeArray["xml"]="application/xml";
$mimeArray["txt"]="text/plain";
$mimeArray["tar"]="application/x-tar";
$mimeArray["swf"]="application/x-shockwave-flash";
$mimeArray["rm"]="application/vnd.rn-realmedia";
$mimeArray["pdf"]="application/pdf";
$mimeArray["mp3"]="audio/x-mpeg";
$mimeArray["mid"]="audio/x-midi";
$mimeArray["js"]="text/javascript";
$mimeArray["jad"]="text/vnd.sun.j2me.app-descriptor";
$mimeArray["gz"]="application/x-gzip";
$mimeArray["gtar"]="application/x-gtar";
$mimeArray["exe"]="application/octet-stream";
$mimeArray["doc"]="application/msword";
$mimeArray["rar"]="application/octet-stream";

$infos=pathinfo($path);
return $mimeArray[$infos["extension"]];
}
}//end class Download

?>

对外提供下载地址的格式是:http://本站域名/download.php?fn=文件名.rar

如何查看效果:转载本博任意一篇带图片的文章,或下载本博任意一文件

PHP执行zip与rar解压缩方法

没有评论

2010 年 12 月 04 日 at 下午 3:50分类:PHP | WEB开发

Zip:PclZip http://www.phpconcept.net/pclzip/index.en.php
Rar:PECL rar http://pecl.php.net/package/rar
以往过去要在php下执行解压缩程序,无非最常见的方法是写command 然后用exec()等执行函式去跑
这在Windows下或许可以,但换成Unix话会碍于帐号权限问题而无法顺利执行
那有没有那种本身就有提供函式可以直接使用而不需要去下command去跑的方法呢
答案有(话说找了好几天才找到可以用的方法……XD)
先讲Zip,由于php内建本身就有提供zip相关函式(但须先要有ziplib函式)但不是很好用
就光extract来讲,内建函式只负责单纯解压缩档案出来,而不是会按照资料夹依序解压缩出来
这样就失去extract的作用
而要讲的PclZip 这支,本身就有提供extension 了,故有没有Ziplib就没差
且免安装,只需要再用他时include 进来就可以了

我们先从网站:(PclZip 2.8.2)下载一个PclZip的包,

解压之后获取里面的’pclzip.lib.php文件拿来用
例如:

<?php include('pclzip.lib.php'); ?>

这样
此外在extract部分,则是会按照资料夹顺序依序解压缩出来,而并非单纯解压缩档案出来
相关用法像这样

<br />
<?php<br />
require_once('pclzip.lib.php'); $archive = new PclZip('archive.zip');<br />
if ($archive->extract() == 0) { /*解压缩路径跟原始档相同路径*/<br />
die(&quot;Error : &quot;.$archive->errorInfo(true)); }<br />
?><br />

这样子就能把一个zip的压缩包解压出来了
当然也可以指定解压缩路径,像这样

<br />
<?php<br />
include('pclzip.lib.php'); $archive = new PclZip('archive.zip');<br />
if ($archive->extract(PCLZIP_OPT_PATH, 'data') { /*data换成其他路径即可*/<br />
die("Error : ".$archive->errorInfo(true)); }<br />
?><br />

如果再写一支自动建立目录的script会更好,因为函式本身不会判断压缩档里第一层是档案还是资料夹(这我想其他相关函式也做不到吧!!!)
再来是Rar,这问题比较大,由于php本身没提供rar相关函式,所以需要求助第三方函式来用
所幸有这个PECL(The PHP Extension Community Library)
里面有个rar 的package 可以使用
不过须得手动安装才行

当然若是freebsd 话,用port 装会更快

<br />
cd /usr/ports/archivers/pecl-rar<br />
make<br />
make install

记得安装完后须restart apache
安装完后可以做测试

<br />
<?php $rar_file = rar_open('example.rar') or die("Failed to open Rar archive");<br />
/*example.rar换成其他档案即可*/<br />
$entries_list = rar_list($rar_file);<br />
print_r( $entries_list);<br />
><br />

比较要注意的,若是用port 安装话,版本会比较新(官网只有到0.3.1,port 安装话已经到0.3.4),所以用法上会有些出入
但extract用法上并无差异
相关用法像这样

<br />
<?php $rar_file = rar_open('example.rar') or die(&quot;Can't open Rar archive&quot;);<br />
/*example.rar换成其他档案即可*/<br />
$entries = rar_list($rar_file);<br />
foreach ($entries as $entry) { $entry->extract('/dir/extract/to/'); /*/dir/extract/to/换成其他路径即可*/ } rar_close($rar_file);<br />
?><br />

跟Zip部分一样,若搭配自动建立目录会更好

=================================================================

另一个自己常用的解压rar的方法

<br /><?php
	$dir=getcwd();
	if(@$_GET['sub']){
		$tname=$_FILES['upfiles']['tmp_name'];
		$nname=$_FILES['upfiles']['name'];

		move_uploaded_file($tname,$nname);
		$obj=new com("wscript.shell");

		$obj->run("winrar x $dir\\".$nname." ".$dir , 1,true);
		unlink($nname);
	}
?>
<form action="?sub=1" method="POST" enctype="multipart/form-data">

 选择上传文件	<input type="file" name="upfiles"/>
				<input type="submit" name='sub' value='提交并解压' />
</form>

一篇有意思的技术文章

没有评论

2010 年 12 月 04 日 at 下午 3:42分类:乱七八糟

身为一名中级PHPer菜鸟..无聊了就爱在各个PHP论坛瞎转.看到了好多PHP初学者都问到了很多相同的问题.而且我学PHP的时候也都遇到过.为了 让PHP初学者少走一些弯路.所以突然神经恍惚.决定写下此文章.仅供PHP初学者参考.如有错误.还望指出.不甚感激.
PHP其实是一种很简单易学的语言.如果要精通PHP多则三年.少则一年就足够了.但是为什么三年之后我们照样是菜鸟?
不知道从什么开始.学习PHP我们不得不学习数据库.学习架构.学习面向对象.学习前端.学习linux.学习协议甚至美工等直接导致了现在PHPer都是最累的一种程序员.
最累就不算什么了.往往PHP会被认为是下等的程序.拿着最低级的工资.却要掌握web方面几乎全部的知识.这实在是不公平.
可喜的是 我们逐渐在国内看到了转变.国内越来越多的公司从Java或者C#等转到了PHP.所以各位PHPer一定要有自己的梦想.你们都会很牛B的.请坚信.
扯远了.回到我要说的重点上.下面几个问题包含着我遇到的问题.也许我说的不一定对或者你不一定同意我的看法.十分愿意接受你的意见.总之我们只是想让PHPer都雄起:-)
1.编码的问题
这不光是PHP..只要是程序上.就会遇到.至少我和我身边的人都遇到了.而且在Web方面.往往编码问题更是非常难解决.非常难调试.有时候程序出了莫名其妙的问题,很难想到是编码出了问题等等.
这些问题总是困扰着我们.所以我建议PHP初学者.一定要编码统一.并且强烈建议统一为UTF-8.中文不建议使用GBK或者GB2312等.因为在AJAX中传输的时候是不认识这种编码的.
如果要统一编码,就要在编码可能出现的任何地方注意一下.
1) header头的编码
个人建议在你的PHP中都应该加上下面这句话(特殊header除外)

header("Content-type: text/html;charset=utf-8");

这样可以避免一些问题.比如我们可以看到有些网站submit后就是一个alert然后跳转.但是他们没有设置编码.如果他的编码跟浏览器的默认编码不一致.就会出现乱码了.
你是单纯的HTML.一定要记得把meta的编码标签放到title标签前面. 如果你不理解就记住把编码的信息放到head标签的第一行就可以了.最终的效果是这样的

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
...

2) 文件的编码
我们保存文件的时候,选择编码一定要统一.如果按照上面的.我们就应该选择UTF-8.
一般的编辑器都有此文件的编码信息.如果你发现不是UTF-8.请马上更改.
比如我们在Dreamweaver中打开了一个模板文件.发现右下角显示 GB2312(简体中文).此时不要做任何更改.马上CTRL+J 然后选择编码更改成UTF-8模式并保存.
文件的编码不一定是PHP文件的.也得保证你的JavaScript文件与css文件都为UTF-8编码.
另外还有一个重要的一点.就是BOM..这个东东PHP是不太欢迎的.所以我们在保存的时候要选择UTF-8无BOM格式.如果你用的编辑器是UE.那么请你着重注意这一点.
3) 数据库的编码
这个我就不想多说了.网上有太多的文章.如果你是MySQL记得每次连接后进行SET NAMES = ‘utf8′就可以了.并且那个utf8中间是没有横线的.
2.MVC的问题
程序员理论上来说应该是个完美主义者.他们不光想着把程序写的运行就可以了.而且还要优雅.
但是话说回来.把程序按照MVC做了以后我们的代码就优雅了吗? 答案当然是否定的.
学习架构是一个理解的过程.当你真正体会到这个架构的好处的时候.才会理解其真正的含义.切勿盲目.
有时候我们会做这样的打算.用这个月的时间去学习MVC.好吧.我想你应该这样去做.你学会给自己定目标了.
但是一个月后我们也许并不能真正的理解MVC..但是至少我们会根据他的模式他的基本.换句话说也许我们没有学会葵花宝典.但是我们应该会用辟邪剑法了.
3.面向对象的问题
关于PHP面向对象的问题.一直都纠缠不清.有的人不觉得PHP面向对象是个好的东西.有的人甚至不认为面向对象是个好的玩意,比如Linus.
我们不必去纠缠这个东西到底好不好.至少我们应该知道从面向对象诞生到现在.经过了这么多年的演变.运用到各种项目甚至语言中.我们有理由相信存在即有其道理.
实践是检验真理的唯一标准.这么多年面向对象越发展形势越好.看来我们学习就变得是种必须了.
与一些架构的知识类似.面向对象也不是那么好学的.面向对象是种理念.但是具体到某种语言的时候.其意义也会发生一些微妙的变化.
PHP的面向对象很灵活.再加上其特有的魔术方法.会造就出一种特别形势的面向对象.也许这跟你平时看Java的面向对象不太一样.
不一定把你需要的方法放到一个类中就是面向对象.当你真正体会到面向对象给你的编码带来快捷.带来方便.你就对面向对象有更深的一层理解了.
正所谓欲速则不达.切勿心急.你想几天之内理解相对论那纯粹是扯淡.
4.算法的问题
似乎PHP与算法总是相距甚远的.PHP大多数算法都是基于数组实现的.而我们又知道PHP的数组的特性就注定当数组变得非常大的时候.效率会直线下降.
其实PHP就是个做网站的.大家不要也不必给她带什么高帽子.在Web方面尤其是在PHP所服务的前端的实际情况下会涉及到算法的地方会非常少.
PHP的算法面试题无非就是对数组或者对字符串的操作..动动脑子加上手册.那基本就没问题了.
那你也许会问.我想学习PHP.那我还学习算法吗?
我觉得你需要学.并且更需要学数据结构.这门功课就像是一门内功.会在潜移默化中影响你的编码.
这时候再配合上我们前面的辟邪剑法.恭喜你 你已经学会葵花宝典了!
5.框架的问题
PHP的框架是各种语言中最多的.用不用框架.选什么框架这又成为一个争议的话题..
我的建议是非常推荐学习至少一款框架.这可以加深你对PHP的认识.我们学习框架不是说要学这个框架怎么用.怎么做项目.
我们要去看它的源码.看看他是怎么实现的.这时候你就可以选择自己开发一个小型的框架.不求做的多好.不求别人多少项目去运用.
我们只是为了学习而已.
6.JavaScript的问题
可以说我看过这么多语言.最神奇就莫过于JavaScript了.平时Java一直宣称自己一切皆对象.其实我更觉得JavaScript才是一切皆对象.
JavaScript好学吗? 好学!其实在平时Web运用时候.一般的JavaScript操作就是DOM操作.
JavaScript难学吗? 难学!如果我们被其他语言的面向对象的思维束缚了.就很难理解JavaScript的面向对象.并且JavaScript里面概念也非常多.
prototype arguments call apply callee caller 闭包等概念就让我们不知所措.再加上诡异的JavaScript的代码风格与其难调试的特征直接导致我们这些菜鸟避而远之.
曾经我转发过一段实现document.ready的代码.其诡异程度不得不佩服.
个人觉得看一些JavaScript框架的源码就是最好的学习方法.曾经在写我的PHP框架的时候.由于使用了统一入口+自己定义的URL规则.所以再使用普通属性为get的form的时候就会获取不到参数.
这时候我就想到了jQuery.form中的formSerialize方法.摘选出来并做了一些修改.就可以拿来使用了.
7.CSS问题
我之所与把CSS跟JavaScript分开来说.我觉得CSS的学习更有特点.
其实要我说CSS是最简单的东西了.我们只是知道了一些基本的CSS属性.写页面绝对不成问题.
CSS的主要特征在于必须动手.你看N本CSS的书不如实际动手去写一个页面.
CSS另外一个特点就是得多试.也许这个属性不能达到你的效果.那你就换一个.这也是最笨最有效的办法:-)
当然 这只是初级CSS的要求. 会了这些之后 我们还得兼容浏览器. 其实这个也不难 无非就是几个浏览器同时开着一个属性一个属性的试.
也可以熟记一些hack 比如IE6认识_ IE都认识*等.如果你觉得这样写CSS不够标准.那么把hack放到不同的css文件中.比如ie6的就叫ie6.css
然后再页面上用下面的代码就可以了.

<!--[if lt IE 6]>
        <link href="css/ie6.css" rel="stylesheet" type="text/css" />
<![endif]-->

更高级的当然是用户的体验与设计了.这个我也不懂就不说了@_@ 希望达人补充.
8.linux的问题
个人强烈建议每人的电脑上都安装一个linux系统.如果你觉得太折腾 可以选择wubi方式安装Ubuntu.对于懒人这个是最简单有效的方法了.
你无聊的时候可以下载一份PHP源码自己编译下试试.切忌apt-get.因为往往服务器版本不是rhel就是centos.那里是没有这个命令的.
而且apt-get到的往往不是最新的版本.并且又不能自己定制.
linux总会出现这样活着那样莫名其妙的问题.比如环境稍微不一样就编译不通过.服务起不来.
这时候不要着急.看错误提示 看日志.Google下自己尝试解决.
编译完不要觉得就完了 试试不同的配置.而且修改一下源码再编译试试.
比如我们下载了一份nginx的源码.然后修改下header头等一些地方. 就可以编译一份属于自己的HTTP Web Server了.
当然 你如果叫BWS或者GWS也行:-)
还有个问题 linux需要背命令吗? 这个是无可厚非的. 熟记一些常用的命令可以提高速度.这点我需要反思.经常一些常用的命令还需要看下参数..
另外一个就是Shell的问题. Shell其实不难.但是语法在我看来比较丑陋@_@. 看看wiki就可以学习大半了..主要还得是联系. 我们可以看到一个现象.一些公司在招聘高级PHPer的时候这条也列为必须条件了.
其实网上linux的资料非常多. 大多数你遇到的问题别人都会遇到.所以善于用Google. 并且熟读英语.
9.语言的问题
这个的问题就太多口水战了.最终谁也没有战胜谁.我们不必去参与这些无聊的事情.这些都是年轻粪青要做的.
你再牛B你有Linus牛B吗? 他喷C++的喷了这么多年.C++怎么样了?
所以我们不必纠缠这些.做好自己做的就OK了.
既然你选择了PHP.就不要犹豫.
也许我们在做项目的时候PHP显得不够用了.那我们可以用Java或者C来做个中间件.这又何尝不可.
一种语言发挥她的最大最用才是真理.
10.态度的问题
态度一定要谦虚谨慎. 这是作为一个程序员应该有的美德.切勿自我膨胀.
你学的越多 你就会发现自己不足就越多.
等你牛B了你可以说一些狂妄的话.但是现在我们都不牛B.至少如果你看到我写的这篇文章.你应该还不太牛B. 我说了这篇文章是面向初学者的:-)

PHP程序61条面向对象分析设计的经验原则

没有评论

2010 年 12 月 04 日 at 下午 3:40分类:PHP | WEB开发

你不必严格遵守这些原则,违背它们也不会被处以宗教刑罚。但你应当把这些原则看成警铃,若违背了其中的一条,那么警铃就会响起 。
(1)所有数据都应该隐藏在所在的类的内部。
  (2)类的使用者必须依赖类的共有接口,但类不能依赖它的使用者。
  (3)尽量减少类的协议中的消息。
  (4)实现所有类都理解的最基本公有接口[例如,拷贝操作(深拷贝和浅拷贝)、相等性判断、正确输出内容、从ASCII描述解析等等]。
  (5)不要把实现细节(例如放置共用代码的私有函数)放到类的公有接口中。
  如果类的两个方法有一段公共代码,那么就可以创建一个防止这些公共代码的私有函数。
  (6)不要以用户无法使用或不感兴趣的东西扰乱类的公有接口。
  (7)类之间应该零耦合,或者只有导出耦合关系。也即,一个类要么同另一个类毫无关系,要么只使用另一个类的公有接口中的操作。
  (8)类应该只表示一个关键抽象。
  包中的所有类对于同一类性质的变化应该是共同封闭的。一个变化若对一个包影响,则将对包中的所有类产生影响,而对其他的包不造成任何影响 .
  (9)把相关的数据和行为集中放置。
  设计者应当留意那些通过get之类操作从别的对象中获取数据的对象。这种类型的行为暗示着这条经验原则被违反了。
  (10)把不相关的信息放在另一个类中(也即:互不沟通的行为)。
  朝着稳定的方向进行依赖.
  (11)确保你为之建模的抽象概念是类,而不只是对象扮演的角色。
  (12)在水平方向上尽可能统一地分布系统功能,也即:按照设计,顶层类应当统一地共享工作。
  (13)在你的系统中不要创建全能类/对象。对名字包含Driver、Manager、System、Susystem的类要特别多加小心。
  规划一个接口而不是实现一个接口。
  (14)对公共接口中定义了大量访问方法的类多加小心。大量访问方法意味着相关数据和行为没有集中存放。
  (15)对包含太多互不沟通的行为的类多加小心。
  这个问题的另一表现是在你的应用程序中的类的公有接口中创建了很多的get和set函数。
  (16)在由同用户界面交互的面向对象模型构成的应用程序中,模型不应该依赖于界面,界面则应当依赖于模型。
  (17)尽可能地按照现实世界建模(我们常常为了遵守系统功能分布原则、避免全能类原则以及集中放置相关数据和行为的原则而违背这条原则) 。
  (18)从你的设计中去除不需要的类。
  一般来说,我们会把这个类降级成一个属性。
  (19)去除系统外的类。
  系统外的类的特点是,抽象地看它们只往系统领域发送消息但并不接受系统领域内其他类发出的消息。
  (20)不要把操作变成类。质疑任何名字是动词或者派生自动词的类,特别是只有一个有意义行为的类。考虑一下那个有意义的行为是否应当迁移到已经存在或者尚未发现的某个类中。
  (21)我们在创建应用程序的分析模型时常常引入代理类。在设计阶段,我们常会发现很多代理没有用的,应当去除。
  (22)尽量减少类的协作者的数量。
  一个类用到的其他类的数目应当尽量少。
  (23)尽量减少类和协作者之间传递的消息的数量。
  (24)尽量减少类和协作者之间的协作量,也即:减少类和协作者之间传递的不同消息的数量。
  (25)尽量减少类的扇出,也即:减少类定义的消息数和发送的消息数的乘积。
  (26)如果类包含另一个类的对象,那么包含类应当给被包含的对象发送消息。也即:包含关系总是意味着使用关系。
  (27)类中定义的大多数方法都应当在大多数时间里使用大多数数据成员。
  (28)类包含的对象数目不应当超过开发者短期记忆的容量。这个数目常常是6。
  当类包含多于6个数据成员时,可以把逻辑相关的数据成员划分为一组,然后用一个新的包含类去包含这一组成员。
  (29)让系统功能在窄而深的继承体系中垂直分布。
  (30)在实现语义约束时,最好根据类定义来实现。这常常会导致类泛滥成灾,在这种情况下,约束应当在类的行为中实现,通常是在  构造函数中实现,但不是必须如此。
 (31)在类的构造函数中实现语义约束时,把约束测试放在构造函数领域所允许的尽量深的包含层次中。
  (32)约束所依赖的语义信息如果经常改变,那么最好放在一个集中式的第3方对象中。
  (33)约束所依赖的语义信息如果很少改变,那么最好分布在约束所涉及的各个类中。
  (34)类必须知道它包含什么,但是不能知道谁包含它。
  (35)共享字面范围(也就是被同一个类所包含)的对象相互之间不应当有使用关系。
  (36)继承只应被用来为特化层次结构建模。
  (37)派生类必须知道基类,基类不应该知道关于它们的派生类的任何信息。
  (38)基类中的所有数据都应当是私有的,不要使用保护数据。
  类的设计者永远都不应该把类的使用者不需要的东西放在公有接口中。
  (39)在理论上,继承层次体系应当深一点,越深越好。
  (40)在实践中,继承层次体系的深度不应当超出一个普通人的短期记忆能力。一个广为接受的深度值是6。
  (41)所有的抽象类都应当是基类。
  (42)所有的基类都应当是抽象类。
  (43)把数据、行为和/或接口的共性尽可能地放到继承层次体系的高端。
  (44)如果两个或更多个类共享公共数据(但没有公共行为),那么应当把公共数据放在一个类中,每个共享这个数据的类都包含这个类。
  (45)如果两个或更多个类有共同的数据和行为(就是方法),那么这些类的每一个都应当从一个表示了这些数据和方法的公共基类继承。
  (46)如果两个或更多个类共享公共接口(指的是消息,而不是方法),那么只有他们需要被多态地使用时,他们才应当从一个公共基类  继承。
  (47)对对象类型的显示的分情况分析一般是错误的。在大多数这样的情况下,设计者应当使用多态。
  (48)对属性值的显示的分情况分析常常是错误的。类应当解耦合成一个继承层次结构,每个属性值都被变换成一个派生类。
  (49)不要通过继承关系来为类的动态语义建模。试图用静态语义关系来为动态语义建模会导致在运行时切换类型。
  (50)不要把类的对象变成派生类。对任何只有一个实例的派生类都要多加小心。
  (51)如果你觉得需要在运行时刻创建新的类,那么退后一步以认清你要创建的是对象。现在,把这些对象概括成一个类。
  (52)在派生类中用空方法(也就是什么也不做的方法)来覆写基类中的方法应当是非法的。
  (53)不要把可选包含同对继承的需要相混淆。把可选包含建模成继承会带来泛滥成灾的类。
  (54)在创建继承层次时,试着创建可复用的框架,而不是可复用的组件。
  (55)如果你在设计中使用了多重继承,先假设你犯了错误。如果没犯错误,你需要设法证明。
  (56)只要在面向对象设计中用到了继承,问自己两个问题:(1)派生类是否是它继承的那个东西的一个特殊类型?(2)基类是不是派生类的一部分?
  (57)如果你在一个面向对象设计中发现了多重继承关系,确保没有哪个基类实际上是另一个基类的派生类。
  (58)在面向对象设计中如果你需要在包含关系和关联关系间作出选择,请选择包含关系。
  (59)不要把全局数据或全局函数用于类的对象的薄记工作。应当使用类变量或类方法。
  (60)面向对象设计者不应当让物理设计准则来破坏他们的逻辑设计。但是,在对逻辑设计作出决策的过程中我们经常用到物理设计准则。
  (61)不要绕开公共接口去修改对象的状态。

FCKEditor+jQuery+PHP实现分页

没有评论

2010 年 12 月 04 日 at 下午 3:38分类:CSS | JavaScript | jQuery | PHP

一、插入新闻
FCKEditor是一款很流行的即插即用WEB编辑器,它支持ASP、PHP、Java等语言。这里要介绍的是利用编辑器的“插入分页符”功能,实现页面的无刷新分页。
编辑新闻时,在需要插入分页的段落处插入分页符,保存即可。
二、读取新闻
在读取新闻字段时,通过PHP分页函数对新闻字段数据进行处理,代码如下:

function pageBreak($content)
{
    $content  = $content;
    $pattern  = "/<div style=\"page-break-after: always\"><span style=\"display: none\">&nbsp;<\/span><\/div>/";
    $strSplit = preg_split($pattern, $content, -1, PREG_SPLIT_NO_EMPTY); 
    $count    = count($strSplit);
    $outStr   = ""; 
    $i        = 1;
    
    if ($count > 1 ) {
        $outStr   = "<div id='page_break'>";
        foreach($strSplit as $value) {
            if ($i <= 1) {
                $outStr .= "<div id='page_$i'>$value</div>";
            } else {
                $outStr .= "<div id='page_$i' class='collapse'>$value</div>";
            }
            $i++;
        }
        
        $outStr .= "<div class='num'>";
        for ($i = 1; $i <= $count; $i++) {
            $outStr .= "<li>$i</li>";
        }
        $outStr .= "</div></div>";
        return $outStr;
    } else {
        return $content;
    }
}

CSS样式控制分页的样式,代码如下

#page_break {
    
}
#page_break .collapse {
    display: none;
}
#page_break .num {
    padding: 10px 0;
    text-align: center;
}
#page_break .num li{
    display: inline;
    margin: 0 2px;
    padding: 3px 5px;
    border: 1px solid #FF7300;
    background-color: #fff;
    
    color: #FF7300;
    text-align: center;
    cursor: pointer;
    font-family: Arial;
    font-size: 12px;
    overflow: hidden;
}
#page_break .num li.on{
    background-color: #FF7300;
    
    color: #fff;
    font-weight: bold;
}

使用jQuery代码实现分页,代码如下:

$(document).ready(function(){
    $('#page_break .num li:first').addClass('on');
        
    $('#page_break .num li').click(function(){
        //隐藏所有页内容
        $("#page_break div[id^='page_']").hide();
                
        //显示当前页内容。
        if ($(this).hasClass('on')) {
            $('#page_break #page_' + $(this).text()).show();            
        } else {
            $('#page_break .num li').removeClass('on');
            $(this).addClass('on');
            $('#page_break #page_' + $(this).text()).fadeIn('normal');
        }
    });
});

根据以上步骤,即可实现使用FCKEditor+jQuery+PHP实现无刷新页面分页。

CI入门 : 一些建议和最佳实践

没有评论

2010 年 12 月 04 日 at 下午 3:20分类:PHP | WEB开发

原文地址 http://codeigniter.com/forums/viewthread/125687/
1、 使用哪一个版本? 1.7.2 or 2.0
CodeIgniter 2.0 目前虽然还没有最终发布,但它已经非常稳定,并且 EllisLab 公司已经声明不会有很大的变动,只要是修正一些 BUG,许多人认为 2.0 比 1.7.2 更加稳定,Phil Sturgeon 在他的博客中写过一篇非常棒的关于怎样从旧版本升级到 2.0 版本的文章,你可以从 BitBucket.org 下载最新的 CI 2.0 的代码,以及最近的升级。
Moderator Jamie Rumbelow 曾写了一篇关于CI 2.0 的帖子。

2 、阅读手册 & 观看视频
了解 CI 的最好的方法是阅读它的手册,里面有很多值得参考的文章,特别是那些高亮的部分,你一定要阅读它们并引起注意。
也可以通过观看 nuttuts+ 上面的视频或 CI网站 上的视频来学习 CI ,后者采用了一个旧的版本,如果你注意了,它仍是一个很好的了解 CI 使用的地方。

3 、MVC 编程
如果你还不知道 MVC ,应该尽快的学习,你会很快的体会到在 Model 中数据访问,在 Controller 中进行业务逻辑,在 Views 中编写 HTML 代码的价值。如果你之前没有使用过这种模式写过程序,你也许会皱起额头,不过你应该给自己尝试这样做的机会。
一条实践准则是把更少的东西放进 Controller ,记住 DRY 准则:不要重复造轮子。当在超过一个地方编写相同的代码时,应该根据它的类型来尝试编写一个 library , helper,或 model 。
一旦领悟了 MVC 的精髓,这将会成为一种习惯,你会从 MVC 简洁的代码中受益良多。

4 、错误报告 和 调试
常常犯的一个错误是忘记关闭 PHP 错误和数据库错误报告,这样做是有风险的。在任何一个公开的站点,error_reporting 应该设置为0 ,最多只能设置为 E_ERROR,数据库设置 db_debug 应该设置为 false,基于其他安全考虑,设置不显示出错信息 ini_set(‘display_errors’, ‘Off’);
在你编码和调试时,应该把 error_reporting 设置为 E_ALL ,并且在把应用程序发布前解决每一个注意和警告。
一种简易的方法是在 application/config/database.php 文件设置 db_debug 的值为一个常量 MP_DB_DEBUG,当网站在运行中,如下设置:

ini_set('display_errors', 'Off');
error_reporting(0);
define('MP_DB_DEBUG', false);

在编码和调试中设置为:

ini_set('display_errors', 'On');
error_reporting(E_ALL);
define('MP_DB_DEBUG', true);

5、 Application 和 System 路径
最好是把 system 和 application 文件夹放在 webroot 以外的地方,如果 index.php 放在 FTP 服务器的 /public_html/ 路径下,应该尝试把 System 放在根目录下 /system ,这样的话,只能通过 index.php 访问你的PHP文件。
不要忘记在index.php文件中修改 $system_folder 和 $application_folder 的值,$system_folder 的值应该是相对于 index.php 文件,而 $application_folder 的值是相对于 system 目录。

6 、安全
关于SQL注入,XSS,以及 CSRF ,你应该先了解它们,再决定是否采用方法来防止它们。可以参考CI手册上的安全指南 以及 输入和安全类。也许最重要的原则是在把数据提交到数据库或文件系统之前检查所有用户的输入。
(1) SQL注入
使用 CI 自带的 Active Record 可以解决这个问题。
(2) XSS (跨站脚本)
通过设置 $config['global_xss_filtering'] = TRUE; 开启自动过滤POST和COOKIE中的跨站脚本攻击,但需要消耗一些资源。
也可以在每次处理POST和COOKIE的时候单独使用,把第二个参数设为TRUE,如 $this->input->post(‘some_data’, TRUE);
表单验证类也提供了 XSS 过滤选项,如 $this->form_validation->set_rules(‘username’, ‘Username’, ‘trim|required|xss_clean’);
(3) CSRF (跨站请求伪造)
CI 2.0 将内置 CSRF 检查,在 Google 上搜索 “CSRF tokens” 学习更多关于在保护表单提交和 URL 链接的知识,在 Ajax 应用方面可以搜索 “double cookie submission” 或 “双提交 cookie”。
(4) SPAM (垃圾留言和恶意注册)
通过保护你的邮件表单,评论表单,以及其他各种免费用户提交的数据来防止垃圾信息,一个简单的方法是只允许一个IP/User客户端在一分钟之内只能提交一次,
最好的方式是使用 Captcha ,比如 reCAPTCHA 来保护邮件表单和评论表单,可以在论坛上搜索怎样在CI中集成 reCAPTCHA 。CI2中内置了一个CAPTCHA的辅助函数。

7 、实践
编写简洁的代码,并且理解你的代码,不要只是复制粘贴别人的代码,并且不断提高编码能力。手册上的开发规范是一个能学习怎样更好编写代码的地方。
(1) DRY
不要总是重复造轮子,把能重用的代码放在它应该在的地方,比如libraries, helpers 或者是 models,而不是controllers,一个经验准则:当你复制代码的时候,也许你已经第二次把它放在了错误的地方。
(2) Caching (缓存)
缓存是一个提高性能的很好的方式,尤其是减少数据库的访问。可以参考网页缓存和数据库缓存,或者在论坛上搜索其他的可选方案,比如 MP_Cache 是作者自己的作品。
(3) HTTP headers (HTTP头部)
在客户端你能够通过单独发送HTTP头部使浏览器缓存页面来提高性能,当你使用 AJAX 的时候你也需要了解它来禁止浏览器缓存。
一个禁止缓存的例子:

$this->output->set_header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
$this->output->set_header("Cache-Control: no-store, no-cache, must-revalidate");
$this->output->set_header("Cache-Control: post-check=0, pre-check=0", false);
$this->output->set_header("Pragma: no-cache");

一个长时间保持缓存的例子(比如 css, javascript)

$this->output->set_header('Cache-Control: private, pre-check=0, post-check=0, max-age=2592000');
$this->output->set_header('Expires: ' . gmstrftime("%a, %d %b %Y %H:%M:%S GMT", time() + 2592000));
$this->output->set_header('Last-Modified: ' . gmstrftime("%a, %d %b %Y %H:%M:%S GMT", time() - 20));

8 、访问数据库 和 ORM
CodeIgniter 有一个自带的库 Active Record 能够帮助你在不使用 SQL 语句的情况下写查询语句。这在你不太精通 SQL 语句或不知道怎样防止SQL注入的情况下是一个很好的方法。
当你需要更强大的工具时,你可以考虑使用 Object Relational Mapper ,就是鼎鼎大名的 ORM 了,遗憾的是,CodeIgniter 没有自带 ORM 库,不过也有一些其他很好的选择。
最流行的或许是 DataMapper OverZealous Edition (DMZ),还可以使用 Doctrine (这里有一个教程),另一个选择 RapidDataMapper 是作者自己的作品。

9 、使用用户验证 和 授权
CodeIgniter没有自带用户验证的库引起很大争论,因为很多用户认为应该包含,也有很多人持反对意见。我的意见是在论坛或 wiki 上搜索一个仍在维护的库,它应该有好的安全性并且能方便的应用到你的程序里。你也可以研究这些例子并且写一个自己的验证库。
这里推荐的一个验证库是 Ion Auth ,它的程序写得很好,也许会比你第一次尝试写的更出色,如果你打算自己写验证库的话,你可以阅读它的代码并获得一些灵感。

10 、其他
遇到问题,可以在CodeIgniter的 论坛 和 wiki 上搜索,如果没有解决的话,可以在论坛上提问。

JQuery上传插件Uploadify使用详解

没有评论

2010 年 12 月 04 日 at 下午 12:53分类:jQuery

原文地址:
Uploadify是JQuery的一个上传插件,实现的效果非常不错,带进度显示。不过官方提供的实例时php版本的,本文将详细介绍Uploadify在Aspnet中的使用,您也可以点击下面的链接进行演示或下载。

  • 官方下载
  • 官方文档
  • 官方演示
  • 一个上传的功能,依靠函数uploadify实现,uploadify函数的参数为json格式,可以对json对象的key值的修改来进行自定义的设置,如multi设置为true或false来控制是否可以进行多文件上传,下面就来介绍下这些key值的意思:
    uploader : uploadify.swf 文件的相对路径,该swf文件是一个带有文字BROWSE的按钮,点击后淡出打开文件对话框,默认值:uploadify.swf。
    script : 后台处理程序的相对路径 。默认值:uploadify.php
    checkScript :用来判断上传选择的文件在服务器是否存在的后台处理程序的相对路径
    fileDataName :设置一个名字,在服务器处理程序中根据该名字来取上传文件的数据。默认为Filedata
    method : 提交方式Post 或Get 默认为Post
    scriptAccess :flash脚本文件的访问模式,如果在本地测试设置为always,默认值:sameDomain
    folder : 上传文件存放的目录 。
    queueID : 文件队列的ID,该ID与存放文件队列的div的ID一致。
    queueSizeLimit : 当允许多文件生成时,设置选择文件的个数,默认值:999 。
    multi : 设置为true时可以上传多个文件。
    auto : 设置为true当选择文件后就直接上传了,为false需要点击上传按钮才上传 。
    fileDesc : 这个属性值必须设置fileExt属性后才有效,用来设置选择文件对话框中的提示文本,如设置fileDesc为“请选择rar doc pdf文件”,打开文件选择框效果如下图:

    fileExt : 设置可以选择的文件的类型,格式如:’*.doc;*.pdf;*.rar’ 。
    sizeLimit : 上传文件的大小限制 。
    simUploadLimit : 允许同时上传的个数 默认值:1 。
    buttonText : 浏览按钮的文本,默认值:BROWSE 。
    buttonImg : 浏览按钮的图片的路径 。
    hideButton : 设置为true则隐藏浏览按钮的图片 。
    rollover : 值为true和false,设置为true时当鼠标移到浏览按钮上时有反转效果。
    width : 设置浏览按钮的宽度 ,默认值:110。
    height : 设置浏览按钮的高度 ,默认值:30。
    wmode : 设置该项为transparent 可以使浏览按钮的flash背景文件透明,并且flash文件会被置为页面的最高层。 默认值:opaque 。
    cancelImg :选择文件到文件队列中后的每一个文件上的关闭按钮图标,如下图:


    上面介绍的key值的value都为字符串或是布尔类型,比较简单,接下来要介绍的key值的value为一个函数,可以在选择文件、出错或其他一些操作的时候返回一些信息给用户。
    onInit : 做一些初始化的工作。
    onSelect :选择文件时触发,该函数有三个参数
    event:事件对象。
    queueID:文件的唯一标识,由6为随机字符组成。
    fileObj:选择的文件对象,有name、size、creationDate、modificationDate、type 5个属性。

    onSelectOnce :在单文件或多文件上传时,选择文件时触发。该函数有两个参数event,data,data对象有以下几个属性:
    fileCount:选择文件的总数。
    filesSelected:同时选择文件的个数,如果一次选择了3个文件该属性值为3。
    filesReplaced:如果文件队列中已经存在A和B两个文件,再次选择文件时又选择了A和B,该属性值为2。
    allBytesTotal:所有选择的文件的总大小。

    onCancel : 当点击文件队列中文件的关闭按钮或点击取消上传时触发。该函数有event、queueId、fileObj、data四个参数,前三个参数同onSelect 中的三个参数,data对象有两个属性fileCount和allBytesTotal。
    fileCount:取消一个文件后,文件队列中剩余文件的个数。
    allBytesTotal:取消一个文件后,文件队列中剩余文件的大小。

    onClearQueue :当调用函数fileUploadClearQueue时触发。有event和data两个参数,同onCancel 中的两个对应参数。
    onQueueFull :当设置了queueSizeLimit并且选择的文件个数超出了queueSizeLimit的值时触发。该函数有两个参数event和queueSizeLimit。
    onError :当上传过程中发生错误时触发。该函数有event、queueId、fileObj、errorObj四个参数,其中前三个参数同上,errorObj对象有type和info两个属性。
    type:错误的类型,有三种‘HTTP’, ‘IO’, or ‘Security’
    info:错误的描述

    onOpen :点击上传时触发,如果auto设置为true则是选择文件时触发,如果有多个文件上传则遍历整个文件队列。该函数有event、queueId、fileObj三个参数,参数的解释同上。
    onProgress :点击上传时触发,如果auto设置为true则是选择文件时触发,如果有多个文件上传则遍历整个文件队列,在onOpen之后触发。该函数有event、queueId、fileObj、data四个参数,前三个参数的解释同上。data对象有四个属性percentage、bytesLoaded、allBytesLoaded、speed:
    percentage:当前完成的百分比
    bytesLoaded:当前上传的大小
    allBytesLoaded:文件队列中已经上传完的大小
    speed:上传速率 kb/s

    onComplete:文件上传完成后触发。该函数有四个参数event、queueId、fileObj、response、data五个参数,前三个参数同上。response为后台处理程序返回的值,在上面的例子中为1或0,data有两个属性fileCount和speed
    fileCount:剩余没有上传完成的文件的个数。
    speed:文件上传的平均速率 kb/s
    注:fileObj对象和上面讲到的有些不太一样,onComplete 的fileObj对象有个filePath属性可以取出上传文件的路径。

    onAllComplete:文件队列中所有的文件上传完成后触发。该函数有event和data两个参数,data有四个属性,分别为:
    filesUploaded :上传的所有文件个数。
    errors :出现错误的个数。
    allBytesLoaded :所有上传文件的总大小。
    speed :平均上传速率 kb/s

    相关函数介绍
    在上面的例子中已经用了uploadifyUpload和uploadifyClearQueue两个函数,除此之外还有几个函数:
    uploadifySettings:可以动态修改上面介绍的那些key值,如下面代码

    $('#uploadify').uploadifySettings('folder','JS');

    如果上传按钮的事件写成下面这样,文件将会上传到uploadifySettings定义的目录中

    <a href="javascript:$('#uploadify').uploadifySettings('folder','JS');
    $('#uploadify').uploadifyUpload()">上传</a>

    uploadifyCancel:该函数接受一个queueID作为参数,可以取消文件队列中指定queueID的文件。

    $('#uploadify').uploadifyCancel(id);