自己写的简单一个分页函数

几乎任何PHP项目都要用到分页程序,这里自己写了一个,以便自己重复使用的分页函数,比较简单。

先看看测试效果吧:http://imethan.com/demo/page_test.php

再上代码:
PHP部分

/**
* 初始化分页
* Coded By Jenen.WangSen http://imethan.com
*
* 使用$_GET['page']区分页数
*
* @param Templet $tpl
* @param Number $page
* @param Number $perpage
* @param Number $count
*/
function init_pages($page=1, $perpage, $count){
    function build_page($uri, $n, $sel = false, $alias = ''){
        $uri['page'] = 'page='.$n;
        $alias = $alias?$alias:$n;
        $url = 'http://'.$_SERVER['HTTP_HOST'].'/'.$_SERVER['PHP_SELF'].'?'.implode('&', $uri);
        return '<a href="'.$url.'">'.$alias.'</a>';
    }
    $perloop = 5;
    $page_count = ceil($count / $perpage);
    $out = '';
    if ($page_count>1){
        $uri = array();
        foreach ($_GET as $key=>$val){
            $uri[$key] = $key.'='.$val;
        }
        $page_prev = $page-1<1?1:$page-1;
        $page_next = $page+1<=$page_count?$page+1:$page_count;
        $loop_start = $page - $page % $perloop - 1;
        $loop_start = $loop_start>0?$loop_start:1;
        if ($page_count-$loop_start>=$perloop+3){
            $out.=build_page($uri, $page_prev, false, '上一页');
            $out.=build_page($uri, $page_next, false, '下一页');
            ($loop_start>1)&&$out.=build_page($uri, 1, false, '首页');
            for ($i=$loop_start;$i<=$loop_start+$perloop+1;$i++) $out.=build_page($uri, $i, $i==$page);
            $out .= '..';
            for ($i=$page_count-1;$i<=$page_count;$i++) $out.=build_page($uri, $i, $i==$page);
        }else {
            $loop_end = $page_count-($perloop+3)<1?1:$page_count-($perloop+3);
            for ($i=$page_count;$i>=$loop_end;$i--) $out = build_page($uri, $i, $i==$page).$out;
            ($loop_start>1&&$page_count>$perloop+3)&&$out=build_page($uri, 1, false, '首页').$out;
            $out=build_page($uri, $page_next, false, '下一页').$out;
            $out=build_page($uri, $page_prev, false, '上一页').$out;
        }
    }
    return $out;
}

调用示例:

$perpage = 10;
$count = 1000;
$page = isset($_GET['page'])?intval($_GET['page']):1;
echo init_pages($page, $perpage, $count);

模板化应用重复输出

在进行PHP开发中,或许我们常常会与foreach循环输出打交道。

举例说,PHP从数据库中读取10条最新新闻,存在数据库$news_list中,通常,在以往的PHP程序编写中,我们常常PHP代码襄套进HTML代码中,这样虽然方便,但是不利于直观的设计,不便于后期的维护。现在普遍使用模板引擎将代码与显示分离,比如Smarty。可以让我们将程序与前端代码分离。更直观,更易于后期维护更改。

以往我们喜欢这样:
<ul>
<?php
foreach($news_list as $news){
?>
<li><a href=”news.php?id=<?=$news['id']?>”><?=$news['title']?></a></li>
<?
}
?>
</ul>

现在,我们让他们分离:
<?php
$news_html = repeat_out($news_list, ‘<li><a href=”news.php?id={$id}”>{$title}</a></li>’);

function repeat_out($arr, $tpl){
     $html = ”;
     foreach ($array as $arr){
         if (is_array($arr)){
              foreach ($arr as $key=>$val){
                    $$key = $val;
              }
        }else {
               $value = $arr;
        }
        eval(“\$html.=\”{$tpl}\r\n\”;”);
    }
    return $html;
}
?>

仅供参考,不知道eval的效率要比直接字符串处理要低很多。下面是个Benchmark的结果:
———————–一万次eval和字符串处理的耗时结果——————–
10,000 Times ’string’ Time Spent(s):
0.0074520111084

10,000 Times ‘eval’ Time Spent(s):
0.101042032242

————————-百万次eval和字符串处理的耗时结果——————-
1,000,000 Times ‘String’ Time Spent(s):
0.780632972717

1,000,000 Times ‘eval’ Time Spent(s):
6.93951201439

不出所料的,eval由于要处理变量转化语句等,其耗时会超过直接字符串处理十倍左右。但是对于常见应用来说,这耗时其实也并不算高。

用PHP读取MS Word(.doc)中的文字

好像是去年吧,微软公布了自己的Office系列文件格式的规范,http://www.microsoft.com/interop/docs/OfficeBinaryFormats.mspx

这几天放假,有点空,就拿来研究了一下,看了一会儿就开始犯晕,云里雾里的,再结合到How_to_retrieve_text_from_a_binary_doc_file这篇文章,总算知道怎么从MS Word Binary Format中提取出文字了。下面将演示用PHP从.DOC文档中提取文字。

至于用处,不用我多说,搜索引擎能索引Word、QQ邮箱可以直接以HTML方式查看Word文档,etc…

首先,找个测试文档吧,以这个aaa.doc为例。

然后要说明的是,MS Word Binary File Format版本从97到2003没太大变化,对于我们提取文本无影响,这之间的格式都遵循着OLE规范的,不懂的可以看看MS的OLE规范。

用WinHex打开这个测试文章:
hex01
选中的区域(0×30)即为文档目录的入口,0×1C出开始的两个字节FE FF表明文档使用Little Endian,即高位在后低位在前,所以这里目录的开头为0×00000041 = 65。这个65是Sector ID。

什么是Sector?Sector是将文件按512字节进行划分,第一个512直接为Header,即文件头,第二个512为Sector #0,后面的类推。

所以,要定位到目录位置,偏移算法为 (65+1)x512=33792=0×8400,定位到文件0×8400位置:
hex01
本来可以按照相连的规律来读出所有的目录,但略显复杂,我们就直接判断每128个字节为一个目录,直到遇到开始两个字节为零时终止。
因为我们这里只是提取文字,所以只需要读取目录的名字(前64字节)和目录的入口(倒数第12个字节开头的4个字节),这里我们只说有用信息:
WordDocument目录位置 0×000000,即Sector #0
1Table位置 0×00001D,即Sector #29

定位到WordDocument目录位置(0+1)*0×200=0×200,偏移到0×3A0位置,可以找到Piece Table在1Table中的偏移位置:
hex01
这里为 0×2094 , 长度为0×0021

定位到Piece Table,0×2094+(0×1D+1)*0×200=0×5C94:hex01
截取0×21个字节的长度,即为选中部分,02表示这行数据是Piece Table(PT),1C 00 00 00表示数据长度=28,可以计算出PT的数量=(28-4)/12=2,那么,00 00 00 00表示第一个PT的位置偏移,E4 05 00 00表示第一个PT的结束同时也是第二个PT的开始,BF 0B 00 00为第二个PT的结束。80 00 00 08 00 00 00 00表示第一个PT的描述,80 00 00 18 00 00 00 00表示第二个PT的描述,从描述中可以确定文本编码是Unicode双字节还是CP,还能确定字符开始的起始基数。具体算法见后面源代码。这里得到的信息是,以Unicode编码存储,起始偏移为0×800。那么文字读取的位置即为0×0800+(0×00+0×0001)*0×0200+0×0000=0xA000。然后按照Unicode编码和Little Endian顺序,就可以一个一个的将文字读取出来了。

这里是测试地址[http://imethan.com/demo/msdoc.php]

源代码:msdoc

ecshop2.7去标题版权

去除底部版权请查看之前的文章“ecshop去版权办法

去除标题版权很简单,进入includes目录,用文本编辑器比如记事本,打开lib_main.php,搜索“page_title”(不含引号)字样,你会看到:

/* 初始化“页面标题”和“当前位置” */
$page_title = $GLOBALS['_CFG']['shop_title'] . ‘ – ‘ . ‘Powered by ECShop’;

把后面的去掉就行了。

注意:如果使用记事本编辑,由于编码问题,保存的时候不能直接保存,要选择另存为->编码那选择ANSI,否则将程序会出现问题。

PHP扩展入门之吸取QQ歌词

书接上篇博文讲的《PHP吸星大法之吸取QQ歌词》,为了学习PHP扩展的编写,我把这个用PHP扩展实现,与同学们共同学习。

QQ歌词的获取办法如果有不清楚的回到上一篇文章看一下。

这个扩展我取名叫qqlrc,只有一个函数 string fetch_lrc($author, $songname),该函数传递歌手和歌名信息,返回LRC歌词字符串。

进入PHP源码中的扩展目录:
$ cd php-5.2.10/ext 

编写qqlrc.proto函数列表如下:
fetch_lrc(char *author, char *songname)

使用ext_skel生成向导文件:
$ ./ext_skel –extname=qqlrc –proto=qqlrc.proto

进入qqlrc目录,修改config.m4,并用phpize预设编译环境:
去掉“PHP_ARG_WITH(qqlrc, for qqlrc support,
dnl Make sure that the comment is aligned:
[  --with-qqlrc             Include qqlrc support])”前的dnl
$ /usr/local/php/bin/phpize

打开qqlrc.c文件,找到函数fetch_lrc的地方。

添加三个函数replace_all(用于替换空格等)、get_songid(分析出获得数据中的歌曲id号)、get_lrc(获取指定歌曲的lrc数据),这三个函数如下:

static void replace_all(char* *o_str, char *r_str, char *t_str){
    char *pts = estrdup(*o_str);
    char result[strlen(*o_str)];
    result[0] = '\0';
    while(pts[0]!='\0'){
        char *tmp = estrndup(pts, strlen(r_str));
        if(strcmp(tmp, r_str)==0){
            strcat(result, t_str);
        }else {
            tmp = estrndup(pts, 1);
            strcat(result, tmp);
        }
        efree(tmp);
        pts++;
    }
    (*o_str) = strdup(result);
}
static void get_songid(char *json_str, char* *songid, int offset){
    int num = 0;
    char *flag_word = "song_id:\"";
    char tmpid[100];
    tmpid[0] = '\0';
    *songid = emalloc(1);
    (*songid)[0] = '\0';
    while(json_str[0]!='\0'){
        if(strcmp(estrndup(json_str, strlen(flag_word)), flag_word)==0){
            if(num==offset){
                char *tmp;
                tmp = estrndup(json_str+strlen(flag_word), 1);
                strcat(tmpid, tmp);
            }
            json_str+=strlen(flag_word)+1;
            num++;
        }else {
            if(strlen(tmpid)>0){
                char *c;
                c = estrndup(json_str, 1);
                if(strcmp(c, "\"")==0){
                    *songid = estrdup(tmpid);
                    return;
                }else{
                    strcat(tmpid, c);
                }
                efree(c);
            }
            json_str++;
        }
    }
}
static void get_lrc(char *xml, char* *lrc){
    char *xml2 = estrdup(xml);
    char *start_tag = "<![CDATA[";
    char *end_tag = "]]>";
    int start_pos = 0;
    int end_pos = 0;
    int pos = 0;
    *lrc = emalloc(1);
    (*lrc)[0] = '\0';
    while(xml2[0]!='\0'){
        if(start_pos==0&&strcmp(estrndup(xml2, strlen(start_tag)), start_tag)==0){
            start_pos = pos+strlen(start_tag);
        }else if(strcmp(estrndup(xml2, strlen(end_tag)), end_tag)==0){
            end_pos = pos;
        }
        xml2++;
        pos++;
    }
    if(start_pos!=end_pos)
        *lrc = estrndup(xml+start_pos, end_pos-start_pos-1);
}

主程序函数fetch_lrc(char *author, char *song_name, int offset):

/* {{{ proto  fetch_lrc()
   char *author, char *song, int offset) */
PHP_FUNCTION(fetch_lrc)
{
//判断传递的参数数量,必须三个都有
 if (ZEND_NUM_ARGS() != 3) {
  WRONG_PARAM_COUNT;
 }
 char *author;
 int author_len;
 char *songname;
 int songname_len;
 int offset;
//读出参数值,存入变量author songname songname_len,数据类型为ssl,即String String Long(PHP中没有int) 
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &author, &author_len, &songname, &songname_len, &offset)==FAILURE) RETURN_FALSE;
 char *keywords = (char*)emalloc(100);
//将songname和author合并,并将所有的空格替换为"+"
 sprintf(keywords, "%s %s", author, songname);
 replace_all(&keywords, " ", "+");
 char *search_url = (char*)emalloc(250);
//构建出QQ Music网页版的歌曲搜索Url
 sprintf(search_url, http://shopcgi.qqmusic.qq.com/fcgi-bin/shopsearch.fcg?value=%s&type=qry_song&out=json&page_no=1&page_record_num=10&", keywords);
//新建PHP Stream,用于读取seach_url的返回值 
 php_stream *qqstream = php_stream_open_wrapper(search_url, "r", REPORT_ERRORS, NULL);
 if(qqstream){
     char json_buffer[4096];
     json_buffer[0] = '\0';
     while(!php_stream_eof(qqstream)){
         char buff[1024];
         if(php_stream_gets(qqstream, buff, sizeof(buff))){
             strcat(json_buffer, buff);
         }
     }
     if(strlen(json_buffer)>0){
         char *song_id;
        //返回格式为JSON,用笨方法来读取吧,offset是指读取Songid在歌曲列表中偏移
         get_songid(json_buffer, &song_id, offset);
            if(strlen(song_id)>0){
                char *song_cid = estrndup(song_id+strlen(song_id)-2, 2);
               //获得了songid,组织lrc歌词的URL
                char xml_url[150];
                sprintf(xml_url, "http://music.qq.com/miniportal/static/lyric/%d/%s.xml", atoi(song_cid), song_id);
                php_stream *xml_stream = php_stream_open_wrapper(xml_url, "r", REPORT_ERRORS, NULL);
                if(xml_stream){
                    char xml_buff[5120];
                    xml_buff[0] = '\0';
                    while(!php_stream_eof(xml_stream)){
                        char buff[1024];
                        if(php_stream_gets(xml_stream, buff, sizeof(buff))){
                            strcat(xml_buff, buff);
                        }
                    }
                    php_stream_close(xml_stream);
                    if(strlen(xml_buff)>0){
                        char *lrc;
                        //读出XML中的LRC数据,还是用笨方法
                        get_lrc(xml_buff, &lrc);
                        //函数返回值
                        RETURN_STRING(lrc, 1);                   
                    }
                    RETURN_FALSE;
                }
                RETURN_FALSE;
            }
            RETURN_FALSE;
     }   
     php_stream_close(qqstream);
     RETURN_FALSE;
 }
 RETURN_FALSE; 
}

附上测试地址:QQ Music LRC Demo
最后附上源码打包(Linux+PHP5.1 Above):qqlrc.tar.gz