存档

‘脚本’ 分类的存档

用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

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

ie下用location.href切换页面后获取不到来源页HTTP_REFERER的解决方法

2010年12月26日 wenhui 没有评论

ie问题多多,大家懂得

解决方法为,用js生成一个链接,然后触发这个链接的点击事件
下面代码中的参数 “u” 即为需要跳转的url

function go(u){
    if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)){
        var referLink = document.createElement('a');
        referLink.href = u;
        document.body.appendChild(referLink);
        referLink.click();
    } else {
        location.href = u;
    }
}
分类: 脚本 标签:

玩转四方页面阴影效果的jQuery实现(暂未插件化)

2010年12月22日 wenhui 没有评论

http://sifang.com

它用图片实现的,我ps+css烂,还是jquery简单些,有问题给我留言哦 :-)

    var BGSC = 4; //阴影的宽度 这里是4个px
    for(var BGSi=1;BGSi<BGSC;BGSi++){
        if(!$('.BGShadow'+BGSi).length){
            $('.wrapper').append('<div class="BGShadow'+BGSi+'"></div>');
             //这里的.wrapper是需要添加阴影的对象~
        }
        $('.BGShadow'+BGSi).css({position:'absolute',left:$('.wrapper').offset().left-BGSi
                ,top:$('.wrapper').offset().top-BGSi,width:$('.wrapper').width()+BGSi*2+'px'
                ,height:$('.wrapper').height()+BGSi*2+'px',zIndex:-BGSi
                ,backgroundColor:'#555',opacity:'0.08'});
    }
分类: jQuery, 脚本 标签:

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, 脚本 标签:

一个类似mac os x的图片相册预览效果的jQuery实现

2010年10月18日 wenhui 没有评论

版本暂定为0.1吧
参考效果:http://ecug.ncu.me/test/op/photo.php

    jQuery.extend({macosphotoview:function(element,images,speed,width,height,preloadcount){
        var i=0;
        var j=0;
        $(element).html("<img id='imageholder' width='"+(width==null?"240":width)+"' style='height:"+(height==null?"240":height)+"px;' src='"+images[0]+"' />").mouseover(function(){
            $(this).attr('accesskey','1');
            for(var xi=0;xi<images.length;xi++){
                imagestopreload += "<img src='"+images[xi]+"' />";
            }
            $('#macosphotoview_preload').append(imagestopreload);
        }).mouseout(function(){
            $(this).attr('accesskey','0');
        }).mousemove(function(){
            if(i>images.length-1){
                i=-1;
            }
            if($(this).attr('accesskey')=='1'){
                j++;
                if(speed==null){
                    speed=8;
                }
                if(j>speed){
                    i++;
                    if(images[i]!='') $(this).children().attr('src',images[i]);
                    j=0;
                }
            }
        });
        if(!$("#macosphotoview_preload").length){
            $('body').append("<div style='display:none' id='macosphotoview_preload'></div>");
        }
        var imagestopreload="";
        if(preloadcount==null){
            preloadcount=4;
        }
        preloadcount = preloadcount>images.length?images.length:preloadcount;
        for(var xi=0;xi<preloadcount;xi++){
            imagestopreload += "<img src='"+images[xi]+"' />";
        }
        $('#macosphotoview_preload').append(imagestopreload);
    }});

用法及参数介绍:

    $(document).ready(function(){
         $.macosphotoview('#imagecontainer',currentimages);
         $.macosphotoview('#imagecontainer1',currentimages,4);
         $.macosphotoview('#imagecontainer2',currentimages,12);
         $.macosphotoview('#imagecontainer3',currentimages,8,480,320);
         $.macosphotoview('#imagecontainer4',currentimages,8,480,320,10);
    });
//参数1为element的id啊class什么的,第二个参数为图片数组,第三个参数为图片切换速度(值越大越慢),第四第五个参数为图片的宽和高,第六个参数为预加载的图片数量
});
分类: jQuery, 脚本 标签:

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, 脚本 标签: ,

ExtJS Grid 获得选中项数据[转]

2010年8月18日 wenhui 没有评论

来源:http://hi.baidu.com/bluelotus7/blog/item/e0e36e039687c880d43f7c8c.html

Grid的数据源是store,我们有时需要做点击某一个项而触发一定的事件,在这个处理过程中,我们不免需要得到点击的那个项,如何获得相应的数据呢?

首先,来看看怎么写触发事件。假设变量_grid就是一个GridPanel的句柄(或者说引用),我们要对实现双击GridPanel中的某一项时触发一个event。那么我们只需要

_grid.addListener(‘celldblclick’,function(grid, rowIndex, columnIndex, e){
//Todo Something here
});

function中的4个参数的含义,我们来参看下文档。
celldblclick : ( Grid this, Number rowIndex, Number columnIndex, Ext.EventObject e )
Fires when a cell is double clicked
(如果你希望是单击,可以把celldblclick换成cellclick)
我们已经可以得到点击的那个项所对应的列索引和行索引,接下来要做的就是靠这两个重要的索引去帮助找出你想要的数据。我们假设,记录(Record)的field为['pid','name','gender','birthday'].
那么我们先获得Store,使用GridPanel的getStore()方法。
方法原型是:getStore() : DataSource ,这里的DataSource其实就是Ext.data.Store.
然后我们使用getAt( Number index ) : Ext.data.Record方法去得到点击触发事件的那个记录(Record),
得到了Record后怎么办呢?注意哦,这里的Record的用法并不是像Json那样直接用属性去取值的。
而是需要使用方法get( String name ) : Object
这时,虽然返回的是Object,其实就已经是String了,可以直接alert或者做其他的处理。

_grid.addListener(‘celldblclick’,function(grid, rowIndex, columnIndex, e){
var s=grid.getStore();
var x=s.getAt(rowIndex);
alert(x.get(‘pid’));
});

分类: Extjs, 脚本 标签: ,

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, 脚本 标签: