微信公衆號開發總結

最近公司用到了微信公衆平臺,因此研究了一下微信公衆號的開發技術,整體來講比較簡單,結合現有的平臺核技術,實現起來很是方便。php

首先先來了解一下微信公衆平臺。html

「微信,是一個生活方式」 ,這是微信的自我評價,是否是以爲若是那天不在朋友圈裏分享一下本身的最新狀態, 
而且收到幾個贊和評價的話,會以爲空虛寂寞呢?它實實在在的改變了咱們的生活方式。git

「 微信,也是一個生意方式 」,在微信成爲咱們平常必備之app的同時,它一樣具有巨大的的商業 
或許不該該稱爲潛力,由於有不少人已經獲利,名人們在微信上開設公衆帳戶來吸金,商家來作推廣, 
服務行業藉此拓展渠道,甚至微信已經支持支付了, 還有愈來愈的自媒體在微信平臺涌現出來。 

這篇文章就是介紹如何快速的成爲公衆平臺開發者,因爲我的只能申請訂閱號,所以本文是以訂閱號爲例。
關於訂閱號和服務號的區別,請參見 微信公衆平臺服務號、訂閱號的相關說明數組

從微信用戶角度簡單來講:服務器

訂閱號 主要用於信息輻射,典型的如各家 新聞媒體 。 
服務號 主要因爲自助服務,典型的如 招商銀行 。微信

申請公衆平臺帳戶

  • 按照提示激活郵箱

  • 上傳我的照片,須要有清晰的身份證照片

  • 選擇公衆帳戶的類型,對於我的帳戶只能選擇 訂閱號

  • 最後你會看到本身帳戶的全部信息,請上傳帳號的頭像,不然沒法完成開發者的申請

  • 等待審覈經過,這個過程大約須要2~3天,當你收到以下通知,那麼恭喜你,你已經成功的申請到了微信公衆帳戶了

關於微信公衆賬號註冊的步驟就再也不多說了,能夠找到大量的圖文教程。app

賬號註冊成功以後,須要驗證本身的服務器,若是你沒有本身的服務器,那能夠用新浪SAE或者百度BAE,本文采用的是新浪SAE平臺來搭建服務器。微信公衆平臺

註冊過程略,使用新浪SAE建立應用,能夠選擇應用開發框架,選項中有比較熱門的開發框架,選擇微信公衆平臺phpSDK,點擊後跳轉到介紹頁面,點擊安裝框架,系統會生成一個搭建好的微信公衆平臺應用,爲了方便開發,咱們可使用svn來管理此應用代碼,關於svn搭建可參見sae代碼部署手冊框架

使用新浪SAE是比較方便的,若是咱們有本身的服務器,能夠把代碼clone到本身的服務器上,下面來看一下代碼curl

首先定義一個Wechat的基類

  1 <?php
  2 /**
  3  * 微信公衆平臺 PHP SDK
  4  *
  5  * @author hanc <congcongsky2010@gmail.com>
  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 void
105      */
106     protected function onUnsubscribe() {}
107     
108     /**
109      * 用戶自動上報地理位置觸發,用於子類重寫
110      *
111      * @return void
112      */
113     protected function onAutoloaction() {}
114       
115     /**
116      * 用戶點擊菜單時觸發,用於子類重寫
117      *
118      * @return void
119      */
120     protected function onClick() {}
121     
122     /**
123      * 用戶點擊跳轉連接時觸發,用於子類重寫
124      *
125      * @return void
126      */
127     protected function onView() {}
128 
129     /**
130      * 收到文本消息時觸發,用於子類重寫
131      *
132      * @return void
133      */
134     protected function onText() {}
135 
136     /**
137      * 收到圖片消息時觸發,用於子類重寫
138      *
139      * @return void
140      */
141     protected function onImage() {}
142 
143     /**
144      * 收到地理位置消息時觸發,用於子類重寫
145      *
146      * @return void
147      */
148     protected function onLocation() {}
149 
150     /**
151      * 收到連接消息時觸發,用於子類重寫
152      *
153      * @return void
154      */
155     protected function onLink() {}
156     /**
157      * 收到語音消息時觸發,用於子類重寫
158      *
159      * @return void
160      */
161     protected function onVoice() {}
162 
163     /**
164      * 收到未知類型消息時觸發,用於子類重寫
165      *
166      * @return void
167      */
168     protected function onUnknown() {}
169 
170     /**
171      * 回覆文本消息
172      *
173      * @param  string  $content  消息內容
174      * @param  integer $funcFlag 默認爲0,設爲1時星標剛纔收到的消息
175      * @return void
176      */
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 void
190      */
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 void
200      */
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 void
209      */
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 void
218      */
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               break;
244 
245           }
246           break;
247 
248         case 'text':
249           $this->onText();
250           break;
251 
252         case 'image':
253           $this->onImage();
254           break;
255 
256         case 'location':
257           $this->onLocation();
258           break;
259 
260         case 'link':
261           $this->onLink();
262           break;
263 
264         case 'voice':
265           $this->onVoice();
266           break;
267           
268         default:
269           $this->onUnknown();
270           break;
271 
272       }
273     }
274 
275     /**
276      * 自定義的錯誤處理函數,將 PHP 錯誤經過文本消息回覆顯示
277      * @param  int $level   錯誤代碼
278      * @param  string $msg  錯誤內容
279      * @param  string $file 產生錯誤的文件
280      * @param  int $line    產生錯誤的行數
281      * @return void
282      */
283     protected function errorHandler($level, $msg, $file, $line) {
284       if ( ! $this->debug) {
285         return;
286       }
287 
288       $error_type = array(
289         // E_ERROR             => 'Error',
290         E_WARNING           => 'Warning',
291         // E_PARSE             => 'Parse Error',
292         E_NOTICE            => 'Notice',
293         // E_CORE_ERROR        => 'Core Error',
294         // E_CORE_WARNING      => 'Core Warning',
295         // E_COMPILE_ERROR     => 'Compile Error',
296         // E_COMPILE_WARNING   => 'Compile Warning',
297         E_USER_ERROR        => 'User Error',
298         E_USER_WARNING      => 'User Warning',
299         E_USER_NOTICE       => 'User Notice',
300         E_STRICT            => 'Strict',
301         E_RECOVERABLE_ERROR => 'Recoverable Error',
302         E_DEPRECATED        => 'Deprecated',
303         E_USER_DEPRECATED   => 'User Deprecated',
304       );
305 
306       $template = <<<ERR
307 PHP 報錯啦!
308 
309 %s: %s
310 File: %s
311 Line: %s
312 ERR;
313 
314       $this->responseText(sprintf($template,
315         $error_type[$level],
316         $msg,
317         $file,
318         $line
319       ));
320     }
321 
322   }
323 
324   /**
325    * 用於回覆的基本消息類型
326    */
327   abstract class WechatResponse {
328 
329     protected $toUserName;
330     protected $fromUserName;
331     protected $funcFlag;
332 
333     public function __construct($toUserName, $fromUserName, $funcFlag) {
334       $this->toUserName = $toUserName;
335       $this->fromUserName = $fromUserName;
336       $this->funcFlag = $funcFlag;
337     }
338 
339     abstract public function __toString();
340 
341   }
342 
343 
344   /**
345    * 用於回覆的文本消息類型
346    */
347   class TextResponse extends WechatResponse {
348 
349     protected $content;
350 
351     protected $template = <<<XML
352 <xml>
353   <ToUserName><![CDATA[%s]]></ToUserName>
354   <FromUserName><![CDATA[%s]]></FromUserName>
355   <CreateTime>%s</CreateTime>
356   <MsgType><![CDATA[text]]></MsgType>
357   <Content><![CDATA[%s]]></Content>
358   <FuncFlag>%s<FuncFlag>
359 </xml>
360 XML;
361 
362     public function __construct($toUserName, $fromUserName, $content, $funcFlag = 0) {
363       parent::__construct($toUserName, $fromUserName, $funcFlag);
364       $this->content = $content;
365     }
366 
367     public function __toString() {
368       return sprintf($this->template,
369         $this->toUserName,
370         $this->fromUserName,
371         time(),
372         $this->content,
373         $this->funcFlag
374       );
375     }
376 
377   }
378 
379   /**
380    * 用於回覆的音樂消息類型
381    */
382   class MusicResponse extends WechatResponse {
383 
384     protected $title;
385     protected $description;
386     protected $musicUrl;
387     protected $hqMusicUrl;
388 
389     protected $template = <<<XML
390 <xml>
391   <ToUserName><![CDATA[%s]]></ToUserName>
392   <FromUserName><![CDATA[%s]]></FromUserName>
393   <CreateTime>%s</CreateTime>
394   <MsgType><![CDATA[music]]></MsgType>
395   <Music>
396     <Title><![CDATA[%s]]></Title>
397     <Description><![CDATA[%s]]></Description>
398     <MusicUrl><![CDATA[%s]]></MusicUrl>
399     <HQMusicUrl><![CDATA[%s]]></HQMusicUrl>
400   </Music>
401   <FuncFlag>%s<FuncFlag>
402 </xml>
403 XML;
404 
405     public function __construct($toUserName, $fromUserName, $title, $description, $musicUrl, $hqMusicUrl, $funcFlag) {
406       parent::__construct($toUserName, $fromUserName, $funcFlag);
407       $this->title = $title;
408       $this->description = $description;
409       $this->musicUrl = $musicUrl;
410       $this->hqMusicUrl = $hqMusicUrl;
411     }
412 
413     public function __toString() {
414       return sprintf($this->template,
415         $this->toUserName,
416         $this->fromUserName,
417         time(),
418         $this->title,
419         $this->description,
420         $this->musicUrl,
421         $this->hqMusicUrl,
422         $this->funcFlag
423       );
424     }
425 
426   }
427 
428 
429   /**
430    * 用於回覆的圖文消息類型
431    */
432   class NewsResponse extends WechatResponse {
433 
434     protected $items = array();
435 
436     protected $template = <<<XML
437 <xml>
438   <ToUserName><![CDATA[%s]]></ToUserName>
439   <FromUserName><![CDATA[%s]]></FromUserName>
440   <CreateTime>%s</CreateTime>
441   <MsgType><![CDATA[news]]></MsgType>
442   <ArticleCount>%s</ArticleCount>
443   <Articles>
444     %s
445   </Articles>
446   <FuncFlag>%s<FuncFlag>
447 </xml>'
448 XML;
449 
450     public function __construct($toUserName, $fromUserName, $items, $funcFlag) {
451       parent::__construct($toUserName, $fromUserName, $funcFlag);
452       $this->items = $items;
453     }
454 
455     public function __toString() {
456       return sprintf($this->template,
457         $this->toUserName,
458         $this->fromUserName,
459         time(),
460         count($this->items),
461         implode($this->items),
462         $this->funcFlag
463       );
464     }
465 
466   }
467 
468 
469   /**
470    * 單條圖文消息類型
471    */
472   class NewsResponseItem {
473 
474     protected $title;
475     protected $description;
476     protected $picUrl;
477     protected $url;
478 
479     protected $template = <<<XML
480 <item>
481   <Title><![CDATA[%s]]></Title>
482   <Description><![CDATA[%s]]></Description>
483   <PicUrl><![CDATA[%s]]></PicUrl>
484   <Url><![CDATA[%s]]></Url>
485 </item>
486 XML;
487 
488     public function __construct($title, $description, $picUrl, $url) {
489       $this->title = $title;
490       $this->description = $description;
491       $this->picUrl = $picUrl;
492       $this->url = $url;
493     }
494 
495     public function __toString() {
496       return sprintf($this->template,
497         $this->title,
498         $this->description,
499         $this->picUrl,
500         $this->url
501       );
502     }
503 
504   }

此基類我稍做了更改,包含了能實現的微信全部的接口,經過繼承 `Wechat` 類進行擴展,例如經過重寫 `onSubscribe()` 等方法響應關注等請求,下面是實現的示例代碼:

  1 <?php
  2 /**
  3  * 微信公衆平臺 PHP SDK 示例文件
  4  *
  5  * @author hanc <congcongsky2010@gmail.com>
  6  */
  7 
  8   require('src/Wechat.php');
  9 
 10   /**
 11    * 微信公衆平臺演示類
 12    */
 13   class MyWechat extends Wechat {
 14 
 15     /**
 16      * 用戶關注時觸發,回覆「歡迎關注」
 17      *
 18      * @return void
 19      */
 20     protected function onSubscribe() {
 21       $this->responseText('歡迎關注韓聰的微信號');
 22     }
 23 
 24     /**
 25      * 用戶取消關注時觸發
 26      *
 27      * @return void
 28      */
 29     protected function onUnsubscribe() {
 30       // 「悄悄的我走了,正如我悄悄的來;我揮一揮衣袖,不帶走一片雲彩。」
 31     }
 32 
 33     /**
 34      * 用戶自動上報地理位置時觸發
 35      *
 36      * @return void
 37      */
 38     protected function onAutoloaction() {
 39 
 40       $this->responseText('您的地理位置爲:' . $this->getRequest('Latitude') . ',' . $this->getRequest('Longitude'));
 41     }
 42       
 43     /**
 44      * 用戶點擊菜單時觸發
 45      *
 46      * @return void
 47      */
 48     protected function onClick() {
 49        $eventKey=$this->getRequest('EventKey');
 50        switch($eventKey){
 51          case 'C001':
 52            $this->responseText('我贏了');
 53            break;
 54          case 'C002':
 55            $this->responseText('我最近很好o(∩_∩)o ');
 56            break;
 57          case 'C003':
 58            $this->responseText('謝謝(*^__^*) 嘻嘻');
 59            break;
 60        }
 61     }
 62     
 63     /**
 64      * 收到文本消息時觸發,回覆收到的文本消息內容
 65      *
 66      * @return void
 67      */
 68     protected function onText() {
 69       $this->responseText('收到了文字消息:' . $this->getRequest('content'));
 70     }
 71 
 72     /**
 73      * 收到圖片消息時觸發,回覆由收到的圖片組成的圖文消息
 74      *
 75      * @return void
 76      */
 77     protected function onImage() {
 78       $items = array(
 79         new NewsResponseItem('標題一', '描述一', $this->getRequest('picurl'), $this->getRequest('picurl')),
 80         new NewsResponseItem('標題二', '描述二', $this->getRequest('picurl'), $this->getRequest('picurl')),
 81       );
 82 
 83       $this->responseNews($items);
 84     }
 85 
 86     /**
 87      * 收到地理位置消息時觸發,回覆收到的地理位置
 88      *
 89      * @return void
 90      */
 91     protected function onLocation() {
 92         //$num = 1 / 0;
 93       // 故意觸發錯誤,用於演示調試功能
 94 
 95       $this->responseText('收到了位置消息:' . $this->getRequest('location_x') . ',' . $this->getRequest('location_y'));
 96     }
 97 
 98     /**
 99      * 收到連接消息時觸發,回覆收到的連接地址
100      *
101      * @return void
102      */
103     protected function onLink() {
104       $this->responseText('收到了連接:' . $this->getRequest('url'));
105     }
106       
107      /**
108      * 收到語音消息時觸發,回覆收到的語音
109      *
110      * @return void
111      */
112     protected function onVoice() {
113       $this->responseVoice('收到了語音:' . $this->getRequest('recognition'));
114     }
115 
116     /**
117      * 收到未知類型消息時觸發,回覆收到的消息類型
118      *
119      * @return void
120      */
121     protected function onUnknown() {
122       $this->responseText('收到了未知類型消息:' . $this->getRequest('msgtype'));
123     }
124 
125   }
126 
127   $wechat = new MyWechat('hancong', TRUE);
128   $wechat->run();

以上代碼部分功能須要開通服務號而且申請認證,好比語音識別,地理信息,添加菜單的功能,申請認證須要300元/年,能夠享受微信全部的接口功能。

 

注:若是驗證服務器URL,須要修改一句代碼

$wechat = new MyWechat('hancong', TRUE);
//$wechat->run();
$wechat->validateSignature('hancong');//參數爲填寫的token

驗證完後回覆調用run方法,validateSignature方法只是第一次驗證服務器調用,驗證完後便可刪掉。

相關文章
相關標籤/搜索