书接上篇博文讲的《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