Http://www.firephp.org/
FirePHP的php调试信息都是通过在http头里面添加X-FirePHP-Data信息串来标识,不会直接输出到页面上,这样也就避免对php正常输出产生影响。可以输出的调试信息类型如下:
* 正常的调试字符串,类型有LOG,INFO,WARN,ERROR几种
* 数组array
* object
* 异常Exception
* SQL返回数据
* http header
通过使用Firephp你可以在Firebug的Console栏中看到要调试的数据,而不影响php程序的正常执行,所以说,这东西对于Ajax开发是很有帮助的!
如何使用:http://www.blankyao.cn/blog/php-firephp-ajax.html
require('FirePHPCore/fb.php');
//echo 'FirePHP测试';
//echo "hello the world!";
fb('Hello World'); /* Defaults to FirePHP::LOG */
fb('Log message' ,FirePHP::LOG);
fb('Info message' ,FirePHP::INFO);
fb('Warn message' ,FirePHP::WARN);
fb('Error message',FirePHP::ERROR);
fb('Message with label','Label',FirePHP::LOG);
fb(array('key1'=>'val1',
'key2'=>array(array('v1','v2'),'v3')),
'TestArray',FirePHP::LOG);
function test($Arg1) {
throw new Exception('Test Exception');
}
try {
test(array('Hello'=>'World'));
} catch(Exception $e) {
/* Log exception including stack trace & variables */
fb($e);
}
fb(array('2 SQL queries took 0.06 seconds',array(
array('SQL Statement','Time','Result'),
array('SELECT * FROM Foo','0.02',array('row1','row2')),
array('SELECT * FROM Bar','0.04',array('row1','row2'))
)),FirePHP::TABLE);
?>
firebug firecookie ietab httpfox
下面一起来看下Firephp的使用方法。
第一步:安装
1.如果你的FireFox没有Firebug这个插件的话,首先要安装Firebug这个插件,可以到其官方地址去下载:http://www.getfirebug.com/
2.安装Firephp,官方地址:http://www.firephp.org/
3.下载Firephp的php文件。并放在合适的目录。
第二步:
包含fb.php,根据你放置的Firephp文件的地址来包含fb.php,比如:
require(’FirePHPCore/fb.php’)
第三步:
打开输出缓冲(因为Firephp主要用到的是header函数),有如下三种方法:
在程序的前面加上ob_start()
修改php.ini 将output_buffering设为1或者on
修改apache的设置,在配置文件中加上php_flag output_buffering on
第四步:开始调试:
可以调试输出以下数据类型:
字符串,可以分为LOG,INFO,WARN,ERROR四种
Object或者Array
通过sql查询返回的数据
抛出的异常信息
服务器返回的信息(不输出在console中,而是NET中
如果你感觉还不错的话,可以安装后运行以下程序看下结果:
$var = array('a'=>'pizza', 'b'=>'cookies', 'c'=>'celery');
fb($var);
fb($var, "An array");
fb($var, FirePHP::WARN);
fb($var, FirePHP::INFO);
fb($var, 'An array with an Error type', FirePHP::ERROR);
你也可以使用FirePHP来跟踪你程序的执行情况:通过使用FirePHP::TRACE常量,你可以在 fb被调用的地方查看行数、类名和方法名
function hello() {
fb('Hello World!', FirePHP::TRACE);
}
function greet() {
hello();
}
greet();
产生的输出如下:
这个跟踪功能可以完美的调试更复杂的代码,让你精确的知道你的方法是在哪里被调用的。
当然,别忘了你需要在你代码发布之前移除你的调试语句。
参考:http://blog.csdn.net/leijuly/archive/2009/05/31/4227613.aspx
自己测试了下,是Ok的,以后就可以用它来调试PHP变量的哇,测试代码如下:
firephp的Consol输出结果如下:
http://test3.qq.com/firephp_test3.php
2 SQL queries took 0.06 seconds
SQL Statement Time Result
SELECT * FROM Foo 0.02 array('0'=>' row1 ', '1'=>' row2 ')
SELECT * FROM Bar 0.04 array('0'=>' row1 ', '1'=>' row2 ')
自己试了下也是Ok的,如下:
name
jack lixu
echo xxd
当firefox升级到firefox6后,这个firephp和firebug匹配上有些问题,只能开启一次,再刷新后控制台就给屏蔽了,点再开启也不行,最后再次升级了firebug插件才Ok,这点要注意。
以上的输出截图,注意table和log,以及info是很适用的输出模型:
特别注意,如出现:
其中某个包含文件中如,password.php,最后两行,vi能看到一行空输出,91行:
90 ?>
91
这儿有一行空输出,一般是看不见的。
这儿有空格输出,于是出现了报错:
Fatal error: Uncaught exception 'Exception' with message 'Headers already sent in /data/home/jackxiang/public_html/pms_proj/trunk/php/lib/password.php on line 92. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.' in /data/home/admin/libs/FirePHPCore/FirePHP.class.php on line 1178
这儿得用到前面的:
第三步:
打开输出缓冲(因为Firephp主要用到的是header函数),有如下三种方法:
在程序的前面加上ob_start()
修改php.ini 将output_buffering设为1或者on
修改apache的设置,在配置文件中加上php_flag output_buffering on
其实就是在password.php中输出前加入,ob_start(); #开启缓冲,然后在打firephp变量前加入:
ob_clean(); #关闭缓冲
分别如下:password.php,
89 ob_start();
90 ?>
91
在打印变量处如,BuildProcess.php中:
于是出现如下结果:
其实这个为何要这样加,或者就是头一个文件里里加的原因请看:http://wyllife.blog.163.com/blog/static/41163901201153113512946/
map <C-J> :!php -l %<CR>
这样在vim里面就可以直接用ctrl+j,调用php解析器对当前文档进行语法检查了.当然如果php解析器不在你的路径下的话,那么应该写上全路径,象下面这样:
map <C-J> :!c:/php/php -l %<CR>
PS:vim的语法美化功能也很方便,只要在程序的第一行,command模式下输入=:1,$,就可以把当前代码排列的井然有序.而editplus则需要另外的工具配合,也能做到这一点.
参考资料:http://www.vim.org/tips/tip.php?tip_id=692
怎么我在vim中不能用你所说的 =:1,$ 来美化PHP代码呢?
难道要先装zendcodeanalyzer?
volcano 于 2008-05-20 @ 00:24:37 留言 :
不需要装zendcodeanalyzer。
首先你需要跳到代码的第一行,然后再输入=:1,$
Jeffery 于 2008-05-20 @ 10:54:57 留言 :
嗯,可以了,我之前是输了:再输的=:1,$
但是这样搞,好像只做了缩进..不知道还有没有其他的参数..
volcano 于 2008-05-20 @ 11:00:12 留言 :
我是这样理解的,如果你输入了=号,那就表示你要做代码自动缩进的操作,后面输入需要操作的行数范围即可
wen 于 2008-05-30 @ 12:28:10 留言 :
你好!看了你php语法检查,确实很有用谢谢!
但是总觉得没吃都要调用cmd,有点丑,可否用QUICKFIX窗口输出
我在网上看了一篇文章 ,他是用quickfix输出,但是我试了一下,不行。
代码如下,你能否改改。谢谢
“”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"
” => Check PHP Syntax using makeprg
“”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"
function! PhpCheckSyntax()
” Check php syntax
setlocal makeprg=\”C:\php5\php.exe\”\ -l\ -n\ -d\ html_errors=off
” Set shellpipe
setlocal shellpipe=>
” Use error format for parsing PHP error output
setlocal errorformat=%m\ in\ %f\ on\ line\ %l
make %
endfunction
” Perform :PhpCheckSyntax()
map :call PhpCheckSyntax()
imap :call PhpCheckSyntax()
volcano 于 2008-05-30 @ 13:39:33 留言 :
你机器上的php.exe在这个位置么?C:\php5\php.exe
现在有个问题:
$page = (isset($_GET['page']))?intval($_GET['page']):0;
怎样判断$page是不是个数字!!不知道用什么函数了
因为前面已经intval了!`不会正则!!谁说下
我也来说两句 查看全部评论 相关评论
fyland (2008-4-17 22:48:59)
is_numeric
CrossMaya (2008-4-17 22:55:13)
QUOTE:
原帖由 fyland 于 2008-4-17 22:48 发表
is_numeric
不行
我连is_int都试了
襣吇揷蔥の犭者 (2008-4-24 17:52:56)
就是用is_numeric没必要用正则
lince343 (2008-4-24 18:20:13)
$_GET的东西好像是以字符串形式存在的,is_numeric没用
不管page是不是数字,用过intval($_GET['page'])后都会成为数字
piaomiao163 (2008-4-24 19:18:48)
xiaojie515 (2008-6-25 20:13:21)
ctype_digit()
pylong (2008-6-25 20:16:29)
先intval,再is_int
浮点数另外处理
beilee80 (2008-6-25 21:13:07)
来个简单的,有点小漏洞,但对于page来说已经足够了,呵呵
if ( 0 + $_GET['page'] <= 0 ) echo 'error';
yingwei13 (2008-6-25 22:01:06)
用正则好了,最快
if (!ereg("^[0-9]*$",$_GET['page'] )) echo "false";
qianziai0912 (2008-6-26 17:04:41)
????????????????????不管page是不是整数,intval()都要转换成整数,还判断什么?
intval()
变量转成整数类型。
语
一个男人一夜未归.
(一)
老婆一夜未睡。
第二天来到一家私人侦探社,甩下2000元,委托私家侦探收集花心丈夫出轨的所有证据。
过了一周,老公收到一张法院的传票,老婆起诉要离婚。
最后丈夫被判过错方,房子、家产尽归老婆。
这是个北京老婆。
(二)
老婆一夜未睡。
第二天,老婆上午到美发店做个离子烫,下午做了个面膜,顺便到情趣商店买套性感内衣。晚上在家准备一个烛光晚餐,一共花费四百元。老公晚上回到家后,看到美丽性感的老婆,惊讶得嘴里可以放下一个鸡蛋,深悔自己有眼无珠。并发誓一辈子不会让老婆离开自己。
一周后,老婆写了一篇题为《我怎样留住了我得花心老公?》的文章,并在杂志上发表,还得了五百元稿费。
这是个上海老婆。
(三)
老婆一夜没睡。
第二天,老婆打扮得花枝招展,给初恋情人打了一个电话:喂,还记得我吗?
我很寂寞,我今天晚上有空......
于是老公在外面继续潇洒,老婆在家里私会情人,井水不犯河水,相安无事。
这是个广东老婆。
(四)
老婆一夜未睡。
第二天一起床,老婆把屋里收拾得干干净净,把丈夫的换洗衣服迭的整整齐齐,留了一张纸条,告诉丈夫按时吃药。于是回娘家了。
后来老公良心发现,到岳母家负荆请罪,请回了老婆,并发誓好好过日子。
这是个四川老婆。
(五)
老婆一夜没睡。
第二天,老婆把家里的两把菜刀磨的雪亮,前 . 、后背各掖一把,决定和丈夫摊牌。心里说:哼哼,我跟你不是鱼死就是网破。
后来老公乖乖和老婆回到家里。
这是个湖南老婆。
(六)
老婆一夜没睡。
第二天一起床,老婆摞起袖子下厨房。平时一顿可以吃二两汤面加一张烧饼,今天做一斤汤面外加十张烧饼,并且一顿就消灭掉。
吃完以后,老婆摸着圆滚滚的肚皮,倒在床上放声大哭:这今后的日子可怎么过啊?依尔呦......
老公并没有因为外遇离婚,可是半年后提出离婚,理由是老婆胖的像一头猪......
这是个山西老婆。
(七)
老婆一夜没睡。
第二天,老婆哭着回到娘家,把这件事原原本本的告诉了自己的弟弟。弟弟喊上姑姑家的大哥、舅舅家的老弟。一人手里提着条木棍,在丈夫回家的路上等候......
后来鼻青脸肿的老公到法院提出离婚。经调解无效,法院判双方离婚,财产一认一半。并判老婆负担老公被打的医药费。
这是个东北老婆。
(八)
老婆一夜未睡。
第二天一起床,跑到丈夫单位大哭小叫,当众把丈夫和他那位年轻漂亮的"狐狸精"同事的丑事揭露出来,单位答应一定给予处分。
后来老公和她离婚了,离婚后一周就又和那位年轻妹妹结了婚.
这是个山东老婆。
(九)
老婆一夜未睡。
第二天一起床,老婆把户口本、结婚证、房产证、存折藏了起来。并切断丈夫的一切经济来源,然后洋洋得意的对丈夫说:我看你拿什么来养那个狐狸精......我也不和你离婚,*死你!
这是个......
……
……
……
……
……
……
……
看完不给我跟贴人的老婆!!
/*
常用的ADODB使用方法
整理:飞豹游侠 QQ:8527385 E-mail:liuchengcn # 163.com
如有错误之处,敬请谅解,并QQ或E-mail通知我,谢谢
*/
//定义数据库变量
$DB_TYPE = "mysql";
$DB_HOST = "localhost";
$DB_USER = "root";
$DB_PASS = "";
$DB_DATABASE = "ai-part";
require_once("../adodb/adodb.inc.php");
$db = NewADOConnection("$DB_TYPE");//建立数据库对象
$db->debug = true;//数据库的DEBUG测试,程序开发期,可设置为true,正式版要注释掉这行,(默认值是false)
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
/*
返回的记录集形式
define('ADODB_FETCH_DEFAULT',0);
define('ADODB_FETCH_NUM',1);
define('ADODB_FETCH_ASSOC',2);
define('ADODB_FETCH_BOTH',3);
以上的常量,是在adodb.inc.php里定义的,也就是$ADODB_FETCH_MODE 这个变量可以设置的值
常用的是:ADODB_FETCH_NUM 或 ADODB_FETCH_ASSOC
ADODB_FETCH_NUM 返回的记录集中的索引,是数字形式,即数据库字段的排序顺序值
ADODB_FETCH_ASSOC 返回的记录集中的索引,是原数据库字段名
ADODB_FETCH_BOTH 和 ADODB_FETCH_DEFAULT 是同时返回 ADODB_FETCH_NUM, ADODB_FETCH_ASSOC的值,某些数据库不支持
An example:
$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
$rs1 = $db->Execute('select * from table');
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
$rs2 = $db->Execute('select * from table');
print_r($rs1->fields); # 返回的数组是: array([0]=>'v0',[1] =>'v1')
print_r($rs2->fields); # 返回的数组是: array(['col1']=>'v0',['col2'] =>'v1')
*/
//连接数据库,方法有Connect,PConnect,NConnect,一般使用Connect. NConnect是连接特殊的数据库时才用
if (!@$db->Connect("$DB_HOST", "$DB_USER", "$DB_PASS", "$DB_DATABASE")) {
exit('服务器忙,请稍候再访问');
}
/*
$db-> $rs-> 此类的使用方法
Execute($sql,$inputarr=false),执行参数中的$sql语句,后面的那个$inputarr参数,一般情况下不需要
SelectLimit($sql,$numrows=-1,$offset=-1,$inputarr=false) $numrows:取几条记录,$offset,从第几条开始取,SelectLimit,一般是用于分页,或只取出几条记录的时候用
*/
//Example: 取出多个记录
$sql = "SELECT * FROM table ORDER BY id DESC";
if (!$rs = $db->Execute($sql)) {//执行SQL语句,并把结果返回给$rs变量
echo $db->ErrorMsg();//这个是打印出错信息
$db->Close();//关闭数据库
exit();
}
while (!$rs->EOF) {//遍历记录集
echo $rs->fields['username'] . '
';//print_r($rs->fields)试试,$rs->fields['字段名'],返回的是这个字段里的值
$rs->MoveNext();//将指针指到下一条记录,不用的话,会出现死循环!
}
$rs->Close();//关闭它,以便释放内存,每次操作完都进行一次关闭,养成编程的好习惯
//插入新记录
$sql = "INSERT table (user_type,username) VALUES (3, 'liucheng')";
$db->Execute($sql);
//更新记录
$sql = "UPDATE table SET user_type=3 WHERE id=2";
$db->Execute($sql);
//删除记录
$sql = "DELETE FROM table WHERE id=2";
$db->Execute($sql);
// 取单个记录
//$db->GetRow($sql), 取出SQL中的第一条记录,并返回一个数组,如果出错,则返回false
$sql = "SELECT username,password,user_type FROM table WHERE id=3";
$data_ary = $db->GetRow($sql);
if ($data_ary == false) {//如果用===,可能不是你想要的结果
echo '没有找到此记录';
exit();
} else {
echo $data_ary['username'] . ' ' . $data_ary['password'] . ' ' . $data_ary['user_type'] . '
';
}
//这里没有用到$rs,则不需要$rs->Close();
//另一种方法 (使用上面的方法比较好,又方便)
$sql = "SELECT username,password,user_type FROM table WHERE id=3";
if (!$rs = $db->Execute($sql)) {
echo $db->ErrorMsg();
$db->Close();
exit();
}
if (!$result = $rs->FetchRow()) {
echo '没有找到此记录';
exit();
} else {
echo $result['username'] . ' ' . $result['password'] . ' ' . $result['user_type'] . '
';
}
//$db->GetOne($sql) 取出SQL中的第一条记录的第一个字段的值,如果出错,则返回false
$sql = "SELECT COUNT(id) FROM table";
$record_nums = $db->GetOne($sql);
echo $record_nums;
$sql = "SELECT username,password,user_type FROM table WHERE user_id=1";
$result = $db->GetOne($sql);
echo $result;//此值为记录中的username的值
/*
在进行添加,修改,删除记录操作时,要对字符串型的字段,使用$db->qstr()对用户输入的字符进行处理,对数字型字段,要在之前,进行数据判断
更新记录,注意:这是针对php.ini中,magic_quotes被设置为Off的情况,如果不确定,可以使用
$db->qstr($content,get_magic_quotes_gpc())
注意:content= 等号右边,没有单引号
*/
$sql = "UPDATE table SET content=" . $db->qstr($content) . " WHERE id=2";
$db->Execute($sql);
/*$db->Insert_ID(),无参数,返回刚刚插入的那条记录的ID值,仅支持部分数据库,带auto-increment功能的数据库,如PostgreSQL, MySQL 和 MS SQL
*/
//Example:
$sql = "INSERT table (user_type,username) VALUES (3, 'liucheng')";
$db->Execute($sql);
$data_id = $db->Insert_ID();
echo $data_id;
/*$db->GenID($seqName = 'adodbseq',$startID=1),产生一个ID值.$seqName:用于产生此ID的数据库表名,$startID:起始值,一般不用设置,它会把$seqName中的值自动加1.支持部分数据库,某些数据库不支持
Insert_ID,GenID,一般我用GenID,使用它的目的,是在插入记录后,要马上得到它的ID时,才用
*/
/*Example:
先创建一个列名为user_id_seq的表,里面只有一个字段,id,int(10),NOT NULL,然后插入一条值为0的记录
*/
$user_id = $db->GenID('user_id_seq');
$sql = "INSERT table (id, user_type,username) VALUES (" . $user_id . ", 3, 'liucheng')";
$db->Execute($sql);
/*
$rs->RecordCount(),取出记录集总数,无参数
它好像是把取出的记录集,用count()数组的方法,取得数据的数量
如果取大量数据,效率比较慢,建议使用SQL里的COUNT(*)的方法
$sql = "SELECT COUNT(*) FROM table", 用此方法时,不要在SQL里加ORDER BY,那样会降低执行速度
Example:
*/
$sql = "SELECT * FROM table ORDER BY id DESC";
if (!$rs = $db->Execute($sql)) {
echo $db->ErrorMsg();
$db->Close();
exit();
}
$record_nums = $rs->RecordCount();
/*
如果想对某一结果集,要进行两次同样的循环处理,可以用下面方法
以下,只是一个例子,只为说明$rs->MoveFirst()的使用方法
*/
$sql = "SELECT * FROM table ORDER BY id DESC";
if (!$rs = $db->Execute($sql)) {
echo $db->ErrorMsg();
$db->Close();
exit();
}
$username_ary = array();
while (!$rs->EOF) {
$username_ary[] = $rs->fields['username']
echo $rs->fields['username'] . '
';//print_r($rs->fields)试试,$rs->fields['字段名'],返回的是这个字段里的值
$rs->MoveNext();//将指针指到下一条记录,不用的话,会出现死循环!
}
$username_ary = array_unique($username_ary);
$rs->MoveFirst();//将指针指回第一条记录,无参数
while (!$rs->EOF) {
echo $rs->fields['password'] . '
';//print_r($rs->fields)试试,$rs->fields['字段名'],返回的是这个字段里的值
$rs->MoveNext();//将指针指到下一条记录,不用的话,会出现死循环!
}
$rs->Close();
/*
当本页程序,对数据库的操作完毕后,要$db->Close();
*/
$db->Close();
/*一个不错的方法 */
if (isset($db)) {
$db->Close();
}
?>
一般来说,缓存的目的是把数据放在一个地方让访问的更快点,毫无疑问,内存是最快的,但是,几百M的数据能往内存放么?这不现实,当然,有的时候临时放如服务器缓存,如ob_start()这个缓存页面开启的话在发送文件头之前页面内容都被缓存在内存中,知道等页面输出自动清楚或者等待ob_get_contents的返回,[或者被ob_end_clean显示的清除,这在静态页面的生成中能很好的利用,在模板中能得到很好的体现,我的这篇文章深入的讨论了:
谈PHP生成静态页面,这是一种方式,但这是临时性的,不是解决我们问题的好方法.
另外,在asp中有一对象application,可以保存公用的参数,这也算点缓存,但在php,我至今没看到开发者产出这种对象,的确,没必要.asp.net的页面缓存技术就用的是viewstate,而cache就是文件关联,(不一定准确),文件被修改,更新缓存,文件没被修改而且不超时(注释1),就读取缓存,返回结果,就是这个思路,看看这个源码:
<?php
classcache{
/*
ClassName:cache
Description:controltocachedata,$cache_out_timeisaarraytosavecachedatetimeout.
Version:1.0
Author:老农cjjer
Lastmodify:2006-2-26
AuthorURL:http://www.cjjer.com
*/
private$cache_dir;
private$expireTime=180;//缓存的时间是60秒
function__construct($cache_dirname){
if(!@is_dir($cache_dirname)){
if(!@mkdir($cache_dirname,0777)){
$this->warn('缓存文件不存在而且不能创建,需要手动创建.');
returnfalse;
}
}
$this->cache_dir=$cache_dirname;
}
function__destruct(){
echo'Cacheclassbye.';
}
functionget_url(){
if(!isset($_SERVER['REQUEST_URI'])){
$url=$_SERVER['REQUEST_URI'];
}else{
$url=$_SERVER['SCRIPT_NAME'];
$url.=(!empty($_SERVER['QUERY_STRING']))?'?'.$_SERVER['QUERY_STRING']:'';
}
return$url;
}
functionwarn($errorstring){
echo"<b><fontcolor='red'>发生错误:<pre>".$errorstring."</pre></font></b>";
}
functioncache_page($pageurl,$pagedata){
if(!$fso=fopen($pageurl,'w')){
$this->warns('无法打开缓存文件.');//trigger_error
returnfalse;
}
if(!flock($fso,LOCK_EX)){//LOCK_NB,排它型锁定
$this->warns('无法锁定缓存文件.');//trigger_error
returnfalse;
}
if(!fwrite($fso,$pagedata)){//写入字节流,serialize写入其他格式
$this->warns('无法写入缓存文件.');//trigger_error
returnfalse;
}
flock($fso,LOCK_UN);//释放锁定
fclose($fso);
returntrue;
}
functiondisplay_cache($cacheFile){
if(!file_exists($cacheFile)){
$this->warn('无法读取缓存文件.');//trigger_error
returnfalse;
}
echo'读取缓存文件:'.$cacheFile;
//returnunserialize(file_get_contents($cacheFile));
$fso=fopen($cacheFile,'r');
$data=fread($fso,filesize($cacheFile));
fclose($fso);
return$data;
}
functionreadData($cacheFile='default_cache.txt'){
$cacheFile=$this->cache_dir."/".$cacheFile;
if(file_exists($cacheFile)&&filemtime($cacheFile)>(time()-$this->expireTime)){
$data=$this->display_cache($cacheFile);
}else{
$data="fromherewocangetitfrommysqldatabase,updatetimeis<b>".date('ldSofFYh:i:sA')."</b>,过期时间是:".date('ldSofFYh:i:sA',time()+$this->expireTime)."----------";
$this->cache_page($cacheFile,$data);
}
return$data;
}
}
?>
下面我打断这个代码逐行解释.
三:程序透析
这个缓存类(类没什么好怕的.请继续看)名称是cache,有2个属性:
private$cache_dir;
private$expireTime=180;
$cache_dir是缓存文件所放的相对网站目录的父目录,$expireTime(注释一)是我们缓存的数据过期的时间,主要是这个思路:
当数据或者文件被加载的时候,先判断缓存文件存在不,返回false,文件最后修改时间和缓存的时间和比当前时间大不,大的话说明缓存还没到期,小的话返回false,当返回false的时候,读取原始数据,写入缓存文件中,返回数据.,
接着看程序:
function__construct($cache_dirname){
if(!@is_dir($cache_dirname)){
if(!@mkdir($cache_dirname,0777)){
$this->warn('缓存文件不存在而且不能创建,需要手动创建.');
returnfalse;
}
}
$this->cache_dir=$cache_dirname;
}
当类第一次被实例的时候构造默认函数带参数缓存文件名称,如文件不存在,创建一个有编辑权限的文件夹,创建失败的时候抛出异常.然后把cache类的$cache_dir属性设置为这个文件夹名称,我们的所有缓存文件都是在这个文件夹下面的.
function__destruct(){
echo'Cacheclassbye.';
}
这是class类的析构函数,为了演示,我们输出一个字符串表示我们释放cache类资源成功.
functionwarn($errorstring){
echo"<b><fontcolor='red'>发生错误:<pre>".$errorstring."</pre></font></b>";
}
这个方法输出错误信息.
functionget_url(){
if(!isset($_SERVER['REQUEST_URI'])){
$url=$_SERVER['REQUEST_URI'];
}else{
$url=$_SERVER['SCRIPT_NAME'];
$url.=(!empty($_SERVER['QUERY_STRING']))?'?'.$_SERVER['QUERY_STRING']:'';
}
return$url;
}
这个方法返回当前url的信息,这是我看国外很多人的cms系统这样做,主要是缓存x.php?page=1,x.php?page=2,等这种文件的,这里列出是为了扩展的这个cache类功能的.
functioncache_page($pageurl,$pagedata){
if(!$fso=fopen($pageurl,'w')){
$this->warns('无法打开缓存文件.');//trigger_error
returnfalse;
}
if(!flock($fso,LOCK_EX)){//LOCK_NB,排它型锁定
$this->warns('无法锁定缓存文件.');//trigger_error
returnfalse;
}
if(!fwrite($fso,$pagedata)){//写入字节流,serialize写入其他格式
$this->warns('无法写入缓存文件.');//trigger_error
returnfalse;
}
flock($fso,LOCK_UN);//释放锁定
fclose($fso);
returntrue;
}
cache_page方法分别传入的是缓存的文件名称和数据,这是把数据写到文件里的方法,先用fopen打开文件,然后调用句柄锁定这个文件,然后用fwrite写入文件,最后释放这个句柄,任何一步发生错误将抛出错误.您可能看到这个注释
写入字节流,serialize写入其他格式
,顺便一提的是如果我们要把一个数组,(可以从MySQL数据库里面select查询除了的结果)用serialize函数写入,用unserialize读取到原来的类型.
functiondisplay_cache($cacheFile){
if(!file_exists($cacheFile)){
$this->warn('无法读取缓存文件.');//trigger_error
returnfalse;
}
echo'读取缓存文件:'.$cacheFile;
//returnunserialize(file_get_contents($cacheFile));
$fso=fopen($cacheFile,'r');
$data=fread($fso,filesize($cacheFile));
fclose($fso);
return$data;
}
这是由文件名称读取缓存的方法,直接打开文件,读取全部,如果文件不存在的或者无法读取的话返回false,当然,你感到不人性的话,可以重新生成缓存.
functionreadData($cacheFile='default_cache.txt'){
$cacheFile=$this->cache_dir."/".$cacheFile;
if(file_exists($cacheFile)&&filemtime($cacheFile)>(time()-$this->expireTime)){
$data=$this->display_cache($cacheFile);
}else{
$data="fromherewocangetitfrommysqldatabase,updatetimeis<b>".date('ldSofFYh:i:sA')."</b>,过期时间是:".date('ldSofFYh:i:sA',time()+$this->expireTime)."----------";
$this->cache_page($cacheFile,$data);
}
return$data;
}
这个函数是我们调用的方法,可以写成接口的方法,由传入参数判断文件存在不,文件最后修改时间+expireTime的时间是不是过了当前时间(大于的话说明没有过期),如果文件不存在或者已经过期,重新加载原始数据,这里,为了简单期间,我们是直接源是字符串,您可以把cache类继承某类,取到数据库的数据.(注释2)
四:补充说明,结语
注释一:这个缓存的时间您可以自己调,可以根据时间情况读取数组,xml,缓存等,请按照您的方便,值得一提的是缓存的时间(也就是缓存的key)也用缓存控制,.这在cms系统中被广泛使用,他们把要更新的key放在缓存中,非常容易控制全战.
注释二:php5开始支持类继承,这是让人兴奋的,把网站全局休息写在一个配置的类里面,再写与数据层交互的类(如与MySQL交互的类),我们的这个cache类继承数据交互的类,可以非常容易的读取数据库,这是外话,此处不再展开,有时间和大家详谈.
特别说明,这个类文件针对的php5以上版本,其他版本的请不要使用类.
前言:Linux下线程的创建
介绍在Linux下线程的创建和基本的使用. Linux下的线程是一个非常复杂的问题,由
于我对线程的学习不时很好,我在这里只是简单的介绍线程的创建和基本的使用,关于线
程的高级使用(如线程的属性,线程的互斥,线程的同步等等问题)可以参考我后面给出的
资料. 现在关于线程的资料在网络上可以找到许多英文资料,后面我罗列了许多链接,对
线程的高级属性感兴趣的话可以参考一下. 等到我对线程的了解比较深刻的时候,我回来
完成这篇文章.如果您对线程了解的详尽我也非常高兴能够由您来完善.
先介绍什么是线程.我们编写的程序大多数可以看成是单线程的.就是程序是按照一定的
顺序来执行.如果我们使用线程的话,程序就会在我们创建线成的地方分叉,变成两个"程
序"在执行.粗略的看来好象和子进程差不多的,其实不然.子进程是通过拷贝父进程的地
址空间来执行的.而线程是通过共享程序代码来执行的,讲的通俗一点就是线程的相同的
代码会被执行几次.使用线程的好处是可以节省资源,由于线程是通过共享代码的,所以没
有进程调度那么复杂.
线程的创建和使用
线程的创建是用下面的几个函数来实现的.
view plainprint?
#include <pthread.h>
int pthread_create(pthread_t *thread,pthread_attr_t *attr,
void *(*start_routine)(void *),void *arg);
void pthread_exit(void *retval);
int pthread_join(pthread *thread,void **thread_return);
#include <pthread.h>
int pthread_create(pthread_t *thread,pthread_attr_t *attr,
void *(*start_routine)(void *),void *arg);
void pthread_exit(void *retval);
int pthread_join(pthread *thread,void **thread_return);
pthread_create创建一个线程,thread是用来表明创建线程的ID,attr指出线程创建时候
的属性,我们用NULL来表明使用缺省属性.start_routine函数指针是线程创建成功后开始
执行的函数,arg是这个函数的唯一一个参数.表明传递给start_routine的参数. pthrea
d_exit函数和exit函数类似用来退出线程.这个函数结束线程,释放函数的资源,并在最后
阻塞,直到其他线程使用pthread_join函数等待它.然后将*retval的值传递给**thread_
return.由于这个函数释放所以的函数资源,所以retval不能够指向函数的局部变量. pt
hread_join和wait调用一样用来等待指定的线程. 下面我们使用一个实例来解释一下使
用方法.在实践中,我们经常要备份一些文件.下面这个程序可以实现当前目录下的所有文
件备份.备份后的后缀名为bak
view plainprint?
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#define BUFFER 512
struct copy_file {
int infile;
int outfile;
};
void *copy(void *arg)
{
int infile,outfile;
int bytes_read,bytes_write,*bytes_copy_p;
char buffer[BUFFER],*buffer_p;
struct copy_file *file=(struct copy_file *)arg;
infile=file->infile;
outfile=file->outfile;
/* 因为线程退出时,所有的变量空间都要被释放,所以我们只好自己分配内存了 */
if((bytes_copy_p=(int *)malloc(sizeof(int)))==NULL) pthread_exit(NULL);
bytes_read=bytes_write=0;
*bytes_copy_p=0;
/* 还记得怎么拷贝文件吗 */
while((bytes_read=read(infile,buffer,BUFFER))!=0)
{
if((bytes_read==-1)&&(errno!=EINTR))break;
else if(bytes_read>0)
{
buffer_p=buffer;
while((bytes_write=write(outfile,buffer_p,bytes_read))!=0)
{
if((bytes_write==-1)&&(errno!=EINTR))break;
else if(bytes_write==bytes_read)break;
else if(bytes_write>0)
{
buffer_p+=bytes_write;
bytes_read-=bytes_write;
}
}
if(bytes_write==-1)break;
*bytes_copy_p+=bytes_read;
}
}
close(infile);
close(outfile);
pthread_exit(bytes_copy_p);
}
int main(int argc,char **argv)
{
pthread_t *thread;
struct copy_file *file;
int byte_copy,*byte_copy_p,num,i,j;
char filename[BUFFER];
struct dirent **namelist;
struct stat filestat;
/* 得到当前路径下面所有的文件(包含目录)的个数 */
if((num=scandir(".",&namelist,0,alphasort))<0)
{
fprintf(stderr,"Get File Num Error:%s\n\a",strerror(errno));
exit(1);
}
/* 给线程分配空间,其实没有必要这么多的 */
if(((thread=(pthread_t *)malloc(sizeof(pthread_t)*num))==NULL)||
((file=(struct copy_file *)malloc(sizeof(struct copy_file)*num))==NULL)
)
{
fprintf(stderr,"Out Of Memory!\n\a");
exit(1);
}
for(i=0,j=0;i<num;i++)
{
memset(filename,'\0',BUFFER);
strcpy(filename,namelist[i]->d_name);
if(stat(filename,&filestat)==-1)
{
fprintf(stderr,"Get File Information:%s\n\a",strerror(errno));
exit(1);
}
/* 我们忽略目录 */
if(!S_ISREG(filestat.st_mode))continue;
if((file[j].infile=open(filename,O_RDONLY))<0)
{
fprintf(stderr,"Open %s Error:%s\n\a",filename,strerror(errno));
continue;
}
strcat(filename,".bak");
if((file[j].outfile=open(filename,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))
<0)
{
fprintf(stderr,"Creat %s Error:%s\n\a",filename,strerror(errno
));
continue;
}
/* 创建线程,进行文件拷贝 */
if(pthread_create(&thread[j],NULL,copy,(void *)&file[j])!=0)
fprintf(stderr,"Create Thread[%d] Error:%s\n\a",i,strerror(errno));
j++;
}
byte_copy=0;
for(i=0;i<j;i++)
{
/* 等待线程结束 */
if(pthread_join(thread[i],(void **)&byte_copy_p)!=0)
fprintf(stderr,"Thread[%d] Join Error:%s\n\a",
i,strerror(errno));
else
{
if(bytes_copy_p==NULL)continue;
printf("Thread[%d] Copy %d bytes\n\a",i,*byte_copy_p);
byte_copy+=*byte_copy_p;
/* 释放我们在copy函数里面创建的内存 */
free(byte_copy_p);
}
}
printf("Total Copy Bytes %d\n\a",byte_copy);
free(thread);
free(file);
exit(0);
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#define BUFFER 512
struct copy_file {
int infile;
int outfile;
};
void *copy(void *arg)
{
int infile,outfile;
int bytes_read,bytes_write,*bytes_copy_p;
char buffer[BUFFER],*buffer_p;
struct copy_file *file=(struct copy_file *)arg;
infile=file->infile;
outfile=file->outfile;
/* 因为线程退出时,所有的变量空间都要被释放,所以我们只好自己分配内存了 */
if((bytes_copy_p=(int *)malloc(sizeof(int)))==NULL) pthread_exit(NULL);
bytes_read=bytes_write=0;
*bytes_copy_p=0;
/* 还记得怎么拷贝文件吗 */
while((bytes_read=read(infile,buffer,BUFFER))!=0)
{
if((bytes_read==-1)&&(errno!=EINTR))break;
else if(bytes_read>0)
{
buffer_p=buffer;
while((bytes_write=write(outfile,buffer_p,bytes_read))!=0)
{
if((bytes_write==-1)&&(errno!=EINTR))break;
else if(bytes_write==bytes_read)break;
else if(bytes_write>0)
{
buffer_p+=bytes_write;
bytes_read-=bytes_write;
}
}
if(bytes_write==-1)break;
*bytes_copy_p+=bytes_read;
}
}
close(infile);
close(outfile);
pthread_exit(bytes_copy_p);
}
int main(int argc,char **argv)
{
pthread_t *thread;
struct copy_file *file;
int byte_copy,*byte_copy_p,num,i,j;
char filename[BUFFER];
struct dirent **namelist;
struct stat filestat;
/* 得到当前路径下面所有的文件(包含目录)的个数 */
if((num=scandir(".",&namelist,0,alphasort))<0)
{
fprintf(stderr,"Get File Num Error:%s\n\a",strerror(errno));
exit(1);
}
/* 给线程分配空间,其实没有必要这么多的 */
if(((thread=(pthread_t *)malloc(sizeof(pthread_t)*num))==NULL)||
((file=(struct copy_file *)malloc(sizeof(struct copy_file)*num))==NULL)
)
{
fprintf(stderr,"Out Of Memory!\n\a");
exit(1);
}
for(i=0,j=0;i<num;i++)
{
memset(filename,'\0',BUFFER);
strcpy(filename,namelist[i]->d_name);
if(stat(filename,&filestat)==-1)
{
fprintf(stderr,"Get File Information:%s\n\a",strerror(errno));
exit(1);
}
/* 我们忽略目录 */
if(!S_ISREG(filestat.st_mode))continue;
if((file[j].infile=open(filename,O_RDONLY))<0)
{
fprintf(stderr,"Open %s Error:%s\n\a",filename,strerror(errno));
continue;
}
strcat(filename,".bak");
if((file[j].outfile=open(filename,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))
<0)
{
fprintf(stderr,"Creat %s Error:%s\n\a",filename,strerror(errno
));
continue;
}
/* 创建线程,进行文件拷贝 */
if(pthread_create(&thread[j],NULL,copy,(void *)&file[j])!=0)
fprintf(stderr,"Create Thread[%d] Error:%s\n\a",i,strerror(errno));
j++;
}
byte_copy=0;
for(i=0;i<j;i++)
{
/* 等待线程结束 */
if(pthread_join(thread[i],(void **)&byte_copy_p)!=0)
fprintf(stderr,"Thread[%d] Join Error:%s\n\a",
i,strerror(errno));
else
{
if(bytes_copy_p==NULL)continue;
printf("Thread[%d] Copy %d bytes\n\a",i,*byte_copy_p);
byte_copy+=*byte_copy_p;
/* 释放我们在copy函数里面创建的内存 */
free(byte_copy_p);
}
}
printf("Total Copy Bytes %d\n\a",byte_copy);
free(thread);
free(file);
exit(0);
}
线程的介绍就到这里了,关于线程的其他资料可以查看下面这写链接.
Getting Started With POSIX Threads
★方式一:
一、服务器端(例:10.0.0.1*):
1、创建用户和组
groupadd www -g 48
useradd -u 48 -g www www
mkdir -p /opt/htdocs
chmod +w /opt/htdocs
chown www:www /opt/htdocs
2、编辑rsync的配置文件
vi /etc/rsyncd.conf
输入以下内容:
引用
uid=www
gid=www
max connections=10
use chroot=no
log file=/var/log/rsyncd.log
pid file=/var/run/rsyncd.pid
lock file=/var/run/rsyncd.lock
[zhangyan]
path=/opt/htdocs
comment = my htdocs
ignore errors
read only = no
hosts allow=10.0.0.21 10.0.0.22
3、启动rsync服务器端
/usr/bin/rsync --daemon
二、客户端(例:10.0.0.21和10.0.0.22):
1、创建一个shell脚本push.sh
vi push.sh
输入以下内容(10.0.0.1*为要推送到的目标服务器,zhangyan为服务器端rsyncd.conf配置文件中的模块名):
引用
#!/bin/sh
/usr/bin/rsync -vzrtopg --delete $1 10.0.0.10::zhangyan/
/usr/bin/rsync -vzrtopg --delete $1 10.0.0.11::zhangyan/
/usr/bin/rsync -vzrtopg --delete $1 10.0.0.16::zhangyan/
/usr/bin/rsync -vzrtopg --delete $1 10.0.0.19::zhangyan/
注:因为是内部局域网之间传输,这里没有设置密码。
2、赋予push.sh执行权限
chmod +x ./push.sh
3、同步推送客户端某个目录中(例:/home/zhanguan/abc)的文件到服务器端的/opt/htdocs目录:
./push.sh /home/zhanguan/abc/
注意:不要忘了abc后面的/。如果更改的文件太大,可以在上面语句的末尾加上空格和&号,推到后台执行。例:
./push.sh /home/zhanguan/abc/ &
★方式二:
一、服务器端(例:10.0.0.1*):
1、创建用户和组
groupadd www -g 48
useradd -u 48 -g www www
mkdir -p /opt/htdocs
chmod +w /opt/htdocs
chown www:www /opt/htdocs
passwd www
为www用户设置一个密码。
二、客户端:
1、同步推送客户端某个目录中(例:/home/zhanguan/abc)的文件到服务器端的/opt/htdocs目录:
/usr/bin/rsync -vzrtopg --delete /home/zhanguan/abc/ -e ssh www@10.0.0.10:/opt/htdocs/
输入密码,回车即可,无须服务器端启动rsync服务。
[ 2007-7-16 16:44 | by 张宴 ]
推送:
/usr/bin/rsync -avgt --progress --password-file=/usr/local/etc/rsyncd.secrets /data0/vshare/logs/user_search/iasks/* backupuser@219.142.118.48::boke_2008_sum_olympic_video
查看目录:
/usr/bin/rsync --password-file=/usr/local/etc/rsyncd.secrets backupuser@219.142.118.48::boke_2008_sum_olympic_video/
列出所有目录结构:
/usr/bin/rsync -vzrtopg --password-file=/usr/local/etc/rsyncd.secrets backupuser@219.142.118.48::boke_2008_sum_olympic_video/
匿名推送(不要用户名和密码):
/usr/bin/rsync -avgt /home/xiangdong2/rsync/* localhost::app4
app:
/usr/bin/rsync -vzrtopg --password-file=/home/xiangdong2/.rsync.secrets appspace@10.210.128.114::app_space_sina_com_cn
在home下建立test3目录(chmod -R a+x test3)然后同步(注意:/home/xiangdong2/.rsync.secrets文件的chmod 600 /home/xiangdong2/.rsync.secrets):
/usr/bin/rsync --password-file=/home/xiangdong2/.rsync.secrets test3 appspace@10.210.128.114::app_space_sina_com_cn -rt
客户端操作
客户端同步出错:
[test01@tonykorn02 ~]$ rsync -vzrtopg --progress test01@192.168.1.250::test01 /front/test01
Password:
@ERROR: auth failed on module test01
rsync error: error starting client-server protocol (code 5) at main.c(1296) [receiver=2.6.8]
[test01@tonykorn02 ~]$
原因:
secret file的文件属性有问题,该文件属性要是600的
"/home/FileServer:" 不明白这个目录是哪里来的 :)
rsync error: some files could not be transferred (code 23) at main.c(892) 有些文件不能被移动
是不是文件没有读的权限?
chmod -R a+r /home/wow/dl
所以说现在最缺的东西其实还是技术。过去因为缺技术让中国受尽列强欺负,但现在也同样因为缺技术导致中国做了很多赔本买卖。精明的外国人,从中国买走原材料,然后深加工,变成了电子芯片,再卖到中国,赚了大笔的钱,但中国为了出口这些原材料消耗了资源,污染了环境,付出了大量的廉价劳动力。微软、IBM、摩托罗拉、富士通,这些企业在包括中国在内的很多国家开办了形形色色的软件工厂,用廉价的劳动力繁荣了自己本国的经济,但这些付出廉价劳动力的国家只是跟着赚了一口剩饭吃,虽然比没得吃要好一些,但也绝对别指望吃得多丰盛。其实这道理大家都明白,但还是不得不继续这种现实,归根结底还是因为缺技术。因为缺技术,所以受制于人,所以吃人家的剩饭,所以让我们的同胞变成廉价劳动力。技术的发展能够让国家、让人民赚实实在在的钱,而缺乏技术底气的发展,只能带来一阵虚火。如果中国在技术方面能更强一些,更加尊重技术人才,那么,我不敢说GDP是否能有这么大的增长,但是经济绝对不会像现在这么热,通货膨胀绝对不会这么严重,人民生活得肯定比现在幸福,国家也会比现在更强盛。房地产不是洪水猛兽,但这几年中国的这帮房地产商,绝对是历史的罪人。
1. Linux
优点: 充分发挥 PC 的功能,花样极多,玩起来很有趣,各方面的表现都不错。
缺点: 太过自由,以致於发散掉了,维护方面比 FreeBSD 麻烦(对一般人来说)。
-> 适合喜欢「玩 PC」,更甚於「玩 UNIX(Network)」的人。
2. FreeBSD
优点: 非常 UNIX、非常 Free、非常 BSD -- UNIX 的理想归宿!!
缺点: 太过 UNIX,以致於玩下去很难收手 ^^;;
-> 适合喜欢 UNIX,有心好好经营 service 的人;也是 programmer 的理想 OS。
FreeBSD Core Team 并不是刻意忽略「入门的方便性」,只是人力有限,把主力投注在「UNIX 风味的主题」上。
FreeBSD 对硬体的需求实在也不会太严刻,对刚接触的人,建议使用「最一般化」的 硬体,像是: IDE (BigFoot)、ne2000 compatible 杂牌卡,S3Trito64,最烂的14寸 VGA,(atapi-cdrom)。
想说明的是,希望对 FreeBSD 有兴趣的人,别买些「太高档(或者说奇怪)」的硬体, 到时候装不起来就骂 FreeBSD 怎麽这麽烂 ^^;;
可以想一下,到底想试试自己的PC能跑多少东西,还是真的有心进入 UNIX 的世界。
=== 为什麽要选择 FreeBSD ?! ===
嗯...现在有许多免费的 i386 UNIX (在 386 以上 PC 执行的 UNIX),例如 Linux、NetBSD、FreeBSD、OpenBSD、386BSD 等,究竟你要如何选择属於你的
UNIX ?
玩了三年多的 UNIX (一年半 Linux,两个月 NetBSD,两年 FreeBSD)
笔者只能以非正式的说法说说笔者的个人意见,希望这些意见不要引起争论
各个作业系统优缺点的大战。
Linux 是容易上手而且好玩的作业系统,也是现今最多人玩的,正因 为它太好装了,只要硬体没问题闭著眼睛都装的起来,因此 如果你是 i386 UNIX 的新手,这可说是你入门的最佳试金石。
NetBSD 支援 13 种硬体架构,这也是它的强处,算是 multi-platform
的典范。 也因此,i386 在里面只算是 13 种中的一种,自然无法取得全力的发展,再加上其 core team 比较不活跃,所以在 i386 上的硬体支援并不是很好。
OpenBSD 源自 NetBSD,刚出来半年左右,专门把 NetBSD 跟 FreeBSD 的 新功能跟修正加在一起,算是 NetBSD+FreeBSD 的混血儿,由於 其 core team 人数少,加上程式码很少是自己开发的,因此现在
前景还不明朗。
FreeBSD 跟 NetBSD 一样都是基於 4.4 BSD-lite,但是 FreeBSD 现在只支援 i386,所以在 PC 上来说要比 NetBSD/OpenBSD 好太多了, 在从前NetBSD 跟 FreeBSD 的 core team 是一起的,後来分家了。 FreeBSD 具有一般 BSD 系统的稳定,又从其他作业系统学习了许 多优点,再加上自己开发的各种新功能,时时改进演算法以增加 执行效率,现在已是免费 BSD 系列中效率最好的,最主要是因为core team 活跃又乐於接受使用者的意见并改进。
* 什麽是 core team ?
core team 是一个专门对原始程式码做发展跟维护的组织,Linux 没有 core team,NetBSD/OpenBSD/FreeBSD 有。有 core team 的优点是
原始程式码会有一致性,会有组织的被更新,但是整个 OS 的活力也操在 core team 的手中,这就是 NetBSD 在笔者眼中无法兴盛的原因。而没有 core team(如 Linux),好处是全世界每个人都可以发表自己的修正(patch) 不须经由 core team 的审核,但缺点是 source code 杂乱无章且可能会 不同步。所以 Linux 在更新东东的时候,必须由使用者自己注意 kernel、 gcc、library、net-tool、modules、甚至各种 kernel patch 版本的一致性。
(或许在 RetHat Linux 已经稍微好一点了)
而这些可怜的情形在 FreeBSD 身上都不会发生。
* 要选择怎样的 OS 必须看你自己的需求及能力,还有周遭玩的人多不多, 多装几种,多装几次,自己感觉一下才是真的 !
(其实只要不怕 format 硬碟,吃饱撑著,装什麽东西、装几次都好说嘛)
1. 稳定性
一个作业系统最重要的就是稳定性,比方说能连续开机多久,能忍受 多少系统负荷,网路不稳时会不会当掉,网路负荷太大时网路会不会 死掉,笔者个人觉得 FreeBSD > Linux。
尤其许多研究已经提出,Linux 在高系统负荷下的表现相当不好,而
FreeBSD 却不会。要知道世界上最大的 ftp site - wcarchive.cdrom.com 是一台跑著
FreeBSD 的 Pentium pro 机器 (P6-150,512MB RAM,72GB HDs online
more than 1200 ftp users allowed)
注 : wcarchive.cdrom.com = ftp.cdrom.com
2. 网路
争夺封包(packet)的速度,除了网路卡好坏之外,最重要的还是作业系统跟 驱动程式,使用一样的网路卡 FreeBSD > Linux >>> DOS+NCSA. 而且
FreeBSD 在 RPC 及 NFS 上都比 Linux 来的稳定及快速。毕竟 BSD 在网路
这方面是始祖.
3. 移植软体的难易程度
现今一般的软体大多是为 BSD 写的,所以一般软体在 BSD 上会比在SYSV 上容易编译。而 FreeBSD 是 4.4BSD based,Linux 是 SYSV 加 上BSD-extension,所以在 Linux 上编译东西有时是个梦靥 (不是很 SYSV 也不是很 Posix 也不是很 BSD)。不过现在越来越多的软体会注 意到 Linux,因为 Linux 使用者太多了。
FreeBSD 有收集数百种软体的 ports,只要打个 make 就可以轻松编译,不然也有编译好的 binary 可以直接安装使用。
4. 硬体支援
Linux 支援最多种的硬体,NetBSD 最少,而 FreeBSD 夹在中间正急起
直追中,而且许多 FreeBSD 的 driver 都写的相当棒,反而後来被
移植到 NetBSD 跟 Linux。
5. Merged VM/buffer cache
Linux 的磁碟 I/O 速度是一流的,因为一来 Linux 的 ext2fs 是 async-mount 的,写入资料时不须一直更新 meta-data,最主要还是 Linux 会把目前没用到的记忆体尽量拿来做 I/O buffer。一般传统 BSD(如 SunOS,NetBSD)都只有固定大小的 buffer,而 FreeBSD 自己发展出类似 Linux 的 Merged VM/buffer cache,大大提高了 I/O 时的效率以及记忆体利用率,而且现在 FreeBSD 已支援 async-mount, 使得 FreeBSD 的档案系统已经跟 Linux 不相上下,甚至更胜一筹。
6. tty 限制
现在 Linux 要用超过 64 个 tty 除了必须更改应用程式的原始程式码, 还必须做 kernel patch,而 FreeBSD 内定支援 tty[pqrsPQRS][0-9a-v] 总共 256 个 tty,只要到 /dev 下用 MAKEDEV 把 tty 建出来,在 /etc/ttys 加入新的 tty 设定,再到 kernel config file 中把 pty 的数目打入 256 就好了,要使用超过 256 tty 也相当容易修改。
7. 完整原始程式码取得
一般人使用的 Slackware 版 Linux 是由 Slackware 公司整理,所 以一般人要取得完整原始程式码必须自己东抓西抓,这也是 Linux 在 NCTUCCCA 的 mirror 量这麽大的缘故。但往往 Linux 使用者找不到 自己须要的原始程式码,如果没有那些整理 Linux packages 的公司, 以及帮忙 Linux 发展系统工具及函式库的人,Linux 充其量算是只有 Linus 写的 kernel 而已,不过最大的问题还是各家写出来的东东 一致性的问题。不过新出来的 RedHat 已经提供一个简单的软体同步 与更新的方法 - RPM,也算是稍微抒解这一类问题的严重性。 而 FreeBSD 提供完整的系统原始程式码,从 /bin /sbin /usr/bin
/usr/sbin /usr/lib ... 甚至 /etc /usr/share/FAQ 都在里面, 让你可以很容易的更改自己想要的东东,要更新系统时也可以抓取 最新的 source 打个 make world 就成了 (当然也可以用 core team 做好的 binary),它甚至会自动检查各目录的权限是否正确。 简单一句,就是非常的有组织! 利用 binary 来升级只要不到一小时就可以完成,甚至有写好的 script 可以使用。
8. 目录档案组织化
FreeBSD 根据 4.4BSD 规范,什麽档案应该在那里,应该是什麽权限,
编译时应该连结(link)成 static 或 dynamic,都非常的严谨,该有的
manpages 绝对不会少。不像 Linux,写 kernel 一个人、写 library
另一个,写 manpages 又另一个、整理 utility 又另一个,各自为政
不同步,常常档案到处乱放或是重覆,manpages 不完整,许多目录档案
为了新旧版本的相容性而 link 来 link 去。
9. 系统安全
FreeBSD 使用 shadow password,支援 secure NFS,不像 Linux 要 自己安装 shadow password,将来编译 ftpd,sudo 时又得改来改去。
因为USA 版的 DES 禁止输出到美加以外地区,FreeBSD 为了全世界广大
的使用者,在密码系统上内定使用 MD5 编码,它比 DES 来的安全,如果
你不跟 SunOS 类的 YP server 跑 NIS,那你是不须要安装 DES 的。如果
你要使用 DES,你可以安装可以自由流动的 DES 版本 (非 USA 版),在
/usr/share/FAQ/Text/FreeBSD.FAQ 中有提及那里可以取得,或是到台湾
任何一个 FTP 站取得。
此外,FreeBSD 的使用者登入控制,以及档案安全层级保护都比其他
作业系统来的好 (kernel secure level)。
FreeBSD 的 core team 会注意 source code 跟 security 的同步性,
一有新的问题或 sendmail 漏洞,就会立刻更新程式码,已达到最佳的
系统安全。
8. core team 活跃
FreeBSD 的 core team 非常活跃而且谦虚,带动整个 FreeBSD 迅速
发展,每天都有新的 patch 出来,让使用者以 sup/ctm 来定时自动
更新原始程式码。
9. 4.4BSD-lite based
由於 FreeBSD 是基於 4.4BSD-lite 的,因此带来了许多 BSD 的好处,
像网路速度稳定、容易移植软体、安全快速等。
10. 从 Linux 而来的优点
FreeBSD 正在把 Linux 的 dosemu 移植过来,甚至可以直接执行 linux
的 binary (linux emulator),还有移植 Linux 支援的一些驱动程式。
11. 支援 LKM
FreeBSD 支援 Loadable kernel module,也就是说许多驱动程式
在编译 kernel 时可以不必做进去,一旦你要用到时,kernel 会自动
从 /lkm/*.o 载入该 driver,这样可以提高弹性并减小 kernel 使用的
记忆体空间。未来 FreeBSD 会朝向 LKM device 迈进,就像 Solaris
一样不需编译 kernel。
12. 直接执行 gzip 的程式
FreeBSD 可以直接执行 gzip 的程式,如果你把所有的执行档都 gzip
起来,不就等於用 stacker/doublespace 一样了 ?!
13. 线上监控
kernel 支援 tty snoop,可以监控线上使用者 (不像 linux 那个半调子
ttysnoop,会导致许多问题)。
14. 众多档案系统
支援 MFS (Memory File System),类似 SunOS tmpfs 的东东,还有
许多 4.4BSD 定义的档案系统,如 LFS、NULLFS、PORTALFS、UMAPFS
、UNIONFS。
15. Interleaved swap
当你有一个以上的 swap 装置时,会同时使用以增加速度 (尤其是使用
SCSI 装置时),而不是像 Linux 一个接著一个使用。
16. 新的 slice 观念
新的 slice 观念使得 FreeBSD 对其他 OS 的 partition 相容性比
传统的 BSD 好很多,在安装上也较为容易。
17. Binary 相容性
FreeBSD 可以执行 NetBSD-static,BSDI-static,Linux-a.out/elf,
SCO-static 等等的 binary code,增加不少相容性。
18. ccd (软体 RAID)
Concatenated disk (ccd) 驱动程式能让你拥有 Strip、Mirror,甚至
Parity 等 RAID card 才有的功能。
19. 多国语言的支援
FreeBSD 的 localization 是所有免费作业系统中做的最好的,甚至已经
有了亚洲语系(中文、日文)的安装介面。
20. 有组织的原始程式码
FreeBSD 的程式开发者在撰写程式码的时候,会去参考各种 RFC 规范以及 新的理论文献,因此 FreeBSD 的程式码有条不紊、层次鲜明;反观 Linux
常常为了急就章而走捷径写出来的东西,到最後开发新功能时又必须改来
改去。
不过随著时间的发展,Linux、*BSD 都会进步,对於免费的作业系统能
越来越好自然是乐见其成的。
一般而言,如果你须要一台稳定快速的 Internet Server,FreeBSD 是你绝对 的选择;如果你是个人使用或只是想学习 UNIX,Linux 跟 FreeBSD 都是很好 的试金石。
Linux 浮上台面已经四年了,而 FreeBSD 以短短的两年时间就拥有了众多的 使用者人口 (尤其是伺服器,以及程式开发者),高手的选择必有他的道理。
用过 FreeBSD 才知道,『PC 不只是很便宜的工作站』
但是,Linux 的优点是『好玩』,而且随著 kernel 日渐更新,很多东西也 越来越稳定。我们系上从两年前开始就用 Linux 当 mail, acounts, ftp, gopher, terminal, ppp, slip, BBS servers, 最近又加入 WWW server,服务几百位师生。 目前系上已经有好几台 Linux PC 一起运作,其中包含 NFS,与 WinNT,Win95 的连线与资源共享(by SAMBA packages),我们也在测试用其中一台摹拟 Novell Server.
我们的同时上线人数一般不会超过 100 人,用 Linux 来应付绰绰有馀。如果你想开 的是一次几百人上线的 BBS 大站,那可能 FreeBSD 会比较适合。不过话说 回来,能开这种大站的单位都很有钱,大都拿 SUN 或其他 workstation 级的来 run。
Linux 另一个优点是全球的 Linux users 远超过 FreeBSD,这使得 Linux 上面 新的软体跟硬体 drivers 更新数目及速度远超过 FreeBSD。例如,DOSEMU 可以 摹拟 DOS,WINE 可以摹拟 Windows 3.1,smbfs 可以将 Win95 或 WinNT 上的 partition 拿来用:这些在 FreeBSD 上面都还在发展中,甚至没有。新电脑 硬体 drivers 的更新也是如此,几乎任何新的硬体都会有 Linux 迷很快地帮大家 写好 drivers。你如果用过 FreeBSD 跟 Linux,你就会发现 FreeBSD 目前对 硬体要求仍然比较『严格』(其实是还没有人写 drivers)。我用的 scanner, 还有 voice modem,都已经有 Linux 迷写好程式,让我可以在 Linux 上 scan 以及有语音信箱。
我个人的建议是,如果你是个人使用,或者网路同时上线人数不超过一百人以上, Linux 的确是好玩又实用,而且新的硬体很快地几乎都可以在 Linux 上使用。 如果你要架的是几百人上站的机器,又没钱买 workstation,那 FreeBSD 在 网路壅塞时的 performance 的确不错。如果是个人要『玩』,我并不建议 FreeBSD,那会使你觉得提不起兴致(纯属个人观点)。
在 csie gopher 中有关 Linux 与 FreeBSD 的比较中,有一项是 FreeBSD 上 software porting 比较 easy。但是这个 comment 随著 Linux users 群日渐庞大, 我觉得已经有些改变:现在在 Linux 很多东西根本用不著 porting,因为很多 软体根本就是 Linux fans 专门为 Linux 设计写出来的,反而要用这些东西 需要额外费心去修改以便能在 FreeBSD 上使用。DOSEMU,smbfs 即是其中几个
例子。据最近的 newsgroups,FreeBSD core team 有五十多人,但是 Linux fans 散布在全球各地的 programmers 其数量根本无法计算。有心的话, 比较下 Linux 跟 FreeBSD announce newsgroups 就可知一二。
所以,我并不是很赞同一个 UNIX 的新手去玩 FreeBSD。但是,假如有人已经玩过 Linux ,或者在其他工作站级机器有过简单管理经验,那他们会发现
FreeBSD 极易入手。玩过 FreeBSD 的人一定知道光要新增 partitions 就已经是一件麻烦的事。堂堂一个 FreeBSD 的 fdisk 介面连 M$DOS 的都不如, 可见 FreeBSD core team 之目标不在一般连 ls, cp, tar 都不懂的newbie。 另外一个动机是假如你必须要架一台超稳定的 Internet server,那 FreeBSD 是目前的 best choice。
其实呢,如果有心要玩,大可弄个大点的硬碟,同时装上两个系统,一定
可以如鱼得水。我的 office 中同时有一台 FreeBSD,一台 Linux,各做各的事, 也是很快乐。。。。
就目前使用者能观察到的来看, 一般相信 linux 的 data-path-consumed process 的执行速度, 是众多 x86 作业系统中最快的; 而 high load 下的网路则 令人不能感到非常满意. 虽然 linux 第二版後网路 部分有了很大的改善, 据 Linus 本人的说法, linux 在传 single package 已比 FreeBSD 还优胜, 但作
为 NFS 或 high load netserver 还是显得略有不顺 (所谓 "不顺" 与 "不稳" 无关). 毕竟, 考查 linux 的发展历史, 的确是先在 x86-embeded scheduler, fs, 等核心process 执行部分, 最後才加进网路部分, process 执行最佳而网路稍逊乃是合理 的结果.
一般建议如果机器用来执行程式 (如跑 project) 为主, 跑各式怪模怪样的小程式及 server, 或有非正统硬体者 使用 linux 可能较佳.
http://hi.baidu.com/widebright/blog/item/eb039950a0c0996785352427.html
/sbin/service crond start //启动服务
/sbin/service crond stop //关闭服务
/sbin/service crond restart //重启服务
/sbin/service crond reload //重新载入配置
你也可以将这个服务在系统启动的时候自动启动:
在/etc/rc.d/rc.local这个脚本的末尾加上:
/sbin/service crond start
现在Cron这个服务已经在进程里面了,我们就可以用这个服务了,Cron服务提供以下几种接口供大家使用:
1、直接用crontab命令编辑
cron服务提供crontab命令来设定cron服务的,以下是这个命令的一些参数与说明:
crontab -u //设定某个用户的cron服务,一般root用户在执行这个命令的时候需要此参数
crontab -l //列出某个用户cron服务的详细内容
crontab -r //删除某个用户的cron服务
crontab -e //编辑某个用户的cron服务
比如说root查看自己的cron设置:crontab -u root -l
再例如,root想删除fred的cron设置:crontab -u fred -r
在编辑cron服务时,编辑的内容有一些格式和约定,输入:crontab -u root -e
进入vi编辑模式,编辑的内容一定要符合下面的格式:*/1 * * * * ls >> /tmp/ls.txt
这个格式的前一部分是对时间的设定,后面一部分是要执行的命令,如果要执行的命令太多,可以把这些命令写到一个脚本里面,然后在这里直接调用这个脚本就可以了,调用的时候记得写出命令的完整路径。时间的设定我们有一定的约定,前面五个*号代表五个数字,数字的取值范围和含义如下:
分钟 (0-59)
小時 (0-23)
日期 (1-31)
月份 (1-12)
星期 (0-6)//0代表星期天
除了数字还有几个个特殊的符号就是"*"、"/"和"-"、",",*代表所有的取值范围内的数字,"/"代表每的意思,"*/5"表示每5个单位,"-"代表从某个数字到某个数字,","分开几个离散的数字。以下举几个例子说明问题:
每天早上6点
0 6 * * * echo "Good morning." >> /tmp/test.txt //注意单纯echo,从屏幕上看不到任何输出,因为cron把任何输出都email到root的信箱了。
每两个小时
0 */2 * * * echo "Have a break now." >> /tmp/test.txt
晚上11点到早上8点之间每两个小时,早上八点
0 23-7/2,8 * * * echo "Have a good dream:)" >> /tmp/test.txt
每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点
0 11 4 * 1-3 command line
1月1日早上4点
0 4 1 1 * command line
每次编辑完某个用户的cron设置后,cron自动在/var/spool/cron下生成一个与此用户同名的文件,此用户的cron信息都记录在这个文件中,这个文件是不可以直接编辑的,只可以用crontab -e 来编辑。cron启动后每过一份钟读一次这个文件,检查是否要执行里面的命令。因此此文件修改后不需要重新启动cron服务。
2、编辑/etc/crontab 文件配置cron
cron服务每分钟不仅要读一次/var/spool/cron内的所有文件,还需要读一次/etc/crontab,因此我们配置这个文件也能运用 cron服务做一些事情。用crontab配置是针对某个用户的,而编辑/etc/crontab是针对系统的任务。此文件的文件格式是:
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root //如果出现错误,或者有数据输出,数据作为邮件发给这个帐号
HOME=/ //使用者运行的路径,这里是根目录
# run-parts
01 * * * * root run-parts /etc/cron.hourly //每小时执行/etc/cron.hourly内的脚本
02 4 * * * root run-parts /etc/cron.daily //每天执行/etc/cron.daily内的脚本
22 4 * * 0 root run-parts /etc/cron.weekly //每星期执行/etc/cron.weekly内的脚本
42 4 1 * * root run-parts /etc/cron.monthly //每月去执行/etc/cron.monthly内的脚本
大家注意"run-parts"这个参数了,如果去掉这个参数的话,后面就可以写要运行的某个脚本名,而不是文件夹名了。
--------------------------------------
基本格式 :
* * * * * command
分 时 日 月 周 命令
第1列表示分钟1~59 每分钟用*或者 */1表示
第2列表示小时1~23(0表示0点)
第3列表示日期1~31
第4列表示月份1~12
第5列标识号星期0~6(0表示星期天)
第6列要运行的命令
crontab文件的一些例子:
30 21 * * * /usr/local/etc/rc.d/lighttpd restart
上面的例子表示每晚的21:30重启lighttpd 。
45 4 1,10,22 * * /usr/local/etc/rc.d/lighttpd restart
上面的例子表示每月1、10、22日的4 : 45重启lighttpd 。
10 1 * * 6,0 /usr/local/etc/rc.d/lighttpd restart
上面的例子表示每周六、周日的1 : 10重启lighttpd 。
0,30 18-23 * * * /usr/local/etc/rc.d/lighttpd restart
上面的例子表示在每天18 : 00至23 : 00之间每隔30分钟重启lighttpd 。
0 23 * * 6 /usr/local/etc/rc.d/lighttpd restart
上面的例子表示每星期六的11 : 00 pm重启lighttpd 。
* */1 * * * /usr/local/etc/rc.d/lighttpd restart
每一小时重启lighttpd
* 23-7/1 * * * /usr/local/etc/rc.d/lighttpd restart
晚上11点到早上7点之间,每隔一小时重启lighttpd
0 11 4 * mon-wed /usr/local/etc/rc.d/lighttpd restart
每月的4号与每周一到周三的11点重启lighttpd
0 4 1 jan * /usr/local/etc/rc.d/lighttpd restart
一月一号的4点重启lighttpd
1 前言
- 主要目的让大家了解一下怎么做PHP语言的扩展,仍然以上次叙述的Perl的扩展的例子——获得IP来源地址为例。
2 前提条件
- 假设你已经拥有了 LAMP (Linux+Apache+Mysql+PHP的缩写),并假设其安装路径分别是:
Apache:/usr/local/apache
Mysql:/usr/local/mysql
PHP:/usr/local/php
- 目前所叙述的实例可能与 mysql 没有太多的关系,所以无关紧要,但是 PHP 是必须安装的;
- 另外,需要 PHP 的源码,如果你没有,可以去 http://www.php.net 获取 PHP 源码,其源码路径位:
PHPSrc:/usr/local/php/src
- 末了说句,本实例完全在 Linux 64 位平台下演示,若有出入,可以随时联系笔者。
3 流程及步骤
- 其实制作PHP扩展非常简单, ext_skel 命令的帮助已说明得非常详细,如下所示:
[Cnangel@localhost ~]$cd /usr/local/php/src/ext
[Cnangel@localhost ext]$ext_skel --help
./ext_skel --extname=module [--proto=file] [--stubs=file] [--xml[=file]]
[--skel=dir] [--full-xml] [--no-help]
--extname=module module is the name of your extension
--proto=file file contains prototypes of functions to create
--stubs=file generate only function stubs in file
--xml generate xml documentation to be added to phpdoc-cvs
--skel=dir path to the skeleton directory
--full-xml generate xml documentation for a self-contained extension
(not yet implemented)
--no-help don't try to be nice and create comments in the code
and helper functions to test if the module compiled
- 尝试着创建一个扩展 getaddress :
[Cnangel@localhost ext]$ext_skel --extname=getaddress
Creating directory getaddress
Creating basic files: config.m4 config.w32 .cvsignore getaddress.c php_getaddress.h CREDITS EXPERIMENTAL tests/001.phpt getaddress.php [done].
To use your new extension, you will have to execute the following steps:
1. $ cd ..
2. $ vi ext/getaddress/config.m4
3. $ ./buildconf
4. $ ./configure --[with|enable]-getaddress
5. $ make
6. $ ./php -f ext/getaddress/getaddress.php
7. $ vi ext/getaddress/getaddress.c
8. $ make
Repeat steps 3-6 until you are satisfied with ext/getaddress/config.m4 and
step 6 confirms that your module is compiled into PHP. Then, start writing
code and repeat the last two steps as often as necessary.
- 上面的后续的帮助步骤已经说得比较清楚,一共 8 个步骤;
4 详细过程描述
4.1 建立扩展目录
- 方便的 ext_skel 命令能帮助建立一个扩展模型 getaddress ,系统会自动建立一个 getaddress 目录,其文件会相应的生成在 getaddress 目录内;
4.2 描述及编辑config.m4
- 用 ext_skel 建立的目录里面一般有 config.m4 这个文件,这里面有一些基础的宏定义:
- dnl 是注释;
- PHP_ARG_WITH 或者 PHP_ARG_ENABLE 指定了PHP扩展模块的工作方式;
- PHP_REQUIRE_CXX 用于指定这个扩展用到了C++;
- PHP_ADD_INCLUDE 指定PHP扩展模块用到的头文件目录;
- PHP_CHECK_LIBRARY 指定PHP扩展模块PHP_ADD_LIBRARY_WITH_PATH定义以及库连接错误信息等;
- PHP_SUBST 用于说明这个扩展编译成动态链接库的形式;
- PHP_NEW_EXTENSION 用于指定有哪些源文件应该被编译,文件和文件之间用空格隔开;
- 这里指定PHP扩展模块的工作方式为 PHP_ARG_ENABLE ,需要修改config.m4文件为:
[Cnangel@localhost getaddress]vim config.m4找到里面有类似几行:
dnl PHP_ARG_WITH(getaddress, for getaddress support,修改成:
dnl Make sure that the comment is aligned:
dnl [ --with-getaddress Include getaddress support])
dnl Otherwise use enable:
dnl PHP_ARG_ENABLE(getaddress, whether to enable getaddress support,
dnl Make sure that the comment is aligned:
dnl [ --enable-getaddress Enable getaddress support])
dnl PHP_ARG_WITH(getaddress, for getaddress support,
dnl Make sure that the comment is aligned:
dnl [ --with-getaddress Include getaddress support])
dnl Otherwise use enable:
PHP_ARG_ENABLE(getaddress, whether to enable getaddress support,
Make sure that the comment is aligned:
[ --enable-getaddress Enable getaddress support])
- 除了修改 config.m4 外,还需要修改的文件有 getaddress.c 、 php_getaddress.h 两个文件,下面会说到该文件的修改。
4.3 创建configure文件
- 源码修改:进入源代码根目录,使用工具 buildconf 创建 configure 文件,其命令如下:
[Cnangel@localhost getaddress]cd /usr/local/php/src
[Cnangel@localhost src]./buildconf
- 扩展修改:进入扩展目录,使用工具 phpize 创建 configure 文件,其目录如下:
[Cnangel@localhost getaddress]/usr/local/php/bin/phpize
Configuring for:
PHP Api Version: 20041225
Zend Module Api No: 20060613
Zend Extension Api No: 220060519
4.4 创建Makefile文件
- 上个步骤中创建了 configure 文件,记住,一般在类unix系统中, configure 文件是一个可执行文件,用来创建编译过程中所用到的make命令所需要的Makefile文件,其创建过程如下:
./configure --enable-getaddress --with-apxs=/usr/local/apache/bin/apxs --with-php-config=/usr/local/php/bin/php-config
- 注意,如果你写的扩展与apache有关,则需要关联apxs,产生apache的modules。
4.5 编译过程
- 编译过程是一个调试过程,出现错误了需要检查 config.m4 、 getaddress.c 、 php_getaddress.h 这几个文件是否编写正确,编译的过程十分简单,命令如下:
make
4.6 安装扩展模块
- 安装扩展模块一般有两种安装,一种是直接:
make install
- 结果会安装到: /usr/local/php/lib/php/extensions/no-debug-non-zts-20060613/ 目录,然后在 php.ini 里面通过 extension_dir 将该目录设置为php的扩展目录,并在php.ini中打开这个扩展:
extension=getaddress.so
- 另外一种是直接复制。一般经过编译过程这个步骤后,会在 getaddress 目录下生成一个目录: modules ,该目录下面有一个已经编译好的.so文件,如果是静态编译,可能是.a或.la;把该文件复制到一个目录下,比如: /usr/loca/php/ext/ ,然后直接调用函数 dl 来调用其 api。
4.7 运行测试
- getaddress 目录下有一个 getaddress.php 文件专门用来测试你的扩展模块是否正确运行,该文件即可以作为 CGI 来运行又可以当作脚本执行,命令如下:
[Cnangel@localhost getaddress]php -f getaddress.php
Functions available in the test extension:
confirm_getaddress_compiled
Congratulations! You have successfully modified ext/getaddress/config.m4. Module getaddress is now compiled into PHP.
4.8 增添PHP扩展模块函数
- 上述讲了这么多,这节才是最主要的。PHP扩展模块主要有三个方面的作用:
- 增加PHP基础函数没有的功能或更加 OOP(Object Oriented Programming) 的思想供工程调用;
- PHP的扩展一般是汇编、C/C++等编译性语言缩写,二进制运行消耗时间比解释性语言实现同样的功能少得多;
- 由于PHP扩展一般是二进制的,所以一般来说不开源,很方便的保护了版权,能够进行商业运用。
- 默认的 getaddress.c 中, zend_function_entry 是导出函数列表, zend_module_entry 描述了模块的信息。
- 编写代码应该注意:
- 如果是C++开发,记住把getaddress.c的后缀改为cpp,并用extern "C"来把c相关代码包起来;
5 Bugs
- 在64位机器上和32位机器上会出现很大的差异,主要在QQWry.c文件;
- 多个引用php函数会出现corp dump情况,具体原因还未查清;
6 实例代码
- 注意,修复一处Bug:将
Z_STRVAL_P(file) == NULL代替
Z_STRLEN_P(file) == 0
7 总结
- 由于作者对C指针运用不太熟练,所以在操作指针时,往往会出现corp dump,这方面有待加强;
- 就PHP扩展来说,涵盖的面比较广,需要丰富的知识面做铺垫才能做好一个优秀的扩展。
参考:
http://tech.ddvip.com/2009-09/1252583445132022.html
目前该类库可以实现,简体中文 <-> 繁体中文编码互换,简体中文、繁体中文 -> 拼音单向转换,
简体中文、繁体中文 <-> UTF8 编码转换,简体中文、繁体中文 -> Unicode单向转换
@作者 Hessian(solarischan@21cn.com)
@版本 1.5
@版权所有 Hessian / NETiS
@使用授权 GPL(不能应用于任何商业用途,无须经过作者同意即可修改代码,但修改后的代码必须按照GPL协议发布)
@特别鸣谢 unknow(繁简转换代码片断)
@起始 2003-04-01
@最后修改 2003-06-06
@访问 公开
更新记录
ver 1.7 2005-07-26
修改了while循环导致的bug。此bug当字符串最后一个字符为"0"的时候将处理错误。
受影响方法: CHStoUTF8() , CHStoUNICODE()
ver 1.6 2005-05-16
构造函数增加了一个参数以便用户可以方便的在使用的时候设置配置文件路径
ver 1.5 2003-06-06
增加 UTF8 转换到 GB2312、BIG5的功能。
ver 1.4 2003-04-07
增加 当转换HTML时设定为true,即可改变charset的值。
ver 1.3 2003-04-02
增加 繁体中文转换至拼音的功能。
ver 1.2 2003-04-02
合并 简体、繁体中文转换至UTF8的函数。
修改 简体中文转换至拼音的函数,返回值更改为字符串,每一个汉字的拼音用空格分开
增加 简体中文转换为 UNICODE 的功能。
增加 繁体中文转换为 UNICODE 的功能。
ver 1.1 2003-04-02
增加 OpenFile() 函数,支持打开本地文件和远程文件。
增加 简体中文转换为 UTF8 的功能。
增加 繁体中文转换为 UTF8 的功能。
ver 1.0 2003-04-01
一个集合了中文简体,中文繁体对应各种编码互换的类库已经初步完成。