存档

‘PHP’ 分类的存档

用redis+swcs实现的简单搜索引擎

2011年1月10日 wenhui 没有评论

在上一篇文章:搜索引擎的多重分词算法及分词词典更新时对索引的重新生成的简单想法 中,我用十分纠结的语言描述了一种基于redis+swcs实现简单搜索引擎的思路,今天就做了一个简单的demo,测试了下搜索的可行性.
悲剧,下面的代码被wordpress过滤的很严重,大家先将就着看,我研究研究

首先是分词及索引:

//以下代码的使用前提是已经安装了scws的php扩展和phpredis扩展
//scws init
$nowtime =time(); //获取当前的系统时间,搜索引擎已内容的发表时间作为搜索结果的排序权重
$cws = scws_new();
$cws->set_charset('utf8'); //注意!!!!redis良好支持utf8!!!!!!gbk编码未测!!!!
$multi = 8;
$cws->set_duality(false); //是否开启二元分词
$cws->set_ignore(true); //是否忽略标点
$cws->set_multi($multi); //设置多元分词模式,8表示将句子拆成单字,返回的分词结果为"正常分词"+"所有单字"
//假设"魔兽争霸"的正常分词结果为"魔兽"和"争霸",它的单字为"魔","兽","争","霸"
//那么将multi设为8的时候的结果就是"魔兽","争霸","魔","兽","争","霸"
//更详细的内容参加scws的文档
//end
$r = Redis();
$r->connect('127.0.0.1');
$r->select(15);//用redis第15号数据库作为测试数据库
$postid = $r->incr("global:nextPostId"); //获取当前数据的id,很redis的写法
$post = $_POST['content'];
$r->set("post:{$postid}",$post); //$post中保存为获取到的内容

//word split and index
$cws->send_text($post);      //将$post中的内容发往scws分词程序
while ($res = $cws->get_result()){    //scws的结果并非一次性返回,而是分多次,若取到的结果为空则表示分词完毕
    foreach ($res as $tmp)
    {
        if ($tmp['len'] == 1 && $tmp['word'] == "\r"){
            continue;
        }elseif ($tmp['len'] == 1 && $tmp['word'] == "\n"){
            continue;
        }else{
            $r->zAdd("index:time:{$tmp['word']}",$nowtime,$postid);  //将分好的词作为索引的key,时间作为权重,$postid作为zset的内容
//在这里我采用zset(sorted sets)来保存索引的内容,方便对搜索结果进行排行
        }
    }
}
$cws->close();
//end

然后是搜索部分:

$r = Redis();  //初始化redis
$r->connect('127.0.0.1');
$r->select(15);

$keyword = $_GET['k'];  //获取搜索的关键字

// scws init
$nowtime = time();
$cws = scws_new();
$cws->set_charset('utf8');
$cws->set_duality(false);
$cws->set_ignore(true);
$cws->set_multi(0);   //这里的0表示,只返回正常的分词结果
//end

//word split and search
$cws->send_text($keyword);   //对关键字进行分词
$keyarray = array();        //用于保存用于查询的key的数组,这么做的原因是phpredis扩展在使用zinter来判断zset的相同内容时所传递的参数为key的数组
while ($res = $cws->get_result()){
    foreach ($res as $tmp)
    {
        if ($tmp['len'] == 1 && $tmp['word'] == "\r"){
            continue;
        }elseif ($tmp['len'] == 1 && $tmp['word'] == "\n"){
            continue;
        }else{
            $keyarray[] = "index:time:{$tmp['word']}";  //将需要查询的key放入数组
        }
    }
}
$cws->close();
$tmpkeyname = "tmpkey:{$nowtime}";  //用于保存zinter命令产生的临时搜索结果列表
//redis的另一个好处就是,你可以很方便的给搜索结果做缓存
$r->zInter($tmpkeyname ,$keyarray);  //获取搜索结果
//这里我们还是以"魔兽争霸"做例子,"魔兽争霸"被分词为"魔兽"和"争霸"
//那么在$keyarray中保存的内容就应该是 array("index:time:魔兽","index:time:争霸")
//zInter所返回的结果,就是post内容中既包含"魔兽",又包含"争霸"的$postid号,继续看下面
$data = $r->sort($tmpkeyname,array('get'=>"post:*"));//通过sort指令对$tmpkeyname中的$postid数据按score(这里是时间)排序
//并根据排序结果get到post的内容 参见redis 的sort指令
foreach($data as $d){   //输出结果
  echo "{$d}";
}

$r->delete($tmpkeyname );   //删除临时搜索结果,此例子没有用到缓存
//end

此代码仅仅实现了一个很简单的索引,和搜索功能,不过值得惊叹的是,就这么几十行代码就能实现一个简单的搜索引擎.
文章写的不是很清楚,各位看官有什么不清楚的地方可以跟我留言交流.谢谢:-)

PHP中$_SERVER的详细用法

2010年12月4日 wenhui 没有评论

$_SERVER['PHP_SELF'] #当前正在执行脚本的文件名,与 document root相关。
$_SERVER['argv'] #传递给该脚本的参数。
$_SERVER['argc'] #包含传递给程序的命令行参数的个数(如果运行在命令行模式)。
$_SERVER['GATEWAY_INTERFACE'] #服务器使用的 CGI 规范的版本。例如,“CGI/1.1”。
$_SERVER['SERVER_NAME'] #当前运行脚本所在服务器主机的名称。
$_SERVER['SERVER_SOFTWARE'] #服务器标识的字串,在响应请求时的头部中给出。
$_SERVER['SERVER_PROTOCOL'] #请求页面时通信协议的名称和版本。例如,“HTTP/1.0”。
$_SERVER['REQUEST_METHOD'] #访问页面时的请求方法。例如:“GET”、“HEAD”,“POST”,“PUT”。
$_SERVER['QUERY_STRING'] #查询(query)的字符串。
$_SERVER['DOCUMENT_ROOT'] #当前运行脚本所在的文档根目录。在服务器配置文件中定义。
$_SERVER['HTTP_ACCEPT'] #当前请求的 Accept: 头部的内容。
$_SERVER['HTTP_ACCEPT_CHARSET'] #当前请求的 Accept-Charset: 头部的内容。例如:“iso-8859-1,*,utf-8”。
$_SERVER['HTTP_ACCEPT_ENCODING'] #当前请求的 Accept-Encoding: 头部的内容。例如:“gzip”。
$_SERVER['HTTP_ACCEPT_LANGUAGE']#当前请求的 Accept-Language: 头部的内容。例如:“en”。
$_SERVER['HTTP_CONNECTION'] #当前请求的 Connection: 头部的内容。例如:“Keep-Alive”。
$_SERVER['HTTP_HOST'] #当前请求的 Host: 头部的内容。
$_SERVER['HTTP_REFERER'] #链接到当前页面的前一页面的 URL 地址。
$_SERVER['HTTP_USER_AGENT'] #当前请求的 User_Agent: 头部的内容。
$_SERVER['REMOTE_ADDR'] #正在浏览当前页面用户的 IP 地址。
$_SERVER['REMOTE_HOST'] #正在浏览当前页面用户的主机名。
$_SERVER['REMOTE_PORT'] #用户连接到服务器时所使用的端口。
$_SERVER['SCRIPT_FILENAME'] #当前执行脚本的绝对路径名。
$_SERVER['SERVER_ADMIN'] #管理员信息
$_SERVER['SERVER_PORT'] #服务器所使用的端口
$_SERVER['SERVER_SIGNATURE'] #包含服务器版本和虚拟主机名的字符串。
$_SERVER['PATH_TRANSLATED'] #当前脚本所在文件系统(不是文档根目录)的基本路径。
$_SERVER['SCRIPT_NAME'] #包含当前脚本的路径。这在页面需要指向自己时非常有用。
$_SERVER['REQUEST_URI'] #访问此页面所需的 URI。例如,“/index.html”。
$_SERVER['PHP_AUTH_USER'] #当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是用户输入的用户名。
$_SERVER['PHP_AUTH_PW'] #当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是用户输入的密码。
$_SERVER['AUTH_TYPE'] #当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是认证的类型。

分类: PHP, 脚本 标签:

PHP获取搜索引擎关键字的算法

2010年8月25日 wenhui 没有评论

下面的代码是PHP的

<?php
$q='http://www.baidu.com/s?wd=%E9%80%9A%E8%BF%87%E7%9C%8B%E6%89%8B%E7%9B%B8%E4%B8%8E%E5%A5%B3%E7%94%9F%E8%BF%9B%E8%A1%8C%E8%BA%AB%E4%BD%93%E6%8E%A5%E8%A7%A6&ie=utf-8 ';
//$_SERVER['HTTP_REFERER']
//根据url取得utf-8关键词
function searchword($form){
    global $url;//本站域名
    if(empty($form)||strpos($form,$url)===false){return null;}
    list($enginer,$keys)=explode('?',trim($form));
    $is_utf8=false;
    parse_str($keys,$query);
    if(strpos($enginer,'baidu')!=false){
        $Keyword= $query["wd"]?$query["wd"]:$query["word"];
        $is_utf8=($query["ie"]=='utf-8')?true:false;
    }elseif(strpos($enginer,'google')!=false){
        $Keyword= $query["q"];
        $is_utf8=($query["ie"]=='gb2312'||$query["ie"]=='gb')?false:true;
    }elseif(strpos($enginer,'soso')!=false){
        $Keyword= $query["w"];
        $is_utf8=false;
    }elseif(strpos($enginer,'sogou')!=false){
        $Keyword= $query["query"];
        $is_utf8=false;
    }elseif(strpos($enginer,'youdao')!=false){
        $Keyword= $query["q"];
        $is_utf8=(strtolower($query["ue"])=='gb2312')?false:true;
    }elseif(strpos($enginer,'bing')!=false){
        $Keyword= $query["q"];
        $is_utf8=true;
    }else{
         $Keyword=null;
    }
    return urldecode($is_utf8?$Keyword:iconv("gb2312","utf-8",$Keyword));
}
echo searchword($q);
?>
分类: PHP, 脚本 标签: ,

PHP实现多服务器session共享之memcache共享[转]

2010年8月13日 wenhui 没有评论

作/译者:叶金荣(Email:imysql@imysql.cn ),来源:http://imysql.cn,转载请注明作/译者和出处,并且不能用于商业用途,违者必究。

本文是 PHP实现多服务器session共享之NFS共享的延续,不过这次,我采用memcache来存储session了,memcache的安装请看这里: 搭建高效、可靠、稳定的WEB服务器 — 二、) 安装MySQL、memcache。
接下来,再自定义一套session处理机制,关于session的实现方法我就不再多讲,直接贴程序了。

connect(MEMCACHE_HOST , MEMCACHE_PORT))
{
die(‘Fatal Error: Can not connect to memcache host ‘. MEMCACHE_HOST .’:’. MEMCACHE_PORT);
}

return TRUE;
}
// }}}

/** {{{ sessOpen($pSavePath, $name)
*
* @param String $pSavePath
* @param String $pSessName
*
* @return Bool TRUE/FALSE
*/
public function sessOpen($pSavePath = ”, $pSessName = ”)
{
self::$mSessSavePath = $pSavePath;
self::$mSessName = $pSessName;

return TRUE;
}
// }}}

/** {{{ sessClose()
*
* @param NULL
*
* @return Bool TRUE/FALSE
*/
public function sessClose()
{
return TRUE;
}
// }}}

/** {{{ sessRead($wSessId)
*
* @param String $wSessId
*
* @return Bool TRUE/FALSE
*/
public function sessRead($wSessId = ”)
{
$wData = self::$mMemcacheObj->get($wSessId);

//先读数据,如果没有,就初始化一个
if (!empty($wData))
{
return $wData;
}
else
{
//初始化一条空记录
$ret = self::$mMemcacheObj->set($wSessId, ”, 0, SESS_LIFTTIME);

if (TRUE != $ret)
{
die(“Fatal Error: Session ID $wSessId init failed!”);

return FALSE;
}

return TRUE;
}
}
// }}}

/** {{{ sessWrite($wSessId, $wData)
*
* @param String $wSessId
* @param String $wData
*
* @return Bool TRUE/FALSE
*/
public function sessWrite($wSessId = ”, $wData = ”)
{
$ret = self::$mMemcacheObj->replace($wSessId, $wData, 0, SESS_LIFTTIME);

if (TRUE != $ret)
{
die(“Fatal Error: SessionID $wSessId Save data failed!”);

return FALSE;
}

return TRUE;
}
// }}}

/** {{{ sessDestroy($wSessId)
*
* @param String $wSessId
*
* @return Bool TRUE/FALSE
*/
public function sessDestroy($wSessId = ”)
{
self::sessWrite($wSessId);

return FALSE;
}
// }}}

/** {{{ sessGc()
*
* @param NULL
*
* @return Bool TRUE/FALSE
*/
public function sessGc()
{
//无需额外回收,memcache有自己的过期回收机制

return TRUE;
}
// }}}

/** {{{ initSess()
*
* @param NULL
*
* @return Bool TRUE/FALSE
*/
public function initSess()
{
$domain = ‘.imysql.cn’;

//不使用 GET/POST 变量方式
ini_set(‘session.use_trans_sid’, 0);

//设置垃圾回收最大生存时间
ini_set(‘session.gc_maxlifetime’, SESS_LIFTTIME);

//使用 COOKIE 保存 SESSION ID 的方式
ini_set(‘session.use_cookies’, 1);
ini_set(‘session.cookie_path’, ‘/’);

//多主机共享保存 SESSION ID 的 COOKIE
ini_set(‘session.cookie_domain’, $domain);

//将 session.save_handler 设置为 user,而不是默认的 files
session_module_name(‘user’);

//定义 SESSION 各项操作所对应的方法名:
session_set_save_handler(
array(‘MemacheSession’, ‘sessOpen’), //对应于静态方法 My_Sess::open(),下同。
array(‘MemacheSession’, ‘sessClose’),
array(‘MemacheSession’, ‘sessRead’),
array(‘MemacheSession’, ‘sessWrite’),
array(‘MemacheSession’, ‘sessDestroy’),
array(‘MemacheSession’, ‘sessGc’)
);

session_start();

return TRUE;
}
// }}}

}//end class

}//end define

$memSess = new MemacheSession;
$memSess->initSess();
?>

然后,在项目程序的头文件中直接包含 MemacheSession.inc.php 即可,并且以前的程序不用做任何改动。

特别感谢:黑夜路人 的 实现基于Memcache存储的Session类。

备注:memcache PECL 未来版本中,可以直接设置 php.ini 来这定自己的 session.save_handler,大致如下:

session.save_handler = memcache
session.save_path = “tcp://host:port?persistent=1&weight=2&timeout=2&retry_interval=15,tcp://host2:port2″

分类: PHP, 脚本 标签: ,

Facebook架构分析:Facebook 的 PHP 性能与扩展性[转]

2010年8月9日 wenhui 没有评论

来源:貌似是http://www.dbanotes.net

炙手可热的 Facebook 是用 PHP 开发的。随着一些技术交流,逐渐能看到 Facebook 技术人员分享的经验。近期这个 http://www.geeksessions.com/ 站点上看到 Facebook 的 Lucas Nealan 分享的文档比较有参考价值。

Cache 为 王
任何一个成功的站点都有一套最合适自己的 Cache 策略。

Note:这个层次图画的稍微有点问题,不是严格从上到下的。

The Alternative PHP Cache , APC
Facebook 平均每个用户每天要访问超过 50 个页面,PHP的页面载入时间的优化就比较重要了。在 PHP Cache 层,Facebook 采用了 APC。
http://www.php.net/apc

Lucas Nealan 的 PPT 举了一个例子,一个页面显示的时间从 4000 多毫秒降到了 100 多 毫秒。在 apc.stat 关闭的模式下,性能还要更好一些。不过需要重启动或用apc_cache_clear() 来通知更新。


Memcached 层
APC Cache 的是非用户相关的信息,而用户相关的数据 Cache 当然是在 Memcached 中。

Facebook 部署了超过 400 台 Memcached 服务器,超过 5TB 的数据在 Memcached 中。这是当前世界上最大的 Memcached 集群了。也给 Memcached (http://developers.facebook.com/opensource.php)贡献了不少代码,包括 UDP 的支持和性能上的提升(性能提升超过 20%)。

程序 Profiling
Facebook 开发人员大量采用 Callgrind 、APD、 xdebug 、KCachegrind 等工具进行基准性能测试。任何一个 Web 项目,这也是不可或缺,也是比较容易忽略的一环。所有开发人员都应该具备熟练使用这些工具的能力才好。

补充一下:语言的选择
为什么 Facebook 选择 PHP 而不是其他语言? 用Flickr 的 Cal Henderson 这句话就能说明了: “Languages’s don’t Scale, Architecture Scale”。

从 80-20 的原则看,APC 和 Memcached 是最主要的。在这两个环节上下功夫,受益/开销比要大于另外几个环节。

(上面的图是从 Lucas Nealan 的文档截的,版权所有是他的)

分类: PHP, 架构 标签: ,

phpize未找到的解决方法(debian or ubuntu)

2010年8月3日 wenhui 没有评论

phpize 未找到的原因,是因为php5-dev包没有安装

在源中添加

deb http://php53.dotdeb.org stable all
deb-src http://php53.dotdeb.org stable all

然后

apt-get install php5-dev

即可

分类: PHP, 脚本 标签:

PHP中关于正则表达式匹配任意字符(包括换行符)的写法

2010年7月27日 wenhui 2 条评论

今天在Java中想使用正则表达式来获取一段文本中的任意字符。于是很随意得就写出如下匹配规则:

(.*)

结果运行之后才发现,无法获得换行之后的文本。于是查了一下手册,才发现正则表达式中,“.”(点符号)匹配的是除了换行符“\n”以外的所有字符。同时,手册上还有一句话:要匹配包括 ‘\n’ 在内的任何字符,请使用像 ‘[.\n]‘ 的模式。于是我将正则表达式的匹配规则修改如下:

([.\n]*),当然,如果是在java程序中直接写到话,需要改为([.\\n]*)

结果再次运行程序,发现什么内容也取不到了。我百思不得其解,又将其修改为如下规则:

([.|\n]*) 以及 ([\n.]*)

结果还是不行,什么内容都取不到。看来点符号和换行符卯上劲了~

然后上网一查,虽然没有查出上述规则到底是什么地方出问题了,但是查出了一个解决办法,经过一试,果然可以匹配包括换行符在内的任意字符,以下为正确的正则表达式匹配规则:

([\s\S]*)

同时,也可以用 “([\d\D]*)”、“([\w\W]*)” 来表示。

本文可以自由转载,转载时请保留全文并注明出处:
转载自仲子说 [ http://www.wangzhongyuan.com/ ]
原文链接:http://www.wangzhongyuan.com/archives/640.html