最近公司用到了微信公众平台,所以研究了一下微信公众号的开发技术,总体来说比较简单,结合现有的平台核技术,实现起来非常方便。
首先先来了解一下微信公众平台。
“微信,是一个生活方式”,这是微信的自我评价,是不是觉得如果那天不在朋友圈里分享一下自己的最新状态,并且收到几个赞和评价的话,会觉得空虚寂寞呢?它实实在在的改变了我们的生活方式。
“微信,也是一个生意方式”,在微信成为我们日常必备之app的同时,它同样具备巨大的的商业或许不应该称为潜力,因为有很多人已经获利,名人们在微信上开设公众账户来吸金,商家来做推广,服务行业借此拓展渠道,甚至微信已经支持支付了, 还有越来越的自媒体在微信平台涌现出来。这篇文章就是介绍如何快速的成为公众平台开发者,由于个人只能申请订阅号,因此本文是以订阅号为例。关于订阅号和服务号的区别,请参见微信公众平台服务号、订阅号的相关说明。
从微信用户角度简单来说:
订阅号主要用于信息辐射,典型的如各家新闻媒体。服务号主要由于自助服务,典型的如招商银行。
申请公众平台账户关于微信公众帐号注册的步骤就不再多说了,可以找到大量的图文教程。
帐号注册成功之后,需要验证自己的服务器,如果你没有自己的服务器,那可以用新浪SAE或者百度BAE,本文采用的是新浪SAE平台来搭建服务器。
注册过程略,使用新浪SAE创建应用,可以选择应用开发框架,选项中有比较热门的开发框架,选择微信公众平台phpSDK,点击后跳转到介绍页面,点击安装框架,系统会生成一个搭建好的微信公众平台应用,为了方便开发,我们可以使用svn来管理此应用代码,关于svn搭建可参见sae代码部署手册。
使用新浪SAE是比较方便的,如果我们有自己的服务器,可以把代码clone到自己的服务器上,下面来看一下代码
首先定义一个Wechat的基类
1 <?php 2 /** 3 * 微信公众平台 PHP SDK 4 * 5 * @author hanc <[email protected]> 6 */ 7 8 /** 9 * 微信公众平台处理类 10 */ 11 class Wechat { 12 13 /** 14 * 调试模式,将错误通过文本消息回复显示 15 * 16 * @var boolean 17 */ 18 PRivate $debug; 19 20 /** 21 * 以数组的形式保存微信服务器每次发来的请求 22 * 23 * @var array 24 */ 25 private $request; 26 27 /** 28 * 初始化,判断此次请求是否为验证请求,并以数组形式保存 29 * 30 * @param string $token 验证信息 31 * @param boolean $debug 调试模式,默认为关闭 32 */ 33 public function __construct($token, $debug = FALSE) { 34 if ($this->isValid() && $this->validateSignature($token)) { 35 exit($_GET['echostr']); 36 } 37 38 $this->debug = $debug; 39 set_error_handler(array(&$this, 'errorHandler')); 40 // 设置错误处理函数,将错误通过文本消息回复显示 41 42 $xml = (array) simplexml_load_string($GLOBALS['HTTP_RAW_POST_DATA'], 'SimpleXMLElement', LIBXML_NOCDATA); 43 44 $this->request = array_change_key_case($xml, CASE_LOWER); 45 // 将数组键名转换为小写,提高健壮性,减少因大小写不同而出现的问题 46 } 47 48 /** 49 * 判断此次请求是否为验证请求 50 * 51 * @return boolean 52 */ 53 private function isValid() { 54 return isset($_GET['echostr']); 55 } 56 57 /** 58 * 判断验证请求的签名信息是否正确 59 * 60 * @param string $token 验证信息 61 * @return boolean 62 */ 63 private function validateSignature($token) { 64 $signature = $_GET['signature']; 65 $timestamp = $_GET['timestamp']; 66 $nonce = $_GET['nonce']; 67 68 $signatureArray = array($token, $timestamp, $nonce); 69 sort($signatureArray); 70 71 return sha1(implode($signatureArray)) == $signature; 72 } 73 74 /** 75 * 获取本次请求中的参数,不区分大小 76 * 77 * @param string $param 参数名,默认为无参 78 * @return mixed 79 */ 80 protected function getRequest($param = FALSE) { 81 if ($param === FALSE) { 82 return $this->request; 83 } 84 85 $param = strtolower($param); 86 87 if (isset($this->request[$param])) { 88 return $this->request[$param]; 89 } 90 91 return NULL; 92 } 93 94 /** 95 * 用户关注时触发,用于子类重写 96 * 97 * @return void 98 */ 99 protected function onSubscribe() {}100 101 /**102 * 用户取消关注时触发,用于子类重写103 *104 * @return void105 */106 protected function onUnsubscribe() {}107 108 /**109 * 用户自动上报地理位置触发,用于子类重写110 *111 * @return void112 */113 protected function onAutoloaction() {}114 115 /**116 * 用户点击菜单时触发,用于子类重写117 *118 * @return void119 */120 protected function onClick() {}121 122 /**123 * 用户点击跳转链接时触发,用于子类重写124 *125 * @return void126 */127 protected function onView() {}128 129 /**130 * 收到文本消息时触发,用于子类重写131 *132 * @return void133 */134 protected function onText() {}135 136 /**137 * 收到图片消息时触发,用于子类重写138 *139 * @return void140 */141 protected function onImage() {}142 143 /**144 * 收到地理位置消息时触发,用于子类重写145 *146 * @return void147 */148 protected function onLocation() {}149 150 /**151 * 收到链接消息时触发,用于子类重写152 *153 * @return void154 */155 protected function onLink() {}156 /**157 * 收到语音消息时触发,用于子类重写158 *159 * @return void160 */161 protected function onVoice() {}162 163 /**164 * 收到未知类型消息时触发,用于子类重写165 *166 * @return void167 */168 protected function onUnknown() {}169 170 /**171 * 回复文本消息172 *173 * @param string $content 消息内容174 * @param integer $funcFlag 默认为0,设为1时星标刚才收到的消息175 * @return void176 */177 protected function responseText($content, $funcFlag = 0) {178 exit(new TextResponse($this->getRequest('fromusername'), $this->getRequest('tousername'), $content, $funcFlag));179 }180 181 /**182 * 回复音乐消息183 *184 * @param string $title 音乐标题185 * @param string $description 音乐描述186 * @param string $musicUrl 音乐链接187 * @param string $hqMusicUrl 高质量音乐链接,Wi-Fi 环境下优先使用188 * @param integer $funcFlag 默认为0,设为1时星标刚才收到的消息189 * @return void190 */191 protected function responseMusic($title, $description, $musicUrl, $hqMusicUrl, $funcFlag = 0) {192 exit(new MusicResponse($this->getRequest('fromusername'), $this->getRequest('tousername'), $title, $description, $musicUrl, $hqMusicUrl, $funcFlag));193 }194 195 /**196 * 回复图文消息197 * @param array $items 由单条图文消息类型 NewsResponseItem() 组成的数组198 * @param integer $funcFlag 默认为0,设为1时星标刚才收到的消息199 * @return void200 */201 protected function responseNews($items, $funcFlag = 0) {202 exit(new NewsResponse($this->getRequest('fromusername'), $this->getRequest('tousername'), $items, $funcFlag));203 }204 /**205 * 回复语音识别消息206 * @param array $recognition 系统接收到语音后识别的字符串207 * @param integer $funcFlag 默认为0,设为1时星标刚才收到的消息208 * @return void209 */210 protected function responseVoice($recognition, $funcFlag = 0) {211 exit(new TextResponse($this->getRequest('fromusername'), $this->getRequest('tousername'), $recognition, $funcFlag));212 }213 214 /**215 * 分析消息类型,并分发给对应的函数216 *217 * @return void218 */219 public function run() {220 switch ($this->getRequest('msgtype')) {221 222 case 'event':223 switch ($this->getRequest('event')) {224 225 case 'subscribe':226 $this->onSubscribe();227 break;228 229 case 'unsubscribe':230 $this->onUnsubscribe();231 break;232 233 case 'LOCATION':234 $this->onAutoloaction();235 break;236 237 case 'CLICK':238 $this->onClick();239 break;240 241 case 'VIEW':242 $this->onView();243 b