├── README.md ├── corp ├── api │ ├── Auth.php │ ├── Chat.php │ ├── Department.php │ ├── Message.php │ └── User.php ├── composer.json ├── composer.lock ├── config.php ├── crypto │ ├── DingtalkCrypt.php │ ├── errorCode.php │ ├── pkcs7Encoder.php │ └── sha1.php ├── index.php ├── indexpc.php ├── public │ ├── javascripts │ │ ├── demo.js │ │ ├── demopc.js │ │ ├── logger.js │ │ └── zepto.min.js │ └── stylesheets │ │ └── style.css ├── sendMsg.php ├── util │ ├── Cache.php │ ├── Http.php │ └── Log.php └── vendor │ ├── autoload.php │ ├── composer │ ├── ClassLoader.php │ ├── autoload_classmap.php │ ├── autoload_namespaces.php │ ├── autoload_psr4.php │ ├── autoload_real.php │ └── installed.json │ └── nategood │ └── httpful │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE.txt │ ├── README.md │ ├── bootstrap.php │ ├── build │ ├── composer.json │ ├── examples │ ├── freebase.php │ ├── github.php │ ├── override.php │ └── showclix.php │ ├── src │ └── Httpful │ │ ├── Bootstrap.php │ │ ├── Exception │ │ └── ConnectionErrorException.php │ │ ├── Handlers │ │ ├── CsvHandler.php │ │ ├── FormHandler.php │ │ ├── JsonHandler.php │ │ ├── MimeHandlerAdapter.php │ │ ├── README.md │ │ ├── XHtmlHandler.php │ │ └── XmlHandler.php │ │ ├── Http.php │ │ ├── Httpful.php │ │ ├── Mime.php │ │ ├── Proxy.php │ │ ├── Request.php │ │ ├── Response.php │ │ └── Response │ │ └── Headers.php │ └── tests │ ├── Httpful │ ├── HttpfulTest.php │ └── requestTest.php │ ├── bootstrap-server.php │ ├── phpunit.xml │ ├── static │ └── test.json │ └── test_image.jpg └── isv ├── api ├── Activate.php ├── Auth.php ├── Chat.php ├── Department.php ├── ISVClass.php ├── ISVService.php ├── Message.php └── User.php ├── composer.json ├── composer.lock ├── config.php ├── crypto ├── DingtalkCrypt.php ├── errorCode.php ├── pkcs7Encoder.php └── sha1.php ├── getPerson.php ├── index.php ├── indexpc.php ├── public ├── javascripts │ ├── demo.js │ ├── demopc.js │ ├── logger.js │ └── zepto.min.js └── stylesheets │ └── style.css ├── receive.php ├── sendMsg.php ├── util ├── Cache.php ├── Http.php └── Log.php └── vendor ├── autoload.php ├── composer ├── ClassLoader.php ├── autoload_classmap.php ├── autoload_namespaces.php ├── autoload_psr4.php ├── autoload_real.php └── installed.json └── nategood └── httpful ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── bootstrap.php ├── build ├── composer.json ├── examples ├── freebase.php ├── github.php ├── override.php └── showclix.php ├── src └── Httpful │ ├── Bootstrap.php │ ├── Exception │ └── ConnectionErrorException.php │ ├── Handlers │ ├── CsvHandler.php │ ├── FormHandler.php │ ├── JsonHandler.php │ ├── MimeHandlerAdapter.php │ ├── README.md │ ├── XHtmlHandler.php │ └── XmlHandler.php │ ├── Http.php │ ├── Httpful.php │ ├── Mime.php │ ├── Proxy.php │ ├── Request.php │ ├── Response.php │ └── Response │ └── Headers.php └── tests ├── Httpful ├── HttpfulTest.php └── requestTest.php ├── bootstrap-server.php ├── phpunit.xml ├── static └── test.json └── test_image.jpg /README.md: -------------------------------------------------------------------------------- 1 | 2 | ISV应用和企业应用php demo 3 | 注意!注意!注意!demo中的数据库存储一定要修改为mysql等持久化存储。 4 | 目录结构: 5 | 6 | 7 | isv目录:isv应用php demo 8 | 9 | 10 | corp目录:企业应用php demo 11 | 12 | 运行前先看开发文档:http://ddtalk.github.io/dingTalkDoc/?spm=a3140.7785475.0.0.Q5c5r7 13 | 14 | ## Getting Started 15 | 16 | ISV应用注册开发流程 17 | ###创建套件前 18 | 登录到 http://console.d.aliyun.com/#/dingding/suite 创建套件(需要先注册开发者账号和钉钉企业才能创建套件) 19 | ###创建套件 20 | 3.填写套件信息 21 | 其中: 22 | 23 | - Token: 可以随意填写,填写完之后,打开工程的isv/config.php文件,把Token的值复制给TOKEN 24 | - 数据加密密钥:点击自动生成,然后打开工程的isv/config.php文件,把值复制给给ENCODING_AES_KEY 25 | - 应用ID:把应用ID的值复制给APPID 26 | - IP白名单: 调用钉钉API的合法IP列表(例如,工程部署在ip地址为123.56.71.118的主机上,那就填写"123.56.71.118") 27 | - 回调URL: url为`工程地址/receive.php`(例如,工程将部署在ip地址为123.56.71.118的主机上,端口为8080,那么我的回调URL即为:`http://123.56.71.118:8080/receive.php`,假如你有域名的话,也可以把IP地址换成域名) 28 | 29 | 4.配置PHP服务器环境(php+apache/nginx),安装mcrypt扩展(注意,一定要安装mcrypt扩展),保证apache服务根目录与可写权限(存储json数据) 30 | 31 | 5.将demo工程(isv)部署到服务器上 32 | 33 | 6.部署成功之后,点击『创建套件』弹窗中的『验证有效性』。 34 | 35 | 具体是如何验证回调URL有效性的,请查看(isv/receive.php) 36 | 37 | 7.创建套件成功之后,将得到的SuiteKey和SuiteSecret填写到工程的config.php中。 38 | 39 | 8.点击['测试企业和文档'],注册测试企业,注册完成后,点击『登录管理』到```oa.dingtalk.com```完成测试企业的激活 40 | 41 | 9.测试企业激活完成后,进入套件『管理』,在页面底部选择要授权的测试企业进行授权 42 | 43 | 10.修改微应用主页地址和PC主页地址 44 | 45 | 点击应用最右侧的`编辑`,编辑微应用信息,例如,工程部署在ip地址为123.56.71.118的主机上,端口为8080,那么微应用首页地址即为:`http://123.56.71.118:8080/index.php?corpid=$CORPID$`,PC版首页地址为:`http://123.56.71.118:8080/indexpc.php?corpid=$CORPID$`,点击保存。 46 | 47 | 11.打开钉钉,进入对应企业,即可看到微应用,点击进入 48 | 49 | 注意:Ticket推送状态成功之后,再授权企业 50 | 51 | ###创建企业应用 52 | 1.进入`https://oa.dingtalk.com/#/microApp/microAppList`,点击『新建应用』 53 | 54 | 2.配置PHP服务器环境(php+apache/nginx),安装mcrypt扩展(注意,一定要安装mcrypt扩展),保证apache服务根目录与可写权限(存储json数据) 55 | 56 | 3.微应用主页地址填写。地址为`根目录/index.php`,(例如,工程部署在ip地址为123.56.71.118的主机上,端口为8080,那么微应用首页地址即为:`http://123.56.71.118:8080/index.php`,PC版首页地址为:`http://123.56.71.118:8080/indexpc.php`,假如你有域名的话,也可以把IP地址换成域名) 57 | 修改config.php中的CORPID,SECRET,AGENTID,其中CORPID,SECRET在微应用设置页面`https://oa.dingtalk.com/#/microApp/microAppSet`获取,AGENTID在创建微应用的时候可以获取 58 | 59 | 4.微应用创建成功后,需要把微应用首页地址改为'根目录/index.php' 60 | 61 | 5.打开钉钉,进入对应企业,即可看到微应用,点击进入 62 | 63 | 64 | ###本DEMO具体实现 65 | 66 | 1.URL回调流程 67 | 68 | 请查看[文档](http://ddtalk.github.io/dingTalkDoc/#2-回调接口(分为五个回调类型)) 69 | 70 | 2.jsapi权限验证配置流程 71 | 72 | 请查看[文档](http://ddtalk.github.io/dingTalkDoc/#页面引入js文件) 73 | 74 | 3.免登流程 75 | 76 | 请查看[文档](http://ddtalk.github.io/dingTalkDoc/#手机客户端微应用中调用免登) 77 | -------------------------------------------------------------------------------- /corp/api/Auth.php: -------------------------------------------------------------------------------- 1 | CORPID, 'corpsecret' => SECRET)); 18 | $accessToken = $response->access_token; 19 | Cache::set('corp_access_token', $accessToken); 20 | } 21 | return $accessToken; 22 | } 23 | 24 | /** 25 | * 缓存jsTicket。jsTicket有效期为两小时,需要在失效前请求新的jsTicket(注意:以下代码没有在失效前刷新缓存的jsTicket)。 26 | */ 27 | public static function getTicket($accessToken) 28 | { 29 | $jsticket = Cache::getJsTicket('js_ticket'); 30 | if (!$jsticket) 31 | { 32 | $response = Http::get('/get_jsapi_ticket', array('type' => 'jsapi', 'access_token' => $accessToken)); 33 | self::check($response); 34 | $jsticket = $response->ticket; 35 | Cache::setJsTicket($jsticket); 36 | } 37 | return $jsticket; 38 | } 39 | 40 | 41 | function curPageURL() 42 | { 43 | $pageURL = 'http'; 44 | 45 | if (array_key_exists('HTTPS',$_SERVER)&&$_SERVER["HTTPS"] == "on") 46 | { 47 | $pageURL .= "s"; 48 | } 49 | $pageURL .= "://"; 50 | 51 | if ($_SERVER["SERVER_PORT"] != "80") 52 | { 53 | $pageURL .= $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"]; 54 | } 55 | else 56 | { 57 | $pageURL .= $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"]; 58 | } 59 | return $pageURL; 60 | } 61 | 62 | public static function getConfig() 63 | { 64 | $corpId = CORPID; 65 | $agentId = AGENTID; 66 | $nonceStr = 'abcdefg'; 67 | $timeStamp = time(); 68 | $url = self::curPageURL(); 69 | $corpAccessToken = self::getAccessToken(); 70 | if (!$corpAccessToken) 71 | { 72 | Log::e("[getConfig] ERR: no corp access token"); 73 | } 74 | $ticket = self::getTicket($corpAccessToken); 75 | $signature = self::sign($ticket, $nonceStr, $timeStamp, $url); 76 | 77 | $config = array( 78 | 'url' => $url, 79 | 'nonceStr' => $nonceStr, 80 | 'agentId' => $agentId, 81 | 'timeStamp' => $timeStamp, 82 | 'corpId' => $corpId, 83 | 'signature' => $signature); 84 | return json_encode($config, JSON_UNESCAPED_SLASHES); 85 | } 86 | 87 | 88 | public static function sign($ticket, $nonceStr, $timeStamp, $url) 89 | { 90 | $plain = 'jsapi_ticket=' . $ticket . 91 | '&noncestr=' . $nonceStr . 92 | '×tamp=' . $timeStamp . 93 | '&url=' . $url; 94 | return sha1($plain); 95 | } 96 | 97 | static function check($res) 98 | { 99 | if ($res->errcode != 0) 100 | { 101 | Log::e("FAIL: " . json_encode($res)); 102 | exit("Failed: " . json_encode($res)); 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /corp/api/Chat.php: -------------------------------------------------------------------------------- 1 | $accessToken), 12 | json_encode($chatOpt)); 13 | return $response; 14 | } 15 | 16 | public static function bindChat($accessToken, $chatid,$agentid) 17 | { 18 | $response = Http::get("/chat/bind", 19 | array("access_token" => $accessToken,"chatid"=>$chatid,"agentid"=>$agentid)); 20 | return $response; 21 | } 22 | 23 | public static function sendmsg($accessToken, $opt) 24 | { 25 | $response = Http::post("/chat/send", 26 | array("access_token" => $accessToken), 27 | json_encode($opt)); 28 | return $response; 29 | } 30 | 31 | public static function callback($accessToken, $opt) 32 | { 33 | $response = Http::post("/call_back/register_call_back", 34 | array("access_token" => $accessToken), 35 | json_encode($opt)); 36 | return $response; 37 | } 38 | } -------------------------------------------------------------------------------- /corp/api/Department.php: -------------------------------------------------------------------------------- 1 | $accessToken), 9 | json_encode($dept)); 10 | return $response->id; 11 | } 12 | 13 | 14 | public static function listDept($accessToken) 15 | { 16 | $response = Http::get("/department/list", 17 | array("access_token" => $accessToken)); 18 | Log::i($accessToken."【--department/list--】".json_encode($response->department)); 19 | return $response->department; 20 | } 21 | 22 | 23 | public static function deleteDept($accessToken, $id) 24 | { 25 | $response = Http::get("/department/delete", 26 | array("access_token" => $accessToken, "id" => $id)); 27 | return $response->errcode == 0; 28 | } 29 | } -------------------------------------------------------------------------------- /corp/api/Message.php: -------------------------------------------------------------------------------- 1 | $accessToken), 9 | json_encode($opt)); 10 | return $response; 11 | } 12 | 13 | public static function send($accessToken, $opt) 14 | { 15 | $response = Http::post("/message/send", 16 | array("access_token" => $accessToken),json_encode($opt)); 17 | return $response; 18 | } 19 | } -------------------------------------------------------------------------------- /corp/api/User.php: -------------------------------------------------------------------------------- 1 | $accessToken, "code" => $code)); 8 | return json_encode($response); 9 | } 10 | 11 | 12 | public static function simplelist($accessToken,$deptId){ 13 | $response = Http::get("/user/simplelist", 14 | array("access_token" => $accessToken,"department_id"=>$deptId)); 15 | return $response->userlist; 16 | 17 | } 18 | } -------------------------------------------------------------------------------- /corp/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "nategood/httpful": "*" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /corp/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "e23d30d76ba6b3d3bcdab145c9f7f9f3", 8 | "packages": [ 9 | { 10 | "name": "nategood/httpful", 11 | "version": "0.2.19", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/nategood/httpful.git", 15 | "reference": "bd73f89d34d8f879c54ac46eb94b0f7be1d00820" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/nategood/httpful/zipball/bd73f89d34d8f879c54ac46eb94b0f7be1d00820", 20 | "reference": "bd73f89d34d8f879c54ac46eb94b0f7be1d00820", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-curl": "*", 25 | "php": ">=5.3" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "*" 29 | }, 30 | "type": "library", 31 | "autoload": { 32 | "psr-0": { 33 | "Httpful": "src/" 34 | } 35 | }, 36 | "notification-url": "https://packagist.org/downloads/", 37 | "license": [ 38 | "MIT" 39 | ], 40 | "authors": [ 41 | { 42 | "name": "Nate Good", 43 | "email": "me@nategood.com", 44 | "homepage": "http://nategood.com" 45 | } 46 | ], 47 | "description": "A Readable, Chainable, REST friendly, PHP HTTP Client", 48 | "homepage": "http://github.com/nategood/httpful", 49 | "keywords": [ 50 | "api", 51 | "curl", 52 | "http", 53 | "requests", 54 | "rest", 55 | "restful" 56 | ], 57 | "time": "2015-03-08 15:22:23" 58 | } 59 | ], 60 | "packages-dev": [], 61 | "aliases": [], 62 | "minimum-stability": "stable", 63 | "stability-flags": [], 64 | "prefer-stable": false, 65 | "prefer-lowest": false, 66 | "platform": [], 67 | "platform-dev": [] 68 | } 69 | -------------------------------------------------------------------------------- /corp/config.php: -------------------------------------------------------------------------------- 1 | m_token = $token; 17 | $this->m_encodingAesKey = $encodingAesKey; 18 | $this->m_suiteKey = $suiteKey; 19 | } 20 | 21 | 22 | public function EncryptMsg($plain, $timeStamp, $nonce, &$encryptMsg) 23 | { 24 | $pc = new Prpcrypt($this->m_encodingAesKey); 25 | 26 | $array = $pc->encrypt($plain, $this->m_suiteKey); 27 | $ret = $array[0]; 28 | if ($ret != 0) { 29 | return $ret; 30 | } 31 | 32 | if ($timeStamp == null) { 33 | $timeStamp = time(); 34 | } 35 | $encrypt = $array[1]; 36 | 37 | $sha1 = new SHA1; 38 | $array = $sha1->getSHA1($this->m_token, $timeStamp, $nonce, $encrypt); 39 | $ret = $array[0]; 40 | if ($ret != 0) { 41 | return $ret; 42 | } 43 | $signature = $array[1]; 44 | 45 | $encryptMsg = json_encode(array( 46 | "msg_signature" => $signature, 47 | "encrypt" => $encrypt, 48 | "timeStamp" => $timeStamp, 49 | "nonce" => $nonce 50 | )); 51 | return ErrorCode::$OK; 52 | } 53 | 54 | 55 | public function DecryptMsg($signature, $timeStamp = null, $nonce, $encrypt, &$decryptMsg) 56 | { 57 | if (strlen($this->m_encodingAesKey) != 43) { 58 | return ErrorCode::$IllegalAesKey; 59 | } 60 | 61 | $pc = new Prpcrypt($this->m_encodingAesKey); 62 | 63 | if ($timeStamp == null) { 64 | $timeStamp = time(); 65 | } 66 | 67 | $sha1 = new SHA1; 68 | $array = $sha1->getSHA1($this->m_token, $timeStamp, $nonce, $encrypt); 69 | $ret = $array[0]; 70 | 71 | if ($ret != 0) { 72 | return $ret; 73 | } 74 | 75 | $verifySignature = $array[1]; 76 | if ($verifySignature != $signature) { 77 | return ErrorCode::$ValidateSignatureError; 78 | } 79 | 80 | $result = $pc->decrypt($encrypt, $this->m_suiteKey); 81 | if ($result[0] != 0) { 82 | return $result[0]; 83 | } 84 | $decryptMsg = $result[1]; 85 | 86 | return ErrorCode::$OK; 87 | } 88 | 89 | } 90 | 91 | -------------------------------------------------------------------------------- /corp/crypto/errorCode.php: -------------------------------------------------------------------------------- 1 | 6 | *
  • -900004: encodingAesKey 非法
  • 7 | *
  • -900005: 签名验证错误
  • 8 | *
  • -900006: sha加密生成签名失败
  • 9 | *
  • -900007: aes 加密失败
  • 10 | *
  • -900008: aes 解密失败
  • 11 | *
  • -900010: suiteKey 校验错误
  • 12 | * 13 | */ 14 | class ErrorCode 15 | { 16 | public static $OK = 0; 17 | 18 | public static $IllegalAesKey = 900004; 19 | public static $ValidateSignatureError = 900005; 20 | public static $ComputeSignatureError = 900006; 21 | public static $EncryptAESError = 900007; 22 | public static $DecryptAESError = 900008; 23 | public static $ValidateSuiteKeyError = 900010; 24 | } 25 | 26 | ?> -------------------------------------------------------------------------------- /corp/crypto/pkcs7Encoder.php: -------------------------------------------------------------------------------- 1 | PKCS7Encoder::$block_size) { 30 | $pad = 0; 31 | } 32 | return substr($text, 0, (strlen($text) - $pad)); 33 | } 34 | 35 | } 36 | 37 | 38 | class Prpcrypt 39 | { 40 | public $key; 41 | 42 | function Prpcrypt($k) 43 | { 44 | $this->key = base64_decode($k . "="); 45 | } 46 | 47 | public function encrypt($text, $corpid) 48 | { 49 | 50 | try { 51 | //获得16位随机字符串,填充到明文之前 52 | $random = $this->getRandomStr(); 53 | $text = $random . pack("N", strlen($text)) . $text . $corpid; 54 | // 网络字节序 55 | $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); 56 | $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); 57 | $iv = substr($this->key, 0, 16); 58 | //使用自定义的填充方式对明文进行补位填充 59 | $pkc_encoder = new PKCS7Encoder; 60 | $text = $pkc_encoder->encode($text); 61 | mcrypt_generic_init($module, $this->key, $iv); 62 | //加密 63 | $encrypted = mcrypt_generic($module, $text); 64 | mcrypt_generic_deinit($module); 65 | mcrypt_module_close($module); 66 | 67 | //print(base64_encode($encrypted)); 68 | //使用BASE64对加密后的字符串进行编码 69 | return array(ErrorCode::$OK, base64_encode($encrypted)); 70 | } catch (Exception $e) { 71 | print $e; 72 | return array(ErrorCode::$EncryptAESError, null); 73 | } 74 | } 75 | 76 | public function decrypt($encrypted, $corpid) 77 | { 78 | 79 | try { 80 | $ciphertext_dec = base64_decode($encrypted); 81 | $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); 82 | $iv = substr($this->key, 0, 16); 83 | mcrypt_generic_init($module, $this->key, $iv); 84 | 85 | $decrypted = mdecrypt_generic($module, $ciphertext_dec); 86 | mcrypt_generic_deinit($module); 87 | mcrypt_module_close($module); 88 | } catch (Exception $e) { 89 | return array(ErrorCode::$DecryptAESError, null); 90 | } 91 | 92 | 93 | try { 94 | //去除补位字符 95 | $pkc_encoder = new PKCS7Encoder; 96 | $result = $pkc_encoder->decode($decrypted); 97 | //去除16位随机字符串,网络字节序和AppId 98 | if (strlen($result) < 16) 99 | return ""; 100 | $content = substr($result, 16, strlen($result)); 101 | $len_list = unpack("N", substr($content, 0, 4)); 102 | $xml_len = $len_list[1]; 103 | $xml_content = substr($content, 4, $xml_len); 104 | $from_corpid = substr($content, $xml_len + 4); 105 | } catch (Exception $e) { 106 | print $e; 107 | return array(ErrorCode::$DecryptAESError, null); 108 | } 109 | if ($from_corpid != $corpid) 110 | return array(ErrorCode::$ValidateSuiteKeyError, null); 111 | return array(0, $xml_content); 112 | 113 | } 114 | 115 | function getRandomStr() 116 | { 117 | 118 | $str = ""; 119 | $str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; 120 | $max = strlen($str_pol) - 1; 121 | for ($i = 0; $i < 16; $i++) { 122 | $str .= $str_pol[mt_rand(0, $max)]; 123 | } 124 | return $str; 125 | } 126 | 127 | } 128 | 129 | ?> -------------------------------------------------------------------------------- /corp/crypto/sha1.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /corp/index.php: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | jsapi方式免登手机客户端demo 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /corp/indexpc.php: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | jsapi demo 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /corp/public/javascripts/demo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 8/10/15. 3 | */ 4 | 5 | logger.i('Here we go...'); 6 | 7 | logger.i(location.href); 8 | 9 | /** 10 | * _config comes from server-side template. see views/index.jade 11 | */ 12 | dd.config({ 13 | agentId: _config.agentId, 14 | corpId: _config.corpId, 15 | timeStamp: _config.timeStamp, 16 | nonceStr: _config.nonceStr, 17 | signature: _config.signature, 18 | jsApiList: [ 19 | 'runtime.info', 20 | 'device.notification.prompt', 21 | 'biz.chat.pickConversation', 22 | 'device.notification.confirm', 23 | 'device.notification.alert', 24 | 'device.notification.prompt', 25 | 'biz.chat.open', 26 | 'biz.util.open', 27 | 'biz.user.get', 28 | 'biz.contact.choose', 29 | 'biz.telephone.call', 30 | 'biz.ding.post'] 31 | }); 32 | dd.userid=0; 33 | dd.ready(function() { 34 | logger.i('dd.ready rocks!'); 35 | 36 | dd.runtime.info({ 37 | onSuccess: function(info) { 38 | logger.i('runtime info: ' + JSON.stringify(info)); 39 | }, 40 | onFail: function(err) { 41 | logger.e('fail: ' + JSON.stringify(err)); 42 | } 43 | }); 44 | 45 | dd.runtime.permission.requestAuthCode({ 46 | corpId: _config.corpId, //企业id 47 | onSuccess: function (info) { 48 | logger.i('authcode: ' + info.code); 49 | $.ajax({ 50 | url: '/sendMsg.php', 51 | type:"POST", 52 | data: {"event":"get_userinfo","code":info.code}, 53 | dataType:'json', 54 | timeout: 900, 55 | success: function (data, status, xhr) { 56 | var info = JSON.parse(data); 57 | if (info.errcode === 0) { 58 | logger.i('user id: ' + info.userid); 59 | dd.userid = info.userid; 60 | } 61 | else { 62 | logger.e('auth error: ' + data); 63 | } 64 | }, 65 | error: function (xhr, errorType, error) { 66 | logger.e(errorType + ', ' + error); 67 | } 68 | }); 69 | }, 70 | onFail: function (err) { 71 | logger.e('requestAuthCode fail: ' + JSON.stringify(err)); 72 | } 73 | }); 74 | 75 | $('.chooseonebtn').on('click', function() { 76 | 77 | dd.biz.chat.pickConversation({ 78 | corpId: _config.corpId, //企业id 79 | isConfirm:'false', //是否弹出确认窗口,默认为true 80 | onSuccess: function (data) { 81 | var chatinfo = data; 82 | if(chatinfo){ 83 | console.log(chatinfo.cid); 84 | dd.device.notification.prompt({ 85 | message: "发送消息", 86 | title: chatinfo.title, 87 | buttonLabels: ['发送', '取消'], 88 | onSuccess : function(result) { 89 | var text = result.value; 90 | if(text==''){ 91 | return false; 92 | } 93 | 94 | $.ajax({ 95 | url: '/sendMsg.php', 96 | type:"POST", 97 | data: {"event":"send_to_conversation","cid":chatinfo.cid,"sender":dd.userid,"content":text}, 98 | dataType:'json', 99 | timeout: 900, 100 | success: function (data, status, xhr) { 101 | var info = data; 102 | logger.i('sendMsg: ' + JSON.stringify(data)); 103 | if(info.errcode==0){ 104 | logger.i('sendMsg: 发送成功'); 105 | /** 106 | * 跳转到对话界面 107 | */ 108 | dd.biz.chat.open({ 109 | cid:chatinfo.cid, 110 | onSuccess : function(result) { 111 | }, 112 | onFail : function(err) {} 113 | }); 114 | }else{ 115 | logger.e('sendMsg: 发送失败'+info.errmsg); 116 | } 117 | }, 118 | error: function (xhr, errorType, error) { 119 | logger.e(errorType + ', ' + error); 120 | } 121 | }); 122 | }, 123 | onFail : function(err) {} 124 | }); 125 | } 126 | }, 127 | onFail: function (err) { 128 | } 129 | }); 130 | }); 131 | 132 | $('.phonecall').on('click', function() { 133 | dd.biz.contact.choose({ 134 | startWithDepartmentId: 0, //-1表示打开的通讯录从自己所在部门开始展示, 0表示从企业最上层开始,(其他数字表示从该部门开始:暂时不支持) 135 | multiple: false, //是否多选: true多选 false单选; 默认true 136 | users: [], //默认选中的用户列表,userid;成功回调中应包含该信息 137 | corpId: _config.corpId, //企业id 138 | max: 10, //人数限制,当multiple为true才生效,可选范围1-1500 139 | onSuccess: function(data) { 140 | if(data&&data.length>0){ 141 | var selectUserId = data[0].emplId; 142 | if(selectUserId>0){ 143 | dd.biz.telephone.call({ 144 | users: [selectUserId], //用户列表,工号 145 | corpId: _config.corpId, //企业id 146 | onSuccess : function(info) { 147 | logger.i('biz.telephone.call: info' + JSON.stringify(info)); 148 | 149 | }, 150 | onFail : function(err) { 151 | logger.e('biz.telephone.call: error' + JSON.stringify(err)); 152 | } 153 | }) 154 | }else{ 155 | return false; 156 | } 157 | }else{ 158 | return false; 159 | } 160 | }, 161 | onFail : function(err) {} 162 | }); 163 | }); 164 | }); 165 | 166 | dd.error(function(err) { 167 | logger.e('dd error: ' + JSON.stringify(err)); 168 | }); 169 | -------------------------------------------------------------------------------- /corp/public/javascripts/demopc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 8/10/15. 3 | */ 4 | 5 | logger.i('Here we go...'); 6 | 7 | logger.i(location.href); 8 | 9 | /** 10 | * _config comes from server-side template. see views/index.jade 11 | */ 12 | DingTalkPC.config({ 13 | agentId: _config.agentId, 14 | corpId: _config.corpId, 15 | timeStamp: _config.timeStamp, 16 | nonceStr: _config.nonceStr, 17 | signature: _config.signature, 18 | jsApiList: [ 19 | 'runtime.permission.requestAuthCode', 20 | 'device.notification.alert', 21 | 'device.notification.confirm', 22 | 'biz.contact.choose', 23 | 'device.notification.prompt', 24 | 'biz.ding.post' 25 | ] // 必填,需要使用的jsapi列表 26 | }); 27 | DingTalkPC.userid=0; 28 | DingTalkPC.ready(function(res){ 29 | logger.i('dd.ready rocks!'); 30 | 31 | DingTalkPC.runtime.permission.requestAuthCode({ 32 | corpId: _config.corpId, //企业ID 33 | onSuccess: function(info) { 34 | /*{ 35 | code: 'hYLK98jkf0m' //string authCode 36 | }*/ 37 | logger.i('authcode: ' + info.code); 38 | $.ajax({ 39 | url: '/sendMsg.php', 40 | type:"POST", 41 | data: {"event":"get_userinfo","code":info.code}, 42 | dataType:'json', 43 | timeout: 900, 44 | success: function (data, status, xhr) { 45 | var info = JSON.parse(data); 46 | if (info.errcode === 0) { 47 | logger.i('user id: ' + info.userid); 48 | DingTalkPC.userid = info.userid; 49 | } 50 | else { 51 | logger.e('auth error: ' + data); 52 | } 53 | }, 54 | error: function (xhr, errorType, error) { 55 | logger.e(errorType + ', ' + error); 56 | } 57 | }); 58 | }, 59 | onFail : function(err) { 60 | logger.e(JSON.stringify(err)); 61 | } 62 | 63 | }); 64 | $('.chooseonebtn').on('click', function() { 65 | 66 | DingTalkPC.biz.contact.choose({ 67 | multiple: false, //是否多选: true多选 false单选; 默认true 68 | users: [], //默认选中的用户列表,工号;成功回调中应包含该信息 69 | corpId: _config.corpId, //企业id 70 | max: 1, //人数限制,当multiple为true才生效,可选范围1-1500 71 | onSuccess: function(data) { 72 | if(data&&data.length>0){ 73 | var selectUserId = data[0].emplId; 74 | if(selectUserId>0){ 75 | DingTalkPC.device.notification.prompt({ 76 | message: "发送消息", 77 | title: data[0].name, 78 | buttonLabels: ['发送', '取消'], 79 | onSuccess : function(result) { 80 | var textContent = result.value; 81 | if(textContent==''){ 82 | return false; 83 | } 84 | DingTalkPC.biz.ding.post({ 85 | users : [selectUserId],//用户列表,工号 86 | corpId: _config.corpId, //加密的企业id 87 | type: 1, //钉类型 1:image 2:link 88 | alertType: 2, 89 | alertDate: {"format":"yyyy-MM-dd HH:mm","value":"2016-05-09 08:00"}, 90 | attachment: { 91 | images: [] //只取第一个image 92 | }, //附件信息 93 | text: textContent, //消息体 94 | onSuccess : function(info) { 95 | logger.i('DingTalkPC.biz.ding.post: info' + JSON.stringify(info)); 96 | }, 97 | onFail : function(err) { 98 | logger.e('DingTalkPC.biz.ding.post: info' + JSON.stringify(err)); 99 | } 100 | }) 101 | /* 102 | { 103 | buttonIndex: 0, //被点击按钮的索引值,Number类型,从0开始 104 | value: '' //输入的值 105 | } 106 | */ 107 | }, 108 | onFail : function(err) {} 109 | }); 110 | } 111 | } 112 | }, 113 | onFail : function(err) {} 114 | }); 115 | }); 116 | /*DingTalkPC.biz.util.uploadImage({ 117 | multiple: false, //是否多选,默认false 118 | max: 5, //最多可选个数 119 | onSuccess : function(result) { 120 | logger.i(result); 121 | }, 122 | onFail : function() {} 123 | });*/ 124 | /*DingTalkPC.device.notification.alert({ 125 | message: "亲爱的", 126 | title: "提示",//可传空 127 | buttonName: "收到", 128 | onSuccess : function() { 129 | }, 130 | onFail : function(err) {} 131 | });*/ 132 | }); 133 | 134 | 135 | -------------------------------------------------------------------------------- /corp/public/javascripts/logger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 8/14/15. 3 | */ 4 | 5 | var log = document.createElement('div'); 6 | log.setAttribute('id', 'log'); 7 | document.body.appendChild(log); 8 | 9 | var logger = { 10 | i: function(info) { 11 | add(info, 'i'); 12 | }, 13 | e: function(err) { 14 | add(err, 'e'); 15 | } 16 | }; 17 | 18 | function add(msg, level) { 19 | var row = document.createElement('div'); 20 | row.setAttribute('class', 'log-row log-' + level); 21 | row.innerHTML = msg; 22 | 23 | document.querySelector('#log').appendChild(row); 24 | } -------------------------------------------------------------------------------- /corp/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | .log-i { 11 | color: green; 12 | } 13 | 14 | .log-e { 15 | color: red; 16 | } 17 | 18 | .tag { 19 | display: inline-block; 20 | width: 170px; 21 | } 22 | 23 | .api { 24 | display: inline-block; 25 | width: 300px; 26 | } 27 | 28 | .hidden { 29 | display: none; 30 | } 31 | 32 | .row button { 33 | width: 100%; 34 | height: 100px; 35 | } 36 | 37 | .log-row { 38 | margin: 20px 10px 20px 10px; 39 | font-size: 30px; 40 | word-break: break-all;word-wrap: break-word; 41 | } -------------------------------------------------------------------------------- /corp/sendMsg.php: -------------------------------------------------------------------------------- 1 | "4000")); 13 | break; 14 | case 'send_to_conversation': 15 | $sender = $_POST['sender']; 16 | $cid = $_POST['cid']; 17 | $content = $_POST['content']; 18 | $accessToken = Auth::getAccessToken(); 19 | $option = array( 20 | "sender"=>$sender, 21 | "cid"=>$cid, 22 | "msgtype"=>"text", 23 | "text"=>array("content"=>$content) 24 | ); 25 | $response = Message::sendToConversation($accessToken,$option); 26 | 27 | echo json_encode($response); 28 | break; 29 | 30 | case 'get_userinfo': 31 | $accessToken = Auth::getAccessToken(); 32 | $code = $_POST["code"]; 33 | $userInfo = User::getUserInfo($accessToken, $code); 34 | Log::i("[USERINFO]".json_encode($userInfo)); 35 | echo json_encode($userInfo); 36 | break; 37 | } 38 | -------------------------------------------------------------------------------- /corp/util/Cache.php: -------------------------------------------------------------------------------- 1 | set("suite_ticket", $ticket); 9 | } 10 | 11 | public static function getSuiteTicket() 12 | { 13 | $memcache = self::getMemcache(); 14 | return $memcache->get("suite_ticket"); 15 | } 16 | 17 | public static function setJsTicket($ticket) 18 | { 19 | $memcache = self::getMemcache(); 20 | $memcache->set("js_ticket", $ticket, 0, time() + 7000); // js ticket有效期为7200秒,这里设置为7000秒 21 | } 22 | 23 | public static function getJsTicket() 24 | { 25 | $memcache = self::getMemcache(); 26 | return $memcache->get("js_ticket"); 27 | } 28 | 29 | public static function setSuiteAccessToken($accessToken) 30 | { 31 | $memcache = self::getMemcache(); 32 | $memcache->set("suite_access_token", $accessToken, 0, time() + 7000); // suite access token有效期为7200秒,这里设置为7000秒 33 | } 34 | 35 | public static function getSuiteAccessToken() 36 | { 37 | $memcache = self::getMemcache(); 38 | return $memcache->get("suite_access_token"); 39 | } 40 | 41 | public static function setCorpAccessToken($accessToken) 42 | { 43 | $memcache = self::getMemcache(); 44 | $memcache->set("corp_access_token", $accessToken, 0, time() + 7000); // corp access token有效期为7200秒,这里设置为7000秒 45 | } 46 | 47 | public static function getCorpAccessToken() 48 | { 49 | $memcache = self::getMemcache(); 50 | return $memcache->get("corp_access_token"); 51 | } 52 | 53 | public static function setIsvCorpAccessToken($accessToken) 54 | { 55 | $memcache = self::getMemcache(); 56 | $memcache->set("isv_corp_access_token", $accessToken, 0, time() + 7000); // corp access token有效期为7200秒,这里设置为7000秒 57 | } 58 | 59 | public static function getIsvCorpAccessToken() 60 | { 61 | $memcache = self::getMemcache(); 62 | return $memcache->get("isv_corp_access_token"); 63 | } 64 | 65 | public static function setTmpAuthCode($tmpAuthCode){ 66 | $memcache = self::getMemcache(); 67 | $memcache->set("tmp_auth_code", $tmpAuthCode); 68 | } 69 | 70 | public static function getTmpAuthCode(){ 71 | $memcache = self::getMemcache(); 72 | $memcache->get("tmp_auth_code"); 73 | } 74 | 75 | public static function setPermanentAuthCodeInfo($code) 76 | { 77 | $memcache = self::getMemcache(); 78 | $memcache->set("permanent_auth_code_info", $code); 79 | } 80 | 81 | public static function getPermanentAuthCodeInfo() 82 | { 83 | $memcache = self::getMemcache(); 84 | return $memcache->get("permanent_auth_code_info"); 85 | } 86 | 87 | 88 | private static function getMemcache() 89 | { 90 | /*if (class_exists("Memcache")) 91 | { 92 | $memcache = new Memcache; 93 | if ($memcache->connect('localhost', 11211)) 94 | { 95 | return $memcache; 96 | } 97 | }*/ 98 | 99 | return new FileCache; 100 | } 101 | 102 | public static function get($key) 103 | { 104 | return self::getMemcache()->get($key); 105 | } 106 | 107 | public static function set($key, $value) 108 | { 109 | self::getMemcache()->set($key, $value); 110 | } 111 | } 112 | 113 | /** 114 | * fallbacks 115 | */ 116 | class FileCache 117 | { 118 | function set($key, $value) 119 | { 120 | if($key&&$value){ 121 | $data = json_decode($this->get_file(DIR_ROOT ."filecache.php"),true); 122 | $item = array(); 123 | $item["$key"] = $value; 124 | 125 | $keyList = array('isv_corp_access_token','suite_access_token','js_ticket','corp_access_token'); 126 | if(in_array($key,$keyList)){ 127 | $item['expire_time'] = time() + 7000; 128 | }else{ 129 | $item['expire_time'] = 0; 130 | } 131 | $item['create_time'] = time(); 132 | $data["$key"] = $item; 133 | $this->set_file("filecache.php",json_encode($data)); 134 | } 135 | } 136 | 137 | function get($key) 138 | { 139 | if($key){ 140 | $data = json_decode($this->get_file(DIR_ROOT ."filecache.php"),true); 141 | if($data&&array_key_exists($key,$data)){ 142 | $item = $data["$key"]; 143 | if(!$item){ 144 | return false; 145 | } 146 | if($item['expire_time']>0&&$item['expire_time'] < time()){ 147 | return false; 148 | } 149 | 150 | return $item["$key"]; 151 | }else{ 152 | return false; 153 | } 154 | 155 | } 156 | } 157 | 158 | function get_file($filename) { 159 | if (!file_exists($filename)) { 160 | $fp = fopen($filename, "w"); 161 | fwrite($fp, "" . ''); 162 | fclose($fp); 163 | return false; 164 | }else{ 165 | $content = trim(substr(file_get_contents($filename), 15)); 166 | } 167 | return $content; 168 | } 169 | 170 | function set_file($filename, $content) { 171 | $fp = fopen($filename, "w"); 172 | fwrite($fp, "" . $content); 173 | fclose($fp); 174 | } 175 | } -------------------------------------------------------------------------------- /corp/util/Http.php: -------------------------------------------------------------------------------- 1 | send(); 12 | if ($response->hasErrors()) 13 | { 14 | var_dump($response); 15 | } 16 | if ($response->body->errcode != 0) 17 | { 18 | var_dump($response->body); 19 | } 20 | return $response->body; 21 | } 22 | 23 | 24 | public static function post($path, $params, $data) 25 | { 26 | $url = self::joinParams($path, $params); 27 | $response = \Httpful\Request::post($url) 28 | ->body($data) 29 | ->sendsJson() 30 | ->send(); 31 | if ($response->hasErrors()) 32 | { 33 | var_dump($response); 34 | } 35 | if ($response->body->errcode != 0) 36 | { 37 | var_dump($response->body); 38 | } 39 | return $response->body; 40 | } 41 | 42 | 43 | private static function joinParams($path, $params) 44 | { 45 | $url = OAPI_HOST . $path; 46 | if (count($params) > 0) 47 | { 48 | $url = $url . "?"; 49 | foreach ($params as $key => $value) 50 | { 51 | $url = $url . $key . "=" . $value . "&"; 52 | } 53 | $length = count($url); 54 | if ($url[$length - 1] == '&') 55 | { 56 | $url = substr($url, 0, $length - 1); 57 | } 58 | } 59 | return $url; 60 | } 61 | } -------------------------------------------------------------------------------- /corp/util/Log.php: -------------------------------------------------------------------------------- 1 | array($vendorDir . '/nategood/httpful/src'), 10 | ); 11 | -------------------------------------------------------------------------------- /corp/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 composerRequire9c336c4d71b270c36d1e071cf0b19270($file) 48 | { 49 | require $file; 50 | } 51 | -------------------------------------------------------------------------------- /corp/vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "nategood/httpful", 4 | "version": "0.2.19", 5 | "version_normalized": "0.2.19.0", 6 | "source": { 7 | "type": "git", 8 | "url": "https://github.com/nategood/httpful.git", 9 | "reference": "bd73f89d34d8f879c54ac46eb94b0f7be1d00820" 10 | }, 11 | "dist": { 12 | "type": "zip", 13 | "url": "https://api.github.com/repos/nategood/httpful/zipball/bd73f89d34d8f879c54ac46eb94b0f7be1d00820", 14 | "reference": "bd73f89d34d8f879c54ac46eb94b0f7be1d00820", 15 | "shasum": "" 16 | }, 17 | "require": { 18 | "ext-curl": "*", 19 | "php": ">=5.3" 20 | }, 21 | "require-dev": { 22 | "phpunit/phpunit": "*" 23 | }, 24 | "time": "2015-03-08 15:22:23", 25 | "type": "library", 26 | "installation-source": "dist", 27 | "autoload": { 28 | "psr-0": { 29 | "Httpful": "src/" 30 | } 31 | }, 32 | "notification-url": "https://packagist.org/downloads/", 33 | "license": [ 34 | "MIT" 35 | ], 36 | "authors": [ 37 | { 38 | "name": "Nate Good", 39 | "email": "me@nategood.com", 40 | "homepage": "http://nategood.com" 41 | } 42 | ], 43 | "description": "A Readable, Chainable, REST friendly, PHP HTTP Client", 44 | "homepage": "http://github.com/nategood/httpful", 45 | "keywords": [ 46 | "api", 47 | "curl", 48 | "http", 49 | "requests", 50 | "rest", 51 | "restful" 52 | ] 53 | } 54 | ] 55 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | composer.lock 3 | vendor 4 | downloads 5 | .idea/* 6 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | before_script: cd tests 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | - hhvm -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Nate Good 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/README.md: -------------------------------------------------------------------------------- 1 | # Httpful 2 | 3 | [![Build Status](https://secure.travis-ci.org/nategood/httpful.png?branch=master)](http://travis-ci.org/nategood/httpful) [![Total Downloads](https://poser.pugx.org/nategood/httpful/downloads.png)](https://packagist.org/packages/nategood/httpful) 4 | 5 | [Httpful](http://phphttpclient.com) is a simple Http Client library for PHP 5.3+. There is an emphasis of readability, simplicity, and flexibility – basically provide the features and flexibility to get the job done and make those features really easy to use. 6 | 7 | Features 8 | 9 | - Readable HTTP Method Support (GET, PUT, POST, DELETE, HEAD, PATCH and OPTIONS) 10 | - Custom Headers 11 | - Automatic "Smart" Parsing 12 | - Automatic Payload Serialization 13 | - Basic Auth 14 | - Client Side Certificate Auth 15 | - Request "Templates" 16 | 17 | # Sneak Peak 18 | 19 | Here's something to whet your appetite. Search the twitter API for tweets containing "#PHP". Include a trivial header for the heck of it. Notice that the library automatically interprets the response as JSON (can override this if desired) and parses it as an array of objects. 20 | 21 | ```php 22 | $url = "http://search.twitter.com/search.json?q=" . urlencode('#PHP'); 23 | $response = Request::get($url) 24 | ->withXTrivialHeader('Just as a demo') 25 | ->send(); 26 | 27 | foreach ($response->body->results as $tweet) { 28 | echo "@{$tweet->from_user} tweets \"{$tweet->text}\"\n"; 29 | } 30 | ``` 31 | 32 | # Installation 33 | 34 | ## Phar 35 | 36 | A [PHP Archive](http://php.net/manual/en/book.phar.php) (or .phar) file is available for [downloading](http://phphttpclient.com/httpful.phar). Simply [download](http://phphttpclient.com/httpful.phar) the .phar, drop it into your project, and include it like you would any other php file. _This method is ideal for smaller projects, one off scripts, and quick API hacking_. 37 | 38 | ```php 39 | include('httpful.phar'); 40 | $r = \Httpful\Request::get($uri)->sendIt(); 41 | ... 42 | ``` 43 | 44 | ## Composer 45 | 46 | Httpful is PSR-0 compliant and can be installed using [composer](http://getcomposer.org/). Simply add `nategood/httpful` to your composer.json file. _Composer is the sane alternative to PEAR. It is excellent for managing dependencies in larger projects_. 47 | 48 | { 49 | "require": { 50 | "nategood/httpful": "*" 51 | } 52 | } 53 | 54 | ## Install from Source 55 | 56 | Because Httpful is PSR-0 compliant, you can also just clone the Httpful repository and use a PSR-0 compatible autoloader to load the library, like [Symfony's](http://symfony.com/doc/current/components/class_loader.html). Alternatively you can use the PSR-0 compliant autoloader included with the Httpful (simply `require("bootstrap.php")`). 57 | 58 | # Show Me More! 59 | 60 | You can checkout the [Httpful Landing Page](http://phphttpclient.com) for more info including many examples and [documentation](http://phphttpclient.com/docs). 61 | 62 | # Contributing 63 | 64 | Httpful highly encourages sending in pull requests. When submitting a pull request please: 65 | 66 | - All pull requests should target the `dev` branch (not `master`) 67 | - Make sure your code follows the [coding conventions](http://pear.php.net/manual/en/standards.php) 68 | - Please use soft tabs (four spaces) instead of hard tabs 69 | - Make sure you add appropriate test coverage for your changes 70 | - Run all unit tests in the test directory via `phpunit ./tests` 71 | - Include commenting where appropriate and add a descriptive pull request message 72 | 73 | # Changelog 74 | 75 | ## 0.2.19 76 | 77 | - FEATURE Before send hook [PR #164](https://github.com/nategood/httpful/pull/164) 78 | - MINOR More descriptive connection exceptions [PR #166](https://github.com/nategood/httpful/pull/166) 79 | 80 | ## 0.2.18 81 | 82 | - FIX [PR #149](https://github.com/nategood/httpful/pull/149) 83 | - FIX [PR #150](https://github.com/nategood/httpful/pull/150) 84 | - FIX [PR #156](https://github.com/nategood/httpful/pull/156) 85 | 86 | ## 0.2.17 87 | 88 | - FEATURE [PR #144](https://github.com/nategood/httpful/pull/144) Adds additional parameter to the Response class to specify additional meta data about the request/response (e.g. number of redirect). 89 | 90 | ## 0.2.16 91 | 92 | - FEATURE Added support for whenError to define a custom callback to be fired upon error. Useful for logging or overriding the default error_log behavior. 93 | 94 | ## 0.2.15 95 | 96 | - FEATURE [I #131](https://github.com/nategood/httpful/pull/131) Support for SOCKS proxy 97 | 98 | ## 0.2.14 99 | 100 | - FEATURE [I #138](https://github.com/nategood/httpful/pull/138) Added alternative option for XML request construction. In the next major release this will likely supplant the older version. 101 | 102 | ## 0.2.13 103 | 104 | - REFACTOR [I #121](https://github.com/nategood/httpful/pull/121) Throw more descriptive exception on curl errors 105 | - REFACTOR [I #122](https://github.com/nategood/httpful/issues/122) Better proxy scrubbing in Request 106 | - REFACTOR [I #119](https://github.com/nategood/httpful/issues/119) Better document the mimeType param on Request::body 107 | - Misc code and test cleanup 108 | 109 | ## 0.2.12 110 | 111 | - REFACTOR [I #123](https://github.com/nategood/httpful/pull/123) Support new curl file upload method 112 | - FEATURE [I #118](https://github.com/nategood/httpful/pull/118) 5.4 HTTP Test Server 113 | - FIX [I #109](https://github.com/nategood/httpful/pull/109) Typo 114 | - FIX [I #103](https://github.com/nategood/httpful/pull/103) Handle also CURLOPT_SSL_VERIFYHOST for strictSsl mode 115 | 116 | ## 0.2.11 117 | 118 | - FIX [I #99](https://github.com/nategood/httpful/pull/99) Prevent hanging on HEAD requests 119 | 120 | ## 0.2.10 121 | 122 | - FIX [I #93](https://github.com/nategood/httpful/pull/86) Fixes edge case where content-length would be set incorrectly 123 | 124 | ## 0.2.9 125 | 126 | - FEATURE [I #89](https://github.com/nategood/httpful/pull/89) multipart/form-data support (a.k.a. file uploads)! Thanks @dtelaroli! 127 | 128 | ## 0.2.8 129 | 130 | - FIX Notice fix for Pull Request 86 131 | 132 | ## 0.2.7 133 | 134 | - FIX [I #86](https://github.com/nategood/httpful/pull/86) Remove Connection Established header when using a proxy 135 | 136 | ## 0.2.6 137 | 138 | - FIX [I #85](https://github.com/nategood/httpful/issues/85) Empty Content Length issue resolved 139 | 140 | ## 0.2.5 141 | 142 | - FEATURE [I #80](https://github.com/nategood/httpful/issues/80) [I #81](https://github.com/nategood/httpful/issues/81) Proxy support added with `useProxy` method. 143 | 144 | ## 0.2.4 145 | 146 | - FEATURE [I #77](https://github.com/nategood/httpful/issues/77) Convenience method for setting a timeout (seconds) `$req->timeoutIn(10);` 147 | - FIX [I #75](https://github.com/nategood/httpful/issues/75) [I #78](https://github.com/nategood/httpful/issues/78) Bug with checking if digest auth is being used. 148 | 149 | ## 0.2.3 150 | 151 | - FIX Overriding default Mime Handlers 152 | - FIX [PR #73](https://github.com/nategood/httpful/pull/73) Parsing http status codes 153 | 154 | ## 0.2.2 155 | 156 | - FEATURE Add support for parsing JSON responses as associative arrays instead of objects 157 | - FEATURE Better support for setting constructor arguments on Mime Handlers 158 | 159 | ## 0.2.1 160 | 161 | - FEATURE [PR #72](https://github.com/nategood/httpful/pull/72) Allow support for custom Accept header 162 | 163 | ## 0.2.0 164 | 165 | - REFACTOR [PR #49](https://github.com/nategood/httpful/pull/49) Broke headers out into their own class 166 | - REFACTOR [PR #54](https://github.com/nategood/httpful/pull/54) Added more specific Exceptions 167 | - FIX [PR #58](https://github.com/nategood/httpful/pull/58) Fixes throwing an error on an empty xml response 168 | - FEATURE [PR #57](https://github.com/nategood/httpful/pull/57) Adds support for digest authentication 169 | 170 | ## 0.1.6 171 | 172 | - Ability to set the number of max redirects via overloading `followRedirects(int max_redirects)` 173 | - Standards Compliant fix to `Accepts` header 174 | - Bug fix for bootstrap process when installed via Composer 175 | 176 | ## 0.1.5 177 | 178 | - Use `DIRECTORY_SEPARATOR` constant [PR #33](https://github.com/nategood/httpful/pull/32) 179 | - [PR #35](https://github.com/nategood/httpful/pull/35) 180 | - Added the raw\_headers property reference to response. 181 | - Compose request header and added raw\_header to Request object. 182 | - Fixed response has errors and added more comments for clarity. 183 | - Fixed header parsing to allow the minimum (status line only) and also cater for the actual CRLF ended headers as per RFC2616. 184 | - Added the perfect test Accept: header for all Acceptable scenarios see @b78e9e82cd9614fbe137c01bde9439c4e16ca323 for details. 185 | - Added default User-Agent header 186 | - `User-Agent: Httpful/0.1.5` + curl version + server software + PHP version 187 | - To bypass this "default" operation simply add a User-Agent to the request headers even a blank User-Agent is sufficient and more than simple enough to produce me thinks. 188 | - Completed test units for additions. 189 | - Added phpunit coverage reporting and helped phpunit auto locate the tests a bit easier. 190 | 191 | ## 0.1.4 192 | 193 | - Add support for CSV Handling [PR #32](https://github.com/nategood/httpful/pull/32) 194 | 195 | ## 0.1.3 196 | 197 | - Handle empty responses in JsonParser and XmlParser 198 | 199 | ## 0.1.2 200 | 201 | - Added support for setting XMLHandler configuration options 202 | - Added examples for overriding XmlHandler and registering a custom parser 203 | - Removed the httpful.php download (deprecated in favor of httpful.phar) 204 | 205 | ## 0.1.1 206 | 207 | - Bug fix serialization default case and phpunit tests 208 | 209 | ## 0.1.0 210 | 211 | - Added Support for Registering Mime Handlers 212 | - Created AbstractMimeHandler type that all Mime Handlers must extend 213 | - Pulled out the parsing/serializing logic from the Request/Response classes into their own MimeHandler classes 214 | - Added ability to register new mime handlers for mime types 215 | 216 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/bootstrap.php: -------------------------------------------------------------------------------- 1 | setStub($stub); 36 | } catch(Exception $e) { 37 | $phar = false; 38 | } 39 | exit_unless($phar, "Unable to create a phar. Make certain you have phar.readonly=0 set in your ini file."); 40 | $phar->buildFromDirectory(dirname($source_dir)); 41 | echo "[ OK ]\n"; 42 | 43 | 44 | 45 | // Add it to git! 46 | //echo "Adding httpful.phar to the repo... "; 47 | //$return_code = 0; 48 | //passthru("git add $phar_path", $return_code); 49 | //exit_unless($return_code === 0, "Unable to add download files to git."); 50 | //echo "[ OK ]\n"; 51 | echo "\nBuild completed successfully.\n\n"; 52 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nategood/httpful", 3 | "description": "A Readable, Chainable, REST friendly, PHP HTTP Client", 4 | "homepage": "http://github.com/nategood/httpful", 5 | "license": "MIT", 6 | "keywords": ["http", "curl", "rest", "restful", "api", "requests"], 7 | "version": "0.2.19", 8 | "authors": [ 9 | { 10 | "name": "Nate Good", 11 | "email": "me@nategood.com", 12 | "homepage": "http://nategood.com" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=5.3", 17 | "ext-curl": "*" 18 | }, 19 | "autoload": { 20 | "psr-0": { 21 | "Httpful": "src/" 22 | } 23 | }, 24 | "require-dev": { 25 | "phpunit/phpunit": "*" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/examples/freebase.php: -------------------------------------------------------------------------------- 1 | expectsJson() 10 | ->sendIt(); 11 | 12 | echo 'The Dead Weather has ' . count($response->body->result->album) . " albums.\n"; -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/examples/github.php: -------------------------------------------------------------------------------- 1 | send(); 8 | 9 | echo "{$request->body->name} joined GitHub on " . date('M jS', strtotime($request->body->{'created-at'})) ."\n"; -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/examples/override.php: -------------------------------------------------------------------------------- 1 | 'http://example.com'); 9 | \Httpful\Httpful::register(\Httpful\Mime::XML, new \Httpful\Handlers\XmlHandler($conf)); 10 | 11 | // We can also add the parsers with our own... 12 | class SimpleCsvHandler extends \Httpful\Handlers\MimeHandlerAdapter 13 | { 14 | /** 15 | * Takes a response body, and turns it into 16 | * a two dimensional array. 17 | * 18 | * @param string $body 19 | * @return mixed 20 | */ 21 | public function parse($body) 22 | { 23 | return str_getcsv($body); 24 | } 25 | 26 | /** 27 | * Takes a two dimensional array and turns it 28 | * into a serialized string to include as the 29 | * body of a request 30 | * 31 | * @param mixed $payload 32 | * @return string 33 | */ 34 | public function serialize($payload) 35 | { 36 | $serialized = ''; 37 | foreach ($payload as $line) { 38 | $serialized .= '"' . implode('","', $line) . '"' . "\n"; 39 | } 40 | return $serialized; 41 | } 42 | } 43 | 44 | \Httpful\Httpful::register('text/csv', new SimpleCsvHandler()); -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/examples/showclix.php: -------------------------------------------------------------------------------- 1 | expectsType('json') 11 | ->send(); 12 | 13 | // Print out the event details 14 | echo "The event {$response->body->event} will take place on {$response->body->event_start}\n"; 15 | 16 | // Example overriding the default JSON handler with one that encodes the response as an array 17 | \Httpful\Httpful::register(\Httpful\Mime::JSON, new \Httpful\Handlers\JsonHandler(array('decode_as_array' => true))); 18 | 19 | $response = Request::get($uri) 20 | ->expectsType('json') 21 | ->send(); 22 | 23 | // Print out the event details 24 | echo "The event {$response->body['event']} will take place on {$response->body['event_start']}\n"; -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/src/Httpful/Bootstrap.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Bootstrap 12 | { 13 | 14 | const DIR_GLUE = DIRECTORY_SEPARATOR; 15 | const NS_GLUE = '\\'; 16 | 17 | public static $registered = false; 18 | 19 | /** 20 | * Register the autoloader and any other setup needed 21 | */ 22 | public static function init() 23 | { 24 | spl_autoload_register(array('\Httpful\Bootstrap', 'autoload')); 25 | self::registerHandlers(); 26 | } 27 | 28 | /** 29 | * The autoload magic (PSR-0 style) 30 | * 31 | * @param string $classname 32 | */ 33 | public static function autoload($classname) 34 | { 35 | self::_autoload(dirname(dirname(__FILE__)), $classname); 36 | } 37 | 38 | /** 39 | * Register the autoloader and any other setup needed 40 | */ 41 | public static function pharInit() 42 | { 43 | spl_autoload_register(array('\Httpful\Bootstrap', 'pharAutoload')); 44 | self::registerHandlers(); 45 | } 46 | 47 | /** 48 | * Phar specific autoloader 49 | * 50 | * @param string $classname 51 | */ 52 | public static function pharAutoload($classname) 53 | { 54 | self::_autoload('phar://httpful.phar', $classname); 55 | } 56 | 57 | /** 58 | * @param string base 59 | * @param string classname 60 | */ 61 | private static function _autoload($base, $classname) 62 | { 63 | $parts = explode(self::NS_GLUE, $classname); 64 | $path = $base . self::DIR_GLUE . implode(self::DIR_GLUE, $parts) . '.php'; 65 | 66 | if (file_exists($path)) { 67 | require_once($path); 68 | } 69 | } 70 | /** 71 | * Register default mime handlers. Is idempotent. 72 | */ 73 | public static function registerHandlers() 74 | { 75 | if (self::$registered === true) { 76 | return; 77 | } 78 | 79 | // @todo check a conf file to load from that instead of 80 | // hardcoding into the library? 81 | $handlers = array( 82 | \Httpful\Mime::JSON => new \Httpful\Handlers\JsonHandler(), 83 | \Httpful\Mime::XML => new \Httpful\Handlers\XmlHandler(), 84 | \Httpful\Mime::FORM => new \Httpful\Handlers\FormHandler(), 85 | \Httpful\Mime::CSV => new \Httpful\Handlers\CsvHandler(), 86 | ); 87 | 88 | foreach ($handlers as $mime => $handler) { 89 | // Don't overwrite if the handler has already been registered 90 | if (Httpful::hasParserRegistered($mime)) 91 | continue; 92 | Httpful::register($mime, $handler); 93 | } 94 | 95 | self::$registered = true; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/src/Httpful/Exception/ConnectionErrorException.php: -------------------------------------------------------------------------------- 1 | 5 | */ 6 | 7 | namespace Httpful\Handlers; 8 | 9 | class CsvHandler extends MimeHandlerAdapter 10 | { 11 | /** 12 | * @param string $body 13 | * @return mixed 14 | */ 15 | public function parse($body) 16 | { 17 | if (empty($body)) 18 | return null; 19 | 20 | $parsed = array(); 21 | $fp = fopen('data://text/plain;base64,' . base64_encode($body), 'r'); 22 | while (($r = fgetcsv($fp)) !== FALSE) { 23 | $parsed[] = $r; 24 | } 25 | 26 | if (empty($parsed)) 27 | throw new \Exception("Unable to parse response as CSV"); 28 | return $parsed; 29 | } 30 | 31 | /** 32 | * @param mixed $payload 33 | * @return string 34 | */ 35 | public function serialize($payload) 36 | { 37 | $fp = fopen('php://temp/maxmemory:'. (6*1024*1024), 'r+'); 38 | $i = 0; 39 | foreach ($payload as $fields) { 40 | if($i++ == 0) { 41 | fputcsv($fp, array_keys($fields)); 42 | } 43 | fputcsv($fp, $fields); 44 | } 45 | rewind($fp); 46 | $data = stream_get_contents($fp); 47 | fclose($fp); 48 | return $data; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/src/Httpful/Handlers/FormHandler.php: -------------------------------------------------------------------------------- 1 | 5 | */ 6 | 7 | namespace Httpful\Handlers; 8 | 9 | class FormHandler extends MimeHandlerAdapter 10 | { 11 | /** 12 | * @param string $body 13 | * @return mixed 14 | */ 15 | public function parse($body) 16 | { 17 | $parsed = array(); 18 | parse_str($body, $parsed); 19 | return $parsed; 20 | } 21 | 22 | /** 23 | * @param mixed $payload 24 | * @return string 25 | */ 26 | public function serialize($payload) 27 | { 28 | return http_build_query($payload, null, '&'); 29 | } 30 | } -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/src/Httpful/Handlers/JsonHandler.php: -------------------------------------------------------------------------------- 1 | 5 | */ 6 | 7 | namespace Httpful\Handlers; 8 | 9 | class JsonHandler extends MimeHandlerAdapter 10 | { 11 | private $decode_as_array = false; 12 | 13 | public function init(array $args) 14 | { 15 | $this->decode_as_array = !!(array_key_exists('decode_as_array', $args) ? $args['decode_as_array'] : false); 16 | } 17 | 18 | /** 19 | * @param string $body 20 | * @return mixed 21 | */ 22 | public function parse($body) 23 | { 24 | $body = $this->stripBom($body); 25 | if (empty($body)) 26 | return null; 27 | $parsed = json_decode($body, $this->decode_as_array); 28 | if (is_null($parsed) && 'null' !== strtolower($body)) 29 | throw new \Exception("Unable to parse response as JSON"); 30 | return $parsed; 31 | } 32 | 33 | /** 34 | * @param mixed $payload 35 | * @return string 36 | */ 37 | public function serialize($payload) 38 | { 39 | return json_encode($payload); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/src/Httpful/Handlers/MimeHandlerAdapter.php: -------------------------------------------------------------------------------- 1 | init($args); 16 | } 17 | 18 | /** 19 | * Initial setup of 20 | * @param array $args 21 | */ 22 | public function init(array $args) 23 | { 24 | } 25 | 26 | /** 27 | * @param string $body 28 | * @return mixed 29 | */ 30 | public function parse($body) 31 | { 32 | return $body; 33 | } 34 | 35 | /** 36 | * @param mixed $payload 37 | * @return string 38 | */ 39 | function serialize($payload) 40 | { 41 | return (string) $payload; 42 | } 43 | 44 | protected function stripBom($body) 45 | { 46 | if ( substr($body,0,3) === "\xef\xbb\xbf" ) // UTF-8 47 | $body = substr($body,3); 48 | else if ( substr($body,0,4) === "\xff\xfe\x00\x00" || substr($body,0,4) === "\x00\x00\xfe\xff" ) // UTF-32 49 | $body = substr($body,4); 50 | else if ( substr($body,0,2) === "\xff\xfe" || substr($body,0,2) === "\xfe\xff" ) // UTF-16 51 | $body = substr($body,2); 52 | return $body; 53 | } 54 | } -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/src/Httpful/Handlers/README.md: -------------------------------------------------------------------------------- 1 | # Handlers 2 | 3 | Handlers are simple classes that are used to parse response bodies and serialize request payloads. All Handlers must extend the `MimeHandlerAdapter` class and implement two methods: `serialize($payload)` and `parse($response)`. Let's build a very basic Handler to register for the `text/csv` mime type. 4 | 5 | 7 | */ 8 | 9 | namespace Httpful\Handlers; 10 | 11 | class XHtmlHandler extends MimeHandlerAdapter 12 | { 13 | // @todo add html specific parsing 14 | // see DomDocument::load http://docs.php.net/manual/en/domdocument.loadhtml.php 15 | } -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/src/Httpful/Handlers/XmlHandler.php: -------------------------------------------------------------------------------- 1 | 6 | * @author Nathan Good 7 | */ 8 | 9 | namespace Httpful\Handlers; 10 | 11 | class XmlHandler extends MimeHandlerAdapter 12 | { 13 | /** 14 | * @var string $namespace xml namespace to use with simple_load_string 15 | */ 16 | private $namespace; 17 | 18 | /** 19 | * @var int $libxml_opts see http://www.php.net/manual/en/libxml.constants.php 20 | */ 21 | private $libxml_opts; 22 | 23 | /** 24 | * @param array $conf sets configuration options 25 | */ 26 | public function __construct(array $conf = array()) 27 | { 28 | $this->namespace = isset($conf['namespace']) ? $conf['namespace'] : ''; 29 | $this->libxml_opts = isset($conf['libxml_opts']) ? $conf['libxml_opts'] : 0; 30 | } 31 | 32 | /** 33 | * @param string $body 34 | * @return mixed 35 | * @throws Exception if unable to parse 36 | */ 37 | public function parse($body) 38 | { 39 | $body = $this->stripBom($body); 40 | if (empty($body)) 41 | return null; 42 | $parsed = simplexml_load_string($body, null, $this->libxml_opts, $this->namespace); 43 | if ($parsed === false) 44 | throw new \Exception("Unable to parse response as XML"); 45 | return $parsed; 46 | } 47 | 48 | /** 49 | * @param mixed $payload 50 | * @return string 51 | * @throws Exception if unable to serialize 52 | */ 53 | public function serialize($payload) 54 | { 55 | list($_, $dom) = $this->_future_serializeAsXml($payload); 56 | return $dom->saveXml(); 57 | } 58 | 59 | /** 60 | * @param mixed $payload 61 | * @return string 62 | * @author Ted Zellers 63 | */ 64 | public function serialize_clean($payload) 65 | { 66 | $xml = new \XMLWriter; 67 | $xml->openMemory(); 68 | $xml->startDocument('1.0','ISO-8859-1'); 69 | $this->serialize_node($xml, $payload); 70 | return $xml->outputMemory(true); 71 | } 72 | 73 | /** 74 | * @param XMLWriter $xmlw 75 | * @param mixed $node to serialize 76 | * @author Ted Zellers 77 | */ 78 | public function serialize_node(&$xmlw, $node){ 79 | if (!is_array($node)){ 80 | $xmlw->text($node); 81 | } else { 82 | foreach ($node as $k => $v){ 83 | $xmlw->startElement($k); 84 | $this->serialize_node($xmlw, $v); 85 | $xmlw->endElement(); 86 | } 87 | } 88 | } 89 | 90 | /** 91 | * @author Zack Douglas 92 | */ 93 | private function _future_serializeAsXml($value, $node = null, $dom = null) 94 | { 95 | if (!$dom) { 96 | $dom = new \DOMDocument; 97 | } 98 | if (!$node) { 99 | if (!is_object($value)) { 100 | $node = $dom->createElement('response'); 101 | $dom->appendChild($node); 102 | } else { 103 | $node = $dom; 104 | } 105 | } 106 | if (is_object($value)) { 107 | $objNode = $dom->createElement(get_class($value)); 108 | $node->appendChild($objNode); 109 | $this->_future_serializeObjectAsXml($value, $objNode, $dom); 110 | } else if (is_array($value)) { 111 | $arrNode = $dom->createElement('array'); 112 | $node->appendChild($arrNode); 113 | $this->_future_serializeArrayAsXml($value, $arrNode, $dom); 114 | } else if (is_bool($value)) { 115 | $node->appendChild($dom->createTextNode($value?'TRUE':'FALSE')); 116 | } else { 117 | $node->appendChild($dom->createTextNode($value)); 118 | } 119 | return array($node, $dom); 120 | } 121 | /** 122 | * @author Zack Douglas 123 | */ 124 | private function _future_serializeArrayAsXml($value, &$parent, &$dom) 125 | { 126 | foreach ($value as $k => &$v) { 127 | $n = $k; 128 | if (is_numeric($k)) { 129 | $n = "child-{$n}"; 130 | } 131 | $el = $dom->createElement($n); 132 | $parent->appendChild($el); 133 | $this->_future_serializeAsXml($v, $el, $dom); 134 | } 135 | return array($parent, $dom); 136 | } 137 | /** 138 | * @author Zack Douglas 139 | */ 140 | private function _future_serializeObjectAsXml($value, &$parent, &$dom) 141 | { 142 | $refl = new \ReflectionObject($value); 143 | foreach ($refl->getProperties() as $pr) { 144 | if (!$pr->isPrivate()) { 145 | $el = $dom->createElement($pr->getName()); 146 | $parent->appendChild($el); 147 | $this->_future_serializeAsXml($pr->getValue($value), $el, $dom); 148 | } 149 | } 150 | return array($parent, $dom); 151 | } 152 | } -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/src/Httpful/Http.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class Http 9 | { 10 | const HEAD = 'HEAD'; 11 | const GET = 'GET'; 12 | const POST = 'POST'; 13 | const PUT = 'PUT'; 14 | const DELETE = 'DELETE'; 15 | const PATCH = 'PATCH'; 16 | const OPTIONS = 'OPTIONS'; 17 | const TRACE = 'TRACE'; 18 | 19 | /** 20 | * @return array of HTTP method strings 21 | */ 22 | public static function safeMethods() 23 | { 24 | return array(self::HEAD, self::GET, self::OPTIONS, self::TRACE); 25 | } 26 | 27 | /** 28 | * @return bool 29 | * @param string HTTP method 30 | */ 31 | public static function isSafeMethod($method) 32 | { 33 | return in_array($method, self::safeMethods()); 34 | } 35 | 36 | /** 37 | * @return bool 38 | * @param string HTTP method 39 | */ 40 | public static function isUnsafeMethod($method) 41 | { 42 | return !in_array($method, self::safeMethods()); 43 | } 44 | 45 | /** 46 | * @return array list of (always) idempotent HTTP methods 47 | */ 48 | public static function idempotentMethods() 49 | { 50 | // Though it is possible to be idempotent, POST 51 | // is not guarunteed to be, and more often than 52 | // not, it is not. 53 | return array(self::HEAD, self::GET, self::PUT, self::DELETE, self::OPTIONS, self::TRACE, self::PATCH); 54 | } 55 | 56 | /** 57 | * @return bool 58 | * @param string HTTP method 59 | */ 60 | public static function isIdempotent($method) 61 | { 62 | return in_array($method, self::safeidempotentMethodsMethods()); 63 | } 64 | 65 | /** 66 | * @return bool 67 | * @param string HTTP method 68 | */ 69 | public static function isNotIdempotent($method) 70 | { 71 | return !in_array($method, self::idempotentMethods()); 72 | } 73 | 74 | /** 75 | * @deprecated Technically anything *can* have a body, 76 | * they just don't have semantic meaning. So say's Roy 77 | * http://tech.groups.yahoo.com/group/rest-discuss/message/9962 78 | * 79 | * @return array of HTTP method strings 80 | */ 81 | public static function canHaveBody() 82 | { 83 | return array(self::POST, self::PUT, self::PATCH, self::OPTIONS); 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/src/Httpful/Httpful.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class Mime 10 | { 11 | const JSON = 'application/json'; 12 | const XML = 'application/xml'; 13 | const XHTML = 'application/html+xml'; 14 | const FORM = 'application/x-www-form-urlencoded'; 15 | const UPLOAD = 'multipart/form-data'; 16 | const PLAIN = 'text/plain'; 17 | const JS = 'text/javascript'; 18 | const HTML = 'text/html'; 19 | const YAML = 'application/x-yaml'; 20 | const CSV = 'text/csv'; 21 | 22 | /** 23 | * Map short name for a mime type 24 | * to a full proper mime type 25 | */ 26 | public static $mimes = array( 27 | 'json' => self::JSON, 28 | 'xml' => self::XML, 29 | 'form' => self::FORM, 30 | 'plain' => self::PLAIN, 31 | 'text' => self::PLAIN, 32 | 'upload' => self::UPLOAD, 33 | 'html' => self::HTML, 34 | 'xhtml' => self::XHTML, 35 | 'js' => self::JS, 36 | 'javascript'=> self::JS, 37 | 'yaml' => self::YAML, 38 | 'csv' => self::CSV, 39 | ); 40 | 41 | /** 42 | * Get the full Mime Type name from a "short name". 43 | * Returns the short if no mapping was found. 44 | * @return string full mime type (e.g. application/json) 45 | * @param string common name for mime type (e.g. json) 46 | */ 47 | public static function getFullMime($short_name) 48 | { 49 | return array_key_exists($short_name, self::$mimes) ? self::$mimes[$short_name] : $short_name; 50 | } 51 | 52 | /** 53 | * @return bool 54 | * @param string $short_name 55 | */ 56 | public static function supportsMimeType($short_name) 57 | { 58 | return array_key_exists($short_name, self::$mimes); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/src/Httpful/Proxy.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Response 11 | { 12 | 13 | public $body, 14 | $raw_body, 15 | $headers, 16 | $raw_headers, 17 | $request, 18 | $code = 0, 19 | $content_type, 20 | $parent_type, 21 | $charset, 22 | $meta_data, 23 | $is_mime_vendor_specific = false, 24 | $is_mime_personal = false; 25 | 26 | private $parsers; 27 | /** 28 | * @param string $body 29 | * @param string $headers 30 | * @param Request $request 31 | * @param array $meta_data 32 | */ 33 | public function __construct($body, $headers, Request $request, array $meta_data = array()) 34 | { 35 | $this->request = $request; 36 | $this->raw_headers = $headers; 37 | $this->raw_body = $body; 38 | $this->meta_data = $meta_data; 39 | 40 | $this->code = $this->_parseCode($headers); 41 | $this->headers = Response\Headers::fromString($headers); 42 | 43 | $this->_interpretHeaders(); 44 | 45 | $this->body = $this->_parse($body); 46 | } 47 | 48 | /** 49 | * Status Code Definitions 50 | * 51 | * Informational 1xx 52 | * Successful 2xx 53 | * Redirection 3xx 54 | * Client Error 4xx 55 | * Server Error 5xx 56 | * 57 | * http://pretty-rfc.herokuapp.com/RFC2616#status.codes 58 | * 59 | * @return bool Did we receive a 4xx or 5xx? 60 | */ 61 | public function hasErrors() 62 | { 63 | return $this->code >= 400; 64 | } 65 | 66 | /** 67 | * @return return bool 68 | */ 69 | public function hasBody() 70 | { 71 | return !empty($this->body); 72 | } 73 | 74 | /** 75 | * Parse the response into a clean data structure 76 | * (most often an associative array) based on the expected 77 | * Mime type. 78 | * @return array|string|object the response parse accordingly 79 | * @param string Http response body 80 | */ 81 | public function _parse($body) 82 | { 83 | // If the user decided to forgo the automatic 84 | // smart parsing, short circuit. 85 | if (!$this->request->auto_parse) { 86 | return $body; 87 | } 88 | 89 | // If provided, use custom parsing callback 90 | if (isset($this->request->parse_callback)) { 91 | return call_user_func($this->request->parse_callback, $body); 92 | } 93 | 94 | // Decide how to parse the body of the response in the following order 95 | // 1. If provided, use the mime type specifically set as part of the `Request` 96 | // 2. If a MimeHandler is registered for the content type, use it 97 | // 3. If provided, use the "parent type" of the mime type from the response 98 | // 4. Default to the content-type provided in the response 99 | $parse_with = $this->request->expected_type; 100 | if (empty($this->request->expected_type)) { 101 | $parse_with = Httpful::hasParserRegistered($this->content_type) 102 | ? $this->content_type 103 | : $this->parent_type; 104 | } 105 | 106 | return Httpful::get($parse_with)->parse($body); 107 | } 108 | 109 | /** 110 | * Parse text headers from response into 111 | * array of key value pairs 112 | * @return array parse headers 113 | * @param string $headers raw headers 114 | */ 115 | public function _parseHeaders($headers) 116 | { 117 | $headers = preg_split("/(\r|\n)+/", $headers, -1, \PREG_SPLIT_NO_EMPTY); 118 | $parse_headers = array(); 119 | for ($i = 1; $i < count($headers); $i++) { 120 | list($key, $raw_value) = explode(':', $headers[$i], 2); 121 | $key = trim($key); 122 | $value = trim($raw_value); 123 | if (array_key_exists($key, $parse_headers)) { 124 | // See HTTP RFC Sec 4.2 Paragraph 5 125 | // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 126 | // If a header appears more than once, it must also be able to 127 | // be represented as a single header with a comma-separated 128 | // list of values. We transform accordingly. 129 | $parse_headers[$key] .= ',' . $value; 130 | } else { 131 | $parse_headers[$key] = $value; 132 | } 133 | } 134 | return $parse_headers; 135 | } 136 | 137 | public function _parseCode($headers) 138 | { 139 | $end = strpos($headers, "\r\n"); 140 | if ($end === false) $end = strlen($headers); 141 | $parts = explode(' ', substr($headers, 0, $end)); 142 | if (count($parts) < 2 || !is_numeric($parts[1])) { 143 | throw new \Exception("Unable to parse response code from HTTP response due to malformed response"); 144 | } 145 | return intval($parts[1]); 146 | } 147 | 148 | /** 149 | * After we've parse the headers, let's clean things 150 | * up a bit and treat some headers specially 151 | */ 152 | public function _interpretHeaders() 153 | { 154 | // Parse the Content-Type and charset 155 | $content_type = isset($this->headers['Content-Type']) ? $this->headers['Content-Type'] : ''; 156 | $content_type = explode(';', $content_type); 157 | 158 | $this->content_type = $content_type[0]; 159 | if (count($content_type) == 2 && strpos($content_type[1], '=') !== false) { 160 | list($nill, $this->charset) = explode('=', $content_type[1]); 161 | } 162 | 163 | // RFC 2616 states "text/*" Content-Types should have a default 164 | // charset of ISO-8859-1. "application/*" and other Content-Types 165 | // are assumed to have UTF-8 unless otherwise specified. 166 | // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1 167 | // http://www.w3.org/International/O-HTTP-charset.en.php 168 | if (!isset($this->charset)) { 169 | $this->charset = substr($this->content_type, 5) === 'text/' ? 'iso-8859-1' : 'utf-8'; 170 | } 171 | 172 | // Is vendor type? Is personal type? 173 | if (strpos($this->content_type, '/') !== false) { 174 | list($type, $sub_type) = explode('/', $this->content_type); 175 | $this->is_mime_vendor_specific = substr($sub_type, 0, 4) === 'vnd.'; 176 | $this->is_mime_personal = substr($sub_type, 0, 4) === 'prs.'; 177 | } 178 | 179 | // Parent type (e.g. xml for application/vnd.github.message+xml) 180 | $this->parent_type = $this->content_type; 181 | if (strpos($this->content_type, '+') !== false) { 182 | list($vendor, $this->parent_type) = explode('+', $this->content_type, 2); 183 | $this->parent_type = Mime::getFullMime($this->parent_type); 184 | } 185 | } 186 | 187 | /** 188 | * @return string 189 | */ 190 | public function __toString() 191 | { 192 | return $this->raw_body; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/src/Httpful/Response/Headers.php: -------------------------------------------------------------------------------- 1 | headers = $headers; 12 | } 13 | 14 | public static function fromString($string) 15 | { 16 | $lines = preg_split("/(\r|\n)+/", $string, -1, PREG_SPLIT_NO_EMPTY); 17 | array_shift($lines); // HTTP HEADER 18 | $headers = array(); 19 | foreach ($lines as $line) { 20 | list($name, $value) = explode(':', $line, 2); 21 | $headers[strtolower(trim($name))] = trim($value); 22 | } 23 | return new self($headers); 24 | } 25 | 26 | public function offsetExists($offset) 27 | { 28 | return isset($this->headers[strtolower($offset)]); 29 | } 30 | 31 | public function offsetGet($offset) 32 | { 33 | if (isset($this->headers[$name = strtolower($offset)])) { 34 | return $this->headers[$name]; 35 | } 36 | } 37 | 38 | public function offsetSet($offset, $value) 39 | { 40 | throw new \Exception("Headers are read-only."); 41 | } 42 | 43 | public function offsetUnset($offset) 44 | { 45 | throw new \Exception("Headers are read-only."); 46 | } 47 | 48 | public function count() 49 | { 50 | return count($this->headers); 51 | } 52 | 53 | public function toArray() 54 | { 55 | return $this->headers; 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/tests/Httpful/requestTest.php: -------------------------------------------------------------------------------- 1 | 4 | */ 5 | namespace Httpful\Test; 6 | 7 | class requestTest extends \PHPUnit_Framework_TestCase 8 | { 9 | 10 | /** 11 | * @author Nick Fox 12 | * @expectedException Httpful\Exception\ConnectionErrorException 13 | * @expectedExceptionMessage Unable to connect 14 | */ 15 | public function testGet_InvalidURL() 16 | { 17 | // Silence the default logger via whenError override 18 | \Httpful\Request::get('unavailable.url')->whenError(function($error) {})->send(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/tests/bootstrap-server.php: -------------------------------------------------------------------------------- 1 | ./server.log 2>&1 & echo $!', WEB_SERVER_HOST, WEB_SERVER_PORT, WEB_SERVER_DOCROOT); 16 | 17 | // Execute the command and store the process ID 18 | $output = array(); 19 | exec($command, $output, $exit_code); 20 | 21 | // sleep for a second to let server come up 22 | sleep(1); 23 | $pid = (int) $output[0]; 24 | 25 | // check server.log to see if it failed to start 26 | $server_logs = file_get_contents("./server.log"); 27 | if (strpos($server_logs, "Fail") !== false) { 28 | // server failed to start for some reason 29 | print "Failed to start server! Logs:" . PHP_EOL . PHP_EOL; 30 | print_r($server_logs); 31 | exit(1); 32 | } 33 | 34 | echo sprintf('%s - Web server started on %s:%d with PID %d', date('r'), WEB_SERVER_HOST, WEB_SERVER_PORT, $pid) . PHP_EOL; 35 | 36 | register_shutdown_function(function() { 37 | // cleanup after ourselves -- remove log file, shut down server 38 | global $pid; 39 | unlink("./server.log"); 40 | posix_kill($pid, SIGKILL); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/tests/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | . 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/tests/static/test.json: -------------------------------------------------------------------------------- 1 | {"foo": "bar", "baz": false} 2 | -------------------------------------------------------------------------------- /corp/vendor/nategood/httpful/tests/test_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-php/18c4edc1898fb0fea2817d25dd00eb83edca22fc/corp/vendor/nategood/httpful/tests/test_image.jpg -------------------------------------------------------------------------------- /isv/api/Activate.php: -------------------------------------------------------------------------------- 1 | errcode != 0) 56 | { 57 | exit("Failed: " . json_encode($res)); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /isv/api/Auth.php: -------------------------------------------------------------------------------- 1 | 'jsapi', 'access_token' => $accessToken)); 19 | self::check($response); 20 | $jsticket = $response->ticket; 21 | Cache::setJsTicket('js_ticket_'.$corpId,$jsticket); 22 | } 23 | 24 | return $jsticket; 25 | } 26 | 27 | 28 | function curPageURL() 29 | { 30 | $pageURL = 'http'; 31 | 32 | if (array_key_exists('HTTPS',$_SERVER)&&$_SERVER["HTTPS"] == "on") 33 | { 34 | $pageURL .= "s"; 35 | } 36 | $pageURL .= "://"; 37 | 38 | if ($_SERVER["SERVER_PORT"] != "80") 39 | { 40 | $pageURL .= $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"]; 41 | } 42 | else 43 | { 44 | $pageURL .= $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"]; 45 | } 46 | return $pageURL; 47 | } 48 | 49 | public static function isvConfig($corpId) 50 | { 51 | $corpInfo = ISVClass::getCorpInfo($corpId); 52 | $corpId = $corpInfo['corp_id']; 53 | $agentId = ISVService::getCurAgentId($corpId,APPID); 54 | $nonceStr = 'abcdefg'; 55 | $timeStamp = time(); 56 | $url = self::curPageURL(); 57 | $ticket = self::getTicket($corpId,$corpInfo['corpAccessToken']); 58 | $signature = self::sign($ticket, $nonceStr, $timeStamp, $url); 59 | $arr = array(); 60 | $arr['ticket'] = $ticket; 61 | $arr['nonceStr'] = $nonceStr; 62 | $arr['timeStamp'] = $timeStamp; 63 | $arr['url'] = $url; 64 | $arr['signature'] = $signature; 65 | 66 | $config = array( 67 | 'url' => $url, 68 | 'nonceStr' => $nonceStr, 69 | 'agentId' => $agentId, 70 | 'timeStamp' => $timeStamp, 71 | 'corpId' => $corpId, 72 | 'suite_key' => SUITE_KEY, 73 | 'signature' => $signature); 74 | return json_encode($config, JSON_UNESCAPED_SLASHES); 75 | } 76 | 77 | public static function sign($ticket, $nonceStr, $timeStamp, $url) 78 | { 79 | $plain = 'jsapi_ticket=' . $ticket . 80 | '&noncestr=' . $nonceStr . 81 | '×tamp=' . $timeStamp . 82 | '&url=' . $url; 83 | return sha1($plain); 84 | } 85 | 86 | /** 87 | * @param $accessToken 88 | * @param $code 89 | * @return 个人授权信息 90 | */ 91 | public static function getPerson($accessToken, $code) 92 | { 93 | $response = Http::get("/user/get_private_info", 94 | array("access_token" => $accessToken, "tmp_auth_code" => $code)); 95 | return json_encode($response); 96 | } 97 | 98 | 99 | static function check($res) 100 | { 101 | if ($res->errcode != 0) 102 | { 103 | Log::e("FAIL: " . json_encode($res)); 104 | exit("Failed: " . json_encode($res)); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /isv/api/Chat.php: -------------------------------------------------------------------------------- 1 | $accessToken), 12 | json_encode($chatOpt)); 13 | return $response; 14 | } 15 | 16 | public static function bindChat($accessToken, $chatid,$agentid) 17 | { 18 | $response = Http::get("/chat/bind", 19 | array("access_token" => $accessToken,"chatid"=>$chatid,"agentid"=>$agentid)); 20 | return $response; 21 | } 22 | 23 | public static function sendmsg($accessToken, $opt) 24 | { 25 | $response = Http::post("/chat/send", 26 | array("access_token" => $accessToken), 27 | json_encode($opt)); 28 | return $response; 29 | } 30 | 31 | public static function callback($accessToken, $opt) 32 | { 33 | $response = Http::post("/call_back/register_call_back", 34 | array("access_token" => $accessToken), 35 | json_encode($opt)); 36 | return $response; 37 | } 38 | } -------------------------------------------------------------------------------- /isv/api/Department.php: -------------------------------------------------------------------------------- 1 | $accessToken), 9 | json_encode($dept)); 10 | return $response->id; 11 | } 12 | 13 | 14 | public static function listDept($accessToken) 15 | { 16 | $response = Http::get("/department/list", 17 | array("access_token" => $accessToken)); 18 | return $response->department; 19 | } 20 | 21 | 22 | public static function deleteDept($accessToken, $id) 23 | { 24 | $response = Http::get("/department/delete", 25 | array("access_token" => $accessToken, "id" => $id)); 26 | return $response->errcode == 0; 27 | } 28 | } -------------------------------------------------------------------------------- /isv/api/ISVClass.php: -------------------------------------------------------------------------------- 1 | errcode != 0) 45 | { 46 | exit("Failed: " . json_encode($res)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /isv/api/ISVService.php: -------------------------------------------------------------------------------- 1 | SUITE_KEY, 21 | "suite_secret" => SUITE_SECRET, 22 | "suite_ticket" => $suiteTicket 23 | ))); 24 | self::check($response); 25 | $suiteAccessToken = $response->suite_access_token; 26 | Cache::setSuiteAccessToken($suiteAccessToken); 27 | } 28 | return $suiteAccessToken; 29 | } 30 | 31 | public static function getCorpInfoByCorId($corpId){ 32 | $corpList = json_decode(Cache::getCorpInfo(),true); 33 | if(!is_array($corpList)){ 34 | return false; 35 | } 36 | 37 | foreach($corpList as $corp){ 38 | if($corp['corp_id']==$corpId){ 39 | return $corp; 40 | } 41 | } 42 | } 43 | 44 | public static function getCorpInfoByTmpCode($code){ 45 | $corpList = json_decode(Cache::getCorpInfo(),true); 46 | if(!is_array($corpList)){ 47 | return false; 48 | } 49 | 50 | foreach($corpList as $corp){ 51 | if($corp['tmp_auth_code']==$code){ 52 | return $corp; 53 | } 54 | } 55 | } 56 | 57 | public static function getPermanentCodeInfo($suiteAccessToken,$tmpAuthCode) 58 | { 59 | $permanentCodeInfo = json_decode(ISVService::getCorpInfoByTmpCode($tmpAuthCode)); 60 | 61 | if (!$permanentCodeInfo) 62 | { 63 | $permanentCodeResult = Http::post("/service/get_permanent_code", 64 | array( 65 | "suite_access_token" => $suiteAccessToken 66 | ), 67 | json_encode(array( 68 | "tmp_auth_code" => $tmpAuthCode 69 | ))); 70 | self::check($permanentCodeResult); 71 | $permanentCodeInfo = self::savePermanentCodeInfo($permanentCodeResult,$tmpAuthCode); 72 | } 73 | return $permanentCodeInfo; 74 | } 75 | 76 | public static function savePermanentCodeInfo($permanentCodeInfo,$tmpAuthCode){ 77 | $arr = array(); 78 | $arr['corp_name'] = $permanentCodeInfo->auth_corp_info->corp_name; 79 | $arr['corp_id'] = $permanentCodeInfo->auth_corp_info->corpid; 80 | $arr['permanent_code'] = $permanentCodeInfo->permanent_code; 81 | $arr['tmp_auth_code'] = $tmpAuthCode; 82 | $corpInfo = json_decode(Cache::getCorpInfo()); 83 | if(!$corpInfo){ 84 | $corpInfo = array(); 85 | } 86 | 87 | $corpExist = false; 88 | foreach($corpInfo as $cp){ 89 | if($cp->corp_id == $arr['corp_id']){ 90 | $corpExist = true; 91 | } 92 | } 93 | 94 | if(!$corpExist){ 95 | $corpInfo[] = $arr; 96 | Cache::setCorpInfo(json_encode($corpInfo)); 97 | } 98 | return $arr; 99 | } 100 | 101 | public static function getCurAgentId($corpId,$appId){ 102 | $authInfo = json_decode(Cache::getAuthInfo("corpAuthInfo_".$corpId)); 103 | $agents = $authInfo->agent; 104 | $agentId = 0; 105 | foreach($agents as $agent){ 106 | if($agent->appid==$appId){ 107 | $agentId = $agent->agentid; 108 | break; 109 | } 110 | } 111 | Log::i("[AGENTID]".$corpId."-----".$appId."-----".$agentId); 112 | return $agentId; 113 | } 114 | 115 | public static function getIsvCorpAccessToken($suiteAccessToken, $authCorpId, $permanentCode) 116 | { 117 | $key = "IsvCorpAccessToken_".$authCorpId; 118 | $corpAccessToken = Cache::getIsvCorpAccessToken($key); 119 | if (!$corpAccessToken) 120 | { 121 | $response = Http::post("/service/get_corp_token", 122 | array( 123 | "suite_access_token" => $suiteAccessToken 124 | ), 125 | json_encode(array( 126 | "auth_corpid" => $authCorpId, 127 | "permanent_code" => $permanentCode 128 | ))); 129 | self::check($response); 130 | $corpAccessToken = $response->access_token; 131 | Cache::setIsvCorpAccessToken($key,$corpAccessToken); 132 | } 133 | return $corpAccessToken; 134 | } 135 | 136 | public static function getAuthInfo($suiteAccessToken, $authCorpId, $permanentCode) 137 | { 138 | $authInfo = json_decode(Cache::getAuthInfo("corpAuthInfo_".$authCorpId)); 139 | if (!$authInfo) 140 | { 141 | $authInfo = Http::post("/service/get_auth_info", 142 | array( 143 | "suite_access_token" => $suiteAccessToken 144 | ), 145 | json_encode(array( 146 | "suite_key" => SUITE_KEY, 147 | "auth_corpid" => $authCorpId, 148 | "permanent_code" => $permanentCode 149 | ))); 150 | self::check($authInfo); 151 | Cache::setAuthInfo("corpAuthInfo_".$authCorpId,json_encode($authInfo->auth_info)); 152 | } 153 | 154 | return $authInfo; 155 | } 156 | 157 | 158 | public static function getAgent($suiteAccessToken, $authCorpId, $permanentCode, $agentId) 159 | { 160 | $response = Http::post("/service/get_agent", 161 | array( 162 | "suite_access_token" => $suiteAccessToken 163 | ), 164 | json_encode(array( 165 | "suite_key" => SUITE_KEY, 166 | "auth_corpid" => $authCorpId, 167 | "permanent_code" => $permanentCode, 168 | "agentid" => $agentId 169 | ))); 170 | self::check($response); 171 | return $response; 172 | } 173 | 174 | 175 | public static function activeSuite($suiteAccessToken, $authCorpId, $permanentCode) 176 | { 177 | $key = "dingdingActive_".$authCorpId; 178 | $response = Http::post("/service/activate_suite", 179 | array( 180 | "suite_access_token" => $suiteAccessToken 181 | ), 182 | json_encode(array( 183 | "suite_key" => SUITE_KEY, 184 | "auth_corpid" => $authCorpId, 185 | "permanent_code" => $permanentCode 186 | ))); 187 | 188 | if($response->errcode==0){ 189 | Cache::setActiveStatus($key); 190 | } 191 | self::check($response); 192 | return $response; 193 | } 194 | 195 | public static function removeCorpInfo($authCorpId){ 196 | $arr = array(); 197 | $key1 = "dingdingActive_".$authCorpId; 198 | $key2 = "corpAuthInfo_".$authCorpId; 199 | $key3 = "IsvCorpAccessToken_".$authCorpId; 200 | $key4 = "js_ticket_".$authCorpId; 201 | $arr[] = $key1; 202 | $arr[] = $key2; 203 | $arr[] = $key3; 204 | $arr[] = $key4; 205 | Cache::removeByKeyArr($arr); 206 | } 207 | 208 | static function check($res) 209 | { 210 | if ($res->errcode != 0) 211 | { 212 | Log::e("[FAIL]: " . json_encode($res)); 213 | exit("Failed: " . json_encode($res)); 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /isv/api/Message.php: -------------------------------------------------------------------------------- 1 | $accessToken), 8 | json_encode($opt)); 9 | return $response; 10 | } 11 | 12 | public static function send($accessToken, $opt) 13 | { 14 | $response = Http::post("/message/send", 15 | array("access_token" => $accessToken),json_encode($opt)); 16 | return $response; 17 | } 18 | } -------------------------------------------------------------------------------- /isv/api/User.php: -------------------------------------------------------------------------------- 1 | $accessToken, "code" => $code)); 8 | return json_encode($response); 9 | } 10 | 11 | 12 | public static function simplelist($accessToken,$deptId){ 13 | $response = Http::get("/user/simplelist", 14 | array("access_token" => $accessToken,"department_id"=>$deptId)); 15 | return $response->userlist; 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /isv/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "nategood/httpful": "*" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /isv/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "e23d30d76ba6b3d3bcdab145c9f7f9f3", 8 | "packages": [ 9 | { 10 | "name": "nategood/httpful", 11 | "version": "0.2.19", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/nategood/httpful.git", 15 | "reference": "bd73f89d34d8f879c54ac46eb94b0f7be1d00820" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/nategood/httpful/zipball/bd73f89d34d8f879c54ac46eb94b0f7be1d00820", 20 | "reference": "bd73f89d34d8f879c54ac46eb94b0f7be1d00820", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-curl": "*", 25 | "php": ">=5.3" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "*" 29 | }, 30 | "type": "library", 31 | "autoload": { 32 | "psr-0": { 33 | "Httpful": "src/" 34 | } 35 | }, 36 | "notification-url": "https://packagist.org/downloads/", 37 | "license": [ 38 | "MIT" 39 | ], 40 | "authors": [ 41 | { 42 | "name": "Nate Good", 43 | "email": "me@nategood.com", 44 | "homepage": "http://nategood.com" 45 | } 46 | ], 47 | "description": "A Readable, Chainable, REST friendly, PHP HTTP Client", 48 | "homepage": "http://github.com/nategood/httpful", 49 | "keywords": [ 50 | "api", 51 | "curl", 52 | "http", 53 | "requests", 54 | "rest", 55 | "restful" 56 | ], 57 | "time": "2015-03-08 15:22:23" 58 | } 59 | ], 60 | "packages-dev": [], 61 | "aliases": [], 62 | "minimum-stability": "stable", 63 | "stability-flags": [], 64 | "prefer-stable": false, 65 | "prefer-lowest": false, 66 | "platform": [], 67 | "platform-dev": [] 68 | } 69 | -------------------------------------------------------------------------------- /isv/config.php: -------------------------------------------------------------------------------- 1 | m_token = $token; 17 | $this->m_encodingAesKey = $encodingAesKey; 18 | $this->m_suiteKey = $suiteKey; 19 | } 20 | 21 | 22 | public function EncryptMsg($plain, $timeStamp, $nonce, &$encryptMsg) 23 | { 24 | $pc = new Prpcrypt($this->m_encodingAesKey); 25 | 26 | $array = $pc->encrypt($plain, $this->m_suiteKey); 27 | $ret = $array[0]; 28 | if ($ret != 0) { 29 | return $ret; 30 | } 31 | 32 | if ($timeStamp == null) { 33 | $timeStamp = time(); 34 | } 35 | $encrypt = $array[1]; 36 | 37 | $sha1 = new SHA1; 38 | $array = $sha1->getSHA1($this->m_token, $timeStamp, $nonce, $encrypt); 39 | $ret = $array[0]; 40 | if ($ret != 0) { 41 | return $ret; 42 | } 43 | $signature = $array[1]; 44 | 45 | $encryptMsg = json_encode(array( 46 | "msg_signature" => $signature, 47 | "encrypt" => $encrypt, 48 | "timeStamp" => $timeStamp, 49 | "nonce" => $nonce 50 | )); 51 | return ErrorCode::$OK; 52 | } 53 | 54 | 55 | public function DecryptMsg($signature, $timeStamp = null, $nonce, $encrypt, &$decryptMsg) 56 | { 57 | if (strlen($this->m_encodingAesKey) != 43) { 58 | return ErrorCode::$IllegalAesKey; 59 | } 60 | 61 | $pc = new Prpcrypt($this->m_encodingAesKey); 62 | 63 | if ($timeStamp == null) { 64 | $timeStamp = time(); 65 | } 66 | 67 | $sha1 = new SHA1; 68 | $array = $sha1->getSHA1($this->m_token, $timeStamp, $nonce, $encrypt); 69 | $ret = $array[0]; 70 | 71 | if ($ret != 0) { 72 | return $ret; 73 | } 74 | 75 | $verifySignature = $array[1]; 76 | if ($verifySignature != $signature) { 77 | return ErrorCode::$ValidateSignatureError; 78 | } 79 | 80 | $result = $pc->decrypt($encrypt, $this->m_suiteKey); 81 | if ($result[0] != 0) { 82 | return $result[0]; 83 | } 84 | $decryptMsg = $result[1]; 85 | 86 | return ErrorCode::$OK; 87 | } 88 | 89 | } 90 | 91 | -------------------------------------------------------------------------------- /isv/crypto/errorCode.php: -------------------------------------------------------------------------------- 1 | 6 | *
  • -900004: encodingAesKey 非法
  • 7 | *
  • -900005: 签名验证错误
  • 8 | *
  • -900006: sha加密生成签名失败
  • 9 | *
  • -900007: aes 加密失败
  • 10 | *
  • -900008: aes 解密失败
  • 11 | *
  • -900010: suiteKey 校验错误
  • 12 | * 13 | */ 14 | class ErrorCode 15 | { 16 | public static $OK = 0; 17 | 18 | public static $IllegalAesKey = 900004; 19 | public static $ValidateSignatureError = 900005; 20 | public static $ComputeSignatureError = 900006; 21 | public static $EncryptAESError = 900007; 22 | public static $DecryptAESError = 900008; 23 | public static $ValidateSuiteKeyError = 900010; 24 | } 25 | 26 | ?> -------------------------------------------------------------------------------- /isv/crypto/pkcs7Encoder.php: -------------------------------------------------------------------------------- 1 | PKCS7Encoder::$block_size) { 30 | $pad = 0; 31 | } 32 | return substr($text, 0, (strlen($text) - $pad)); 33 | } 34 | 35 | } 36 | 37 | 38 | class Prpcrypt 39 | { 40 | public $key; 41 | 42 | function Prpcrypt($k) 43 | { 44 | $this->key = base64_decode($k . "="); 45 | } 46 | 47 | public function encrypt($text, $corpid) 48 | { 49 | 50 | try { 51 | //获得16位随机字符串,填充到明文之前 52 | $random = $this->getRandomStr(); 53 | $text = $random . pack("N", strlen($text)) . $text . $corpid; 54 | // 网络字节序 55 | $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); 56 | $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); 57 | $iv = substr($this->key, 0, 16); 58 | //使用自定义的填充方式对明文进行补位填充 59 | $pkc_encoder = new PKCS7Encoder; 60 | $text = $pkc_encoder->encode($text); 61 | mcrypt_generic_init($module, $this->key, $iv); 62 | //加密 63 | $encrypted = mcrypt_generic($module, $text); 64 | mcrypt_generic_deinit($module); 65 | mcrypt_module_close($module); 66 | 67 | //print(base64_encode($encrypted)); 68 | //使用BASE64对加密后的字符串进行编码 69 | return array(ErrorCode::$OK, base64_encode($encrypted)); 70 | } catch (Exception $e) { 71 | print $e; 72 | return array(ErrorCode::$EncryptAESError, null); 73 | } 74 | } 75 | 76 | public function decrypt($encrypted, $corpid) 77 | { 78 | 79 | try { 80 | $ciphertext_dec = base64_decode($encrypted); 81 | $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); 82 | $iv = substr($this->key, 0, 16); 83 | mcrypt_generic_init($module, $this->key, $iv); 84 | 85 | $decrypted = mdecrypt_generic($module, $ciphertext_dec); 86 | mcrypt_generic_deinit($module); 87 | mcrypt_module_close($module); 88 | } catch (Exception $e) { 89 | return array(ErrorCode::$DecryptAESError, null); 90 | } 91 | 92 | 93 | try { 94 | //去除补位字符 95 | $pkc_encoder = new PKCS7Encoder; 96 | $result = $pkc_encoder->decode($decrypted); 97 | //去除16位随机字符串,网络字节序和AppId 98 | if (strlen($result) < 16) 99 | return ""; 100 | $content = substr($result, 16, strlen($result)); 101 | $len_list = unpack("N", substr($content, 0, 4)); 102 | $xml_len = $len_list[1]; 103 | $xml_content = substr($content, 4, $xml_len); 104 | $from_corpid = substr($content, $xml_len + 4); 105 | } catch (Exception $e) { 106 | print $e; 107 | return array(ErrorCode::$DecryptAESError, null); 108 | } 109 | if ($from_corpid != $corpid) 110 | return array(ErrorCode::$ValidateSuiteKeyError, null); 111 | return array(0, $xml_content); 112 | 113 | } 114 | 115 | function getRandomStr() 116 | { 117 | 118 | $str = ""; 119 | $str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; 120 | $max = strlen($str_pol) - 1; 121 | for ($i = 0; $i < 16; $i++) { 122 | $str .= $str_pol[mt_rand(0, $max)]; 123 | } 124 | return $str; 125 | } 126 | 127 | } 128 | 129 | ?> -------------------------------------------------------------------------------- /isv/crypto/sha1.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /isv/getPerson.php: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | jsapi demo 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /isv/indexpc.php: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | jsapi demo 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /isv/public/javascripts/demo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 8/10/15. 3 | */ 4 | 5 | logger.i('Here we go...'); 6 | 7 | logger.i(location.href); 8 | 9 | /** 10 | * _config comes from server-side template. see views/index.jade 11 | */ 12 | dd.config({ 13 | agentId: _config.agentId, 14 | corpId: _config.corpId, 15 | timeStamp: _config.timeStamp, 16 | nonceStr: _config.nonceStr, 17 | signature: _config.signature, 18 | jsApiList: [ 19 | 'runtime.info', 20 | 'device.notification.prompt', 21 | 'biz.chat.pickConversation', 22 | 'device.notification.confirm', 23 | 'device.notification.alert', 24 | 'device.notification.prompt', 25 | 'biz.chat.open', 26 | 'biz.util.open', 27 | 'biz.user.get', 28 | 'biz.contact.choose', 29 | 'biz.telephone.call', 30 | 'biz.util.uploadImage', 31 | 'biz.ding.post'] 32 | }); 33 | dd.userid=0; 34 | dd.ready(function() { 35 | logger.i('dd.ready rocks!'); 36 | 37 | dd.runtime.info({ 38 | onSuccess: function(info) { 39 | logger.i('runtime info: ' + JSON.stringify(info)); 40 | }, 41 | onFail: function(err) { 42 | logger.e('fail: ' + JSON.stringify(err)); 43 | } 44 | }); 45 | 46 | dd.runtime.permission.requestAuthCode({ 47 | corpId: _config.corpId, //企业id 48 | onSuccess: function (info) { 49 | logger.i('authcode: ' + info.code); 50 | $.ajax({ 51 | url: '/sendMsg.php', 52 | type:"POST", 53 | data: {"event":"get_userinfo","code":info.code,"corpId":_config.corpId}, 54 | dataType:'json', 55 | timeout: 900, 56 | success: function (data, status, xhr) { 57 | var info = JSON.parse(data); 58 | if (info.errcode === 0) { 59 | logger.i('user id: ' + info.userid); 60 | dd.userid = info.userid; 61 | } 62 | else { 63 | logger.e('auth error: ' + data); 64 | } 65 | }, 66 | error: function (xhr, errorType, error) { 67 | logger.e(errorType + ', ' + error); 68 | } 69 | }); 70 | }, 71 | onFail: function (err) { 72 | logger.e('requestAuthCode fail: ' + JSON.stringify(err)); 73 | } 74 | }); 75 | 76 | $('.chooseonebtn').on('click', function() { 77 | 78 | dd.biz.chat.pickConversation({ 79 | corpId: _config.corpId, //企业id 80 | isConfirm:'false', //是否弹出确认窗口,默认为true 81 | onSuccess: function (data) { 82 | var chatinfo = data; 83 | if(chatinfo){ 84 | console.log(chatinfo.cid); 85 | dd.device.notification.prompt({ 86 | message: "发送消息", 87 | title: chatinfo.title, 88 | buttonLabels: ['发送', '取消'], 89 | onSuccess : function(result) { 90 | var text = result.value; 91 | if(text==''){ 92 | return false; 93 | } 94 | 95 | $.ajax({ 96 | url: '/sendMsg.php', 97 | type:"POST", 98 | data: {"event":"send_to_conversation","cid":chatinfo.cid,"sender":dd.userid,"content":text,"corpId":_config.corpId}, 99 | dataType:'json', 100 | timeout: 900, 101 | success: function (data, status, xhr) { 102 | var info = data; 103 | logger.i('sendMsg: ' + JSON.stringify(data)); 104 | if(info.errcode==0){ 105 | logger.i('sendMsg: 发送成功'); 106 | /** 107 | * 跳转到对话界面 108 | */ 109 | dd.biz.chat.open({ 110 | cid:chatinfo.cid, 111 | onSuccess : function(result) { 112 | }, 113 | onFail : function(err) {} 114 | }); 115 | }else{ 116 | logger.e('sendMsg: 发送失败'+info.errmsg); 117 | } 118 | }, 119 | error: function (xhr, errorType, error) { 120 | logger.e(errorType + ', ' + error); 121 | } 122 | }); 123 | }, 124 | onFail : function(err) {} 125 | }); 126 | } 127 | }, 128 | onFail: function (err) { 129 | } 130 | }); 131 | }); 132 | 133 | $('.phonecall').on('click', function() { 134 | dd.biz.contact.choose({ 135 | startWithDepartmentId: 0, //-1表示打开的通讯录从自己所在部门开始展示, 0表示从企业最上层开始,(其他数字表示从该部门开始:暂时不支持) 136 | multiple: false, //是否多选: true多选 false单选; 默认true 137 | users: [], //默认选中的用户列表,userid;成功回调中应包含该信息 138 | corpId: _config.corpId, //企业id 139 | max: 10, //人数限制,当multiple为true才生效,可选范围1-1500 140 | onSuccess: function(data) { 141 | if(data&&data.length>0){ 142 | var selectUserId = data[0].emplId; 143 | if(selectUserId>0){ 144 | dd.biz.telephone.call({ 145 | users: [selectUserId], //用户列表,工号 146 | corpId: _config.corpId, //企业id 147 | onSuccess : function(info) { 148 | logger.i('biz.telephone.call: info' + JSON.stringify(info)); 149 | 150 | }, 151 | onFail : function(err) { 152 | logger.e('biz.telephone.call: error' + JSON.stringify(err)); 153 | } 154 | }) 155 | }else{ 156 | return false; 157 | } 158 | }else{ 159 | return false; 160 | } 161 | }, 162 | onFail : function(err) {} 163 | }); 164 | }); 165 | }); 166 | 167 | dd.error(function(err) { 168 | logger.e('dd error: ' + JSON.stringify(err)); 169 | }); 170 | -------------------------------------------------------------------------------- /isv/public/javascripts/demopc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 8/10/15. 3 | */ 4 | 5 | logger.i('Here we go...'); 6 | 7 | logger.i(location.href); 8 | 9 | /** 10 | * _config comes from server-side template. see views/index.jade 11 | */ 12 | DingTalkPC.config({ 13 | agentId: _config.agentId, 14 | corpId: _config.corpId, 15 | timeStamp: _config.timeStamp, 16 | nonceStr: _config.nonceStr, 17 | signature: _config.signature, 18 | jsApiList: [ 19 | 'runtime.permission.requestAuthCode', 20 | 'device.notification.alert', 21 | 'device.notification.confirm', 22 | 'biz.contact.choose', 23 | 'device.notification.prompt', 24 | 'biz.ding.post' 25 | ] // 必填,需要使用的jsapi列表 26 | }); 27 | DingTalkPC.userid=0; 28 | DingTalkPC.ready(function(res){ 29 | logger.i('dd.ready rocks!'); 30 | 31 | DingTalkPC.runtime.permission.requestAuthCode({ 32 | corpId: _config.corpId, //企业ID 33 | onSuccess: function(info) { 34 | logger.i('authcode: ' + info.code); 35 | $.ajax({ 36 | url: '/sendMsg.php', 37 | type:"POST", 38 | data: {"event":"get_userinfo","code":info.code,"corpId":_config.corpId}, 39 | dataType:'json', 40 | timeout: 900, 41 | success: function (data, status, xhr) { 42 | var info = JSON.parse(data); 43 | if (info.errcode === 0) { 44 | logger.i('user id: ' + info.userid); 45 | DingTalkPC.userid = info.userid; 46 | } 47 | else { 48 | logger.e('auth error: ' + data); 49 | } 50 | }, 51 | error: function (xhr, errorType, error) { 52 | logger.e(errorType + ', ' + error); 53 | } 54 | }); 55 | }, 56 | onFail : function(err) { 57 | logger.e(JSON.stringify(err)); 58 | } 59 | 60 | }); 61 | $('.chooseonebtn').on('click', function() { 62 | 63 | DingTalkPC.biz.contact.choose({ 64 | multiple: false, //是否多选: true多选 false单选; 默认true 65 | users: [], //默认选中的用户列表,工号;成功回调中应包含该信息 66 | corpId: _config.corpId, //企业id 67 | max: 1, //人数限制,当multiple为true才生效,可选范围1-1500 68 | onSuccess: function(data) { 69 | if(data&&data.length>0){ 70 | var selectUserId = data[0].emplId; 71 | if(selectUserId>0){ 72 | DingTalkPC.device.notification.prompt({ 73 | message: "发送消息", 74 | title: data[0].name, 75 | buttonLabels: ['发送', '取消'], 76 | onSuccess : function(result) { 77 | var textContent = result.value; 78 | alert(textContent+">>>1"); 79 | if(textContent==''){ 80 | return false; 81 | } 82 | DingTalkPC.biz.ding.post({ 83 | users : [selectUserId],//用户列表,工号 84 | corpId: _config.corpId, //加密的企业id 85 | type: 1, //钉类型 1:image 2:link 86 | alertType: 2, 87 | alertDate: {"format":"yyyy-MM-dd HH:mm","value":"2016-05-09 08:00"}, 88 | attachment: { 89 | images: [] //只取第一个image 90 | }, //附件信息 91 | text: textContent, //消息体 92 | onSuccess : function(info) { 93 | alert(">>>2"); 94 | logger.i('DingTalkPC.biz.ding.post: info' + JSON.stringify(info)); 95 | }, 96 | onFail : function(err) { 97 | logger.e('DingTalkPC.biz.ding.post: info' + JSON.stringify(err)); 98 | } 99 | }) 100 | /* 101 | { 102 | buttonIndex: 0, //被点击按钮的索引值,Number类型,从0开始 103 | value: '' //输入的值 104 | } 105 | */ 106 | }, 107 | onFail : function(err) { 108 | logger.e('DingTalkPC.biz.ding.post: info' + JSON.stringify(err)); 109 | } 110 | }); 111 | } 112 | } 113 | }, 114 | onFail : function(err) {} 115 | }); 116 | }); 117 | /*DingTalkPC.biz.util.uploadImage({ 118 | multiple: false, //是否多选,默认false 119 | max: 5, //最多可选个数 120 | onSuccess : function(result) { 121 | logger.i(result); 122 | }, 123 | onFail : function() {} 124 | });*/ 125 | /*DingTalkPC.device.notification.alert({ 126 | message: "亲爱的", 127 | title: "提示",//可传空 128 | buttonName: "收到", 129 | onSuccess : function() { 130 | }, 131 | onFail : function(err) {} 132 | });*/ 133 | }); 134 | 135 | 136 | -------------------------------------------------------------------------------- /isv/public/javascripts/logger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 8/14/15. 3 | */ 4 | 5 | var log = document.createElement('div'); 6 | log.setAttribute('id', 'log'); 7 | document.body.appendChild(log); 8 | 9 | var logger = { 10 | i: function(info) { 11 | add(info, 'i'); 12 | }, 13 | e: function(err) { 14 | add(err, 'e'); 15 | } 16 | }; 17 | 18 | function add(msg, level) { 19 | var row = document.createElement('div'); 20 | row.setAttribute('class', 'log-row log-' + level); 21 | row.innerHTML = msg; 22 | 23 | document.querySelector('#log').appendChild(row); 24 | } -------------------------------------------------------------------------------- /isv/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | .log-i { 11 | color: green; 12 | } 13 | 14 | .log-e { 15 | color: red; 16 | } 17 | 18 | .tag { 19 | display: inline-block; 20 | width: 170px; 21 | } 22 | 23 | .api { 24 | display: inline-block; 25 | width: 300px; 26 | } 27 | 28 | .hidden { 29 | display: none; 30 | } 31 | 32 | .row button { 33 | width: 100%; 34 | height: 100px; 35 | } 36 | 37 | .log-row { 38 | margin: 20px 10px 20px 10px; 39 | font-size: 30px; 40 | word-break: break-all;word-wrap: break-word; 41 | } -------------------------------------------------------------------------------- /isv/receive.php: -------------------------------------------------------------------------------- 1 | DecryptMsg($signature, $timeStamp, $nonce, $encrypt, $msg); 19 | 20 | if ($errCode != 0) 21 | { 22 | Log::e(json_encode($_GET) . " ERR:" . $errCode); 23 | 24 | /** 25 | * 创建套件时检测回调地址有效性,使用CREATE_SUITE_KEY作为SuiteKey 26 | */ 27 | $crypt = new DingtalkCrypt(TOKEN, ENCODING_AES_KEY, CREATE_SUITE_KEY); 28 | $errCode = $crypt->DecryptMsg($signature, $timeStamp, $nonce, $encrypt, $msg); 29 | if ($errCode == 0) 30 | { 31 | Log::i("DECRYPT CREATE SUITE MSG SUCCESS " . json_encode($_GET) . " " . $msg); 32 | $eventMsg = json_decode($msg); 33 | $eventType = $eventMsg->EventType; 34 | if ("check_create_suite_url" === $eventType) 35 | { 36 | $random = $eventMsg->Random; 37 | $testSuiteKey = $eventMsg->TestSuiteKey; 38 | 39 | $encryptMsg = ""; 40 | $errCode = $crypt->EncryptMsg($random, $timeStamp, $nonce, $encryptMsg); 41 | if ($errCode == 0) 42 | { 43 | Log::i("CREATE SUITE URL RESPONSE: " . $encryptMsg); 44 | echo $encryptMsg; 45 | } 46 | else 47 | { 48 | Log::e("CREATE SUITE URL RESPONSE ERR: " . $errCode); 49 | } 50 | } 51 | else 52 | { 53 | //should never happened 54 | } 55 | } 56 | else 57 | { 58 | Log::e(json_encode($_GET) . "CREATE SUITE ERR:" . $errCode); 59 | } 60 | return; 61 | } 62 | else 63 | { 64 | /** 65 | * 套件创建成功后的回调推送 66 | */ 67 | Log::i("DECRYPT MSG SUCCESS " . json_encode($_GET) . " " . $msg); 68 | $eventMsg = json_decode($msg); 69 | $eventType = $eventMsg->EventType; 70 | /** 71 | * 套件ticket 72 | */ 73 | if ("suite_ticket" === $eventType) 74 | { 75 | Cache::setSuiteTicket($eventMsg->SuiteTicket); 76 | } 77 | /** 78 | * 临时授权码 79 | */ 80 | else if ("tmp_auth_code" === $eventType) 81 | { 82 | $tmpAuthCode = $eventMsg->AuthCode; 83 | Activate::autoActivateSuite($tmpAuthCode); 84 | } 85 | /** 86 | * 授权变更事件 87 | */ 88 | 89 | /*user_add_org : 通讯录用户增加 90 | user_modify_org : 通讯录用户更改 91 | user_leave_org : 通讯录用户离职 92 | org_admin_add :通讯录用户被设为管理员 93 | org_admin_remove :通讯录用户被取消设置管理员 94 | org_dept_create : 通讯录企业部门创建 95 | org_dept_modify : 通讯录企业部门修改 96 | org_dept_remove : 通讯录企业部门删除 97 | org_remove : 企业被解散 98 | */ 99 | 100 | else if ("user_add_org" === $eventType) 101 | { 102 | Log::e(json_encode($_GET) . " ERR:user_add_org"); 103 | //handle auth change event 104 | } 105 | 106 | else if ("user_modify_org" === $eventType) 107 | { 108 | Log::e(json_encode($_GET) . " ERR:user_modify_org"); 109 | //handle auth change event 110 | } 111 | 112 | else if ("user_leave_org" === $eventType) 113 | { 114 | Log::e(json_encode($_GET) . " ERR:user_leave_org"); 115 | //handle auth change event 116 | } 117 | /** 118 | * 应用被解除授权的时候,需要删除相应企业的存储信息 119 | */ 120 | else if ("suite_relieve" === $eventType) 121 | { 122 | $corpid = $eventMsg->AuthCorpId; 123 | ISVService::removeCorpInfo($corpid); 124 | //handle auth change event 125 | }else if ("change_auth" === $eventType) 126 | { 127 | //handle auth change event 128 | } 129 | 130 | /** 131 | * 回调地址更新 132 | */ 133 | else if ("check_update_suite_url" === $eventType) 134 | { 135 | $random = $eventMsg->Random; 136 | $testSuiteKey = $eventMsg->TestSuiteKey; 137 | 138 | $encryptMsg = ""; 139 | $errCode = $crypt->EncryptMsg($random, $timeStamp, $nonce, $encryptMsg); 140 | if ($errCode == 0) 141 | { 142 | Log::i("UPDATE SUITE URL RESPONSE: " . $encryptMsg); 143 | echo $encryptMsg; 144 | return; 145 | } 146 | else 147 | { 148 | Log::e("UPDATE SUITE URL RESPONSE ERR: " . $errCode); 149 | } 150 | } 151 | else 152 | { 153 | //should never happen 154 | } 155 | 156 | $res = "success"; 157 | $encryptMsg = ""; 158 | $errCode = $crypt->EncryptMsg($res, $timeStamp, $nonce, $encryptMsg); 159 | if ($errCode == 0) 160 | { 161 | echo $encryptMsg; 162 | Log::i("RESPONSE: " . $encryptMsg); 163 | } 164 | else 165 | { 166 | Log::e("RESPONSE ERR: " . $errCode); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /isv/sendMsg.php: -------------------------------------------------------------------------------- 1 | "4000")); 14 | break; 15 | case 'send_to_conversation': 16 | $sender = $_POST['sender']; 17 | $cid = $_POST['cid']; 18 | $content = $_POST['content']; 19 | $corpId = $_POST['corpId']; 20 | $corpInfo = ISVClass::getCorpInfo($corpId); 21 | $accessToken = $corpInfo['corpAccessToken']; 22 | $option = array( 23 | "sender"=>$sender, 24 | "cid"=>$cid, 25 | "msgtype"=>"text", 26 | "text"=>array("content"=>$content) 27 | ); 28 | $response = Message::sendToConversation($accessToken,$option); 29 | echo json_encode($response); 30 | break; 31 | 32 | case 'get_userinfo': 33 | $corpId = $_POST['corpId']; 34 | $corpInfo = ISVClass::getCorpInfo($corpId); 35 | $accessToken = $corpInfo['corpAccessToken']; 36 | $code = $_POST["code"]; 37 | $userInfo = User::getUserInfo($accessToken, $code); 38 | echo json_encode($userInfo); 39 | break; 40 | } -------------------------------------------------------------------------------- /isv/util/Cache.php: -------------------------------------------------------------------------------- 1 | set("suite_ticket", $ticket); 9 | } 10 | 11 | public static function getSuiteTicket() 12 | { 13 | $memcache = self::getMemcache(); 14 | return $memcache->get("suite_ticket"); 15 | } 16 | 17 | public static function setJsTicket($key,$ticket) 18 | { 19 | $memcache = self::getMemcache(); 20 | $memcache->set($key, $ticket, 0, time() + 7000); // js ticket有效期为7200秒,这里设置为7000秒 21 | } 22 | 23 | public static function getJsTicket($key) 24 | { 25 | $memcache = self::getMemcache(); 26 | return $memcache->get($key); 27 | } 28 | 29 | public static function setSuiteAccessToken($accessToken) 30 | { 31 | $memcache = self::getMemcache(); 32 | $memcache->set("suite_access_token", $accessToken, 0, time() + 7000); // suite access token有效期为7200秒,这里设置为7000秒 33 | } 34 | 35 | public static function getSuiteAccessToken() 36 | { 37 | $memcache = self::getMemcache(); 38 | return $memcache->get("suite_access_token"); 39 | } 40 | 41 | public static function setIsvCorpAccessToken($key,$accessToken) 42 | { 43 | $memcache = self::getMemcache(); 44 | $memcache->set($key, $accessToken, 0, time() + 7000); 45 | } 46 | 47 | public static function getIsvCorpAccessToken($key) 48 | { 49 | $memcache = self::getMemcache(); 50 | return $memcache->get($key); 51 | } 52 | 53 | public static function setTmpAuthCode($tmpAuthCode){ 54 | $memcache = self::getMemcache(); 55 | $memcache->set("tmp_auth_code", $tmpAuthCode); 56 | } 57 | 58 | public static function getTmpAuthCode(){ 59 | $memcache = self::getMemcache(); 60 | return $memcache->get("tmp_auth_code"); 61 | } 62 | 63 | public static function setPermanentCode($key,$value){ 64 | $memcache = self::getMemcache(); 65 | $memcache->set($key, $value); 66 | } 67 | 68 | public static function getPermanentCode($key){ 69 | $memcache = self::getMemcache(); 70 | return $memcache->get($key); 71 | } 72 | 73 | public static function setActiveStatus($corpKey){ 74 | $memcache = self::getMemcache(); 75 | $memcache->set($corpKey,100); 76 | } 77 | 78 | public static function getActiveStatus($key){ 79 | $memcache = self::getMemcache(); 80 | return $memcache->get($key); 81 | } 82 | 83 | public static function setCorpInfo($data){ 84 | $memcache = self::getMemcache(); 85 | $memcache->set('dingding_corp_info',$data); 86 | } 87 | 88 | public static function getCorpInfo(){ 89 | $memcache = self::getMemcache(); 90 | $corpInfo = $memcache->get('dingding_corp_info'); 91 | return $corpInfo; 92 | } 93 | 94 | 95 | public static function setAuthInfo($key,$authInfo){ 96 | $memcache = self::getMemcache(); 97 | $memcache->set($key,$authInfo); 98 | } 99 | 100 | public static function getAuthInfo($key){ 101 | $memcache = self::getMemcache(); 102 | return $memcache->get($key); 103 | } 104 | 105 | public static function removeByKeyArr($arr){ 106 | $memcache = self::getMemcache(); 107 | foreach($arr as $a){ 108 | $memcache->set($a,''); 109 | } 110 | } 111 | 112 | private static function getMemcache() 113 | { 114 | /*if (class_exists("Memcache")) 115 | { 116 | $memcache = new Memcache; 117 | if ($memcache->connect('localhost', 11211)) 118 | { 119 | return $memcache; 120 | } 121 | }*/ 122 | 123 | return new FileCache; 124 | } 125 | 126 | public static function get($key) 127 | { 128 | return self::getMemcache()->get($key); 129 | } 130 | 131 | public static function set($key, $value) 132 | { 133 | self::getMemcache()->set($key, $value); 134 | } 135 | } 136 | 137 | /** 138 | * fallbacks 139 | */ 140 | class FileCache 141 | { 142 | function __call($name, $args) 143 | { 144 | if($name=='set') 145 | { 146 | switch(count($args)) 147 | { 148 | case 0:break; 149 | case 1: break; 150 | case 2: $this->setComm($args[0], $args[1]); break; 151 | case 4: $this->setLimit($args[0], $args[1],$args[2],$args[3]); break; 152 | default: //do something 153 | break; 154 | } 155 | } 156 | } 157 | 158 | function setComm($key, $value) 159 | { 160 | if($key){ 161 | $data = json_decode($this->get_file(DIR_ROOT ."filecache.php"),true); 162 | if(!$value){ 163 | unset($data["$key"]); 164 | }else{ 165 | $item = array(); 166 | $item["$key"] = $value; 167 | $item['expire_time'] = 0; 168 | $item['create_time'] = time(); 169 | $data["$key"] = $item; 170 | } 171 | $this->set_file("filecache.php",json_encode($data)); 172 | } 173 | } 174 | 175 | function setLimit($key, $value,$tag,$time) 176 | { 177 | if($key){ 178 | $data = json_decode($this->get_file(DIR_ROOT ."filecache.php"),true); 179 | if(!$value){ 180 | unset($data["$key"]); 181 | }else{ 182 | $item = array(); 183 | $item["$key"] = $value; 184 | $item['expire_time'] = $time; 185 | $item['create_time'] = time(); 186 | $data["$key"] = $item; 187 | } 188 | 189 | $this->set_file("filecache.php",json_encode($data)); 190 | } 191 | } 192 | 193 | function get($key) 194 | { 195 | if($key){ 196 | $data = json_decode($this->get_file(DIR_ROOT ."filecache.php"),true); 197 | if($data&&array_key_exists($key,$data)){ 198 | $item = $data["$key"]; 199 | if(!$item){ 200 | return false; 201 | } 202 | if($item['expire_time']>0&&$item['expire_time'] < time()){ 203 | return false; 204 | } 205 | 206 | return $item["$key"]; 207 | }else{ 208 | return false; 209 | } 210 | 211 | } 212 | } 213 | 214 | function get_file($filename) { 215 | if (!file_exists($filename)) { 216 | $fp = fopen($filename, "w"); 217 | fwrite($fp, "" . ''); 218 | fclose($fp); 219 | return false; 220 | }else{ 221 | $content = trim(substr(file_get_contents($filename), 15)); 222 | } 223 | return $content; 224 | } 225 | 226 | function set_file($filename, $content) { 227 | $fp = fopen($filename, "w"); 228 | fwrite($fp, "" . $content); 229 | fclose($fp); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /isv/util/Http.php: -------------------------------------------------------------------------------- 1 | send(); 12 | if ($response->hasErrors()) 13 | { 14 | var_dump($response); 15 | } 16 | if ($response->body->errcode != 0) 17 | { 18 | var_dump($response->body); 19 | } 20 | return $response->body; 21 | } 22 | 23 | 24 | public static function post($path, $params, $data) 25 | { 26 | $url = self::joinParams($path, $params); 27 | $response = \Httpful\Request::post($url) 28 | ->body($data) 29 | ->sendsJson() 30 | ->send(); 31 | if ($response->hasErrors()) 32 | { 33 | var_dump($response); 34 | } 35 | if ($response->body->errcode != 0) 36 | { 37 | var_dump($response->body); 38 | } 39 | return $response->body; 40 | } 41 | 42 | 43 | private static function joinParams($path, $params) 44 | { 45 | $url = OAPI_HOST . $path; 46 | if (count($params) > 0) 47 | { 48 | $url = $url . "?"; 49 | foreach ($params as $key => $value) 50 | { 51 | $url = $url . $key . "=" . $value . "&"; 52 | } 53 | $length = count($url); 54 | if ($url[$length - 1] == '&') 55 | { 56 | $url = substr($url, 0, $length - 1); 57 | } 58 | } 59 | return $url; 60 | } 61 | } -------------------------------------------------------------------------------- /isv/util/Log.php: -------------------------------------------------------------------------------- 1 | array($vendorDir . '/nategood/httpful/src'), 10 | ); 11 | -------------------------------------------------------------------------------- /isv/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 composerRequire9c336c4d71b270c36d1e071cf0b19270($file) 48 | { 49 | require $file; 50 | } 51 | -------------------------------------------------------------------------------- /isv/vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "nategood/httpful", 4 | "version": "0.2.19", 5 | "version_normalized": "0.2.19.0", 6 | "source": { 7 | "type": "git", 8 | "url": "https://github.com/nategood/httpful.git", 9 | "reference": "bd73f89d34d8f879c54ac46eb94b0f7be1d00820" 10 | }, 11 | "dist": { 12 | "type": "zip", 13 | "url": "https://api.github.com/repos/nategood/httpful/zipball/bd73f89d34d8f879c54ac46eb94b0f7be1d00820", 14 | "reference": "bd73f89d34d8f879c54ac46eb94b0f7be1d00820", 15 | "shasum": "" 16 | }, 17 | "require": { 18 | "ext-curl": "*", 19 | "php": ">=5.3" 20 | }, 21 | "require-dev": { 22 | "phpunit/phpunit": "*" 23 | }, 24 | "time": "2015-03-08 15:22:23", 25 | "type": "library", 26 | "installation-source": "dist", 27 | "autoload": { 28 | "psr-0": { 29 | "Httpful": "src/" 30 | } 31 | }, 32 | "notification-url": "https://packagist.org/downloads/", 33 | "license": [ 34 | "MIT" 35 | ], 36 | "authors": [ 37 | { 38 | "name": "Nate Good", 39 | "email": "me@nategood.com", 40 | "homepage": "http://nategood.com" 41 | } 42 | ], 43 | "description": "A Readable, Chainable, REST friendly, PHP HTTP Client", 44 | "homepage": "http://github.com/nategood/httpful", 45 | "keywords": [ 46 | "api", 47 | "curl", 48 | "http", 49 | "requests", 50 | "rest", 51 | "restful" 52 | ] 53 | } 54 | ] 55 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | composer.lock 3 | vendor 4 | downloads 5 | .idea/* 6 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | before_script: cd tests 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | - hhvm -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Nate Good 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/README.md: -------------------------------------------------------------------------------- 1 | # Httpful 2 | 3 | [![Build Status](https://secure.travis-ci.org/nategood/httpful.png?branch=master)](http://travis-ci.org/nategood/httpful) [![Total Downloads](https://poser.pugx.org/nategood/httpful/downloads.png)](https://packagist.org/packages/nategood/httpful) 4 | 5 | [Httpful](http://phphttpclient.com) is a simple Http Client library for PHP 5.3+. There is an emphasis of readability, simplicity, and flexibility – basically provide the features and flexibility to get the job done and make those features really easy to use. 6 | 7 | Features 8 | 9 | - Readable HTTP Method Support (GET, PUT, POST, DELETE, HEAD, PATCH and OPTIONS) 10 | - Custom Headers 11 | - Automatic "Smart" Parsing 12 | - Automatic Payload Serialization 13 | - Basic Auth 14 | - Client Side Certificate Auth 15 | - Request "Templates" 16 | 17 | # Sneak Peak 18 | 19 | Here's something to whet your appetite. Search the twitter API for tweets containing "#PHP". Include a trivial header for the heck of it. Notice that the library automatically interprets the response as JSON (can override this if desired) and parses it as an array of objects. 20 | 21 | ```php 22 | $url = "http://search.twitter.com/search.json?q=" . urlencode('#PHP'); 23 | $response = Request::get($url) 24 | ->withXTrivialHeader('Just as a demo') 25 | ->send(); 26 | 27 | foreach ($response->body->results as $tweet) { 28 | echo "@{$tweet->from_user} tweets \"{$tweet->text}\"\n"; 29 | } 30 | ``` 31 | 32 | # Installation 33 | 34 | ## Phar 35 | 36 | A [PHP Archive](http://php.net/manual/en/book.phar.php) (or .phar) file is available for [downloading](http://phphttpclient.com/httpful.phar). Simply [download](http://phphttpclient.com/httpful.phar) the .phar, drop it into your project, and include it like you would any other php file. _This method is ideal for smaller projects, one off scripts, and quick API hacking_. 37 | 38 | ```php 39 | include('httpful.phar'); 40 | $r = \Httpful\Request::get($uri)->sendIt(); 41 | ... 42 | ``` 43 | 44 | ## Composer 45 | 46 | Httpful is PSR-0 compliant and can be installed using [composer](http://getcomposer.org/). Simply add `nategood/httpful` to your composer.json file. _Composer is the sane alternative to PEAR. It is excellent for managing dependencies in larger projects_. 47 | 48 | { 49 | "require": { 50 | "nategood/httpful": "*" 51 | } 52 | } 53 | 54 | ## Install from Source 55 | 56 | Because Httpful is PSR-0 compliant, you can also just clone the Httpful repository and use a PSR-0 compatible autoloader to load the library, like [Symfony's](http://symfony.com/doc/current/components/class_loader.html). Alternatively you can use the PSR-0 compliant autoloader included with the Httpful (simply `require("bootstrap.php")`). 57 | 58 | # Show Me More! 59 | 60 | You can checkout the [Httpful Landing Page](http://phphttpclient.com) for more info including many examples and [documentation](http://phphttpclient.com/docs). 61 | 62 | # Contributing 63 | 64 | Httpful highly encourages sending in pull requests. When submitting a pull request please: 65 | 66 | - All pull requests should target the `dev` branch (not `master`) 67 | - Make sure your code follows the [coding conventions](http://pear.php.net/manual/en/standards.php) 68 | - Please use soft tabs (four spaces) instead of hard tabs 69 | - Make sure you add appropriate test coverage for your changes 70 | - Run all unit tests in the test directory via `phpunit ./tests` 71 | - Include commenting where appropriate and add a descriptive pull request message 72 | 73 | # Changelog 74 | 75 | ## 0.2.19 76 | 77 | - FEATURE Before send hook [PR #164](https://github.com/nategood/httpful/pull/164) 78 | - MINOR More descriptive connection exceptions [PR #166](https://github.com/nategood/httpful/pull/166) 79 | 80 | ## 0.2.18 81 | 82 | - FIX [PR #149](https://github.com/nategood/httpful/pull/149) 83 | - FIX [PR #150](https://github.com/nategood/httpful/pull/150) 84 | - FIX [PR #156](https://github.com/nategood/httpful/pull/156) 85 | 86 | ## 0.2.17 87 | 88 | - FEATURE [PR #144](https://github.com/nategood/httpful/pull/144) Adds additional parameter to the Response class to specify additional meta data about the request/response (e.g. number of redirect). 89 | 90 | ## 0.2.16 91 | 92 | - FEATURE Added support for whenError to define a custom callback to be fired upon error. Useful for logging or overriding the default error_log behavior. 93 | 94 | ## 0.2.15 95 | 96 | - FEATURE [I #131](https://github.com/nategood/httpful/pull/131) Support for SOCKS proxy 97 | 98 | ## 0.2.14 99 | 100 | - FEATURE [I #138](https://github.com/nategood/httpful/pull/138) Added alternative option for XML request construction. In the next major release this will likely supplant the older version. 101 | 102 | ## 0.2.13 103 | 104 | - REFACTOR [I #121](https://github.com/nategood/httpful/pull/121) Throw more descriptive exception on curl errors 105 | - REFACTOR [I #122](https://github.com/nategood/httpful/issues/122) Better proxy scrubbing in Request 106 | - REFACTOR [I #119](https://github.com/nategood/httpful/issues/119) Better document the mimeType param on Request::body 107 | - Misc code and test cleanup 108 | 109 | ## 0.2.12 110 | 111 | - REFACTOR [I #123](https://github.com/nategood/httpful/pull/123) Support new curl file upload method 112 | - FEATURE [I #118](https://github.com/nategood/httpful/pull/118) 5.4 HTTP Test Server 113 | - FIX [I #109](https://github.com/nategood/httpful/pull/109) Typo 114 | - FIX [I #103](https://github.com/nategood/httpful/pull/103) Handle also CURLOPT_SSL_VERIFYHOST for strictSsl mode 115 | 116 | ## 0.2.11 117 | 118 | - FIX [I #99](https://github.com/nategood/httpful/pull/99) Prevent hanging on HEAD requests 119 | 120 | ## 0.2.10 121 | 122 | - FIX [I #93](https://github.com/nategood/httpful/pull/86) Fixes edge case where content-length would be set incorrectly 123 | 124 | ## 0.2.9 125 | 126 | - FEATURE [I #89](https://github.com/nategood/httpful/pull/89) multipart/form-data support (a.k.a. file uploads)! Thanks @dtelaroli! 127 | 128 | ## 0.2.8 129 | 130 | - FIX Notice fix for Pull Request 86 131 | 132 | ## 0.2.7 133 | 134 | - FIX [I #86](https://github.com/nategood/httpful/pull/86) Remove Connection Established header when using a proxy 135 | 136 | ## 0.2.6 137 | 138 | - FIX [I #85](https://github.com/nategood/httpful/issues/85) Empty Content Length issue resolved 139 | 140 | ## 0.2.5 141 | 142 | - FEATURE [I #80](https://github.com/nategood/httpful/issues/80) [I #81](https://github.com/nategood/httpful/issues/81) Proxy support added with `useProxy` method. 143 | 144 | ## 0.2.4 145 | 146 | - FEATURE [I #77](https://github.com/nategood/httpful/issues/77) Convenience method for setting a timeout (seconds) `$req->timeoutIn(10);` 147 | - FIX [I #75](https://github.com/nategood/httpful/issues/75) [I #78](https://github.com/nategood/httpful/issues/78) Bug with checking if digest auth is being used. 148 | 149 | ## 0.2.3 150 | 151 | - FIX Overriding default Mime Handlers 152 | - FIX [PR #73](https://github.com/nategood/httpful/pull/73) Parsing http status codes 153 | 154 | ## 0.2.2 155 | 156 | - FEATURE Add support for parsing JSON responses as associative arrays instead of objects 157 | - FEATURE Better support for setting constructor arguments on Mime Handlers 158 | 159 | ## 0.2.1 160 | 161 | - FEATURE [PR #72](https://github.com/nategood/httpful/pull/72) Allow support for custom Accept header 162 | 163 | ## 0.2.0 164 | 165 | - REFACTOR [PR #49](https://github.com/nategood/httpful/pull/49) Broke headers out into their own class 166 | - REFACTOR [PR #54](https://github.com/nategood/httpful/pull/54) Added more specific Exceptions 167 | - FIX [PR #58](https://github.com/nategood/httpful/pull/58) Fixes throwing an error on an empty xml response 168 | - FEATURE [PR #57](https://github.com/nategood/httpful/pull/57) Adds support for digest authentication 169 | 170 | ## 0.1.6 171 | 172 | - Ability to set the number of max redirects via overloading `followRedirects(int max_redirects)` 173 | - Standards Compliant fix to `Accepts` header 174 | - Bug fix for bootstrap process when installed via Composer 175 | 176 | ## 0.1.5 177 | 178 | - Use `DIRECTORY_SEPARATOR` constant [PR #33](https://github.com/nategood/httpful/pull/32) 179 | - [PR #35](https://github.com/nategood/httpful/pull/35) 180 | - Added the raw\_headers property reference to response. 181 | - Compose request header and added raw\_header to Request object. 182 | - Fixed response has errors and added more comments for clarity. 183 | - Fixed header parsing to allow the minimum (status line only) and also cater for the actual CRLF ended headers as per RFC2616. 184 | - Added the perfect test Accept: header for all Acceptable scenarios see @b78e9e82cd9614fbe137c01bde9439c4e16ca323 for details. 185 | - Added default User-Agent header 186 | - `User-Agent: Httpful/0.1.5` + curl version + server software + PHP version 187 | - To bypass this "default" operation simply add a User-Agent to the request headers even a blank User-Agent is sufficient and more than simple enough to produce me thinks. 188 | - Completed test units for additions. 189 | - Added phpunit coverage reporting and helped phpunit auto locate the tests a bit easier. 190 | 191 | ## 0.1.4 192 | 193 | - Add support for CSV Handling [PR #32](https://github.com/nategood/httpful/pull/32) 194 | 195 | ## 0.1.3 196 | 197 | - Handle empty responses in JsonParser and XmlParser 198 | 199 | ## 0.1.2 200 | 201 | - Added support for setting XMLHandler configuration options 202 | - Added examples for overriding XmlHandler and registering a custom parser 203 | - Removed the httpful.php download (deprecated in favor of httpful.phar) 204 | 205 | ## 0.1.1 206 | 207 | - Bug fix serialization default case and phpunit tests 208 | 209 | ## 0.1.0 210 | 211 | - Added Support for Registering Mime Handlers 212 | - Created AbstractMimeHandler type that all Mime Handlers must extend 213 | - Pulled out the parsing/serializing logic from the Request/Response classes into their own MimeHandler classes 214 | - Added ability to register new mime handlers for mime types 215 | 216 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/bootstrap.php: -------------------------------------------------------------------------------- 1 | setStub($stub); 36 | } catch(Exception $e) { 37 | $phar = false; 38 | } 39 | exit_unless($phar, "Unable to create a phar. Make certain you have phar.readonly=0 set in your ini file."); 40 | $phar->buildFromDirectory(dirname($source_dir)); 41 | echo "[ OK ]\n"; 42 | 43 | 44 | 45 | // Add it to git! 46 | //echo "Adding httpful.phar to the repo... "; 47 | //$return_code = 0; 48 | //passthru("git add $phar_path", $return_code); 49 | //exit_unless($return_code === 0, "Unable to add download files to git."); 50 | //echo "[ OK ]\n"; 51 | echo "\nBuild completed successfully.\n\n"; 52 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nategood/httpful", 3 | "description": "A Readable, Chainable, REST friendly, PHP HTTP Client", 4 | "homepage": "http://github.com/nategood/httpful", 5 | "license": "MIT", 6 | "keywords": ["http", "curl", "rest", "restful", "api", "requests"], 7 | "version": "0.2.19", 8 | "authors": [ 9 | { 10 | "name": "Nate Good", 11 | "email": "me@nategood.com", 12 | "homepage": "http://nategood.com" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=5.3", 17 | "ext-curl": "*" 18 | }, 19 | "autoload": { 20 | "psr-0": { 21 | "Httpful": "src/" 22 | } 23 | }, 24 | "require-dev": { 25 | "phpunit/phpunit": "*" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/examples/freebase.php: -------------------------------------------------------------------------------- 1 | expectsJson() 10 | ->sendIt(); 11 | 12 | echo 'The Dead Weather has ' . count($response->body->result->album) . " albums.\n"; -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/examples/github.php: -------------------------------------------------------------------------------- 1 | send(); 8 | 9 | echo "{$request->body->name} joined GitHub on " . date('M jS', strtotime($request->body->{'created-at'})) ."\n"; -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/examples/override.php: -------------------------------------------------------------------------------- 1 | 'http://example.com'); 9 | \Httpful\Httpful::register(\Httpful\Mime::XML, new \Httpful\Handlers\XmlHandler($conf)); 10 | 11 | // We can also add the parsers with our own... 12 | class SimpleCsvHandler extends \Httpful\Handlers\MimeHandlerAdapter 13 | { 14 | /** 15 | * Takes a response body, and turns it into 16 | * a two dimensional array. 17 | * 18 | * @param string $body 19 | * @return mixed 20 | */ 21 | public function parse($body) 22 | { 23 | return str_getcsv($body); 24 | } 25 | 26 | /** 27 | * Takes a two dimensional array and turns it 28 | * into a serialized string to include as the 29 | * body of a request 30 | * 31 | * @param mixed $payload 32 | * @return string 33 | */ 34 | public function serialize($payload) 35 | { 36 | $serialized = ''; 37 | foreach ($payload as $line) { 38 | $serialized .= '"' . implode('","', $line) . '"' . "\n"; 39 | } 40 | return $serialized; 41 | } 42 | } 43 | 44 | \Httpful\Httpful::register('text/csv', new SimpleCsvHandler()); -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/examples/showclix.php: -------------------------------------------------------------------------------- 1 | expectsType('json') 11 | ->send(); 12 | 13 | // Print out the event details 14 | echo "The event {$response->body->event} will take place on {$response->body->event_start}\n"; 15 | 16 | // Example overriding the default JSON handler with one that encodes the response as an array 17 | \Httpful\Httpful::register(\Httpful\Mime::JSON, new \Httpful\Handlers\JsonHandler(array('decode_as_array' => true))); 18 | 19 | $response = Request::get($uri) 20 | ->expectsType('json') 21 | ->send(); 22 | 23 | // Print out the event details 24 | echo "The event {$response->body['event']} will take place on {$response->body['event_start']}\n"; -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/src/Httpful/Bootstrap.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Bootstrap 12 | { 13 | 14 | const DIR_GLUE = DIRECTORY_SEPARATOR; 15 | const NS_GLUE = '\\'; 16 | 17 | public static $registered = false; 18 | 19 | /** 20 | * Register the autoloader and any other setup needed 21 | */ 22 | public static function init() 23 | { 24 | spl_autoload_register(array('\Httpful\Bootstrap', 'autoload')); 25 | self::registerHandlers(); 26 | } 27 | 28 | /** 29 | * The autoload magic (PSR-0 style) 30 | * 31 | * @param string $classname 32 | */ 33 | public static function autoload($classname) 34 | { 35 | self::_autoload(dirname(dirname(__FILE__)), $classname); 36 | } 37 | 38 | /** 39 | * Register the autoloader and any other setup needed 40 | */ 41 | public static function pharInit() 42 | { 43 | spl_autoload_register(array('\Httpful\Bootstrap', 'pharAutoload')); 44 | self::registerHandlers(); 45 | } 46 | 47 | /** 48 | * Phar specific autoloader 49 | * 50 | * @param string $classname 51 | */ 52 | public static function pharAutoload($classname) 53 | { 54 | self::_autoload('phar://httpful.phar', $classname); 55 | } 56 | 57 | /** 58 | * @param string base 59 | * @param string classname 60 | */ 61 | private static function _autoload($base, $classname) 62 | { 63 | $parts = explode(self::NS_GLUE, $classname); 64 | $path = $base . self::DIR_GLUE . implode(self::DIR_GLUE, $parts) . '.php'; 65 | 66 | if (file_exists($path)) { 67 | require_once($path); 68 | } 69 | } 70 | /** 71 | * Register default mime handlers. Is idempotent. 72 | */ 73 | public static function registerHandlers() 74 | { 75 | if (self::$registered === true) { 76 | return; 77 | } 78 | 79 | // @todo check a conf file to load from that instead of 80 | // hardcoding into the library? 81 | $handlers = array( 82 | \Httpful\Mime::JSON => new \Httpful\Handlers\JsonHandler(), 83 | \Httpful\Mime::XML => new \Httpful\Handlers\XmlHandler(), 84 | \Httpful\Mime::FORM => new \Httpful\Handlers\FormHandler(), 85 | \Httpful\Mime::CSV => new \Httpful\Handlers\CsvHandler(), 86 | ); 87 | 88 | foreach ($handlers as $mime => $handler) { 89 | // Don't overwrite if the handler has already been registered 90 | if (Httpful::hasParserRegistered($mime)) 91 | continue; 92 | Httpful::register($mime, $handler); 93 | } 94 | 95 | self::$registered = true; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/src/Httpful/Exception/ConnectionErrorException.php: -------------------------------------------------------------------------------- 1 | 5 | */ 6 | 7 | namespace Httpful\Handlers; 8 | 9 | class CsvHandler extends MimeHandlerAdapter 10 | { 11 | /** 12 | * @param string $body 13 | * @return mixed 14 | */ 15 | public function parse($body) 16 | { 17 | if (empty($body)) 18 | return null; 19 | 20 | $parsed = array(); 21 | $fp = fopen('data://text/plain;base64,' . base64_encode($body), 'r'); 22 | while (($r = fgetcsv($fp)) !== FALSE) { 23 | $parsed[] = $r; 24 | } 25 | 26 | if (empty($parsed)) 27 | throw new \Exception("Unable to parse response as CSV"); 28 | return $parsed; 29 | } 30 | 31 | /** 32 | * @param mixed $payload 33 | * @return string 34 | */ 35 | public function serialize($payload) 36 | { 37 | $fp = fopen('php://temp/maxmemory:'. (6*1024*1024), 'r+'); 38 | $i = 0; 39 | foreach ($payload as $fields) { 40 | if($i++ == 0) { 41 | fputcsv($fp, array_keys($fields)); 42 | } 43 | fputcsv($fp, $fields); 44 | } 45 | rewind($fp); 46 | $data = stream_get_contents($fp); 47 | fclose($fp); 48 | return $data; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/src/Httpful/Handlers/FormHandler.php: -------------------------------------------------------------------------------- 1 | 5 | */ 6 | 7 | namespace Httpful\Handlers; 8 | 9 | class FormHandler extends MimeHandlerAdapter 10 | { 11 | /** 12 | * @param string $body 13 | * @return mixed 14 | */ 15 | public function parse($body) 16 | { 17 | $parsed = array(); 18 | parse_str($body, $parsed); 19 | return $parsed; 20 | } 21 | 22 | /** 23 | * @param mixed $payload 24 | * @return string 25 | */ 26 | public function serialize($payload) 27 | { 28 | return http_build_query($payload, null, '&'); 29 | } 30 | } -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/src/Httpful/Handlers/JsonHandler.php: -------------------------------------------------------------------------------- 1 | 5 | */ 6 | 7 | namespace Httpful\Handlers; 8 | 9 | class JsonHandler extends MimeHandlerAdapter 10 | { 11 | private $decode_as_array = false; 12 | 13 | public function init(array $args) 14 | { 15 | $this->decode_as_array = !!(array_key_exists('decode_as_array', $args) ? $args['decode_as_array'] : false); 16 | } 17 | 18 | /** 19 | * @param string $body 20 | * @return mixed 21 | */ 22 | public function parse($body) 23 | { 24 | $body = $this->stripBom($body); 25 | if (empty($body)) 26 | return null; 27 | $parsed = json_decode($body, $this->decode_as_array); 28 | if (is_null($parsed) && 'null' !== strtolower($body)) 29 | throw new \Exception("Unable to parse response as JSON"); 30 | return $parsed; 31 | } 32 | 33 | /** 34 | * @param mixed $payload 35 | * @return string 36 | */ 37 | public function serialize($payload) 38 | { 39 | return json_encode($payload); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/src/Httpful/Handlers/MimeHandlerAdapter.php: -------------------------------------------------------------------------------- 1 | init($args); 16 | } 17 | 18 | /** 19 | * Initial setup of 20 | * @param array $args 21 | */ 22 | public function init(array $args) 23 | { 24 | } 25 | 26 | /** 27 | * @param string $body 28 | * @return mixed 29 | */ 30 | public function parse($body) 31 | { 32 | return $body; 33 | } 34 | 35 | /** 36 | * @param mixed $payload 37 | * @return string 38 | */ 39 | function serialize($payload) 40 | { 41 | return (string) $payload; 42 | } 43 | 44 | protected function stripBom($body) 45 | { 46 | if ( substr($body,0,3) === "\xef\xbb\xbf" ) // UTF-8 47 | $body = substr($body,3); 48 | else if ( substr($body,0,4) === "\xff\xfe\x00\x00" || substr($body,0,4) === "\x00\x00\xfe\xff" ) // UTF-32 49 | $body = substr($body,4); 50 | else if ( substr($body,0,2) === "\xff\xfe" || substr($body,0,2) === "\xfe\xff" ) // UTF-16 51 | $body = substr($body,2); 52 | return $body; 53 | } 54 | } -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/src/Httpful/Handlers/README.md: -------------------------------------------------------------------------------- 1 | # Handlers 2 | 3 | Handlers are simple classes that are used to parse response bodies and serialize request payloads. All Handlers must extend the `MimeHandlerAdapter` class and implement two methods: `serialize($payload)` and `parse($response)`. Let's build a very basic Handler to register for the `text/csv` mime type. 4 | 5 | 7 | */ 8 | 9 | namespace Httpful\Handlers; 10 | 11 | class XHtmlHandler extends MimeHandlerAdapter 12 | { 13 | // @todo add html specific parsing 14 | // see DomDocument::load http://docs.php.net/manual/en/domdocument.loadhtml.php 15 | } -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/src/Httpful/Handlers/XmlHandler.php: -------------------------------------------------------------------------------- 1 | 6 | * @author Nathan Good 7 | */ 8 | 9 | namespace Httpful\Handlers; 10 | 11 | class XmlHandler extends MimeHandlerAdapter 12 | { 13 | /** 14 | * @var string $namespace xml namespace to use with simple_load_string 15 | */ 16 | private $namespace; 17 | 18 | /** 19 | * @var int $libxml_opts see http://www.php.net/manual/en/libxml.constants.php 20 | */ 21 | private $libxml_opts; 22 | 23 | /** 24 | * @param array $conf sets configuration options 25 | */ 26 | public function __construct(array $conf = array()) 27 | { 28 | $this->namespace = isset($conf['namespace']) ? $conf['namespace'] : ''; 29 | $this->libxml_opts = isset($conf['libxml_opts']) ? $conf['libxml_opts'] : 0; 30 | } 31 | 32 | /** 33 | * @param string $body 34 | * @return mixed 35 | * @throws Exception if unable to parse 36 | */ 37 | public function parse($body) 38 | { 39 | $body = $this->stripBom($body); 40 | if (empty($body)) 41 | return null; 42 | $parsed = simplexml_load_string($body, null, $this->libxml_opts, $this->namespace); 43 | if ($parsed === false) 44 | throw new \Exception("Unable to parse response as XML"); 45 | return $parsed; 46 | } 47 | 48 | /** 49 | * @param mixed $payload 50 | * @return string 51 | * @throws Exception if unable to serialize 52 | */ 53 | public function serialize($payload) 54 | { 55 | list($_, $dom) = $this->_future_serializeAsXml($payload); 56 | return $dom->saveXml(); 57 | } 58 | 59 | /** 60 | * @param mixed $payload 61 | * @return string 62 | * @author Ted Zellers 63 | */ 64 | public function serialize_clean($payload) 65 | { 66 | $xml = new \XMLWriter; 67 | $xml->openMemory(); 68 | $xml->startDocument('1.0','ISO-8859-1'); 69 | $this->serialize_node($xml, $payload); 70 | return $xml->outputMemory(true); 71 | } 72 | 73 | /** 74 | * @param XMLWriter $xmlw 75 | * @param mixed $node to serialize 76 | * @author Ted Zellers 77 | */ 78 | public function serialize_node(&$xmlw, $node){ 79 | if (!is_array($node)){ 80 | $xmlw->text($node); 81 | } else { 82 | foreach ($node as $k => $v){ 83 | $xmlw->startElement($k); 84 | $this->serialize_node($xmlw, $v); 85 | $xmlw->endElement(); 86 | } 87 | } 88 | } 89 | 90 | /** 91 | * @author Zack Douglas 92 | */ 93 | private function _future_serializeAsXml($value, $node = null, $dom = null) 94 | { 95 | if (!$dom) { 96 | $dom = new \DOMDocument; 97 | } 98 | if (!$node) { 99 | if (!is_object($value)) { 100 | $node = $dom->createElement('response'); 101 | $dom->appendChild($node); 102 | } else { 103 | $node = $dom; 104 | } 105 | } 106 | if (is_object($value)) { 107 | $objNode = $dom->createElement(get_class($value)); 108 | $node->appendChild($objNode); 109 | $this->_future_serializeObjectAsXml($value, $objNode, $dom); 110 | } else if (is_array($value)) { 111 | $arrNode = $dom->createElement('array'); 112 | $node->appendChild($arrNode); 113 | $this->_future_serializeArrayAsXml($value, $arrNode, $dom); 114 | } else if (is_bool($value)) { 115 | $node->appendChild($dom->createTextNode($value?'TRUE':'FALSE')); 116 | } else { 117 | $node->appendChild($dom->createTextNode($value)); 118 | } 119 | return array($node, $dom); 120 | } 121 | /** 122 | * @author Zack Douglas 123 | */ 124 | private function _future_serializeArrayAsXml($value, &$parent, &$dom) 125 | { 126 | foreach ($value as $k => &$v) { 127 | $n = $k; 128 | if (is_numeric($k)) { 129 | $n = "child-{$n}"; 130 | } 131 | $el = $dom->createElement($n); 132 | $parent->appendChild($el); 133 | $this->_future_serializeAsXml($v, $el, $dom); 134 | } 135 | return array($parent, $dom); 136 | } 137 | /** 138 | * @author Zack Douglas 139 | */ 140 | private function _future_serializeObjectAsXml($value, &$parent, &$dom) 141 | { 142 | $refl = new \ReflectionObject($value); 143 | foreach ($refl->getProperties() as $pr) { 144 | if (!$pr->isPrivate()) { 145 | $el = $dom->createElement($pr->getName()); 146 | $parent->appendChild($el); 147 | $this->_future_serializeAsXml($pr->getValue($value), $el, $dom); 148 | } 149 | } 150 | return array($parent, $dom); 151 | } 152 | } -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/src/Httpful/Http.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class Http 9 | { 10 | const HEAD = 'HEAD'; 11 | const GET = 'GET'; 12 | const POST = 'POST'; 13 | const PUT = 'PUT'; 14 | const DELETE = 'DELETE'; 15 | const PATCH = 'PATCH'; 16 | const OPTIONS = 'OPTIONS'; 17 | const TRACE = 'TRACE'; 18 | 19 | /** 20 | * @return array of HTTP method strings 21 | */ 22 | public static function safeMethods() 23 | { 24 | return array(self::HEAD, self::GET, self::OPTIONS, self::TRACE); 25 | } 26 | 27 | /** 28 | * @return bool 29 | * @param string HTTP method 30 | */ 31 | public static function isSafeMethod($method) 32 | { 33 | return in_array($method, self::safeMethods()); 34 | } 35 | 36 | /** 37 | * @return bool 38 | * @param string HTTP method 39 | */ 40 | public static function isUnsafeMethod($method) 41 | { 42 | return !in_array($method, self::safeMethods()); 43 | } 44 | 45 | /** 46 | * @return array list of (always) idempotent HTTP methods 47 | */ 48 | public static function idempotentMethods() 49 | { 50 | // Though it is possible to be idempotent, POST 51 | // is not guarunteed to be, and more often than 52 | // not, it is not. 53 | return array(self::HEAD, self::GET, self::PUT, self::DELETE, self::OPTIONS, self::TRACE, self::PATCH); 54 | } 55 | 56 | /** 57 | * @return bool 58 | * @param string HTTP method 59 | */ 60 | public static function isIdempotent($method) 61 | { 62 | return in_array($method, self::safeidempotentMethodsMethods()); 63 | } 64 | 65 | /** 66 | * @return bool 67 | * @param string HTTP method 68 | */ 69 | public static function isNotIdempotent($method) 70 | { 71 | return !in_array($method, self::idempotentMethods()); 72 | } 73 | 74 | /** 75 | * @deprecated Technically anything *can* have a body, 76 | * they just don't have semantic meaning. So say's Roy 77 | * http://tech.groups.yahoo.com/group/rest-discuss/message/9962 78 | * 79 | * @return array of HTTP method strings 80 | */ 81 | public static function canHaveBody() 82 | { 83 | return array(self::POST, self::PUT, self::PATCH, self::OPTIONS); 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/src/Httpful/Httpful.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class Mime 10 | { 11 | const JSON = 'application/json'; 12 | const XML = 'application/xml'; 13 | const XHTML = 'application/html+xml'; 14 | const FORM = 'application/x-www-form-urlencoded'; 15 | const UPLOAD = 'multipart/form-data'; 16 | const PLAIN = 'text/plain'; 17 | const JS = 'text/javascript'; 18 | const HTML = 'text/html'; 19 | const YAML = 'application/x-yaml'; 20 | const CSV = 'text/csv'; 21 | 22 | /** 23 | * Map short name for a mime type 24 | * to a full proper mime type 25 | */ 26 | public static $mimes = array( 27 | 'json' => self::JSON, 28 | 'xml' => self::XML, 29 | 'form' => self::FORM, 30 | 'plain' => self::PLAIN, 31 | 'text' => self::PLAIN, 32 | 'upload' => self::UPLOAD, 33 | 'html' => self::HTML, 34 | 'xhtml' => self::XHTML, 35 | 'js' => self::JS, 36 | 'javascript'=> self::JS, 37 | 'yaml' => self::YAML, 38 | 'csv' => self::CSV, 39 | ); 40 | 41 | /** 42 | * Get the full Mime Type name from a "short name". 43 | * Returns the short if no mapping was found. 44 | * @return string full mime type (e.g. application/json) 45 | * @param string common name for mime type (e.g. json) 46 | */ 47 | public static function getFullMime($short_name) 48 | { 49 | return array_key_exists($short_name, self::$mimes) ? self::$mimes[$short_name] : $short_name; 50 | } 51 | 52 | /** 53 | * @return bool 54 | * @param string $short_name 55 | */ 56 | public static function supportsMimeType($short_name) 57 | { 58 | return array_key_exists($short_name, self::$mimes); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/src/Httpful/Proxy.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Response 11 | { 12 | 13 | public $body, 14 | $raw_body, 15 | $headers, 16 | $raw_headers, 17 | $request, 18 | $code = 0, 19 | $content_type, 20 | $parent_type, 21 | $charset, 22 | $meta_data, 23 | $is_mime_vendor_specific = false, 24 | $is_mime_personal = false; 25 | 26 | private $parsers; 27 | /** 28 | * @param string $body 29 | * @param string $headers 30 | * @param Request $request 31 | * @param array $meta_data 32 | */ 33 | public function __construct($body, $headers, Request $request, array $meta_data = array()) 34 | { 35 | $this->request = $request; 36 | $this->raw_headers = $headers; 37 | $this->raw_body = $body; 38 | $this->meta_data = $meta_data; 39 | 40 | $this->code = $this->_parseCode($headers); 41 | $this->headers = Response\Headers::fromString($headers); 42 | 43 | $this->_interpretHeaders(); 44 | 45 | $this->body = $this->_parse($body); 46 | } 47 | 48 | /** 49 | * Status Code Definitions 50 | * 51 | * Informational 1xx 52 | * Successful 2xx 53 | * Redirection 3xx 54 | * Client Error 4xx 55 | * Server Error 5xx 56 | * 57 | * http://pretty-rfc.herokuapp.com/RFC2616#status.codes 58 | * 59 | * @return bool Did we receive a 4xx or 5xx? 60 | */ 61 | public function hasErrors() 62 | { 63 | return $this->code >= 400; 64 | } 65 | 66 | /** 67 | * @return return bool 68 | */ 69 | public function hasBody() 70 | { 71 | return !empty($this->body); 72 | } 73 | 74 | /** 75 | * Parse the response into a clean data structure 76 | * (most often an associative array) based on the expected 77 | * Mime type. 78 | * @return array|string|object the response parse accordingly 79 | * @param string Http response body 80 | */ 81 | public function _parse($body) 82 | { 83 | // If the user decided to forgo the automatic 84 | // smart parsing, short circuit. 85 | if (!$this->request->auto_parse) { 86 | return $body; 87 | } 88 | 89 | // If provided, use custom parsing callback 90 | if (isset($this->request->parse_callback)) { 91 | return call_user_func($this->request->parse_callback, $body); 92 | } 93 | 94 | // Decide how to parse the body of the response in the following order 95 | // 1. If provided, use the mime type specifically set as part of the `Request` 96 | // 2. If a MimeHandler is registered for the content type, use it 97 | // 3. If provided, use the "parent type" of the mime type from the response 98 | // 4. Default to the content-type provided in the response 99 | $parse_with = $this->request->expected_type; 100 | if (empty($this->request->expected_type)) { 101 | $parse_with = Httpful::hasParserRegistered($this->content_type) 102 | ? $this->content_type 103 | : $this->parent_type; 104 | } 105 | 106 | return Httpful::get($parse_with)->parse($body); 107 | } 108 | 109 | /** 110 | * Parse text headers from response into 111 | * array of key value pairs 112 | * @return array parse headers 113 | * @param string $headers raw headers 114 | */ 115 | public function _parseHeaders($headers) 116 | { 117 | $headers = preg_split("/(\r|\n)+/", $headers, -1, \PREG_SPLIT_NO_EMPTY); 118 | $parse_headers = array(); 119 | for ($i = 1; $i < count($headers); $i++) { 120 | list($key, $raw_value) = explode(':', $headers[$i], 2); 121 | $key = trim($key); 122 | $value = trim($raw_value); 123 | if (array_key_exists($key, $parse_headers)) { 124 | // See HTTP RFC Sec 4.2 Paragraph 5 125 | // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 126 | // If a header appears more than once, it must also be able to 127 | // be represented as a single header with a comma-separated 128 | // list of values. We transform accordingly. 129 | $parse_headers[$key] .= ',' . $value; 130 | } else { 131 | $parse_headers[$key] = $value; 132 | } 133 | } 134 | return $parse_headers; 135 | } 136 | 137 | public function _parseCode($headers) 138 | { 139 | $end = strpos($headers, "\r\n"); 140 | if ($end === false) $end = strlen($headers); 141 | $parts = explode(' ', substr($headers, 0, $end)); 142 | if (count($parts) < 2 || !is_numeric($parts[1])) { 143 | throw new \Exception("Unable to parse response code from HTTP response due to malformed response"); 144 | } 145 | return intval($parts[1]); 146 | } 147 | 148 | /** 149 | * After we've parse the headers, let's clean things 150 | * up a bit and treat some headers specially 151 | */ 152 | public function _interpretHeaders() 153 | { 154 | // Parse the Content-Type and charset 155 | $content_type = isset($this->headers['Content-Type']) ? $this->headers['Content-Type'] : ''; 156 | $content_type = explode(';', $content_type); 157 | 158 | $this->content_type = $content_type[0]; 159 | if (count($content_type) == 2 && strpos($content_type[1], '=') !== false) { 160 | list($nill, $this->charset) = explode('=', $content_type[1]); 161 | } 162 | 163 | // RFC 2616 states "text/*" Content-Types should have a default 164 | // charset of ISO-8859-1. "application/*" and other Content-Types 165 | // are assumed to have UTF-8 unless otherwise specified. 166 | // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1 167 | // http://www.w3.org/International/O-HTTP-charset.en.php 168 | if (!isset($this->charset)) { 169 | $this->charset = substr($this->content_type, 5) === 'text/' ? 'iso-8859-1' : 'utf-8'; 170 | } 171 | 172 | // Is vendor type? Is personal type? 173 | if (strpos($this->content_type, '/') !== false) { 174 | list($type, $sub_type) = explode('/', $this->content_type); 175 | $this->is_mime_vendor_specific = substr($sub_type, 0, 4) === 'vnd.'; 176 | $this->is_mime_personal = substr($sub_type, 0, 4) === 'prs.'; 177 | } 178 | 179 | // Parent type (e.g. xml for application/vnd.github.message+xml) 180 | $this->parent_type = $this->content_type; 181 | if (strpos($this->content_type, '+') !== false) { 182 | list($vendor, $this->parent_type) = explode('+', $this->content_type, 2); 183 | $this->parent_type = Mime::getFullMime($this->parent_type); 184 | } 185 | } 186 | 187 | /** 188 | * @return string 189 | */ 190 | public function __toString() 191 | { 192 | return $this->raw_body; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/src/Httpful/Response/Headers.php: -------------------------------------------------------------------------------- 1 | headers = $headers; 12 | } 13 | 14 | public static function fromString($string) 15 | { 16 | $lines = preg_split("/(\r|\n)+/", $string, -1, PREG_SPLIT_NO_EMPTY); 17 | array_shift($lines); // HTTP HEADER 18 | $headers = array(); 19 | foreach ($lines as $line) { 20 | list($name, $value) = explode(':', $line, 2); 21 | $headers[strtolower(trim($name))] = trim($value); 22 | } 23 | return new self($headers); 24 | } 25 | 26 | public function offsetExists($offset) 27 | { 28 | return isset($this->headers[strtolower($offset)]); 29 | } 30 | 31 | public function offsetGet($offset) 32 | { 33 | if (isset($this->headers[$name = strtolower($offset)])) { 34 | return $this->headers[$name]; 35 | } 36 | } 37 | 38 | public function offsetSet($offset, $value) 39 | { 40 | throw new \Exception("Headers are read-only."); 41 | } 42 | 43 | public function offsetUnset($offset) 44 | { 45 | throw new \Exception("Headers are read-only."); 46 | } 47 | 48 | public function count() 49 | { 50 | return count($this->headers); 51 | } 52 | 53 | public function toArray() 54 | { 55 | return $this->headers; 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/tests/Httpful/requestTest.php: -------------------------------------------------------------------------------- 1 | 4 | */ 5 | namespace Httpful\Test; 6 | 7 | class requestTest extends \PHPUnit_Framework_TestCase 8 | { 9 | 10 | /** 11 | * @author Nick Fox 12 | * @expectedException Httpful\Exception\ConnectionErrorException 13 | * @expectedExceptionMessage Unable to connect 14 | */ 15 | public function testGet_InvalidURL() 16 | { 17 | // Silence the default logger via whenError override 18 | \Httpful\Request::get('unavailable.url')->whenError(function($error) {})->send(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/tests/bootstrap-server.php: -------------------------------------------------------------------------------- 1 | ./server.log 2>&1 & echo $!', WEB_SERVER_HOST, WEB_SERVER_PORT, WEB_SERVER_DOCROOT); 16 | 17 | // Execute the command and store the process ID 18 | $output = array(); 19 | exec($command, $output, $exit_code); 20 | 21 | // sleep for a second to let server come up 22 | sleep(1); 23 | $pid = (int) $output[0]; 24 | 25 | // check server.log to see if it failed to start 26 | $server_logs = file_get_contents("./server.log"); 27 | if (strpos($server_logs, "Fail") !== false) { 28 | // server failed to start for some reason 29 | print "Failed to start server! Logs:" . PHP_EOL . PHP_EOL; 30 | print_r($server_logs); 31 | exit(1); 32 | } 33 | 34 | echo sprintf('%s - Web server started on %s:%d with PID %d', date('r'), WEB_SERVER_HOST, WEB_SERVER_PORT, $pid) . PHP_EOL; 35 | 36 | register_shutdown_function(function() { 37 | // cleanup after ourselves -- remove log file, shut down server 38 | global $pid; 39 | unlink("./server.log"); 40 | posix_kill($pid, SIGKILL); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/tests/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | . 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/tests/static/test.json: -------------------------------------------------------------------------------- 1 | {"foo": "bar", "baz": false} 2 | -------------------------------------------------------------------------------- /isv/vendor/nategood/httpful/tests/test_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-dingtalk/openapi-demo-php/18c4edc1898fb0fea2817d25dd00eb83edca22fc/isv/vendor/nategood/httpful/tests/test_image.jpg --------------------------------------------------------------------------------