123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- <?php
- // +----------------------------------------------------------------------
- // | WeChatDeveloper
- // +----------------------------------------------------------------------
- // | 版权所有 2014~2020 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
- // +----------------------------------------------------------------------
- // | 官方网站: http://think.ctolog.com
- // +----------------------------------------------------------------------
- // | 开源协议 ( https://mit-license.org )
- // +----------------------------------------------------------------------
- // | github开源项目:https://github.com/zoujingli/WeChatDeveloper
- // +----------------------------------------------------------------------
- namespace WeChat\Contracts;
- use WeChat\Exceptions\InvalidArgumentException;
- use WeChat\Exceptions\InvalidResponseException;
- use WeChat\Exceptions\LocalCacheException;
- /**
- * 网络请求支持
- * Class Tools
- * @package WeChat\Contracts
- */
- class Tools
- {
- /**
- * 缓存路径
- * @var null
- */
- public static $cache_path = null;
- /**
- * 缓存写入操作
- * @var array
- */
- public static $cache_callable = [
- 'set' => null, // 写入缓存
- 'get' => null, // 获取缓存
- 'del' => null, // 删除缓存
- 'put' => null, // 写入文件
- ];
- /**
- * 网络缓存
- * @var array
- */
- private static $cache_curl = [];
- /**
- * 产生随机字符串
- * @param int $length 指定字符长度
- * @param string $str 字符串前缀
- * @return string
- */
- public static function createNoncestr($length = 32, $str = "")
- {
- $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
- for ($i = 0; $i < $length; $i++) {
- $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
- }
- return $str;
- }
- /**
- * 根据文件后缀获取文件类型
- * @param string|array $ext 文件后缀
- * @param array $mine 文件后缀MINE信息
- * @return string
- * @throws LocalCacheException
- */
- public static function getExtMine($ext, $mine = [])
- {
- $mines = self::getMines();
- foreach (is_string($ext) ? explode(',', $ext) : $ext as $e) {
- $mine[] = isset($mines[strtolower($e)]) ? $mines[strtolower($e)] : 'application/octet-stream';
- }
- return join(',', array_unique($mine));
- }
- /**
- * 获取所有文件扩展的类型
- * @return array
- * @throws LocalCacheException
- */
- private static function getMines()
- {
- $mines = self::getCache('all_ext_mine');
- if (empty($mines)) {
- $content = file_get_contents('http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types');
- preg_match_all('#^([^\s]{2,}?)\s+(.+?)$#ism', $content, $matches, PREG_SET_ORDER);
- foreach ($matches as $match) foreach (explode(" ", $match[2]) as $ext) $mines[$ext] = $match[1];
- self::setCache('all_ext_mine', $mines);
- }
- return $mines;
- }
- /**
- * 创建CURL文件对象
- * @param $filename
- * @param string $mimetype
- * @param string $postname
- * @return \CURLFile|string
- * @throws LocalCacheException
- */
- public static function createCurlFile($filename, $mimetype = null, $postname = null)
- {
- if (is_string($filename) && file_exists($filename)) {
- if (is_null($postname)) $postname = basename($filename);
- if (is_null($mimetype)) $mimetype = self::getExtMine(pathinfo($filename, 4));
- if (function_exists('curl_file_create')) {
- return curl_file_create($filename, $mimetype, $postname);
- }
- return "@{$filename};filename={$postname};type={$mimetype}";
- }
- return $filename;
- }
- /**
- * 数组转XML内容
- * @param array $data
- * @return string
- */
- public static function arr2xml($data)
- {
- return "<xml>" . self::_arr2xml($data) . "</xml>";
- }
- /**
- * XML内容生成
- * @param array $data 数据
- * @param string $content
- * @return string
- */
- private static function _arr2xml($data, $content = '')
- {
- foreach ($data as $key => $val) {
- is_numeric($key) && $key = 'item';
- $content .= "<{$key}>";
- if (is_array($val) || is_object($val)) {
- $content .= self::_arr2xml($val);
- } elseif (is_string($val)) {
- $content .= '<![CDATA[' . preg_replace("/[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]/", '', $val) . ']]>';
- } else {
- $content .= $val;
- }
- $content .= "</{$key}>";
- }
- return $content;
- }
- /**
- * 解析XML内容到数组
- * @param string $xml
- * @return array
- */
- public static function xml2arr($xml)
- {
- $entity = libxml_disable_entity_loader(true);
- $data = (array)simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
- libxml_disable_entity_loader($entity);
- return json_decode(json_encode($data), true);
- }
- /**
- * 解析XML文本内容
- * @param string $xml
- * @return boolean|mixed
- */
- public static function xml3arr($xml)
- {
- $state = xml_parse($parser = xml_parser_create(), $xml, true);
- return xml_parser_free($parser) && $state ? self::xml2arr($xml) : false;
- }
- /**
- * 数组转xml内容
- * @param array $data
- * @return null|string
- */
- public static function arr2json($data)
- {
- $json = json_encode(self::buildEnEmojiData($data), JSON_UNESCAPED_UNICODE);
- return $json === '[]' ? '{}' : $json;
- }
- /**
- * 数组对象Emoji编译处理
- * @param array $data
- * @return array
- */
- public static function buildEnEmojiData(array $data)
- {
- foreach ($data as $key => $value) {
- if (is_array($value)) {
- $data[$key] = self::buildEnEmojiData($value);
- } elseif (is_string($value)) {
- $data[$key] = self::emojiEncode($value);
- } else {
- $data[$key] = $value;
- }
- }
- return $data;
- }
- /**
- * 数组对象Emoji反解析处理
- * @param array $data
- * @return array
- */
- public static function buildDeEmojiData(array $data)
- {
- foreach ($data as $key => $value) {
- if (is_array($value)) {
- $data[$key] = self::buildDeEmojiData($value);
- } elseif (is_string($value)) {
- $data[$key] = self::emojiDecode($value);
- } else {
- $data[$key] = $value;
- }
- }
- return $data;
- }
- /**
- * Emoji原形转换为String
- * @param string $content
- * @return string
- */
- public static function emojiEncode($content)
- {
- return json_decode(preg_replace_callback("/(\\\u[ed][0-9a-f]{3})/i", function ($string) {
- return addslashes($string[0]);
- }, json_encode($content)));
- }
- /**
- * Emoji字符串转换为原形
- * @param string $content
- * @return string
- */
- public static function emojiDecode($content)
- {
- return json_decode(preg_replace_callback('/\\\\\\\\/i', function () {
- return '\\';
- }, json_encode($content)));
- }
- /**
- * 解析JSON内容到数组
- * @param string $json
- * @return array
- * @throws InvalidResponseException
- */
- public static function json2arr($json)
- {
- $result = json_decode($json, true);
- if (empty($result)) {
- throw new InvalidResponseException('invalid response.', '0');
- }
- if (!empty($result['errcode'])) {
- throw new InvalidResponseException($result['errmsg'], $result['errcode'], $result);
- }
- return $result;
- }
- /**
- * 以get访问模拟访问
- * @param string $url 访问URL
- * @param array $query GET数
- * @param array $options
- * @return boolean|string
- * @throws LocalCacheException
- */
- public static function get($url, $query = [], $options = [])
- {
- $options['query'] = $query;
- return self::doRequest('get', $url, $options);
- }
- /**
- * 以post访问模拟访问
- * @param string $url 访问URL
- * @param array $data POST数据
- * @param array $options
- * @return boolean|string
- * @throws LocalCacheException
- */
- public static function post($url, $data = [], $options = [])
- {
- $options['data'] = $data;
- return self::doRequest('post', $url, $options);
- }
- /**
- * CURL模拟网络请求
- * @param string $method 请求方法
- * @param string $url 请求方法
- * @param array $options 请求参数[headers,data,ssl_cer,ssl_key]
- * @return boolean|string
- * @throws LocalCacheException
- */
- public static function doRequest($method, $url, $options = [])
- {
- $curl = curl_init();
- // GET参数设置
- if (!empty($options['query'])) {
- $url .= (stripos($url, '?') !== false ? '&' : '?') . http_build_query($options['query']);
- }
- // CURL头信息设置
- if (!empty($options['headers'])) {
- curl_setopt($curl, CURLOPT_HTTPHEADER, $options['headers']);
- }
- // POST数据设置
- if (strtolower($method) === 'post') {
- curl_setopt($curl, CURLOPT_POST, true);
- curl_setopt($curl, CURLOPT_POSTFIELDS, self::_buildHttpData($options['data']));
- }
- // 证书文件设置
- if (!empty($options['ssl_cer'])) if (file_exists($options['ssl_cer'])) {
- curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM');
- curl_setopt($curl, CURLOPT_SSLCERT, $options['ssl_cer']);
- } else throw new InvalidArgumentException("Certificate files that do not exist. --- [ssl_cer]");
- // 证书文件设置
- if (!empty($options['ssl_key'])) if (file_exists($options['ssl_key'])) {
- curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM');
- curl_setopt($curl, CURLOPT_SSLKEY, $options['ssl_key']);
- } else throw new InvalidArgumentException("Certificate files that do not exist. --- [ssl_key]");
- curl_setopt($curl, CURLOPT_URL, $url);
- curl_setopt($curl, CURLOPT_TIMEOUT, 60);
- curl_setopt($curl, CURLOPT_HEADER, false);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
- list($content) = [curl_exec($curl), curl_close($curl)];
- // 清理 CURL 缓存文件
- if (!empty(self::$cache_curl)) foreach (self::$cache_curl as $key => $file) {
- Tools::delCache($file);
- unset(self::$cache_curl[$key]);
- }
- return $content;
- }
- /**
- * POST数据过滤处理
- * @param array $data 需要处理的数据
- * @param boolean $build 是否编译数据
- * @return array|string
- * @throws \WeChat\Exceptions\LocalCacheException
- */
- private static function _buildHttpData($data, $build = true)
- {
- if (!is_array($data)) return $data;
- foreach ($data as $key => $value) if (is_object($value) && $value instanceof \CURLFile) {
- $build = false;
- } elseif (is_object($value) && isset($value->datatype) && $value->datatype === 'MY_CURL_FILE') {
- $build = false;
- $mycurl = new MyCurlFile((array)$value);
- $data[$key] = $mycurl->get();
- array_push(self::$cache_curl, $mycurl->tempname);
- } elseif (is_array($value) && isset($value['datatype']) && $value['datatype'] === 'MY_CURL_FILE') {
- $build = false;
- $mycurl = new MyCurlFile($value);
- $data[$key] = $mycurl->get();
- array_push(self::$cache_curl, $mycurl->tempname);
- } elseif (is_string($value) && class_exists('CURLFile', false) && stripos($value, '@') === 0) {
- if (($filename = realpath(trim($value, '@'))) && file_exists($filename)) {
- $build = false;
- $data[$key] = self::createCurlFile($filename);
- }
- }
- return $build ? http_build_query($data) : $data;
- }
- /**
- * 写入文件
- * @param string $name 文件名称
- * @param string $content 文件内容
- * @return string
- * @throws LocalCacheException
- */
- public static function pushFile($name, $content)
- {
- if (is_callable(self::$cache_callable['put'])) {
- return call_user_func_array(self::$cache_callable['put'], func_get_args());
- }
- $file = self::_getCacheName($name);
- if (!file_put_contents($file, $content)) {
- throw new LocalCacheException('local file write error.', '0');
- }
- return $file;
- }
- /**
- * 缓存配置与存储
- * @param string $name 缓存名称
- * @param string $value 缓存内容
- * @param int $expired 缓存时间(0表示永久缓存)
- * @return string
- * @throws LocalCacheException
- */
- public static function setCache($name, $value = '', $expired = 3600)
- {
- if (is_callable(self::$cache_callable['set'])) {
- return call_user_func_array(self::$cache_callable['set'], func_get_args());
- }
- $file = self::_getCacheName($name);
- $data = ['name' => $name, 'value' => $value, 'expired' => time() + intval($expired)];
- if (!file_put_contents($file, serialize($data))) {
- throw new LocalCacheException('local cache error.', '0');
- }
- return $file;
- }
- /**
- * 获取缓存内容
- * @param string $name 缓存名称
- * @return null|mixed
- */
- public static function getCache($name)
- {
- if (is_callable(self::$cache_callable['get'])) {
- return call_user_func_array(self::$cache_callable['get'], func_get_args());
- }
- $file = self::_getCacheName($name);
- if (file_exists($file) && ($content = file_get_contents($file))) {
- $data = unserialize($content);
- if (isset($data['expired']) && (intval($data['expired']) === 0 || intval($data['expired']) >= time())) {
- return $data['value'];
- }
- self::delCache($name);
- }
- return null;
- }
- /**
- * 移除缓存文件
- * @param string $name 缓存名称
- * @return boolean
- */
- public static function delCache($name)
- {
- if (is_callable(self::$cache_callable['del'])) {
- return call_user_func_array(self::$cache_callable['del'], func_get_args());
- }
- $file = self::_getCacheName($name);
- return file_exists($file) ? unlink($file) : true;
- }
- /**
- * 应用缓存目录
- * @param string $name
- * @return string
- */
- private static function _getCacheName($name)
- {
- if (empty(self::$cache_path)) {
- self::$cache_path = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'Cache' . DIRECTORY_SEPARATOR;
- }
- self::$cache_path = rtrim(self::$cache_path, '/\\') . DIRECTORY_SEPARATOR;
- file_exists(self::$cache_path) || mkdir(self::$cache_path, 0755, true);
- return self::$cache_path . $name;
- }
- }
|