├── README.md
├── ROADMAP.md
├── composer.json
├── doc
├── CLIENT.md
└── SERVER.md
├── examples
├── Menu.php
├── Server.php
└── TextMessage.php
├── src
└── Jenner
│ └── Wechat
│ ├── Client
│ ├── Card
│ │ ├── AbstractCard.php
│ │ ├── BoardingPass.php
│ │ ├── Card.php
│ │ ├── Code.php
│ │ ├── Color.php
│ │ ├── Location.php
│ │ ├── LuckyMoney.php
│ │ ├── MemberCard.php
│ │ ├── MovieTicket.php
│ │ ├── QrCode.php
│ │ └── WhiteList.php
│ ├── Client.php
│ ├── CustomService.php
│ ├── JsTicket.php
│ ├── Media.php
│ ├── Menu.php
│ ├── MenuStructure
│ │ ├── Button.php
│ │ ├── ButtonMenu.php
│ │ ├── ClickButton.php
│ │ ├── EventButton.php
│ │ ├── Factory.php
│ │ └── ViewButton.php
│ ├── Merchant
│ │ ├── AbstractMerchant.php
│ │ ├── Category.php
│ │ ├── Common.php
│ │ ├── Express.php
│ │ ├── Good.php
│ │ ├── Merchant.php
│ │ ├── Order.php
│ │ ├── Shelf.php
│ │ └── Stock.php
│ ├── Message
│ │ ├── AbstractMessage.php
│ │ ├── Image.php
│ │ ├── Music.php
│ │ ├── News.php
│ │ ├── NewsItem.php
│ │ ├── Text.php
│ │ ├── Video.php
│ │ └── Voice.php
│ ├── QrCode.php
│ ├── Semantic.php
│ ├── ShortUrl.php
│ ├── System.php
│ ├── User.php
│ └── UserGroup.php
│ ├── Coding
│ ├── ErrorCode.php
│ ├── PKCS7Encoder.php
│ ├── Prpcrypt.php
│ ├── SHA1.php
│ └── WXBizMsgCrypt.php
│ ├── Config
│ ├── Event.php
│ └── URI.php
│ ├── Exception
│ ├── ResponseErrorException.php
│ └── WechatException.php
│ ├── Redirect.php
│ ├── Request
│ └── XmlRequest.php
│ ├── Response
│ ├── AbstractXml.php
│ ├── Image.php
│ ├── Media.php
│ ├── Music.php
│ ├── News.php
│ ├── NewsItem.php
│ ├── Text.php
│ └── Voice.php
│ ├── Server.php
│ └── Tool
│ └── Http.php
└── tests
├── composer.json
├── composer.lock
└── vendor
├── autoload.php
├── composer
├── ClassLoader.php
├── autoload_classmap.php
├── autoload_namespaces.php
├── autoload_psr4.php
├── autoload_real.php
└── installed.json
└── jenner
└── tools
├── README.md
├── composer.json
└── src
└── Jenner
└── Zebra
└── Tools
├── CommonArray.php
├── Http.php
└── Server.php
/README.md:
--------------------------------------------------------------------------------
1 | Zebra-Wechat
2 | ===================
3 |
4 | 微信SDK
5 | 目前处于开发状态,目前实现了以下功能:
6 | * 接收微信服务器推送信息,对推送信息类型进行识别
7 | * 微信API客户端封装(用户管理、用户组管理、客服管理、自定义菜单管理、系统管理等)
8 | * 微信跳转验证封装
9 | * 微信卡卷支持
10 |
11 | [博客地址:www.huyanping.cn](http://www.huyanping.cn/ "程序猿始终不够")
12 |
13 | **接收微信推送示例**
14 | ```php
15 | use \Jenner\Zebra\Wechat\WechatServer;
16 | use \Jenner\Zebra\Wechat\Response\TextResponse;
17 |
18 | $token = 'you wechat token';
19 | $server = new WechatServer($token);
20 |
21 | //处理事件前调用,无论是否有注册事件处理器
22 | $server->on('before', function(WechatServer $server, $request){
23 | //do something
24 | });
25 |
26 | //处理事件后调用,$result为事件处理器的返回值
27 | $server->on('after', function(WechatServer $server, $result){
28 | //do something
29 | });
30 |
31 | //未知消息处理器
32 | $server->on('unknown_message', function(WechatServer $server, $request){
33 | //do something
34 | });
35 |
36 | //未知时间处理器
37 | $server->on('unknown_event', function(WechatServer $server, $request){
38 | //do something
39 | });
40 |
41 | //处理微信文本消息推送
42 | $server->on('text', function(WechatServer $server, $request){
43 | $to_user = $server->getFromUserName();
44 | $from_user = $server->getToUserName();
45 | $response = new TextResponse($to_user, $from_user, 'hello');
46 | $server->send($response);
47 | $result = 'success';
48 |
49 | //如果你定义了after的回调,这个返回值将作为参数传递给after函数
50 | return $result;
51 | });
52 |
53 |
54 | //处理微信关注推送
55 | $server->on('subscribe', function(WechatServer $server, $request){
56 | $to_user = $server->getFromUserName();
57 | $from_user = $server->getToUserName();
58 | $response = new TextResponse($to_user, $from_user, 'thx');
59 | $server->send($response);
60 | });
61 |
62 | ```
63 |
64 | **主动向微信发送消息**
65 | ```php
66 | define('WECHAT_APP_ID', 'your app id');
67 | define('WECHAT_SECRET', 'your secret');
68 |
69 | $to_user = 'to_user_open_id';
70 | $text = 'hello';
71 | $text_message = new \Jenner\Zebra\Wechat\Client\Message\TextMessage($to_user, $text);
72 | $text_message->send();
73 | ```
74 |
--------------------------------------------------------------------------------
/ROADMAP.md:
--------------------------------------------------------------------------------
1 | Zebra-Wechat线路图
2 | ==================
3 |
4 | ### 0.1 基础框架搭建,用户接口设计
5 | * 用户接口设计
6 | * 微信推送接收服务设计
7 | * 通信底层框架搭建
8 |
9 | ### 0.3 完善工作
10 | * 补充完善微信接口
11 | * 代码重构
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jenner/wechat",
3 | "description": "wechat api client",
4 | "license": "MIT",
5 | "keywords": ["wechat", "api"],
6 | "version": "v1.0.0",
7 | "authors": [
8 | {
9 | "name": "Jenner",
10 | "email": "hypxm@qq.com"
11 | }
12 | ],
13 | "require": {
14 | "jenner/tools": "2.1.0",
15 | "php": ">=5.3.0"
16 | },
17 |
18 | "autoload": {
19 | "psr-0": {
20 | "Jenner\\Zebra\\Wechat": "src/"
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/doc/CLIENT.md:
--------------------------------------------------------------------------------
1 | 微信客户端
2 | ================
--------------------------------------------------------------------------------
/doc/SERVER.md:
--------------------------------------------------------------------------------
1 | 微信推送接收器
2 | ================
--------------------------------------------------------------------------------
/examples/Menu.php:
--------------------------------------------------------------------------------
1 |
12 | array (
13 | 'name' => '功能列表',
14 | 'type' => NULL,
15 | 'url' => '',
16 | 'key' => '1',
17 | 'sub_button' =>
18 | array (
19 | 0 =>
20 | array (
21 | 'id' => 6,
22 | 'shop_id' => 1,
23 | 'type' => 'view',
24 | 'name' => '百度',
25 | 'key' => '1',
26 | 'url' => 'http://www.baidu.com',
27 | 'pid' => 1,
28 | 'rank' => 0,
29 | 'created_at' => '2014-12-19 16:57:04',
30 | 'updated_at' => '2014-12-19 16:57:04',
31 | 'text' => '',
32 | ),
33 | 1 =>
34 | array (
35 | 'id' => 7,
36 | 'shop_id' => 1,
37 | 'type' => 'click',
38 | 'name' => '测试',
39 | 'key' => '1',
40 | 'url' => '',
41 | 'pid' => 1,
42 | 'rank' => 1,
43 | 'created_at' => '2014-12-19 16:57:42',
44 | 'updated_at' => '2014-12-19 16:57:42',
45 | 'text' => 'dddd',
46 | ),
47 | ),
48 | ),
49 | );
50 |
51 | $button = \Jenner\Zebra\Wechat\Client\MenuStructure\Factory::create($layered_menus);
52 |
53 |
54 | define('WECHAT_APP_ID', 'your wechat app_id');
55 | define('WECHAT_SECRET', 'your wechat secret');
56 |
57 | $menu = new \Jenner\Zebra\Wechat\Client\Menu();
58 | $menu->create($button);
--------------------------------------------------------------------------------
/examples/Server.php:
--------------------------------------------------------------------------------
1 | on('before', function(WechatServer $server, $request){
16 | //do something
17 | });
18 |
19 | //处理事件后调用,$result为事件处理器的返回值
20 | $server->on('after', function(WechatServer $server, $result){
21 | //do something
22 | });
23 |
24 | //未知消息处理器
25 | $server->on('unknown_message', function(WechatServer $server, $request){
26 | //do something
27 | });
28 |
29 | //未知时间处理器
30 | $server->on('unknown_event', function(WechatServer $server, $request){
31 | //do something
32 | });
33 |
34 | //处理微信文本消息推送
35 | $server->on('text', function(WechatServer $server, $request){
36 | $to_user = $server->getFromUserName();
37 | $from_user = $server->getToUserName();
38 | $response = new TextResponse($to_user, $from_user, 'hello');
39 | $server->send($response);
40 | $result = 'success';
41 |
42 | //如果你定义了after的回调,这个返回值将作为参数传递给after函数
43 | return $result;
44 | });
45 |
46 |
47 | //处理微信关注推送
48 | $server->on('subscribe', function(WechatServer $server, $request){
49 | $to_user = $server->getFromUserName();
50 | $from_user = $server->getToUserName();
51 | $response = new TextResponse($to_user, $from_user, 'thx');
52 | $server->send($response);
53 | });
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/examples/TextMessage.php:
--------------------------------------------------------------------------------
1 | send();
19 |
20 |
21 | /**
22 | * 或者 你可以这样
23 | */
24 |
25 | \Jenner\Zebra\Wechat\Client\WechatClient::registerAuthInfo('your app_id', 'your secret');
26 | $to_user = 'to_user_open_id';
27 | $text = 'hello';
28 | $text_message = new \Jenner\Zebra\Wechat\Client\Message\TextMessage($to_user, $text);
29 | $text_message->send();
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Card/AbstractCard.php:
--------------------------------------------------------------------------------
1 | card_uri_prefix = URI::CARD_PREFIX;
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Card/BoardingPass.php:
--------------------------------------------------------------------------------
1 | card_uri_prefix . URI::CARD_BOARDING_PASS_CHECK_IN;
24 | return $this->request_post($uri, $card);
25 | }
26 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Card/Card.php:
--------------------------------------------------------------------------------
1 | card_uri_prefix . URI::CARD_CREATE;
23 | return $this->request_post($uri, $card);
24 | }
25 |
26 |
27 | /**
28 | * 查询卡券详情
29 | * @param $card_id
30 | * @return bool|mixed
31 | */
32 | public function get($card_id)
33 | {
34 | $uri = $this->card_uri_prefix . URI::CARD_GET;
35 | return $this->request_post($uri, compact('card_id'));
36 | }
37 |
38 | public function update($card)
39 | {
40 | $uri = $this->card_uri_prefix . URI::CARD_UPDATE;
41 | return $this->request_post($uri, $card);
42 | }
43 |
44 | public function batchGet($offset = 0, $count = 50)
45 | {
46 | $uri = $this->card_uri_prefix . URI::CARD_BATCH_GET;
47 | return $this->request_post($uri, compact('offset', 'count'));
48 | }
49 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Card/Code.php:
--------------------------------------------------------------------------------
1 | card_uri_prefix . URI::CARD_CODE_CONSUME;
25 | return $this->request_post($uri, compact('code', 'card_id'));
26 | }
27 |
28 | /**
29 | * code解码接口
30 | * 通过 choose_card_info 获取的加密字符串
31 | * @param $encrypt_code
32 | * @return bool|mixed
33 | */
34 | public function decrypt($encrypt_code)
35 | {
36 | $uri = $this->card_uri_prefix . URI::CARD_CODE_DECRYPT;
37 | return $this->request_post($uri, compact('encrypt_code'));
38 | }
39 |
40 | /**
41 | * 查询code
42 | * @param $code
43 | * @return bool|mixed
44 | */
45 | public function get($code)
46 | {
47 | $uri = $this->card_uri_prefix . URI::CARD_CODE_GET;
48 | return $this->request_post($uri, compact('code'));
49 | }
50 |
51 | /**
52 | * 批量查询卡列表
53 | * @param $offset
54 | * @param $count
55 | * @return bool|mixed
56 | */
57 | public function batchGet($offset, $count)
58 | {
59 | $uri = $this->card_uri_prefix . URI::CARD_CODE_BATCH_GET;
60 | return $this->request_post($uri, compact('offset', 'count'));
61 | }
62 |
63 | /**
64 | * 更改code
65 | * @param $code
66 | * @param $card_id
67 | * @param $new_code
68 | * @return bool|mixed
69 | */
70 | public function update($code, $card_id, $new_code)
71 | {
72 | $uri = $this->card_uri_prefix . URI::CARD_CODE_UPDATE;
73 | return $this->request_post($uri, compact('code', 'card_id', 'new_code'));
74 | }
75 |
76 | /**
77 | * 设置卡券失效接口
78 | * @param $code
79 | * @param null $card_id
80 | * @return bool|mixed
81 | */
82 | public function unavailable($code, $card_id = null)
83 | {
84 | $uri = $this->card_uri_prefix . URI::CARD_CODE_UNAVAILABLE;
85 | $params['code'] = $code;
86 | if (!is_null($card_id)) $params['card_id'] = $card_id;
87 | return $this->request_post($uri, $params);
88 | }
89 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Card/Color.php:
--------------------------------------------------------------------------------
1 | card_uri_prefix . URI::CARD_COLOR_GET;
19 | return $this->request_get($uri);
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Card/Location.php:
--------------------------------------------------------------------------------
1 | card_uri_prefix . URI::CARD_LOCATION_BATCH_ADD;
19 | return $this->request_post($uri, $location);
20 | }
21 |
22 | public function batchGet($offset, $count)
23 | {
24 | $uri = $this->card_uri_prefix . URI::CARD_LOCATION_BATCH_GET;
25 | return $this->request_post($uri, compact('offset', 'count'));
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Card/LuckyMoney.php:
--------------------------------------------------------------------------------
1 | card_uri_prefix . URI::CARD_LUCKY_MONEY_UPDATE_USER_BALANCE;
24 | return $this->request_post($uri, $card);
25 | }
26 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Card/MemberCard.php:
--------------------------------------------------------------------------------
1 | card_uri_prefix . URI::CARD_MEMBER_ACTIVATE;
24 | return $this->request_post($uri, $card);
25 | }
26 |
27 | /**
28 | * 会员卡交易 会员卡交易后每次积分及余额变更需通过接口通知微信,便于后续消息通知及其他扩展功能。
29 | * @param $member_card
30 | * @return bool|mixed
31 | */
32 | public function updateUser($member_card)
33 | {
34 | $uri = $this->card_uri_prefix . URI::CARD_MEMBER_UPDATE;
35 | return $this->request_post($uri, $member_card);
36 | }
37 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Card/MovieTicket.php:
--------------------------------------------------------------------------------
1 | card_uri_prefix . URI::CARD_MOVIE_TICKET;
24 | return $this->request_post($uri, $card);
25 | }
26 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Card/QrCode.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/white-poto/Wechat_PHP_SDK/329c03ba617edd348ce3d8c259c81c45cf9780c5/src/Jenner/Wechat/Client/Card/QrCode.php
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Card/WhiteList.php:
--------------------------------------------------------------------------------
1 | card_uri_prefix . URI::CARD_TEST_WHITE_LIST;
24 | return $this->request_post($uri, $list);
25 | }
26 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Client.php:
--------------------------------------------------------------------------------
1 | app_id = $app_id;
57 | $this->secret = $secret;
58 | }
59 |
60 | /**
61 | * @param $callback
62 | */
63 | public static function registerGetAccessTokenCallback($callback)
64 | {
65 | self::$get_access_token_callback = $callback;
66 | }
67 |
68 | /**
69 | * @param $callback
70 | */
71 | public static function registerSetAccessTokenCallback($callback)
72 | {
73 | self::$set_access_token_callback = $callback;
74 | }
75 |
76 | /**
77 | * 发起普通GET请求
78 | * @param $uri
79 | * @param null $params
80 | * @return bool|mixed
81 | */
82 | public function request_get($uri, $params = null)
83 | {
84 | return $this->request($uri, null, $params);
85 | }
86 |
87 | /**
88 | * 发起POST请求
89 | * @param $uri
90 | * @param $post_params
91 | * @param bool $file_upload 是否上传文件
92 | * @return bool|mixed
93 | */
94 | public function request_post($uri, $post_params, $file_upload = false)
95 | {
96 | return $this->request($uri, $post_params, $file_upload);
97 | }
98 |
99 | /**
100 | * 发起带有GET参数的POST请求
101 | * @param $uri
102 | * @param null $post_params POST参数
103 | * @param null $get_params GET参数
104 | * @param bool $file_upload 是否上传文件
105 | * @return bool|mixed
106 | */
107 | public function request($uri, $post_params = null, $get_params = null, $file_upload = false)
108 | {
109 | $access_token = $this->getAccessToken();
110 | $get_params['access_token'] = $access_token;
111 | $query_string = http_build_query($get_params);
112 | $post_params = json_encode($post_params, JSON_UNESCAPED_UNICODE);
113 | $http = new \Jenner\Wechat\Tool\Http($uri . '?' . $query_string);
114 | $result_json = $http->POST($post_params, $file_upload);
115 |
116 | //存在errcode并且errcode不为0时,为错误返回
117 | return $this->checkResponse($result_json);
118 | }
119 |
120 | /**
121 | * 获取微信access_key
122 | * @throws \Exception
123 | * @internal param $app_id
124 | * @internal param $secret
125 | * @return mixed
126 | */
127 | public function getAccessToken()
128 | {
129 | if ($cache = $this->getAccessTokenAndCheckExpiresIn()) {
130 | return $cache['access_token'];
131 | }
132 |
133 | $params = [
134 | 'grant_type' => 'client_credential',
135 | 'appid' => $this->app_id,
136 | 'secret' => $this->secret,
137 | ];
138 | $http = new \Jenner\Wechat\Tool\Http(self::API_AUTH_TOKEN);
139 | $response_json = $http->GET($params);
140 | $this->checkResponse($response_json);
141 | $result = json_decode($response_json, true);
142 |
143 | $cache['access_token'] = $result['access_token'];
144 | $cache['expires_in'] = $result['expires_in'];
145 | $cache['create_time'] = time();
146 | if (!empty(self::$set_access_token_callback) && is_callable(self::$set_access_token_callback)) {
147 | call_user_func(self::$set_access_token_callback, $cache);
148 | }
149 |
150 | return $result['access_token'];
151 | }
152 |
153 | /**
154 | * 检查access_key是否过期
155 | * @return bool
156 | */
157 | public function getAccessTokenAndCheckExpiresIn()
158 | {
159 | if (empty(self::$get_access_token_callback) || !is_callable(self::$get_access_token_callback))
160 | return false;
161 |
162 | $cache = call_user_func(self::$get_access_token_callback);
163 |
164 | if (empty($cache)) return false;
165 | $now = time();
166 | if ($now - $cache['create_time'] > $cache['expires_in']) {
167 | return false;
168 | }
169 |
170 | return $cache;
171 | }
172 |
173 | /**
174 | * 检查微信响应是否出错,如果出错,抛出异常
175 | * @param $response_json
176 | * @return mixed
177 | * @throws \Jenner\Wechat\Exception\ResponseErrorException
178 | */
179 | public function checkResponse($response_json)
180 | {
181 | $response = json_decode($response_json, true);
182 | if (isset($response['errcode']) && $response['errcode'] != 0) {
183 | throw new ResponseErrorException($response['errmsg'], $response['errcode']);
184 | }
185 |
186 | return $response;
187 | }
188 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/CustomService.php:
--------------------------------------------------------------------------------
1 | request_get(self::API_LIST);
36 | }
37 |
38 | /**
39 | * 获取在线客服接待信息
40 | * @return bool|mixed
41 | */
42 | public function getOnline()
43 | {
44 | return $this->request_get(self::API_ONLINE_LIST);
45 | }
46 |
47 | /**
48 | * 添加客服账号
49 | * @param $account
50 | * @param $nickname
51 | * @param $password
52 | * @return bool|mixed
53 | */
54 | public function add($account, $nickname, $password)
55 | {
56 | $params = ['account' => $account, 'nickname' => $nickname, 'password' => $password];
57 | return $this->request_post(self::API_ADD, $params);
58 | }
59 |
60 | /**
61 | * 设置客服信息
62 | * @param $account
63 | * @param $nickname
64 | * @param $password
65 | * @return bool|mixed
66 | */
67 | public function update($account, $nickname, $password)
68 | {
69 | $params = ['account' => $account, 'nickname' => $nickname, 'password' => $password];
70 | return $this->request_post(self::API_UPDATE, $params);
71 | }
72 |
73 | /**
74 | * 上传客服头像
75 | * @param $account
76 | * @param string $img_with_full_path 图片地址,绝对路径
77 | * @return bool|mixed
78 | */
79 | public function uploadHeadImg($account, $img_with_full_path)
80 | {
81 | $get_params = ['kf_account' => $account];
82 | $post_params = ['media' => '@' . $img_with_full_path];
83 | return $this->request(self::API_UPLOAD_HEAD_IMG, $post_params, $get_params, true);
84 | }
85 |
86 | /**
87 | * 删除客服账号
88 | * @param $account
89 | * @return bool|mixed
90 | */
91 | public function delete($account)
92 | {
93 | $params = ['kf_account' => $account];
94 | return $this->request_get(self::API_DELETE, $params);
95 | }
96 |
97 | /**
98 | * 获取客服聊天记录
99 | * @param $start_time 查询开始时间,UNIX时间戳
100 | * @param $end_time 查询结束时间,UNIX时间戳,每次查询不能跨日查询
101 | * @param $open_id 普通用户的标识,对当前公众号唯一
102 | * @param $page_size 每页大小,每页最多拉取1000条
103 | * @param $page_index 查询第几页,从1开始
104 | * @throws \Jenner\Wechat\Exception\WechatException
105 | * @return bool|mixed
106 | */
107 | public function getRecord($start_time, $end_time, $open_id, $page_size, $page_index)
108 | {
109 | if ($page_size > 1000) {
110 | throw new WechatException('page_size out of range');
111 | }
112 |
113 | $start_date = date('Y-m-d', $start_time);
114 | $end_date = date('Y-m-d', $end_time);
115 | if ($start_date != $end_date) {
116 | throw new WechatException('param start_time and end_time cannot span multiple days');
117 | }
118 |
119 | $params = [
120 | 'starttime' => $start_time,
121 | 'endtime' => $end_time,
122 | 'openid' => $open_id,
123 | 'pagesize' => $page_size,
124 | 'pageindex' => $page_index,
125 | ];
126 |
127 | return $this->request_post(self::API_RECORD, $params);
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/JsTicket.php:
--------------------------------------------------------------------------------
1 | getJsApiTicket()['ticket'];
21 | }
22 | $url = "http://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
23 | $timestamp = time();
24 | $nonce_str = $this->createNonceStr();
25 |
26 | // 这里参数的顺序要按照 key 值 ASCII 码升faf序排序
27 | $string = "jsapi_ticket={$js_api_ticket}&noncestr={$nonce_str}×tamp={$timestamp}&url={$url}";
28 |
29 | $signature = sha1($string);
30 |
31 | $signPackage = array(
32 | "appId" => $this->app_id,
33 | "nonceStr" => $nonce_str,
34 | "timestamp" => $timestamp,
35 | "url" => $url,
36 | "signature" => $signature,
37 | "rawString" => $string,
38 | 'jsapi_ticket' => $js_api_ticket,
39 | );
40 | return $signPackage;
41 | }
42 |
43 | private function createNonceStr($length = 16)
44 | {
45 | $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
46 | $str = "";
47 | for ($i = 0; $i < $length; $i++) {
48 | $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
49 | }
50 | return $str;
51 | }
52 |
53 | public function getCardTicket()
54 | {
55 | return $this->getTicket('wx_card');
56 | }
57 |
58 | public function getJsApiTicket()
59 | {
60 | return $this->getTicket('jsapi');
61 | }
62 |
63 | public function getTicket($type)
64 | {
65 | $params = ['type' => $type];
66 | return $this->request_get(self::API_GET, $params);
67 | }
68 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Media.php:
--------------------------------------------------------------------------------
1 | '@' . $absolute_file];
29 | return $this->request(self::API_UPLOAD, $post_params, $get_params, true);
30 | }
31 |
32 | public function uploadImage($absolute_file)
33 | {
34 |
35 | }
36 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Menu.php:
--------------------------------------------------------------------------------
1 | request_get(self::API_GET);
27 | }
28 |
29 | /**
30 | * 删除微信菜单
31 | * @return bool|mixed
32 | */
33 | public function delete()
34 | {
35 | return $this->request_get(self::API_DELETE);
36 | }
37 |
38 | /**
39 | * 创建微信菜单,格式如:
40 | * {"button":[
41 | * {"type":"click","name":"今日歌曲","key":"V1001_TODAY_MUSIC"},
42 | * {"name":"菜单","sub_button":[{"type":"view","name":"搜索","url":"http:\/\/www.soso.com\/"},
43 | * {"type":"view","name":"视频","url":"http:\/\/v.qq.com\/"},
44 | * {"type":"click","name":"赞一下我们","key":"V1001_GOOD"}]}
45 | * ]}
46 | * @param $menu
47 | * @return bool|mixed
48 | */
49 | public function create($menu)
50 | {
51 | return $this->request_post(self::API_CREATE, $menu);
52 | }
53 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/MenuStructure/Button.php:
--------------------------------------------------------------------------------
1 | 40)
26 | throw new WechatException('Illegal name size');
27 |
28 | $this->button['name'] = $name;
29 | if (!in_array($type, ['click', 'view', 'scancode_push', 'scancode_waitmsg', 'pic_sysphoto', 'pic_photo_or_album', 'pic_weixin', 'location_select']) && !is_null($type))
30 | throw new WechatException('button type error');
31 |
32 | if (!is_null($type)) {
33 | $this->button['type'] = $type;
34 | }
35 | }
36 |
37 | /**
38 | * @param $key
39 | * @throws \Jenner\Wechat\Exception\WechatException
40 | */
41 | public function setKey($key)
42 | {
43 | if (empty($key)) {
44 | throw new WechatException('Illegal empty button key');
45 | }
46 | if (strlen($key) > 128) {
47 | throw new WechatException('Illegal button key size');
48 | }
49 |
50 | $this->button['key'] = $key;
51 | }
52 |
53 | /**
54 | * @param $url
55 | * @throws \Jenner\Wechat\Exception\WechatException
56 | */
57 | public function setUrl($url)
58 | {
59 | if (empty($url)) {
60 | throw new WechatException('Illegal empty button url');
61 | }
62 | if (strlen($url) > 256) {
63 | throw new WechatException('Illegal button key url');
64 | }
65 |
66 | $this->button['url'] = $url;
67 | }
68 |
69 | /**
70 | * @param Button $button
71 | * @throws \Jenner\Wechat\Exception\WechatException
72 | */
73 | public function addSubButton(Button $button)
74 | {
75 | if (!isset($this->button['sub_button']))
76 | $this->button['sub_button'] = [];
77 |
78 | if (count($this->button) == 5) {
79 | throw new WechatException('too many sub buttons');
80 | }
81 |
82 | $this->button['sub_button'][] = $button->getButton();
83 | }
84 |
85 | public function getButton()
86 | {
87 | return $this->button;
88 | }
89 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/MenuStructure/ButtonMenu.php:
--------------------------------------------------------------------------------
1 | buttons[] = $button->getButton();
21 | }
22 |
23 | public function create()
24 | {
25 | if (count($this->buttons) > 3 || count($this->buttons) < 1) {
26 | throw new WechatException('Illegal button size');
27 | }
28 | foreach ($this->buttons as $button) {
29 | if (strlen($button['name']) > 16)
30 | throw new WechatException('Illegal button name size');
31 | }
32 | return ['button' => $this->buttons];
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/MenuStructure/ClickButton.php:
--------------------------------------------------------------------------------
1 | setKey($key);
18 | }
19 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/MenuStructure/EventButton.php:
--------------------------------------------------------------------------------
1 | setKey($key);
18 | }
19 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/MenuStructure/Factory.php:
--------------------------------------------------------------------------------
1 | addSubButton($sub_button_obj);
32 | }
33 | }
34 | $button_menu->addButton($button);
35 | }
36 |
37 | return $button_menu->create();
38 | }
39 |
40 | protected static function CreateButton($menu)
41 | {
42 | // 父级按钮创建
43 | if (empty($menu['type'])) {
44 | return new Button($menu['name']);
45 | }
46 |
47 | //点击事件类型推送按钮创建
48 | if ($menu['type'] == 'click') {
49 | if (empty($menu['key']))
50 | throw new WechatException('button key cannot be empty');
51 | return new ClickButton($menu['name'], $menu['key']);
52 | } //跳转页面类型按钮创建
53 | elseif ($menu['type'] == 'view') {
54 | if (empty($menu['url']))
55 | throw new WechatException('button url cannot be empty');
56 | return new ViewButton($menu['name'], $menu['url']);
57 | } //事件类型按钮创建
58 | else {
59 | if (empty($menu['key']))
60 | throw new WechatException('button key cannot be empty');
61 | return new EventButton($menu['name'], $menu['type'], $menu['key']);
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/MenuStructure/ViewButton.php:
--------------------------------------------------------------------------------
1 | setUrl($url);
18 | }
19 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Merchant/AbstractMerchant.php:
--------------------------------------------------------------------------------
1 | merchant_uri_prefix = URI::MERCHANT_PREFIX;
23 | }
24 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Merchant/Category.php:
--------------------------------------------------------------------------------
1 | merchant_uri_prefix . URI::MERCHANT_CATEGORY_GET_SUB;
24 | return $this->request_post($uri, ['cate_id' => $category_id]);
25 | }
26 |
27 | /**
28 | * 获取指定子分类的所有SKU
29 | * @param $category_id
30 | * @return bool|mixed
31 | */
32 | public function getSku($category_id)
33 | {
34 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_CATEGORY_GET_SKU;
35 | return $this->request_post($uri, ['cate_id' => $category_id]);
36 | }
37 |
38 | /**
39 | * 获取指定分类的所有属性
40 | * @param $category_id
41 | * @return bool|mixed
42 | */
43 | public function getProperty($category_id)
44 | {
45 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_CATEGORY_GET_PROPERTY;
46 | return $this->request_post($uri, ['cate_id' => $category_id]);
47 | }
48 |
49 |
50 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Merchant/Common.php:
--------------------------------------------------------------------------------
1 | merchant_uri_prefix . URI::MERCHANT_COMMON_UPLOAD_IMG;
19 | $file_name = basename($filename_with_full_path);
20 | return $this->request($uri, ['filename' => $file_name], file_get_contents($filename_with_full_path), true);
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Merchant/Express.php:
--------------------------------------------------------------------------------
1 | merchant_uri_prefix . URI::MERCHANT_EXPRESS_ADD;
24 | return $this->request_post($uri, $express);
25 | }
26 |
27 | /**
28 | * 删除邮费模板
29 | * @param $template_id
30 | * @return bool|mixed
31 | */
32 | public function del($template_id)
33 | {
34 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_EXPRESS_DEL;
35 | return $this->request_post($uri, compact('template_id'));
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Merchant/Good.php:
--------------------------------------------------------------------------------
1 | merchant_uri_prefix . URI::MERCHANT_GOOD_CREATE;
25 | return $this->request_post($uri, $product);
26 | }
27 |
28 | /**
29 | * 删除商品
30 | * @param $product_id
31 | * @return bool|mixed
32 | */
33 | public function del($product_id)
34 | {
35 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_GOOD_DEL;
36 | return $this->request_post($uri, ['product_id' => $product_id]);
37 | }
38 |
39 | /**
40 | * 修改商品
41 | * @param $product
42 | * @return bool|mixed
43 | */
44 | public function update($product)
45 | {
46 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_GOOD_UPDATE;
47 | return $this->request_post($uri, $product);
48 | }
49 |
50 | /**
51 | * 查询商品
52 | * @param $product_id
53 | * @return bool|mixed
54 | */
55 | public function get($product_id)
56 | {
57 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_GOOD_GET;
58 | return $this->request_post($uri, ['product_id' => $product_id]);
59 | }
60 |
61 | /**
62 | * 获取指定状态的所有商品
63 | * @param $status
64 | * @return bool|mixed
65 | */
66 | public function getByStatus($status)
67 | {
68 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_GOOD_GET_BY_STATUS;
69 | return $this->request_post($uri, ['status' => $status]);
70 | }
71 |
72 | /**
73 | * 商品上下架
74 | * @param $product_id
75 | * @param $status
76 | * @return bool|mixed
77 | */
78 | public function modStatus($product_id, $status)
79 | {
80 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_GOOD_MOD_STATUS;
81 | return $this->request_post($uri, ['product_id' => $product_id, 'status' => $status]);
82 | }
83 |
84 |
85 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Merchant/Merchant.php:
--------------------------------------------------------------------------------
1 | uri_prefix = URI::MERCHANT_PREFIX;
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Merchant/Order.php:
--------------------------------------------------------------------------------
1 | merchant_uri_prefix . URI::MERCHANT_ORDER_GET_BY_ID;
24 | return $this->request_post($uri, ['order_id' => $order_id]);
25 | }
26 |
27 | /**
28 | * 根据订单状态/创建时间获取订单详情
29 | */
30 | public function getByFilter($status = null, $begin_time = null, $end_time = null)
31 | {
32 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_ORDER_GET_BY_FILTER;
33 | is_null($status) ? '' : $params['status'] = $status;
34 | is_null($begin_time) ? '' : $params['begintime'] = $begin_time;
35 | is_null($end_time) ? '' : $params['endtime'] = $end_time;
36 |
37 | return $this->request_post($uri, $params);
38 | }
39 |
40 | /**
41 | * 设置订单发货信息
42 | * @param $order_id 订单ID
43 | * @param $delivery_company 物流公司ID(参考《物流公司ID》
44 | * @param $delivery_track_no 运单ID
45 | * @param $need_delivery 商品是否需要物流(0-不需要,1-需要,无该字段默认为需要物流)
46 | * @param $is_others 是否为6.4.5表之外的其它物流公司(0-否,1-是,无该字段默认为不是其它物流公司)
47 | * @return bool|mixed
48 | */
49 | public function setDelivery($order_id, $delivery_company, $delivery_track_no, $need_delivery, $is_others)
50 | {
51 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_ORDER_SET_DELIVERY;
52 | $params = compact('order_id', 'delivery_company', 'delivery_track_no', 'need_delivery', 'is_others');
53 | return $this->request_post($uri, $params);
54 | }
55 |
56 | /**
57 | * 关闭订单
58 | * @param $order_id 订单ID
59 | * @return bool|mixed
60 | */
61 | public function close($order_id)
62 | {
63 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_ORDER_CLOSE;
64 | return $this->request_post($uri, ['order_id' => $order_id]);
65 | }
66 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Merchant/Shelf.php:
--------------------------------------------------------------------------------
1 | merchant_uri_prefix . URI::MERCHANT_SHELF_ADD;
26 | return $this->request_post($uri, $shelf);
27 | }
28 |
29 | /**
30 | * 删除货架
31 | * @param $shelf_id 货架ID
32 | * @return bool|mixed
33 | */
34 | public function del($shelf_id)
35 | {
36 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_SHELF_DEL;
37 | return $this->request_post($uri, compact('shelf_id'));
38 | }
39 |
40 | /**
41 | * 修改货架
42 | * @param $shelf
43 | * @return bool|mixed
44 | */
45 | public function update($shelf)
46 | {
47 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_SHELF_MOD;
48 | return $this->request_post($uri, $shelf);
49 | }
50 |
51 | /**
52 | * 获取所有货架
53 | * @return bool|mixed
54 | */
55 | public function getAll()
56 | {
57 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_SHELF_GET_ALL;
58 | return $this->request_get($uri);
59 | }
60 |
61 | /**
62 | * 根据货架ID获取货架信息
63 | * @param $shelf_id
64 | * @return bool|mixed
65 | */
66 | public function getById($shelf_id)
67 | {
68 | $uri = $this->merchant_uri_prefix . URI::MERCHANT_SHELF_GET_BY_ID;
69 | return $this->request_post($uri, compact('shelf_id'));
70 | }
71 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Merchant/Stock.php:
--------------------------------------------------------------------------------
1 | merchant_uri_prefix . URI::MERCHANT_STOCK_ADD;
26 | $params = [
27 | 'product_id' => $product_id,
28 | 'quantity' => $quantity,
29 | 'sku_info' => $sku_info,
30 | ];
31 | return $this->request_post($uri, $params);
32 | }
33 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Message/AbstractMessage.php:
--------------------------------------------------------------------------------
1 | params['touser'] = $to_user;
41 | $this->uri = $this->uri_prefix . URI::MESSAGE_SEND;
42 | }
43 |
44 | /**
45 | * 获取请求微信的参数数组
46 | * @return mixed
47 | */
48 | public function getParams()
49 | {
50 | return $this->params;
51 | }
52 |
53 | /**
54 | * 向微信发起请求
55 | * @return bool|mixed
56 | */
57 | public function send()
58 | {
59 | $params = $this->getParams();
60 | return $this->request($this->uri, $params);
61 | }
62 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Message/Image.php:
--------------------------------------------------------------------------------
1 | params['msgtype'] = 'image';
24 | $this->params['image'] = [];
25 | $this->params['image']['media_id'] = $media_id;
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Message/Music.php:
--------------------------------------------------------------------------------
1 | params['msgtype'] = 'music';
27 | $this->params['music'] = [];
28 | $this->params['music']['title'] = $title;
29 | $this->params['music']['description'] = $description;
30 | $this->params['music']['musicurl'] = $music_url;
31 | $this->params['music']['hqmusicurl'] = $hq_music_url;
32 | $this->params['music']['thumb_media_id'] = $thumb_media_id;
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Message/News.php:
--------------------------------------------------------------------------------
1 | params['msgtype'] = 'news';
23 | $this->params['news'] = [];
24 | $this->params['news']['articles'] = [];
25 | foreach ($news_items as $item) {
26 | $this->params['news']['articles'][] = $item->getParams();
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Message/NewsItem.php:
--------------------------------------------------------------------------------
1 | params['title'] = $title;
23 | $this->params['description'] = $description;
24 | $this->params['url'] = $url;
25 | $this->params['picurl'] = $pic_url;
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Message/Text.php:
--------------------------------------------------------------------------------
1 | params['msgtype'] = 'text';
24 | $this->params['text'] = [];
25 | $this->params['text']['content'] = $text;
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Message/Video.php:
--------------------------------------------------------------------------------
1 | params['msgtype'] = 'video';
26 | $this->params['video'] = [];
27 | $this->params['video']['media_id'] = $media_id;
28 | $this->params['video']['thumb_media_id'] = $thumb_media_id;
29 | $this->params['video']['title'] = $title;
30 | $this->params['video']['description'] = $description;
31 |
32 | }
33 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Message/Voice.php:
--------------------------------------------------------------------------------
1 | params['msgtype'] = 'voice';
23 | $this->params['voice'] = [];
24 | $this->params['voice']['content'] = $media_id;
25 | }
26 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/QrCode.php:
--------------------------------------------------------------------------------
1 | $expire_seconds,
30 | 'action_name' => 'QR_LIMIT_SCENE',
31 | 'scene' => ['scene_id' => $scene_id]
32 | ];
33 | return $this->request_post(self::API_CREATE, $params);
34 | }
35 |
36 | /**
37 | * 创建临时二维码
38 | * @param $expire_seconds
39 | * @param $scene_id
40 | * @return bool|mixed
41 | */
42 | public function createTemp($expire_seconds, $scene_id)
43 | {
44 | $params = [
45 | 'expire_seconds' => $expire_seconds,
46 | 'action_name' => 'QR_SCENE',
47 | 'scene' => ['scene_id' => $scene_id]
48 | ];
49 | return $this->request_post(self::API_CREATE, $params);
50 | }
51 |
52 | /**
53 | * 下载二维码
54 | * @param $ticket
55 | * @return mixed
56 | * @throws \Jenner\Wechat\Exception\WechatException
57 | */
58 | public function download($ticket)
59 | {
60 | $ticket = urlencode($ticket);
61 | $http = new Http(self::API_DOWNLOAD);
62 | $image = $http->GET(compact('ticket'));
63 | if ($http->getStatus() != 200) {
64 | $message = 'download qrcode image failed. the ticket param error';
65 | throw new WechatException($message);
66 | }
67 |
68 | return $image;
69 | }
70 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/Semantic.php:
--------------------------------------------------------------------------------
1 | WECHAT_APP_ID,
17 | 'uid' => $uid,
18 | 'category' => $category,
19 |
20 | ];
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/ShortUrl.php:
--------------------------------------------------------------------------------
1 | 'long2short',
26 | 'long_url' => $long_url,
27 | ];
28 | return $this->request_post(self::API_URL, $param);
29 | }
30 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/System.php:
--------------------------------------------------------------------------------
1 | request_get(self::API_CALLBACK_IP);
21 |
22 | return $result;
23 | }
24 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/User.php:
--------------------------------------------------------------------------------
1 | $openid, 'remark' => $remark];
27 | $result = $this->request_get(self::API_UPDATE_REMARK, $params);
28 |
29 | return $result;
30 | }
31 |
32 | /**
33 | * 获取用户基本信息
34 | * @param $openid
35 | * @param string $lang
36 | * @return bool|mixed
37 | */
38 | public function info($openid, $lang = 'zh_CN')
39 | {
40 | $params = ['openid' => $openid, 'lang' => $lang];
41 | $response = $this->request_get(self::API_INFO, $params);
42 |
43 | return $response;
44 | }
45 |
46 | /**
47 | * 获取全部关注列表的OPEN_ID
48 | * @return mixed
49 | */
50 | public function getAll()
51 | {
52 | $data = $openid_list = [];
53 | while (true) {
54 | if ($data) {
55 | $data = $this->request_get(self::API_GET, ['next_openid' => $data['next_openid']]);
56 | } else {
57 | $data = $this->request_get(self::API_GET);
58 | }
59 | $openid_list = array_merge($openid_list, $data['data']['openid']);
60 | if (empty($data['next_openid'])) {
61 | break;
62 | }
63 | }
64 | $result['count'] = count($openid_list);
65 | $result['total'] = $data['total'];
66 |
67 | return $result;
68 | }
69 | }
70 |
71 |
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Client/UserGroup.php:
--------------------------------------------------------------------------------
1 | request_post(self::API_CREATE, $group);
33 | }
34 |
35 | /**
36 | * 获取分组
37 | * @return bool|mixed
38 | */
39 | public function get()
40 | {
41 | return $this->request_get(self::API_GET);
42 | }
43 |
44 | /**
45 | * 根据OPEN_ID获取用户所属分组
46 | * @param $open_id
47 | * @return bool|mixed
48 | */
49 | public function getByOpenId($open_id)
50 | {
51 | return $this->request_post(self::API_GET_ID, ['openid' => $open_id]);
52 | }
53 |
54 | /**
55 | * 更新分组名称
56 | * @param $group_id
57 | * @param $name
58 | * @return bool|mixed
59 | */
60 | public function update($group_id, $name)
61 | {
62 | $params = ['group' => ['id' => $group_id, 'name' => $name]];
63 | return $this->request_post(self::API_UPDATE, $params);
64 | }
65 |
66 | /**
67 | * 移动用户分组
68 | * @param $open_id
69 | * @param $to_group_id
70 | * @return bool|mixed
71 | */
72 | public function userGroupUpdate($open_id, $to_group_id)
73 | {
74 | $params = ['openid' => $open_id, 'to_group_id' => $to_group_id];
75 | return $this->request_post(self::API_MEMBER_UPDATE, $params);
76 | }
77 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Coding/ErrorCode.php:
--------------------------------------------------------------------------------
1 |
15 | *
-40001: 签名验证错误
16 | * -40002: xml解析失败
17 | * -40003: sha加密生成签名失败
18 | * -40004: encodingAesKey 非法
19 | * -40005: appid 校验错误
20 | * -40006: aes 加密失败
21 | * -40007: aes 解密失败
22 | * -40008: 解密后得到的buffer非法
23 | * -40009: base64加密失败
24 | * -40010: base64解密失败
25 | * -40011: 生成xml失败
26 | *
27 | */
28 | class ErrorCode
29 | {
30 | public static $OK = 0;
31 | public static $ValidateSignatureError = -40001;
32 | public static $ParseXmlError = -40002;
33 | public static $ComputeSignatureError = -40003;
34 | public static $IllegalAesKey = -40004;
35 | public static $ValidateAppidError = -40005;
36 | public static $EncryptAESError = -40006;
37 | public static $DecryptAESError = -40007;
38 | public static $IllegalBuffer = -40008;
39 | public static $EncodeBase64Error = -40009;
40 | public static $DecodeBase64Error = -40010;
41 | public static $GenReturnXmlError = -40011;
42 | }
43 |
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Coding/PKCS7Encoder.php:
--------------------------------------------------------------------------------
1 | 32) {
53 | $pad = 0;
54 | }
55 | return substr($text, 0, (strlen($text) - $pad));
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Coding/Prpcrypt.php:
--------------------------------------------------------------------------------
1 | key = base64_decode($k . "=");
24 | }
25 |
26 | /**
27 | * 对明文进行加密
28 | * @param string $text 需要加密的明文
29 | * @return string 加密后的密文
30 | */
31 | public function encrypt($text, $appid)
32 | {
33 |
34 | try {
35 | //获得16位随机字符串,填充到明文之前
36 | $random = $this->getRandomStr();
37 | $text = $random . pack("N", strlen($text)) . $text . $appid;
38 | // 网络字节序
39 | $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
40 | $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
41 | $iv = substr($this->key, 0, 16);
42 | //使用自定义的填充方式对明文进行补位填充
43 | $pkc_encoder = new PKCS7Encoder;
44 | $text = $pkc_encoder->encode($text);
45 | mcrypt_generic_init($module, $this->key, $iv);
46 | //加密
47 | $encrypted = mcrypt_generic($module, $text);
48 | mcrypt_generic_deinit($module);
49 | mcrypt_module_close($module);
50 |
51 | //print(base64_encode($encrypted));
52 | //使用BASE64对加密后的字符串进行编码
53 | return array(ErrorCode::$OK, base64_encode($encrypted));
54 | } catch (Exception $e) {
55 | //print $e;
56 | return array(ErrorCode::$EncryptAESError, null);
57 | }
58 | }
59 |
60 | /**
61 | * 对密文进行解密
62 | * @param string $encrypted 需要解密的密文
63 | * @return string 解密得到的明文
64 | */
65 | public function decrypt($encrypted, $appid)
66 | {
67 |
68 | try {
69 | //使用BASE64对需要解密的字符串进行解码
70 | $ciphertext_dec = base64_decode($encrypted);
71 | $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
72 | $iv = substr($this->key, 0, 16);
73 | mcrypt_generic_init($module, $this->key, $iv);
74 |
75 | //解密
76 | $decrypted = mdecrypt_generic($module, $ciphertext_dec);
77 | mcrypt_generic_deinit($module);
78 | mcrypt_module_close($module);
79 | } catch (Exception $e) {
80 | return array(ErrorCode::$DecryptAESError, null);
81 | }
82 |
83 |
84 | try {
85 | //去除补位字符
86 | $pkc_encoder = new PKCS7Encoder;
87 | $result = $pkc_encoder->decode($decrypted);
88 | //去除16位随机字符串,网络字节序和AppId
89 | if (strlen($result) < 16)
90 | return "";
91 | $content = substr($result, 16, strlen($result));
92 | $len_list = unpack("N", substr($content, 0, 4));
93 | $xml_len = $len_list[1];
94 | $xml_content = substr($content, 4, $xml_len);
95 | $from_appid = substr($content, $xml_len + 4);
96 | } catch (Exception $e) {
97 | //print $e;
98 | return array(ErrorCode::$IllegalBuffer, null);
99 | }
100 | if ($from_appid != $appid)
101 | return array(ErrorCode::$ValidateAppidError, null);
102 | return array(0, $xml_content);
103 |
104 | }
105 |
106 |
107 | /**
108 | * 随机生成16位字符串
109 | * @return string 生成的字符串
110 | */
111 | function getRandomStr()
112 | {
113 |
114 | $str = "";
115 | $str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
116 | $max = strlen($str_pol) - 1;
117 | for ($i = 0; $i < 16; $i++) {
118 | $str .= $str_pol[mt_rand(0, $max)];
119 | }
120 | return $str;
121 | }
122 |
123 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Coding/SHA1.php:
--------------------------------------------------------------------------------
1 | token = $token;
31 | $this->encodingAesKey = $encodingAesKey;
32 | $this->appId = $appId;
33 | }
34 |
35 | /**
36 | * 将公众平台回复用户的消息加密打包.
37 | *
38 | * - 对要发送的消息进行AES-CBC加密
39 | * - 生成安全签名
40 | * - 将消息密文和安全签名打包成xml格式
41 | *
42 | *
43 | * @param $replyMsg string 公众平台待回复用户的消息,xml格式的字符串
44 | * @param $timeStamp string 时间戳,可以自己生成,也可以用URL参数的timestamp
45 | * @param $nonce string 随机串,可以自己生成,也可以用URL参数的nonce
46 | * @param &$encryptMsg string 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,
47 | * 当return返回0时有效
48 | *
49 | * @return int 成功0,失败返回对应的错误码
50 | */
51 | public function encryptMsg($replyMsg, $timeStamp, $nonce, &$encryptMsg)
52 | {
53 | $pc = new Prpcrypt($this->encodingAesKey);
54 |
55 | //加密
56 | $array = $pc->encrypt($replyMsg, $this->appId);
57 | $ret = $array[0];
58 | if ($ret != 0) {
59 | return $ret;
60 | }
61 |
62 | if ($timeStamp == null) {
63 | $timeStamp = time();
64 | }
65 | $encrypt = $array[1];
66 |
67 | //生成安全签名
68 | $sha1 = new SHA1;
69 | $array = $sha1->getSHA1($this->token, $timeStamp, $nonce, $encrypt);
70 | $ret = $array[0];
71 | if ($ret != 0) {
72 | return $ret;
73 | }
74 | $signature = $array[1];
75 |
76 | //生成发送的xml
77 | $xmlparse = new XMLParse;
78 | $encryptMsg = $xmlparse->generate($encrypt, $signature, $timeStamp, $nonce);
79 | return ErrorCode::$OK;
80 | }
81 |
82 |
83 | /**
84 | * 检验消息的真实性,并且获取解密后的明文.
85 | *
86 | * - 利用收到的密文生成安全签名,进行签名验证
87 | * - 若验证通过,则提取xml中的加密消息
88 | * - 对消息进行解密
89 | *
90 | *
91 | * @param $msgSignature string 签名串,对应URL参数的msg_signature
92 | * @param $timestamp string 时间戳 对应URL参数的timestamp
93 | * @param $nonce string 随机串,对应URL参数的nonce
94 | * @param $postData string 密文,对应POST请求的数据
95 | * @param &$msg string 解密后的原文,当return返回0时有效
96 | *
97 | * @return int 成功0,失败返回对应的错误码
98 | */
99 | public function decryptMsg($msgSignature, $timestamp = null, $nonce, $postData, &$msg)
100 | {
101 | if (strlen($this->encodingAesKey) != 43) {
102 | return ErrorCode::$IllegalAesKey;
103 | }
104 |
105 | $pc = new Prpcrypt($this->encodingAesKey);
106 |
107 | //提取密文
108 | $xmlparse = new XMLParse;
109 | $array = $xmlparse->extract($postData);
110 | $ret = $array[0];
111 |
112 | if ($ret != 0) {
113 | return $ret;
114 | }
115 |
116 | if ($timestamp == null) {
117 | $timestamp = time();
118 | }
119 |
120 | $encrypt = $array[1];
121 | $touser_name = $array[2];
122 |
123 | //验证安全签名
124 | $sha1 = new SHA1;
125 | $array = $sha1->getSHA1($this->token, $timestamp, $nonce, $encrypt);
126 | $ret = $array[0];
127 |
128 | if ($ret != 0) {
129 | return $ret;
130 | }
131 |
132 | $signature = $array[1];
133 | if ($signature != $msgSignature) {
134 | return ErrorCode::$ValidateSignatureError;
135 | }
136 |
137 | $result = $pc->decrypt($encrypt, $this->appId);
138 | if ($result[0] != 0) {
139 | return $result[0];
140 | }
141 | $msg = $result[1];
142 |
143 | return ErrorCode::$OK;
144 | }
145 |
146 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Config/Event.php:
--------------------------------------------------------------------------------
1 | redirectToWechat($redirect_uri, 'snsapi_base');
40 | }
41 |
42 | /**
43 | * 跳转到用户认证页面
44 | * @param $redirect_uri
45 | */
46 | public function userInfoRedirect($redirect_uri)
47 | {
48 | $this->redirectToWechat($redirect_uri, 'snsapi_userinfo');
49 | }
50 |
51 | //跳转到微信认证
52 | protected function redirectToWechat($redirect_uri, $scope = 'snsapi_base')
53 | {
54 | $response_type = 'code';
55 | $redirect_uri = WechatConfig::REDIRECT_AUTH . '?appid='
56 | . WECHAT_APP_ID . '&redirect_uri=' . urlencode($redirect_uri)
57 | . '&response_type=' . $response_type
58 | . '&scope=' . $scope . '#wechat_redirect';
59 |
60 | header('Location:' . $redirect_uri);
61 | }
62 |
63 | public function baseInfo($code)
64 | {
65 | $params = [
66 | 'appid' => WECHAT_APP_ID,
67 | 'secret' => WECHAT_SECRET,
68 | 'code' => $code,
69 | 'grant_type' => 'authorization_code',
70 | ];
71 | $uri = WechatConfig::REDIRECT_TOKEN;
72 | $http = new Http($uri);
73 | $response_json = $http->GET($params);
74 |
75 | return $this->checkResponse($response_json);
76 | }
77 |
78 | public function userInfo($access_token, $openid, $lang = 'zh_CN')
79 | {
80 | $params = [
81 | 'access_token' => $access_token,
82 | 'openid' => $openid,
83 | 'lang' => $lang,
84 | ];
85 | $uri = WechatConfig::REDIRECT_USER_INFO;
86 | $http = new Http($uri);
87 | $response_json = $http->GET($params);
88 |
89 | $response = json_decode($response_json, true);
90 | if (isset($response['errcode'])) {
91 | throw new ResponseErrorException($response['errmsg'], $response['errcode']);
92 | }
93 |
94 | return $response;
95 | }
96 |
97 | /**
98 | * 检查微信响应是否出错,如果出错,抛出异常
99 | * @param $response_json
100 | * @return mixed
101 | * @throws \Jenner\Wechat\Exception\ResponseErrorException
102 | */
103 | public function checkResponse($response_json)
104 | {
105 | $response = json_decode($response_json, true);
106 | if (isset($response['errcode'])) {
107 | throw new ResponseErrorException($response['errmsg'], $response['errcode']);
108 | }
109 |
110 | return $response;
111 | }
112 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Request/XmlRequest.php:
--------------------------------------------------------------------------------
1 | $val) {
26 | $val = (is_array($val)) || is_object($val) ? self::object_to_array($val) : $val;
27 | $arr[$key] = $val;
28 | }
29 |
30 | return $_arr;
31 | }
32 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Response/AbstractXml.php:
--------------------------------------------------------------------------------
1 | replace['ToUserName'] = $to_user;
35 | $this->replace['FromUserName'] = $from_user;
36 | $this->initTemplate();
37 | }
38 |
39 | /**
40 | * 生成响应主体
41 | * @return mixed
42 | */
43 | abstract protected function initTemplate();
44 |
45 | /**
46 | * @return mixed
47 | */
48 | public function create()
49 | {
50 | $this->replace['CreateTime'] = time();
51 | $replace_keys = array_keys($this->replace);
52 | $response = $this->template;
53 | foreach ($replace_keys as $key) {
54 | //字符串转换,使其支持下划线、中划线写法
55 | if (strstr($key, '-') || strstr($key, '_')) {
56 | $search = '{' . $this->studlyCase($key) . '}';
57 | } else {
58 | $search = '{' . $key . '}';
59 | }
60 | if (!strstr($response, $search)) continue;
61 | $response = str_replace($search, $this->replace[$key], $response);
62 | }
63 |
64 | return $response;
65 | }
66 |
67 | /**
68 | * 将foo_bar foo-bar格式的字符串转换为FooBar
69 | * @param $value
70 | * @return mixed
71 | */
72 | protected function studlyCase($value)
73 | {
74 | $value = ucwords(str_replace(array('-', '_'), ' ', $value));
75 |
76 | return str_replace(' ', '', $value);
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Response/Image.php:
--------------------------------------------------------------------------------
1 | replace['MediaId'] = $media_id;
19 | }
20 |
21 | protected function initTemplate()
22 | {
23 | $this->template = <<
25 |
26 |
27 | {CreateTime}
28 |
29 |
30 |
31 |
32 |
33 | XML;
34 | }
35 | }
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Response/Media.php:
--------------------------------------------------------------------------------
1 | replace['MediaId'] = $media_id;
19 | }
20 |
21 | protected function initTemplate()
22 | {
23 | $this->template = <<
25 |
26 |
27 | {CreateTime}
28 |
29 |
30 |
31 |
32 |
33 | XML;
34 | }
35 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Response/Music.php:
--------------------------------------------------------------------------------
1 | replace['Title'] = $title;
19 | $this->replace['Description'] = $description;
20 | $this->replace['MusicUrl'] = $music_url;
21 | $this->replace['HQMusicUrl'] = $hq_music_url;
22 | $this->replace['ThumbMediaId'] = $thumb_media_id;
23 | }
24 |
25 | protected function initTemplate()
26 | {
27 | $this->template = <<
29 |
30 |
31 | {CreateTime}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | XML;
42 |
43 | }
44 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Response/News.php:
--------------------------------------------------------------------------------
1 | items = $items;
24 | }
25 |
26 | public function create()
27 | {
28 | $response = $this->create();
29 | $item_response = '';
30 | foreach ($this->items as $item) {
31 | $item_response .= $item->create();
32 | }
33 | $response = str_replace('{item}', $item_response, $response);
34 |
35 | return $response;
36 | }
37 |
38 | protected function initTemplate()
39 | {
40 | $this->template = <<
42 |
43 |
44 | {CreateTime}
45 |
46 | {ArticleCount}
47 |
48 | {item}
49 |
50 |
51 | XML;
52 | }
53 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Response/NewsItem.php:
--------------------------------------------------------------------------------
1 | replace['Title'] = $title;
18 | $this->replace['Description'] = $description;
19 | $this->replace['PicUrl'] = $pic_url;
20 | $this->replace['Url'] = $url;
21 | }
22 |
23 | /**
24 | * @return mixed
25 | */
26 | protected function initTemplate()
27 | {
28 | $this->item_template = <<
30 |
31 |
32 |
33 |
34 |
35 | XML;
36 |
37 | }
38 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Response/Text.php:
--------------------------------------------------------------------------------
1 | replace['Content'] = $content;
18 | }
19 |
20 | protected function initTemplate()
21 | {
22 | $this->template = <<
24 |
25 |
26 | {CreateTime}
27 |
28 |
29 |
30 | XML;
31 | }
32 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Response/Voice.php:
--------------------------------------------------------------------------------
1 | replace['MediaId'] = $media_id;
18 | }
19 |
20 | protected function initTemplate()
21 | {
22 | $this->template = <<
24 |
25 |
26 | {CreateTime}
27 |
28 |
29 |
30 |
31 |
32 | XML;
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Server.php:
--------------------------------------------------------------------------------
1 | send方法向微信返回消息
26 | * $request是微信请求的信息,
27 | * 同时你也可以通过$server->getRequest方法读取request中的信息,另种方式任你选择
28 | * after函数接收两个参数,WechatServer $server, $result,$result为事件处理函数的返回值
29 | *
30 | * 微信消息推送列表(不区分大小写):
31 | * text
32 | * image
33 | * voice
34 | * location
35 | * link
36 | *
37 | *
38 | * 微信事件推送列表
39 | * subscribe 关注事件
40 | * unsubscribe 取消关注事件
41 | * SCAN 扫描带参数二维码事件
42 | * LOCATION 上报地理位置事件
43 | * CLICK 点击菜单拉取消息时的事件推送
44 | * VIEW 点击菜单跳转链接时的事件推送
45 | * scancode_push 扫码推事件的事件推送
46 | * scancode_waitmsg 扫码推事件且弹出“消息接收中”提示框的事件推送
47 | * pic_sysphoto 弹出系统拍照发图的事件推送
48 | * pic_photo_or_album 弹出拍照或者相册发图的事件推送
49 | * pic_weixin 弹出微信相册发图器的事件推送
50 | * location_select 弹出地理位置选择器的事件推送
51 | * merchant_order 订单付款时间
52 | *
53 | * card_pass_check 生成的卡券通过审核
54 | * card_not_pass_check 卡券未通过审核
55 | * user_get_card 用户领取卡券
56 | * user_del_card 用户删除卡券
57 | *
58 | * 自定义事件
59 | * unknown_event 未知事件推送
60 | * unknown_message 未知消息推送
61 | *
62 | * Class WechatServer
63 | * @package Jenner\Wechat
64 | */
65 | class WechatServer
66 | {
67 |
68 | const UNKNOWN_EVENT = 'unknown_event'; //未知事件推送
69 | const UNKNOWN_MESSAGE = 'unknown_message'; //未知消息推送
70 |
71 | const SUBSCRIBE = 'subscribe'; //关注事件
72 | const UNSUBSCRIBE = 'unsubscribe'; //取消关注事件
73 | const SCAN = 'SCAN'; //扫描带参数二维码事件
74 | const LOCATION = 'LOCATION'; //上报地理位置事件
75 | const CLICK = 'CLICK'; //点击菜单拉取消息时的事件推送
76 | const VIEW = 'VIEW'; //点击菜单跳转链接时的事件推送
77 | const SCANCODE_PUSH = 'scancode_push'; //扫码推事件的事件推送
78 | const SCANCODE_WAITMSG = 'scancode_waitmsg'; //扫码推事件且弹出“消息接收中”提示框的事件推送
79 | const PIC_SYSPHOTO = 'pic_sysphoto'; //弹出系统拍照发图的事件推送
80 | const PIC_PHOTO_OR_ALBUM = 'pic_photo_or_album'; //弹出拍照或者相册发图的事件推送
81 | const PIC_WEIXIN = 'pic_weixin'; //弹出微信相册发图器的事件推送
82 | const LOCATION_SELECT = 'location_select'; //弹出地理位置选择器的事件推送
83 | const MERCHANT_ORDER = 'merchant_order'; //订单付款时间
84 | const CARD_PASS_CHECK = 'card_pass_check'; //生成的卡券通过审核
85 | const CARD_NOT_PASS_CHECK = 'card_not_pass_check'; //卡券未通过审核
86 | const USER_GET_CARD = 'user_get_card'; //用户领取卡券
87 | const USER_DEL_CARD = 'user_del_card'; //用户删除卡券
88 |
89 | /**
90 | * 微信账号的token,验证消息真实性时需要用到
91 | */
92 | protected $token;
93 |
94 | /**
95 | * 微信发送的请求包,数组格式,下标统一转换为了小写
96 | */
97 | protected $request;
98 |
99 |
100 | /**
101 | * 微信推送回调函数数组
102 | */
103 | protected $callback;
104 |
105 | /**
106 | * 构造函数
107 | * @param $token 微信账号的token
108 | */
109 | public function __construct($token)
110 | {
111 | $this->token = $token;
112 | }
113 |
114 | /**
115 | * 注册推送事件回调函数
116 | * @param $event
117 | * @param $callback
118 | */
119 | public function on($event, $callback)
120 | {
121 | $event = strtolower($event);
122 | $this->callback[$event] = $callback;
123 | }
124 |
125 | /**
126 | * 解除推送事件回调函数
127 | * @param $event
128 | */
129 | public function off($event)
130 | {
131 | $event = strtolower($event);
132 | unset($this->callback[$event]);
133 | }
134 |
135 | /**
136 | * 微信消息、时间推送执行入口
137 | */
138 | public function start()
139 | {
140 | //验证是否为微信验证服务器
141 | if ($this->isValid()) {
142 | echo $_GET['echostr'];
143 | return;
144 | }
145 |
146 | //验证是否是微信发来的请求
147 | if (!$this->validateSignature($this->token)) {
148 | throw new WechatException('wechat request Illegal');
149 | }
150 |
151 | if (!isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
152 | throw new WechatException('HTTP_RAW_POST_DATA empty');
153 | }
154 |
155 | $request_xml = $GLOBALS['HTTP_RAW_POST_DATA'];
156 | $request = XmlRequest::toArray($request_xml);
157 | //将下标统一转换为小写,获取信息统一使用$this->getRequest('field_name');
158 | $this->request = array_change_key_case($request, CASE_LOWER);
159 |
160 | //调用before回调
161 | if (!empty($this->callback['before']) && is_callable($this->callback['before'])) {
162 | $result = call_user_func($this->callback['before'], $this, $request);
163 | }
164 |
165 | //处理事件推送
166 | if ($this->getMsgType() == 'event') {
167 |
168 | //处理全局事件回调
169 | if (!empty($this->callback['before_event']) && is_callable($this->callback['before_event'])) {
170 | $result = call_user_func($this->callback['before_event'], $this, $request);
171 | }
172 |
173 | //处理事件回调
174 | $event_type = $this->getEvent();
175 | $event_type = strtolower($event_type);
176 | if (!empty($this->callback[$event_type]) && is_callable($this->callback[$event_type])) {
177 | $result = call_user_func($this->callback[$event_type], $this, $request);
178 | } else {
179 | //未定义时间回调处理
180 | if (!empty($this->callback['unknown_event']) && is_callable($this->callback['unknown_event'])) {
181 | $result = call_user_func($this->callback['unknown_event'], $this, $request);
182 | }
183 | }
184 | } else {
185 |
186 | //处理全局消息推送回调
187 | if (!empty($this->callback['before_message']) && is_callable($this->callback['before_message'])) {
188 | $result = call_user_func($this->callback['before_message'], $this, $request);
189 | }
190 |
191 | //处理消息推送回调
192 | $message_type = $this->getMsgType();
193 | $message_type = strtolower($message_type);
194 | if (!empty($this->callback[$message_type]) && is_callable($this->callback[$message_type])) {
195 | $result = call_user_func($this->callback[$message_type], $this, $request);
196 | } else {
197 | //处理未知消息推送回调
198 | if (!empty($this->callback['unknown_message']) && is_callable($this->callback['unknown_message'])) {
199 | $result = call_user_func($this->callback['unknown_message'], $this, $request);
200 | }
201 | }
202 | }
203 |
204 | //全局处理结束回调
205 | if (!empty($this->callback['after']) && is_callable($this->callback['after'])) {
206 | call_user_func($this->callback['after'], $this, $result);
207 | }
208 |
209 | }
210 |
211 | /**
212 | * 向服务器发送消息
213 | * @param AbstractXml $response
214 | */
215 | public function send(AbstractXml $response)
216 | {
217 | $message = $response->create();
218 | echo $message;
219 | return;
220 | }
221 |
222 | /**
223 | * 获取本次请求中的参数,不区分大小
224 | *
225 | * @param bool|string $param 参数名,默认为无参
226 | * @return mixed
227 | */
228 | public function getRequest($param = FALSE)
229 | {
230 | if ($param === false) {
231 | return $this->request;
232 | }
233 | $param = strtolower($param);
234 | if (isset($this->request[$param])) {
235 | return $this->request[$param];
236 | }
237 | return null;
238 | }
239 |
240 | /**
241 | * 获取消息发送者open_id
242 | * @return mixed
243 | */
244 | public function getFromUserName()
245 | {
246 | return $this->getRequest('FromUserName');
247 | }
248 |
249 | /**
250 | * 获取消息接受者(一般是自己)的open_id
251 | * @return mixed
252 | */
253 | public function getToUserName()
254 | {
255 | return $this->getRequest('ToUserName');
256 | }
257 |
258 | /**
259 | * 获取消息创建时间
260 | * @return mixed
261 | */
262 | public function getCreateTime()
263 | {
264 | return $this->getRequest('CreateTime');
265 | }
266 |
267 | /**
268 | * 获取消息类型
269 | * @return mixed
270 | */
271 | public function getMsgType()
272 | {
273 | return $this->getRequest('MsgType');
274 | }
275 |
276 | /**
277 | * 获取事件名称
278 | * @return mixed
279 | */
280 | public function getEvent()
281 | {
282 | return $this->getRequest('Event');
283 | }
284 |
285 | /**
286 | * 获取消息ID
287 | * @return mixed
288 | */
289 | public function getMsgId()
290 | {
291 | return $this->getRequest('MsgId');
292 | }
293 |
294 | /**
295 | * 验证消息真实性
296 | *
297 | * @param string $token 验证信息
298 | * @return boolean
299 | */
300 | public function validateSignature($token)
301 | {
302 | if (!(isset($_GET['signature']) && isset($_GET['timestamp']) && isset($_GET['nonce']))) {
303 | return false;
304 | }
305 |
306 | $signature = $_GET['signature'];
307 | $timestamp = $_GET['timestamp'];
308 | $nonce = $_GET['nonce'];
309 | $signatureArray = [$token, $timestamp, $nonce];
310 | sort($signatureArray, SORT_STRING);
311 | return sha1(implode($signatureArray)) == $signature;
312 | }
313 |
314 | /**
315 | * 修改微信API配置时,微信会发送验证到URL
316 | * 判断此次请求是否为验证请求
317 | * 如果是,你需要在上层直接输出原始的echostr并返回
318 | *
319 | * @return boolean
320 | */
321 | public function isValid()
322 | {
323 | return isset($_GET['echostr']);
324 | }
325 | }
326 |
--------------------------------------------------------------------------------
/src/Jenner/Wechat/Tool/Http.php:
--------------------------------------------------------------------------------
1 | url = $url;
40 | $this->timeOut = $timeOut;
41 | $this->connectTimeout = $connectTimeout;
42 | //代理服务器I的设置
43 | if ($proxyIp) {
44 | $this->proxyIp = $proxyIp;
45 | $this->proxyPort = $proxyPort ? $proxyPort : 80;
46 | }
47 | }
48 |
49 | /**
50 | * 设置代理服务器
51 | * @param $ip
52 | * @param int $port
53 | */
54 | public function setProxy($ip, $port = 80)
55 | {
56 | if ($ip) {
57 | $this->proxyIp = $ip;
58 | $this->proxyPort = $port;
59 | }
60 | }
61 |
62 | /**
63 | * GET请求
64 | * @param null $params
65 | * @return mixed
66 | */
67 | public function GET($params = null)
68 | {
69 | //组合带参数的URL
70 | $url = &$this->url;
71 | if ($params && is_array($params)) {
72 | $url .= '?';
73 | $amp = '';
74 | foreach ($params as $paramKey => $paramValue) {
75 | $url .= $amp . $paramKey . '=' . urlencode($paramValue);
76 | $amp = '&';
77 | }
78 | }
79 | //初始化curl
80 | $curl = curl_init();
81 | $this->initProxy($curl);
82 | $this->initCurlParam($curl);
83 | $content = curl_exec($curl);
84 | $this->httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
85 | curl_close($curl);
86 | return $content;
87 | }
88 |
89 | /**
90 | * POST请求,支持文件上传
91 | * 文件上传的params格式['key'=>'@file_path/filename']
92 | * @param null $params
93 | * @param bool $fileUpload
94 | * @return mixed
95 | */
96 | public function POST($params = null, $fileUpload = false)
97 | {
98 | //初始化curl
99 | $curl = curl_init();
100 | $this->initProxy($curl);
101 | $this->initCurlParam($curl);
102 | //设置POST参数
103 | curl_setopt($curl, CURLOPT_POST, 1);
104 | if ($params && is_array($params)) {
105 | if ($fileUpload) {
106 | curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
107 | } else {
108 | $amp = '';
109 | $postFields = '';
110 | foreach ($params as $paramKey => $paramValue) {
111 | $postFields .= $amp . $paramKey . '=' . urlencode($paramValue);
112 | $amp = '&';
113 | }
114 | curl_setopt($curl, CURLOPT_POSTFIELDS, $postFields);
115 | }
116 | } elseif ($params) {
117 | curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
118 | }
119 | $content = curl_exec($curl);
120 | $this->httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
121 | curl_close($curl);
122 | return $content;
123 | }
124 |
125 | /**
126 | * 获取HTTP状态码
127 | * @return mixed
128 | */
129 | public function getStatus()
130 | {
131 | return $this->httpStatus;
132 | }
133 |
134 | /**
135 | * 初始化代理
136 | * @param $curl
137 | */
138 | private function initProxy($curl)
139 | {
140 | if ($this->proxyIp && $this->proxyPort) {
141 | $proxy = "http://{$this->proxyIp}:{$this->proxyPort}";
142 | curl_setopt($curl, CURLOPT_PROXY, $proxy);
143 | }
144 | }
145 |
146 | /**
147 | * 初始化CURL参数
148 | * @param $curl
149 | */
150 | private function initCurlParam($curl)
151 | {
152 | curl_setopt($curl, CURLOPT_URL, $this->url);
153 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
154 | curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
155 | curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $this->connectTimeout);
156 | curl_setopt($curl, CURLOPT_TIMEOUT, $this->timeOut);
157 | }
158 | }
--------------------------------------------------------------------------------
/tests/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "authors": [
3 | {
4 | "name": "Jenner",
5 | "email": "hypxm@qq.com",
6 | "homepage" : "http://www.huyanping.cn"
7 | }
8 | ],
9 | "require": {
10 | "jenner/wechat": "dev-master",
11 | "php": ">=5.3.0"
12 | }
13 | }
--------------------------------------------------------------------------------
/tests/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 | "This file is @generated automatically"
6 | ],
7 | "hash": "0527fd011fa5d183f1a458882cb8af21",
8 | "packages": [
9 | {
10 | "name": "jenner/tools",
11 | "version": "v1.2.0",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/huyanping/Zebra-Tools.git",
15 | "reference": "bfd0533f8071eaf12778f3e1a47a3bab9dfd7e09"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/huyanping/Zebra-Tools/zipball/bfd0533f8071eaf12778f3e1a47a3bab9dfd7e09",
20 | "reference": "bfd0533f8071eaf12778f3e1a47a3bab9dfd7e09",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "php": ">=5.3.0"
25 | },
26 | "type": "library",
27 | "autoload": {
28 | "psr-0": {
29 | "Jenner\\Zebra\\Tools": "src/"
30 | }
31 | },
32 | "notification-url": "https://packagist.org/downloads/",
33 | "license": [
34 | "MIT"
35 | ],
36 | "authors": [
37 | {
38 | "name": "Jenner",
39 | "email": "hypxm@qq.com"
40 | }
41 | ],
42 | "description": "common static functions",
43 | "keywords": [
44 | "tools",
45 | "utils"
46 | ],
47 | "time": "2014-11-17 08:15:54"
48 | },
49 | {
50 | "name": "jenner/wechat",
51 | "version": "dev-master",
52 | "source": {
53 | "type": "git",
54 | "url": "git@github.com:huyanping/Zebra-Wechat.git",
55 | "reference": "cde2c63f90c976dd3be4611cabb1948448708dc9"
56 | },
57 | "dist": {
58 | "type": "zip",
59 | "url": "https://api.github.com/repos/huyanping/Zebra-Wechat/zipball/cde2c63f90c976dd3be4611cabb1948448708dc9",
60 | "reference": "cde2c63f90c976dd3be4611cabb1948448708dc9",
61 | "shasum": ""
62 | },
63 | "require": {
64 | "jenner/tools": "1.2.0",
65 | "php": ">=5.3.0"
66 | },
67 | "type": "library",
68 | "autoload": {
69 | "psr-0": {
70 | "Jenner\\Zebra\\Wechat": "src/"
71 | }
72 | },
73 | "notification-url": "https://packagist.org/downloads/",
74 | "license": [
75 | "MIT"
76 | ],
77 | "authors": [
78 | {
79 | "name": "Jenner",
80 | "email": "hypxm@qq.com"
81 | }
82 | ],
83 | "description": "wechat api client",
84 | "keywords": [
85 | "api",
86 | "wechat"
87 | ],
88 | "time": "2014-12-14 04:06:52"
89 | }
90 | ],
91 | "packages-dev": [],
92 | "aliases": [],
93 | "minimum-stability": "stable",
94 | "stability-flags": {
95 | "jenner/wechat": 20
96 | },
97 | "prefer-stable": false,
98 | "platform": {
99 | "php": ">=5.3.0"
100 | },
101 | "platform-dev": []
102 | }
103 |
--------------------------------------------------------------------------------
/tests/vendor/autoload.php:
--------------------------------------------------------------------------------
1 |
7 | * Jordi Boggiano
8 | *
9 | * For the full copyright and license information, please view the LICENSE
10 | * file that was distributed with this source code.
11 | */
12 |
13 | namespace Composer\Autoload;
14 |
15 | /**
16 | * ClassLoader implements a PSR-0 class loader
17 | *
18 | * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
19 | *
20 | * $loader = new \Composer\Autoload\ClassLoader();
21 | *
22 | * // register classes with namespaces
23 | * $loader->add('Symfony\Component', __DIR__.'/component');
24 | * $loader->add('Symfony', __DIR__.'/framework');
25 | *
26 | * // activate the autoloader
27 | * $loader->register();
28 | *
29 | * // to enable searching the include path (eg. for PEAR packages)
30 | * $loader->setUseIncludePath(true);
31 | *
32 | * In this example, if you try to use a class in the Symfony\Component
33 | * namespace or one of its children (Symfony\Component\Console for instance),
34 | * the autoloader will first look for the class under the component/
35 | * directory, and it will then fallback to the framework/ directory if not
36 | * found before giving up.
37 | *
38 | * This class is loosely based on the Symfony UniversalClassLoader.
39 | *
40 | * @author Fabien Potencier
41 | * @author Jordi Boggiano
42 | */
43 | class ClassLoader
44 | {
45 | // PSR-4
46 | private $prefixLengthsPsr4 = array();
47 | private $prefixDirsPsr4 = array();
48 | private $fallbackDirsPsr4 = array();
49 |
50 | // PSR-0
51 | private $prefixesPsr0 = array();
52 | private $fallbackDirsPsr0 = array();
53 |
54 | private $useIncludePath = false;
55 | private $classMap = array();
56 |
57 | public function getPrefixes()
58 | {
59 | return call_user_func_array('array_merge', $this->prefixesPsr0);
60 | }
61 |
62 | public function getPrefixesPsr4()
63 | {
64 | return $this->prefixDirsPsr4;
65 | }
66 |
67 | public function getFallbackDirs()
68 | {
69 | return $this->fallbackDirsPsr0;
70 | }
71 |
72 | public function getFallbackDirsPsr4()
73 | {
74 | return $this->fallbackDirsPsr4;
75 | }
76 |
77 | public function getClassMap()
78 | {
79 | return $this->classMap;
80 | }
81 |
82 | /**
83 | * @param array $classMap Class to filename map
84 | */
85 | public function addClassMap(array $classMap)
86 | {
87 | if ($this->classMap) {
88 | $this->classMap = array_merge($this->classMap, $classMap);
89 | } else {
90 | $this->classMap = $classMap;
91 | }
92 | }
93 |
94 | /**
95 | * Registers a set of PSR-0 directories for a given prefix, either
96 | * appending or prepending to the ones previously set for this prefix.
97 | *
98 | * @param string $prefix The prefix
99 | * @param array|string $paths The PSR-0 root directories
100 | * @param bool $prepend Whether to prepend the directories
101 | */
102 | public function add($prefix, $paths, $prepend = false)
103 | {
104 | if (!$prefix) {
105 | if ($prepend) {
106 | $this->fallbackDirsPsr0 = array_merge(
107 | (array) $paths,
108 | $this->fallbackDirsPsr0
109 | );
110 | } else {
111 | $this->fallbackDirsPsr0 = array_merge(
112 | $this->fallbackDirsPsr0,
113 | (array) $paths
114 | );
115 | }
116 |
117 | return;
118 | }
119 |
120 | $first = $prefix[0];
121 | if (!isset($this->prefixesPsr0[$first][$prefix])) {
122 | $this->prefixesPsr0[$first][$prefix] = (array) $paths;
123 |
124 | return;
125 | }
126 | if ($prepend) {
127 | $this->prefixesPsr0[$first][$prefix] = array_merge(
128 | (array) $paths,
129 | $this->prefixesPsr0[$first][$prefix]
130 | );
131 | } else {
132 | $this->prefixesPsr0[$first][$prefix] = array_merge(
133 | $this->prefixesPsr0[$first][$prefix],
134 | (array) $paths
135 | );
136 | }
137 | }
138 |
139 | /**
140 | * Registers a set of PSR-4 directories for a given namespace, either
141 | * appending or prepending to the ones previously set for this namespace.
142 | *
143 | * @param string $prefix The prefix/namespace, with trailing '\\'
144 | * @param array|string $paths The PSR-0 base directories
145 | * @param bool $prepend Whether to prepend the directories
146 | *
147 | * @throws \InvalidArgumentException
148 | */
149 | public function addPsr4($prefix, $paths, $prepend = false)
150 | {
151 | if (!$prefix) {
152 | // Register directories for the root namespace.
153 | if ($prepend) {
154 | $this->fallbackDirsPsr4 = array_merge(
155 | (array) $paths,
156 | $this->fallbackDirsPsr4
157 | );
158 | } else {
159 | $this->fallbackDirsPsr4 = array_merge(
160 | $this->fallbackDirsPsr4,
161 | (array) $paths
162 | );
163 | }
164 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
165 | // Register directories for a new namespace.
166 | $length = strlen($prefix);
167 | if ('\\' !== $prefix[$length - 1]) {
168 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
169 | }
170 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
171 | $this->prefixDirsPsr4[$prefix] = (array) $paths;
172 | } elseif ($prepend) {
173 | // Prepend directories for an already registered namespace.
174 | $this->prefixDirsPsr4[$prefix] = array_merge(
175 | (array) $paths,
176 | $this->prefixDirsPsr4[$prefix]
177 | );
178 | } else {
179 | // Append directories for an already registered namespace.
180 | $this->prefixDirsPsr4[$prefix] = array_merge(
181 | $this->prefixDirsPsr4[$prefix],
182 | (array) $paths
183 | );
184 | }
185 | }
186 |
187 | /**
188 | * Registers a set of PSR-0 directories for a given prefix,
189 | * replacing any others previously set for this prefix.
190 | *
191 | * @param string $prefix The prefix
192 | * @param array|string $paths The PSR-0 base directories
193 | */
194 | public function set($prefix, $paths)
195 | {
196 | if (!$prefix) {
197 | $this->fallbackDirsPsr0 = (array) $paths;
198 | } else {
199 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
200 | }
201 | }
202 |
203 | /**
204 | * Registers a set of PSR-4 directories for a given namespace,
205 | * replacing any others previously set for this namespace.
206 | *
207 | * @param string $prefix The prefix/namespace, with trailing '\\'
208 | * @param array|string $paths The PSR-4 base directories
209 | *
210 | * @throws \InvalidArgumentException
211 | */
212 | public function setPsr4($prefix, $paths)
213 | {
214 | if (!$prefix) {
215 | $this->fallbackDirsPsr4 = (array) $paths;
216 | } else {
217 | $length = strlen($prefix);
218 | if ('\\' !== $prefix[$length - 1]) {
219 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
220 | }
221 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
222 | $this->prefixDirsPsr4[$prefix] = (array) $paths;
223 | }
224 | }
225 |
226 | /**
227 | * Turns on searching the include path for class files.
228 | *
229 | * @param bool $useIncludePath
230 | */
231 | public function setUseIncludePath($useIncludePath)
232 | {
233 | $this->useIncludePath = $useIncludePath;
234 | }
235 |
236 | /**
237 | * Can be used to check if the autoloader uses the include path to check
238 | * for classes.
239 | *
240 | * @return bool
241 | */
242 | public function getUseIncludePath()
243 | {
244 | return $this->useIncludePath;
245 | }
246 |
247 | /**
248 | * Registers this instance as an autoloader.
249 | *
250 | * @param bool $prepend Whether to prepend the autoloader or not
251 | */
252 | public function register($prepend = false)
253 | {
254 | spl_autoload_register(array($this, 'loadClass'), true, $prepend);
255 | }
256 |
257 | /**
258 | * Unregisters this instance as an autoloader.
259 | */
260 | public function unregister()
261 | {
262 | spl_autoload_unregister(array($this, 'loadClass'));
263 | }
264 |
265 | /**
266 | * Loads the given class or interface.
267 | *
268 | * @param string $class The name of the class
269 | * @return bool|null True if loaded, null otherwise
270 | */
271 | public function loadClass($class)
272 | {
273 | if ($file = $this->findFile($class)) {
274 | includeFile($file);
275 |
276 | return true;
277 | }
278 | }
279 |
280 | /**
281 | * Finds the path to the file where the class is defined.
282 | *
283 | * @param string $class The name of the class
284 | *
285 | * @return string|false The path if found, false otherwise
286 | */
287 | public function findFile($class)
288 | {
289 | // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
290 | if ('\\' == $class[0]) {
291 | $class = substr($class, 1);
292 | }
293 |
294 | // class map lookup
295 | if (isset($this->classMap[$class])) {
296 | return $this->classMap[$class];
297 | }
298 |
299 | $file = $this->findFileWithExtension($class, '.php');
300 |
301 | // Search for Hack files if we are running on HHVM
302 | if ($file === null && defined('HHVM_VERSION')) {
303 | $file = $this->findFileWithExtension($class, '.hh');
304 | }
305 |
306 | if ($file === null) {
307 | // Remember that this class does not exist.
308 | return $this->classMap[$class] = false;
309 | }
310 |
311 | return $file;
312 | }
313 |
314 | private function findFileWithExtension($class, $ext)
315 | {
316 | // PSR-4 lookup
317 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
318 |
319 | $first = $class[0];
320 | if (isset($this->prefixLengthsPsr4[$first])) {
321 | foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
322 | if (0 === strpos($class, $prefix)) {
323 | foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
324 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
325 | return $file;
326 | }
327 | }
328 | }
329 | }
330 | }
331 |
332 | // PSR-4 fallback dirs
333 | foreach ($this->fallbackDirsPsr4 as $dir) {
334 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
335 | return $file;
336 | }
337 | }
338 |
339 | // PSR-0 lookup
340 | if (false !== $pos = strrpos($class, '\\')) {
341 | // namespaced class name
342 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
343 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
344 | } else {
345 | // PEAR-like class name
346 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
347 | }
348 |
349 | if (isset($this->prefixesPsr0[$first])) {
350 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
351 | if (0 === strpos($class, $prefix)) {
352 | foreach ($dirs as $dir) {
353 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
354 | return $file;
355 | }
356 | }
357 | }
358 | }
359 | }
360 |
361 | // PSR-0 fallback dirs
362 | foreach ($this->fallbackDirsPsr0 as $dir) {
363 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
364 | return $file;
365 | }
366 | }
367 |
368 | // PSR-0 include paths.
369 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
370 | return $file;
371 | }
372 | }
373 | }
374 |
375 | /**
376 | * Scope isolated include.
377 | *
378 | * Prevents access to $this/self from included files.
379 | */
380 | function includeFile($file)
381 | {
382 | include $file;
383 | }
384 |
--------------------------------------------------------------------------------
/tests/vendor/composer/autoload_classmap.php:
--------------------------------------------------------------------------------
1 | array($vendorDir . '/jenner/wechat/src'),
10 | 'Jenner\\Zebra\\Tools' => array($vendorDir . '/jenner/tools/src'),
11 | );
12 |
--------------------------------------------------------------------------------
/tests/vendor/composer/autoload_psr4.php:
--------------------------------------------------------------------------------
1 | $path) {
28 | $loader->set($namespace, $path);
29 | }
30 |
31 | $map = require __DIR__ . '/autoload_psr4.php';
32 | foreach ($map as $namespace => $path) {
33 | $loader->setPsr4($namespace, $path);
34 | }
35 |
36 | $classMap = require __DIR__ . '/autoload_classmap.php';
37 | if ($classMap) {
38 | $loader->addClassMap($classMap);
39 | }
40 |
41 | $loader->register(true);
42 |
43 | return $loader;
44 | }
45 | }
46 |
47 | function composerRequire7b361b6bc0dd4fff94980c130273a18b($file)
48 | {
49 | require $file;
50 | }
51 |
--------------------------------------------------------------------------------
/tests/vendor/composer/installed.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "jenner/tools",
4 | "version": "v1.2.0",
5 | "version_normalized": "1.2.0.0",
6 | "source": {
7 | "type": "git",
8 | "url": "https://github.com/huyanping/Zebra-Tools.git",
9 | "reference": "bfd0533f8071eaf12778f3e1a47a3bab9dfd7e09"
10 | },
11 | "dist": {
12 | "type": "zip",
13 | "url": "https://api.github.com/repos/huyanping/Zebra-Tools/zipball/bfd0533f8071eaf12778f3e1a47a3bab9dfd7e09",
14 | "reference": "bfd0533f8071eaf12778f3e1a47a3bab9dfd7e09",
15 | "shasum": ""
16 | },
17 | "require": {
18 | "php": ">=5.3.0"
19 | },
20 | "time": "2014-11-17 08:15:54",
21 | "type": "library",
22 | "installation-source": "dist",
23 | "autoload": {
24 | "psr-0": {
25 | "Jenner\\Zebra\\Tools": "src/"
26 | }
27 | },
28 | "notification-url": "https://packagist.org/downloads/",
29 | "license": [
30 | "MIT"
31 | ],
32 | "authors": [
33 | {
34 | "name": "Jenner",
35 | "email": "hypxm@qq.com"
36 | }
37 | ],
38 | "description": "common static functions",
39 | "keywords": [
40 | "tools",
41 | "utils"
42 | ]
43 | },
44 | {
45 | "name": "jenner/wechat",
46 | "version": "dev-master",
47 | "version_normalized": "9999999-dev",
48 | "source": {
49 | "type": "git",
50 | "url": "git@github.com:huyanping/Zebra-Wechat.git",
51 | "reference": "cde2c63f90c976dd3be4611cabb1948448708dc9"
52 | },
53 | "dist": {
54 | "type": "zip",
55 | "url": "https://api.github.com/repos/huyanping/Zebra-Wechat/zipball/cde2c63f90c976dd3be4611cabb1948448708dc9",
56 | "reference": "cde2c63f90c976dd3be4611cabb1948448708dc9",
57 | "shasum": ""
58 | },
59 | "require": {
60 | "jenner/tools": "1.2.0",
61 | "php": ">=5.3.0"
62 | },
63 | "time": "2014-12-14 04:06:52",
64 | "type": "library",
65 | "installation-source": "source",
66 | "autoload": {
67 | "psr-0": {
68 | "Jenner\\Zebra\\Wechat": "src/"
69 | }
70 | },
71 | "notification-url": "https://packagist.org/downloads/",
72 | "license": [
73 | "MIT"
74 | ],
75 | "authors": [
76 | {
77 | "name": "Jenner",
78 | "email": "hypxm@qq.com"
79 | }
80 | ],
81 | "description": "wechat api client",
82 | "keywords": [
83 | "api",
84 | "wechat"
85 | ]
86 | }
87 | ]
88 |
--------------------------------------------------------------------------------
/tests/vendor/jenner/tools/README.md:
--------------------------------------------------------------------------------
1 | Zebra-Tools
2 | ===================
3 | 常用行数库
4 | [博客地址:www.huyanping.cn](http://www.huyanping.cn/ "程序猿始终不够")
--------------------------------------------------------------------------------
/tests/vendor/jenner/tools/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jenner/tools",
3 | "description": "common static functions",
4 | "license": "MIT",
5 | "keywords": ["tools", "utils"],
6 | "version": "v1.2.0",
7 | "authors": [
8 | {
9 | "name": "Jenner",
10 | "email": "hypxm@qq.com"
11 | }
12 | ],
13 | "require": {
14 | "php": ">=5.3.0"
15 | },
16 |
17 | "autoload": {
18 | "psr-0": {
19 | "Jenner\\Zebra\\Tools": "src/"
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/tests/vendor/jenner/tools/src/Jenner/Zebra/Tools/CommonArray.php:
--------------------------------------------------------------------------------
1 | $filed) ? false : $value->$filed;
44 | }
45 | return $result;
46 | }
47 |
48 | /**
49 | * 类似SQL ORDER BY 的多为数组排序函数
50 | * example: $sorted = array_orderby($data, 'volume', SORT_DESC, 'edition', SORT_ASC);
51 | *
52 | * @return mixed
53 | */
54 | public static function arrayOrderby()
55 | {
56 | $args = \func_get_args();
57 | $data = \array_shift($args);
58 | foreach ($args as $n => $field) {
59 | if (\is_string($field)) {
60 | $tmp = array();
61 | foreach ($data as $key => $row)
62 | $tmp[$key] = $row[$field];
63 | $args[$n] = $tmp;
64 | }
65 | }
66 | $args[] = & $data;
67 | \call_user_func_array('array_multisort', $args);
68 | return \array_pop($args);
69 | }
70 |
71 | /**
72 | * object 转 array
73 | */
74 | function objectToArray($obj){
75 | $_arr = is_object($obj)? get_object_vars($obj) : $obj;
76 | foreach ($_arr as $key => $val) {
77 | $val = (is_array($val)) || is_object($val) ? object_to_array($val) : $val;
78 | $arr[$key] = $val;
79 | }
80 |
81 | return $arr;
82 | }
83 | }
--------------------------------------------------------------------------------
/tests/vendor/jenner/tools/src/Jenner/Zebra/Tools/Http.php:
--------------------------------------------------------------------------------
1 | url = $url;
25 | $this->timeOut = $timeOut;
26 | $this->transferTimeOut = $transferTimeOut;
27 |
28 | //代理服务器I的设置
29 | if ($proxyIp) {
30 | $this->proxyIp = $proxyIp;
31 | $this->proxyPort = $proxyPort ? $proxyPort : 80;
32 | }
33 |
34 | }
35 |
36 | public function setProxy($ip, $port = 80) {
37 | if ($ip) {
38 | $this->proxyIp = $ip;
39 | $this->proxyPort = $port;
40 | }
41 | }
42 |
43 | public function GET($params = null) {
44 |
45 | //组合带参数的URL
46 | $url = $this->url;
47 | if ($params && is_array($params)) {
48 | $url .= '?';
49 | $amp = '';
50 | foreach ($params as $paramKey => $paramValue) {
51 | $url .= $amp . $paramKey . '=' . urlencode($paramValue);
52 | $amp = '&';
53 | }
54 | }
55 |
56 | //初始化curl
57 | $curl = curl_init();
58 | if ($this->proxyIp && $this->proxyPort) {
59 | $proxy = "http://{$this->proxyIp}:{$this->proxyPort}";
60 | curl_setopt($curl, CURLOPT_PROXY, $proxy);
61 | }
62 | curl_setopt($curl, CURLOPT_URL, $url);
63 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
64 | curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
65 | curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $this->timeOut);
66 | curl_setopt($curl, CURLOPT_TIMEOUT, $this->transferTimeOut);
67 |
68 | $content = curl_exec($curl);
69 | curl_close($curl);
70 |
71 | return $content;
72 |
73 | }
74 |
75 | public function POST($params = null, $fileUpload = false) {
76 |
77 | //初始化curl
78 | $curl = curl_init();
79 | if ($this->proxyIp && $this->proxyPort) {
80 | $proxy = "http://{$this->proxyIp}:{$this->proxyPort}";
81 | curl_setopt($curl, CURLOPT_PROXY, $proxy);
82 | }
83 | curl_setopt($curl, CURLOPT_URL, $this->url);
84 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
85 | curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
86 | curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $this->timeOut);
87 | curl_setopt($curl, CURLOPT_TIMEOUT, $this->transferTimeOut);
88 |
89 | //设置POST参数
90 | curl_setopt($curl, CURLOPT_POST, 1);
91 | if ($params && is_array($params)) {
92 | if ($fileUpload) {
93 | curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
94 | } else {
95 | $amp = '';
96 | $postFields = '';
97 | foreach ($params as $paramKey => $paramValue) {
98 | $postFields .= $amp . $paramKey . '=' . urlencode($paramValue);
99 | $amp = '&';
100 | }
101 | curl_setopt($curl, CURLOPT_POSTFIELDS, $postFields);
102 | }
103 | }
104 |
105 | $content = curl_exec($curl);
106 | curl_close($curl);
107 |
108 | return $content;
109 |
110 | }
111 |
112 | }
--------------------------------------------------------------------------------
/tests/vendor/jenner/tools/src/Jenner/Zebra/Tools/Server.php:
--------------------------------------------------------------------------------
1 |