first commit

This commit is contained in:
2026-01-25 18:18:09 +08:00
commit 509312e604
8136 changed files with 2349298 additions and 0 deletions

285
vendor/qiniu/php-sdk/src/Qiniu/Auth.php vendored Normal file
View File

@ -0,0 +1,285 @@
<?php
namespace Qiniu;
use Qiniu\Http\Header;
use Qiniu\Zone;
final class Auth
{
private $accessKey;
private $secretKey;
public $options;
public function __construct($accessKey, $secretKey, $options = null)
{
$this->accessKey = $accessKey;
$this->secretKey = $secretKey;
$defaultOptions = array(
'disableQiniuTimestampSignature' => null
);
if ($options == null) {
$options = $defaultOptions;
}
$this->options = array_merge($defaultOptions, $options);
}
public function getAccessKey()
{
return $this->accessKey;
}
public function sign($data)
{
$hmac = hash_hmac('sha1', $data, $this->secretKey, true);
return $this->accessKey . ':' . \Qiniu\base64_urlSafeEncode($hmac);
}
public function signWithData($data)
{
$encodedData = \Qiniu\base64_urlSafeEncode($data);
return $this->sign($encodedData) . ':' . $encodedData;
}
public function signRequest($urlString, $body, $contentType = null)
{
$url = parse_url($urlString);
$data = '';
if (array_key_exists('path', $url)) {
$data = $url['path'];
}
if (array_key_exists('query', $url)) {
$data .= '?' . $url['query'];
}
$data .= "\n";
if ($body !== null && $contentType === 'application/x-www-form-urlencoded') {
$data .= $body;
}
return $this->sign($data);
}
/**
* @param string $urlString
* @param string $method
* @param string $body
* @param null|Header $headers
*/
public function signQiniuAuthorization($urlString, $method = "GET", $body = "", $headers = null)
{
$url = parse_url($urlString);
if (!$url) {
return array(null, new \Exception("parse_url error"));
}
// append method, path and query
if ($method === "") {
$data = "GET ";
} else {
$data = $method . " ";
}
if (isset($url["path"])) {
$data .= $url["path"];
}
if (isset($url["query"])) {
$data .= "?" . $url["query"];
}
// append Host
$data .= "\n";
$data .= "Host: ";
if (isset($url["host"])) {
$data .= $url["host"];
}
if (isset($url["port"]) && $url["port"] > 0) {
$data .= ":" . $url["port"];
}
// try to append content type
if ($headers != null && isset($headers["Content-Type"])) {
// append content type
$data .= "\n";
$data .= "Content-Type: " . $headers["Content-Type"];
}
// try append xQiniuHeaders
if ($headers != null) {
$headerLines = array();
$keyPrefix = "X-Qiniu-";
foreach ($headers as $k => $v) {
if (strlen($k) > strlen($keyPrefix) && strpos($k, $keyPrefix) === 0) {
array_push(
$headerLines,
$k . ": " . $v
);
}
}
if (count($headerLines) > 0) {
$data .= "\n";
sort($headerLines);
$data .= implode("\n", $headerLines);
}
}
// append body
$data .= "\n\n";
if (!is_null($body)
&& strlen($body) > 0
&& isset($headers["Content-Type"])
&& $headers["Content-Type"] != "application/octet-stream"
) {
$data .= $body;
}
return array($this->sign($data), null);
}
public function verifyCallback(
$contentType,
$originAuthorization,
$url,
$body,
$method = "GET",
$headers = array()
) {
if (strpos($originAuthorization, 'Qiniu') === 0) {
$qnHeaders = new Header($headers);
if (!isset($qnHeaders['Content-Type'])) {
$qnHeaders['Content-Type'] = $contentType;
}
list($sign, $err) = $this->signQiniuAuthorization(
$url,
$method,
$body,
$qnHeaders
);
if ($err !== null) {
return false;
}
$authorization = 'Qiniu ' . $sign;
} else {
$authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
}
return $originAuthorization === $authorization;
}
public function privateDownloadUrl($baseUrl, $expires = 3600)
{
$deadline = time() + $expires;
$pos = strpos($baseUrl, '?');
if ($pos !== false) {
$baseUrl .= '&e=';
} else {
$baseUrl .= '?e=';
}
$baseUrl .= $deadline;
$token = $this->sign($baseUrl);
return "$baseUrl&token=$token";
}
public function uploadToken($bucket, $key = null, $expires = 3600, $policy = null, $strictPolicy = true)
{
$deadline = time() + $expires;
$scope = $bucket;
if ($key !== null) {
$scope .= ':' . $key;
}
$args = self::copyPolicy($args, $policy, $strictPolicy);
$args['scope'] = $scope;
$args['deadline'] = $deadline;
$b = json_encode($args);
return $this->signWithData($b);
}
/**
*上传策略,参数规格详见
*http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html
*/
private static $policyFields = array(
'callbackUrl',
'callbackBody',
'callbackHost',
'callbackBodyType',
'callbackFetchKey',
'returnUrl',
'returnBody',
'endUser',
'saveKey',
'forceSaveKey',
'insertOnly',
'detectMime',
'mimeLimit',
'fsizeMin',
'fsizeLimit',
'persistentOps', // 与 persistentWorkflowTemplateID 二选一
'persistentNotifyUrl',
'persistentPipeline',
'persistentType', // 为 `1` 时开启闲时任务
'persistentWorkflowTemplateID', // 与 persistentOps 二选一
'deleteAfterDays',
'fileType',
'isPrefixalScope',
'transform', // deprecated
'transformFallbackKey', // deprecated
'transformFallbackMode', // deprecated
);
private static function copyPolicy(&$policy, $originPolicy, $strictPolicy)
{
if ($originPolicy === null) {
return array();
}
foreach ($originPolicy as $key => $value) {
if (!$strictPolicy || in_array((string)$key, self::$policyFields, true)) {
$policy[$key] = $value;
}
}
return $policy;
}
public function authorization($url, $body = null, $contentType = null)
{
$authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
return array('Authorization' => $authorization);
}
public function authorizationV2($url, $method, $body = null, $contentType = null)
{
$headers = new Header();
$result = array();
if ($contentType != null) {
$headers['Content-Type'] = $contentType;
$result['Content-Type'] = $contentType;
}
$signDate = gmdate('Ymd\THis\Z', time());
if ($this->options['disableQiniuTimestampSignature'] !== null) {
if (!$this->options['disableQiniuTimestampSignature']) {
$headers['X-Qiniu-Date'] = $signDate;
$result['X-Qiniu-Date'] = $signDate;
}
} elseif (getenv("DISABLE_QINIU_TIMESTAMP_SIGNATURE")) {
if (strtolower(getenv("DISABLE_QINIU_TIMESTAMP_SIGNATURE")) !== "true") {
$headers['X-Qiniu-Date'] = $signDate;
$result['X-Qiniu-Date'] = $signDate;
}
} else {
$headers['X-Qiniu-Date'] = $signDate;
$result['X-Qiniu-Date'] = $signDate;
}
list($sign) = $this->signQiniuAuthorization($url, $method, $body, $headers);
$result['Authorization'] = 'Qiniu ' . $sign;
return $result;
}
}

View File

@ -0,0 +1,263 @@
<?php
namespace Qiniu\Cdn;
use Qiniu\Auth;
use Qiniu\Http\Error;
use Qiniu\Http\Client;
use Qiniu\Http\Proxy;
final class CdnManager
{
private $auth;
private $server;
private $proxy;
public function __construct(Auth $auth, $proxy = null, $proxy_auth = null, $proxy_user_password = null)
{
$this->auth = $auth;
$this->server = 'http://fusion.qiniuapi.com';
$this->proxy = new Proxy($proxy, $proxy_auth, $proxy_user_password);
}
/**
* @param array $urls 待刷新的文件链接数组
* @return array
*/
public function refreshUrls(array $urls)
{
return $this->refreshUrlsAndDirs($urls, array());
}
/**
* @param array $dirs 待刷新的文件链接数组
* @return array
* 目前客户默认没有目录刷新权限刷新会有400038报错参考https://developer.qiniu.com/fusion/api/1229/cache-refresh
* 需要刷新目录请工单联系技术支持 https://support.qiniu.com/tickets/category
*/
public function refreshDirs(array $dirs)
{
return $this->refreshUrlsAndDirs(array(), $dirs);
}
/**
* @param array $urls 待刷新的文件链接数组
* @param array $dirs 待刷新的目录链接数组
*
* @return array 刷新的请求回复和错误,参考 examples/cdn_manager.php 代码
* @link http://developer.qiniu.com/article/fusion/api/refresh.html
*
* 目前客户默认没有目录刷新权限刷新会有400038报错参考https://developer.qiniu.com/fusion/api/1229/cache-refresh
* 需要刷新目录请工单联系技术支持 https://support.qiniu.com/tickets/category
*/
public function refreshUrlsAndDirs(array $urls, array $dirs)
{
$req = array();
if (!empty($urls)) {
$req['urls'] = $urls;
}
if (!empty($dirs)) {
$req['dirs'] = $dirs;
}
$url = $this->server . '/v2/tune/refresh';
$body = json_encode($req);
return $this->post($url, $body);
}
/**
* 查询 CDN 刷新记录
*
* @param string $requestId 指定要查询记录所在的刷新请求id
* @param string $isDir 指定是否查询目录,取值为 yes/no默认不填则为两种类型记录都查询
* @param array $urls 要查询的url列表每个url可以是文件url也可以是目录url
* @param string $state 指定要查询记录的状态取值processingsuccessfailure
* @param int $pageNo 要求返回的页号默认为0
* @param int $pageSize 要求返回的页长度默认为100
* @param string $startTime 指定查询的开始日期格式2006-01-01
* @param string $endTime 指定查询的结束日期格式2006-01-01
* @return array
* @link https://developer.qiniu.com/fusion/api/1229/cache-refresh#4
*/
public function getCdnRefreshList(
$requestId = null,
$isDir = null,
$urls = array(),
$state = null,
$pageNo = 0,
$pageSize = 100,
$startTime = null,
$endTime = null
) {
$req = array();
\Qiniu\setWithoutEmpty($req, 'requestId', $requestId);
\Qiniu\setWithoutEmpty($req, 'isDir', $isDir);
\Qiniu\setWithoutEmpty($req, 'urls', $urls);
\Qiniu\setWithoutEmpty($req, 'state', $state);
\Qiniu\setWithoutEmpty($req, 'pageNo', $pageNo);
\Qiniu\setWithoutEmpty($req, 'pageSize', $pageSize);
\Qiniu\setWithoutEmpty($req, 'startTime', $startTime);
\Qiniu\setWithoutEmpty($req, 'endTime', $endTime);
$body = json_encode($req);
$url = $this->server . '/v2/tune/refresh/list';
return $this->post($url, $body);
}
/**
* @param array $urls 待预取的文件链接数组
*
* @return array 预取的请求回复和错误,参考 examples/cdn_manager.php 代码
*
* @link http://developer.qiniu.com/article/fusion/api/refresh.html
*/
public function prefetchUrls(array $urls)
{
$req = array(
'urls' => $urls,
);
$url = $this->server . '/v2/tune/prefetch';
$body = json_encode($req);
return $this->post($url, $body);
}
/**
* 查询 CDN 预取记录
*
* @param string $requestId 指定要查询记录所在的刷新请求id
* @param array $urls 要查询的url列表每个url可以是文件url也可以是目录url
* @param string $state 指定要查询记录的状态取值processingsuccessfailure
* @param int $pageNo 要求返回的页号默认为0
* @param int $pageSize 要求返回的页长度默认为100
* @param string $startTime 指定查询的开始日期格式2006-01-01
* @param string $endTime 指定查询的结束日期格式2006-01-01
* @return array
* @link https://developer.qiniu.com/fusion/api/1227/file-prefetching#4
*/
public function getCdnPrefetchList(
$requestId = null,
$urls = array(),
$state = null,
$pageNo = 0,
$pageSize = 100,
$startTime = null,
$endTime = null
) {
$req = array();
\Qiniu\setWithoutEmpty($req, 'requestId', $requestId);
\Qiniu\setWithoutEmpty($req, 'urls', $urls);
\Qiniu\setWithoutEmpty($req, 'state', $state);
\Qiniu\setWithoutEmpty($req, 'pageNo', $pageNo);
\Qiniu\setWithoutEmpty($req, 'pageSize', $pageSize);
\Qiniu\setWithoutEmpty($req, 'startTime', $startTime);
\Qiniu\setWithoutEmpty($req, 'endTime', $endTime);
$body = json_encode($req);
$url = $this->server . '/v2/tune/prefetch/list';
return $this->post($url, $body);
}
/**
* @param array $domains 待获取带宽数据的域名数组
* @param string $startDate 开始的日期,格式类似 2017-01-01
* @param string $endDate 结束的日期,格式类似 2017-01-01
* @param string $granularity 获取数据的时间间隔,可以是 5min, hour 或者 day
*
* @return array 带宽数据和错误信息,参考 examples/cdn_manager.php 代码
*
* @link http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
*/
public function getBandwidthData(array $domains, $startDate, $endDate, $granularity)
{
$req = array();
$req['domains'] = implode(';', $domains);
$req['startDate'] = $startDate;
$req['endDate'] = $endDate;
$req['granularity'] = $granularity;
$url = $this->server . '/v2/tune/bandwidth';
$body = json_encode($req);
return $this->post($url, $body);
}
/**
* @param array $domains 待获取流量数据的域名数组
* @param string $startDate 开始的日期,格式类似 2017-01-01
* @param string $endDate 结束的日期,格式类似 2017-01-01
* @param string $granularity 获取数据的时间间隔,可以是 5min, hour 或者 day
*
* @return array 流量数据和错误信息,参考 examples/cdn_manager.php 代码
*
* @link http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
*/
public function getFluxData(array $domains, $startDate, $endDate, $granularity)
{
$req = array();
$req['domains'] = implode(';', $domains);
$req['startDate'] = $startDate;
$req['endDate'] = $endDate;
$req['granularity'] = $granularity;
$url = $this->server . '/v2/tune/flux';
$body = json_encode($req);
return $this->post($url, $body);
}
/**
* @param array $domains 待获取日志下载链接的域名数组
* @param string $logDate 获取指定日期的日志下载链接,格式类似 2017-01-01
*
* @return array 日志下载链接数据和错误信息,参考 examples/cdn_manager.php 代码
*
* @link http://developer.qiniu.com/article/fusion/api/log.html
*/
public function getCdnLogList(array $domains, $logDate)
{
$req = array();
$req['domains'] = implode(';', $domains);
$req['day'] = $logDate;
$url = $this->server . '/v2/tune/log/list';
$body = json_encode($req);
return $this->post($url, $body);
}
private function post($url, $body)
{
$headers = $this->auth->authorization($url, $body, 'application/json');
$headers['Content-Type'] = 'application/json';
$ret = Client::post($url, $body, $headers, $this->proxy->makeReqOpt());
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
return array($r, null);
}
/**
* 构建时间戳防盗链鉴权的访问外链
*
* @param string $rawUrl 需要签名的资源url
* @param string $encryptKey 时间戳防盗链密钥
* @param string $durationInSeconds 链接的有效期(以秒为单位)
*
* @return string 带鉴权信息的资源外链,参考 examples/cdn_timestamp_antileech.php 代码
*/
public static function createTimestampAntiLeechUrl($rawUrl, $encryptKey, $durationInSeconds)
{
$parsedUrl = parse_url($rawUrl);
$deadline = time() + $durationInSeconds;
$expireHex = dechex($deadline);
$path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '';
$strToSign = $encryptKey . $path . $expireHex;
$signStr = md5($strToSign);
if (isset($parsedUrl['query'])) {
$signedUrl = $rawUrl . '&sign=' . $signStr . '&t=' . $expireHex;
} else {
$signedUrl = $rawUrl . '?sign=' . $signStr . '&t=' . $expireHex;
}
return $signedUrl;
}
}

View File

@ -0,0 +1,398 @@
<?php
namespace Qiniu;
final class Config
{
const SDK_VER = '7.14.0';
const BLOCK_SIZE = 4194304; //4*1024*1024 分块上传块大小,该参数为接口规格,不能修改
const RSF_HOST = 'rsf.qiniuapi.com';
const API_HOST = 'api.qiniuapi.com';
const RS_HOST = 'rs.qiniuapi.com'; // RS Host
const UC_HOST = 'uc.qiniuapi.com'; // UC Host
const QUERY_REGION_HOST = Config::UC_HOST;
const RTCAPI_HOST = 'http://rtc.qiniuapi.com';
const ARGUS_HOST = 'ai.qiniuapi.com';
const CASTER_HOST = 'pili-caster.qiniuapi.com';
const SMS_HOST = "https://sms.qiniuapi.com";
const RTCAPI_VERSION = 'v3';
const SMS_VERSION = 'v1';
// Zone 空间对应的存储区域
public $region;
//BOOL 是否使用https域名
public $useHTTPS;
//BOOL 是否使用CDN加速上传域名
public $useCdnDomains;
/**
* @var Region
*/
public $zone;
// Zone Cache
private $regionCache;
// UC Host
private $ucHost;
private $queryRegionHost;
// backup UC Hosts
private $backupQueryRegionHosts;
// backup UC Hosts max retry time
public $backupUcHostsRetryTimes;
// 构造函数
public function __construct(Region $z = null)
{
$this->zone = $z;
$this->useHTTPS = false;
$this->useCdnDomains = false;
$this->regionCache = array();
$this->ucHost = Config::UC_HOST;
$this->queryRegionHost = Config::QUERY_REGION_HOST;
$this->backupQueryRegionHosts = array(
"kodo-config.qiniuapi.com",
"uc.qbox.me",
);
$this->backupUcHostsRetryTimes = 2;
}
public function setUcHost($ucHost)
{
$this->ucHost = $ucHost;
$this->setQueryRegionHost($ucHost);
}
public function getUcHost()
{
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return $scheme . $this->ucHost;
}
public function setQueryRegionHost($host, $backupHosts = array())
{
$this->queryRegionHost = $host;
$this->backupQueryRegionHosts = $backupHosts;
}
public function getQueryRegionHost()
{
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return $scheme . $this->queryRegionHost;
}
public function setBackupQueryRegionHosts($hosts = array())
{
$this->backupQueryRegionHosts = $hosts;
}
public function getBackupQueryRegionHosts()
{
return $this->backupQueryRegionHosts;
}
public function getUpHost($accessKey, $bucket, $reqOpt = null)
{
$region = $this->getRegion($accessKey, $bucket, $reqOpt);
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
$host = $region->srcUpHosts[0];
if ($this->useCdnDomains === true) {
$host = $region->cdnUpHosts[0];
}
return $scheme . $host;
}
public function getUpHostV2($accessKey, $bucket, $reqOpt = null)
{
list($region, $err) = $this->getRegionV2($accessKey, $bucket, $reqOpt);
if ($err != null) {
return array(null, $err);
}
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
$host = $region->srcUpHosts[0];
if ($this->useCdnDomains === true) {
$host = $region->cdnUpHosts[0];
}
return array($scheme . $host, null);
}
public function getUpBackupHost($accessKey, $bucket, $reqOpt = null)
{
$region = $this->getRegion($accessKey, $bucket, $reqOpt);
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
$host = $region->cdnUpHosts[0];
if ($this->useCdnDomains === true) {
$host = $region->srcUpHosts[0];
}
return $scheme . $host;
}
public function getUpBackupHostV2($accessKey, $bucket, $reqOpt = null)
{
list($region, $err) = $this->getRegionV2($accessKey, $bucket, $reqOpt);
if ($err != null) {
return array(null, $err);
}
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
$host = $region->cdnUpHosts[0];
if ($this->useCdnDomains === true) {
$host = $region->srcUpHosts[0];
}
return array($scheme . $host, null);
}
public function getRsHost($accessKey, $bucket, $reqOpt = null)
{
$region = $this->getRegion($accessKey, $bucket, $reqOpt);
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return $scheme . $region->rsHost;
}
public function getRsHostV2($accessKey, $bucket, $reqOpt = null)
{
list($region, $err) = $this->getRegionV2($accessKey, $bucket, $reqOpt);
if ($err != null) {
return array(null, $err);
}
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return array($scheme . $region->rsHost, null);
}
public function getRsfHost($accessKey, $bucket, $reqOpt = null)
{
$region = $this->getRegion($accessKey, $bucket, $reqOpt);
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return $scheme . $region->rsfHost;
}
public function getRsfHostV2($accessKey, $bucket, $reqOpt = null)
{
list($region, $err) = $this->getRegionV2($accessKey, $bucket, $reqOpt);
if ($err != null) {
return array(null, $err);
}
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return array($scheme . $region->rsfHost, null);
}
public function getIovipHost($accessKey, $bucket, $reqOpt = null)
{
$region = $this->getRegion($accessKey, $bucket, $reqOpt);
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return $scheme . $region->iovipHost;
}
public function getIovipHostV2($accessKey, $bucket, $reqOpt = null)
{
list($region, $err) = $this->getRegionV2($accessKey, $bucket, $reqOpt);
if ($err != null) {
return array(null, $err);
}
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return array($scheme . $region->iovipHost, null);
}
public function getApiHost($accessKey, $bucket, $reqOpt = null)
{
$region = $this->getRegion($accessKey, $bucket, $reqOpt);
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return $scheme . $region->apiHost;
}
public function getApiHostV2($accessKey, $bucket, $reqOpt = null)
{
list($region, $err) = $this->getRegionV2($accessKey, $bucket, $reqOpt);
if ($err != null) {
return array(null, $err);
}
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return array($scheme . $region->apiHost, null);
}
/**
* 从缓存中获取区域
*
* @param string $cacheId 缓存 ID
* @return null|Region
*/
private function getRegionCache($cacheId)
{
if (isset($this->regionCache[$cacheId]) &&
isset($this->regionCache[$cacheId]["deadline"]) &&
time() < $this->regionCache[$cacheId]["deadline"]) {
return $this->regionCache[$cacheId]["region"];
}
return null;
}
/**
* 将区域设置到缓存中
*
* @param string $cacheId 缓存 ID
* @param Region $region 缓存 ID
* @return void
*/
private function setRegionCache($cacheId, $region)
{
$this->regionCache[$cacheId] = array(
"region" => $region,
);
if (isset($region->ttl)) {
$this->regionCache[$cacheId]["deadline"] = time() + $region->ttl;
}
}
/**
* 从缓存中获取区域
*
* @param string $accessKey
* @param string $bucket
* @return Region
*
* @throws \Exception
*/
private function getRegion($accessKey, $bucket, $reqOpt = null)
{
if (isset($this->zone)) {
return $this->zone;
}
$cacheId = "$accessKey:$bucket";
$regionCache = $this->getRegionCache($cacheId);
if ($regionCache) {
return $regionCache;
}
$region = Zone::queryZone(
$accessKey,
$bucket,
$this->getQueryRegionHost(),
$this->getBackupQueryRegionHosts(),
$this->backupUcHostsRetryTimes,
$reqOpt
);
if (is_array($region)) {
list($region, $err) = $region;
if ($err != null) {
throw new \Exception($err->message());
}
}
$this->setRegionCache($cacheId, $region);
return $region;
}
private function getRegionV2($accessKey, $bucket, $reqOpt = null)
{
if (isset($this->zone)) {
return array($this->zone, null);
}
$cacheId = "$accessKey:$bucket";
$regionCache = $this->getRegionCache($cacheId);
if (isset($regionCache)) {
return array($regionCache, null);
}
$region = Zone::queryZone(
$accessKey,
$bucket,
$this->getQueryRegionHost(),
$this->getBackupQueryRegionHosts(),
$this->backupUcHostsRetryTimes,
$reqOpt
);
if (is_array($region)) {
list($region, $err) = $region;
return array($region, $err);
}
$this->setRegionCache($cacheId, $region);
return array($region, null);
}
}

View File

@ -0,0 +1,53 @@
<?php
// @codingStandardsIgnoreStart
// phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
namespace Qiniu\Enum;
use MyCLabs\Enum\Enum;
if (method_exists("MyCLabs\\Enum\\Enum", "from")) {
abstract class QiniuEnum extends Enum
{
// @codingStandardsIgnoreEnd
// @codingStandardsIgnoreStart
}
} else {
/**
* poly fill MyCLabs\Enum\Enum::from in low version
*
* @link https://github.com/myclabs/php-enum
*/
abstract class QiniuEnum extends Enum
{
// @codingStandardsIgnoreEnd
/**
* @param mixed $value
* @return static
*/
public static function from($value)
{
$key = self::assertValidValueReturningKey($value);
return self::__callStatic($key, array());
}
/**
* Asserts valid enum value
*
* @psalm-pure
* @psalm-assert T $value
* @param mixed $value
* @return string
*/
private static function assertValidValueReturningKey($value)
{
if (false === ($key = self::search($value))) {
throw new \UnexpectedValueException("Value '$value' is not part of the enum " . __CLASS__);
}
return $key;
}
// @codingStandardsIgnoreStart
}
}

View File

@ -0,0 +1,9 @@
<?php
namespace Qiniu\Enum;
final class SplitUploadVersion extends QiniuEnum
{
const V1 = 'v1';
const V2 = 'v2';
}

76
vendor/qiniu/php-sdk/src/Qiniu/Etag.php vendored Normal file
View File

@ -0,0 +1,76 @@
<?php
namespace Qiniu;
use Qiniu\Config;
final class Etag
{
private static function packArray($v, $a)
{
return call_user_func_array('pack', array_merge(array($v), (array)$a));
}
private static function blockCount($fsize)
{
return intval(($fsize + (Config::BLOCK_SIZE - 1)) / Config::BLOCK_SIZE);
}
private static function calcSha1($data)
{
$sha1Str = sha1($data, true);
$err = error_get_last();
if ($err !== null) {
return array(null, $err);
}
$byteArray = unpack('C*', $sha1Str);
return array($byteArray, null);
}
public static function sum($filename)
{
$fhandler = fopen($filename, 'r');
$err = error_get_last();
if ($err !== null) {
return array(null, $err);
}
$fstat = fstat($fhandler);
$fsize = $fstat['size'];
if ((int)$fsize === 0) {
fclose($fhandler);
return array('Fto5o-5ea0sNMlW_75VgGJCv2AcJ', null);
}
$blockCnt = self::blockCount($fsize);
$sha1Buf = array();
if ($blockCnt <= 1) {
array_push($sha1Buf, 0x16);
$fdata = fread($fhandler, Config::BLOCK_SIZE);
if ($err !== null) {
fclose($fhandler);
return array(null, $err);
}
list($sha1Code,) = self::calcSha1($fdata);
$sha1Buf = array_merge($sha1Buf, $sha1Code);
} else {
array_push($sha1Buf, 0x96);
$sha1BlockBuf = array();
for ($i = 0; $i < $blockCnt; $i++) {
$fdata = fread($fhandler, Config::BLOCK_SIZE);
list($sha1Code, $err) = self::calcSha1($fdata);
if ($err !== null) {
fclose($fhandler);
return array(null, $err);
}
$sha1BlockBuf = array_merge($sha1BlockBuf, $sha1Code);
}
$tmpData = self::packArray('C*', $sha1BlockBuf);
list($sha1Final,) = self::calcSha1($tmpData);
$sha1Buf = array_merge($sha1Buf, $sha1Final);
}
$etag = \Qiniu\base64_urlSafeEncode(self::packArray('C*', $sha1Buf));
return array($etag, null);
}
}

View File

@ -0,0 +1,198 @@
<?php
namespace Qiniu\Http;
use Qiniu\Config;
use Qiniu\Http\Middleware;
final class Client
{
/**
* @param $url
* @param array $headers
* @param RequestOptions $opt
* @return Response
*/
public static function get($url, array $headers = array(), $opt = null)
{
$request = new Request('GET', $url, $headers, null, $opt);
return self::sendRequestWithMiddleware($request);
}
/**
* @param $url
* @param array $headers
* @param array $opt detail see {@see Request::$opt}
* @return Response
*/
public static function delete($url, array $headers = array(), $opt = null)
{
$request = new Request('DELETE', $url, $headers, null, $opt);
return self::sendRequest($request);
}
/**
* @param $url
* @param $body
* @param array $headers
* @param RequestOptions $opt
* @return Response
*/
public static function post($url, $body, array $headers = array(), $opt = null)
{
$request = new Request('POST', $url, $headers, $body, $opt);
return self::sendRequest($request);
}
/**
* @param $url
* @param $body
* @param array $headers
* @param RequestOptions $opt
* @return Response
*/
public static function PUT($url, $body, array $headers = array(), $opt = null)
{
$request = new Request('PUT', $url, $headers, $body, $opt);
return self::sendRequest($request);
}
/**
* @param $url
* @param array $fields
* @param string $name
* @param string $fileName
* @param $fileBody
* @param null $mimeType
* @param array $headers
* @param RequestOptions $opt
* @return Response
*/
public static function multipartPost(
$url,
$fields,
$name,
$fileName,
$fileBody,
$mimeType = null,
$headers = array(),
$opt = null
) {
$data = array();
$mimeBoundary = md5(microtime());
foreach ($fields as $key => $val) {
array_push($data, '--' . $mimeBoundary);
array_push($data, "Content-Disposition: form-data; name=\"$key\"");
array_push($data, '');
array_push($data, $val);
}
array_push($data, '--' . $mimeBoundary);
$finalMimeType = empty($mimeType) ? 'application/octet-stream' : $mimeType;
$finalFileName = self::escapeQuotes($fileName);
array_push($data, "Content-Disposition: form-data; name=\"$name\"; filename=\"$finalFileName\"");
array_push($data, "Content-Type: $finalMimeType");
array_push($data, '');
array_push($data, $fileBody);
array_push($data, '--' . $mimeBoundary . '--');
array_push($data, '');
$body = implode("\r\n", $data);
$contentType = 'multipart/form-data; boundary=' . $mimeBoundary;
$headers['Content-Type'] = $contentType;
$request = new Request('POST', $url, $headers, $body, $opt);
return self::sendRequest($request);
}
private static function userAgent()
{
$sdkInfo = "QiniuPHP/" . Config::SDK_VER;
$systemInfo = php_uname("s");
$machineInfo = php_uname("m");
$envInfo = "($systemInfo/$machineInfo)";
$phpVer = phpversion();
$ua = "$sdkInfo $envInfo PHP/$phpVer";
return $ua;
}
/**
* @param Request $request
* @return Response
*/
public static function sendRequestWithMiddleware($request)
{
$middlewares = $request->opt->middlewares;
$handle = Middleware\compose($middlewares, function ($req) {
return Client::sendRequest($req);
});
return $handle($request);
}
/**
* @param Request $request
* @return Response
*/
public static function sendRequest($request)
{
$t1 = microtime(true);
$ch = curl_init();
$options = array(
CURLOPT_USERAGENT => self::userAgent(),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_CUSTOMREQUEST => $request->method,
CURLOPT_URL => $request->url,
);
foreach ($request->opt->getCurlOpt() as $k => $v) {
$options[$k] = $v;
}
// Handle open_basedir & safe mode
if (!ini_get('safe_mode') && !ini_get('open_basedir')) {
$options[CURLOPT_FOLLOWLOCATION] = true;
}
if (!empty($request->headers)) {
$headers = array();
foreach ($request->headers as $key => $val) {
array_push($headers, "$key: $val");
}
$options[CURLOPT_HTTPHEADER] = $headers;
}
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
if (!empty($request->body)) {
$options[CURLOPT_POSTFIELDS] = $request->body;
}
curl_setopt_array($ch, $options);
$result = curl_exec($ch);
$t2 = microtime(true);
$duration = round($t2 - $t1, 3);
$ret = curl_errno($ch);
if ($ret !== 0) {
$r = new Response(-1, $duration, array(), null, curl_error($ch));
curl_close($ch);
return $r;
}
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = Header::parseRawText(substr($result, 0, $header_size));
$body = substr($result, $header_size);
curl_close($ch);
return new Response($code, $duration, $headers, $body, null);
}
private static function escapeQuotes($str)
{
if (is_null($str)) {
return null;
}
$find = array("\\", "\"");
$replace = array("\\\\", "\\\"");
return str_replace($find, $replace, $str);
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace Qiniu\Http;
/**
* 七牛业务请求逻辑错误封装类主要用来解析API请求返回如下的内容
* <pre>
* {"error" : "detailed error message"}
* </pre>
*/
final class Error
{
private $url;
/**
* @var Response
*/
private $response;
public function __construct($url, $response)
{
$this->url = $url;
$this->response = $response;
}
public function code()
{
return $this->response->statusCode;
}
public function getResponse()
{
return $this->response;
}
public function message()
{
return $this->response->error;
}
}

View File

@ -0,0 +1,291 @@
<?php
namespace Qiniu\Http;
/**
* field name case-insensitive Header
*/
class Header implements \ArrayAccess, \IteratorAggregate, \Countable
{
/** @var array normalized key name map */
private $data = array();
/**
* @param array $obj non-normalized header object
*/
public function __construct($obj = array())
{
foreach ($obj as $key => $values) {
$normalizedKey = self::normalizeKey($key);
$normalizedValues = array();
if (!is_array($values)) {
array_push(
$normalizedValues,
self::normalizeValue($values)
);
} else {
foreach ($values as $value) {
array_push(
$normalizedValues,
self::normalizeValue($value)
);
}
}
$this->data[$normalizedKey] = $normalizedValues;
}
return $this;
}
/**
* return origin headers, which is field name case-sensitive
*
* @param string $raw
*
* @return array
*/
public static function parseRawText($raw)
{
$multipleHeaders = explode("\r\n\r\n", trim($raw));
$headers = array();
$headerLines = explode("\r\n", end($multipleHeaders));
foreach ($headerLines as $line) {
$headerLine = trim($line);
$kv = explode(':', $headerLine);
if (count($kv) <= 1) {
continue;
}
// for http2 [Pseudo-Header Fields](https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.1)
if ($kv[0] == "") {
$fieldName = ":" . $kv[1];
} else {
$fieldName = $kv[0];
}
$fieldValue = trim(substr($headerLine, strlen($fieldName . ":")));
if (isset($headers[$fieldName])) {
array_push($headers[$fieldName], $fieldValue);
} else {
$headers[$fieldName] = array($fieldValue);
}
}
return $headers;
}
/**
* @param string $raw
*
* @return Header
*/
public static function fromRawText($raw)
{
return new Header(self::parseRawText($raw));
}
/**
* @param string $key
*
* @return string
*/
public static function normalizeKey($key)
{
$key = trim($key);
if (!self::isValidKeyName($key)) {
return $key;
}
return \Qiniu\ucwords(strtolower($key), '-');
}
/**
* @param string|numeric $value
*
* @return string|numeric
*/
public static function normalizeValue($value)
{
if (is_numeric($value)) {
return $value + 0;
}
return trim($value);
}
/**
* @return array
*/
public function getRawData()
{
return $this->data;
}
/**
* @param $offset string
*
* @return boolean
*/
#[\ReturnTypeWillChange] // temporarily suppress the type check of php 8.x
public function offsetExists($offset)
{
$key = self::normalizeKey($offset);
return isset($this->data[$key]);
}
/**
* @param $offset string
*
* @return string|null
*/
#[\ReturnTypeWillChange] // temporarily suppress the type check of php 8.x
public function offsetGet($offset)
{
$key = self::normalizeKey($offset);
if (isset($this->data[$key]) && count($this->data[$key])) {
return $this->data[$key][0];
} else {
return null;
}
}
/**
* @param $offset string
* @param $value string
*
* @return void
*/
#[\ReturnTypeWillChange] // temporarily suppress the type check of php 8.x
public function offsetSet($offset, $value)
{
$key = self::normalizeKey($offset);
if (isset($this->data[$key]) && count($this->data[$key]) > 0) {
$this->data[$key][0] = self::normalizeValue($value);
} else {
$this->data[$key] = array(self::normalizeValue($value));
}
}
/**
* @return void
*/
#[\ReturnTypeWillChange] // temporarily suppress the type check of php 8.x
public function offsetUnset($offset)
{
$key = self::normalizeKey($offset);
unset($this->data[$key]);
}
/**
* @return \ArrayIterator
*/
#[\ReturnTypeWillChange] // temporarily suppress the type check of php 8.x
public function getIterator()
{
$arr = array();
foreach ($this->data as $k => $v) {
$arr[$k] = $v[0];
}
return new \ArrayIterator($arr);
}
/**
* @return int
*/
#[\ReturnTypeWillChange] // temporarily suppress the type check of php 8.x
public function count()
{
return count($this->data);
}
private static $isTokenTable = array(
'!' => true,
'#' => true,
'$' => true,
'%' => true,
'&' => true,
'\'' => true,
'*' => true,
'+' => true,
'-' => true,
'.' => true,
'0' => true,
'1' => true,
'2' => true,
'3' => true,
'4' => true,
'5' => true,
'6' => true,
'7' => true,
'8' => true,
'9' => true,
'A' => true,
'B' => true,
'C' => true,
'D' => true,
'E' => true,
'F' => true,
'G' => true,
'H' => true,
'I' => true,
'J' => true,
'K' => true,
'L' => true,
'M' => true,
'N' => true,
'O' => true,
'P' => true,
'Q' => true,
'R' => true,
'S' => true,
'T' => true,
'U' => true,
'W' => true,
'V' => true,
'X' => true,
'Y' => true,
'Z' => true,
'^' => true,
'_' => true,
'`' => true,
'a' => true,
'b' => true,
'c' => true,
'd' => true,
'e' => true,
'f' => true,
'g' => true,
'h' => true,
'i' => true,
'j' => true,
'k' => true,
'l' => true,
'm' => true,
'n' => true,
'o' => true,
'p' => true,
'q' => true,
'r' => true,
's' => true,
't' => true,
'u' => true,
'v' => true,
'w' => true,
'x' => true,
'y' => true,
'z' => true,
'|' => true,
'~' => true,
);
/**
* @param string $str
*
* @return boolean
*/
private static function isValidKeyName($str)
{
for ($i = 0; $i < strlen($str); $i += 1) {
if (!isset(self::$isTokenTable[$str[$i]])) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Qiniu\Http\Middleware;
use Qiniu\Http\Request;
use Qiniu\Http\Response;
interface Middleware
{
/**
* @param Request $request
* @param callable(Request): Response $next
* @return Response
*/
public function send($request, $next);
}
/**
* @param array<Middleware> $middlewares
* @param callable(Request): Response $handler
* @return callable(Request): Response
*/
function compose($middlewares, $handler)
{
$next = $handler;
foreach (array_reverse($middlewares) as $middleware) {
$next = function ($request) use ($middleware, $next) {
return $middleware->send($request, $next);
};
}
return $next;
}

View File

@ -0,0 +1,76 @@
<?php
namespace Qiniu\Http\Middleware;
use Qiniu\Http\Request;
use Qiniu\Http\Response;
class RetryDomainsMiddleware implements Middleware
{
/**
* @var array<string> backup domains.
*/
private $backupDomains;
/**
* @var numeric max retry times for each backup domains.
*/
private $maxRetryTimes;
/**
* @var callable args response and request; returns bool; If true will retry with backup domains.
*/
private $retryCondition;
/**
* @param array<string> $backupDomains
* @param numeric $maxRetryTimes
*/
public function __construct($backupDomains, $maxRetryTimes = 2, $retryCondition = null)
{
$this->backupDomains = $backupDomains;
$this->maxRetryTimes = $maxRetryTimes;
$this->retryCondition = $retryCondition;
}
private function shouldRetry($resp, $req)
{
if (is_callable($this->retryCondition)) {
return call_user_func($this->retryCondition, $resp, $req);
}
return !$resp || $resp->needRetry();
}
/**
* @param Request $request
* @param callable(Request): Response $next
* @return Response
*/
public function send($request, $next)
{
$response = null;
$urlComponents = parse_url($request->url);
foreach (array_merge(array($urlComponents["host"]), $this->backupDomains) as $backupDomain) {
$urlComponents["host"] = $backupDomain;
$request->url = \Qiniu\unparse_url($urlComponents);
$retriedTimes = 0;
while ($retriedTimes < $this->maxRetryTimes) {
$response = $next($request);
$retriedTimes += 1;
if (!$this->shouldRetry($response, $request)) {
return $response;
}
}
}
if (!$response) {
$response = $next($request);
}
return $response;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Qiniu\Http;
use Qiniu\Http\RequestOptions;
final class Proxy
{
private $proxy;
private $proxy_auth;
private $proxy_user_password;
public function __construct($proxy = null, $proxy_auth = null, $proxy_user_password = null)
{
$this->proxy = $proxy;
$this->proxy_auth = $proxy_auth;
$this->proxy_user_password = $proxy_user_password;
}
public function makeReqOpt()
{
$reqOpt = new RequestOptions();
if ($this->proxy !== null) {
$reqOpt->proxy = $this->proxy;
}
if ($this->proxy_auth !== null) {
$reqOpt->proxy_auth = $this->proxy_auth;
}
if ($this->proxy_user_password !== null) {
$reqOpt->proxy_user_password = $this->proxy_user_password;
}
return $reqOpt;
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace Qiniu\Http;
final class Request
{
/**
* @var string
*/
public $url;
/**
* @var array<string, string>
*/
public $headers;
/**
* @var mixed|null
*/
public $body;
/**
* @var string
*/
public $method;
/**
* @var RequestOptions
*/
public $opt;
public function __construct($method, $url, array $headers = array(), $body = null, $opt = null)
{
$this->method = strtoupper($method);
$this->url = $url;
$this->headers = $headers;
$this->body = $body;
if ($opt === null) {
$opt = new RequestOptions();
}
$this->opt = $opt;
}
}

View File

@ -0,0 +1,104 @@
<?php
namespace Qiniu\Http;
use Qiniu\Http\Middleware\Middleware;
final class RequestOptions
{
/**
* @var int|null
* http 请求的超时时间单位默认0不超时
*/
public $connection_timeout;
/**
* @var int|null
* http 请求的超时时间单位毫秒默认0不超时
*/
public $connection_timeout_ms;
/**
* @var int|null
* http 请求的超时时间单位默认0不超时
*/
public $timeout;
/**
* @var int|null
* http 请求的超时时间单位毫秒默认0不超时
*/
public $timeout_ms;
/**
* @var string|null
* 代理URL默认
*/
public $proxy;
/**
* @var int|null
* 代理鉴权方式,默认:空
*/
public $proxy_auth;
/**
* @var string|null
* 代理鉴权参数,默认:空
*/
public $proxy_user_password;
/**
* @var array<Middleware>
*/
public $middlewares;
public function __construct(
$connection_timeout = null,
$connection_timeout_ms = null,
$timeout = null,
$timeout_ms = null,
$middlewares = array(),
$proxy = null,
$proxy_auth = null,
$proxy_user_password = null
) {
$this->connection_timeout = $connection_timeout;
$this->connection_timeout_ms = $connection_timeout_ms;
$this->timeout = $timeout;
$this->timeout_ms = $timeout_ms;
$this->proxy = $proxy;
$this->proxy_auth = $proxy_auth;
$this->proxy_user_password = $proxy_user_password;
$this->middlewares = $middlewares;
}
public function getCurlOpt()
{
$result = array();
if ($this->connection_timeout != null) {
$result[CURLOPT_CONNECTTIMEOUT] = $this->connection_timeout;
}
if ($this->connection_timeout_ms != null) {
$result[CURLOPT_CONNECTTIMEOUT_MS] = $this->connection_timeout_ms;
}
if ($this->timeout != null) {
$result[CURLOPT_TIMEOUT] = $this->timeout;
}
if ($this->timeout_ms != null) {
$result[CURLOPT_TIMEOUT_MS] = $this->timeout_ms;
}
if ($this->proxy != null) {
$result[CURLOPT_PROXY] = $this->proxy;
}
if ($this->proxy_auth != null) {
$result[CURLOPT_PROXYAUTH] = $this->proxy_auth;
}
if ($this->proxy_user_password != null) {
$result[CURLOPT_PROXYUSERPWD] = $this->proxy_user_password;
}
return $result;
}
}

View File

@ -0,0 +1,220 @@
<?php
namespace Qiniu\Http;
/**
* HTTP response Object
*/
final class Response
{
public $statusCode;
/**
* deprecated because of field names case-sensitive.
* use $normalizedHeaders instead which field names are case-insensitive.
* but be careful not to use $normalizedHeaders with `array_*` functions,
* such as `array_key_exists`, `array_keys`, `array_values`.
*
* use `isset` instead of `array_key_exists`,
* and should never use `array_key_exists` at http header.
*
* use `foreach` instead of `array_keys`, `array_values`.
*
* @deprecated
*/
public $headers;
public $normalizedHeaders;
public $body;
public $error;
private $jsonData;
public $duration;
/** @var array Mapping of status codes to reason phrases */
private static $statusTexts = array(
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-Status',
208 => 'Already Reported',
226 => 'IM Used',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
307 => 'Temporary Redirect',
308 => 'Permanent Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
422 => 'Unprocessable Entity',
423 => 'Locked',
424 => 'Failed Dependency',
425 => 'Reserved for WebDAV advanced collections expired proposal',
426 => 'Upgrade required',
428 => 'Precondition Required',
429 => 'Too Many Requests',
431 => 'Request Header Fields Too Large',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
506 => 'Variant Also Negotiates (Experimental)',
507 => 'Insufficient Storage',
508 => 'Loop Detected',
510 => 'Not Extended',
511 => 'Network Authentication Required',
);
/**
* @param int $code 状态码
* @param double $duration 请求时长
* @param array $headers 响应头部
* @param string $body 响应内容
* @param string $error 错误描述
*/
public function __construct($code, $duration, array $headers = array(), $body = null, $error = null)
{
$this->statusCode = $code;
$this->duration = $duration;
$this->headers = array();
$this->body = $body;
$this->error = $error;
$this->jsonData = null;
if ($error !== null) {
return;
}
foreach ($headers as $k => $vs) {
if (is_array($vs)) {
$this->headers[$k] = $vs[count($vs) - 1];
} else {
$this->headers[$k] = $vs;
}
}
$this->normalizedHeaders = new Header($headers);
if ($body === null) {
if ($code >= 400) {
$this->error = self::$statusTexts[$code];
}
return;
}
if (self::isJson($this->normalizedHeaders)) {
try {
$jsonData = self::bodyJson($body);
if ($code >= 400) {
$this->error = $body;
if ($jsonData['error'] !== null) {
$this->error = $jsonData['error'];
}
}
$this->jsonData = $jsonData;
} catch (\InvalidArgumentException $e) {
$this->error = $body;
if ($code >= 200 && $code < 300) {
$this->error = $e->getMessage();
}
}
} elseif ($code >= 400) {
$this->error = $body;
}
return;
}
public function json()
{
return $this->jsonData;
}
public function headers($normalized = false)
{
if ($normalized) {
return $this->normalizedHeaders;
}
return $this->headers;
}
public function body()
{
return $this->body;
}
private static function bodyJson($body)
{
return \Qiniu\json_decode((string) $body, true, 512);
}
public function xVia()
{
$via = $this->normalizedHeaders['X-Via'];
if ($via === null) {
$via = $this->normalizedHeaders['X-Px'];
}
if ($via === null) {
$via = $this->normalizedHeaders['Fw-Via'];
}
return $via;
}
public function xLog()
{
return $this->normalizedHeaders['X-Log'];
}
public function xReqId()
{
return $this->normalizedHeaders['X-Reqid'];
}
public function ok()
{
return $this->statusCode >= 200 && $this->statusCode < 300 && $this->error === null;
}
public function needRetry()
{
if ($this->statusCode > 0 && $this->statusCode < 500) {
return false;
}
// https://developer.qiniu.com/fusion/kb/1352/the-http-request-return-a-status-code
if (in_array($this->statusCode, array(
501, 509, 573, 579, 608, 612, 614, 616, 618, 630, 631, 632, 640, 701
))) {
return false;
}
return true;
}
private static function isJson($headers)
{
return isset($headers['Content-Type']) && strpos($headers['Content-Type'], 'application/json') === 0;
}
}

View File

@ -0,0 +1,292 @@
<?php
namespace Qiniu\Processing;
use Qiniu;
/**
* 主要涉及图片链接拼接
*
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
*/
final class ImageUrlBuilder
{
/**
* mode合法范围值
*
* @var array
*/
protected $modeArr = array(0, 1, 2, 3, 4, 5);
/**
* format合法值
*
* @var array
*/
protected $formatArr = array('psd', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'bmp');
/**
* 水印图片位置合法值
*
* @var array
*/
protected $gravityArr = array(
'NorthWest',
'North',
'NorthEast',
'West',
'Center',
'East',
'SouthWest',
'South',
'SouthEast'
);
/**
* 缩略图链接拼接
*
* @param string $url 图片链接
* @param int $mode 缩略模式
* @param int $width 宽度
* @param int $height 长度
* @param string $format 输出类型
* @param int $quality 图片质量
* @param int $interlace 是否支持渐进显示
* @param int $ignoreError 忽略结果
* @return string
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
public function thumbnail(
$url,
$mode,
$width,
$height,
$format = null,
$interlace = null,
$quality = null,
$ignoreError = 1
) {
// url合法效验
if (!$this->isUrl($url)) {
return $url;
}
// 参数合法性效验
if (!in_array(intval($mode), $this->modeArr, true)) {
return $url;
}
if (!$width || !$height) {
return $url;
}
$thumbStr = 'imageView2/' . $mode . '/w/' . $width . '/h/' . $height . '/';
// 拼接输出格式
if (!is_null($format)
&& in_array($format, $this->formatArr)
) {
$thumbStr .= 'format/' . $format . '/';
}
// 拼接渐进显示
if (!is_null($interlace)
&& in_array(intval($interlace), array(0, 1), true)
) {
$thumbStr .= 'interlace/' . $interlace . '/';
}
// 拼接图片质量
if (!is_null($quality)
&& intval($quality) >= 0
&& intval($quality) <= 100
) {
$thumbStr .= 'q/' . $quality . '/';
}
$thumbStr .= 'ignore-error/' . $ignoreError . '/';
// 如果有query_string用|线分割实现多参数
return $url . ($this->hasQuery($url) ? '|' : '?') . $thumbStr;
}
/**
* 图片水印
*
* @param string $url 图片链接
* @param string $image 水印图片链接
* @param int $dissolve 透明度
* @param string $gravity 水印位置
* @param int $dx 横轴边距
* @param int $dy 纵轴边距
* @param int $watermarkScale 自适应原图的短边比例
* @return string
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
public function waterImg(
$url,
$image,
$dissolve = 100,
$gravity = 'SouthEast',
$dx = null,
$dy = null,
$watermarkScale = null
) {
// url合法效验
if (!$this->isUrl($url)) {
return $url;
}
$waterStr = 'watermark/1/image/' . \Qiniu\base64_urlSafeEncode($image) . '/';
// 拼接水印透明度
if (is_numeric($dissolve)
&& $dissolve <= 100
) {
$waterStr .= 'dissolve/' . $dissolve . '/';
}
// 拼接水印位置
if (in_array($gravity, $this->gravityArr, true)) {
$waterStr .= 'gravity/' . $gravity . '/';
}
// 拼接横轴边距
if (!is_null($dx)
&& is_numeric($dx)
) {
$waterStr .= 'dx/' . $dx . '/';
}
// 拼接纵轴边距
if (!is_null($dy)
&& is_numeric($dy)
) {
$waterStr .= 'dy/' . $dy . '/';
}
// 拼接自适应原图的短边比例
if (!is_null($watermarkScale)
&& is_numeric($watermarkScale)
&& $watermarkScale > 0
&& $watermarkScale < 1
) {
$waterStr .= 'ws/' . $watermarkScale . '/';
}
// 如果有query_string用|线分割实现多参数
return $url . ($this->hasQuery($url) ? '|' : '?') . $waterStr;
}
/**
* 文字水印
*
* @param string $url 图片链接
* @param string $text 文字
* @param string $font 文字字体
* @param string $fontSize 文字字号
* @param string $fontColor 文字颜色
* @param int $dissolve 透明度
* @param string $gravity 水印位置
* @param int $dx 横轴边距
* @param int $dy 纵轴边距
* @return string
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html#text-watermark
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
public function waterText(
$url,
$text,
$font = '黑体',
$fontSize = 0,
$fontColor = null,
$dissolve = 100,
$gravity = 'SouthEast',
$dx = null,
$dy = null
) {
// url合法效验
if (!$this->isUrl($url)) {
return $url;
}
$waterStr = 'watermark/2/text/'
. \Qiniu\base64_urlSafeEncode($text) . '/font/'
. \Qiniu\base64_urlSafeEncode($font) . '/';
// 拼接文字大小
if (is_int($fontSize)) {
$waterStr .= 'fontsize/' . $fontSize . '/';
}
// 拼接文字颜色
if (!is_null($fontColor)
&& $fontColor
) {
$waterStr .= 'fill/' . \Qiniu\base64_urlSafeEncode($fontColor) . '/';
}
// 拼接水印透明度
if (is_numeric($dissolve)
&& $dissolve <= 100
) {
$waterStr .= 'dissolve/' . $dissolve . '/';
}
// 拼接水印位置
if (in_array($gravity, $this->gravityArr, true)) {
$waterStr .= 'gravity/' . $gravity . '/';
}
// 拼接横轴边距
if (!is_null($dx)
&& is_numeric($dx)
) {
$waterStr .= 'dx/' . $dx . '/';
}
// 拼接纵轴边距
if (!is_null($dy)
&& is_numeric($dy)
) {
$waterStr .= 'dy/' . $dy . '/';
}
// 如果有query_string用|线分割实现多参数
return $url . ($this->hasQuery($url) ? '|' : '?') . $waterStr;
}
/**
* 效验url合法性
*
* @param string $url url链接
* @return string
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
protected function isUrl($url)
{
$urlArr = parse_url($url);
return $urlArr['scheme']
&& in_array($urlArr['scheme'], array('http', 'https'))
&& $urlArr['host']
&& $urlArr['path'];
}
/**
* 检测是否有query
*
* @param string $url url链接
* @return string
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
protected function hasQuery($url)
{
$urlArr = parse_url($url);
return !empty($urlArr['query']);
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace Qiniu\Processing;
use Qiniu\Http\Client;
use Qiniu\Http\Error;
use Qiniu\Http\Proxy;
final class Operation
{
private $auth;
private $token_expire;
private $domain;
private $proxy;
public function __construct(
$domain,
$auth = null,
$token_expire = 3600,
$proxy = null,
$proxy_auth = null,
$proxy_user_password = null
) {
$this->auth = $auth;
$this->domain = $domain;
$this->token_expire = $token_expire;
$this->proxy = new Proxy($proxy, $proxy_auth, $proxy_user_password);
}
/**
* 对资源文件进行处理
*
* @param string $key 待处理的资源文件名
* @param string $fops string|array fop操作多次fop操作以array的形式传入。
* eg. imageView2/1/w/200/h/200, imageMogr2/thumbnail/!75px
*
* @return array 文件处理后的结果及错误。
*
* @link http://developer.qiniu.com/docs/v6/api/reference/fop/
*/
public function execute($key, $fops)
{
$url = $this->buildUrl($key, $fops);
$resp = Client::get($url, array(), $this->proxy->makeReqOpt());
if (!$resp->ok()) {
return array(null, new Error($url, $resp));
}
if ($resp->json() !== null) {
return array($resp->json(), null);
}
return array($resp->body, null);
}
public function buildUrl($key, $fops, $protocol = 'http')
{
if (is_array($fops)) {
$fops = implode('|', $fops);
}
$url = $protocol . "://$this->domain/$key?$fops";
if ($this->auth !== null) {
$url = $this->auth->privateDownloadUrl($url, $this->token_expire);
}
return $url;
}
}

View File

@ -0,0 +1,135 @@
<?php
namespace Qiniu\Processing;
use Qiniu\Config;
use Qiniu\Http\Error;
use Qiniu\Http\Client;
use Qiniu\Http\Proxy;
use Qiniu\Zone;
/**
* 持久化处理类,该类用于主动触发异步持久化操作.
*
* @link http://developer.qiniu.com/docs/v6/api/reference/fop/pfop/pfop.html
*/
final class PersistentFop
{
/**
* @var 账号管理密钥对Auth对象
*/
private $auth;
/*
* @var 配置对象Config 对象
* */
private $config;
/**
* @var 代理信息
*/
private $proxy;
public function __construct($auth, $config = null, $proxy = null, $proxy_auth = null, $proxy_user_password = null)
{
$this->auth = $auth;
if ($config == null) {
$this->config = new Config();
} else {
$this->config = $config;
}
$this->proxy = new Proxy($proxy, $proxy_auth, $proxy_user_password);
}
/**
* 对资源文件进行异步持久化处理
* @param string $bucket 资源所在空间
* @param string $key 待处理的源文件
* @param string|array $fops 待处理的pfop操作多个pfop操作以array的形式传入。
* eg. avthumb/mp3/ab/192k, vframe/jpg/offset/7/w/480/h/360
* @param string $pipeline 资源处理队列
* @param string $notify_url 处理结果通知地址
* @param bool $force 是否强制执行一次新的指令
* @param int $type 为 `1` 时开启闲时任务
*
*
* @return array 返回持久化处理的 persistentId 与可能出现的错误。
*
* @link http://developer.qiniu.com/docs/v6/api/reference/fop/
*/
public function execute(
$bucket,
$key,
$fops = null,
$pipeline = null,
$notify_url = null,
$force = false,
$type = null,
$workflow_template_id = null
) {
if (is_array($fops)) {
$fops = implode(';', $fops);
}
if (!$fops && !$workflow_template_id) {
throw new \InvalidArgumentException('Must provide one of fops or template_id');
}
$params = array('bucket' => $bucket, 'key' => $key);
\Qiniu\setWithoutEmpty($params, 'fops', $fops);
\Qiniu\setWithoutEmpty($params, 'pipeline', $pipeline);
\Qiniu\setWithoutEmpty($params, 'notifyURL', $notify_url);
\Qiniu\setWithoutEmpty($params, 'type', $type);
\Qiniu\setWithoutEmpty($params, 'workflowTemplateID', $workflow_template_id);
if ($force) {
$params['force'] = 1;
}
$data = http_build_query($params);
$scheme = "http://";
if ($this->config->useHTTPS === true) {
$scheme = "https://";
}
$apiHost = $this->getApiHost();
$url = $scheme . $apiHost . '/pfop/';
$headers = $this->auth->authorization($url, $data, 'application/x-www-form-urlencoded');
$headers['Content-Type'] = 'application/x-www-form-urlencoded';
$response = Client::post($url, $data, $headers, $this->proxy->makeReqOpt());
if (!$response->ok()) {
return array(null, new Error($url, $response));
}
$r = $response->json();
$id = $r['persistentId'];
return array($id, null);
}
/**
* @param string $id
* @return array 返回任务状态与可能出现的错误
*/
public function status($id)
{
$scheme = "http://";
if ($this->config->useHTTPS === true) {
$scheme = "https://";
}
$apiHost = $this->getApiHost();
$url = $scheme . $apiHost . "/status/get/prefop?id=$id";
$response = Client::get($url, array(), $this->proxy->makeReqOpt());
if (!$response->ok()) {
return array(null, new Error($url, $response));
}
return array($response->json(), null);
}
private function getApiHost()
{
if (!empty($this->config->zone) && !empty($this->config->zone->apiHost)) {
$apiHost = $this->config->zone->apiHost;
} else {
$apiHost = Config::API_HOST;
}
return $apiHost;
}
}

View File

@ -0,0 +1,229 @@
<?php
namespace Qiniu;
use Qiniu\Http\Client;
use Qiniu\Http\Error;
use Qiniu\Http\Middleware\RetryDomainsMiddleware;
use Qiniu\Http\RequestOptions;
class Region
{
//源站上传域名
public $srcUpHosts;
//CDN加速上传域名
public $cdnUpHosts;
//资源管理域名
public $rsHost;
//资源列举域名
public $rsfHost;
//资源处理域名
public $apiHost;
//IOVIP域名
public $iovipHost;
// TTL
public $ttl;
//构造一个Region对象
public function __construct(
$srcUpHosts = array(),
$cdnUpHosts = array(),
$rsHost = "rs-z0.qiniuapi.com",
$rsfHost = "rsf-z0.qiniuapi.com",
$apiHost = "api.qiniuapi.com",
$iovipHost = null,
$ttl = null
) {
$this->srcUpHosts = $srcUpHosts;
$this->cdnUpHosts = $cdnUpHosts;
$this->rsHost = $rsHost;
$this->rsfHost = $rsfHost;
$this->apiHost = $apiHost;
$this->iovipHost = $iovipHost;
$this->ttl = $ttl;
}
//华东机房
public static function regionHuadong()
{
$regionHuadong = new Region(
array("up.qiniup.com"),
array('upload.qiniup.com'),
'rs-z0.qiniuapi.com',
'rsf-z0.qiniuapi.com',
'api.qiniuapi.com',
'iovip.qbox.me'
);
return $regionHuadong;
}
//华东机房内网上传
public static function qvmRegionHuadong()
{
$qvmRegionHuadong = new Region(
array("free-qvm-z0-xs.qiniup.com"),
'rs-z0.qiniuapi.com',
'rsf-z0.qiniuapi.com',
'api.qiniuapi.com',
'iovip.qbox.me'
);
return $qvmRegionHuadong;
}
//华北机房内网上传
public static function qvmRegionHuabei()
{
$qvmRegionHuabei = new Region(
array("free-qvm-z1-zz.qiniup.com"),
"rs-z1.qiniuapi.com",
"rsf-z1.qiniuapi.com",
"api-z1.qiniuapi.com",
"iovip-z1.qbox.me"
);
return $qvmRegionHuabei;
}
//华北机房
public static function regionHuabei()
{
$regionHuabei = new Region(
array('up-z1.qiniup.com'),
array('upload-z1.qiniup.com'),
"rs-z1.qiniuapi.com",
"rsf-z1.qiniuapi.com",
"api-z1.qiniuapi.com",
"iovip-z1.qbox.me"
);
return $regionHuabei;
}
//华南机房
public static function regionHuanan()
{
$regionHuanan = new Region(
array('up-z2.qiniup.com'),
array('upload-z2.qiniup.com'),
"rs-z2.qiniuapi.com",
"rsf-z2.qiniuapi.com",
"api-z2.qiniuapi.com",
"iovip-z2.qbox.me"
);
return $regionHuanan;
}
//华东2 机房
public static function regionHuadong2()
{
return new Region(
array('up-cn-east-2.qiniup.com'),
array('upload-cn-east-2.qiniup.com'),
"rs-cn-east-2.qiniuapi.com",
"rsf-cn-east-2.qiniuapi.com",
"api-cn-east-2.qiniuapi.com",
"iovip-cn-east-2.qiniuio.com"
);
}
//北美机房
public static function regionNorthAmerica()
{
//北美机房
$regionNorthAmerica = new Region(
array('up-na0.qiniup.com'),
array('upload-na0.qiniup.com'),
"rs-na0.qiniuapi.com",
"rsf-na0.qiniuapi.com",
"api-na0.qiniuapi.com",
"iovip-na0.qbox.me"
);
return $regionNorthAmerica;
}
//新加坡机房
public static function regionSingapore()
{
//新加坡机房
$regionSingapore = new Region(
array('up-as0.qiniup.com'),
array('upload-as0.qiniup.com'),
"rs-as0.qiniuapi.com",
"rsf-as0.qiniuapi.com",
"api-as0.qiniuapi.com",
"iovip-as0.qbox.me"
);
return $regionSingapore;
}
/*
* GET /v4/query?ak=<ak>&bucket=<bucket>
* @param string $ak
* @param string $bucket
* @param string $ucHost|null
* @param array $backupUcHosts
* @param int $retryTimes
* @param RequestOptions|null $reqOpt
* @return Response
**/
public static function queryRegion(
$ak,
$bucket,
$ucHost = null,
$backupUcHosts = array(),
$retryTimes = 2,
$reqOpt = null
) {
$region = new Region();
if (!$ucHost) {
$ucHost = "https://" . Config::QUERY_REGION_HOST;
}
$url = $ucHost . '/v4/query' . "?ak=$ak&bucket=$bucket";
if ($reqOpt == null) {
$reqOpt = new RequestOptions();
}
$reqOpt->middlewares = array(
new RetryDomainsMiddleware(
$backupUcHosts,
$retryTimes
)
);
$ret = Client::get($url, array(), $reqOpt);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
if (!is_array($r["hosts"]) || count($r["hosts"]) == 0) {
return array(null, new Error($url, $ret));
}
// parse region;
$regionHost = $r["hosts"][0];
$region->cdnUpHosts = array_merge($region->cdnUpHosts, $regionHost['up']['domains']);
$region->srcUpHosts = array_merge($region->srcUpHosts, $regionHost['up']['domains']);
// set specific hosts
$region->iovipHost = $regionHost['io']['domains'][0];
if (isset($regionHost['rs']['domains']) && count($regionHost['rs']['domains']) > 0) {
$region->rsHost = $regionHost['rs']['domains'][0];
} else {
$region->rsHost = Config::RS_HOST;
}
if (isset($regionHost['rsf']['domains']) && count($regionHost['rsf']['domains']) > 0) {
$region->rsfHost = $regionHost['rsf']['domains'][0];
} else {
$region->rsfHost = Config::RSF_HOST;
}
if (isset($regionHost['api']['domains']) && count($regionHost['api']['domains']) > 0) {
$region->apiHost = $regionHost['api']['domains'][0];
} else {
$region->apiHost = Config::API_HOST;
}
// set ttl
$region->ttl = $regionHost['ttl'];
return $region;
}
}

View File

@ -0,0 +1,236 @@
<?php
namespace Qiniu\Rtc;
use Qiniu\Auth;
use Qiniu\Config;
use Qiniu\Http\Error;
use Qiniu\Http\Client;
use Qiniu\Http\Proxy;
class AppClient
{
private $auth;
private $baseURL;
private $proxy;
public function __construct(Auth $auth, $proxy = null, $proxy_auth = null, $proxy_user_password = null)
{
$this->auth = $auth;
$this->baseURL = sprintf("%s/%s/apps", Config::RTCAPI_HOST, Config::RTCAPI_VERSION);
$this->proxy = new Proxy($proxy, $proxy_auth, $proxy_user_password);
}
/**
* 创建应用
*
* @param string $hub 绑定的直播 hub
* @param string $title app 的名称 注意Title 不是唯一标识,重复 create 动作将生成多个 app
* @param int $maxUsers 连麦房间支持的最大在线人数
* @param bool $noAutoKickUser 禁止自动踢人(抢流),默认为 false
* @return array
* @link https://doc.qnsdk.com/rtn/docs/server_overview#2_1
*/
public function createApp($hub, $title, $maxUsers = null, $noAutoKickUser = null)
{
$params = array();
$params['hub'] = $hub;
$params['title'] = $title;
if (!empty($maxUsers)) {
$params['maxUsers'] = $maxUsers;
}
if ($noAutoKickUser !== null) {
$params['noAutoKickUser'] = $noAutoKickUser;
}
$body = json_encode($params);
return $this->post($this->baseURL, $body);
}
/**
* 更新一个应用的配置信息
*
* @param string $appId app 的唯一标识,创建的时候由系统生成
* @param string $hub app 的名称,可选
* @param string $title 绑定的直播 hub可选用于合流后 rtmp 推流
* @param int $maxUsers 连麦房间支持的最大在线人数,可选
* @param bool $noAutoKickUser 禁止自动踢人,可选
* @param null $mergePublishRtmp 连麦合流转推 RTMP 的配置,可选择。其详细配置可以参考文档
* @return array
* @link https://doc.qnsdk.com/rtn/docs/server_overview#2_1
*/
public function updateApp($appId, $hub, $title, $maxUsers = null, $noAutoKickUser = null, $mergePublishRtmp = null)
{
$url = $this->baseURL . '/' . $appId;
$params = array();
$params['hub'] = $hub;
$params['title'] = $title;
if (!empty($maxUsers)) {
$params['maxUsers'] = $maxUsers;
}
if ($noAutoKickUser !== null) {
$params['noAutoKickUser'] = $noAutoKickUser;
}
if (!empty($mergePublishRtmp)) {
$params['mergePublishRtmp'] = $mergePublishRtmp;
}
$body = json_encode($params);
return $this->post($url, $body);
}
/**
* 获取应用信息
*
* @param string $appId
* @return array
* @link https://doc.qnsdk.com/rtn/docs/server_overview#2_1
*/
public function getApp($appId)
{
$url = $this->baseURL . '/' . $appId;
return $this->get($url);
}
/**
* 删除应用
*
* @param string $appId app 的唯一标识,创建的时候由系统生成
* @return array
* @link https://doc.qnsdk.com/rtn/docs/server_overview#2_1
*/
public function deleteApp($appId)
{
$url = $this->baseURL . '/' . $appId;
return $this->delete($url);
}
/**
* 获取房间内用户列表
*
* @param string $appId app 的唯一标识,创建的时候由系统生成
* @param string $roomName 操作所查询的连麦房间
* @return array
* @link https://doc.qnsdk.com/rtn/docs/server_overview#2_2
*/
public function listUser($appId, $roomName)
{
$url = sprintf("%s/%s/rooms/%s/users", $this->baseURL, $appId, $roomName);
return $this->get($url);
}
/**
* 指定一个用户踢出房间
*
* @param string $appId app 的唯一标识,创建的时候由系统生成
* @param string $roomName 连麦房间
* @param string $userId 操作所剔除的用户
* @return mixed
* @link https://doc.qnsdk.com/rtn/docs/server_overview#2_2
*/
public function kickUser($appId, $roomName, $userId)
{
$url = sprintf("%s/%s/rooms/%s/users/%s", $this->baseURL, $appId, $roomName, $userId);
return $this->delete($url);
}
/**
* 停止一个房间的合流转推
*
* @param string $appId
* @param string $roomName
* @return array
* @link https://doc.qnsdk.com/rtn/docs/server_overview#2_2
*/
public function stopMerge($appId, $roomName)
{
$url = sprintf("%s/%s/rooms/%s/merge", $this->baseURL, $appId, $roomName);
return $this->delete($url);
}
/**
* 获取应用中活跃房间
*
* @param string $appId 连麦房间所属的 app
* @param null $prefix 所查询房间名的前缀索引,可以为空。
* @param int $offset 分页查询的位移标记
* @param int $limit 此次查询的最大长度
* @return array
* @link https://doc.qnsdk.com/rtn/docs/server_overview#2_2
*/
public function listActiveRooms($appId, $prefix = null, $offset = null, $limit = null)
{
$query = array();
if (isset($prefix)) {
$query['prefix'] = $prefix;
}
if (isset($offset)) {
$query['offset'] = $offset;
}
if (isset($limit)) {
$query['limit'] = $limit;
}
if (isset($query) && !empty($query)) {
$query = '?' . http_build_query($query);
$url = sprintf("%s/%s/rooms%s", $this->baseURL, $appId, $query);
} else {
$url = sprintf("%s/%s/rooms", $this->baseURL, $appId);
}
return $this->get($url);
}
/**
* 生成加入房间的令牌
*
* @param string $appId app 的唯一标识,创建的时候由系统生成
* @param string $roomName 房间名称,需满足规格 ^[a-zA-Z0-9_-]{3,64}$
* @param string $userId 请求加入房间的用户 ID需满足规格 ^[a-zA-Z0-9_-]{3,50}$
* @param int $expireAt 鉴权的有效时间传入以秒为单位的64位 Unix 绝对时间
* @param string $permission 该用户的房间管理权限,"admin" 或 "user",默认为 "user"
* @return string
* @link https://doc.qnsdk.com/rtn/docs/server_overview#1
*/
public function appToken($appId, $roomName, $userId, $expireAt, $permission)
{
$params = array();
$params['appId'] = $appId;
$params['userId'] = $userId;
$params['roomName'] = $roomName;
$params['permission'] = $permission;
$params['expireAt'] = $expireAt;
$appAccessString = json_encode($params);
return $this->auth->signWithData($appAccessString);
}
private function get($url, $cType = null)
{
$rtcToken = $this->auth->authorizationV2($url, "GET", null, $cType);
$rtcToken['Content-Type'] = $cType;
$ret = Client::get($url, $rtcToken, $this->proxy->makeReqOpt());
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
return array($ret->json(), null);
}
private function delete($url, $contentType = 'application/json')
{
$rtcToken = $this->auth->authorizationV2($url, "DELETE", null, $contentType);
$rtcToken['Content-Type'] = $contentType;
$ret = Client::delete($url, $rtcToken, $this->proxy->makeReqOpt());
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
return array($ret->json(), null);
}
private function post($url, $body, $contentType = 'application/json')
{
$rtcToken = $this->auth->authorizationV2($url, "POST", $body, $contentType);
$rtcToken['Content-Type'] = $contentType;
$ret = Client::post($url, $body, $rtcToken, $this->proxy->makeReqOpt());
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
return array($r, null);
}
}

View File

@ -0,0 +1,382 @@
<?php
namespace Qiniu\Sms;
use Qiniu\Auth;
use Qiniu\Config;
use Qiniu\Http\Error;
use Qiniu\Http\Client;
use Qiniu\Http\Proxy;
class Sms
{
private $auth;
private $baseURL;
private $proxy;
public function __construct(Auth $auth, $proxy = null, $proxy_auth = null, $proxy_user_password = null)
{
$this->auth = $auth;
$this->baseURL = sprintf("%s/%s/", Config::SMS_HOST, Config::SMS_VERSION);
$this->proxy = new Proxy($proxy, $proxy_auth, $proxy_user_password);
}
/**
* 创建签名
*
* @param string $signature 签名
* @param string $source 签名来源,申请签名时必须指定签名来源
* @param string $pics 签名对应的资质证明图片进行 base64 编码格式转换后的字符串,可选
* @return array
*
* @link https://developer.qiniu.com/sms/api/5844/sms-api-create-signature
*/
public function createSignature($signature, $source, $pics = null)
{
$params = array();
$params['signature'] = $signature;
$params['source'] = $source;
if (!empty($pics)) {
$params['pics'] = array($this->imgToBase64($pics));
}
$body = json_encode($params);
$url = $this->baseURL . 'signature';
return $this->post($url, $body);
}
/**
* 编辑签名
*
* @param string $id 签名 ID
* @param string $signature 签名
* @param string $source 签名来源
* @param string $pics 签名对应的资质证明图片进行 base64 编码格式转换后的字符串,可选
* @return array
* @link https://developer.qiniu.com/sms/api/5890/sms-api-edit-signature
*/
public function updateSignature($id, $signature, $source, $pics = null)
{
$params = array();
$params['signature'] = $signature;
$params['source'] = $source;
if (!empty($pics)) {
$params['pics'] = array($this->imgToBase64($pics));
}
$body = json_encode($params);
$url = $this->baseURL . 'signature/' . $id;
return $this->PUT($url, $body);
}
/**
* 列出签名
*
* @param string $audit_status 审核状态:"passed"(通过), "rejected"(未通过), "reviewing"(审核中)
* @param int $page 页码。默认为 1
* @param int $page_size 分页大小。默认为 20
* @return array
* @link https://developer.qiniu.com/sms/api/5889/sms-api-query-signature
*/
public function querySignature($audit_status = null, $page = 1, $page_size = 20)
{
$url = sprintf(
"%s?audit_status=%s&page=%s&page_size=%s",
$this->baseURL . 'signature',
$audit_status,
$page,
$page_size
);
return $this->get($url);
}
/**
* 查询单个签名
*
* @param string $signature_id
* @return array
* @link https://developer.qiniu.com/sms/api/5970/query-a-single-signature
*/
public function checkSingleSignature($signature_id)
{
$url = sprintf(
"%s/%s",
$this->baseURL . 'signature',
$signature_id
);
return $this->get($url);
}
/**
* 删除签名
*
* @param string $signature_id 签名 ID
* @return array
* @link https://developer.qiniu.com/sms/api/5891/sms-api-delete-signature
*/
public function deleteSignature($signature_id)
{
$url = $this->baseURL . 'signature/' . $signature_id;
return $this->delete($url);
}
/**
* 创建模板
*
* @param string $name 模板名称
* @param string $template 模板内容 可设置自定义变量,发送短信时候使用,参考:${code}
* @param string $type notification通知类,verification验证码,marketing营销类,voice语音类
* @param string $description 申请理由简述
* @param string $signature_id 已经审核通过的签名
* @return array array
* @link https://developer.qiniu.com/sms/api/5893/sms-api-create-template
*/
public function createTemplate(
$name,
$template,
$type,
$description,
$signature_id
) {
$params = array();
$params['name'] = $name;
$params['template'] = $template;
$params['type'] = $type;
$params['description'] = $description;
$params['signature_id'] = $signature_id;
$body = json_encode($params);
$url = $this->baseURL . 'template';
return $this->post($url, $body);
}
/**
* 列出模板
*
* @param string $audit_status 审核状态passed (通过), rejected (未通过), reviewing (审核中)
* @param int $page 页码。默认为 1
* @param int $page_size 分页大小。默认为 20
* @return array
* @link https://developer.qiniu.com/sms/api/5894/sms-api-query-template
*/
public function queryTemplate($audit_status = null, $page = 1, $page_size = 20)
{
$url = sprintf(
"%s?audit_status=%s&page=%s&page_size=%s",
$this->baseURL . 'template',
$audit_status,
$page,
$page_size
);
return $this->get($url);
}
/**
* 查询单个模版
*
* @param string $template_id 模版ID
* @return array
* @link https://developer.qiniu.com/sms/api/5969/query-a-single-template
*/
public function querySingleTemplate($template_id)
{
$url = sprintf(
"%s/%s",
$this->baseURL . 'template',
$template_id
);
return $this->get($url);
}
/**
* 编辑模板
*
* @param string $id 模板 ID
* @param string $name 模板名称
* @param string $template 模板内容
* @param string $description 申请理由简述
* @param string $signature_id 已经审核通过的签名 ID
* @return array
* @link https://developer.qiniu.com/sms/api/5895/sms-api-edit-template
*/
public function updateTemplate(
$id,
$name,
$template,
$description,
$signature_id
) {
$params = array();
$params['name'] = $name;
$params['template'] = $template;
$params['description'] = $description;
$params['signature_id'] = $signature_id;
$body = json_encode($params);
$url = $this->baseURL . 'template/' . $id;
return $this->PUT($url, $body);
}
/**
* 删除模板
*
* @param string $template_id 模板 ID
* @return array
* @link https://developer.qiniu.com/sms/api/5896/sms-api-delete-template
*/
public function deleteTemplate($template_id)
{
$url = $this->baseURL . 'template/' . $template_id;
return $this->delete($url);
}
/**
* 发送短信
*
* @param string $template_id 模板 ID
* @param array $mobiles 手机号
* @param array $parameters 自定义模板变量变量设置在创建模板时参数template指定
* @return array
* @link https://developer.qiniu.com/sms/api/5897/sms-api-send-message
*/
public function sendMessage($template_id, $mobiles, $parameters = null)
{
$params = array();
$params['template_id'] = $template_id;
$params['mobiles'] = $mobiles;
if (!empty($parameters)) {
$params['parameters'] = $parameters;
}
$body = json_encode($params);
$url = $this->baseURL . 'message';
return $this->post($url, $body);
}
/**
* 查询发送记录
*
* @param string $job_id 发送任务返回的 id
* @param string $message_id 单条短信发送接口返回的 id
* @param string $mobile 接收短信的手机号码
* @param string $status sending: 发送中success: 发送成功failed: 发送失败waiting: 等待发送
* @param string $template_id 模版 id
* @param string $type marketing:营销notification:通知verification:验证码voice:语音
* @param string $start 开始时间timestamp例如: 1563280448
* @param int $end 结束时间timestamp例如: 1563280471
* @param int $page 页码,默认为 1
* @param int $page_size 每页返回的数据条数默认20最大200
* @return array
* @link https://developer.qiniu.com/sms/api/5852/query-send-sms
*/
public function querySendSms(
$job_id = null,
$message_id = null,
$mobile = null,
$status = null,
$template_id = null,
$type = null,
$start = null,
$end = null,
$page = 1,
$page_size = 20
) {
$query = array();
\Qiniu\setWithoutEmpty($query, 'job_id', $job_id);
\Qiniu\setWithoutEmpty($query, 'message_id', $message_id);
\Qiniu\setWithoutEmpty($query, 'mobile', $mobile);
\Qiniu\setWithoutEmpty($query, 'status', $status);
\Qiniu\setWithoutEmpty($query, 'template_id', $template_id);
\Qiniu\setWithoutEmpty($query, 'type', $type);
\Qiniu\setWithoutEmpty($query, 'start', $start);
\Qiniu\setWithoutEmpty($query, 'end', $end);
\Qiniu\setWithoutEmpty($query, 'page', $page);
\Qiniu\setWithoutEmpty($query, 'page_size', $page_size);
$url = $this->baseURL . 'messages?' . http_build_query($query);
return $this->get($url);
}
public function imgToBase64($img_file)
{
$img_base64 = '';
if (file_exists($img_file)) {
$app_img_file = $img_file; // 图片路径
$img_info = getimagesize($app_img_file); // 取得图片的大小,类型等
$fp = fopen($app_img_file, "r"); // 图片是否可读权限
if ($fp) {
$filesize = filesize($app_img_file);
if ($filesize > 5 * 1024 * 1024) {
die("pic size < 5M !");
}
$img_type = null;
$content = fread($fp, $filesize);
$file_content = chunk_split(base64_encode($content)); // base64编码
switch ($img_info[2]) { //判读图片类型
case 1:
$img_type = 'gif';
break;
case 2:
$img_type = 'jpg';
break;
case 3:
$img_type = 'png';
break;
}
//合成图片的base64编码
$img_base64 = 'data:image/' . $img_type . ';base64,' . $file_content;
}
fclose($fp);
}
return $img_base64;
}
private function get($url, $contentType = 'application/x-www-form-urlencoded')
{
$headers = $this->auth->authorizationV2($url, "GET", null, $contentType);
$headers['Content-Type'] = $contentType;
$ret = Client::get($url, $headers, $this->proxy->makeReqOpt());
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
return array($ret->json(), null);
}
private function delete($url, $contentType = 'application/json')
{
$headers = $this->auth->authorizationV2($url, "DELETE", null, $contentType);
$headers['Content-Type'] = $contentType;
$ret = Client::delete($url, $headers, $this->proxy->makeReqOpt());
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
return array($ret->json(), null);
}
private function post($url, $body, $contentType = 'application/json')
{
$headers = $this->auth->authorizationV2($url, "POST", $body, $contentType);
$headers['Content-Type'] = $contentType;
$ret = Client::post($url, $body, $headers, $this->proxy->makeReqOpt());
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
return array($r, null);
}
private function PUT($url, $body, $contentType = 'application/json')
{
$headers = $this->auth->authorizationV2($url, "PUT", $body, $contentType);
$headers['Content-Type'] = $contentType;
$ret = Client::put($url, $body, $headers, $this->proxy->makeReqOpt());
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
return array($r, null);
}
}

View File

@ -0,0 +1,129 @@
<?php
namespace Qiniu\Storage;
use Qiniu\Auth;
use Qiniu\Config;
use Qiniu\Zone;
use Qiniu\Http\Client;
use Qiniu\Http\Error;
use Qiniu\Http\Proxy;
/**
* 主要涉及了内容审核接口的实现,具体的接口规格可以参考
*
* @link https://developer.qiniu.com/censor/api/5620/video-censor
*/
final class ArgusManager
{
private $auth;
private $config;
private $proxy;
public function __construct(
Auth $auth,
Config $config = null,
$proxy = null,
$proxy_auth = null,
$proxy_user_password = null
) {
$this->auth = $auth;
if ($config == null) {
$this->config = new Config();
} else {
$this->config = $config;
}
$this->proxy = new Proxy($proxy, $proxy_auth, $proxy_user_password);
}
/**
* 视频审核
*
* @param string $body body信息
*
* @return array 成功返回NULL失败返回对象Qiniu\Http\Error
* @link https://developer.qiniu.com/censor/api/5620/video-censor
*/
public function censorVideo($body)
{
$path = '/v3/video/censor';
return $this->arPost($path, $body);
}
/**
* 图片审核
*
* @param string $body
*
* @return array 成功返回NULL失败返回对象Qiniu\Http\Error
* @link https://developer.qiniu.com/censor/api/5588/image-censor
*/
public function censorImage($body)
{
$path = '/v3/image/censor';
return $this->arPost($path, $body);
}
/**
* 查询视频审核结果
*
* @param string $jobid 任务ID
* @return array
* @link https://developer.qiniu.com/censor/api/5620/video-censor
*/
public function censorStatus($jobid)
{
$scheme = "http://";
if ($this->config->useHTTPS === true) {
$scheme = "https://";
}
$url = $scheme . Config::ARGUS_HOST . "/v3/jobs/video/$jobid";
$response = $this->get($url);
if (!$response->ok()) {
return array(null, new Error($url, $response));
}
return array($response->json(), null);
}
private function getArHost()
{
$scheme = "http://";
if ($this->config->useHTTPS === true) {
$scheme = "https://";
}
return $scheme . Config::ARGUS_HOST;
}
private function arPost($path, $body = null)
{
$url = $this->getArHost() . $path;
return $this->post($url, $body);
}
private function get($url)
{
$headers = $this->auth->authorizationV2($url, 'GET');
return Client::get($url, $headers, $this->proxy->makeReqOpt());
}
private function post($url, $body)
{
$headers = $this->auth->authorizationV2($url, 'POST', $body, 'application/json');
$headers['Content-Type'] = 'application/json';
$ret = Client::post($url, $body, $headers, $this->proxy->makeReqOpt());
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
if (strstr($url, "video")) {
$jobid = $r['job'];
return array($jobid, null);
}
return array($r, null);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,165 @@
<?php
namespace Qiniu\Storage;
use Qiniu\Config;
use Qiniu\Http\Error;
use Qiniu\Http\Client;
use Qiniu\Http\RequestOptions;
final class FormUploader
{
/**
* 上传二进制流到七牛, 内部使用
*
* @param string $upToken 上传凭证
* @param string $key 上传文件名
* @param string $data 上传二进制流
* @param Config $config 上传配置
* @param string $params 自定义变量,规格参考
* {@link https://developer.qiniu.com/kodo/manual/1235/vars#xvar}
* @param string $mime 上传数据的mimeType
* @param string $fname
* @param RequestOptions $reqOpt
*
* @return array 包含已上传文件的信息,类似:
* [
* "hash" => "<Hash string>",
* "key" => "<Key string>"
* ]
*/
public static function put(
$upToken,
$key,
$data,
$config,
$params,
$mime,
$fname,
$reqOpt = null
) {
if ($reqOpt == null) {
$reqOpt = new RequestOptions();
}
$fields = array('token' => $upToken);
if ($key === null) {
} else {
$fields['key'] = $key;
}
//enable crc32 check by default
$fields['crc32'] = \Qiniu\crc32_data($data);
if ($params) {
foreach ($params as $k => $v) {
$fields[$k] = $v;
}
}
list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);
if ($err != null) {
return array(null, $err);
}
list($upHost, $err) = $config->getUpHostV2($accessKey, $bucket, $reqOpt);
if ($err != null) {
return array(null, $err);
}
$response = Client::multipartPost(
$upHost,
$fields,
'file',
$fname,
$data,
$mime,
array(),
$reqOpt
);
if (!$response->ok()) {
return array(null, new Error($upHost, $response));
}
return array($response->json(), null);
}
/**
* 上传文件到七牛,内部使用
*
* @param string $upToken 上传凭证
* @param string $key 上传文件名
* @param string $filePath 上传文件的路径
* @param Config $config 上传配置
* @param string $params 自定义变量,规格参考
* https://developer.qiniu.com/kodo/manual/1235/vars#xvar
* @param string $mime 上传数据的mimeType
*
* @return array 包含已上传文件的信息,类似:
* [
* "hash" => "<Hash string>",
* "key" => "<Key string>"
* ]
*/
public static function putFile(
$upToken,
$key,
$filePath,
$config,
$params,
$mime,
$reqOpt = null
) {
if ($reqOpt == null) {
$reqOpt = new RequestOptions();
}
$fields = array('token' => $upToken, 'file' => self::createFile($filePath, $mime));
if ($key !== null) {
$fields['key'] = $key;
}
$fields['crc32'] = \Qiniu\crc32_file($filePath);
if ($params) {
foreach ($params as $k => $v) {
$fields[$k] = $v;
}
}
$fields['key'] = $key;
$headers = array('Content-Type' => 'multipart/form-data');
list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);
if ($err != null) {
return array(null, $err);
}
list($upHost, $err) = $config->getUpHostV2($accessKey, $bucket, $reqOpt);
if ($err != null) {
return array(null, $err);
}
$response = Client::post($upHost, $fields, $headers, $reqOpt);
if (!$response->ok()) {
return array(null, new Error($upHost, $response));
}
return array($response->json(), null);
}
private static function createFile($filename, $mime)
{
// PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax
// See: https://wiki.php.net/rfc/curl-file-upload
if (function_exists('curl_file_create')) {
return curl_file_create($filename, $mime);
}
// Use the old style if using an older version of PHP
$value = "@{$filename}";
if (!empty($mime)) {
$value .= ';type=' . $mime;
}
return $value;
}
}

View File

@ -0,0 +1,580 @@
<?php
namespace Qiniu\Storage;
use Qiniu\Config;
use Qiniu\Http\Client;
use Qiniu\Http\Error;
use Qiniu\Enum\SplitUploadVersion;
use Qiniu\Http\RequestOptions;
/**
* 断点续上传类, 该类主要实现了断点续上传中的分块上传,
* 以及相应地创建块和创建文件过程.
*
* @link http://developer.qiniu.com/docs/v6/api/reference/up/mkblk.html
* @link http://developer.qiniu.com/docs/v6/api/reference/up/mkfile.html
*/
final class ResumeUploader
{
private $upToken;
private $key;
private $inputStream;
private $size;
private $params;
private $mime;
private $contexts;
private $finishedEtags;
private $host;
private $bucket;
private $currentUrl;
private $config;
private $resumeRecordFile;
private $version;
private $partSize;
/**
* @var RequestOptions
*/
private $reqOpt;
/**
* 上传二进制流到七牛
*
* @param string $upToken 上传凭证
* @param string $key 上传文件名
* @param resource $inputStream 上传二进制流
* @param int $size 上传流的大小
* @param array<string, string> $params 自定义变量
* @param string $mime 上传数据的mimeType
* @param Config $config
* @param string $resumeRecordFile 断点续传的已上传的部分信息记录文件
* @param string $version 分片上传版本 目前支持v1/v2版本 默认v1
* @param int $partSize 分片上传v2字段 默认大小为4MB 分片大小范围为1 MB - 1 GB
* @param RequestOptions $reqOpt 分片上传v2字段 默认大小为4MB 分片大小范围为1 MB - 1 GB
* @throws \Exception
*
* @link http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
*/
public function __construct(
$upToken,
$key,
$inputStream,
$size,
$params,
$mime,
$config,
$resumeRecordFile = null,
$version = 'v1',
$partSize = config::BLOCK_SIZE,
$reqOpt = null
) {
$this->upToken = $upToken;
$this->key = $key;
$this->inputStream = $inputStream;
$this->size = $size;
$this->params = $params;
$this->mime = $mime;
$this->contexts = array();
$this->finishedEtags = array("etags" => array(), "uploadId" => "", "expiredAt" => 0, "uploaded" => 0);
$this->config = $config;
$this->resumeRecordFile = $resumeRecordFile ? $resumeRecordFile : null;
$this->partSize = $partSize ? $partSize : config::BLOCK_SIZE;
if ($reqOpt === null) {
$reqOpt = new RequestOptions();
}
$this->reqOpt = $reqOpt;
try {
$this->version = SplitUploadVersion::from($version ? $version : 'v1');
} catch (\Exception $e) {
throw new \Exception("only support v1/v2 now!", 0, $e);
}
list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);
$this->bucket = $bucket;
if ($err != null) {
return array(null, $err);
}
list($upHost, $err) = $config->getUpHostV2($accessKey, $bucket, $reqOpt);
if ($err != null) {
throw new \Exception($err->message(), 1);
}
$this->host = $upHost;
}
/**
* 上传操作
* @param $fname string 文件名
*
* @throws \Exception
*/
public function upload($fname)
{
$blkputRets = null;
// get upload record from resumeRecordFile
if ($this->resumeRecordFile != null) {
if (file_exists($this->resumeRecordFile)) {
$stream = fopen($this->resumeRecordFile, 'r');
if ($stream) {
$streamLen = filesize($this->resumeRecordFile);
if ($streamLen > 0) {
$contents = fread($stream, $streamLen);
fclose($stream);
if ($contents) {
$blkputRets = json_decode($contents, true);
if ($blkputRets === null) {
error_log("resumeFile contents decode error");
}
} else {
error_log("read resumeFile failed");
}
} else {
error_log("resumeFile is empty");
}
} else {
error_log("resumeFile open failed");
}
} else {
error_log("resumeFile not exists");
}
}
if ($this->version == SplitUploadVersion::V1) {
return $this->uploadV1($fname, $blkputRets);
} elseif ($this->version == SplitUploadVersion::V2) {
return $this->uploadV2($fname, $blkputRets);
} else {
throw new \Exception("only support v1/v2 now!");
}
}
/**
* @param string $fname 文件名
* @param null|array $blkputRets
*
* @throws \Exception
*/
private function uploadV1($fname, $blkputRets = null)
{
// 尝试恢复恢复已上传的数据
$isResumeUpload = $blkputRets !== null;
$this->contexts = array();
if ($blkputRets) {
if (isset($blkputRets['contexts']) && isset($blkputRets['uploaded']) &&
is_array($blkputRets['contexts']) && is_int($blkputRets['uploaded'])
) {
$this->contexts = array_map(function ($ctx) {
if (is_array($ctx)) {
return $ctx;
} else {
// 兼容旧版本(旧版本没有存储 expireAt
return array(
"ctx" => $ctx,
"expiredAt" => 0,
);
}
}, $blkputRets['contexts']);
}
}
// 上传分片
$uploaded = 0;
while ($uploaded < $this->size) {
$blockSize = $this->blockSize($uploaded);
$blockIndex = $uploaded / $this->partSize;
if (!is_int($blockIndex)) {
throw new \Exception("v1 part size changed");
}
// 如果已上传该分片且没有过期
if (isset($this->contexts[$blockIndex]) && $this->contexts[$blockIndex]["expiredAt"] >= time()) {
$uploaded += $blockSize;
fseek($this->inputStream, $blockSize, SEEK_CUR);
continue;
}
$data = fread($this->inputStream, $blockSize);
if ($data === false) {
throw new \Exception("file read failed", 1);
}
$crc = \Qiniu\crc32_data($data);
$response = $this->makeBlock($data, $blockSize);
$ret = null;
if ($response->ok() && $response->json() != null) {
$ret = $response->json();
}
if ($response->statusCode < 0) {
list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($this->upToken);
if ($err != null) {
return array(null, $err);
}
list($upHostBackup, $err) = $this->config->getUpBackupHostV2($accessKey, $bucket, $this->reqOpt);
if ($err != null) {
return array(null, $err);
}
$this->host = $upHostBackup;
}
if ($response->needRetry() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
$response = $this->makeBlock($data, $blockSize);
$ret = $response->json();
}
if (!$response->ok() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
return array(null, new Error($this->currentUrl, $response));
}
// 如果可以在已上传取到说明是过期分片直接修改已上传信息,否则是新的片添加到已上传分片尾部
if (isset($this->contexts[$blockIndex])) {
$this->contexts[$blockIndex] = array(
'ctx' => $ret['ctx'],
'expiredAt' => $ret['expired_at'],
);
} else {
array_push($this->contexts, array(
'ctx' => $ret['ctx'],
'expiredAt' => $ret['expired_at'],
));
}
$uploaded += $blockSize;
// 记录断点
if ($this->resumeRecordFile !== null) {
$recordData = array(
'contexts' => $this->contexts,
'uploaded' => $uploaded
);
$recordData = json_encode($recordData);
if ($recordData) {
$isWritten = file_put_contents($this->resumeRecordFile, $recordData);
if ($isWritten === false) {
error_log("write resumeRecordFile failed");
}
} else {
error_log('resumeRecordData encode failed');
}
}
}
// 完成上传
list($ret, $err) = $this->makeFile($fname);
if ($err !== null) {
$response = $err->getResponse();
if ($isResumeUpload && $response->statusCode === 701) {
fseek($this->inputStream, 0);
return $this->uploadV1($fname);
}
}
return array($ret, $err);
}
/**
* @param string $fname 文件名
* @param null|array $blkputRets
*
* @throws \Exception
*/
private function uploadV2($fname, $blkputRets = null)
{
$uploaded = 0;
$partNumber = 1;
$encodedObjectName = $this->key ? \Qiniu\base64_urlSafeEncode($this->key) : '~';
$isResumeUpload = $blkputRets !== null;
// 初始化 upload id
$err = null;
if ($blkputRets) {
if (isset($blkputRets["etags"]) && isset($blkputRets["uploadId"]) &&
isset($blkputRets["expiredAt"]) && $blkputRets["expiredAt"] > time() &&
$blkputRets["uploaded"] > 0 && is_array($blkputRets["etags"]) &&
is_string($blkputRets["uploadId"]) && is_int($blkputRets["expiredAt"])
) {
$this->finishedEtags['etags'] = $blkputRets["etags"];
$this->finishedEtags["uploadId"] = $blkputRets["uploadId"];
$this->finishedEtags["expiredAt"] = $blkputRets["expiredAt"];
$this->finishedEtags["uploaded"] = $blkputRets["uploaded"];
$uploaded = $blkputRets["uploaded"];
$partNumber = count($this->finishedEtags["etags"]) + 1;
} else {
$err = $this->makeInitReq($encodedObjectName);
}
} else {
$err = $this->makeInitReq($encodedObjectName);
}
if ($err != null) {
return array(null, $err);
}
// 上传分片
fseek($this->inputStream, $uploaded);
while ($uploaded < $this->size) {
$blockSize = $this->blockSize($uploaded);
$data = fread($this->inputStream, $blockSize);
if ($data === false) {
throw new \Exception("file read failed", 1);
}
$md5 = md5($data);
$response = $this->uploadPart(
$data,
$partNumber,
$this->finishedEtags["uploadId"],
$encodedObjectName,
$md5
);
$ret = null;
if ($response->ok() && $response->json() != null) {
$ret = $response->json();
}
if ($response->statusCode < 0) {
list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($this->upToken);
if ($err != null) {
return array(null, $err);
}
list($upHostBackup, $err) = $this->config->getUpBackupHostV2($accessKey, $bucket, $this->reqOpt);
if ($err != null) {
return array(null, $err);
}
$this->host = $upHostBackup;
}
if ($response->needRetry() || !isset($ret['md5']) || $md5 != $ret['md5']) {
$response = $this->uploadPart(
$data,
$partNumber,
$this->finishedEtags["uploadId"],
$encodedObjectName,
$md5
);
$ret = $response->json();
}
if ($isResumeUpload && $response->statusCode === 612) {
return $this->uploadV2($fname);
}
if (!$response->ok() || !isset($ret['md5']) || $md5 != $ret['md5']) {
return array(null, new Error($this->currentUrl, $response));
}
$blockStatus = array('etag' => $ret['etag'], 'partNumber' => $partNumber);
array_push($this->finishedEtags['etags'], $blockStatus);
$partNumber += 1;
$uploaded += $blockSize;
$this->finishedEtags['uploaded'] = $uploaded;
if ($this->resumeRecordFile !== null) {
$recordData = json_encode($this->finishedEtags);
if ($recordData) {
$isWritten = file_put_contents($this->resumeRecordFile, $recordData);
if ($isWritten === false) {
error_log("write resumeRecordFile failed");
}
} else {
error_log('resumeRecordData encode failed');
}
}
}
list($ret, $err) = $this->completeParts($fname, $this->finishedEtags['uploadId'], $encodedObjectName);
if ($err !== null) {
$response = $err->getResponse();
if ($isResumeUpload && $response->statusCode === 612) {
return $this->uploadV2($fname);
}
}
return array($ret, $err);
}
/**
* 创建块
*/
private function makeBlock($block, $blockSize)
{
$url = $this->host . '/mkblk/' . $blockSize;
return $this->post($url, $block);
}
private function fileUrl($fname)
{
$url = $this->host . '/mkfile/' . $this->size;
$url .= '/mimeType/' . \Qiniu\base64_urlSafeEncode($this->mime);
if ($this->key != null) {
$url .= '/key/' . \Qiniu\base64_urlSafeEncode($this->key);
}
$url .= '/fname/' . \Qiniu\base64_urlSafeEncode($fname);
if (!empty($this->params)) {
foreach ($this->params as $key => $value) {
$val = \Qiniu\base64_urlSafeEncode($value);
$url .= "/$key/$val";
}
}
return $url;
}
/**
* 创建文件
*
* @param string $fname 文件名
* @return array{array | null, Error | null}
*/
private function makeFile($fname)
{
$url = $this->fileUrl($fname);
$body = implode(',', array_map(function ($ctx) {
return $ctx['ctx'];
}, $this->contexts));
$response = $this->post($url, $body);
if ($response->needRetry()) {
$response = $this->post($url, $body);
}
if ($response->statusCode === 200 || $response->statusCode === 701) {
if ($this->resumeRecordFile !== null) {
@unlink($this->resumeRecordFile);
}
}
if (!$response->ok()) {
return array(null, new Error($this->currentUrl, $response));
}
return array($response->json(), null);
}
private function post($url, $data)
{
$this->currentUrl = $url;
$headers = array('Authorization' => 'UpToken ' . $this->upToken);
return Client::post($url, $data, $headers, $this->reqOpt);
}
private function blockSize($uploaded)
{
if ($this->size < $uploaded + $this->partSize) {
return $this->size - $uploaded;
}
return $this->partSize;
}
private function makeInitReq($encodedObjectName)
{
list($ret, $err) = $this->initReq($encodedObjectName);
if ($ret == null) {
return $err;
}
$this->finishedEtags["uploadId"] = $ret['uploadId'];
$this->finishedEtags["expiredAt"] = $ret['expireAt'];
return $err;
}
/**
* 初始化上传任务
*/
private function initReq($encodedObjectName)
{
$url = $this->host . '/buckets/' . $this->bucket . '/objects/' . $encodedObjectName . '/uploads';
$headers = array(
'Authorization' => 'UpToken ' . $this->upToken,
'Content-Type' => 'application/json'
);
$response = $this->postWithHeaders($url, null, $headers);
$ret = $response->json();
if ($response->ok() && $ret != null) {
return array($ret, null);
}
return array(null, new Error($url, $response));
}
/**
* 分块上传v2
*/
private function uploadPart($block, $partNumber, $uploadId, $encodedObjectName, $md5)
{
$headers = array(
'Authorization' => 'UpToken ' . $this->upToken,
'Content-Type' => 'application/octet-stream',
'Content-MD5' => $md5
);
$url = $this->host . '/buckets/' . $this->bucket . '/objects/' . $encodedObjectName .
'/uploads/' . $uploadId . '/' . $partNumber;
$response = $this->put($url, $block, $headers);
if ($response->statusCode === 612) {
if ($this->resumeRecordFile !== null) {
@unlink($this->resumeRecordFile);
}
}
return $response;
}
/**
* 完成分片上传V2
*
* @param string $fname 文件名
* @param int $uploadId 由 {@see initReq} 获取
* @param string $encodedObjectName 经过编码的存储路径
* @return array{array | null, Error | null}
*/
private function completeParts($fname, $uploadId, $encodedObjectName)
{
$headers = array(
'Authorization' => 'UpToken ' . $this->upToken,
'Content-Type' => 'application/json'
);
$etags = $this->finishedEtags['etags'];
$sortedEtags = \Qiniu\arraySort($etags, 'partNumber');
$metadata = array();
$customVars = array();
if ($this->params) {
foreach ($this->params as $k => $v) {
if (strpos($k, 'x:') === 0) {
$customVars[$k] = $v;
} elseif (strpos($k, 'x-qn-meta-') === 0) {
$metadata[$k] = $v;
}
}
}
if (empty($metadata)) {
$metadata = null;
}
if (empty($customVars)) {
$customVars = null;
}
$body = array(
'fname' => $fname,
'mimeType' => $this->mime,
'metadata' => $metadata,
'customVars' => $customVars,
'parts' => $sortedEtags
);
$jsonBody = json_encode($body);
$url = $this->host . '/buckets/' . $this->bucket . '/objects/' . $encodedObjectName . '/uploads/' . $uploadId;
$response = $this->postWithHeaders($url, $jsonBody, $headers);
if ($response->needRetry()) {
$response = $this->postWithHeaders($url, $jsonBody, $headers);
}
if ($response->statusCode === 200 || $response->statusCode === 612) {
if ($this->resumeRecordFile !== null) {
@unlink($this->resumeRecordFile);
}
}
if (!$response->ok()) {
return array(null, new Error($this->currentUrl, $response));
}
return array($response->json(), null);
}
private function put($url, $data, $headers)
{
$this->currentUrl = $url;
return Client::put($url, $data, $headers, $this->reqOpt);
}
private function postWithHeaders($url, $data, $headers)
{
$this->currentUrl = $url;
return Client::post($url, $data, $headers, $this->reqOpt);
}
}

View File

@ -0,0 +1,176 @@
<?php
namespace Qiniu\Storage;
use Qiniu\Config;
use Qiniu\Http\HttpClient;
use Qiniu\Http\RequestOptions;
use Qiniu\Storage\ResumeUploader;
use Qiniu\Storage\FormUploader;
/**
* 主要涉及了资源上传接口的实现
*
* @link http://developer.qiniu.com/docs/v6/api/reference/up/
*/
final class UploadManager
{
private $config;
/**
* @var RequestOptions
*/
private $reqOpt;
/**
* @param Config|null $config
* @param RequestOptions|null $reqOpt
*/
public function __construct(Config $config = null, RequestOptions $reqOpt = null)
{
if ($config === null) {
$config = new Config();
}
$this->config = $config;
if ($reqOpt === null) {
$reqOpt = new RequestOptions();
}
$this->reqOpt = $reqOpt;
}
/**
* 上传二进制流到七牛
*
* @param string $upToken 上传凭证
* @param string $key 上传文件名
* @param string $data 上传二进制流
* @param array<string, string> $params 自定义变量,规格参考
* http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
* @param string $mime 上传数据的mimeType
* @param string $fname
* @param RequestOptions $reqOpt
* @return array 包含已上传文件的信息,类似:
* [
* "hash" => "<Hash string>",
* "key" => "<Key string>"
* ]
*/
public function put(
$upToken,
$key,
$data,
$params = null,
$mime = 'application/octet-stream',
$fname = "default_filename",
$reqOpt = null
) {
$reqOpt = $reqOpt === null ? $this->reqOpt : $reqOpt;
$params = self::trimParams($params);
return FormUploader::put(
$upToken,
$key,
$data,
$this->config,
$params,
$mime,
$fname,
$reqOpt
);
}
/**
* 上传文件到七牛
*
* @param string $upToken 上传凭证
* @param string $key 上传文件名
* @param string $filePath 上传文件的路径
* @param array<string, mixed> $params 定义变量,规格参考
* http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
* @param boolean $mime 上传数据的mimeType
* @param string $checkCrc 是否校验crc32
* @param string $resumeRecordFile 断点续传文件路径 默认为null
* @param string $version 分片上传版本 目前支持v1/v2版本 默认v1
* @param int $partSize 分片上传v2字段 默认大小为4MB 分片大小范围为1 MB - 1 GB
*
* @return array<string, mixed> 包含已上传文件的信息,类似:
* [
* "hash" => "<Hash string>",
* "key" => "<Key string>"
* ]
* @throws \Exception
*/
public function putFile(
$upToken,
$key,
$filePath,
$params = null,
$mime = 'application/octet-stream',
$checkCrc = false,
$resumeRecordFile = null,
$version = 'v1',
$partSize = config::BLOCK_SIZE,
$reqOpt = null
) {
$reqOpt = $reqOpt === null ? $this->reqOpt : $reqOpt;
$file = fopen($filePath, 'rb');
if ($file === false) {
throw new \Exception("file can not open", 1);
}
$params = self::trimParams($params);
$stat = fstat($file);
$size = $stat['size'];
if ($size <= Config::BLOCK_SIZE) {
$data = fread($file, $size);
fclose($file);
if ($data === false) {
throw new \Exception("file can not read", 1);
}
return FormUploader::put(
$upToken,
$key,
$data,
$this->config,
$params,
$mime,
basename($filePath),
$reqOpt
);
}
$up = new ResumeUploader(
$upToken,
$key,
$file,
$size,
$params,
$mime,
$this->config,
$resumeRecordFile,
$version,
$partSize,
$reqOpt
);
$ret = $up->upload(basename($filePath));
fclose($file);
return $ret;
}
public static function trimParams($params)
{
if ($params === null) {
return null;
}
$ret = array();
foreach ($params as $k => $v) {
$pos1 = strpos($k, 'x:');
$pos2 = strpos($k, 'x-qn-meta-');
if (($pos1 === 0 || $pos2 === 0) && !empty($v)) {
$ret[$k] = $v;
}
}
return $ret;
}
}

58
vendor/qiniu/php-sdk/src/Qiniu/Zone.php vendored Normal file
View File

@ -0,0 +1,58 @@
<?php
namespace Qiniu;
use Qiniu\Region;
class Zone extends Region
{
public static function zonez0()
{
return parent::regionHuadong();
}
public static function zonez1()
{
return parent::regionHuabei();
}
public static function zonez2()
{
return parent::regionHuanan();
}
public static function zoneCnEast2()
{
return parent::regionHuadong2();
}
public static function zoneAs0()
{
return parent::regionSingapore();
}
public static function zoneNa0()
{
return parent::regionNorthAmerica();
}
public static function qvmZonez0()
{
return parent::qvmRegionHuadong();
}
public static function qvmZonez1()
{
return parent::qvmRegionHuabei();
}
public static function queryZone(
$ak,
$bucket,
$ucHost = null,
$backupUcHosts = array(),
$retryTimes = 2,
$reqOpt = null
) {
return parent::queryRegion($ak, $bucket, $ucHost, $backupUcHosts, $retryTimes, $reqOpt);
}
}

View File

@ -0,0 +1,346 @@
<?php
namespace Qiniu;
use Qiniu\Config;
if (!defined('QINIU_FUNCTIONS_VERSION')) {
define('QINIU_FUNCTIONS_VERSION', Config::SDK_VER);
/**
* 计算文件的crc32检验码:
*
* @param $file string 待计算校验码的文件路径
*
* @return string 文件内容的crc32校验码
*/
function crc32_file($file)
{
$hash = hash_file('crc32b', $file);
$array = unpack('N', pack('H*', $hash));
return sprintf('%u', $array[1]);
}
/**
* 计算输入流的crc32检验码
*
* @param $data string 待计算校验码的字符串
*
* @return string 输入字符串的crc32校验码
*/
function crc32_data($data)
{
$hash = hash('crc32b', $data);
$array = unpack('N', pack('H*', $hash));
return sprintf('%u', $array[1]);
}
/**
* 对提供的数据进行urlsafe的base64编码。
*
* @param string $data 待编码的数据,一般为字符串
*
* @return string 编码后的字符串
* @link http://developer.qiniu.com/docs/v6/api/overview/appendix.html#urlsafe-base64
*/
function base64_urlSafeEncode($data)
{
$find = array('+', '/');
$replace = array('-', '_');
return str_replace($find, $replace, base64_encode($data));
}
/**
* 对提供的urlsafe的base64编码的数据进行解码
*
* @param string $str 待解码的数据,一般为字符串
*
* @return string 解码后的字符串
*/
function base64_urlSafeDecode($str)
{
$find = array('-', '_');
$replace = array('+', '/');
return base64_decode(str_replace($find, $replace, $str));
}
/**
* 二维数组根据某个字段排序
* @param array $array 要排序的数组
* @param string $key 要排序的键
* @param string $sort 排序类型 SORT_ASC SORT_DESC
* return array 排序后的数组
*/
function arraySort($array, $key, $sort = SORT_ASC)
{
$keysValue = array();
foreach ($array as $k => $v) {
$keysValue[$k] = $v[$key];
}
array_multisort($keysValue, $sort, $array);
return $array;
}
/**
* Wrapper for JSON decode that implements error detection with helpful
* error messages.
*
* @param string $json JSON data to parse
* @param bool $assoc When true, returned objects will be converted
* into associative arrays.
* @param int $depth User specified recursion depth.
*
* @return mixed
* @throws \InvalidArgumentException if the JSON cannot be parsed.
* @link http://www.php.net/manual/en/function.json-decode.php
*/
function json_decode($json, $assoc = false, $depth = 512)
{
static $jsonErrors = array(
JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded',
JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch',
JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found',
JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON',
JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded'
);
if (empty($json)) {
return null;
}
$data = \json_decode($json, $assoc, $depth);
if (JSON_ERROR_NONE !== json_last_error()) {
$last = json_last_error();
throw new \InvalidArgumentException(
'Unable to parse JSON data: '
. (isset($jsonErrors[$last])
? $jsonErrors[$last]
: 'Unknown error')
);
}
return $data;
}
/**
* 计算七牛API中的数据格式
*
* @param string $bucket 待操作的空间名
* @param string $key 待操作的文件名
*
* @return string 符合七牛API规格的数据格式
* @link https://developer.qiniu.com/kodo/api/data-format
*/
function entry($bucket, $key = null)
{
$en = $bucket;
if ($key !== null) {
$en = $bucket . ':' . $key;
}
return base64_urlSafeEncode($en);
}
function decodeEntry($entry)
{
$en = base64_urlSafeDecode($entry);
$en = explode(':', $en);
if (count($en) == 1) {
return array($en[0], null);
}
return array($en[0], $en[1]);
}
/**
* array 辅助方法无值时不set
*
* @param array $array 待操作array
* @param string $key key
* @param string $value value 为null时 不设置
*
* @return array 原来的array便于连续操作
*/
function setWithoutEmpty(&$array, $key, $value)
{
if (!empty($value)) {
$array[$key] = $value;
}
return $array;
}
/**
* 缩略图链接拼接
*
* @param string $url 图片链接
* @param int $mode 缩略模式
* @param int $width 宽度
* @param int $height 长度
* @param string $format 输出类型
* @param int $quality 图片质量
* @param int $interlace 是否支持渐进显示
* @param int $ignoreError 忽略结果
* @return string
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
function thumbnail(
$url,
$mode,
$width,
$height,
$format = null,
$quality = null,
$interlace = null,
$ignoreError = 1
) {
static $imageUrlBuilder = null;
if (is_null($imageUrlBuilder)) {
$imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
}
return call_user_func_array(array($imageUrlBuilder, 'thumbnail'), func_get_args());
}
/**
* 图片水印
*
* @param string $url 图片链接
* @param string $image 水印图片链接
* @param numeric $dissolve 透明度
* @param string $gravity 水印位置
* @param numeric $dx 横轴边距
* @param numeric $dy 纵轴边距
* @param numeric $watermarkScale 自适应原图的短边比例
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html
* @return string
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
function waterImg(
$url,
$image,
$dissolve = 100,
$gravity = 'SouthEast',
$dx = null,
$dy = null,
$watermarkScale = null
) {
static $imageUrlBuilder = null;
if (is_null($imageUrlBuilder)) {
$imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
}
return call_user_func_array(array($imageUrlBuilder, 'waterImg'), func_get_args());
}
/**
* 文字水印
*
* @param string $url 图片链接
* @param string $text 文字
* @param string $font 文字字体
* @param string $fontSize 文字字号
* @param string $fontColor 文字颜色
* @param numeric $dissolve 透明度
* @param string $gravity 水印位置
* @param numeric $dx 横轴边距
* @param numeric $dy 纵轴边距
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html#text-watermark
* @return string
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
function waterText(
$url,
$text,
$font = '黑体',
$fontSize = 0,
$fontColor = null,
$dissolve = 100,
$gravity = 'SouthEast',
$dx = null,
$dy = null
) {
static $imageUrlBuilder = null;
if (is_null($imageUrlBuilder)) {
$imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
}
return call_user_func_array(array($imageUrlBuilder, 'waterText'), func_get_args());
}
/**
* 从uptoken解析accessKey和bucket
*
* @param $upToken
* @return array(ak,bucket,err=null)
*/
function explodeUpToken($upToken)
{
$items = explode(':', $upToken);
if (count($items) != 3) {
return array(null, null, "invalid uptoken");
}
$accessKey = $items[0];
$putPolicy = json_decode(base64_urlSafeDecode($items[2]));
$scope = $putPolicy->scope;
$scopeItems = explode(':', $scope);
$bucket = $scopeItems[0];
return array($accessKey, $bucket, null);
}
// polyfill ucwords for `php version < 5.4.32` or `5.5.0 <= php version < 5.5.16`
if (version_compare(phpversion(), "5.4.32") < 0 ||
(
version_compare(phpversion(), "5.5.0") >= 0 &&
version_compare(phpversion(), "5.5.16") < 0
)
) {
function ucwords($str, $delimiters = " \t\r\n\f\v")
{
$delims = preg_split('//u', $delimiters, -1, PREG_SPLIT_NO_EMPTY);
foreach ($delims as $delim) {
$str = implode($delim, array_map('ucfirst', explode($delim, $str)));
}
return $str;
}
} else {
function ucwords($str, $delimiters)
{
return \ucwords($str, $delimiters);
}
}
/**
* 将 parse_url 的结果转换回字符串
* TODO: add unit test
*
* @param $parsed_url - parse_url 的结果
* @return string
*/
function unparse_url($parsed_url)
{
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
return "$scheme$user$pass$host$port$path$query$fragment";
}
}