isValid() && $this -> validateSignature($token)) { exit($_GET['echostr']); } $this -> debug = $debug; set_error_handler(array(&$this, 'errorHandler')); // 设置错误处理函数,将错误通过文本消息回复显示 $xml = (array) simplexml_load_string($GLOBALS['HTTP_RAW_POST_DATA'], 'SimpleXMLElement', LIBXML_NOCDATA); $this -> request = array_change_key_case($xml, CASE_LOWER); // 将数组键名转换为小写,提高健壮性,减少因大小写不同而出现的问题 } /** * 判断此次请求是否为验证请求 * * @return boolean */ private function isValid() { return isset($_GET['echostr']); } /** * 判断验证请求的签名信息是否正确 * * @param string $token 验证信息 * @return boolean */ private function validateSignature($token) { $signature = $_GET['signature']; $timestamp = $_GET['timestamp']; $nonce = $_GET['nonce']; $signatureArray = array($token, $timestamp, $nonce); sort($signatureArray); return sha1(implode($signatureArray)) == $signature; } /** * 获取本次请求中的参数,不区分大小 * * @param string $param 参数名,默认为无参 * @return mixed */ protected function getRequest($param = FALSE) { if ($param === FALSE) { return $this -> request; } $param = strtolower($param); if (isset($this -> request[$param])) { return $this -> request[$param]; } return NULL; } /** * 用户关注时触发,用于子类重写 * * @return void */ protected function onSubscribe() { } /** * 用户Event时时触发,用于子类重写 * * @return void */ protected function onClickEvent(){ } /** * 用户取消关注时触发,用于子类重写 * * @return void */ protected function onUnsubscribe() { } /** * 收到文本消息时触发,用于子类重写 * * @return void */ protected function onText() { } /** * 收到图片消息时触发,用于子类重写 * * @return void */ protected function onImage() { } /** * 收到地理位置消息时触发,用于子类重写 * * @return void */ protected function onLocation() { } /** * 收到链接消息时触发,用于子类重写 * * @return void */ protected function onLink() { } /** * 收到未知类型消息时触发,用于子类重写 * * @return void */ protected function onUnknown() { } /** * 回复文本消息 * * @param string $content 消息内容 * @param integer $funcFlag 默认为0,设为1时星标刚才收到的消息 * @return void */ protected function responseText($content, $funcFlag = 0) { exit(new TextResponse($this -> getRequest('fromusername'), $this -> getRequest('tousername'), $content, $funcFlag)); } /** * 回复音乐消息 * * @param string $title 音乐标题 * @param string $description 音乐描述 * @param string $musicUrl 音乐链接 * @param string $hqMusicUrl 高质量音乐链接,Wi-Fi 环境下优先使用 * @param integer $funcFlag 默认为0,设为1时星标刚才收到的消息 * @return void */ protected function responseMusic($title, $description, $musicUrl, $hqMusicUrl, $funcFlag = 0) { exit(new MusicResponse($this -> getRequest('fromusername'), $this -> getRequest('tousername'), $title, $description, $musicUrl, $hqMusicUrl, $funcFlag)); } /** * 回复图文消息 * @param array $items 由单条图文消息类型 NewsResponseItem() 组成的数组 * @param integer $funcFlag 默认为0,设为1时星标刚才收到的消息 * @return void */ protected function responseNews($items, $funcFlag = 0) { exit(new NewsResponse($this -> getRequest('fromusername'), $this -> getRequest('tousername'), $items, $funcFlag)); } /** * 分析消息类型,并分发给对应的函数 * * @return void */ public function run() { switch ($this->getRequest('msgtype')) { case 'event' : switch ($this->getRequest('event')) { case 'subscribe' : $this -> onSubscribe(); break; case 'unsubscribe' : $this -> onUnsubscribe(); break; case 'CLICK' : $this -> onClickEvent('halo_hot'); break; default: $this -> onText(); break; } break; case 'text' : $this -> onText(); break; case 'image' : $this -> onImage(); break; case 'location' : $this -> onLocation(); break; case 'link' : $this -> onLink(); break; default : $this -> onUnknown(); break; } } /** * 自定义的错误处理函数,将 PHP 错误通过文本消息回复显示 * @param int $level 错误代码 * @param string $msg 错误内容 * @param string $file 产生错误的文件 * @param int $line 产生错误的行数 * @return void */ protected function errorHandler($level, $msg, $file, $line) { if (!$this -> debug) { return; } $error_type = array( // E_ERROR => 'Error', E_WARNING => 'Warning', // E_PARSE => 'Parse Error', E_NOTICE => 'Notice', // E_CORE_ERROR => 'Core Error', // E_CORE_WARNING => 'Core Warning', // E_COMPILE_ERROR => 'Compile Error', // E_COMPILE_WARNING => 'Compile Warning', E_USER_ERROR => 'User Error', E_USER_WARNING => 'User Warning', E_USER_NOTICE => 'User Notice', E_STRICT => 'Strict', E_RECOVERABLE_ERROR => 'Recoverable Error', E_DEPRECATED => 'Deprecated', E_USER_DEPRECATED => 'User Deprecated', ); $template = << responseText(sprintf($template, $error_type[$level], $msg, $file, $line)); } } /** * 用于回复的基本消息类型 */ abstract class WechatResponse { protected $toUserName; protected $fromUserName; protected $funcFlag; public function __construct($toUserName, $fromUserName, $funcFlag) { $this -> toUserName = $toUserName; $this -> fromUserName = $fromUserName; $this -> funcFlag = $funcFlag; } abstract public function __toString(); } /** * 用于回复的文本消息类型 */ class TextResponse extends WechatResponse { protected $content; protected $template = << %s %s XML; public function __construct($toUserName, $fromUserName, $content, $funcFlag = 0) { parent::__construct($toUserName, $fromUserName, $funcFlag); $this -> content = $content; } public function __toString() { return sprintf($this -> template, $this -> toUserName, $this -> fromUserName, time(), $this -> content, $this -> funcFlag); } } /** * 用于回复的音乐消息类型 */ class MusicResponse extends WechatResponse { protected $title; protected $description; protected $musicUrl; protected $hqMusicUrl; protected $template = << %s <![CDATA[%s]]> %s XML; public function __construct($toUserName, $fromUserName, $title, $description, $musicUrl, $hqMusicUrl, $funcFlag) { parent::__construct($toUserName, $fromUserName, $funcFlag); $this -> title = $title; $this -> description = $description; $this -> musicUrl = $musicUrl; $this -> hqMusicUrl = $hqMusicUrl; } public function __toString() { return sprintf($this -> template, $this -> toUserName, $this -> fromUserName, time(), $this -> title, $this -> description, $this -> musicUrl, $this -> hqMusicUrl, $this -> funcFlag); } } /** * 用于回复的图文消息类型 */ class NewsResponse extends WechatResponse { protected $items = array(); protected $template = << %s %s %s %s ' XML; public function __construct($toUserName, $fromUserName, $items, $funcFlag) { parent::__construct($toUserName, $fromUserName, $funcFlag); $this -> items = $items; } public function __toString() { return sprintf($this -> template, $this -> toUserName, $this -> fromUserName, time(), count($this -> items), implode($this -> items), $this -> funcFlag); } } /** * 单条图文消息类型 */ class NewsResponseItem { protected $title; protected $description; protected $picUrl; protected $url; protected $template = << <![CDATA[%s]]> XML; public function __construct($title, $description, $picUrl, $url) { $this -> title = $title; $this -> description = $description; $this -> picUrl = $picUrl; $this -> url = $url; } public function __toString() { return sprintf($this -> template, $this -> title, $this -> description, $this -> picUrl, $this -> url); } }