Commit 71b2deb372de068fbd0655542c4076fe520498fb

Authored by u123231312
1 parent ba62decf

no message

  1 +<?php
  2 +require_once __DIR__ . '/../autoload.php';
  3 +
  4 +use \Qiniu\Auth;
  5 +
  6 +$accessKey = getenv('QINIU_ACCESS_KEY');
  7 +$secretKey = getenv('QINIU_SECRET_KEY');
  8 +$bucket = getenv('QINIU_TEST_BUCKET');
  9 +
  10 +// 初始化Auth状态
  11 +$auth = new Auth($accessKey, $secretKey);
  12 +
  13 +// 简单上传凭证
  14 +$expires = 3600;
  15 +
  16 +$policy = null;
  17 +$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
  18 +print($upToken . "\n");
  19 +
  20 +// 自定义凭证有效期(示例2小时)
  21 +$expires = 7200;
  22 +$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
  23 +print($upToken . "\n");
  24 +
  25 +// 覆盖上传凭证
  26 +$expires = 3600;
  27 +$keyToOverwrite = 'qiniu.mp4';
  28 +$upToken = $auth->uploadToken($bucket, $keyToOverwrite, $expires, $policy, true);
  29 +print($upToken . "\n");
  30 +
  31 +//自定义上传回复(非callback模式)凭证
  32 +$returnBody = '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}';
  33 +$policy = array(
  34 + 'returnBody' => $returnBody
  35 +);
  36 +$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
  37 +print($upToken . "\n");
  38 +
  39 +//带回调业务服务器的凭证(application/json)
  40 +$policy = array(
  41 + 'callbackUrl' => 'http://api.example.com/qiniu/upload/callback',
  42 + 'callbackBody' => '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}',
  43 + 'callbackBodyType' => 'application/json'
  44 +);
  45 +$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
  46 +print($upToken . "\n");
  47 +
  48 +
  49 +//带回调业务服务器的凭证(application/x-www-form-urlencoded)
  50 +$policy = array(
  51 + 'callbackUrl' => 'http://api.example.com/qiniu/upload/callback',
  52 + 'callbackBody' => 'key=$(key)&hash=$(etag)&bucket=$(bucket)&fsize=$(fsize)&name=$(x:name)'
  53 +);
  54 +$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
  55 +print($upToken . "\n");
  56 +
  57 +//带数据处理的凭证
  58 +$saveMp4Entry = \Qiniu\base64_urlSafeEncode($bucket . ":avthumb_test_target.mp4");
  59 +$saveJpgEntry = \Qiniu\base64_urlSafeEncode($bucket . ":vframe_test_target.jpg");
  60 +$avthumbMp4Fop = "avthumb/mp4|saveas/" . $saveMp4Entry;
  61 +$vframeJpgFop = "vframe/jpg/offset/1|saveas/" . $saveJpgEntry;
  62 +$policy = array(
  63 + 'persistentOps' => $avthumbMp4Fop . ";" . $vframeJpgFop,
  64 + 'persistentPipeline' => "video-pipe",
  65 + 'persistentNotifyUrl' => "http://api.example.com/qiniu/pfop/notify",
  66 +);
  67 +$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
  68 +print($upToken . "\n");
  1 +<?php
  2 +require_once __DIR__ . '/../autoload.php';
  3 +
  4 +use Qiniu\Auth;
  5 +
  6 +$accessKey = getenv('QINIU_ACCESS_KEY');
  7 +$secretKey = getenv('QINIU_SECRET_KEY');
  8 +$bucket = getenv('QINIU_TEST_BUCKET');
  9 +
  10 +$auth = new Auth($accessKey, $secretKey);
  11 +
  12 +//获取回调的body信息
  13 +$callbackBody = file_get_contents('php://input');
  14 +
  15 +//回调的contentType
  16 +$contentType = 'application/x-www-form-urlencoded';
  17 +
  18 +//回调的签名信息,可以验证该回调是否来自七牛
  19 +$authorization = $_SERVER['HTTP_AUTHORIZATION'];
  20 +
  21 +//七牛回调的url,具体可以参考:http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html
  22 +$url = 'http://172.30.251.210/upload_verify_callback.php';
  23 +
  24 +$isQiniuCallback = $auth->verifyCallback($contentType, $authorization, $url, $callbackBody);
  25 +
  26 +if ($isQiniuCallback) {
  27 + $resp = array('ret' => 'success');
  28 +} else {
  29 + $resp = array('ret' => 'failed');
  30 +}
  31 +
  32 +echo json_encode($resp);
  1 +<?php
  2 +namespace Qiniu;
  3 +
  4 +use Qiniu\Zone;
  5 +
  6 +final class Auth
  7 +{
  8 + private $accessKey;
  9 + private $secretKey;
  10 +
  11 + public function __construct($accessKey, $secretKey)
  12 + {
  13 + $this->accessKey = $accessKey;
  14 + $this->secretKey = $secretKey;
  15 + }
  16 +
  17 + public function getAccessKey()
  18 + {
  19 + return $this->accessKey;
  20 + }
  21 +
  22 + public function sign($data)
  23 + {
  24 + $hmac = hash_hmac('sha1', $data, $this->secretKey, true);
  25 + return $this->accessKey . ':' . \Qiniu\base64_urlSafeEncode($hmac);
  26 + }
  27 +
  28 + public function signWithData($data)
  29 + {
  30 + $encodedData = \Qiniu\base64_urlSafeEncode($data);
  31 + return $this->sign($encodedData) . ':' . $encodedData;
  32 + }
  33 +
  34 + public function signRequest($urlString, $body, $contentType = null)
  35 + {
  36 + $url = parse_url($urlString);
  37 + $data = '';
  38 + if (array_key_exists('path', $url)) {
  39 + $data = $url['path'];
  40 + }
  41 + if (array_key_exists('query', $url)) {
  42 + $data .= '?' . $url['query'];
  43 + }
  44 + $data .= "\n";
  45 +
  46 + if ($body !== null && $contentType === 'application/x-www-form-urlencoded') {
  47 + $data .= $body;
  48 + }
  49 + return $this->sign($data);
  50 + }
  51 +
  52 + public function verifyCallback($contentType, $originAuthorization, $url, $body)
  53 + {
  54 + $authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
  55 + return $originAuthorization === $authorization;
  56 + }
  57 +
  58 + public function privateDownloadUrl($baseUrl, $expires = 3600)
  59 + {
  60 + $deadline = time() + $expires;
  61 +
  62 + $pos = strpos($baseUrl, '?');
  63 + if ($pos !== false) {
  64 + $baseUrl .= '&e=';
  65 + } else {
  66 + $baseUrl .= '?e=';
  67 + }
  68 + $baseUrl .= $deadline;
  69 +
  70 + $token = $this->sign($baseUrl);
  71 + return "$baseUrl&token=$token";
  72 + }
  73 +
  74 + public function uploadToken($bucket, $key = null, $expires = 3600, $policy = null, $strictPolicy = true)
  75 + {
  76 + $deadline = time() + $expires;
  77 + $scope = $bucket;
  78 + if ($key !== null) {
  79 + $scope .= ':' . $key;
  80 + }
  81 +
  82 + $args = self::copyPolicy($args, $policy, $strictPolicy);
  83 + $args['scope'] = $scope;
  84 + $args['deadline'] = $deadline;
  85 +
  86 + $b = json_encode($args);
  87 + return $this->signWithData($b);
  88 + }
  89 +
  90 + /**
  91 + *上传策略,参数规格详见
  92 + *http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html
  93 + */
  94 + private static $policyFields = array(
  95 + 'callbackUrl',
  96 + 'callbackBody',
  97 + 'callbackHost',
  98 + 'callbackBodyType',
  99 + 'callbackFetchKey',
  100 +
  101 + 'returnUrl',
  102 + 'returnBody',
  103 +
  104 + 'endUser',
  105 + 'saveKey',
  106 + 'insertOnly',
  107 +
  108 + 'detectMime',
  109 + 'mimeLimit',
  110 + 'fsizeMin',
  111 + 'fsizeLimit',
  112 +
  113 + 'persistentOps',
  114 + 'persistentNotifyUrl',
  115 + 'persistentPipeline',
  116 +
  117 + 'deleteAfterDays',
  118 + 'fileType',
  119 + 'isPrefixalScope',
  120 + );
  121 +
  122 + private static function copyPolicy(&$policy, $originPolicy, $strictPolicy)
  123 + {
  124 + if ($originPolicy === null) {
  125 + return array();
  126 + }
  127 + foreach ($originPolicy as $key => $value) {
  128 + if (!$strictPolicy || in_array((string)$key, self::$policyFields, true)) {
  129 + $policy[$key] = $value;
  130 + }
  131 + }
  132 + return $policy;
  133 + }
  134 +
  135 + public function authorization($url, $body = null, $contentType = null)
  136 + {
  137 + $authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
  138 + return array('Authorization' => $authorization);
  139 + }
  140 +
  141 + public function authorizationV2($url, $method, $body = null, $contentType = null)
  142 + {
  143 + $urlItems = parse_url($url);
  144 + $host = $urlItems['host'];
  145 +
  146 + if (isset($urlItems['port'])) {
  147 + $port = $urlItems['port'];
  148 + } else {
  149 + $port = '';
  150 + }
  151 +
  152 + $path = $urlItems['path'];
  153 + if (isset($urlItems['query'])) {
  154 + $query = $urlItems['query'];
  155 + } else {
  156 + $query = '';
  157 + }
  158 +
  159 + //write request uri
  160 + $toSignStr = $method . ' ' . $path;
  161 + if (!empty($query)) {
  162 + $toSignStr .= '?' . $query;
  163 + }
  164 +
  165 + //write host and port
  166 + $toSignStr .= "\nHost: " . $host;
  167 + if (!empty($port)) {
  168 + $toSignStr .= ":" . $port;
  169 + }
  170 +
  171 + //write content type
  172 + if (!empty($contentType)) {
  173 + $toSignStr .= "\nContent-Type: " . $contentType;
  174 + }
  175 +
  176 + $toSignStr .= "\n\n";
  177 +
  178 + //write body
  179 + if (!empty($body)) {
  180 + $toSignStr .= $body;
  181 + }
  182 +
  183 + $sign = $this->sign($toSignStr);
  184 + $auth = 'Qiniu ' . $sign;
  185 + return array('Authorization' => $auth);
  186 + }
  187 +}
  1 +<?php
  2 +
  3 +namespace Qiniu\Cdn;
  4 +
  5 +use Qiniu\Auth;
  6 +use Qiniu\Http\Error;
  7 +use Qiniu\Http\Client;
  8 +
  9 +final class CdnManager
  10 +{
  11 +
  12 + private $auth;
  13 + private $server;
  14 +
  15 + public function __construct(Auth $auth)
  16 + {
  17 + $this->auth = $auth;
  18 + $this->server = 'http://fusion.qiniuapi.com';
  19 + }
  20 +
  21 + /**
  22 + * @param array $urls 待刷新的文件链接数组
  23 + * @return array
  24 + */
  25 + public function refreshUrls(array $urls)
  26 + {
  27 + return $this->refreshUrlsAndDirs($urls, array());
  28 + }
  29 +
  30 + /**
  31 + * @param array $dirs 待刷新的文件链接数组
  32 + * @return array
  33 + * 目前客户默认没有目录刷新权限,刷新会有400038报错,参考:https://developer.qiniu.com/fusion/api/1229/cache-refresh
  34 + * 需要刷新目录请工单联系技术支持 https://support.qiniu.com/tickets/category
  35 + */
  36 + public function refreshDirs(array $dirs)
  37 + {
  38 + return $this->refreshUrlsAndDirs(array(), $dirs);
  39 + }
  40 +
  41 + /**
  42 + * @param array $urls 待刷新的文件链接数组
  43 + * @param array $dirs 待刷新的目录链接数组
  44 + *
  45 + * @return array 刷新的请求回复和错误,参考 examples/cdn_manager.php 代码
  46 + * @link http://developer.qiniu.com/article/fusion/api/refresh.html
  47 + *
  48 + * 目前客户默认没有目录刷新权限,刷新会有400038报错,参考:https://developer.qiniu.com/fusion/api/1229/cache-refresh
  49 + * 需要刷新目录请工单联系技术支持 https://support.qiniu.com/tickets/category
  50 + */
  51 + public function refreshUrlsAndDirs(array $urls, array $dirs)
  52 + {
  53 + $req = array();
  54 + if (!empty($urls)) {
  55 + $req['urls'] = $urls;
  56 + }
  57 + if (!empty($dirs)) {
  58 + $req['dirs'] = $dirs;
  59 + }
  60 +
  61 + $url = $this->server . '/v2/tune/refresh';
  62 + $body = json_encode($req);
  63 + return $this->post($url, $body);
  64 + }
  65 +
  66 + /**
  67 + * @param array $urls 待预取的文件链接数组
  68 + *
  69 + * @return array 预取的请求回复和错误,参考 examples/cdn_manager.php 代码
  70 + *
  71 + * @link http://developer.qiniu.com/article/fusion/api/refresh.html
  72 + */
  73 + public function prefetchUrls(array $urls)
  74 + {
  75 + $req = array(
  76 + 'urls' => $urls,
  77 + );
  78 +
  79 + $url = $this->server . '/v2/tune/prefetch';
  80 + $body = json_encode($req);
  81 + return $this->post($url, $body);
  82 + }
  83 +
  84 + /**
  85 + * @param array $domains 待获取带宽数据的域名数组
  86 + * @param string $startDate 开始的日期,格式类似 2017-01-01
  87 + * @param string $endDate 结束的日期,格式类似 2017-01-01
  88 + * @param string $granularity 获取数据的时间间隔,可以是 5min, hour 或者 day
  89 + *
  90 + * @return array 带宽数据和错误信息,参考 examples/cdn_manager.php 代码
  91 + *
  92 + * @link http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
  93 + */
  94 + public function getBandwidthData(array $domains, $startDate, $endDate, $granularity)
  95 + {
  96 + $req = array();
  97 + $req['domains'] = implode(';', $domains);
  98 + $req['startDate'] = $startDate;
  99 + $req['endDate'] = $endDate;
  100 + $req['granularity'] = $granularity;
  101 +
  102 + $url = $this->server . '/v2/tune/bandwidth';
  103 + $body = json_encode($req);
  104 + return $this->post($url, $body);
  105 + }
  106 +
  107 + /**
  108 + * @param array $domains 待获取流量数据的域名数组
  109 + * @param string $startDate 开始的日期,格式类似 2017-01-01
  110 + * @param string $endDate 结束的日期,格式类似 2017-01-01
  111 + * @param string $granularity 获取数据的时间间隔,可以是 5min, hour 或者 day
  112 + *
  113 + * @return array 流量数据和错误信息,参考 examples/cdn_manager.php 代码
  114 + *
  115 + * @link http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
  116 + */
  117 + public function getFluxData(array $domains, $startDate, $endDate, $granularity)
  118 + {
  119 + $req = array();
  120 + $req['domains'] = implode(';', $domains);
  121 + $req['startDate'] = $startDate;
  122 + $req['endDate'] = $endDate;
  123 + $req['granularity'] = $granularity;
  124 +
  125 + $url = $this->server . '/v2/tune/flux';
  126 + $body = json_encode($req);
  127 + return $this->post($url, $body);
  128 + }
  129 +
  130 + /**
  131 + * @param array $domains 待获取日志下载链接的域名数组
  132 + * @param string $logDate 获取指定日期的日志下载链接,格式类似 2017-01-01
  133 + *
  134 + * @return array 日志下载链接数据和错误信息,参考 examples/cdn_manager.php 代码
  135 + *
  136 + * @link http://developer.qiniu.com/article/fusion/api/log.html
  137 + */
  138 + public function getCdnLogList(array $domains, $logDate)
  139 + {
  140 + $req = array();
  141 + $req['domains'] = implode(';', $domains);
  142 + $req['day'] = $logDate;
  143 +
  144 + $url = $this->server . '/v2/tune/log/list';
  145 + $body = json_encode($req);
  146 + return $this->post($url, $body);
  147 + }
  148 +
  149 + private function post($url, $body)
  150 + {
  151 + $headers = $this->auth->authorization($url, $body, 'application/json');
  152 + $headers['Content-Type'] = 'application/json';
  153 + $ret = Client::post($url, $body, $headers);
  154 + if (!$ret->ok()) {
  155 + return array(null, new Error($url, $ret));
  156 + }
  157 + $r = ($ret->body === null) ? array() : $ret->json();
  158 + return array($r, null);
  159 + }
  160 +
  161 + /**
  162 + * 构建时间戳防盗链鉴权的访问外链
  163 + *
  164 + * @param string $rawUrl 需要签名的资源url
  165 + * @param string $encryptKey 时间戳防盗链密钥
  166 + * @param string $durationInSeconds 链接的有效期(以秒为单位)
  167 + *
  168 + * @return string 带鉴权信息的资源外链,参考 examples/cdn_timestamp_antileech.php 代码
  169 + */
  170 + public static function createTimestampAntiLeechUrl($rawUrl, $encryptKey, $durationInSeconds)
  171 + {
  172 +
  173 + $parsedUrl = parse_url($rawUrl);
  174 +
  175 + $deadline = time() + $durationInSeconds;
  176 + $expireHex = dechex($deadline);
  177 + $path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '';
  178 +
  179 + $strToSign = $encryptKey . $path . $expireHex;
  180 + $signStr = md5($strToSign);
  181 +
  182 + if (isset($parsedUrl['query'])) {
  183 + $signedUrl = $rawUrl . '&sign=' . $signStr . '&t=' . $expireHex;
  184 + } else {
  185 + $signedUrl = $rawUrl . '?sign=' . $signStr . '&t=' . $expireHex;
  186 + }
  187 +
  188 + return $signedUrl;
  189 + }
  190 +}
  1 +<?php
  2 +namespace Qiniu;
  3 +
  4 +final class Config
  5 +{
  6 + const SDK_VER = '7.2.5';
  7 +
  8 + const BLOCK_SIZE = 4194304; //4*1024*1024 分块上传块大小,该参数为接口规格,不能修改
  9 +
  10 + const RSF_HOST = 'rsf.qiniu.com';
  11 + const API_HOST = 'api.qiniu.com';
  12 + const RS_HOST = 'rs.qiniu.com'; //RS Host
  13 + const UC_HOST = 'https://api.qiniu.com'; //UC Host
  14 + const RTCAPI_HOST = 'http://rtc.qiniuapi.com';
  15 + const RTCAPI_VERSION = 'v3';
  16 +
  17 + // Zone 空间对应的机房
  18 + public $zone;
  19 + //BOOL 是否使用https域名
  20 + public $useHTTPS;
  21 + //BOOL 是否使用CDN加速上传域名
  22 + public $useCdnDomains;
  23 + // Zone Cache
  24 + private $zoneCache;
  25 +
  26 + // 构造函数
  27 + public function __construct(Zone $z = null)
  28 + {
  29 + $this->zone = $z;
  30 + $this->useHTTPS = false;
  31 + $this->useCdnDomains = false;
  32 + $this->zoneCache = array();
  33 + }
  34 +
  35 + public function getUpHost($accessKey, $bucket)
  36 + {
  37 + $zone = $this->getZone($accessKey, $bucket);
  38 + if ($this->useHTTPS === true) {
  39 + $scheme = "https://";
  40 + } else {
  41 + $scheme = "http://";
  42 + }
  43 +
  44 + $host = $zone->srcUpHosts[0];
  45 + if ($this->useCdnDomains === true) {
  46 + $host = $zone->cdnUpHosts[0];
  47 + }
  48 +
  49 + return $scheme . $host;
  50 + }
  51 +
  52 + public function getUpBackupHost($accessKey, $bucket)
  53 + {
  54 + $zone = $this->getZone($accessKey, $bucket);
  55 + if ($this->useHTTPS === true) {
  56 + $scheme = "https://";
  57 + } else {
  58 + $scheme = "http://";
  59 + }
  60 +
  61 + $host = $zone->cdnUpHosts[0];
  62 + if ($this->useCdnDomains === true) {
  63 + $host = $zone->srcUpHosts[0];
  64 + }
  65 +
  66 + return $scheme . $host;
  67 + }
  68 +
  69 + public function getRsHost($accessKey, $bucket)
  70 + {
  71 + $zone = $this->getZone($accessKey, $bucket);
  72 +
  73 + if ($this->useHTTPS === true) {
  74 + $scheme = "https://";
  75 + } else {
  76 + $scheme = "http://";
  77 + }
  78 +
  79 + return $scheme . $zone->rsHost;
  80 + }
  81 +
  82 + public function getRsfHost($accessKey, $bucket)
  83 + {
  84 + $zone = $this->getZone($accessKey, $bucket);
  85 +
  86 + if ($this->useHTTPS === true) {
  87 + $scheme = "https://";
  88 + } else {
  89 + $scheme = "http://";
  90 + }
  91 +
  92 + return $scheme . $zone->rsfHost;
  93 + }
  94 +
  95 + public function getIovipHost($accessKey, $bucket)
  96 + {
  97 + $zone = $this->getZone($accessKey, $bucket);
  98 +
  99 + if ($this->useHTTPS === true) {
  100 + $scheme = "https://";
  101 + } else {
  102 + $scheme = "http://";
  103 + }
  104 +
  105 + return $scheme . $zone->iovipHost;
  106 + }
  107 +
  108 + public function getApiHost($accessKey, $bucket)
  109 + {
  110 + $zone = $this->getZone($accessKey, $bucket);
  111 +
  112 + if ($this->useHTTPS === true) {
  113 + $scheme = "https://";
  114 + } else {
  115 + $scheme = "http://";
  116 + }
  117 +
  118 + return $scheme . $zone->apiHost;
  119 + }
  120 +
  121 + private function getZone($accessKey, $bucket)
  122 + {
  123 + $cacheId = "$accessKey:$bucket";
  124 +
  125 + if (isset($this->zoneCache[$cacheId])) {
  126 + $zone = $this->zoneCache[$cacheId];
  127 + } elseif (isset($this->zone)) {
  128 + $zone = $this->zone;
  129 + $this->zoneCache[$cacheId] = $zone;
  130 + } else {
  131 + $zone = Zone::queryZone($accessKey, $bucket);
  132 + $this->zoneCache[$cacheId] = $zone;
  133 + }
  134 + return $zone;
  135 + }
  136 +}
  1 +<?php
  2 +
  3 +namespace Qiniu;
  4 +
  5 +use Qiniu\Config;
  6 +
  7 +final class Etag
  8 +{
  9 + private static function packArray($v, $a)
  10 + {
  11 + return call_user_func_array('pack', array_merge(array($v), (array)$a));
  12 + }
  13 +
  14 + private static function blockCount($fsize)
  15 + {
  16 + return intval(($fsize + (Config::BLOCK_SIZE - 1)) / Config::BLOCK_SIZE);
  17 + }
  18 +
  19 + private static function calcSha1($data)
  20 + {
  21 + $sha1Str = sha1($data, true);
  22 + $err = error_get_last();
  23 + if ($err !== null) {
  24 + return array(null, $err);
  25 + }
  26 + $byteArray = unpack('C*', $sha1Str);
  27 + return array($byteArray, null);
  28 + }
  29 +
  30 +
  31 + public static function sum($filename)
  32 + {
  33 + $fhandler = fopen($filename, 'r');
  34 + $err = error_get_last();
  35 + if ($err !== null) {
  36 + return array(null, $err);
  37 + }
  38 +
  39 + $fstat = fstat($fhandler);
  40 + $fsize = $fstat['size'];
  41 + if ((int)$fsize === 0) {
  42 + fclose($fhandler);
  43 + return array('Fto5o-5ea0sNMlW_75VgGJCv2AcJ', null);
  44 + }
  45 + $blockCnt = self::blockCount($fsize);
  46 + $sha1Buf = array();
  47 +
  48 + if ($blockCnt <= 1) {
  49 + array_push($sha1Buf, 0x16);
  50 + $fdata = fread($fhandler, Config::BLOCK_SIZE);
  51 + if ($err !== null) {
  52 + fclose($fhandler);
  53 + return array(null, $err);
  54 + }
  55 + list($sha1Code,) = self::calcSha1($fdata);
  56 + $sha1Buf = array_merge($sha1Buf, $sha1Code);
  57 + } else {
  58 + array_push($sha1Buf, 0x96);
  59 + $sha1BlockBuf = array();
  60 + for ($i = 0; $i < $blockCnt; $i++) {
  61 + $fdata = fread($fhandler, Config::BLOCK_SIZE);
  62 + list($sha1Code, $err) = self::calcSha1($fdata);
  63 + if ($err !== null) {
  64 + fclose($fhandler);
  65 + return array(null, $err);
  66 + }
  67 + $sha1BlockBuf = array_merge($sha1BlockBuf, $sha1Code);
  68 + }
  69 + $tmpData = self::packArray('C*', $sha1BlockBuf);
  70 + list($sha1Final,) = self::calcSha1($tmpData);
  71 + $sha1Buf = array_merge($sha1Buf, $sha1Final);
  72 + }
  73 + $etag = \Qiniu\base64_urlSafeEncode(self::packArray('C*', $sha1Buf));
  74 + return array($etag, null);
  75 + }
  76 +}
  1 +<?php
  2 +namespace Qiniu\Http;
  3 +
  4 +use Qiniu\Config;
  5 +use Qiniu\Http\Request;
  6 +use Qiniu\Http\Response;
  7 +
  8 +final class Client
  9 +{
  10 + public static function get($url, array $headers = array())
  11 + {
  12 + $request = new Request('GET', $url, $headers);
  13 + return self::sendRequest($request);
  14 + }
  15 +
  16 + public static function delete($url, array $headers = array())
  17 + {
  18 + $request = new Request('DELETE', $url, $headers);
  19 + return self::sendRequest($request);
  20 + }
  21 +
  22 + public static function post($url, $body, array $headers = array())
  23 + {
  24 + $request = new Request('POST', $url, $headers, $body);
  25 + return self::sendRequest($request);
  26 + }
  27 +
  28 + public static function multipartPost(
  29 + $url,
  30 + $fields,
  31 + $name,
  32 + $fileName,
  33 + $fileBody,
  34 + $mimeType = null,
  35 + array $headers = array()
  36 + ) {
  37 + $data = array();
  38 + $mimeBoundary = md5(microtime());
  39 +
  40 + foreach ($fields as $key => $val) {
  41 + array_push($data, '--' . $mimeBoundary);
  42 + array_push($data, "Content-Disposition: form-data; name=\"$key\"");
  43 + array_push($data, '');
  44 + array_push($data, $val);
  45 + }
  46 +
  47 + array_push($data, '--' . $mimeBoundary);
  48 + $finalMimeType = empty($mimeType) ? 'application/octet-stream' : $mimeType;
  49 + $finalFileName = self::escapeQuotes($fileName);
  50 + array_push($data, "Content-Disposition: form-data; name=\"$name\"; filename=\"$finalFileName\"");
  51 + array_push($data, "Content-Type: $finalMimeType");
  52 + array_push($data, '');
  53 + array_push($data, $fileBody);
  54 +
  55 + array_push($data, '--' . $mimeBoundary . '--');
  56 + array_push($data, '');
  57 +
  58 + $body = implode("\r\n", $data);
  59 + $contentType = 'multipart/form-data; boundary=' . $mimeBoundary;
  60 + $headers['Content-Type'] = $contentType;
  61 + $request = new Request('POST', $url, $headers, $body);
  62 + return self::sendRequest($request);
  63 + }
  64 +
  65 + private static function userAgent()
  66 + {
  67 + $sdkInfo = "QiniuPHP/" . Config::SDK_VER;
  68 +
  69 + $systemInfo = php_uname("s");
  70 + $machineInfo = php_uname("m");
  71 +
  72 + $envInfo = "($systemInfo/$machineInfo)";
  73 +
  74 + $phpVer = phpversion();
  75 +
  76 + $ua = "$sdkInfo $envInfo PHP/$phpVer";
  77 + return $ua;
  78 + }
  79 +
  80 + public static function sendRequest($request)
  81 + {
  82 + $t1 = microtime(true);
  83 + $ch = curl_init();
  84 + $options = array(
  85 + CURLOPT_USERAGENT => self::userAgent(),
  86 + CURLOPT_RETURNTRANSFER => true,
  87 + CURLOPT_SSL_VERIFYPEER => false,
  88 + CURLOPT_SSL_VERIFYHOST => false,
  89 + CURLOPT_HEADER => true,
  90 + CURLOPT_NOBODY => false,
  91 + CURLOPT_CUSTOMREQUEST => $request->method,
  92 + CURLOPT_URL => $request->url,
  93 + );
  94 +
  95 + // Handle open_basedir & safe mode
  96 + if (!ini_get('safe_mode') && !ini_get('open_basedir')) {
  97 + $options[CURLOPT_FOLLOWLOCATION] = true;
  98 + }
  99 +
  100 + if (!empty($request->headers)) {
  101 + $headers = array();
  102 + foreach ($request->headers as $key => $val) {
  103 + array_push($headers, "$key: $val");
  104 + }
  105 + $options[CURLOPT_HTTPHEADER] = $headers;
  106 + }
  107 + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
  108 +
  109 + if (!empty($request->body)) {
  110 + $options[CURLOPT_POSTFIELDS] = $request->body;
  111 + }
  112 + curl_setopt_array($ch, $options);
  113 + $result = curl_exec($ch);
  114 + $t2 = microtime(true);
  115 + $duration = round($t2 - $t1, 3);
  116 + $ret = curl_errno($ch);
  117 + if ($ret !== 0) {
  118 + $r = new Response(-1, $duration, array(), null, curl_error($ch));
  119 + curl_close($ch);
  120 + return $r;
  121 + }
  122 + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  123 + $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
  124 + $headers = self::parseHeaders(substr($result, 0, $header_size));
  125 + $body = substr($result, $header_size);
  126 + curl_close($ch);
  127 + return new Response($code, $duration, $headers, $body, null);
  128 + }
  129 +
  130 + private static function parseHeaders($raw)
  131 + {
  132 + $headers = array();
  133 + $headerLines = explode("\r\n", $raw);
  134 + foreach ($headerLines as $line) {
  135 + $headerLine = trim($line);
  136 + $kv = explode(':', $headerLine);
  137 + if (count($kv) > 1) {
  138 + $kv[0] =self::ucwordsHyphen($kv[0]);
  139 + $headers[$kv[0]] = trim($kv[1]);
  140 + }
  141 + }
  142 + return $headers;
  143 + }
  144 +
  145 + private static function escapeQuotes($str)
  146 + {
  147 + $find = array("\\", "\"");
  148 + $replace = array("\\\\", "\\\"");
  149 + return str_replace($find, $replace, $str);
  150 + }
  151 +
  152 + private static function ucwordsHyphen($str)
  153 + {
  154 + return str_replace('- ', '-', ucwords(str_replace('-', '- ', $str)));
  155 + }
  156 +}
  1 +<?php
  2 +namespace Qiniu\Http;
  3 +
  4 +/**
  5 + * 七牛业务请求逻辑错误封装类,主要用来解析API请求返回如下的内容:
  6 + * <pre>
  7 + * {"error" : "detailed error message"}
  8 + * </pre>
  9 + */
  10 +final class Error
  11 +{
  12 + private $url;
  13 + private $response;
  14 +
  15 + public function __construct($url, $response)
  16 + {
  17 + $this->url = $url;
  18 + $this->response = $response;
  19 + }
  20 +
  21 + public function code()
  22 + {
  23 + return $this->response->statusCode;
  24 + }
  25 +
  26 + public function getResponse()
  27 + {
  28 + return $this->response;
  29 + }
  30 +
  31 + public function message()
  32 + {
  33 + return $this->response->error;
  34 + }
  35 +}
  1 +<?php
  2 +namespace Qiniu\Http;
  3 +
  4 +final class Request
  5 +{
  6 + public $url;
  7 + public $headers;
  8 + public $body;
  9 + public $method;
  10 +
  11 + public function __construct($method, $url, array $headers = array(), $body = null)
  12 + {
  13 + $this->method = strtoupper($method);
  14 + $this->url = $url;
  15 + $this->headers = $headers;
  16 + $this->body = $body;
  17 + }
  18 +}
  1 +<?php
  2 +
  3 +namespace Qiniu\Http;
  4 +
  5 +/**
  6 + * HTTP response Object
  7 + */
  8 +final class Response
  9 +{
  10 + public $statusCode;
  11 + public $headers;
  12 + public $body;
  13 + public $error;
  14 + private $jsonData;
  15 + public $duration;
  16 +
  17 + /** @var array Mapping of status codes to reason phrases */
  18 + private static $statusTexts = array(
  19 + 100 => 'Continue',
  20 + 101 => 'Switching Protocols',
  21 + 102 => 'Processing',
  22 + 200 => 'OK',
  23 + 201 => 'Created',
  24 + 202 => 'Accepted',
  25 + 203 => 'Non-Authoritative Information',
  26 + 204 => 'No Content',
  27 + 205 => 'Reset Content',
  28 + 206 => 'Partial Content',
  29 + 207 => 'Multi-Status',
  30 + 208 => 'Already Reported',
  31 + 226 => 'IM Used',
  32 + 300 => 'Multiple Choices',
  33 + 301 => 'Moved Permanently',
  34 + 302 => 'Found',
  35 + 303 => 'See Other',
  36 + 304 => 'Not Modified',
  37 + 305 => 'Use Proxy',
  38 + 307 => 'Temporary Redirect',
  39 + 308 => 'Permanent Redirect',
  40 + 400 => 'Bad Request',
  41 + 401 => 'Unauthorized',
  42 + 402 => 'Payment Required',
  43 + 403 => 'Forbidden',
  44 + 404 => 'Not Found',
  45 + 405 => 'Method Not Allowed',
  46 + 406 => 'Not Acceptable',
  47 + 407 => 'Proxy Authentication Required',
  48 + 408 => 'Request Timeout',
  49 + 409 => 'Conflict',
  50 + 410 => 'Gone',
  51 + 411 => 'Length Required',
  52 + 412 => 'Precondition Failed',
  53 + 413 => 'Request Entity Too Large',
  54 + 414 => 'Request-URI Too Long',
  55 + 415 => 'Unsupported Media Type',
  56 + 416 => 'Requested Range Not Satisfiable',
  57 + 417 => 'Expectation Failed',
  58 + 422 => 'Unprocessable Entity',
  59 + 423 => 'Locked',
  60 + 424 => 'Failed Dependency',
  61 + 425 => 'Reserved for WebDAV advanced collections expired proposal',
  62 + 426 => 'Upgrade required',
  63 + 428 => 'Precondition Required',
  64 + 429 => 'Too Many Requests',
  65 + 431 => 'Request Header Fields Too Large',
  66 + 500 => 'Internal Server Error',
  67 + 501 => 'Not Implemented',
  68 + 502 => 'Bad Gateway',
  69 + 503 => 'Service Unavailable',
  70 + 504 => 'Gateway Timeout',
  71 + 505 => 'HTTP Version Not Supported',
  72 + 506 => 'Variant Also Negotiates (Experimental)',
  73 + 507 => 'Insufficient Storage',
  74 + 508 => 'Loop Detected',
  75 + 510 => 'Not Extended',
  76 + 511 => 'Network Authentication Required',
  77 + );
  78 +
  79 + /**
  80 + * @param int $code 状态码
  81 + * @param double $duration 请求时长
  82 + * @param array $headers 响应头部
  83 + * @param string $body 响应内容
  84 + * @param string $error 错误描述
  85 + */
  86 + public function __construct($code, $duration, array $headers = array(), $body = null, $error = null)
  87 + {
  88 + $this->statusCode = $code;
  89 + $this->duration = $duration;
  90 + $this->headers = $headers;
  91 + $this->body = $body;
  92 + $this->error = $error;
  93 + $this->jsonData = null;
  94 + if ($error !== null) {
  95 + return;
  96 + }
  97 +
  98 + if ($body === null) {
  99 + if ($code >= 400) {
  100 + $this->error = self::$statusTexts[$code];
  101 + }
  102 + return;
  103 + }
  104 + if (self::isJson($headers)) {
  105 + try {
  106 + $jsonData = self::bodyJson($body);
  107 + if ($code >= 400) {
  108 + $this->error = $body;
  109 + if ($jsonData['error'] !== null) {
  110 + $this->error = $jsonData['error'];
  111 + }
  112 + }
  113 + $this->jsonData = $jsonData;
  114 + } catch (\InvalidArgumentException $e) {
  115 + $this->error = $body;
  116 + if ($code >= 200 && $code < 300) {
  117 + $this->error = $e->getMessage();
  118 + }
  119 + }
  120 + } elseif ($code >= 400) {
  121 + $this->error = $body;
  122 + }
  123 + return;
  124 + }
  125 +
  126 + public function json()
  127 + {
  128 + return $this->jsonData;
  129 + }
  130 +
  131 + private static function bodyJson($body)
  132 + {
  133 + return \Qiniu\json_decode((string) $body, true, 512);
  134 + }
  135 +
  136 + public function xVia()
  137 + {
  138 + $via = $this->headers['X-Via'];
  139 + if ($via === null) {
  140 + $via = $this->headers['X-Px'];
  141 + }
  142 + if ($via === null) {
  143 + $via = $this->headers['Fw-Via'];
  144 + }
  145 + return $via;
  146 + }
  147 +
  148 + public function xLog()
  149 + {
  150 + return $this->headers['X-Log'];
  151 + }
  152 +
  153 + public function xReqId()
  154 + {
  155 + return $this->headers['X-Reqid'];
  156 + }
  157 +
  158 + public function ok()
  159 + {
  160 + return $this->statusCode >= 200 && $this->statusCode < 300 && $this->error === null;
  161 + }
  162 +
  163 + public function needRetry()
  164 + {
  165 + $code = $this->statusCode;
  166 + if ($code < 0 || ($code / 100 === 5 and $code !== 579) || $code === 996) {
  167 + return true;
  168 + }
  169 + }
  170 +
  171 + private static function isJson($headers)
  172 + {
  173 + return array_key_exists('Content-Type', $headers) &&
  174 + strpos($headers['Content-Type'], 'application/json') === 0;
  175 + }
  176 +}
  1 +<?php
  2 +namespace Qiniu\Processing;
  3 +
  4 +use Qiniu;
  5 +
  6 +/**
  7 + * 主要涉及图片链接拼接
  8 + *
  9 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
  10 + */
  11 +final class ImageUrlBuilder
  12 +{
  13 + /**
  14 + * mode合法范围值
  15 + *
  16 + * @var array
  17 + */
  18 + protected $modeArr = array(0, 1, 2, 3, 4, 5);
  19 +
  20 + /**
  21 + * format合法值
  22 + *
  23 + * @var array
  24 + */
  25 + protected $formatArr = array('psd', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'bmp');
  26 +
  27 + /**
  28 + * 水印图片位置合法值
  29 + *
  30 + * @var array
  31 + */
  32 + protected $gravityArr = array('NorthWest', 'North', 'NorthEast',
  33 + 'West', 'Center', 'East', 'SouthWest', 'South', 'SouthEast');
  34 +
  35 + /**
  36 + * 缩略图链接拼接
  37 + *
  38 + * @param string $url 图片链接
  39 + * @param int $mode 缩略模式
  40 + * @param int $width 宽度
  41 + * @param int $height 长度
  42 + * @param string $format 输出类型
  43 + * @param int $quality 图片质量
  44 + * @param int $interlace 是否支持渐进显示
  45 + * @param int $ignoreError 忽略结果
  46 + * @return string
  47 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
  48 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  49 + */
  50 + public function thumbnail(
  51 + $url,
  52 + $mode,
  53 + $width,
  54 + $height,
  55 + $format = null,
  56 + $interlace = null,
  57 + $quality = null,
  58 + $ignoreError = 1
  59 + ) {
  60 +
  61 + // url合法效验
  62 + if (!$this->isUrl($url)) {
  63 + return $url;
  64 + }
  65 +
  66 + // 参数合法性效验
  67 + if (!in_array(intval($mode), $this->modeArr, true)) {
  68 + return $url;
  69 + }
  70 +
  71 + if (!$width || !$height) {
  72 + return $url;
  73 + }
  74 +
  75 + $thumbStr = 'imageView2/' . $mode . '/w/' . $width . '/h/' . $height . '/';
  76 +
  77 + // 拼接输出格式
  78 + if (!is_null($format)
  79 + && in_array($format, $this->formatArr)
  80 + ) {
  81 + $thumbStr .= 'format/' . $format . '/';
  82 + }
  83 +
  84 + // 拼接渐进显示
  85 + if (!is_null($interlace)
  86 + && in_array(intval($interlace), array(0, 1), true)
  87 + ) {
  88 + $thumbStr .= 'interlace/' . $interlace . '/';
  89 + }
  90 +
  91 + // 拼接图片质量
  92 + if (!is_null($quality)
  93 + && intval($quality) >= 0
  94 + && intval($quality) <= 100
  95 + ) {
  96 + $thumbStr .= 'q/' . $quality . '/';
  97 + }
  98 +
  99 + $thumbStr .= 'ignore-error/' . $ignoreError . '/';
  100 +
  101 + // 如果有query_string用|线分割实现多参数
  102 + return $url . ($this->hasQuery($url) ? '|' : '?') . $thumbStr;
  103 + }
  104 +
  105 + /**
  106 + * 图片水印
  107 + *
  108 + * @param string $url 图片链接
  109 + * @param string $image 水印图片链接
  110 + * @param numeric $dissolve 透明度
  111 + * @param string $gravity 水印位置
  112 + * @param numeric $dx 横轴边距
  113 + * @param numeric $dy 纵轴边距
  114 + * @param numeric $watermarkScale 自适应原图的短边比例
  115 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html
  116 + * @return string
  117 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  118 + */
  119 + public function waterImg(
  120 + $url,
  121 + $image,
  122 + $dissolve = 100,
  123 + $gravity = 'SouthEast',
  124 + $dx = null,
  125 + $dy = null,
  126 + $watermarkScale = null
  127 + ) {
  128 + // url合法效验
  129 + if (!$this->isUrl($url)) {
  130 + return $url;
  131 + }
  132 +
  133 + $waterStr = 'watermark/1/image/' . \Qiniu\base64_urlSafeEncode($image) . '/';
  134 +
  135 + // 拼接水印透明度
  136 + if (is_numeric($dissolve)
  137 + && $dissolve <= 100
  138 + ) {
  139 + $waterStr .= 'dissolve/' . $dissolve . '/';
  140 + }
  141 +
  142 + // 拼接水印位置
  143 + if (in_array($gravity, $this->gravityArr, true)) {
  144 + $waterStr .= 'gravity/' . $gravity . '/';
  145 + }
  146 +
  147 + // 拼接横轴边距
  148 + if (!is_null($dx)
  149 + && is_numeric($dx)
  150 + ) {
  151 + $waterStr .= 'dx/' . $dx . '/';
  152 + }
  153 +
  154 + // 拼接纵轴边距
  155 + if (!is_null($dy)
  156 + && is_numeric($dy)
  157 + ) {
  158 + $waterStr .= 'dy/' . $dy . '/';
  159 + }
  160 +
  161 + // 拼接自适应原图的短边比例
  162 + if (!is_null($watermarkScale)
  163 + && is_numeric($watermarkScale)
  164 + && $watermarkScale > 0
  165 + && $watermarkScale < 1
  166 + ) {
  167 + $waterStr .= 'ws/' . $watermarkScale . '/';
  168 + }
  169 +
  170 + // 如果有query_string用|线分割实现多参数
  171 + return $url . ($this->hasQuery($url) ? '|' : '?') . $waterStr;
  172 + }
  173 +
  174 + /**
  175 + * 文字水印
  176 + *
  177 + * @param string $url 图片链接
  178 + * @param string $text 文字
  179 + * @param string $font 文字字体
  180 + * @param string $fontSize 文字字号
  181 + * @param string $fontColor 文字颜色
  182 + * @param numeric $dissolve 透明度
  183 + * @param string $gravity 水印位置
  184 + * @param numeric $dx 横轴边距
  185 + * @param numeric $dy 纵轴边距
  186 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html#text-watermark
  187 + * @return string
  188 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  189 + */
  190 + public function waterText(
  191 + $url,
  192 + $text,
  193 + $font = '黑体',
  194 + $fontSize = 0,
  195 + $fontColor = null,
  196 + $dissolve = 100,
  197 + $gravity = 'SouthEast',
  198 + $dx = null,
  199 + $dy = null
  200 + ) {
  201 + // url合法效验
  202 + if (!$this->isUrl($url)) {
  203 + return $url;
  204 + }
  205 +
  206 + $waterStr = 'watermark/2/text/'
  207 + . \Qiniu\base64_urlSafeEncode($text) . '/font/'
  208 + . \Qiniu\base64_urlSafeEncode($font) . '/';
  209 +
  210 + // 拼接文字大小
  211 + if (is_int($fontSize)) {
  212 + $waterStr .= 'fontsize/' . $fontSize . '/';
  213 + }
  214 +
  215 + // 拼接文字颜色
  216 + if (!is_null($fontColor)
  217 + && $fontColor
  218 + ) {
  219 + $waterStr .= 'fill/' . \Qiniu\base64_urlSafeEncode($fontColor) . '/';
  220 + }
  221 +
  222 + // 拼接水印透明度
  223 + if (is_numeric($dissolve)
  224 + && $dissolve <= 100
  225 + ) {
  226 + $waterStr .= 'dissolve/' . $dissolve . '/';
  227 + }
  228 +
  229 + // 拼接水印位置
  230 + if (in_array($gravity, $this->gravityArr, true)) {
  231 + $waterStr .= 'gravity/' . $gravity . '/';
  232 + }
  233 +
  234 + // 拼接横轴边距
  235 + if (!is_null($dx)
  236 + && is_numeric($dx)
  237 + ) {
  238 + $waterStr .= 'dx/' . $dx . '/';
  239 + }
  240 +
  241 + // 拼接纵轴边距
  242 + if (!is_null($dy)
  243 + && is_numeric($dy)
  244 + ) {
  245 + $waterStr .= 'dy/' . $dy . '/';
  246 + }
  247 +
  248 + // 如果有query_string用|线分割实现多参数
  249 + return $url . ($this->hasQuery($url) ? '|' : '?') . $waterStr;
  250 + }
  251 +
  252 + /**
  253 + * 效验url合法性
  254 + *
  255 + * @param string $url url链接
  256 + * @return string
  257 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  258 + */
  259 + protected function isUrl($url)
  260 + {
  261 + $urlArr = parse_url($url);
  262 +
  263 + return $urlArr['scheme']
  264 + && in_array($urlArr['scheme'], array('http', 'https'))
  265 + && $urlArr['host']
  266 + && $urlArr['path'];
  267 + }
  268 +
  269 + /**
  270 + * 检测是否有query
  271 + *
  272 + * @param string $url url链接
  273 + * @return string
  274 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  275 + */
  276 + protected function hasQuery($url)
  277 + {
  278 + $urlArr = parse_url($url);
  279 +
  280 + return !empty($urlArr['query']);
  281 + }
  282 +}
  1 +<?php
  2 +
  3 +namespace Qiniu\Processing;
  4 +
  5 +use Qiniu\Http\Client;
  6 +use Qiniu\Http\Error;
  7 +
  8 +final class Operation
  9 +{
  10 +
  11 + private $auth;
  12 + private $token_expire;
  13 + private $domain;
  14 +
  15 + public function __construct($domain, $auth = null, $token_expire = 3600)
  16 + {
  17 + $this->auth = $auth;
  18 + $this->domain = $domain;
  19 + $this->token_expire = $token_expire;
  20 + }
  21 +
  22 +
  23 + /**
  24 + * 对资源文件进行处理
  25 + *
  26 + * @param $key 待处理的资源文件名
  27 + * @param $fops string|array fop操作,多次fop操作以array的形式传入。
  28 + * eg. imageView2/1/w/200/h/200, imageMogr2/thumbnail/!75px
  29 + *
  30 + * @return array 文件处理后的结果及错误。
  31 + *
  32 + * @link http://developer.qiniu.com/docs/v6/api/reference/fop/
  33 + */
  34 + public function execute($key, $fops)
  35 + {
  36 + $url = $this->buildUrl($key, $fops);
  37 + $resp = Client::get($url);
  38 + if (!$resp->ok()) {
  39 + return array(null, new Error($url, $resp));
  40 + }
  41 + if ($resp->json() !== null) {
  42 + return array($resp->json(), null);
  43 + }
  44 + return array($resp->body, null);
  45 + }
  46 +
  47 + public function buildUrl($key, $fops, $protocol = 'http')
  48 + {
  49 + if (is_array($fops)) {
  50 + $fops = implode('|', $fops);
  51 + }
  52 +
  53 + $url = $protocol . "://$this->domain/$key?$fops";
  54 + if ($this->auth !== null) {
  55 + $url = $this->auth->privateDownloadUrl($url, $this->token_expire);
  56 + }
  57 +
  58 + return $url;
  59 + }
  60 +}
  1 +<?php
  2 +namespace Qiniu\Processing;
  3 +
  4 +use Qiniu\Config;
  5 +use Qiniu\Http\Client;
  6 +use Qiniu\Http\Error;
  7 +use Qiniu\Processing\Operation;
  8 +
  9 +/**
  10 + * 持久化处理类,该类用于主动触发异步持久化操作.
  11 + *
  12 + * @link http://developer.qiniu.com/docs/v6/api/reference/fop/pfop/pfop.html
  13 + */
  14 +final class PersistentFop
  15 +{
  16 + /**
  17 + * @var 账号管理密钥对,Auth对象
  18 + */
  19 + private $auth;
  20 +
  21 + /*
  22 + * @var 配置对象,Config 对象
  23 + * */
  24 + private $config;
  25 +
  26 +
  27 + public function __construct($auth, $config = null)
  28 + {
  29 + $this->auth = $auth;
  30 + if ($config == null) {
  31 + $this->config = new Config();
  32 + } else {
  33 + $this->config = $config;
  34 + }
  35 + }
  36 +
  37 + /**
  38 + * 对资源文件进行异步持久化处理
  39 + * @param $bucket 资源所在空间
  40 + * @param $key 待处理的源文件
  41 + * @param $fops string|array 待处理的pfop操作,多个pfop操作以array的形式传入。
  42 + * eg. avthumb/mp3/ab/192k, vframe/jpg/offset/7/w/480/h/360
  43 + * @param $pipeline 资源处理队列
  44 + * @param $notify_url 处理结果通知地址
  45 + * @param $force 是否强制执行一次新的指令
  46 + *
  47 + *
  48 + * @return array 返回持久化处理的persistentId, 和返回的错误。
  49 + *
  50 + * @link http://developer.qiniu.com/docs/v6/api/reference/fop/
  51 + */
  52 + public function execute($bucket, $key, $fops, $pipeline = null, $notify_url = null, $force = false)
  53 + {
  54 + if (is_array($fops)) {
  55 + $fops = implode(';', $fops);
  56 + }
  57 + $params = array('bucket' => $bucket, 'key' => $key, 'fops' => $fops);
  58 + \Qiniu\setWithoutEmpty($params, 'pipeline', $pipeline);
  59 + \Qiniu\setWithoutEmpty($params, 'notifyURL', $notify_url);
  60 + if ($force) {
  61 + $params['force'] = 1;
  62 + }
  63 + $data = http_build_query($params);
  64 + $scheme = "http://";
  65 + if ($this->config->useHTTPS === true) {
  66 + $scheme = "https://";
  67 + }
  68 + $url = $scheme . Config::API_HOST . '/pfop/';
  69 + $headers = $this->auth->authorization($url, $data, 'application/x-www-form-urlencoded');
  70 + $headers['Content-Type'] = 'application/x-www-form-urlencoded';
  71 + $response = Client::post($url, $data, $headers);
  72 + if (!$response->ok()) {
  73 + return array(null, new Error($url, $response));
  74 + }
  75 + $r = $response->json();
  76 + $id = $r['persistentId'];
  77 + return array($id, null);
  78 + }
  79 +
  80 + public function status($id)
  81 + {
  82 + $scheme = "http://";
  83 +
  84 + if ($this->config->useHTTPS === true) {
  85 + $scheme = "https://";
  86 + }
  87 + $url = $scheme . Config::API_HOST . "/status/get/prefop?id=$id";
  88 + $response = Client::get($url);
  89 + if (!$response->ok()) {
  90 + return array(null, new Error($url, $response));
  91 + }
  92 + return array($response->json(), null);
  93 + }
  94 +}
  1 +<?php
  2 +namespace Qiniu\Rtc;
  3 +
  4 +use Qiniu\Http\Client;
  5 +use Qiniu\Http\Error;
  6 +use Qiniu\Config;
  7 +use Qiniu\Auth;
  8 +
  9 +class AppClient
  10 +{
  11 + private $auth;
  12 + private $baseURL;
  13 +
  14 + public function __construct(Auth $auth)
  15 + {
  16 + $this->auth = $auth;
  17 +
  18 + $this->baseURL = sprintf("%s/%s/apps", Config::RTCAPI_HOST, Config::RTCAPI_VERSION);
  19 + }
  20 +
  21 + /*
  22 + * hub: 直播空间名
  23 + * title: app 的名称 注意,Title 不是唯一标识,重复 create 动作将生成多个 app
  24 + * maxUsers:人数限制
  25 + * NoAutoKickUser: bool 类型,可选,禁止自动踢人(抢流)。默认为 false ,
  26 + 即同一个身份的 client (app/room/user) ,新的连麦请求可以成功,旧连接被关闭。
  27 + */
  28 + public function createApp($hub, $title, $maxUsers = null, $noAutoKickUser = null)
  29 + {
  30 + $params['hub'] = $hub;
  31 + $params['title'] = $title;
  32 + if (!empty($maxUsers)) {
  33 + $params['maxUsers'] = $maxUsers;
  34 + }
  35 + if (!empty($noAutoKickUser)) {
  36 + $params['noAutoKickUser'] = $noAutoKickUser;
  37 + }
  38 + $body = json_encode($params);
  39 + $ret = $this->post($this->baseURL, $body);
  40 + return $ret;
  41 + }
  42 +
  43 + /*
  44 + * appId: app 的唯一标识,创建的时候由系统生成。
  45 + * Title: app 的名称, 可选。
  46 + * Hub: 绑定的直播 hub,可选,用于合流后 rtmp 推流。
  47 + * MaxUsers: int 类型,可选,连麦房间支持的最大在线人数。
  48 + * NoAutoKickUser: bool 类型,可选,禁止自动踢人。
  49 + * MergePublishRtmp: 连麦合流转推 RTMP 的配置,可选择。其详细配置包括如下
  50 + Enable: 布尔类型,用于开启和关闭所有房间的合流功能。
  51 + AudioOnly: 布尔类型,可选,指定是否只合成音频。
  52 + Height, Width: int64,可选,指定合流输出的高和宽,默认为 640 x 480。
  53 + OutputFps: int64,可选,指定合流输出的帧率,默认为 25 fps 。
  54 + OutputKbps: int64,可选,指定合流输出的码率,默认为 1000 。
  55 + URL: 合流后转推旁路直播的地址,可选,支持魔法变量配置按照连麦房间号生成不同的推流地址。如果是转推到七牛直播云,不建议使用该配置。
  56 + StreamTitle: 转推七牛直播云的流名,可选,支持魔法变量配置按照连麦房间号生成不同的流名。例如,配置 Hub 为 qn-zhibo ,配置 StreamTitle 为 $(roomName) ,则房间 meeting-001 的合流将会被转推到 rtmp://pili-publish.qn-zhibo.***.com/qn-zhibo/meeting-001地址。详细配置细则,请咨询七牛技术支持。
  57 + */
  58 + public function updateApp($appId, $hub, $title, $maxUsers = null, $mergePublishRtmp = null, $noAutoKickUser = null)
  59 + {
  60 + $url = $this->baseURL . '/' . $appId;
  61 + $params['hub'] = $hub;
  62 + $params['title'] = $title;
  63 + if (!empty($maxUsers)) {
  64 + $params['maxUsers'] = $maxUsers;
  65 + }
  66 + if (!empty($noAutoKickUser)) {
  67 + $params['noAutoKickUser'] = $noAutoKickUser;
  68 + }
  69 + if (!empty($mergePublishRtmp)) {
  70 + $params['mergePublishRtmp'] = $mergePublishRtmp;
  71 + }
  72 + $body = json_encode($params);
  73 + $ret = $this->post($url, $body);
  74 + return $ret;
  75 + }
  76 +
  77 + /*
  78 + * appId: app 的唯一标识,创建的时候由系统生成。
  79 + */
  80 + public function getApp($appId)
  81 + {
  82 + $url = $this->baseURL . '/' . $appId;
  83 + $ret = $this->get($url);
  84 + return $ret;
  85 + }
  86 +
  87 + /*
  88 + * appId: app 的唯一标识,创建的时候由系统生成
  89 + */
  90 + public function deleteApp($appId)
  91 + {
  92 + $url = $this->baseURL . '/' . $appId;
  93 + list(, $err) = $this->delete($url);
  94 + return $err;
  95 + }
  96 +
  97 + /*
  98 + * 获取房间的人数
  99 + * appId: app 的唯一标识,创建的时候由系统生成。
  100 + * roomName: 操作所查询的连麦房间。
  101 + */
  102 + public function listUser($appId, $roomName)
  103 + {
  104 + $url = sprintf("%s/%s/rooms/%s/users", $this->baseURL, $appId, $roomName);
  105 + $ret = $this->get($url);
  106 + return $ret;
  107 + }
  108 +
  109 + /*
  110 + * 踢出玩家
  111 + * appId: app 的唯一标识,创建的时候由系统生成。
  112 + * roomName: 连麦房间
  113 + * userId: 请求加入房间的用户ID
  114 + */
  115 + public function kickUser($appId, $roomName, $userId)
  116 + {
  117 + $url = sprintf("%s/%s/rooms/%s/users/%s", $this->baseURL, $appId, $roomName, $userId);
  118 + list(, $err) = $this->delete($url);
  119 + return $err;
  120 + }
  121 +
  122 + /*
  123 + * 获取房间的人数
  124 + * appId: app 的唯一标识,创建的时候由系统生成。
  125 + * prefix: 所查询房间名的前缀索引,可以为空。
  126 + * offset: int 类型,分页查询的位移标记。
  127 + * limit: int 类型,此次查询的最大长度。
  128 + * GET /v3/apps/<AppID>/rooms?prefix=<RoomNamePrefix>&offset=<Offset>&limit=<Limit>
  129 + */
  130 + public function listActiveRooms($appId, $prefix = null, $offset = null, $limit = null)
  131 + {
  132 + if (isset($prefix)) {
  133 + $query['prefix'] = $prefix;
  134 + }
  135 + if (isset($offset)) {
  136 + $query['offset'] = $offset;
  137 + }
  138 + if (isset($limit)) {
  139 + $query['limit'] = $limit;
  140 + }
  141 + if (isset($query) && !empty($query)) {
  142 + $query = '?' . http_build_query($query);
  143 + $url = sprintf("%s/%s/rooms%s", $this->baseURL, $appId, $query);
  144 + } else {
  145 + $url = sprintf("%s/%s/rooms", $this->baseURL, $appId);
  146 + }
  147 + $ret = $this->get($url);
  148 + return $ret;
  149 + }
  150 +
  151 + /*
  152 + * appId: app 的唯一标识,创建的时候由系统生成。
  153 + * roomName: 房间名称,需满足规格 ^[a-zA-Z0-9_-]{3,64}$
  154 + * userId: 请求加入房间的用户 ID,需满足规格 ^[a-zA-Z0-9_-]{3,50}$
  155 + * expireAt: int64 类型,鉴权的有效时间,传入以秒为单位的64位Unix
  156 + 绝对时间,token 将在该时间后失效。
  157 + * permission: 该用户的房间管理权限,"admin" 或 "user",默认为 "user" 。
  158 + 当权限角色为 "admin" 时,拥有将其他用户移除出房间等特权.
  159 + */
  160 + public function appToken($appId, $roomName, $userId, $expireAt, $permission)
  161 + {
  162 + $params['appId'] = $appId;
  163 + $params['userId'] = $userId;
  164 + $params['roomName'] = $roomName;
  165 + $params['permission'] = $permission;
  166 + $params['expireAt'] = $expireAt;
  167 + $appAccessString = json_encode($params);
  168 + return $this->auth->signWithData($appAccessString);
  169 + }
  170 +
  171 + private function get($url, $cType = null)
  172 + {
  173 + $rtcToken = $this->auth->authorizationV2($url, "GET", null, $cType);
  174 + $rtcToken['Content-Type'] = $cType;
  175 + $ret = Client::get($url, $rtcToken);
  176 + if (!$ret->ok()) {
  177 + return array(null, new Error($url, $ret));
  178 + }
  179 + return array($ret->json(), null);
  180 + }
  181 +
  182 + private function delete($url, $contentType = 'application/json')
  183 + {
  184 + $rtcToken = $this->auth->authorizationV2($url, "DELETE", null, $contentType);
  185 + $rtcToken['Content-Type'] = $contentType;
  186 + $ret = Client::delete($url, $rtcToken);
  187 + if (!$ret->ok()) {
  188 + return array(null, new Error($url, $ret));
  189 + }
  190 + return array($ret->json(), null);
  191 + }
  192 +
  193 + private function post($url, $body, $contentType = 'application/json')
  194 + {
  195 + $rtcToken = $this->auth->authorizationV2($url, "POST", $body, $contentType);
  196 + $rtcToken['Content-Type'] = $contentType;
  197 + $ret = Client::post($url, $body, $rtcToken);
  198 + if (!$ret->ok()) {
  199 + return array(null, new Error($url, $ret));
  200 + }
  201 + $r = ($ret->body === null) ? array() : $ret->json();
  202 + return array($r, null);
  203 + }
  204 +}
  1 +<?php
  2 +namespace Qiniu;
  3 +
  4 +use Qiniu\Http\Client;
  5 +use Qiniu\Http\Error;
  6 +
  7 +final class Zone
  8 +{
  9 +
  10 + //源站上传域名
  11 + public $srcUpHosts;
  12 + //CDN加速上传域名
  13 + public $cdnUpHosts;
  14 + //资源管理域名
  15 + public $rsHost;
  16 + //资源列举域名
  17 + public $rsfHost;
  18 + //资源处理域名
  19 + public $apiHost;
  20 + //IOVIP域名
  21 + public $iovipHost;
  22 +
  23 + //构造一个Zone对象
  24 + public function __construct(
  25 + $srcUpHosts = array(),
  26 + $cdnUpHosts = array(),
  27 + $rsHost = "rs.qiniu.com",
  28 + $rsfHost = "rsf.qiniu.com",
  29 + $apiHost = "api.qiniu.com",
  30 + $iovipHost = null
  31 + ) {
  32 +
  33 + $this->srcUpHosts = $srcUpHosts;
  34 + $this->cdnUpHosts = $cdnUpHosts;
  35 + $this->rsHost = $rsHost;
  36 + $this->rsfHost = $rsfHost;
  37 + $this->apiHost = $apiHost;
  38 + $this->iovipHost = $iovipHost;
  39 + }
  40 +
  41 + //华东机房
  42 + public static function zone0()
  43 + {
  44 + $Zone_z0 = new Zone(
  45 + array("up.qiniup.com", 'up-jjh.qiniup.com', 'up-xs.qiniup.com'),
  46 + array('upload.qiniup.com', 'upload-jjh.qiniup.com', 'upload-xs.qiniup.com'),
  47 + 'rs.qbox.me',
  48 + 'rsf.qbox.me',
  49 + 'api.qiniu.com',
  50 + 'iovip.qbox.me'
  51 + );
  52 + return $Zone_z0;
  53 + }
  54 +
  55 + //华北机房
  56 + public static function zone1()
  57 + {
  58 + $Zone_z1 = new Zone(
  59 + array('up-z1.qiniup.com'),
  60 + array('upload-z1.qiniup.com'),
  61 + "rs-z1.qbox.me",
  62 + "rsf-z1.qbox.me",
  63 + "api-z1.qiniu.com",
  64 + "iovip-z1.qbox.me"
  65 + );
  66 +
  67 + return $Zone_z1;
  68 + }
  69 +
  70 + //华南机房
  71 + public static function zone2()
  72 + {
  73 + $Zone_z2 = new Zone(
  74 + array('up-z2.qiniup.com', 'up-dg.qiniup.com', 'up-fs.qiniup.com'),
  75 + array('upload-z2.qiniup.com', 'upload-dg.qiniup.com', 'upload-fs.qiniup.com'),
  76 + "rs-z2.qbox.me",
  77 + "rsf-z2.qbox.me",
  78 + "api-z2.qiniu.com",
  79 + "iovip-z2.qbox.me"
  80 + );
  81 + return $Zone_z2;
  82 + }
  83 +
  84 + //北美机房
  85 + public static function zoneNa0()
  86 + {
  87 + //北美机房
  88 + $Zone_na0 = new Zone(
  89 + array('up-na0.qiniup.com'),
  90 + array('upload-na0.qiniup.com'),
  91 + "rs-na0.qbox.me",
  92 + "rsf-na0.qbox.me",
  93 + "api-na0.qiniu.com",
  94 + "iovip-na0.qbox.me"
  95 + );
  96 + return $Zone_na0;
  97 + }
  98 +
  99 + //新加坡机房
  100 + public static function zoneAs0()
  101 + {
  102 + //新加坡机房
  103 + $Zone_as0 = new Zone(
  104 + array('up-as0.qiniup.com'),
  105 + array('upload-as0.qiniup.com'),
  106 + "rs-as0.qbox.me",
  107 + "rsf-as0.qbox.me",
  108 + "api-as0.qiniu.com",
  109 + "iovip-as0.qbox.me"
  110 + );
  111 + return $Zone_as0;
  112 + }
  113 +
  114 + /*
  115 + * GET /v2/query?ak=<ak>&&bucket=<bucket>
  116 + **/
  117 + public static function queryZone($ak, $bucket)
  118 + {
  119 + $zone = new Zone();
  120 + $url = Config::UC_HOST . '/v2/query' . "?ak=$ak&bucket=$bucket";
  121 + $ret = Client::Get($url);
  122 + if (!$ret->ok()) {
  123 + return array(null, new Error($url, $ret));
  124 + }
  125 + $r = ($ret->body === null) ? array() : $ret->json();
  126 + //print_r($ret);
  127 + //parse zone;
  128 +
  129 + $iovipHost = $r['io']['src']['main'][0];
  130 + $zone->iovipHost = $iovipHost;
  131 + $accMain = $r['up']['acc']['main'][0];
  132 + array_push($zone->cdnUpHosts, $accMain);
  133 + if (isset($r['up']['acc']['backup'])) {
  134 + foreach ($r['up']['acc']['backup'] as $key => $value) {
  135 + array_push($zone->cdnUpHosts, $value);
  136 + }
  137 + }
  138 + $srcMain = $r['up']['src']['main'][0];
  139 + array_push($zone->srcUpHosts, $srcMain);
  140 + if (isset($r['up']['src']['backup'])) {
  141 + foreach ($r['up']['src']['backup'] as $key => $value) {
  142 + array_push($zone->srcUpHosts, $value);
  143 + }
  144 + }
  145 +
  146 + //set specific hosts
  147 + if (strstr($zone->iovipHost, "z1") !== false) {
  148 + $zone->rsHost = "rs-z1.qbox.me";
  149 + $zone->rsfHost = "rsf-z1.qbox.me";
  150 + $zone->apiHost = "api-z1.qiniu.com";
  151 + } elseif (strstr($zone->iovipHost, "z2") !== false) {
  152 + $zone->rsHost = "rs-z2.qbox.me";
  153 + $zone->rsfHost = "rsf-z2.qbox.me";
  154 + $zone->apiHost = "api-z2.qiniu.com";
  155 + } elseif (strstr($zone->iovipHost, "na0") !== false) {
  156 + $zone->rsHost = "rs-na0.qbox.me";
  157 + $zone->rsfHost = "rsf-na0.qbox.me";
  158 + $zone->apiHost = "api-na0.qiniu.com";
  159 + } elseif (strstr($zone->iovipHost, "as0") !== false) {
  160 + $zone->rsHost = "rs-as0.qbox.me";
  161 + $zone->rsfHost = "rsf-as0.qbox.me";
  162 + $zone->apiHost = "api-as0.qiniu.com";
  163 + } else {
  164 + $zone->rsHost = "rs.qbox.me";
  165 + $zone->rsfHost = "rsf.qbox.me";
  166 + $zone->apiHost = "api.qiniu.com";
  167 + }
  168 +
  169 + return $zone;
  170 + }
  171 +}
  1 +<?php
  2 +
  3 +namespace Qiniu;
  4 +
  5 +use Qiniu\Config;
  6 +
  7 +if (!defined('QINIU_FUNCTIONS_VERSION')) {
  8 + define('QINIU_FUNCTIONS_VERSION', Config::SDK_VER);
  9 +
  10 + /**
  11 + * 计算文件的crc32检验码:
  12 + *
  13 + * @param $file string 待计算校验码的文件路径
  14 + *
  15 + * @return string 文件内容的crc32校验码
  16 + */
  17 + function crc32_file($file)
  18 + {
  19 + $hash = hash_file('crc32b', $file);
  20 + $array = unpack('N', pack('H*', $hash));
  21 + return sprintf('%u', $array[1]);
  22 + }
  23 +
  24 + /**
  25 + * 计算输入流的crc32检验码
  26 + *
  27 + * @param $data 待计算校验码的字符串
  28 + *
  29 + * @return string 输入字符串的crc32校验码
  30 + */
  31 + function crc32_data($data)
  32 + {
  33 + $hash = hash('crc32b', $data);
  34 + $array = unpack('N', pack('H*', $hash));
  35 + return sprintf('%u', $array[1]);
  36 + }
  37 +
  38 + /**
  39 + * 对提供的数据进行urlsafe的base64编码。
  40 + *
  41 + * @param string $data 待编码的数据,一般为字符串
  42 + *
  43 + * @return string 编码后的字符串
  44 + * @link http://developer.qiniu.com/docs/v6/api/overview/appendix.html#urlsafe-base64
  45 + */
  46 + function base64_urlSafeEncode($data)
  47 + {
  48 + $find = array('+', '/');
  49 + $replace = array('-', '_');
  50 + return str_replace($find, $replace, base64_encode($data));
  51 + }
  52 +
  53 + /**
  54 + * 对提供的urlsafe的base64编码的数据进行解码
  55 + *
  56 + * @param string $str 待解码的数据,一般为字符串
  57 + *
  58 + * @return string 解码后的字符串
  59 + */
  60 + function base64_urlSafeDecode($str)
  61 + {
  62 + $find = array('-', '_');
  63 + $replace = array('+', '/');
  64 + return base64_decode(str_replace($find, $replace, $str));
  65 + }
  66 +
  67 + /**
  68 + * Wrapper for JSON decode that implements error detection with helpful
  69 + * error messages.
  70 + *
  71 + * @param string $json JSON data to parse
  72 + * @param bool $assoc When true, returned objects will be converted
  73 + * into associative arrays.
  74 + * @param int $depth User specified recursion depth.
  75 + *
  76 + * @return mixed
  77 + * @throws \InvalidArgumentException if the JSON cannot be parsed.
  78 + * @link http://www.php.net/manual/en/function.json-decode.php
  79 + */
  80 + function json_decode($json, $assoc = false, $depth = 512)
  81 + {
  82 + static $jsonErrors = array(
  83 + JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded',
  84 + JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch',
  85 + JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found',
  86 + JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON',
  87 + JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded'
  88 + );
  89 +
  90 + if (empty($json)) {
  91 + return null;
  92 + }
  93 + $data = \json_decode($json, $assoc, $depth);
  94 +
  95 + if (JSON_ERROR_NONE !== json_last_error()) {
  96 + $last = json_last_error();
  97 + throw new \InvalidArgumentException(
  98 + 'Unable to parse JSON data: '
  99 + . (isset($jsonErrors[$last])
  100 + ? $jsonErrors[$last]
  101 + : 'Unknown error')
  102 + );
  103 + }
  104 +
  105 + return $data;
  106 + }
  107 +
  108 + /**
  109 + * 计算七牛API中的数据格式
  110 + *
  111 + * @param $bucket 待操作的空间名
  112 + * @param $key 待操作的文件名
  113 + *
  114 + * @return string 符合七牛API规格的数据格式
  115 + * @link http://developer.qiniu.com/docs/v6/api/reference/data-formats.html
  116 + */
  117 + function entry($bucket, $key)
  118 + {
  119 + $en = $bucket;
  120 + if (!empty($key)) {
  121 + $en = $bucket . ':' . $key;
  122 + }
  123 + return base64_urlSafeEncode($en);
  124 + }
  125 +
  126 + /**
  127 + * array 辅助方法,无值时不set
  128 + *
  129 + * @param $array 待操作array
  130 + * @param $key key
  131 + * @param $value value 为null时 不设置
  132 + *
  133 + * @return array 原来的array,便于连续操作
  134 + */
  135 + function setWithoutEmpty(&$array, $key, $value)
  136 + {
  137 + if (!empty($value)) {
  138 + $array[$key] = $value;
  139 + }
  140 + return $array;
  141 + }
  142 +
  143 + /**
  144 + * 缩略图链接拼接
  145 + *
  146 + * @param string $url 图片链接
  147 + * @param int $mode 缩略模式
  148 + * @param int $width 宽度
  149 + * @param int $height 长度
  150 + * @param string $format 输出类型
  151 + * @param int $quality 图片质量
  152 + * @param int $interlace 是否支持渐进显示
  153 + * @param int $ignoreError 忽略结果
  154 + * @return string
  155 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
  156 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  157 + */
  158 + function thumbnail(
  159 + $url,
  160 + $mode,
  161 + $width,
  162 + $height,
  163 + $format = null,
  164 + $quality = null,
  165 + $interlace = null,
  166 + $ignoreError = 1
  167 + ) {
  168 +
  169 + static $imageUrlBuilder = null;
  170 + if (is_null($imageUrlBuilder)) {
  171 + $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
  172 + }
  173 +
  174 + return call_user_func_array(array($imageUrlBuilder, 'thumbnail'), func_get_args());
  175 + }
  176 +
  177 + /**
  178 + * 图片水印
  179 + *
  180 + * @param string $url 图片链接
  181 + * @param string $image 水印图片链接
  182 + * @param numeric $dissolve 透明度
  183 + * @param string $gravity 水印位置
  184 + * @param numeric $dx 横轴边距
  185 + * @param numeric $dy 纵轴边距
  186 + * @param numeric $watermarkScale 自适应原图的短边比例
  187 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html
  188 + * @return string
  189 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  190 + */
  191 + function waterImg(
  192 + $url,
  193 + $image,
  194 + $dissolve = 100,
  195 + $gravity = 'SouthEast',
  196 + $dx = null,
  197 + $dy = null,
  198 + $watermarkScale = null
  199 + ) {
  200 +
  201 + static $imageUrlBuilder = null;
  202 + if (is_null($imageUrlBuilder)) {
  203 + $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
  204 + }
  205 +
  206 + return call_user_func_array(array($imageUrlBuilder, 'waterImg'), func_get_args());
  207 + }
  208 +
  209 + /**
  210 + * 文字水印
  211 + *
  212 + * @param string $url 图片链接
  213 + * @param string $text 文字
  214 + * @param string $font 文字字体
  215 + * @param string $fontSize 文字字号
  216 + * @param string $fontColor 文字颜色
  217 + * @param numeric $dissolve 透明度
  218 + * @param string $gravity 水印位置
  219 + * @param numeric $dx 横轴边距
  220 + * @param numeric $dy 纵轴边距
  221 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html#text-watermark
  222 + * @return string
  223 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  224 + */
  225 + function waterText(
  226 + $url,
  227 + $text,
  228 + $font = '黑体',
  229 + $fontSize = 0,
  230 + $fontColor = null,
  231 + $dissolve = 100,
  232 + $gravity = 'SouthEast',
  233 + $dx = null,
  234 + $dy = null
  235 + ) {
  236 +
  237 + static $imageUrlBuilder = null;
  238 + if (is_null($imageUrlBuilder)) {
  239 + $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
  240 + }
  241 +
  242 + return call_user_func_array(array($imageUrlBuilder, 'waterText'), func_get_args());
  243 + }
  244 +
  245 + /**
  246 + * 从uptoken解析accessKey和bucket
  247 + *
  248 + * @param $upToken
  249 + * @return array(ak,bucket,err=null)
  250 + */
  251 + function explodeUpToken($upToken)
  252 + {
  253 + $items = explode(':', $upToken);
  254 + if (count($items) != 3) {
  255 + return array(null, null, "invalid uptoken");
  256 + }
  257 + $accessKey = $items[0];
  258 + $putPolicy = json_decode(base64_decode($items[2]));
  259 + $scope = $putPolicy->scope;
  260 + $scopeItems = explode(':', $scope);
  261 + $bucket = $scopeItems[0];
  262 + return array($accessKey, $bucket, null);
  263 + }
  264 +}