diff --git a/Plugin.php b/Plugin.php
new file mode 100644
index 0000000..431c73e
--- /dev/null
+++ b/Plugin.php
@@ -0,0 +1,602 @@
+uploadHandle = array('Qiniu_Plugin', 'uploadHandle');
+ Typecho_Plugin::factory('Widget_Upload')->modifyHandle = array('Qiniu_Plugin', 'modifyHandle');
+ Typecho_Plugin::factory('Widget_Upload')->deleteHandle = array('Qiniu_Plugin', 'deleteHandle');
+ Typecho_Plugin::factory('Widget_Upload')->attachmentHandle = array('Qiniu_Plugin', 'attachmentHandle');
+
+ // 添加文章内容输出时的EXIF信息显示
+ Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = array('Qiniu_Plugin', 'parseContent');
+ Typecho_Plugin::factory('Widget_Abstract_Contents')->excerptEx = array('Qiniu_Plugin', 'parseContent');
+
+ // 使用安全的CSS加载方式
+ Typecho_Plugin::factory('Widget_Archive')->footer = array('Qiniu_Plugin', 'footer');
+
+ return _t('插件已激活,请先配置七牛云信息!');
+ }
+
+ // 禁用插件
+ public static function deactivate()
+ {
+ return _t('插件已禁用');
+ }
+
+ // 插件配置面板
+ public static function config(Typecho_Widget_Helper_Form $form)
+ {
+ $bucket = new Typecho_Widget_Helper_Form_Element_Text('bucket', null, null, _t('空间名称:'), _t('七牛云存储空间名称'));
+ $bucket->addRule('required', _t('"空间名称"不能为空!'));
+ $form->addInput($bucket);
+
+ $accesskey = new Typecho_Widget_Helper_Form_Element_Text('accesskey', null, null, _t('AccessKey:'), _t('从七牛云控制台获取'));
+ $form->addInput($accesskey->addRule('required', _t('AccessKey不能为空!')));
+
+ $secretkey = new Typecho_Widget_Helper_Form_Element_Text('secretkey', null, null, _t('SecretKey:'), _t('从七牛云控制台获取'));
+ $form->addInput($secretkey->addRule('required', _t('SecretKey不能为空!')));
+
+ $domain = new Typecho_Widget_Helper_Form_Element_Text('domain', null, 'https://', _t('绑定域名:'), _t('空间绑定的域名,如:https://cdn.example.com'));
+ $form->addInput($domain->addRule('required', _t('请填写空间绑定的域名!'))->addRule('url', _t('您输入的域名格式错误!')));
+
+ $savepath = new Typecho_Widget_Helper_Form_Element_Text('savepath', null, 'typecho/{year}/{month}/', _t('保存路径前缀'), _t('可使用变量:{year}, {month}, {day}, {random}'));
+ $form->addInput($savepath);
+
+ // WebP转换开关
+ $webp = new Typecho_Widget_Helper_Form_Element_Radio('webp',
+ array('0' => _t('关闭'), '1' => _t('开启')),
+ '0',
+ _t('WebP自动转换:'),
+ _t('开启后上传的JPEG/PNG图片将自动转换为WebP格式'));
+ $form->addInput($webp);
+
+ // 图片质量设置
+ $quality = new Typecho_Widget_Helper_Form_Element_Text('quality', null, '85', _t('图片质量:'),
+ _t('设置图片压缩质量(1-100),仅对JPEG/PNG/WEBP格式有效。85为推荐值'));
+ $quality->addRule('required', _t('图片质量不能为空!'))
+ ->addRule('isInteger', _t('必须输入数字!'))
+ ->addRule('range', _t('请输入1-100之间的数字!'), array(1, 100));
+ $form->addInput($quality);
+
+ // EXIF信息开关
+ $exif = new Typecho_Widget_Helper_Form_Element_Radio('exif',
+ array('0' => _t('关闭'), '1' => _t('开启')),
+ '0',
+ _t('EXIF信息显示:'),
+ _t('开启后文章图片悬停时会显示拍摄信息(相机型号、光圈、快门等)'));
+ $form->addInput($exif);
+ }
+
+ // 个人用户配置面板
+ public static function personalConfig(Typecho_Widget_Helper_Form $form)
+ {
+ }
+
+ // 获得插件配置信息
+ public static function getConfig()
+ {
+ return Typecho_Widget::widget('Widget_Options')->plugin('Qiniu');
+ }
+
+ // 安全的CSS加载方式
+ public static function footer()
+ {
+ $option = self::getConfig();
+ if ($option->exif && !self::$_cssLoaded) {
+ // 只在文章页面加载CSS
+ $widget = Typecho_Widget::widget('Widget_Archive');
+ if ($widget->is('single')) {
+ echo '';
+ self::$_cssLoaded = true;
+ }
+ }
+ }
+
+ // 输出CSS内容
+ private static function echoCss()
+ {
+ echo '.qiniu-exif-container {
+ display: inline-block;
+ position: relative;
+ max-width: 100%;
+}
+.qiniu-exif-info {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ background: rgba(0, 0, 0, 0.85);
+ color: white;
+ padding: 8px 10px;
+ font-size: 12px;
+ line-height: 1.4;
+ opacity: 0;
+ visibility: hidden;
+ transition: opacity 0.3s ease, visibility 0.3s ease;
+ z-index: 9999;
+ border-radius: 4px;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
+ pointer-events: none;
+}
+.qiniu-exif-container:hover .qiniu-exif-info {
+ opacity: 1;
+ visibility: visible;
+}
+.qiniu-exif-info div {
+ margin: 3px 0;
+}
+.qiniu-exif-info strong {
+ font-weight: 600;
+ color: #fff;
+}';
+ }
+
+ /**
+ * 解析文章内容,为图片添加EXIF容器
+ */
+ public static function parseContent($content, $widget, $lastResult)
+ {
+ $content = $lastResult ? $lastResult : $content;
+ $option = self::getConfig();
+
+ if (!$option->exif || !$widget->is('single')) {
+ return $content;
+ }
+
+ $pattern = '/
]*src=["\']([^"\']+)["\'][^>]*>/i';
+ return preg_replace_callback($pattern, function($matches) {
+ $imgTag = $matches[0];
+ $src = $matches[1];
+
+ $option = self::getConfig();
+ $domain = rtrim($option->domain, '/');
+
+ // 检查是否是七牛云的图片
+ if (strpos($src, $domain) === false) {
+ return $imgTag;
+ }
+
+ // 从URL中提取文件路径
+ $path = str_replace($domain, '', $src);
+ $path = ltrim(parse_url($path, PHP_URL_PATH), '/');
+
+ // 去除图片处理参数
+ if (strpos($path, '?') !== false) {
+ $path = substr($path, 0, strpos($path, '?'));
+ }
+
+ // 从数据库获取EXIF信息
+ $db = Typecho_Db::get();
+ $row = $db->fetchRow($db->select()->from('table.contents')
+ ->where('type = ?', 'attachment')
+ ->where('text LIKE ?', '%"path":"' . $path . '"%')
+ ->limit(1));
+
+ if ($row) {
+ $attachment = unserialize($row['text']);
+
+ if (isset($attachment['exif']) && !empty($attachment['exif'])) {
+ $exifData = $attachment['exif'];
+
+ // 生成简化的EXIF信息HTML
+ $exifHtml = '
';
+
+ if (!empty($exifData['camera'])) {
+ $exifHtml .= '
' . htmlspecialchars($exifData['camera']) . '
';
+ }
+
+ $details = array();
+ if (!empty($exifData['exposure'])) {
+ $details[] = '快门:' . htmlspecialchars($exifData['exposure']);
+ }
+ if (!empty($exifData['aperture'])) {
+ $details[] = '光圈:f/' . htmlspecialchars($exifData['aperture']);
+ }
+ if (!empty($exifData['iso'])) {
+ $details[] = 'ISO:' . htmlspecialchars($exifData['iso']);
+ }
+ if (!empty($exifData['focal_length'])) {
+ $details[] = '焦距:' . htmlspecialchars($exifData['focal_length']) . 'mm';
+ }
+
+ if (!empty($details)) {
+ $exifHtml .= '
' . implode(' ', $details) . '
';
+ }
+
+ $exifHtml .= '
';
+
+ return '' . $imgTag . $exifHtml . '
';
+ }
+ }
+
+ return $imgTag;
+ }, $content);
+ }
+
+ /**
+ * 提取EXIF信息
+ */
+ private static function extractExif($filePath)
+ {
+ if (!function_exists('exif_read_data')) {
+ return array();
+ }
+
+ $exif = @exif_read_data($filePath);
+ if (!$exif) {
+ return array();
+ }
+
+ $result = array();
+
+ // 相机型号
+ if (!empty($exif['Make']) || !empty($exif['Model'])) {
+ $make = !empty($exif['Make']) ? trim($exif['Make']) : '';
+ $model = !empty($exif['Model']) ? trim($exif['Model']) : '';
+ $result['camera'] = trim($make . ' ' . $model);
+ }
+
+ // 曝光时间
+ if (!empty($exif['ExposureTime'])) {
+ $result['exposure'] = $exif['ExposureTime'];
+ }
+
+ // 光圈值
+ if (!empty($exif['FNumber'])) {
+ $fnumber = is_array($exif['FNumber']) ? $exif['FNumber'][0] / $exif['FNumber'][1] : $exif['FNumber'];
+ $result['aperture'] = number_format($fnumber, 1);
+ }
+
+ // ISO
+ if (!empty($exif['ISOSpeedRatings'])) {
+ $result['iso'] = $exif['ISOSpeedRatings'];
+ }
+
+ // 焦距
+ if (!empty($exif['FocalLength'])) {
+ if (is_array($exif['FocalLength'])) {
+ $focal = $exif['FocalLength'][0] / $exif['FocalLength'][1];
+ } else {
+ $focal = $exif['FocalLength'];
+ }
+ $result['focal_length'] = number_format($focal, 1);
+ }
+
+ // 拍摄时间
+ if (!empty($exif['DateTimeOriginal'])) {
+ $result['date'] = date('Y-m-d H:i', strtotime($exif['DateTimeOriginal']));
+ }
+
+ return $result;
+ }
+
+ /**
+ * 检测是否支持WebP转换
+ */
+ private static function canConvertWebp()
+ {
+ if (!extension_loaded('gd')) {
+ return false;
+ }
+
+ $gdInfo = gd_info();
+ return isset($gdInfo['WebP Support']) && $gdInfo['WebP Support'];
+ }
+
+ /**
+ * 压缩图片并转换格式
+ */
+ private static function compressAndConvert($sourcePath, $targetExt, $quality)
+ {
+ $imageInfo = @getimagesize($sourcePath);
+ if (!$imageInfo) {
+ return $sourcePath;
+ }
+
+ $mime = $imageInfo['mime'];
+
+ // 创建图像资源
+ switch ($mime) {
+ case 'image/jpeg':
+ $image = imagecreatefromjpeg($sourcePath);
+ break;
+ case 'image/png':
+ $image = imagecreatefrompng($sourcePath);
+ imagepalettetotruecolor($image);
+ imagealphablending($image, false);
+ imagesavealpha($image, true);
+ break;
+ case 'image/gif':
+ $image = imagecreatefromgif($sourcePath);
+ break;
+ default:
+ return $sourcePath;
+ }
+
+ if (!$image) {
+ return $sourcePath;
+ }
+
+ $tempFile = tempnam(sys_get_temp_dir(), 'img_') . '.' . $targetExt;
+
+ $success = false;
+ switch ($targetExt) {
+ case 'jpg':
+ case 'jpeg':
+ $success = imagejpeg($image, $tempFile, $quality);
+ break;
+ case 'png':
+ $pngQuality = 9 - round(($quality / 100) * 9);
+ $success = imagepng($image, $tempFile, $pngQuality);
+ break;
+ case 'webp':
+ if (self::canConvertWebp()) {
+ $success = imagewebp($image, $tempFile, $quality);
+ }
+ break;
+ }
+
+ imagedestroy($image);
+
+ if ($success && file_exists($tempFile)) {
+ return $tempFile;
+ }
+
+ return $sourcePath;
+ }
+
+ /**
+ * 使用七牛基本图片处理
+ */
+ private static function applyQiniuStyle($url, $option)
+ {
+ $pathInfo = pathinfo($url);
+ $currentExt = isset($pathInfo['extension']) ? strtolower($pathInfo['extension']) : '';
+
+ $needQuality = $option->quality && $option->quality != 85;
+ $needWebP = $option->webp && $currentExt != 'webp';
+
+ if (!$needQuality && !$needWebP) {
+ return $url;
+ }
+
+ $params = array();
+
+ if ($needQuality) {
+ $params[] = 'q/' . intval($option->quality);
+ }
+
+ if ($needWebP) {
+ $params[] = 'format/webp';
+ }
+
+ if (!empty($params)) {
+ $separator = (strpos($url, '?') === false) ? '?' : '&';
+ $url .= $separator . 'imageView2/2/' . implode('/', $params);
+ }
+
+ return $url;
+ }
+
+ /**
+ * 删除文件
+ */
+ public static function deleteFile($filepath)
+ {
+ try {
+ $option = self::getConfig();
+ $auth = new Auth($option->accesskey, $option->secretkey);
+ $bucketMgr = new BucketManager($auth);
+
+ $err = $bucketMgr->delete($option->bucket, $filepath);
+
+ return $err === null;
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * 上传文件到七牛云 - 修复中文文件名问题
+ */
+ public static function uploadFile($file, $content = null)
+ {
+ error_reporting(0);
+
+ if (empty($file['name']) || !isset($file['tmp_name'])) {
+ return array('error' => 1, 'message' => '上传文件无效');
+ }
+
+ // 获取原始文件名
+ $originalName = basename($file['name']);
+
+ // 使用pathinfo正确处理中文字符
+ $pathInfo = pathinfo($originalName);
+ $originalExt = isset($pathInfo['extension']) ? strtolower($pathInfo['extension']) : '';
+ $baseName = isset($pathInfo['filename']) ? $pathInfo['filename'] : '';
+
+ // 校验扩展名
+ if (!Widget_Upload::checkFileType($originalExt)) {
+ return array('error' => 1, 'message' => '不允许上传此类型文件');
+ }
+
+ $option = self::getConfig();
+
+ if (empty($option->bucket) || empty($option->accesskey) || empty($option->secretkey) || empty($option->domain)) {
+ return array('error' => 1, 'message' => '请先正确配置七牛云插件');
+ }
+
+ $date = new Typecho_Date(Typecho_Widget::widget('Widget_Options')->gmtTime);
+
+ // 提取EXIF信息
+ $exifData = array();
+ if ($option->exif && in_array($originalExt, array('jpg', 'jpeg'))) {
+ $exifData = self::extractExif($file['tmp_name']);
+ }
+
+ // 处理图片压缩和转换
+ $tempFile = null;
+ $uploadPath = $file['tmp_name'];
+ $finalExt = $originalExt;
+
+ $supportedImages = array('jpg', 'jpeg', 'png', 'gif');
+ if (in_array($originalExt, $supportedImages)) {
+ $quality = isset($option->quality) ? intval($option->quality) : 85;
+ $quality = max(1, min(100, $quality));
+
+ $targetExt = $originalExt;
+ if ($option->webp && self::canConvertWebp() && $originalExt != 'gif') {
+ $targetExt = 'webp';
+ }
+
+ if ($targetExt != $originalExt || $quality != 85) {
+ $processedPath = self::compressAndConvert($file['tmp_name'], $targetExt, $quality);
+
+ if ($processedPath != $file['tmp_name']) {
+ $uploadPath = $processedPath;
+ $finalExt = $targetExt;
+ $tempFile = $processedPath;
+ }
+ }
+ }
+
+ // 生成存储路径 - 保持原始文件名,只替换文件系统不允许的字符
+ // 处理文件名,保留中文字符,只替换特殊字符
+ $safeName = $baseName;
+
+ // 替换文件系统不允许的字符,但保留中文字符
+ $safeName = preg_replace('/[<>:"\/\\|?*]/', '_', $safeName);
+ // 移除首尾空格
+ $safeName = trim($safeName);
+ // 如果名称为空,使用时间戳
+ if (empty($safeName)) {
+ $safeName = 'image_' . time();
+ }
+
+ if (isset($content)) {
+ $filePath = $content['attachment']->path;
+ self::deleteFile($filePath);
+ } else {
+ $savepath = preg_replace(array('/\{year\}/', '/\{month\}/', '/\{day\}/', '/\{random\}/'),
+ array($date->year, $date->month, $date->day, uniqid()),
+ $option->savepath);
+ $filePath = rtrim($savepath, '/') . '/' . $safeName . '.' . $finalExt;
+ }
+
+ if (!file_exists($uploadPath)) {
+ return array('error' => 1, 'message' => '上传文件不存在');
+ }
+
+ try {
+ $upManager = new UploadManager();
+ $auth = new Auth($option->accesskey, $option->secretkey);
+ $token = $auth->uploadToken($option->bucket);
+
+ // 七牛云SDK会自动处理文件名编码
+ list($ret, $error) = $upManager->putFile($token, $filePath, $uploadPath);
+
+ if ($tempFile && file_exists($tempFile) && $tempFile != $file['tmp_name']) {
+ @unlink($tempFile);
+ }
+
+ if ($error == null) {
+ $rawUrl = Typecho_Common::url($filePath, $option->domain);
+ $processedUrl = self::applyQiniuStyle($rawUrl, $option);
+
+ $fileSize = @filesize($uploadPath);
+
+ $result = array(
+ 'name' => $originalName, // 使用原始文件名
+ 'path' => $filePath,
+ 'size' => $fileSize ? (int)$fileSize : 0,
+ 'type' => $finalExt,
+ 'mime' => $finalExt == 'webp' ? 'image/webp' : Typecho_Common::mimeContentType($uploadPath),
+ 'url' => $processedUrl
+ );
+
+ if (!empty($exifData)) {
+ $result['exif'] = $exifData;
+ }
+
+ return $result;
+ } else {
+ return array('error' => 1, 'message' => '七牛云上传失败: ' . $error->message());
+ }
+ } catch (Exception $e) {
+ if ($tempFile && file_exists($tempFile) && $tempFile != $file['tmp_name']) {
+ @unlink($tempFile);
+ }
+ return array('error' => 1, 'message' => '上传异常: ' . $e->getMessage());
+ }
+ }
+
+ // 上传文件处理函数
+ public static function uploadHandle($file)
+ {
+ return self::uploadFile($file);
+ }
+
+ // 修改文件处理函数
+ public static function modifyHandle($content, $file)
+ {
+ return self::uploadFile($file, $content);
+ }
+
+ // 删除文件处理函数
+ public static function deleteHandle(array $content)
+ {
+ if (isset($content['attachment'])) {
+ self::deleteFile($content['attachment']->path);
+ }
+ }
+
+ // 获取实际文件绝对访问路径
+ public static function attachmentHandle(array $content)
+ {
+ $option = self::getConfig();
+ if (isset($content['attachment'])) {
+ $rawUrl = Typecho_Common::url($content['attachment']->path, $option->domain);
+ return self::applyQiniuStyle($rawUrl, $option);
+ }
+ return '';
+ }
+}
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..97d16ff
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,5 @@
+{
+ "require": {
+ "qiniu/php-sdk": "^7.14"
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..e20d4a2
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,142 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "e53b7aa650906cf4c75020ac18b97155",
+ "packages": [
+ {
+ "name": "myclabs/php-enum",
+ "version": "1.8.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/php-enum.git",
+ "reference": "e7be26966b7398204a234f8673fdad5ac6277802"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/php-enum/zipball/e7be26966b7398204a234f8673fdad5ac6277802",
+ "reference": "e7be26966b7398204a234f8673fdad5ac6277802",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": "^7.3 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5",
+ "squizlabs/php_codesniffer": "1.*",
+ "vimeo/psalm": "^4.6.2 || ^5.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "MyCLabs\\Enum\\": "src/"
+ },
+ "classmap": [
+ "stubs/Stringable.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP Enum contributors",
+ "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
+ }
+ ],
+ "description": "PHP Enum implementation",
+ "homepage": "https://github.com/myclabs/php-enum",
+ "keywords": [
+ "enum"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/php-enum/issues",
+ "source": "https://github.com/myclabs/php-enum/tree/1.8.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/mnapoli",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-01-14T11:49:03+00:00"
+ },
+ {
+ "name": "qiniu/php-sdk",
+ "version": "v7.14.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/qiniu/php-sdk.git",
+ "reference": "ee752ffa7263ce99fca0bd7340cf13c486a3516c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/qiniu/php-sdk/zipball/ee752ffa7263ce99fca0bd7340cf13c486a3516c",
+ "reference": "ee752ffa7263ce99fca0bd7340cf13c486a3516c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "ext-xml": "*",
+ "myclabs/php-enum": "~1.5.2 || ~1.6.6 || ~1.7.7 || ~1.8.4",
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "paragonie/random_compat": ">=2",
+ "phpunit/phpunit": "^4.8 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4",
+ "squizlabs/php_codesniffer": "^2.3 || ~3.6"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/Qiniu/functions.php",
+ "src/Qiniu/Http/Middleware/Middleware.php"
+ ],
+ "psr-4": {
+ "Qiniu\\": "src/Qiniu"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Qiniu",
+ "email": "sdk@qiniu.com",
+ "homepage": "http://www.qiniu.com"
+ }
+ ],
+ "description": "Qiniu Resource (Cloud) Storage SDK for PHP",
+ "homepage": "http://developer.qiniu.com/",
+ "keywords": [
+ "cloud",
+ "qiniu",
+ "sdk",
+ "storage"
+ ],
+ "support": {
+ "issues": "https://github.com/qiniu/php-sdk/issues",
+ "source": "https://github.com/qiniu/php-sdk/tree/v7.14.0"
+ },
+ "time": "2024-10-25T08:39:01+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": [],
+ "plugin-api-version": "2.0.0"
+}
diff --git a/php-sdk/.github/workflows/test-ci.yml b/php-sdk/.github/workflows/test-ci.yml
new file mode 100644
index 0000000..00f964e
--- /dev/null
+++ b/php-sdk/.github/workflows/test-ci.yml
@@ -0,0 +1,71 @@
+name: PHP CI with Composer
+
+on: [push, pull_request]
+
+jobs:
+ build:
+ strategy:
+ fail-fast: false
+ matrix:
+ php-versions: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Setup php for mock server
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.2'
+
+ - name: Install Go
+ uses: actions/setup-go@v2
+ with:
+ go-version: '1.21.x'
+
+ - name: Setup build-in server
+ run: |
+ nohup php -S localhost:9000 -t ./tests/mock-server/ > phpd.log 2>&1 &
+ echo $! > mock-server.pid
+
+ cd tests/socks5-server/
+ nohup go run main.go > ../../socks5.log 2>&1 &
+ echo $! > ../../socks-server.pid
+
+ - name: Setup php
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-versions }}
+
+ - name: Install dependencies
+ run: |
+ composer self-update
+ composer install --no-interaction --prefer-source --dev
+
+ - name: Run cases
+ run: |
+ ./vendor/bin/phpcs --standard=PSR2 src
+ ./vendor/bin/phpcs --standard=PSR2 examples
+ ./vendor/bin/phpcs --standard=PSR2 tests
+ ./vendor/bin/phpunit --coverage-clover=coverage.xml
+ cat mock-server.pid | xargs kill
+ cat socks-server.pid | xargs kill
+
+ env:
+ QINIU_ACCESS_KEY: ${{ secrets.QINIU_ACCESS_KEY }}
+ QINIU_SECRET_KEY: ${{ secrets.QINIU_SECRET_KEY }}
+ QINIU_TEST_BUCKET: ${{ secrets.QINIU_TEST_BUCKET }}
+ QINIU_TEST_DOMAIN: ${{ secrets.QINIU_TEST_DOMAIN }}
+
+ - name: Print mock server log
+ if: ${{ failure() }}
+ run: |
+ cat phpd.log
+
+ - name: Print socks5 server log
+ if: ${{ failure() }}
+ run: |
+ cat socks5.log
+
+ - name: After_success
+ run: bash <(curl -s https://codecov.io/bash)
diff --git a/php-sdk/.github/workflows/version-check.yml b/php-sdk/.github/workflows/version-check.yml
new file mode 100644
index 0000000..983a98f
--- /dev/null
+++ b/php-sdk/.github/workflows/version-check.yml
@@ -0,0 +1,19 @@
+name: PHP SDK Version Check
+on:
+ push:
+ tags:
+ - "v[0-9]+.[0-9]+.[0-9]+"
+jobs:
+ linux:
+ name: Version Check
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+ - name: Set env
+ run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV
+ - name: Check
+ run: |
+ set -e
+ grep -qF "## ${RELEASE_VERSION}" CHANGELOG.md
+ grep -qF "const SDK_VER = '${RELEASE_VERSION}';" src/Qiniu/Config.php
diff --git a/php-sdk/.gitignore b/php-sdk/.gitignore
new file mode 100644
index 0000000..4c842c8
--- /dev/null
+++ b/php-sdk/.gitignore
@@ -0,0 +1,12 @@
+*.phar
+*.zip
+build/artifacts
+phpunit.xml
+phpunit.functional.xml
+.DS_Store
+.swp
+.build
+composer.lock
+vendor
+src/package.xml
+.idea/
diff --git a/php-sdk/.scrutinizer.yml b/php-sdk/.scrutinizer.yml
new file mode 100644
index 0000000..6a2d0d8
--- /dev/null
+++ b/php-sdk/.scrutinizer.yml
@@ -0,0 +1,42 @@
+filter:
+ excluded_paths: [tests/*]
+checks:
+ php:
+ code_rating: true
+ remove_extra_empty_lines: true
+ remove_php_closing_tag: true
+ remove_trailing_whitespace: true
+ fix_use_statements:
+ remove_unused: true
+ preserve_multiple: false
+ preserve_blanklines: true
+ order_alphabetically: true
+ fix_php_opening_tag: true
+ fix_linefeed: true
+ fix_line_ending: true
+ fix_identation_4spaces: true
+ fix_doc_comments: true
+tools:
+ external_code_coverage:
+ timeout: 1200
+ runs: 3
+ php_analyzer: true
+ php_code_coverage: false
+ php_code_sniffer:
+ config:
+ standard: PSR2
+ filter:
+ paths: ['src']
+ php_loc:
+ enabled: true
+ excluded_dirs: [vendor, tests]
+ php_cpd:
+ enabled: true
+ excluded_dirs: [vendor, tests]
+build:
+ nodes:
+ analysis:
+ tests:
+ override:
+ - php-scrutinizer-run
+
diff --git a/php-sdk/CHANGELOG.md b/php-sdk/CHANGELOG.md
new file mode 100644
index 0000000..6f8da5d
--- /dev/null
+++ b/php-sdk/CHANGELOG.md
@@ -0,0 +1,196 @@
+# Changelog
+
+## 7.14.0 (2024-10-16)
+* 对象存储,持久化处理支持工作流模版
+
+## 7.13.0 (2024-09-05)
+* 对象存储,验证回调方法新增支持 Qiniu 签名
+* 对象存储,调整查询空间区域域名顺序与默认空间管理域名
+* 支持闲时任务配置
+
+## 7.12.1 (2024-02-21)
+* 对象存储,添加上传策略部分字段
+
+## 7.12.0 (2023-12-11)
+* 对象存储,支持归档直读存储
+* 对象存储,批量操作支持自动查询 rs 服务域名
+
+## 7.11.0 (2023-09-05)
+* 支持代理
+
+## 7.10.1 (2023-08-04)
+* 修复部分 API 调用中间件合并失败(#417)
+
+## 7.10.0 (2023-06-20)
+* 对象存储,新增请求中间件逻辑,方便拓展请求逻辑
+* 对象存储,新增备用 UC 域名用于查询区域域名
+* 对象存储,修复分片上传初始化失败无法快速失败
+* 对象存储,移除首尔区域
+
+## 7.9.0 (2023-03-31)
+* 对象存储,修复无法对 key 为空字符串的对象进行操作
+* 修复 301 重定向无法正确获取 header 信息
+* 对象存储,新增查询区域域名过期时间
+* 对象存储,更新获取区域域名的接口
+* 对象存储,更新查询 bucket 域名为 uc 服务
+* 对象存储,新增 uc 服务可配置
+
+## 7.8.0 (2022-10-25)
+* 移除不推荐域名,并增加区域亚太-首尔和华东-浙江2
+* 对象存储,修复断点上传的文件内容不正确
+* 对象存储,优化分片上传 ctx 超时检测
+
+## 7.7.0 (2022-09-02)
+* 对象存储,新增支持设置文件级别生命周期 setObjectLifecycle API
+* 对象存储,内置增加七牛新建存储区域域名信息
+* 修复当前已知问题
+
+## 7.6.0 (2022-06-08)
+* 对象存储,管理类 API 发送请求时增加 [X-Qiniu-Date](https://developer.qiniu.com/kodo/3924/common-request-headers) (生成请求的时间) header
+
+
+## 7.5.0 (2022-04-18)
+* 对象存储,新增支持 [深度归档存储类型](https://developer.qiniu.com/kodo/3956/kodo-category#deep_archive)
+
+## 7.4.3 (2022-04-01)
+* 优化签名算法逻辑
+
+## 7.4.2(2022-03-01)
+* 修复已知关于请求 Header 处理不当问题,比如没有处理为大小写不敏感等问题
+
+## 7.4.1(2021-09-24)
+* 修复了 分片上传 v2 已知问题,明确给出了参数不合理情况下对应的错误提示信息
+
+## 7.4.0 (2021-07-19)
+* 【对象存储】支持 [分片上传 v2](https://developer.qiniu.com/kodo/7458/multipartupload) 和 断点续传,使用方式见 [开发者文档](https://developer.qiniu.com/kodo/1241/php#resume-upload-file)
+
+## 7.3.0 (2020-09-24)
+### 新增
+* 【对象存储】增加异步抓取方法与demo
+* 【融合cdn】增加查询CDN刷新记录、查询CDN预取记录方法与demo
+* 【云短信】增加查询短信发送记录的方法
+* 【实时音视频】增加rtc停止房间的合流转推方法
+* 【内容审核】增加图片审核、视频审核方法与demo
+
+### 修复
+* 【对象存储】修复签算 token 时上传策略中的 forceSaveKey 字段不生效的问题
+* 【对象存储】修复更新空间事件通知规则方法
+
+### 优化
+* 【对象存储】创建空间迁移到mkbucketv3 api
+* 优化对 http2 返回头的判断
+* 优化 demo 中的文档注释说明
+* docs 目录下的 rtc demo 移动至 examples/rtc 目录下
+* docs 目录下的 sms demo 移动至 examples/sms 目录下
+
+## 7.2.10 (2019-10-28)
+* 去除云短信类类型指定
+* 修改不传文件名时存在表单上传错误的情况
+
+## 7.2.9 (2019-07-09)
+* 添加空间管理、云短信接口
+* 去除无效参数
+
+## 7.2.7 (2018-11-06)
+* 添加 QVM 内网上传到 KODO 的 zone 设置
+
+## 7.2.6 (2018-05-18)
+* 修复rs,rsf在不同机房默认的https域名
+
+## 7.2.5 (2018-05-10)
+* 修复表单上传中多余的参数checkCrc导致的fname错位问题
+
+## 7.2.4 (2018-05-09)
+### 增加
+* 连麦功能
+
+## 7.2.3 (2018-01-20)
+### 增加
+* 新加坡机房
+### 修正
+* 获取域名的入口域名
+* http回复头部兼容大小写
+
+## 7.2.2 (2017-11-06)
+### 增加
+* Qiniu算法的鉴权方法
+
+## 7.1.4 (2017-06-21)
+### 增加
+* cdn 文件/目录 刷新
+* cdn 获取 流量/带宽
+* cdn 获取域名的访问日志列表
+* cdn 对资源链接进行时间戳防盗链签名
+
+## 7.1.3 (2016-11-18)
+### 增加
+* move, copy操作增加force参数
+
+## 7.1.2 (2016-11-12)
+### 修正
+* 明确抛出获取各区域域名失败时的报错
+
+## 7.1.1 (2016-11-02)
+### 修正
+* 多区域配置文件存储目录从home修改到tmp目录
+
+
+## 7.1.0 (2016-10-22)
+### 增加
+* 多存储区域的支持
+
+## 7.0.8 (2016-07-19)
+### 增加
+* demo
+* https url 支持
+* deleteAfterDays 策略
+* 添加图片处理链接统一拼接方法 by @SherlockRen
+
+## 7.0.7 (2016-01-12)
+### 修正
+* PersistentFop参数pipeline和notify_url失效
+* resume 模式 close file inputstream
+
+## 7.0.6 (2015-12-05)
+### 修正
+* php7.0 Json 对空字符串解析单元测试报错
+* 开启安全模式或者设置可操作目录树时,设置CURLOPT_FOLLOWLOCATION报错, by @twocabbages
+* fetch 支持不指定key, by @sinkcup
+
+## 7.0.5 (2015-10-29)
+### 增加
+* 增加上传策略最小文件大小限制 fsizeMin
+* 增加常见examples
+
+## 7.0.4 (2015-07-23)
+### 修正
+* 一些地方的严格比较检查
+* resumeupload 备用地址失效
+
+## 7.0.3 (2015-07-10)
+### 修改
+* 多zone 支持
+
+## 7.0.2 (2015-04-18)
+### 修改
+* fetch 接口返回内容调整
+* pfop 接口调整
+
+###修正
+* exception 类调用
+
+## 7.0.1 (2015-03-27)
+### 增加
+* 增加代码注释
+
+## 7.0.0 (2015-02-03)
+
+### 增加
+* 简化上传接口
+* 自动选择断点续上传还是直传
+* 重构代码,接口和内部结构更清晰
+* 改变mime
+* 代码覆盖度报告
+* policy改为array, 便于灵活增加,并加入过期字段检查
+* 文件列表支持目录形式
+* 利用元编程方式支持 fop 和 pfop
diff --git a/php-sdk/CONTRIBUTING.md b/php-sdk/CONTRIBUTING.md
new file mode 100644
index 0000000..0466bf9
--- /dev/null
+++ b/php-sdk/CONTRIBUTING.md
@@ -0,0 +1,30 @@
+# 贡献代码指南
+
+我们非常欢迎大家来贡献代码,我们会向贡献者致以最诚挚的敬意。
+
+一般可以通过在Github上提交[Pull Request](https://github.com/qiniu/php-sdk)来贡献代码。
+
+## Pull Request要求
+
+- **[PSR-2 编码风格标准](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** 。要通过项目中的code sniffer检查。
+
+- **代码格式** 提交前 请按 ./vendor/bin/phpcbf --standard=PSR2 进行格式化。
+
+- **必须添加测试!** - 如果没有测试(单元测试、集成测试都可以),那么提交的补丁是不会通过的。
+
+- **记得更新文档** - 保证`README.md`以及其他相关文档及时更新,和代码的变更保持一致性。
+
+- **考虑我们的发布周期** - 我们的版本号会服从[SemVer v2.0.0](http://semver.org/),我们绝对不会随意变更对外的API。
+
+- **创建feature分支** - 最好不要从你的master分支提交 pull request。
+
+- **一个feature提交一个pull请求** - 如果你的代码变更了多个操作,那就提交多个pull请求吧。
+
+- **清晰的commit历史** - 保证你的pull请求的每次commit操作都是有意义的。如果你开发中需要执行多次的即时commit操作,那么请把它们放到一起再提交pull请求。
+
+## 运行测试
+
+``` bash
+./vendor/bin/phpunit tests/Qiniu/Tests/
+
+```
diff --git a/php-sdk/LICENSE b/php-sdk/LICENSE
new file mode 100644
index 0000000..ba646be
--- /dev/null
+++ b/php-sdk/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Qiniu, Ltd.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/php-sdk/README.md b/php-sdk/README.md
new file mode 100644
index 0000000..784d735
--- /dev/null
+++ b/php-sdk/README.md
@@ -0,0 +1,76 @@
+# Qiniu Cloud SDK for PHP
+[](LICENSE)
+[](https://travis-ci.org/qiniu/php-sdk)
+[](https://github.com/qiniu/php-sdk/releases)
+[](https://packagist.org/packages/qiniu/php-sdk)
+[](https://packagist.org/packages/qiniu/php-sdk)
+[](https://scrutinizer-ci.com/g/qiniu/php-sdk/?branch=master)
+[](https://codecov.io/gh/qiniu/php-sdk)
+[](https://gitter.im/qiniu/php-sdk?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[](http://weibo.com/qiniutek)
+
+
+## 安装
+
+推荐使用 `composer` 进行安装。可以使用 composer.json 声明依赖,或者运行下面的命令。SDK 包已经放到这里 [`qiniu/php-sdk`][install-packagist] 。
+
+```bash
+$ composer require qiniu/php-sdk
+```
+
+## 运行环境
+
+| Qiniu SDK版本 | PHP 版本 |
+|:--------------------:|:-----------------------------------------------:|
+| 7.x | cURL extension, 5.3 - 5.6, 7.0 - 7.4, 8.0-8.1 |
+| 6.x | cURL extension, 5.2 - 5.6 |
+
+## 使用方法
+
+### 上传
+```php
+use Qiniu\Storage\UploadManager;
+use Qiniu\Auth;
+...
+ $uploadMgr = new UploadManager();
+ $auth = new Auth($accessKey, $secretKey);
+ $token = $auth->uploadToken($bucket);
+ list($ret, $error) = $uploadMgr->putFile($token, 'key', 'filePath');
+...
+```
+
+## 测试
+
+``` bash
+$ ./vendor/bin/phpunit tests/Qiniu/Tests/
+```
+
+## 常见问题
+
+- `$error` 保留了请求响应的信息,失败情况下 `ret` 为 `none`, 将 `$error` 可以打印出来,提交给我们。
+- API 的使用 demo 可以参考 [examples](https://github.com/qiniu/php-sdk/tree/master/examples)。
+
+## 代码贡献
+
+详情参考[代码提交指南](https://github.com/qiniu/php-sdk/blob/master/CONTRIBUTING.md)。
+
+## 贡献记录
+
+- [所有贡献者](https://github.com/qiniu/php-sdk/contributors)
+
+## 联系我们
+
+- 如果需要帮助,请提交工单(在portal右侧点击咨询和建议提交工单,或者直接向 support@qiniu.com 发送邮件)
+- 如果有什么问题,可以到问答社区提问,[问答社区](https://qiniu.segmentfault.com/)
+- 更详细的文档,见[官方文档站](https://developer.qiniu.com/)
+- 如果发现了 bug, 欢迎提交 [issue](https://github.com/qiniu/php-sdk/issues)
+- 如果有功能需求,欢迎提交 [issue](https://github.com/qiniu/php-sdk/issues)
+- 如果要提交代码,欢迎提交 pull request
+- 欢迎关注我们的[微信](https://www.qiniu.com/#weixin) [微博](https://weibo.com/qiniutek),及时获取动态信息。
+
+## 代码许可
+
+The MIT License (MIT).详情见 [License文件](https://github.com/qiniu/php-sdk/blob/master/LICENSE).
+
+[packagist]: http://packagist.org
+[install-packagist]: https://packagist.org/packages/qiniu/php-sdk
diff --git a/php-sdk/autoload.php b/php-sdk/autoload.php
new file mode 100644
index 0000000..9efddd7
--- /dev/null
+++ b/php-sdk/autoload.php
@@ -0,0 +1,19 @@
+=5.3.3",
+ "ext-xml": "*",
+ "ext-curl": "*",
+ "myclabs/php-enum": "~1.5.2 || ~1.6.6 || ~1.7.7 || ~1.8.4"
+ },
+ "require-dev": {
+ "paragonie/random_compat": ">=2",
+ "phpunit/phpunit": "^4.8 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4",
+ "squizlabs/php_codesniffer": "^2.3 || ~3.6"
+ },
+ "autoload": {
+ "psr-4": {
+ "Qiniu\\": "src/Qiniu"
+ },
+ "files": [
+ "src/Qiniu/functions.php",
+ "src/Qiniu/Http/Middleware/Middleware.php"
+ ]
+ }
+}
diff --git a/php-sdk/examples/README.md b/php-sdk/examples/README.md
new file mode 100644
index 0000000..b7b4f98
--- /dev/null
+++ b/php-sdk/examples/README.md
@@ -0,0 +1,10 @@
+# examples
+
+这些 examples 旨在帮助你快速了解使用七牛的 SDK。这些 demo 都是可以直接运行的, 但是在运行之前需要填上您自己的参数。
+
+比如:
+
+* `$bucket` 需要填上您想操作的 [bucket名字](https://portal.qiniu.com/kodo/bucket)。
+* `$accessKey` 和 `$secretKey` 可以在我们的[管理后台](https://portal.qiniu.com/user/key)找到。
+* 在进行`视频转码`, `压缩文件`等异步操作时 需要使用到的队列名称也可以在我们[管理后台](https://portal.qiniu.com/dora/media-gate/pipeline)新建。
+
diff --git a/php-sdk/examples/bucket_lifecycleRule.php b/php-sdk/examples/bucket_lifecycleRule.php
new file mode 100644
index 0000000..f51524c
--- /dev/null
+++ b/php-sdk/examples/bucket_lifecycleRule.php
@@ -0,0 +1,42 @@
+bucketLifecycleRule(
+ $bucket,
+ $name,
+ $prefix,
+ $delete_after_days,
+ $to_line_after_days,
+ $to_archive_after_days,
+ $to_deep_archive_after_days,
+ $to_archive_ir_after_days
+);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/cdn_get_bandwidth.php b/php-sdk/examples/cdn_get_bandwidth.php
new file mode 100644
index 0000000..c9de0e6
--- /dev/null
+++ b/php-sdk/examples/cdn_get_bandwidth.php
@@ -0,0 +1,41 @@
+getBandwidthData(
+ $domains,
+ $startDate,
+ $endDate,
+ $granularity
+);
+
+if ($getBandwidthErr != null) {
+ var_dump($getBandwidthErr);
+} else {
+ echo "get bandwidth data success\n";
+ print_r($bandwidthData);
+}
diff --git a/php-sdk/examples/cdn_get_flux.php b/php-sdk/examples/cdn_get_flux.php
new file mode 100644
index 0000000..57df808
--- /dev/null
+++ b/php-sdk/examples/cdn_get_flux.php
@@ -0,0 +1,35 @@
+getFluxData($domains, $startDate, $endDate, $granularity);
+if ($getFluxErr != null) {
+ var_dump($getFluxErr);
+} else {
+ echo "get flux data success\n";
+ print_r($fluxData);
+}
diff --git a/php-sdk/examples/cdn_get_log_list.php b/php-sdk/examples/cdn_get_log_list.php
new file mode 100644
index 0000000..2b3f7dd
--- /dev/null
+++ b/php-sdk/examples/cdn_get_log_list.php
@@ -0,0 +1,31 @@
+getCdnLogList($domains, $logDate);
+if ($getLogErr != null) {
+ var_dump($getLogErr);
+} else {
+ echo "get cdn log list success\n";
+ print_r($logListData);
+}
diff --git a/php-sdk/examples/cdn_get_prefetch_list.php b/php-sdk/examples/cdn_get_prefetch_list.php
new file mode 100644
index 0000000..958e5eb
--- /dev/null
+++ b/php-sdk/examples/cdn_get_prefetch_list.php
@@ -0,0 +1,46 @@
+getCdnPrefetchList(
+ $requestId,
+ $urls,
+ $state,
+ $pageNo,
+ $pageSize,
+ $startTime,
+ $endTime
+);
+echo "\n====> query prefetch list: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/cdn_get_refresh_list.php b/php-sdk/examples/cdn_get_refresh_list.php
new file mode 100644
index 0000000..ad4fca2
--- /dev/null
+++ b/php-sdk/examples/cdn_get_refresh_list.php
@@ -0,0 +1,48 @@
+getCdnRefreshList(
+ $requestId,
+ $isDir,
+ $urls,
+ $state,
+ $pageNo,
+ $pageSize,
+ $startTime,
+ $endTime
+);
+echo "\n====> query refresh list: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/cdn_refresh_urls_dirs.php b/php-sdk/examples/cdn_refresh_urls_dirs.php
new file mode 100644
index 0000000..2140378
--- /dev/null
+++ b/php-sdk/examples/cdn_refresh_urls_dirs.php
@@ -0,0 +1,59 @@
+refreshUrlsAndDirs($urls, $dirs);
+if ($refreshErr != null) {
+ var_dump($refreshErr);
+} else {
+ echo "refresh request sent\n";
+ print_r($refreshResult);
+}
+
+//---------------------------------------- demo2 ----------------------------------------
+// 刷新文件
+
+list($refreshResult, $refreshErr) = $cdnManager->refreshUrls($urls);
+if ($refreshErr != null) {
+ var_dump($refreshErr);
+} else {
+ echo "refresh urls request sent\n";
+ print_r($refreshResult);
+}
+
+//---------------------------------------- demo3 ----------------------------------------
+// 刷新目录
+
+list($refreshResult, $refreshErr) = $cdnManager->refreshDirs($dirs);
+if ($refreshErr != null) {
+ var_dump($refreshErr);
+} else {
+ echo "refresh dirs request sent\n";
+ print_r($refreshResult);
+}
diff --git a/php-sdk/examples/cdn_timestamp_antileech.php b/php-sdk/examples/cdn_timestamp_antileech.php
new file mode 100644
index 0000000..f2d7855
--- /dev/null
+++ b/php-sdk/examples/cdn_timestamp_antileech.php
@@ -0,0 +1,20 @@
+censorImage($body);
+echo "\n====> Result is: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/censor_video.php b/php-sdk/examples/censor_video.php
new file mode 100644
index 0000000..7ac056f
--- /dev/null
+++ b/php-sdk/examples/censor_video.php
@@ -0,0 +1,52 @@
+censorVideo($body);
+echo "\n====> Result is: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "job_id is: $jobid\n";
+}
+
+// 查询视频审核结果
+list($ret, $err) = $argusManager->censorStatus($jobid);
+echo "\n====> job status: \n";
+
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/delete_bucket.php b/php-sdk/examples/delete_bucket.php
new file mode 100644
index 0000000..325a47a
--- /dev/null
+++ b/php-sdk/examples/delete_bucket.php
@@ -0,0 +1,27 @@
+deleteBucket($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/delete_bucketEvent.php b/php-sdk/examples/delete_bucketEvent.php
new file mode 100644
index 0000000..7eb744d
--- /dev/null
+++ b/php-sdk/examples/delete_bucketEvent.php
@@ -0,0 +1,28 @@
+deleteBucketEvent($bucket, $name);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/delete_bucketLifecycleRule.php b/php-sdk/examples/delete_bucketLifecycleRule.php
new file mode 100644
index 0000000..2146b1b
--- /dev/null
+++ b/php-sdk/examples/delete_bucketLifecycleRule.php
@@ -0,0 +1,27 @@
+deleteBucketLifecycleRule($bucket, $name);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/get_bucketEvents.php b/php-sdk/examples/get_bucketEvents.php
new file mode 100644
index 0000000..2379584
--- /dev/null
+++ b/php-sdk/examples/get_bucketEvents.php
@@ -0,0 +1,26 @@
+getBucketEvents($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/get_bucketLifecycleRules.php b/php-sdk/examples/get_bucketLifecycleRules.php
new file mode 100644
index 0000000..a35feed
--- /dev/null
+++ b/php-sdk/examples/get_bucketLifecycleRules.php
@@ -0,0 +1,26 @@
+getBucketLifecycleRules($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/get_bucketList.php b/php-sdk/examples/get_bucketList.php
new file mode 100644
index 0000000..6a2f7b0
--- /dev/null
+++ b/php-sdk/examples/get_bucketList.php
@@ -0,0 +1,26 @@
+listbuckets($region);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/get_bucketQuota.php b/php-sdk/examples/get_bucketQuota.php
new file mode 100644
index 0000000..93474b5
--- /dev/null
+++ b/php-sdk/examples/get_bucketQuota.php
@@ -0,0 +1,26 @@
+getBucketQuota($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/get_bucketinfo.php b/php-sdk/examples/get_bucketinfo.php
new file mode 100644
index 0000000..98fd9f7
--- /dev/null
+++ b/php-sdk/examples/get_bucketinfo.php
@@ -0,0 +1,25 @@
+bucketInfo($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/get_bucketinfos.php b/php-sdk/examples/get_bucketinfos.php
new file mode 100644
index 0000000..5eec1d8
--- /dev/null
+++ b/php-sdk/examples/get_bucketinfos.php
@@ -0,0 +1,26 @@
+bucketInfos($region);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/get_corsRules.php b/php-sdk/examples/get_corsRules.php
new file mode 100644
index 0000000..58e28be
--- /dev/null
+++ b/php-sdk/examples/get_corsRules.php
@@ -0,0 +1,26 @@
+getCorsRules($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/image_url_builder.php b/php-sdk/examples/image_url_builder.php
new file mode 100644
index 0000000..20e2b00
--- /dev/null
+++ b/php-sdk/examples/image_url_builder.php
@@ -0,0 +1,74 @@
+
+ */
+$thumbLink = $imageUrlBuilder->thumbnail($url, 1, 100, 100);
+
+// 函数方式调用 也可拼接多个操作参数 图片+水印
+$thumbLink2 = \Qiniu\thumbnail($url2, 1, 100, 100);
+var_dump($thumbLink, $thumbLink2);
+
+/**
+ * 图片水印
+ *
+ * @param string $url 图片链接
+ * @param string $image 水印图片链接
+ * @param int $dissolve 透明度 [可选]
+ * @param string $gravity 水印位置 [可选]
+ * @param int $dx 横轴边距 [可选]
+ * @param int $dy 纵轴边距 [可选]
+ * @param int $watermarkScale 自适应原图的短边比例 [可选]
+ * @link https://developer.qiniu.com/dora/api/1316/image-watermarking-processing-watermark
+ * @return string
+ * @author Sherlock Ren
+ */
+$waterLink = $imageUrlBuilder->waterImg($url, $waterImage);
+// 函数调用方法
+//$waterLink = \Qiniu\waterImg($url, $waterImage);
+var_dump($waterLink);
+
+/**
+ * 文字水印
+ *
+ * @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 纵轴边距 [可选]
+ * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html#text-watermark
+ * @return string
+ * @author Sherlock Ren
+ */
+$textLink = $imageUrlBuilder->waterText($url, '你瞅啥', '微软雅黑', 300);
+// 函数调用方法
+// $textLink = \Qiniu\waterText($url, '你瞅啥', '微软雅黑', 300);
+var_dump($textLink);
diff --git a/php-sdk/examples/persistent_fop_init.php b/php-sdk/examples/persistent_fop_init.php
new file mode 100644
index 0000000..baca846
--- /dev/null
+++ b/php-sdk/examples/persistent_fop_init.php
@@ -0,0 +1,18 @@
+useHTTPS=true;
+
+// 初始化
+$pfop = new PersistentFop($auth, $config);
diff --git a/php-sdk/examples/persistent_fop_status.php b/php-sdk/examples/persistent_fop_status.php
new file mode 100644
index 0000000..73e85a3
--- /dev/null
+++ b/php-sdk/examples/persistent_fop_status.php
@@ -0,0 +1,19 @@
+status($persistentId);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/pfop_mkzip.php b/php-sdk/examples/pfop_mkzip.php
new file mode 100644
index 0000000..fb95cc2
--- /dev/null
+++ b/php-sdk/examples/pfop_mkzip.php
@@ -0,0 +1,58 @@
+execute($bucket, $key, $fops, $pipeline, $notify_url, $force);
+
+echo "\n====> pfop mkzip result: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ echo "PersistentFop Id: $id\n";
+}
+
+// 查询转码的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop mkzip status: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/pfop_vframe.php b/php-sdk/examples/pfop_vframe.php
new file mode 100644
index 0000000..49fd36d
--- /dev/null
+++ b/php-sdk/examples/pfop_vframe.php
@@ -0,0 +1,55 @@
+useHTTPS = true;
+$pfop = new PersistentFop($auth, $config);
+
+// 视频处理完毕后保存到空间中的名称
+$saveasKey = 'qiniu_480x360.jpg';
+
+// 进行视频截帧操作
+$fops = "vframe/jpg/offset/1/w/480/h/360/rotate/90|saveas/" .
+ \Qiniu\base64_urlSafeEncode("$bucket:$saveasKey");
+
+list($id, $err) = $pfop->execute($bucket, $key, $fops, $pipeline, $notifyUrl, $force);
+echo "\n====> pfop avthumb result: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ echo "PersistentFop Id: $id\n";
+}
+
+// 查询转码的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop avthumb status: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/pfop_video_avthumb.php b/php-sdk/examples/pfop_video_avthumb.php
new file mode 100644
index 0000000..986aa8c
--- /dev/null
+++ b/php-sdk/examples/pfop_video_avthumb.php
@@ -0,0 +1,55 @@
+useHTTPS=true;
+
+// 视频处理完毕后保存到空间中的名称
+$saveasKey = 'qiniu_640x360.mp4';
+
+$pfop = new PersistentFop($auth, $config);
+
+// 进行视频转码操作
+$fops = "avthumb/mp4/s/640x360/vb/1.4m|saveas/" . \Qiniu\base64_urlSafeEncode("$bucket:$saveasKey");
+
+list($id, $err) = $pfop->execute($bucket, $key, $fops, $pipeline, $notifyUrl, $force);
+echo "\n====> pfop avthumb result: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ echo "PersistentFop Id: $id\n";
+}
+
+// 查询转码的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop avthumb status: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/pfop_watermark.php b/php-sdk/examples/pfop_watermark.php
new file mode 100644
index 0000000..ea3d6bc
--- /dev/null
+++ b/php-sdk/examples/pfop_watermark.php
@@ -0,0 +1,59 @@
+useHTTPS=true;
+$pfop = new PersistentFop($auth, $config);
+
+// 图片水印的源路径,也就是给视频打图片水印的图片
+$base64URL = Qiniu\base64_urlSafeEncode('http://test-2.qiniudn.com/logo.png');
+
+// 视频处理完毕后保存到空间中的名称
+$saveasKey = 'qiniu_watermark.mp4';
+
+// 进行视频打图片水印操作
+$fops = "avthumb/mp4/wmImage/" . $base64URL . "|saveas/"
+ . \Qiniu\base64_urlSafeEncode("$bucket:$saveasKey");
+
+list($id, $err) = $pfop->execute($bucket, $key, $fops, $pipeline, $notifyUrl, $force);
+echo "\n====> pfop avthumb result: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ echo "PersistentFop Id: $id\n";
+}
+
+// 查询转码的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop avthumb status: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/php-logo.png b/php-sdk/examples/php-logo.png
new file mode 100644
index 0000000..77e051f
Binary files /dev/null and b/php-sdk/examples/php-logo.png differ
diff --git a/php-sdk/examples/prefop.php b/php-sdk/examples/prefop.php
new file mode 100644
index 0000000..1b8950a
--- /dev/null
+++ b/php-sdk/examples/prefop.php
@@ -0,0 +1,27 @@
+useHTTPS=true;
+
+$pfop = new PersistentFop($auth, $config);
+
+$id = "z2.01z201c4oyre6q1hgy00murnel0002nh";
+
+// 查询持久化处理的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop avthumb status: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/put_bucketAccessMode.php b/php-sdk/examples/put_bucketAccessMode.php
new file mode 100644
index 0000000..638ae3c
--- /dev/null
+++ b/php-sdk/examples/put_bucketAccessMode.php
@@ -0,0 +1,27 @@
+putBucketAccessMode($bucket, $private);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/put_bucketAccessStyleMode.php b/php-sdk/examples/put_bucketAccessStyleMode.php
new file mode 100644
index 0000000..3cc2aec
--- /dev/null
+++ b/php-sdk/examples/put_bucketAccessStyleMode.php
@@ -0,0 +1,27 @@
+putBucketAccessStyleMode($bucket, $mode);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/put_bucketEvent.php b/php-sdk/examples/put_bucketEvent.php
new file mode 100644
index 0000000..f3c830d
--- /dev/null
+++ b/php-sdk/examples/put_bucketEvent.php
@@ -0,0 +1,32 @@
+putBucketEvent($bucket, $name, $prefix, $suffix, $event, $callbackURL);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/put_bucketMaxAge.php b/php-sdk/examples/put_bucketMaxAge.php
new file mode 100644
index 0000000..4890174
--- /dev/null
+++ b/php-sdk/examples/put_bucketMaxAge.php
@@ -0,0 +1,27 @@
+putBucketMaxAge($bucket, $maxAge);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/put_bucketQuota.php b/php-sdk/examples/put_bucketQuota.php
new file mode 100644
index 0000000..b00ec48
--- /dev/null
+++ b/php-sdk/examples/put_bucketQuota.php
@@ -0,0 +1,29 @@
+putBucketQuota($bucket, $size, $count);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/put_referAntiLeech.php b/php-sdk/examples/put_referAntiLeech.php
new file mode 100644
index 0000000..7d56d1e
--- /dev/null
+++ b/php-sdk/examples/put_referAntiLeech.php
@@ -0,0 +1,30 @@
+putReferAntiLeech($bucket, $mode, $norefer, $pattern);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/qetag.php b/php-sdk/examples/qetag.php
new file mode 100644
index 0000000..1fe90d1
--- /dev/null
+++ b/php-sdk/examples/qetag.php
@@ -0,0 +1,14 @@
+useHTTPS = true; // 接口是否使用 HTTPS 协议
+
+$bucketManager = new BucketManager($auth, $config);
+
+// 异步第三方资源抓取
+// 参考文档:https://developer.qiniu.com/kodo/api/4097/asynch-fetch
+
+// 需要抓取的文件 URL
+$url = 'http://devtools.qiniu.com/qiniu.png';
+
+//回调 URL(需要可以公网访问,并能够相应 200 OK)
+$callbackurl = "http://your.domain.com/upload_verify_callback.php";
+
+// 回调Body
+$callbackbody = '{"key":"$(key)","hash":"$(etag)","w":"$(imageInfo.width)","h":"$(imageInfo.height)"}';
+
+
+//---------------------------------------- demo1 ----------------------------------------
+// 指定抓取的文件保存到七牛云空间中的名称
+
+$key = time() . '.png';
+list($ret, $err) = $bucketManager->asynchFetch($url, $bucket, null, $key, null, null, $callbackurl, $callbackbody);
+echo "=====> asynch fetch $url to bucket: $bucket key: $key\n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ $id = $ret['id'];
+ echo "id is: $id\n";
+}
+
+//---------------------------------------- demo2 ----------------------------------------
+// 不指定 key 时,以文件内容的 hash 作为文件名
+
+$key = null;
+list($ret, $err) = $bucketManager->asynchFetch($url, $bucket, null, $key, null, null, $callbackurl, $callbackbody);
+echo "=====> asynch fetch $url to bucket: $bucket key: $(etag)\n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ $id = $ret['id'];
+ echo "id is: $id\n";
+}
+
+// 查询异步抓取的进度和状态
+
+// 华东:z0,华北:z1,华南:z2,北美:na0,东南亚:as0
+$zone = 'z2';
+
+sleep(10); // 由于异步抓取需要耗时,等待 10 秒后再查询状态
+list($ret, $err) = $bucketManager->asynchFetchStatus($zone, $id);
+echo "\n====> asynch fetch status: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_batch_change_mime.php b/php-sdk/examples/rs_batch_change_mime.php
new file mode 100644
index 0000000..c5bd6b4
--- /dev/null
+++ b/php-sdk/examples/rs_batch_change_mime.php
@@ -0,0 +1,32 @@
+ 'video/x-mp4',
+ 'qiniu.png' => 'image/x-png',
+ 'qiniu.jpg' => 'image/x-jpg'
+);
+
+$ops = $bucketManager->buildBatchChangeMime($bucket, $keyMimePairs);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_batch_change_type.php b/php-sdk/examples/rs_batch_change_type.php
new file mode 100644
index 0000000..a19d0d4
--- /dev/null
+++ b/php-sdk/examples/rs_batch_change_type.php
@@ -0,0 +1,45 @@
+batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_batch_copy.php b/php-sdk/examples/rs_batch_copy.php
new file mode 100644
index 0000000..66c4d4d
--- /dev/null
+++ b/php-sdk/examples/rs_batch_copy.php
@@ -0,0 +1,40 @@
+buildBatchCopy($srcBucket, $keyPairs, $destBucket, true);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_batch_delete.php b/php-sdk/examples/rs_batch_delete.php
new file mode 100644
index 0000000..ebcdbe6
--- /dev/null
+++ b/php-sdk/examples/rs_batch_delete.php
@@ -0,0 +1,32 @@
+buildBatchDelete($bucket, $keys);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_batch_delete_after_days.php b/php-sdk/examples/rs_batch_delete_after_days.php
new file mode 100644
index 0000000..928dd14
--- /dev/null
+++ b/php-sdk/examples/rs_batch_delete_after_days.php
@@ -0,0 +1,39 @@
+buildBatchDeleteAfterDays($bucket, $keyDayPairs);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_batch_move.php b/php-sdk/examples/rs_batch_move.php
new file mode 100644
index 0000000..01d8c91
--- /dev/null
+++ b/php-sdk/examples/rs_batch_move.php
@@ -0,0 +1,40 @@
+buildBatchMove($srcBucket, $keyPairs, $destBucket, true);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_batch_restore_ar.php b/php-sdk/examples/rs_batch_restore_ar.php
new file mode 100644
index 0000000..b2f79d0
--- /dev/null
+++ b/php-sdk/examples/rs_batch_restore_ar.php
@@ -0,0 +1,41 @@
+batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_batch_stat.php b/php-sdk/examples/rs_batch_stat.php
new file mode 100644
index 0000000..88bc32e
--- /dev/null
+++ b/php-sdk/examples/rs_batch_stat.php
@@ -0,0 +1,32 @@
+buildBatchStat($bucket, $keys);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_bucket_domains.php b/php-sdk/examples/rs_bucket_domains.php
new file mode 100644
index 0000000..3cc9cb3
--- /dev/null
+++ b/php-sdk/examples/rs_bucket_domains.php
@@ -0,0 +1,26 @@
+domains($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_buckets.php b/php-sdk/examples/rs_buckets.php
new file mode 100644
index 0000000..84263a9
--- /dev/null
+++ b/php-sdk/examples/rs_buckets.php
@@ -0,0 +1,25 @@
+buckets(true);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_change_mime.php b/php-sdk/examples/rs_change_mime.php
new file mode 100644
index 0000000..f4442aa
--- /dev/null
+++ b/php-sdk/examples/rs_change_mime.php
@@ -0,0 +1,29 @@
+changeMime($bucket, $key, $newMime);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_change_status.php b/php-sdk/examples/rs_change_status.php
new file mode 100644
index 0000000..bedf61c
--- /dev/null
+++ b/php-sdk/examples/rs_change_status.php
@@ -0,0 +1,29 @@
+changeStatus($bucket, $key, $status);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_change_type.php b/php-sdk/examples/rs_change_type.php
new file mode 100644
index 0000000..8b3201f
--- /dev/null
+++ b/php-sdk/examples/rs_change_type.php
@@ -0,0 +1,36 @@
+changeType($bucket, $key, $fileType);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_copy.php b/php-sdk/examples/rs_copy.php
new file mode 100644
index 0000000..aae4d96
--- /dev/null
+++ b/php-sdk/examples/rs_copy.php
@@ -0,0 +1,33 @@
+copy($srcBucket, $srcKey, $destBucket, $destKey, true);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_delete.php b/php-sdk/examples/rs_delete.php
new file mode 100644
index 0000000..ad97266
--- /dev/null
+++ b/php-sdk/examples/rs_delete.php
@@ -0,0 +1,27 @@
+delete($bucket, $key);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_delete_after_days.php b/php-sdk/examples/rs_delete_after_days.php
new file mode 100644
index 0000000..96e55de
--- /dev/null
+++ b/php-sdk/examples/rs_delete_after_days.php
@@ -0,0 +1,26 @@
+deleteAfterDays($bucket, $key, $days);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_download_urls.php b/php-sdk/examples/rs_download_urls.php
new file mode 100644
index 0000000..e803ddc
--- /dev/null
+++ b/php-sdk/examples/rs_download_urls.php
@@ -0,0 +1,19 @@
+/,一定要带访问协议,也就是 http:// 或者 https://
+$baseUrl = 'http://if-pri.qiniudn.com/qiniu.png?imageView2/1/h/500';
+
+// 对链接进行签名,参考文档:https://developer.qiniu.com/kodo/manual/1656/download-private
+$signedUrl = $auth->privateDownloadUrl($baseUrl);
+
+echo $signedUrl;
diff --git a/php-sdk/examples/rs_fetch.php b/php-sdk/examples/rs_fetch.php
new file mode 100644
index 0000000..5c1a5ab
--- /dev/null
+++ b/php-sdk/examples/rs_fetch.php
@@ -0,0 +1,43 @@
+fetch($url, $bucket, $key);
+echo "=====> fetch $url to bucket: $bucket key: $key\n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ print_r($ret);
+}
+
+//---------------------------------------- demo2 ----------------------------------------
+// 不指定 key 时,以文件内容的 hash 作为文件名
+
+$key = null;
+list($ret, $err) = $bucketManager->fetch($url, $bucket, $key);
+echo "=====> fetch $url to bucket: $bucket key: $(etag)\n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ print_r($ret);
+}
diff --git a/php-sdk/examples/rs_move.php b/php-sdk/examples/rs_move.php
new file mode 100644
index 0000000..a399665
--- /dev/null
+++ b/php-sdk/examples/rs_move.php
@@ -0,0 +1,29 @@
+move($srcBucket, $srcKey, $destBucket, $destKey, true);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_prefetch.php b/php-sdk/examples/rs_prefetch.php
new file mode 100644
index 0000000..28af115
--- /dev/null
+++ b/php-sdk/examples/rs_prefetch.php
@@ -0,0 +1,25 @@
+prefetch($bucket, $key);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_restore.php b/php-sdk/examples/rs_restore.php
new file mode 100644
index 0000000..a3bf070
--- /dev/null
+++ b/php-sdk/examples/rs_restore.php
@@ -0,0 +1,28 @@
+restoreAr($bucket, $key, 1);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rs_stat.php b/php-sdk/examples/rs_stat.php
new file mode 100644
index 0000000..36e863e
--- /dev/null
+++ b/php-sdk/examples/rs_stat.php
@@ -0,0 +1,28 @@
+stat($bucket, $key);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rsf_list_bucket.php b/php-sdk/examples/rsf_list_bucket.php
new file mode 100644
index 0000000..97a5838
--- /dev/null
+++ b/php-sdk/examples/rsf_list_bucket.php
@@ -0,0 +1,47 @@
+listFiles($bucket, $prefix, $marker, $limit, $delimiter);
+ if ($err !== null) {
+ echo "\n====> list file err: \n";
+ var_dump($err);
+ } else {
+ $marker = null;
+ if (array_key_exists('marker', $ret)) {
+ $marker = $ret['marker'];
+ }
+ echo "Marker: $marker\n";
+ echo "\nList Items====>\n";
+ //var_dump($ret['items']);
+ print('items count:' . count($ret['items']) . "\n");
+ if (array_key_exists('commonPrefixes', $ret)) {
+ print_r($ret['commonPrefixes']);
+ }
+ }
+} while (!empty($marker));
diff --git a/php-sdk/examples/rsf_list_files.php b/php-sdk/examples/rsf_list_files.php
new file mode 100644
index 0000000..31c455b
--- /dev/null
+++ b/php-sdk/examples/rsf_list_files.php
@@ -0,0 +1,39 @@
+listFiles($bucket, $prefix, $marker, $limit, $delimiter);
+if ($err !== null) {
+ echo "\n====> list file err: \n";
+ var_dump($err);
+} else {
+ if (array_key_exists('marker', $ret)) {
+ echo "Marker:" . $ret["marker"] . "\n";
+ }
+ echo "\nList Iterms====>\n";
+}
diff --git a/php-sdk/examples/rsf_v2list_bucket.php b/php-sdk/examples/rsf_v2list_bucket.php
new file mode 100644
index 0000000..5f9d763
--- /dev/null
+++ b/php-sdk/examples/rsf_v2list_bucket.php
@@ -0,0 +1,34 @@
+listFilesv2($bucket, $prefix, $marker, $limit, $delimiter, true);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rtc/README.md b/php-sdk/examples/rtc/README.md
new file mode 100644
index 0000000..c7fff4d
--- /dev/null
+++ b/php-sdk/examples/rtc/README.md
@@ -0,0 +1,34 @@
+# Rtc Streaming Cloud Server-Side Library For PHP
+
+## Features
+
+- RoomToken 签发
+ - [x] 生成 RoomToken: client->appToken()
+
+- App 管理
+ - [x] 创建应用: client->createApp()
+ - [x] 获取应用配置信息: client->getApp()
+ - [x] 更新应用配置信息: client->updateApp()
+ - [x] 删除应用: client->deleteApp()
+
+- 房间管理
+ - [x] 列举房间下的所有用户: client->listUser()
+ - [x] 指定一个用户踢出房间: client->kickUser()
+ - [x] 停止一个房间的合流转推: client->stopMerge()
+ - [x] 获取当前所有活跃的房间: client->listActiveRooms()
+
+## Demo
+- RoomToken 签发
+ - [生成 RoomToken](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_create_roomToken.php)
+
+- App 管理
+ - [创建应用](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_createApp.php)
+ - [获取应用配置信息](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_getApp.php)
+ - [更新应用配置信息](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_updateApp.php)
+ - [删除应用](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_deleteApp.php)
+
+- 房间管理
+ - [列举房间下的所有用户](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_rooms_listUser.php)
+ - [指定一个用户踢出房间](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_rooms_kickUser.php)
+ - [停止一个房间的合流转推](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_rooms_stopMerge.php)
+ - [获取当前所有活跃的房间](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_rooms_listActiveRooms.php)
\ No newline at end of file
diff --git a/php-sdk/examples/rtc/rtc_createApp.php b/php-sdk/examples/rtc/rtc_createApp.php
new file mode 100644
index 0000000..039eadd
--- /dev/null
+++ b/php-sdk/examples/rtc/rtc_createApp.php
@@ -0,0 +1,32 @@
+createApp($hub, $title, $maxUsers);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Create Successfully: \n";
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rtc/rtc_create_roomToken.php b/php-sdk/examples/rtc/rtc_create_roomToken.php
new file mode 100644
index 0000000..6a62aa2
--- /dev/null
+++ b/php-sdk/examples/rtc/rtc_create_roomToken.php
@@ -0,0 +1,34 @@
+appToken($appId, $roomName, $userId, $expireAt, $permission);
+echo "\n====> Create RoomToken Successfully: \n";
+var_dump($RoomToken);
diff --git a/php-sdk/examples/rtc/rtc_deleteApp.php b/php-sdk/examples/rtc/rtc_deleteApp.php
new file mode 100644
index 0000000..68bff33
--- /dev/null
+++ b/php-sdk/examples/rtc/rtc_deleteApp.php
@@ -0,0 +1,25 @@
+deleteApp($appId);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Delete $appId Successfully \n";
+}
diff --git a/php-sdk/examples/rtc/rtc_getApp.php b/php-sdk/examples/rtc/rtc_getApp.php
new file mode 100644
index 0000000..9f8e374
--- /dev/null
+++ b/php-sdk/examples/rtc/rtc_getApp.php
@@ -0,0 +1,26 @@
+getApp($appId);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> $appId Conf: \n";
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rtc/rtc_rooms_kickUser.php b/php-sdk/examples/rtc/rtc_rooms_kickUser.php
new file mode 100644
index 0000000..019c3f2
--- /dev/null
+++ b/php-sdk/examples/rtc/rtc_rooms_kickUser.php
@@ -0,0 +1,31 @@
+kickUser($appId, $roomName, $userId);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Kick User $userId Successfully \n";
+}
diff --git a/php-sdk/examples/rtc/rtc_rooms_listActiveRooms.php b/php-sdk/examples/rtc/rtc_rooms_listActiveRooms.php
new file mode 100644
index 0000000..16e6027
--- /dev/null
+++ b/php-sdk/examples/rtc/rtc_rooms_listActiveRooms.php
@@ -0,0 +1,35 @@
+listActiveRooms($appId, $prefix, $offset, $limit);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Active Rooms:\n";
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rtc/rtc_rooms_listUser.php b/php-sdk/examples/rtc/rtc_rooms_listUser.php
new file mode 100644
index 0000000..a839728
--- /dev/null
+++ b/php-sdk/examples/rtc/rtc_rooms_listUser.php
@@ -0,0 +1,29 @@
+listUser($appId, $roomName);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> User List: \n";
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/rtc/rtc_rooms_stopMerge.php b/php-sdk/examples/rtc/rtc_rooms_stopMerge.php
new file mode 100644
index 0000000..e140907
--- /dev/null
+++ b/php-sdk/examples/rtc/rtc_rooms_stopMerge.php
@@ -0,0 +1,28 @@
+stopMerge($appId, $roomName);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Stop Merge Successfully \n";
+}
diff --git a/php-sdk/examples/rtc/rtc_updateApp.php b/php-sdk/examples/rtc/rtc_updateApp.php
new file mode 100644
index 0000000..f771075
--- /dev/null
+++ b/php-sdk/examples/rtc/rtc_updateApp.php
@@ -0,0 +1,40 @@
+updateApp($appId, $hub, $title, $maxUsers, false, $mergePublishRtmp);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Update $appId Conf Successfully: \n";
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/saveas.php b/php-sdk/examples/saveas.php
new file mode 100644
index 0000000..5d51ef4
--- /dev/null
+++ b/php-sdk/examples/saveas.php
@@ -0,0 +1,33 @@
+为生成缩略图的文件名
+$entry = ':';
+
+// 生成的值
+$encodedEntryURI = \Qiniu\base64_urlSafeEncode($entry);
+
+// 使用 SecretKey 对新的下载 URL 进行 HMAC1-SHA1 签名
+$newurl = "78re52.com1.z0.glb.clouddn.com/resource/Ship.jpg?imageView2/2/w/200/h/200|saveas/" . $encodedEntryURI;
+
+$sign = hash_hmac("sha1", $newurl, $secretKey, true);
+
+// 对签名进行 URL 安全的 Base64 编码
+$encodedSign = \Qiniu\base64_urlSafeEncode($sign);
+
+// 最终得到的完整下载 URL
+$finalURL = "http://" . $newurl . "/sign/" . $accessKey . ":" . $encodedSign;
+
+$callbackBody = file_get_contents("$finalURL");
+
+echo $callbackBody;
diff --git a/php-sdk/examples/sms/README.md b/php-sdk/examples/sms/README.md
new file mode 100644
index 0000000..8c80a38
--- /dev/null
+++ b/php-sdk/examples/sms/README.md
@@ -0,0 +1,45 @@
+# SMS Server-Side Library For PHP
+
+## Features
+
+- 签名管理
+ - [x] 创建签名: client->createSignature()
+ - [x] 列出签名: client->checkSignature()
+ - [x] 查询单个签名: client->checkSingleSignature()
+ - [x] 编辑签名: client->updateSignature()
+ - [x] 删除签名: client->deleteSignature()
+
+- 模板管理
+ - [x] 创建模板: client->createTemplate()
+ - [x] 列出模板: client->queryTemplate()
+ - [x] 查询单个模板: client->querySingleTemplate()
+ - [x] 编辑模板: client->updateTemplate()
+ - [x] 删除模板: client->deleteTemplate()
+
+- 发送短信
+ - [x] 发送短信: client->sendMessage()
+
+- 查询发送记录
+ - [x] 查询发送记录: client->querySendSms()
+
+## Demo
+
+- 签名管理
+ - [创建签名](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_create_signature.php)
+ - [列出签名](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_query_signature.php)
+ - [查询单个签名](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_query_single_signature.php)
+ - [编辑签名](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_edit_signature.php)
+ - [删除签名](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_delete_signature.php)
+
+- 模板管理
+ - [创建模板](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_create_template.php)
+ - [列出模板](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_query_template.php)
+ - [查询单个模板](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_query_single_template.php)
+ - [编辑模板](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_edit_template.php)
+ - [删除模板](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_delete_template.php)
+
+- 发送短信
+ - [发送短信](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_send_message.php)
+
+- 查询发送记录
+ - [查询发送记录](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_query_send_sms.php)
diff --git a/php-sdk/examples/sms/sms_create_signature.php b/php-sdk/examples/sms/sms_create_signature.php
new file mode 100644
index 0000000..ea1f158
--- /dev/null
+++ b/php-sdk/examples/sms/sms_create_signature.php
@@ -0,0 +1,29 @@
+createSignature($signature, $source, $pics);
+
+echo "\n====> create signature result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/sms/sms_create_template.php b/php-sdk/examples/sms/sms_create_template.php
new file mode 100644
index 0000000..3cb3874
--- /dev/null
+++ b/php-sdk/examples/sms/sms_create_template.php
@@ -0,0 +1,33 @@
+createTemplate($name, $template, $type, $description, $signature_id);
+
+echo "\n====> create signature result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/sms/sms_delete_signature.php b/php-sdk/examples/sms/sms_delete_signature.php
new file mode 100644
index 0000000..fd873fa
--- /dev/null
+++ b/php-sdk/examples/sms/sms_delete_signature.php
@@ -0,0 +1,25 @@
+deleteSignature($signature_id);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Delete Signature $signature_id Successfully\n";
+}
diff --git a/php-sdk/examples/sms/sms_delete_template.php b/php-sdk/examples/sms/sms_delete_template.php
new file mode 100644
index 0000000..4590835
--- /dev/null
+++ b/php-sdk/examples/sms/sms_delete_template.php
@@ -0,0 +1,25 @@
+deleteTemplate($template_id);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Delete Template $template_id Successfully\n";
+}
diff --git a/php-sdk/examples/sms/sms_edit_signature.php b/php-sdk/examples/sms/sms_edit_signature.php
new file mode 100644
index 0000000..edf14e0
--- /dev/null
+++ b/php-sdk/examples/sms/sms_edit_signature.php
@@ -0,0 +1,30 @@
+updateSignature($id, $signature, $source, $pics);
+
+echo "\n====> edit signature result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Update Signature Successfully\n";
+}
diff --git a/php-sdk/examples/sms/sms_edit_template.php b/php-sdk/examples/sms/sms_edit_template.php
new file mode 100644
index 0000000..1be5509
--- /dev/null
+++ b/php-sdk/examples/sms/sms_edit_template.php
@@ -0,0 +1,31 @@
+updateTemplate($template_id, $name, $template, $description, $signature_id);
+
+echo "\n====> edit template result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Update Template Successfully\n";
+}
diff --git a/php-sdk/examples/sms/sms_query_send_sms.php b/php-sdk/examples/sms/sms_query_send_sms.php
new file mode 100644
index 0000000..cdbbe71
--- /dev/null
+++ b/php-sdk/examples/sms/sms_query_send_sms.php
@@ -0,0 +1,50 @@
+querySendSms(
+ $job_id,
+ $message_id,
+ $mobile,
+ $status,
+ $template_id,
+ $type,
+ $start,
+ $end,
+ $page,
+ $page_size
+);
+echo "\n====> query send sms result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/sms/sms_query_signature.php b/php-sdk/examples/sms/sms_query_signature.php
new file mode 100644
index 0000000..224d09b
--- /dev/null
+++ b/php-sdk/examples/sms/sms_query_signature.php
@@ -0,0 +1,28 @@
+querySignature($audit_status, $page, $page_size);
+echo "\n====> query signature result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/sms/sms_query_single_signature.php b/php-sdk/examples/sms/sms_query_single_signature.php
new file mode 100644
index 0000000..8afb4d5
--- /dev/null
+++ b/php-sdk/examples/sms/sms_query_single_signature.php
@@ -0,0 +1,26 @@
+checkSingleSignature($signature_id);
+echo "\n====> query single signature result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/sms/sms_query_single_template.php b/php-sdk/examples/sms/sms_query_single_template.php
new file mode 100644
index 0000000..8e0b279
--- /dev/null
+++ b/php-sdk/examples/sms/sms_query_single_template.php
@@ -0,0 +1,26 @@
+querySingleTemplate($template_id);
+echo "\n====> query single template result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/sms/sms_query_template.php b/php-sdk/examples/sms/sms_query_template.php
new file mode 100644
index 0000000..6be260e
--- /dev/null
+++ b/php-sdk/examples/sms/sms_query_template.php
@@ -0,0 +1,28 @@
+queryTemplate($audit_status, $page, $page_size);
+echo "\n====> query template result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/sms/sms_send_message.php b/php-sdk/examples/sms/sms_send_message.php
new file mode 100644
index 0000000..d943e52
--- /dev/null
+++ b/php-sdk/examples/sms/sms_send_message.php
@@ -0,0 +1,32 @@
+ 'xxxx');
+
+list($ret, $err) = $client->sendMessage($template_id, $mobiles, $code);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Send Message Successfully: \n";
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/update_bucketEvent.php b/php-sdk/examples/update_bucketEvent.php
new file mode 100644
index 0000000..7b0d1d0
--- /dev/null
+++ b/php-sdk/examples/update_bucketEvent.php
@@ -0,0 +1,31 @@
+updateBucketEvent($bucket, $name, $prefix, $suffix, $event, $callbackURL);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/update_bucketLifecycleRule.php b/php-sdk/examples/update_bucketLifecycleRule.php
new file mode 100644
index 0000000..73f0f56
--- /dev/null
+++ b/php-sdk/examples/update_bucketLifecycleRule.php
@@ -0,0 +1,36 @@
+updateBucketLifecycleRule(
+ $bucket,
+ $name,
+ $prefix,
+ $delete_after_days,
+ $to_line_after_days
+);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/upload_and_callback.php b/php-sdk/examples/upload_and_callback.php
new file mode 100644
index 0000000..a0c793a
--- /dev/null
+++ b/php-sdk/examples/upload_and_callback.php
@@ -0,0 +1,31 @@
+ 'http://your.domain.com/upload_verify_callback.php',
+ 'callbackBody' => 'filename=$(fname)&filesize=$(fsize)'
+);
+$uptoken = $auth->uploadToken($bucket, null, 3600, $policy);
+
+// 上传文件的本地路径
+$filePath = './php-logo.png';
+
+$uploadMgr = new UploadManager();
+list($ret, $err) = $uploadMgr->putFile($uptoken, null, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/upload_and_pfop.php b/php-sdk/examples/upload_and_pfop.php
new file mode 100644
index 0000000..32c1eb5
--- /dev/null
+++ b/php-sdk/examples/upload_and_pfop.php
@@ -0,0 +1,49 @@
+ $pfop,
+ 'persistentNotifyUrl' => $notifyUrl,
+ 'persistentPipeline' => $pipeline
+);
+$token = $auth->uploadToken($bucket, null, 3600, $policy);
+
+list($ret, $err) = $uploadMgr->putFile($token, $key, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/upload_mgr_init.php b/php-sdk/examples/upload_mgr_init.php
new file mode 100644
index 0000000..1164c90
--- /dev/null
+++ b/php-sdk/examples/upload_mgr_init.php
@@ -0,0 +1,19 @@
+uploadToken($bucket);
+
+// 构建 UploadManager 对象
+$uploadMgr = new UploadManager();
diff --git a/php-sdk/examples/upload_multi_demos.php b/php-sdk/examples/upload_multi_demos.php
new file mode 100644
index 0000000..d724235
--- /dev/null
+++ b/php-sdk/examples/upload_multi_demos.php
@@ -0,0 +1,89 @@
+uploadToken($bucket);
+$uploadMgr = new UploadManager();
+
+//---------------------------------------- upload demo1 ----------------------------------------
+// 上传字符串到七牛
+
+list($ret, $err) = $uploadMgr->put($token, null, 'content string');
+echo "\n====> put result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
+
+
+//---------------------------------------- upload demo2 ----------------------------------------
+// 上传文件到七牛
+
+$filePath = './php-logo.png';
+$key = 'php-logo.png';
+list($ret, $err) = $uploadMgr->putFile($token, $key, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
+
+
+//---------------------------------------- upload demo3 ----------------------------------------
+// 上传文件到七牛后, 七牛将文件名和文件大小回调给业务服务器.
+// 可参考文档: https://developer.qiniu.com/kodo/manual/1206/put-policy
+
+$policy = array(
+ 'callbackUrl' => 'http://172.30.251.210/upload_verify_callback.php',
+ 'callbackBody' => 'filename=$(fname)&filesize=$(fsize)'
+// 'callbackBodyType' => 'application/json',
+// 'callbackBody' => '{"filename":$(fname), "filesize": $(fsize)}' //设置application/json格式回调
+);
+$token = $auth->uploadToken($bucket, null, 3600, $policy);
+
+
+list($ret, $err) = $uploadMgr->putFile($token, null, $key);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
+
+
+//---------------------------------------- upload demo4 ----------------------------------------
+// 上传视频,上传完成后进行 m3u8 的转码, 并给视频打水印
+
+$wmImg = Qiniu\base64_urlSafeEncode('http://devtools.qiniudn.com/qiniu.png');
+$pfop = "avthumb/m3u8/wmImage/$wmImg";
+
+// 转码完成后回调到业务服务器。(公网可以访问,并相应 200 OK)
+$notifyUrl = 'http://notify.fake.com';
+
+$policy = array(
+ 'persistentOps' => $pfop,
+ 'persistentNotifyUrl' => $notifyUrl,
+ 'persistentPipeline' => $pipeline
+);
+$token = $auth->uploadToken($bucket, null, 3600, $policy);
+print($token);
+list($ret, $err) = $uploadMgr->putFile($token, null, $key);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/upload_simple_file.php b/php-sdk/examples/upload_simple_file.php
new file mode 100644
index 0000000..f495a02
--- /dev/null
+++ b/php-sdk/examples/upload_simple_file.php
@@ -0,0 +1,37 @@
+uploadToken($bucket);
+
+// 要上传文件的本地路径
+$filePath = './php-logo.png';
+
+// 上传到七牛存储后保存的文件名
+$key = 'my-php-logo.png';
+
+// 初始化 UploadManager 对象并进行文件的上传。
+$uploadMgr = new UploadManager();
+
+// 调用 UploadManager 的 putFile 方法进行文件的上传,该方法会判断文件大小,进而决定使用表单上传还是分片上传,无需手动配置。
+list($ret, $err) = $uploadMgr->putFile($token, $key, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/upload_tokens.php b/php-sdk/examples/upload_tokens.php
new file mode 100644
index 0000000..d2cf02c
--- /dev/null
+++ b/php-sdk/examples/upload_tokens.php
@@ -0,0 +1,82 @@
+uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+//---------------------------------------- demo2 ----------------------------------------
+// 自定义凭证有效期(示例2小时)
+
+$expires = 7200;
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+//---------------------------------------- demo3 ----------------------------------------
+// 覆盖上传凭证
+
+$expires = 3600;
+$keyToOverwrite = 'qiniu.mp4';
+$upToken = $auth->uploadToken($bucket, $keyToOverwrite, $expires, $policy, true);
+print($upToken . "\n");
+
+//---------------------------------------- demo4 ----------------------------------------
+// 自定义上传回复(非callback模式)凭证
+
+$returnBody = '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}';
+$policy = array(
+ 'returnBody' => $returnBody
+);
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+//---------------------------------------- demo5 ----------------------------------------
+// 带回调业务服务器的凭证(application/json)
+
+$policy = array(
+ 'callbackUrl' => 'http://api.example.com/qiniu/upload/callback',
+ 'callbackBody' => '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}',
+ 'callbackBodyType' => 'application/json'
+);
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+//---------------------------------------- demo6 ----------------------------------------
+// 带回调业务服务器的凭证(application/x-www-form-urlencoded)
+
+$policy = array(
+ 'callbackUrl' => 'http://api.example.com/qiniu/upload/callback',
+ 'callbackBody' => 'key=$(key)&hash=$(etag)&bucket=$(bucket)&fsize=$(fsize)&name=$(x:name)'
+);
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+//---------------------------------------- demo7 ----------------------------------------
+// 带数据处理的凭证
+
+$saveMp4Entry = \Qiniu\base64_urlSafeEncode($bucket . ":avthumb_test_target.mp4");
+$saveJpgEntry = \Qiniu\base64_urlSafeEncode($bucket . ":vframe_test_target.jpg");
+$avthumbMp4Fop = "avthumb/mp4|saveas/" . $saveMp4Entry;
+$vframeJpgFop = "vframe/jpg/offset/1|saveas/" . $saveJpgEntry;
+$policy = array(
+ 'persistentOps' => $avthumbMp4Fop . ";" . $vframeJpgFop,
+ 'persistentPipeline' => "video-pipe",
+ 'persistentNotifyUrl' => "http://api.example.com/qiniu/pfop/notify",
+);
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
diff --git a/php-sdk/examples/upload_verify_callback.php b/php-sdk/examples/upload_verify_callback.php
new file mode 100644
index 0000000..dcb64c9
--- /dev/null
+++ b/php-sdk/examples/upload_verify_callback.php
@@ -0,0 +1,34 @@
+verifyCallback($contentType, $authorization, $url, $callbackBody);
+
+if ($isQiniuCallback) {
+ $resp = array('ret' => 'success');
+} else {
+ $resp = array('ret' => 'failed');
+}
+
+echo json_encode($resp);
diff --git a/php-sdk/examples/upload_with_qvmzone.php b/php-sdk/examples/upload_with_qvmzone.php
new file mode 100644
index 0000000..ce2b21f
--- /dev/null
+++ b/php-sdk/examples/upload_with_qvmzone.php
@@ -0,0 +1,40 @@
+uploadToken($bucket);
+
+// 上传文件的本地路径
+$filePath = './php-logo.png';
+
+// 七牛云主机QVM和七牛对象存储KODO内网上传,目前支持华东1区域(杭州)和华北2区域(北京)的云主机可以访问同区域的对象存储服务
+// 参考文档:https://developer.qiniu.com/qvm/manual/4269/qvm-kodo
+
+$zone = Zone::qvmZonez0(); // 华东:z0,华北:z1
+$config = new Config($zone);
+$config->useHTTPS = true;
+
+// 指定 config
+$uploadMgr = new UploadManager($config);
+
+list($ret, $err) = $uploadMgr->putFile($uptoken, $key, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/examples/upload_with_zone.php b/php-sdk/examples/upload_with_zone.php
new file mode 100644
index 0000000..6192666
--- /dev/null
+++ b/php-sdk/examples/upload_with_zone.php
@@ -0,0 +1,39 @@
+uploadToken($bucket);
+
+// 上传文件的本地路径
+$filePath = './php-logo.png';
+
+// 指定 zone 上传
+// 参考文档:https://developer.qiniu.com/kodo/manual/1671/region-endpoint
+$zone = Zone::zonez0(); // 华东:z0,华北:z1,华南:z2,北美:na0,东南亚:as0
+$config = new Config($zone);
+$config->useHTTPS = true;
+
+// 指定 config
+$uploadMgr = new UploadManager($config);
+
+list($ret, $err) = $uploadMgr->putFile($uptoken, $key, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/php-sdk/phpunit.xml.dist b/php-sdk/phpunit.xml.dist
new file mode 100644
index 0000000..840f6e5
--- /dev/null
+++ b/php-sdk/phpunit.xml.dist
@@ -0,0 +1,18 @@
+
+
+
+
+ tests
+
+
+
+
diff --git a/php-sdk/src/Qiniu/Auth.php b/php-sdk/src/Qiniu/Auth.php
new file mode 100644
index 0000000..6da2be4
--- /dev/null
+++ b/php-sdk/src/Qiniu/Auth.php
@@ -0,0 +1,285 @@
+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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Cdn/CdnManager.php b/php-sdk/src/Qiniu/Cdn/CdnManager.php
new file mode 100644
index 0000000..60052d3
--- /dev/null
+++ b/php-sdk/src/Qiniu/Cdn/CdnManager.php
@@ -0,0 +1,263 @@
+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 指定要查询记录的状态,取值processing/success/failure
+ * @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 指定要查询记录的状态,取值processing/success/failure
+ * @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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Config.php b/php-sdk/src/Qiniu/Config.php
new file mode 100644
index 0000000..3ce7fa5
--- /dev/null
+++ b/php-sdk/src/Qiniu/Config.php
@@ -0,0 +1,398 @@
+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);
+ }
+}
diff --git a/php-sdk/src/Qiniu/Enum/QiniuEnum.php b/php-sdk/src/Qiniu/Enum/QiniuEnum.php
new file mode 100644
index 0000000..8399b54
--- /dev/null
+++ b/php-sdk/src/Qiniu/Enum/QiniuEnum.php
@@ -0,0 +1,53 @@
+ $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);
+ }
+}
diff --git a/php-sdk/src/Qiniu/Http/Error.php b/php-sdk/src/Qiniu/Http/Error.php
new file mode 100644
index 0000000..8fba74f
--- /dev/null
+++ b/php-sdk/src/Qiniu/Http/Error.php
@@ -0,0 +1,38 @@
+
+ * {"error" : "detailed error message"}
+ *
+ */
+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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Http/Header.php b/php-sdk/src/Qiniu/Http/Header.php
new file mode 100644
index 0000000..1dcf328
--- /dev/null
+++ b/php-sdk/src/Qiniu/Http/Header.php
@@ -0,0 +1,291 @@
+ $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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Http/Middleware/Middleware.php b/php-sdk/src/Qiniu/Http/Middleware/Middleware.php
new file mode 100644
index 0000000..fe8a64c
--- /dev/null
+++ b/php-sdk/src/Qiniu/Http/Middleware/Middleware.php
@@ -0,0 +1,31 @@
+ $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;
+}
diff --git a/php-sdk/src/Qiniu/Http/Middleware/RetryDomainsMiddleware.php b/php-sdk/src/Qiniu/Http/Middleware/RetryDomainsMiddleware.php
new file mode 100644
index 0000000..829ab87
--- /dev/null
+++ b/php-sdk/src/Qiniu/Http/Middleware/RetryDomainsMiddleware.php
@@ -0,0 +1,76 @@
+ 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 $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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Http/Proxy.php b/php-sdk/src/Qiniu/Http/Proxy.php
new file mode 100644
index 0000000..fac6ba1
--- /dev/null
+++ b/php-sdk/src/Qiniu/Http/Proxy.php
@@ -0,0 +1,34 @@
+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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Http/Request.php b/php-sdk/src/Qiniu/Http/Request.php
new file mode 100644
index 0000000..5a31bf6
--- /dev/null
+++ b/php-sdk/src/Qiniu/Http/Request.php
@@ -0,0 +1,42 @@
+
+ */
+ 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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Http/RequestOptions.php b/php-sdk/src/Qiniu/Http/RequestOptions.php
new file mode 100644
index 0000000..be0c6d5
--- /dev/null
+++ b/php-sdk/src/Qiniu/Http/RequestOptions.php
@@ -0,0 +1,104 @@
+
+ */
+ 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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Http/Response.php b/php-sdk/src/Qiniu/Http/Response.php
new file mode 100644
index 0000000..cd77903
--- /dev/null
+++ b/php-sdk/src/Qiniu/Http/Response.php
@@ -0,0 +1,220 @@
+ '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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Processing/ImageUrlBuilder.php b/php-sdk/src/Qiniu/Processing/ImageUrlBuilder.php
new file mode 100644
index 0000000..f5575ed
--- /dev/null
+++ b/php-sdk/src/Qiniu/Processing/ImageUrlBuilder.php
@@ -0,0 +1,292 @@
+
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ protected function hasQuery($url)
+ {
+ $urlArr = parse_url($url);
+
+ return !empty($urlArr['query']);
+ }
+}
diff --git a/php-sdk/src/Qiniu/Processing/Operation.php b/php-sdk/src/Qiniu/Processing/Operation.php
new file mode 100644
index 0000000..839703c
--- /dev/null
+++ b/php-sdk/src/Qiniu/Processing/Operation.php
@@ -0,0 +1,69 @@
+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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Processing/PersistentFop.php b/php-sdk/src/Qiniu/Processing/PersistentFop.php
new file mode 100644
index 0000000..8dca4a9
--- /dev/null
+++ b/php-sdk/src/Qiniu/Processing/PersistentFop.php
@@ -0,0 +1,135 @@
+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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Region.php b/php-sdk/src/Qiniu/Region.php
new file mode 100644
index 0000000..220a5a3
--- /dev/null
+++ b/php-sdk/src/Qiniu/Region.php
@@ -0,0 +1,229 @@
+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=&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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Rtc/AppClient.php b/php-sdk/src/Qiniu/Rtc/AppClient.php
new file mode 100644
index 0000000..3f245db
--- /dev/null
+++ b/php-sdk/src/Qiniu/Rtc/AppClient.php
@@ -0,0 +1,236 @@
+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);
+ }
+}
diff --git a/php-sdk/src/Qiniu/Sms/Sms.php b/php-sdk/src/Qiniu/Sms/Sms.php
new file mode 100644
index 0000000..c96409b
--- /dev/null
+++ b/php-sdk/src/Qiniu/Sms/Sms.php
@@ -0,0 +1,382 @@
+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);
+ }
+}
diff --git a/php-sdk/src/Qiniu/Storage/ArgusManager.php b/php-sdk/src/Qiniu/Storage/ArgusManager.php
new file mode 100644
index 0000000..51b4200
--- /dev/null
+++ b/php-sdk/src/Qiniu/Storage/ArgusManager.php
@@ -0,0 +1,129 @@
+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);
+ }
+}
diff --git a/php-sdk/src/Qiniu/Storage/BucketManager.php b/php-sdk/src/Qiniu/Storage/BucketManager.php
new file mode 100644
index 0000000..bfca4fc
--- /dev/null
+++ b/php-sdk/src/Qiniu/Storage/BucketManager.php
@@ -0,0 +1,1324 @@
+auth = $auth;
+ if ($config == null) {
+ $this->config = new Config();
+ } else {
+ $this->config = $config;
+ }
+ $this->proxy = new Proxy($proxy, $proxy_auth, $proxy_user_password);
+ }
+
+ /**
+ * 获取指定账号下所有的空间名
+ *
+ * @param bool $shared 指定共享空间,rw:读写权限空间,rd:读权限空间
+ * @return array 包含所有空间名
+ */
+ public function buckets($shared = true)
+ {
+ $includeShared = "false";
+ if ($shared === true) {
+ $includeShared = "true";
+ }
+ return $this->getV2($this->config->getUcHost() . '/buckets?shared=' . $includeShared);
+ }
+
+ /**
+ * 列举空间,返回bucket列表
+ *
+ * @param string $region 区域
+ * @param string $line
+ * @param string $shared 指定共享空间,rw:读写权限空间,rd:读权限空间
+ * @return array
+ */
+ public function listbuckets(
+ $region = null,
+ $line = 'false',
+ $shared = 'false'
+ ) {
+ $path = '/v3/buckets?region=' . $region . '&line=' . $line . '&shared=' . $shared;
+ return $this->ucPost($path);
+ }
+
+ /**
+ * 创建空间
+ *
+ * @param string $name 创建的空间名
+ * @param string $region 创建的区域,默认华东
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1382/mkbucketv3
+ */
+ public function createBucket($name, $region = 'z0')
+ {
+ $path = '/mkbucketv3/' . $name . '/region/' . $region;
+ return $this->postV2($this->config->getUcHost() . $path, null);
+ }
+
+ /**
+ * 删除空间
+ *
+ * @param string $name 需要删除的目标空间名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1601/drop-bucket
+ */
+ public function deleteBucket($name)
+ {
+ $path = '/drop/' . $name;
+ return $this->postV2($this->config->getUcHost() . $path, null);
+ }
+
+ /**
+ * 获取指定空间绑定的所有的域名
+ *
+ * @param string $bucket 空间名称
+ * @return array
+ */
+ public function domains($bucket)
+ {
+ return $this->ucGet('/v2/domains?tbl=' . $bucket);
+ }
+
+ /**
+ * 获取指定空间的相关信息
+ *
+ * @param string $bucket 空间名称
+ * @return array
+ */
+ public function bucketInfo($bucket)
+ {
+ $path = '/v2/bucketInfo?bucket=' . $bucket;
+ return $this->ucPost($path);
+ }
+
+ /**
+ * 获取指定zone的空间信息列表
+ *
+ * @param string $region 区域
+ * @param string $shared 指定共享空间,rw:读写权限空间,rd:读权限空间
+ * @param string $fs 如果为 true,会返回每个空间当前的文件数和存储量(实时数据)
+ * @return array
+ */
+ public function bucketInfos($region = null, $shared = 'false', $fs = 'false')
+ {
+ $path = '/v2/bucketInfos?region=' . $region . '&shared=' . $shared . '&fs=' . $fs;
+ return $this->ucPost($path);
+ }
+
+ /**
+ * 列取空间的文件列表
+ *
+ * @param string $bucket 空间名
+ * @param string $prefix 列举前缀
+ * @param string $marker 列举标识符
+ * @param int $limit 单次列举个数限制
+ * @param string $delimiter 指定目录分隔符
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1284/list
+ */
+ public function listFiles(
+ $bucket,
+ $prefix = null,
+ $marker = null,
+ $limit = 1000,
+ $delimiter = null
+ ) {
+ $query = array('bucket' => $bucket);
+ \Qiniu\setWithoutEmpty($query, 'prefix', $prefix);
+ \Qiniu\setWithoutEmpty($query, 'marker', $marker);
+ \Qiniu\setWithoutEmpty($query, 'limit', $limit);
+ \Qiniu\setWithoutEmpty($query, 'delimiter', $delimiter);
+ return $this->rsfGet($bucket, '/list?' . http_build_query($query));
+ }
+
+ /**
+ * 列取空间的文件列表
+ *
+ * @deprecated API 可能返回仅包含 marker,不包含 item 或 dir 的项,请使用 {@link listFiles}
+ *
+ * @param string $bucket 空间名
+ * @param string $prefix 列举前缀
+ * @param string $marker 列举标识符
+ * @param int $limit 单次列举个数限制
+ * @param string $delimiter 指定目录分隔符
+ * @param bool $skipconfirm 是否跳过已删除条目的确认机制
+ *
+ * @return array
+ * @link http://developer.qiniu.com/docs/v6/api/reference/rs/list.html
+ */
+ public function listFilesv2(
+ $bucket,
+ $prefix = null,
+ $marker = null,
+ $limit = 1000,
+ $delimiter = null,
+ $skipconfirm = true
+ ) {
+ $query = array('bucket' => $bucket);
+ \Qiniu\setWithoutEmpty($query, 'prefix', $prefix);
+ \Qiniu\setWithoutEmpty($query, 'marker', $marker);
+ \Qiniu\setWithoutEmpty($query, 'limit', $limit);
+ \Qiniu\setWithoutEmpty($query, 'delimiter', $delimiter);
+ \Qiniu\setWithoutEmpty($query, 'skipconfirm', $skipconfirm);
+ $path = '/v2/list?' . http_build_query($query);
+
+ list($host, $err) = $this->config->getRsfHostV2(
+ $this->auth->getAccessKey(),
+ $bucket,
+ $this->proxy->makeReqOpt()
+ );
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ $url = $host . $path;
+ $headers = $this->auth->authorizationV2($url, 'POST', null, 'application/x-www-form-urlencoded');
+ $ret = Client::post($url, null, $headers, $this->proxy->makeReqOpt());
+ if (!$ret->ok()) {
+ return array(null, new Error($url, $ret));
+ }
+ $r = explode("\n", $ret->body);
+ array_pop($r);
+ return array($r, null);
+ }
+
+ /**
+ * 增加bucket生命规则
+ *
+ * @param string $bucket
+ * 空间名
+ * @param string $name
+ * 规则名称 bucket 内唯一,长度小于50,不能为空,只能为字母、数字、下划线
+ * @param string $prefix
+ * 同一个 bucket 里面前缀不能重复
+ * @param int $delete_after_days
+ * 指定上传文件多少天后删除,指定为0表示不删除,大于0表示多少天后删除。
+ * 需大于 to_line_after_days
+ * @param int $to_line_after_days
+ * 指定文件上传多少天后转低频存储。指定为0表示不转低频存储
+ * @param int $to_archive_ir_after_days
+ * 指定文件上传多少天后转归档直读。指定为0表示不转归档直读
+ * @param int $to_archive_after_days
+ * 指定文件上传多少天后转归档存储。指定为0表示不转归档存储
+ * @param int $to_deep_archive_after_days
+ * 指定文件上传多少天后转深度归档存储。指定为0表示不转深度归档存储
+ * @return array
+ */
+ public function bucketLifecycleRule(
+ $bucket,
+ $name,
+ $prefix,
+ $delete_after_days = null,
+ $to_line_after_days = null,
+ $to_archive_after_days = null,
+ $to_deep_archive_after_days = null,
+ $to_archive_ir_after_days = null
+ ) {
+ $path = '/rules/add';
+ $params = array();
+ if ($bucket) {
+ $params['bucket'] = $bucket;
+ }
+ if ($name) {
+ $params['name'] = $name;
+ }
+ if ($prefix) {
+ $params['prefix'] = $prefix;
+ }
+ if ($delete_after_days) {
+ $params['delete_after_days'] = $delete_after_days;
+ }
+ if ($to_line_after_days) {
+ $params['to_line_after_days'] = $to_line_after_days;
+ }
+ if ($to_archive_ir_after_days) {
+ $params['to_archive_ir_after_days'] = $to_archive_ir_after_days;
+ }
+ if ($to_archive_after_days) {
+ $params['to_archive_after_days'] = $to_archive_after_days;
+ }
+ if ($to_deep_archive_after_days) {
+ $params['to_deep_archive_after_days'] = $to_deep_archive_after_days;
+ }
+ $data = http_build_query($params);
+ $info = $this->ucPost($path, $data);
+ return $info;
+ }
+
+ /**
+ * 更新bucket生命规则
+ *
+ * @param string $bucket
+ * 空间名
+ * @param string $name
+ * 规则名称 bucket 内唯一,长度小于50,不能为空,只能为字母、数字、下划线
+ * @param string $prefix
+ * 同一个 bucket 里面前缀不能重复
+ * @param int $delete_after_days
+ * 指定上传文件多少天后删除,指定为0表示不删除,大于0表示多少天后删除
+ * 需大于 to_line_after_days
+ * @param int $to_line_after_days
+ * 指定文件上传多少天后转低频存储。指定为0表示不转低频存储
+ * @param int $to_archive_ir_after_days
+ * 指定文件上传多少天后转归档只读。指定为0表示不转归档只读
+ * @param int $to_archive_after_days
+ * 指定文件上传多少天后转归档存储。指定为0表示不转归档存储
+ * @param int $to_deep_archive_after_days
+ * 指定文件上传多少天后转深度归档存储。指定为0表示不转深度归档存储
+ * @return array
+ */
+ public function updateBucketLifecycleRule(
+ $bucket,
+ $name,
+ $prefix,
+ $delete_after_days = null,
+ $to_line_after_days = null,
+ $to_archive_after_days = null,
+ $to_deep_archive_after_days = null,
+ $to_archive_ir_after_days = null
+ ) {
+ $path = '/rules/update';
+ $params = array();
+ if ($bucket) {
+ $params['bucket'] = $bucket;
+ }
+ if ($name) {
+ $params['name'] = $name;
+ }
+ if ($prefix) {
+ $params['prefix'] = $prefix;
+ }
+ if ($delete_after_days) {
+ $params['delete_after_days'] = $delete_after_days;
+ }
+ if ($to_line_after_days) {
+ $params['to_line_after_days'] = $to_line_after_days;
+ }
+ if ($to_archive_ir_after_days) {
+ $params['to_archive_ir_after_days'] = $to_archive_ir_after_days;
+ }
+ if ($to_archive_after_days) {
+ $params['to_archive_after_days'] = $to_archive_after_days;
+ }
+ if ($to_deep_archive_after_days) {
+ $params['to_deep_archive_after_days'] = $to_deep_archive_after_days;
+ }
+ $data = http_build_query($params);
+ return $this->ucPost($path, $data);
+ }
+
+ /**
+ * 获取bucket生命规则
+ *
+ * @param string $bucket 空间名
+ * @return array
+ */
+ public function getBucketLifecycleRules($bucket)
+ {
+ $path = '/rules/get?bucket=' . $bucket;
+ $info = $this->ucGet($path);
+ return $info;
+ }
+
+ /**
+ * 删除bucket生命规则
+ *
+ * @param string $bucket 空间名
+ * @param string $name 规则名称 bucket 内唯一,长度小于50,不能为空,
+ * 只能为字母、数字、下划线()
+ * @return array
+ */
+ public function deleteBucketLifecycleRule($bucket, $name)
+ {
+ $path = '/rules/delete';
+ $params = array();
+ if ($bucket) {
+ $params['bucket'] = $bucket;
+ }
+ if ($name) {
+ $params['name'] = $name;
+ }
+ $data = http_build_query($params);
+ $info = $this->ucPost($path, $data);
+ return $info;
+ }
+
+ /**
+ * 增加bucket事件通知规则
+ *
+ * @param string $bucket 空间名
+ * @param string $name 规则名称 bucket 内唯一,长度小于50,不能为空,
+ * 只能为字母、数字、下划线()
+ * @param string $prefix 同一个 bucket 里面前缀不能重复
+ * @param string $suffix 可选,文件配置的后缀
+ * @param array $event 事件类型,可以指定多个,包括 put,mkfile,delete,copy,move,append,
+ * disable,enable,deleteMarkerCreate
+ * @param string $callbackURL 通知URL,可以指定多个,失败依次重试
+ * @param string $access_key 可选,设置的话会对通知请求用对应的ak、sk进行签名
+ * @param string $host 可选,通知请求的host
+ *
+ * @return array
+ */
+ public function putBucketEvent(
+ $bucket,
+ $name,
+ $prefix,
+ $suffix,
+ $event,
+ $callbackURL,
+ $access_key = null,
+ $host = null
+ ) {
+ $path = '/events/add';
+ $params = array();
+ if (!empty($bucket)) {
+ $params['bucket'] = $bucket;
+ }
+ if (!empty($name)) {
+ $params['name'] = $name;
+ }
+ if (!empty($prefix)) {
+ $params['prefix'] = $prefix;
+ }
+ if (!empty($suffix)) {
+ $params['suffix'] = $suffix;
+ }
+ if (!empty($callbackURL)) {
+ $params['callbackURL'] = $callbackURL;
+ }
+ if (!empty($access_key)) {
+ $params['access_key'] = $access_key;
+ }
+ if (!empty($host)) {
+ $params['host'] = $host;
+ }
+ $data = http_build_query($params);
+ if (!empty($event)) {
+ $eventpath = "";
+ foreach ($event as $key => $value) {
+ $eventpath .= "&event=$value";
+ }
+ $data .= $eventpath;
+ }
+ $info = $this->ucPost($path, $data);
+ return $info;
+ }
+
+ /**
+ * 更新bucket事件通知规则
+ *
+ * @param string $bucket 空间名
+ * @param string $name 规则名称 bucket 内唯一,长度小于50,不能为空,
+ * 只能为字母、数字、下划线()
+ * @param string $prefix 同一个 bucket 里面前缀不能重复
+ * @param string $suffix 可选,文件配置的后缀
+ * @param array $event 事件类型,可以指定多个,包括 put,mkfile,delete,copy,move,append,disable,
+ * enable,deleteMarkerCreate
+ * @param string $callbackURL 通知URL,可以指定多个,失败依次重试
+ * @param string $access_key 可选,设置的话会对通知请求用对应的ak、sk进行签名
+ * @param string $host 可选,通知请求的host
+ *
+ * @return array
+ */
+ public function updateBucketEvent(
+ $bucket,
+ $name,
+ $prefix,
+ $suffix,
+ $event,
+ $callbackURL,
+ $access_key = null,
+ $host = null
+ ) {
+ $path = '/events/update';
+ $params = array();
+ if (!empty($bucket)) {
+ $params['bucket'] = $bucket;
+ }
+ if (!empty($name)) {
+ $params['name'] = $name;
+ }
+ if (!empty($prefix)) {
+ $params['prefix'] = $prefix;
+ }
+ if ($suffix) {
+ $params['suffix'] = $suffix;
+ }
+ if (!empty($event)) {
+ $params['event'] = $event;
+ }
+ if (!empty($callbackURL)) {
+ $params['callbackURL'] = $callbackURL;
+ }
+ if (!empty($access_key)) {
+ $params['access_key'] = $access_key;
+ }
+ if (!empty($host)) {
+ $params['host'] = $host;
+ }
+ $data = http_build_query($params);
+ if (!empty($event)) {
+ $eventpath = "";
+ foreach ($event as $key => $value) {
+ $eventpath .= "&event=$value";
+ }
+ $data .= $eventpath;
+ }
+ return $this->ucPost($path, $data);
+ }
+
+ /**
+ * 获取bucket事件通知规则
+ *
+ * @param string $bucket 空间名
+ * @return array
+ */
+ public function getBucketEvents($bucket)
+ {
+ $path = '/events/get?bucket=' . $bucket;
+ return $this->ucGet($path);
+ }
+
+ /**
+ * 删除bucket事件通知规则
+ *
+ * @param string $bucket 空间名
+ * @param string $name 规则名称bucket内唯一,长度小于50,不能为空,只能为字母、数字、下划线
+ * @return array
+ */
+ public function deleteBucketEvent($bucket, $name)
+ {
+ $path = '/events/delete';
+ $params = array();
+ if ($bucket) {
+ $params['bucket'] = $bucket;
+ }
+ if ($name) {
+ $params['name'] = $name;
+ }
+ $data = http_build_query($params);
+ return $this->ucPost($path, $data);
+ }
+
+ /**
+ * 获取bucket的跨域信息
+ *
+ * @param string $bucket 空间名
+ * @return array
+ */
+ public function getCorsRules($bucket)
+ {
+ $path = '/corsRules/get/' . $bucket;
+ return $this->ucGet($path);
+ }
+
+ /**
+ * 开关原图保护
+ *
+ * @param string $bucket 空间名称
+ * @param int $mode mode 为1表示开启原图保护,0表示关闭
+ * @return array
+ */
+ public function putBucketAccessStyleMode($bucket, $mode)
+ {
+ $path = '/accessMode/' . $bucket . '/mode/' . $mode;
+ return $this->ucPost($path, null);
+ }
+
+ /**
+ * 设置私有属性
+ *
+ * @param string $bucket 空间名称
+ * @param int $private private为0表示公开,为1表示私有
+ * @return array
+ */
+ public function putBucketAccessMode($bucket, $private)
+ {
+ $path = "/private?bucket=$bucket&private=$private";
+ return $this->ucPost($path, null);
+ }
+
+ /**
+ * 设置 referer 防盗链
+ *
+ * @param string $bucket 空间名称
+ * @param int $mode 0:关闭Referer(使用此选项将会忽略以下参数并将恢复默认值);
+ * 1:设置Referer白名单; 2:设置Referer黑名单
+ * @param string $norefer 0:不允许空 Refer 访问; 1:表示允许空Refer访问
+ * @param string $pattern 规则字符串
+ * @param int $enabled 源站是否支持,默认为0只给CDN配置, 设置为1表示开启源站防盗链
+ * @return array
+ * @link https://developer.qiniu.com/kodo/manual/6093/set-the-hotlinking-prevention
+ */
+ public function putReferAntiLeech($bucket, $mode, $norefer, $pattern, $enabled = 1)
+ {
+ $path = "/referAntiLeech?bucket=$bucket&mode=$mode&norefer=$norefer&pattern=$pattern&source_enabled=$enabled";
+ return $this->ucPost($path, null);
+ }
+
+ /**
+ * 设置Bucket的maxAge
+ *
+ * @param string $bucket 空间名称
+ * @param int $maxAge maxAge为0或者负数表示为默认值(31536000)
+ * @return array
+ */
+ public function putBucketMaxAge($bucket, $maxAge)
+ {
+ $path = '/maxAge?bucket=' . $bucket . '&maxAge=' . $maxAge;
+ return $this->ucPost($path, null);
+ }
+
+ /**
+ * 设置空间配额
+ *
+ * @param string $bucket 空间名称,不支持授权空间
+ * @param string $size 空间存储量配额,参数传入0或不传表示不更改当前配置,传入-1表示取消限额,新创建的空间默认没有限额
+ * @param string $count 空间文件数配额,参数含义同
+ * @return array
+ */
+ public function putBucketQuota($bucket, $size, $count)
+ {
+ $path = '/setbucketquota/' . $bucket . '/size/' . $size . '/count/' . $count;
+ return $this->apiPost($bucket, $path);
+ }
+
+ /**
+ * 获取空间配额
+ *
+ * @param string $bucket 空间名称
+ * @return array
+ */
+ public function getBucketQuota($bucket)
+ {
+ $path = '/getbucketquota/' . $bucket;
+ return $this->apiPost($bucket, $path);
+ }
+
+ /**
+ * 获取资源的元信息,但不返回文件内容
+ *
+ * @param string $bucket 待获取信息资源所在的空间
+ * @param string $key 待获取资源的文件名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1308/stat
+ */
+ public function stat($bucket, $key)
+ {
+ $path = '/stat/' . \Qiniu\entry($bucket, $key);
+ return $this->rsGet($bucket, $path);
+ }
+
+ /**
+ * 删除指定资源
+ *
+ * @param string $bucket 待删除资源所在的空间
+ * @param string $key 待删除资源的文件名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1257/delete
+ */
+ public function delete($bucket, $key)
+ {
+ $path = '/delete/' . \Qiniu\entry($bucket, $key);
+ return $this->rsPost($bucket, $path);
+ }
+
+ /**
+ * 给资源进行重命名,本质为move操作。
+ *
+ * @param string $bucket 待操作资源所在空间
+ * @param string $oldname 待操作资源文件名
+ * @param string $newname 目标资源文件名
+ *
+ * @return array
+ */
+ public function rename($bucket, $oldname, $newname)
+ {
+ return $this->move($bucket, $oldname, $bucket, $newname);
+ }
+
+ /**
+ * 对资源进行复制。
+ *
+ * @param string $from_bucket 待操作资源所在空间
+ * @param string $from_key 待操作资源文件名
+ * @param string $to_bucket 目标资源空间名
+ * @param string $to_key 目标资源文件名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1254/copy
+ */
+ public function copy($from_bucket, $from_key, $to_bucket, $to_key, $force = false)
+ {
+ $from = \Qiniu\entry($from_bucket, $from_key);
+ $to = \Qiniu\entry($to_bucket, $to_key);
+ $path = '/copy/' . $from . '/' . $to;
+ if ($force === true) {
+ $path .= '/force/true';
+ }
+ return $this->rsPost($from_bucket, $path);
+ }
+
+ /**
+ * 将资源从一个空间到另一个空间
+ *
+ * @param string $from_bucket 待操作资源所在空间
+ * @param string $from_key 待操作资源文件名
+ * @param string $to_bucket 目标资源空间名
+ * @param string $to_key 目标资源文件名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1288/move
+ */
+ public function move($from_bucket, $from_key, $to_bucket, $to_key, $force = false)
+ {
+ $from = \Qiniu\entry($from_bucket, $from_key);
+ $to = \Qiniu\entry($to_bucket, $to_key);
+ $path = '/move/' . $from . '/' . $to;
+ if ($force) {
+ $path .= '/force/true';
+ }
+ return $this->rsPost($from_bucket, $path);
+ }
+
+ /**
+ * 主动修改指定资源的文件元信息
+ *
+ * @param string $bucket 待操作资源所在空间
+ * @param string $key 待操作资源文件名
+ * @param string $mime 待操作文件目标mimeType
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1252/chgm
+ */
+ public function changeMime($bucket, $key, $mime)
+ {
+ $resource = \Qiniu\entry($bucket, $key);
+ $encode_mime = \Qiniu\base64_urlSafeEncode($mime);
+ $path = '/chgm/' . $resource . '/mime/' . $encode_mime;
+ return $this->rsPost($bucket, $path);
+ }
+
+
+ /**
+ * 修改指定资源的存储类型
+ *
+ * @param string $bucket 待操作资源所在空间
+ * @param string $key 待操作资源文件名
+ * @param int $fileType 对象存储类型
+ * 0 表示标准存储;
+ * 1 表示低频存储;
+ * 2 表示归档存储;
+ * 3 表示深度归档存储;
+ * 4 表示归档直读存储;
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/3710/chtype
+ */
+ public function changeType($bucket, $key, $fileType)
+ {
+ $resource = \Qiniu\entry($bucket, $key);
+ $path = '/chtype/' . $resource . '/type/' . $fileType;
+ return $this->rsPost($bucket, $path);
+ }
+
+ /**
+ * 解冻指定资源的存储类型
+ *
+ * @param string $bucket 待操作资源所在空间
+ * @param string $key 待操作资源文件名
+ * @param int $freezeAfterDays 解冻有效时长,取值范围 1~7
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/6380/restore-archive
+ */
+ public function restoreAr($bucket, $key, $freezeAfterDays)
+ {
+ $resource = \Qiniu\entry($bucket, $key);
+ $path = '/restoreAr/' . $resource . '/freezeAfterDays/' . $freezeAfterDays;
+ return $this->rsPost($bucket, $path);
+ }
+
+ /**
+ * 修改文件的存储状态,即禁用状态和启用状态间的的互相转换
+ *
+ * @param string $bucket 待操作资源所在空间
+ * @param string $key 待操作资源文件名
+ * @param int $status 0表示启用;1表示禁用
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/4173/modify-the-file-status
+ */
+ public function changeStatus($bucket, $key, $status)
+ {
+ $resource = \Qiniu\entry($bucket, $key);
+ $path = '/chstatus/' . $resource . '/status/' . $status;
+ return $this->rsPost($bucket, $path);
+ }
+
+ /**
+ * 从指定URL抓取资源,并将该资源存储到指定空间中
+ *
+ * @param string $url 指定的URL
+ * @param string $bucket 目标资源空间
+ * @param string $key 目标资源文件名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1263/fetch
+ */
+ public function fetch($url, $bucket, $key = null)
+ {
+
+ $resource = \Qiniu\base64_urlSafeEncode($url);
+ $to = \Qiniu\entry($bucket, $key);
+ $path = '/fetch/' . $resource . '/to/' . $to;
+
+ $ak = $this->auth->getAccessKey();
+
+
+ list($ioHost, $err) = $this->config->getIovipHostV2($ak, $bucket, $this->proxy->makeReqOpt());
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ $url = $ioHost . $path;
+ return $this->postV2($url, null);
+ }
+
+ /**
+ * 从指定URL异步抓取资源,并将该资源存储到指定空间中
+ *
+ * @param string $url 需要抓取的url
+ * @param string $bucket 所在区域的bucket
+ * @param string $host 从指定url下载数据时使用的Host
+ * @param string $key 文件存储的key
+ * @param string $md5 文件md5
+ * @param string $etag 文件etag
+ * @param string $callbackurl 回调URL
+ * @param string $callbackbody 回调Body
+ * @param string $callbackbodytype 回调Body内容类型,默认为"application/x-www-form-urlencoded"
+ * @param string $callbackhost 回调时使用的Host
+ * @param int $file_type 存储文件类型
+ * 0:标准存储(默认)
+ * 1:低频存储
+ * 2:归档存储
+ * 3:深度归档存储
+ * 4:归档直读存储
+ * @param bool $ignore_same_key 如果空间中已经存在同名文件则放弃本次抓取
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/4097/asynch-fetch
+ */
+ public function asynchFetch(
+ $url,
+ $bucket,
+ $host = null,
+ $key = null,
+ $md5 = null,
+ $etag = null,
+ $callbackurl = null,
+ $callbackbody = null,
+ $callbackbodytype = 'application/x-www-form-urlencoded',
+ $callbackhost = null,
+ $file_type = 0,
+ $ignore_same_key = false
+ ) {
+ $path = '/sisyphus/fetch';
+
+ $params = array('url' => $url, 'bucket' => $bucket);
+ \Qiniu\setWithoutEmpty($params, 'host', $host);
+ \Qiniu\setWithoutEmpty($params, 'key', $key);
+ \Qiniu\setWithoutEmpty($params, 'md5', $md5);
+ \Qiniu\setWithoutEmpty($params, 'etag', $etag);
+ \Qiniu\setWithoutEmpty($params, 'callbackurl', $callbackurl);
+ \Qiniu\setWithoutEmpty($params, 'callbackbody', $callbackbody);
+ \Qiniu\setWithoutEmpty($params, 'callbackbodytype', $callbackbodytype);
+ \Qiniu\setWithoutEmpty($params, 'callbackhost', $callbackhost);
+ \Qiniu\setWithoutEmpty($params, 'file_type', $file_type);
+ \Qiniu\setWithoutEmpty($params, 'ignore_same_key', $ignore_same_key);
+ $data = json_encode($params);
+
+ return $this->apiPost($bucket, $path, $data);
+ }
+
+
+ /**
+ * 查询异步第三方资源抓取任务状态
+ *
+ * @param string $zone
+ * @param string $id
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/4097/asynch-fetch
+ */
+ public function asynchFetchStatus($zone, $id)
+ {
+ $scheme = "http://";
+
+ if ($this->config->useHTTPS === true) {
+ $scheme = "https://";
+ }
+
+ $url = $scheme . "api-" . $zone . ".qiniuapi.com/sisyphus/fetch?id=" . $id;
+
+ list($ret, $err) = $this->getV2($url);
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+ return array($ret, null);
+ }
+
+
+ /**
+ * 从镜像源站抓取资源到空间中,如果空间中已经存在,则覆盖该资源
+ *
+ * @param string $bucket 待获取资源所在的空间
+ * @param string $key 代获取资源文件名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1293/prefetch
+ */
+ public function prefetch($bucket, $key)
+ {
+ $resource = \Qiniu\entry($bucket, $key);
+ $path = '/prefetch/' . $resource;
+
+ $ak = $this->auth->getAccessKey();
+ list($ioHost, $err) = $this->config->getIovipHostV2($ak, $bucket, $this->proxy->makeReqOpt());
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ $url = $ioHost . $path;
+ return $this->postV2($url, null);
+ }
+
+ /**
+ * 在单次请求中进行多个资源管理操作
+ *
+ * @param array $operations 资源管理操作数组
+ *
+ * @return array 每个资源的处理情况,结果类似:
+ * [
+ * { "code" => , "data" => },
+ * { "code" => },
+ * { "code" => },
+ * { "code" => },
+ * { "code" => , "data" => { "error": "" } },
+ * ...
+ * ]
+ * @link http://developer.qiniu.com/docs/v6/api/reference/rs/batch.html
+ */
+ public function batch($operations)
+ {
+ $scheme = "http://";
+ if ($this->config->useHTTPS === true) {
+ $scheme = "https://";
+ }
+ $params = 'op=' . implode('&op=', $operations);
+ $errResp = new Response(0, 0);
+ if (count($operations) <= 0) {
+ $errResp->error = 'empty operations';
+ return array(null, new Error($scheme . '/batch', $errResp));
+ }
+ $bucket = '';
+ foreach ($operations as $op) {
+ $segments = explode('/', $op);
+ if (count($segments) < 3) {
+ continue;
+ }
+ list($bucket,) = \Qiniu\decodeEntry($segments[2]);
+ }
+ return $this->rsPost($bucket, '/batch', $params);
+ }
+
+ /**
+ * 设置文件的生命周期
+ *
+ * @param string $bucket 设置文件生命周期文件所在的空间
+ * @param string $key 设置文件生命周期文件的文件名
+ * @param int $days 设置该文件多少天后删除,当$days设置为0时表示取消该文件的生命周期
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/update-file-lifecycle
+ */
+ public function deleteAfterDays($bucket, $key, $days)
+ {
+ $entry = \Qiniu\entry($bucket, $key);
+ $path = "/deleteAfterDays/$entry/$days";
+ return $this->rsPost($bucket, $path);
+ }
+
+ /**
+ * 更新 object 生命周期
+ *
+ * @param string $bucket 空间名
+ * @param string $key 目标资源
+ * @param int $to_line_after_days 多少天后将文件转为低频存储。
+ * -1 表示取消已设置的转低频存储的生命周期规则;
+ * 0 表示不修改转低频生命周期规则。
+ * @param int $to_archive_ir_after_days 多少天后转为归档直读存储。
+ * -1 表示取消已设置的转归档直读存储的生命周期规则;
+ * 0 表示不修改转归档直读生命周期规则。
+ * @param int $to_archive_after_days 多少天后将文件转为归档存储。
+ * -1 表示取消已设置的转归档存储的生命周期规则;
+ * 0 表示不修改转归档生命周期规则。
+ * @param int $to_deep_archive_after_days 多少天后将文件转为深度归档存储。
+ * -1 表示取消已设置的转深度归档存储的生命周期规则;
+ * 0 表示不修改转深度归档生命周期规则。
+ * @param int $delete_after_days 多少天后将文件删除。
+ * -1 表示取消已设置的删除存储的生命周期规则;
+ * 0 表示不修改删除存储的生命周期规则。
+ * @return array
+ */
+ public function setObjectLifecycle(
+ $bucket,
+ $key,
+ $to_line_after_days = 0,
+ $to_archive_after_days = 0,
+ $to_deep_archive_after_days = 0,
+ $delete_after_days = 0,
+ $to_archive_ir_after_days = 0
+ ) {
+ return $this->setObjectLifecycleWithCond(
+ $bucket,
+ $key,
+ null,
+ $to_line_after_days,
+ $to_archive_after_days,
+ $to_deep_archive_after_days,
+ $delete_after_days,
+ $to_archive_ir_after_days
+ );
+ }
+
+ /**
+ * 更新 object 生命周期
+ *
+ * @param string $bucket 空间名
+ * @param string $key 目标资源
+ * @param int $to_line_after_days 多少天后将文件转为低频存储。
+ * 设置为 -1 表示取消已设置的转低频存储的生命周期规则;
+ * 0 表示不修改转低频生命周期规则。
+ * @param int $to_archive_ir_after_days 多少天后将文件转为归档直读存储。
+ * 设置为 -1 表示取消已设置的转归档直读存储的生命周期规则;
+ * 0 表示不修改转归档直读生命周期规则。
+ * @param int $to_archive_after_days 多少天后将文件转为归档存储。
+ * -1 表示取消已设置的转归档存储的生命周期规则;
+ * 0 表示不修改转归档生命周期规则。
+ * @param int $to_deep_archive_after_days 多少天后将文件转为深度归档存储。
+ * -1 表示取消已设置的转深度归档存储的生命周期规则;
+ * 0 表示不修改转深度归档生命周期规则。
+ * @param int $delete_after_days 多少天后将文件删除。
+ * -1 表示取消已设置的删除存储的生命周期规则;
+ * 0 表示不修改删除存储的生命周期规则。
+ * @param array $cond 匹配条件,只有条件匹配才会设置成功。
+ * 目前支持:hash、mime、fsize、putTime
+ * @return array
+ */
+ public function setObjectLifecycleWithCond(
+ $bucket,
+ $key,
+ $cond = null,
+ $to_line_after_days = 0,
+ $to_archive_after_days = 0,
+ $to_deep_archive_after_days = 0,
+ $delete_after_days = 0,
+ $to_archive_ir_after_days = 0
+ ) {
+ $encodedEntry = \Qiniu\entry($bucket, $key);
+ $path = '/lifecycle/' . $encodedEntry .
+ '/toIAAfterDays/' . $to_line_after_days .
+ '/toArchiveIRAfterDays/' . $to_archive_ir_after_days .
+ '/toArchiveAfterDays/' . $to_archive_after_days .
+ '/toDeepArchiveAfterDays/' . $to_deep_archive_after_days .
+ '/deleteAfterDays/' . $delete_after_days;
+ if ($cond != null) {
+ $condStrArr = array();
+ foreach ($cond as $key => $value) {
+ array_push($condStrArr, $key . '=' . $value);
+ }
+ $condStr = implode('&', $condStrArr);
+ $path .= '/cond' . \Qiniu\base64_urlSafeEncode($condStr);
+ }
+ return $this->rsPost($bucket, $path);
+ }
+
+ private function rsfGet($bucket, $path)
+ {
+ list($host, $err) = $this->config->getRsfHostV2(
+ $this->auth->getAccessKey(),
+ $bucket,
+ $this->proxy->makeReqOpt()
+ );
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ return $this->getV2($host . $path);
+ }
+
+ private function rsGet($bucket, $path)
+ {
+ list($host, $err) = $this->config->getRsHostV2(
+ $this->auth->getAccessKey(),
+ $bucket,
+ $this->proxy->makeReqOpt()
+ );
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ return $this->getV2($host . $path);
+ }
+
+ private function rsPost($bucket, $path, $body = null)
+ {
+ list($host, $err) = $this->config->getRsHostV2(
+ $this->auth->getAccessKey(),
+ $bucket,
+ $this->proxy->makeReqOpt()
+ );
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ return $this->postV2($host . $path, $body);
+ }
+
+ private function apiGet($bucket, $path)
+ {
+ list($host, $err) = $this->config->getApiHostV2(
+ $this->auth->getAccessKey(),
+ $bucket,
+ $this->proxy->makeReqOpt()
+ );
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ return $this->getV2($host . $path);
+ }
+
+ private function apiPost($bucket, $path, $body = null)
+ {
+
+ list($host, $err) = $this->config->getApiHostV2(
+ $this->auth->getAccessKey(),
+ $bucket,
+ $this->proxy->makeReqOpt()
+ );
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ return $this->postV2($host . $path, $body);
+ }
+
+ private function ucGet($path)
+ {
+ $url = $this->config->getUcHost() . $path;
+ return $this->getV2($url);
+ }
+
+ private function ucPost($path, $body = null)
+ {
+ $url = $this->config->getUcHost() . $path;
+ return $this->postV2($url, $body);
+ }
+
+ private function getV2($url)
+ {
+ $headers = $this->auth->authorizationV2($url, 'GET', null, 'application/x-www-form-urlencoded');
+ $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 postV2($url, $body)
+ {
+ $headers = $this->auth->authorizationV2($url, 'POST', $body, 'application/x-www-form-urlencoded');
+ $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);
+ }
+
+ public static function buildBatchCopy($source_bucket, $key_pairs, $target_bucket, $force)
+ {
+ return self::twoKeyBatch('/copy', $source_bucket, $key_pairs, $target_bucket, $force);
+ }
+
+
+ public static function buildBatchRename($bucket, $key_pairs, $force)
+ {
+ return self::buildBatchMove($bucket, $key_pairs, $bucket, $force);
+ }
+
+
+ public static function buildBatchMove($source_bucket, $key_pairs, $target_bucket, $force)
+ {
+ return self::twoKeyBatch('/move', $source_bucket, $key_pairs, $target_bucket, $force);
+ }
+
+
+ public static function buildBatchDelete($bucket, $keys)
+ {
+ return self::oneKeyBatch('/delete', $bucket, $keys);
+ }
+
+
+ public static function buildBatchStat($bucket, $keys)
+ {
+ return self::oneKeyBatch('/stat', $bucket, $keys);
+ }
+
+ public static function buildBatchDeleteAfterDays($bucket, $key_day_pairs)
+ {
+ $data = array();
+ foreach ($key_day_pairs as $key => $day) {
+ array_push($data, '/deleteAfterDays/' . \Qiniu\entry($bucket, $key) . '/' . $day);
+ }
+ return $data;
+ }
+
+ /**
+ * @param string $bucket 空间名
+ * @param array $keys 目标资源
+ * @param int $to_line_after_days 多少天后将文件转为低频存储。
+ * -1 表示取消已设置的转低频存储的生命周期规则;
+ * 0 表示不修改转低频生命周期规则。
+ * @param int $to_archive_ir_after_days 多少天后将文件转为归档直读。
+ * -1 表示取消已设置的转归档只读的生命周期规则;
+ * 0 表示不修改转归档只读周期规则。
+ * @param int $to_archive_after_days 多少天后将文件转为归档存储。
+ * -1 表示取消已设置的转归档存储的生命周期规则;
+ * 0 表示不修改转归档生命周期规则。
+ * @param int $to_deep_archive_after_days 多少天后将文件转为深度归档存储。
+ * -1 表示取消已设置的转深度归档存储的生命周期规则;
+ * 0 表示不修改转深度归档生命周期规则。
+ * @param int $delete_after_days 多少天后将文件删除。
+ * -1 表示取消已设置的删除存储的生命周期规则;
+ * 0 表示不修改删除存储的生命周期规则。
+ *
+ * @retrun array
+ */
+ public static function buildBatchSetObjectLifecycle(
+ $bucket,
+ $keys,
+ $to_line_after_days,
+ $to_archive_after_days,
+ $to_deep_archive_after_days,
+ $delete_after_days,
+ $to_archive_ir_after_days = 0
+ ) {
+ $result = array();
+ foreach ($keys as $key) {
+ $encodedEntry = \Qiniu\entry($bucket, $key);
+ $op = '/lifecycle/' . $encodedEntry .
+ '/toIAAfterDays/' . $to_line_after_days .
+ '/toArchiveIRAfterDays/' . $to_archive_ir_after_days .
+ '/toArchiveAfterDays/' . $to_archive_after_days .
+ '/toDeepArchiveAfterDays/' . $to_deep_archive_after_days .
+ '/deleteAfterDays/' . $delete_after_days;
+ array_push($result, $op);
+ }
+ return $result;
+ }
+
+ public static function buildBatchChangeMime($bucket, $key_mime_pairs)
+ {
+ $data = array();
+ foreach ($key_mime_pairs as $key => $mime) {
+ array_push($data, '/chgm/' . \Qiniu\entry($bucket, $key) . '/mime/' . base64_encode($mime));
+ }
+ return $data;
+ }
+
+ public static function buildBatchChangeType($bucket, $key_type_pairs)
+ {
+ $data = array();
+ foreach ($key_type_pairs as $key => $type) {
+ array_push($data, '/chtype/' . \Qiniu\entry($bucket, $key) . '/type/' . $type);
+ }
+ return $data;
+ }
+
+ public static function buildBatchRestoreAr($bucket, $key_restore_days_pairs)
+ {
+ $data = array();
+ foreach ($key_restore_days_pairs as $key => $restore_days) {
+ array_push($data, '/restoreAr/' . \Qiniu\entry($bucket, $key) . '/freezeAfterDays/' . $restore_days);
+ }
+ return $data;
+ }
+
+ private static function oneKeyBatch($operation, $bucket, $keys)
+ {
+ $data = array();
+ foreach ($keys as $key) {
+ array_push($data, $operation . '/' . \Qiniu\entry($bucket, $key));
+ }
+ return $data;
+ }
+
+ private static function twoKeyBatch($operation, $source_bucket, $key_pairs, $target_bucket, $force)
+ {
+ if ($target_bucket === null) {
+ $target_bucket = $source_bucket;
+ }
+ $data = array();
+ $forceOp = "false";
+ if ($force) {
+ $forceOp = "true";
+ }
+ foreach ($key_pairs as $from_key => $to_key) {
+ $from = \Qiniu\entry($source_bucket, $from_key);
+ $to = \Qiniu\entry($target_bucket, $to_key);
+ array_push($data, $operation . '/' . $from . '/' . $to . "/force/" . $forceOp);
+ }
+ return $data;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Storage/FormUploader.php b/php-sdk/src/Qiniu/Storage/FormUploader.php
new file mode 100644
index 0000000..d68654d
--- /dev/null
+++ b/php-sdk/src/Qiniu/Storage/FormUploader.php
@@ -0,0 +1,165 @@
+ "",
+ * "key" => ""
+ * ]
+ */
+ 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" => "",
+ * "key" => ""
+ * ]
+ */
+ 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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Storage/ResumeUploader.php b/php-sdk/src/Qiniu/Storage/ResumeUploader.php
new file mode 100644
index 0000000..00e88ef
--- /dev/null
+++ b/php-sdk/src/Qiniu/Storage/ResumeUploader.php
@@ -0,0 +1,580 @@
+ $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);
+ }
+}
diff --git a/php-sdk/src/Qiniu/Storage/UploadManager.php b/php-sdk/src/Qiniu/Storage/UploadManager.php
new file mode 100644
index 0000000..fcd11fa
--- /dev/null
+++ b/php-sdk/src/Qiniu/Storage/UploadManager.php
@@ -0,0 +1,176 @@
+config = $config;
+
+ if ($reqOpt === null) {
+ $reqOpt = new RequestOptions();
+ }
+
+ $this->reqOpt = $reqOpt;
+ }
+
+ /**
+ * 上传二进制流到七牛
+ *
+ * @param string $upToken 上传凭证
+ * @param string $key 上传文件名
+ * @param string $data 上传二进制流
+ * @param array $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" => "",
+ * "key" => ""
+ * ]
+ */
+ 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 $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 包含已上传文件的信息,类似:
+ * [
+ * "hash" => "",
+ * "key" => ""
+ * ]
+ * @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;
+ }
+}
diff --git a/php-sdk/src/Qiniu/Zone.php b/php-sdk/src/Qiniu/Zone.php
new file mode 100644
index 0000000..50d60c6
--- /dev/null
+++ b/php-sdk/src/Qiniu/Zone.php
@@ -0,0 +1,58 @@
+ $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
+ */
+ 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
+ */
+ 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
+ */
+ 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";
+ }
+}
diff --git a/php-sdk/test-env.sh b/php-sdk/test-env.sh
new file mode 100644
index 0000000..eedf6b5
--- /dev/null
+++ b/php-sdk/test-env.sh
@@ -0,0 +1,4 @@
+export QINIU_ACCESS_KEY=xxx
+export QINIU_SECRET_KEY=xxx
+export QINIU_TEST_BUCKET=phpsdk
+export QINIU_TEST_DOMAIN=phpsdk.qiniudn.com
\ No newline at end of file
diff --git a/php-sdk/tests/Qiniu/Tests/AuthTest.php b/php-sdk/tests/Qiniu/Tests/AuthTest.php
new file mode 100644
index 0000000..99aec85
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/AuthTest.php
@@ -0,0 +1,296 @@
+sign('test');
+ $this->assertEquals('abcdefghklmnopq:mSNBTR7uS2crJsyFr2Amwv1LaYg=', $token);
+ }
+
+ public function testSignWithData()
+ {
+ global $dummyAuth;
+ $token = $dummyAuth->signWithData('test');
+ $this->assertEquals('abcdefghklmnopq:-jP8eEV9v48MkYiBGs81aDxl60E=:dGVzdA==', $token);
+ }
+
+ public function testSignRequest()
+ {
+ global $dummyAuth;
+ $token = $dummyAuth->signRequest('http://www.qiniu.com?go=1', 'test', '');
+ $this->assertEquals('abcdefghklmnopq:cFyRVoWrE3IugPIMP5YJFTO-O-Y=', $token);
+ $ctype = 'application/x-www-form-urlencoded';
+ $token = $dummyAuth->signRequest('http://www.qiniu.com?go=1', 'test', $ctype);
+ $this->assertEquals($token, 'abcdefghklmnopq:svWRNcacOE-YMsc70nuIYdaa1e4=');
+ }
+
+ public function testPrivateDownloadUrl()
+ {
+ global $dummyAuth;
+ $_SERVER['override_qiniu_auth_time'] = true;
+ $url = $dummyAuth->privateDownloadUrl('http://www.qiniu.com?go=1');
+ $expect = 'http://www.qiniu.com?go=1&e=1234571490&token=abcdefghklmnopq:8vzBeLZ9W3E4kbBLFLW0Xe0u7v4=';
+ $this->assertEquals($expect, $url);
+ unset($_SERVER['override_qiniu_auth_time']);
+ }
+
+ public function testUploadToken()
+ {
+ global $dummyAuth;
+ $_SERVER['override_qiniu_auth_time'] = true;
+ $token = $dummyAuth->uploadToken('1', '2', 3600, array('endUser' => 'y'));
+ // @codingStandardsIgnoreStart
+ $exp = 'abcdefghklmnopq:yyeexeUkPOROoTGvwBjJ0F0VLEo=:eyJlbmRVc2VyIjoieSIsInNjb3BlIjoiMToyIiwiZGVhZGxpbmUiOjEyMzQ1NzE0OTB9';
+ // @codingStandardsIgnoreEnd
+ $this->assertEquals($exp, $token);
+ unset($_SERVER['override_qiniu_auth_time']);
+ }
+
+ public function testSignQiniuAuthorization()
+ {
+ $auth = new Auth("ak", "sk");
+
+ $testCases = array(
+ array(
+ "url" => "",
+ "method" => "",
+ "headers" => array(
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ "Content-Type" => array("application/x-www-form-urlencoded")
+ ),
+ "body" => "{\"name\": \"test\"}",
+ "expectedToken" => "ak:0i1vKClRDWFyNkcTFzwcE7PzX74="
+ ),
+ array(
+ "url" => "",
+ "method" => "",
+ "headers" => array(
+ "Content-Type" => array("application/json")
+ ),
+ "body" => "{\"name\": \"test\"}",
+ "expectedToken" => "ak:K1DI0goT05yhGizDFE5FiPJxAj4="
+ ),
+ array(
+ "url" => "",
+ "method" => "GET",
+ "headers" => array(
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ "Content-Type" => array("application/x-www-form-urlencoded"),
+ ),
+ "body" => "{\"name\": \"test\"}",
+ "expectedToken" => "ak:0i1vKClRDWFyNkcTFzwcE7PzX74="
+ ),
+ array(
+ "url" => "",
+ "method" => "POST",
+ "headers" => array(
+ "Content-Type" => array("application/json"),
+ "X-Qiniu" => array("b"),
+ ),
+ "body" => "{\"name\": \"test\"}",
+ "expectedToken" => "ak:0ujEjW_vLRZxebsveBgqa3JyQ-w="
+ ),
+ array(
+ "url" => "http://upload.qiniup.com",
+ "method" => "",
+ "headers" => array(
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ "Content-Type" => array("application/x-www-form-urlencoded"),
+ ),
+ "body" => "{\"name\": \"test\"}",
+ "expectedToken" => "ak:GShw5NitGmd5TLoo38nDkGUofRw="
+ ),
+ array(
+ "url" => "http://upload.qiniup.com",
+ "method" => "",
+ "headers" => array(
+ "Content-Type" => array("application/json"),
+ "X-Qiniu-Bbb" => array("BBB", "AAA"),
+ "X-Qiniu-Aaa" => array("DDD", "CCC"),
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ ),
+ "body" => "{\"name\": \"test\"}",
+ "expectedToken" => "ak:DhNA1UCaBqSHCsQjMOLRfVn63GQ="
+ ),
+ array(
+ "url" => "http://upload.qiniup.com",
+ "method" => "",
+ "headers" => array(
+ "Content-Type" => array("application/x-www-form-urlencoded"),
+ "X-Qiniu-Bbb" => array("BBB", "AAA"),
+ "X-Qiniu-Aaa" => array("DDD", "CCC"),
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ ),
+ "body" => "name=test&language=go",
+ "expectedToken" => "ak:KUAhrYh32P9bv0COD8ugZjDCmII="
+ ),
+ array(
+ "url" => "http://upload.qiniup.com",
+ "method" => "",
+ "headers" => array(
+ "Content-Type" => array("application/x-www"),
+ "Content-Type" => array("application/x-www-form-urlencoded"),
+ "X-Qiniu-Bbb" => array("BBB", "AAA"),
+ "X-Qiniu-Aaa" => array("DDD", "CCC"),
+ ),
+ "body" => "name=test&language=go",
+ "expectedToken" => "ak:KUAhrYh32P9bv0COD8ugZjDCmII="
+ ),
+ array(
+ "url" => "http://upload.qiniup.com/mkfile/sdf.jpg",
+ "method" => "",
+ "headers" => array(
+ "Content-Type" => array("application/x-www-form-urlencoded"),
+ "X-Qiniu-Bbb" => array("BBB", "AAA"),
+ "X-Qiniu-Aaa" => array("DDD", "CCC"),
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ ),
+ "body" => "name=test&language=go",
+ "expectedToken" => "ak:fkRck5_LeyfwdkyyLk-hyNwGKac="
+ ),
+ array(
+ "url" => "http://upload.qiniup.com/mkfile/sdf.jpg?s=er3&df",
+ "method" => "",
+ "headers" => array(
+ "Content-Type" => array("application/x-www-form-urlencoded"),
+ "X-Qiniu-Bbb" => array("BBB", "AAA"),
+ "X-Qiniu-Aaa" => array("DDD", "CCC"),
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ ),
+ "body" => "name=test&language=go",
+ "expectedToken" => "ak:PUFPWsEUIpk_dzUvvxTTmwhp3p4="
+ )
+ );
+
+ foreach ($testCases as $testCase) {
+ list($sign, $err) = $auth->signQiniuAuthorization(
+ $testCase["url"],
+ $testCase["method"],
+ $testCase["body"],
+ new Header($testCase["headers"])
+ );
+
+ $this->assertNull($err);
+ $this->assertEquals($testCase["expectedToken"], $sign);
+ }
+ }
+
+ public function testDisableQiniuTimestampSignatureDefault()
+ {
+ $auth = new Auth("ak", "sk");
+ $authedHeaders = $auth->authorizationV2("https://example.com", "GET");
+ $this->assertArrayHasKey("X-Qiniu-Date", $authedHeaders);
+ }
+
+ public function testDisableQiniuTimestampSignature()
+ {
+ $auth = new Auth("ak", "sk", array(
+ "disableQiniuTimestampSignature" => true
+ ));
+ $authedHeaders = $auth->authorizationV2("https://example.com", "GET");
+ $this->assertArrayNotHasKey("X-Qiniu-Date", $authedHeaders);
+ }
+ public function testDisableQiniuTimestampSignatureEnv()
+ {
+ putenv("DISABLE_QINIU_TIMESTAMP_SIGNATURE=true");
+ $auth = new Auth("ak", "sk");
+ $authedHeaders = $auth->authorizationV2("https://example.com", "GET");
+ $this->assertArrayNotHasKey("X-Qiniu-Date", $authedHeaders);
+ putenv('DISABLE_QINIU_TIMESTAMP_SIGNATURE');
+ }
+ public function testDisableQiniuTimestampSignatureEnvBeIgnored()
+ {
+ putenv("DISABLE_QINIU_TIMESTAMP_SIGNATURE=true");
+ $auth = new Auth("ak", "sk", array(
+ "disableQiniuTimestampSignature" => false
+ ));
+ $authedHeaders = $auth->authorizationV2("https://example.com", "GET");
+ $this->assertArrayHasKey("X-Qiniu-Date", $authedHeaders);
+ putenv('DISABLE_QINIU_TIMESTAMP_SIGNATURE');
+ }
+ public function testQboxVerifyCallbackShouldOkWithRequiredOptions()
+ {
+ $auth = new Auth('abcdefghklmnopq', '1234567890');
+ $ok = $auth->verifyCallback(
+ 'application/x-www-form-urlencoded',
+ 'QBox abcdefghklmnopq:T7F-SjxX7X2zI4Fc1vANiNt1AUE=',
+ 'https://test.qiniu.com/callback',
+ 'name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2&location=Shanghai&price=1500.00&uid=123'
+ );
+ $this->assertTrue($ok);
+ }
+ public function testQboxVerifyCallbackShouldOkWithOmitOptions()
+ {
+ $auth = new Auth('abcdefghklmnopq', '1234567890');
+ $ok = $auth->verifyCallback(
+ 'application/x-www-form-urlencoded',
+ 'QBox abcdefghklmnopq:T7F-SjxX7X2zI4Fc1vANiNt1AUE=',
+ 'https://test.qiniu.com/callback',
+ 'name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2&location=Shanghai&price=1500.00&uid=123',
+ 'POST', // this should be omit
+ array(
+ 'X-Qiniu-Bbb' => 'BBB'
+ ) // this should be omit
+ );
+ $this->assertTrue($ok);
+ }
+ public function testQiniuVerifyCallbackShouldOk()
+ {
+ $auth = new Auth('abcdefghklmnopq', '1234567890');
+ $ok = $auth->verifyCallback(
+ 'application/x-www-form-urlencoded',
+ 'Qiniu abcdefghklmnopq:ZqS7EZuAKrhZaEIxqNGxDJi41IQ=',
+ 'https://test.qiniu.com/callback',
+ 'name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2&location=Shanghai&price=1500.00&uid=123',
+ 'GET',
+ array(
+ 'X-Qiniu-Bbb' => 'BBB'
+ )
+ );
+ $this->assertTrue($ok);
+ }
+ public function testQiniuVerifyCallbackShouldFailed()
+ {
+ $auth = new Auth('abcdefghklmnopq', '1234567890');
+ $ok = $auth->verifyCallback(
+ 'application/x-www-form-urlencoded',
+ 'Qiniu abcdefghklmnopq:ZqS7EZuAKrhZaEIxqNGxDJi41IQ=',
+ 'https://test.qiniu.com/callback',
+ 'name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2&location=Shanghai&price=1500.00&uid=123',
+ 'POST',
+ array(
+ 'X-Qiniu-Bbb' => 'BBB'
+ )
+ );
+ $this->assertFalse($ok);
+ }
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/Base64Test.php b/php-sdk/tests/Qiniu/Tests/Base64Test.php
new file mode 100644
index 0000000..fed3da0
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/Base64Test.php
@@ -0,0 +1,16 @@
+assertEquals($a, \Qiniu\base64_urlSafeDecode($b));
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/BucketTest.php b/php-sdk/tests/Qiniu/Tests/BucketTest.php
new file mode 100644
index 0000000..0467698
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/BucketTest.php
@@ -0,0 +1,733 @@
+batch($ops);
+ }
+
+ private static function getObjectKey($key)
+ {
+ $result = $key . rand();
+
+ self::$bucketManager->copy(
+ self::$bucketName,
+ $key,
+ self::$bucketName,
+ $result
+ );
+
+ self::$keysToCleanup[] = $result;
+
+ return $result;
+ }
+
+ public function testBuckets()
+ {
+
+ list($list, $error) = self::$bucketManager->buckets();
+ $this->assertNull($error);
+ $this->assertTrue(in_array(self::$bucketName, $list));
+
+ list($list2, $error) = self::$dummyBucketManager->buckets();
+ $this->assertEquals(401, $error->code());
+ $this->assertNotNull($error->message());
+ $this->assertNotNull($error->getResponse());
+ $this->assertNull($list2);
+ }
+
+ public function testListBuckets()
+ {
+ list($ret, $error) = self::$bucketManager->listbuckets('z0');
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testCreateBucket()
+ {
+ list($ret, $error) = self::$bucketManager->createBucket(self::$bucketToCreate);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testDeleteBucket()
+ {
+ list($ret, $error) = self::$bucketManager->deleteBucket(self::$bucketToCreate);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testDomains()
+ {
+ list($ret, $error) = self::$bucketManager->domains(self::$bucketName);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testBucketInfo()
+ {
+ list($ret, $error) = self::$bucketManager->bucketInfo(self::$bucketName);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testBucketInfos()
+ {
+ list($ret, $error) = self::$bucketManager->bucketInfos('z0');
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testList()
+ {
+ list($ret, $error) = self::$bucketManager->listFiles(self::$bucketName, null, null, 10);
+ $this->assertNull($error);
+ $this->assertNotNull($ret['items'][0]);
+ $this->assertNotNull($ret['marker']);
+ }
+
+ public function testListFilesv2()
+ {
+ list($ret, $error) = self::$bucketManager->listFilesv2(self::$bucketName, null, null, 10);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testBucketLifecycleRule()
+ {
+ // delete
+ self::$bucketManager->deleteBucketLifecycleRule(self::$bucketName, self::$bucketLifeRuleName);
+
+ // add
+ list($ret, $error) = self::$bucketManager->bucketLifecycleRule(
+ self::$bucketName,
+ self::$bucketLifeRuleName,
+ self::$bucketLifeRulePrefix,
+ 80,
+ 70,
+ 72,
+ 74,
+ 71
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+
+ // get
+ list($ret, $error) = self::$bucketManager->getBucketLifecycleRules(self::$bucketName);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ $rule = null;
+ foreach ($ret as $r) {
+ if ($r["name"] === self::$bucketLifeRuleName) {
+ $rule = $r;
+ break;
+ }
+ }
+ $this->assertNotNull($rule);
+ $this->assertEquals(self::$bucketLifeRulePrefix, $rule["prefix"]);
+ $this->assertEquals(80, $rule["delete_after_days"]);
+ $this->assertEquals(70, $rule["to_line_after_days"]);
+ $this->assertEquals(71, $rule["to_archive_ir_after_days"]);
+ $this->assertEquals(72, $rule["to_archive_after_days"]);
+ $this->assertEquals(74, $rule["to_deep_archive_after_days"]);
+
+ // update
+ list($ret, $error) = self::$bucketManager->updateBucketLifecycleRule(
+ self::$bucketName,
+ self::$bucketLifeRuleName,
+ 'update-' . self::$bucketLifeRulePrefix,
+ 90,
+ 75,
+ 80,
+ 85,
+ 78
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+
+ // get
+ list($ret, $error) = self::$bucketManager->getBucketLifecycleRules(self::$bucketName);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ $rule = null;
+ foreach ($ret as $r) {
+ if ($r["name"] === self::$bucketLifeRuleName) {
+ $rule = $r;
+ break;
+ }
+ }
+ $this->assertNotNull($rule);
+ $this->assertEquals('update-' . self::$bucketLifeRulePrefix, $rule["prefix"]);
+ $this->assertEquals(90, $rule["delete_after_days"]);
+ $this->assertEquals(75, $rule["to_line_after_days"]);
+ $this->assertEquals(78, $rule["to_archive_ir_after_days"]);
+ $this->assertEquals(80, $rule["to_archive_after_days"]);
+ $this->assertEquals(85, $rule["to_deep_archive_after_days"]);
+
+ // delete
+ list($ret, $error) = self::$bucketManager->deleteBucketLifecycleRule(
+ self::$bucketName,
+ self::$bucketLifeRuleName
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testPutBucketEvent()
+ {
+ list($ret, $error) = self::$bucketManager->putBucketEvent(
+ self::$bucketName,
+ self::$bucketEventName,
+ self::$bucketEventPrefix,
+ 'img',
+ array('copy'),
+ self::$customCallbackURL
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testUpdateBucketEvent()
+ {
+ list($ret, $error) = self::$bucketManager->updateBucketEvent(
+ self::$bucketName,
+ self::$bucketEventName,
+ self::$bucketEventPrefix,
+ 'video',
+ array('copy'),
+ self::$customCallbackURL
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testGetBucketEvents()
+ {
+ list($ret, $error) = self::$bucketManager->getBucketEvents(self::$bucketName);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testDeleteBucketEvent()
+ {
+ list($ret, $error) = self::$bucketManager->deleteBucketEvent(self::$bucketName, self::$bucketEventName);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testStat()
+ {
+ list($stat, $error) = self::$bucketManager->stat(self::$bucketName, self::$key);
+ $this->assertNull($error);
+ $this->assertNotNull($stat);
+ $this->assertNotNull($stat['hash']);
+
+ list($stat, $error) = self::$bucketManager->stat(self::$bucketName, 'nofile');
+ $this->assertEquals(612, $error->code());
+ $this->assertNotNull($error->message());
+ $this->assertNull($stat);
+
+ list($stat, $error) = self::$bucketManager->stat('nobucket', 'nofile');
+ $this->assertEquals(631, $error->code());
+ $this->assertNotNull($error->message());
+ $this->assertNull($stat);
+ }
+
+ public function testDelete()
+ {
+ $fileToDel = self::getObjectKey(self::$key);
+ list(, $error) = self::$bucketManager->delete(self::$bucketName, $fileToDel);
+ $this->assertNull($error);
+ }
+
+
+ public function testRename()
+ {
+ $fileToRename = self::getObjectKey(self::$key);
+ $fileRenamed = $fileToRename . 'new';
+ list(, $error) = self::$bucketManager->rename(self::$bucketName, $fileToRename, $fileRenamed);
+ $this->assertNull($error);
+ self::$keysToCleanup[] = $fileRenamed;
+ }
+
+
+ public function testCopy()
+ {
+ $fileToCopy = self::getObjectKey(self::$key2);
+ $fileCopied = $fileToCopy . 'copied';
+
+ //test force copy
+ list(, $error) = self::$bucketManager->copy(
+ self::$bucketName,
+ $fileToCopy,
+ self::$bucketName,
+ $fileCopied,
+ true
+ );
+ $this->assertNull($error);
+
+ list($fileToCopyStat,) = self::$bucketManager->stat(self::$bucketName, $fileToCopy);
+ list($fileCopiedStat,) = self::$bucketManager->stat(self::$bucketName, $fileCopied);
+
+ $this->assertEquals($fileToCopyStat['hash'], $fileCopiedStat['hash']);
+
+ self::$keysToCleanup[] = $fileCopied;
+ }
+
+
+ public function testChangeMime()
+ {
+ $fileToChange = self::getObjectKey('php-sdk.html');
+ list(, $error) = self::$bucketManager->changeMime(
+ self::$bucketName,
+ $fileToChange,
+ 'text/plain'
+ );
+ $this->assertNull($error);
+
+ list($ret, $error) = self::$bucketManager->stat(
+ self::$bucketName,
+ $fileToChange
+ );
+ $this->assertNull($error);
+ $this->assertEquals('text/plain', $ret['mimeType']);
+ }
+
+ public function testPrefetch()
+ {
+ list($ret, $error) = self::$bucketManager->prefetch(
+ self::$bucketName,
+ 'php-sdk.html'
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testPrefetchFailed()
+ {
+ list($ret, $error) = self::$bucketManager->prefetch(
+ 'fakebucket',
+ 'php-sdk.html'
+ );
+ $this->assertNotNull($error);
+ $this->assertNull($ret);
+ }
+
+ public function testFetch()
+ {
+ list($ret, $error) = self::$bucketManager->fetch(
+ 'http://developer.qiniu.com/docs/v6/sdk/php-sdk.html',
+ self::$bucketName,
+ 'fetch.html'
+ );
+ $this->assertNull($error);
+ $this->assertArrayHasKey('hash', $ret);
+
+ list($ret, $error) = self::$bucketManager->fetch(
+ 'http://developer.qiniu.com/docs/v6/sdk/php-sdk.html',
+ self::$bucketName,
+ ''
+ );
+ $this->assertNull($error);
+ $this->assertArrayHasKey('key', $ret);
+
+ list($ret, $error) = self::$bucketManager->fetch(
+ 'http://developer.qiniu.com/docs/v6/sdk/php-sdk.html',
+ self::$bucketName
+ );
+ $this->assertNull($error);
+ $this->assertArrayHasKey('key', $ret);
+ }
+
+ public function testFetchFailed()
+ {
+ list($ret, $error) = self::$bucketManager->fetch(
+ 'http://developer.qiniu.com/docs/v6/sdk/php-sdk.html',
+ 'fakebucket'
+ );
+ $this->assertNotNull($error);
+ $this->assertNull($ret);
+ }
+
+ public function testAsynchFetch()
+ {
+ list($ret, $error) = self::$bucketManager->asynchFetch(
+ 'http://devtools.qiniu.com/qiniu.png',
+ self::$bucketName,
+ null,
+ 'qiniu.png'
+ );
+ $this->assertNull($error);
+ $this->assertArrayHasKey('id', $ret);
+
+ list($ret, $error) = self::$bucketManager->asynchFetch(
+ 'http://devtools.qiniu.com/qiniu.png',
+ self::$bucketName,
+ null,
+ ''
+ );
+ $this->assertNull($error);
+ $this->assertArrayHasKey('id', $ret);
+
+ list($ret, $error) = self::$bucketManager->asynchFetch(
+ 'http://devtools.qiniu.com/qiniu.png',
+ self::$bucketName
+ );
+ $this->assertNull($error);
+ $this->assertArrayHasKey('id', $ret);
+ }
+
+ public function testAsynchFetchFailed()
+ {
+ list($ret, $error) = self::$bucketManager->asynchFetch(
+ 'http://devtools.qiniu.com/qiniu.png',
+ 'fakebucket'
+ );
+ $this->assertNotNull($error);
+ $this->assertNull($ret);
+ }
+
+
+ public function testBatchCopy()
+ {
+ $key = 'copyto' . rand();
+ $ops = BucketManager::buildBatchCopy(
+ self::$bucketName,
+ array(self::$key => $key),
+ self::$bucketName,
+ true
+ );
+ list($ret, $error) = self::$bucketManager->batch($ops);
+ $this->assertNull($error);
+ $this->assertEquals(200, $ret[0]['code']);
+
+ self::$keysToCleanup[] = $key;
+ }
+
+ public function testBatchMove()
+ {
+ $fileToMove = self::getObjectKey(self::$key);
+ $fileMoved = $fileToMove . 'to';
+ $ops = BucketManager::buildBatchMove(
+ self::$bucketName,
+ array($fileToMove => $fileMoved),
+ self::$bucketName,
+ true
+ );
+ list($ret, $error) = self::$bucketManager->batch($ops);
+ $this->assertNull($error);
+ $this->assertEquals(200, $ret[0]['code']);
+ self::$keysToCleanup[] = $fileMoved;
+ }
+
+ public function testBatchRename()
+ {
+ $fileToRename = self::getObjectKey(self::$key);
+ $fileRenamed = $fileToRename . 'to';
+
+ $ops = BucketManager::buildBatchRename(
+ self::$bucketName,
+ array($fileToRename => $fileRenamed),
+ true
+ );
+ list($ret, $error) = self::$bucketManager->batch($ops);
+ $this->assertNull($error);
+ $this->assertEquals(200, $ret[0]['code']);
+
+ self::$keysToCleanup[] = $fileRenamed;
+ }
+
+ public function testBatchStat()
+ {
+ $ops = BucketManager::buildBatchStat(self::$bucketName, array('php-sdk.html'));
+ list($ret, $error) = self::$bucketManager->batch($ops);
+ $this->assertNull($error);
+ $this->assertEquals(200, $ret[0]['code']);
+ }
+
+ public function testBatchChangeTypeAndBatchRestoreAr()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ $ops = BucketManager::buildBatchChangeType(self::$bucketName, array($key => 2)); // 2 Archive
+ list($ret, $error) = self::$bucketManager->batch($ops);
+ $this->assertNull($error);
+ $this->assertEquals(200, $ret[0]['code']);
+
+ $ops = BucketManager::buildBatchRestoreAr(self::$bucketName, array($key => 1)); // 1 day
+ list($ret, $error) = self::$bucketManager->batch($ops);
+ $this->assertNull($error);
+ $this->assertEquals(200, $ret[0]['code']);
+ }
+
+ public function testDeleteAfterDays()
+ {
+ $key = "noexist" . rand();
+ list($ret, $error) = self::$bucketManager->deleteAfterDays(self::$bucketName, $key, 1);
+ $this->assertNotNull($error);
+ $this->assertNull($ret);
+
+ $key = self::getObjectKey(self::$key);
+ list(, $error) = self::$bucketManager->deleteAfterDays(self::$bucketName, $key, 1);
+ $this->assertNull($error);
+
+ list($ret, $error) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($error);
+ $this->assertGreaterThan(23 * 3600, $ret['expiration'] - time());
+ $this->assertLessThan(48 * 3600, $ret['expiration'] - time());
+ }
+
+ public function testSetObjectLifecycle()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ list(, $err) = self::$bucketManager->setObjectLifecycle(
+ self::$bucketName,
+ $key,
+ 10,
+ 20,
+ 30,
+ 40,
+ 15
+ );
+ $this->assertNull($err);
+
+ list($ret, $error) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($error);
+ $this->assertNotNull($ret['transitionToIA']);
+ $this->assertNotNull($ret['transitionToArchiveIR']);
+ $this->assertNotNull($ret['transitionToARCHIVE']);
+ $this->assertNotNull($ret['transitionToDeepArchive']);
+ $this->assertNotNull($ret['expiration']);
+ }
+
+ public function testSetObjectLifecycleWithCond()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ list($ret, $err) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($err);
+ $key_hash = $ret['hash'];
+ $key_fsize = $ret['fsize'];
+
+ list(, $err) = self::$bucketManager->setObjectLifecycleWithCond(
+ self::$bucketName,
+ $key,
+ array(
+ 'hash' => $key_hash,
+ 'fsize' => $key_fsize
+ ),
+ 10,
+ 20,
+ 30,
+ 40,
+ 15
+ );
+ $this->assertNull($err);
+
+ list($ret, $error) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($error);
+ $this->assertNotNull($ret['transitionToIA']);
+ $this->assertNotNull($ret['transitionToArchiveIR']);
+ $this->assertNotNull($ret['transitionToARCHIVE']);
+ $this->assertNotNull($ret['transitionToDeepArchive']);
+ $this->assertNotNull($ret['expiration']);
+ }
+
+ public function testBatchSetObjectLifecycle()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ $ops = BucketManager::buildBatchSetObjectLifecycle(
+ self::$bucketName,
+ array($key),
+ 10,
+ 20,
+ 30,
+ 40,
+ 15
+ );
+ list($ret, $err) = self::$bucketManager->batch($ops);
+ $this->assertNull($err);
+ $this->assertEquals(200, $ret[0]['code']);
+ }
+
+ public function testGetCorsRules()
+ {
+ list(, $err) = self::$bucketManager->getCorsRules(self::$bucketName);
+ $this->assertNull($err);
+ }
+
+ public function testPutBucketAccessStyleMode()
+ {
+ list(, $err) = self::$bucketManager->putBucketAccessStyleMode(self::$bucketName, 0);
+ $this->assertNull($err);
+ }
+
+ public function testPutBucketAccessMode()
+ {
+ list(, $err) = self::$bucketManager->putBucketAccessMode(self::$bucketName, 0);
+ $this->assertNull($err);
+ }
+
+ public function testPutReferAntiLeech()
+ {
+ list(, $err) = self::$bucketManager->putReferAntiLeech(self::$bucketName, 0, "1", "*");
+ $this->assertNull($err);
+ }
+
+ public function testPutBucketMaxAge()
+ {
+ list(, $err) = self::$bucketManager->putBucketMaxAge(self::$bucketName, 31536000);
+ $this->assertNull($err);
+ }
+
+ public function testPutBucketQuota()
+ {
+ list(, $err) = self::$bucketManager->putBucketQuota(self::$bucketName, -1, -1);
+ $this->assertNull($err);
+ }
+
+ public function testGetBucketQuota()
+ {
+ list(, $err) = self::$bucketManager->getBucketQuota(self::$bucketName);
+ $this->assertNull($err);
+ }
+
+ public function testChangeType()
+ {
+ $fileToChange = self::getObjectKey(self::$key);
+
+ list(, $err) = self::$bucketManager->changeType(self::$bucketName, $fileToChange, 0);
+ $this->assertNull($err);
+
+ list(, $err) = self::$bucketManager->changeType(self::$bucketName, $fileToChange, 1);
+ $this->assertNull($err);
+ }
+
+ public function testArchiveRestoreAr()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ self::$bucketManager->changeType(self::$bucketName, $key, 2);
+
+ list(, $err) = self::$bucketManager->restoreAr(self::$bucketName, $key, 2);
+ $this->assertNull($err);
+
+ list($ret, $err) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($err);
+
+ $this->assertEquals(2, $ret["type"]);
+
+ // restoreStatus
+ // null means frozen;
+ // 1 means be unfreezing;
+ // 2 means be unfrozen;
+ $this->assertNotNull($ret["restoreStatus"]);
+ $this->assertContains($ret["restoreStatus"], array(1, 2));
+ }
+
+ public function testDeepArchiveRestoreAr()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ self::$bucketManager->changeType(self::$bucketName, $key, 3);
+
+ list(, $err) = self::$bucketManager->restoreAr(self::$bucketName, $key, 1);
+ $this->assertNull($err);
+ list($ret, $err) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($err);
+
+ $this->assertEquals(3, $ret["type"]);
+
+ // restoreStatus
+ // null means frozen;
+ // 1 means be unfreezing;
+ // 2 means be unfrozen;
+ $this->assertNotNull($ret["restoreStatus"]);
+ $this->assertContains($ret["restoreStatus"], array(1, 2));
+ }
+
+ public function testChangeStatus()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ list(, $err) = self::$bucketManager->changeStatus(self::$bucketName, $key, 1);
+ $this->assertNull($err);
+ list($ret, $err) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($err);
+ $this->assertEquals(1, $ret['status']);
+
+ list(, $err) = self::$bucketManager->changeStatus(self::$bucketName, $key, 0);
+ $this->assertNull($err);
+ list($ret, $err) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($err);
+ $this->assertArrayNotHasKey('status', $ret);
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/CdnManagerTest.php b/php-sdk/tests/Qiniu/Tests/CdnManagerTest.php
new file mode 100644
index 0000000..baa9486
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/CdnManagerTest.php
@@ -0,0 +1,151 @@
+cdnManager = new CdnManager($testAuth);
+
+ global $timestampAntiLeechEncryptKey;
+ $this->encryptKey = $timestampAntiLeechEncryptKey;
+
+ global $testStartDate;
+ $this->testStartDate = $testStartDate;
+
+ global $testEndDate;
+ $this->testEndDate = $testEndDate;
+
+ global $testGranularity;
+ $this->testGranularity = $testGranularity;
+
+ global $testLogDate;
+ $this->testLogDate = $testLogDate;
+
+ global $customDomain;
+ $this->refreshUrl = $customDomain . '/sdktest.png';
+ $this->refreshDirs = $customDomain;
+ $this->customDomain = $customDomain;
+
+ global $customDomain2;
+ $this->customDomain2 = $customDomain2;
+ }
+
+ public function testRefreshUrls()
+ {
+ list($ret, $err) = $this->cdnManager->refreshUrls(array($this->refreshUrl));
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testRefreshDirs()
+ {
+ list($ret, $err) = $this->cdnManager->refreshDirs(array($this->refreshDirs));
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testRefreshUrlsAndDirs()
+ {
+ list($ret, $err) = $this->cdnManager->refreshUrlsAndDirs(array($this->refreshUrl), array($this->refreshDirs));
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testGetCdnRefreshList()
+ {
+ list($ret, $err) = $this->cdnManager->getCdnRefreshList(null, null, null, 'success');
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testPrefetchUrls()
+ {
+ list($ret, $err) = $this->cdnManager->prefetchUrls(array($this->refreshUrl));
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testGetCdnPrefetchList()
+ {
+ list($ret, $err) = $this->cdnManager->getCdnPrefetchList(null, null, 'success');
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testGetBandwidthData()
+ {
+ list($ret, $err) = $this->cdnManager->getBandwidthData(
+ array($this->customDomain2),
+ $this->testStartDate,
+ $this->testEndDate,
+ $this->testGranularity
+ );
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testGetFluxData()
+ {
+ list($ret, $err) = $this->cdnManager->getFluxData(
+ array($this->customDomain2),
+ $this->testStartDate,
+ $this->testEndDate,
+ $this->testGranularity
+ );
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testGetCdnLogList()
+ {
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ list($ret, $err) = $this->cdnManager->getCdnLogList(array($domain), $this->testLogDate);
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testCreateTimestampAntiLeechUrl()
+ {
+ $signUrl = $this->cdnManager->createTimestampAntiLeechUrl($this->refreshUrl, $this->encryptKey, 3600);
+ $response = Client::get($signUrl);
+ $this->assertNull($response->error);
+ $this->assertEquals($response->statusCode, 200);
+
+ $signUrl = $this->cdnManager->createTimestampAntiLeechUrl(
+ $this->refreshUrl . '?qiniu',
+ $this->encryptKey,
+ 3600
+ );
+ $response = Client::get($signUrl);
+ $this->assertNull($response->error);
+ $this->assertEquals($response->statusCode, 200);
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/ConfigTest.php b/php-sdk/tests/Qiniu/Tests/ConfigTest.php
new file mode 100644
index 0000000..3c39a5c
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/ConfigTest.php
@@ -0,0 +1,118 @@
+accessKey = $accessKey;
+ global $bucketName;
+ $this->bucketName = $bucketName;
+ }
+
+ public function testGetApiHost()
+ {
+ $conf = new Config();
+ $hasException = false;
+ $apiHost = '';
+ try {
+ $apiHost = $conf->getApiHost($this->accessKey, $this->bucketName);
+ } catch (\Exception $e) {
+ $hasException = true;
+ }
+ $this->assertFalse($hasException);
+ }
+
+ public function testGetApiHostErrored()
+ {
+ $conf = new Config();
+ $hasException = false;
+ try {
+ $conf->getApiHost($this->accessKey, "fakebucket");
+ } catch (\Exception $e) {
+ $hasException = true;
+ }
+ $this->assertTrue($hasException);
+ }
+
+ public function testGetApiHostV2()
+ {
+ $conf = new Config();
+ list($apiHost, $err) = $conf->getApiHostV2($this->accessKey, $this->bucketName);
+ $this->assertNull($err);
+ }
+
+ public function testGetApiHostV2Errored()
+ {
+ $conf = new Config();
+ list($apiHost, $err) = $conf->getApiHostV2($this->accessKey, "fakebucket");
+ $this->assertNotNull($err->code());
+ $this->assertEquals(631, $err->code());
+ $this->assertNull($apiHost);
+ }
+
+ public function testSetUcHost()
+ {
+ $conf = new Config();
+ $this->assertEquals('http://' . Config::UC_HOST, $conf->getUcHost());
+ $conf->setUcHost("uc.example.com");
+ $this->assertEquals("http://uc.example.com", $conf->getUcHost());
+
+ $conf = new Config();
+ $conf->useHTTPS = true;
+ $this->assertEquals('https://' . Config::UC_HOST, $conf->getUcHost());
+ $conf->setUcHost("uc.example.com");
+ $this->assertEquals("https://uc.example.com", $conf->getUcHost());
+ }
+
+ public function testGetRegionWithCustomDomain()
+ {
+ $conf = new Config();
+ $conf->setQueryRegionHost(
+ "uc.qbox.me"
+ );
+ list(, $err) = $conf->getRsHostV2($this->accessKey, $this->bucketName);
+ $this->assertNull($err);
+ }
+
+ public function testGetRegionWithBackupDomains()
+ {
+ $conf = new Config();
+ $conf->setQueryRegionHost(
+ "fake-uc.phpsdk.qiniu.com",
+ array(
+ "unavailable-uc.phpsdk.qiniu.com",
+ Config::UC_HOST // real uc
+ )
+ );
+ list(, $err) = $conf->getRsHostV2($this->accessKey, $this->bucketName);
+ $this->assertNull($err);
+ }
+
+ public function testGetRegionWithUcAndBackupDomains()
+ {
+ $conf = new Config();
+ $conf->setUcHost("fake-uc.phpsdk.qiniu.com");
+ $conf->setBackupQueryRegionHosts(
+ array(
+ "unavailable-uc.phpsdk.qiniu.com",
+ Config::UC_HOST // real uc
+ )
+ );
+ list(, $err) = $conf->getRsHostV2($this->accessKey, $this->bucketName);
+ $this->assertNull($err);
+ }
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/Crc32Test.php b/php-sdk/tests/Qiniu/Tests/Crc32Test.php
new file mode 100644
index 0000000..63e24fd
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/Crc32Test.php
@@ -0,0 +1,23 @@
+assertEquals('1352841281', $b);
+ }
+
+ public function testFile()
+ {
+ $b = \Qiniu\crc32_file(__file__);
+ $c = \Qiniu\crc32_file(__file__);
+ $this->assertEquals($c, $b);
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/DownloadTest.php b/php-sdk/tests/Qiniu/Tests/DownloadTest.php
new file mode 100644
index 0000000..9b4b034
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/DownloadTest.php
@@ -0,0 +1,27 @@
+privateDownloadUrl($base_url);
+ $response = Client::get($private_url);
+ $this->assertEquals(200, $response->statusCode);
+ }
+
+ public function testFop()
+ {
+ global $testAuth;
+ $base_url = 'http://sdk.peterpy.cn/gogopher.jpg?exif';
+ $private_url = $testAuth->privateDownloadUrl($base_url);
+ $response = Client::get($private_url);
+ $this->assertEquals(200, $response->statusCode);
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/EntryTest.php b/php-sdk/tests/Qiniu/Tests/EntryTest.php
new file mode 100644
index 0000000..73bfac4
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/EntryTest.php
@@ -0,0 +1,88 @@
+assertEquals('cWluaXVwaG90b3M6Z29nb3BoZXIuanBn', $encodeEntryURI);
+ }
+
+ public function testKeyEmpty()
+ {
+ $bucket = 'qiniuphotos';
+ $key = '';
+ $encodeEntryURI = Qiniu\entry($bucket, $key);
+ $this->assertEquals('cWluaXVwaG90b3M6', $encodeEntryURI);
+ }
+
+ public function testKeyNull()
+ {
+ $bucket = 'qiniuphotos';
+ $key = null;
+ $encodeEntryURI = Qiniu\entry($bucket, $key);
+ $this->assertEquals('cWluaXVwaG90b3M=', $encodeEntryURI);
+ }
+
+ public function testKeyNeedReplacePlusSymbol()
+ {
+ $bucket = 'qiniuphotos';
+ $key = '012ts>a';
+ $encodeEntryURI = Qiniu\entry($bucket, $key);
+ $this->assertEquals('cWluaXVwaG90b3M6MDEydHM-YQ==', $encodeEntryURI);
+ }
+
+ public function testKeyNeedReplaceSlashSymbol()
+ {
+ $bucket = 'qiniuphotos';
+ $key = '012ts?a';
+ $encodeEntryURI = Qiniu\entry($bucket, $key);
+ $this->assertEquals('cWluaXVwaG90b3M6MDEydHM_YQ==', $encodeEntryURI);
+ }
+ public function testDecodeEntry()
+ {
+ $entry = 'cWluaXVwaG90b3M6Z29nb3BoZXIuanBn';
+ list($bucket, $key) = Qiniu\decodeEntry($entry);
+ $this->assertEquals('qiniuphotos', $bucket);
+ $this->assertEquals('gogopher.jpg', $key);
+ }
+
+ public function testDecodeEntryWithEmptyKey()
+ {
+ $entry = 'cWluaXVwaG90b3M6';
+ list($bucket, $key) = Qiniu\decodeEntry($entry);
+ $this->assertEquals('qiniuphotos', $bucket);
+ $this->assertEquals('', $key);
+ }
+
+ public function testDecodeEntryWithNullKey()
+ {
+ $entry = 'cWluaXVwaG90b3M=';
+ list($bucket, $key) = Qiniu\decodeEntry($entry);
+ $this->assertEquals('qiniuphotos', $bucket);
+ $this->assertNull($key);
+ }
+
+ public function testDecodeEntryWithPlusSymbol()
+ {
+ $entry = 'cWluaXVwaG90b3M6MDEydHM-YQ==';
+ list($bucket, $key) = Qiniu\decodeEntry($entry);
+ $this->assertEquals('qiniuphotos', $bucket);
+ $this->assertEquals('012ts>a', $key);
+ }
+
+ public function testDecodeEntryWithSlashSymbol()
+ {
+ $entry = 'cWluaXVwaG90b3M6MDEydHM_YQ==';
+ list($bucket, $key) = Qiniu\decodeEntry($entry);
+ $this->assertEquals('qiniuphotos', $bucket);
+ $this->assertEquals('012ts?a', $key);
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/EtagTest.php b/php-sdk/tests/Qiniu/Tests/EtagTest.php
new file mode 100644
index 0000000..4e09a78
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/EtagTest.php
@@ -0,0 +1,54 @@
+assertEquals('Fto5o-5ea0sNMlW_75VgGJCv2AcJ', $r);
+ $this->assertNull($error);
+ }
+
+ public function testLess4M()
+ {
+ $file = qiniuTempFile(3 * 1024 * 1024, false);
+ list($r, $error) = Etag::sum($file);
+ unlink($file);
+ $this->assertEquals('Fs5BpnAjRykYTg6o5E09cjuXrDkG', $r);
+ $this->assertNull($error);
+ }
+
+ public function test4M()
+ {
+ $file = qiniuTempFile(4 * 1024 * 1024, false);
+ list($r, $error) = Etag::sum($file);
+ unlink($file);
+ $this->assertEquals('FiuKULnybewpEnrfTmxjsxc-3dWp', $r);
+ $this->assertNull($error);
+ }
+
+ public function testMore4M()
+ {
+ $file = qiniuTempFile(5 * 1024 * 1024, false);
+ list($r, $error) = Etag::sum($file);
+ unlink($file);
+ $this->assertEquals('lhvyfIWMYFTq4s4alzlhXoAkqfVL', $r);
+ $this->assertNull($error);
+ }
+
+ public function test8M()
+ {
+ $file = qiniuTempFile(8 * 1024 * 1024, false);
+ list($r, $error) = Etag::sum($file);
+ unlink($file);
+ $this->assertEquals('lmRm9ZfGZ86bnMys4wRTWtJj9ClG', $r);
+ $this->assertNull($error);
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/FopTest.php b/php-sdk/tests/Qiniu/Tests/FopTest.php
new file mode 100644
index 0000000..42b7997
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/FopTest.php
@@ -0,0 +1,39 @@
+execute('gogopher.jpg', 'exif');
+ $this->assertNull($error);
+ $this->assertNotNull($exif);
+ }
+
+ public function testExifPrivate()
+ {
+ global $testAuth;
+ $fop = new Operation('private-res.qiniudn.com', $testAuth);
+ list($exif, $error) = $fop->execute('noexif.jpg', 'exif');
+ $this->assertNotNull($error);
+ $this->assertNull($exif);
+ }
+
+ public function testbuildUrl()
+ {
+ $fops = 'imageView2/2/h/200';
+ $fop = new Operation('testres.qiniudn.com');
+ $url = $fop->buildUrl('gogopher.jpg', $fops);
+ $this->assertEquals($url, 'http://testres.qiniudn.com/gogopher.jpg?imageView2/2/h/200');
+
+ $fops = array('imageView2/2/h/200', 'imageInfo');
+ $url = $fop->buildUrl('gogopher.jpg', $fops);
+ $this->assertEquals($url, 'http://testres.qiniudn.com/gogopher.jpg?imageView2/2/h/200|imageInfo');
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/FormUpTest.php b/php-sdk/tests/Qiniu/Tests/FormUpTest.php
new file mode 100644
index 0000000..f75794e
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/FormUpTest.php
@@ -0,0 +1,205 @@
+batch($ops);
+ }
+
+ private static function getObjectKey($key)
+ {
+ $result = $key . rand();
+ self::$keysToCleanup[] = $result;
+ return $result;
+ }
+
+ public function testData()
+ {
+ $key = self::getObjectKey('formput');
+ $token = self::$auth->uploadToken(self::$bucketName);
+ list($ret, $error) = FormUploader::put($token, $key, 'hello world', self::$cfg, null, 'text/plain', null);
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testDataWithProxy()
+ {
+ $key = self::getObjectKey('formput');
+ $token = self::$auth->uploadToken(self::$bucketName);
+ list($ret, $error) = FormUploader::put(
+ $token,
+ $key,
+ 'hello world',
+ self::$cfg,
+ null,
+ 'text/plain',
+ null,
+ $this->makeReqOpt()
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testData2()
+ {
+ $key = self::getObjectKey('formput');
+ $upManager = new UploadManager();
+ $token = self::$auth->uploadToken(self::$bucketName);
+ list($ret, $error) = $upManager->put($token, $key, 'hello world', null, 'text/plain', null);
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testData2WithProxy()
+ {
+ $key = self::getObjectKey('formput');
+ $upManager = new UploadManager();
+ $token = self::$auth->uploadToken(self::$bucketName);
+ list($ret, $error) = $upManager->put(
+ $token,
+ $key,
+ 'hello world',
+ null,
+ 'text/plain',
+ null,
+ $this->makeReqOpt()
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testDataFailed()
+ {
+ $key = self::getObjectKey('formput');
+ $token = self::$auth->uploadToken('fakebucket');
+ list($ret, $error) = FormUploader::put(
+ $token,
+ $key,
+ 'hello world',
+ self::$cfg,
+ null,
+ 'text/plain',
+ null
+ );
+ $this->assertNull($ret);
+ $this->assertNotNull($error);
+ }
+
+ public function testFile()
+ {
+ $key = self::getObjectKey('formPutFile');
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ list($ret, $error) = FormUploader::putFile(
+ $token,
+ $key,
+ __file__,
+ self::$cfg,
+ null,
+ 'text/plain',
+ null
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testFileWithProxy()
+ {
+ $key = self::getObjectKey('formPutFile');
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ list($ret, $error) = FormUploader::putFile(
+ $token,
+ $key,
+ __file__,
+ self::$cfg,
+ null,
+ 'text/plain',
+ $this->makeReqOpt()
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testFile2()
+ {
+ $key = self::getObjectKey('formPutFile');
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $upManager = new UploadManager();
+ list($ret, $error) = $upManager->putFile($token, $key, __file__, null, 'text/plain', null);
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testFile2WithProxy()
+ {
+ $key = self::getObjectKey('formPutFile');
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $upManager = new UploadManager();
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ __file__,
+ null,
+ 'text/plain',
+ false,
+ null,
+ 'v1',
+ Config::BLOCK_SIZE,
+ $this->makeReqOpt()
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testFileFailed()
+ {
+ $key = self::getObjectKey('fakekey');
+ $token = self::$auth->uploadToken('fakebucket', $key);
+ list($ret, $error) = FormUploader::putFile($token, $key, __file__, self::$cfg, null, 'text/plain', null);
+ $this->assertNull($ret);
+ $this->assertNotNull($error);
+ }
+
+ private function makeReqOpt()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->proxy = 'socks5://127.0.0.1:8080';
+ $reqOpt->proxy_user_password = 'user:pass';
+ return $reqOpt;
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/HeaderTest.php b/php-sdk/tests/Qiniu/Tests/HeaderTest.php
new file mode 100644
index 0000000..28af5f3
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/HeaderTest.php
@@ -0,0 +1,184 @@
+ array('200'),
+ ':x-test-1' => array('hello1'),
+ ':x-Test-2' => array('hello2'),
+ 'content-type' => array('application/json'),
+ 'CONTENT-LENGTH' => array(1234),
+ 'oRiGin' => array('https://www.qiniu.com'),
+ 'ReFer' => array('www.qiniu.com'),
+ 'Last-Modified' => array('Mon, 06 Sep 2021 06:44:52 GMT'),
+ 'acCePt-ChArsEt' => array('utf-8'),
+ 'x-test-3' => array('hello3'),
+ 'cache-control' => array('no-cache', 'no-store'),
+ );
+
+ public function testNormalizeKey()
+ {
+ $except = array(
+ ':status',
+ ':x-test-1',
+ ':x-Test-2',
+ 'Content-Type',
+ 'Content-Length',
+ 'Origin',
+ 'Refer',
+ 'Last-Modified',
+ 'Accept-Charset',
+ 'X-Test-3',
+ 'Cache-Control'
+ );
+ $actual = array_map(function ($str) {
+ return Header::normalizeKey($str);
+ }, array_keys($this->heads));
+ $this->assertEquals($actual, $except);
+ }
+
+
+ public function testInvalidKeyName()
+ {
+ $except = array(
+ 'a:x-test-1',
+ );
+
+ $actual = array_map(function ($str) {
+ return Header::normalizeKey($str);
+ }, $except);
+
+ $this->assertEquals($except, $actual);
+ }
+
+ public function testGetRawData()
+ {
+ $header = new Header($this->heads);
+ foreach ($this->heads as $k => $v) {
+ $rawHeader = $header->getRawData();
+ $this->assertEquals($v, $rawHeader[Header::normalizeKey($k)]);
+ }
+ }
+
+ public function testOffsetExists()
+ {
+ $header = new Header($this->heads);
+ foreach (array_keys($this->heads) as $k) {
+ $this->assertNotNull($header[$k]);
+ }
+
+ $except = array(
+ ':status',
+ ':x-test-1',
+ ':x-Test-2',
+ 'Content-Type',
+ 'Content-Length',
+ 'Origin',
+ 'Refer',
+ 'Last-Modified',
+ 'Accept-Charset',
+ 'X-Test-3',
+ 'Cache-Control'
+ );
+ foreach ($except as $k) {
+ $this->assertNotNull($header[$k], $k." is null");
+ }
+ }
+
+ public function testOffsetGet()
+ {
+ $header = new Header($this->heads);
+ foreach ($this->heads as $k => $v) {
+ $this->assertEquals($v[0], $header[$k]);
+ }
+
+ $this->assertNull($header['no-exist']);
+ }
+
+ public function testOffsetSet()
+ {
+ $header = new Header($this->heads);
+ $header["X-Test-3"] = "hello";
+ $this->assertEquals("hello", $header["X-Test-3"]);
+ $header["x-test-3"] = "hello test3";
+ $this->assertEquals("hello test3", $header["x-test-3"]);
+ $header[":x-Test-2"] = "hello";
+ $this->assertEquals("hello", $header[":x-Test-2"]);
+ $header[":x-test-2"] = "hello test2";
+ $this->assertEquals("hello", $header[":x-Test-2"]);
+ }
+
+ public function testOffsetUnset()
+ {
+ $header = new Header($this->heads);
+ unset($header["X-Test-3"]);
+ $this->assertFalse(isset($header["X-Test-3"]));
+
+ $header = new Header($this->heads);
+ unset($header["x-test-3"]);
+ $this->assertFalse(isset($header["x-test-3"]));
+
+ $header = new Header($this->heads);
+ unset($header[":x-test-2"]);
+ $this->assertTrue(isset($header[":x-Test-2"]));
+
+ $header = new Header($this->heads);
+ unset($header[":x-Test-2"]);
+ $this->assertFalse(isset($header[":x-Test-2"]));
+ }
+
+ public function testGetIterator()
+ {
+ $header = new Header($this->heads);
+
+ $hasException = false;
+ try {
+ foreach ($header as $k => $v) {
+ $hasException = !isset($header[$k]);
+ }
+ } catch (\Exception $e) {
+ $hasException = true;
+ }
+ $this->assertFalse($hasException);
+ }
+
+ public function testEmptyHeaderIterator()
+ {
+ $emptyHeader = new Header();
+
+ $hasException = false;
+ try {
+ foreach ($emptyHeader as $k => $v) {
+ $hasException = !isset($header[$k]);
+ }
+ } catch (\Exception $e) {
+ $hasException = true;
+ }
+ $this->assertFalse($hasException);
+ }
+
+ public function testCount()
+ {
+ $header = new Header($this->heads);
+
+ $this->assertEquals(count($this->heads), count($header));
+ }
+
+ public function testFromRaw()
+ {
+ $lines = array();
+ foreach ($this->heads as $k => $vs) {
+ foreach ($vs as $v) {
+ array_push($lines, $k . ": " . $v);
+ }
+ }
+ $raw = implode("\r\n", $lines);
+ $headerFromRaw = Header::fromRawText($raw);
+ $this->assertEquals(new Header($this->heads), $headerFromRaw);
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/HttpTest.php b/php-sdk/tests/Qiniu/Tests/HttpTest.php
new file mode 100644
index 0000000..c122f8e
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/HttpTest.php
@@ -0,0 +1,163 @@
+assertEquals(200, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNull($response->error);
+ }
+
+ public function testGetQiniu()
+ {
+ $response = Client::get('upload.qiniu.com');
+ $this->assertEquals(405, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNotNull($response->xReqId());
+ $this->assertNotNull($response->xLog());
+ $this->assertNotNull($response->error);
+ }
+
+ public function testGetTimeout()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->timeout = 1;
+ $response = Client::get('localhost:9000/timeout.php', array(), $reqOpt);
+ $this->assertEquals(-1, $response->statusCode);
+ }
+
+ public function testGetRedirect()
+ {
+ $response = Client::get('localhost:9000/redirect.php');
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals('application/json;charset=UTF-8', $response->normalizedHeaders['Content-Type']);
+ $respData = $response->json();
+ $this->assertEquals('ok', $respData['msg']);
+ }
+
+ public function testDelete()
+ {
+ $response = Client::delete('uc.qbox.me/bucketTagging', array());
+ $this->assertEquals(401, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNotNull($response->error);
+ }
+
+ public function testDeleteQiniu()
+ {
+ $response = Client::delete('uc.qbox.me/bucketTagging', array());
+ $this->assertEquals(401, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNotNull($response->xReqId());
+ $this->assertNotNull($response->xLog());
+ $this->assertNotNull($response->error);
+ }
+
+ public function testDeleteTimeout()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->timeout = 1;
+ $response = Client::delete('localhost:9000/timeout.php', array(), $reqOpt);
+ $this->assertEquals(-1, $response->statusCode);
+ }
+
+
+ public function testPost()
+ {
+ $response = Client::post('qiniu.com', null);
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNull($response->error);
+ }
+
+ public function testPostQiniu()
+ {
+ $response = Client::post('upload.qiniu.com', null);
+ $this->assertEquals(400, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNotNull($response->xReqId());
+ $this->assertNotNull($response->xLog());
+ $this->assertNotNull($response->error);
+ }
+
+ public function testPostTimeout()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->timeout = 1;
+ $response = Client::post('localhost:9000/timeout.php', null, array(), $reqOpt);
+ $this->assertEquals(-1, $response->statusCode);
+ }
+
+ public function testSocks5Proxy()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->proxy = 'socks5://localhost:8080';
+ $response = Client::post('qiniu.com', null, array(), $reqOpt);
+ $this->assertEquals(-1, $response->statusCode);
+
+ $reqOpt->proxy_user_password = 'user:pass';
+ $response = Client::post('qiniu.com', null, array(), $reqOpt);
+ $this->assertEquals(200, $response->statusCode);
+ }
+
+ public function testPut()
+ {
+ $response = Client::PUT('uc.qbox.me/bucketTagging', null);
+ $this->assertEquals(401, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNotNull($response->error);
+ }
+
+ public function testPutQiniu()
+ {
+ $response = Client::put('uc.qbox.me/bucketTagging', null);
+ $this->assertEquals(401, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNotNull($response->xReqId());
+ $this->assertNotNull($response->xLog());
+ $this->assertNotNull($response->error);
+ }
+
+
+ public function testPutTimeout()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->timeout = 1;
+ $response = Client::put('localhost:9000/timeout.php', null, array(), $reqOpt);
+ $this->assertEquals(-1, $response->statusCode);
+ }
+
+ public function testNeedRetry()
+ {
+ $testCases = array_merge(
+ array(array(-1, true)),
+ array_map(function ($i) {
+ return array($i, false);
+ }, range(100, 499)),
+ array_map(function ($i) {
+ if (in_array($i, array(
+ 501, 509, 573, 579, 608, 612, 614, 616, 618, 630, 631, 632, 640, 701
+ ))) {
+ return array($i, false);
+ }
+ return array($i, true);
+ }, range(500, 799))
+ );
+ $resp = new Response(-1, 222, array(), '{"msg": "mock"}', null);
+ foreach ($testCases as $testCase) {
+ list($code, $expectNeedRetry) = $testCase;
+ $resp->statusCode = $code;
+ $msg = $resp->statusCode . ' need' . ($expectNeedRetry ? '' : ' NOT') . ' retry';
+ $this->assertEquals($expectNeedRetry, $resp->needRetry(), $msg);
+ }
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/ImageUrlBuilderTest.php b/php-sdk/tests/Qiniu/Tests/ImageUrlBuilderTest.php
new file mode 100644
index 0000000..486323c
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/ImageUrlBuilderTest.php
@@ -0,0 +1,263 @@
+
+ */
+class ImageUrlBuilderTest extends TestCase
+{
+ /**
+ * 缩略图测试
+ *
+ * @test
+ * @return void
+ * @author Sherlock Ren
+ */
+ public function testThumbutl()
+ {
+ $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder();
+ $url = 'http://78re52.com1.z0.glb.clouddn.com/resource/gogopher.jpg';
+ $url2 = $url . '?watermark/1/gravity/SouthEast/dx/0/dy/0/image/'
+ . 'aHR0cDovL2Fkcy1jZG4uY2h1Y2h1amllLmNvbS9Ga1R6bnpIY2RLdmRBUFc5cHZZZ3pTc21UY0tB';
+ // 异常测试
+ $this->assertEquals($url, $imageUrlBuilder->thumbnail($url, 1, 0, 0));
+ $this->assertEquals($url, \Qiniu\thumbnail($url, 1, 0, 0));
+
+ // 简单缩略测试
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/ignore-error/1/',
+ $imageUrlBuilder->thumbnail($url, 1, 200, 200)
+ );
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/ignore-error/1/',
+ \Qiniu\thumbnail($url, 1, 200, 200)
+ );
+
+ // 输出格式测试
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/',
+ $imageUrlBuilder->thumbnail($url, 1, 200, 200, 'png')
+ );
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/',
+ \Qiniu\thumbnail($url, 1, 200, 200, 'png')
+ );
+
+ // 渐进显示测试
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/format/png/interlace/1/ignore-error/1/',
+ $imageUrlBuilder->thumbnail($url, 1, 200, 200, 'png', 1)
+ );
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/',
+ \Qiniu\thumbnail($url, 1, 200, 200, 'png', 2)
+ );
+
+ // 图片质量测试
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/format/png/interlace/1/q/80/ignore-error/1/',
+ $imageUrlBuilder->thumbnail($url, 1, 200, 200, 'png', 1, 80)
+ );
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/format/png/interlace/1/ignore-error/1/',
+ \Qiniu\thumbnail($url, 1, 200, 200, 'png', 1, 101)
+ );
+
+ // 多参数测试
+ $this->assertEquals(
+ $url2 . '|imageView2/1/w/200/h/200/ignore-error/1/',
+ $imageUrlBuilder->thumbnail($url2, 1, 200, 200)
+ );
+ $this->assertEquals(
+ $url2 . '|imageView2/1/w/200/h/200/ignore-error/1/',
+ \Qiniu\thumbnail($url2, 1, 200, 200)
+ );
+ }
+
+ /**
+ * 图片水印测试
+ *
+ * @test
+ * @param void
+ * @return void
+ * @author Sherlock Ren
+ */
+ public function waterImgTest()
+ {
+ $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder();
+ $url = 'http://78re52.com1.z0.glb.clouddn.com/resource/gogopher.jpg';
+ $url2 = $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/';
+ $image = 'http://developer.qiniu.com/resource/logo-2.jpg';
+
+ // 水印简单测试
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ $imageUrlBuilder->waterImg($url, $image)
+ );
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/gravity/SouthEast/',
+ $imageUrlBuilder->waterImg($url, $image, 101)
+ );
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw==/',
+ $imageUrlBuilder->waterImg($url, $image, 101, 'sdfsd')
+ );
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterImg($url, $image)
+ );
+
+ // 横轴边距测试
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/dx/10/',
+ $imageUrlBuilder->waterImg($url, $image, 100, 'SouthEast', 10)
+ );
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterImg($url, $image, 100, 'SouthEast', 'sad')
+ );
+
+ // 纵轴边距测试
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/dx/10/dy/10/',
+ $imageUrlBuilder->waterImg($url, $image, 100, 'SouthEast', 10, 10)
+ );
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterImg($url, $image, 100, 'SouthEast', 'sad', 'asdf')
+ );
+
+ // 自适应原图的短边比例测试
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/dx/10/dy/10/ws/0.5/',
+ $imageUrlBuilder->waterImg($url, $image, 100, 'SouthEast', 10, 10, 0.5)
+ );
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterImg($url, $image, 100, 'SouthEast', 'sad', 'asdf', 2)
+ );
+
+ // 多参数测试
+ $this->assertEquals(
+ $url2 . '|watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ $imageUrlBuilder->waterImg($url2, $image)
+ );
+ $this->assertEquals(
+ $url2 . '|watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterImg($url2, $image)
+ );
+ }
+
+ /**
+ * 文字水印测试
+ *
+ * @test
+ * @param void
+ * @return void
+ * @author Sherlock Ren
+ */
+ public function waterTextTest()
+ {
+ $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder();
+ $url = 'http://78re52.com1.z0.glb.clouddn.com/resource/gogopher.jpg';
+ $url2 = $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/';
+ $text = '测试一下';
+ $font = '微软雅黑';
+ $fontColor = '#FF0000';
+
+ // 水印简单测试
+ $this->assertEquals($url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/'
+ . 'fontsize/500/dissolve/100/gravity/SouthEast/', $imageUrlBuilder->waterText($url, $text, $font, 500));
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/'
+ . 'dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterText($url, $text, $font, 'sdf')
+ );
+
+ // 字体颜色测试
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/'
+ . 'I0ZGMDAwMA==/dissolve/100/gravity/SouthEast/',
+ $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor)
+ );
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA=='
+ . '/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor)
+ );
+
+ // 透明度测试
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0ZGMDAwMA=='
+ . '/dissolve/80/gravity/SouthEast/',
+ $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor, 80)
+ );
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA=='
+ . '/gravity/SouthEast/',
+ \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor, 101)
+ );
+
+ // 水印位置测试
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0ZGMDAwMA=='
+ . '/dissolve/80/gravity/East/',
+ $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor, 80, 'East')
+ );
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA==/',
+ \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor, 101, 'sdfsdf')
+ );
+
+ // 横轴距离测试
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0ZGMDAwMA=='
+ . '/dissolve/80/gravity/East/dx/10/',
+ $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor, 80, 'East', 10)
+ );
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA==/',
+ \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor, 101, 'sdfsdf', 'sdfs')
+ );
+
+ // 纵轴距离测试
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0ZGMDAwMA=='
+ . '/dissolve/80/gravity/East/dx/10/dy/10/',
+ $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor, 80, 'East', 10, 10)
+ );
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA==/',
+ \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor, 101, 'sdfsdf', 'sdfs', 'ssdf')
+ );
+ // 多参数测试
+ $this->assertEquals(
+ $url2 . '|watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/'
+ . 'fontsize/500/dissolve/100/gravity/SouthEast/',
+ $imageUrlBuilder->waterText($url2, $text, $font, 500)
+ );
+ $this->assertEquals(
+ $url2 . '|watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/'
+ . 'fontsize/500/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterText($url2, $text, $font, 500)
+ );
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/MiddlewareTest.php b/php-sdk/tests/Qiniu/Tests/MiddlewareTest.php
new file mode 100644
index 0000000..969cad4
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/MiddlewareTest.php
@@ -0,0 +1,160 @@
+
+ */
+ private $orderRecorder;
+
+ /**
+ * @var string
+ */
+ private $label;
+
+ public function __construct(&$orderRecorder, $label)
+ {
+ $this->orderRecorder =& $orderRecorder;
+ $this->label = $label;
+ }
+
+ public function send($request, $next)
+ {
+ $this->orderRecorder[] = "bef_" . $this->label . count($this->orderRecorder);
+ $response = $next($request);
+ $this->orderRecorder[] = "aft_" . $this->label . count($this->orderRecorder);
+ return $response;
+ }
+}
+
+class MiddlewareTest extends TestCase
+{
+ public function testSendWithMiddleware()
+ {
+ $orderRecorder = array();
+
+ $reqOpt = new RequestOptions();
+ $reqOpt->middlewares = array(
+ new RecorderMiddleware($orderRecorder, "A"),
+ new RecorderMiddleware($orderRecorder, "B")
+ );
+
+ $request = new Request(
+ "GET",
+ "http://localhost:9000/ok.php",
+ array(),
+ null,
+ $reqOpt
+ );
+ $response = Client::sendRequestWithMiddleware($request);
+
+ $expectRecords = array(
+ "bef_A0",
+ "bef_B1",
+ "aft_B2",
+ "aft_A3"
+ );
+
+ $this->assertEquals($expectRecords, $orderRecorder);
+ $this->assertEquals(200, $response->statusCode);
+ }
+
+ public function testSendWithRetryDomains()
+ {
+ $orderRecorder = array();
+
+ $reqOpt = new RequestOptions();
+ $reqOpt->middlewares = array(
+ new Middleware\RetryDomainsMiddleware(
+ array(
+ "unavailable.phpsdk.qiniu.com",
+ "localhost:9000",
+ ),
+ 3
+ ),
+ new RecorderMiddleware($orderRecorder, "rec")
+ );
+
+ $request = new Request(
+ "GET",
+ "http://fake.phpsdk.qiniu.com/ok.php",
+ array(),
+ null,
+ $reqOpt
+ );
+ $response = Client::sendRequestWithMiddleware($request);
+
+ $expectRecords = array(
+ // 'fake.phpsdk.qiniu.com' with retried 3 times
+ 'bef_rec0',
+ 'aft_rec1',
+ 'bef_rec2',
+ 'aft_rec3',
+ 'bef_rec4',
+ 'aft_rec5',
+
+ // 'unavailable.pysdk.qiniu.com' with retried 3 times
+ 'bef_rec6',
+ 'aft_rec7',
+ 'bef_rec8',
+ 'aft_rec9',
+ 'bef_rec10',
+ 'aft_rec11',
+
+ // 'qiniu.com' and it's success
+ 'bef_rec12',
+ 'aft_rec13'
+ );
+
+ $this->assertEquals($expectRecords, $orderRecorder);
+ $this->assertEquals(200, $response->statusCode);
+ }
+
+ public function testSendFailFastWithRetryDomains()
+ {
+ $orderRecorder = array();
+
+ $reqOpt = new RequestOptions();
+ $reqOpt->middlewares = array(
+ new Middleware\RetryDomainsMiddleware(
+ array(
+ "unavailable.phpsdk.qiniu.com",
+ "localhost:9000",
+ ),
+ 3,
+ function () {
+ return false;
+ }
+ ),
+ new RecorderMiddleware($orderRecorder, "rec")
+ );
+
+ $request = new Request(
+ "GET",
+ "http://fake.phpsdk.qiniu.com/ok.php",
+ array(),
+ null,
+ $reqOpt
+ );
+ $response = Client::sendRequestWithMiddleware($request);
+
+ $expectRecords = array(
+ // 'fake.phpsdk.qiniu.com' will fail fast
+ 'bef_rec0',
+ 'aft_rec1'
+ );
+ $this->assertEquals($expectRecords, $orderRecorder);
+ $this->assertEquals(-1, $response->statusCode);
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/PfopTest.php b/php-sdk/tests/Qiniu/Tests/PfopTest.php
new file mode 100644
index 0000000..77d06ec
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/PfopTest.php
@@ -0,0 +1,304 @@
+execute($bucket, $key, $fops);
+ $this->assertNull($error);
+ list($status, $error) = $pfop->status($id);
+ $this->assertNotNull($status);
+ $this->assertNull($error);
+ }
+
+
+ public function testPfopExecuteAndStatusWithMultipleFops()
+ {
+ global $testAuth;
+ $bucket = 'testres';
+ $key = 'sintel_trailer.mp4';
+ $fops = array(
+ 'avthumb/m3u8/segtime/10/vcodec/libx264/s/320x240',
+ 'vframe/jpg/offset/7/w/480/h/360',
+ );
+ $pfop = new PersistentFop($testAuth, self::getConfig());
+
+ list($id, $error) = $pfop->execute($bucket, $key, $fops);
+ $this->assertNull($error);
+
+ list($status, $error) = $pfop->status($id);
+ $this->assertNotNull($status);
+ $this->assertNull($error);
+ }
+
+ private function pfopOptionsTestData()
+ {
+ return array(
+ array(
+ 'type' => null
+ ),
+ array(
+ 'type' => -1
+ ),
+ array(
+ 'type' => 0
+ ),
+ array(
+ 'type' => 1
+ ),
+ array(
+ 'type' => 2
+ ),
+ array(
+ 'workflowTemplateID' => 'test-workflow'
+ )
+ );
+ }
+
+ public function testPfopExecuteWithOptions()
+ {
+ $bucket = self::$bucketName;
+ $key = 'qiniu.png';
+ $pfop = new PersistentFop(self::$testAuth, self::getConfig());
+
+ $testCases = $this->pfopOptionsTestData();
+
+ foreach ($testCases as $testCase) {
+ $workflowTemplateID = null;
+ $type = null;
+
+ if (array_key_exists('workflowTemplateID', $testCase)) {
+ $workflowTemplateID = $testCase['workflowTemplateID'];
+ }
+ if (array_key_exists('type', $testCase)) {
+ $type = $testCase['type'];
+ }
+
+ if ($workflowTemplateID) {
+ $fops = null;
+ } else {
+ $persistentEntry = \Qiniu\entry(
+ $bucket,
+ implode(
+ '_',
+ array(
+ 'test-pfop/test-pfop-by-api',
+ 'type',
+ $type
+ )
+ )
+ );
+ $fops = 'avinfo|saveas/' . $persistentEntry;
+ }
+ list($id, $error) = $pfop->execute(
+ $bucket,
+ $key,
+ $fops,
+ null,
+ null,
+ false,
+ $type,
+ $workflowTemplateID
+ );
+
+ if (in_array($type, array(null, 0, 1))) {
+ $this->assertNull($error);
+ list($status, $error) = $pfop->status($id);
+ $this->assertNotNull($status);
+ $this->assertNull($error);
+ if ($type == 1) {
+ $this->assertEquals(1, $status['type']);
+ }
+ if ($workflowTemplateID) {
+ // assertStringContainsString when PHPUnit >= 8.0
+ $this->assertTrue(
+ strpos(
+ $status['taskFrom'],
+ $workflowTemplateID
+ ) !== false
+ );
+ }
+ $this->assertNotEmpty($status['creationDate']);
+ } else {
+ $this->assertNotNull($error);
+ }
+ }
+ }
+
+ public function testPfopWithInvalidArgument()
+ {
+ $bucket = self::$bucketName;
+ $key = 'qiniu.png';
+ $pfop = new PersistentFop(self::$testAuth, self::getConfig());
+ $err = null;
+ try {
+ $pfop->execute(
+ $bucket,
+ $key
+ );
+ } catch (\Exception $e) {
+ $err = $e;
+ }
+
+ $this->assertNotEmpty($err);
+ $this->assertTrue(
+ strpos(
+ $err->getMessage(),
+ 'Must provide one of fops or template_id'
+ ) !== false
+ );
+ }
+
+ public function testPfopWithUploadPolicy()
+ {
+ $bucket = self::$bucketName;
+ $testAuth = self::$testAuth;
+ $key = 'test-pfop/upload-file';
+
+ $testCases = $this->pfopOptionsTestData();
+
+ foreach ($testCases as $testCase) {
+ $workflowTemplateID = null;
+ $type = null;
+
+ if (array_key_exists('workflowTemplateID', $testCase)) {
+ $workflowTemplateID = $testCase['workflowTemplateID'];
+ }
+ if (array_key_exists('type', $testCase)) {
+ $type = $testCase['type'];
+ }
+
+ $putPolicy = array(
+ 'persistentType' => $type
+ );
+ if ($workflowTemplateID) {
+ $putPolicy['persistentWorkflowTemplateID'] = $workflowTemplateID;
+ } else {
+ $persistentEntry = \Qiniu\entry(
+ $bucket,
+ implode(
+ '_',
+ array(
+ 'test-pfop/test-pfop-by-upload',
+ 'type',
+ $type
+ )
+ )
+ );
+ $putPolicy['persistentOps'] = 'avinfo|saveas/' . $persistentEntry;
+ }
+
+ if ($type == null) {
+ unset($putPolicy['persistentType']);
+ }
+
+ $token = $testAuth->uploadToken(
+ $bucket,
+ $key,
+ 3600,
+ $putPolicy
+ );
+ $upManager = new UploadManager(self::getConfig());
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ __file__,
+ null,
+ 'text/plain',
+ true
+ );
+
+ if (in_array($type, array(null, 0, 1))) {
+ $this->assertNull($error);
+ $this->assertNotEmpty($ret['persistentId']);
+ $id = $ret['persistentId'];
+ } else {
+ $this->assertNotNull($error);
+ return;
+ }
+
+ $pfop = new PersistentFop($testAuth, self::getConfig());
+ list($status, $error) = $pfop->status($id);
+
+ $this->assertNotNull($status);
+ $this->assertNull($error);
+ if ($type == 1) {
+ $this->assertEquals(1, $status['type']);
+ }
+ if ($workflowTemplateID) {
+ // assertStringContainsString when PHPUnit >= 8.0
+ $this->assertTrue(
+ strpos(
+ $status['taskFrom'],
+ $workflowTemplateID
+ ) !== false
+ );
+ }
+ $this->assertNotEmpty($status['creationDate']);
+ }
+ }
+
+ public function testMkzip()
+ {
+ $bucket = self::$bucketName;
+ $key = 'php-logo.png';
+ $pfop = new PersistentFop(self::$testAuth, null);
+
+ $url1 = 'http://phpsdk.qiniudn.com/php-logo.png';
+ $url2 = 'http://phpsdk.qiniudn.com/php-sdk.html';
+ $zipKey = 'test.zip';
+
+ $fops = 'mkzip/2/url/' . \Qiniu\base64_urlSafeEncode($url1);
+ $fops .= '/url/' . \Qiniu\base64_urlSafeEncode($url2);
+ $fops .= '|saveas/' . \Qiniu\base64_urlSafeEncode("$bucket:$zipKey");
+
+ list($id, $error) = $pfop->execute($bucket, $key, $fops);
+ $this->assertNull($error);
+
+ list($status, $error) = $pfop->status($id);
+ $this->assertNotNull($status);
+ $this->assertNull($error);
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/ResumeUpTest.php b/php-sdk/tests/Qiniu/Tests/ResumeUpTest.php
new file mode 100644
index 0000000..6feee55
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/ResumeUpTest.php
@@ -0,0 +1,354 @@
+batch($ops);
+ }
+
+ private static function getObjectKey($key)
+ {
+ $result = $key . rand();
+ self::$keysToCleanup[] = $result;
+ return $result;
+ }
+
+ public function test4ML()
+ {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $upManager = new UploadManager();
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $tempFile = qiniuTempFile(4 * 1024 * 1024 + 10);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ null,
+ 'application/octet-stream',
+ false,
+ $resumeFile
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ $response = Client::get("http://$domain/$key");
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals(md5_file($tempFile, true), md5($response->body(), true));
+ unlink($tempFile);
+ }
+
+ public function test4ML2()
+ {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $cfg = new Config();
+ $upManager = new UploadManager($cfg);
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $tempFile = qiniuTempFile(4 * 1024 * 1024 + 10);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ null,
+ 'application/octet-stream',
+ false,
+ $resumeFile
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ $response = Client::get("http://$domain/$key");
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals(md5_file($tempFile, true), md5($response->body(), true));
+ unlink($tempFile);
+ }
+
+ public function test4ML2WithProxy()
+ {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $cfg = new Config();
+ $upManager = new UploadManager($cfg);
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $tempFile = qiniuTempFile(4 * 1024 * 1024 + 10);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ null,
+ 'application/octet-stream',
+ false,
+ $resumeFile,
+ 'v2',
+ Config::BLOCK_SIZE,
+ $this->makeReqOpt()
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ $response = Client::get("http://$domain/$key");
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals(md5_file($tempFile, true), md5($response->body(), true));
+ unlink($tempFile);
+ }
+
+ // public function test8M()
+ // {
+ // $key = 'resumePutFile8M';
+ // $upManager = new UploadManager();
+ // $token = self::$auth->uploadToken(self::$bucketName, $key);
+ // $tempFile = qiniuTempFile(8*1024*1024+10);
+ // list($ret, $error) = $upManager->putFile($token, $key, $tempFile);
+ // $this->assertNull($error);
+ // $this->assertNotNull($ret['hash']);
+ // unlink($tempFile);
+ // }
+
+ public function testFileWithFileType()
+ {
+ $config = new Config();
+ $bucketManager = new BucketManager(self::$auth, $config);
+
+ $testCases = array(
+ array(
+ "fileType" => 1,
+ "name" => "IA"
+ ),
+ array(
+ "fileType" => 2,
+ "name" => "Archive"
+ ),
+ array(
+ "fileType" => 3,
+ "name" => "DeepArchive"
+ )
+ );
+
+ foreach ($testCases as $testCase) {
+ $key = self::getObjectKey('FileType' . $testCase["name"]);
+ $police = array(
+ "fileType" => $testCase["fileType"],
+ );
+ $token = self::$auth->uploadToken(self::$bucketName, $key, 3600, $police);
+ $upManager = new UploadManager();
+ list($ret, $error) = $upManager->putFile($token, $key, __file__, null, 'text/plain');
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ list($ret, $err) = $bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($err);
+ $this->assertEquals($testCase["fileType"], $ret["type"]);
+ }
+ }
+
+ public function testResumeUploadWithParams()
+ {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $upManager = new UploadManager();
+ $policy = array('returnBody' => '{"hash":$(etag),"fname":$(fname),"var_1":$(x:var_1),"var_2":$(x:var_2)}');
+ $token = self::$auth->uploadToken(self::$bucketName, $key, 3600, $policy);
+ $tempFile = qiniuTempFile(4 * 1024 * 1024 + 10);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ array("x:var_1" => "val_1", "x:var_2" => "val_2", "x-qn-meta-m1" => "val_1", "x-qn-meta-m2" => "val_2"),
+ 'application/octet-stream',
+ false,
+ $resumeFile
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ $this->assertEquals("val_1", $ret['var_1']);
+ $this->assertEquals("val_2", $ret['var_2']);
+ $this->assertEquals(basename($tempFile), $ret['fname']);
+
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ $response = Client::get("http://$domain/$key");
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals(md5_file($tempFile, true), md5($response->body(), true));
+ $headers = $response->headers();
+ $this->assertEquals("val_1", $headers["X-Qn-Meta-M1"]);
+ $this->assertEquals("val_2", $headers["X-Qn-Meta-M2"]);
+ unlink($tempFile);
+ }
+
+ public function testResumeUploadV2()
+ {
+ $cfg = new Config();
+ $upManager = new UploadManager($cfg);
+ $testFileSize = array(
+ config::BLOCK_SIZE / 2,
+ config::BLOCK_SIZE,
+ config::BLOCK_SIZE + 10,
+ config::BLOCK_SIZE * 2,
+ config::BLOCK_SIZE * 2.5
+ );
+ $partSize = 5 * 1024 * 1024;
+ foreach ($testFileSize as $item) {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $tempFile = qiniuTempFile($item);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ null,
+ 'application/octet-stream',
+ false,
+ $resumeFile,
+ 'v2',
+ $partSize
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ $response = Client::get("http://$domain/$key");
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals(md5_file($tempFile, true), md5($response->body(), true));
+ unlink($tempFile);
+ }
+ }
+
+ public function testResumeUploadV2WithParams()
+ {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $upManager = new UploadManager();
+ $policy = array('returnBody' => '{"hash":$(etag),"fname":$(fname),"var_1":$(x:var_1),"var_2":$(x:var_2)}');
+ $token = self::$auth->uploadToken(self::$bucketName, $key, 3600, $policy);
+ $tempFile = qiniuTempFile(4 * 1024 * 1024 + 10);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ array("x:var_1" => "val_1", "x:var_2" => "val_2", "x-qn-meta-m1" => "val_1", "x-qn-meta-m2" => "val_2"),
+ 'application/octet-stream',
+ false,
+ $resumeFile,
+ 'v2'
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ $this->assertEquals("val_1", $ret['var_1']);
+ $this->assertEquals("val_2", $ret['var_2']);
+ $this->assertEquals(basename($tempFile), $ret['fname']);
+
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ $response = Client::get("http://$domain/$key");
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals(md5_file($tempFile, true), md5($response->body(), true));
+ $headers = $response->headers();
+ $this->assertEquals("val_1", $headers["X-Qn-Meta-M1"]);
+ $this->assertEquals("val_2", $headers["X-Qn-Meta-M2"]);
+ unlink($tempFile);
+ }
+
+ // valid versions are tested above
+ // Use PHPUnit's Data Provider to test multiple Exception is better,
+ // but not match the test style of this project
+ public function testResumeUploadWithInvalidVersion()
+ {
+ $cfg = new Config();
+ $upManager = new UploadManager($cfg);
+ $testFileSize = config::BLOCK_SIZE * 2;
+ $partSize = 5 * 1024 * 1024;
+ $testInvalidVersions = array(
+ // High probability invalid versions
+ 'v',
+ '1',
+ '2'
+ );
+
+ $expectExceptionCount = 0;
+ foreach ($testInvalidVersions as $invalidVersion) {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $tempFile = qiniuTempFile($testFileSize);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ try {
+ $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ null,
+ 'application/octet-stream',
+ false,
+ $resumeFile,
+ $invalidVersion,
+ $partSize
+ );
+ } catch (\Exception $e) {
+ $isRightException = false;
+ $expectExceptionCount++;
+ while ($e) {
+ $isRightException = $e instanceof \UnexpectedValueException;
+ if ($isRightException) {
+ break;
+ }
+ $e = $e->getPrevious();
+ }
+ $this->assertTrue($isRightException);
+ }
+
+ unlink($tempFile);
+ }
+ $this->assertEquals(count($testInvalidVersions), $expectExceptionCount);
+ }
+
+ private function makeReqOpt()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->proxy = 'socks5://127.0.0.1:8080';
+ $reqOpt->proxy_user_password = 'user:pass';
+ return $reqOpt;
+ }
+}
diff --git a/php-sdk/tests/Qiniu/Tests/ZoneTest.php b/php-sdk/tests/Qiniu/Tests/ZoneTest.php
new file mode 100644
index 0000000..fbab528
--- /dev/null
+++ b/php-sdk/tests/Qiniu/Tests/ZoneTest.php
@@ -0,0 +1,136 @@
+bucketName = $bucketName;
+
+ global $bucketNameBC;
+ $this->bucketNameBC = $bucketNameBC;
+
+ global $bucketNameNA;
+ $this->bucketNameNA = $bucketNameNA;
+
+ global $bucketNameFS;
+ $this->bucketNameFS = $bucketNameFS;
+
+ global $bucketNameAS;
+ $this->bucketNameAS = $bucketNameAS;
+
+ global $accessKey;
+ $this->ak = $accessKey;
+
+ $this->zone = new Zone();
+ $this->zoneHttps = new Zone('https');
+ }
+
+ public function testUpHosts()
+ {
+ list($ret, $err) = Zone::queryZone($this->ak, 'fakebucket');
+ $this->assertNull($ret);
+ $this->assertNotNull($err);
+
+ $zone = Zone::queryZone($this->ak, $this->bucketName);
+ $this->assertContains('upload.qiniup.com', $zone->cdnUpHosts);
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameBC);
+ $this->assertContains('upload-z1.qiniup.com', $zone->cdnUpHosts);
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameFS);
+ $this->assertContains('upload-z2.qiniup.com', $zone->cdnUpHosts);
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameNA);
+ $this->assertContains('upload-na0.qiniup.com', $zone->cdnUpHosts);
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameAS);
+ $this->assertContains('upload-as0.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testIoHosts()
+ {
+ $zone = Zone::queryZone($this->ak, $this->bucketName);
+ $this->assertEquals($zone->iovipHost, 'iovip.qbox.me');
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameBC);
+ $this->assertEquals($zone->iovipHost, 'iovip-z1.qbox.me');
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameFS);
+ $this->assertEquals($zone->iovipHost, 'iovip-z2.qbox.me');
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameNA);
+ $this->assertEquals($zone->iovipHost, 'iovip-na0.qbox.me');
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameAS);
+ $this->assertEquals($zone->iovipHost, 'iovip-as0.qbox.me');
+ }
+
+ public function testZonez0()
+ {
+ $zone = Zone::zonez0();
+ $this->assertContains('upload.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testZonez1()
+ {
+ $zone = Zone::zonez1();
+ $this->assertContains('upload-z1.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testZonez2()
+ {
+ $zone = Zone::zonez2();
+ $this->assertContains('upload-z2.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testZoneCnEast2()
+ {
+ $zone = Zone::zoneCnEast2();
+ $this->assertContains('upload-cn-east-2.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testZoneNa0()
+ {
+ $zone = Zone::zoneNa0();
+ $this->assertContains('upload-na0.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testZoneAs0()
+ {
+ $zone = Zone::zoneAs0();
+ $this->assertContains('upload-as0.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testQvmZonez0()
+ {
+ $zone = Zone::qvmZonez0();
+ $this->assertContains('free-qvm-z0-xs.qiniup.com', $zone->srcUpHosts);
+ }
+
+ public function testQvmZonez1()
+ {
+ $zone = Zone::qvmZonez1();
+ $this->assertContains('free-qvm-z1-zz.qiniup.com', $zone->srcUpHosts);
+ }
+}
diff --git a/php-sdk/tests/bootstrap.php b/php-sdk/tests/bootstrap.php
new file mode 100644
index 0000000..9859a81
--- /dev/null
+++ b/php-sdk/tests/bootstrap.php
@@ -0,0 +1,61 @@
+ 0) {
+ $length = min($rest_size, 4 * 1024);
+ if (fwrite($file, random_bytes($length)) == false) {
+ return false;
+ }
+ $rest_size -= $length;
+ }
+ } else if ($size > 0) {
+ fseek($file, $size - 1);
+ fwrite($file, ' ');
+ }
+ fclose($file);
+ return $fileName;
+}
diff --git a/php-sdk/tests/mock-server/ok.php b/php-sdk/tests/mock-server/ok.php
new file mode 100644
index 0000000..5b0a65d
--- /dev/null
+++ b/php-sdk/tests/mock-server/ok.php
@@ -0,0 +1,3 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier
+ * @author Jordi Boggiano
+ * @see https://www.php-fig.org/psr/psr-0/
+ * @see https://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ private $vendorDir;
+
+ // PSR-4
+ private $prefixLengthsPsr4 = array();
+ private $prefixDirsPsr4 = array();
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ private $prefixesPsr0 = array();
+ private $fallbackDirsPsr0 = array();
+
+ private $useIncludePath = false;
+ private $classMap = array();
+ private $classMapAuthoritative = false;
+ private $missingClasses = array();
+ private $apcuPrefix;
+
+ private static $registeredLoaders = array();
+
+ public function __construct($vendorDir = null)
+ {
+ $this->vendorDir = $vendorDir;
+ }
+
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+ }
+
+ return array();
+ }
+
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array $classMap Class to filename map
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ (array) $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ (array) $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 base directories
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+ if (null === $this->vendorDir) {
+ return;
+ }
+
+ if ($prepend) {
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+ } else {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ self::$registeredLoaders[$this->vendorDir] = $this;
+ }
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+
+ if (null !== $this->vendorDir) {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ }
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return bool|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ includeFile($file);
+
+ return true;
+ }
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ /**
+ * Returns the currently registered loaders indexed by their corresponding vendor directories.
+ *
+ * @return self[]
+ */
+ public static function getRegisteredLoaders()
+ {
+ return self::$registeredLoaders;
+ }
+
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath . '\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ if (file_exists($file = $dir . $pathEnd)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ return false;
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+ include $file;
+}
diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php
new file mode 100644
index 0000000..2dcfa10
--- /dev/null
+++ b/vendor/composer/InstalledVersions.php
@@ -0,0 +1,317 @@
+
+ array (
+ 'pretty_version' => '1.0.0+no-version-set',
+ 'version' => '1.0.0.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => NULL,
+ 'name' => '__root__',
+ ),
+ 'versions' =>
+ array (
+ '__root__' =>
+ array (
+ 'pretty_version' => '1.0.0+no-version-set',
+ 'version' => '1.0.0.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => NULL,
+ ),
+ 'myclabs/php-enum' =>
+ array (
+ 'pretty_version' => '1.8.5',
+ 'version' => '1.8.5.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => 'e7be26966b7398204a234f8673fdad5ac6277802',
+ ),
+ 'qiniu/php-sdk' =>
+ array (
+ 'pretty_version' => 'v7.14.0',
+ 'version' => '7.14.0.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => 'ee752ffa7263ce99fca0bd7340cf13c486a3516c',
+ ),
+ ),
+);
+private static $canGetVendors;
+private static $installedByVendor = array();
+
+
+
+
+
+
+
+public static function getInstalledPackages()
+{
+$packages = array();
+foreach (self::getInstalled() as $installed) {
+$packages[] = array_keys($installed['versions']);
+}
+
+if (1 === \count($packages)) {
+return $packages[0];
+}
+
+return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+}
+
+
+
+
+
+
+
+
+
+public static function isInstalled($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (isset($installed['versions'][$packageName])) {
+return true;
+}
+}
+
+return false;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+public static function satisfies(VersionParser $parser, $packageName, $constraint)
+{
+$constraint = $parser->parseConstraints($constraint);
+$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+return $provided->matches($constraint);
+}
+
+
+
+
+
+
+
+
+
+
+public static function getVersionRanges($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (!isset($installed['versions'][$packageName])) {
+continue;
+}
+
+$ranges = array();
+if (isset($installed['versions'][$packageName]['pretty_version'])) {
+$ranges[] = $installed['versions'][$packageName]['pretty_version'];
+}
+if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+}
+if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+}
+if (array_key_exists('provided', $installed['versions'][$packageName])) {
+$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+}
+
+return implode(' || ', $ranges);
+}
+
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+
+
+
+
+public static function getVersion($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (!isset($installed['versions'][$packageName])) {
+continue;
+}
+
+if (!isset($installed['versions'][$packageName]['version'])) {
+return null;
+}
+
+return $installed['versions'][$packageName]['version'];
+}
+
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+
+
+
+
+public static function getPrettyVersion($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (!isset($installed['versions'][$packageName])) {
+continue;
+}
+
+if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+return null;
+}
+
+return $installed['versions'][$packageName]['pretty_version'];
+}
+
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+
+
+
+
+public static function getReference($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (!isset($installed['versions'][$packageName])) {
+continue;
+}
+
+if (!isset($installed['versions'][$packageName]['reference'])) {
+return null;
+}
+
+return $installed['versions'][$packageName]['reference'];
+}
+
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+
+
+
+
+public static function getRootPackage()
+{
+$installed = self::getInstalled();
+
+return $installed[0]['root'];
+}
+
+
+
+
+
+
+
+
+public static function getRawData()
+{
+@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
+
+return self::$installed;
+}
+
+
+
+
+
+
+
+public static function getAllRawData()
+{
+return self::getInstalled();
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+public static function reload($data)
+{
+self::$installed = $data;
+self::$installedByVendor = array();
+}
+
+
+
+
+
+private static function getInstalled()
+{
+if (null === self::$canGetVendors) {
+self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+}
+
+$installed = array();
+
+if (self::$canGetVendors) {
+foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+if (isset(self::$installedByVendor[$vendorDir])) {
+$installed[] = self::$installedByVendor[$vendorDir];
+} elseif (is_file($vendorDir.'/composer/installed.php')) {
+$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
+}
+}
+}
+
+$installed[] = self::$installed;
+
+return $installed;
+}
+}
diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE
new file mode 100644
index 0000000..f27399a
--- /dev/null
+++ b/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
new file mode 100644
index 0000000..bb58823
--- /dev/null
+++ b/vendor/composer/autoload_classmap.php
@@ -0,0 +1,11 @@
+ $vendorDir . '/composer/InstalledVersions.php',
+ 'Stringable' => $vendorDir . '/myclabs/php-enum/stubs/Stringable.php',
+);
diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php
new file mode 100644
index 0000000..8402a80
--- /dev/null
+++ b/vendor/composer/autoload_files.php
@@ -0,0 +1,11 @@
+ $vendorDir . '/qiniu/php-sdk/src/Qiniu/functions.php',
+ '5dd19d8a547b7318af0c3a93c8bd6565' => $vendorDir . '/qiniu/php-sdk/src/Qiniu/Http/Middleware/Middleware.php',
+);
diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php
new file mode 100644
index 0000000..b7fc012
--- /dev/null
+++ b/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,9 @@
+ array($vendorDir . '/qiniu/php-sdk/src/Qiniu'),
+ 'MyCLabs\\Enum\\' => array($vendorDir . '/myclabs/php-enum/src'),
+);
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
new file mode 100644
index 0000000..70531e2
--- /dev/null
+++ b/vendor/composer/autoload_real.php
@@ -0,0 +1,75 @@
+= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+ if ($useStaticLoader) {
+ require __DIR__ . '/autoload_static.php';
+
+ call_user_func(\Composer\Autoload\ComposerStaticInit33806c1377e553ceececb4a264f74f6f::getInitializer($loader));
+ } else {
+ $map = require __DIR__ . '/autoload_namespaces.php';
+ foreach ($map as $namespace => $path) {
+ $loader->set($namespace, $path);
+ }
+
+ $map = require __DIR__ . '/autoload_psr4.php';
+ foreach ($map as $namespace => $path) {
+ $loader->setPsr4($namespace, $path);
+ }
+
+ $classMap = require __DIR__ . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
+ }
+
+ $loader->register(true);
+
+ if ($useStaticLoader) {
+ $includeFiles = Composer\Autoload\ComposerStaticInit33806c1377e553ceececb4a264f74f6f::$files;
+ } else {
+ $includeFiles = require __DIR__ . '/autoload_files.php';
+ }
+ foreach ($includeFiles as $fileIdentifier => $file) {
+ composerRequire33806c1377e553ceececb4a264f74f6f($fileIdentifier, $file);
+ }
+
+ return $loader;
+ }
+}
+
+function composerRequire33806c1377e553ceececb4a264f74f6f($fileIdentifier, $file)
+{
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+ require $file;
+
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+ }
+}
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
new file mode 100644
index 0000000..75011ef
--- /dev/null
+++ b/vendor/composer/autoload_static.php
@@ -0,0 +1,50 @@
+ __DIR__ . '/..' . '/qiniu/php-sdk/src/Qiniu/functions.php',
+ '5dd19d8a547b7318af0c3a93c8bd6565' => __DIR__ . '/..' . '/qiniu/php-sdk/src/Qiniu/Http/Middleware/Middleware.php',
+ );
+
+ public static $prefixLengthsPsr4 = array (
+ 'Q' =>
+ array (
+ 'Qiniu\\' => 6,
+ ),
+ 'M' =>
+ array (
+ 'MyCLabs\\Enum\\' => 13,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'Qiniu\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/qiniu/php-sdk/src/Qiniu',
+ ),
+ 'MyCLabs\\Enum\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/myclabs/php-enum/src',
+ ),
+ );
+
+ public static $classMap = array (
+ 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+ 'Stringable' => __DIR__ . '/..' . '/myclabs/php-enum/stubs/Stringable.php',
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInit33806c1377e553ceececb4a264f74f6f::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInit33806c1377e553ceececb4a264f74f6f::$prefixDirsPsr4;
+ $loader->classMap = ComposerStaticInit33806c1377e553ceececb4a264f74f6f::$classMap;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
new file mode 100644
index 0000000..0e6edfc
--- /dev/null
+++ b/vendor/composer/installed.json
@@ -0,0 +1,135 @@
+{
+ "packages": [
+ {
+ "name": "myclabs/php-enum",
+ "version": "1.8.5",
+ "version_normalized": "1.8.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/php-enum.git",
+ "reference": "e7be26966b7398204a234f8673fdad5ac6277802"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/php-enum/zipball/e7be26966b7398204a234f8673fdad5ac6277802",
+ "reference": "e7be26966b7398204a234f8673fdad5ac6277802",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": "^7.3 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5",
+ "squizlabs/php_codesniffer": "1.*",
+ "vimeo/psalm": "^4.6.2 || ^5.2"
+ },
+ "time": "2025-01-14T11:49:03+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "MyCLabs\\Enum\\": "src/"
+ },
+ "classmap": [
+ "stubs/Stringable.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP Enum contributors",
+ "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
+ }
+ ],
+ "description": "PHP Enum implementation",
+ "homepage": "https://github.com/myclabs/php-enum",
+ "keywords": [
+ "enum"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/php-enum/issues",
+ "source": "https://github.com/myclabs/php-enum/tree/1.8.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/mnapoli",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
+ "type": "tidelift"
+ }
+ ],
+ "install-path": "../myclabs/php-enum"
+ },
+ {
+ "name": "qiniu/php-sdk",
+ "version": "v7.14.0",
+ "version_normalized": "7.14.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/qiniu/php-sdk.git",
+ "reference": "ee752ffa7263ce99fca0bd7340cf13c486a3516c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/qiniu/php-sdk/zipball/ee752ffa7263ce99fca0bd7340cf13c486a3516c",
+ "reference": "ee752ffa7263ce99fca0bd7340cf13c486a3516c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "ext-xml": "*",
+ "myclabs/php-enum": "~1.5.2 || ~1.6.6 || ~1.7.7 || ~1.8.4",
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "paragonie/random_compat": ">=2",
+ "phpunit/phpunit": "^4.8 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4",
+ "squizlabs/php_codesniffer": "^2.3 || ~3.6"
+ },
+ "time": "2024-10-25T08:39:01+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "src/Qiniu/functions.php",
+ "src/Qiniu/Http/Middleware/Middleware.php"
+ ],
+ "psr-4": {
+ "Qiniu\\": "src/Qiniu"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Qiniu",
+ "email": "sdk@qiniu.com",
+ "homepage": "http://www.qiniu.com"
+ }
+ ],
+ "description": "Qiniu Resource (Cloud) Storage SDK for PHP",
+ "homepage": "http://developer.qiniu.com/",
+ "keywords": [
+ "cloud",
+ "qiniu",
+ "sdk",
+ "storage"
+ ],
+ "support": {
+ "issues": "https://github.com/qiniu/php-sdk/issues",
+ "source": "https://github.com/qiniu/php-sdk/tree/v7.14.0"
+ },
+ "install-path": "../qiniu/php-sdk"
+ }
+ ],
+ "dev": true,
+ "dev-package-names": []
+}
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
new file mode 100644
index 0000000..fed962a
--- /dev/null
+++ b/vendor/composer/installed.php
@@ -0,0 +1,42 @@
+
+ array (
+ 'pretty_version' => '1.0.0+no-version-set',
+ 'version' => '1.0.0.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => NULL,
+ 'name' => '__root__',
+ ),
+ 'versions' =>
+ array (
+ '__root__' =>
+ array (
+ 'pretty_version' => '1.0.0+no-version-set',
+ 'version' => '1.0.0.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => NULL,
+ ),
+ 'myclabs/php-enum' =>
+ array (
+ 'pretty_version' => '1.8.5',
+ 'version' => '1.8.5.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => 'e7be26966b7398204a234f8673fdad5ac6277802',
+ ),
+ 'qiniu/php-sdk' =>
+ array (
+ 'pretty_version' => 'v7.14.0',
+ 'version' => '7.14.0.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => 'ee752ffa7263ce99fca0bd7340cf13c486a3516c',
+ ),
+ ),
+);
diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php
new file mode 100644
index 0000000..92370c5
--- /dev/null
+++ b/vendor/composer/platform_check.php
@@ -0,0 +1,26 @@
+= 70300)) {
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 7.3.0". You are running ' . PHP_VERSION . '.';
+}
+
+if ($issues) {
+ if (!headers_sent()) {
+ header('HTTP/1.1 500 Internal Server Error');
+ }
+ if (!ini_get('display_errors')) {
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+ fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
+ } elseif (!headers_sent()) {
+ echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
+ }
+ }
+ trigger_error(
+ 'Composer detected issues in your platform: ' . implode(' ', $issues),
+ E_USER_ERROR
+ );
+}
diff --git a/vendor/myclabs/php-enum/LICENSE b/vendor/myclabs/php-enum/LICENSE
new file mode 100644
index 0000000..2a8cf22
--- /dev/null
+++ b/vendor/myclabs/php-enum/LICENSE
@@ -0,0 +1,18 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 My C-Labs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/myclabs/php-enum/README.md b/vendor/myclabs/php-enum/README.md
new file mode 100644
index 0000000..948f374
--- /dev/null
+++ b/vendor/myclabs/php-enum/README.md
@@ -0,0 +1,196 @@
+# PHP Enum implementation inspired from SplEnum
+
+[![GitHub Actions][GA Image]][GA Link]
+[](https://packagist.org/packages/myclabs/php-enum)
+[](https://packagist.org/packages/myclabs/php-enum)
+[![Psalm Shepherd][Shepherd Image]][Shepherd Link]
+
+Maintenance for this project is [supported via Tidelift](https://tidelift.com/subscription/pkg/packagist-myclabs-php-enum?utm_source=packagist-myclabs-php-enum&utm_medium=referral&utm_campaign=readme).
+
+## Why?
+
+First, and mainly, `SplEnum` is not integrated to PHP, you have to install the extension separately.
+
+Using an enum instead of class constants provides the following advantages:
+
+- You can use an enum as a parameter type: `function setAction(Action $action) {`
+- You can use an enum as a return type: `function getAction() : Action {`
+- You can enrich the enum with methods (e.g. `format`, `parse`, …)
+- You can extend the enum to add new values (make your enum `final` to prevent it)
+- You can get a list of all the possible values (see below)
+
+This Enum class is not intended to replace class constants, but only to be used when it makes sense.
+
+## Installation
+
+```
+composer require myclabs/php-enum
+```
+
+## Declaration
+
+```php
+use MyCLabs\Enum\Enum;
+
+/**
+ * Action enum
+ *
+ * @extends Enum
+ */
+final class Action extends Enum
+{
+ private const VIEW = 'view';
+ private const EDIT = 'edit';
+}
+```
+
+## Usage
+
+```php
+$action = Action::VIEW();
+
+// or with a dynamic key:
+$action = Action::$key();
+// or with a dynamic value:
+$action = Action::from($value);
+// or
+$action = new Action($value);
+```
+
+As you can see, static methods are automatically implemented to provide quick access to an enum value.
+
+One advantage over using class constants is to be able to use an enum as a parameter type:
+
+```php
+function setAction(Action $action) {
+ // ...
+}
+```
+
+## Documentation
+
+- `__construct()` The constructor checks that the value exist in the enum
+- `__toString()` You can `echo $myValue`, it will display the enum value (value of the constant)
+- `getValue()` Returns the current value of the enum
+- `getKey()` Returns the key of the current value on Enum
+- `equals()` Tests whether enum instances are equal (returns `true` if enum values are equal, `false` otherwise)
+
+Static methods:
+
+- `from()` Creates an Enum instance, checking that the value exist in the enum
+- `toArray()` method Returns all possible values as an array (constant name in key, constant value in value)
+- `keys()` Returns the names (keys) of all constants in the Enum class
+- `values()` Returns instances of the Enum class of all Enum constants (constant name in key, Enum instance in value)
+- `isValid()` Check if tested value is valid on enum set
+- `isValidKey()` Check if tested key is valid on enum set
+- `assertValidValue()` Assert the value is valid on enum set, throwing exception otherwise
+- `search()` Return key for searched value
+
+### Static methods
+
+```php
+final class Action extends Enum
+{
+ private const VIEW = 'view';
+ private const EDIT = 'edit';
+}
+
+// Static method:
+$action = Action::VIEW();
+$action = Action::EDIT();
+```
+
+Static method helpers are implemented using [`__callStatic()`](http://www.php.net/manual/en/language.oop5.overloading.php#object.callstatic).
+
+If you care about IDE autocompletion, you can either implement the static methods yourself:
+
+```php
+final class Action extends Enum
+{
+ private const VIEW = 'view';
+
+ /**
+ * @return Action
+ */
+ public static function VIEW() {
+ return new Action(self::VIEW);
+ }
+}
+```
+
+or you can use phpdoc (this is supported in PhpStorm for example):
+
+```php
+/**
+ * @method static Action VIEW()
+ * @method static Action EDIT()
+ */
+final class Action extends Enum
+{
+ private const VIEW = 'view';
+ private const EDIT = 'edit';
+}
+```
+
+## Native enums and migration
+Native enum arrived to PHP in version 8.1: https://www.php.net/enumerations
+If your project is running PHP 8.1+ or your library has it as a minimum requirement you should use it instead of this library.
+
+When migrating from `myclabs/php-enum`, the effort should be small if the usage was in the recommended way:
+- private constants
+- final classes
+- no method overridden
+
+Changes for migration:
+- Class definition should be changed from
+```php
+/**
+ * @method static Action VIEW()
+ * @method static Action EDIT()
+ */
+final class Action extends Enum
+{
+ private const VIEW = 'view';
+ private const EDIT = 'edit';
+}
+```
+ to
+```php
+enum Action: string
+{
+ case VIEW = 'view';
+ case EDIT = 'edit';
+}
+```
+All places where the class was used as a type will continue to work.
+
+Usages and the change needed:
+
+| Operation | myclabs/php-enum | native enum |
+|----------------------------------------------------------------|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Obtain an instance will change from | `$enumCase = Action::VIEW()` | `$enumCase = Action::VIEW` |
+| Create an enum from a backed value | `$enumCase = new Action('view')` | `$enumCase = Action::from('view')` |
+| Get the backed value of the enum instance | `$enumCase->getValue()` | `$enumCase->value` |
+| Compare two enum instances | `$enumCase1 == $enumCase2`
or
`$enumCase1->equals($enumCase2)` | `$enumCase1 === $enumCase2` |
+| Get the key/name of the enum instance | `$enumCase->getKey()` | `$enumCase->name` |
+| Get a list of all the possible instances of the enum | `Action::values()` | `Action::cases()` |
+| Get a map of possible instances of the enum mapped by name | `Action::values()` | `array_combine(array_map(fn($case) => $case->name, Action::cases()), Action::cases())`
or
`(new ReflectionEnum(Action::class))->getConstants()` |
+| Get a list of all possible names of the enum | `Action::keys()` | `array_map(fn($case) => $case->name, Action::cases())` |
+| Get a list of all possible backed values of the enum | `Action::toArray()` | `array_map(fn($case) => $case->value, Action::cases())` |
+| Get a map of possible backed values of the enum mapped by name | `Action::toArray()` | `array_combine(array_map(fn($case) => $case->name, Action::cases()), array_map(fn($case) => $case->value, Action::cases()))`
or
`array_map(fn($case) => $case->value, (new ReflectionEnum(Action::class))->getConstants()))` |
+
+## Related projects
+
+- [PHP 8.1+ native enum](https://www.php.net/enumerations)
+- [Doctrine enum mapping](https://github.com/acelaya/doctrine-enum-type)
+- [Symfony ParamConverter integration](https://github.com/Ex3v/MyCLabsEnumParamConverter)
+- [PHPStan integration](https://github.com/timeweb/phpstan-enum)
+
+
+[GA Image]: https://github.com/myclabs/php-enum/workflows/CI/badge.svg
+
+[GA Link]: https://github.com/myclabs/php-enum/actions?query=workflow%3A%22CI%22+branch%3Amaster
+
+[Shepherd Image]: https://shepherd.dev/github/myclabs/php-enum/coverage.svg
+
+[Shepherd Link]: https://shepherd.dev/github/myclabs/php-enum
diff --git a/vendor/myclabs/php-enum/SECURITY.md b/vendor/myclabs/php-enum/SECURITY.md
new file mode 100644
index 0000000..84fd4e3
--- /dev/null
+++ b/vendor/myclabs/php-enum/SECURITY.md
@@ -0,0 +1,11 @@
+# Security Policy
+
+## Supported Versions
+
+Only the latest stable release is supported.
+
+## Reporting a Vulnerability
+
+To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
+
+Tidelift will coordinate the fix and disclosure.
diff --git a/vendor/myclabs/php-enum/composer.json b/vendor/myclabs/php-enum/composer.json
new file mode 100644
index 0000000..eab6263
--- /dev/null
+++ b/vendor/myclabs/php-enum/composer.json
@@ -0,0 +1,36 @@
+{
+ "name": "myclabs/php-enum",
+ "type": "library",
+ "description": "PHP Enum implementation",
+ "keywords": ["enum"],
+ "homepage": "https://github.com/myclabs/php-enum",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "PHP Enum contributors",
+ "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "MyCLabs\\Enum\\": "src/"
+ },
+ "classmap": [
+ "stubs/Stringable.php"
+ ]
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "MyCLabs\\Tests\\Enum\\": "tests/"
+ }
+ },
+ "require": {
+ "php": "^7.3 || ^8.0",
+ "ext-json": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5",
+ "squizlabs/php_codesniffer": "1.*",
+ "vimeo/psalm": "^4.6.2 || ^5.2"
+ }
+}
diff --git a/vendor/myclabs/php-enum/src/Enum.php b/vendor/myclabs/php-enum/src/Enum.php
new file mode 100644
index 0000000..1bd5592
--- /dev/null
+++ b/vendor/myclabs/php-enum/src/Enum.php
@@ -0,0 +1,319 @@
+
+ * @author Daniel Costa
+ * @author Mirosław Filip
+ *
+ * @psalm-template T
+ * @psalm-immutable
+ * @psalm-consistent-constructor
+ */
+abstract class Enum implements \JsonSerializable, \Stringable
+{
+ /**
+ * Enum value
+ *
+ * @var mixed
+ * @psalm-var T
+ */
+ protected $value;
+
+ /**
+ * Enum key, the constant name
+ *
+ * @var string
+ */
+ private $key;
+
+ /**
+ * Store existing constants in a static cache per object.
+ *
+ *
+ * @var array
+ * @psalm-var array>
+ */
+ protected static $cache = [];
+
+ /**
+ * Cache of instances of the Enum class
+ *
+ * @var array
+ * @psalm-var array>
+ */
+ protected static $instances = [];
+
+ /**
+ * Creates a new value of some type
+ *
+ * @psalm-pure
+ * @param mixed $value
+ *
+ * @psalm-param T $value
+ * @throws \UnexpectedValueException if incompatible type is given.
+ */
+ public function __construct($value)
+ {
+ if ($value instanceof static) {
+ /** @psalm-var T */
+ $value = $value->getValue();
+ }
+
+ /** @psalm-suppress ImplicitToStringCast assertValidValueReturningKey returns always a string but psalm has currently an issue here */
+ $this->key = static::assertValidValueReturningKey($value);
+
+ /** @psalm-var T */
+ $this->value = $value;
+ }
+
+ /**
+ * This method exists only for the compatibility reason when deserializing a previously serialized version
+ * that didn't had the key property
+ */
+ public function __wakeup()
+ {
+ /** @psalm-suppress DocblockTypeContradiction key can be null when deserializing an enum without the key */
+ if ($this->key === null) {
+ /**
+ * @psalm-suppress InaccessibleProperty key is not readonly as marked by psalm
+ * @psalm-suppress PossiblyFalsePropertyAssignmentValue deserializing a case that was removed
+ */
+ $this->key = static::search($this->value);
+ }
+ }
+
+ /**
+ * @param mixed $value
+ * @return static
+ */
+ public static function from($value): self
+ {
+ $key = static::assertValidValueReturningKey($value);
+
+ return self::__callStatic($key, []);
+ }
+
+ /**
+ * @psalm-pure
+ * @return mixed
+ * @psalm-return T
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Returns the enum key (i.e. the constant name).
+ *
+ * @psalm-pure
+ * @return string
+ */
+ public function getKey()
+ {
+ return $this->key;
+ }
+
+ /**
+ * @psalm-pure
+ * @psalm-suppress InvalidCast
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string)$this->value;
+ }
+
+ /**
+ * Determines if Enum should be considered equal with the variable passed as a parameter.
+ * Returns false if an argument is an object of different class or not an object.
+ *
+ * This method is final, for more information read https://github.com/myclabs/php-enum/issues/4
+ *
+ * @psalm-pure
+ * @psalm-param mixed $variable
+ * @return bool
+ */
+ final public function equals($variable = null): bool
+ {
+ return $variable instanceof self
+ && $this->getValue() === $variable->getValue()
+ && static::class === \get_class($variable);
+ }
+
+ /**
+ * Returns the names (keys) of all constants in the Enum class
+ *
+ * @psalm-pure
+ * @psalm-return list
+ * @return array
+ */
+ public static function keys()
+ {
+ return \array_keys(static::toArray());
+ }
+
+ /**
+ * Returns instances of the Enum class of all Enum constants
+ *
+ * @psalm-pure
+ * @psalm-return array
+ * @return static[] Constant name in key, Enum instance in value
+ */
+ public static function values()
+ {
+ $values = array();
+
+ /** @psalm-var T $value */
+ foreach (static::toArray() as $key => $value) {
+ /** @psalm-suppress UnsafeGenericInstantiation */
+ $values[$key] = new static($value);
+ }
+
+ return $values;
+ }
+
+ /**
+ * Returns all possible values as an array
+ *
+ * @psalm-pure
+ * @psalm-suppress ImpureStaticProperty
+ *
+ * @psalm-return array
+ * @return array Constant name in key, constant value in value
+ */
+ public static function toArray()
+ {
+ $class = static::class;
+
+ if (!isset(static::$cache[$class])) {
+ /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
+ $reflection = new \ReflectionClass($class);
+ /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
+ static::$cache[$class] = $reflection->getConstants();
+ }
+
+ return static::$cache[$class];
+ }
+
+ /**
+ * Check if is valid enum value
+ *
+ * @param $value
+ * @psalm-param mixed $value
+ * @psalm-pure
+ * @psalm-assert-if-true T $value
+ * @return bool
+ */
+ public static function isValid($value)
+ {
+ return \in_array($value, static::toArray(), true);
+ }
+
+ /**
+ * Asserts valid enum value
+ *
+ * @psalm-pure
+ * @psalm-assert T $value
+ * @param mixed $value
+ */
+ public static function assertValidValue($value): void
+ {
+ self::assertValidValueReturningKey($value);
+ }
+
+ /**
+ * Asserts valid enum value
+ *
+ * @psalm-pure
+ * @psalm-assert T $value
+ * @param mixed $value
+ * @return string
+ */
+ private static function assertValidValueReturningKey($value): string
+ {
+ if (false === ($key = static::search($value))) {
+ throw new \UnexpectedValueException("Value '$value' is not part of the enum " . static::class);
+ }
+
+ return $key;
+ }
+
+ /**
+ * Check if is valid enum key
+ *
+ * @param $key
+ * @psalm-param string $key
+ * @psalm-pure
+ * @return bool
+ */
+ public static function isValidKey($key)
+ {
+ $array = static::toArray();
+
+ return isset($array[$key]) || \array_key_exists($key, $array);
+ }
+
+ /**
+ * Return key for value
+ *
+ * @param mixed $value
+ *
+ * @psalm-param mixed $value
+ * @psalm-pure
+ * @return string|false
+ */
+ public static function search($value)
+ {
+ return \array_search($value, static::toArray(), true);
+ }
+
+ /**
+ * Returns a value when called statically like so: MyEnum::SOME_VALUE() given SOME_VALUE is a class constant
+ *
+ * @param string $name
+ * @param array $arguments
+ *
+ * @return static
+ * @throws \BadMethodCallException
+ *
+ * @psalm-pure
+ */
+ public static function __callStatic($name, $arguments)
+ {
+ $class = static::class;
+ if (!isset(self::$instances[$class][$name])) {
+ $array = static::toArray();
+ if (!isset($array[$name]) && !\array_key_exists($name, $array)) {
+ $message = "No static method or enum constant '$name' in class " . static::class;
+ throw new \BadMethodCallException($message);
+ }
+ /** @psalm-suppress UnsafeGenericInstantiation */
+ return self::$instances[$class][$name] = new static($array[$name]);
+ }
+ return clone self::$instances[$class][$name];
+ }
+
+ /**
+ * Specify data which should be serialized to JSON. This method returns data that can be serialized by json_encode()
+ * natively.
+ *
+ * @return mixed
+ * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return $this->getValue();
+ }
+}
diff --git a/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php b/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php
new file mode 100644
index 0000000..7c65e4e
--- /dev/null
+++ b/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php
@@ -0,0 +1,54 @@
+register(new \MyCLabs\Enum\PHPUnit\Comparator());
+ */
+final class Comparator extends \SebastianBergmann\Comparator\Comparator
+{
+ public function accepts($expected, $actual)
+ {
+ return $expected instanceof Enum && (
+ $actual instanceof Enum || $actual === null
+ );
+ }
+
+ /**
+ * @param Enum $expected
+ * @param Enum|null $actual
+ *
+ * @return void
+ */
+ public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
+ {
+ if ($expected->equals($actual)) {
+ return;
+ }
+
+ throw new ComparisonFailure(
+ $expected,
+ $actual,
+ $this->formatEnum($expected),
+ $this->formatEnum($actual),
+ false,
+ 'Failed asserting that two Enums are equal.'
+ );
+ }
+
+ private function formatEnum(?Enum $enum = null)
+ {
+ if ($enum === null) {
+ return "null";
+ }
+
+ return get_class($enum)."::{$enum->getKey()}()";
+ }
+}
diff --git a/vendor/myclabs/php-enum/stubs/Stringable.php b/vendor/myclabs/php-enum/stubs/Stringable.php
new file mode 100644
index 0000000..4811af7
--- /dev/null
+++ b/vendor/myclabs/php-enum/stubs/Stringable.php
@@ -0,0 +1,11 @@
+ phpd.log 2>&1 &
+ echo $! > mock-server.pid
+
+ cd tests/socks5-server/
+ nohup go run main.go > ../../socks5.log 2>&1 &
+ echo $! > ../../socks-server.pid
+
+ - name: Setup php
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-versions }}
+
+ - name: Install dependencies
+ run: |
+ composer self-update
+ composer install --no-interaction --prefer-source --dev
+
+ - name: Run cases
+ run: |
+ ./vendor/bin/phpcs --standard=PSR2 src
+ ./vendor/bin/phpcs --standard=PSR2 examples
+ ./vendor/bin/phpcs --standard=PSR2 tests
+ ./vendor/bin/phpunit --coverage-clover=coverage.xml
+ cat mock-server.pid | xargs kill
+ cat socks-server.pid | xargs kill
+
+ env:
+ QINIU_ACCESS_KEY: ${{ secrets.QINIU_ACCESS_KEY }}
+ QINIU_SECRET_KEY: ${{ secrets.QINIU_SECRET_KEY }}
+ QINIU_TEST_BUCKET: ${{ secrets.QINIU_TEST_BUCKET }}
+ QINIU_TEST_DOMAIN: ${{ secrets.QINIU_TEST_DOMAIN }}
+
+ - name: Print mock server log
+ if: ${{ failure() }}
+ run: |
+ cat phpd.log
+
+ - name: Print socks5 server log
+ if: ${{ failure() }}
+ run: |
+ cat socks5.log
+
+ - name: After_success
+ run: bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/qiniu/php-sdk/.github/workflows/version-check.yml b/vendor/qiniu/php-sdk/.github/workflows/version-check.yml
new file mode 100644
index 0000000..983a98f
--- /dev/null
+++ b/vendor/qiniu/php-sdk/.github/workflows/version-check.yml
@@ -0,0 +1,19 @@
+name: PHP SDK Version Check
+on:
+ push:
+ tags:
+ - "v[0-9]+.[0-9]+.[0-9]+"
+jobs:
+ linux:
+ name: Version Check
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+ - name: Set env
+ run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV
+ - name: Check
+ run: |
+ set -e
+ grep -qF "## ${RELEASE_VERSION}" CHANGELOG.md
+ grep -qF "const SDK_VER = '${RELEASE_VERSION}';" src/Qiniu/Config.php
diff --git a/vendor/qiniu/php-sdk/.gitignore b/vendor/qiniu/php-sdk/.gitignore
new file mode 100644
index 0000000..4c842c8
--- /dev/null
+++ b/vendor/qiniu/php-sdk/.gitignore
@@ -0,0 +1,12 @@
+*.phar
+*.zip
+build/artifacts
+phpunit.xml
+phpunit.functional.xml
+.DS_Store
+.swp
+.build
+composer.lock
+vendor
+src/package.xml
+.idea/
diff --git a/vendor/qiniu/php-sdk/.scrutinizer.yml b/vendor/qiniu/php-sdk/.scrutinizer.yml
new file mode 100644
index 0000000..6a2d0d8
--- /dev/null
+++ b/vendor/qiniu/php-sdk/.scrutinizer.yml
@@ -0,0 +1,42 @@
+filter:
+ excluded_paths: [tests/*]
+checks:
+ php:
+ code_rating: true
+ remove_extra_empty_lines: true
+ remove_php_closing_tag: true
+ remove_trailing_whitespace: true
+ fix_use_statements:
+ remove_unused: true
+ preserve_multiple: false
+ preserve_blanklines: true
+ order_alphabetically: true
+ fix_php_opening_tag: true
+ fix_linefeed: true
+ fix_line_ending: true
+ fix_identation_4spaces: true
+ fix_doc_comments: true
+tools:
+ external_code_coverage:
+ timeout: 1200
+ runs: 3
+ php_analyzer: true
+ php_code_coverage: false
+ php_code_sniffer:
+ config:
+ standard: PSR2
+ filter:
+ paths: ['src']
+ php_loc:
+ enabled: true
+ excluded_dirs: [vendor, tests]
+ php_cpd:
+ enabled: true
+ excluded_dirs: [vendor, tests]
+build:
+ nodes:
+ analysis:
+ tests:
+ override:
+ - php-scrutinizer-run
+
diff --git a/vendor/qiniu/php-sdk/CHANGELOG.md b/vendor/qiniu/php-sdk/CHANGELOG.md
new file mode 100644
index 0000000..6f8da5d
--- /dev/null
+++ b/vendor/qiniu/php-sdk/CHANGELOG.md
@@ -0,0 +1,196 @@
+# Changelog
+
+## 7.14.0 (2024-10-16)
+* 对象存储,持久化处理支持工作流模版
+
+## 7.13.0 (2024-09-05)
+* 对象存储,验证回调方法新增支持 Qiniu 签名
+* 对象存储,调整查询空间区域域名顺序与默认空间管理域名
+* 支持闲时任务配置
+
+## 7.12.1 (2024-02-21)
+* 对象存储,添加上传策略部分字段
+
+## 7.12.0 (2023-12-11)
+* 对象存储,支持归档直读存储
+* 对象存储,批量操作支持自动查询 rs 服务域名
+
+## 7.11.0 (2023-09-05)
+* 支持代理
+
+## 7.10.1 (2023-08-04)
+* 修复部分 API 调用中间件合并失败(#417)
+
+## 7.10.0 (2023-06-20)
+* 对象存储,新增请求中间件逻辑,方便拓展请求逻辑
+* 对象存储,新增备用 UC 域名用于查询区域域名
+* 对象存储,修复分片上传初始化失败无法快速失败
+* 对象存储,移除首尔区域
+
+## 7.9.0 (2023-03-31)
+* 对象存储,修复无法对 key 为空字符串的对象进行操作
+* 修复 301 重定向无法正确获取 header 信息
+* 对象存储,新增查询区域域名过期时间
+* 对象存储,更新获取区域域名的接口
+* 对象存储,更新查询 bucket 域名为 uc 服务
+* 对象存储,新增 uc 服务可配置
+
+## 7.8.0 (2022-10-25)
+* 移除不推荐域名,并增加区域亚太-首尔和华东-浙江2
+* 对象存储,修复断点上传的文件内容不正确
+* 对象存储,优化分片上传 ctx 超时检测
+
+## 7.7.0 (2022-09-02)
+* 对象存储,新增支持设置文件级别生命周期 setObjectLifecycle API
+* 对象存储,内置增加七牛新建存储区域域名信息
+* 修复当前已知问题
+
+## 7.6.0 (2022-06-08)
+* 对象存储,管理类 API 发送请求时增加 [X-Qiniu-Date](https://developer.qiniu.com/kodo/3924/common-request-headers) (生成请求的时间) header
+
+
+## 7.5.0 (2022-04-18)
+* 对象存储,新增支持 [深度归档存储类型](https://developer.qiniu.com/kodo/3956/kodo-category#deep_archive)
+
+## 7.4.3 (2022-04-01)
+* 优化签名算法逻辑
+
+## 7.4.2(2022-03-01)
+* 修复已知关于请求 Header 处理不当问题,比如没有处理为大小写不敏感等问题
+
+## 7.4.1(2021-09-24)
+* 修复了 分片上传 v2 已知问题,明确给出了参数不合理情况下对应的错误提示信息
+
+## 7.4.0 (2021-07-19)
+* 【对象存储】支持 [分片上传 v2](https://developer.qiniu.com/kodo/7458/multipartupload) 和 断点续传,使用方式见 [开发者文档](https://developer.qiniu.com/kodo/1241/php#resume-upload-file)
+
+## 7.3.0 (2020-09-24)
+### 新增
+* 【对象存储】增加异步抓取方法与demo
+* 【融合cdn】增加查询CDN刷新记录、查询CDN预取记录方法与demo
+* 【云短信】增加查询短信发送记录的方法
+* 【实时音视频】增加rtc停止房间的合流转推方法
+* 【内容审核】增加图片审核、视频审核方法与demo
+
+### 修复
+* 【对象存储】修复签算 token 时上传策略中的 forceSaveKey 字段不生效的问题
+* 【对象存储】修复更新空间事件通知规则方法
+
+### 优化
+* 【对象存储】创建空间迁移到mkbucketv3 api
+* 优化对 http2 返回头的判断
+* 优化 demo 中的文档注释说明
+* docs 目录下的 rtc demo 移动至 examples/rtc 目录下
+* docs 目录下的 sms demo 移动至 examples/sms 目录下
+
+## 7.2.10 (2019-10-28)
+* 去除云短信类类型指定
+* 修改不传文件名时存在表单上传错误的情况
+
+## 7.2.9 (2019-07-09)
+* 添加空间管理、云短信接口
+* 去除无效参数
+
+## 7.2.7 (2018-11-06)
+* 添加 QVM 内网上传到 KODO 的 zone 设置
+
+## 7.2.6 (2018-05-18)
+* 修复rs,rsf在不同机房默认的https域名
+
+## 7.2.5 (2018-05-10)
+* 修复表单上传中多余的参数checkCrc导致的fname错位问题
+
+## 7.2.4 (2018-05-09)
+### 增加
+* 连麦功能
+
+## 7.2.3 (2018-01-20)
+### 增加
+* 新加坡机房
+### 修正
+* 获取域名的入口域名
+* http回复头部兼容大小写
+
+## 7.2.2 (2017-11-06)
+### 增加
+* Qiniu算法的鉴权方法
+
+## 7.1.4 (2017-06-21)
+### 增加
+* cdn 文件/目录 刷新
+* cdn 获取 流量/带宽
+* cdn 获取域名的访问日志列表
+* cdn 对资源链接进行时间戳防盗链签名
+
+## 7.1.3 (2016-11-18)
+### 增加
+* move, copy操作增加force参数
+
+## 7.1.2 (2016-11-12)
+### 修正
+* 明确抛出获取各区域域名失败时的报错
+
+## 7.1.1 (2016-11-02)
+### 修正
+* 多区域配置文件存储目录从home修改到tmp目录
+
+
+## 7.1.0 (2016-10-22)
+### 增加
+* 多存储区域的支持
+
+## 7.0.8 (2016-07-19)
+### 增加
+* demo
+* https url 支持
+* deleteAfterDays 策略
+* 添加图片处理链接统一拼接方法 by @SherlockRen
+
+## 7.0.7 (2016-01-12)
+### 修正
+* PersistentFop参数pipeline和notify_url失效
+* resume 模式 close file inputstream
+
+## 7.0.6 (2015-12-05)
+### 修正
+* php7.0 Json 对空字符串解析单元测试报错
+* 开启安全模式或者设置可操作目录树时,设置CURLOPT_FOLLOWLOCATION报错, by @twocabbages
+* fetch 支持不指定key, by @sinkcup
+
+## 7.0.5 (2015-10-29)
+### 增加
+* 增加上传策略最小文件大小限制 fsizeMin
+* 增加常见examples
+
+## 7.0.4 (2015-07-23)
+### 修正
+* 一些地方的严格比较检查
+* resumeupload 备用地址失效
+
+## 7.0.3 (2015-07-10)
+### 修改
+* 多zone 支持
+
+## 7.0.2 (2015-04-18)
+### 修改
+* fetch 接口返回内容调整
+* pfop 接口调整
+
+###修正
+* exception 类调用
+
+## 7.0.1 (2015-03-27)
+### 增加
+* 增加代码注释
+
+## 7.0.0 (2015-02-03)
+
+### 增加
+* 简化上传接口
+* 自动选择断点续上传还是直传
+* 重构代码,接口和内部结构更清晰
+* 改变mime
+* 代码覆盖度报告
+* policy改为array, 便于灵活增加,并加入过期字段检查
+* 文件列表支持目录形式
+* 利用元编程方式支持 fop 和 pfop
diff --git a/vendor/qiniu/php-sdk/CONTRIBUTING.md b/vendor/qiniu/php-sdk/CONTRIBUTING.md
new file mode 100644
index 0000000..0466bf9
--- /dev/null
+++ b/vendor/qiniu/php-sdk/CONTRIBUTING.md
@@ -0,0 +1,30 @@
+# 贡献代码指南
+
+我们非常欢迎大家来贡献代码,我们会向贡献者致以最诚挚的敬意。
+
+一般可以通过在Github上提交[Pull Request](https://github.com/qiniu/php-sdk)来贡献代码。
+
+## Pull Request要求
+
+- **[PSR-2 编码风格标准](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** 。要通过项目中的code sniffer检查。
+
+- **代码格式** 提交前 请按 ./vendor/bin/phpcbf --standard=PSR2 进行格式化。
+
+- **必须添加测试!** - 如果没有测试(单元测试、集成测试都可以),那么提交的补丁是不会通过的。
+
+- **记得更新文档** - 保证`README.md`以及其他相关文档及时更新,和代码的变更保持一致性。
+
+- **考虑我们的发布周期** - 我们的版本号会服从[SemVer v2.0.0](http://semver.org/),我们绝对不会随意变更对外的API。
+
+- **创建feature分支** - 最好不要从你的master分支提交 pull request。
+
+- **一个feature提交一个pull请求** - 如果你的代码变更了多个操作,那就提交多个pull请求吧。
+
+- **清晰的commit历史** - 保证你的pull请求的每次commit操作都是有意义的。如果你开发中需要执行多次的即时commit操作,那么请把它们放到一起再提交pull请求。
+
+## 运行测试
+
+``` bash
+./vendor/bin/phpunit tests/Qiniu/Tests/
+
+```
diff --git a/vendor/qiniu/php-sdk/LICENSE b/vendor/qiniu/php-sdk/LICENSE
new file mode 100644
index 0000000..ba646be
--- /dev/null
+++ b/vendor/qiniu/php-sdk/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Qiniu, Ltd.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/qiniu/php-sdk/README.md b/vendor/qiniu/php-sdk/README.md
new file mode 100644
index 0000000..784d735
--- /dev/null
+++ b/vendor/qiniu/php-sdk/README.md
@@ -0,0 +1,76 @@
+# Qiniu Cloud SDK for PHP
+[](LICENSE)
+[](https://travis-ci.org/qiniu/php-sdk)
+[](https://github.com/qiniu/php-sdk/releases)
+[](https://packagist.org/packages/qiniu/php-sdk)
+[](https://packagist.org/packages/qiniu/php-sdk)
+[](https://scrutinizer-ci.com/g/qiniu/php-sdk/?branch=master)
+[](https://codecov.io/gh/qiniu/php-sdk)
+[](https://gitter.im/qiniu/php-sdk?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[](http://weibo.com/qiniutek)
+
+
+## 安装
+
+推荐使用 `composer` 进行安装。可以使用 composer.json 声明依赖,或者运行下面的命令。SDK 包已经放到这里 [`qiniu/php-sdk`][install-packagist] 。
+
+```bash
+$ composer require qiniu/php-sdk
+```
+
+## 运行环境
+
+| Qiniu SDK版本 | PHP 版本 |
+|:--------------------:|:-----------------------------------------------:|
+| 7.x | cURL extension, 5.3 - 5.6, 7.0 - 7.4, 8.0-8.1 |
+| 6.x | cURL extension, 5.2 - 5.6 |
+
+## 使用方法
+
+### 上传
+```php
+use Qiniu\Storage\UploadManager;
+use Qiniu\Auth;
+...
+ $uploadMgr = new UploadManager();
+ $auth = new Auth($accessKey, $secretKey);
+ $token = $auth->uploadToken($bucket);
+ list($ret, $error) = $uploadMgr->putFile($token, 'key', 'filePath');
+...
+```
+
+## 测试
+
+``` bash
+$ ./vendor/bin/phpunit tests/Qiniu/Tests/
+```
+
+## 常见问题
+
+- `$error` 保留了请求响应的信息,失败情况下 `ret` 为 `none`, 将 `$error` 可以打印出来,提交给我们。
+- API 的使用 demo 可以参考 [examples](https://github.com/qiniu/php-sdk/tree/master/examples)。
+
+## 代码贡献
+
+详情参考[代码提交指南](https://github.com/qiniu/php-sdk/blob/master/CONTRIBUTING.md)。
+
+## 贡献记录
+
+- [所有贡献者](https://github.com/qiniu/php-sdk/contributors)
+
+## 联系我们
+
+- 如果需要帮助,请提交工单(在portal右侧点击咨询和建议提交工单,或者直接向 support@qiniu.com 发送邮件)
+- 如果有什么问题,可以到问答社区提问,[问答社区](https://qiniu.segmentfault.com/)
+- 更详细的文档,见[官方文档站](https://developer.qiniu.com/)
+- 如果发现了 bug, 欢迎提交 [issue](https://github.com/qiniu/php-sdk/issues)
+- 如果有功能需求,欢迎提交 [issue](https://github.com/qiniu/php-sdk/issues)
+- 如果要提交代码,欢迎提交 pull request
+- 欢迎关注我们的[微信](https://www.qiniu.com/#weixin) [微博](https://weibo.com/qiniutek),及时获取动态信息。
+
+## 代码许可
+
+The MIT License (MIT).详情见 [License文件](https://github.com/qiniu/php-sdk/blob/master/LICENSE).
+
+[packagist]: http://packagist.org
+[install-packagist]: https://packagist.org/packages/qiniu/php-sdk
diff --git a/vendor/qiniu/php-sdk/autoload.php b/vendor/qiniu/php-sdk/autoload.php
new file mode 100644
index 0000000..9efddd7
--- /dev/null
+++ b/vendor/qiniu/php-sdk/autoload.php
@@ -0,0 +1,19 @@
+=5.3.3",
+ "ext-xml": "*",
+ "ext-curl": "*",
+ "myclabs/php-enum": "~1.5.2 || ~1.6.6 || ~1.7.7 || ~1.8.4"
+ },
+ "require-dev": {
+ "paragonie/random_compat": ">=2",
+ "phpunit/phpunit": "^4.8 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4",
+ "squizlabs/php_codesniffer": "^2.3 || ~3.6"
+ },
+ "autoload": {
+ "psr-4": {
+ "Qiniu\\": "src/Qiniu"
+ },
+ "files": [
+ "src/Qiniu/functions.php",
+ "src/Qiniu/Http/Middleware/Middleware.php"
+ ]
+ }
+}
diff --git a/vendor/qiniu/php-sdk/examples/README.md b/vendor/qiniu/php-sdk/examples/README.md
new file mode 100644
index 0000000..b7b4f98
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/README.md
@@ -0,0 +1,10 @@
+# examples
+
+这些 examples 旨在帮助你快速了解使用七牛的 SDK。这些 demo 都是可以直接运行的, 但是在运行之前需要填上您自己的参数。
+
+比如:
+
+* `$bucket` 需要填上您想操作的 [bucket名字](https://portal.qiniu.com/kodo/bucket)。
+* `$accessKey` 和 `$secretKey` 可以在我们的[管理后台](https://portal.qiniu.com/user/key)找到。
+* 在进行`视频转码`, `压缩文件`等异步操作时 需要使用到的队列名称也可以在我们[管理后台](https://portal.qiniu.com/dora/media-gate/pipeline)新建。
+
diff --git a/vendor/qiniu/php-sdk/examples/bucket_lifecycleRule.php b/vendor/qiniu/php-sdk/examples/bucket_lifecycleRule.php
new file mode 100644
index 0000000..f51524c
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/bucket_lifecycleRule.php
@@ -0,0 +1,42 @@
+bucketLifecycleRule(
+ $bucket,
+ $name,
+ $prefix,
+ $delete_after_days,
+ $to_line_after_days,
+ $to_archive_after_days,
+ $to_deep_archive_after_days,
+ $to_archive_ir_after_days
+);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/cdn_get_bandwidth.php b/vendor/qiniu/php-sdk/examples/cdn_get_bandwidth.php
new file mode 100644
index 0000000..c9de0e6
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/cdn_get_bandwidth.php
@@ -0,0 +1,41 @@
+getBandwidthData(
+ $domains,
+ $startDate,
+ $endDate,
+ $granularity
+);
+
+if ($getBandwidthErr != null) {
+ var_dump($getBandwidthErr);
+} else {
+ echo "get bandwidth data success\n";
+ print_r($bandwidthData);
+}
diff --git a/vendor/qiniu/php-sdk/examples/cdn_get_flux.php b/vendor/qiniu/php-sdk/examples/cdn_get_flux.php
new file mode 100644
index 0000000..57df808
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/cdn_get_flux.php
@@ -0,0 +1,35 @@
+getFluxData($domains, $startDate, $endDate, $granularity);
+if ($getFluxErr != null) {
+ var_dump($getFluxErr);
+} else {
+ echo "get flux data success\n";
+ print_r($fluxData);
+}
diff --git a/vendor/qiniu/php-sdk/examples/cdn_get_log_list.php b/vendor/qiniu/php-sdk/examples/cdn_get_log_list.php
new file mode 100644
index 0000000..2b3f7dd
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/cdn_get_log_list.php
@@ -0,0 +1,31 @@
+getCdnLogList($domains, $logDate);
+if ($getLogErr != null) {
+ var_dump($getLogErr);
+} else {
+ echo "get cdn log list success\n";
+ print_r($logListData);
+}
diff --git a/vendor/qiniu/php-sdk/examples/cdn_get_prefetch_list.php b/vendor/qiniu/php-sdk/examples/cdn_get_prefetch_list.php
new file mode 100644
index 0000000..958e5eb
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/cdn_get_prefetch_list.php
@@ -0,0 +1,46 @@
+getCdnPrefetchList(
+ $requestId,
+ $urls,
+ $state,
+ $pageNo,
+ $pageSize,
+ $startTime,
+ $endTime
+);
+echo "\n====> query prefetch list: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/cdn_get_refresh_list.php b/vendor/qiniu/php-sdk/examples/cdn_get_refresh_list.php
new file mode 100644
index 0000000..ad4fca2
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/cdn_get_refresh_list.php
@@ -0,0 +1,48 @@
+getCdnRefreshList(
+ $requestId,
+ $isDir,
+ $urls,
+ $state,
+ $pageNo,
+ $pageSize,
+ $startTime,
+ $endTime
+);
+echo "\n====> query refresh list: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/cdn_refresh_urls_dirs.php b/vendor/qiniu/php-sdk/examples/cdn_refresh_urls_dirs.php
new file mode 100644
index 0000000..2140378
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/cdn_refresh_urls_dirs.php
@@ -0,0 +1,59 @@
+refreshUrlsAndDirs($urls, $dirs);
+if ($refreshErr != null) {
+ var_dump($refreshErr);
+} else {
+ echo "refresh request sent\n";
+ print_r($refreshResult);
+}
+
+//---------------------------------------- demo2 ----------------------------------------
+// 刷新文件
+
+list($refreshResult, $refreshErr) = $cdnManager->refreshUrls($urls);
+if ($refreshErr != null) {
+ var_dump($refreshErr);
+} else {
+ echo "refresh urls request sent\n";
+ print_r($refreshResult);
+}
+
+//---------------------------------------- demo3 ----------------------------------------
+// 刷新目录
+
+list($refreshResult, $refreshErr) = $cdnManager->refreshDirs($dirs);
+if ($refreshErr != null) {
+ var_dump($refreshErr);
+} else {
+ echo "refresh dirs request sent\n";
+ print_r($refreshResult);
+}
diff --git a/vendor/qiniu/php-sdk/examples/cdn_timestamp_antileech.php b/vendor/qiniu/php-sdk/examples/cdn_timestamp_antileech.php
new file mode 100644
index 0000000..f2d7855
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/cdn_timestamp_antileech.php
@@ -0,0 +1,20 @@
+censorImage($body);
+echo "\n====> Result is: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/censor_video.php b/vendor/qiniu/php-sdk/examples/censor_video.php
new file mode 100644
index 0000000..7ac056f
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/censor_video.php
@@ -0,0 +1,52 @@
+censorVideo($body);
+echo "\n====> Result is: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "job_id is: $jobid\n";
+}
+
+// 查询视频审核结果
+list($ret, $err) = $argusManager->censorStatus($jobid);
+echo "\n====> job status: \n";
+
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/delete_bucket.php b/vendor/qiniu/php-sdk/examples/delete_bucket.php
new file mode 100644
index 0000000..325a47a
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/delete_bucket.php
@@ -0,0 +1,27 @@
+deleteBucket($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/delete_bucketEvent.php b/vendor/qiniu/php-sdk/examples/delete_bucketEvent.php
new file mode 100644
index 0000000..7eb744d
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/delete_bucketEvent.php
@@ -0,0 +1,28 @@
+deleteBucketEvent($bucket, $name);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/delete_bucketLifecycleRule.php b/vendor/qiniu/php-sdk/examples/delete_bucketLifecycleRule.php
new file mode 100644
index 0000000..2146b1b
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/delete_bucketLifecycleRule.php
@@ -0,0 +1,27 @@
+deleteBucketLifecycleRule($bucket, $name);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_bucketEvents.php b/vendor/qiniu/php-sdk/examples/get_bucketEvents.php
new file mode 100644
index 0000000..2379584
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_bucketEvents.php
@@ -0,0 +1,26 @@
+getBucketEvents($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_bucketLifecycleRules.php b/vendor/qiniu/php-sdk/examples/get_bucketLifecycleRules.php
new file mode 100644
index 0000000..a35feed
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_bucketLifecycleRules.php
@@ -0,0 +1,26 @@
+getBucketLifecycleRules($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_bucketList.php b/vendor/qiniu/php-sdk/examples/get_bucketList.php
new file mode 100644
index 0000000..6a2f7b0
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_bucketList.php
@@ -0,0 +1,26 @@
+listbuckets($region);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_bucketQuota.php b/vendor/qiniu/php-sdk/examples/get_bucketQuota.php
new file mode 100644
index 0000000..93474b5
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_bucketQuota.php
@@ -0,0 +1,26 @@
+getBucketQuota($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_bucketinfo.php b/vendor/qiniu/php-sdk/examples/get_bucketinfo.php
new file mode 100644
index 0000000..98fd9f7
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_bucketinfo.php
@@ -0,0 +1,25 @@
+bucketInfo($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_bucketinfos.php b/vendor/qiniu/php-sdk/examples/get_bucketinfos.php
new file mode 100644
index 0000000..5eec1d8
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_bucketinfos.php
@@ -0,0 +1,26 @@
+bucketInfos($region);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/get_corsRules.php b/vendor/qiniu/php-sdk/examples/get_corsRules.php
new file mode 100644
index 0000000..58e28be
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/get_corsRules.php
@@ -0,0 +1,26 @@
+getCorsRules($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/image_url_builder.php b/vendor/qiniu/php-sdk/examples/image_url_builder.php
new file mode 100644
index 0000000..20e2b00
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/image_url_builder.php
@@ -0,0 +1,74 @@
+
+ */
+$thumbLink = $imageUrlBuilder->thumbnail($url, 1, 100, 100);
+
+// 函数方式调用 也可拼接多个操作参数 图片+水印
+$thumbLink2 = \Qiniu\thumbnail($url2, 1, 100, 100);
+var_dump($thumbLink, $thumbLink2);
+
+/**
+ * 图片水印
+ *
+ * @param string $url 图片链接
+ * @param string $image 水印图片链接
+ * @param int $dissolve 透明度 [可选]
+ * @param string $gravity 水印位置 [可选]
+ * @param int $dx 横轴边距 [可选]
+ * @param int $dy 纵轴边距 [可选]
+ * @param int $watermarkScale 自适应原图的短边比例 [可选]
+ * @link https://developer.qiniu.com/dora/api/1316/image-watermarking-processing-watermark
+ * @return string
+ * @author Sherlock Ren
+ */
+$waterLink = $imageUrlBuilder->waterImg($url, $waterImage);
+// 函数调用方法
+//$waterLink = \Qiniu\waterImg($url, $waterImage);
+var_dump($waterLink);
+
+/**
+ * 文字水印
+ *
+ * @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 纵轴边距 [可选]
+ * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html#text-watermark
+ * @return string
+ * @author Sherlock Ren
+ */
+$textLink = $imageUrlBuilder->waterText($url, '你瞅啥', '微软雅黑', 300);
+// 函数调用方法
+// $textLink = \Qiniu\waterText($url, '你瞅啥', '微软雅黑', 300);
+var_dump($textLink);
diff --git a/vendor/qiniu/php-sdk/examples/persistent_fop_init.php b/vendor/qiniu/php-sdk/examples/persistent_fop_init.php
new file mode 100644
index 0000000..baca846
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/persistent_fop_init.php
@@ -0,0 +1,18 @@
+useHTTPS=true;
+
+// 初始化
+$pfop = new PersistentFop($auth, $config);
diff --git a/vendor/qiniu/php-sdk/examples/persistent_fop_status.php b/vendor/qiniu/php-sdk/examples/persistent_fop_status.php
new file mode 100644
index 0000000..73e85a3
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/persistent_fop_status.php
@@ -0,0 +1,19 @@
+status($persistentId);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/pfop_mkzip.php b/vendor/qiniu/php-sdk/examples/pfop_mkzip.php
new file mode 100644
index 0000000..fb95cc2
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/pfop_mkzip.php
@@ -0,0 +1,58 @@
+execute($bucket, $key, $fops, $pipeline, $notify_url, $force);
+
+echo "\n====> pfop mkzip result: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ echo "PersistentFop Id: $id\n";
+}
+
+// 查询转码的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop mkzip status: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/pfop_vframe.php b/vendor/qiniu/php-sdk/examples/pfop_vframe.php
new file mode 100644
index 0000000..49fd36d
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/pfop_vframe.php
@@ -0,0 +1,55 @@
+useHTTPS = true;
+$pfop = new PersistentFop($auth, $config);
+
+// 视频处理完毕后保存到空间中的名称
+$saveasKey = 'qiniu_480x360.jpg';
+
+// 进行视频截帧操作
+$fops = "vframe/jpg/offset/1/w/480/h/360/rotate/90|saveas/" .
+ \Qiniu\base64_urlSafeEncode("$bucket:$saveasKey");
+
+list($id, $err) = $pfop->execute($bucket, $key, $fops, $pipeline, $notifyUrl, $force);
+echo "\n====> pfop avthumb result: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ echo "PersistentFop Id: $id\n";
+}
+
+// 查询转码的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop avthumb status: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/pfop_video_avthumb.php b/vendor/qiniu/php-sdk/examples/pfop_video_avthumb.php
new file mode 100644
index 0000000..986aa8c
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/pfop_video_avthumb.php
@@ -0,0 +1,55 @@
+useHTTPS=true;
+
+// 视频处理完毕后保存到空间中的名称
+$saveasKey = 'qiniu_640x360.mp4';
+
+$pfop = new PersistentFop($auth, $config);
+
+// 进行视频转码操作
+$fops = "avthumb/mp4/s/640x360/vb/1.4m|saveas/" . \Qiniu\base64_urlSafeEncode("$bucket:$saveasKey");
+
+list($id, $err) = $pfop->execute($bucket, $key, $fops, $pipeline, $notifyUrl, $force);
+echo "\n====> pfop avthumb result: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ echo "PersistentFop Id: $id\n";
+}
+
+// 查询转码的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop avthumb status: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/pfop_watermark.php b/vendor/qiniu/php-sdk/examples/pfop_watermark.php
new file mode 100644
index 0000000..ea3d6bc
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/pfop_watermark.php
@@ -0,0 +1,59 @@
+useHTTPS=true;
+$pfop = new PersistentFop($auth, $config);
+
+// 图片水印的源路径,也就是给视频打图片水印的图片
+$base64URL = Qiniu\base64_urlSafeEncode('http://test-2.qiniudn.com/logo.png');
+
+// 视频处理完毕后保存到空间中的名称
+$saveasKey = 'qiniu_watermark.mp4';
+
+// 进行视频打图片水印操作
+$fops = "avthumb/mp4/wmImage/" . $base64URL . "|saveas/"
+ . \Qiniu\base64_urlSafeEncode("$bucket:$saveasKey");
+
+list($id, $err) = $pfop->execute($bucket, $key, $fops, $pipeline, $notifyUrl, $force);
+echo "\n====> pfop avthumb result: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ echo "PersistentFop Id: $id\n";
+}
+
+// 查询转码的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop avthumb status: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/php-logo.png b/vendor/qiniu/php-sdk/examples/php-logo.png
new file mode 100644
index 0000000..77e051f
Binary files /dev/null and b/vendor/qiniu/php-sdk/examples/php-logo.png differ
diff --git a/vendor/qiniu/php-sdk/examples/prefop.php b/vendor/qiniu/php-sdk/examples/prefop.php
new file mode 100644
index 0000000..1b8950a
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/prefop.php
@@ -0,0 +1,27 @@
+useHTTPS=true;
+
+$pfop = new PersistentFop($auth, $config);
+
+$id = "z2.01z201c4oyre6q1hgy00murnel0002nh";
+
+// 查询持久化处理的进度和状态
+list($ret, $err) = $pfop->status($id);
+echo "\n====> pfop avthumb status: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/put_bucketAccessMode.php b/vendor/qiniu/php-sdk/examples/put_bucketAccessMode.php
new file mode 100644
index 0000000..638ae3c
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/put_bucketAccessMode.php
@@ -0,0 +1,27 @@
+putBucketAccessMode($bucket, $private);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/put_bucketAccessStyleMode.php b/vendor/qiniu/php-sdk/examples/put_bucketAccessStyleMode.php
new file mode 100644
index 0000000..3cc2aec
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/put_bucketAccessStyleMode.php
@@ -0,0 +1,27 @@
+putBucketAccessStyleMode($bucket, $mode);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/put_bucketEvent.php b/vendor/qiniu/php-sdk/examples/put_bucketEvent.php
new file mode 100644
index 0000000..f3c830d
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/put_bucketEvent.php
@@ -0,0 +1,32 @@
+putBucketEvent($bucket, $name, $prefix, $suffix, $event, $callbackURL);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/put_bucketMaxAge.php b/vendor/qiniu/php-sdk/examples/put_bucketMaxAge.php
new file mode 100644
index 0000000..4890174
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/put_bucketMaxAge.php
@@ -0,0 +1,27 @@
+putBucketMaxAge($bucket, $maxAge);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/put_bucketQuota.php b/vendor/qiniu/php-sdk/examples/put_bucketQuota.php
new file mode 100644
index 0000000..b00ec48
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/put_bucketQuota.php
@@ -0,0 +1,29 @@
+putBucketQuota($bucket, $size, $count);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/put_referAntiLeech.php b/vendor/qiniu/php-sdk/examples/put_referAntiLeech.php
new file mode 100644
index 0000000..7d56d1e
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/put_referAntiLeech.php
@@ -0,0 +1,30 @@
+putReferAntiLeech($bucket, $mode, $norefer, $pattern);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/qetag.php b/vendor/qiniu/php-sdk/examples/qetag.php
new file mode 100644
index 0000000..1fe90d1
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/qetag.php
@@ -0,0 +1,14 @@
+useHTTPS = true; // 接口是否使用 HTTPS 协议
+
+$bucketManager = new BucketManager($auth, $config);
+
+// 异步第三方资源抓取
+// 参考文档:https://developer.qiniu.com/kodo/api/4097/asynch-fetch
+
+// 需要抓取的文件 URL
+$url = 'http://devtools.qiniu.com/qiniu.png';
+
+//回调 URL(需要可以公网访问,并能够相应 200 OK)
+$callbackurl = "http://your.domain.com/upload_verify_callback.php";
+
+// 回调Body
+$callbackbody = '{"key":"$(key)","hash":"$(etag)","w":"$(imageInfo.width)","h":"$(imageInfo.height)"}';
+
+
+//---------------------------------------- demo1 ----------------------------------------
+// 指定抓取的文件保存到七牛云空间中的名称
+
+$key = time() . '.png';
+list($ret, $err) = $bucketManager->asynchFetch($url, $bucket, null, $key, null, null, $callbackurl, $callbackbody);
+echo "=====> asynch fetch $url to bucket: $bucket key: $key\n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ $id = $ret['id'];
+ echo "id is: $id\n";
+}
+
+//---------------------------------------- demo2 ----------------------------------------
+// 不指定 key 时,以文件内容的 hash 作为文件名
+
+$key = null;
+list($ret, $err) = $bucketManager->asynchFetch($url, $bucket, null, $key, null, null, $callbackurl, $callbackbody);
+echo "=====> asynch fetch $url to bucket: $bucket key: $(etag)\n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ $id = $ret['id'];
+ echo "id is: $id\n";
+}
+
+// 查询异步抓取的进度和状态
+
+// 华东:z0,华北:z1,华南:z2,北美:na0,东南亚:as0
+$zone = 'z2';
+
+sleep(10); // 由于异步抓取需要耗时,等待 10 秒后再查询状态
+list($ret, $err) = $bucketManager->asynchFetchStatus($zone, $id);
+echo "\n====> asynch fetch status: \n";
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_change_mime.php b/vendor/qiniu/php-sdk/examples/rs_batch_change_mime.php
new file mode 100644
index 0000000..c5bd6b4
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_change_mime.php
@@ -0,0 +1,32 @@
+ 'video/x-mp4',
+ 'qiniu.png' => 'image/x-png',
+ 'qiniu.jpg' => 'image/x-jpg'
+);
+
+$ops = $bucketManager->buildBatchChangeMime($bucket, $keyMimePairs);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_change_type.php b/vendor/qiniu/php-sdk/examples/rs_batch_change_type.php
new file mode 100644
index 0000000..a19d0d4
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_change_type.php
@@ -0,0 +1,45 @@
+batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_copy.php b/vendor/qiniu/php-sdk/examples/rs_batch_copy.php
new file mode 100644
index 0000000..66c4d4d
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_copy.php
@@ -0,0 +1,40 @@
+buildBatchCopy($srcBucket, $keyPairs, $destBucket, true);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_delete.php b/vendor/qiniu/php-sdk/examples/rs_batch_delete.php
new file mode 100644
index 0000000..ebcdbe6
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_delete.php
@@ -0,0 +1,32 @@
+buildBatchDelete($bucket, $keys);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_delete_after_days.php b/vendor/qiniu/php-sdk/examples/rs_batch_delete_after_days.php
new file mode 100644
index 0000000..928dd14
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_delete_after_days.php
@@ -0,0 +1,39 @@
+buildBatchDeleteAfterDays($bucket, $keyDayPairs);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_move.php b/vendor/qiniu/php-sdk/examples/rs_batch_move.php
new file mode 100644
index 0000000..01d8c91
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_move.php
@@ -0,0 +1,40 @@
+buildBatchMove($srcBucket, $keyPairs, $destBucket, true);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_restore_ar.php b/vendor/qiniu/php-sdk/examples/rs_batch_restore_ar.php
new file mode 100644
index 0000000..b2f79d0
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_restore_ar.php
@@ -0,0 +1,41 @@
+batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_batch_stat.php b/vendor/qiniu/php-sdk/examples/rs_batch_stat.php
new file mode 100644
index 0000000..88bc32e
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_batch_stat.php
@@ -0,0 +1,32 @@
+buildBatchStat($bucket, $keys);
+list($ret, $err) = $bucketManager->batch($ops);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_bucket_domains.php b/vendor/qiniu/php-sdk/examples/rs_bucket_domains.php
new file mode 100644
index 0000000..3cc9cb3
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_bucket_domains.php
@@ -0,0 +1,26 @@
+domains($bucket);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_buckets.php b/vendor/qiniu/php-sdk/examples/rs_buckets.php
new file mode 100644
index 0000000..84263a9
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_buckets.php
@@ -0,0 +1,25 @@
+buckets(true);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_change_mime.php b/vendor/qiniu/php-sdk/examples/rs_change_mime.php
new file mode 100644
index 0000000..f4442aa
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_change_mime.php
@@ -0,0 +1,29 @@
+changeMime($bucket, $key, $newMime);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_change_status.php b/vendor/qiniu/php-sdk/examples/rs_change_status.php
new file mode 100644
index 0000000..bedf61c
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_change_status.php
@@ -0,0 +1,29 @@
+changeStatus($bucket, $key, $status);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_change_type.php b/vendor/qiniu/php-sdk/examples/rs_change_type.php
new file mode 100644
index 0000000..8b3201f
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_change_type.php
@@ -0,0 +1,36 @@
+changeType($bucket, $key, $fileType);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_copy.php b/vendor/qiniu/php-sdk/examples/rs_copy.php
new file mode 100644
index 0000000..aae4d96
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_copy.php
@@ -0,0 +1,33 @@
+copy($srcBucket, $srcKey, $destBucket, $destKey, true);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_delete.php b/vendor/qiniu/php-sdk/examples/rs_delete.php
new file mode 100644
index 0000000..ad97266
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_delete.php
@@ -0,0 +1,27 @@
+delete($bucket, $key);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_delete_after_days.php b/vendor/qiniu/php-sdk/examples/rs_delete_after_days.php
new file mode 100644
index 0000000..96e55de
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_delete_after_days.php
@@ -0,0 +1,26 @@
+deleteAfterDays($bucket, $key, $days);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_download_urls.php b/vendor/qiniu/php-sdk/examples/rs_download_urls.php
new file mode 100644
index 0000000..e803ddc
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_download_urls.php
@@ -0,0 +1,19 @@
+/,一定要带访问协议,也就是 http:// 或者 https://
+$baseUrl = 'http://if-pri.qiniudn.com/qiniu.png?imageView2/1/h/500';
+
+// 对链接进行签名,参考文档:https://developer.qiniu.com/kodo/manual/1656/download-private
+$signedUrl = $auth->privateDownloadUrl($baseUrl);
+
+echo $signedUrl;
diff --git a/vendor/qiniu/php-sdk/examples/rs_fetch.php b/vendor/qiniu/php-sdk/examples/rs_fetch.php
new file mode 100644
index 0000000..5c1a5ab
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_fetch.php
@@ -0,0 +1,43 @@
+fetch($url, $bucket, $key);
+echo "=====> fetch $url to bucket: $bucket key: $key\n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ print_r($ret);
+}
+
+//---------------------------------------- demo2 ----------------------------------------
+// 不指定 key 时,以文件内容的 hash 作为文件名
+
+$key = null;
+list($ret, $err) = $bucketManager->fetch($url, $bucket, $key);
+echo "=====> fetch $url to bucket: $bucket key: $(etag)\n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ print_r($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_move.php b/vendor/qiniu/php-sdk/examples/rs_move.php
new file mode 100644
index 0000000..a399665
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_move.php
@@ -0,0 +1,29 @@
+move($srcBucket, $srcKey, $destBucket, $destKey, true);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_prefetch.php b/vendor/qiniu/php-sdk/examples/rs_prefetch.php
new file mode 100644
index 0000000..28af115
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_prefetch.php
@@ -0,0 +1,25 @@
+prefetch($bucket, $key);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_restore.php b/vendor/qiniu/php-sdk/examples/rs_restore.php
new file mode 100644
index 0000000..a3bf070
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_restore.php
@@ -0,0 +1,28 @@
+restoreAr($bucket, $key, 1);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rs_stat.php b/vendor/qiniu/php-sdk/examples/rs_stat.php
new file mode 100644
index 0000000..36e863e
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rs_stat.php
@@ -0,0 +1,28 @@
+stat($bucket, $key);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rsf_list_bucket.php b/vendor/qiniu/php-sdk/examples/rsf_list_bucket.php
new file mode 100644
index 0000000..97a5838
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rsf_list_bucket.php
@@ -0,0 +1,47 @@
+listFiles($bucket, $prefix, $marker, $limit, $delimiter);
+ if ($err !== null) {
+ echo "\n====> list file err: \n";
+ var_dump($err);
+ } else {
+ $marker = null;
+ if (array_key_exists('marker', $ret)) {
+ $marker = $ret['marker'];
+ }
+ echo "Marker: $marker\n";
+ echo "\nList Items====>\n";
+ //var_dump($ret['items']);
+ print('items count:' . count($ret['items']) . "\n");
+ if (array_key_exists('commonPrefixes', $ret)) {
+ print_r($ret['commonPrefixes']);
+ }
+ }
+} while (!empty($marker));
diff --git a/vendor/qiniu/php-sdk/examples/rsf_list_files.php b/vendor/qiniu/php-sdk/examples/rsf_list_files.php
new file mode 100644
index 0000000..31c455b
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rsf_list_files.php
@@ -0,0 +1,39 @@
+listFiles($bucket, $prefix, $marker, $limit, $delimiter);
+if ($err !== null) {
+ echo "\n====> list file err: \n";
+ var_dump($err);
+} else {
+ if (array_key_exists('marker', $ret)) {
+ echo "Marker:" . $ret["marker"] . "\n";
+ }
+ echo "\nList Iterms====>\n";
+}
diff --git a/vendor/qiniu/php-sdk/examples/rsf_v2list_bucket.php b/vendor/qiniu/php-sdk/examples/rsf_v2list_bucket.php
new file mode 100644
index 0000000..5f9d763
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rsf_v2list_bucket.php
@@ -0,0 +1,34 @@
+listFilesv2($bucket, $prefix, $marker, $limit, $delimiter, true);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rtc/README.md b/vendor/qiniu/php-sdk/examples/rtc/README.md
new file mode 100644
index 0000000..c7fff4d
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rtc/README.md
@@ -0,0 +1,34 @@
+# Rtc Streaming Cloud Server-Side Library For PHP
+
+## Features
+
+- RoomToken 签发
+ - [x] 生成 RoomToken: client->appToken()
+
+- App 管理
+ - [x] 创建应用: client->createApp()
+ - [x] 获取应用配置信息: client->getApp()
+ - [x] 更新应用配置信息: client->updateApp()
+ - [x] 删除应用: client->deleteApp()
+
+- 房间管理
+ - [x] 列举房间下的所有用户: client->listUser()
+ - [x] 指定一个用户踢出房间: client->kickUser()
+ - [x] 停止一个房间的合流转推: client->stopMerge()
+ - [x] 获取当前所有活跃的房间: client->listActiveRooms()
+
+## Demo
+- RoomToken 签发
+ - [生成 RoomToken](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_create_roomToken.php)
+
+- App 管理
+ - [创建应用](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_createApp.php)
+ - [获取应用配置信息](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_getApp.php)
+ - [更新应用配置信息](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_updateApp.php)
+ - [删除应用](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_deleteApp.php)
+
+- 房间管理
+ - [列举房间下的所有用户](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_rooms_listUser.php)
+ - [指定一个用户踢出房间](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_rooms_kickUser.php)
+ - [停止一个房间的合流转推](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_rooms_stopMerge.php)
+ - [获取当前所有活跃的房间](https://github.com/qiniu/php-sdk/tree/master/examples/rtc/rtc_rooms_listActiveRooms.php)
\ No newline at end of file
diff --git a/vendor/qiniu/php-sdk/examples/rtc/rtc_createApp.php b/vendor/qiniu/php-sdk/examples/rtc/rtc_createApp.php
new file mode 100644
index 0000000..039eadd
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rtc/rtc_createApp.php
@@ -0,0 +1,32 @@
+createApp($hub, $title, $maxUsers);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Create Successfully: \n";
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rtc/rtc_create_roomToken.php b/vendor/qiniu/php-sdk/examples/rtc/rtc_create_roomToken.php
new file mode 100644
index 0000000..6a62aa2
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rtc/rtc_create_roomToken.php
@@ -0,0 +1,34 @@
+appToken($appId, $roomName, $userId, $expireAt, $permission);
+echo "\n====> Create RoomToken Successfully: \n";
+var_dump($RoomToken);
diff --git a/vendor/qiniu/php-sdk/examples/rtc/rtc_deleteApp.php b/vendor/qiniu/php-sdk/examples/rtc/rtc_deleteApp.php
new file mode 100644
index 0000000..68bff33
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rtc/rtc_deleteApp.php
@@ -0,0 +1,25 @@
+deleteApp($appId);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Delete $appId Successfully \n";
+}
diff --git a/vendor/qiniu/php-sdk/examples/rtc/rtc_getApp.php b/vendor/qiniu/php-sdk/examples/rtc/rtc_getApp.php
new file mode 100644
index 0000000..9f8e374
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rtc/rtc_getApp.php
@@ -0,0 +1,26 @@
+getApp($appId);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> $appId Conf: \n";
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rtc/rtc_rooms_kickUser.php b/vendor/qiniu/php-sdk/examples/rtc/rtc_rooms_kickUser.php
new file mode 100644
index 0000000..019c3f2
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rtc/rtc_rooms_kickUser.php
@@ -0,0 +1,31 @@
+kickUser($appId, $roomName, $userId);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Kick User $userId Successfully \n";
+}
diff --git a/vendor/qiniu/php-sdk/examples/rtc/rtc_rooms_listActiveRooms.php b/vendor/qiniu/php-sdk/examples/rtc/rtc_rooms_listActiveRooms.php
new file mode 100644
index 0000000..16e6027
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rtc/rtc_rooms_listActiveRooms.php
@@ -0,0 +1,35 @@
+listActiveRooms($appId, $prefix, $offset, $limit);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Active Rooms:\n";
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rtc/rtc_rooms_listUser.php b/vendor/qiniu/php-sdk/examples/rtc/rtc_rooms_listUser.php
new file mode 100644
index 0000000..a839728
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rtc/rtc_rooms_listUser.php
@@ -0,0 +1,29 @@
+listUser($appId, $roomName);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> User List: \n";
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/rtc/rtc_rooms_stopMerge.php b/vendor/qiniu/php-sdk/examples/rtc/rtc_rooms_stopMerge.php
new file mode 100644
index 0000000..e140907
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rtc/rtc_rooms_stopMerge.php
@@ -0,0 +1,28 @@
+stopMerge($appId, $roomName);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Stop Merge Successfully \n";
+}
diff --git a/vendor/qiniu/php-sdk/examples/rtc/rtc_updateApp.php b/vendor/qiniu/php-sdk/examples/rtc/rtc_updateApp.php
new file mode 100644
index 0000000..f771075
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/rtc/rtc_updateApp.php
@@ -0,0 +1,40 @@
+updateApp($appId, $hub, $title, $maxUsers, false, $mergePublishRtmp);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Update $appId Conf Successfully: \n";
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/saveas.php b/vendor/qiniu/php-sdk/examples/saveas.php
new file mode 100644
index 0000000..5d51ef4
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/saveas.php
@@ -0,0 +1,33 @@
+为生成缩略图的文件名
+$entry = ':';
+
+// 生成的值
+$encodedEntryURI = \Qiniu\base64_urlSafeEncode($entry);
+
+// 使用 SecretKey 对新的下载 URL 进行 HMAC1-SHA1 签名
+$newurl = "78re52.com1.z0.glb.clouddn.com/resource/Ship.jpg?imageView2/2/w/200/h/200|saveas/" . $encodedEntryURI;
+
+$sign = hash_hmac("sha1", $newurl, $secretKey, true);
+
+// 对签名进行 URL 安全的 Base64 编码
+$encodedSign = \Qiniu\base64_urlSafeEncode($sign);
+
+// 最终得到的完整下载 URL
+$finalURL = "http://" . $newurl . "/sign/" . $accessKey . ":" . $encodedSign;
+
+$callbackBody = file_get_contents("$finalURL");
+
+echo $callbackBody;
diff --git a/vendor/qiniu/php-sdk/examples/sms/README.md b/vendor/qiniu/php-sdk/examples/sms/README.md
new file mode 100644
index 0000000..8c80a38
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/sms/README.md
@@ -0,0 +1,45 @@
+# SMS Server-Side Library For PHP
+
+## Features
+
+- 签名管理
+ - [x] 创建签名: client->createSignature()
+ - [x] 列出签名: client->checkSignature()
+ - [x] 查询单个签名: client->checkSingleSignature()
+ - [x] 编辑签名: client->updateSignature()
+ - [x] 删除签名: client->deleteSignature()
+
+- 模板管理
+ - [x] 创建模板: client->createTemplate()
+ - [x] 列出模板: client->queryTemplate()
+ - [x] 查询单个模板: client->querySingleTemplate()
+ - [x] 编辑模板: client->updateTemplate()
+ - [x] 删除模板: client->deleteTemplate()
+
+- 发送短信
+ - [x] 发送短信: client->sendMessage()
+
+- 查询发送记录
+ - [x] 查询发送记录: client->querySendSms()
+
+## Demo
+
+- 签名管理
+ - [创建签名](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_create_signature.php)
+ - [列出签名](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_query_signature.php)
+ - [查询单个签名](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_query_single_signature.php)
+ - [编辑签名](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_edit_signature.php)
+ - [删除签名](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_delete_signature.php)
+
+- 模板管理
+ - [创建模板](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_create_template.php)
+ - [列出模板](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_query_template.php)
+ - [查询单个模板](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_query_single_template.php)
+ - [编辑模板](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_edit_template.php)
+ - [删除模板](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_delete_template.php)
+
+- 发送短信
+ - [发送短信](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_send_message.php)
+
+- 查询发送记录
+ - [查询发送记录](https://github.com/qiniu/php-sdk/tree/master/examples/sms/sms_query_send_sms.php)
diff --git a/vendor/qiniu/php-sdk/examples/sms/sms_create_signature.php b/vendor/qiniu/php-sdk/examples/sms/sms_create_signature.php
new file mode 100644
index 0000000..ea1f158
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/sms/sms_create_signature.php
@@ -0,0 +1,29 @@
+createSignature($signature, $source, $pics);
+
+echo "\n====> create signature result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/sms/sms_create_template.php b/vendor/qiniu/php-sdk/examples/sms/sms_create_template.php
new file mode 100644
index 0000000..3cb3874
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/sms/sms_create_template.php
@@ -0,0 +1,33 @@
+createTemplate($name, $template, $type, $description, $signature_id);
+
+echo "\n====> create signature result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/sms/sms_delete_signature.php b/vendor/qiniu/php-sdk/examples/sms/sms_delete_signature.php
new file mode 100644
index 0000000..fd873fa
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/sms/sms_delete_signature.php
@@ -0,0 +1,25 @@
+deleteSignature($signature_id);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Delete Signature $signature_id Successfully\n";
+}
diff --git a/vendor/qiniu/php-sdk/examples/sms/sms_delete_template.php b/vendor/qiniu/php-sdk/examples/sms/sms_delete_template.php
new file mode 100644
index 0000000..4590835
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/sms/sms_delete_template.php
@@ -0,0 +1,25 @@
+deleteTemplate($template_id);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Delete Template $template_id Successfully\n";
+}
diff --git a/vendor/qiniu/php-sdk/examples/sms/sms_edit_signature.php b/vendor/qiniu/php-sdk/examples/sms/sms_edit_signature.php
new file mode 100644
index 0000000..edf14e0
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/sms/sms_edit_signature.php
@@ -0,0 +1,30 @@
+updateSignature($id, $signature, $source, $pics);
+
+echo "\n====> edit signature result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Update Signature Successfully\n";
+}
diff --git a/vendor/qiniu/php-sdk/examples/sms/sms_edit_template.php b/vendor/qiniu/php-sdk/examples/sms/sms_edit_template.php
new file mode 100644
index 0000000..1be5509
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/sms/sms_edit_template.php
@@ -0,0 +1,31 @@
+updateTemplate($template_id, $name, $template, $description, $signature_id);
+
+echo "\n====> edit template result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Update Template Successfully\n";
+}
diff --git a/vendor/qiniu/php-sdk/examples/sms/sms_query_send_sms.php b/vendor/qiniu/php-sdk/examples/sms/sms_query_send_sms.php
new file mode 100644
index 0000000..cdbbe71
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/sms/sms_query_send_sms.php
@@ -0,0 +1,50 @@
+querySendSms(
+ $job_id,
+ $message_id,
+ $mobile,
+ $status,
+ $template_id,
+ $type,
+ $start,
+ $end,
+ $page,
+ $page_size
+);
+echo "\n====> query send sms result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/sms/sms_query_signature.php b/vendor/qiniu/php-sdk/examples/sms/sms_query_signature.php
new file mode 100644
index 0000000..224d09b
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/sms/sms_query_signature.php
@@ -0,0 +1,28 @@
+querySignature($audit_status, $page, $page_size);
+echo "\n====> query signature result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/sms/sms_query_single_signature.php b/vendor/qiniu/php-sdk/examples/sms/sms_query_single_signature.php
new file mode 100644
index 0000000..8afb4d5
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/sms/sms_query_single_signature.php
@@ -0,0 +1,26 @@
+checkSingleSignature($signature_id);
+echo "\n====> query single signature result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/sms/sms_query_single_template.php b/vendor/qiniu/php-sdk/examples/sms/sms_query_single_template.php
new file mode 100644
index 0000000..8e0b279
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/sms/sms_query_single_template.php
@@ -0,0 +1,26 @@
+querySingleTemplate($template_id);
+echo "\n====> query single template result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/sms/sms_query_template.php b/vendor/qiniu/php-sdk/examples/sms/sms_query_template.php
new file mode 100644
index 0000000..6be260e
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/sms/sms_query_template.php
@@ -0,0 +1,28 @@
+queryTemplate($audit_status, $page, $page_size);
+echo "\n====> query template result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/sms/sms_send_message.php b/vendor/qiniu/php-sdk/examples/sms/sms_send_message.php
new file mode 100644
index 0000000..d943e52
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/sms/sms_send_message.php
@@ -0,0 +1,32 @@
+ 'xxxx');
+
+list($ret, $err) = $client->sendMessage($template_id, $mobiles, $code);
+if ($err !== null) {
+ var_dump($err);
+} else {
+ echo "\n====> Send Message Successfully: \n";
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/update_bucketEvent.php b/vendor/qiniu/php-sdk/examples/update_bucketEvent.php
new file mode 100644
index 0000000..7b0d1d0
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/update_bucketEvent.php
@@ -0,0 +1,31 @@
+updateBucketEvent($bucket, $name, $prefix, $suffix, $event, $callbackURL);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/update_bucketLifecycleRule.php b/vendor/qiniu/php-sdk/examples/update_bucketLifecycleRule.php
new file mode 100644
index 0000000..73f0f56
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/update_bucketLifecycleRule.php
@@ -0,0 +1,36 @@
+updateBucketLifecycleRule(
+ $bucket,
+ $name,
+ $prefix,
+ $delete_after_days,
+ $to_line_after_days
+);
+if ($err != null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/upload_and_callback.php b/vendor/qiniu/php-sdk/examples/upload_and_callback.php
new file mode 100644
index 0000000..a0c793a
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_and_callback.php
@@ -0,0 +1,31 @@
+ 'http://your.domain.com/upload_verify_callback.php',
+ 'callbackBody' => 'filename=$(fname)&filesize=$(fsize)'
+);
+$uptoken = $auth->uploadToken($bucket, null, 3600, $policy);
+
+// 上传文件的本地路径
+$filePath = './php-logo.png';
+
+$uploadMgr = new UploadManager();
+list($ret, $err) = $uploadMgr->putFile($uptoken, null, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/upload_and_pfop.php b/vendor/qiniu/php-sdk/examples/upload_and_pfop.php
new file mode 100644
index 0000000..32c1eb5
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_and_pfop.php
@@ -0,0 +1,49 @@
+ $pfop,
+ 'persistentNotifyUrl' => $notifyUrl,
+ 'persistentPipeline' => $pipeline
+);
+$token = $auth->uploadToken($bucket, null, 3600, $policy);
+
+list($ret, $err) = $uploadMgr->putFile($token, $key, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/upload_mgr_init.php b/vendor/qiniu/php-sdk/examples/upload_mgr_init.php
new file mode 100644
index 0000000..1164c90
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_mgr_init.php
@@ -0,0 +1,19 @@
+uploadToken($bucket);
+
+// 构建 UploadManager 对象
+$uploadMgr = new UploadManager();
diff --git a/vendor/qiniu/php-sdk/examples/upload_multi_demos.php b/vendor/qiniu/php-sdk/examples/upload_multi_demos.php
new file mode 100644
index 0000000..d724235
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_multi_demos.php
@@ -0,0 +1,89 @@
+uploadToken($bucket);
+$uploadMgr = new UploadManager();
+
+//---------------------------------------- upload demo1 ----------------------------------------
+// 上传字符串到七牛
+
+list($ret, $err) = $uploadMgr->put($token, null, 'content string');
+echo "\n====> put result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
+
+
+//---------------------------------------- upload demo2 ----------------------------------------
+// 上传文件到七牛
+
+$filePath = './php-logo.png';
+$key = 'php-logo.png';
+list($ret, $err) = $uploadMgr->putFile($token, $key, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
+
+
+//---------------------------------------- upload demo3 ----------------------------------------
+// 上传文件到七牛后, 七牛将文件名和文件大小回调给业务服务器.
+// 可参考文档: https://developer.qiniu.com/kodo/manual/1206/put-policy
+
+$policy = array(
+ 'callbackUrl' => 'http://172.30.251.210/upload_verify_callback.php',
+ 'callbackBody' => 'filename=$(fname)&filesize=$(fsize)'
+// 'callbackBodyType' => 'application/json',
+// 'callbackBody' => '{"filename":$(fname), "filesize": $(fsize)}' //设置application/json格式回调
+);
+$token = $auth->uploadToken($bucket, null, 3600, $policy);
+
+
+list($ret, $err) = $uploadMgr->putFile($token, null, $key);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
+
+
+//---------------------------------------- upload demo4 ----------------------------------------
+// 上传视频,上传完成后进行 m3u8 的转码, 并给视频打水印
+
+$wmImg = Qiniu\base64_urlSafeEncode('http://devtools.qiniudn.com/qiniu.png');
+$pfop = "avthumb/m3u8/wmImage/$wmImg";
+
+// 转码完成后回调到业务服务器。(公网可以访问,并相应 200 OK)
+$notifyUrl = 'http://notify.fake.com';
+
+$policy = array(
+ 'persistentOps' => $pfop,
+ 'persistentNotifyUrl' => $notifyUrl,
+ 'persistentPipeline' => $pipeline
+);
+$token = $auth->uploadToken($bucket, null, 3600, $policy);
+print($token);
+list($ret, $err) = $uploadMgr->putFile($token, null, $key);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/upload_simple_file.php b/vendor/qiniu/php-sdk/examples/upload_simple_file.php
new file mode 100644
index 0000000..f495a02
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_simple_file.php
@@ -0,0 +1,37 @@
+uploadToken($bucket);
+
+// 要上传文件的本地路径
+$filePath = './php-logo.png';
+
+// 上传到七牛存储后保存的文件名
+$key = 'my-php-logo.png';
+
+// 初始化 UploadManager 对象并进行文件的上传。
+$uploadMgr = new UploadManager();
+
+// 调用 UploadManager 的 putFile 方法进行文件的上传,该方法会判断文件大小,进而决定使用表单上传还是分片上传,无需手动配置。
+list($ret, $err) = $uploadMgr->putFile($token, $key, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/upload_tokens.php b/vendor/qiniu/php-sdk/examples/upload_tokens.php
new file mode 100644
index 0000000..d2cf02c
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_tokens.php
@@ -0,0 +1,82 @@
+uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+//---------------------------------------- demo2 ----------------------------------------
+// 自定义凭证有效期(示例2小时)
+
+$expires = 7200;
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+//---------------------------------------- demo3 ----------------------------------------
+// 覆盖上传凭证
+
+$expires = 3600;
+$keyToOverwrite = 'qiniu.mp4';
+$upToken = $auth->uploadToken($bucket, $keyToOverwrite, $expires, $policy, true);
+print($upToken . "\n");
+
+//---------------------------------------- demo4 ----------------------------------------
+// 自定义上传回复(非callback模式)凭证
+
+$returnBody = '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}';
+$policy = array(
+ 'returnBody' => $returnBody
+);
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+//---------------------------------------- demo5 ----------------------------------------
+// 带回调业务服务器的凭证(application/json)
+
+$policy = array(
+ 'callbackUrl' => 'http://api.example.com/qiniu/upload/callback',
+ 'callbackBody' => '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}',
+ 'callbackBodyType' => 'application/json'
+);
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+//---------------------------------------- demo6 ----------------------------------------
+// 带回调业务服务器的凭证(application/x-www-form-urlencoded)
+
+$policy = array(
+ 'callbackUrl' => 'http://api.example.com/qiniu/upload/callback',
+ 'callbackBody' => 'key=$(key)&hash=$(etag)&bucket=$(bucket)&fsize=$(fsize)&name=$(x:name)'
+);
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
+
+//---------------------------------------- demo7 ----------------------------------------
+// 带数据处理的凭证
+
+$saveMp4Entry = \Qiniu\base64_urlSafeEncode($bucket . ":avthumb_test_target.mp4");
+$saveJpgEntry = \Qiniu\base64_urlSafeEncode($bucket . ":vframe_test_target.jpg");
+$avthumbMp4Fop = "avthumb/mp4|saveas/" . $saveMp4Entry;
+$vframeJpgFop = "vframe/jpg/offset/1|saveas/" . $saveJpgEntry;
+$policy = array(
+ 'persistentOps' => $avthumbMp4Fop . ";" . $vframeJpgFop,
+ 'persistentPipeline' => "video-pipe",
+ 'persistentNotifyUrl' => "http://api.example.com/qiniu/pfop/notify",
+);
+$upToken = $auth->uploadToken($bucket, null, $expires, $policy, true);
+print($upToken . "\n");
diff --git a/vendor/qiniu/php-sdk/examples/upload_verify_callback.php b/vendor/qiniu/php-sdk/examples/upload_verify_callback.php
new file mode 100644
index 0000000..dcb64c9
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_verify_callback.php
@@ -0,0 +1,34 @@
+verifyCallback($contentType, $authorization, $url, $callbackBody);
+
+if ($isQiniuCallback) {
+ $resp = array('ret' => 'success');
+} else {
+ $resp = array('ret' => 'failed');
+}
+
+echo json_encode($resp);
diff --git a/vendor/qiniu/php-sdk/examples/upload_with_qvmzone.php b/vendor/qiniu/php-sdk/examples/upload_with_qvmzone.php
new file mode 100644
index 0000000..ce2b21f
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_with_qvmzone.php
@@ -0,0 +1,40 @@
+uploadToken($bucket);
+
+// 上传文件的本地路径
+$filePath = './php-logo.png';
+
+// 七牛云主机QVM和七牛对象存储KODO内网上传,目前支持华东1区域(杭州)和华北2区域(北京)的云主机可以访问同区域的对象存储服务
+// 参考文档:https://developer.qiniu.com/qvm/manual/4269/qvm-kodo
+
+$zone = Zone::qvmZonez0(); // 华东:z0,华北:z1
+$config = new Config($zone);
+$config->useHTTPS = true;
+
+// 指定 config
+$uploadMgr = new UploadManager($config);
+
+list($ret, $err) = $uploadMgr->putFile($uptoken, $key, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/examples/upload_with_zone.php b/vendor/qiniu/php-sdk/examples/upload_with_zone.php
new file mode 100644
index 0000000..6192666
--- /dev/null
+++ b/vendor/qiniu/php-sdk/examples/upload_with_zone.php
@@ -0,0 +1,39 @@
+uploadToken($bucket);
+
+// 上传文件的本地路径
+$filePath = './php-logo.png';
+
+// 指定 zone 上传
+// 参考文档:https://developer.qiniu.com/kodo/manual/1671/region-endpoint
+$zone = Zone::zonez0(); // 华东:z0,华北:z1,华南:z2,北美:na0,东南亚:as0
+$config = new Config($zone);
+$config->useHTTPS = true;
+
+// 指定 config
+$uploadMgr = new UploadManager($config);
+
+list($ret, $err) = $uploadMgr->putFile($uptoken, $key, $filePath);
+echo "\n====> putFile result: \n";
+if ($err !== null) {
+ var_dump($err);
+} else {
+ var_dump($ret);
+}
diff --git a/vendor/qiniu/php-sdk/phpunit.xml.dist b/vendor/qiniu/php-sdk/phpunit.xml.dist
new file mode 100644
index 0000000..840f6e5
--- /dev/null
+++ b/vendor/qiniu/php-sdk/phpunit.xml.dist
@@ -0,0 +1,18 @@
+
+
+
+
+ tests
+
+
+
+
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Auth.php b/vendor/qiniu/php-sdk/src/Qiniu/Auth.php
new file mode 100644
index 0000000..6da2be4
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Auth.php
@@ -0,0 +1,285 @@
+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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Cdn/CdnManager.php b/vendor/qiniu/php-sdk/src/Qiniu/Cdn/CdnManager.php
new file mode 100644
index 0000000..60052d3
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Cdn/CdnManager.php
@@ -0,0 +1,263 @@
+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 指定要查询记录的状态,取值processing/success/failure
+ * @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 指定要查询记录的状态,取值processing/success/failure
+ * @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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Config.php b/vendor/qiniu/php-sdk/src/Qiniu/Config.php
new file mode 100644
index 0000000..3ce7fa5
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Config.php
@@ -0,0 +1,398 @@
+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);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Enum/QiniuEnum.php b/vendor/qiniu/php-sdk/src/Qiniu/Enum/QiniuEnum.php
new file mode 100644
index 0000000..8399b54
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Enum/QiniuEnum.php
@@ -0,0 +1,53 @@
+ $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);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Http/Error.php b/vendor/qiniu/php-sdk/src/Qiniu/Http/Error.php
new file mode 100644
index 0000000..8fba74f
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Http/Error.php
@@ -0,0 +1,38 @@
+
+ * {"error" : "detailed error message"}
+ *
+ */
+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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Http/Header.php b/vendor/qiniu/php-sdk/src/Qiniu/Http/Header.php
new file mode 100644
index 0000000..1dcf328
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Http/Header.php
@@ -0,0 +1,291 @@
+ $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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Http/Middleware/Middleware.php b/vendor/qiniu/php-sdk/src/Qiniu/Http/Middleware/Middleware.php
new file mode 100644
index 0000000..fe8a64c
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Http/Middleware/Middleware.php
@@ -0,0 +1,31 @@
+ $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;
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Http/Middleware/RetryDomainsMiddleware.php b/vendor/qiniu/php-sdk/src/Qiniu/Http/Middleware/RetryDomainsMiddleware.php
new file mode 100644
index 0000000..829ab87
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Http/Middleware/RetryDomainsMiddleware.php
@@ -0,0 +1,76 @@
+ 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 $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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Http/Proxy.php b/vendor/qiniu/php-sdk/src/Qiniu/Http/Proxy.php
new file mode 100644
index 0000000..fac6ba1
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Http/Proxy.php
@@ -0,0 +1,34 @@
+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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Http/Request.php b/vendor/qiniu/php-sdk/src/Qiniu/Http/Request.php
new file mode 100644
index 0000000..5a31bf6
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Http/Request.php
@@ -0,0 +1,42 @@
+
+ */
+ 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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Http/RequestOptions.php b/vendor/qiniu/php-sdk/src/Qiniu/Http/RequestOptions.php
new file mode 100644
index 0000000..be0c6d5
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Http/RequestOptions.php
@@ -0,0 +1,104 @@
+
+ */
+ 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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Http/Response.php b/vendor/qiniu/php-sdk/src/Qiniu/Http/Response.php
new file mode 100644
index 0000000..cd77903
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Http/Response.php
@@ -0,0 +1,220 @@
+ '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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Processing/ImageUrlBuilder.php b/vendor/qiniu/php-sdk/src/Qiniu/Processing/ImageUrlBuilder.php
new file mode 100644
index 0000000..f5575ed
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Processing/ImageUrlBuilder.php
@@ -0,0 +1,292 @@
+
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ protected function hasQuery($url)
+ {
+ $urlArr = parse_url($url);
+
+ return !empty($urlArr['query']);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Processing/Operation.php b/vendor/qiniu/php-sdk/src/Qiniu/Processing/Operation.php
new file mode 100644
index 0000000..839703c
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Processing/Operation.php
@@ -0,0 +1,69 @@
+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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Processing/PersistentFop.php b/vendor/qiniu/php-sdk/src/Qiniu/Processing/PersistentFop.php
new file mode 100644
index 0000000..8dca4a9
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Processing/PersistentFop.php
@@ -0,0 +1,135 @@
+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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Region.php b/vendor/qiniu/php-sdk/src/Qiniu/Region.php
new file mode 100644
index 0000000..220a5a3
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Region.php
@@ -0,0 +1,229 @@
+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=&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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Rtc/AppClient.php b/vendor/qiniu/php-sdk/src/Qiniu/Rtc/AppClient.php
new file mode 100644
index 0000000..3f245db
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Rtc/AppClient.php
@@ -0,0 +1,236 @@
+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);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Sms/Sms.php b/vendor/qiniu/php-sdk/src/Qiniu/Sms/Sms.php
new file mode 100644
index 0000000..c96409b
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Sms/Sms.php
@@ -0,0 +1,382 @@
+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);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Storage/ArgusManager.php b/vendor/qiniu/php-sdk/src/Qiniu/Storage/ArgusManager.php
new file mode 100644
index 0000000..51b4200
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Storage/ArgusManager.php
@@ -0,0 +1,129 @@
+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);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Storage/BucketManager.php b/vendor/qiniu/php-sdk/src/Qiniu/Storage/BucketManager.php
new file mode 100644
index 0000000..bfca4fc
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Storage/BucketManager.php
@@ -0,0 +1,1324 @@
+auth = $auth;
+ if ($config == null) {
+ $this->config = new Config();
+ } else {
+ $this->config = $config;
+ }
+ $this->proxy = new Proxy($proxy, $proxy_auth, $proxy_user_password);
+ }
+
+ /**
+ * 获取指定账号下所有的空间名
+ *
+ * @param bool $shared 指定共享空间,rw:读写权限空间,rd:读权限空间
+ * @return array 包含所有空间名
+ */
+ public function buckets($shared = true)
+ {
+ $includeShared = "false";
+ if ($shared === true) {
+ $includeShared = "true";
+ }
+ return $this->getV2($this->config->getUcHost() . '/buckets?shared=' . $includeShared);
+ }
+
+ /**
+ * 列举空间,返回bucket列表
+ *
+ * @param string $region 区域
+ * @param string $line
+ * @param string $shared 指定共享空间,rw:读写权限空间,rd:读权限空间
+ * @return array
+ */
+ public function listbuckets(
+ $region = null,
+ $line = 'false',
+ $shared = 'false'
+ ) {
+ $path = '/v3/buckets?region=' . $region . '&line=' . $line . '&shared=' . $shared;
+ return $this->ucPost($path);
+ }
+
+ /**
+ * 创建空间
+ *
+ * @param string $name 创建的空间名
+ * @param string $region 创建的区域,默认华东
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1382/mkbucketv3
+ */
+ public function createBucket($name, $region = 'z0')
+ {
+ $path = '/mkbucketv3/' . $name . '/region/' . $region;
+ return $this->postV2($this->config->getUcHost() . $path, null);
+ }
+
+ /**
+ * 删除空间
+ *
+ * @param string $name 需要删除的目标空间名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1601/drop-bucket
+ */
+ public function deleteBucket($name)
+ {
+ $path = '/drop/' . $name;
+ return $this->postV2($this->config->getUcHost() . $path, null);
+ }
+
+ /**
+ * 获取指定空间绑定的所有的域名
+ *
+ * @param string $bucket 空间名称
+ * @return array
+ */
+ public function domains($bucket)
+ {
+ return $this->ucGet('/v2/domains?tbl=' . $bucket);
+ }
+
+ /**
+ * 获取指定空间的相关信息
+ *
+ * @param string $bucket 空间名称
+ * @return array
+ */
+ public function bucketInfo($bucket)
+ {
+ $path = '/v2/bucketInfo?bucket=' . $bucket;
+ return $this->ucPost($path);
+ }
+
+ /**
+ * 获取指定zone的空间信息列表
+ *
+ * @param string $region 区域
+ * @param string $shared 指定共享空间,rw:读写权限空间,rd:读权限空间
+ * @param string $fs 如果为 true,会返回每个空间当前的文件数和存储量(实时数据)
+ * @return array
+ */
+ public function bucketInfos($region = null, $shared = 'false', $fs = 'false')
+ {
+ $path = '/v2/bucketInfos?region=' . $region . '&shared=' . $shared . '&fs=' . $fs;
+ return $this->ucPost($path);
+ }
+
+ /**
+ * 列取空间的文件列表
+ *
+ * @param string $bucket 空间名
+ * @param string $prefix 列举前缀
+ * @param string $marker 列举标识符
+ * @param int $limit 单次列举个数限制
+ * @param string $delimiter 指定目录分隔符
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1284/list
+ */
+ public function listFiles(
+ $bucket,
+ $prefix = null,
+ $marker = null,
+ $limit = 1000,
+ $delimiter = null
+ ) {
+ $query = array('bucket' => $bucket);
+ \Qiniu\setWithoutEmpty($query, 'prefix', $prefix);
+ \Qiniu\setWithoutEmpty($query, 'marker', $marker);
+ \Qiniu\setWithoutEmpty($query, 'limit', $limit);
+ \Qiniu\setWithoutEmpty($query, 'delimiter', $delimiter);
+ return $this->rsfGet($bucket, '/list?' . http_build_query($query));
+ }
+
+ /**
+ * 列取空间的文件列表
+ *
+ * @deprecated API 可能返回仅包含 marker,不包含 item 或 dir 的项,请使用 {@link listFiles}
+ *
+ * @param string $bucket 空间名
+ * @param string $prefix 列举前缀
+ * @param string $marker 列举标识符
+ * @param int $limit 单次列举个数限制
+ * @param string $delimiter 指定目录分隔符
+ * @param bool $skipconfirm 是否跳过已删除条目的确认机制
+ *
+ * @return array
+ * @link http://developer.qiniu.com/docs/v6/api/reference/rs/list.html
+ */
+ public function listFilesv2(
+ $bucket,
+ $prefix = null,
+ $marker = null,
+ $limit = 1000,
+ $delimiter = null,
+ $skipconfirm = true
+ ) {
+ $query = array('bucket' => $bucket);
+ \Qiniu\setWithoutEmpty($query, 'prefix', $prefix);
+ \Qiniu\setWithoutEmpty($query, 'marker', $marker);
+ \Qiniu\setWithoutEmpty($query, 'limit', $limit);
+ \Qiniu\setWithoutEmpty($query, 'delimiter', $delimiter);
+ \Qiniu\setWithoutEmpty($query, 'skipconfirm', $skipconfirm);
+ $path = '/v2/list?' . http_build_query($query);
+
+ list($host, $err) = $this->config->getRsfHostV2(
+ $this->auth->getAccessKey(),
+ $bucket,
+ $this->proxy->makeReqOpt()
+ );
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ $url = $host . $path;
+ $headers = $this->auth->authorizationV2($url, 'POST', null, 'application/x-www-form-urlencoded');
+ $ret = Client::post($url, null, $headers, $this->proxy->makeReqOpt());
+ if (!$ret->ok()) {
+ return array(null, new Error($url, $ret));
+ }
+ $r = explode("\n", $ret->body);
+ array_pop($r);
+ return array($r, null);
+ }
+
+ /**
+ * 增加bucket生命规则
+ *
+ * @param string $bucket
+ * 空间名
+ * @param string $name
+ * 规则名称 bucket 内唯一,长度小于50,不能为空,只能为字母、数字、下划线
+ * @param string $prefix
+ * 同一个 bucket 里面前缀不能重复
+ * @param int $delete_after_days
+ * 指定上传文件多少天后删除,指定为0表示不删除,大于0表示多少天后删除。
+ * 需大于 to_line_after_days
+ * @param int $to_line_after_days
+ * 指定文件上传多少天后转低频存储。指定为0表示不转低频存储
+ * @param int $to_archive_ir_after_days
+ * 指定文件上传多少天后转归档直读。指定为0表示不转归档直读
+ * @param int $to_archive_after_days
+ * 指定文件上传多少天后转归档存储。指定为0表示不转归档存储
+ * @param int $to_deep_archive_after_days
+ * 指定文件上传多少天后转深度归档存储。指定为0表示不转深度归档存储
+ * @return array
+ */
+ public function bucketLifecycleRule(
+ $bucket,
+ $name,
+ $prefix,
+ $delete_after_days = null,
+ $to_line_after_days = null,
+ $to_archive_after_days = null,
+ $to_deep_archive_after_days = null,
+ $to_archive_ir_after_days = null
+ ) {
+ $path = '/rules/add';
+ $params = array();
+ if ($bucket) {
+ $params['bucket'] = $bucket;
+ }
+ if ($name) {
+ $params['name'] = $name;
+ }
+ if ($prefix) {
+ $params['prefix'] = $prefix;
+ }
+ if ($delete_after_days) {
+ $params['delete_after_days'] = $delete_after_days;
+ }
+ if ($to_line_after_days) {
+ $params['to_line_after_days'] = $to_line_after_days;
+ }
+ if ($to_archive_ir_after_days) {
+ $params['to_archive_ir_after_days'] = $to_archive_ir_after_days;
+ }
+ if ($to_archive_after_days) {
+ $params['to_archive_after_days'] = $to_archive_after_days;
+ }
+ if ($to_deep_archive_after_days) {
+ $params['to_deep_archive_after_days'] = $to_deep_archive_after_days;
+ }
+ $data = http_build_query($params);
+ $info = $this->ucPost($path, $data);
+ return $info;
+ }
+
+ /**
+ * 更新bucket生命规则
+ *
+ * @param string $bucket
+ * 空间名
+ * @param string $name
+ * 规则名称 bucket 内唯一,长度小于50,不能为空,只能为字母、数字、下划线
+ * @param string $prefix
+ * 同一个 bucket 里面前缀不能重复
+ * @param int $delete_after_days
+ * 指定上传文件多少天后删除,指定为0表示不删除,大于0表示多少天后删除
+ * 需大于 to_line_after_days
+ * @param int $to_line_after_days
+ * 指定文件上传多少天后转低频存储。指定为0表示不转低频存储
+ * @param int $to_archive_ir_after_days
+ * 指定文件上传多少天后转归档只读。指定为0表示不转归档只读
+ * @param int $to_archive_after_days
+ * 指定文件上传多少天后转归档存储。指定为0表示不转归档存储
+ * @param int $to_deep_archive_after_days
+ * 指定文件上传多少天后转深度归档存储。指定为0表示不转深度归档存储
+ * @return array
+ */
+ public function updateBucketLifecycleRule(
+ $bucket,
+ $name,
+ $prefix,
+ $delete_after_days = null,
+ $to_line_after_days = null,
+ $to_archive_after_days = null,
+ $to_deep_archive_after_days = null,
+ $to_archive_ir_after_days = null
+ ) {
+ $path = '/rules/update';
+ $params = array();
+ if ($bucket) {
+ $params['bucket'] = $bucket;
+ }
+ if ($name) {
+ $params['name'] = $name;
+ }
+ if ($prefix) {
+ $params['prefix'] = $prefix;
+ }
+ if ($delete_after_days) {
+ $params['delete_after_days'] = $delete_after_days;
+ }
+ if ($to_line_after_days) {
+ $params['to_line_after_days'] = $to_line_after_days;
+ }
+ if ($to_archive_ir_after_days) {
+ $params['to_archive_ir_after_days'] = $to_archive_ir_after_days;
+ }
+ if ($to_archive_after_days) {
+ $params['to_archive_after_days'] = $to_archive_after_days;
+ }
+ if ($to_deep_archive_after_days) {
+ $params['to_deep_archive_after_days'] = $to_deep_archive_after_days;
+ }
+ $data = http_build_query($params);
+ return $this->ucPost($path, $data);
+ }
+
+ /**
+ * 获取bucket生命规则
+ *
+ * @param string $bucket 空间名
+ * @return array
+ */
+ public function getBucketLifecycleRules($bucket)
+ {
+ $path = '/rules/get?bucket=' . $bucket;
+ $info = $this->ucGet($path);
+ return $info;
+ }
+
+ /**
+ * 删除bucket生命规则
+ *
+ * @param string $bucket 空间名
+ * @param string $name 规则名称 bucket 内唯一,长度小于50,不能为空,
+ * 只能为字母、数字、下划线()
+ * @return array
+ */
+ public function deleteBucketLifecycleRule($bucket, $name)
+ {
+ $path = '/rules/delete';
+ $params = array();
+ if ($bucket) {
+ $params['bucket'] = $bucket;
+ }
+ if ($name) {
+ $params['name'] = $name;
+ }
+ $data = http_build_query($params);
+ $info = $this->ucPost($path, $data);
+ return $info;
+ }
+
+ /**
+ * 增加bucket事件通知规则
+ *
+ * @param string $bucket 空间名
+ * @param string $name 规则名称 bucket 内唯一,长度小于50,不能为空,
+ * 只能为字母、数字、下划线()
+ * @param string $prefix 同一个 bucket 里面前缀不能重复
+ * @param string $suffix 可选,文件配置的后缀
+ * @param array $event 事件类型,可以指定多个,包括 put,mkfile,delete,copy,move,append,
+ * disable,enable,deleteMarkerCreate
+ * @param string $callbackURL 通知URL,可以指定多个,失败依次重试
+ * @param string $access_key 可选,设置的话会对通知请求用对应的ak、sk进行签名
+ * @param string $host 可选,通知请求的host
+ *
+ * @return array
+ */
+ public function putBucketEvent(
+ $bucket,
+ $name,
+ $prefix,
+ $suffix,
+ $event,
+ $callbackURL,
+ $access_key = null,
+ $host = null
+ ) {
+ $path = '/events/add';
+ $params = array();
+ if (!empty($bucket)) {
+ $params['bucket'] = $bucket;
+ }
+ if (!empty($name)) {
+ $params['name'] = $name;
+ }
+ if (!empty($prefix)) {
+ $params['prefix'] = $prefix;
+ }
+ if (!empty($suffix)) {
+ $params['suffix'] = $suffix;
+ }
+ if (!empty($callbackURL)) {
+ $params['callbackURL'] = $callbackURL;
+ }
+ if (!empty($access_key)) {
+ $params['access_key'] = $access_key;
+ }
+ if (!empty($host)) {
+ $params['host'] = $host;
+ }
+ $data = http_build_query($params);
+ if (!empty($event)) {
+ $eventpath = "";
+ foreach ($event as $key => $value) {
+ $eventpath .= "&event=$value";
+ }
+ $data .= $eventpath;
+ }
+ $info = $this->ucPost($path, $data);
+ return $info;
+ }
+
+ /**
+ * 更新bucket事件通知规则
+ *
+ * @param string $bucket 空间名
+ * @param string $name 规则名称 bucket 内唯一,长度小于50,不能为空,
+ * 只能为字母、数字、下划线()
+ * @param string $prefix 同一个 bucket 里面前缀不能重复
+ * @param string $suffix 可选,文件配置的后缀
+ * @param array $event 事件类型,可以指定多个,包括 put,mkfile,delete,copy,move,append,disable,
+ * enable,deleteMarkerCreate
+ * @param string $callbackURL 通知URL,可以指定多个,失败依次重试
+ * @param string $access_key 可选,设置的话会对通知请求用对应的ak、sk进行签名
+ * @param string $host 可选,通知请求的host
+ *
+ * @return array
+ */
+ public function updateBucketEvent(
+ $bucket,
+ $name,
+ $prefix,
+ $suffix,
+ $event,
+ $callbackURL,
+ $access_key = null,
+ $host = null
+ ) {
+ $path = '/events/update';
+ $params = array();
+ if (!empty($bucket)) {
+ $params['bucket'] = $bucket;
+ }
+ if (!empty($name)) {
+ $params['name'] = $name;
+ }
+ if (!empty($prefix)) {
+ $params['prefix'] = $prefix;
+ }
+ if ($suffix) {
+ $params['suffix'] = $suffix;
+ }
+ if (!empty($event)) {
+ $params['event'] = $event;
+ }
+ if (!empty($callbackURL)) {
+ $params['callbackURL'] = $callbackURL;
+ }
+ if (!empty($access_key)) {
+ $params['access_key'] = $access_key;
+ }
+ if (!empty($host)) {
+ $params['host'] = $host;
+ }
+ $data = http_build_query($params);
+ if (!empty($event)) {
+ $eventpath = "";
+ foreach ($event as $key => $value) {
+ $eventpath .= "&event=$value";
+ }
+ $data .= $eventpath;
+ }
+ return $this->ucPost($path, $data);
+ }
+
+ /**
+ * 获取bucket事件通知规则
+ *
+ * @param string $bucket 空间名
+ * @return array
+ */
+ public function getBucketEvents($bucket)
+ {
+ $path = '/events/get?bucket=' . $bucket;
+ return $this->ucGet($path);
+ }
+
+ /**
+ * 删除bucket事件通知规则
+ *
+ * @param string $bucket 空间名
+ * @param string $name 规则名称bucket内唯一,长度小于50,不能为空,只能为字母、数字、下划线
+ * @return array
+ */
+ public function deleteBucketEvent($bucket, $name)
+ {
+ $path = '/events/delete';
+ $params = array();
+ if ($bucket) {
+ $params['bucket'] = $bucket;
+ }
+ if ($name) {
+ $params['name'] = $name;
+ }
+ $data = http_build_query($params);
+ return $this->ucPost($path, $data);
+ }
+
+ /**
+ * 获取bucket的跨域信息
+ *
+ * @param string $bucket 空间名
+ * @return array
+ */
+ public function getCorsRules($bucket)
+ {
+ $path = '/corsRules/get/' . $bucket;
+ return $this->ucGet($path);
+ }
+
+ /**
+ * 开关原图保护
+ *
+ * @param string $bucket 空间名称
+ * @param int $mode mode 为1表示开启原图保护,0表示关闭
+ * @return array
+ */
+ public function putBucketAccessStyleMode($bucket, $mode)
+ {
+ $path = '/accessMode/' . $bucket . '/mode/' . $mode;
+ return $this->ucPost($path, null);
+ }
+
+ /**
+ * 设置私有属性
+ *
+ * @param string $bucket 空间名称
+ * @param int $private private为0表示公开,为1表示私有
+ * @return array
+ */
+ public function putBucketAccessMode($bucket, $private)
+ {
+ $path = "/private?bucket=$bucket&private=$private";
+ return $this->ucPost($path, null);
+ }
+
+ /**
+ * 设置 referer 防盗链
+ *
+ * @param string $bucket 空间名称
+ * @param int $mode 0:关闭Referer(使用此选项将会忽略以下参数并将恢复默认值);
+ * 1:设置Referer白名单; 2:设置Referer黑名单
+ * @param string $norefer 0:不允许空 Refer 访问; 1:表示允许空Refer访问
+ * @param string $pattern 规则字符串
+ * @param int $enabled 源站是否支持,默认为0只给CDN配置, 设置为1表示开启源站防盗链
+ * @return array
+ * @link https://developer.qiniu.com/kodo/manual/6093/set-the-hotlinking-prevention
+ */
+ public function putReferAntiLeech($bucket, $mode, $norefer, $pattern, $enabled = 1)
+ {
+ $path = "/referAntiLeech?bucket=$bucket&mode=$mode&norefer=$norefer&pattern=$pattern&source_enabled=$enabled";
+ return $this->ucPost($path, null);
+ }
+
+ /**
+ * 设置Bucket的maxAge
+ *
+ * @param string $bucket 空间名称
+ * @param int $maxAge maxAge为0或者负数表示为默认值(31536000)
+ * @return array
+ */
+ public function putBucketMaxAge($bucket, $maxAge)
+ {
+ $path = '/maxAge?bucket=' . $bucket . '&maxAge=' . $maxAge;
+ return $this->ucPost($path, null);
+ }
+
+ /**
+ * 设置空间配额
+ *
+ * @param string $bucket 空间名称,不支持授权空间
+ * @param string $size 空间存储量配额,参数传入0或不传表示不更改当前配置,传入-1表示取消限额,新创建的空间默认没有限额
+ * @param string $count 空间文件数配额,参数含义同
+ * @return array
+ */
+ public function putBucketQuota($bucket, $size, $count)
+ {
+ $path = '/setbucketquota/' . $bucket . '/size/' . $size . '/count/' . $count;
+ return $this->apiPost($bucket, $path);
+ }
+
+ /**
+ * 获取空间配额
+ *
+ * @param string $bucket 空间名称
+ * @return array
+ */
+ public function getBucketQuota($bucket)
+ {
+ $path = '/getbucketquota/' . $bucket;
+ return $this->apiPost($bucket, $path);
+ }
+
+ /**
+ * 获取资源的元信息,但不返回文件内容
+ *
+ * @param string $bucket 待获取信息资源所在的空间
+ * @param string $key 待获取资源的文件名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1308/stat
+ */
+ public function stat($bucket, $key)
+ {
+ $path = '/stat/' . \Qiniu\entry($bucket, $key);
+ return $this->rsGet($bucket, $path);
+ }
+
+ /**
+ * 删除指定资源
+ *
+ * @param string $bucket 待删除资源所在的空间
+ * @param string $key 待删除资源的文件名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1257/delete
+ */
+ public function delete($bucket, $key)
+ {
+ $path = '/delete/' . \Qiniu\entry($bucket, $key);
+ return $this->rsPost($bucket, $path);
+ }
+
+ /**
+ * 给资源进行重命名,本质为move操作。
+ *
+ * @param string $bucket 待操作资源所在空间
+ * @param string $oldname 待操作资源文件名
+ * @param string $newname 目标资源文件名
+ *
+ * @return array
+ */
+ public function rename($bucket, $oldname, $newname)
+ {
+ return $this->move($bucket, $oldname, $bucket, $newname);
+ }
+
+ /**
+ * 对资源进行复制。
+ *
+ * @param string $from_bucket 待操作资源所在空间
+ * @param string $from_key 待操作资源文件名
+ * @param string $to_bucket 目标资源空间名
+ * @param string $to_key 目标资源文件名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1254/copy
+ */
+ public function copy($from_bucket, $from_key, $to_bucket, $to_key, $force = false)
+ {
+ $from = \Qiniu\entry($from_bucket, $from_key);
+ $to = \Qiniu\entry($to_bucket, $to_key);
+ $path = '/copy/' . $from . '/' . $to;
+ if ($force === true) {
+ $path .= '/force/true';
+ }
+ return $this->rsPost($from_bucket, $path);
+ }
+
+ /**
+ * 将资源从一个空间到另一个空间
+ *
+ * @param string $from_bucket 待操作资源所在空间
+ * @param string $from_key 待操作资源文件名
+ * @param string $to_bucket 目标资源空间名
+ * @param string $to_key 目标资源文件名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1288/move
+ */
+ public function move($from_bucket, $from_key, $to_bucket, $to_key, $force = false)
+ {
+ $from = \Qiniu\entry($from_bucket, $from_key);
+ $to = \Qiniu\entry($to_bucket, $to_key);
+ $path = '/move/' . $from . '/' . $to;
+ if ($force) {
+ $path .= '/force/true';
+ }
+ return $this->rsPost($from_bucket, $path);
+ }
+
+ /**
+ * 主动修改指定资源的文件元信息
+ *
+ * @param string $bucket 待操作资源所在空间
+ * @param string $key 待操作资源文件名
+ * @param string $mime 待操作文件目标mimeType
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1252/chgm
+ */
+ public function changeMime($bucket, $key, $mime)
+ {
+ $resource = \Qiniu\entry($bucket, $key);
+ $encode_mime = \Qiniu\base64_urlSafeEncode($mime);
+ $path = '/chgm/' . $resource . '/mime/' . $encode_mime;
+ return $this->rsPost($bucket, $path);
+ }
+
+
+ /**
+ * 修改指定资源的存储类型
+ *
+ * @param string $bucket 待操作资源所在空间
+ * @param string $key 待操作资源文件名
+ * @param int $fileType 对象存储类型
+ * 0 表示标准存储;
+ * 1 表示低频存储;
+ * 2 表示归档存储;
+ * 3 表示深度归档存储;
+ * 4 表示归档直读存储;
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/3710/chtype
+ */
+ public function changeType($bucket, $key, $fileType)
+ {
+ $resource = \Qiniu\entry($bucket, $key);
+ $path = '/chtype/' . $resource . '/type/' . $fileType;
+ return $this->rsPost($bucket, $path);
+ }
+
+ /**
+ * 解冻指定资源的存储类型
+ *
+ * @param string $bucket 待操作资源所在空间
+ * @param string $key 待操作资源文件名
+ * @param int $freezeAfterDays 解冻有效时长,取值范围 1~7
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/6380/restore-archive
+ */
+ public function restoreAr($bucket, $key, $freezeAfterDays)
+ {
+ $resource = \Qiniu\entry($bucket, $key);
+ $path = '/restoreAr/' . $resource . '/freezeAfterDays/' . $freezeAfterDays;
+ return $this->rsPost($bucket, $path);
+ }
+
+ /**
+ * 修改文件的存储状态,即禁用状态和启用状态间的的互相转换
+ *
+ * @param string $bucket 待操作资源所在空间
+ * @param string $key 待操作资源文件名
+ * @param int $status 0表示启用;1表示禁用
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/4173/modify-the-file-status
+ */
+ public function changeStatus($bucket, $key, $status)
+ {
+ $resource = \Qiniu\entry($bucket, $key);
+ $path = '/chstatus/' . $resource . '/status/' . $status;
+ return $this->rsPost($bucket, $path);
+ }
+
+ /**
+ * 从指定URL抓取资源,并将该资源存储到指定空间中
+ *
+ * @param string $url 指定的URL
+ * @param string $bucket 目标资源空间
+ * @param string $key 目标资源文件名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1263/fetch
+ */
+ public function fetch($url, $bucket, $key = null)
+ {
+
+ $resource = \Qiniu\base64_urlSafeEncode($url);
+ $to = \Qiniu\entry($bucket, $key);
+ $path = '/fetch/' . $resource . '/to/' . $to;
+
+ $ak = $this->auth->getAccessKey();
+
+
+ list($ioHost, $err) = $this->config->getIovipHostV2($ak, $bucket, $this->proxy->makeReqOpt());
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ $url = $ioHost . $path;
+ return $this->postV2($url, null);
+ }
+
+ /**
+ * 从指定URL异步抓取资源,并将该资源存储到指定空间中
+ *
+ * @param string $url 需要抓取的url
+ * @param string $bucket 所在区域的bucket
+ * @param string $host 从指定url下载数据时使用的Host
+ * @param string $key 文件存储的key
+ * @param string $md5 文件md5
+ * @param string $etag 文件etag
+ * @param string $callbackurl 回调URL
+ * @param string $callbackbody 回调Body
+ * @param string $callbackbodytype 回调Body内容类型,默认为"application/x-www-form-urlencoded"
+ * @param string $callbackhost 回调时使用的Host
+ * @param int $file_type 存储文件类型
+ * 0:标准存储(默认)
+ * 1:低频存储
+ * 2:归档存储
+ * 3:深度归档存储
+ * 4:归档直读存储
+ * @param bool $ignore_same_key 如果空间中已经存在同名文件则放弃本次抓取
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/4097/asynch-fetch
+ */
+ public function asynchFetch(
+ $url,
+ $bucket,
+ $host = null,
+ $key = null,
+ $md5 = null,
+ $etag = null,
+ $callbackurl = null,
+ $callbackbody = null,
+ $callbackbodytype = 'application/x-www-form-urlencoded',
+ $callbackhost = null,
+ $file_type = 0,
+ $ignore_same_key = false
+ ) {
+ $path = '/sisyphus/fetch';
+
+ $params = array('url' => $url, 'bucket' => $bucket);
+ \Qiniu\setWithoutEmpty($params, 'host', $host);
+ \Qiniu\setWithoutEmpty($params, 'key', $key);
+ \Qiniu\setWithoutEmpty($params, 'md5', $md5);
+ \Qiniu\setWithoutEmpty($params, 'etag', $etag);
+ \Qiniu\setWithoutEmpty($params, 'callbackurl', $callbackurl);
+ \Qiniu\setWithoutEmpty($params, 'callbackbody', $callbackbody);
+ \Qiniu\setWithoutEmpty($params, 'callbackbodytype', $callbackbodytype);
+ \Qiniu\setWithoutEmpty($params, 'callbackhost', $callbackhost);
+ \Qiniu\setWithoutEmpty($params, 'file_type', $file_type);
+ \Qiniu\setWithoutEmpty($params, 'ignore_same_key', $ignore_same_key);
+ $data = json_encode($params);
+
+ return $this->apiPost($bucket, $path, $data);
+ }
+
+
+ /**
+ * 查询异步第三方资源抓取任务状态
+ *
+ * @param string $zone
+ * @param string $id
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/4097/asynch-fetch
+ */
+ public function asynchFetchStatus($zone, $id)
+ {
+ $scheme = "http://";
+
+ if ($this->config->useHTTPS === true) {
+ $scheme = "https://";
+ }
+
+ $url = $scheme . "api-" . $zone . ".qiniuapi.com/sisyphus/fetch?id=" . $id;
+
+ list($ret, $err) = $this->getV2($url);
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+ return array($ret, null);
+ }
+
+
+ /**
+ * 从镜像源站抓取资源到空间中,如果空间中已经存在,则覆盖该资源
+ *
+ * @param string $bucket 待获取资源所在的空间
+ * @param string $key 代获取资源文件名
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/1293/prefetch
+ */
+ public function prefetch($bucket, $key)
+ {
+ $resource = \Qiniu\entry($bucket, $key);
+ $path = '/prefetch/' . $resource;
+
+ $ak = $this->auth->getAccessKey();
+ list($ioHost, $err) = $this->config->getIovipHostV2($ak, $bucket, $this->proxy->makeReqOpt());
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ $url = $ioHost . $path;
+ return $this->postV2($url, null);
+ }
+
+ /**
+ * 在单次请求中进行多个资源管理操作
+ *
+ * @param array $operations 资源管理操作数组
+ *
+ * @return array 每个资源的处理情况,结果类似:
+ * [
+ * { "code" => , "data" => },
+ * { "code" => },
+ * { "code" => },
+ * { "code" => },
+ * { "code" => , "data" => { "error": "" } },
+ * ...
+ * ]
+ * @link http://developer.qiniu.com/docs/v6/api/reference/rs/batch.html
+ */
+ public function batch($operations)
+ {
+ $scheme = "http://";
+ if ($this->config->useHTTPS === true) {
+ $scheme = "https://";
+ }
+ $params = 'op=' . implode('&op=', $operations);
+ $errResp = new Response(0, 0);
+ if (count($operations) <= 0) {
+ $errResp->error = 'empty operations';
+ return array(null, new Error($scheme . '/batch', $errResp));
+ }
+ $bucket = '';
+ foreach ($operations as $op) {
+ $segments = explode('/', $op);
+ if (count($segments) < 3) {
+ continue;
+ }
+ list($bucket,) = \Qiniu\decodeEntry($segments[2]);
+ }
+ return $this->rsPost($bucket, '/batch', $params);
+ }
+
+ /**
+ * 设置文件的生命周期
+ *
+ * @param string $bucket 设置文件生命周期文件所在的空间
+ * @param string $key 设置文件生命周期文件的文件名
+ * @param int $days 设置该文件多少天后删除,当$days设置为0时表示取消该文件的生命周期
+ *
+ * @return array
+ * @link https://developer.qiniu.com/kodo/api/update-file-lifecycle
+ */
+ public function deleteAfterDays($bucket, $key, $days)
+ {
+ $entry = \Qiniu\entry($bucket, $key);
+ $path = "/deleteAfterDays/$entry/$days";
+ return $this->rsPost($bucket, $path);
+ }
+
+ /**
+ * 更新 object 生命周期
+ *
+ * @param string $bucket 空间名
+ * @param string $key 目标资源
+ * @param int $to_line_after_days 多少天后将文件转为低频存储。
+ * -1 表示取消已设置的转低频存储的生命周期规则;
+ * 0 表示不修改转低频生命周期规则。
+ * @param int $to_archive_ir_after_days 多少天后转为归档直读存储。
+ * -1 表示取消已设置的转归档直读存储的生命周期规则;
+ * 0 表示不修改转归档直读生命周期规则。
+ * @param int $to_archive_after_days 多少天后将文件转为归档存储。
+ * -1 表示取消已设置的转归档存储的生命周期规则;
+ * 0 表示不修改转归档生命周期规则。
+ * @param int $to_deep_archive_after_days 多少天后将文件转为深度归档存储。
+ * -1 表示取消已设置的转深度归档存储的生命周期规则;
+ * 0 表示不修改转深度归档生命周期规则。
+ * @param int $delete_after_days 多少天后将文件删除。
+ * -1 表示取消已设置的删除存储的生命周期规则;
+ * 0 表示不修改删除存储的生命周期规则。
+ * @return array
+ */
+ public function setObjectLifecycle(
+ $bucket,
+ $key,
+ $to_line_after_days = 0,
+ $to_archive_after_days = 0,
+ $to_deep_archive_after_days = 0,
+ $delete_after_days = 0,
+ $to_archive_ir_after_days = 0
+ ) {
+ return $this->setObjectLifecycleWithCond(
+ $bucket,
+ $key,
+ null,
+ $to_line_after_days,
+ $to_archive_after_days,
+ $to_deep_archive_after_days,
+ $delete_after_days,
+ $to_archive_ir_after_days
+ );
+ }
+
+ /**
+ * 更新 object 生命周期
+ *
+ * @param string $bucket 空间名
+ * @param string $key 目标资源
+ * @param int $to_line_after_days 多少天后将文件转为低频存储。
+ * 设置为 -1 表示取消已设置的转低频存储的生命周期规则;
+ * 0 表示不修改转低频生命周期规则。
+ * @param int $to_archive_ir_after_days 多少天后将文件转为归档直读存储。
+ * 设置为 -1 表示取消已设置的转归档直读存储的生命周期规则;
+ * 0 表示不修改转归档直读生命周期规则。
+ * @param int $to_archive_after_days 多少天后将文件转为归档存储。
+ * -1 表示取消已设置的转归档存储的生命周期规则;
+ * 0 表示不修改转归档生命周期规则。
+ * @param int $to_deep_archive_after_days 多少天后将文件转为深度归档存储。
+ * -1 表示取消已设置的转深度归档存储的生命周期规则;
+ * 0 表示不修改转深度归档生命周期规则。
+ * @param int $delete_after_days 多少天后将文件删除。
+ * -1 表示取消已设置的删除存储的生命周期规则;
+ * 0 表示不修改删除存储的生命周期规则。
+ * @param array $cond 匹配条件,只有条件匹配才会设置成功。
+ * 目前支持:hash、mime、fsize、putTime
+ * @return array
+ */
+ public function setObjectLifecycleWithCond(
+ $bucket,
+ $key,
+ $cond = null,
+ $to_line_after_days = 0,
+ $to_archive_after_days = 0,
+ $to_deep_archive_after_days = 0,
+ $delete_after_days = 0,
+ $to_archive_ir_after_days = 0
+ ) {
+ $encodedEntry = \Qiniu\entry($bucket, $key);
+ $path = '/lifecycle/' . $encodedEntry .
+ '/toIAAfterDays/' . $to_line_after_days .
+ '/toArchiveIRAfterDays/' . $to_archive_ir_after_days .
+ '/toArchiveAfterDays/' . $to_archive_after_days .
+ '/toDeepArchiveAfterDays/' . $to_deep_archive_after_days .
+ '/deleteAfterDays/' . $delete_after_days;
+ if ($cond != null) {
+ $condStrArr = array();
+ foreach ($cond as $key => $value) {
+ array_push($condStrArr, $key . '=' . $value);
+ }
+ $condStr = implode('&', $condStrArr);
+ $path .= '/cond' . \Qiniu\base64_urlSafeEncode($condStr);
+ }
+ return $this->rsPost($bucket, $path);
+ }
+
+ private function rsfGet($bucket, $path)
+ {
+ list($host, $err) = $this->config->getRsfHostV2(
+ $this->auth->getAccessKey(),
+ $bucket,
+ $this->proxy->makeReqOpt()
+ );
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ return $this->getV2($host . $path);
+ }
+
+ private function rsGet($bucket, $path)
+ {
+ list($host, $err) = $this->config->getRsHostV2(
+ $this->auth->getAccessKey(),
+ $bucket,
+ $this->proxy->makeReqOpt()
+ );
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ return $this->getV2($host . $path);
+ }
+
+ private function rsPost($bucket, $path, $body = null)
+ {
+ list($host, $err) = $this->config->getRsHostV2(
+ $this->auth->getAccessKey(),
+ $bucket,
+ $this->proxy->makeReqOpt()
+ );
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ return $this->postV2($host . $path, $body);
+ }
+
+ private function apiGet($bucket, $path)
+ {
+ list($host, $err) = $this->config->getApiHostV2(
+ $this->auth->getAccessKey(),
+ $bucket,
+ $this->proxy->makeReqOpt()
+ );
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ return $this->getV2($host . $path);
+ }
+
+ private function apiPost($bucket, $path, $body = null)
+ {
+
+ list($host, $err) = $this->config->getApiHostV2(
+ $this->auth->getAccessKey(),
+ $bucket,
+ $this->proxy->makeReqOpt()
+ );
+
+ if ($err != null) {
+ return array(null, $err);
+ }
+
+ return $this->postV2($host . $path, $body);
+ }
+
+ private function ucGet($path)
+ {
+ $url = $this->config->getUcHost() . $path;
+ return $this->getV2($url);
+ }
+
+ private function ucPost($path, $body = null)
+ {
+ $url = $this->config->getUcHost() . $path;
+ return $this->postV2($url, $body);
+ }
+
+ private function getV2($url)
+ {
+ $headers = $this->auth->authorizationV2($url, 'GET', null, 'application/x-www-form-urlencoded');
+ $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 postV2($url, $body)
+ {
+ $headers = $this->auth->authorizationV2($url, 'POST', $body, 'application/x-www-form-urlencoded');
+ $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);
+ }
+
+ public static function buildBatchCopy($source_bucket, $key_pairs, $target_bucket, $force)
+ {
+ return self::twoKeyBatch('/copy', $source_bucket, $key_pairs, $target_bucket, $force);
+ }
+
+
+ public static function buildBatchRename($bucket, $key_pairs, $force)
+ {
+ return self::buildBatchMove($bucket, $key_pairs, $bucket, $force);
+ }
+
+
+ public static function buildBatchMove($source_bucket, $key_pairs, $target_bucket, $force)
+ {
+ return self::twoKeyBatch('/move', $source_bucket, $key_pairs, $target_bucket, $force);
+ }
+
+
+ public static function buildBatchDelete($bucket, $keys)
+ {
+ return self::oneKeyBatch('/delete', $bucket, $keys);
+ }
+
+
+ public static function buildBatchStat($bucket, $keys)
+ {
+ return self::oneKeyBatch('/stat', $bucket, $keys);
+ }
+
+ public static function buildBatchDeleteAfterDays($bucket, $key_day_pairs)
+ {
+ $data = array();
+ foreach ($key_day_pairs as $key => $day) {
+ array_push($data, '/deleteAfterDays/' . \Qiniu\entry($bucket, $key) . '/' . $day);
+ }
+ return $data;
+ }
+
+ /**
+ * @param string $bucket 空间名
+ * @param array $keys 目标资源
+ * @param int $to_line_after_days 多少天后将文件转为低频存储。
+ * -1 表示取消已设置的转低频存储的生命周期规则;
+ * 0 表示不修改转低频生命周期规则。
+ * @param int $to_archive_ir_after_days 多少天后将文件转为归档直读。
+ * -1 表示取消已设置的转归档只读的生命周期规则;
+ * 0 表示不修改转归档只读周期规则。
+ * @param int $to_archive_after_days 多少天后将文件转为归档存储。
+ * -1 表示取消已设置的转归档存储的生命周期规则;
+ * 0 表示不修改转归档生命周期规则。
+ * @param int $to_deep_archive_after_days 多少天后将文件转为深度归档存储。
+ * -1 表示取消已设置的转深度归档存储的生命周期规则;
+ * 0 表示不修改转深度归档生命周期规则。
+ * @param int $delete_after_days 多少天后将文件删除。
+ * -1 表示取消已设置的删除存储的生命周期规则;
+ * 0 表示不修改删除存储的生命周期规则。
+ *
+ * @retrun array
+ */
+ public static function buildBatchSetObjectLifecycle(
+ $bucket,
+ $keys,
+ $to_line_after_days,
+ $to_archive_after_days,
+ $to_deep_archive_after_days,
+ $delete_after_days,
+ $to_archive_ir_after_days = 0
+ ) {
+ $result = array();
+ foreach ($keys as $key) {
+ $encodedEntry = \Qiniu\entry($bucket, $key);
+ $op = '/lifecycle/' . $encodedEntry .
+ '/toIAAfterDays/' . $to_line_after_days .
+ '/toArchiveIRAfterDays/' . $to_archive_ir_after_days .
+ '/toArchiveAfterDays/' . $to_archive_after_days .
+ '/toDeepArchiveAfterDays/' . $to_deep_archive_after_days .
+ '/deleteAfterDays/' . $delete_after_days;
+ array_push($result, $op);
+ }
+ return $result;
+ }
+
+ public static function buildBatchChangeMime($bucket, $key_mime_pairs)
+ {
+ $data = array();
+ foreach ($key_mime_pairs as $key => $mime) {
+ array_push($data, '/chgm/' . \Qiniu\entry($bucket, $key) . '/mime/' . base64_encode($mime));
+ }
+ return $data;
+ }
+
+ public static function buildBatchChangeType($bucket, $key_type_pairs)
+ {
+ $data = array();
+ foreach ($key_type_pairs as $key => $type) {
+ array_push($data, '/chtype/' . \Qiniu\entry($bucket, $key) . '/type/' . $type);
+ }
+ return $data;
+ }
+
+ public static function buildBatchRestoreAr($bucket, $key_restore_days_pairs)
+ {
+ $data = array();
+ foreach ($key_restore_days_pairs as $key => $restore_days) {
+ array_push($data, '/restoreAr/' . \Qiniu\entry($bucket, $key) . '/freezeAfterDays/' . $restore_days);
+ }
+ return $data;
+ }
+
+ private static function oneKeyBatch($operation, $bucket, $keys)
+ {
+ $data = array();
+ foreach ($keys as $key) {
+ array_push($data, $operation . '/' . \Qiniu\entry($bucket, $key));
+ }
+ return $data;
+ }
+
+ private static function twoKeyBatch($operation, $source_bucket, $key_pairs, $target_bucket, $force)
+ {
+ if ($target_bucket === null) {
+ $target_bucket = $source_bucket;
+ }
+ $data = array();
+ $forceOp = "false";
+ if ($force) {
+ $forceOp = "true";
+ }
+ foreach ($key_pairs as $from_key => $to_key) {
+ $from = \Qiniu\entry($source_bucket, $from_key);
+ $to = \Qiniu\entry($target_bucket, $to_key);
+ array_push($data, $operation . '/' . $from . '/' . $to . "/force/" . $forceOp);
+ }
+ return $data;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Storage/FormUploader.php b/vendor/qiniu/php-sdk/src/Qiniu/Storage/FormUploader.php
new file mode 100644
index 0000000..d68654d
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Storage/FormUploader.php
@@ -0,0 +1,165 @@
+ "",
+ * "key" => ""
+ * ]
+ */
+ 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" => "",
+ * "key" => ""
+ * ]
+ */
+ 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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Storage/ResumeUploader.php b/vendor/qiniu/php-sdk/src/Qiniu/Storage/ResumeUploader.php
new file mode 100644
index 0000000..00e88ef
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Storage/ResumeUploader.php
@@ -0,0 +1,580 @@
+ $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);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Storage/UploadManager.php b/vendor/qiniu/php-sdk/src/Qiniu/Storage/UploadManager.php
new file mode 100644
index 0000000..fcd11fa
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Storage/UploadManager.php
@@ -0,0 +1,176 @@
+config = $config;
+
+ if ($reqOpt === null) {
+ $reqOpt = new RequestOptions();
+ }
+
+ $this->reqOpt = $reqOpt;
+ }
+
+ /**
+ * 上传二进制流到七牛
+ *
+ * @param string $upToken 上传凭证
+ * @param string $key 上传文件名
+ * @param string $data 上传二进制流
+ * @param array $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" => "",
+ * "key" => ""
+ * ]
+ */
+ 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 $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 包含已上传文件的信息,类似:
+ * [
+ * "hash" => "",
+ * "key" => ""
+ * ]
+ * @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;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/src/Qiniu/Zone.php b/vendor/qiniu/php-sdk/src/Qiniu/Zone.php
new file mode 100644
index 0000000..50d60c6
--- /dev/null
+++ b/vendor/qiniu/php-sdk/src/Qiniu/Zone.php
@@ -0,0 +1,58 @@
+ $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
+ */
+ 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
+ */
+ 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
+ */
+ 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";
+ }
+}
diff --git a/vendor/qiniu/php-sdk/test-env.sh b/vendor/qiniu/php-sdk/test-env.sh
new file mode 100644
index 0000000..eedf6b5
--- /dev/null
+++ b/vendor/qiniu/php-sdk/test-env.sh
@@ -0,0 +1,4 @@
+export QINIU_ACCESS_KEY=xxx
+export QINIU_SECRET_KEY=xxx
+export QINIU_TEST_BUCKET=phpsdk
+export QINIU_TEST_DOMAIN=phpsdk.qiniudn.com
\ No newline at end of file
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/AuthTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/AuthTest.php
new file mode 100644
index 0000000..99aec85
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/AuthTest.php
@@ -0,0 +1,296 @@
+sign('test');
+ $this->assertEquals('abcdefghklmnopq:mSNBTR7uS2crJsyFr2Amwv1LaYg=', $token);
+ }
+
+ public function testSignWithData()
+ {
+ global $dummyAuth;
+ $token = $dummyAuth->signWithData('test');
+ $this->assertEquals('abcdefghklmnopq:-jP8eEV9v48MkYiBGs81aDxl60E=:dGVzdA==', $token);
+ }
+
+ public function testSignRequest()
+ {
+ global $dummyAuth;
+ $token = $dummyAuth->signRequest('http://www.qiniu.com?go=1', 'test', '');
+ $this->assertEquals('abcdefghklmnopq:cFyRVoWrE3IugPIMP5YJFTO-O-Y=', $token);
+ $ctype = 'application/x-www-form-urlencoded';
+ $token = $dummyAuth->signRequest('http://www.qiniu.com?go=1', 'test', $ctype);
+ $this->assertEquals($token, 'abcdefghklmnopq:svWRNcacOE-YMsc70nuIYdaa1e4=');
+ }
+
+ public function testPrivateDownloadUrl()
+ {
+ global $dummyAuth;
+ $_SERVER['override_qiniu_auth_time'] = true;
+ $url = $dummyAuth->privateDownloadUrl('http://www.qiniu.com?go=1');
+ $expect = 'http://www.qiniu.com?go=1&e=1234571490&token=abcdefghklmnopq:8vzBeLZ9W3E4kbBLFLW0Xe0u7v4=';
+ $this->assertEquals($expect, $url);
+ unset($_SERVER['override_qiniu_auth_time']);
+ }
+
+ public function testUploadToken()
+ {
+ global $dummyAuth;
+ $_SERVER['override_qiniu_auth_time'] = true;
+ $token = $dummyAuth->uploadToken('1', '2', 3600, array('endUser' => 'y'));
+ // @codingStandardsIgnoreStart
+ $exp = 'abcdefghklmnopq:yyeexeUkPOROoTGvwBjJ0F0VLEo=:eyJlbmRVc2VyIjoieSIsInNjb3BlIjoiMToyIiwiZGVhZGxpbmUiOjEyMzQ1NzE0OTB9';
+ // @codingStandardsIgnoreEnd
+ $this->assertEquals($exp, $token);
+ unset($_SERVER['override_qiniu_auth_time']);
+ }
+
+ public function testSignQiniuAuthorization()
+ {
+ $auth = new Auth("ak", "sk");
+
+ $testCases = array(
+ array(
+ "url" => "",
+ "method" => "",
+ "headers" => array(
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ "Content-Type" => array("application/x-www-form-urlencoded")
+ ),
+ "body" => "{\"name\": \"test\"}",
+ "expectedToken" => "ak:0i1vKClRDWFyNkcTFzwcE7PzX74="
+ ),
+ array(
+ "url" => "",
+ "method" => "",
+ "headers" => array(
+ "Content-Type" => array("application/json")
+ ),
+ "body" => "{\"name\": \"test\"}",
+ "expectedToken" => "ak:K1DI0goT05yhGizDFE5FiPJxAj4="
+ ),
+ array(
+ "url" => "",
+ "method" => "GET",
+ "headers" => array(
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ "Content-Type" => array("application/x-www-form-urlencoded"),
+ ),
+ "body" => "{\"name\": \"test\"}",
+ "expectedToken" => "ak:0i1vKClRDWFyNkcTFzwcE7PzX74="
+ ),
+ array(
+ "url" => "",
+ "method" => "POST",
+ "headers" => array(
+ "Content-Type" => array("application/json"),
+ "X-Qiniu" => array("b"),
+ ),
+ "body" => "{\"name\": \"test\"}",
+ "expectedToken" => "ak:0ujEjW_vLRZxebsveBgqa3JyQ-w="
+ ),
+ array(
+ "url" => "http://upload.qiniup.com",
+ "method" => "",
+ "headers" => array(
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ "Content-Type" => array("application/x-www-form-urlencoded"),
+ ),
+ "body" => "{\"name\": \"test\"}",
+ "expectedToken" => "ak:GShw5NitGmd5TLoo38nDkGUofRw="
+ ),
+ array(
+ "url" => "http://upload.qiniup.com",
+ "method" => "",
+ "headers" => array(
+ "Content-Type" => array("application/json"),
+ "X-Qiniu-Bbb" => array("BBB", "AAA"),
+ "X-Qiniu-Aaa" => array("DDD", "CCC"),
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ ),
+ "body" => "{\"name\": \"test\"}",
+ "expectedToken" => "ak:DhNA1UCaBqSHCsQjMOLRfVn63GQ="
+ ),
+ array(
+ "url" => "http://upload.qiniup.com",
+ "method" => "",
+ "headers" => array(
+ "Content-Type" => array("application/x-www-form-urlencoded"),
+ "X-Qiniu-Bbb" => array("BBB", "AAA"),
+ "X-Qiniu-Aaa" => array("DDD", "CCC"),
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ ),
+ "body" => "name=test&language=go",
+ "expectedToken" => "ak:KUAhrYh32P9bv0COD8ugZjDCmII="
+ ),
+ array(
+ "url" => "http://upload.qiniup.com",
+ "method" => "",
+ "headers" => array(
+ "Content-Type" => array("application/x-www"),
+ "Content-Type" => array("application/x-www-form-urlencoded"),
+ "X-Qiniu-Bbb" => array("BBB", "AAA"),
+ "X-Qiniu-Aaa" => array("DDD", "CCC"),
+ ),
+ "body" => "name=test&language=go",
+ "expectedToken" => "ak:KUAhrYh32P9bv0COD8ugZjDCmII="
+ ),
+ array(
+ "url" => "http://upload.qiniup.com/mkfile/sdf.jpg",
+ "method" => "",
+ "headers" => array(
+ "Content-Type" => array("application/x-www-form-urlencoded"),
+ "X-Qiniu-Bbb" => array("BBB", "AAA"),
+ "X-Qiniu-Aaa" => array("DDD", "CCC"),
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ ),
+ "body" => "name=test&language=go",
+ "expectedToken" => "ak:fkRck5_LeyfwdkyyLk-hyNwGKac="
+ ),
+ array(
+ "url" => "http://upload.qiniup.com/mkfile/sdf.jpg?s=er3&df",
+ "method" => "",
+ "headers" => array(
+ "Content-Type" => array("application/x-www-form-urlencoded"),
+ "X-Qiniu-Bbb" => array("BBB", "AAA"),
+ "X-Qiniu-Aaa" => array("DDD", "CCC"),
+ "X-Qiniu-" => array("a"),
+ "X-Qiniu" => array("b"),
+ ),
+ "body" => "name=test&language=go",
+ "expectedToken" => "ak:PUFPWsEUIpk_dzUvvxTTmwhp3p4="
+ )
+ );
+
+ foreach ($testCases as $testCase) {
+ list($sign, $err) = $auth->signQiniuAuthorization(
+ $testCase["url"],
+ $testCase["method"],
+ $testCase["body"],
+ new Header($testCase["headers"])
+ );
+
+ $this->assertNull($err);
+ $this->assertEquals($testCase["expectedToken"], $sign);
+ }
+ }
+
+ public function testDisableQiniuTimestampSignatureDefault()
+ {
+ $auth = new Auth("ak", "sk");
+ $authedHeaders = $auth->authorizationV2("https://example.com", "GET");
+ $this->assertArrayHasKey("X-Qiniu-Date", $authedHeaders);
+ }
+
+ public function testDisableQiniuTimestampSignature()
+ {
+ $auth = new Auth("ak", "sk", array(
+ "disableQiniuTimestampSignature" => true
+ ));
+ $authedHeaders = $auth->authorizationV2("https://example.com", "GET");
+ $this->assertArrayNotHasKey("X-Qiniu-Date", $authedHeaders);
+ }
+ public function testDisableQiniuTimestampSignatureEnv()
+ {
+ putenv("DISABLE_QINIU_TIMESTAMP_SIGNATURE=true");
+ $auth = new Auth("ak", "sk");
+ $authedHeaders = $auth->authorizationV2("https://example.com", "GET");
+ $this->assertArrayNotHasKey("X-Qiniu-Date", $authedHeaders);
+ putenv('DISABLE_QINIU_TIMESTAMP_SIGNATURE');
+ }
+ public function testDisableQiniuTimestampSignatureEnvBeIgnored()
+ {
+ putenv("DISABLE_QINIU_TIMESTAMP_SIGNATURE=true");
+ $auth = new Auth("ak", "sk", array(
+ "disableQiniuTimestampSignature" => false
+ ));
+ $authedHeaders = $auth->authorizationV2("https://example.com", "GET");
+ $this->assertArrayHasKey("X-Qiniu-Date", $authedHeaders);
+ putenv('DISABLE_QINIU_TIMESTAMP_SIGNATURE');
+ }
+ public function testQboxVerifyCallbackShouldOkWithRequiredOptions()
+ {
+ $auth = new Auth('abcdefghklmnopq', '1234567890');
+ $ok = $auth->verifyCallback(
+ 'application/x-www-form-urlencoded',
+ 'QBox abcdefghklmnopq:T7F-SjxX7X2zI4Fc1vANiNt1AUE=',
+ 'https://test.qiniu.com/callback',
+ 'name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2&location=Shanghai&price=1500.00&uid=123'
+ );
+ $this->assertTrue($ok);
+ }
+ public function testQboxVerifyCallbackShouldOkWithOmitOptions()
+ {
+ $auth = new Auth('abcdefghklmnopq', '1234567890');
+ $ok = $auth->verifyCallback(
+ 'application/x-www-form-urlencoded',
+ 'QBox abcdefghklmnopq:T7F-SjxX7X2zI4Fc1vANiNt1AUE=',
+ 'https://test.qiniu.com/callback',
+ 'name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2&location=Shanghai&price=1500.00&uid=123',
+ 'POST', // this should be omit
+ array(
+ 'X-Qiniu-Bbb' => 'BBB'
+ ) // this should be omit
+ );
+ $this->assertTrue($ok);
+ }
+ public function testQiniuVerifyCallbackShouldOk()
+ {
+ $auth = new Auth('abcdefghklmnopq', '1234567890');
+ $ok = $auth->verifyCallback(
+ 'application/x-www-form-urlencoded',
+ 'Qiniu abcdefghklmnopq:ZqS7EZuAKrhZaEIxqNGxDJi41IQ=',
+ 'https://test.qiniu.com/callback',
+ 'name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2&location=Shanghai&price=1500.00&uid=123',
+ 'GET',
+ array(
+ 'X-Qiniu-Bbb' => 'BBB'
+ )
+ );
+ $this->assertTrue($ok);
+ }
+ public function testQiniuVerifyCallbackShouldFailed()
+ {
+ $auth = new Auth('abcdefghklmnopq', '1234567890');
+ $ok = $auth->verifyCallback(
+ 'application/x-www-form-urlencoded',
+ 'Qiniu abcdefghklmnopq:ZqS7EZuAKrhZaEIxqNGxDJi41IQ=',
+ 'https://test.qiniu.com/callback',
+ 'name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2&location=Shanghai&price=1500.00&uid=123',
+ 'POST',
+ array(
+ 'X-Qiniu-Bbb' => 'BBB'
+ )
+ );
+ $this->assertFalse($ok);
+ }
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Base64Test.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Base64Test.php
new file mode 100644
index 0000000..fed3da0
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Base64Test.php
@@ -0,0 +1,16 @@
+assertEquals($a, \Qiniu\base64_urlSafeDecode($b));
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/BucketTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/BucketTest.php
new file mode 100644
index 0000000..0467698
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/BucketTest.php
@@ -0,0 +1,733 @@
+batch($ops);
+ }
+
+ private static function getObjectKey($key)
+ {
+ $result = $key . rand();
+
+ self::$bucketManager->copy(
+ self::$bucketName,
+ $key,
+ self::$bucketName,
+ $result
+ );
+
+ self::$keysToCleanup[] = $result;
+
+ return $result;
+ }
+
+ public function testBuckets()
+ {
+
+ list($list, $error) = self::$bucketManager->buckets();
+ $this->assertNull($error);
+ $this->assertTrue(in_array(self::$bucketName, $list));
+
+ list($list2, $error) = self::$dummyBucketManager->buckets();
+ $this->assertEquals(401, $error->code());
+ $this->assertNotNull($error->message());
+ $this->assertNotNull($error->getResponse());
+ $this->assertNull($list2);
+ }
+
+ public function testListBuckets()
+ {
+ list($ret, $error) = self::$bucketManager->listbuckets('z0');
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testCreateBucket()
+ {
+ list($ret, $error) = self::$bucketManager->createBucket(self::$bucketToCreate);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testDeleteBucket()
+ {
+ list($ret, $error) = self::$bucketManager->deleteBucket(self::$bucketToCreate);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testDomains()
+ {
+ list($ret, $error) = self::$bucketManager->domains(self::$bucketName);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testBucketInfo()
+ {
+ list($ret, $error) = self::$bucketManager->bucketInfo(self::$bucketName);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testBucketInfos()
+ {
+ list($ret, $error) = self::$bucketManager->bucketInfos('z0');
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testList()
+ {
+ list($ret, $error) = self::$bucketManager->listFiles(self::$bucketName, null, null, 10);
+ $this->assertNull($error);
+ $this->assertNotNull($ret['items'][0]);
+ $this->assertNotNull($ret['marker']);
+ }
+
+ public function testListFilesv2()
+ {
+ list($ret, $error) = self::$bucketManager->listFilesv2(self::$bucketName, null, null, 10);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testBucketLifecycleRule()
+ {
+ // delete
+ self::$bucketManager->deleteBucketLifecycleRule(self::$bucketName, self::$bucketLifeRuleName);
+
+ // add
+ list($ret, $error) = self::$bucketManager->bucketLifecycleRule(
+ self::$bucketName,
+ self::$bucketLifeRuleName,
+ self::$bucketLifeRulePrefix,
+ 80,
+ 70,
+ 72,
+ 74,
+ 71
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+
+ // get
+ list($ret, $error) = self::$bucketManager->getBucketLifecycleRules(self::$bucketName);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ $rule = null;
+ foreach ($ret as $r) {
+ if ($r["name"] === self::$bucketLifeRuleName) {
+ $rule = $r;
+ break;
+ }
+ }
+ $this->assertNotNull($rule);
+ $this->assertEquals(self::$bucketLifeRulePrefix, $rule["prefix"]);
+ $this->assertEquals(80, $rule["delete_after_days"]);
+ $this->assertEquals(70, $rule["to_line_after_days"]);
+ $this->assertEquals(71, $rule["to_archive_ir_after_days"]);
+ $this->assertEquals(72, $rule["to_archive_after_days"]);
+ $this->assertEquals(74, $rule["to_deep_archive_after_days"]);
+
+ // update
+ list($ret, $error) = self::$bucketManager->updateBucketLifecycleRule(
+ self::$bucketName,
+ self::$bucketLifeRuleName,
+ 'update-' . self::$bucketLifeRulePrefix,
+ 90,
+ 75,
+ 80,
+ 85,
+ 78
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+
+ // get
+ list($ret, $error) = self::$bucketManager->getBucketLifecycleRules(self::$bucketName);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ $rule = null;
+ foreach ($ret as $r) {
+ if ($r["name"] === self::$bucketLifeRuleName) {
+ $rule = $r;
+ break;
+ }
+ }
+ $this->assertNotNull($rule);
+ $this->assertEquals('update-' . self::$bucketLifeRulePrefix, $rule["prefix"]);
+ $this->assertEquals(90, $rule["delete_after_days"]);
+ $this->assertEquals(75, $rule["to_line_after_days"]);
+ $this->assertEquals(78, $rule["to_archive_ir_after_days"]);
+ $this->assertEquals(80, $rule["to_archive_after_days"]);
+ $this->assertEquals(85, $rule["to_deep_archive_after_days"]);
+
+ // delete
+ list($ret, $error) = self::$bucketManager->deleteBucketLifecycleRule(
+ self::$bucketName,
+ self::$bucketLifeRuleName
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testPutBucketEvent()
+ {
+ list($ret, $error) = self::$bucketManager->putBucketEvent(
+ self::$bucketName,
+ self::$bucketEventName,
+ self::$bucketEventPrefix,
+ 'img',
+ array('copy'),
+ self::$customCallbackURL
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testUpdateBucketEvent()
+ {
+ list($ret, $error) = self::$bucketManager->updateBucketEvent(
+ self::$bucketName,
+ self::$bucketEventName,
+ self::$bucketEventPrefix,
+ 'video',
+ array('copy'),
+ self::$customCallbackURL
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testGetBucketEvents()
+ {
+ list($ret, $error) = self::$bucketManager->getBucketEvents(self::$bucketName);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testDeleteBucketEvent()
+ {
+ list($ret, $error) = self::$bucketManager->deleteBucketEvent(self::$bucketName, self::$bucketEventName);
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testStat()
+ {
+ list($stat, $error) = self::$bucketManager->stat(self::$bucketName, self::$key);
+ $this->assertNull($error);
+ $this->assertNotNull($stat);
+ $this->assertNotNull($stat['hash']);
+
+ list($stat, $error) = self::$bucketManager->stat(self::$bucketName, 'nofile');
+ $this->assertEquals(612, $error->code());
+ $this->assertNotNull($error->message());
+ $this->assertNull($stat);
+
+ list($stat, $error) = self::$bucketManager->stat('nobucket', 'nofile');
+ $this->assertEquals(631, $error->code());
+ $this->assertNotNull($error->message());
+ $this->assertNull($stat);
+ }
+
+ public function testDelete()
+ {
+ $fileToDel = self::getObjectKey(self::$key);
+ list(, $error) = self::$bucketManager->delete(self::$bucketName, $fileToDel);
+ $this->assertNull($error);
+ }
+
+
+ public function testRename()
+ {
+ $fileToRename = self::getObjectKey(self::$key);
+ $fileRenamed = $fileToRename . 'new';
+ list(, $error) = self::$bucketManager->rename(self::$bucketName, $fileToRename, $fileRenamed);
+ $this->assertNull($error);
+ self::$keysToCleanup[] = $fileRenamed;
+ }
+
+
+ public function testCopy()
+ {
+ $fileToCopy = self::getObjectKey(self::$key2);
+ $fileCopied = $fileToCopy . 'copied';
+
+ //test force copy
+ list(, $error) = self::$bucketManager->copy(
+ self::$bucketName,
+ $fileToCopy,
+ self::$bucketName,
+ $fileCopied,
+ true
+ );
+ $this->assertNull($error);
+
+ list($fileToCopyStat,) = self::$bucketManager->stat(self::$bucketName, $fileToCopy);
+ list($fileCopiedStat,) = self::$bucketManager->stat(self::$bucketName, $fileCopied);
+
+ $this->assertEquals($fileToCopyStat['hash'], $fileCopiedStat['hash']);
+
+ self::$keysToCleanup[] = $fileCopied;
+ }
+
+
+ public function testChangeMime()
+ {
+ $fileToChange = self::getObjectKey('php-sdk.html');
+ list(, $error) = self::$bucketManager->changeMime(
+ self::$bucketName,
+ $fileToChange,
+ 'text/plain'
+ );
+ $this->assertNull($error);
+
+ list($ret, $error) = self::$bucketManager->stat(
+ self::$bucketName,
+ $fileToChange
+ );
+ $this->assertNull($error);
+ $this->assertEquals('text/plain', $ret['mimeType']);
+ }
+
+ public function testPrefetch()
+ {
+ list($ret, $error) = self::$bucketManager->prefetch(
+ self::$bucketName,
+ 'php-sdk.html'
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ }
+
+ public function testPrefetchFailed()
+ {
+ list($ret, $error) = self::$bucketManager->prefetch(
+ 'fakebucket',
+ 'php-sdk.html'
+ );
+ $this->assertNotNull($error);
+ $this->assertNull($ret);
+ }
+
+ public function testFetch()
+ {
+ list($ret, $error) = self::$bucketManager->fetch(
+ 'http://developer.qiniu.com/docs/v6/sdk/php-sdk.html',
+ self::$bucketName,
+ 'fetch.html'
+ );
+ $this->assertNull($error);
+ $this->assertArrayHasKey('hash', $ret);
+
+ list($ret, $error) = self::$bucketManager->fetch(
+ 'http://developer.qiniu.com/docs/v6/sdk/php-sdk.html',
+ self::$bucketName,
+ ''
+ );
+ $this->assertNull($error);
+ $this->assertArrayHasKey('key', $ret);
+
+ list($ret, $error) = self::$bucketManager->fetch(
+ 'http://developer.qiniu.com/docs/v6/sdk/php-sdk.html',
+ self::$bucketName
+ );
+ $this->assertNull($error);
+ $this->assertArrayHasKey('key', $ret);
+ }
+
+ public function testFetchFailed()
+ {
+ list($ret, $error) = self::$bucketManager->fetch(
+ 'http://developer.qiniu.com/docs/v6/sdk/php-sdk.html',
+ 'fakebucket'
+ );
+ $this->assertNotNull($error);
+ $this->assertNull($ret);
+ }
+
+ public function testAsynchFetch()
+ {
+ list($ret, $error) = self::$bucketManager->asynchFetch(
+ 'http://devtools.qiniu.com/qiniu.png',
+ self::$bucketName,
+ null,
+ 'qiniu.png'
+ );
+ $this->assertNull($error);
+ $this->assertArrayHasKey('id', $ret);
+
+ list($ret, $error) = self::$bucketManager->asynchFetch(
+ 'http://devtools.qiniu.com/qiniu.png',
+ self::$bucketName,
+ null,
+ ''
+ );
+ $this->assertNull($error);
+ $this->assertArrayHasKey('id', $ret);
+
+ list($ret, $error) = self::$bucketManager->asynchFetch(
+ 'http://devtools.qiniu.com/qiniu.png',
+ self::$bucketName
+ );
+ $this->assertNull($error);
+ $this->assertArrayHasKey('id', $ret);
+ }
+
+ public function testAsynchFetchFailed()
+ {
+ list($ret, $error) = self::$bucketManager->asynchFetch(
+ 'http://devtools.qiniu.com/qiniu.png',
+ 'fakebucket'
+ );
+ $this->assertNotNull($error);
+ $this->assertNull($ret);
+ }
+
+
+ public function testBatchCopy()
+ {
+ $key = 'copyto' . rand();
+ $ops = BucketManager::buildBatchCopy(
+ self::$bucketName,
+ array(self::$key => $key),
+ self::$bucketName,
+ true
+ );
+ list($ret, $error) = self::$bucketManager->batch($ops);
+ $this->assertNull($error);
+ $this->assertEquals(200, $ret[0]['code']);
+
+ self::$keysToCleanup[] = $key;
+ }
+
+ public function testBatchMove()
+ {
+ $fileToMove = self::getObjectKey(self::$key);
+ $fileMoved = $fileToMove . 'to';
+ $ops = BucketManager::buildBatchMove(
+ self::$bucketName,
+ array($fileToMove => $fileMoved),
+ self::$bucketName,
+ true
+ );
+ list($ret, $error) = self::$bucketManager->batch($ops);
+ $this->assertNull($error);
+ $this->assertEquals(200, $ret[0]['code']);
+ self::$keysToCleanup[] = $fileMoved;
+ }
+
+ public function testBatchRename()
+ {
+ $fileToRename = self::getObjectKey(self::$key);
+ $fileRenamed = $fileToRename . 'to';
+
+ $ops = BucketManager::buildBatchRename(
+ self::$bucketName,
+ array($fileToRename => $fileRenamed),
+ true
+ );
+ list($ret, $error) = self::$bucketManager->batch($ops);
+ $this->assertNull($error);
+ $this->assertEquals(200, $ret[0]['code']);
+
+ self::$keysToCleanup[] = $fileRenamed;
+ }
+
+ public function testBatchStat()
+ {
+ $ops = BucketManager::buildBatchStat(self::$bucketName, array('php-sdk.html'));
+ list($ret, $error) = self::$bucketManager->batch($ops);
+ $this->assertNull($error);
+ $this->assertEquals(200, $ret[0]['code']);
+ }
+
+ public function testBatchChangeTypeAndBatchRestoreAr()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ $ops = BucketManager::buildBatchChangeType(self::$bucketName, array($key => 2)); // 2 Archive
+ list($ret, $error) = self::$bucketManager->batch($ops);
+ $this->assertNull($error);
+ $this->assertEquals(200, $ret[0]['code']);
+
+ $ops = BucketManager::buildBatchRestoreAr(self::$bucketName, array($key => 1)); // 1 day
+ list($ret, $error) = self::$bucketManager->batch($ops);
+ $this->assertNull($error);
+ $this->assertEquals(200, $ret[0]['code']);
+ }
+
+ public function testDeleteAfterDays()
+ {
+ $key = "noexist" . rand();
+ list($ret, $error) = self::$bucketManager->deleteAfterDays(self::$bucketName, $key, 1);
+ $this->assertNotNull($error);
+ $this->assertNull($ret);
+
+ $key = self::getObjectKey(self::$key);
+ list(, $error) = self::$bucketManager->deleteAfterDays(self::$bucketName, $key, 1);
+ $this->assertNull($error);
+
+ list($ret, $error) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($error);
+ $this->assertGreaterThan(23 * 3600, $ret['expiration'] - time());
+ $this->assertLessThan(48 * 3600, $ret['expiration'] - time());
+ }
+
+ public function testSetObjectLifecycle()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ list(, $err) = self::$bucketManager->setObjectLifecycle(
+ self::$bucketName,
+ $key,
+ 10,
+ 20,
+ 30,
+ 40,
+ 15
+ );
+ $this->assertNull($err);
+
+ list($ret, $error) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($error);
+ $this->assertNotNull($ret['transitionToIA']);
+ $this->assertNotNull($ret['transitionToArchiveIR']);
+ $this->assertNotNull($ret['transitionToARCHIVE']);
+ $this->assertNotNull($ret['transitionToDeepArchive']);
+ $this->assertNotNull($ret['expiration']);
+ }
+
+ public function testSetObjectLifecycleWithCond()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ list($ret, $err) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($err);
+ $key_hash = $ret['hash'];
+ $key_fsize = $ret['fsize'];
+
+ list(, $err) = self::$bucketManager->setObjectLifecycleWithCond(
+ self::$bucketName,
+ $key,
+ array(
+ 'hash' => $key_hash,
+ 'fsize' => $key_fsize
+ ),
+ 10,
+ 20,
+ 30,
+ 40,
+ 15
+ );
+ $this->assertNull($err);
+
+ list($ret, $error) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($error);
+ $this->assertNotNull($ret['transitionToIA']);
+ $this->assertNotNull($ret['transitionToArchiveIR']);
+ $this->assertNotNull($ret['transitionToARCHIVE']);
+ $this->assertNotNull($ret['transitionToDeepArchive']);
+ $this->assertNotNull($ret['expiration']);
+ }
+
+ public function testBatchSetObjectLifecycle()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ $ops = BucketManager::buildBatchSetObjectLifecycle(
+ self::$bucketName,
+ array($key),
+ 10,
+ 20,
+ 30,
+ 40,
+ 15
+ );
+ list($ret, $err) = self::$bucketManager->batch($ops);
+ $this->assertNull($err);
+ $this->assertEquals(200, $ret[0]['code']);
+ }
+
+ public function testGetCorsRules()
+ {
+ list(, $err) = self::$bucketManager->getCorsRules(self::$bucketName);
+ $this->assertNull($err);
+ }
+
+ public function testPutBucketAccessStyleMode()
+ {
+ list(, $err) = self::$bucketManager->putBucketAccessStyleMode(self::$bucketName, 0);
+ $this->assertNull($err);
+ }
+
+ public function testPutBucketAccessMode()
+ {
+ list(, $err) = self::$bucketManager->putBucketAccessMode(self::$bucketName, 0);
+ $this->assertNull($err);
+ }
+
+ public function testPutReferAntiLeech()
+ {
+ list(, $err) = self::$bucketManager->putReferAntiLeech(self::$bucketName, 0, "1", "*");
+ $this->assertNull($err);
+ }
+
+ public function testPutBucketMaxAge()
+ {
+ list(, $err) = self::$bucketManager->putBucketMaxAge(self::$bucketName, 31536000);
+ $this->assertNull($err);
+ }
+
+ public function testPutBucketQuota()
+ {
+ list(, $err) = self::$bucketManager->putBucketQuota(self::$bucketName, -1, -1);
+ $this->assertNull($err);
+ }
+
+ public function testGetBucketQuota()
+ {
+ list(, $err) = self::$bucketManager->getBucketQuota(self::$bucketName);
+ $this->assertNull($err);
+ }
+
+ public function testChangeType()
+ {
+ $fileToChange = self::getObjectKey(self::$key);
+
+ list(, $err) = self::$bucketManager->changeType(self::$bucketName, $fileToChange, 0);
+ $this->assertNull($err);
+
+ list(, $err) = self::$bucketManager->changeType(self::$bucketName, $fileToChange, 1);
+ $this->assertNull($err);
+ }
+
+ public function testArchiveRestoreAr()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ self::$bucketManager->changeType(self::$bucketName, $key, 2);
+
+ list(, $err) = self::$bucketManager->restoreAr(self::$bucketName, $key, 2);
+ $this->assertNull($err);
+
+ list($ret, $err) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($err);
+
+ $this->assertEquals(2, $ret["type"]);
+
+ // restoreStatus
+ // null means frozen;
+ // 1 means be unfreezing;
+ // 2 means be unfrozen;
+ $this->assertNotNull($ret["restoreStatus"]);
+ $this->assertContains($ret["restoreStatus"], array(1, 2));
+ }
+
+ public function testDeepArchiveRestoreAr()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ self::$bucketManager->changeType(self::$bucketName, $key, 3);
+
+ list(, $err) = self::$bucketManager->restoreAr(self::$bucketName, $key, 1);
+ $this->assertNull($err);
+ list($ret, $err) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($err);
+
+ $this->assertEquals(3, $ret["type"]);
+
+ // restoreStatus
+ // null means frozen;
+ // 1 means be unfreezing;
+ // 2 means be unfrozen;
+ $this->assertNotNull($ret["restoreStatus"]);
+ $this->assertContains($ret["restoreStatus"], array(1, 2));
+ }
+
+ public function testChangeStatus()
+ {
+ $key = self::getObjectKey(self::$key);
+
+ list(, $err) = self::$bucketManager->changeStatus(self::$bucketName, $key, 1);
+ $this->assertNull($err);
+ list($ret, $err) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($err);
+ $this->assertEquals(1, $ret['status']);
+
+ list(, $err) = self::$bucketManager->changeStatus(self::$bucketName, $key, 0);
+ $this->assertNull($err);
+ list($ret, $err) = self::$bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($err);
+ $this->assertArrayNotHasKey('status', $ret);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/CdnManagerTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/CdnManagerTest.php
new file mode 100644
index 0000000..baa9486
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/CdnManagerTest.php
@@ -0,0 +1,151 @@
+cdnManager = new CdnManager($testAuth);
+
+ global $timestampAntiLeechEncryptKey;
+ $this->encryptKey = $timestampAntiLeechEncryptKey;
+
+ global $testStartDate;
+ $this->testStartDate = $testStartDate;
+
+ global $testEndDate;
+ $this->testEndDate = $testEndDate;
+
+ global $testGranularity;
+ $this->testGranularity = $testGranularity;
+
+ global $testLogDate;
+ $this->testLogDate = $testLogDate;
+
+ global $customDomain;
+ $this->refreshUrl = $customDomain . '/sdktest.png';
+ $this->refreshDirs = $customDomain;
+ $this->customDomain = $customDomain;
+
+ global $customDomain2;
+ $this->customDomain2 = $customDomain2;
+ }
+
+ public function testRefreshUrls()
+ {
+ list($ret, $err) = $this->cdnManager->refreshUrls(array($this->refreshUrl));
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testRefreshDirs()
+ {
+ list($ret, $err) = $this->cdnManager->refreshDirs(array($this->refreshDirs));
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testRefreshUrlsAndDirs()
+ {
+ list($ret, $err) = $this->cdnManager->refreshUrlsAndDirs(array($this->refreshUrl), array($this->refreshDirs));
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testGetCdnRefreshList()
+ {
+ list($ret, $err) = $this->cdnManager->getCdnRefreshList(null, null, null, 'success');
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testPrefetchUrls()
+ {
+ list($ret, $err) = $this->cdnManager->prefetchUrls(array($this->refreshUrl));
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testGetCdnPrefetchList()
+ {
+ list($ret, $err) = $this->cdnManager->getCdnPrefetchList(null, null, 'success');
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testGetBandwidthData()
+ {
+ list($ret, $err) = $this->cdnManager->getBandwidthData(
+ array($this->customDomain2),
+ $this->testStartDate,
+ $this->testEndDate,
+ $this->testGranularity
+ );
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testGetFluxData()
+ {
+ list($ret, $err) = $this->cdnManager->getFluxData(
+ array($this->customDomain2),
+ $this->testStartDate,
+ $this->testEndDate,
+ $this->testGranularity
+ );
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testGetCdnLogList()
+ {
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ list($ret, $err) = $this->cdnManager->getCdnLogList(array($domain), $this->testLogDate);
+ $this->assertNull($err);
+ $this->assertNotNull($ret);
+ }
+
+ public function testCreateTimestampAntiLeechUrl()
+ {
+ $signUrl = $this->cdnManager->createTimestampAntiLeechUrl($this->refreshUrl, $this->encryptKey, 3600);
+ $response = Client::get($signUrl);
+ $this->assertNull($response->error);
+ $this->assertEquals($response->statusCode, 200);
+
+ $signUrl = $this->cdnManager->createTimestampAntiLeechUrl(
+ $this->refreshUrl . '?qiniu',
+ $this->encryptKey,
+ 3600
+ );
+ $response = Client::get($signUrl);
+ $this->assertNull($response->error);
+ $this->assertEquals($response->statusCode, 200);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ConfigTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ConfigTest.php
new file mode 100644
index 0000000..3c39a5c
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ConfigTest.php
@@ -0,0 +1,118 @@
+accessKey = $accessKey;
+ global $bucketName;
+ $this->bucketName = $bucketName;
+ }
+
+ public function testGetApiHost()
+ {
+ $conf = new Config();
+ $hasException = false;
+ $apiHost = '';
+ try {
+ $apiHost = $conf->getApiHost($this->accessKey, $this->bucketName);
+ } catch (\Exception $e) {
+ $hasException = true;
+ }
+ $this->assertFalse($hasException);
+ }
+
+ public function testGetApiHostErrored()
+ {
+ $conf = new Config();
+ $hasException = false;
+ try {
+ $conf->getApiHost($this->accessKey, "fakebucket");
+ } catch (\Exception $e) {
+ $hasException = true;
+ }
+ $this->assertTrue($hasException);
+ }
+
+ public function testGetApiHostV2()
+ {
+ $conf = new Config();
+ list($apiHost, $err) = $conf->getApiHostV2($this->accessKey, $this->bucketName);
+ $this->assertNull($err);
+ }
+
+ public function testGetApiHostV2Errored()
+ {
+ $conf = new Config();
+ list($apiHost, $err) = $conf->getApiHostV2($this->accessKey, "fakebucket");
+ $this->assertNotNull($err->code());
+ $this->assertEquals(631, $err->code());
+ $this->assertNull($apiHost);
+ }
+
+ public function testSetUcHost()
+ {
+ $conf = new Config();
+ $this->assertEquals('http://' . Config::UC_HOST, $conf->getUcHost());
+ $conf->setUcHost("uc.example.com");
+ $this->assertEquals("http://uc.example.com", $conf->getUcHost());
+
+ $conf = new Config();
+ $conf->useHTTPS = true;
+ $this->assertEquals('https://' . Config::UC_HOST, $conf->getUcHost());
+ $conf->setUcHost("uc.example.com");
+ $this->assertEquals("https://uc.example.com", $conf->getUcHost());
+ }
+
+ public function testGetRegionWithCustomDomain()
+ {
+ $conf = new Config();
+ $conf->setQueryRegionHost(
+ "uc.qbox.me"
+ );
+ list(, $err) = $conf->getRsHostV2($this->accessKey, $this->bucketName);
+ $this->assertNull($err);
+ }
+
+ public function testGetRegionWithBackupDomains()
+ {
+ $conf = new Config();
+ $conf->setQueryRegionHost(
+ "fake-uc.phpsdk.qiniu.com",
+ array(
+ "unavailable-uc.phpsdk.qiniu.com",
+ Config::UC_HOST // real uc
+ )
+ );
+ list(, $err) = $conf->getRsHostV2($this->accessKey, $this->bucketName);
+ $this->assertNull($err);
+ }
+
+ public function testGetRegionWithUcAndBackupDomains()
+ {
+ $conf = new Config();
+ $conf->setUcHost("fake-uc.phpsdk.qiniu.com");
+ $conf->setBackupQueryRegionHosts(
+ array(
+ "unavailable-uc.phpsdk.qiniu.com",
+ Config::UC_HOST // real uc
+ )
+ );
+ list(, $err) = $conf->getRsHostV2($this->accessKey, $this->bucketName);
+ $this->assertNull($err);
+ }
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Crc32Test.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Crc32Test.php
new file mode 100644
index 0000000..63e24fd
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Crc32Test.php
@@ -0,0 +1,23 @@
+assertEquals('1352841281', $b);
+ }
+
+ public function testFile()
+ {
+ $b = \Qiniu\crc32_file(__file__);
+ $c = \Qiniu\crc32_file(__file__);
+ $this->assertEquals($c, $b);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/DownloadTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/DownloadTest.php
new file mode 100644
index 0000000..9b4b034
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/DownloadTest.php
@@ -0,0 +1,27 @@
+privateDownloadUrl($base_url);
+ $response = Client::get($private_url);
+ $this->assertEquals(200, $response->statusCode);
+ }
+
+ public function testFop()
+ {
+ global $testAuth;
+ $base_url = 'http://sdk.peterpy.cn/gogopher.jpg?exif';
+ $private_url = $testAuth->privateDownloadUrl($base_url);
+ $response = Client::get($private_url);
+ $this->assertEquals(200, $response->statusCode);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/EntryTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/EntryTest.php
new file mode 100644
index 0000000..73bfac4
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/EntryTest.php
@@ -0,0 +1,88 @@
+assertEquals('cWluaXVwaG90b3M6Z29nb3BoZXIuanBn', $encodeEntryURI);
+ }
+
+ public function testKeyEmpty()
+ {
+ $bucket = 'qiniuphotos';
+ $key = '';
+ $encodeEntryURI = Qiniu\entry($bucket, $key);
+ $this->assertEquals('cWluaXVwaG90b3M6', $encodeEntryURI);
+ }
+
+ public function testKeyNull()
+ {
+ $bucket = 'qiniuphotos';
+ $key = null;
+ $encodeEntryURI = Qiniu\entry($bucket, $key);
+ $this->assertEquals('cWluaXVwaG90b3M=', $encodeEntryURI);
+ }
+
+ public function testKeyNeedReplacePlusSymbol()
+ {
+ $bucket = 'qiniuphotos';
+ $key = '012ts>a';
+ $encodeEntryURI = Qiniu\entry($bucket, $key);
+ $this->assertEquals('cWluaXVwaG90b3M6MDEydHM-YQ==', $encodeEntryURI);
+ }
+
+ public function testKeyNeedReplaceSlashSymbol()
+ {
+ $bucket = 'qiniuphotos';
+ $key = '012ts?a';
+ $encodeEntryURI = Qiniu\entry($bucket, $key);
+ $this->assertEquals('cWluaXVwaG90b3M6MDEydHM_YQ==', $encodeEntryURI);
+ }
+ public function testDecodeEntry()
+ {
+ $entry = 'cWluaXVwaG90b3M6Z29nb3BoZXIuanBn';
+ list($bucket, $key) = Qiniu\decodeEntry($entry);
+ $this->assertEquals('qiniuphotos', $bucket);
+ $this->assertEquals('gogopher.jpg', $key);
+ }
+
+ public function testDecodeEntryWithEmptyKey()
+ {
+ $entry = 'cWluaXVwaG90b3M6';
+ list($bucket, $key) = Qiniu\decodeEntry($entry);
+ $this->assertEquals('qiniuphotos', $bucket);
+ $this->assertEquals('', $key);
+ }
+
+ public function testDecodeEntryWithNullKey()
+ {
+ $entry = 'cWluaXVwaG90b3M=';
+ list($bucket, $key) = Qiniu\decodeEntry($entry);
+ $this->assertEquals('qiniuphotos', $bucket);
+ $this->assertNull($key);
+ }
+
+ public function testDecodeEntryWithPlusSymbol()
+ {
+ $entry = 'cWluaXVwaG90b3M6MDEydHM-YQ==';
+ list($bucket, $key) = Qiniu\decodeEntry($entry);
+ $this->assertEquals('qiniuphotos', $bucket);
+ $this->assertEquals('012ts>a', $key);
+ }
+
+ public function testDecodeEntryWithSlashSymbol()
+ {
+ $entry = 'cWluaXVwaG90b3M6MDEydHM_YQ==';
+ list($bucket, $key) = Qiniu\decodeEntry($entry);
+ $this->assertEquals('qiniuphotos', $bucket);
+ $this->assertEquals('012ts?a', $key);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/EtagTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/EtagTest.php
new file mode 100644
index 0000000..4e09a78
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/EtagTest.php
@@ -0,0 +1,54 @@
+assertEquals('Fto5o-5ea0sNMlW_75VgGJCv2AcJ', $r);
+ $this->assertNull($error);
+ }
+
+ public function testLess4M()
+ {
+ $file = qiniuTempFile(3 * 1024 * 1024, false);
+ list($r, $error) = Etag::sum($file);
+ unlink($file);
+ $this->assertEquals('Fs5BpnAjRykYTg6o5E09cjuXrDkG', $r);
+ $this->assertNull($error);
+ }
+
+ public function test4M()
+ {
+ $file = qiniuTempFile(4 * 1024 * 1024, false);
+ list($r, $error) = Etag::sum($file);
+ unlink($file);
+ $this->assertEquals('FiuKULnybewpEnrfTmxjsxc-3dWp', $r);
+ $this->assertNull($error);
+ }
+
+ public function testMore4M()
+ {
+ $file = qiniuTempFile(5 * 1024 * 1024, false);
+ list($r, $error) = Etag::sum($file);
+ unlink($file);
+ $this->assertEquals('lhvyfIWMYFTq4s4alzlhXoAkqfVL', $r);
+ $this->assertNull($error);
+ }
+
+ public function test8M()
+ {
+ $file = qiniuTempFile(8 * 1024 * 1024, false);
+ list($r, $error) = Etag::sum($file);
+ unlink($file);
+ $this->assertEquals('lmRm9ZfGZ86bnMys4wRTWtJj9ClG', $r);
+ $this->assertNull($error);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FopTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FopTest.php
new file mode 100644
index 0000000..42b7997
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FopTest.php
@@ -0,0 +1,39 @@
+execute('gogopher.jpg', 'exif');
+ $this->assertNull($error);
+ $this->assertNotNull($exif);
+ }
+
+ public function testExifPrivate()
+ {
+ global $testAuth;
+ $fop = new Operation('private-res.qiniudn.com', $testAuth);
+ list($exif, $error) = $fop->execute('noexif.jpg', 'exif');
+ $this->assertNotNull($error);
+ $this->assertNull($exif);
+ }
+
+ public function testbuildUrl()
+ {
+ $fops = 'imageView2/2/h/200';
+ $fop = new Operation('testres.qiniudn.com');
+ $url = $fop->buildUrl('gogopher.jpg', $fops);
+ $this->assertEquals($url, 'http://testres.qiniudn.com/gogopher.jpg?imageView2/2/h/200');
+
+ $fops = array('imageView2/2/h/200', 'imageInfo');
+ $url = $fop->buildUrl('gogopher.jpg', $fops);
+ $this->assertEquals($url, 'http://testres.qiniudn.com/gogopher.jpg?imageView2/2/h/200|imageInfo');
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FormUpTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FormUpTest.php
new file mode 100644
index 0000000..f75794e
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FormUpTest.php
@@ -0,0 +1,205 @@
+batch($ops);
+ }
+
+ private static function getObjectKey($key)
+ {
+ $result = $key . rand();
+ self::$keysToCleanup[] = $result;
+ return $result;
+ }
+
+ public function testData()
+ {
+ $key = self::getObjectKey('formput');
+ $token = self::$auth->uploadToken(self::$bucketName);
+ list($ret, $error) = FormUploader::put($token, $key, 'hello world', self::$cfg, null, 'text/plain', null);
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testDataWithProxy()
+ {
+ $key = self::getObjectKey('formput');
+ $token = self::$auth->uploadToken(self::$bucketName);
+ list($ret, $error) = FormUploader::put(
+ $token,
+ $key,
+ 'hello world',
+ self::$cfg,
+ null,
+ 'text/plain',
+ null,
+ $this->makeReqOpt()
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testData2()
+ {
+ $key = self::getObjectKey('formput');
+ $upManager = new UploadManager();
+ $token = self::$auth->uploadToken(self::$bucketName);
+ list($ret, $error) = $upManager->put($token, $key, 'hello world', null, 'text/plain', null);
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testData2WithProxy()
+ {
+ $key = self::getObjectKey('formput');
+ $upManager = new UploadManager();
+ $token = self::$auth->uploadToken(self::$bucketName);
+ list($ret, $error) = $upManager->put(
+ $token,
+ $key,
+ 'hello world',
+ null,
+ 'text/plain',
+ null,
+ $this->makeReqOpt()
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testDataFailed()
+ {
+ $key = self::getObjectKey('formput');
+ $token = self::$auth->uploadToken('fakebucket');
+ list($ret, $error) = FormUploader::put(
+ $token,
+ $key,
+ 'hello world',
+ self::$cfg,
+ null,
+ 'text/plain',
+ null
+ );
+ $this->assertNull($ret);
+ $this->assertNotNull($error);
+ }
+
+ public function testFile()
+ {
+ $key = self::getObjectKey('formPutFile');
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ list($ret, $error) = FormUploader::putFile(
+ $token,
+ $key,
+ __file__,
+ self::$cfg,
+ null,
+ 'text/plain',
+ null
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testFileWithProxy()
+ {
+ $key = self::getObjectKey('formPutFile');
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ list($ret, $error) = FormUploader::putFile(
+ $token,
+ $key,
+ __file__,
+ self::$cfg,
+ null,
+ 'text/plain',
+ $this->makeReqOpt()
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testFile2()
+ {
+ $key = self::getObjectKey('formPutFile');
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $upManager = new UploadManager();
+ list($ret, $error) = $upManager->putFile($token, $key, __file__, null, 'text/plain', null);
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testFile2WithProxy()
+ {
+ $key = self::getObjectKey('formPutFile');
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $upManager = new UploadManager();
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ __file__,
+ null,
+ 'text/plain',
+ false,
+ null,
+ 'v1',
+ Config::BLOCK_SIZE,
+ $this->makeReqOpt()
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ }
+
+ public function testFileFailed()
+ {
+ $key = self::getObjectKey('fakekey');
+ $token = self::$auth->uploadToken('fakebucket', $key);
+ list($ret, $error) = FormUploader::putFile($token, $key, __file__, self::$cfg, null, 'text/plain', null);
+ $this->assertNull($ret);
+ $this->assertNotNull($error);
+ }
+
+ private function makeReqOpt()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->proxy = 'socks5://127.0.0.1:8080';
+ $reqOpt->proxy_user_password = 'user:pass';
+ return $reqOpt;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/HeaderTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/HeaderTest.php
new file mode 100644
index 0000000..28af5f3
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/HeaderTest.php
@@ -0,0 +1,184 @@
+ array('200'),
+ ':x-test-1' => array('hello1'),
+ ':x-Test-2' => array('hello2'),
+ 'content-type' => array('application/json'),
+ 'CONTENT-LENGTH' => array(1234),
+ 'oRiGin' => array('https://www.qiniu.com'),
+ 'ReFer' => array('www.qiniu.com'),
+ 'Last-Modified' => array('Mon, 06 Sep 2021 06:44:52 GMT'),
+ 'acCePt-ChArsEt' => array('utf-8'),
+ 'x-test-3' => array('hello3'),
+ 'cache-control' => array('no-cache', 'no-store'),
+ );
+
+ public function testNormalizeKey()
+ {
+ $except = array(
+ ':status',
+ ':x-test-1',
+ ':x-Test-2',
+ 'Content-Type',
+ 'Content-Length',
+ 'Origin',
+ 'Refer',
+ 'Last-Modified',
+ 'Accept-Charset',
+ 'X-Test-3',
+ 'Cache-Control'
+ );
+ $actual = array_map(function ($str) {
+ return Header::normalizeKey($str);
+ }, array_keys($this->heads));
+ $this->assertEquals($actual, $except);
+ }
+
+
+ public function testInvalidKeyName()
+ {
+ $except = array(
+ 'a:x-test-1',
+ );
+
+ $actual = array_map(function ($str) {
+ return Header::normalizeKey($str);
+ }, $except);
+
+ $this->assertEquals($except, $actual);
+ }
+
+ public function testGetRawData()
+ {
+ $header = new Header($this->heads);
+ foreach ($this->heads as $k => $v) {
+ $rawHeader = $header->getRawData();
+ $this->assertEquals($v, $rawHeader[Header::normalizeKey($k)]);
+ }
+ }
+
+ public function testOffsetExists()
+ {
+ $header = new Header($this->heads);
+ foreach (array_keys($this->heads) as $k) {
+ $this->assertNotNull($header[$k]);
+ }
+
+ $except = array(
+ ':status',
+ ':x-test-1',
+ ':x-Test-2',
+ 'Content-Type',
+ 'Content-Length',
+ 'Origin',
+ 'Refer',
+ 'Last-Modified',
+ 'Accept-Charset',
+ 'X-Test-3',
+ 'Cache-Control'
+ );
+ foreach ($except as $k) {
+ $this->assertNotNull($header[$k], $k." is null");
+ }
+ }
+
+ public function testOffsetGet()
+ {
+ $header = new Header($this->heads);
+ foreach ($this->heads as $k => $v) {
+ $this->assertEquals($v[0], $header[$k]);
+ }
+
+ $this->assertNull($header['no-exist']);
+ }
+
+ public function testOffsetSet()
+ {
+ $header = new Header($this->heads);
+ $header["X-Test-3"] = "hello";
+ $this->assertEquals("hello", $header["X-Test-3"]);
+ $header["x-test-3"] = "hello test3";
+ $this->assertEquals("hello test3", $header["x-test-3"]);
+ $header[":x-Test-2"] = "hello";
+ $this->assertEquals("hello", $header[":x-Test-2"]);
+ $header[":x-test-2"] = "hello test2";
+ $this->assertEquals("hello", $header[":x-Test-2"]);
+ }
+
+ public function testOffsetUnset()
+ {
+ $header = new Header($this->heads);
+ unset($header["X-Test-3"]);
+ $this->assertFalse(isset($header["X-Test-3"]));
+
+ $header = new Header($this->heads);
+ unset($header["x-test-3"]);
+ $this->assertFalse(isset($header["x-test-3"]));
+
+ $header = new Header($this->heads);
+ unset($header[":x-test-2"]);
+ $this->assertTrue(isset($header[":x-Test-2"]));
+
+ $header = new Header($this->heads);
+ unset($header[":x-Test-2"]);
+ $this->assertFalse(isset($header[":x-Test-2"]));
+ }
+
+ public function testGetIterator()
+ {
+ $header = new Header($this->heads);
+
+ $hasException = false;
+ try {
+ foreach ($header as $k => $v) {
+ $hasException = !isset($header[$k]);
+ }
+ } catch (\Exception $e) {
+ $hasException = true;
+ }
+ $this->assertFalse($hasException);
+ }
+
+ public function testEmptyHeaderIterator()
+ {
+ $emptyHeader = new Header();
+
+ $hasException = false;
+ try {
+ foreach ($emptyHeader as $k => $v) {
+ $hasException = !isset($header[$k]);
+ }
+ } catch (\Exception $e) {
+ $hasException = true;
+ }
+ $this->assertFalse($hasException);
+ }
+
+ public function testCount()
+ {
+ $header = new Header($this->heads);
+
+ $this->assertEquals(count($this->heads), count($header));
+ }
+
+ public function testFromRaw()
+ {
+ $lines = array();
+ foreach ($this->heads as $k => $vs) {
+ foreach ($vs as $v) {
+ array_push($lines, $k . ": " . $v);
+ }
+ }
+ $raw = implode("\r\n", $lines);
+ $headerFromRaw = Header::fromRawText($raw);
+ $this->assertEquals(new Header($this->heads), $headerFromRaw);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/HttpTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/HttpTest.php
new file mode 100644
index 0000000..c122f8e
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/HttpTest.php
@@ -0,0 +1,163 @@
+assertEquals(200, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNull($response->error);
+ }
+
+ public function testGetQiniu()
+ {
+ $response = Client::get('upload.qiniu.com');
+ $this->assertEquals(405, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNotNull($response->xReqId());
+ $this->assertNotNull($response->xLog());
+ $this->assertNotNull($response->error);
+ }
+
+ public function testGetTimeout()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->timeout = 1;
+ $response = Client::get('localhost:9000/timeout.php', array(), $reqOpt);
+ $this->assertEquals(-1, $response->statusCode);
+ }
+
+ public function testGetRedirect()
+ {
+ $response = Client::get('localhost:9000/redirect.php');
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals('application/json;charset=UTF-8', $response->normalizedHeaders['Content-Type']);
+ $respData = $response->json();
+ $this->assertEquals('ok', $respData['msg']);
+ }
+
+ public function testDelete()
+ {
+ $response = Client::delete('uc.qbox.me/bucketTagging', array());
+ $this->assertEquals(401, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNotNull($response->error);
+ }
+
+ public function testDeleteQiniu()
+ {
+ $response = Client::delete('uc.qbox.me/bucketTagging', array());
+ $this->assertEquals(401, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNotNull($response->xReqId());
+ $this->assertNotNull($response->xLog());
+ $this->assertNotNull($response->error);
+ }
+
+ public function testDeleteTimeout()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->timeout = 1;
+ $response = Client::delete('localhost:9000/timeout.php', array(), $reqOpt);
+ $this->assertEquals(-1, $response->statusCode);
+ }
+
+
+ public function testPost()
+ {
+ $response = Client::post('qiniu.com', null);
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNull($response->error);
+ }
+
+ public function testPostQiniu()
+ {
+ $response = Client::post('upload.qiniu.com', null);
+ $this->assertEquals(400, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNotNull($response->xReqId());
+ $this->assertNotNull($response->xLog());
+ $this->assertNotNull($response->error);
+ }
+
+ public function testPostTimeout()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->timeout = 1;
+ $response = Client::post('localhost:9000/timeout.php', null, array(), $reqOpt);
+ $this->assertEquals(-1, $response->statusCode);
+ }
+
+ public function testSocks5Proxy()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->proxy = 'socks5://localhost:8080';
+ $response = Client::post('qiniu.com', null, array(), $reqOpt);
+ $this->assertEquals(-1, $response->statusCode);
+
+ $reqOpt->proxy_user_password = 'user:pass';
+ $response = Client::post('qiniu.com', null, array(), $reqOpt);
+ $this->assertEquals(200, $response->statusCode);
+ }
+
+ public function testPut()
+ {
+ $response = Client::PUT('uc.qbox.me/bucketTagging', null);
+ $this->assertEquals(401, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNotNull($response->error);
+ }
+
+ public function testPutQiniu()
+ {
+ $response = Client::put('uc.qbox.me/bucketTagging', null);
+ $this->assertEquals(401, $response->statusCode);
+ $this->assertNotNull($response->body);
+ $this->assertNotNull($response->xReqId());
+ $this->assertNotNull($response->xLog());
+ $this->assertNotNull($response->error);
+ }
+
+
+ public function testPutTimeout()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->timeout = 1;
+ $response = Client::put('localhost:9000/timeout.php', null, array(), $reqOpt);
+ $this->assertEquals(-1, $response->statusCode);
+ }
+
+ public function testNeedRetry()
+ {
+ $testCases = array_merge(
+ array(array(-1, true)),
+ array_map(function ($i) {
+ return array($i, false);
+ }, range(100, 499)),
+ array_map(function ($i) {
+ if (in_array($i, array(
+ 501, 509, 573, 579, 608, 612, 614, 616, 618, 630, 631, 632, 640, 701
+ ))) {
+ return array($i, false);
+ }
+ return array($i, true);
+ }, range(500, 799))
+ );
+ $resp = new Response(-1, 222, array(), '{"msg": "mock"}', null);
+ foreach ($testCases as $testCase) {
+ list($code, $expectNeedRetry) = $testCase;
+ $resp->statusCode = $code;
+ $msg = $resp->statusCode . ' need' . ($expectNeedRetry ? '' : ' NOT') . ' retry';
+ $this->assertEquals($expectNeedRetry, $resp->needRetry(), $msg);
+ }
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ImageUrlBuilderTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ImageUrlBuilderTest.php
new file mode 100644
index 0000000..486323c
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ImageUrlBuilderTest.php
@@ -0,0 +1,263 @@
+
+ */
+class ImageUrlBuilderTest extends TestCase
+{
+ /**
+ * 缩略图测试
+ *
+ * @test
+ * @return void
+ * @author Sherlock Ren
+ */
+ public function testThumbutl()
+ {
+ $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder();
+ $url = 'http://78re52.com1.z0.glb.clouddn.com/resource/gogopher.jpg';
+ $url2 = $url . '?watermark/1/gravity/SouthEast/dx/0/dy/0/image/'
+ . 'aHR0cDovL2Fkcy1jZG4uY2h1Y2h1amllLmNvbS9Ga1R6bnpIY2RLdmRBUFc5cHZZZ3pTc21UY0tB';
+ // 异常测试
+ $this->assertEquals($url, $imageUrlBuilder->thumbnail($url, 1, 0, 0));
+ $this->assertEquals($url, \Qiniu\thumbnail($url, 1, 0, 0));
+
+ // 简单缩略测试
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/ignore-error/1/',
+ $imageUrlBuilder->thumbnail($url, 1, 200, 200)
+ );
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/ignore-error/1/',
+ \Qiniu\thumbnail($url, 1, 200, 200)
+ );
+
+ // 输出格式测试
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/',
+ $imageUrlBuilder->thumbnail($url, 1, 200, 200, 'png')
+ );
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/',
+ \Qiniu\thumbnail($url, 1, 200, 200, 'png')
+ );
+
+ // 渐进显示测试
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/format/png/interlace/1/ignore-error/1/',
+ $imageUrlBuilder->thumbnail($url, 1, 200, 200, 'png', 1)
+ );
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/',
+ \Qiniu\thumbnail($url, 1, 200, 200, 'png', 2)
+ );
+
+ // 图片质量测试
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/format/png/interlace/1/q/80/ignore-error/1/',
+ $imageUrlBuilder->thumbnail($url, 1, 200, 200, 'png', 1, 80)
+ );
+ $this->assertEquals(
+ $url . '?imageView2/1/w/200/h/200/format/png/interlace/1/ignore-error/1/',
+ \Qiniu\thumbnail($url, 1, 200, 200, 'png', 1, 101)
+ );
+
+ // 多参数测试
+ $this->assertEquals(
+ $url2 . '|imageView2/1/w/200/h/200/ignore-error/1/',
+ $imageUrlBuilder->thumbnail($url2, 1, 200, 200)
+ );
+ $this->assertEquals(
+ $url2 . '|imageView2/1/w/200/h/200/ignore-error/1/',
+ \Qiniu\thumbnail($url2, 1, 200, 200)
+ );
+ }
+
+ /**
+ * 图片水印测试
+ *
+ * @test
+ * @param void
+ * @return void
+ * @author Sherlock Ren
+ */
+ public function waterImgTest()
+ {
+ $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder();
+ $url = 'http://78re52.com1.z0.glb.clouddn.com/resource/gogopher.jpg';
+ $url2 = $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/';
+ $image = 'http://developer.qiniu.com/resource/logo-2.jpg';
+
+ // 水印简单测试
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ $imageUrlBuilder->waterImg($url, $image)
+ );
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/gravity/SouthEast/',
+ $imageUrlBuilder->waterImg($url, $image, 101)
+ );
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw==/',
+ $imageUrlBuilder->waterImg($url, $image, 101, 'sdfsd')
+ );
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterImg($url, $image)
+ );
+
+ // 横轴边距测试
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/dx/10/',
+ $imageUrlBuilder->waterImg($url, $image, 100, 'SouthEast', 10)
+ );
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterImg($url, $image, 100, 'SouthEast', 'sad')
+ );
+
+ // 纵轴边距测试
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/dx/10/dy/10/',
+ $imageUrlBuilder->waterImg($url, $image, 100, 'SouthEast', 10, 10)
+ );
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterImg($url, $image, 100, 'SouthEast', 'sad', 'asdf')
+ );
+
+ // 自适应原图的短边比例测试
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/dx/10/dy/10/ws/0.5/',
+ $imageUrlBuilder->waterImg($url, $image, 100, 'SouthEast', 10, 10, 0.5)
+ );
+ $this->assertEquals(
+ $url . '?watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterImg($url, $image, 100, 'SouthEast', 'sad', 'asdf', 2)
+ );
+
+ // 多参数测试
+ $this->assertEquals(
+ $url2 . '|watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ $imageUrlBuilder->waterImg($url2, $image)
+ );
+ $this->assertEquals(
+ $url2 . '|watermark/1/image/aHR0cDovL2RldmVsb3Blci5xaW5pdS5jb20vcmVzb3VyY2UvbG9nby0yLmpwZw=='
+ . '/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterImg($url2, $image)
+ );
+ }
+
+ /**
+ * 文字水印测试
+ *
+ * @test
+ * @param void
+ * @return void
+ * @author Sherlock Ren
+ */
+ public function waterTextTest()
+ {
+ $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder();
+ $url = 'http://78re52.com1.z0.glb.clouddn.com/resource/gogopher.jpg';
+ $url2 = $url . '?imageView2/1/w/200/h/200/format/png/ignore-error/1/';
+ $text = '测试一下';
+ $font = '微软雅黑';
+ $fontColor = '#FF0000';
+
+ // 水印简单测试
+ $this->assertEquals($url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/'
+ . 'fontsize/500/dissolve/100/gravity/SouthEast/', $imageUrlBuilder->waterText($url, $text, $font, 500));
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/'
+ . 'dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterText($url, $text, $font, 'sdf')
+ );
+
+ // 字体颜色测试
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/'
+ . 'I0ZGMDAwMA==/dissolve/100/gravity/SouthEast/',
+ $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor)
+ );
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA=='
+ . '/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor)
+ );
+
+ // 透明度测试
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0ZGMDAwMA=='
+ . '/dissolve/80/gravity/SouthEast/',
+ $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor, 80)
+ );
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA=='
+ . '/gravity/SouthEast/',
+ \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor, 101)
+ );
+
+ // 水印位置测试
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0ZGMDAwMA=='
+ . '/dissolve/80/gravity/East/',
+ $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor, 80, 'East')
+ );
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA==/',
+ \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor, 101, 'sdfsdf')
+ );
+
+ // 横轴距离测试
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0ZGMDAwMA=='
+ . '/dissolve/80/gravity/East/dx/10/',
+ $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor, 80, 'East', 10)
+ );
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA==/',
+ \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor, 101, 'sdfsdf', 'sdfs')
+ );
+
+ // 纵轴距离测试
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0ZGMDAwMA=='
+ . '/dissolve/80/gravity/East/dx/10/dy/10/',
+ $imageUrlBuilder->waterText($url, $text, $font, 500, $fontColor, 80, 'East', 10, 10)
+ );
+ $this->assertEquals(
+ $url . '?watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/fill/I0ZGMDAwMA==/',
+ \Qiniu\waterText($url, $text, $font, 'sdf', $fontColor, 101, 'sdfsdf', 'sdfs', 'ssdf')
+ );
+ // 多参数测试
+ $this->assertEquals(
+ $url2 . '|watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/'
+ . 'fontsize/500/dissolve/100/gravity/SouthEast/',
+ $imageUrlBuilder->waterText($url2, $text, $font, 500)
+ );
+ $this->assertEquals(
+ $url2 . '|watermark/2/text/5rWL6K-V5LiA5LiL/font/5b6u6L2v6ZuF6buR/'
+ . 'fontsize/500/dissolve/100/gravity/SouthEast/',
+ \Qiniu\waterText($url2, $text, $font, 500)
+ );
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/MiddlewareTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/MiddlewareTest.php
new file mode 100644
index 0000000..969cad4
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/MiddlewareTest.php
@@ -0,0 +1,160 @@
+
+ */
+ private $orderRecorder;
+
+ /**
+ * @var string
+ */
+ private $label;
+
+ public function __construct(&$orderRecorder, $label)
+ {
+ $this->orderRecorder =& $orderRecorder;
+ $this->label = $label;
+ }
+
+ public function send($request, $next)
+ {
+ $this->orderRecorder[] = "bef_" . $this->label . count($this->orderRecorder);
+ $response = $next($request);
+ $this->orderRecorder[] = "aft_" . $this->label . count($this->orderRecorder);
+ return $response;
+ }
+}
+
+class MiddlewareTest extends TestCase
+{
+ public function testSendWithMiddleware()
+ {
+ $orderRecorder = array();
+
+ $reqOpt = new RequestOptions();
+ $reqOpt->middlewares = array(
+ new RecorderMiddleware($orderRecorder, "A"),
+ new RecorderMiddleware($orderRecorder, "B")
+ );
+
+ $request = new Request(
+ "GET",
+ "http://localhost:9000/ok.php",
+ array(),
+ null,
+ $reqOpt
+ );
+ $response = Client::sendRequestWithMiddleware($request);
+
+ $expectRecords = array(
+ "bef_A0",
+ "bef_B1",
+ "aft_B2",
+ "aft_A3"
+ );
+
+ $this->assertEquals($expectRecords, $orderRecorder);
+ $this->assertEquals(200, $response->statusCode);
+ }
+
+ public function testSendWithRetryDomains()
+ {
+ $orderRecorder = array();
+
+ $reqOpt = new RequestOptions();
+ $reqOpt->middlewares = array(
+ new Middleware\RetryDomainsMiddleware(
+ array(
+ "unavailable.phpsdk.qiniu.com",
+ "localhost:9000",
+ ),
+ 3
+ ),
+ new RecorderMiddleware($orderRecorder, "rec")
+ );
+
+ $request = new Request(
+ "GET",
+ "http://fake.phpsdk.qiniu.com/ok.php",
+ array(),
+ null,
+ $reqOpt
+ );
+ $response = Client::sendRequestWithMiddleware($request);
+
+ $expectRecords = array(
+ // 'fake.phpsdk.qiniu.com' with retried 3 times
+ 'bef_rec0',
+ 'aft_rec1',
+ 'bef_rec2',
+ 'aft_rec3',
+ 'bef_rec4',
+ 'aft_rec5',
+
+ // 'unavailable.pysdk.qiniu.com' with retried 3 times
+ 'bef_rec6',
+ 'aft_rec7',
+ 'bef_rec8',
+ 'aft_rec9',
+ 'bef_rec10',
+ 'aft_rec11',
+
+ // 'qiniu.com' and it's success
+ 'bef_rec12',
+ 'aft_rec13'
+ );
+
+ $this->assertEquals($expectRecords, $orderRecorder);
+ $this->assertEquals(200, $response->statusCode);
+ }
+
+ public function testSendFailFastWithRetryDomains()
+ {
+ $orderRecorder = array();
+
+ $reqOpt = new RequestOptions();
+ $reqOpt->middlewares = array(
+ new Middleware\RetryDomainsMiddleware(
+ array(
+ "unavailable.phpsdk.qiniu.com",
+ "localhost:9000",
+ ),
+ 3,
+ function () {
+ return false;
+ }
+ ),
+ new RecorderMiddleware($orderRecorder, "rec")
+ );
+
+ $request = new Request(
+ "GET",
+ "http://fake.phpsdk.qiniu.com/ok.php",
+ array(),
+ null,
+ $reqOpt
+ );
+ $response = Client::sendRequestWithMiddleware($request);
+
+ $expectRecords = array(
+ // 'fake.phpsdk.qiniu.com' will fail fast
+ 'bef_rec0',
+ 'aft_rec1'
+ );
+ $this->assertEquals($expectRecords, $orderRecorder);
+ $this->assertEquals(-1, $response->statusCode);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/PfopTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/PfopTest.php
new file mode 100644
index 0000000..77d06ec
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/PfopTest.php
@@ -0,0 +1,304 @@
+execute($bucket, $key, $fops);
+ $this->assertNull($error);
+ list($status, $error) = $pfop->status($id);
+ $this->assertNotNull($status);
+ $this->assertNull($error);
+ }
+
+
+ public function testPfopExecuteAndStatusWithMultipleFops()
+ {
+ global $testAuth;
+ $bucket = 'testres';
+ $key = 'sintel_trailer.mp4';
+ $fops = array(
+ 'avthumb/m3u8/segtime/10/vcodec/libx264/s/320x240',
+ 'vframe/jpg/offset/7/w/480/h/360',
+ );
+ $pfop = new PersistentFop($testAuth, self::getConfig());
+
+ list($id, $error) = $pfop->execute($bucket, $key, $fops);
+ $this->assertNull($error);
+
+ list($status, $error) = $pfop->status($id);
+ $this->assertNotNull($status);
+ $this->assertNull($error);
+ }
+
+ private function pfopOptionsTestData()
+ {
+ return array(
+ array(
+ 'type' => null
+ ),
+ array(
+ 'type' => -1
+ ),
+ array(
+ 'type' => 0
+ ),
+ array(
+ 'type' => 1
+ ),
+ array(
+ 'type' => 2
+ ),
+ array(
+ 'workflowTemplateID' => 'test-workflow'
+ )
+ );
+ }
+
+ public function testPfopExecuteWithOptions()
+ {
+ $bucket = self::$bucketName;
+ $key = 'qiniu.png';
+ $pfop = new PersistentFop(self::$testAuth, self::getConfig());
+
+ $testCases = $this->pfopOptionsTestData();
+
+ foreach ($testCases as $testCase) {
+ $workflowTemplateID = null;
+ $type = null;
+
+ if (array_key_exists('workflowTemplateID', $testCase)) {
+ $workflowTemplateID = $testCase['workflowTemplateID'];
+ }
+ if (array_key_exists('type', $testCase)) {
+ $type = $testCase['type'];
+ }
+
+ if ($workflowTemplateID) {
+ $fops = null;
+ } else {
+ $persistentEntry = \Qiniu\entry(
+ $bucket,
+ implode(
+ '_',
+ array(
+ 'test-pfop/test-pfop-by-api',
+ 'type',
+ $type
+ )
+ )
+ );
+ $fops = 'avinfo|saveas/' . $persistentEntry;
+ }
+ list($id, $error) = $pfop->execute(
+ $bucket,
+ $key,
+ $fops,
+ null,
+ null,
+ false,
+ $type,
+ $workflowTemplateID
+ );
+
+ if (in_array($type, array(null, 0, 1))) {
+ $this->assertNull($error);
+ list($status, $error) = $pfop->status($id);
+ $this->assertNotNull($status);
+ $this->assertNull($error);
+ if ($type == 1) {
+ $this->assertEquals(1, $status['type']);
+ }
+ if ($workflowTemplateID) {
+ // assertStringContainsString when PHPUnit >= 8.0
+ $this->assertTrue(
+ strpos(
+ $status['taskFrom'],
+ $workflowTemplateID
+ ) !== false
+ );
+ }
+ $this->assertNotEmpty($status['creationDate']);
+ } else {
+ $this->assertNotNull($error);
+ }
+ }
+ }
+
+ public function testPfopWithInvalidArgument()
+ {
+ $bucket = self::$bucketName;
+ $key = 'qiniu.png';
+ $pfop = new PersistentFop(self::$testAuth, self::getConfig());
+ $err = null;
+ try {
+ $pfop->execute(
+ $bucket,
+ $key
+ );
+ } catch (\Exception $e) {
+ $err = $e;
+ }
+
+ $this->assertNotEmpty($err);
+ $this->assertTrue(
+ strpos(
+ $err->getMessage(),
+ 'Must provide one of fops or template_id'
+ ) !== false
+ );
+ }
+
+ public function testPfopWithUploadPolicy()
+ {
+ $bucket = self::$bucketName;
+ $testAuth = self::$testAuth;
+ $key = 'test-pfop/upload-file';
+
+ $testCases = $this->pfopOptionsTestData();
+
+ foreach ($testCases as $testCase) {
+ $workflowTemplateID = null;
+ $type = null;
+
+ if (array_key_exists('workflowTemplateID', $testCase)) {
+ $workflowTemplateID = $testCase['workflowTemplateID'];
+ }
+ if (array_key_exists('type', $testCase)) {
+ $type = $testCase['type'];
+ }
+
+ $putPolicy = array(
+ 'persistentType' => $type
+ );
+ if ($workflowTemplateID) {
+ $putPolicy['persistentWorkflowTemplateID'] = $workflowTemplateID;
+ } else {
+ $persistentEntry = \Qiniu\entry(
+ $bucket,
+ implode(
+ '_',
+ array(
+ 'test-pfop/test-pfop-by-upload',
+ 'type',
+ $type
+ )
+ )
+ );
+ $putPolicy['persistentOps'] = 'avinfo|saveas/' . $persistentEntry;
+ }
+
+ if ($type == null) {
+ unset($putPolicy['persistentType']);
+ }
+
+ $token = $testAuth->uploadToken(
+ $bucket,
+ $key,
+ 3600,
+ $putPolicy
+ );
+ $upManager = new UploadManager(self::getConfig());
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ __file__,
+ null,
+ 'text/plain',
+ true
+ );
+
+ if (in_array($type, array(null, 0, 1))) {
+ $this->assertNull($error);
+ $this->assertNotEmpty($ret['persistentId']);
+ $id = $ret['persistentId'];
+ } else {
+ $this->assertNotNull($error);
+ return;
+ }
+
+ $pfop = new PersistentFop($testAuth, self::getConfig());
+ list($status, $error) = $pfop->status($id);
+
+ $this->assertNotNull($status);
+ $this->assertNull($error);
+ if ($type == 1) {
+ $this->assertEquals(1, $status['type']);
+ }
+ if ($workflowTemplateID) {
+ // assertStringContainsString when PHPUnit >= 8.0
+ $this->assertTrue(
+ strpos(
+ $status['taskFrom'],
+ $workflowTemplateID
+ ) !== false
+ );
+ }
+ $this->assertNotEmpty($status['creationDate']);
+ }
+ }
+
+ public function testMkzip()
+ {
+ $bucket = self::$bucketName;
+ $key = 'php-logo.png';
+ $pfop = new PersistentFop(self::$testAuth, null);
+
+ $url1 = 'http://phpsdk.qiniudn.com/php-logo.png';
+ $url2 = 'http://phpsdk.qiniudn.com/php-sdk.html';
+ $zipKey = 'test.zip';
+
+ $fops = 'mkzip/2/url/' . \Qiniu\base64_urlSafeEncode($url1);
+ $fops .= '/url/' . \Qiniu\base64_urlSafeEncode($url2);
+ $fops .= '|saveas/' . \Qiniu\base64_urlSafeEncode("$bucket:$zipKey");
+
+ list($id, $error) = $pfop->execute($bucket, $key, $fops);
+ $this->assertNull($error);
+
+ list($status, $error) = $pfop->status($id);
+ $this->assertNotNull($status);
+ $this->assertNull($error);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ResumeUpTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ResumeUpTest.php
new file mode 100644
index 0000000..6feee55
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ResumeUpTest.php
@@ -0,0 +1,354 @@
+batch($ops);
+ }
+
+ private static function getObjectKey($key)
+ {
+ $result = $key . rand();
+ self::$keysToCleanup[] = $result;
+ return $result;
+ }
+
+ public function test4ML()
+ {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $upManager = new UploadManager();
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $tempFile = qiniuTempFile(4 * 1024 * 1024 + 10);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ null,
+ 'application/octet-stream',
+ false,
+ $resumeFile
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ $response = Client::get("http://$domain/$key");
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals(md5_file($tempFile, true), md5($response->body(), true));
+ unlink($tempFile);
+ }
+
+ public function test4ML2()
+ {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $cfg = new Config();
+ $upManager = new UploadManager($cfg);
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $tempFile = qiniuTempFile(4 * 1024 * 1024 + 10);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ null,
+ 'application/octet-stream',
+ false,
+ $resumeFile
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ $response = Client::get("http://$domain/$key");
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals(md5_file($tempFile, true), md5($response->body(), true));
+ unlink($tempFile);
+ }
+
+ public function test4ML2WithProxy()
+ {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $cfg = new Config();
+ $upManager = new UploadManager($cfg);
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $tempFile = qiniuTempFile(4 * 1024 * 1024 + 10);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ null,
+ 'application/octet-stream',
+ false,
+ $resumeFile,
+ 'v2',
+ Config::BLOCK_SIZE,
+ $this->makeReqOpt()
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ $response = Client::get("http://$domain/$key");
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals(md5_file($tempFile, true), md5($response->body(), true));
+ unlink($tempFile);
+ }
+
+ // public function test8M()
+ // {
+ // $key = 'resumePutFile8M';
+ // $upManager = new UploadManager();
+ // $token = self::$auth->uploadToken(self::$bucketName, $key);
+ // $tempFile = qiniuTempFile(8*1024*1024+10);
+ // list($ret, $error) = $upManager->putFile($token, $key, $tempFile);
+ // $this->assertNull($error);
+ // $this->assertNotNull($ret['hash']);
+ // unlink($tempFile);
+ // }
+
+ public function testFileWithFileType()
+ {
+ $config = new Config();
+ $bucketManager = new BucketManager(self::$auth, $config);
+
+ $testCases = array(
+ array(
+ "fileType" => 1,
+ "name" => "IA"
+ ),
+ array(
+ "fileType" => 2,
+ "name" => "Archive"
+ ),
+ array(
+ "fileType" => 3,
+ "name" => "DeepArchive"
+ )
+ );
+
+ foreach ($testCases as $testCase) {
+ $key = self::getObjectKey('FileType' . $testCase["name"]);
+ $police = array(
+ "fileType" => $testCase["fileType"],
+ );
+ $token = self::$auth->uploadToken(self::$bucketName, $key, 3600, $police);
+ $upManager = new UploadManager();
+ list($ret, $error) = $upManager->putFile($token, $key, __file__, null, 'text/plain');
+ $this->assertNull($error);
+ $this->assertNotNull($ret);
+ list($ret, $err) = $bucketManager->stat(self::$bucketName, $key);
+ $this->assertNull($err);
+ $this->assertEquals($testCase["fileType"], $ret["type"]);
+ }
+ }
+
+ public function testResumeUploadWithParams()
+ {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $upManager = new UploadManager();
+ $policy = array('returnBody' => '{"hash":$(etag),"fname":$(fname),"var_1":$(x:var_1),"var_2":$(x:var_2)}');
+ $token = self::$auth->uploadToken(self::$bucketName, $key, 3600, $policy);
+ $tempFile = qiniuTempFile(4 * 1024 * 1024 + 10);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ array("x:var_1" => "val_1", "x:var_2" => "val_2", "x-qn-meta-m1" => "val_1", "x-qn-meta-m2" => "val_2"),
+ 'application/octet-stream',
+ false,
+ $resumeFile
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ $this->assertEquals("val_1", $ret['var_1']);
+ $this->assertEquals("val_2", $ret['var_2']);
+ $this->assertEquals(basename($tempFile), $ret['fname']);
+
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ $response = Client::get("http://$domain/$key");
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals(md5_file($tempFile, true), md5($response->body(), true));
+ $headers = $response->headers();
+ $this->assertEquals("val_1", $headers["X-Qn-Meta-M1"]);
+ $this->assertEquals("val_2", $headers["X-Qn-Meta-M2"]);
+ unlink($tempFile);
+ }
+
+ public function testResumeUploadV2()
+ {
+ $cfg = new Config();
+ $upManager = new UploadManager($cfg);
+ $testFileSize = array(
+ config::BLOCK_SIZE / 2,
+ config::BLOCK_SIZE,
+ config::BLOCK_SIZE + 10,
+ config::BLOCK_SIZE * 2,
+ config::BLOCK_SIZE * 2.5
+ );
+ $partSize = 5 * 1024 * 1024;
+ foreach ($testFileSize as $item) {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $tempFile = qiniuTempFile($item);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ null,
+ 'application/octet-stream',
+ false,
+ $resumeFile,
+ 'v2',
+ $partSize
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ $response = Client::get("http://$domain/$key");
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals(md5_file($tempFile, true), md5($response->body(), true));
+ unlink($tempFile);
+ }
+ }
+
+ public function testResumeUploadV2WithParams()
+ {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $upManager = new UploadManager();
+ $policy = array('returnBody' => '{"hash":$(etag),"fname":$(fname),"var_1":$(x:var_1),"var_2":$(x:var_2)}');
+ $token = self::$auth->uploadToken(self::$bucketName, $key, 3600, $policy);
+ $tempFile = qiniuTempFile(4 * 1024 * 1024 + 10);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ list($ret, $error) = $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ array("x:var_1" => "val_1", "x:var_2" => "val_2", "x-qn-meta-m1" => "val_1", "x-qn-meta-m2" => "val_2"),
+ 'application/octet-stream',
+ false,
+ $resumeFile,
+ 'v2'
+ );
+ $this->assertNull($error);
+ $this->assertNotNull($ret['hash']);
+ $this->assertEquals("val_1", $ret['var_1']);
+ $this->assertEquals("val_2", $ret['var_2']);
+ $this->assertEquals(basename($tempFile), $ret['fname']);
+
+ $domain = getenv('QINIU_TEST_DOMAIN');
+ $response = Client::get("http://$domain/$key");
+ $this->assertEquals(200, $response->statusCode);
+ $this->assertEquals(md5_file($tempFile, true), md5($response->body(), true));
+ $headers = $response->headers();
+ $this->assertEquals("val_1", $headers["X-Qn-Meta-M1"]);
+ $this->assertEquals("val_2", $headers["X-Qn-Meta-M2"]);
+ unlink($tempFile);
+ }
+
+ // valid versions are tested above
+ // Use PHPUnit's Data Provider to test multiple Exception is better,
+ // but not match the test style of this project
+ public function testResumeUploadWithInvalidVersion()
+ {
+ $cfg = new Config();
+ $upManager = new UploadManager($cfg);
+ $testFileSize = config::BLOCK_SIZE * 2;
+ $partSize = 5 * 1024 * 1024;
+ $testInvalidVersions = array(
+ // High probability invalid versions
+ 'v',
+ '1',
+ '2'
+ );
+
+ $expectExceptionCount = 0;
+ foreach ($testInvalidVersions as $invalidVersion) {
+ $key = self::getObjectKey('resumePutFile4ML_');
+ $token = self::$auth->uploadToken(self::$bucketName, $key);
+ $tempFile = qiniuTempFile($testFileSize);
+ $resumeFile = tempnam(sys_get_temp_dir(), 'resume_file');
+ $this->assertNotFalse($resumeFile);
+ try {
+ $upManager->putFile(
+ $token,
+ $key,
+ $tempFile,
+ null,
+ 'application/octet-stream',
+ false,
+ $resumeFile,
+ $invalidVersion,
+ $partSize
+ );
+ } catch (\Exception $e) {
+ $isRightException = false;
+ $expectExceptionCount++;
+ while ($e) {
+ $isRightException = $e instanceof \UnexpectedValueException;
+ if ($isRightException) {
+ break;
+ }
+ $e = $e->getPrevious();
+ }
+ $this->assertTrue($isRightException);
+ }
+
+ unlink($tempFile);
+ }
+ $this->assertEquals(count($testInvalidVersions), $expectExceptionCount);
+ }
+
+ private function makeReqOpt()
+ {
+ $reqOpt = new RequestOptions();
+ $reqOpt->proxy = 'socks5://127.0.0.1:8080';
+ $reqOpt->proxy_user_password = 'user:pass';
+ return $reqOpt;
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ZoneTest.php b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ZoneTest.php
new file mode 100644
index 0000000..fbab528
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ZoneTest.php
@@ -0,0 +1,136 @@
+bucketName = $bucketName;
+
+ global $bucketNameBC;
+ $this->bucketNameBC = $bucketNameBC;
+
+ global $bucketNameNA;
+ $this->bucketNameNA = $bucketNameNA;
+
+ global $bucketNameFS;
+ $this->bucketNameFS = $bucketNameFS;
+
+ global $bucketNameAS;
+ $this->bucketNameAS = $bucketNameAS;
+
+ global $accessKey;
+ $this->ak = $accessKey;
+
+ $this->zone = new Zone();
+ $this->zoneHttps = new Zone('https');
+ }
+
+ public function testUpHosts()
+ {
+ list($ret, $err) = Zone::queryZone($this->ak, 'fakebucket');
+ $this->assertNull($ret);
+ $this->assertNotNull($err);
+
+ $zone = Zone::queryZone($this->ak, $this->bucketName);
+ $this->assertContains('upload.qiniup.com', $zone->cdnUpHosts);
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameBC);
+ $this->assertContains('upload-z1.qiniup.com', $zone->cdnUpHosts);
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameFS);
+ $this->assertContains('upload-z2.qiniup.com', $zone->cdnUpHosts);
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameNA);
+ $this->assertContains('upload-na0.qiniup.com', $zone->cdnUpHosts);
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameAS);
+ $this->assertContains('upload-as0.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testIoHosts()
+ {
+ $zone = Zone::queryZone($this->ak, $this->bucketName);
+ $this->assertEquals($zone->iovipHost, 'iovip.qbox.me');
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameBC);
+ $this->assertEquals($zone->iovipHost, 'iovip-z1.qbox.me');
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameFS);
+ $this->assertEquals($zone->iovipHost, 'iovip-z2.qbox.me');
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameNA);
+ $this->assertEquals($zone->iovipHost, 'iovip-na0.qbox.me');
+
+ $zone = Zone::queryZone($this->ak, $this->bucketNameAS);
+ $this->assertEquals($zone->iovipHost, 'iovip-as0.qbox.me');
+ }
+
+ public function testZonez0()
+ {
+ $zone = Zone::zonez0();
+ $this->assertContains('upload.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testZonez1()
+ {
+ $zone = Zone::zonez1();
+ $this->assertContains('upload-z1.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testZonez2()
+ {
+ $zone = Zone::zonez2();
+ $this->assertContains('upload-z2.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testZoneCnEast2()
+ {
+ $zone = Zone::zoneCnEast2();
+ $this->assertContains('upload-cn-east-2.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testZoneNa0()
+ {
+ $zone = Zone::zoneNa0();
+ $this->assertContains('upload-na0.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testZoneAs0()
+ {
+ $zone = Zone::zoneAs0();
+ $this->assertContains('upload-as0.qiniup.com', $zone->cdnUpHosts);
+ }
+
+ public function testQvmZonez0()
+ {
+ $zone = Zone::qvmZonez0();
+ $this->assertContains('free-qvm-z0-xs.qiniup.com', $zone->srcUpHosts);
+ }
+
+ public function testQvmZonez1()
+ {
+ $zone = Zone::qvmZonez1();
+ $this->assertContains('free-qvm-z1-zz.qiniup.com', $zone->srcUpHosts);
+ }
+}
diff --git a/vendor/qiniu/php-sdk/tests/bootstrap.php b/vendor/qiniu/php-sdk/tests/bootstrap.php
new file mode 100644
index 0000000..9859a81
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/bootstrap.php
@@ -0,0 +1,61 @@
+ 0) {
+ $length = min($rest_size, 4 * 1024);
+ if (fwrite($file, random_bytes($length)) == false) {
+ return false;
+ }
+ $rest_size -= $length;
+ }
+ } else if ($size > 0) {
+ fseek($file, $size - 1);
+ fwrite($file, ' ');
+ }
+ fclose($file);
+ return $fileName;
+}
diff --git a/vendor/qiniu/php-sdk/tests/mock-server/ok.php b/vendor/qiniu/php-sdk/tests/mock-server/ok.php
new file mode 100644
index 0000000..5b0a65d
--- /dev/null
+++ b/vendor/qiniu/php-sdk/tests/mock-server/ok.php
@@ -0,0 +1,3 @@
+