作者 何书鹏

我的海报

正在显示 38 个修改的文件 包含 2612 行增加183 行删除
1 <?php 1 <?php
  2 +/**
  3 + * 修改图片为圆形
  4 + */
  5 +function createRoundImg($imgpath) {
  6 + $ename=getimagesize($imgpath);
  7 + $ename=explode('/',$ename['mime']);
  8 + $ext=$ename[1];
  9 + $src_img = null;
  10 + switch($ext){
  11 + case "png":
  12 + $src_img=imagecreatefrompng($imgpath);
  13 + break;
  14 + case "jpeg":
  15 + $src_img=imagecreatefromjpeg($imgpath);
  16 + break;
  17 + case "jpg":
  18 + $src_img=imagecreatefromjpeg($imgpath);
  19 + break;
  20 + case "gif":
  21 + $src_img=imagecreatefromgif($imgpath);
  22 + break;
  23 + }
  24 +
  25 + $wh = getimagesize($imgpath);
  26 + $w = $wh[0];
  27 + $h = $wh[1];
  28 + $w = $h = min($w, $h);
  29 +
  30 + $image = imagecreatetruecolor($w, $h);
  31 + $bg = imagecolorallocatealpha($image, 255, 255, 255, 127);
  32 + imagesavealpha($image, true);
  33 + imagefill($image, 0, 0, $bg);
  34 + $r = $w / 2;
  35 + for ($x = 0; $x < $w; $x++) {
  36 + for ($y = 0; $y < $h; $y++) {
  37 + $rgbColor = imagecolorat($src_img, $x, $y);
  38 + if (((($x-$r) * ($x-$r) + ($y-$r) * ($y-$r)) < ($r*$r))) {
  39 + imagesetpixel($image, $x, $y, $rgbColor);
  40 + }
  41 + }
  42 + }
  43 +
  44 + header("content-type:image/png");
  45 + imagepng($image,$imgpath);
  46 + imagedestroy($image);
  47 +}
  48 +
  49 +/**
  50 + * 判断远程文件是否存在
  51 + */
  52 +function url_exists($url) {
  53 + $ch = curl_init();
  54 + curl_setopt ($ch, CURLOPT_URL, $url);
  55 + //不下载
  56 + curl_setopt($ch, CURLOPT_NOBODY, 1);
  57 + //设置超时
  58 + curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, 3);
  59 + curl_setopt($ch, CURLOPT_TIMEOUT, 3);
  60 + curl_exec($ch);
  61 + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  62 + if($http_code == 200) {
  63 + return true;
  64 + }
  65 + return false;
  66 +}
@@ -3,6 +3,11 @@ @@ -3,6 +3,11 @@
3 namespace app\api\controller; 3 namespace app\api\controller;
4 4
5 use addons\shopro\model\Config; 5 use addons\shopro\model\Config;
  6 +use app\common\exception\UploadException;
  7 +use app\common\library\Upload;
  8 +use Qiniu\Auth;
  9 +use Qiniu\Storage\ResumeUploader;
  10 +use Qiniu\Storage\UploadManager;
6 use think\Db; 11 use think\Db;
7 use think\Config as FaConfig; 12 use think\Config as FaConfig;
8 use fast\Random; 13 use fast\Random;
@@ -360,108 +365,115 @@ class Index extends Base @@ -360,108 +365,115 @@ class Index extends Base
360 } 365 }
361 366
362 /** 367 /**
363 - * 上传文件 368 + * 中转上传文件
  369 + * 上传分片
  370 + * 合并分片
364 * @ApiMethod (POST) 371 * @ApiMethod (POST)
365 * @param File $file 文件流 372 * @param File $file 文件流
366 */ 373 */
367 public function upload() 374 public function upload()
368 { 375 {
369 - $file = $this->request->file('file');  
370 - if (empty($file)) {  
371 - $this->error(__('No file upload or server upload limit exceeded'));  
372 - }  
373 -  
374 - //判断是否已经存在附件  
375 - $sha1 = $file->hash(); 376 + FaConfig::set('default_return_type', 'json');
  377 +
  378 +// $this->check();
  379 + $config = get_addon_config('qiniu');
  380 + $config['savekey'] = str_replace(['{year}', '{mon}', '{day}', '{filemd5}', '{.suffix}'], ['$(year)', '$(mon)', '$(day)', '$(etag)', '$(ext)'], $config['savekey']);
  381 +
  382 + // 构建鉴权对象
  383 + $auth = new Auth($config['accessKey'], $config['secretKey']);
  384 + // 生成上传 Token
  385 + $token = $auth->uploadToken($config['bucket'], null, 3600, ['saveKey' => ltrim($config['savekey'], '/')]);
  386 + // 初始化 UploadManager 对象并进行文件的上传。
  387 + $uploadMgr = new UploadManager();
  388 +
  389 + $chunkid = $this->request->post("chunkid");
  390 + if ($chunkid) {
  391 + $action = $this->request->post("action");
  392 + $chunkindex = $this->request->post("chunkindex/d");
  393 + $chunkcount = $this->request->post("chunkcount/d");
  394 + $filesize = $this->request->post("filesize");
  395 + $filename = $this->request->post("filename");
  396 + if ($action == 'merge') {
  397 + if ($config['uploadmode'] == 'server') {
  398 + $attachment = null;
  399 + //合并分片文件
  400 + try {
  401 + $upload = new Upload();
  402 + $attachment = $upload->merge($chunkid, $chunkcount, $filename);
  403 + } catch (UploadException $e) {
  404 + $this->error($e->getMessage());
  405 + }
  406 + }
  407 +
  408 + $contexts = $this->request->post("contexts/a", []);
  409 + $uploader = new ResumeUploader($token, null, null, $filesize);
  410 + list($ret, $err) = $uploader->setContexts($contexts)->makeFile($filename);
  411 + if ($err !== null) {
  412 + $this->error("上传失败");
  413 + } else {
  414 + $this->success("上传成功", '', ['url' => '/' . $ret['key'], 'fullurl' => cdnurl('/' . $ret['key'], true), 'hash' => $ret['hash']]);
  415 + }
  416 + } else {
  417 + //默认普通上传文件
  418 + $file = $this->request->file('file');
  419 + try {
  420 + $upload = new Upload($file);
  421 + $file = $upload->chunk($chunkid, $chunkindex, $chunkcount);
  422 + } catch (UploadException $e) {
  423 + $this->error($e->getMessage());
  424 + }
  425 +
  426 + //上传分片文件
  427 + //$file = $this->request->file('file');
  428 + $filesize = $file->getSize();
  429 + //合并分片文件
  430 + $uploader = new ResumeUploader($token, null, fopen($file->getRealPath(), 'rb'), $filesize);
  431 + $ret = $uploader->uploadChunk($chunkindex, $file, $filesize);
  432 + $this->success("上传成功", "", $ret);
  433 + }
  434 + } else {
  435 + $attachment = null;
  436 + //默认普通上传文件
  437 + $file = $this->request->file('file');
  438 + try {
  439 + $upload = new Upload($file);
  440 +
  441 + $suffix = $upload->getSuffix();
  442 + $md5 = md5_file($file->getRealPath());
  443 + $search = ['$(year)', '$(mon)', '$(day)', '$(etag)', '$(ext)'];
  444 + $replace = [date("Y"), date("m"), date("d"), $md5, '.' . $suffix];
  445 + $savekey = ltrim(str_replace($search, $replace, $config['savekey']), '/');
  446 +
  447 + $attachment = $upload->upload($savekey);
  448 + } catch (UploadException $e) {
  449 + $this->error($e->getMessage());
  450 + }
376 451
377 - $upload = FaConfig::get('upload'); 452 + //文件绝对路径
  453 + $filePath = $upload->getFile()->getRealPath() ?: $upload->getFile()->getPathname();
378 454
379 - preg_match('/(\d+)(\w+)/', $upload['maxsize'], $matches);  
380 - $type = strtolower($matches[2]);  
381 - $typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3];  
382 - $size = (int)$upload['maxsize'] * pow(1024, isset($typeDict[$type]) ? $typeDict[$type] : 0);  
383 - $fileInfo = $file->getInfo();  
384 - $suffix = strtolower(pathinfo($fileInfo['name'], PATHINFO_EXTENSION));  
385 - $suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file'; 455 + //上传到七牛后保存的文件名
  456 + $saveKey = ltrim($attachment->url, '/');
386 457
387 - $mimetypeArr = explode(',', strtolower($upload['mimetype']));  
388 - $typeArr = explode('/', $fileInfo['type']); 458 + try {
  459 + // 调用 UploadManager 的 putFile 方法进行文件的上传。
  460 + list($ret, $err) = $uploadMgr->putFile($token, $saveKey, $filePath);
389 461
390 - //禁止上传PHP和HTML文件  
391 - if (in_array($fileInfo['type'], ['text/x-php', 'text/html']) || in_array($suffix, ['php', 'html', 'htm'])) {  
392 - $this->error(__('Uploaded file format is limited'));  
393 - }  
394 - //验证文件后缀  
395 - if ($upload['mimetype'] !== '*' &&  
396 - (  
397 - !in_array($suffix, $mimetypeArr)  
398 - || (stripos($typeArr[0] . '/', $upload['mimetype']) !== false && (!in_array($fileInfo['type'], $mimetypeArr) && !in_array($typeArr[0] . '/*', $mimetypeArr)))  
399 - )  
400 - ) {  
401 - $this->error(__('Uploaded file format is limited'));  
402 - }  
403 - //验证是否为图片文件  
404 - $imagewidth = $imageheight = 0;  
405 - if (in_array($fileInfo['type'], ['image/gif', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/png', 'image/webp']) || in_array($suffix, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'webp'])) {  
406 - $imgInfo = getimagesize($fileInfo['tmp_name']);  
407 - if (!$imgInfo || !isset($imgInfo[0]) || !isset($imgInfo[1])) {  
408 - $this->error(__('Uploaded file is not a valid image')); 462 + if ($err !== null) {
  463 + throw new \Exception("上传失败");
  464 + }
  465 + //成功不做任何操作
  466 + } catch (\Exception $e) {
  467 + $attachment->delete();
  468 + @unlink($filePath);
  469 + $this->error("上传失败");
409 } 470 }
410 - $imagewidth = isset($imgInfo[0]) ? $imgInfo[0] : $imagewidth;  
411 - $imageheight = isset($imgInfo[1]) ? $imgInfo[1] : $imageheight;  
412 - }  
413 - $replaceArr = [  
414 - '{year}' => date("Y"),  
415 - '{mon}' => date("m"),  
416 - '{day}' => date("d"),  
417 - '{hour}' => date("H"),  
418 - '{min}' => date("i"),  
419 - '{sec}' => date("s"),  
420 - '{random}' => Random::alnum(16),  
421 - '{random32}' => Random::alnum(32),  
422 - '{filename}' => $suffix ? substr($fileInfo['name'], 0, strripos($fileInfo['name'], '.')) : $fileInfo['name'],  
423 - '{suffix}' => $suffix,  
424 - '{.suffix}' => $suffix ? '.' . $suffix : '',  
425 - '{filemd5}' => md5_file($fileInfo['tmp_name']),  
426 - ];  
427 - $savekey = $upload['savekey'];  
428 - $savekey = str_replace(array_keys($replaceArr), array_values($replaceArr), $savekey);  
429 471
430 - $uploadDir = substr($savekey, 0, strripos($savekey, '/') + 1);  
431 - $fileName = substr($savekey, strripos($savekey, '/') + 1);  
432 - //  
433 - $splInfo = $file->validate(['size' => $size])->move(ROOT_PATH . '/public' . $uploadDir, $fileName);  
434 - if ($splInfo) {  
435 - $params = array(  
436 - 'admin_id' => 0,  
437 - 'user_id' => (int)$this->auth->id,  
438 - 'filesize' => $fileInfo['size'],  
439 - 'imagewidth' => $imagewidth,  
440 - 'imageheight' => $imageheight,  
441 - 'imagetype' => $suffix,  
442 - 'imageframes' => 0,  
443 - 'mimetype' => $fileInfo['type'],  
444 - 'url' => $uploadDir . $splInfo->getSaveName(),  
445 - 'uploadtime' => time(),  
446 - 'storage' => 'local',  
447 - 'sha1' => $sha1,  
448 - );  
449 - $attachment = new \app\common\model\Attachment;  
450 - $attachment->data(array_filter($params));  
451 - $attachment->save();  
452 - \think\Hook::listen("upload_after", $attachment);  
453 - $this->success(__('Upload successful'), [  
454 - 'url' => $uploadDir . $splInfo->getSaveName(),  
455 - 'full_url' => request()->domain() . $uploadDir . $splInfo->getSaveName()  
456 - ]);  
457 - } else {  
458 - // 上传失败获取错误信息  
459 - $this->error($file->getError()); 472 + $this->success("上传成功", ['url' => $attachment->url, 'fullurl' => cdnurl($attachment->url, true)]);
460 } 473 }
461 } 474 }
462 475
463 476
464 -  
465 /** 477 /**
466 * 上传 base64 图片 478 * 上传 base64 图片
467 * @ApiMethod (POST) 479 * @ApiMethod (POST)
@@ -85,7 +85,7 @@ class User extends Base @@ -85,7 +85,7 @@ class User extends Base
85 $data['store_id'] = $userStores[0]['store_id']; 85 $data['store_id'] = $userStores[0]['store_id'];
86 } 86 }
87 // 头像全路径 87 // 头像全路径
88 - $data['avatar_full'] = cdnurl($data['avatar'],true); 88 + $data['avatar_full'] = !empty($data['avatar']) ? cdnurl($data['avatar'],true) : '';
89 // 是否会员:0=否,1=是 89 // 是否会员:0=否,1=是
90 $data['is_vip'] = $data['vip_time'] > time() ? 1 : 0; 90 $data['is_vip'] = $data['vip_time'] > time() ? 1 : 0;
91 $data['vip_time'] = date('Y/m/d',$data['vip_time']); 91 $data['vip_time'] = date('Y/m/d',$data['vip_time']);
@@ -493,4 +493,71 @@ class User extends Base @@ -493,4 +493,71 @@ class User extends Base
493 $userConfig = json_decode(\addons\shopro\model\Config::get(['name' => 'user'])->value,true); 493 $userConfig = json_decode(\addons\shopro\model\Config::get(['name' => 'user'])->value,true);
494 return $userConfig; 494 return $userConfig;
495 } 495 }
  496 +
  497 + /**
  498 + * @ApiWeigh (85)
  499 + * @ApiTitle (我的二维码)
  500 + * @ApiSummary (我的二维码)
  501 + * @ApiMethod (GET)
  502 + *
  503 + * @ApiHeaders (name="token", type="string", required=true, description="请求的token")
  504 + *
  505 + * @ApiReturn ({
  506 + 'code':'1',
  507 + 'msg':'返回成功',
  508 + "data": {
  509 + "url": "http://www.recruit.top/uploads/job/1.png", //职位海报地址
  510 + }
  511 + })
  512 + */
  513 + public function userPoster()
  514 + {
  515 + $user = UserModel::get($this->auth->id);
  516 + $avatar_url = !empty($user['avatar']) ? cdnurl($user['avatar']) : '';
  517 + empty($avatar_url) && $this->error('请先上传头像');
  518 + !url_exists($avatar_url) && $this->error('头像失效,请更新头像');
  519 + // 本地路径
  520 + $dir = 'uploads/user';
  521 + if (!file_exists($dir)){
  522 + mkdir($dir,0777,true);
  523 + }
  524 +
  525 + // 用户小程序码
  526 +// $qrcode = $dir.'/qrcode_'.$user['id'].'.png';
  527 +// if(!file_exists($qrcode)){
  528 +// $response = Wechat::miniProgram()->app_code->getUnlimit($user['id'], [
  529 +// 'page' => 'pages/indexone/indexone',
  530 +// 'width' => 280, //最小宽度280
  531 +// ]);
  532 +// if ($response instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
  533 +// $response->saveAs($dir, str_replace($dir.'/','',$qrcode));
  534 +// }
  535 +// // 280不满足,再缩小
  536 +// \think\Image::open($qrcode)->thumb(64,64)->save($qrcode);
  537 +// }
  538 + $qrcode = ROOT_PATH.'public/assets/img/miniProgram/qrcode.png';
  539 +
  540 + //将用户头像保存到本地
  541 + $avatar = $dir.'/avatar_'.$user['id'].'.png';
  542 + file_put_contents($avatar,file_get_contents($avatar_url));
  543 + \think\Image::open($avatar)->thumb(64,64,\think\Image::THUMB_CENTER)->save($avatar);
  544 + createRoundImg($avatar);
  545 +
  546 + $path_ttf = ROOT_PATH.'public/assets/fonts/verdana.ttf';
  547 + $filename = $dir.'/'.$user['id'].'.png';
  548 +
  549 + //将海报背景保存到本地
  550 + $share = json_decode(\addons\shopro\model\Config::where(['name' => 'share'])->value('value'), true);
  551 + $user_poster_bg = ROOT_PATH.'public/assets/img/miniProgram/user_poster_bg.png';
  552 + file_put_contents($user_poster_bg,file_get_contents(cdnurl($share['user_poster_bg'])));
  553 +
  554 + // 生成海报
  555 + \think\Image::open($user_poster_bg)
  556 + ->water($avatar,[16,441])
  557 + ->text($user['nickname'],$path_ttf,17,'#000000',[88,463])
  558 + ->water($qrcode,[253,441])
  559 + ->save($filename);
  560 + $url = request()->domain().'/'.$filename.'?v='.time();
  561 + $this->success('成功',compact('url'));
  562 + }
496 } 563 }
@@ -28,7 +28,8 @@ @@ -28,7 +28,8 @@
28 "ext-json": "*", 28 "ext-json": "*",
29 "ext-curl": "*", 29 "ext-curl": "*",
30 "ext-pdo": "*", 30 "ext-pdo": "*",
31 - "topthink/think-queue": "v1.1.6" 31 + "topthink/think-queue": "v1.1.6",
  32 + "topthink/think-image": "^1.0"
32 }, 33 },
33 "config": { 34 "config": {
34 "preferred-install": "dist" 35 "preferred-install": "dist"
@@ -151,9 +151,9 @@ @@ -151,9 +151,9 @@
151 </div> 151 </div>
152 <a href="#提现接口" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">提现接口 <i class="fa fa-caret-down"></i></a> 152 <a href="#提现接口" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">提现接口 <i class="fa fa-caret-down"></i></a>
153 <div class="child collapse" id="提现接口"> 153 <div class="child collapse" id="提现接口">
154 - <a href="javascript:;" data-id="75" class="list-group-item">提现记录</a>  
155 - <a href="javascript:;" data-id="76" class="list-group-item">申请提现</a>  
156 - <a href="javascript:;" data-id="77" class="list-group-item">提现规则</a> 154 + <a href="javascript:;" data-id="76" class="list-group-item">提现记录</a>
  155 + <a href="javascript:;" data-id="77" class="list-group-item">申请提现</a>
  156 + <a href="javascript:;" data-id="78" class="list-group-item">提现规则</a>
157 </div> 157 </div>
158 <a href="#会员接口" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">会员接口 <i class="fa fa-caret-down"></i></a> 158 <a href="#会员接口" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">会员接口 <i class="fa fa-caret-down"></i></a>
159 <div class="child collapse" id="会员接口"> 159 <div class="child collapse" id="会员接口">
@@ -166,6 +166,7 @@ @@ -166,6 +166,7 @@
166 <a href="javascript:;" data-id="72" class="list-group-item">修改会员个人信息</a> 166 <a href="javascript:;" data-id="72" class="list-group-item">修改会员个人信息</a>
167 <a href="javascript:;" data-id="73" class="list-group-item">分销中心-我的下级-1级</a> 167 <a href="javascript:;" data-id="73" class="list-group-item">分销中心-我的下级-1级</a>
168 <a href="javascript:;" data-id="74" class="list-group-item">分销中心-我的下级-2级</a> 168 <a href="javascript:;" data-id="74" class="list-group-item">分销中心-我的下级-2级</a>
  169 + <a href="javascript:;" data-id="75" class="list-group-item">我的二维码</a>
169 </div> 170 </div>
170 <a href="#手机短信接口" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">手机短信接口 <i class="fa fa-caret-down"></i></a> 171 <a href="#手机短信接口" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">手机短信接口 <i class="fa fa-caret-down"></i></a>
171 <div class="child collapse" id="手机短信接口"> 172 <div class="child collapse" id="手机短信接口">
@@ -278,7 +279,7 @@ @@ -278,7 +279,7 @@
278 </div> 279 </div>
279 <a href="#钱包记录接口" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">钱包记录接口 <i class="fa fa-caret-down"></i></a> 280 <a href="#钱包记录接口" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">钱包记录接口 <i class="fa fa-caret-down"></i></a>
280 <div class="child collapse" id="钱包记录接口"> 281 <div class="child collapse" id="钱包记录接口">
281 - <a href="javascript:;" data-id="78" class="list-group-item">会员中心</a> 282 + <a href="javascript:;" data-id="79" class="list-group-item">会员中心</a>
282 </div> 283 </div>
283 </div> 284 </div>
284 </div> 285 </div>
@@ -842,26 +843,26 @@ @@ -842,26 +843,26 @@
842 <h2>提现接口</h2> 843 <h2>提现接口</h2>
843 <hr> 844 <hr>
844 <div class="panel panel-default"> 845 <div class="panel panel-default">
845 - <div class="panel-heading" id="heading-75"> 846 + <div class="panel-heading" id="heading-76">
846 <h4 class="panel-title"> 847 <h4 class="panel-title">
847 <span class="label label-success">GET</span> 848 <span class="label label-success">GET</span>
848 - <a data-toggle="collapse" data-parent="#accordion75" href="#collapseOne75"> 提现记录 <span class="text-muted">/api/user_wallet_apply/index</span></a> 849 + <a data-toggle="collapse" data-parent="#accordion76" href="#collapseOne76"> 提现记录 <span class="text-muted">/api/user_wallet_apply/index</span></a>
849 </h4> 850 </h4>
850 </div> 851 </div>
851 - <div id="collapseOne75" class="panel-collapse collapse"> 852 + <div id="collapseOne76" class="panel-collapse collapse">
852 <div class="panel-body"> 853 <div class="panel-body">
853 854
854 <!-- Nav tabs --> 855 <!-- Nav tabs -->
855 - <ul class="nav nav-tabs" id="doctab75">  
856 - <li class="active"><a href="#info75" data-toggle="tab">基础信息</a></li>  
857 - <li><a href="#sandbox75" data-toggle="tab">在线测试</a></li>  
858 - <li><a href="#sample75" data-toggle="tab">返回示例</a></li> 856 + <ul class="nav nav-tabs" id="doctab76">
  857 + <li class="active"><a href="#info76" data-toggle="tab">基础信息</a></li>
  858 + <li><a href="#sandbox76" data-toggle="tab">在线测试</a></li>
  859 + <li><a href="#sample76" data-toggle="tab">返回示例</a></li>
859 </ul> 860 </ul>
860 861
861 <!-- Tab panes --> 862 <!-- Tab panes -->
862 <div class="tab-content"> 863 <div class="tab-content">
863 864
864 - <div class="tab-pane active" id="info75"> 865 + <div class="tab-pane active" id="info76">
865 <div class="well"> 866 <div class="well">
866 提现记录 </div> 867 提现记录 </div>
867 <div class="panel panel-default"> 868 <div class="panel panel-default">
@@ -890,24 +891,7 @@ @@ -890,24 +891,7 @@
890 <div class="panel panel-default"> 891 <div class="panel panel-default">
891 <div class="panel-heading"><strong>参数</strong></div> 892 <div class="panel-heading"><strong>参数</strong></div>
892 <div class="panel-body"> 893 <div class="panel-body">
893 - <table class="table table-hover">  
894 - <thead>  
895 - <tr>  
896 - <th>名称</th>  
897 - <th>类型</th>  
898 - <th>必选</th>  
899 - <th>描述</th>  
900 - </tr>  
901 - </thead>  
902 - <tbody>  
903 - <tr>  
904 - <td>goods_id</td>  
905 - <td>inter</td>  
906 - <td></td>  
907 - <td>商品ID</td>  
908 - </tr>  
909 - </tbody>  
910 - </table> 894 +
911 </div> 895 </div>
912 </div> 896 </div>
913 <div class="panel panel-default"> 897 <div class="panel panel-default">
@@ -917,7 +901,7 @@ @@ -917,7 +901,7 @@
917 </div> 901 </div>
918 </div><!-- #info --> 902 </div><!-- #info -->
919 903
920 - <div class="tab-pane" id="sandbox75"> 904 + <div class="tab-pane" id="sandbox76">
921 <div class="row"> 905 <div class="row">
922 <div class="col-md-12"> 906 <div class="col-md-12">
923 <div class="panel panel-default"> 907 <div class="panel panel-default">
@@ -934,14 +918,13 @@ @@ -934,14 +918,13 @@
934 <div class="panel panel-default"> 918 <div class="panel panel-default">
935 <div class="panel-heading"><strong>参数</strong></div> 919 <div class="panel-heading"><strong>参数</strong></div>
936 <div class="panel-body"> 920 <div class="panel-body">
937 - <form enctype="application/x-www-form-urlencoded" role="form" action="/api/user_wallet_apply/index" method="GET" name="form75" id="form75"> 921 + <form enctype="application/x-www-form-urlencoded" role="form" action="/api/user_wallet_apply/index" method="GET" name="form76" id="form76">
938 <div class="form-group"> 922 <div class="form-group">
939 - <label class="control-label" for="goods_id">goods_id</label>  
940 - <input type="inter" class="form-control input-sm" id="goods_id" required placeholder="商品ID" name="goods_id"> 923 +
941 </div> 924 </div>
942 <div class="form-group"> 925 <div class="form-group">
943 - <button type="submit" class="btn btn-success send" rel="75">提交</button>  
944 - <button type="reset" class="btn btn-info" rel="75">重置</button> 926 + <button type="submit" class="btn btn-success send" rel="76">提交</button>
  927 + <button type="reset" class="btn btn-info" rel="76">重置</button>
945 </div> 928 </div>
946 </form> 929 </form>
947 </div> 930 </div>
@@ -951,8 +934,8 @@ @@ -951,8 +934,8 @@
951 <div class="panel-body"> 934 <div class="panel-body">
952 <div class="row"> 935 <div class="row">
953 <div class="col-md-12" style="overflow-x:auto"> 936 <div class="col-md-12" style="overflow-x:auto">
954 - <pre id="response_headers75"></pre>  
955 - <pre id="response75"></pre> 937 + <pre id="response_headers76"></pre>
  938 + <pre id="response76"></pre>
956 </div> 939 </div>
957 </div> 940 </div>
958 </div> 941 </div>
@@ -967,10 +950,10 @@ @@ -967,10 +950,10 @@
967 </div> 950 </div>
968 </div><!-- #sandbox --> 951 </div><!-- #sandbox -->
969 952
970 - <div class="tab-pane" id="sample75"> 953 + <div class="tab-pane" id="sample76">
971 <div class="row"> 954 <div class="row">
972 <div class="col-md-12"> 955 <div class="col-md-12">
973 - <pre id="sample_response75">{ 956 + <pre id="sample_response76">{
974 "code": 1, 957 "code": 1,
975 "msg": "领取成功", 958 "msg": "领取成功",
976 "time": "1607911049", 959 "time": "1607911049",
@@ -990,26 +973,26 @@ @@ -990,26 +973,26 @@
990 </div> 973 </div>
991 </div> 974 </div>
992 <div class="panel panel-default"> 975 <div class="panel panel-default">
993 - <div class="panel-heading" id="heading-76"> 976 + <div class="panel-heading" id="heading-77">
994 <h4 class="panel-title"> 977 <h4 class="panel-title">
995 <span class="label label-primary">POST</span> 978 <span class="label label-primary">POST</span>
996 - <a data-toggle="collapse" data-parent="#accordion76" href="#collapseOne76"> 申请提现 <span class="text-muted">/api/user_wallet_apply/apply</span></a> 979 + <a data-toggle="collapse" data-parent="#accordion77" href="#collapseOne77"> 申请提现 <span class="text-muted">/api/user_wallet_apply/apply</span></a>
997 </h4> 980 </h4>
998 </div> 981 </div>
999 - <div id="collapseOne76" class="panel-collapse collapse"> 982 + <div id="collapseOne77" class="panel-collapse collapse">
1000 <div class="panel-body"> 983 <div class="panel-body">
1001 984
1002 <!-- Nav tabs --> 985 <!-- Nav tabs -->
1003 - <ul class="nav nav-tabs" id="doctab76">  
1004 - <li class="active"><a href="#info76" data-toggle="tab">基础信息</a></li>  
1005 - <li><a href="#sandbox76" data-toggle="tab">在线测试</a></li>  
1006 - <li><a href="#sample76" data-toggle="tab">返回示例</a></li> 986 + <ul class="nav nav-tabs" id="doctab77">
  987 + <li class="active"><a href="#info77" data-toggle="tab">基础信息</a></li>
  988 + <li><a href="#sandbox77" data-toggle="tab">在线测试</a></li>
  989 + <li><a href="#sample77" data-toggle="tab">返回示例</a></li>
1007 </ul> 990 </ul>
1008 991
1009 <!-- Tab panes --> 992 <!-- Tab panes -->
1010 <div class="tab-content"> 993 <div class="tab-content">
1011 994
1012 - <div class="tab-pane active" id="info76"> 995 + <div class="tab-pane active" id="info77">
1013 <div class="well"> 996 <div class="well">
1014 申请提现 </div> 997 申请提现 </div>
1015 <div class="panel panel-default"> 998 <div class="panel panel-default">
@@ -1065,7 +1048,7 @@ @@ -1065,7 +1048,7 @@
1065 </div> 1048 </div>
1066 </div><!-- #info --> 1049 </div><!-- #info -->
1067 1050
1068 - <div class="tab-pane" id="sandbox76"> 1051 + <div class="tab-pane" id="sandbox77">
1069 <div class="row"> 1052 <div class="row">
1070 <div class="col-md-12"> 1053 <div class="col-md-12">
1071 <div class="panel panel-default"> 1054 <div class="panel panel-default">
@@ -1082,14 +1065,14 @@ @@ -1082,14 +1065,14 @@
1082 <div class="panel panel-default"> 1065 <div class="panel panel-default">
1083 <div class="panel-heading"><strong>参数</strong></div> 1066 <div class="panel-heading"><strong>参数</strong></div>
1084 <div class="panel-body"> 1067 <div class="panel-body">
1085 - <form enctype="application/x-www-form-urlencoded" role="form" action="/api/user_wallet_apply/apply" method="POST" name="form76" id="form76"> 1068 + <form enctype="application/x-www-form-urlencoded" role="form" action="/api/user_wallet_apply/apply" method="POST" name="form77" id="form77">
1086 <div class="form-group"> 1069 <div class="form-group">
1087 <label class="control-label" for="money">money</label> 1070 <label class="control-label" for="money">money</label>
1088 <input type="string" class="form-control input-sm" id="money" required placeholder="提现金额" name="money"> 1071 <input type="string" class="form-control input-sm" id="money" required placeholder="提现金额" name="money">
1089 </div> 1072 </div>
1090 <div class="form-group"> 1073 <div class="form-group">
1091 - <button type="submit" class="btn btn-success send" rel="76">提交</button>  
1092 - <button type="reset" class="btn btn-info" rel="76">重置</button> 1074 + <button type="submit" class="btn btn-success send" rel="77">提交</button>
  1075 + <button type="reset" class="btn btn-info" rel="77">重置</button>
1093 </div> 1076 </div>
1094 </form> 1077 </form>
1095 </div> 1078 </div>
@@ -1099,8 +1082,8 @@ @@ -1099,8 +1082,8 @@
1099 <div class="panel-body"> 1082 <div class="panel-body">
1100 <div class="row"> 1083 <div class="row">
1101 <div class="col-md-12" style="overflow-x:auto"> 1084 <div class="col-md-12" style="overflow-x:auto">
1102 - <pre id="response_headers76"></pre>  
1103 - <pre id="response76"></pre> 1085 + <pre id="response_headers77"></pre>
  1086 + <pre id="response77"></pre>
1104 </div> 1087 </div>
1105 </div> 1088 </div>
1106 </div> 1089 </div>
@@ -1115,10 +1098,10 @@ @@ -1115,10 +1098,10 @@
1115 </div> 1098 </div>
1116 </div><!-- #sandbox --> 1099 </div><!-- #sandbox -->
1117 1100
1118 - <div class="tab-pane" id="sample76"> 1101 + <div class="tab-pane" id="sample77">
1119 <div class="row"> 1102 <div class="row">
1120 <div class="col-md-12"> 1103 <div class="col-md-12">
1121 - <pre id="sample_response76">{ 1104 + <pre id="sample_response77">{
1122 "code": 1, 1105 "code": 1,
1123 "msg": "领取成功", 1106 "msg": "领取成功",
1124 "time": "1607911049", 1107 "time": "1607911049",
@@ -1138,26 +1121,26 @@ @@ -1138,26 +1121,26 @@
1138 </div> 1121 </div>
1139 </div> 1122 </div>
1140 <div class="panel panel-default"> 1123 <div class="panel panel-default">
1141 - <div class="panel-heading" id="heading-77"> 1124 + <div class="panel-heading" id="heading-78">
1142 <h4 class="panel-title"> 1125 <h4 class="panel-title">
1143 <span class="label label-success">GET</span> 1126 <span class="label label-success">GET</span>
1144 - <a data-toggle="collapse" data-parent="#accordion77" href="#collapseOne77"> 提现规则 <span class="text-muted">/api/user_wallet_apply/rule</span></a> 1127 + <a data-toggle="collapse" data-parent="#accordion78" href="#collapseOne78"> 提现规则 <span class="text-muted">/api/user_wallet_apply/rule</span></a>
1145 </h4> 1128 </h4>
1146 </div> 1129 </div>
1147 - <div id="collapseOne77" class="panel-collapse collapse"> 1130 + <div id="collapseOne78" class="panel-collapse collapse">
1148 <div class="panel-body"> 1131 <div class="panel-body">
1149 1132
1150 <!-- Nav tabs --> 1133 <!-- Nav tabs -->
1151 - <ul class="nav nav-tabs" id="doctab77">  
1152 - <li class="active"><a href="#info77" data-toggle="tab">基础信息</a></li>  
1153 - <li><a href="#sandbox77" data-toggle="tab">在线测试</a></li>  
1154 - <li><a href="#sample77" data-toggle="tab">返回示例</a></li> 1134 + <ul class="nav nav-tabs" id="doctab78">
  1135 + <li class="active"><a href="#info78" data-toggle="tab">基础信息</a></li>
  1136 + <li><a href="#sandbox78" data-toggle="tab">在线测试</a></li>
  1137 + <li><a href="#sample78" data-toggle="tab">返回示例</a></li>
1155 </ul> 1138 </ul>
1156 1139
1157 <!-- Tab panes --> 1140 <!-- Tab panes -->
1158 <div class="tab-content"> 1141 <div class="tab-content">
1159 1142
1160 - <div class="tab-pane active" id="info77"> 1143 + <div class="tab-pane active" id="info78">
1161 <div class="well"> 1144 <div class="well">
1162 提现规则 </div> 1145 提现规则 </div>
1163 <div class="panel panel-default"> 1146 <div class="panel panel-default">
@@ -1179,19 +1162,19 @@ @@ -1179,19 +1162,19 @@
1179 </div> 1162 </div>
1180 </div><!-- #info --> 1163 </div><!-- #info -->
1181 1164
1182 - <div class="tab-pane" id="sandbox77"> 1165 + <div class="tab-pane" id="sandbox78">
1183 <div class="row"> 1166 <div class="row">
1184 <div class="col-md-12"> 1167 <div class="col-md-12">
1185 <div class="panel panel-default"> 1168 <div class="panel panel-default">
1186 <div class="panel-heading"><strong>参数</strong></div> 1169 <div class="panel-heading"><strong>参数</strong></div>
1187 <div class="panel-body"> 1170 <div class="panel-body">
1188 - <form enctype="application/x-www-form-urlencoded" role="form" action="/api/user_wallet_apply/rule" method="GET" name="form77" id="form77"> 1171 + <form enctype="application/x-www-form-urlencoded" role="form" action="/api/user_wallet_apply/rule" method="GET" name="form78" id="form78">
1189 <div class="form-group"> 1172 <div class="form-group">
1190 1173
1191 </div> 1174 </div>
1192 <div class="form-group"> 1175 <div class="form-group">
1193 - <button type="submit" class="btn btn-success send" rel="77">提交</button>  
1194 - <button type="reset" class="btn btn-info" rel="77">重置</button> 1176 + <button type="submit" class="btn btn-success send" rel="78">提交</button>
  1177 + <button type="reset" class="btn btn-info" rel="78">重置</button>
1195 </div> 1178 </div>
1196 </form> 1179 </form>
1197 </div> 1180 </div>
@@ -1201,8 +1184,8 @@ @@ -1201,8 +1184,8 @@
1201 <div class="panel-body"> 1184 <div class="panel-body">
1202 <div class="row"> 1185 <div class="row">
1203 <div class="col-md-12" style="overflow-x:auto"> 1186 <div class="col-md-12" style="overflow-x:auto">
1204 - <pre id="response_headers77"></pre>  
1205 - <pre id="response77"></pre> 1187 + <pre id="response_headers78"></pre>
  1188 + <pre id="response78"></pre>
1206 </div> 1189 </div>
1207 </div> 1190 </div>
1208 </div> 1191 </div>
@@ -1217,10 +1200,10 @@ @@ -1217,10 +1200,10 @@
1217 </div> 1200 </div>
1218 </div><!-- #sandbox --> 1201 </div><!-- #sandbox -->
1219 1202
1220 - <div class="tab-pane" id="sample77"> 1203 + <div class="tab-pane" id="sample78">
1221 <div class="row"> 1204 <div class="row">
1222 <div class="col-md-12"> 1205 <div class="col-md-12">
1223 - <pre id="sample_response77"></pre> 1206 + <pre id="sample_response78"></pre>
1224 </div> 1207 </div>
1225 </div> 1208 </div>
1226 </div><!-- #sample --> 1209 </div><!-- #sample -->
@@ -2420,6 +2403,132 @@ @@ -2420,6 +2403,132 @@
2420 </div> 2403 </div>
2421 </div> 2404 </div>
2422 </div> 2405 </div>
  2406 + <div class="panel panel-default">
  2407 + <div class="panel-heading" id="heading-75">
  2408 + <h4 class="panel-title">
  2409 + <span class="label label-success">GET</span>
  2410 + <a data-toggle="collapse" data-parent="#accordion75" href="#collapseOne75"> 我的二维码 <span class="text-muted">/api/user/userPoster</span></a>
  2411 + </h4>
  2412 + </div>
  2413 + <div id="collapseOne75" class="panel-collapse collapse">
  2414 + <div class="panel-body">
  2415 +
  2416 + <!-- Nav tabs -->
  2417 + <ul class="nav nav-tabs" id="doctab75">
  2418 + <li class="active"><a href="#info75" data-toggle="tab">基础信息</a></li>
  2419 + <li><a href="#sandbox75" data-toggle="tab">在线测试</a></li>
  2420 + <li><a href="#sample75" data-toggle="tab">返回示例</a></li>
  2421 + </ul>
  2422 +
  2423 + <!-- Tab panes -->
  2424 + <div class="tab-content">
  2425 +
  2426 + <div class="tab-pane active" id="info75">
  2427 + <div class="well">
  2428 + 我的二维码 </div>
  2429 + <div class="panel panel-default">
  2430 + <div class="panel-heading"><strong>Headers</strong></div>
  2431 + <div class="panel-body">
  2432 + <table class="table table-hover">
  2433 + <thead>
  2434 + <tr>
  2435 + <th>名称</th>
  2436 + <th>类型</th>
  2437 + <th>必选</th>
  2438 + <th>描述</th>
  2439 + </tr>
  2440 + </thead>
  2441 + <tbody>
  2442 + <tr>
  2443 + <td>token</td>
  2444 + <td>string</td>
  2445 + <td></td>
  2446 + <td>请求的token</td>
  2447 + </tr>
  2448 + </tbody>
  2449 + </table>
  2450 + </div>
  2451 + </div>
  2452 + <div class="panel panel-default">
  2453 + <div class="panel-heading"><strong>参数</strong></div>
  2454 + <div class="panel-body">
  2455 +
  2456 + </div>
  2457 + </div>
  2458 + <div class="panel panel-default">
  2459 + <div class="panel-heading"><strong>正文</strong></div>
  2460 + <div class="panel-body">
  2461 +</div>
  2462 + </div>
  2463 + </div><!-- #info -->
  2464 +
  2465 + <div class="tab-pane" id="sandbox75">
  2466 + <div class="row">
  2467 + <div class="col-md-12">
  2468 + <div class="panel panel-default">
  2469 + <div class="panel-heading"><strong>Headers</strong></div>
  2470 + <div class="panel-body">
  2471 + <div class="headers">
  2472 + <div class="form-group">
  2473 + <label class="control-label" for="token">token</label>
  2474 + <input type="string" class="form-control input-sm" id="token" required placeholder="请求的token - Ex: " name="token">
  2475 + </div>
  2476 + </div>
  2477 + </div>
  2478 + </div>
  2479 + <div class="panel panel-default">
  2480 + <div class="panel-heading"><strong>参数</strong></div>
  2481 + <div class="panel-body">
  2482 + <form enctype="application/x-www-form-urlencoded" role="form" action="/api/user/userPoster" method="GET" name="form75" id="form75">
  2483 + <div class="form-group">
  2484 +
  2485 + </div>
  2486 + <div class="form-group">
  2487 + <button type="submit" class="btn btn-success send" rel="75">提交</button>
  2488 + <button type="reset" class="btn btn-info" rel="75">重置</button>
  2489 + </div>
  2490 + </form>
  2491 + </div>
  2492 + </div>
  2493 + <div class="panel panel-default">
  2494 + <div class="panel-heading"><strong>响应输出</strong></div>
  2495 + <div class="panel-body">
  2496 + <div class="row">
  2497 + <div class="col-md-12" style="overflow-x:auto">
  2498 + <pre id="response_headers75"></pre>
  2499 + <pre id="response75"></pre>
  2500 + </div>
  2501 + </div>
  2502 + </div>
  2503 + </div>
  2504 + <div class="panel panel-default">
  2505 + <div class="panel-heading"><strong>返回参数</strong></div>
  2506 + <div class="panel-body">
  2507 +
  2508 + </div>
  2509 + </div>
  2510 + </div>
  2511 + </div>
  2512 + </div><!-- #sandbox -->
  2513 +
  2514 + <div class="tab-pane" id="sample75">
  2515 + <div class="row">
  2516 + <div class="col-md-12">
  2517 + <pre id="sample_response75">{
  2518 + 'code':'1',
  2519 + 'msg':'返回成功',
  2520 + "data": {
  2521 + "url": "http://www.recruit.top/uploads/job/1.png", //职位海报地址
  2522 + }
  2523 + }</pre>
  2524 + </div>
  2525 + </div>
  2526 + </div><!-- #sample -->
  2527 +
  2528 + </div><!-- .tab-content -->
  2529 + </div>
  2530 + </div>
  2531 + </div>
2423 <h2>手机短信接口</h2> 2532 <h2>手机短信接口</h2>
2424 <hr> 2533 <hr>
2425 <div class="panel panel-default"> 2534 <div class="panel panel-default">
@@ -11164,26 +11273,26 @@ @@ -11164,26 +11273,26 @@
11164 <h2>钱包记录接口</h2> 11273 <h2>钱包记录接口</h2>
11165 <hr> 11274 <hr>
11166 <div class="panel panel-default"> 11275 <div class="panel panel-default">
11167 - <div class="panel-heading" id="heading-78"> 11276 + <div class="panel-heading" id="heading-79">
11168 <h4 class="panel-title"> 11277 <h4 class="panel-title">
11169 <span class="label label-success">GET</span> 11278 <span class="label label-success">GET</span>
11170 - <a data-toggle="collapse" data-parent="#accordion78" href="#collapseOne78"> 会员中心 <span class="text-muted">/api/user_wallet_log/index</span></a> 11279 + <a data-toggle="collapse" data-parent="#accordion79" href="#collapseOne79"> 会员中心 <span class="text-muted">/api/user_wallet_log/index</span></a>
11171 </h4> 11280 </h4>
11172 </div> 11281 </div>
11173 - <div id="collapseOne78" class="panel-collapse collapse"> 11282 + <div id="collapseOne79" class="panel-collapse collapse">
11174 <div class="panel-body"> 11283 <div class="panel-body">
11175 11284
11176 <!-- Nav tabs --> 11285 <!-- Nav tabs -->
11177 - <ul class="nav nav-tabs" id="doctab78">  
11178 - <li class="active"><a href="#info78" data-toggle="tab">基础信息</a></li>  
11179 - <li><a href="#sandbox78" data-toggle="tab">在线测试</a></li>  
11180 - <li><a href="#sample78" data-toggle="tab">返回示例</a></li> 11286 + <ul class="nav nav-tabs" id="doctab79">
  11287 + <li class="active"><a href="#info79" data-toggle="tab">基础信息</a></li>
  11288 + <li><a href="#sandbox79" data-toggle="tab">在线测试</a></li>
  11289 + <li><a href="#sample79" data-toggle="tab">返回示例</a></li>
11181 </ul> 11290 </ul>
11182 11291
11183 <!-- Tab panes --> 11292 <!-- Tab panes -->
11184 <div class="tab-content"> 11293 <div class="tab-content">
11185 11294
11186 - <div class="tab-pane active" id="info78"> 11295 + <div class="tab-pane active" id="info79">
11187 <div class="well"> 11296 <div class="well">
11188 会员中心 </div> 11297 会员中心 </div>
11189 <div class="panel panel-default"> 11298 <div class="panel panel-default">
@@ -11245,7 +11354,7 @@ @@ -11245,7 +11354,7 @@
11245 </div> 11354 </div>
11246 </div><!-- #info --> 11355 </div><!-- #info -->
11247 11356
11248 - <div class="tab-pane" id="sandbox78"> 11357 + <div class="tab-pane" id="sandbox79">
11249 <div class="row"> 11358 <div class="row">
11250 <div class="col-md-12"> 11359 <div class="col-md-12">
11251 <div class="panel panel-default"> 11360 <div class="panel panel-default">
@@ -11262,7 +11371,7 @@ @@ -11262,7 +11371,7 @@
11262 <div class="panel panel-default"> 11371 <div class="panel panel-default">
11263 <div class="panel-heading"><strong>参数</strong></div> 11372 <div class="panel-heading"><strong>参数</strong></div>
11264 <div class="panel-body"> 11373 <div class="panel-body">
11265 - <form enctype="application/x-www-form-urlencoded" role="form" action="/api/user_wallet_log/index" method="GET" name="form78" id="form78"> 11374 + <form enctype="application/x-www-form-urlencoded" role="form" action="/api/user_wallet_log/index" method="GET" name="form79" id="form79">
11266 <div class="form-group"> 11375 <div class="form-group">
11267 <label class="control-label" for="wallet_type">wallet_type</label> 11376 <label class="control-label" for="wallet_type">wallet_type</label>
11268 <input type="string" class="form-control input-sm" id="wallet_type" required placeholder="钱包类型:money=钱包记录" name="wallet_type"> 11377 <input type="string" class="form-control input-sm" id="wallet_type" required placeholder="钱包类型:money=钱包记录" name="wallet_type">
@@ -11272,8 +11381,8 @@ @@ -11272,8 +11381,8 @@
11272 <input type="string" class="form-control input-sm" id="status" required placeholder="状态:all=全部,add=增加,reduce=减少" name="status"> 11381 <input type="string" class="form-control input-sm" id="status" required placeholder="状态:all=全部,add=增加,reduce=减少" name="status">
11273 </div> 11382 </div>
11274 <div class="form-group"> 11383 <div class="form-group">
11275 - <button type="submit" class="btn btn-success send" rel="78">提交</button>  
11276 - <button type="reset" class="btn btn-info" rel="78">重置</button> 11384 + <button type="submit" class="btn btn-success send" rel="79">提交</button>
  11385 + <button type="reset" class="btn btn-info" rel="79">重置</button>
11277 </div> 11386 </div>
11278 </form> 11387 </form>
11279 </div> 11388 </div>
@@ -11283,8 +11392,8 @@ @@ -11283,8 +11392,8 @@
11283 <div class="panel-body"> 11392 <div class="panel-body">
11284 <div class="row"> 11393 <div class="row">
11285 <div class="col-md-12" style="overflow-x:auto"> 11394 <div class="col-md-12" style="overflow-x:auto">
11286 - <pre id="response_headers78"></pre>  
11287 - <pre id="response78"></pre> 11395 + <pre id="response_headers79"></pre>
  11396 + <pre id="response79"></pre>
11288 </div> 11397 </div>
11289 </div> 11398 </div>
11290 </div> 11399 </div>
@@ -11299,10 +11408,10 @@ @@ -11299,10 +11408,10 @@
11299 </div> 11408 </div>
11300 </div><!-- #sandbox --> 11409 </div><!-- #sandbox -->
11301 11410
11302 - <div class="tab-pane" id="sample78"> 11411 + <div class="tab-pane" id="sample79">
11303 <div class="row"> 11412 <div class="row">
11304 <div class="col-md-12"> 11413 <div class="col-md-12">
11305 - <pre id="sample_response78"></pre> 11414 + <pre id="sample_response79"></pre>
11306 </div> 11415 </div>
11307 </div> 11416 </div>
11308 </div><!-- #sample --> 11417 </div><!-- #sample -->
@@ -11317,7 +11426,7 @@ @@ -11317,7 +11426,7 @@
11317 11426
11318 <div class="row mt0 footer"> 11427 <div class="row mt0 footer">
11319 <div class="col-md-6" align="left"> 11428 <div class="col-md-6" align="left">
11320 - Generated on 2020-12-24 14:03:35 </div> 11429 + Generated on 2020-12-25 16:22:37 </div>
11321 <div class="col-md-6" align="right"> 11430 <div class="col-md-6" align="right">
11322 <a href="./" target="_blank">My Website</a> 11431 <a href="./" target="_blank">My Website</a>
11323 </div> 11432 </div>
@@ -29,7 +29,7 @@ private static $installed = array ( @@ -29,7 +29,7 @@ private static $installed = array (
29 'aliases' => 29 'aliases' =>
30 array ( 30 array (
31 ), 31 ),
32 - 'reference' => '1907524334aef6c232dc16b8fd0cec852244fe97', 32 + 'reference' => '1663c55b246cd257244c47c0ae36e3335c14f95d',
33 'name' => 'karsonzhang/fastadmin', 33 'name' => 'karsonzhang/fastadmin',
34 ), 34 ),
35 'versions' => 35 'versions' =>
@@ -77,7 +77,7 @@ private static $installed = array ( @@ -77,7 +77,7 @@ private static $installed = array (
77 'aliases' => 77 'aliases' =>
78 array ( 78 array (
79 ), 79 ),
80 - 'reference' => '1907524334aef6c232dc16b8fd0cec852244fe97', 80 + 'reference' => '1663c55b246cd257244c47c0ae36e3335c14f95d',
81 ), 81 ),
82 'karsonzhang/fastadmin-addons' => 82 'karsonzhang/fastadmin-addons' =>
83 array ( 83 array (
@@ -443,6 +443,15 @@ private static $installed = array ( @@ -443,6 +443,15 @@ private static $installed = array (
443 ), 443 ),
444 'reference' => 'c28d37743bda4a0455286ca85b17b5791d626e10', 444 'reference' => 'c28d37743bda4a0455286ca85b17b5791d626e10',
445 ), 445 ),
  446 + 'topthink/think-image' =>
  447 + array (
  448 + 'pretty_version' => 'v1.0.7',
  449 + 'version' => '1.0.7.0',
  450 + 'aliases' =>
  451 + array (
  452 + ),
  453 + 'reference' => '8586cf47f117481c6d415b20f7dedf62e79d5512',
  454 + ),
446 'topthink/think-installer' => 455 'topthink/think-installer' =>
447 array ( 456 array (
448 'pretty_version' => 'v1.0.13', 457 'pretty_version' => 'v1.0.13',
@@ -8,7 +8,7 @@ $baseDir = dirname($vendorDir); @@ -8,7 +8,7 @@ $baseDir = dirname($vendorDir);
8 return array( 8 return array(
9 'think\\composer\\' => array($vendorDir . '/topthink/think-installer/src'), 9 'think\\composer\\' => array($vendorDir . '/topthink/think-installer/src'),
10 'think\\captcha\\' => array($vendorDir . '/topthink/think-captcha/src'), 10 'think\\captcha\\' => array($vendorDir . '/topthink/think-captcha/src'),
11 - 'think\\' => array($vendorDir . '/karsonzhang/fastadmin-addons/src', $baseDir . '/thinkphp/library/think', $vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-queue/src'), 11 + 'think\\' => array($vendorDir . '/karsonzhang/fastadmin-addons/src', $baseDir . '/thinkphp/library/think', $vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-queue/src', $vendorDir . '/topthink/think-image/src'),
12 'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'), 12 'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
13 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), 13 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
14 'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'), 14 'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
@@ -158,6 +158,7 @@ class ComposerStaticInit85e84021e2781f5f99c143befcc82f3d @@ -158,6 +158,7 @@ class ComposerStaticInit85e84021e2781f5f99c143befcc82f3d
158 1 => __DIR__ . '/../..' . '/thinkphp/library/think', 158 1 => __DIR__ . '/../..' . '/thinkphp/library/think',
159 2 => __DIR__ . '/..' . '/topthink/think-helper/src', 159 2 => __DIR__ . '/..' . '/topthink/think-helper/src',
160 3 => __DIR__ . '/..' . '/topthink/think-queue/src', 160 3 => __DIR__ . '/..' . '/topthink/think-queue/src',
  161 + 4 => __DIR__ . '/..' . '/topthink/think-image/src',
161 ), 162 ),
162 'Symfony\\Polyfill\\Php72\\' => 163 'Symfony\\Polyfill\\Php72\\' =>
163 array ( 164 array (
@@ -2965,6 +2965,53 @@ @@ -2965,6 +2965,53 @@
2965 "install-path": "../topthink/think-helper" 2965 "install-path": "../topthink/think-helper"
2966 }, 2966 },
2967 { 2967 {
  2968 + "name": "topthink/think-image",
  2969 + "version": "v1.0.7",
  2970 + "version_normalized": "1.0.7.0",
  2971 + "source": {
  2972 + "type": "git",
  2973 + "url": "https://github.com/top-think/think-image.git",
  2974 + "reference": "8586cf47f117481c6d415b20f7dedf62e79d5512"
  2975 + },
  2976 + "dist": {
  2977 + "type": "zip",
  2978 + "url": "https://api.github.com/repos/top-think/think-image/zipball/8586cf47f117481c6d415b20f7dedf62e79d5512",
  2979 + "reference": "8586cf47f117481c6d415b20f7dedf62e79d5512",
  2980 + "shasum": ""
  2981 + },
  2982 + "require": {
  2983 + "ext-gd": "*"
  2984 + },
  2985 + "require-dev": {
  2986 + "phpunit/phpunit": "4.8.*",
  2987 + "topthink/framework": "^5.0"
  2988 + },
  2989 + "time": "2016-09-29T06:05:43+00:00",
  2990 + "type": "library",
  2991 + "installation-source": "dist",
  2992 + "autoload": {
  2993 + "psr-4": {
  2994 + "think\\": "src"
  2995 + }
  2996 + },
  2997 + "notification-url": "https://packagist.org/downloads/",
  2998 + "license": [
  2999 + "Apache-2.0"
  3000 + ],
  3001 + "authors": [
  3002 + {
  3003 + "name": "yunwuxin",
  3004 + "email": "448901948@qq.com"
  3005 + }
  3006 + ],
  3007 + "description": "The ThinkPHP5 Image Package",
  3008 + "support": {
  3009 + "issues": "https://github.com/top-think/think-image/issues",
  3010 + "source": "https://github.com/top-think/think-image/tree/master"
  3011 + },
  3012 + "install-path": "../topthink/think-image"
  3013 + },
  3014 + {
2968 "name": "topthink/think-installer", 3015 "name": "topthink/think-installer",
2969 "version": "v1.0.13", 3016 "version": "v1.0.13",
2970 "version_normalized": "1.0.13.0", 3017 "version_normalized": "1.0.13.0",
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 'aliases' => 6 'aliases' =>
7 array ( 7 array (
8 ), 8 ),
9 - 'reference' => '1907524334aef6c232dc16b8fd0cec852244fe97', 9 + 'reference' => '1663c55b246cd257244c47c0ae36e3335c14f95d',
10 'name' => 'karsonzhang/fastadmin', 10 'name' => 'karsonzhang/fastadmin',
11 ), 11 ),
12 'versions' => 12 'versions' =>
@@ -54,7 +54,7 @@ @@ -54,7 +54,7 @@
54 'aliases' => 54 'aliases' =>
55 array ( 55 array (
56 ), 56 ),
57 - 'reference' => '1907524334aef6c232dc16b8fd0cec852244fe97', 57 + 'reference' => '1663c55b246cd257244c47c0ae36e3335c14f95d',
58 ), 58 ),
59 'karsonzhang/fastadmin-addons' => 59 'karsonzhang/fastadmin-addons' =>
60 array ( 60 array (
@@ -420,6 +420,15 @@ @@ -420,6 +420,15 @@
420 ), 420 ),
421 'reference' => 'c28d37743bda4a0455286ca85b17b5791d626e10', 421 'reference' => 'c28d37743bda4a0455286ca85b17b5791d626e10',
422 ), 422 ),
  423 + 'topthink/think-image' =>
  424 + array (
  425 + 'pretty_version' => 'v1.0.7',
  426 + 'version' => '1.0.7.0',
  427 + 'aliases' =>
  428 + array (
  429 + ),
  430 + 'reference' => '8586cf47f117481c6d415b20f7dedf62e79d5512',
  431 + ),
423 'topthink/think-installer' => 432 'topthink/think-installer' =>
424 array ( 433 array (
425 'pretty_version' => 'v1.0.13', 434 'pretty_version' => 'v1.0.13',
  1 +/vendor/
  2 +/thinkphp/
  3 +/composer.lock
  4 +/.idea/
  1 +language: php
  2 +
  3 +php:
  4 + - 5.4
  5 + - 5.5
  6 + - 5.6
  7 + - 7.0
  8 + - hhvm
  9 +
  10 +matrix:
  11 + allow_failures:
  12 + - php: 7.0
  13 + - php: hhvm
  14 +
  15 +before_script:
  16 + - composer self-update
  17 + - composer install --prefer-source --no-interaction --dev
  18 +
  19 +script: phpunit --coverage-clover=coverage.xml --configuration=phpunit.xml
  20 +
  21 +after_success:
  22 + - bash <(curl -s https://codecov.io/bash)
  1 + Apache License
  2 + Version 2.0, January 2004
  3 + http://www.apache.org/licenses/
  4 +
  5 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
  6 +
  7 + 1. Definitions.
  8 +
  9 + "License" shall mean the terms and conditions for use, reproduction,
  10 + and distribution as defined by Sections 1 through 9 of this document.
  11 +
  12 + "Licensor" shall mean the copyright owner or entity authorized by
  13 + the copyright owner that is granting the License.
  14 +
  15 + "Legal Entity" shall mean the union of the acting entity and all
  16 + other entities that control, are controlled by, or are under common
  17 + control with that entity. For the purposes of this definition,
  18 + "control" means (i) the power, direct or indirect, to cause the
  19 + direction or management of such entity, whether by contract or
  20 + otherwise, or (ii) ownership of fifty percent (50%) or more of the
  21 + outstanding shares, or (iii) beneficial ownership of such entity.
  22 +
  23 + "You" (or "Your") shall mean an individual or Legal Entity
  24 + exercising permissions granted by this License.
  25 +
  26 + "Source" form shall mean the preferred form for making modifications,
  27 + including but not limited to software source code, documentation
  28 + source, and configuration files.
  29 +
  30 + "Object" form shall mean any form resulting from mechanical
  31 + transformation or translation of a Source form, including but
  32 + not limited to compiled object code, generated documentation,
  33 + and conversions to other media types.
  34 +
  35 + "Work" shall mean the work of authorship, whether in Source or
  36 + Object form, made available under the License, as indicated by a
  37 + copyright notice that is included in or attached to the work
  38 + (an example is provided in the Appendix below).
  39 +
  40 + "Derivative Works" shall mean any work, whether in Source or Object
  41 + form, that is based on (or derived from) the Work and for which the
  42 + editorial revisions, annotations, elaborations, or other modifications
  43 + represent, as a whole, an original work of authorship. For the purposes
  44 + of this License, Derivative Works shall not include works that remain
  45 + separable from, or merely link (or bind by name) to the interfaces of,
  46 + the Work and Derivative Works thereof.
  47 +
  48 + "Contribution" shall mean any work of authorship, including
  49 + the original version of the Work and any modifications or additions
  50 + to that Work or Derivative Works thereof, that is intentionally
  51 + submitted to Licensor for inclusion in the Work by the copyright owner
  52 + or by an individual or Legal Entity authorized to submit on behalf of
  53 + the copyright owner. For the purposes of this definition, "submitted"
  54 + means any form of electronic, verbal, or written communication sent
  55 + to the Licensor or its representatives, including but not limited to
  56 + communication on electronic mailing lists, source code control systems,
  57 + and issue tracking systems that are managed by, or on behalf of, the
  58 + Licensor for the purpose of discussing and improving the Work, but
  59 + excluding communication that is conspicuously marked or otherwise
  60 + designated in writing by the copyright owner as "Not a Contribution."
  61 +
  62 + "Contributor" shall mean Licensor and any individual or Legal Entity
  63 + on behalf of whom a Contribution has been received by Licensor and
  64 + subsequently incorporated within the Work.
  65 +
  66 + 2. Grant of Copyright License. Subject to the terms and conditions of
  67 + this License, each Contributor hereby grants to You a perpetual,
  68 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  69 + copyright license to reproduce, prepare Derivative Works of,
  70 + publicly display, publicly perform, sublicense, and distribute the
  71 + Work and such Derivative Works in Source or Object form.
  72 +
  73 + 3. Grant of Patent License. Subject to the terms and conditions of
  74 + this License, each Contributor hereby grants to You a perpetual,
  75 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  76 + (except as stated in this section) patent license to make, have made,
  77 + use, offer to sell, sell, import, and otherwise transfer the Work,
  78 + where such license applies only to those patent claims licensable
  79 + by such Contributor that are necessarily infringed by their
  80 + Contribution(s) alone or by combination of their Contribution(s)
  81 + with the Work to which such Contribution(s) was submitted. If You
  82 + institute patent litigation against any entity (including a
  83 + cross-claim or counterclaim in a lawsuit) alleging that the Work
  84 + or a Contribution incorporated within the Work constitutes direct
  85 + or contributory patent infringement, then any patent licenses
  86 + granted to You under this License for that Work shall terminate
  87 + as of the date such litigation is filed.
  88 +
  89 + 4. Redistribution. You may reproduce and distribute copies of the
  90 + Work or Derivative Works thereof in any medium, with or without
  91 + modifications, and in Source or Object form, provided that You
  92 + meet the following conditions:
  93 +
  94 + (a) You must give any other recipients of the Work or
  95 + Derivative Works a copy of this License; and
  96 +
  97 + (b) You must cause any modified files to carry prominent notices
  98 + stating that You changed the files; and
  99 +
  100 + (c) You must retain, in the Source form of any Derivative Works
  101 + that You distribute, all copyright, patent, trademark, and
  102 + attribution notices from the Source form of the Work,
  103 + excluding those notices that do not pertain to any part of
  104 + the Derivative Works; and
  105 +
  106 + (d) If the Work includes a "NOTICE" text file as part of its
  107 + distribution, then any Derivative Works that You distribute must
  108 + include a readable copy of the attribution notices contained
  109 + within such NOTICE file, excluding those notices that do not
  110 + pertain to any part of the Derivative Works, in at least one
  111 + of the following places: within a NOTICE text file distributed
  112 + as part of the Derivative Works; within the Source form or
  113 + documentation, if provided along with the Derivative Works; or,
  114 + within a display generated by the Derivative Works, if and
  115 + wherever such third-party notices normally appear. The contents
  116 + of the NOTICE file are for informational purposes only and
  117 + do not modify the License. You may add Your own attribution
  118 + notices within Derivative Works that You distribute, alongside
  119 + or as an addendum to the NOTICE text from the Work, provided
  120 + that such additional attribution notices cannot be construed
  121 + as modifying the License.
  122 +
  123 + You may add Your own copyright statement to Your modifications and
  124 + may provide additional or different license terms and conditions
  125 + for use, reproduction, or distribution of Your modifications, or
  126 + for any such Derivative Works as a whole, provided Your use,
  127 + reproduction, and distribution of the Work otherwise complies with
  128 + the conditions stated in this License.
  129 +
  130 + 5. Submission of Contributions. Unless You explicitly state otherwise,
  131 + any Contribution intentionally submitted for inclusion in the Work
  132 + by You to the Licensor shall be under the terms and conditions of
  133 + this License, without any additional terms or conditions.
  134 + Notwithstanding the above, nothing herein shall supersede or modify
  135 + the terms of any separate license agreement you may have executed
  136 + with Licensor regarding such Contributions.
  137 +
  138 + 6. Trademarks. This License does not grant permission to use the trade
  139 + names, trademarks, service marks, or product names of the Licensor,
  140 + except as required for reasonable and customary use in describing the
  141 + origin of the Work and reproducing the content of the NOTICE file.
  142 +
  143 + 7. Disclaimer of Warranty. Unless required by applicable law or
  144 + agreed to in writing, Licensor provides the Work (and each
  145 + Contributor provides its Contributions) on an "AS IS" BASIS,
  146 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  147 + implied, including, without limitation, any warranties or conditions
  148 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
  149 + PARTICULAR PURPOSE. You are solely responsible for determining the
  150 + appropriateness of using or redistributing the Work and assume any
  151 + risks associated with Your exercise of permissions under this License.
  152 +
  153 + 8. Limitation of Liability. In no event and under no legal theory,
  154 + whether in tort (including negligence), contract, or otherwise,
  155 + unless required by applicable law (such as deliberate and grossly
  156 + negligent acts) or agreed to in writing, shall any Contributor be
  157 + liable to You for damages, including any direct, indirect, special,
  158 + incidental, or consequential damages of any character arising as a
  159 + result of this License or out of the use or inability to use the
  160 + Work (including but not limited to damages for loss of goodwill,
  161 + work stoppage, computer failure or malfunction, or any and all
  162 + other commercial damages or losses), even if such Contributor
  163 + has been advised of the possibility of such damages.
  164 +
  165 + 9. Accepting Warranty or Additional Liability. While redistributing
  166 + the Work or Derivative Works thereof, You may choose to offer,
  167 + and charge a fee for, acceptance of support, warranty, indemnity,
  168 + or other liability obligations and/or rights consistent with this
  169 + License. However, in accepting such obligations, You may act only
  170 + on Your own behalf and on Your sole responsibility, not on behalf
  171 + of any other Contributor, and only if You agree to indemnify,
  172 + defend, and hold each Contributor harmless for any liability
  173 + incurred by, or claims asserted against, such Contributor by reason
  174 + of your accepting any such warranty or additional liability.
  175 +
  176 + END OF TERMS AND CONDITIONS
  177 +
  178 + APPENDIX: How to apply the Apache License to your work.
  179 +
  180 + To apply the Apache License to your work, attach the following
  181 + boilerplate notice, with the fields enclosed by brackets "{}"
  182 + replaced with your own identifying information. (Don't include
  183 + the brackets!) The text should be enclosed in the appropriate
  184 + comment syntax for the file format. We also recommend that a
  185 + file or class name and description of purpose be included on the
  186 + same "printed page" as the copyright notice for easier
  187 + identification within third-party archives.
  188 +
  189 + Copyright {yyyy} {name of copyright owner}
  190 +
  191 + Licensed under the Apache License, Version 2.0 (the "License");
  192 + you may not use this file except in compliance with the License.
  193 + You may obtain a copy of the License at
  194 +
  195 + http://www.apache.org/licenses/LICENSE-2.0
  196 +
  197 + Unless required by applicable law or agreed to in writing, software
  198 + distributed under the License is distributed on an "AS IS" BASIS,
  199 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  200 + See the License for the specific language governing permissions and
  201 + limitations under the License.
  1 +# The ThinkPHP5 Image Package
  2 +
  3 +[![Build Status](https://img.shields.io/travis/top-think/think-image.svg)](https://travis-ci.org/top-think/think-image)
  4 +[![Coverage Status](https://img.shields.io/codecov/c/github/top-think/think-image.svg)](https://codecov.io/github/top-think/think-image)
  5 +[![Downloads](https://img.shields.io/github/downloads/top-think/think-image/total.svg)](https://github.com/top-think/think-image/releases)
  6 +[![Releases](https://img.shields.io/github/release/top-think/think-image.svg)](https://github.com/top-think/think-image/releases/latest)
  7 +[![Releases Downloads](https://img.shields.io/github/downloads/top-think/think-image/latest/total.svg)](https://github.com/top-think/think-image/releases/latest)
  8 +[![Packagist Status](https://img.shields.io/packagist/v/top-think/think-image.svg)](https://packagist.org/packages/topthink/think-image)
  9 +[![Packagist Downloads](https://img.shields.io/packagist/dt/top-think/think-image.svg)](https://packagist.org/packages/topthink/think-image)
  10 +
  11 +## 安装
  12 +
  13 +> composer require topthink/think-image
  14 +
  15 +## 使用
  16 +
  17 +~~~
  18 +$image = \think\Image::open('./image.jpg');
  19 +或者
  20 +$image = \think\Image::open(request()->file('image'));
  21 +
  22 +
  23 +$image->crop(...)
  24 + ->thumb(...)
  25 + ->water(...)
  26 + ->text(....)
  27 + ->save(..);
  28 +
  29 +~~~
  1 +{
  2 + "name": "topthink/think-image",
  3 + "description": "The ThinkPHP5 Image Package",
  4 + "license": "Apache-2.0",
  5 + "authors": [
  6 + {
  7 + "name": "yunwuxin",
  8 + "email": "448901948@qq.com"
  9 + }
  10 + ],
  11 + "require": {
  12 + "ext-gd": "*"
  13 + },
  14 + "require-dev": {
  15 + "topthink/framework": "^5.0",
  16 + "phpunit/phpunit": "4.8.*"
  17 + },
  18 + "config": {
  19 + "preferred-install": "dist"
  20 + },
  21 + "autoload": {
  22 + "psr-4": {
  23 + "think\\": "src"
  24 + }
  25 + }
  26 +}
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<phpunit backupGlobals="false"
  3 + backupStaticAttributes="false"
  4 + bootstrap="tests/autoload.php"
  5 + colors="true"
  6 + convertErrorsToExceptions="true"
  7 + convertNoticesToExceptions="true"
  8 + convertWarningsToExceptions="true"
  9 + processIsolation="false"
  10 + stopOnFailure="false"
  11 + syntaxCheck="false">
  12 + <testsuites>
  13 + <testsuite name="Package Test Suite">
  14 + <directory suffix=".php">./tests/</directory>
  15 + </testsuite>
  16 + </testsuites>
  17 + <listeners>
  18 + <listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener" />
  19 + </listeners>
  20 +</phpunit>
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think;
  13 +
  14 +use think\image\Exception as ImageException;
  15 +use think\image\gif\Gif;
  16 +
  17 +class Image
  18 +{
  19 +
  20 + /* 缩略图相关常量定义 */
  21 + const THUMB_SCALING = 1; //常量,标识缩略图等比例缩放类型
  22 + const THUMB_FILLED = 2; //常量,标识缩略图缩放后填充类型
  23 + const THUMB_CENTER = 3; //常量,标识缩略图居中裁剪类型
  24 + const THUMB_NORTHWEST = 4; //常量,标识缩略图左上角裁剪类型
  25 + const THUMB_SOUTHEAST = 5; //常量,标识缩略图右下角裁剪类型
  26 + const THUMB_FIXED = 6; //常量,标识缩略图固定尺寸缩放类型
  27 + /* 水印相关常量定义 */
  28 + const WATER_NORTHWEST = 1; //常量,标识左上角水印
  29 + const WATER_NORTH = 2; //常量,标识上居中水印
  30 + const WATER_NORTHEAST = 3; //常量,标识右上角水印
  31 + const WATER_WEST = 4; //常量,标识左居中水印
  32 + const WATER_CENTER = 5; //常量,标识居中水印
  33 + const WATER_EAST = 6; //常量,标识右居中水印
  34 + const WATER_SOUTHWEST = 7; //常量,标识左下角水印
  35 + const WATER_SOUTH = 8; //常量,标识下居中水印
  36 + const WATER_SOUTHEAST = 9; //常量,标识右下角水印
  37 + /* 翻转相关常量定义 */
  38 + const FLIP_X = 1; //X轴翻转
  39 + const FLIP_Y = 2; //Y轴翻转
  40 +
  41 + /**
  42 + * 图像资源对象
  43 + *
  44 + * @var resource
  45 + */
  46 + protected $im;
  47 +
  48 + /** @var Gif */
  49 + protected $gif;
  50 +
  51 + /**
  52 + * 图像信息,包括 width, height, type, mime, size
  53 + *
  54 + * @var array
  55 + */
  56 + protected $info;
  57 +
  58 + protected function __construct(\SplFileInfo $file)
  59 + {
  60 + //获取图像信息
  61 + $info = @getimagesize($file->getPathname());
  62 +
  63 + //检测图像合法性
  64 + if (false === $info || (IMAGETYPE_GIF === $info[2] && empty($info['bits']))) {
  65 + throw new ImageException('Illegal image file');
  66 + }
  67 +
  68 + //设置图像信息
  69 + $this->info = [
  70 + 'width' => $info[0],
  71 + 'height' => $info[1],
  72 + 'type' => image_type_to_extension($info[2], false),
  73 + 'mime' => $info['mime'],
  74 + ];
  75 +
  76 + //打开图像
  77 + if ('gif' == $this->info['type']) {
  78 + $this->gif = new Gif($file->getPathname());
  79 + $this->im = @imagecreatefromstring($this->gif->image());
  80 + } else {
  81 + $fun = "imagecreatefrom{$this->info['type']}";
  82 + $this->im = @$fun($file->getPathname());
  83 + }
  84 +
  85 + if (empty($this->im)) {
  86 + throw new ImageException('Failed to create image resources!');
  87 + }
  88 +
  89 + }
  90 +
  91 + /**
  92 + * 打开一个图片文件
  93 + * @param \SplFileInfo|string $file
  94 + * @return Image
  95 + */
  96 + public static function open($file)
  97 + {
  98 + if (is_string($file)) {
  99 + $file = new \SplFileInfo($file);
  100 + }
  101 + if (!$file->isFile()) {
  102 + throw new ImageException('image file not exist');
  103 + }
  104 + return new self($file);
  105 + }
  106 +
  107 + /**
  108 + * 保存图像
  109 + * @param string $pathname 图像保存路径名称
  110 + * @param null|string $type 图像类型
  111 + * @param int $quality 图像质量
  112 + * @param bool $interlace 是否对JPEG类型图像设置隔行扫描
  113 + * @return $this
  114 + */
  115 + public function save($pathname, $type = null, $quality = 80, $interlace = true)
  116 + {
  117 + //自动获取图像类型
  118 + if (is_null($type)) {
  119 + $type = $this->info['type'];
  120 + } else {
  121 + $type = strtolower($type);
  122 + }
  123 + //保存图像
  124 + if ('jpeg' == $type || 'jpg' == $type) {
  125 + //JPEG图像设置隔行扫描
  126 + imageinterlace($this->im, $interlace);
  127 + imagejpeg($this->im, $pathname, $quality);
  128 + } elseif ('gif' == $type && !empty($this->gif)) {
  129 + $this->gif->save($pathname);
  130 + } elseif ('png' == $type) {
  131 + //设定保存完整的 alpha 通道信息
  132 + imagesavealpha($this->im, true);
  133 + //ImagePNG生成图像的质量范围从0到9的
  134 + imagepng($this->im, $pathname, min((int) ($quality / 10), 9));
  135 + } else {
  136 + $fun = 'image' . $type;
  137 + $fun($this->im, $pathname);
  138 + }
  139 +
  140 + return $this;
  141 + }
  142 +
  143 + /**
  144 + * 返回图像宽度
  145 + * @return int 图像宽度
  146 + */
  147 + public function width()
  148 + {
  149 + return $this->info['width'];
  150 + }
  151 +
  152 + /**
  153 + * 返回图像高度
  154 + * @return int 图像高度
  155 + */
  156 + public function height()
  157 + {
  158 + return $this->info['height'];
  159 + }
  160 +
  161 + /**
  162 + * 返回图像类型
  163 + * @return string 图像类型
  164 + */
  165 + public function type()
  166 + {
  167 + return $this->info['type'];
  168 + }
  169 +
  170 + /**
  171 + * 返回图像MIME类型
  172 + * @return string 图像MIME类型
  173 + */
  174 + public function mime()
  175 + {
  176 + return $this->info['mime'];
  177 + }
  178 +
  179 + /**
  180 + * 返回图像尺寸数组 0 - 图像宽度,1 - 图像高度
  181 + * @return array 图像尺寸
  182 + */
  183 + public function size()
  184 + {
  185 + return [$this->info['width'], $this->info['height']];
  186 + }
  187 +
  188 + /**
  189 + * 旋转图像
  190 + * @param int $degrees 顺时针旋转的度数
  191 + * @return $this
  192 + */
  193 + public function rotate($degrees = 90)
  194 + {
  195 + do {
  196 + $img = imagerotate($this->im, -$degrees, imagecolorallocatealpha($this->im, 0, 0, 0, 127));
  197 + imagedestroy($this->im);
  198 + $this->im = $img;
  199 + } while (!empty($this->gif) && $this->gifNext());
  200 +
  201 + $this->info['width'] = imagesx($this->im);
  202 + $this->info['height'] = imagesy($this->im);
  203 +
  204 + return $this;
  205 + }
  206 +
  207 + /**
  208 + * 翻转图像
  209 + * @param integer $direction 翻转轴,X或者Y
  210 + * @return $this
  211 + */
  212 + public function flip($direction = self::FLIP_X)
  213 + {
  214 + //原图宽度和高度
  215 + $w = $this->info['width'];
  216 + $h = $this->info['height'];
  217 +
  218 + do {
  219 +
  220 + $img = imagecreatetruecolor($w, $h);
  221 +
  222 + switch ($direction) {
  223 + case self::FLIP_X:
  224 + for ($y = 0; $y < $h; $y++) {
  225 + imagecopy($img, $this->im, 0, $h - $y - 1, 0, $y, $w, 1);
  226 + }
  227 + break;
  228 + case self::FLIP_Y:
  229 + for ($x = 0; $x < $w; $x++) {
  230 + imagecopy($img, $this->im, $w - $x - 1, 0, $x, 0, 1, $h);
  231 + }
  232 + break;
  233 + default:
  234 + throw new ImageException('不支持的翻转类型');
  235 + }
  236 +
  237 + imagedestroy($this->im);
  238 + $this->im = $img;
  239 +
  240 + } while (!empty($this->gif) && $this->gifNext());
  241 +
  242 + return $this;
  243 + }
  244 +
  245 + /**
  246 + * 裁剪图像
  247 + *
  248 + * @param integer $w 裁剪区域宽度
  249 + * @param integer $h 裁剪区域高度
  250 + * @param integer $x 裁剪区域x坐标
  251 + * @param integer $y 裁剪区域y坐标
  252 + * @param integer $width 图像保存宽度
  253 + * @param integer $height 图像保存高度
  254 + *
  255 + * @return $this
  256 + */
  257 + public function crop($w, $h, $x = 0, $y = 0, $width = null, $height = null)
  258 + {
  259 + //设置保存尺寸
  260 + empty($width) && $width = $w;
  261 + empty($height) && $height = $h;
  262 + do {
  263 + //创建新图像
  264 + $img = imagecreatetruecolor($width, $height);
  265 + // 调整默认颜色
  266 + $color = imagecolorallocate($img, 255, 255, 255);
  267 + imagefill($img, 0, 0, $color);
  268 + //裁剪
  269 + imagecopyresampled($img, $this->im, 0, 0, $x, $y, $width, $height, $w, $h);
  270 + imagedestroy($this->im); //销毁原图
  271 + //设置新图像
  272 + $this->im = $img;
  273 + } while (!empty($this->gif) && $this->gifNext());
  274 + $this->info['width'] = (int) $width;
  275 + $this->info['height'] = (int) $height;
  276 + return $this;
  277 + }
  278 +
  279 + /**
  280 + * 生成缩略图
  281 + *
  282 + * @param integer $width 缩略图最大宽度
  283 + * @param integer $height 缩略图最大高度
  284 + * @param int $type 缩略图裁剪类型
  285 + *
  286 + * @return $this
  287 + */
  288 + public function thumb($width, $height, $type = self::THUMB_SCALING)
  289 + {
  290 + //原图宽度和高度
  291 + $w = $this->info['width'];
  292 + $h = $this->info['height'];
  293 + /* 计算缩略图生成的必要参数 */
  294 + switch ($type) {
  295 + /* 等比例缩放 */
  296 + case self::THUMB_SCALING:
  297 + //原图尺寸小于缩略图尺寸则不进行缩略
  298 + if ($w < $width && $h < $height) {
  299 + return $this;
  300 + }
  301 + //计算缩放比例
  302 + $scale = min($width / $w, $height / $h);
  303 + //设置缩略图的坐标及宽度和高度
  304 + $x = $y = 0;
  305 + $width = $w * $scale;
  306 + $height = $h * $scale;
  307 + break;
  308 + /* 居中裁剪 */
  309 + case self::THUMB_CENTER:
  310 + //计算缩放比例
  311 + $scale = max($width / $w, $height / $h);
  312 + //设置缩略图的坐标及宽度和高度
  313 + $w = $width / $scale;
  314 + $h = $height / $scale;
  315 + $x = ($this->info['width'] - $w) / 2;
  316 + $y = ($this->info['height'] - $h) / 2;
  317 + break;
  318 + /* 左上角裁剪 */
  319 + case self::THUMB_NORTHWEST:
  320 + //计算缩放比例
  321 + $scale = max($width / $w, $height / $h);
  322 + //设置缩略图的坐标及宽度和高度
  323 + $x = $y = 0;
  324 + $w = $width / $scale;
  325 + $h = $height / $scale;
  326 + break;
  327 + /* 右下角裁剪 */
  328 + case self::THUMB_SOUTHEAST:
  329 + //计算缩放比例
  330 + $scale = max($width / $w, $height / $h);
  331 + //设置缩略图的坐标及宽度和高度
  332 + $w = $width / $scale;
  333 + $h = $height / $scale;
  334 + $x = $this->info['width'] - $w;
  335 + $y = $this->info['height'] - $h;
  336 + break;
  337 + /* 填充 */
  338 + case self::THUMB_FILLED:
  339 + //计算缩放比例
  340 + if ($w < $width && $h < $height) {
  341 + $scale = 1;
  342 + } else {
  343 + $scale = min($width / $w, $height / $h);
  344 + }
  345 + //设置缩略图的坐标及宽度和高度
  346 + $neww = $w * $scale;
  347 + $newh = $h * $scale;
  348 + $x = $this->info['width'] - $w;
  349 + $y = $this->info['height'] - $h;
  350 + $posx = ($width - $w * $scale) / 2;
  351 + $posy = ($height - $h * $scale) / 2;
  352 + do {
  353 + //创建新图像
  354 + $img = imagecreatetruecolor($width, $height);
  355 + // 调整默认颜色
  356 + $color = imagecolorallocate($img, 255, 255, 255);
  357 + imagefill($img, 0, 0, $color);
  358 + //裁剪
  359 + imagecopyresampled($img, $this->im, $posx, $posy, $x, $y, $neww, $newh, $w, $h);
  360 + imagedestroy($this->im); //销毁原图
  361 + $this->im = $img;
  362 + } while (!empty($this->gif) && $this->gifNext());
  363 + $this->info['width'] = (int) $width;
  364 + $this->info['height'] = (int) $height;
  365 + return $this;
  366 + /* 固定 */
  367 + case self::THUMB_FIXED:
  368 + $x = $y = 0;
  369 + break;
  370 + default:
  371 + throw new ImageException('不支持的缩略图裁剪类型');
  372 + }
  373 + /* 裁剪图像 */
  374 + return $this->crop($w, $h, $x, $y, $width, $height);
  375 + }
  376 +
  377 + /**
  378 + * 添加水印
  379 + *
  380 + * @param string $source 水印图片路径
  381 + * @param int $locate 水印位置
  382 + * @param int $alpha 透明度
  383 + * @return $this
  384 + */
  385 + public function water($source, $locate = self::WATER_SOUTHEAST, $alpha = 100)
  386 + {
  387 + if (!is_file($source)) {
  388 + throw new ImageException('水印图像不存在');
  389 + }
  390 + //获取水印图像信息
  391 + $info = getimagesize($source);
  392 + if (false === $info || (IMAGETYPE_GIF === $info[2] && empty($info['bits']))) {
  393 + throw new ImageException('非法水印文件');
  394 + }
  395 + //创建水印图像资源
  396 + $fun = 'imagecreatefrom' . image_type_to_extension($info[2], false);
  397 + $water = $fun($source);
  398 + //设定水印图像的混色模式
  399 + imagealphablending($water, true);
  400 + /* 设定水印位置 */
  401 + switch ($locate) {
  402 + /* 右下角水印 */
  403 + case self::WATER_SOUTHEAST:
  404 + $x = $this->info['width'] - $info[0];
  405 + $y = $this->info['height'] - $info[1];
  406 + break;
  407 + /* 左下角水印 */
  408 + case self::WATER_SOUTHWEST:
  409 + $x = 0;
  410 + $y = $this->info['height'] - $info[1];
  411 + break;
  412 + /* 左上角水印 */
  413 + case self::WATER_NORTHWEST:
  414 + $x = $y = 0;
  415 + break;
  416 + /* 右上角水印 */
  417 + case self::WATER_NORTHEAST:
  418 + $x = $this->info['width'] - $info[0];
  419 + $y = 0;
  420 + break;
  421 + /* 居中水印 */
  422 + case self::WATER_CENTER:
  423 + $x = ($this->info['width'] - $info[0]) / 2;
  424 + $y = ($this->info['height'] - $info[1]) / 2;
  425 + break;
  426 + /* 下居中水印 */
  427 + case self::WATER_SOUTH:
  428 + $x = ($this->info['width'] - $info[0]) / 2;
  429 + $y = $this->info['height'] - $info[1];
  430 + break;
  431 + /* 右居中水印 */
  432 + case self::WATER_EAST:
  433 + $x = $this->info['width'] - $info[0];
  434 + $y = ($this->info['height'] - $info[1]) / 2;
  435 + break;
  436 + /* 上居中水印 */
  437 + case self::WATER_NORTH:
  438 + $x = ($this->info['width'] - $info[0]) / 2;
  439 + $y = 0;
  440 + break;
  441 + /* 左居中水印 */
  442 + case self::WATER_WEST:
  443 + $x = 0;
  444 + $y = ($this->info['height'] - $info[1]) / 2;
  445 + break;
  446 + default:
  447 + /* 自定义水印坐标 */
  448 + if (is_array($locate)) {
  449 + list($x, $y) = $locate;
  450 + } else {
  451 + throw new ImageException('不支持的水印位置类型');
  452 + }
  453 + }
  454 + do {
  455 + //添加水印
  456 + $src = imagecreatetruecolor($info[0], $info[1]);
  457 + // 调整默认颜色
  458 + $color = imagecolorallocate($src, 255, 255, 255);
  459 + imagefill($src, 0, 0, $color);
  460 + imagecopy($src, $this->im, 0, 0, $x, $y, $info[0], $info[1]);
  461 + imagecopy($src, $water, 0, 0, 0, 0, $info[0], $info[1]);
  462 + imagecopymerge($this->im, $src, $x, $y, 0, 0, $info[0], $info[1], $alpha);
  463 + //销毁零时图片资源
  464 + imagedestroy($src);
  465 + } while (!empty($this->gif) && $this->gifNext());
  466 + //销毁水印资源
  467 + imagedestroy($water);
  468 + return $this;
  469 + }
  470 +
  471 + /**
  472 + * 图像添加文字
  473 + *
  474 + * @param string $text 添加的文字
  475 + * @param string $font 字体路径
  476 + * @param integer $size 字号
  477 + * @param string $color 文字颜色
  478 + * @param int $locate 文字写入位置
  479 + * @param integer $offset 文字相对当前位置的偏移量
  480 + * @param integer $angle 文字倾斜角度
  481 + *
  482 + * @return $this
  483 + * @throws ImageException
  484 + */
  485 + public function text($text, $font, $size, $color = '#00000000',
  486 + $locate = self::WATER_SOUTHEAST, $offset = 0, $angle = 0) {
  487 +
  488 + if (!is_file($font)) {
  489 + throw new ImageException("不存在的字体文件:{$font}");
  490 + }
  491 + //获取文字信息
  492 + $info = imagettfbbox($size, $angle, $font, $text);
  493 + $minx = min($info[0], $info[2], $info[4], $info[6]);
  494 + $maxx = max($info[0], $info[2], $info[4], $info[6]);
  495 + $miny = min($info[1], $info[3], $info[5], $info[7]);
  496 + $maxy = max($info[1], $info[3], $info[5], $info[7]);
  497 + /* 计算文字初始坐标和尺寸 */
  498 + $x = $minx;
  499 + $y = abs($miny);
  500 + $w = $maxx - $minx;
  501 + $h = $maxy - $miny;
  502 + /* 设定文字位置 */
  503 + switch ($locate) {
  504 + /* 右下角文字 */
  505 + case self::WATER_SOUTHEAST:
  506 + $x += $this->info['width'] - $w;
  507 + $y += $this->info['height'] - $h;
  508 + break;
  509 + /* 左下角文字 */
  510 + case self::WATER_SOUTHWEST:
  511 + $y += $this->info['height'] - $h;
  512 + break;
  513 + /* 左上角文字 */
  514 + case self::WATER_NORTHWEST:
  515 + // 起始坐标即为左上角坐标,无需调整
  516 + break;
  517 + /* 右上角文字 */
  518 + case self::WATER_NORTHEAST:
  519 + $x += $this->info['width'] - $w;
  520 + break;
  521 + /* 居中文字 */
  522 + case self::WATER_CENTER:
  523 + $x += ($this->info['width'] - $w) / 2;
  524 + $y += ($this->info['height'] - $h) / 2;
  525 + break;
  526 + /* 下居中文字 */
  527 + case self::WATER_SOUTH:
  528 + $x += ($this->info['width'] - $w) / 2;
  529 + $y += $this->info['height'] - $h;
  530 + break;
  531 + /* 右居中文字 */
  532 + case self::WATER_EAST:
  533 + $x += $this->info['width'] - $w;
  534 + $y += ($this->info['height'] - $h) / 2;
  535 + break;
  536 + /* 上居中文字 */
  537 + case self::WATER_NORTH:
  538 + $x += ($this->info['width'] - $w) / 2;
  539 + break;
  540 + /* 左居中文字 */
  541 + case self::WATER_WEST:
  542 + $y += ($this->info['height'] - $h) / 2;
  543 + break;
  544 + default:
  545 + /* 自定义文字坐标 */
  546 + if (is_array($locate)) {
  547 + list($posx, $posy) = $locate;
  548 + $x += $posx;
  549 + $y += $posy;
  550 + } else {
  551 + throw new ImageException('不支持的文字位置类型');
  552 + }
  553 + }
  554 + /* 设置偏移量 */
  555 + if (is_array($offset)) {
  556 + $offset = array_map('intval', $offset);
  557 + list($ox, $oy) = $offset;
  558 + } else {
  559 + $offset = intval($offset);
  560 + $ox = $oy = $offset;
  561 + }
  562 + /* 设置颜色 */
  563 + if (is_string($color) && 0 === strpos($color, '#')) {
  564 + $color = str_split(substr($color, 1), 2);
  565 + $color = array_map('hexdec', $color);
  566 + if (empty($color[3]) || $color[3] > 127) {
  567 + $color[3] = 0;
  568 + }
  569 + } elseif (!is_array($color)) {
  570 + throw new ImageException('错误的颜色值');
  571 + }
  572 + do {
  573 + /* 写入文字 */
  574 + $col = imagecolorallocatealpha($this->im, $color[0], $color[1], $color[2], $color[3]);
  575 + imagettftext($this->im, $size, $angle, $x + $ox, $y + $oy, $col, $font, $text);
  576 + } while (!empty($this->gif) && $this->gifNext());
  577 + return $this;
  578 + }
  579 +
  580 + /**
  581 + * 切换到GIF的下一帧并保存当前帧
  582 + */
  583 + protected function gifNext()
  584 + {
  585 + ob_start();
  586 + ob_implicit_flush(0);
  587 + imagegif($this->im);
  588 + $img = ob_get_clean();
  589 + $this->gif->image($img);
  590 + $next = $this->gif->nextImage();
  591 + if ($next) {
  592 + imagedestroy($this->im);
  593 + $this->im = imagecreatefromstring($next);
  594 + return $next;
  595 + } else {
  596 + imagedestroy($this->im);
  597 + $this->im = imagecreatefromstring($this->gif->image());
  598 + return false;
  599 + }
  600 + }
  601 +
  602 + /**
  603 + * 析构方法,用于销毁图像资源
  604 + */
  605 + public function __destruct()
  606 + {
  607 + empty($this->im) || imagedestroy($this->im);
  608 + }
  609 +
  610 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think\image;
  13 +
  14 +
  15 +class Exception extends \RuntimeException
  16 +{
  17 +
  18 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think\image\gif;
  13 +
  14 +
  15 +class Decoder
  16 +{
  17 + public $GIF_buffer = [];
  18 + public $GIF_arrays = [];
  19 + public $GIF_delays = [];
  20 + public $GIF_stream = "";
  21 + public $GIF_string = "";
  22 + public $GIF_bfseek = 0;
  23 + public $GIF_screen = [];
  24 + public $GIF_global = [];
  25 + public $GIF_sorted;
  26 + public $GIF_colorS;
  27 + public $GIF_colorC;
  28 + public $GIF_colorF;
  29 +
  30 + /*
  31 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  32 + ::
  33 + :: GIFDecoder ( $GIF_pointer )
  34 + ::
  35 + */
  36 + public function __construct($GIF_pointer)
  37 + {
  38 + $this->GIF_stream = $GIF_pointer;
  39 + $this->getByte(6); // GIF89a
  40 + $this->getByte(7); // Logical Screen Descriptor
  41 + $this->GIF_screen = $this->GIF_buffer;
  42 + $this->GIF_colorF = $this->GIF_buffer[4] & 0x80 ? 1 : 0;
  43 + $this->GIF_sorted = $this->GIF_buffer[4] & 0x08 ? 1 : 0;
  44 + $this->GIF_colorC = $this->GIF_buffer[4] & 0x07;
  45 + $this->GIF_colorS = 2 << $this->GIF_colorC;
  46 + if (1 == $this->GIF_colorF) {
  47 + $this->getByte(3 * $this->GIF_colorS);
  48 + $this->GIF_global = $this->GIF_buffer;
  49 + }
  50 +
  51 + for ($cycle = 1; $cycle;) {
  52 + if ($this->getByte(1)) {
  53 + switch ($this->GIF_buffer[0]) {
  54 + case 0x21:
  55 + $this->readExtensions();
  56 + break;
  57 + case 0x2C:
  58 + $this->readDescriptor();
  59 + break;
  60 + case 0x3B:
  61 + $cycle = 0;
  62 + break;
  63 + }
  64 + } else {
  65 + $cycle = 0;
  66 + }
  67 + }
  68 + }
  69 +
  70 + /*
  71 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  72 + ::
  73 + :: GIFReadExtension ( )
  74 + ::
  75 + */
  76 + public function readExtensions()
  77 + {
  78 + $this->getByte(1);
  79 + for (; ;) {
  80 + $this->getByte(1);
  81 + if (($u = $this->GIF_buffer[0]) == 0x00) {
  82 + break;
  83 + }
  84 + $this->getByte($u);
  85 + /*
  86 + * 07.05.2007.
  87 + * Implemented a new line for a new function
  88 + * to determine the originaly delays between
  89 + * frames.
  90 + *
  91 + */
  92 + if (4 == $u) {
  93 + $this->GIF_delays[] = ($this->GIF_buffer[1] | $this->GIF_buffer[2] << 8);
  94 + }
  95 + }
  96 + }
  97 +
  98 + /*
  99 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  100 + ::
  101 + :: GIFReadExtension ( )
  102 + ::
  103 + */
  104 + public function readDescriptor()
  105 + {
  106 + $this->getByte(9);
  107 + $GIF_screen = $this->GIF_buffer;
  108 + $GIF_colorF = $this->GIF_buffer[8] & 0x80 ? 1 : 0;
  109 + if ($GIF_colorF) {
  110 + $GIF_code = $this->GIF_buffer[8] & 0x07;
  111 + $GIF_sort = $this->GIF_buffer[8] & 0x20 ? 1 : 0;
  112 + } else {
  113 + $GIF_code = $this->GIF_colorC;
  114 + $GIF_sort = $this->GIF_sorted;
  115 + }
  116 + $GIF_size = 2 << $GIF_code;
  117 + $this->GIF_screen[4] &= 0x70;
  118 + $this->GIF_screen[4] |= 0x80;
  119 + $this->GIF_screen[4] |= $GIF_code;
  120 + if ($GIF_sort) {
  121 + $this->GIF_screen[4] |= 0x08;
  122 + }
  123 + $this->GIF_string = "GIF87a";
  124 + $this->putByte($this->GIF_screen);
  125 + if (1 == $GIF_colorF) {
  126 + $this->getByte(3 * $GIF_size);
  127 + $this->putByte($this->GIF_buffer);
  128 + } else {
  129 + $this->putByte($this->GIF_global);
  130 + }
  131 + $this->GIF_string .= chr(0x2C);
  132 + $GIF_screen[8] &= 0x40;
  133 + $this->putByte($GIF_screen);
  134 + $this->getByte(1);
  135 + $this->putByte($this->GIF_buffer);
  136 + for (; ;) {
  137 + $this->getByte(1);
  138 + $this->putByte($this->GIF_buffer);
  139 + if (($u = $this->GIF_buffer[0]) == 0x00) {
  140 + break;
  141 + }
  142 + $this->getByte($u);
  143 + $this->putByte($this->GIF_buffer);
  144 + }
  145 + $this->GIF_string .= chr(0x3B);
  146 + /*
  147 + Add frames into $GIF_stream array...
  148 + */
  149 + $this->GIF_arrays[] = $this->GIF_string;
  150 + }
  151 +
  152 + /*
  153 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  154 + ::
  155 + :: GIFGetByte ( $len )
  156 + ::
  157 + */
  158 + public function getByte($len)
  159 + {
  160 + $this->GIF_buffer = [];
  161 + for ($i = 0; $i < $len; $i++) {
  162 + if ($this->GIF_bfseek > strlen($this->GIF_stream)) {
  163 + return 0;
  164 + }
  165 + $this->GIF_buffer[] = ord($this->GIF_stream{$this->GIF_bfseek++});
  166 + }
  167 + return 1;
  168 + }
  169 +
  170 + /*
  171 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  172 + ::
  173 + :: GIFPutByte ( $bytes )
  174 + ::
  175 + */
  176 + public function putByte($bytes)
  177 + {
  178 + for ($i = 0; $i < count($bytes); $i++) {
  179 + $this->GIF_string .= chr($bytes[$i]);
  180 + }
  181 + }
  182 +
  183 + /*
  184 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  185 + ::
  186 + :: PUBLIC FUNCTIONS
  187 + ::
  188 + ::
  189 + :: GIFGetFrames ( )
  190 + ::
  191 + */
  192 + public function getFrames()
  193 + {
  194 + return ($this->GIF_arrays);
  195 + }
  196 +
  197 + /*
  198 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  199 + ::
  200 + :: GIFGetDelays ( )
  201 + ::
  202 + */
  203 + public function getDelays()
  204 + {
  205 + return ($this->GIF_delays);
  206 + }
  207 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace think\image\gif;
  12 +
  13 +class Encoder
  14 +{
  15 + public $GIF = "GIF89a"; /* GIF header 6 bytes */
  16 + public $VER = "GIFEncoder V2.05"; /* Encoder version */
  17 + public $BUF = [];
  18 + public $LOP = 0;
  19 + public $DIS = 2;
  20 + public $COL = -1;
  21 + public $IMG = -1;
  22 + public $ERR = [
  23 + 'ERR00' => "Does not supported function for only one image!",
  24 + 'ERR01' => "Source is not a GIF image!",
  25 + 'ERR02' => "Unintelligible flag ",
  26 + 'ERR03' => "Does not make animation from animated GIF source",
  27 + ];
  28 +
  29 + /*
  30 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  31 + ::
  32 + :: GIFEncoder...
  33 + ::
  34 + */
  35 + public function __construct(
  36 + $GIF_src, $GIF_dly, $GIF_lop, $GIF_dis,
  37 + $GIF_red, $GIF_grn, $GIF_blu, $GIF_mod
  38 + )
  39 + {
  40 + if (!is_array($GIF_src)) {
  41 + printf("%s: %s", $this->VER, $this->ERR['ERR00']);
  42 + exit(0);
  43 + }
  44 + $this->LOP = ($GIF_lop > -1) ? $GIF_lop : 0;
  45 + $this->DIS = ($GIF_dis > -1) ? (($GIF_dis < 3) ? $GIF_dis : 3) : 2;
  46 + $this->COL = ($GIF_red > -1 && $GIF_grn > -1 && $GIF_blu > -1) ?
  47 + ($GIF_red | ($GIF_grn << 8) | ($GIF_blu << 16)) : -1;
  48 + for ($i = 0; $i < count($GIF_src); $i++) {
  49 + if (strtolower($GIF_mod) == "url") {
  50 + $this->BUF[] = fread(fopen($GIF_src[$i], "rb"), filesize($GIF_src[$i]));
  51 + } else if (strtolower($GIF_mod) == "bin") {
  52 + $this->BUF[] = $GIF_src[$i];
  53 + } else {
  54 + printf("%s: %s ( %s )!", $this->VER, $this->ERR['ERR02'], $GIF_mod);
  55 + exit(0);
  56 + }
  57 + if (substr($this->BUF[$i], 0, 6) != "GIF87a" && substr($this->BUF[$i], 0, 6) != "GIF89a") {
  58 + printf("%s: %d %s", $this->VER, $i, $this->ERR['ERR01']);
  59 + exit(0);
  60 + }
  61 + for ($j = (13 + 3 * (2 << (ord($this->BUF[$i]{10}) & 0x07))), $k = true; $k; $j++) {
  62 + switch ($this->BUF[$i]{$j}) {
  63 + case "!":
  64 + if ((substr($this->BUF[$i], ($j + 3), 8)) == "NETSCAPE") {
  65 + printf("%s: %s ( %s source )!", $this->VER, $this->ERR['ERR03'], ($i + 1));
  66 + exit(0);
  67 + }
  68 + break;
  69 + case ";":
  70 + $k = false;
  71 + break;
  72 + }
  73 + }
  74 + }
  75 + $this->addHeader();
  76 + for ($i = 0; $i < count($this->BUF); $i++) {
  77 + $this->addFrames($i, $GIF_dly[$i]);
  78 + }
  79 + $this->addFooter();
  80 + }
  81 +
  82 + /*
  83 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  84 + ::
  85 + :: GIFAddHeader...
  86 + ::
  87 + */
  88 + public function addHeader()
  89 + {
  90 + if (ord($this->BUF[0]{10}) & 0x80) {
  91 + $cmap = 3 * (2 << (ord($this->BUF[0]{10}) & 0x07));
  92 + $this->GIF .= substr($this->BUF[0], 6, 7);
  93 + $this->GIF .= substr($this->BUF[0], 13, $cmap);
  94 + $this->GIF .= "!\377\13NETSCAPE2.0\3\1" . $this->word($this->LOP) . "\0";
  95 + }
  96 + }
  97 +
  98 + /*
  99 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  100 + ::
  101 + :: GIFAddFrames...
  102 + ::
  103 + */
  104 + public function addFrames($i, $d)
  105 + {
  106 + $Locals_img = '';
  107 + $Locals_str = 13 + 3 * (2 << (ord($this->BUF[$i]{10}) & 0x07));
  108 + $Locals_end = strlen($this->BUF[$i]) - $Locals_str - 1;
  109 + $Locals_tmp = substr($this->BUF[$i], $Locals_str, $Locals_end);
  110 + $Global_len = 2 << (ord($this->BUF[0]{10}) & 0x07);
  111 + $Locals_len = 2 << (ord($this->BUF[$i]{10}) & 0x07);
  112 + $Global_rgb = substr($this->BUF[0], 13,
  113 + 3 * (2 << (ord($this->BUF[0]{10}) & 0x07)));
  114 + $Locals_rgb = substr($this->BUF[$i], 13,
  115 + 3 * (2 << (ord($this->BUF[$i]{10}) & 0x07)));
  116 + $Locals_ext = "!\xF9\x04" . chr(($this->DIS << 2) + 0) .
  117 + chr(($d >> 0) & 0xFF) . chr(($d >> 8) & 0xFF) . "\x0\x0";
  118 + if ($this->COL > -1 && ord($this->BUF[$i]{10}) & 0x80) {
  119 + for ($j = 0; $j < (2 << (ord($this->BUF[$i]{10}) & 0x07)); $j++) {
  120 + if (
  121 + ord($Locals_rgb{3 * $j + 0}) == (($this->COL >> 16) & 0xFF) &&
  122 + ord($Locals_rgb{3 * $j + 1}) == (($this->COL >> 8) & 0xFF) &&
  123 + ord($Locals_rgb{3 * $j + 2}) == (($this->COL >> 0) & 0xFF)
  124 + ) {
  125 + $Locals_ext = "!\xF9\x04" . chr(($this->DIS << 2) + 1) .
  126 + chr(($d >> 0) & 0xFF) . chr(($d >> 8) & 0xFF) . chr($j) . "\x0";
  127 + break;
  128 + }
  129 + }
  130 + }
  131 + switch ($Locals_tmp{0}) {
  132 + case "!":
  133 + /**
  134 + * @var string $Locals_img ;
  135 + */
  136 + $Locals_img = substr($Locals_tmp, 8, 10);
  137 + $Locals_tmp = substr($Locals_tmp, 18, strlen($Locals_tmp) - 18);
  138 + break;
  139 + case ",":
  140 + $Locals_img = substr($Locals_tmp, 0, 10);
  141 + $Locals_tmp = substr($Locals_tmp, 10, strlen($Locals_tmp) - 10);
  142 + break;
  143 + }
  144 + if (ord($this->BUF[$i]{10}) & 0x80 && $this->IMG > -1) {
  145 + if ($Global_len == $Locals_len) {
  146 + if ($this->blockCompare($Global_rgb, $Locals_rgb, $Global_len)) {
  147 + $this->GIF .= ($Locals_ext . $Locals_img . $Locals_tmp);
  148 + } else {
  149 + $byte = ord($Locals_img{9});
  150 + $byte |= 0x80;
  151 + $byte &= 0xF8;
  152 + $byte |= (ord($this->BUF[0]{10}) & 0x07);
  153 + $Locals_img{9} = chr($byte);
  154 + $this->GIF .= ($Locals_ext . $Locals_img . $Locals_rgb . $Locals_tmp);
  155 + }
  156 + } else {
  157 + $byte = ord($Locals_img{9});
  158 + $byte |= 0x80;
  159 + $byte &= 0xF8;
  160 + $byte |= (ord($this->BUF[$i]{10}) & 0x07);
  161 + $Locals_img{9} = chr($byte);
  162 + $this->GIF .= ($Locals_ext . $Locals_img . $Locals_rgb . $Locals_tmp);
  163 + }
  164 + } else {
  165 + $this->GIF .= ($Locals_ext . $Locals_img . $Locals_tmp);
  166 + }
  167 + $this->IMG = 1;
  168 + }
  169 +
  170 + /*
  171 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  172 + ::
  173 + :: GIFAddFooter...
  174 + ::
  175 + */
  176 + public function addFooter()
  177 + {
  178 + $this->GIF .= ";";
  179 + }
  180 +
  181 + /*
  182 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  183 + ::
  184 + :: GIFBlockCompare...
  185 + ::
  186 + */
  187 + public function blockCompare($GlobalBlock, $LocalBlock, $Len)
  188 + {
  189 + for ($i = 0; $i < $Len; $i++) {
  190 + if (
  191 + $GlobalBlock{3 * $i + 0} != $LocalBlock{3 * $i + 0} ||
  192 + $GlobalBlock{3 * $i + 1} != $LocalBlock{3 * $i + 1} ||
  193 + $GlobalBlock{3 * $i + 2} != $LocalBlock{3 * $i + 2}
  194 + ) {
  195 + return (0);
  196 + }
  197 + }
  198 + return (1);
  199 + }
  200 +
  201 + /*
  202 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  203 + ::
  204 + :: GIFWord...
  205 + ::
  206 + */
  207 + public function word($int)
  208 + {
  209 + return (chr($int & 0xFF) . chr(($int >> 8) & 0xFF));
  210 + }
  211 +
  212 + /*
  213 + :::::::::::::::::::::::::::::::::::::::::::::::::::
  214 + ::
  215 + :: GetAnimation...
  216 + ::
  217 + */
  218 + public function getAnimation()
  219 + {
  220 + return ($this->GIF);
  221 + }
  222 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace think\image\gif;
  13 +
  14 +class Gif
  15 +{
  16 + /**
  17 + * GIF帧列表
  18 + *
  19 + * @var array
  20 + */
  21 + private $frames = [];
  22 + /**
  23 + * 每帧等待时间列表
  24 + *
  25 + * @var array
  26 + */
  27 + private $delays = [];
  28 +
  29 + /**
  30 + * 构造方法,用于解码GIF图片
  31 + *
  32 + * @param string $src GIF图片数据
  33 + * @param string $mod 图片数据类型
  34 + * @throws \Exception
  35 + */
  36 + public function __construct($src = null, $mod = 'url')
  37 + {
  38 + if (!is_null($src)) {
  39 + if ('url' == $mod && is_file($src)) {
  40 + $src = file_get_contents($src);
  41 + }
  42 + /* 解码GIF图片 */
  43 + try {
  44 + $de = new Decoder($src);
  45 + $this->frames = $de->getFrames();
  46 + $this->delays = $de->getDelays();
  47 + } catch (\Exception $e) {
  48 + throw new \Exception("解码GIF图片出错");
  49 + }
  50 + }
  51 + }
  52 +
  53 + /**
  54 + * 设置或获取当前帧的数据
  55 + *
  56 + * @param string $stream 二进制数据流
  57 + * @return mixed 获取到的数据
  58 + */
  59 + public function image($stream = null)
  60 + {
  61 + if (is_null($stream)) {
  62 + $current = current($this->frames);
  63 + return false === $current ? reset($this->frames) : $current;
  64 + }
  65 + $this->frames[key($this->frames)] = $stream;
  66 + }
  67 +
  68 + /**
  69 + * 将当前帧移动到下一帧
  70 + *
  71 + * @return string 当前帧数据
  72 + */
  73 + public function nextImage()
  74 + {
  75 + return next($this->frames);
  76 + }
  77 +
  78 + /**
  79 + * 编码并保存当前GIF图片
  80 + *
  81 + * @param string $pathname 图片名称
  82 + */
  83 + public function save($pathname)
  84 + {
  85 + $gif = new Encoder($this->frames, $this->delays, 0, 2, 0, 0, 0, 'bin');
  86 + file_put_contents($pathname, $gif->getAnimation());
  87 + }
  88 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace tests;
  12 +
  13 +use think\Image;
  14 +
  15 +class CropTest extends TestCase
  16 +{
  17 + public function testJpeg()
  18 + {
  19 + $pathname = TEST_PATH . 'tmp/crop.jpg';
  20 + $image = Image::open($this->getJpeg());
  21 +
  22 + $image->crop(200, 200, 100, 100, 300, 300)->save($pathname);
  23 +
  24 + $this->assertEquals(300, $image->width());
  25 + $this->assertEquals(300, $image->height());
  26 +
  27 + $file = new \SplFileInfo($pathname);
  28 +
  29 + $this->assertTrue($file->isFile());
  30 +
  31 + @unlink($pathname);
  32 + }
  33 +
  34 + public function testPng()
  35 + {
  36 + $pathname = TEST_PATH . 'tmp/crop.png';
  37 + $image = Image::open($this->getPng());
  38 +
  39 + $image->crop(200, 200, 100, 100, 300, 300)->save($pathname);
  40 +
  41 + $this->assertEquals(300, $image->width());
  42 + $this->assertEquals(300, $image->height());
  43 +
  44 + $file = new \SplFileInfo($pathname);
  45 +
  46 + $this->assertTrue($file->isFile());
  47 +
  48 + @unlink($pathname);
  49 + }
  50 +
  51 + public function testGif()
  52 + {
  53 + $pathname = TEST_PATH . 'tmp/crop.gif';
  54 + $image = Image::open($this->getGif());
  55 +
  56 + $image->crop(200, 200, 100, 100, 300, 300)->save($pathname);
  57 +
  58 + $this->assertEquals(300, $image->width());
  59 + $this->assertEquals(300, $image->height());
  60 +
  61 + $file = new \SplFileInfo($pathname);
  62 +
  63 + $this->assertTrue($file->isFile());
  64 +
  65 + @unlink($pathname);
  66 + }
  67 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace tests;
  12 +
  13 +use think\Image;
  14 +
  15 +class FlipTest extends TestCase
  16 +{
  17 + public function testJpeg()
  18 + {
  19 + $pathname = TEST_PATH . 'tmp/flip.jpg';
  20 + $image = Image::open($this->getJpeg());
  21 + $image->flip()->save($pathname);
  22 +
  23 + $file = new \SplFileInfo($pathname);
  24 +
  25 + $this->assertTrue($file->isFile());
  26 +
  27 + @unlink($pathname);
  28 + }
  29 +
  30 +
  31 + public function testGif()
  32 + {
  33 + $pathname = TEST_PATH . 'tmp/flip.gif';
  34 + $image = Image::open($this->getGif());
  35 + $image->flip(Image::FLIP_Y)->save($pathname);
  36 +
  37 + $file = new \SplFileInfo($pathname);
  38 +
  39 + $this->assertTrue($file->isFile());
  40 +
  41 + @unlink($pathname);
  42 + }
  43 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace tests;
  12 +
  13 +use think\Image;
  14 +
  15 +class InfoTest extends TestCase
  16 +{
  17 +
  18 + public function testOpen()
  19 + {
  20 + $this->setExpectedException("\\think\\image\\Exception");
  21 + Image::open('');
  22 + }
  23 +
  24 + public function testIllegal()
  25 + {
  26 + $this->setExpectedException("\\think\\image\\Exception", 'Illegal image file');
  27 + Image::open(TEST_PATH . 'images/test.bmp');
  28 + }
  29 +
  30 + public function testJpeg()
  31 + {
  32 + $image = Image::open($this->getJpeg());
  33 + $this->assertEquals(800, $image->width());
  34 + $this->assertEquals(600, $image->height());
  35 + $this->assertEquals('jpeg', $image->type());
  36 + $this->assertEquals('image/jpeg', $image->mime());
  37 + $this->assertEquals([800, 600], $image->size());
  38 + }
  39 +
  40 +
  41 + public function testPng()
  42 + {
  43 + $image = Image::open($this->getPng());
  44 + $this->assertEquals(800, $image->width());
  45 + $this->assertEquals(600, $image->height());
  46 + $this->assertEquals('png', $image->type());
  47 + $this->assertEquals('image/png', $image->mime());
  48 + $this->assertEquals([800, 600], $image->size());
  49 + }
  50 +
  51 + public function testGif()
  52 + {
  53 + $image = Image::open($this->getGif());
  54 + $this->assertEquals(380, $image->width());
  55 + $this->assertEquals(216, $image->height());
  56 + $this->assertEquals('gif', $image->type());
  57 + $this->assertEquals('image/gif', $image->mime());
  58 + $this->assertEquals([380, 216], $image->size());
  59 + }
  60 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace tests;
  12 +
  13 +use think\Image;
  14 +
  15 +class RotateTest extends TestCase
  16 +{
  17 + public function testJpeg()
  18 + {
  19 + $pathname = TEST_PATH . 'tmp/rotate.jpg';
  20 + $image = Image::open($this->getJpeg());
  21 + $image->rotate(90)->save($pathname);
  22 +
  23 + $file = new \SplFileInfo($pathname);
  24 +
  25 + $this->assertTrue($file->isFile());
  26 +
  27 + @unlink($pathname);
  28 + }
  29 +
  30 + public function testGif()
  31 + {
  32 + $pathname = TEST_PATH . 'tmp/rotate.gif';
  33 + $image = Image::open($this->getGif());
  34 + $image->rotate(90)->save($pathname);
  35 +
  36 + $file = new \SplFileInfo($pathname);
  37 +
  38 + $this->assertTrue($file->isFile());
  39 +
  40 + @unlink($pathname);
  41 + }
  42 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +
  12 +namespace tests;
  13 +
  14 +use think\File;
  15 +
  16 +abstract class TestCase extends \PHPUnit_Framework_TestCase
  17 +{
  18 +
  19 + protected function getJpeg()
  20 + {
  21 + return new File(TEST_PATH . 'images/test.jpg');
  22 + }
  23 +
  24 + protected function getPng()
  25 + {
  26 + return new File(TEST_PATH . 'images/test.png');
  27 + }
  28 +
  29 + protected function getGif()
  30 + {
  31 + return new File(TEST_PATH . 'images/test.gif');
  32 + }
  33 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace tests;
  12 +
  13 +use think\Image;
  14 +
  15 +class TextTest extends TestCase
  16 +{
  17 + public function testJpeg()
  18 + {
  19 + $pathname = TEST_PATH . 'tmp/text.jpg';
  20 + $image = Image::open($this->getJpeg());
  21 +
  22 + $image->text('test', TEST_PATH . 'images/test.ttf', 12)->save($pathname);
  23 +
  24 + $file = new \SplFileInfo($pathname);
  25 +
  26 + $this->assertTrue($file->isFile());
  27 +
  28 + @unlink($pathname);
  29 + }
  30 +
  31 + public function testPng()
  32 + {
  33 + $pathname = TEST_PATH . 'tmp/text.png';
  34 + $image = Image::open($this->getPng());
  35 +
  36 + $image->text('test', TEST_PATH . 'images/test.ttf', 12)->save($pathname);
  37 +
  38 + $file = new \SplFileInfo($pathname);
  39 +
  40 + $this->assertTrue($file->isFile());
  41 +
  42 + @unlink($pathname);
  43 + }
  44 +
  45 + public function testGif()
  46 + {
  47 + $pathname = TEST_PATH . 'tmp/text.gif';
  48 + $image = Image::open($this->getGif());
  49 +
  50 + $image->text('test', TEST_PATH . 'images/test.ttf', 12)->save($pathname);
  51 +
  52 + $file = new \SplFileInfo($pathname);
  53 +
  54 + $this->assertTrue($file->isFile());
  55 +
  56 + @unlink($pathname);
  57 + }
  58 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace tests;
  12 +
  13 +use think\Image;
  14 +
  15 +class ThumbTest extends TestCase
  16 +{
  17 + public function testJpeg()
  18 + {
  19 + $pathname = TEST_PATH . 'tmp/thumb.jpg';
  20 +
  21 + //1
  22 + $image = Image::open($this->getJpeg());
  23 +
  24 + $image->thumb(200, 200, Image::THUMB_CENTER)->save($pathname);
  25 +
  26 + $this->assertEquals(200, $image->width());
  27 + $this->assertEquals(200, $image->height());
  28 +
  29 + $file = new \SplFileInfo($pathname);
  30 +
  31 + $this->assertTrue($file->isFile());
  32 +
  33 + @unlink($pathname);
  34 +
  35 + //2
  36 + $image = Image::open($this->getJpeg());
  37 +
  38 + $image->thumb(200, 200, Image::THUMB_SCALING)->save($pathname);
  39 +
  40 + $this->assertEquals(200, $image->width());
  41 + $this->assertEquals(150, $image->height());
  42 +
  43 + $file = new \SplFileInfo($pathname);
  44 +
  45 + $this->assertTrue($file->isFile());
  46 +
  47 + @unlink($pathname);
  48 +
  49 + //3
  50 + $image = Image::open($this->getJpeg());
  51 +
  52 + $image->thumb(200, 200, Image::THUMB_FILLED)->save($pathname);
  53 +
  54 + $this->assertEquals(200, $image->width());
  55 + $this->assertEquals(200, $image->height());
  56 +
  57 + $file = new \SplFileInfo($pathname);
  58 +
  59 + $this->assertTrue($file->isFile());
  60 +
  61 + @unlink($pathname);
  62 +
  63 + //4
  64 + $image = Image::open($this->getJpeg());
  65 +
  66 + $image->thumb(200, 200, Image::THUMB_NORTHWEST)->save($pathname);
  67 +
  68 + $this->assertEquals(200, $image->width());
  69 + $this->assertEquals(200, $image->height());
  70 +
  71 + $file = new \SplFileInfo($pathname);
  72 +
  73 + $this->assertTrue($file->isFile());
  74 +
  75 + @unlink($pathname);
  76 +
  77 + //5
  78 + $image = Image::open($this->getJpeg());
  79 +
  80 + $image->thumb(200, 200, Image::THUMB_SOUTHEAST)->save($pathname);
  81 +
  82 + $this->assertEquals(200, $image->width());
  83 + $this->assertEquals(200, $image->height());
  84 +
  85 + $file = new \SplFileInfo($pathname);
  86 +
  87 + $this->assertTrue($file->isFile());
  88 +
  89 + @unlink($pathname);
  90 +
  91 + //6
  92 + $image = Image::open($this->getJpeg());
  93 +
  94 + $image->thumb(200, 200, Image::THUMB_FIXED)->save($pathname);
  95 +
  96 + $this->assertEquals(200, $image->width());
  97 + $this->assertEquals(200, $image->height());
  98 +
  99 + $file = new \SplFileInfo($pathname);
  100 +
  101 + $this->assertTrue($file->isFile());
  102 +
  103 + @unlink($pathname);
  104 + }
  105 +
  106 +
  107 + public function testPng()
  108 + {
  109 + $pathname = TEST_PATH . 'tmp/thumb.png';
  110 +
  111 + //1
  112 + $image = Image::open($this->getPng());
  113 +
  114 + $image->thumb(200, 200, Image::THUMB_CENTER)->save($pathname);
  115 +
  116 + $this->assertEquals(200, $image->width());
  117 + $this->assertEquals(200, $image->height());
  118 +
  119 + $file = new \SplFileInfo($pathname);
  120 +
  121 + $this->assertTrue($file->isFile());
  122 +
  123 + @unlink($pathname);
  124 +
  125 + //2
  126 + $image = Image::open($this->getPng());
  127 +
  128 + $image->thumb(200, 200, Image::THUMB_SCALING)->save($pathname);
  129 +
  130 + $this->assertEquals(200, $image->width());
  131 + $this->assertEquals(150, $image->height());
  132 +
  133 + $file = new \SplFileInfo($pathname);
  134 +
  135 + $this->assertTrue($file->isFile());
  136 +
  137 + @unlink($pathname);
  138 +
  139 + //3
  140 + $image = Image::open($this->getPng());
  141 +
  142 + $image->thumb(200, 200, Image::THUMB_FILLED)->save($pathname);
  143 +
  144 + $this->assertEquals(200, $image->width());
  145 + $this->assertEquals(200, $image->height());
  146 +
  147 + $file = new \SplFileInfo($pathname);
  148 +
  149 + $this->assertTrue($file->isFile());
  150 +
  151 + @unlink($pathname);
  152 +
  153 + //4
  154 + $image = Image::open($this->getPng());
  155 +
  156 + $image->thumb(200, 200, Image::THUMB_NORTHWEST)->save($pathname);
  157 +
  158 + $this->assertEquals(200, $image->width());
  159 + $this->assertEquals(200, $image->height());
  160 +
  161 + $file = new \SplFileInfo($pathname);
  162 +
  163 + $this->assertTrue($file->isFile());
  164 +
  165 + @unlink($pathname);
  166 +
  167 + //5
  168 + $image = Image::open($this->getPng());
  169 +
  170 + $image->thumb(200, 200, Image::THUMB_SOUTHEAST)->save($pathname);
  171 +
  172 + $this->assertEquals(200, $image->width());
  173 + $this->assertEquals(200, $image->height());
  174 +
  175 + $file = new \SplFileInfo($pathname);
  176 +
  177 + $this->assertTrue($file->isFile());
  178 +
  179 + @unlink($pathname);
  180 +
  181 + //6
  182 + $image = Image::open($this->getPng());
  183 +
  184 + $image->thumb(200, 200, Image::THUMB_FIXED)->save($pathname);
  185 +
  186 + $this->assertEquals(200, $image->width());
  187 + $this->assertEquals(200, $image->height());
  188 +
  189 + $file = new \SplFileInfo($pathname);
  190 +
  191 + $this->assertTrue($file->isFile());
  192 +
  193 + @unlink($pathname);
  194 + }
  195 +
  196 + public function testGif()
  197 + {
  198 + $pathname = TEST_PATH . 'tmp/thumb.gif';
  199 +
  200 + //1
  201 + $image = Image::open($this->getGif());
  202 +
  203 + $image->thumb(200, 200, Image::THUMB_CENTER)->save($pathname);
  204 +
  205 + $this->assertEquals(200, $image->width());
  206 + $this->assertEquals(200, $image->height());
  207 +
  208 + $file = new \SplFileInfo($pathname);
  209 +
  210 + $this->assertTrue($file->isFile());
  211 +
  212 + @unlink($pathname);
  213 +
  214 + //2
  215 + $image = Image::open($this->getGif());
  216 +
  217 + $image->thumb(200, 200, Image::THUMB_SCALING)->save($pathname);
  218 +
  219 + $this->assertEquals(200, $image->width());
  220 + $this->assertEquals(113, $image->height());
  221 +
  222 + $file = new \SplFileInfo($pathname);
  223 +
  224 + $this->assertTrue($file->isFile());
  225 +
  226 + @unlink($pathname);
  227 +
  228 + //3
  229 + $image = Image::open($this->getGif());
  230 +
  231 + $image->thumb(200, 200, Image::THUMB_FILLED)->save($pathname);
  232 +
  233 + $this->assertEquals(200, $image->width());
  234 + $this->assertEquals(200, $image->height());
  235 +
  236 + $file = new \SplFileInfo($pathname);
  237 +
  238 + $this->assertTrue($file->isFile());
  239 +
  240 + @unlink($pathname);
  241 +
  242 + //4
  243 + $image = Image::open($this->getGif());
  244 +
  245 + $image->thumb(200, 200, Image::THUMB_NORTHWEST)->save($pathname);
  246 +
  247 + $this->assertEquals(200, $image->width());
  248 + $this->assertEquals(200, $image->height());
  249 +
  250 + $file = new \SplFileInfo($pathname);
  251 +
  252 + $this->assertTrue($file->isFile());
  253 +
  254 + @unlink($pathname);
  255 +
  256 + //5
  257 + $image = Image::open($this->getGif());
  258 +
  259 + $image->thumb(200, 200, Image::THUMB_SOUTHEAST)->save($pathname);
  260 +
  261 + $this->assertEquals(200, $image->width());
  262 + $this->assertEquals(200, $image->height());
  263 +
  264 + $file = new \SplFileInfo($pathname);
  265 +
  266 + $this->assertTrue($file->isFile());
  267 +
  268 + @unlink($pathname);
  269 +
  270 + //6
  271 + $image = Image::open($this->getGif());
  272 +
  273 + $image->thumb(200, 200, Image::THUMB_FIXED)->save($pathname);
  274 +
  275 + $this->assertEquals(200, $image->width());
  276 + $this->assertEquals(200, $image->height());
  277 +
  278 + $file = new \SplFileInfo($pathname);
  279 +
  280 + $this->assertTrue($file->isFile());
  281 +
  282 + @unlink($pathname);
  283 + }
  284 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +namespace tests;
  12 +
  13 +use think\Image;
  14 +
  15 +class WaterTest extends TestCase
  16 +{
  17 + public function testJpeg()
  18 + {
  19 + $pathname = TEST_PATH . 'tmp/water.jpg';
  20 + $image = Image::open($this->getJpeg());
  21 +
  22 + $image->water(TEST_PATH . 'images/test.gif')->save($pathname);
  23 +
  24 + $file = new \SplFileInfo($pathname);
  25 +
  26 + $this->assertTrue($file->isFile());
  27 +
  28 + @unlink($pathname);
  29 + }
  30 +
  31 + public function testPng()
  32 + {
  33 + $pathname = TEST_PATH . 'tmp/water.png';
  34 + $image = Image::open($this->getPng());
  35 +
  36 + $image->water(TEST_PATH . 'images/test.gif')->save($pathname);
  37 +
  38 + $file = new \SplFileInfo($pathname);
  39 +
  40 + $this->assertTrue($file->isFile());
  41 +
  42 + @unlink($pathname);
  43 + }
  44 +
  45 + public function testGif()
  46 + {
  47 + $pathname = TEST_PATH . 'tmp/water.gif';
  48 + $image = Image::open($this->getGif());
  49 +
  50 + $image->water(TEST_PATH . 'images/test.jpg')->save($pathname);
  51 +
  52 + $file = new \SplFileInfo($pathname);
  53 +
  54 + $this->assertTrue($file->isFile());
  55 +
  56 + @unlink($pathname);
  57 + }
  58 +}
  1 +<?php
  2 +// +----------------------------------------------------------------------
  3 +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 +// +----------------------------------------------------------------------
  5 +// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6 +// +----------------------------------------------------------------------
  7 +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 +// +----------------------------------------------------------------------
  9 +// | Author: yunwuxin <448901948@qq.com>
  10 +// +----------------------------------------------------------------------
  11 +define('TEST_PATH', __DIR__ . '/');
  12 +// 加载框架基础文件
  13 +require __DIR__ . '/../thinkphp/base.php';
  14 +\think\Loader::addNamespace('tests', TEST_PATH);
  15 +\think\Loader::addNamespace('think', __DIR__ . '/../src/');