记录一个php的bug

做一个公司后台开发的时候,一切都先在我本机上调试好了,把php传上去,数据库建好后,ajax拖数据时发现老是500错误。百思不得其解。

后来一步一步调试。发现这个公司提供的阿里云服务器装的php版本是5.1的,它的mysql_fetch_row不支持直接在后面加[num]进行数组索引。

先用一个变量存出来,再做数组索引才行,也就是:

 

$temp = mysql_fetch_row(mysql_query(“select * from sometable limit 1”));

$somekey = $temp[0];

而不能直接

$temp = mysql_fetch_row(mysql_query(“select * from sometable limit 1”))[0];

// 在5.5版本上这样是没有问题的

 

PHP – CURl与微信的那些事儿

前几天的微信开发中,由于微信服务器存储的所有图片、语音等资源都只有3天的期限,在某些不能满足需求的情况下,很尴尬。所以需要一个第三方文件服务器来做存储。
1. 从微信服务器下载资源
微信js接口中,上传图片或者上传音频的步骤:
i. 调用wx.chooseImage/wx.startRecord(),wx.stopRecord()接口,得到localId
示例:(只列举图片的)
wx.chooseImage({
success: function (res) {
images.localId = res.localIds;
alert(‘已选择 ‘ + res.localIds.length + ‘ 张图片’);
}
});
ii.调用wx.uploadImage()/wx.uploadVoice接口,将localId传入得到media_id
示例:
document.getElementById(‘uploadImage’).onclick = function () {
if (images.localId.length == 0) {
alert(‘请先使用 chooseImage 接口选择图片’);
return;
}
var i = 0, length = images.localId.length;
images.serverId = [];
function upload() {
wx.uploadImage({
localId: images.localId[i],
success: function (res) {
i++;
alert(‘已上传:’ + i + ‘/’ + length);
images.serverId.push(res.serverId);
if (i < length) {
upload();
}
},
fail: function (res) {
alert(JSON.stringify(res));
}
});
}
upload();
};
然后,我们需要利用得到的media_id通知服务器,通过curl发起get请求,将内容下载至服务器对应的文件夹中。
http请求方式: GET
http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
请求示例(示例为通过curl命令获取多媒体文件)
curl -I -G “http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID”
其中access_token通过appid和appsecret由服务器脚本获取并缓存(我存在数据库中做的缓存,记录了相应时间,判断是否超过expires)
我们讲media_id发给相应服务器脚本,然后利用curl命令,获取文件,并存至服务器。
这里提到curl,下面是详细的使用说明。

cURL 是一个利用URL语法规定来传输文件和数据的工具,支持很多协议,如HTTP、FTP、TELNET等。最爽的是,PHP也支持 cURL 库。将介绍 cURL 的一些高级特性,以及在PHP中如何运用它。

1.初始化

curl_init();

2.设置变量

curl_setopt();最为重要,一切玄妙均在此。有一长串cURL参数可供设置,它们能指定URL请求的各个细节。要一次性全部看完并理解可能比较困难,所以今天我们只试一下那些更常用也更有用的选项。

3.执行并获取结果

curl_exec();

4.释放cURL句柄

curl_close();

5.get请求文件示例
$hander = curl_init();
$fp = fopen($filename,’wb’); //$filename为要存储的文件名,生成存放获取文件的句柄
curl_setopt($hander,CURLOPT_URL,$url); //设置要获取文件的url,即上面提到的http请求方式中的组合链接
curl_setopt($hander,CURLOPT_FILE,$fp); //放置传送的输出文件
curl_setopt($hander,CURLOPT_HEADER,0); //设置header
curl_setopt($hander,CURLOPT_FOLLOWLOCATION, 1); //设置这个选项为一个非零值(像 ‘Location: ‘)的头,服务器会把它当做HTTP头的一部分发送(注意这是递归的,PHP将发送形如 ‘Location: ‘的头)。
curl_setopt($hander,CURLOPT_TIMEOUT,60); //设置一个长整形数,作为最大延续多少秒
curl_exec($hander);
curl_close($hander);
fclose($fp);

2.上传资源到微信服务器

由于从微信服务器下载的音频资源是amr格式的(这个玩意儿真的比mp3小多了,利于数据传输,怪不得微信做的好),没办法直接通过网页播放,还想的是需要时传回微信服务器获得media_id再通过wx.downloadVoice()接口来获取到localid用wx.playVoice()接口来播放就成…是不是很麻烦… (我也觉得)

然后直接说下curl的post文件方法,不具体放这个怎么上传了,跟前面下载的思路是一样的,留给大家思考。实在需要的可以发邮件我要代码。

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //不回显返回的http响应数据,存$output中
// post数据
curl_setopt($ch, CURLOPT_POST, 1);
// 设置post的变量
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); //设置post的数据

$output = curl_exec($ch);

curl_close($ch);

注意一点就是文件数据php分了5.5以后以及5.5以前的发送方法。。

详情参照我上一篇blog。

那这篇就说到这儿吧。

记录一次微信开发

这两天遇到的坑比较多,可能会导致内容有点杂,但是还是想记录一下。

首先,上一篇文章中提到了完整的签名过程,在后台写完脚本后,调试下不会有啥大问题:
1. 确保jsApiList里面有接口信息,不要为空
我当时想着先调试,就先不写调用的接口,以至于忽略了官方文档里面的要求直接就是:必填。导致我一直失败,而且不得知其内容。
2. 有时候开了Debug也没有反应,可能是服务器的原因。所以不要着急,过一会儿再试就好。

然后,由于存在微信服务器中的所有文件只有3天的有效时间,显然不满足客户需求,所以要做一个中转服务器,然后说下做中转服务器中遇到的坑。
两次迁移服务器

我在新浪云上做的测试(边写边保存边调试真的很方便),发现数据只能在storage中写入,当然这个并不碍事,但是为了便于维护管理,加上之前跟客户商量好了要用阿里云,所以就决定先转移服务器了。
我先在阿里云上买了轻云服务器经济版(Linux,PHP版),各个接口调完后,发现因为safe_mode不能更改,给客服打了电话,让换ECS服务器自己配置环境,好吧,就当体验下真正的阿里云服务器咯,加上客服跟我说5天内可退全款,不得不赞一句,退的很快,一退了马上就到账户中了,可以转出支付宝。换ECS最低配的也得自己补贴钱,也得受咯,毕竟之前就说好半年租用的费用了,也添的不多啦。

阿里云的配置过程

在阿里云买好Linux服务器后,开始进行安装环境,这个过程其实可以略,只要有耐心,看下说明文档啥的,版本不装错就行了,以前我用的Apache+PHP,遂这次体验了一下NGINX,据说高并发的时候性能是Apache的10倍,NGINX与Apache从配置上来说唯一的不同就在,PHP(9000)服务也有了一个端口监听,与NGINX(80)之间是有进程通信的,而用Apache只会看到一个端口。

Nginx的域名绑定

/alidata/server/nginx/conf/vhosts(不同环境可能路径不同哈)中新建一个conf,做域名映射就行。

server
{
listen       80;
server_name app.un1q.me;             #绑定域名
index index.htm index.html index.php;      #默认文件
root /home/www/111cn.net;               #网站根目录
include location.conf;                            #调用其他规则,也可去除
}

当然不要忘记了与PHP 的映射

location ~ .*\.(php|php5)?$

{

#fastcgi_pass unix:/tmp/php-cgi.sock;

fastcgi_pass 127.0.0.1:9000;

fastcgi_index index.php;

include fastcgi.conf;
}

 PHP- CURL实现Get及Post

前面提到了,因为需要一个比较长的保存期,所以不得不在第三方服务器存储,而如果要通过第三方接口来下载或者上传文件都是需要高级开发者接口的,这里面的坑就略多了…

1、需要获取access_token再进行上传文件和下载文件的请求,坑的比较惨的是因为我使用的是PHP5.5以上版本,所以上传文件不再是原来的’file’=>’@/home/user/test.png’ 通过@来定义绝对路径了。而建议使用 CURLFile(realpath($filepath));,这是我一直没找到出错原因的一个点。坑了很久。

2、因为上传文件时需要得到一个返回的media_id来返回给前端以调用downloadVoice接口获取localid来播放Voice,返回的数据是json, 所以用json_decode($content)先解析下,但是注意加json_decode($content,true); 这样才能够得到数组格式…否则是object…

大概就讲下这些我遇到的坑,这样只是做一个记录,由于列举的是一个又一个很大的坑,所以我之后会写文章一个一个说。

微信js-sdk使用指南

这两天要帮一个客户做一个基于微信的水果电商增值服务,需要使用到微信的接口,比如录音,上传图片等等。

在淘宝花13块搞了个公众号(懒得一比,活该被赚钱),开始看JSSDK的接入方式,网上一堆文章说这个的,但是没有一个让我感觉特别清楚的,所以我打算自己探索了自己来写。。

我们先看看config中里的传入参数。

wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: ”, // 必填,公众号的唯一标识
timestamp: 0, // 必填,生成签名的时间戳
nonceStr: ”, // 必填,生成签名的随机串
signature: ”,// 必填,签名,见附录1
jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});

现在我们来一步一步分解,到底怎么才能配置成功。

一. 获取access_token
1、为了保密appsecrect,第三方需要一个access_token获取和刷新的中控服务器。而其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则会造成access_token覆盖而影响业务;

2、目前access_token的有效期通过返回的expire_in来传达,目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。在刷新过程中,中控服务器对外输出的依然是老access_token,此时公众平台后台会保证在刷新短时间内,新老access_token都可用,这保证了第三方业务的平滑过渡;

3、access_token的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新access_token的接口,这样便于业务服务器在API调用获知access_token已超时的情况下,可以触发access_token的刷新流程。

对此,我想的方法是,获取了access_token后,我在后台新建一个数据表,用来存储access_token和expire_in,借此判断是否还在有效期内,避免重复申请刷新access_token后产生冲突。
方法: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=your_app_id&secret=your_app_secret
your_app_id和yor_app_secret都可以在公众平台的开发者中心里面找到。
贴一段实现的php代码:
$url = ‘https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=your_app_id&secret=your_app_secret’;

$html = file_get_contents($url);
 $elems = json_decode($html,TRUE);
 echo $elems['access_token'];
 echo $eles['expires_in'];
然后就可以把这两个值(access_token,time()+expires_in)存入你的数据库中,再设计一个判断是否需要再获取ak_token的函数即可。

二.获取ticket

ticket的方法与步骤一差不多,也要做一个缓存,我用同一个表存的,把时间和Ticket都缓存下来再处理。
方法:
用第一步拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi

三.生成signature

签名算法

签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
即signature=sha1(string1)。 示例:

  • noncestr=Wm3WZYTPz0wzccnW
  • jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
  • timestamp=1414587457
  • url=http://mp.weixin.qq.com?params=value

步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序,即j,n,t,u)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:

jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW&timestamp=1414587457&url=http://mp.weixin.qq.com?params=value

按照这个格式连接就行。

步骤2. 对string1进行sha1签名,得到signature:

0f9de62fce790f9a083d5c99e95740ceb90c27ed

可以在后台先用这个示例尝试是否得到相同结果,调试出来了再换成系统生成的。

注意事项

  1. 签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
  2. 签名用的url必须是调用JS接口页面的完整URL。

 

至此,我们已经得到了所有wx.config里面所需要的参数。
想要源代码(PHP)的同学可以给我发邮件索取。

后续问题及处理方法:
1. invalid signature
一定要注意通过固定链接接入,本来我是传入的动态链接(即window.location.href,并去除#后的东西),结果老是报错,所以后来就固定到了一个链接(不要在用微信分享等,现在这个问题也是蛮扯的,在分享后微信加了后缀后就会爆这个错了。希望微信官方之后会解决这个问题。。)

获取积分降序的排名

操刀启德成都定制嗒一个页面游戏,涉及排行版,用的php+mysql写的后台,mysql没有rank()和row_number()这样的方法可以用,而我的sql语句基础也没有多好,查阅了很多资料。最后给一个最终的实现。

 

$sql = “SELECT rowno FROM (SELECT phone,punch,(@rowno:=@rowno+1) AS rowno FROM llg,(SELECT (@rowno:=0)) b ORDER BY punch DESC) c WHERE phone=$phone”;

其中,phone,punch是游戏中留的玩家电话(作为主键)和玩家游戏的打击次数(类似打地鼠的)。 rowno定义排序号,llg为我的表名。

看官可以根据自己的需求更改使用。

php mysql 处理数据

最近在写上菜了的后台,对php操作mysql也是刚开头,以后会做的更好。

说说处理数据的方法,mysql_fetch_row(result)及mysql_fetch_array(result),mysql_fetch_object(result)
1.mysql_fetch_row(result)
用mysql_fetch_row()的方法返回的一行数据(元祖)储存在一个数组的单元中,偏移量从 0 开始。例如你要返回第二个字段的数据,就应该写成$row[1]而不是$row[2]。 这跟PHP定义的数组使用方法一样。

2.mysql_fetch_array(result)
mysql_fetch_array() 和 mysql_fetch_row() 类似,返回的数据也是存储在一个数组中,有所区别的是我们不仅可以用偏移量来访问该数组也可以用字段名来访问该数组,例如$row[‘name’]。

3.mysql_fetch_object(result)
返回的不再是数组而是一个对象,我们应该用对对象的操作方法来读取数据,例如:$row->depart。

js接收PHP数组

做到菜单显示,从后台送回的数据都是数组的,只是用echo是不行了,所以才用了json。

用法:

<?php 
$a    =array('1','2','3');
?>
<script language="javascript">
var obj    =eval('<?php echo json_encode($a);?>');
alert(obj[0]);
alert(obj[1]);
alert(obj[2]);
</script>