扩展库

This commit is contained in:
2016-06-21 17:13:19 +08:00
parent 7ea154d684
commit 18896b4e98
54 changed files with 9456 additions and 0 deletions

226
extend/com/Auth.php Normal file
View File

@@ -0,0 +1,226 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2011 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: luofei614 <weibo.com/luofei614> 
// +----------------------------------------------------------------------
namespace com;
/**
* 权限认证类
* 功能特性:
* 1是对规则进行认证不是对节点进行认证。用户可以把节点当作规则名称实现对节点进行认证。
* $auth=new Auth(); $auth->check('规则名称','用户id')
* 2可以同时对多条规则进行认证并设置多条规则的关系or或者and
* $auth=new Auth(); $auth->check('规则1,规则2','用户id','and')
* 第三个参数为and时表示用户需要同时具有规则1和规则2的权限。 当第三个参数为or时表示用户值需要具备其中一个条件即可。默认为or
* 3一个用户可以属于多个用户组(think_auth_group_access表 定义了用户所属用户组)。我们需要设置每个用户组拥有哪些规则(think_auth_group 定义了用户组权限)
*
* 4支持规则表达式。
* 在think_auth_rule 表中定义一条规则时如果type为1 condition字段就可以定义规则表达式。 如定义{score}>5 and {score}<100 表示用户的分数在5-100之间时这条规则才会通过。
*/
//数据库
/*
-- ----------------------------
-- think_auth_rule规则表
-- id:主键name规则唯一标识, title规则中文名称 status 状态为1正常为0禁用condition规则表达式为空表示存在就验证不为空表示按照条件验证
-- ----------------------------
DROP TABLE IF EXISTS `think_auth_rule`;
CREATE TABLE `think_auth_rule` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` char(80) NOT NULL DEFAULT '',
`title` char(20) NOT NULL DEFAULT '',
`type` tinyint(1) NOT NULL DEFAULT '1',
`status` tinyint(1) NOT NULL DEFAULT '1',
`condition` char(100) NOT NULL DEFAULT '', # 规则附件条件,满足附加条件的规则,才认为是有效的规则
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ----------------------------
-- think_auth_group 用户组表,
-- id主键 title:用户组中文名称, rules用户组拥有的规则id 多个规则","隔开status 状态为1正常为0禁用
-- ----------------------------
DROP TABLE IF EXISTS `think_auth_group`;
CREATE TABLE `think_auth_group` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`title` char(100) NOT NULL DEFAULT '',
`status` tinyint(1) NOT NULL DEFAULT '1',
`rules` char(80) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ----------------------------
-- think_auth_group_access 用户组明细表
-- uid:用户idgroup_id用户组id
-- ----------------------------
DROP TABLE IF EXISTS `think_auth_group_access`;
CREATE TABLE `think_auth_group_access` (
`uid` mediumint(8) unsigned NOT NULL,
`group_id` mediumint(8) unsigned NOT NULL,
UNIQUE KEY `uid_group_id` (`uid`,`group_id`),
KEY `uid` (`uid`),
KEY `group_id` (`group_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
*/
class Auth{
//默认配置
protected $_config = array(
'auth_on' => true, // 认证开关
'auth_type' => 1, // 认证方式1为实时认证2为登录认证。
'auth_group' => 'auth_group', // 用户组数据表名
'auth_group_access' => 'auth_group_access', // 用户-用户组关系表
'auth_rule' => 'auth_rule', // 权限规则表
'auth_user' => 'member' // 用户信息表
);
public function __construct() {
if (config('auth_config')) {
//可设置配置项 auth_config, 此配置项为数组。
$this->_config = array_merge($this->_config, config('auth_config'));
}
}
/**
* 检查权限
* @param name string|array 需要验证的规则列表,支持逗号分隔的权限规则或索引数组
* @param uid int 认证用户的id
* @param string mode 执行check的模式
* @param relation string 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证
* @return boolean 通过验证返回true;失败返回false
*/
public function check($name, $uid, $type=1, $mode='url', $relation='or') {
if (!$this->_config['auth_on'])
return true;
$authList = $this->getAuthList($uid,$type); //获取用户需要验证的所有有效规则列表
if (is_string($name)) {
$name = strtolower($name);
if (strpos($name, ',') !== false) {
$name = explode(',', $name);
} else {
$name = array($name);
}
}
$list = array(); //保存验证通过的规则名
if ($mode=='url') {
$REQUEST = unserialize( strtolower(serialize($_REQUEST)) );
}
foreach ( $authList as $auth ) {
$query = preg_replace('/^.+\?/U','',$auth);
if ($mode=='url' && $query!=$auth ) {
parse_str($query,$param); //解析规则中的param
$intersect = array_intersect_assoc($REQUEST,$param);
$auth = preg_replace('/\?.*$/U','',$auth);
if ( in_array($auth,$name) && $intersect==$param ) { //如果节点相符且url参数满足
$list[] = $auth ;
}
}else if (in_array($auth , $name)){
$list[] = $auth ;
}
}
if ($relation == 'or' and !empty($list)) {
return true;
}
$diff = array_diff($name, $list);
if ($relation == 'and' and empty($diff)) {
return true;
}
return false;
}
/**
* 根据用户id获取用户组,返回值为数组
* @param uid int 用户id
* @return array 用户所属的用户组 array(
* array('uid'=>'用户id','group_id'=>'用户组id','title'=>'用户组名称','rules'=>'用户组拥有的规则id,多个,号隔开'),
* ...)
*/
public function getGroups($uid) {
static $groups = array();
if (isset($groups[$uid]))
return $groups[$uid];
$user_groups = \think\Db::name($this->_config['auth_group_access'])
->alias('a')
->join($this->_config['auth_group']." g", "g.id=a.group_id")
->where("a.uid='$uid' and g.status='1'")
->field('uid,group_id,title,rules')->select();
$groups[$uid] = $user_groups ? $user_groups : array();
return $groups[$uid];
}
/**
* 获得权限列表
* @param integer $uid 用户id
* @param integer $type
*/
protected function getAuthList($uid,$type) {
static $_authList = array(); //保存用户验证通过的权限列表
$t = implode(',',(array)$type);
if (isset($_authList[$uid.$t])) {
return $_authList[$uid.$t];
}
if( $this->_config['auth_type']==2 && isset($_SESSION['_auth_list_'.$uid.$t])){
return $_SESSION['_auth_list_'.$uid.$t];
}
//读取用户所属用户组
$groups = $this->getGroups($uid);
$ids = array();//保存用户所属用户组设置的所有权限规则id
foreach ($groups as $g) {
$ids = array_merge($ids, explode(',', trim($g['rules'], ',')));
}
$ids = array_unique($ids);
if (empty($ids)) {
$_authList[$uid.$t] = array();
return array();
}
$map=array(
'id'=>array('in',$ids),
'type'=>$type,
'status'=>1,
);
//读取用户组所有权限规则
$rules = \think\Db::name($this->_config['auth_rule'])->where($map)->field('condition,name')->select();
//循环规则,判断结果。
$authList = array(); //
foreach ($rules as $rule) {
if (!empty($rule['condition'])) { //根据condition进行验证
$user = $this->getUserInfo($uid);//获取用户信息,一维数组
$command = preg_replace('/\{(\w*?)\}/', '$user[\'\\1\']', $rule['condition']);
//dump($command);//debug
@(eval('$condition=(' . $command . ');'));
if ($condition) {
$authList[] = strtolower($rule['name']);
}
} else {
//只要存在就记录
$authList[] = strtolower($rule['name']);
}
}
$_authList[$uid.$t] = $authList;
if($this->_config['auth_type']==2){
//规则列表结果保存到session
$_SESSION['_auth_list_'.$uid.$t]=$authList;
}
return array_unique($authList);
}
/**
* 获得用户资料,根据自己的情况读取数据库
*/
protected function getUserInfo($uid) {
static $userinfo=array();
if(!isset($userinfo[$uid])){
$userinfo[$uid]=\think\Db::name($this->_config['auth_user'])->where(array('uid'=>$uid))->find();
}
return $userinfo[$uid];
}
}

212
extend/com/Database.php Normal file
View File

@@ -0,0 +1,212 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace com;
use think\Db;
//数据导出模型
class Database{
/**
* 文件指针
* @var resource
*/
private $fp;
/**
* 备份文件信息 part - 卷号name - 文件名
* @var array
*/
private $file;
/**
* 当前打开文件大小
* @var integer
*/
private $size = 0;
/**
* 备份配置
* @var integer
*/
private $config;
/**
* 数据库备份构造方法
* @param array $file 备份或还原的文件信息
* @param array $config 备份配置信息
* @param string $type 执行类型export - 备份数据, import - 还原数据
*/
public function __construct($file, $config, $type = 'export'){
$this->file = $file;
$this->config = $config;
}
/**
* 打开一个卷,用于写入数据
* @param integer $size 写入数据的大小
*/
private function open($size){
if($this->fp){
$this->size += $size;
if($this->size > $this->config['part']){
$this->config['compress'] ? @gzclose($this->fp) : @fclose($this->fp);
$this->fp = null;
$this->file['part']++;
session('backup_file', $this->file);
$this->create();
}
} else {
$backuppath = $this->config['path'];
$filename = "{$backuppath}{$this->file['name']}-{$this->file['part']}.sql";
if($this->config['compress']){
$filename = "{$filename}.gz";
$this->fp = @gzopen($filename, "a{$this->config['level']}");
} else {
$this->fp = @fopen($filename, 'a');
}
$this->size = filesize($filename) + $size;
}
}
/**
* 写入初始数据
* @return boolean true - 写入成功false - 写入失败
*/
public function create(){
$sql = "-- -----------------------------\n";
$sql .= "-- Think MySQL Data Transfer \n";
$sql .= "-- \n";
$sql .= "-- Host : " . config('DB_HOST') . "\n";
$sql .= "-- Port : " . config('DB_PORT') . "\n";
$sql .= "-- Database : " . config('DB_NAME') . "\n";
$sql .= "-- \n";
$sql .= "-- Part : #{$this->file['part']}\n";
$sql .= "-- Date : " . date("Y-m-d H:i:s") . "\n";
$sql .= "-- -----------------------------\n\n";
$sql .= "SET FOREIGN_KEY_CHECKS = 0;\n\n";
return $this->write($sql);
}
/**
* 写入SQL语句
* @param string $sql 要写入的SQL语句
* @return boolean true - 写入成功false - 写入失败!
*/
private function write($sql){
$size = strlen($sql);
//由于压缩原因无法计算出压缩后的长度这里假设压缩率为50%
//一般情况压缩率都会高于50%
$size = $this->config['compress'] ? $size / 2 : $size;
$this->open($size);
return $this->config['compress'] ? @gzwrite($this->fp, $sql) : @fwrite($this->fp, $sql);
}
/**
* 备份表结构
* @param string $table 表名
* @param integer $start 起始行数
* @return boolean false - 备份失败
*/
public function backup($table, $start){
//创建DB对象
$db = \think\Db::connect();
//备份表结构
if(0 == $start){
$result = $db->query("SHOW CREATE TABLE `{$table}`");
$sql = "\n";
$sql .= "-- -----------------------------\n";
$sql .= "-- Table structure for `{$table}`\n";
$sql .= "-- -----------------------------\n";
$sql .= "DROP TABLE IF EXISTS `{$table}`;\n";
$sql .= trim($result[0]['create table']) . ";\n\n";
if(false === $this->write($sql)){
return false;
}
}
//数据总数
$result = $db->query("SELECT COUNT(*) AS count FROM `{$table}`");
$count = $result['0']['count'];
//备份表数据
if($count){
//写入数据注释
if(0 == $start){
$sql = "-- -----------------------------\n";
$sql .= "-- Records of `{$table}`\n";
$sql .= "-- -----------------------------\n";
$this->write($sql);
}
//备份数据记录
$result = $db->query("SELECT * FROM `{$table}` LIMIT {$start}, 1000");
foreach ($result as $row) {
$row = array_map('addslashes', $row);
$sql = "INSERT INTO `{$table}` VALUES ('" . str_replace(array("\r","\n"),array('\r','\n'),implode("', '", $row)) . "');\n";
if(false === $this->write($sql)){
return false;
}
}
//还有更多数据
if($count > $start + 1000){
return array($start + 1000, $count);
}
}
//备份下一表
return 0;
}
public function import($start){
//还原数据
$db = \think\Db::connect();
if($this->config['compress']){
$gz = gzopen($this->file[1], 'r');
$size = 0;
} else {
$size = filesize($this->file[1]);
$gz = fopen($this->file[1], 'r');
}
$sql = '';
if($start){
$this->config['compress'] ? gzseek($gz, $start) : fseek($gz, $start);
}
for($i = 0; $i < 1000; $i++){
$sql .= $this->config['compress'] ? gzgets($gz) : fgets($gz);
if(preg_match('/.*;$/', trim($sql))){
if(false !== $db->execute($sql)){
$start += strlen($sql);
} else {
return false;
}
$sql = '';
} elseif ($this->config['compress'] ? gzeof($gz) : feof($gz)) {
return 0;
}
}
return array($start, $size);
}
/**
* 析构方法,用于关闭文件资源
*/
public function __destruct(){
//$this->config['compress'] ? @gzclose($this->fp) : @fclose($this->fp);
}
}

274
extend/com/Datatable.php Normal file
View File

@@ -0,0 +1,274 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: colin <colin@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace com;
use think\Db;
/**
* 数据库管理类
* @author colin <colin@tensent.cn>
*/
class Datatable{
protected $table; /*数据库操作的表*/
protected $fields = array(); /*数据库操作字段*/
protected $charset = 'utf8'; /*数据库操作字符集*/
public $prefix = ''; /*数据库操作表前缀*/
protected $model_table_prefix = ''; /*模型默认创建的表前缀*/
protected $engine_type = 'MyISAM'; /*数据库引擎*/
protected $key = 'id'; /*数据库主键*/
public $sql = ''; /*最后生成的sql语句*/
/**
* 初始化数据库信息
* @author colin <colin@tensent.cn>
*/
public function __construct(){
//创建DB对象
$this->prefix = config('database.prefix');
$this->model_table_prefix = config('model_table_prefix');
}
/**
* 开始创建表
* @var $table 表名
* @author colin <colin@tensent.cn>
*/
public function start_table($table){
$this->table = $this->getTablename($table,true);
$this->sql .= "CREATE TABLE IF NOT EXISTS `".$this->table."`(";
return $this;
}
/**
* 创建字段
* @var $sql 要执行的字段sql语句可以为array()或者strubf
* @author colin <colin@tensent.cn>
*/
public function create_field($sql){
$this->sql .= $sql.',';
return $this;
}
/**
* 快速创建ID字段
* @var length 字段的长度
* @var comment 字段的描述
* @author colin <colin@tensent.cn>
*/
public function create_id($key = 'id', $length = 11 , $comment = '主键' , $is_auto_increment = true){
$auto_increment = $is_auto_increment ? 'AUTO_INCREMENT' : '';
$this->sql .= "`{$key}` int({$length}) unsigned NOT NULL $auto_increment COMMENT '{$comment}',";
return $this;
}
/**
* 快速创建ID字段
* @var length 字段的长度
* @var comment 字段的描述
* @author colin <colin@tensent.cn>
*/
public function create_uid(){
$this->sql .= "`uid` int(11) NOT NULL DEFAULT '0' COMMENT '用户uid',";
return $this;
}
/**
* 追加字段
* @var $table 追加字段的表名
* @var $attr 属性列表
* @var $is_more 是否为多条同时插入
* @author colin <colin@tensent.cn>
*/
public function colum_field($table,$attr = array()){
$field_attr['table'] = $table ? $this->getTablename($table,true) : $this->table;
$field_attr['field'] = $attr['field'];
$field_attr['type'] = $attr['type'] ? $attr['type'] : 'varchar';
if (intval($attr['length']) && $attr['length']) {
$field_attr['length'] = "(".$attr['length'].")";
}else{
$field_attr['length'] = "";
}
$field_attr['is_null'] = $attr['is_null'] ? 'NOT NULL' : 'null';
$field_attr['default'] = $attr['default'] != '' ? 'default "'.$attr['default'].'"' : 'default null';
if($field_attr['is_null'] == 'null'){
$field_attr['default'] = $field_attr['default'];
}else{
$field_attr['default'] = '';
}
$field_attr['comment'] = (isset($attr['comment']) && $attr['comment']) ? $attr['comment'] : '';
$field_attr['oldname'] = (isset($attr['oldname']) && $attr['oldname']) ? $attr['oldname'] : '';
$field_attr['newname'] = (isset($attr['newname']) && $attr['newname']) ? $attr['newname'] : $field_attr['field'];
$field_attr['after'] = (isset($attr['after']) && $attr['after']) ? ' AFTER `'.$attr['after'].'`' : '';
$field_attr['action'] = (isset($attr['action']) && $attr['action']) ? $attr['action'] : 'ADD';
//确认表是否存在
if($field_attr['action'] == 'ADD'){
$this->sql = "ALTER TABLE `{$field_attr['table']}` ADD `{$field_attr['field']}` {$field_attr['type']}{$field_attr['length']} {$field_attr['is_null']} {$field_attr['default']} COMMENT '{$field_attr['comment']}'";
}elseif($field_attr['action'] == 'CHANGE'){
$this->sql = "ALTER TABLE `{$field_attr['table']}` CHANGE `{$field_attr['oldname']}` `{$field_attr['newname']}` {$field_attr['type']}{$field_attr['length']} {$field_attr['is_null']} {$field_attr['default']} COMMENT '{$field_attr['comment']}'";
}
return $this;
}
/**
* 删除字段
* @var $table 追加字段的表名
* @var $field 字段名
* @author colin <colin@tensent.cn>
*/
public function del_field($table,$field){
$table = $table ? $this->getTablename($table,true) : $this->table;
$this->sql = "ALTER TABLE `$table` DROP `$field`";
return $this;
}
/**
* 删除数据表
* @var $table 追加字段的表名
* @author colin <colin@tensent.cn>
*/
public function del_table($table){
$table = $table ? $this->getTablename($table,true) : $this->table;
$this->sql = "DROP TABLE `$table`";
return $this;
}
/**
* 主键设置
* @var $key 要被设置主键的字段
* @author colin <colin@tensent.cn>
*/
public function create_key($key = null){
if(null != $key){
$this->key = $key;
}
$this->sql .= "PRIMARY KEY (`".$this->key."`)";
return $this;
}
/**
* 结束表
* @var $engine_type 数据库引擎
* @var $comment 表注释
* @var $charset 数据库编码
* @author colin <colin@tensent.cn>
*/
public function end_table($comment,$engine_type = null,$charset = null){
if(null != $charset){
$this->charset = $charset;
}
if(null != $engine_type){
$this->engine_type = $engine_type;
}
$end = "ENGINE=".$this->engine_type." AUTO_INCREMENT=1 DEFAULT CHARSET=".$this->charset." ROW_FORMAT=DYNAMIC COMMENT='".$comment."';";
$this->sql .= ")".$end;
return $this;
}
/**
* 创建动作
* @return int 0
* @author colin <colin@tensent.cn>
*/
public function create(){
$res = Db::execute($this->sql);
return $res !== false;
}
/**
* create的别名
* @return int 0
* @author colin <colin@tensent.cn>
*/
public function query(){
return $this->create();
}
/**
* 获取最后生成的sql语句
* @author colin <colin@tensent.cn>
*/
public function getLastSql(){
return $this->sql;
}
/**
* 获取指定的表名
* @var $table 要获取名字的表名
* @var $prefix 获取表前缀? 默认为不获取 false
* @author colin <colin@tensent.cn>
*/
public function getTablename($table , $prefix = false){
if(false == $prefix){
$this->table = $this->model_table_prefix.$table;
}else{
$this->table = $this->prefix.$this->model_table_prefix.$table;
}
return $this->table;
}
/**
* 获取指定表名的所有字段及详细信息
* @var $table 要获取名字的表名 可以为sent_tengsu_photo、tengsu_photo、photo
* @author colin <colin@tensent.cn>
*/
public function getFields($table){
if(false == $table){
$table = $this->table;//为空调用当前table
}else{
$table = $table;
}
$patten = "/\./";
if(!preg_match_all($patten,$table)){
//匹配_
$patten = "/_+/";
if(!preg_match_all($patten, $table)){
$table = $this->prefix.$this->model_table_prefix.$table;
}else{
//匹配是否包含表前缀,如果是 那么就是手动输入
$patten = "/$this->prefix/";
if(!preg_match_all($patten,$table)){
$table = $this->prefix.$table;
}
}
}
$sql = "SHOW FULL FIELDS FROM $table";
return Db::query($sql);
}
/**
* 确认表是否存在
* @var $table 表名 可以为sent_tengsu_photo、tengsu_photo、photo
* @author colin <colin@tensent.cn>
* @return boolen
*/
public function CheckTable($table){
//获取表名
$this->table = $this->getTablename($table,true);
$result = Db::execute("SHOW TABLES LIKE '%$this->table%'");
return $result;
}
/**
* 确认字段是否存在
* @var $table 表名 可以为sent_tengsu_photo、tengsu_photo、photo
* @var $field 字段名 要检查的字段名
* @author colin <colin@tensent.cn>
* @return boolen
*/
public function CheckField($table,$field){
//检查字段是否存在
$table = $this->getTablename($table,true);
if(!Db::query("Describe $table $field")){
return false;
}else{
return true;
}
}
}

144
extend/com/Sent.php Normal file
View File

@@ -0,0 +1,144 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace com;
use think\template\TagLib;
/**
* CX标签库解析类
* @category Think
* @package Think
* @subpackage Driver.Taglib
* @author liu21st <liu21st@gmail.com>
*/
class Sent extends Taglib{
// 标签定义
protected $tags = array(
// 标签定义: attr 属性列表 close 是否闭合0 或者1 默认1 alias 标签别名 level 嵌套层次
'nav' => array('attr' => 'field,name', 'close' => 1), //获取导航
'list' => array('attr' => 'table,where,order,limit,id,sql,field,key','level'=>3),//列表
'doc' => array('attr' => 'model,field,limit,id,field,key','level'=>3),
'link' => array('attr' => 'type,limit' , 'close' => 1),//友情链接
'prev' => array('attr' => 'id,cate' , 'close' => 1),//上一篇
'next' => array('attr' => 'id,cate' , 'close' => 1),//下一篇
);
public function _nav($tag, $content){
$field = empty($tag['field']) ? 'true' : $tag['field'];
$tree = empty($tag['tree'])? true : false;
$parse = $parse = '<?php ';
$parse .= '$__NAV__ = db(\'Channel\')->field('.$field.')->where("status=1")->order("sort")->select();';
if($tree){
$parse .= '$__NAV__ = list_to_tree($__NAV__, "id", "pid");';
}
$parse .= 'foreach ($__NAV__ as $key => $'.$tag['name'].') {';
$parse .= '?>';
$parse .= $content;
$parse .= '<?php } ?>';
return $parse;
}
public function _doc($tag, $content){
$model = !empty($tag['model']) ? $tag['model']:'';
$cid = !empty($tag['cid']) ? $tag['cid']:'0';
$field = empty($tag['field']) ? '*' : $tag['field'];
$limit = empty($tag['limit']) ? 20 : $tag['limit'];
$order = empty($tag['order']) ? 'create_time desc' : $tag['order'];
//获得当前栏目的所有子栏目
$ids = get_category_child($cid);
$ids = implode(',', $ids);
$where = "category_id IN ({$ids})";
$where .= " and model_id = {$model} and status >= 1";
$parse = $parse = '<?php ';
$parse .= '$__LIST__ = model(\'Document\')->extend(\''.$model.'\')->list(\''.$where.'\',\''.$field.'\',\''.$limit.'\',\''.$order.'\')->all();';
$parse .= 'foreach ($__LIST__ as $key => $'.$tag['name'].') {';
$parse .= '?>';
$parse .= $content;
$parse .= '<?php } ?>';
return $parse;
}
public function _list($tag, $content){
$name = !empty($tag['name']) ? $tag['name'] : '';
$map = !empty($tag['map']) ? $tag['map'] : '';
$field = empty($tag['field']) ? '*' : $tag['field'];
$limit = empty($tag['limit']) ? 20 : $tag['limit'];
$order = empty($tag['order']) ? 'id desc' : $tag['order'];
$where[] = "status > 0";
if ($map) {
$where[] = $map;
}
$map = implode(" and ", $where);
$parse = $parse = '<?php ';
$parse .= '$__LIST__ = model(\''.$name.'\')->list(\''.$map.'\',\''.$field.'\',\''.$limit.'\',\''.$order.'\')->all();';
$parse .= 'foreach ($__LIST__ as $key => $'.$tag['id'].') {';
$parse .= '?>';
$parse .= $content;
$parse .= '<?php } ?>';
return $parse;
}
public function _link($tag, $content){
$type = !empty($tag['type']) ? $tag['type'] : '';
$limit = !empty($tag['limit']) ? $tag['limit'] : '';
$field = empty($tag['field']) ? '*' : $tag['field'];
$limit = empty($tag['limit']) ? 20 : $tag['limit'];
$order = empty($tag['order']) ? "id desc" : $tag['order'];
$where[] = "status > 0";
if ($type) {
$where[] = "ftype = " . $type;
}
$map = implode(" and ", $where);
$parse = $parse = '<?php ';
$parse .= '$__LIST__ = model(\'Link\')->list(\''.$map.'\',\''.$field.'\',\''.$limit.'\',\''.$order.'\')->all();';
$parse .= 'foreach ($__LIST__ as $key => $'.$tag['name'].') {';
$parse .= '?>';
$parse .= $content;
$parse .= '<?php } ?>';
return $parse;
}
public function _prev($tag, $content){
$id = !empty($tag['id']) ? $tag['id'] : '';
$cate = !empty($tag['cate']) ? $tag['cate'] : '';
$model_id = !empty($tag['model']) ? $tag['model'] : '';
$map = "category_id = ".$cate." and model_id=".$model_id." and id < ".$id;
$parse = '<?php ';
$parse .= '$__DATA__ = db(\'Document\')->where("' . $map . '")->order(\'id asc\')->find();';
$parse .= $content;
$parse .= '}?>';
return $parse;
}
public function _next($tag, $content){
$id = !empty($tag['id']) ? $tag['id'] : '';
$cate = !empty($tag['cate']) ? $tag['cate'] : '';
$model_id = !empty($tag['model']) ? $tag['model'] : '';
$map = "category_id = ".$cate." and model_id=".$model_id." and id < ".$id;
$parse = '<?php ';
$parse .= '$__DATA__ = db(\'Document\')->where("' . $map . '")->order(\'id asc\')->find();';
$parse .= $content;
$parse .= '}?>';
return $parse;
}
}

95
extend/com/Tree.php Normal file
View File

@@ -0,0 +1,95 @@
<?php
namespace com;
/**
* 通用的树型类,可以生成任何树型结构
*/
class Tree {
protected $formatTree;
/**
* 把返回的数据集转换成Tree
* @param array $list 要转换的数据集
* @param string $pid parent标记字段
* @param string $level level标记字段
* @return array
* @author 麦当苗儿 <zuojiazi@vip.qq.com>
*/
protected function list_to_tree($list, $pk='id', $pid = 'pid', $child = '_child', $root = 0) {
// 创建Tree
$tree = array();
if(is_array($list)) {
// 创建基于主键的数组引用
$refer = array();
foreach ($list as $key => $data) {
$refer[$data[$pk]] =& $list[$key];
}
foreach ($list as $key => $data) {
// 判断是否存在parent
$parentId = $data[$pid];
if ($root == $parentId) {
$tree[] =& $list[$key];
}else{
if (isset($refer[$parentId])) {
$parent =& $refer[$parentId];
$parent['childs'][] = $data['id'];
$parent[$child][] =& $list[$key];
}
}
}
}
return $tree;
}
/**
* 将树子节点加层级成列表
*/
protected function _toFormatTree($tree, $level = 1) {
foreach ($tree as $key => $value) {
$temp = $value;
if (isset($temp['_child'])) {
$temp['_child'] = true;
$temp['level'] = $level;
} else {
$temp['_child'] = false;
$temp['level'] = $level;
}
array_push($this->formatTree, $temp);
if (isset($value['_child'])) {
$this->_toFormatTree($value['_child'], ($level + 1));
}
}
}
protected function cat_empty_deal($cat, $next_parentid, $pid='pid', $empty = "&nbsp;&nbsp;&nbsp;&nbsp;") {
$str = "";
if ($cat[$pid]) {
for ($i=2; $i < $cat['level']; $i++) {
$str .= $empty."";
}
if ($cat[$pid] != $next_parentid && !$cat['_child']) {
$str .= $empty."└─&nbsp;";
} else {
$str .= $empty."├─&nbsp;";
}
}
return $str;
}
public function toFormatTree($list,$title = 'title',$pk='id',$pid = 'pid',$root = 0){
if (empty($list)) {
return false;
}
$list = $this->list_to_tree($list,$pk,$pid,'_child',$root);
$this->formatTree = array();
$this->_toFormatTree($list);
foreach ($this->formatTree as $key => $value) {
$index = ($key+1);
$next_parentid = isset($this->formatTree[$index][$pid]) ? $this->formatTree[$index][$pid] : '';
$value['level_show'] = $this->cat_empty_deal($value, $next_parentid);
$value['title_show'] = $value['level_show'].$value[$title];
$data[] = $value;
}
return $data;
}
}

356
extend/com/Ueditor.php Normal file
View File

@@ -0,0 +1,356 @@
<?php
/**
* Ueditor插件
* @author Nintendov
*/
namespace com;
class Ueditor{
public $uid;//要操作的用户id 如有登录需要则去掉注释
private $output;//要输出的数据
private $st;
private $rootpath = '/uploads';
private $config = '';
public function __construct($uid = ''){
//uid 为空则导入当前会话uid
if(''===$uid) $this->uid = session('user_auth.uid');
//导入设置
$this->config = json_decode(preg_replace("/\/\*[\s\S]+?\*\//", "", file_get_contents(APP_PATH."ueditor.json")), true);
$action = htmlspecialchars($_GET['action']);
switch($action){
case 'config':
$result = json_encode($this->config);
break;
case 'uploadimage':
$config = array(
"pathFormat" => $this->config['imagePathFormat'],
"maxSize" => $this->config['imageMaxSize'],
"allowFiles" => $this->config['imageAllowFiles']
);
$fieldName = $this->config['imageFieldName'];
$result = $this->uploadFile($config, $fieldName);
break;
case 'uploadscrawl':
$config = array(
"pathFormat" => $this->config['scrawlPathFormat'],
"maxSize" => $this->config['scrawlMaxSize'],
"allowFiles" => $this->config['scrawlAllowFiles'],
"oriName" => "scrawl.png"
);
$fieldName = $this->config['scrawlFieldName'];
$result=$this->uploadBase64($config,$fieldName);
break;
case 'uploadvideo':
$config = array(
"pathFormat" => $this->config['videoPathFormat'],
"maxSize" => $this->config['videoMaxSize'],
"allowFiles" => $this->config['videoAllowFiles']
);
$fieldName = $this->config['videoFieldName'];
$result=$this->uploadFile($config, $fieldName);
break;
case 'uploadfile':
// default:
$config = array(
"pathFormat" => $this->config['filePathFormat'],
"maxSize" => $this->config['fileMaxSize'],
"allowFiles" => $this->config['fileAllowFiles']
);
$fieldName = $this->config['fileFieldName'];
$result=$this->uploadFile($config, $fieldName);
break;
case 'listfile':
$config=array(
'allowFiles' => $this->config['fileManagerAllowFiles'],
'listSize' => $this->config['fileManagerListSize'],
'path' => $this->config['fileManagerListPath'],
);
$result = $this->listFile($config);
break;
case 'listimage':
$config=array(
'allowFiles' => $this->config['imageManagerAllowFiles'],
'listSize' => $this->config['imageManagerListSize'],
'path' => $this->config['imageManagerListPath'],
);
$result = $this->listFile($config);
break;
case 'catchimage':
$config = array(
"pathFormat" => $this->config['catcherPathFormat'],
"maxSize" => $this->config['catcherMaxSize'],
"allowFiles" => $this->config['catcherAllowFiles'],
"oriName" => "remote.png"
);
$fieldName = $this->config['catcherFieldName'];
$result = $this->saveRemote($config , $fieldName);
break;
default:
$result = json_encode(array(
'state'=> 'wrong require'
));
break;
}
if (isset($_GET["callback"])) {
if (preg_match("/^[\w_]+$/", $_GET["callback"])) {
$this->output = htmlspecialchars($_GET["callback"]) . '(' . $result . ')';
} else {
$this->output = json_encode(array(
'state'=> 'callback参数不合法'
));
}
} else {
$this->output = $result;
}
}
/**
*
* 输出结果
* @param data 数组数据
* @return 组合后json格式的结果
*/
public function output(){
return $this->output;
}
/**
* 上传文件方法
*
*/
private function uploadFile($config,$fieldName){
$upload = new \org\Upload();
$upload->maxSize = $config['maxSize'] ;// 设置附件上传大小
$upload->exts = $this->format_exts($config['allowFiles']);// 设置附件上传类型
$upload->rootPath = '.'.$this->rootpath; // 设置附件上传根目录
$upload->autoSub = false;
$upload->savePath = $this->getFullPath($config['pathFormat']); // 设置附件上传(子)目录
$info=$upload->uploadOne($_FILES[$fieldName]);
$rootpath = $this->rootpath;
if(!$info){
$data = array(
"state"=>$upload -> getError(),
);
}else{
$data = array(
'state' => "SUCCESS",
'url' => $rootpath . $info['savepath'] . $info['savename'],
'title' => $info['name'],
'original' => $info['name'],
'type' => '.' . $info['ext'],
'size' => $info['size'],
);
}
return json_encode($data);
}
/**
*
* Enter description here ...
*/
private function uploadBase64($config,$fieldName){
$data = array();
$base64Data = $_POST[$fieldName];
$img = base64_decode($base64Data);
$path = $this->getFullPath($config['pathFormat']);
if(strlen($img)>$config['maxSize']){
$data['states'] = 'too large';
return json_encode($data);
}
$rootpath = $this->rootpath;
//替换随机字符串
$imgname = uniqid().'.png';
$filename = $path.$imgname;
if(\Think\Storage::put($rootpath,$filename,$img)){
$data=array(
'state' =>'SUCCESS',
'url' => $rootpath . $filename,
'title' =>$imgname,
'original' =>'scrawl.png',
'type' => '.png',
'size' => strlen($img),
);
}else{
$data=array(
'state'=>'cant write',
);
}
return json_encode($data);
}
/**
* 列出文件夹下所有文件,如果是目录则向下
*/
private function listFile($config){
$allowFiles = substr(str_replace(".", "|", join("", $config['allowFiles'])), 1);
$size = isset($_GET['size']) ? htmlspecialchars($_GET['size']) : $config['listSize'];
$start = isset($_GET['start']) ? htmlspecialchars($_GET['start']) : 0;
$end = $start + $size;
$rootpath = $this->rootpath;
$path = $config['path'];
$files = \Think\Storage::listFile($rootpath,$path, $allowFiles);
//return $files;
if (!count($files)) {
return json_encode(array(
"state" => "no match file",
"list" => array(),
"start" => $start,
"total" => count($files)
));
}
/* 获取指定范围的列表 */
$len = count($files);
for ($i = min($end, $len) - 1, $list = array(); $i < $len && $i >= 0 && $i >= $start; $i--){
$list[] = $files[$i];
}
//倒序
//for ($i = $end, $list = array(); $i < $len && $i < $end; $i++){
// $list[] = $files[$i];
//}
/* 返回数据 */
$result = json_encode(array(
"state" => "SUCCESS",
"list" => $list,
"start" => $start,
"total" => count($files)
));
return $result;
}
/**
*
* Enter description here ...
*/
private function saveRemote($config , $fieldName){
$list = array();
if (isset($_POST[$fieldName])) {
$source = $_POST[$fieldName];
} else {
$source = $_GET[$fieldName];
}
foreach ($source as $imgUrl) {
$upload = new \org\Upload();
$imgUrl = htmlspecialchars($imgUrl);
$imgUrl = str_replace("&amp;", "&", $imgUrl);
//http开头验证
if (strpos($imgUrl, "http") !== 0) {
$data = array('state'=>'不是http链接');
return json_encode($data);
}
//格式验证(扩展名验证和Content-Type验证)
$fileType = strtolower(strrchr($imgUrl, '.'));
if (!in_array($fileType, $config['allowFiles']) || stristr($heads['Content-Type'], "image")) {
$data = array("state"=>"错误文件格式");
return json_encode($data);
}
//打开输出缓冲区并获取远程图片
ob_start();
$context = stream_context_create(
array('http' => array(
'follow_location' => false // don't follow redirects
))
);
readfile($imgUrl, false, $context);
$img = ob_get_contents();
ob_end_clean();
preg_match("/[\/]([^\/]*)[\.]?[^\.\/]*$/", $imgUrl, $m);
$path = $this->getFullPath($config['pathFormat']);
if(strlen($img)>$config['maxSize']){
$data['states'] = 'too large';
return json_encode($data);
}
$rootpath = $this->rootpath;
$imgname = uniqid().'.png';
$filename = $path.$imgname;
$oriName = $m ? $m[1]:"";
if(\Think\Storage::put($rootpath,$filename,$img)){
array_push($list, array(
"state" => 'SUCCESS',
"url" => \Think\Storage::getPath($rootpath,$filename),
"size" => strlen($img),
"title" => $imgname,
"original" => $oriName,
"source" => htmlspecialchars($imgUrl)
));
}else{
array_push($list,array('state'=>'文件写入失败'));
}
}
/* 返回抓取数据 */
return json_encode(array(
'state'=> count($list) ? 'SUCCESS':'ERROR',
'list'=> $list
));
}
/**
* 规则替换命名文件
* @param $path
* @return string
*/
private function getFullPath($path){
//替换日期事件
$t = time();
$d = explode('-', date("Y-y-m-d-H-i-s"));
$format = $path;
$format = str_replace("{yyyy}", $d[0], $format);
$format = str_replace("{yy}", $d[1], $format);
$format = str_replace("{mm}", $d[2], $format);
$format = str_replace("{dd}", $d[3], $format);
$format = str_replace("{hh}", $d[4], $format);
$format = str_replace("{ii}", $d[5], $format);
$format = str_replace("{ss}", $d[6], $format);
$format = str_replace("{uid}", $this->uid, $format);
return $format;
}
private function format_exts($exts){
$data=array();
foreach ($exts as $key => $value) {
$data[]=ltrim($value,'.');
}
return $data;
}
}

239
extend/org/Crypt.php Normal file
View File

@@ -0,0 +1,239 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Haotong Lin <lofanmi@gmail.com>
// +----------------------------------------------------------------------
namespace org;
use org\transform\driver\Base64;
/**
* ThinkPHP加密模块(AES加密)
*
* 基于Laravel 5.1 Illuminate\Encryption\Encrypter实现, 使用时需打开openssl扩展
*
* 注: 关于key的生成
*
* 当 CIPHER_MODE = AES-128-CBC, IV_SIZE = 16 时,
*
* * key可以由16位随机字符串组成, 如:
* $key = 'gm9?lh=ngV.w86!Q';
*
* * 也可以使用32位十六进制字符串pack而成, 如:
* $key = pack('H*', 'd40e1a08a07fb2e21abe2abc5910f533');
*
* 当使用AES-256-CBC时, 密钥长度和初始化向量大小为32!
*
* 测试一下~
* // 明文
* $data = 'ThinkPHP框架 | 中文最佳实践PHP开源框架,专注WEB应用快速开发8年';
* // 密钥
* $key = 'gm9?lh=ngV.w86!Q';
* // $key = pack('H*', 'd40e1a08a07fb2e21abe2abc5910f533');
* // 1秒后失效
* $expire = 1;
* // 加密结果
* var_dump($test = Crypt::encrypt($data, $key, $expire));
* // 解密结果
* var_dump($test = Crypt::decrypt($test, $key));
* // true
* var_dump($data === $test);
* // 等一下下~
* sleep(0.1 + $expire);
* // false
* var_dump(Crypt::decrypt($test, $key));
*/
class Crypt
{
/**
* 校验码长度
* sha256的长度为32个字节, 这里只取前4个字节
*/
const HMAC_SIZE = 4;
/**
* 初始化向量大小
* AES-128-CBC 长度为16
* AES-256-CBC 长度为32
*/
const IV_SIZE = 16;
/**
* 加密模式
* AES-128-CBC
* AES-256-CBC
*/
const CIPHER_MODE = 'AES-128-CBC';
/**
* 密码有效期长度(4个字节)
*
*/
const EXPIRE_SIZE = 4;
/**
* 加密字符串
*
* @param mixed $value 待加密的数据(数字, 字符串, 数组或对象等)
* @param string $key 加密密钥
* @param int $expire 加密有效期(几秒后加密失效)
* @param string $target 编码目标
*
* @return string
*/
public static function encrypt($value, $key, $expire = 0, $target = 'url')
{
// 随机生成初始化向量, 增加密文随机性
$iv = static::createIV(self::IV_SIZE);
// 序列化待加密的数据(支持数组或对象的加密)
$value = static::packing($value);
// 加密数据
$value = openssl_encrypt($value, self::CIPHER_MODE, $key, OPENSSL_RAW_DATA, $iv);
if (false === $value) {
return false;
}
// 加密有效期
$expire = $expire ? dechex(time() + $expire) : 0;
$expire = sprintf('%08s', $expire);
// 生成密文校验码
$hmac = static::hmac($iv, $value, $key);
// 组合加密结果并base64编码
$base = new Base64();
return $base->encode(pack('H*', $hmac . $expire) . $iv . $value, $target);
}
/**
* 解密字符串
*
* @param string $value 待加密的数据(数字, 字符串, 数组或对象等)
* @param string $key 解密密钥
* @param string $target 解码目标
*
* @return string
*/
public static function decrypt($value, $key, $target = 'url')
{
// Base64解码
$base = new Base64();
$value = $base->decode($value, $target);
// 拆分加密结果(校验码, 有效期, 初始化向量, 加密数据)
$hmac = substr($value, 0, self::HMAC_SIZE);
$expire = substr($value, self::HMAC_SIZE, self::EXPIRE_SIZE);
$iv = substr($value, self::HMAC_SIZE + self::EXPIRE_SIZE, self::IV_SIZE);
$value = substr($value, self::HMAC_SIZE + self::EXPIRE_SIZE + self::IV_SIZE);
// 超出有效期
if (time() > hexdec(bin2hex($expire))) {
return false;
}
// 验证密文是否被篡改
if (static::compareString(static::hmac($iv, $value, $key), bin2hex($hmac)) === false) {
return false;
}
// 解密数据
$value = openssl_decrypt($value, self::CIPHER_MODE, $key, OPENSSL_RAW_DATA, $iv);
if (false === $value) {
return false;
}
// 反序列化
$value = static::unpacking($value);
// 返回解密结果
return $value;
}
/**
* 随机生成指定长度的初始化向量
*
* @param int $size 初始化向量长度
*
* @return string
*/
protected static function createIV($size)
{
if (function_exists('openssl_random_pseudo_bytes')) {
$bytes = openssl_random_pseudo_bytes($size, $strong);
}
if (is_null($bytes) || false === $bytes || false === $strong) {
$size *= 2;
$pool = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$bytes = pack('H*', substr(str_shuffle(str_repeat($pool, $size)), 0, $size));
}
return $bytes;
}
/**
* 生成指定长度的加密校验码, 保证密文安全
*
* @param string $iv 初始化向量
* @param string $value 加密后的数据
* @param string $key 加密密钥
*
* @return string
*/
protected static function hmac($iv, $value, $key)
{
return substr(hash_hmac('sha256', $iv . $value, $key), 0, self::HMAC_SIZE * 2);
}
/**
* 数据打包(数据如何序列化)
* serialize or json_encode
*
* @param mixed $value 待加密的数据
*
* @return string 返回序列化后的数据
*/
protected static function packing($value)
{
return serialize($value);
}
/**
* 数据解包(数据如何反序列化)
* unserialize or json_decode
*
* @param string $value 被序列化的数据
*
* @return mixed 返回被加密的数据
*/
protected static function unpacking($value)
{
return unserialize($value);
}
/**
* 比较字符串是否相等
*
* @param string $known 参考字符串
* @param string $input 待测试字符串
*
* @return boolean
*/
protected static function compareString($known, $input)
{
// 强制转换为字符串类型
$known = (string) $known;
$input = (string) $input;
if (function_exists('hash_equals')) {
return hash_equals($known, $input);
}
// 字符串长度不相等可直接返回
$length = strlen($known);
if (strlen($input) !== $length) {
return false;
}
// 逐位比较字符串
// 遇到字符不一致, 并不是直接返回, 这样就无法猜解字符串在哪里出错
$result = 0;
for ($i = 0; $i < $length; $i++) {
$result |= (ord($known[$i]) ^ ord($input[$i]));
}
return 0 === $result;
}
}

68
extend/org/Image.php Normal file
View File

@@ -0,0 +1,68 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org;
/* 缩略图相关常量定义 */
define('THINKIMAGE_THUMB_SCALING', 1); //常量,标识缩略图等比例缩放类型
define('THINKIMAGE_THUMB_FILLED', 2); //常量,标识缩略图缩放后填充类型
define('THINKIMAGE_THUMB_CENTER', 3); //常量,标识缩略图居中裁剪类型
define('THINKIMAGE_THUMB_NORTHWEST', 4); //常量,标识缩略图左上角裁剪类型
define('THINKIMAGE_THUMB_SOUTHEAST', 5); //常量,标识缩略图右下角裁剪类型
define('THINKIMAGE_THUMB_FIXED', 6); //常量,标识缩略图固定尺寸缩放类型
/* 水印相关常量定义 */
define('THINKIMAGE_WATER_NORTHWEST', 1); //常量,标识左上角水印
define('THINKIMAGE_WATER_NORTH', 2); //常量,标识上居中水印
define('THINKIMAGE_WATER_NORTHEAST', 3); //常量,标识右上角水印
define('THINKIMAGE_WATER_WEST', 4); //常量,标识左居中水印
define('THINKIMAGE_WATER_CENTER', 5); //常量,标识居中水印
define('THINKIMAGE_WATER_EAST', 6); //常量,标识右居中水印
define('THINKIMAGE_WATER_SOUTHWEST', 7); //常量,标识左下角水印
define('THINKIMAGE_WATER_SOUTH', 8); //常量,标识下居中水印
define('THINKIMAGE_WATER_SOUTHEAST', 9); //常量,标识右下角水印
/**
* 图片处理驱动类,可配置图片处理库
* 目前支持GD库和imagick
* @author 麦当苗儿 <zuojiazi.cn@gmail.com>
*/
class Image
{
/**
* 图片资源
* @var resource
*/
private static $im;
/**
* 初始化方法,用于实例化一个图片处理对象
*
* @param string $type 要使用的类库默认使用GD库
* @param null $imgname
*
* @return resource
*/
public static function init($type = 'Gd', $imgname = null)
{
/* 引入处理库,实例化图片处理对象 */
$class = '\\org\\image\\driver\\' . ucwords($type);
self::$im = new $class($imgname);
return self::$im;
}
// 调用驱动类的方法
public static function __callStatic($method, $params)
{
self::$im || self::init();
return call_user_func_array([self::$im, $method], $params);
}
}

79
extend/org/Oauth.php Normal file
View File

@@ -0,0 +1,79 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace org;
// oauth登录接口
// <code>
// Oauth::connect('qq',['app_key'=>'','app_secret'=>'','callback'=>'','authorize'=>'']); // 链接QQ登录
// Oauth::login(); // 跳转到授权登录页面 或者 Oauth::login($callbackUrl);
// Oauth::call('api','params'); // 调用API接口
// </code>
class Oauth
{
/**
* 操作句柄
* @var object
* @access protected
*/
protected static $handler = null;
/**
* 连接oauth
* @access public
* @param string $type Oauth类型
* @param array $options 配置数组
* @return object
*/
public static function connect($type, $options = [])
{
$class = '\\org\\oauth\\driver\\' . ucwords($type);
self::$handler = new $class($options);
return self::$handler;
}
// 跳转到授权登录页面
public static function login($callback = '')
{
self::$handler->login($callback);
}
// 获取access_token
public static function getAccessToken($code)
{
self::$handler->getAccessToken($code);
}
// 设置保存过的token信息
public static function setToken($token)
{
self::$handler->setToken($token);
}
// 获取oauth用户信息
public static function getOauthInfo()
{
return self::$handler->getOauthInfo();
}
// 获取openid信息
public static function getOpenId()
{
return self::$handler->getOpenId();
}
// 调用oauth接口API
public static function call($api, $param = '', $method = 'GET')
{
return self::$handler->call($api, $param, $method);
}
}

38
extend/org/Parser.php Normal file
View File

@@ -0,0 +1,38 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace org;
// 内容解析类
class Parser
{
/**
* @var array $handler
*/
private static $handler = [];
// 解析内容
public static function parse($content, $type)
{
if (!isset(self::$handler[$type])) {
$class = '\\org\\parser\\driver\\' . ucwords($type);
self::$handler[$type] = new $class();
}
return self::$handler[$type]->parse($content);
}
// 调用驱动类的方法
public static function __callStatic($method, $params)
{
return self::parse($params[0], $method);
}
}

88
extend/org/Transform.php Normal file
View File

@@ -0,0 +1,88 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace org;
// 内容解析类
use think\Exception;
class Transform
{
private static $handler = [];
/**
* 初始化解析驱动
* @static
* @access private
* @param string $type 驱动类型
*/
private static function init($type)
{
if (!isset(self::$handler[$type])) {
$class = '\\org\\transform\\driver\\' . ucwords($type);
self::$handler[$type] = new $class();
}
}
/**
* 编码内容
* @static
* @access public
* @param mixed $content 要编码的数据
* @param string $type 数据类型
* @param array $config XML配置参数JSON格式生成无此参数
* @return string 编码后的数据
*/
public static function encode($content, $type, array $config = [])
{
self::init($type);
return self::$handler[$type]->encode($content, $config);
}
/**
* 解码数据
* @param string $content 要解码的数据
* @param string $type 数据类型
* @param boolean $assoc 是否返回数组
* @param array $config XML配置参数JSON格式解码无此参数
* @return mixed 解码后的数据
*/
public static function decode($content, $type, $assoc = true, array $config = [])
{
self::init($type);
return self::$handler[$type]->decode($content, $assoc, $config);
}
// 调用驱动类的方法
// Transform::xmlEncode('abc')
// Transform::jsonDecode('abc', true);
public static function __callStatic($method, $params)
{
if (empty($params[0])) {
return '';
}
//获取类型
$type = substr($method, 0, strlen($method) - 6);
switch (strtolower(substr($method, -6))) {
case 'encode':
$config = empty($params[1]) ? [] : $params[1];
return self::encode($params[0], $type, $config);
case 'decode':
$assoc = empty($params[1]) ? true : $params[1];
$config = empty($params[2]) ? [] : $params[2];
return self::decode($params[0], $type, $assoc, $config);
default:
throw new Exception("call to undefined method {$method}");
}
}
}

466
extend/org/Upload.php Normal file
View File

@@ -0,0 +1,466 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org;
class Upload
{
/**
* 默认上传配置
* @var array
*/
private $config = [
// 允许上传的文件MiMe类型
'mimes' => [],
// 上传的文件大小限制 (0-不做限制)
'maxSize' => 0,
// 允许上传的文件后缀
'exts' => [],
// 自动子目录保存文件
'autoSub' => true,
// 子目录创建方式,[0]-函数名,[1]-参数,多个参数使用数组
'subName' => ['date', 'Y-m-d'],
//保存根路径
'rootPath' => './Uploads/',
// 保存路径
'savePath' => '',
// 上传文件命名规则,[0]-函数名,[1]-参数,多个参数使用数组
'saveName' => ['uniqid', ''],
// 文件保存后缀,空则使用原后缀
'saveExt' => '',
// 存在同名是否覆盖
'replace' => false,
// 是否生成hash编码
'hash' => true,
// 检测文件是否存在回调,如果存在返回文件信息数组
'callback' => false,
// 文件上传驱动e,
'driver' => '',
// 上传驱动配置
'driverConfig' => [],
];
/**
* 上传错误信息
* @var string
*/
private $error = ''; //上传错误信息
/**
* 上传驱动实例
* @var Object
*/
private $uploader;
/**
* 构造方法,用于构造上传实例
* @param array $config 配置
* @param string $driver 要使用的上传驱动 LOCAL-本地上传驱动FTP-FTP上传驱动
*/
public function __construct($config = [], $driver = '', $driverConfig = null)
{
/* 获取配置 */
$this->config = array_merge($this->config, $config);
/* 设置上传驱动 */
$this->setDriver($driver, $driverConfig);
/* 调整配置,把字符串配置参数转换为数组 */
if (!empty($this->config['mimes'])) {
if (is_string($this->mimes)) {
$this->config['mimes'] = explode(',', $this->mimes);
}
$this->config['mimes'] = array_map('strtolower', $this->mimes);
}
if (!empty($this->config['exts'])) {
if (is_string($this->exts)) {
$this->config['exts'] = explode(',', $this->exts);
}
$this->config['exts'] = array_map('strtolower', $this->exts);
}
}
/**
* 使用 $this->name 获取配置
* @param string $name 配置名称
* @return multitype 配置值
*/
public function __get($name)
{
return $this->config[$name];
}
public function __set($name, $value)
{
if (isset($this->config[$name])) {
$this->config[$name] = $value;
if ('driverConfig' == $name) {
//改变驱动配置后重置上传驱动
//注意:必须选改变驱动然后再改变驱动配置
$this->setDriver();
}
}
}
public function __isset($name)
{
return isset($this->config[$name]);
}
/**
* 获取最后一次上传错误信息
* @return string 错误信息
*/
public function getError()
{
return $this->error;
}
/**
* 上传单个文件
* @param array $file 文件数组
* @return array 上传成功后的文件信息
*/
public function uploadOne($file)
{
$info = $this->upload([$file]);
return $info ? $info[0] : $info;
}
/**
* 上传文件
* @param 文件信息数组 $files ,通常是 $_FILES数组
*/
public function upload($files = '')
{
if ('' === $files) {
$files = $_FILES;
}
if (empty($files)) {
$this->error = '没有上传的文件!';
return false;
}
/* 检测上传根目录 */
if (!$this->uploader->checkRootPath($this->rootPath)) {
$this->error = $this->uploader->getError();
return false;
}
/* 检查上传目录 */
if (!$this->uploader->checkSavePath($this->savePath)) {
$this->error = $this->uploader->getError();
return false;
}
/* 逐个检测并上传文件 */
$info = [];
if (function_exists('finfo_open')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
}
// 对上传文件数组信息处理
$files = $this->dealFiles($files);
foreach ($files as $key => $file) {
$file['name'] = strip_tags($file['name']);
if (!isset($file['key'])) {
$file['key'] = $key;
}
/* 通过扩展获取文件类型可解决FLASH上传$FILES数组返回文件类型错误的问题 */
if (isset($finfo)) {
$file['type'] = finfo_file($finfo, $file['tmp_name']);
}
/* 获取上传文件后缀,允许上传无后缀文件 */
$file['ext'] = pathinfo($file['name'], PATHINFO_EXTENSION);
/* 文件上传检测 */
if (!$this->check($file)) {
continue;
}
/* 获取文件hash */
if ($this->hash) {
$file['md5'] = md5_file($file['tmp_name']);
$file['sha1'] = sha1_file($file['tmp_name']);
}
/* 调用回调函数检测文件是否存在 */
if (is_callable($this->callback)) {
$data = call_user_func($this->callback, $file);
if ($data) {
if (file_exists('.' . $data['path'])) {
$info[$key] = $data;
continue;
} elseif ($this->removeTrash) {
call_user_func($this->removeTrash, $data); //删除垃圾据
}
}
}
/* 生成保存文件名 */
$savename = $this->getSaveName($file);
if (false == $savename) {
continue;
} else {
$file['savename'] = $savename;
}
/* 检测并创建子目录 */
$subpath = $this->getSubPath($file['name']);
if (false === $subpath) {
continue;
} else {
$file['savepath'] = $this->savePath . $subpath;
}
/* 对图像文件进行严格检测 */
$ext = strtolower($file['ext']);
if (in_array($ext, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf'])) {
$imginfo = getimagesize($file['tmp_name']);
if (empty($imginfo) || ('gif' == $ext && empty($imginfo['bits']))) {
$this->error = '非法图像文件!';
continue;
}
}
/* 保存文件 并记录保存成功的文件 */
if ($this->uploader->save($file, $this->replace)) {
unset($file['error'], $file['tmp_name']);
$info[$key] = $file;
} else {
$this->error = $this->uploader->getError();
}
}
if (isset($finfo)) {
finfo_close($finfo);
}
return empty($info) ? false : $info;
}
/**
* 转换上传文件数组变量为正确的方式
* @access private
* @param array $files 上传的文件变量
* @return array
*/
private function dealFiles($files)
{
$fileArray = [];
$n = 0;
foreach ($files as $key => $file) {
if (is_array($file['name'])) {
$keys = array_keys($file);
$count = count($file['name']);
for ($i = 0; $i < $count; $i++) {
$fileArray[$n]['key'] = $key;
foreach ($keys as $_key) {
$fileArray[$n][$_key] = $file[$_key][$i];
}
$n++;
}
} else {
$fileArray = $files;
break;
}
}
return $fileArray;
}
/**
* 设置上传驱动
* @param string $driver 驱动名称
* @param array $config 驱动配置
*/
private function setDriver($driver = null, $config = null)
{
$driver = $driver ?: $this->config['driver'];
$config = $config ?: $this->config['driverConfig'];
$class = strpos($driver, '\\') ? $driver : '\\org\\upload\\driver\\' . ucfirst(strtolower($driver));
$this->uploader = new $class($config);
}
/**
* 检查上传的文件
* @param array $file 文件信息
*/
private function check($file)
{
/* 文件上传失败,捕获错误代码 */
if ($file['error']) {
$this->error($file['error']);
return false;
}
/* 无效上传 */
if (empty($file['name'])) {
$this->error = '未知上传错误!';
}
/* 检查是否合法上传 */
if (!is_uploaded_file($file['tmp_name'])) {
$this->error = '非法上传文件!';
return false;
}
/* 检查文件大小 */
if (!$this->checkSize($file['size'])) {
$this->error = '上传文件大小不符!';
return false;
}
/* 检查文件Mime类型 */
//TODO:FLASH上传的文件获取到的mime类型都为application/octet-stream
if (!$this->checkMime($file['type'])) {
$this->error = '上传文件MIME类型不允许';
return false;
}
/* 检查文件后缀 */
if (!$this->checkExt($file['ext'])) {
$this->error = '上传文件后缀不允许';
return false;
}
/* 通过检测 */
return true;
}
/**
* 获取错误代码信息
* @param string $errorNo 错误号
*/
private function error($errorNo)
{
switch ($errorNo) {
case 1:
$this->error = '上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值!';
break;
case 2:
$this->error = '上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值!';
break;
case 3:
$this->error = '文件只有部分被上传!';
break;
case 4:
$this->error = '没有文件被上传!';
break;
case 6:
$this->error = '找不到临时文件夹!';
break;
case 7:
$this->error = '文件写入失败!';
break;
default:
$this->error = '未知上传错误!';
}
}
/**
* 检查文件大小是否合法
* @param integer $size 数据
*/
private function checkSize($size)
{
return !($size > $this->maxSize) || (0 == $this->maxSize);
}
/**
* 检查上传的文件MIME类型是否合法
* @param string $mime 数据
*/
private function checkMime($mime)
{
return empty($this->config['mimes']) ? true : in_array(strtolower($mime), $this->mimes);
}
/**
* 检查上传的文件后缀是否合法
* @param string $ext 后缀
*/
private function checkExt($ext)
{
return empty($this->config['exts']) ? true : in_array(strtolower($ext), $this->exts);
}
/**
* 根据上传文件命名规则取得保存文件名
* @param string $file 文件信息
*/
private function getSaveName($file)
{
$rule = $this->saveName;
if (empty($rule)) {
//保持文件名不变
/* 解决pathinfo中文文件名BUG */
$filename = substr(pathinfo("_{$file['name']}", PATHINFO_FILENAME), 1);
$savename = $filename;
} else {
$savename = $this->getName($rule, $file['name']);
if (empty($savename)) {
$this->error = '文件命名规则错误!';
return false;
}
}
/* 文件保存后缀,支持强制更改文件后缀 */
$ext = empty($this->config['saveExt']) ? $file['ext'] : $this->saveExt;
return $savename . '.' . $ext;
}
/**
* 获取子目录的名称
* @param array $file 上传的文件信息
*/
private function getSubPath($filename)
{
$subpath = '';
$rule = $this->subName;
if ($this->autoSub && !empty($rule)) {
$subpath = $this->getName($rule, $filename) . '/';
if (!empty($subpath) && !$this->uploader->mkdir($this->savePath . $subpath)) {
$this->error = $this->uploader->getError();
return false;
}
}
return $subpath;
}
/**
* 根据指定的规则获取文件或目录名称
* @param array $rule 规则
* @param string $filename 原文件名
* @return string 文件或目录名称
*/
private function getName($rule, $filename)
{
$name = '';
if (is_array($rule)) {
//数组规则
$func = $rule[0];
$param = (array) $rule[1];
foreach ($param as &$value) {
$value = str_replace('__FILE__', $filename, $value);
}
$name = call_user_func_array($func, $param);
} elseif (is_string($rule)) {
//字符串规则
if (function_exists($rule)) {
$name = call_user_func($rule);
} else {
$name = $rule;
}
}
return $name;
}
}

305
extend/org/Verify.php Normal file
View File

@@ -0,0 +1,305 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org;
class Verify
{
protected $config = [
'seKey' => 'ThinkPHP.CN', // 验证码加密密钥
'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY', // 验证码字符集合
'expire' => 1800, // 验证码过期时间s
'useZh' => false, // 使用中文验证码
'zhSet' => '们以我到他会作时要动国产的一是工就年阶义发成部民可出能方进在了不和有大这主中人上为来分生对于学下级地个用同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所二起政三好十战无农使性前等反体合斗路图把结第里正新开论之物从当两些还天资事队批点育重其思与间内去因件日利相由压员气业代全组数果期导平各基或月毛然如应形想制心样干都向变关问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流入接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极土少已根共直团统式转别造切九你取西持总料连任志观调七么山程百报更见必真保热委手改管处己将修支识病象几先老光专什六型具示复安带每东增则完风回南广劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单色坚据速防史拉世设达尔场织历花受求传口断况采精金界品判参层止边清至万确究书术状厂须离再目海交权且儿青才证低越际八试规斯近注办布门铁需走议县兵固除般引齿千胜细影济白格效置推空配刀叶率述今选养德话查差半敌始片施响收华觉备名红续均药标记难存测士身紧液派准斤角降维板许破述技消底床田势端感往神便贺村构照容非搞亚磨族火段算适讲按值美态黄易彪服早班麦削信排台声该击素张密害侯草何树肥继右属市严径螺检左页抗苏显苦英快称坏移约巴材省黑武培著河帝仅针怎植京助升王眼她抓含苗副杂普谈围食射源例致酸旧却充足短划剂宣环落首尺波承粉践府鱼随考刻靠够满夫失包住促枝局菌杆周护岩师举曲春元超负砂封换太模贫减阳扬江析亩木言球朝医校古呢稻宋听唯输滑站另卫字鼓刚写刘微略范供阿块某功套友限项余倒卷创律雨让骨远帮初皮播优占死毒圈伟季训控激找叫云互跟裂粮粒母练塞钢顶策双留误础吸阻故寸盾晚丝女散焊功株亲院冷彻弹错散商视艺灭版烈零室轻血倍缺厘泵察绝富城冲喷壤简否柱李望盘磁雄似困巩益洲脱投送奴侧润盖挥距触星松送获兴独官混纪依未突架宽冬章湿偏纹吃执阀矿寨责熟稳夺硬价努翻奇甲预职评读背协损棉侵灰虽矛厚罗泥辟告卵箱掌氧恩爱停曾溶营终纲孟钱待尽俄缩沙退陈讨奋械载胞幼哪剥迫旋征槽倒握担仍呀鲜吧卡粗介钻逐弱脚怕盐末阴丰雾冠丙街莱贝辐肠付吉渗瑞惊顿挤秒悬姆烂森糖圣凹陶词迟蚕亿矩康遵牧遭幅园腔订香肉弟屋敏恢忘编印蜂急拿扩伤飞露核缘游振操央伍域甚迅辉异序免纸夜乡久隶缸夹念兰映沟乙吗儒杀汽磷艰晶插埃燃欢铁补咱芽永瓦倾阵碳演威附牙芽永瓦斜灌欧献顺猪洋腐请透司危括脉宜笑若尾束壮暴企菜穗楚汉愈绿拖牛份染既秋遍锻玉夏疗尖殖井费州访吹荣铜沿替滚客召旱悟刺脑措贯藏敢令隙炉壳硫煤迎铸粘探临薄旬善福纵择礼愿伏残雷延烟句纯渐耕跑泽慢栽鲁赤繁境潮横掉锥希池败船假亮谓托伙哲怀割摆贡呈劲财仪沉炼麻罪祖息车穿货销齐鼠抽画饲龙库守筑房歌寒喜哥洗蚀废纳腹乎录镜妇恶脂庄擦险赞钟摇典柄辩竹谷卖乱虚桥奥伯赶垂途额壁网截野遗静谋弄挂课镇妄盛耐援扎虑键归符庆聚绕摩忙舞遇索顾胶羊湖钉仁音迹碎伸灯避泛亡答勇频皇柳哈揭甘诺概宪浓岛袭谁洪谢炮浇斑讯懂灵蛋闭孩释乳巨徒私银伊景坦累匀霉杜乐勒隔弯绩招绍胡呼痛峰零柴簧午跳居尚丁秦稍追梁折耗碱殊岗挖氏刃剧堆赫荷胸衡勤膜篇登驻案刊秧缓凸役剪川雪链渔啦脸户洛孢勃盟买杨宗焦赛旗滤硅炭股坐蒸凝竟陷枪黎救冒暗洞犯筒您宋弧爆谬涂味津臂障褐陆啊健尊豆拔莫抵桑坡缝警挑污冰柬嘴啥饭塑寄赵喊垫丹渡耳刨虎笔稀昆浪萨茶滴浅拥穴覆伦娘吨浸袖珠雌妈紫戏塔锤震岁貌洁剖牢锋疑霸闪埔猛诉刷狠忽灾闹乔唐漏闻沈熔氯荒茎男凡抢像浆旁玻亦忠唱蒙予纷捕锁尤乘乌智淡允叛畜俘摸锈扫毕璃宝芯爷鉴秘净蒋钙肩腾枯抛轨堂拌爸循诱祝励肯酒绳穷塘燥泡袋朗喂铝软渠颗惯贸粪综墙趋彼届墨碍启逆卸航衣孙龄岭骗休借', // 中文验证码字符串
'useImgBg' => false, // 使用背景图片
'fontSize' => 25, // 验证码字体大小(px)
'useCurve' => true, // 是否画混淆曲线
'useNoise' => true, // 是否添加杂点
'imageH' => 0, // 验证码图片高度
'imageW' => 0, // 验证码图片宽度
'length' => 5, // 验证码位数
'fontttf' => '', // 验证码字体,不设置随机获取
'bg' => [243, 251, 254], // 背景颜色
'reset' => true, // 验证成功后是否重置
];
private $_image = null; // 验证码图片实例
private $_color = null; // 验证码字体颜色
/**
* 架构方法 设置参数
* @access public
* @param array $config 配置参数
*/
public function __construct($config = [])
{
$this->config = array_merge($this->config, $config);
}
/**
* 使用 $this->name 获取配置
* @access public
* @param string $name 配置名称
* @return multitype 配置值
*/
public function __get($name)
{
return $this->config[$name];
}
/**
* 设置验证码配置
* @access public
* @param string $name 配置名称
* @param string $value 配置值
* @return void
*/
public function __set($name, $value)
{
if (isset($this->config[$name])) {
$this->config[$name] = $value;
}
}
/**
* 检查配置
* @access public
* @param string $name 配置名称
* @return bool
*/
public function __isset($name)
{
return isset($this->config[$name]);
}
/**
* 验证验证码是否正确
* @access public
* @param string $code 用户验证码
* @param string $id 验证码标识
* @return bool 用户验证码是否正确
*/
public function check($code, $id = '')
{
$key = $this->authcode($this->seKey) . $id;
// 验证码不能为空
$secode = session($key);
if (empty($code) || empty($secode)) {
return false;
}
// session 过期
if (time() - $secode['verify_time'] > $this->expire) {
session($key, null);
return false;
}
if ($this->authcode(strtoupper($code)) == $secode['verify_code']) {
$this->reset && session($key, null);
return true;
}
return false;
}
/**
* 输出验证码并把验证码的值保存的session中
* 验证码保存到session的格式为 array('verify_code' => '验证码值', 'verify_time' => '验证码创建时间');
* @access public
* @param string $id 要生成验证码的标识
* @return void
*/
public function entry($id = '')
{
// 图片宽(px)
$this->imageW || $this->imageW = $this->length * $this->fontSize * 1.5 + $this->length * $this->fontSize / 2;
// 图片高(px)
$this->imageH || $this->imageH = $this->fontSize * 2.5;
// 建立一幅 $this->imageW x $this->imageH 的图像
$this->_image = imagecreate($this->imageW, $this->imageH);
// 设置背景
imagecolorallocate($this->_image, $this->bg[0], $this->bg[1], $this->bg[2]);
// 验证码字体随机颜色
$this->_color = imagecolorallocate($this->_image, mt_rand(1, 150), mt_rand(1, 150), mt_rand(1, 150));
// 验证码使用随机字体
$ttfPath = dirname(__FILE__) . '/verify/' . ($this->useZh ? 'zhttfs' : 'ttfs') . '/';
if (empty($this->fontttf)) {
$dir = dir($ttfPath);
$ttfs = [];
while (false !== ($file = $dir->read())) {
if ('.' != $file[0] && substr($file, -4) == '.ttf') {
$ttfs[] = $file;
}
}
$dir->close();
$this->fontttf = $ttfs[array_rand($ttfs)];
}
$this->fontttf = $ttfPath . $this->fontttf;
if ($this->useImgBg) {
$this->_background();
}
if ($this->useNoise) {
// 绘杂点
$this->_writeNoise();
}
if ($this->useCurve) {
// 绘干扰线
$this->_writeCurve();
}
// 绘验证码
$code = []; // 验证码
$codeNX = 0; // 验证码第N个字符的左边距
if ($this->useZh) {
// 中文验证码
for ($i = 0; $i < $this->length; $i++) {
$code[$i] = iconv_substr($this->zhSet, floor(mt_rand(0, mb_strlen($this->zhSet, 'utf-8') - 1)), 1, 'utf-8');
imagettftext($this->_image, $this->fontSize, mt_rand(-40, 40), $this->fontSize * ($i + 1) * 1.5, $this->fontSize + mt_rand(10, 20), $this->_color, $this->fontttf, $code[$i]);
}
} else {
for ($i = 0; $i < $this->length; $i++) {
$code[$i] = $this->codeSet[mt_rand(0, strlen($this->codeSet) - 1)];
$codeNX += mt_rand($this->fontSize * 1.2, $this->fontSize * 1.6);
imagettftext($this->_image, $this->fontSize, mt_rand(-40, 40), $codeNX, $this->fontSize * 1.6, $this->_color, $this->fontttf, $code[$i]);
}
}
// 保存验证码
$key = $this->authcode($this->seKey);
$code = $this->authcode(strtoupper(implode('', $code)));
$secode = [];
$secode['verify_code'] = $code; // 把校验码保存到session
$secode['verify_time'] = time(); // 验证码创建时间
session($key . $id, $secode);
header('Cache-Control: private, max-age=0, no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header("content-type: image/png");
// 输出图像
imagepng($this->_image);
imagedestroy($this->_image);
}
/**
* 画一条由两条连在一起构成的随机正弦函数曲线作干扰线(你可以改成更帅的曲线函数)
*
* 高中的数学公式咋都忘了涅,写出来
* 正弦型函数解析式y=Asin(ωx+φ)+b
* 各常数值对函数图像的影响:
* A决定峰值即纵向拉伸压缩的倍数
* b表示波形在Y轴的位置关系或纵向移动距离上加下减
* φ决定波形与X轴位置关系或横向移动距离左加右减
* ω决定周期最小正周期T=2π/∣ω∣)
*
*/
private function _writeCurve()
{
$px = $py = 0;
// 曲线前部分
$A = mt_rand(1, $this->imageH / 2); // 振幅
$b = mt_rand(-$this->imageH / 4, $this->imageH / 4); // Y轴方向偏移量
$f = mt_rand(-$this->imageH / 4, $this->imageH / 4); // X轴方向偏移量
$T = mt_rand($this->imageH, $this->imageW * 2); // 周期
$w = (2 * M_PI) / $T;
$px1 = 0; // 曲线横坐标起始位置
$px2 = mt_rand($this->imageW / 2, $this->imageW * 0.8); // 曲线横坐标结束位置
for ($px = $px1; $px <= $px2; $px = $px + 1) {
if (0 != $w) {
$py = $A * sin($w * $px + $f) + $b + $this->imageH / 2; // y = Asin(ωx+φ) + b
$i = (int) ($this->fontSize / 5);
while ($i > 0) {
imagesetpixel($this->_image, $px + $i, $py + $i, $this->_color); // 这里(while)循环画像素点比imagettftext和imagestring用字体大小一次画出不用这while循环性能要好很多
$i--;
}
}
}
// 曲线后部分
$A = mt_rand(1, $this->imageH / 2); // 振幅
$f = mt_rand(-$this->imageH / 4, $this->imageH / 4); // X轴方向偏移量
$T = mt_rand($this->imageH, $this->imageW * 2); // 周期
$w = (2 * M_PI) / $T;
$b = $py - $A * sin($w * $px + $f) - $this->imageH / 2;
$px1 = $px2;
$px2 = $this->imageW;
for ($px = $px1; $px <= $px2; $px = $px + 1) {
if (0 != $w) {
$py = $A * sin($w * $px + $f) + $b + $this->imageH / 2; // y = Asin(ωx+φ) + b
$i = (int) ($this->fontSize / 5);
while ($i > 0) {
imagesetpixel($this->_image, $px + $i, $py + $i, $this->_color);
$i--;
}
}
}
}
/**
* 画杂点
* 往图片上写不同颜色的字母或数字
*/
private function _writeNoise()
{
$codeSet = '2345678abcdefhijkmnpqrstuvwxyz';
for ($i = 0; $i < 10; $i++) {
//杂点颜色
$noiseColor = imagecolorallocate($this->_image, mt_rand(150, 225), mt_rand(150, 225), mt_rand(150, 225));
for ($j = 0; $j < 5; $j++) {
// 绘杂点
imagestring($this->_image, 5, mt_rand(-10, $this->imageW), mt_rand(-10, $this->imageH), $codeSet[mt_rand(0, 29)], $noiseColor);
}
}
}
/**
* 绘制背景图片
* 注:如果验证码输出图片比较大,将占用比较多的系统资源
*/
private function _background()
{
$path = dirname(__FILE__) . '/verify/bgs/';
$dir = dir($path);
$bgs = [];
while (false !== ($file = $dir->read())) {
if ('.' != $file[0] && substr($file, -4) == '.jpg') {
$bgs[] = $path . $file;
}
}
$dir->close();
$gb = $bgs[array_rand($bgs)];
list($width, $height) = @getimagesize($gb);
// Resample
$bgImage = @imagecreatefromjpeg($gb);
@imagecopyresampled($this->_image, $bgImage, 0, 0, 0, 0, $this->imageW, $this->imageH, $width, $height);
@imagedestroy($bgImage);
}
/* 加密验证码 */
private function authcode($str)
{
$key = substr(md5($this->seKey), 5, 8);
$str = substr(md5($str), 8, 10);
return md5($key . $str);
}
}

View File

@@ -0,0 +1,639 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\image\driver;
use Exception;
class Gd
{
/**
* 图像资源对象
*
* @var resource
*/
private $im;
/**
* @var $git org\image\driver\Gif
*/
private $gif;
/**
* 图像信息,包括 width, height, type, mime, size
*
* @var array
*/
private $info;
/**
* 构造方法,可用于打开一张图像
*
* @param string $imgname 图像路径
*/
public function __construct($imgname = null)
{
$imgname && $this->open($imgname);
}
/**
* 打开一张图像
*
* @param string $imgname 图像路径
*
* @return $this
* @throws \Exception
*/
public function open($imgname)
{
//检测图像文件
if (!is_file($imgname)) {
throw new \Exception('不存在的图像文件');
}
//获取图像信息
$info = getimagesize($imgname);
//检测图像合法性
if (false === $info || (IMAGETYPE_GIF === $info[2] && empty($info['bits']))) {
throw new Exception('非法图像文件');
}
//设置图像信息
$this->info = [
'width' => $info[0],
'height' => $info[1],
'type' => image_type_to_extension($info[2], false),
'mime' => $info['mime'],
];
//销毁已存在的图像
empty($this->im) || imagedestroy($this->im);
//打开图像
if ('gif' == $this->info['type']) {
$class = '\\Think\\Image\\Driver\\Gif';
$this->gif = new $class($imgname);
$this->im = imagecreatefromstring($this->gif->image());
} else {
$fun = "imagecreatefrom{$this->info['type']}";
$this->im = $fun($imgname);
}
return $this;
}
/**
* 保存图像
*
* @param string $imgname 图像保存名称
* @param string $type 图像类型
* @param boolean $interlace 是否对JPEG类型图像设置隔行扫描
*
* @return $this
* @throws Exception
*/
public function save($imgname, $type = null, $interlace = true)
{
if (empty($this->im)) {
throw new Exception('没有可以被保存的图像资源');
}
//自动获取图像类型
if (is_null($type)) {
$type = $this->info['type'];
} else {
$type = strtolower($type);
}
//JPEG图像设置隔行扫描
if ('jpeg' == $type || 'jpg' == $type) {
$type = 'jpeg';
imageinterlace($this->im, $interlace);
}
//保存图像
if ('gif' == $type && !empty($this->gif)) {
$this->gif->save($imgname);
} else {
$fun = "image{$type}";
$fun($this->im, $imgname);
}
return $this;
}
/**
* 返回图像宽度
* @return int 图像宽度
* @throws Exception
*/
public function width()
{
if (empty($this->im)) {
throw new Exception('没有指定图像资源');
}
return $this->info['width'];
}
/**
* 返回图像高度
* @return int 图像高度
* @throws Exception
*/
public function height()
{
if (empty($this->im)) {
throw new Exception('没有指定图像资源');
}
return $this->info['height'];
}
/**
* 返回图像类型
* @return string 图像类型
* @throws Exception
*/
public function type()
{
if (empty($this->im)) {
throw new Exception('没有指定图像资源');
}
return $this->info['type'];
}
/**
* 返回图像MIME类型
* @return string 图像MIME类型
* @throws Exception
*/
public function mime()
{
if (empty($this->im)) {
throw new Exception('没有指定图像资源');
}
return $this->info['mime'];
}
/**
* 返回图像尺寸数组 0 - 图像宽度1 - 图像高度
* @return array 图像尺寸
* @throws Exception
*/
public function size()
{
if (empty($this->im)) {
throw new Exception('没有指定图像资源');
}
return [$this->info['width'], $this->info['height']];
}
/**
* 裁剪图像
*
* @param integer $w 裁剪区域宽度
* @param integer $h 裁剪区域高度
* @param integer $x 裁剪区域x坐标
* @param integer $y 裁剪区域y坐标
* @param integer $width 图像保存宽度
* @param integer $height 图像保存高度
*
* @return $this
* @throws Exception
*/
public function crop($w, $h, $x = 0, $y = 0, $width = null, $height = null)
{
if (empty($this->im)) {
throw new Exception('没有可以被裁剪的图像资源');
}
//设置保存尺寸
empty($width) && $width = $w;
empty($height) && $height = $h;
do {
//创建新图像
$img = imagecreatetruecolor($width, $height);
// 调整默认颜色
$color = imagecolorallocate($img, 255, 255, 255);
imagefill($img, 0, 0, $color);
//裁剪
imagecopyresampled($img, $this->im, 0, 0, $x, $y, $width, $height, $w, $h);
imagedestroy($this->im); //销毁原图
//设置新图像
$this->im = $img;
} while (!empty($this->gif) && $this->gifNext());
$this->info['width'] = $width;
$this->info['height'] = $height;
return $this;
}
/**
* 生成缩略图
*
* @param integer $width 缩略图最大宽度
* @param integer $height 缩略图最大高度
* @param int $type 缩略图裁剪类型
*
* @return $this
* @throws Exception
*/
public function thumb($width, $height, $type = THINKIMAGE_THUMB_SCALING)
{
if (empty($this->im)) {
throw new Exception('没有可以被缩略的图像资源');
}
//原图宽度和高度
$w = $this->info['width'];
$h = $this->info['height'];
/* 计算缩略图生成的必要参数 */
switch ($type) {
/* 等比例缩放 */
case THINKIMAGE_THUMB_SCALING:
//原图尺寸小于缩略图尺寸则不进行缩略
if ($w < $width && $h < $height) {
return false;
}
//计算缩放比例
$scale = min($width / $w, $height / $h);
//设置缩略图的坐标及宽度和高度
$x = $y = 0;
$width = $w * $scale;
$height = $h * $scale;
break;
/* 居中裁剪 */
case THINKIMAGE_THUMB_CENTER:
//计算缩放比例
$scale = max($width / $w, $height / $h);
//设置缩略图的坐标及宽度和高度
$w = $width / $scale;
$h = $height / $scale;
$x = ($this->info['width'] - $w) / 2;
$y = ($this->info['height'] - $h) / 2;
break;
/* 左上角裁剪 */
case THINKIMAGE_THUMB_NORTHWEST:
//计算缩放比例
$scale = max($width / $w, $height / $h);
//设置缩略图的坐标及宽度和高度
$x = $y = 0;
$w = $width / $scale;
$h = $height / $scale;
break;
/* 右下角裁剪 */
case THINKIMAGE_THUMB_SOUTHEAST:
//计算缩放比例
$scale = max($width / $w, $height / $h);
//设置缩略图的坐标及宽度和高度
$w = $width / $scale;
$h = $height / $scale;
$x = $this->info['width'] - $w;
$y = $this->info['height'] - $h;
break;
/* 填充 */
case THINKIMAGE_THUMB_FILLED:
//计算缩放比例
if ($w < $width && $h < $height) {
$scale = 1;
} else {
$scale = min($width / $w, $height / $h);
}
//设置缩略图的坐标及宽度和高度
$neww = $w * $scale;
$newh = $h * $scale;
$x = $this->info['width'] - $w;
$y = $this->info['height'] - $h;
$posx = ($width - $w * $scale) / 2;
$posy = ($height - $h * $scale) / 2;
do {
//创建新图像
$img = imagecreatetruecolor($width, $height);
// 调整默认颜色
$color = imagecolorallocate($img, 255, 255, 255);
imagefill($img, 0, 0, $color);
//裁剪
imagecopyresampled($img, $this->im, $posx, $posy, $x, $y, $neww, $newh, $w, $h);
imagedestroy($this->im); //销毁原图
$this->im = $img;
} while (!empty($this->gif) && $this->gifNext());
$this->info['width'] = $width;
$this->info['height'] = $height;
return $this;
/* 固定 */
case THINKIMAGE_THUMB_FIXED:
$x = $y = 0;
break;
default:
throw new Exception('不支持的缩略图裁剪类型');
}
/* 裁剪图像 */
$this->crop($w, $h, $x, $y, $width, $height);
return $this;
}
/**
* 添加水印
*
* @param string $source 水印图片路径
* @param int $locate 水印位置
*
* @return $this
* @throws Exception
* @internal param int $alpha 水印透明度
*/
public function water($source, $locate = THINKIMAGE_WATER_SOUTHEAST)
{
//资源检测
if (empty($this->im)) {
throw new Exception('没有可以被添加水印的图像资源');
}
if (!is_file($source)) {
throw new Exception('水印图像不存在');
}
//获取水印图像信息
$info = getimagesize($source);
if (false === $info || (IMAGETYPE_GIF === $info[2] && empty($info['bits']))) {
throw new Exception('非法水印文件');
}
//创建水印图像资源
$fun = 'imagecreatefrom' . image_type_to_extension($info[2], false);
$water = $fun($source);
//设定水印图像的混色模式
imagealphablending($water, true);
/* 设定水印位置 */
switch ($locate) {
/* 右下角水印 */
case THINKIMAGE_WATER_SOUTHEAST:
$x = $this->info['width'] - $info[0];
$y = $this->info['height'] - $info[1];
break;
/* 左下角水印 */
case THINKIMAGE_WATER_SOUTHWEST:
$x = 0;
$y = $this->info['height'] - $info[1];
break;
/* 左上角水印 */
case THINKIMAGE_WATER_NORTHWEST:
$x = $y = 0;
break;
/* 右上角水印 */
case THINKIMAGE_WATER_NORTHEAST:
$x = $this->info['width'] - $info[0];
$y = 0;
break;
/* 居中水印 */
case THINKIMAGE_WATER_CENTER:
$x = ($this->info['width'] - $info[0]) / 2;
$y = ($this->info['height'] - $info[1]) / 2;
break;
/* 下居中水印 */
case THINKIMAGE_WATER_SOUTH:
$x = ($this->info['width'] - $info[0]) / 2;
$y = $this->info['height'] - $info[1];
break;
/* 右居中水印 */
case THINKIMAGE_WATER_EAST:
$x = $this->info['width'] - $info[0];
$y = ($this->info['height'] - $info[1]) / 2;
break;
/* 上居中水印 */
case THINKIMAGE_WATER_NORTH:
$x = ($this->info['width'] - $info[0]) / 2;
$y = 0;
break;
/* 左居中水印 */
case THINKIMAGE_WATER_WEST:
$x = 0;
$y = ($this->info['height'] - $info[1]) / 2;
break;
default:
/* 自定义水印坐标 */
if (is_array($locate)) {
list($x, $y) = $locate;
} else {
throw new Exception('不支持的水印位置类型');
}
}
do {
//添加水印
$src = imagecreatetruecolor($info[0], $info[1]);
// 调整默认颜色
$color = imagecolorallocate($src, 255, 255, 255);
imagefill($src, 0, 0, $color);
imagecopy($src, $this->im, 0, 0, $x, $y, $info[0], $info[1]);
imagecopy($src, $water, 0, 0, 0, 0, $info[0], $info[1]);
imagecopymerge($this->im, $src, $x, $y, 0, 0, $info[0], $info[1], 100);
//销毁零时图片资源
imagedestroy($src);
} while (!empty($this->gif) && $this->gifNext());
//销毁水印资源
imagedestroy($water);
return $this;
}
/**
* 图像添加文字
*
* @param string $text 添加的文字
* @param string $font 字体路径
* @param integer $size 字号
* @param string $color 文字颜色
* @param int $locate 文字写入位置
* @param integer $offset 文字相对当前位置的偏移量
* @param integer $angle 文字倾斜角度
*
* @return $this
* @throws Exception
*/
public function text($text, $font, $size, $color = '#00000000',
$locate = THINKIMAGE_WATER_SOUTHEAST, $offset = 0, $angle = 0) {
//资源检测
if (empty($this->im)) {
throw new Exception('没有可以被写入文字的图像资源');
}
if (!is_file($font)) {
throw new Exception("不存在的字体文件:{$font}");
}
//获取文字信息
$info = imagettfbbox($size, $angle, $font, $text);
$minx = min($info[0], $info[2], $info[4], $info[6]);
$maxx = max($info[0], $info[2], $info[4], $info[6]);
$miny = min($info[1], $info[3], $info[5], $info[7]);
$maxy = max($info[1], $info[3], $info[5], $info[7]);
/* 计算文字初始坐标和尺寸 */
$x = $minx;
$y = abs($miny);
$w = $maxx - $minx;
$h = $maxy - $miny;
/* 设定文字位置 */
switch ($locate) {
/* 右下角文字 */
case THINKIMAGE_WATER_SOUTHEAST:
$x += $this->info['width'] - $w;
$y += $this->info['height'] - $h;
break;
/* 左下角文字 */
case THINKIMAGE_WATER_SOUTHWEST:
$y += $this->info['height'] - $h;
break;
/* 左上角文字 */
case THINKIMAGE_WATER_NORTHWEST:
// 起始坐标即为左上角坐标,无需调整
break;
/* 右上角文字 */
case THINKIMAGE_WATER_NORTHEAST:
$x += $this->info['width'] - $w;
break;
/* 居中文字 */
case THINKIMAGE_WATER_CENTER:
$x += ($this->info['width'] - $w) / 2;
$y += ($this->info['height'] - $h) / 2;
break;
/* 下居中文字 */
case THINKIMAGE_WATER_SOUTH:
$x += ($this->info['width'] - $w) / 2;
$y += $this->info['height'] - $h;
break;
/* 右居中文字 */
case THINKIMAGE_WATER_EAST:
$x += $this->info['width'] - $w;
$y += ($this->info['height'] - $h) / 2;
break;
/* 上居中文字 */
case THINKIMAGE_WATER_NORTH:
$x += ($this->info['width'] - $w) / 2;
break;
/* 左居中文字 */
case THINKIMAGE_WATER_WEST:
$y += ($this->info['height'] - $h) / 2;
break;
default:
/* 自定义文字坐标 */
if (is_array($locate)) {
list($posx, $posy) = $locate;
$x += $posx;
$y += $posy;
} else {
throw new Exception('不支持的文字位置类型');
}
}
/* 设置偏移量 */
if (is_array($offset)) {
$offset = array_map('intval', $offset);
list($ox, $oy) = $offset;
} else {
$offset = intval($offset);
$ox = $oy = $offset;
}
/* 设置颜色 */
if (is_string($color) && 0 === strpos($color, '#')) {
$color = str_split(substr($color, 1), 2);
$color = array_map('hexdec', $color);
if (empty($color[3]) || $color[3] > 127) {
$color[3] = 0;
}
} elseif (!is_array($color)) {
throw new Exception('错误的颜色值');
}
do {
/* 写入文字 */
$col = imagecolorallocatealpha($this->im, $color[0], $color[1], $color[2], $color[3]);
imagettftext($this->im, $size, $angle, $x + $ox, $y + $oy, $col, $font, $text);
} while (!empty($this->gif) && $this->gifNext());
return $this;
}
/* 切换到GIF的下一帧并保存当前帧内部使用 */
private function gifNext()
{
ob_start();
ob_implicit_flush(0);
imagegif($this->im);
$img = ob_get_clean();
$this->gif->image($img);
$next = $this->gif->nextImage();
if ($next) {
$this->im = imagecreatefromstring($next);
return $next;
} else {
$this->im = imagecreatefromstring($this->gif->image());
return false;
}
}
/**
* 析构方法,用于销毁图像资源
*/
public function __destruct()
{
empty($this->im) || imagedestroy($this->im);
}
}

View File

@@ -0,0 +1,584 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\image\driver;
class Gif
{
/**
* GIF帧列表
*
* @var array
*/
private $frames = [];
/**
* 每帧等待时间列表
*
* @var array
*/
private $delays = [];
/**
* 构造方法用于解码GIF图片
*
* @param string $src GIF图片数据
* @param string $mod 图片数据类型
*/
public function __construct($src = null, $mod = 'url')
{
if (!is_null($src)) {
if ('url' == $mod && is_file($src)) {
$src = file_get_contents($src);
}
/* 解码GIF图片 */
try {
$de = new GIFDecoder($src);
$this->frames = $de->GIFGetFrames();
$this->delays = $de->GIFGetDelays();
} catch (\Exception $e) {
throw new \Exception("解码GIF图片出错");
}
}
}
/**
* 设置或获取当前帧的数据
*
* @param string $stream 二进制数据流
* @return mixed 获取到的数据
*/
public function image($stream = null)
{
if (is_null($stream)) {
$current = current($this->frames);
return false === $current ? reset($this->frames) : $current;
}
$this->frames[key($this->frames)] = $stream;
}
/**
* 将当前帧移动到下一帧
*
* @return string 当前帧数据
*/
public function nextImage()
{
return next($this->frames);
}
/**
* 编码并保存当前GIF图片
*
* @param string $gifname 图片名称
*/
public function save($gifname)
{
$gif = new GIFEncoder($this->frames, $this->delays, 0, 2, 0, 0, 0, 'bin');
file_put_contents($gifname, $gif->GetAnimation());
}
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFEncoder Version 2.0 by László Zsidi, http://gifs.hu
::
:: This class is a rewritten 'GifMerge.class.php' version.
::
:: Modification:
:: - Simplified and easy code,
:: - Ultra fast encoding,
:: - Built-in errors,
:: - Stable working
::
::
:: Updated at 2007. 02. 13. '00.05.AM'
::
::
::
:: Try on-line GIFBuilder Form demo based on GIFEncoder.
::
:: http://gifs.hu/phpclasses/demos/GifBuilder/
::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*/
class GIFEncode
{
public $GIF = "GIF89a"; /* GIF header 6 bytes */
public $VER = "GIFEncoder V2.05"; /* Encoder version */
public $BUF = [];
public $LOP = 0;
public $DIS = 2;
public $COL = -1;
public $IMG = -1;
public $ERR = [
'ERR00' => "Does not supported function for only one image!",
'ERR01' => "Source is not a GIF image!",
'ERR02' => "Unintelligible flag ",
'ERR03' => "Does not make animation from animated GIF source",
];
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFEncoder...
::
*/
public function GIFEncoder(
$GIF_src, $GIF_dly, $GIF_lop, $GIF_dis,
$GIF_red, $GIF_grn, $GIF_blu, $GIF_mod
) {
if (!is_array($GIF_src)) {
printf("%s: %s", $this->VER, $this->ERR['ERR00']);
exit(0);
}
$this->LOP = ($GIF_lop > -1) ? $GIF_lop : 0;
$this->DIS = ($GIF_dis > -1) ? (($GIF_dis < 3) ? $GIF_dis : 3) : 2;
$this->COL = ($GIF_red > -1 && $GIF_grn > -1 && $GIF_blu > -1) ?
($GIF_red | ($GIF_grn << 8) | ($GIF_blu << 16)) : -1;
for ($i = 0; $i < count($GIF_src); $i++) {
if (strToLower($GIF_mod) == "url") {
$this->BUF[] = fread(fopen($GIF_src[$i], "rb"), filesize($GIF_src[$i]));
} else if (strToLower($GIF_mod) == "bin") {
$this->BUF[] = $GIF_src[$i];
} else {
printf("%s: %s ( %s )!", $this->VER, $this->ERR['ERR02'], $GIF_mod);
exit(0);
}
if (substr($this->BUF[$i], 0, 6) != "GIF87a" && substr($this->BUF[$i], 0, 6) != "GIF89a") {
printf("%s: %d %s", $this->VER, $i, $this->ERR['ERR01']);
exit(0);
}
for ($j = (13 + 3 * (2 << (ord($this->BUF[$i]{10}) & 0x07))), $k = true; $k; $j++) {
switch ($this->BUF[$i]{ $j}) {
case "!":
if ((substr($this->BUF[$i], ($j + 3), 8)) == "NETSCAPE") {
printf("%s: %s ( %s source )!", $this->VER, $this->ERR['ERR03'], ($i + 1));
exit(0);
}
break;
case ";":
$k = false;
break;
}
}
}
self::GIFAddHeader();
for ($i = 0; $i < count($this->BUF); $i++) {
self::GIFAddFrames($i, $GIF_dly[$i]);
}
self::GIFAddFooter();
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFAddHeader...
::
*/
public function GIFAddHeader()
{
if (ord($this->BUF[0]{10}) & 0x80) {
$cmap = 3 * (2 << (ord($this->BUF[0]{10}) & 0x07));
$this->GIF .= substr($this->BUF[0], 6, 7);
$this->GIF .= substr($this->BUF[0], 13, $cmap);
$this->GIF .= "!\377\13NETSCAPE2.0\3\1" . self::GIFWord($this->LOP) . "\0";
}
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFAddFrames...
::
*/
public function GIFAddFrames($i, $d)
{
$Locals_img = '';
$Locals_str = 13 + 3 * (2 << (ord($this->BUF[$i]{10}) & 0x07));
$Locals_end = strlen($this->BUF[$i]) - $Locals_str - 1;
$Locals_tmp = substr($this->BUF[$i], $Locals_str, $Locals_end);
$Global_len = 2 << (ord($this->BUF[0]{10}) & 0x07);
$Locals_len = 2 << (ord($this->BUF[$i]{10}) & 0x07);
$Global_rgb = substr($this->BUF[0], 13,
3 * (2 << (ord($this->BUF[0]{10}) & 0x07)));
$Locals_rgb = substr($this->BUF[$i], 13,
3 * (2 << (ord($this->BUF[$i]{10}) & 0x07)));
$Locals_ext = "!\xF9\x04" . chr(($this->DIS << 2) + 0) .
chr(($d >> 0) & 0xFF) . chr(($d >> 8) & 0xFF) . "\x0\x0";
if ($this->COL > -1 && ord($this->BUF[$i]{10}) & 0x80) {
for ($j = 0; $j < (2 << (ord($this->BUF[$i]{10}) & 0x07)); $j++) {
if (
ord($Locals_rgb{3 * $j + 0}) == (($this->COL >> 16) & 0xFF) &&
ord($Locals_rgb{3 * $j + 1}) == (($this->COL >> 8) & 0xFF) &&
ord($Locals_rgb{3 * $j + 2}) == (($this->COL >> 0) & 0xFF)
) {
$Locals_ext = "!\xF9\x04" . chr(($this->DIS << 2) + 1) .
chr(($d >> 0) & 0xFF) . chr(($d >> 8) & 0xFF) . chr($j) . "\x0";
break;
}
}
}
switch ($Locals_tmp{0}) {
case "!":
/**
* @var string $Locals_img;
*/
$Locals_img = substr($Locals_tmp, 8, 10);
$Locals_tmp = substr($Locals_tmp, 18, strlen($Locals_tmp) - 18);
break;
case ",":
$Locals_img = substr($Locals_tmp, 0, 10);
$Locals_tmp = substr($Locals_tmp, 10, strlen($Locals_tmp) - 10);
break;
}
if (ord($this->BUF[$i]{10}) & 0x80 && $this->IMG > -1) {
if ($Global_len == $Locals_len) {
if (self::GIFBlockCompare($Global_rgb, $Locals_rgb, $Global_len)) {
$this->GIF .= ($Locals_ext . $Locals_img . $Locals_tmp);
} else {
$byte = ord($Locals_img{9});
$byte |= 0x80;
$byte &= 0xF8;
$byte |= (ord($this->BUF[0]{10}) & 0x07);
$Locals_img{9} = chr($byte);
$this->GIF .= ($Locals_ext . $Locals_img . $Locals_rgb . $Locals_tmp);
}
} else {
$byte = ord($Locals_img{9});
$byte |= 0x80;
$byte &= 0xF8;
$byte |= (ord($this->BUF[$i]{10}) & 0x07);
$Locals_img{9} = chr($byte);
$this->GIF .= ($Locals_ext . $Locals_img . $Locals_rgb . $Locals_tmp);
}
} else {
$this->GIF .= ($Locals_ext . $Locals_img . $Locals_tmp);
}
$this->IMG = 1;
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFAddFooter...
::
*/
public function GIFAddFooter()
{
$this->GIF .= ";";
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFBlockCompare...
::
*/
public function GIFBlockCompare($GlobalBlock, $LocalBlock, $Len)
{
for ($i = 0; $i < $Len; $i++) {
if (
$GlobalBlock{3 * $i + 0} != $LocalBlock{3 * $i + 0} ||
$GlobalBlock{3 * $i + 1} != $LocalBlock{3 * $i + 1} ||
$GlobalBlock{3 * $i + 2} != $LocalBlock{3 * $i + 2}
) {
return (0);
}
}
return (1);
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFWord...
::
*/
public function GIFWord($int)
{
return (chr($int & 0xFF) . chr(($int >> 8) & 0xFF));
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GetAnimation...
::
*/
public function GetAnimation()
{
return ($this->GIF);
}
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFDecoder Version 2.0 by László Zsidi, http://gifs.hu
::
:: Created at 2007. 02. 01. '07.47.AM'
::
::
::
::
:: Try on-line GIFBuilder Form demo based on GIFDecoder.
::
:: http://gifs.hu/phpclasses/demos/GifBuilder/
::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*/
class GIFDecoder
{
public $GIF_buffer = [];
public $GIF_arrays = [];
public $GIF_delays = [];
public $GIF_stream = "";
public $GIF_string = "";
public $GIF_bfseek = 0;
public $GIF_screen = [];
public $GIF_global = [];
public $GIF_sorted;
public $GIF_colorS;
public $GIF_colorC;
public $GIF_colorF;
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFDecoder ( $GIF_pointer )
::
*/
public function GIFDecoder($GIF_pointer)
{
$this->GIF_stream = $GIF_pointer;
GIFDecoder::GIFGetByte(6); // GIF89a
GIFDecoder::GIFGetByte(7); // Logical Screen Descriptor
$this->GIF_screen = $this->GIF_buffer;
$this->GIF_colorF = $this->GIF_buffer[4] & 0x80 ? 1 : 0;
$this->GIF_sorted = $this->GIF_buffer[4] & 0x08 ? 1 : 0;
$this->GIF_colorC = $this->GIF_buffer[4] & 0x07;
$this->GIF_colorS = 2 << $this->GIF_colorC;
if (1 == $this->GIF_colorF) {
GIFDecoder::GIFGetByte(3 * $this->GIF_colorS);
$this->GIF_global = $this->GIF_buffer;
}
/*
*
* 05.06.2007.
* Made a little modification
*
*
- for ( $cycle = 1; $cycle; ) {
+ if ( GIFDecoder::GIFGetByte ( 1 ) ) {
- switch ( $this->GIF_buffer [ 0 ] ) {
- case 0x21:
- GIFDecoder::GIFReadExtensions ( );
- break;
- case 0x2C:
- GIFDecoder::GIFReadDescriptor ( );
- break;
- case 0x3B:
- $cycle = 0;
- break;
- }
- }
+ else {
+ $cycle = 0;
+ }
- }
*/
for ($cycle = 1; $cycle;) {
if (GIFDecoder::GIFGetByte(1)) {
switch ($this->GIF_buffer[0]) {
case 0x21:
GIFDecoder::GIFReadExtensions();
break;
case 0x2C:
GIFDecoder::GIFReadDescriptor();
break;
case 0x3B:
$cycle = 0;
break;
}
} else {
$cycle = 0;
}
}
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFReadExtension ( )
::
*/
public function GIFReadExtensions()
{
GIFDecoder::GIFGetByte(1);
for (;;) {
GIFDecoder::GIFGetByte(1);
if (($u = $this->GIF_buffer[0]) == 0x00) {
break;
}
GIFDecoder::GIFGetByte($u);
/*
* 07.05.2007.
* Implemented a new line for a new function
* to determine the originaly delays between
* frames.
*
*/
if (4 == $u) {
$this->GIF_delays[] = ($this->GIF_buffer[1] | $this->GIF_buffer[2] << 8);
}
}
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFReadExtension ( )
::
*/
public function GIFReadDescriptor()
{
GIFDecoder::GIFGetByte(9);
$GIF_screen = $this->GIF_buffer;
$GIF_colorF = $this->GIF_buffer[8] & 0x80 ? 1 : 0;
if ($GIF_colorF) {
$GIF_code = $this->GIF_buffer[8] & 0x07;
$GIF_sort = $this->GIF_buffer[8] & 0x20 ? 1 : 0;
} else {
$GIF_code = $this->GIF_colorC;
$GIF_sort = $this->GIF_sorted;
}
$GIF_size = 2 << $GIF_code;
$this->GIF_screen[4] &= 0x70;
$this->GIF_screen[4] |= 0x80;
$this->GIF_screen[4] |= $GIF_code;
if ($GIF_sort) {
$this->GIF_screen[4] |= 0x08;
}
$this->GIF_string = "GIF87a";
GIFDecoder::GIFPutByte($this->GIF_screen);
if (1 == $GIF_colorF) {
GIFDecoder::GIFGetByte(3 * $GIF_size);
GIFDecoder::GIFPutByte($this->GIF_buffer);
} else {
GIFDecoder::GIFPutByte($this->GIF_global);
}
$this->GIF_string .= chr(0x2C);
$GIF_screen[8] &= 0x40;
GIFDecoder::GIFPutByte($GIF_screen);
GIFDecoder::GIFGetByte(1);
GIFDecoder::GIFPutByte($this->GIF_buffer);
for (;;) {
GIFDecoder::GIFGetByte(1);
GIFDecoder::GIFPutByte($this->GIF_buffer);
if (($u = $this->GIF_buffer[0]) == 0x00) {
break;
}
GIFDecoder::GIFGetByte($u);
GIFDecoder::GIFPutByte($this->GIF_buffer);
}
$this->GIF_string .= chr(0x3B);
/*
Add frames into $GIF_stream array...
*/
$this->GIF_arrays[] = $this->GIF_string;
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFGetByte ( $len )
::
*/
/*
*
* 05.06.2007.
* Made a little modification
*
*
- function GIFGetByte ( $len ) {
- $this->GIF_buffer = array ( );
-
- for ( $i = 0; $i < $len; $i++ ) {
+ if ( $this->GIF_bfseek > strlen ( $this->GIF_stream ) ) {
+ return 0;
+ }
- $this->GIF_buffer [ ] = ord ( $this->GIF_stream { $this->GIF_bfseek++ } );
- }
+ return 1;
- }
*/
public function GIFGetByte($len)
{
$this->GIF_buffer = [];
for ($i = 0; $i < $len; $i++) {
if ($this->GIF_bfseek > strlen($this->GIF_stream)) {
return 0;
}
$this->GIF_buffer[] = ord($this->GIF_stream{$this->GIF_bfseek++});
}
return 1;
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFPutByte ( $bytes )
::
*/
public function GIFPutByte($bytes)
{
for ($i = 0; $i < count($bytes); $i++) {
$this->GIF_string .= chr($bytes[$i]);
}
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: PUBLIC FUNCTIONS
::
::
:: GIFGetFrames ( )
::
*/
public function GIFGetFrames()
{
return ($this->GIF_arrays);
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFGetDelays ( )
::
*/
public function GIFGetDelays()
{
return ($this->GIF_delays);
}
}

View File

@@ -0,0 +1,663 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\image\driver;
use think\Lang;
class Imagick
{
/**
* 图像资源对象
*
* @var resource
*/
private $im;
/**
* 图像信息,包括 width, height, type, mime, size
*
* @var array
*/
private $info;
/**
* 构造方法,可用于打开一张图像
*
* @param string $imgname 图像路径
*/
public function __construct($imgname = null)
{
if (!extension_loaded('Imagick')) {
throw new \Exception(Lang::get('_NOT_SUPPERT_') . ':Imagick');
}
$imgname && $this->open($imgname);
}
/**
* 打开一张图像
*
* @param string $imgname 图像路径
*/
public function open($imgname)
{
//检测图像文件
if (!is_file($imgname)) {
throw new \Exception('不存在的图像文件');
}
//销毁已存在的图像
empty($this->im) || $this->im->destroy();
//载入图像
$this->im = new \Imagick(realpath($imgname));
//设置图像信息
$this->info = [
'width' => $this->im->getImageWidth(),
'height' => $this->im->getImageHeight(),
'type' => strtolower($this->im->getImageFormat()),
'mime' => $this->im->getImageMimeType(),
];
}
/**
* 保存图像
*
* @param string $imgname 图像保存名称
* @param string $type 图像类型
* @param boolean $interlace 是否对JPEG类型图像设置隔行扫描
*/
public function save($imgname, $type = null, $interlace = true)
{
if (empty($this->im)) {
throw new \Exception('没有可以被保存的图像资源');
}
//设置图片类型
if (is_null($type)) {
$type = $this->info['type'];
} else {
$type = strtolower($type);
$this->im->setImageFormat($type);
}
//JPEG图像设置隔行扫描
if ('jpeg' == $type || 'jpg' == $type) {
$this->im->setImageInterlaceScheme(1);
}
//去除图像配置信息
$this->im->stripImage();
//保存图像
$imgname = realpath(dirname($imgname)) . '/' . basename($imgname); //强制绝对路径
if ('gif' == $type) {
$this->im->writeImages($imgname, true);
} else {
$this->im->writeImage($imgname);
}
}
/**
* 返回图像宽度
*
* @return integer 图像宽度
*/
public function width()
{
if (empty($this->im)) {
throw new \Exception('没有指定图像资源');
}
return $this->info['width'];
}
/**
* 返回图像高度
*
* @return integer 图像高度
*/
public function height()
{
if (empty($this->im)) {
throw new \Exception('没有指定图像资源');
}
return $this->info['height'];
}
/**
* 返回图像类型
*
* @return string 图像类型
*/
public function type()
{
if (empty($this->im)) {
throw new \Exception('没有指定图像资源');
}
return $this->info['type'];
}
/**
* 返回图像MIME类型
*
* @return string 图像MIME类型
*/
public function mime()
{
if (empty($this->im)) {
throw new \Exception('没有指定图像资源');
}
return $this->info['mime'];
}
/**
* 返回图像尺寸数组 0 - 图像宽度1 - 图像高度
*
* @return array 图像尺寸
*/
public function size()
{
if (empty($this->im)) {
throw new \Exception('没有指定图像资源');
}
return [$this->info['width'], $this->info['height']];
}
/**
* 裁剪图像
*
* @param integer $w 裁剪区域宽度
* @param integer $h 裁剪区域高度
* @param integer $x 裁剪区域x坐标
* @param integer $y 裁剪区域y坐标
* @param integer $width 图像保存宽度
* @param integer $height 图像保存高度
*/
public function crop($w, $h, $x = 0, $y = 0, $width = null, $height = null)
{
if (empty($this->im)) {
throw new \Exception('没有可以被裁剪的图像资源');
}
//设置保存尺寸
empty($width) && $width = $w;
empty($height) && $height = $h;
//裁剪图片
if ('gif' == $this->info['type']) {
$img = $this->im->coalesceImages();
$this->im->destroy(); //销毁原图
//循环裁剪每一帧
do {
$this->_crop($w, $h, $x, $y, $width, $height, $img);
} while ($img->nextImage());
//压缩图片
$this->im = $img->deconstructImages();
$img->destroy(); //销毁零时图片
} else {
$this->_crop($w, $h, $x, $y, $width, $height);
}
}
/**
* 裁剪图片,内部调用
*
*/
private function _crop($w, $h, $x, $y, $width, $height, $img = null)
{
is_null($img) && $img = $this->im;
//裁剪
$info = $this->info;
if (0 != $x || 0 != $y || $w != $info['width'] || $h != $info['height']) {
$img->cropImage($w, $h, $x, $y);
$img->setImagePage($w, $h, 0, 0); //调整画布和图片一致
}
//调整大小
if ($w != $width || $h != $height) {
$img->scaleImage($width, $height);
}
//设置缓存尺寸
$this->info['width'] = $w;
$this->info['height'] = $h;
}
/**
* 生成缩略图
*
* @param integer $width 缩略图最大宽度
* @param integer $height 缩略图最大高度
* @param integer $type 缩略图裁剪类型
*/
public function thumb($width, $height, $type = THINKIMAGE_THUMB_SCALE)
{
if (empty($this->im)) {
throw new \Exception('没有可以被缩略的图像资源');
}
//原图宽度和高度
$w = $this->info['width'];
$h = $this->info['height'];
/* 计算缩略图生成的必要参数 */
switch ($type) {
/* 等比例缩放 */
case THINKIMAGE_THUMB_SCALING:
//原图尺寸小于缩略图尺寸则不进行缩略
if ($w < $width && $h < $height) {
return;
}
//计算缩放比例
$scale = min($width / $w, $height / $h);
//设置缩略图的坐标及宽度和高度
$x = $y = 0;
$width = $w * $scale;
$height = $h * $scale;
break;
/* 居中裁剪 */
case THINKIMAGE_THUMB_CENTER:
//计算缩放比例
$scale = max($width / $w, $height / $h);
//设置缩略图的坐标及宽度和高度
$w = $width / $scale;
$h = $height / $scale;
$x = ($this->info['width'] - $w) / 2;
$y = ($this->info['height'] - $h) / 2;
break;
/* 左上角裁剪 */
case THINKIMAGE_THUMB_NORTHWEST:
//计算缩放比例
$scale = max($width / $w, $height / $h);
//设置缩略图的坐标及宽度和高度
$x = $y = 0;
$w = $width / $scale;
$h = $height / $scale;
break;
/* 右下角裁剪 */
case THINKIMAGE_THUMB_SOUTHEAST:
//计算缩放比例
$scale = max($width / $w, $height / $h);
//设置缩略图的坐标及宽度和高度
$w = $width / $scale;
$h = $height / $scale;
$x = $this->info['width'] - $w;
$y = $this->info['height'] - $h;
break;
/* 填充 */
case THINKIMAGE_THUMB_FILLED:
//计算缩放比例
if ($w < $width && $h < $height) {
$scale = 1;
} else {
$scale = min($width / $w, $height / $h);
}
//设置缩略图的坐标及宽度和高度
$neww = $w * $scale;
$newh = $h * $scale;
$posx = ($width - $w * $scale) / 2;
$posy = ($height - $h * $scale) / 2;
//创建一张新图像
$newimg = new Imagick();
$newimg->newImage($width, $height, 'white', $this->info['type']);
if ('gif' == $this->info['type']) {
$imgs = $this->im->coalesceImages();
$img = new Imagick();
$this->im->destroy(); //销毁原图
//循环填充每一帧
do {
//填充图像
$image = $this->_fill($newimg, $posx, $posy, $neww, $newh, $imgs);
$img->addImage($image);
$img->setImageDelay($imgs->getImageDelay());
$img->setImagePage($width, $height, 0, 0);
$image->destroy(); //销毁零时图片
} while ($imgs->nextImage());
//压缩图片
$this->im->destroy();
$this->im = $img->deconstructImages();
$imgs->destroy(); //销毁零时图片
$img->destroy(); //销毁零时图片
} else {
//填充图像
$img = $this->_fill($newimg, $posx, $posy, $neww, $newh);
//销毁原图
$this->im->destroy();
$this->im = $img;
}
//设置新图像属性
$this->info['width'] = $width;
$this->info['height'] = $height;
return;
/* 固定 */
case THINKIMAGE_THUMB_FIXED:
$x = $y = 0;
break;
default:
throw new \Exception('不支持的缩略图裁剪类型');
}
/* 裁剪图像 */
$this->crop($w, $h, $x, $y, $width, $height);
}
/**
* 填充指定图像,内部使用
*
*/
private function _fill($newimg, $posx, $posy, $neww, $newh, $img = null)
{
is_null($img) && $img = $this->im;
/* 将指定图片绘入空白图片 */
$draw = new ImagickDraw();
$draw->composite($img->getImageCompose(), $posx, $posy, $neww, $newh, $img);
$image = $newimg->clone();
$image->drawImage($draw);
$draw->destroy();
return $image;
}
/**
* 添加水印
*
* @param string $source 水印图片路径
* @param integer $locate 水印位置
* @param integer $alpha 水印透明度
*/
public function water($source, $locate = THINKIMAGE_WATER_SOUTHEAST)
{
//资源检测
if (empty($this->im)) {
throw new \Exception('没有可以被添加水印的图像资源');
}
if (!is_file($source)) {
throw new \Exception('水印图像不存在');
}
//创建水印图像资源
$water = new Imagick(realpath($source));
$info = [$water->getImageWidth(), $water->getImageHeight()];
/* 设定水印位置 */
switch ($locate) {
/* 右下角水印 */
case THINKIMAGE_WATER_SOUTHEAST:
$x = $this->info['width'] - $info[0];
$y = $this->info['height'] - $info[1];
break;
/* 左下角水印 */
case THINKIMAGE_WATER_SOUTHWEST:
$x = 0;
$y = $this->info['height'] - $info[1];
break;
/* 左上角水印 */
case THINKIMAGE_WATER_NORTHWEST:
$x = $y = 0;
break;
/* 右上角水印 */
case THINKIMAGE_WATER_NORTHEAST:
$x = $this->info['width'] - $info[0];
$y = 0;
break;
/* 居中水印 */
case THINKIMAGE_WATER_CENTER:
$x = ($this->info['width'] - $info[0]) / 2;
$y = ($this->info['height'] - $info[1]) / 2;
break;
/* 下居中水印 */
case THINKIMAGE_WATER_SOUTH:
$x = ($this->info['width'] - $info[0]) / 2;
$y = $this->info['height'] - $info[1];
break;
/* 右居中水印 */
case THINKIMAGE_WATER_EAST:
$x = $this->info['width'] - $info[0];
$y = ($this->info['height'] - $info[1]) / 2;
break;
/* 上居中水印 */
case THINKIMAGE_WATER_NORTH:
$x = ($this->info['width'] - $info[0]) / 2;
$y = 0;
break;
/* 左居中水印 */
case THINKIMAGE_WATER_WEST:
$x = 0;
$y = ($this->info['height'] - $info[1]) / 2;
break;
default:
/* 自定义水印坐标 */
if (is_array($locate)) {
list($x, $y) = $locate;
} else {
throw new \Exception('不支持的水印位置类型');
}
}
//创建绘图资源
$draw = new ImagickDraw();
$draw->composite($water->getImageCompose(), $x, $y, $info[0], $info[1], $water);
if ('gif' == $this->info['type']) {
$img = $this->im->coalesceImages();
$this->im->destroy(); //销毁原图
do {
//添加水印
$img->drawImage($draw);
} while ($img->nextImage());
//压缩图片
$this->im = $img->deconstructImages();
$img->destroy(); //销毁零时图片
} else {
//添加水印
$this->im->drawImage($draw);
}
//销毁水印资源
$draw->destroy();
$water->destroy();
}
/**
* 图像添加文字
*
* @param string $text 添加的文字
* @param string $font 字体路径
* @param integer $size 字号
* @param string $color 文字颜色
* @param integer $locate 文字写入位置
* @param integer $offset 文字相对当前位置的偏移量
* @param integer $angle 文字倾斜角度
*/
public function text($text, $font, $size, $color = '#00000000',
$locate = THINKIMAGE_WATER_SOUTHEAST, $offset = 0, $angle = 0) {
//资源检测
if (empty($this->im)) {
throw new \Exception('没有可以被写入文字的图像资源');
}
if (!is_file($font)) {
throw new \Exception("不存在的字体文件:{$font}");
}
//获取颜色和透明度
if (is_array($color)) {
$color = array_map('dechex', $color);
foreach ($color as &$value) {
$value = str_pad($value, 2, '0', STR_PAD_LEFT);
}
$color = '#' . implode('', $color);
} elseif (!is_string($color) || 0 !== strpos($color, '#')) {
throw new \Exception('错误的颜色值');
}
$col = substr($color, 0, 7);
$alp = strlen($color) == 9 ? substr($color, -2) : 0;
//获取文字信息
$draw = new ImagickDraw();
$draw->setFont(realpath($font));
$draw->setFontSize($size);
$draw->setFillColor($col);
$draw->setFillAlpha(1 - hexdec($alp) / 127);
$draw->setTextAntialias(true);
$draw->setStrokeAntialias(true);
$metrics = $this->im->queryFontMetrics($draw, $text);
/* 计算文字初始坐标和尺寸 */
$x = 0;
$y = $metrics['ascender'];
$w = $metrics['textWidth'];
$h = $metrics['textHeight'];
/* 设定文字位置 */
switch ($locate) {
/* 右下角文字 */
case THINKIMAGE_WATER_SOUTHEAST:
$x += $this->info['width'] - $w;
$y += $this->info['height'] - $h;
break;
/* 左下角文字 */
case THINKIMAGE_WATER_SOUTHWEST:
$y += $this->info['height'] - $h;
break;
/* 左上角文字 */
case THINKIMAGE_WATER_NORTHWEST:
// 起始坐标即为左上角坐标,无需调整
break;
/* 右上角文字 */
case THINKIMAGE_WATER_NORTHEAST:
$x += $this->info['width'] - $w;
break;
/* 居中文字 */
case THINKIMAGE_WATER_CENTER:
$x += ($this->info['width'] - $w) / 2;
$y += ($this->info['height'] - $h) / 2;
break;
/* 下居中文字 */
case THINKIMAGE_WATER_SOUTH:
$x += ($this->info['width'] - $w) / 2;
$y += $this->info['height'] - $h;
break;
/* 右居中文字 */
case THINKIMAGE_WATER_EAST:
$x += $this->info['width'] - $w;
$y += ($this->info['height'] - $h) / 2;
break;
/* 上居中文字 */
case THINKIMAGE_WATER_NORTH:
$x += ($this->info['width'] - $w) / 2;
break;
/* 左居中文字 */
case THINKIMAGE_WATER_WEST:
$y += ($this->info['height'] - $h) / 2;
break;
default:
/* 自定义文字坐标 */
if (is_array($locate)) {
list($posx, $posy) = $locate;
$x += $posx;
$y += $posy;
} else {
throw new \Exception('不支持的文字位置类型');
}
}
/* 设置偏移量 */
if (is_array($offset)) {
$offset = array_map('intval', $offset);
list($ox, $oy) = $offset;
} else {
$offset = intval($offset);
$ox = $oy = $offset;
}
/* 写入文字 */
if ('gif' == $this->info['type']) {
$img = $this->im->coalesceImages();
$this->im->destroy(); //销毁原图
do {
$img->annotateImage($draw, $x + $ox, $y + $oy, $angle, $text);
} while ($img->nextImage());
//压缩图片
$this->im = $img->deconstructImages();
$img->destroy(); //销毁零时图片
} else {
$this->im->annotateImage($draw, $x + $ox, $y + $oy, $angle, $text);
}
$draw->destroy();
}
/**
* 析构方法,用于销毁图像资源
*
*/
public function __destruct()
{
empty($this->im) || $this->im->destroy();
}
}

248
extend/org/oauth/Driver.php Normal file
View File

@@ -0,0 +1,248 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace org\oauth;
abstract class Driver
{
/**
* oauth版本
* @var string
*/
protected $version = '2.0';
/**
* 申请应用时分配的app_key
* @var string
*/
protected $appKey = '';
/**
* 申请应用时分配的 app_secret
* @var string
*/
protected $appSecret = '';
/**
* 授权类型 response_type 目前只能为code
* @var string
*/
protected $responseType = 'code';
/**
* grant_type 目前只能为 authorization_code
* @var string
*/
protected $grantType = 'authorization_code';
/**
* 获取request_code请求的URL
* @var string
*/
protected $getRequestCodeURL = '';
/**
* 获取access_token请求的URL
* @var string
*/
protected $getAccessTokenURL = '';
/**
* API根路径
* @var string
*/
protected $apiBase = '';
/**
* 授权后获取到的TOKEN信息
* @var array
*/
protected $token = null;
/**
* 回调页面URL 可以通过配置文件配置
* @var string
*/
protected $callback = '';
/**
* 构造方法,配置应用信息
* @param array $config
*/
public function __construct($config = [])
{
$this->appKey = $config['app_key'];
$this->appSecret = $config['app_secret'];
$this->authorize = isset($config['authorize']) ? $config['authorize'] : '';
$this->callback = isset($config['callback']) ? $config['callback'] : '';
}
// 跳转到授权登录页面
public function login($callback = '')
{
if ($callback) {
$this->callback = $callback;
}
//跳转到授权页面
header('Location: ' . $this->getRequestCodeURL());
exit;
}
/**
* 请求code
*/
public function getRequestCodeURL()
{
//Oauth 标准参数
$params = [
'client_id' => $this->appKey,
'redirect_uri' => $this->callback,
'response_type' => $this->responseType,
];
//获取额外参数
if ($this->authorize) {
parse_str($this->authorize, $_param);
if (is_array($_param)) {
$params = array_merge($params, $_param);
} else {
throw new \Exception('AUTHORIZE配置不正确');
}
}
return $this->getRequestCodeURL . '?' . http_build_query($params);
}
/**
* 获取access_token
* @param string $code 授权登录成功后得到的code信息
*/
public function getAccessToken($code)
{
$params = [
'client_id' => $this->appKey,
'client_secret' => $this->appSecret,
'grant_type' => $this->grantType,
'redirect_uri' => $this->callback,
'code' => $code,
];
// 获取token信息
$data = $this->http($this->getAccessTokenURL, $params, 'POST');
// 解析token
$this->token = $this->parseToken($data);
return $this->token;
}
/**
* 设置access_token
* @param string $token
*/
public function setToken($token)
{
$this->token = $token;
}
/**
* 合并默认参数和额外参数
* @param array $params 默认参数
* @param array/string $param 额外参数
* @return array:
*/
protected function param($params, $param)
{
if (is_string($param)) {
parse_str($param, $param);
}
return array_merge($params, $param);
}
/**
* 获取指定API请求的URL
* @param string $api API名称
* @param string $fix api后缀
* @return string 请求的完整URL
*/
protected function url($api, $fix = '')
{
return $this->apiBase . $api . $fix;
}
/**
* 发送HTTP请求方法目前只支持CURL发送请求
* @param string $url 请求URL
* @param array $params 请求参数
* @param string $method 请求方法GET/POST
* @return array $data 响应数据
*/
protected function http($url, $params, $method = 'GET', $header = [], $multi = false)
{
$opts = [
CURLOPT_TIMEOUT => 30,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_HTTPHEADER => $header,
];
/* 根据请求类型设置特定参数 */
switch (strtoupper($method)) {
case 'GET':
$opts[CURLOPT_URL] = $url . '?' . http_build_query($params);
break;
case 'POST':
//判断是否传输文件
$params = $multi ? $params : http_build_query($params);
$opts[CURLOPT_URL] = $url;
$opts[CURLOPT_POST] = 1;
$opts[CURLOPT_POSTFIELDS] = $params;
break;
default:
throw new \Exception('不支持的请求方式!');
}
/* 初始化并执行curl请求 */
$ch = curl_init();
curl_setopt_array($ch, $opts);
$data = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
throw new \Exception('请求发生错误:' . $error);
}
return $data;
}
/**
* 抽象方法在SNSSDK中实现
* 组装接口调用参数 并调用接口
*/
abstract protected function call($api, $param = '', $method = 'GET', $multi = false);
/**
* 抽象方法在SNSSDK中实现
* 解析access_token方法请求后的返回值
*/
abstract protected function parseToken($result);
/**
* 抽象方法在SNSSDK中实现
* 获取当前授权用户的SNS标识
*/
abstract public function getOpenId();
/**
* 抽象方法
* 获取当前授权用户的用户信息
*/
abstract public function getOauthInfo();
}

View File

@@ -0,0 +1,103 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 杨维杰 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class Diandian extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'https://api.diandian.com/oauth/authorize';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://api.diandian.com/oauth/token';
/**
* API根路径
* @var string
*/
protected $apiBase = 'https://api.diandian.com/v1/';
/**
* 组装接口调用参数 并调用接口
* @param string $api 点点网API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'GET')
{
/* 点点网调用公共参数 */
$params = [
'access_token' => $this->token['access_token'],
];
$data = $this->http($this->url($api, '.json'), $this->param($params, $param), $method);
return json_decode($data, true);
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
$data = json_decode($result, true);
if ($data['access_token'] && $data['expires_in'] && $data['token_type'] && $data['uid']) {
$data['openid'] = $data['uid'];
unset($data['uid']);
return $data;
} else {
throw new \Exception("获取点点网ACCESS_TOKEN出错{$data['error']}");
}
}
/**
* 获取当前授权应用的openid
* @return string
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
return null;
}
/**
* 获取当前登录的用户信息
* @return array
*/
public function getOauthInfo()
{
$data = $this->call('user/info');
if (!empty($data['meta']['status']) && 200 == $data['meta']['status']) {
$userInfo['type'] = 'DIANDIAN';
$userInfo['name'] = $data['response']['name'];
$userInfo['nick'] = $data['response']['name'];
$userInfo['avatar'] = "https://api.diandian.com/v1/blog/{$data['response']['blogs'][0]['blogUuid']}/avatar/144";
return $userInfo;
} else {
E("获取点点用户信息失败:{$data}");
}
}
}

View File

@@ -0,0 +1,101 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class Douban extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'https://www.douban.com/service/auth2/auth';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://www.douban.com/service/auth2/token';
/**
* API根路径
* @var string
*/
protected $apiBase = 'https://api.douban.com/v2/';
/**
* 组装接口调用参数 并调用接口
* @param string $api 微博API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'GET')
{
/* 豆瓣调用公共参数 */
$params = [];
$header = ["Authorization: Bearer {$this->token['access_token']}"];
$data = $this->http($this->url($api), $this->param($params, $param), $method, $header);
return json_decode($data, true);
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
$data = json_decode($result, true);
if ($data['access_token'] && $data['expires_in'] && $data['refresh_token'] && $data['douban_user_id']) {
$data['openid'] = $data['douban_user_id'];
unset($data['douban_user_id']);
return $data;
} else {
throw new \Exception("获取豆瓣ACCESS_TOKEN出错{$data['msg']}");
}
}
/**
* 获取当前授权应用的openid
* @return string|null
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
return null;
}
/**
* 获取当前登录的用户信息
* @return array
*/
public function getOauthInfo()
{
$data = $this->call('user/~me');
if (empty($data['code'])) {
$userInfo['type'] = 'DOUBAN';
$userInfo['name'] = $data['name'];
$userInfo['nick'] = $data['name'];
$userInfo['avatar'] = $data['avatar'];
return $userInfo;
} else {
E("获取豆瓣用户信息失败:{$data['msg']}");
}
}
}

View File

@@ -0,0 +1,102 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class Github extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'https://github.com/login/oauth/authorize';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://github.com/login/oauth/access_token';
/**
* API根路径
* @var string
*/
protected $apiBase = 'https://api.github.com/';
/**
* 组装接口调用参数 并调用接口
* @param string $api 微博API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'GET')
{
/* Github 调用公共参数 */
$params = [];
$header = ["Authorization: bearer {$this->token['access_token']}"];
$data = $this->http($this->url($api), $this->param($params, $param), $method, $header);
return json_decode($data, true);
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
parse_str($result, $data);
if ($data['access_token'] && $data['token_type']) {
$data['openid'] = $this->getOpenId();
return $data;
} else {
throw new \Exception("获取 Github ACCESS_TOKEN出错未知错误");
}
}
/**
* 获取当前授权应用的openid
* @return string
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
$data = $this->call('user');
return !empty($data['id']) ? $data['id'] : null;
}
/**
* 获取当前登录的用户信息
* @return array
*/
public function getOauthInfo()
{
$data = $this->call('user');
if (empty($data['code'])) {
$userInfo['type'] = 'GITHUB';
$userInfo['name'] = $data['login'];
$userInfo['nick'] = $data['name'];
$userInfo['avatar'] = $data['avatar_url'];
return $userInfo;
} else {
E("获取Github用户信息失败{$data}");
}
}
}

View File

@@ -0,0 +1,108 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class Google extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'https://accounts.google.com/o/oauth2/auth';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://accounts.google.com/o/oauth2/token';
/**
* 获取request_code的额外参数 URL查询字符串格式
* @var srting
*/
protected $authorize = 'scope=https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email';
/**
* API根路径
* @var string
*/
protected $apiBase = 'https://www.googleapis.com/oauth2/v1/';
/**
* 组装接口调用参数 并调用接口
* @param string $api 微博API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'GET')
{
/* Google 调用公共参数 */
$params = [];
$header = ["Authorization: Bearer {$this->token['access_token']}"];
$data = $this->http($this->url($api), $this->param($params, $param), $method, $header);
return json_decode($data, true);
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
$data = json_decode($result, true);
if ($data['access_token'] && $data['token_type'] && $data['expires_in']) {
$data['openid'] = $this->getOpenId();
return $data;
} else {
throw new \Exception("获取 Google ACCESS_TOKEN出错未知错误");
}
}
/**
* 获取当前授权应用的openid
* @return string
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
$data = $this->call('userinfo');
return !empty($data['id']) ? $data['id'] : null;
}
/**
* 获取当前登录的用户信息
* @return array
*/
public function getOauthInfo()
{
$data = $this->call('userinfo');
if (!empty($data['id'])) {
$userInfo['type'] = 'GOOGLE';
$userInfo['name'] = $data['name'];
$userInfo['nick'] = $data['name'];
$userInfo['avatar'] = $data['picture'];
return $userInfo;
} else {
E("获取Google用户信息失败{$data}");
}
}
}

View File

@@ -0,0 +1,103 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class Kaixin extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'http://api.kaixin001.com/oauth2/authorize';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://api.kaixin001.com/oauth2/access_token';
/**
* API根路径
* @var string
*/
protected $apiBase = 'https://api.kaixin001.com/';
/**
* 组装接口调用参数 并调用接口
* @param string $api 开心网API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'GET')
{
/* 开心网调用公共参数 */
$params = [
'access_token' => $this->token['access_token'],
];
$data = $this->http($this->url($api, '.json'), $this->param($params, $param), $method);
return json_decode($data, true);
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
$data = json_decode($result, true);
if ($data['access_token'] && $data['expires_in'] && $data['refresh_token']) {
$data['openid'] = $this->getOpenId();
return $data;
} else {
throw new \Exception("获取开心网ACCESS_TOKEN出错{$data['error']}");
}
}
/**
* 获取当前授权应用的openid
* @return string
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
$data = $this->call('users/me');
return !empty($data['uid']) ? $data['uid'] : null;
}
/**
* 获取当前登录的用户信息
* @return array
*/
public function getOauthInfo()
{
$data = $this->call('users/me');
if (!empty($data['uid'])) {
$userInfo['type'] = 'KAIXIN';
$userInfo['name'] = $data['uid'];
$userInfo['nick'] = $data['name'];
$userInfo['avatar'] = $data['logo50'];
return $userInfo;
} else {
E("获取开心网用户信息失败:{$data['error']}");
}
}
}

View File

@@ -0,0 +1,109 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class Msn extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'https://login.live.com/oauth20_authorize.srf';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://login.live.com/oauth20_token.srf';
/**
* 获取request_code的额外参数 URL查询字符串格式
* @var srting
*/
protected $authorize = 'scope=wl.basic wl.offline_access wl.signin wl.emails wl.photos';
/**
* API根路径
* @var string
*/
protected $apiBase = 'https://apis.live.net/v5.0/';
/**
* 组装接口调用参数 并调用接口
* @param string $api 微博API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'GET')
{
/* MSN 调用公共参数 */
$params = [
'access_token' => $this->token['access_token'],
];
$data = $this->http($this->url($api), $this->param($params, $param), $method);
return json_decode($data, true);
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
$data = json_decode($result, true);
if ($data['access_token'] && $data['token_type'] && $data['expires_in']) {
$data['openid'] = $this->getOpenId();
return $data;
} else {
throw new \Exception("获取 MSN ACCESS_TOKEN出错未知错误");
}
}
/**
* 获取当前授权应用的openid
* @return string
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
$data = $this->call('me');
return !empty($data['id']) ? $data['id'] : null;
}
/**
* 获取当前登录的用户信息
* @return array
*/
public function getOauthInfo()
{
$data = $this->call('me');
if (!empty($data['id'])) {
$userInfo['type'] = 'MSN';
$userInfo['name'] = $data['name'];
$userInfo['nick'] = $data['name'];
$userInfo['avatar'] = '微软暂未提供头像URL请通过 me/picture 接口下载';
return $userInfo;
} else {
E("获取msn用户信息失败{$data}");
}
}
}

View File

@@ -0,0 +1,114 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class Qq extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'https://graph.qq.com/oauth2.0/authorize';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://graph.qq.com/oauth2.0/token';
/**
* 获取request_code的额外参数,可在配置中修改 URL查询字符串格式
* @var srting
*/
protected $authorize = 'scope=get_user_info,add_share';
/**
* API根路径
* @var string
*/
protected $apiBase = 'https://graph.qq.com/';
/**
* 组装接口调用参数 并调用接口
* @param string $api 微博API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'GET')
{
/* 腾讯QQ调用公共参数 */
$params = [
'oauth_consumer_key' => $this->AppKey,
'access_token' => $this->token['access_token'],
'openid' => $this->openid(),
'format' => 'json',
];
$data = $this->http($this->url($api), $this->param($params, $param), $method);
return json_decode($data, true);
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
parse_str($result, $data);
if ($data['access_token'] && $data['expires_in']) {
$data['openid'] = $this->getOpenId();
return $data;
} else {
throw new \Exception("获取腾讯QQ ACCESS_TOKEN 出错:{$result}");
}
}
/**
* 获取当前授权应用的openid
* @return string
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
if ($data['access_token']) {
$data = $this->http($this->url('oauth2.0/me'), ['access_token' => $data['access_token']]);
$data = json_decode(trim(substr($data, 9), " );\n"), true);
if (isset($data['openid'])) {
return $data['openid'];
}
}
return null;
}
public function getOauthInfo()
{
$data = $this->call('user/get_user_info');
if (0 == $data['ret']) {
$userInfo['type'] = 'QQ';
$userInfo['name'] = $data['nickname'];
$userInfo['nick'] = $data['nickname'];
$userInfo['avatar'] = $data['figureurl_2'];
return $userInfo;
} else {
E("获取腾讯QQ用户信息失败{$data['msg']}");
}
}
}

View File

@@ -0,0 +1,127 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class Renren extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'https://graph.renren.com/oauth/authorize';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://graph.renren.com/oauth/token';
/**
* API根路径
* @var string
*/
protected $apiBase = 'http://api.renren.com/restserver.do';
/**
* 组装接口调用参数 并调用接口
* @param string $api 微博API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'POST')
{
/* 人人网调用公共参数 */
$params = [
'method' => $api,
'access_token' => $this->token['access_token'],
'v' => '1.0',
'format' => 'json',
];
$data = $this->http($this->url(''), $this->param($params, $param), $method);
return json_decode($data, true);
}
/**
* 合并默认参数和额外参数
* @param array $params 默认参数
* @param array/string $param 额外参数
* @return array:
*/
protected function param($params, $param)
{
$params = parent::param($params, $param);
/* 签名 */
ksort($params);
$param = [];
foreach ($params as $key => $value) {
$param[] = "{$key}={$value}";
}
$sign = implode('', $param) . $this->AppSecret;
$params['sig'] = md5($sign);
return $params;
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
$data = json_decode($result, true);
if ($data['access_token'] && $data['expires_in'] && $data['refresh_token'] && $data['user']['id']) {
$data['openid'] = $data['user']['id'];
unset($data['user']);
return $data;
} else {
throw new \Exception("获取人人网ACCESS_TOKEN出错{$data['error_description']}");
}
}
/**
* 获取当前授权应用的openid
* @return string
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
return null;
}
/**
* 获取当前登录的用户信息
* @return array
*/
public function getOauthInfo()
{
$data = $this->call('users.getInfo');
if (!isset($data['error_code'])) {
$userInfo['type'] = 'RENREN';
$userInfo['name'] = $data[0]['name'];
$userInfo['nick'] = $data[0]['name'];
$userInfo['avatar'] = $data[0]['headurl'];
return $userInfo;
} else {
E("获取人人网用户信息失败:{$data['error_msg']}");
}
}
}

View File

@@ -0,0 +1,103 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class Sina extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'https://api.weibo.com/oauth2/authorize';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://api.weibo.com/oauth2/access_token';
/**
* API根路径
* @var string
*/
protected $apiBase = 'https://api.weibo.com/2/';
/**
* 组装接口调用参数 并调用接口
* @param string $api 微博API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'GET', $multi = false)
{
/* 新浪微博调用公共参数 */
$params = [
'access_token' => $this->token['access_token'],
];
$data = $this->http($this->url($api, '.json'), $this->param($params, $param), $method, $multi);
return json_decode($data, true);
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
$data = json_decode($result, true);
if ($data['access_token'] && $data['expires_in'] && $data['remind_in'] && $data['uid']) {
$data['openid'] = $data['uid'];
unset($data['uid']);
return $data;
} else {
throw new \Exception("获取新浪微博ACCESS_TOKEN出错{$data['error']}");
}
}
/**
* 获取当前授权应用的openid
* @return string
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
return null;
}
/**
* 获取当前登录的用户信息
* @return array
*/
public function getOauthInfo()
{
$data = $this->call('users.getInfo');
if (!isset($data['error_code'])) {
$userInfo['type'] = 'RENREN';
$userInfo['name'] = $data[0]['name'];
$userInfo['nick'] = $data[0]['name'];
$userInfo['avatar'] = $data[0]['headurl'];
return $userInfo;
} else {
E("获取人人网用户信息失败:{$data['error_msg']}");
}
}
}

View File

@@ -0,0 +1,103 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class Sohu extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'https://api.sohu.com/oauth2/authorize';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://api.sohu.com/oauth2/token';
/**
* API根路径
* @var string
*/
protected $apiBase = 'https://api.sohu.com/rest/';
/**
* 组装接口调用参数 并调用接口
* @param string $api 搜狐API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'GET')
{
/* 搜狐调用公共参数 */
$params = [
'access_token' => $this->token['access_token'],
];
$data = $this->http($this->url($api), $this->param($params, $param), $method);
return json_decode($data, true);
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
$data = json_decode($result, true);
if ($data['access_token'] && $data['expires_in'] && $data['refresh_token'] && $data['open_id']) {
$data['openid'] = $data['open_id'];
unset($data['open_id']);
return $data;
} else {
throw new \Exception("获取搜狐ACCESS_TOKEN出错{$data['error']}");
}
}
/**
* 获取当前授权应用的openid
* @return string
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
return null;
}
/**
* 获取当前登录的用户信息
* @return array
*/
public function getOauthInfo()
{
$data = $this->call('i/prv/1/user/get-basic-info');
if ('success' == $data['message'] && !empty($data['data'])) {
$userInfo['type'] = 'SOHU';
$userInfo['name'] = $data['data']['open_id'];
$userInfo['nick'] = $data['data']['nick'];
$userInfo['avatar'] = $data['data']['icon'];
return $userInfo;
} else {
E("获取搜狐用户信息失败:{$data['message']}");
}
}
}

View File

@@ -0,0 +1,104 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class T163 extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'https://api.t.163.com/oauth2/authorize';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://api.t.163.com/oauth2/access_token';
/**
* API根路径
* @var string
*/
protected $apiBase = 'https://api.t.163.com/';
/**
* 组装接口调用参数 并调用接口
* @param string $api 微博API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'GET')
{
/* 新浪微博调用公共参数 */
$params = [
'oauth_token' => $this->token['access_token'],
];
$data = $this->http($this->url($api, '.json'), $this->param($params, $param), $method);
return json_decode($data, true);
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
$data = json_decode($result, true);
if ($data['uid'] && $data['access_token'] && $data['expires_in'] && $data['refresh_token']) {
$data['openid'] = $data['uid'];
unset($data['uid']);
return $data;
} else {
throw new \Exception("获取网易微博ACCESS_TOKEN出错{$data['error']}");
}
}
/**
* 获取当前授权应用的openid
* @return string
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
$data = $this->call('users/show');
return !empty($data['id']) ? $data['id'] : null;
}
/**
* 获取当前登录的用户信息
* @return array
*/
public function getOauthInfo()
{
$data = $this->call('users/show');
if (0 == $data['error_code']) {
$userInfo['type'] = 'T163';
$userInfo['name'] = $data['name'];
$userInfo['nick'] = $data['screen_name'];
$userInfo['avatar'] = str_replace('w=48&h=48', 'w=180&h=180', $data['profile_image_url']);
return $userInfo;
} else {
E("获取网易微博用户信息失败:{$data['error']}");
}
}
}

View File

@@ -0,0 +1,107 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class Taobao extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'https://oauth.taobao.com/authorize';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://oauth.taobao.com/token';
/**
* API根路径
* @var string
*/
protected $apiBase = 'https://eco.taobao.com/router/rest';
/**
* 组装接口调用参数 并调用接口
* @param string $api 微博API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'GET')
{
/* 淘宝网调用公共参数 */
$params = [
'method' => $api,
'access_token' => $this->token['access_token'],
'format' => 'json',
'v' => '2.0',
];
$data = $this->http($this->url(''), $this->param($params, $param), $method);
return json_decode($data, true);
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
$data = json_decode($result, true);
if ($data['access_token'] && $data['expires_in'] && $data['taobao_user_id']) {
$data['openid'] = $data['taobao_user_id'];
unset($data['taobao_user_id']);
return $data;
} else {
throw new \Exception("获取淘宝网ACCESS_TOKEN出错{$data['error']}");
}
}
/**
* 获取当前授权应用的openid
* @return string
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
return null;
}
/**
* 获取当前登录的用户信息
* @return array
*/
public function getOauthInfo()
{
$fields = 'user_id,nick,sex,buyer_credit,avatar,has_shop,vip_info';
$data = $this->call('taobao.user.buyer.get', "fields={$fields}");
if (!empty($data['user_buyer_get_response']['user'])) {
$user = $data['user_buyer_get_response']['user'];
$userInfo['type'] = 'TAOBAO';
$userInfo['name'] = $user['user_id'];
$userInfo['nick'] = $user['nick'];
$userInfo['avatar'] = $user['avatar'];
return $userInfo;
} else {
E("获取淘宝网用户信息失败:{$data['error_response']['msg']}");
}
}
}

View File

@@ -0,0 +1,108 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class Tencent extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'https://open.t.qq.com/cgi-bin/oauth2/authorize';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://open.t.qq.com/cgi-bin/oauth2/access_token';
/**
* API根路径
* @var string
*/
protected $apiBase = 'https://open.t.qq.com/api/';
/**
* 组装接口调用参数 并调用接口
* @param string $api 微博API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'GET', $multi = false)
{
/* 腾讯微博调用公共参数 */
$params = [
'oauth_consumer_key' => $this->AppKey,
'access_token' => $this->token['access_token'],
'openid' => $this->openid(),
'clientip' => $_SERVER['REMOTE_ADDR'],
'oauth_version' => '2.a',
'scope' => 'all',
'format' => 'json',
];
$data = $this->http($this->url($api), $this->param($params, $param), $method, $multi);
return json_decode($data, true);
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
parse_str($result, $data);
$data = array_merge($data, ['openid' => $_GET['openid'], 'openkey' => $_GET['openkey']]);
if ($data['access_token'] && $data['expires_in'] && $data['openid']) {
return $data;
} else {
throw new \Exception("获取腾讯微博 ACCESS_TOKEN 出错:{$result}");
}
}
/**
* 获取当前授权应用的openid
* @return string
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
return null;
}
/**
* 获取当前登录的用户信息
* @return array
*/
public function getOauthInfo()
{
$data = $this->call('users.getInfo');
if (!isset($data['error_code'])) {
$userInfo['type'] = 'RENREN';
$userInfo['name'] = $data[0]['name'];
$userInfo['nick'] = $data[0]['name'];
$userInfo['avatar'] = $data[0]['headurl'];
return $userInfo;
} else {
E("获取人人网用户信息失败:{$data['error_msg']}");
}
}
}

View File

@@ -0,0 +1,103 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\oauth\driver;
use org\oauth\Driver;
class X360 extends Driver
{
/**
* 获取requestCode的api接口
* @var string
*/
protected $getRequestCodeURL = 'https://openapi.360.cn/oauth2/authorize';
/**
* 获取access_token的api接口
* @var string
*/
protected $getAccessTokenURL = 'https://openapi.360.cn/oauth2/access_token';
/**
* API根路径
* @var string
*/
protected $apiBase = 'https://openapi.360.cn/';
/**
* 组装接口调用参数 并调用接口
* @param string $api 360开放平台API
* @param string $param 调用API的额外参数
* @param string $method HTTP请求方法 默认为GET
* @return json
*/
public function call($api, $param = '', $method = 'GET')
{
/* 360开放平台调用公共参数 */
$params = [
'access_token' => $this->token['access_token'],
];
$data = $this->http($this->url($api, '.json'), $this->param($params, $param), $method);
return json_decode($data, true);
}
/**
* 解析access_token方法请求后的返回值
* @param string $result 获取access_token的方法的返回值
*/
protected function parseToken($result)
{
$data = json_decode($result, true);
if ($data['access_token'] && $data['expires_in'] && $data['refresh_token']) {
$data['openid'] = $this->getOpenId();
return $data;
} else {
throw new \Exception("获取360开放平台ACCESS_TOKEN出错{$data['error']}");
}
}
/**
* 获取当前授权应用的openid
* @return string
*/
public function getOpenId()
{
if (!empty($this->token['openid'])) {
return $this->token['openid'];
}
$data = $this->call('user/me');
return !empty($data['id']) ? $data['id'] : null;
}
/**
* 获取当前登录的用户信息
* @return array
*/
public function getOauthInfo()
{
$data = $this->call('user/me');
if (0 == $data['error_code']) {
$userInfo['type'] = 'X360';
$userInfo['name'] = $data['name'];
$userInfo['nick'] = $data['name'];
$userInfo['avatar'] = $data['avatar'];
return $userInfo;
} else {
E("获取360用户信息失败{$data['error']}");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,315 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
// | Ubb.php 2013-04-03
// +----------------------------------------------------------------------
namespace org\parser\driver;
class Ubb
{
/**
* UBB标签匹配规则
* @var array
*/
private $ubb = [
['table', '\[table(?:=([\d%]*))?\]', '\[\/table\]', 'width'],
['tr', '\[tr\]', '\[\/tr\]', 'tag'],
['th', '\[th(?:=([\d%]*)(?:,([\d%]*))?)?\]', '\[\/th\]', 'widthAndHeight'],
['td', '\[td(?:=([\d%]*)(?:,([\d%]*))?)?\]', '\[\/td\]', 'widthAndHeight'],
['img', '\[img(?:=([\d%]*)(?:,([\d%]*))?)?\]', '\[\/img\]', 'imgWidthAndHeight'],
['img', '\[img=(.*?)(?:,([\d%]*)(?:,([\d%]*))?)?\/\]', 'img'],
['a', '\[url(?:=(.*?)(?:,([\w\-]*))?)?\]', '\[\/url\]', 'urlClass'],
['a', '\[a(?:=(.*?)(?:,([\w\-]*))?)?\]', '\[\/a\]', 'urlClass'],
['a', '\[url=(.*?)(?:,([\w\-]*))?\/\]', 'url'],
['a', '\[a=(.*?)(?:,([\w\-]*))?\/\]', 'url'],
['a', '\[email(?:=([\w\-]*))?\]', '\[\/email\]', 'emailClass'],
['ul', '\[ul(?:=([\w\-]*))?\]', '\[\/ul\]', 'class'],
['ol', '\[ol(?:=([\w\-]*))?\]', '\[\/ol\]', 'class'],
['li', '\[li(?:=([\w\-]*))?\]', '\[\/li\]', 'class'],
['span', '\[span(?:=([\w\-]*))?\]', '\[\/span\]', 'class'],
['div', '\[div(?:=([\w\-]*))?\]', '\[\/div\]', 'class'],
['p', '\[p(?:=([\w\-]*))?\]', '\[\/p\]', 'class'],
['strong', '\[b\]', '\[\/b\]', 'tag'],
['strong', '\[strong\]', '\[\/strong\]', 'tag'],
['i', '\[i\]', '\[\/i\]', 'tag'],
['em', '\[em\]', '\[\/em\]', 'tag'],
['sub', '\[sub\]', '\[\/sub\]', 'tag'],
['sup', '\[sup\]', '\[\/sup\]', 'tag'],
['pre', '\[code(?:=([a-z#\+\/]*))?\]', '\[\/code\]', 'code'],
['code', '\[line(?:=([a-z#\+\/]*))?\]', '\[\/line\]', 'code'],
];
/**
* 解析UBB代码为HTML
* @param string $content 要解析的UBB代码
* @return string 解析后的HTML代码
*/
public function parse($content = '')
{
if (empty($content)) {
return '';
}
for ($i = 0, $count = count($this->ubb); $i < $count; $i++) {
if (count($this->ubb[$i]) == 4) {
//解析闭合标签
$content = $this->closeTag($content, $this->ubb[$i]);
} else {
$content = $this->onceTag($content, $this->ubb[$i]);
}
}
return nl2br($content);
}
/**
* 解析闭合标签,支持嵌套
* @param string $data 要解析的数据
* @param array $rule 解析规则
* @return string 解析后的内容
*/
private function closeTag($data, $rule = '')
{
static $tag, $reg, $func, $count = 0;
if (is_string($data)) {
list($tag, $reg[0], $reg[1], $func) = $rule;
do {
$data = preg_replace_callback("/({$reg[0]})(.*?)({$reg[1]})/is",
[$this, 'closeTag'], $data);
} while ($count && $count--); //递归解析,直到嵌套解析完毕
return $data;
} elseif (is_array($data)) {
$num = count($data);
if (preg_match("/{$reg[0]}/is", $data[$num - 2])) {
//存在嵌套,进一步解析
$count = 1;
$data[$num - 2] = preg_replace_callback("/({$reg[0]})(.*?)({$reg[1]})/is",
[$this, 'closeTag'], $data[$num - 2] . $data[$num - 1]);
return $data[1] . $data[$num - 2];
} else {
//不存在嵌套,直接解析内容
$parse = '_' . $func;
$data[$num - 2] = trim($data[$num - 2], "\r\n"); //去掉标签内容两端的换行符
return $this->$parse($tag, $data);
}
}
}
/**
* 解析单标签
* @param string $data 要解析的数据
* @param array $rule 解析规则
* @return string 解析后的内容
*/
private function onceTag($data, $rule = '')
{
list($tag, $reg, $func) = $rule;
return preg_replace_callback("/{$reg}/is", [$this, '_' . $func], $data);
}
/**
* 解析img单标签
* @param array $data 解析数据
* @return string 解析后的标签
*/
private function _img($data)
{
$data[4] = $data[1];
return $this->_imgWidthAndHeight('', $data);
}
/**
* 解析url单标签
* @param array $data 解析数据
* @return string 解析后的标签
*/
private function _url($data)
{
$data[3] = $data[2];
$data[4] = $data[2] = $data[1];
return $this->_urlClass('', $data);
}
/**
* 解析没有属性的标签
* @param string $name 标签名
* @param array $data 解析数据 [2] - 标签内容
* @return string 解析后的标签
*/
private function _tag($name, $data)
{
return "<{$name}>{$data[2]}</{$name}>";
}
/**
* 解析代码
* @param string $name 标签名
* @param array $data 解析数据 [2] - 语言类型,[3] - 代码内容
* @return string 解析后的标签
*/
private function _code($name, $data)
{
$fix = ('pre' == $name) ? ['<pre>', '</pre>'] : ['', ''];
if (empty($data[2])) {
$data = "{$fix[0]}<code>{$data[3]}</code>{$fix[1]}";
} else {
$data = "{$fix[0]}<code data-lang=\"{$data[2]}\">{$data[3]}</code>{$fix[1]}";
}
return $data;
}
/**
* 解析含有width属性的标签
* @param string $name 标签名
* @param array $data 解析数据 [2] - width, [3] - 标签内容
* @return string 解析后的标签
*/
private function _width($name, $data)
{
if (empty($data[2])) {
$data = "<{$name}>{$data[3]}</{$name}>";
} else {
$data = "<{$name} width=\"{$data[2]}\">{$data[3]}</{$name}>";
}
return $data;
}
/**
* 解析含有width和height属性的标签
* @param string $name 标签名
* @param array $data 解析数据 [2] - width, [3] - height, [4] - 标签内容
* @return string 解析后的标签
*/
private function _widthAndHeight($name, $data)
{
if (empty($data[2]) && empty($data[3])) {
$data = "<{$name}>{$data[4]}</{$name}>";
} elseif (!empty($data[2]) && empty($data[3])) {
$data = "<{$name} width=\"{$data[2]}\">{$data[4]}</{$name}>";
} elseif (empty($data[2]) && !empty($data[3])) {
$data = "<{$name} height=\"{$data[3]}\">{$data[4]}</{$name}>";
} else {
$data = "<{$name} width=\"{$data[2]}\" height=\"{$data[3]}\">{$data[4]}</{$name}>";
}
return $data;
}
/**
* 解析含有width和height属性的图片标签
* @param string $name 标签名
* @param array $data 解析数据 [2] - width, [3] - height, [4] - 图片URL
* @return string 解析后的标签
*/
private function _imgWidthAndHeight($name, $data)
{
if (empty($data[2]) && empty($data[3])) {
$data = "<img src=\"{$data[4]}\" />";
} elseif (!empty($data[2]) && empty($data[3])) {
$data = "<img width=\"{$data[2]}\" src=\"{$data[4]}\" />";
} elseif (empty($data[2]) && !empty($data[3])) {
$data = "<img height=\"{$data[3]}\" src=\"{$data[4]}\" />";
} else {
$data = "<img width=\"{$data[2]}\" height=\"{$data[3]}\" src=\"{$data[4]}\" />";
}
return $data;
}
/**
* 解析含有class属性的标签
* @param string $name 标签名
* @param array $data 解析数据 [2] - class, [3] - 标签内容
* @return string 解析后的标签
*/
private function _class($name, $data)
{
if (empty($data[2])) {
$data = "<{$name}>{$data[3]}</{$name}>";
} else {
$data = "<{$name} class=\"{$data[2]}\">{$data[3]}</{$name}>";
}
return $data;
}
/**
* 解析含有class属性的url标签
* @param string $name 标签名
* @param array $data 解析数据 [2] - url, [3] - text
* @return string 解析后的标签
*/
private function _urlClass($name, $data)
{
empty($data[2]) && $data[2] = $data[4];
if (empty($data[3])) {
$data = "<a href=\"{$data[2]}\">{$data[4]}</a>";
} else {
$data = "<a href=\"{$data[2]}\" class=\"{$data[3]}\">{$data[4]}</a>";
}
return $data;
}
/**
* 解析含有class属性的email标签
* @param string $name 标签名
* @param array $data 解析数据 [2] - class, [3] - email地址
* @return string 解析后的标签
*/
private function _emailClass($name, $data)
{
//不是正确的EMAIL则不解析
if (preg_match('/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/', $data[3])) {
return $data[0];
}
//编码email地址防治被采集
$email = $this->encodeEmailAddress($data[3]);
if (empty($data[2])) {
$data = "<a href=\"{$email[0]}\">{$email[1]}</a>";
} else {
$data = "<a href=\"{$email[0]}\" class=\"{$data[2]}\">{$email[1]}</a>";
}
return $data;
}
/**
* 编码EMAIL地址可以防治部分采集软件
* @param string $addr EMAIL地址
* @return array 编码后的EMAIL地址 [0] - 带mailto, [1] - 不带mailto
*/
private function encodeEmailAddress($addr)
{
$addr = "mailto:" . $addr;
$chars = preg_split('/(?<!^)(?!$)/', $addr);
$seed = (int) abs(crc32($addr) / strlen($addr)); # Deterministic seed.
foreach ($chars as $key => $char) {
$ord = ord($char);
# Ignore non-ascii chars.
if ($ord < 128) {
$r = ($seed * (1 + $key)) % 100; # Pseudo-random function.
# roughly 10% raw, 45% hex, 45% dec
# '@' *must* be encoded. I insist.
if ($r > 90 && '@' != $char) /* do nothing */;
elseif ($r < 45) {
$chars[$key] = '&#x' . dechex($ord) . ';';
} else {
$chars[$key] = '&#' . $ord . ';';
}
}
}
$addr = implode('', $chars);
$text = implode('', array_slice($chars, 7)); # text without `mailto:`
return [$addr, $text];
}
}

View File

@@ -0,0 +1,87 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Haotong Lin<lofanmi@gmail.com>
// +----------------------------------------------------------------------
namespace org\transform\driver;
/**
* Base64编码实现
*
*/
class Base64
{
/**
* @access public
* @static 编解码目标
* default: 原始的编(解)码
* url : URL友好的编(解)码
* regex : 正则表达式友好的编(解)码
*/
public static $target = 'default';
/**
* Base64编码函数
*
* @param string $data 欲编码的数据
* @param string $target 编码目标
*/
public function encode($data, $target = '')
{
// 当函数没有特别指定编码目标时, 使用类自身编码目标
if (empty($target)) {
$target = self::$target;
}
// 进行一次原始编码
$data = base64_encode($data);
// 根据编码目标替换字符
switch ($target) {
case 'url':
$data = str_replace(['+', '/', '='], ['-', '_', ''], $data);
break;
case 'regex':
$data = str_replace(['+', '/', '='], ['!', '-', ''], $data);
break;
case 'default':
default:
break;
}
// 返回编码结果
return $data;
}
/**
* Base64解码函数
*
* @param string $data 欲解码的数据
* @param string $target 解码目标
*/
public function decode($data, $target = '')
{
// 当函数没有特别指定解码目标时, 使用类自身解码目标
if (empty($target)) {
$target = self::$target;
}
// 根据解码目标替换字符
switch ($target) {
case 'url':
$data = str_replace(['-', '_'], ['+', '/'], $data);
break;
case 'regex':
$data = str_replace(['!', '-'], ['+', '/'], $data);
break;
case 'default':
default:
break;
}
// 原始解码,并返回结果
return base64_decode($data);
}
}

View File

@@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\transform\driver;
class Json
{
public function encode($data)
{
return json_encode($data, JSON_UNESCAPED_UNICODE);
}
public function decode($data, $assoc = true)
{
return json_decode($data, $assoc);
}
}

View File

@@ -0,0 +1,124 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi.cn@gmail.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\transform\driver;
class Xml
{
/**
* XML数据默认配置项
* @var array
*/
private $config = [
'root_name' => 'think', //根节点名称
'root_attr' => [], //根节点属性
'item_name' => 'item', //数字节点转换的名称
'item_key' => 'id', //数字节点转换的属性名
];
/**
* 编码XML数据
* @param mixed $data 被编码的数据
* @param array $config 数据配置项
* @return string 编码后的XML数据
*/
public function encode($data, array $config = [])
{
//初始化配置
$config = array_merge($this->config, $config);
//创建XML对象
$xml = new \SimpleXMLElement("<{$config['root_name']}></{$config['root_name']}>");
self::data2xml($xml, $data, $config['item_name'], $config['item_key']);
return $xml->asXML();
}
/**
* 解码XML数据
* @param string $str XML字符串
* @param boolean $assoc 是否转换为数组
* @param array $config 数据配置项
* @return string 解码后的XML数据
*/
public function decode($str, $assoc = true, array $config = [])
{
//初始化配置
$config = array_merge($this->config, $config);
//创建XML对象
$xml = new \SimpleXMLElement($str);
if ($assoc) {
self::xml2data($xml, $data, $config['item_name'], $config['item_key']);
return $data;
}
return $xml;
}
/**
* 数据XML编码
* @static
* @access public
* @param mixed $data 数据
* @param string $item 数字索引时的节点名称
* @param string $id 数字索引key转换为的属性名
* @return string
*/
public static function data2xml(\SimpleXMLElement $xml, $data, $item = 'item', $id = 'id')
{
foreach ($data as $key => $value) {
//指定默认的数字key
if (is_numeric($key)) {
$id && $val = $key;
$key = $item;
}
//添加子元素
if (is_array($value) || is_object($value)) {
$child = $xml->addChild($key);
self::data2xml($child, $value, $item, $id);
} else {
$child = $xml->addChild($key, $value);
}
//记录原来的key
isset($val) && $child->addAttribute($id, $val);
}
}
/**
* 数据XML解码
* @static
* @access public
* @param SimpleXMLElement $xml xml对象
* @param array $data 解码后的数据
* @param string $item 数字索引时的节点名称
* @param string $id 数字索引key转换为的属性名
*/
public static function xml2data(SimpleXMLElement $xml, &$data, $item = 'item', $id = 'id')
{
foreach ($xml->children() as $items) {
$key = $items->getName();
$attr = $items->attributes();
if ($key == $item && isset($attr[$id])) {
$key = strval($attr[$id]);
}
if ($items->count()) {
self::xml2data($items, $val);
} else {
$val = strval($items);
}
$data[$key] = $val;
}
}
}

View File

@@ -0,0 +1,175 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\upload\driver;
use think\Exception;
class Ftp
{
/**
* 上传文件根目录
* @var string
*/
private $rootPath;
/**
* 本地上传错误信息
* @var string
*/
private $error = ''; //上传错误信息
/**
* FTP连接
* @var resource
*/
private $link;
private $config = [
'host' => '', //服务器
'port' => 21, //端口
'timeout' => 90, //超时时间
'username' => '', //用户名
'password' => '', //密码
];
/**
* 构造函数,用于设置上传根路径
* @param array $config FTP配置
*/
public function __construct($config)
{
/* 默认FTP配置 */
$this->config = array_merge($this->config, $config);
/* 登录FTP服务器 */
if (!$this->login()) {
throw new Exception($this->error);
}
}
/**
* 检测上传根目录
* @param string $rootpath 根目录
* @return boolean true-检测通过false-检测失败
*/
public function checkRootPath($rootpath)
{
/* 设置根目录 */
$this->rootPath = ftp_pwd($this->link) . '/' . ltrim($rootpath, '/');
if (!@ftp_chdir($this->link, $this->rootPath)) {
$this->error = '上传根目录不存在!';
return false;
}
return true;
}
/**
* 检测上传目录
* @param string $savepath 上传目录
* @return boolean 检测结果true-通过false-失败
*/
public function checkSavePath($savepath)
{
/* 检测并创建目录 */
if (!$this->mkdir($savepath)) {
return false;
} else {
//TODO:检测目录是否可写
return true;
}
}
/**
* 保存指定文件
* @param array $file 保存的文件信息
* @param boolean $replace 同名文件是否覆盖
* @return boolean 保存状态true-成功false-失败
*/
public function save($file, $replace = true)
{
$filename = $this->rootPath . $file['savepath'] . $file['savename'];
/* 不覆盖同名文件 */
// if (!$replace && is_file($filename)) {
// $this->error = '存在同名文件' . $file['savename'];
// return false;
// }
/* 移动文件 */
if (!ftp_put($this->link, $filename, $file['tmp_name'], FTP_BINARY)) {
$this->error = '文件上传保存错误!';
return false;
}
return true;
}
/**
* 创建目录
* @param string $savepath 要创建的目录
* @return boolean 创建状态true-成功false-失败
*/
public function mkdir($savepath)
{
$dir = $this->rootPath . $savepath;
if (ftp_chdir($this->link, $dir)) {
return true;
}
if (ftp_mkdir($this->link, $dir)) {
return true;
} elseif ($this->mkdir(dirname($savepath)) && ftp_mkdir($this->link, $dir)) {
return true;
} else {
$this->error = "目录 {$savepath} 创建失败!";
return false;
}
}
/**
* 获取最后一次上传错误信息
* @return string 错误信息
*/
public function getError()
{
return $this->error;
}
/**
* 登录到FTP服务器
* @return boolean true-登录成功false-登录失败
*/
private function login()
{
extract($this->config);
$this->link = ftp_connect($host, $port, $timeout);
if ($this->link) {
if (ftp_login($this->link, $username, $password)) {
return true;
} else {
$this->error = "无法登录到FTP服务器username - {$username}";
}
} else {
$this->error = "无法连接到FTP服务器{$host}";
}
return false;
}
/**
* 析构方法用于断开当前FTP连接
*/
public function __destruct()
{
ftp_close($this->link);
}
}

View File

@@ -0,0 +1,126 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\upload\driver;
class Local
{
/**
* 上传文件根目录
* @var string
*/
private $rootPath;
/**
* 本地上传错误信息
* @var string
*/
private $error = ''; //上传错误信息
/**
* 构造函数,用于设置上传根路径
*/
public function __construct($config = null)
{
}
/**
* 检测上传根目录
* @param string $rootpath 根目录
* @return boolean true-检测通过false-检测失败
*/
public function checkRootPath($rootpath)
{
if (!(is_dir($rootpath) && is_writable($rootpath))) {
$this->error = '上传根目录不存在!请尝试手动创建:' . $rootpath;
return false;
}
$this->rootPath = $rootpath;
return true;
}
/**
* 检测上传目录
* @param string $savepath 上传目录
* @return boolean 检测结果true-通过false-失败
*/
public function checkSavePath($savepath)
{
/* 检测并创建目录 */
if (!$this->mkdir($savepath)) {
return false;
} else {
/* 检测目录是否可写 */
if (!is_writable($this->rootPath . $savepath)) {
$this->error = '上传目录 ' . $savepath . ' 不可写!';
return false;
} else {
return true;
}
}
}
/**
* 保存指定文件
* @param array $file 保存的文件信息
* @param boolean $replace 同名文件是否覆盖
* @return boolean 保存状态true-成功false-失败
*/
public function save($file, $replace = true)
{
$filename = $this->rootPath . $file['savepath'] . $file['savename'];
/* 不覆盖同名文件 */
if (!$replace && is_file($filename)) {
$this->error = '存在同名文件' . $file['savename'];
return false;
}
/* 移动文件 */
if (!move_uploaded_file($file['tmp_name'], $filename)) {
$this->error = '文件上传保存错误!';
return false;
}
return true;
}
/**
* 创建目录
* @param string $savepath 要创建的目录
* @return boolean 创建状态true-成功false-失败
*/
public function mkdir($savepath)
{
$dir = $this->rootPath . $savepath;
if (is_dir($dir)) {
return true;
}
if (mkdir($dir, 0777, true)) {
return true;
} else {
$this->error = "目录 {$savepath} 创建失败!";
return false;
}
}
/**
* 获取最后一次上传错误信息
* @return string 错误信息
*/
public function getError()
{
return $this->error;
}
}

View File

@@ -0,0 +1,110 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yangweijie <yangweijiester@gmail.com> <http://www.code-tech.diandian.com>
// +----------------------------------------------------------------------
namespace org\upload\driver;
use org\upload\driver\qiniu\QiniuStorage;
class Qiniu
{
/**
* 上传文件根目录
* @var string
*/
private $rootPath;
/**
* 上传错误信息
* @var string
*/
private $error = '';
private $config = [
'secretKey' => '', //七牛服务器
'accessKey' => '', //七牛用户
'domain' => '', //七牛密码
'bucket' => '', //空间名称
'timeout' => 300, //超时时间
];
/**
* 构造函数,用于设置上传根路径
* @param array $config FTP配置
*/
public function __construct($config)
{
$this->config = array_merge($this->config, $config);
/* 设置根目录 */
$this->qiniu = new QiniuStorage($config);
}
/**
* 检测上传根目录(七牛上传时支持自动创建目录,直接返回)
* @param string $rootpath 根目录
* @return boolean true-检测通过false-检测失败
*/
public function checkRootPath($rootpath)
{
$this->rootPath = trim($rootpath, './') . '/';
return true;
}
/**
* 检测上传目录(七牛上传时支持自动创建目录,直接返回)
* @param string $savepath 上传目录
* @return boolean 检测结果true-通过false-失败
*/
public function checkSavePath($savepath)
{
return true;
}
/**
* 创建文件夹 (七牛上传时支持自动创建目录,直接返回)
* @param string $savepath 目录名称
* @return boolean true-创建成功false-创建失败
*/
public function mkdir($savepath)
{
return true;
}
/**
* 保存指定文件
* @param array $file 保存的文件信息
* @param boolean $replace 同名文件是否覆盖
* @return boolean 保存状态true-成功false-失败
*/
public function save(&$file, $replace = true)
{
$file['name'] = $file['savepath'] . $file['savename'];
$key = str_replace('/', '_', $file['name']);
$upfile = [
'name' => 'file',
'fileName' => $key,
'fileBody' => file_get_contents($file['tmp_name']),
];
$config = [];
$result = $this->qiniu->upload($config, $upfile);
$url = $this->qiniu->downlink($key);
$file['url'] = $url;
return false === $result ? false : true;
}
/**
* 获取最后一次上传错误信息
* @return string 错误信息
*/
public function getError()
{
return $this->qiniu->errorStr;
}
}

View File

@@ -0,0 +1,114 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: luofei614<weibo.com/luofei614>
// +----------------------------------------------------------------------
namespace org\upload\driver;
class Sae
{
/**
* Storage的Domain
* @var string
*/
private $domain = '';
private $rootPath = '';
/**
* 本地上传错误信息
* @var string
*/
private $error = '';
/**
* 构造函数设置storage的domain 如果有传配置则domain为配置项如果没有传domain为第一个路径的目录名称。
* @param mixed $config 上传配置
*/
public function __construct($config = null)
{
if (is_array($config) && !empty($config['domain'])) {
$this->domain = strtolower($config['domain']);
}
}
/**
* 检测上传根目录
* @param string $rootpath 根目录
* @return boolean true-检测通过false-检测失败
*/
public function checkRootPath($rootpath)
{
$rootpath = trim($rootpath, './');
if (!$this->domain) {
$rootpath = explode('/', $rootpath);
$this->domain = strtolower(array_shift($rootpath));
$rootpath = implode('/', $rootpath);
}
$this->rootPath = $rootpath;
$st = new \SaeStorage();
if (false === $st->getDomainCapacity($this->domain)) {
$this->error = '您好像没有建立Storage的domain[' . $this->domain . ']';
return false;
}
return true;
}
/**
* 检测上传目录
* @param string $savepath 上传目录
* @return boolean 检测结果true-通过false-失败
*/
public function checkSavePath($savepath)
{
return true;
}
/**
* 保存指定文件
* @param array $file 保存的文件信息
* @param boolean $replace 同名文件是否覆盖
* @return boolean 保存状态true-成功false-失败
*/
public function save(&$file, $replace = true)
{
$filename = ltrim($this->rootPath . '/' . $file['savepath'] . $file['savename'], '/');
$st = new \SaeStorage();
/* 不覆盖同名文件 */
if (!$replace && $st->fileExists($this->domain, $filename)) {
$this->error = '存在同名文件' . $file['savename'];
return false;
}
/* 移动文件 */
if (!$st->upload($this->domain, $filename, $file['tmp_name'])) {
$this->error = '文件上传保存错误![' . $st->errno() . ']:' . $st->errmsg();
return false;
} else {
$file['url'] = $st->getUrl($this->domain, $filename);
}
return true;
}
public function mkdir()
{
return true;
}
/**
* 获取最后一次上传错误信息
* @return string 错误信息
*/
public function getError()
{
return $this->error;
}
}

View File

@@ -0,0 +1,230 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace org\upload\driver;
class Upyun
{
/**
* 上传文件根目录
* @var string
*/
private $rootPath;
/**
* 上传错误信息
* @var string
*/
private $error = '';
private $config = [
'host' => '', //又拍云服务器
'username' => '', //又拍云用户
'password' => '', //又拍云密码
'bucket' => '', //空间名称
'timeout' => 90, //超时时间
];
/**
* 构造函数,用于设置上传根路径
* @param array $config FTP配置
*/
public function __construct($config)
{
/* 默认FTP配置 */
$this->config = array_merge($this->config, $config);
$this->config['password'] = md5($this->config['password']);
}
/**
* 检测上传根目录(又拍云上传时支持自动创建目录,直接返回)
* @param string $rootpath 根目录
* @return boolean true-检测通过false-检测失败
*/
public function checkRootPath($rootpath)
{
/* 设置根目录 */
$this->rootPath = trim($rootpath, './') . '/';
return true;
}
/**
* 检测上传目录(又拍云上传时支持自动创建目录,直接返回)
* @param string $savepath 上传目录
* @return boolean 检测结果true-通过false-失败
*/
public function checkSavePath($savepath)
{
return true;
}
/**
* 创建文件夹 (又拍云上传时支持自动创建目录,直接返回)
* @param string $savepath 目录名称
* @return boolean true-创建成功false-创建失败
*/
public function mkdir($savepath)
{
return true;
}
/**
* 保存指定文件
* @param array $file 保存的文件信息
* @param boolean $replace 同名文件是否覆盖
* @return boolean 保存状态true-成功false-失败
*/
public function save($file, $replace = true)
{
$header['Content-Type'] = $file['type'];
$header['Content-MD5'] = $file['md5'];
$header['Mkdir'] = 'true';
$resource = fopen($file['tmp_name'], 'r');
$save = $this->rootPath . $file['savepath'] . $file['savename'];
$data = $this->request($save, 'PUT', $header, $resource);
return false === $data ? false : true;
}
/**
* 获取最后一次上传错误信息
* @return string 错误信息
*/
public function getError()
{
return $this->error;
}
/**
* 请求又拍云服务器
* @param string $path 请求的PATH
* @param string $method 请求方法
* @param array $headers 请求header
* @param resource $body 上传文件资源
* @return boolean
*/
private function request($path, $method, $headers = null, $body = null)
{
$uri = "/{$this->config['bucket']}/{$path}";
$ch = curl_init($this->config['host'] . $uri);
$_headers = ['Expect:'];
if (!is_null($headers) && is_array($headers)) {
foreach ($headers as $k => $v) {
array_push($_headers, "{$k}: {$v}");
}
}
$length = 0;
$date = gmdate('D, d M Y H:i:s \G\M\T');
if (!is_null($body)) {
if (is_resource($body)) {
fseek($body, 0, SEEK_END);
$length = ftell($body);
fseek($body, 0);
array_push($_headers, "Content-Length: {$length}");
curl_setopt($ch, CURLOPT_INFILE, $body);
curl_setopt($ch, CURLOPT_INFILESIZE, $length);
} else {
$length = @strlen($body);
array_push($_headers, "Content-Length: {$length}");
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
} else {
array_push($_headers, "Content-Length: {$length}");
}
array_push($_headers, 'Authorization: ' . $this->sign($method, $uri, $date, $length));
array_push($_headers, "Date: {$date}");
curl_setopt($ch, CURLOPT_HTTPHEADER, $_headers);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->config['timeout']);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
if ('PUT' == $method || 'POST' == $method) {
curl_setopt($ch, CURLOPT_POST, 1);
} else {
curl_setopt($ch, CURLOPT_POST, 0);
}
if ('HEAD' == $method) {
curl_setopt($ch, CURLOPT_NOBODY, true);
}
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
list($header, $body) = explode("\r\n\r\n", $response, 2);
if (200 == $status) {
if ('GET' == $method) {
return $body;
} else {
$data = $this->response($header);
return count($data) > 0 ? $data : true;
}
} else {
$this->error($header);
return false;
}
}
/**
* 获取响应数据
* @param string $text 响应头字符串
* @return array 响应数据列表
*/
private function response($text)
{
$headers = explode("\r\n", $text);
$items = [];
foreach ($headers as $header) {
$header = trim($header);
if (strpos($header, 'x-upyun') !== false) {
list($k, $v) = explode(':', $header);
$items[trim($k)] = in_array(substr($k, 8, 5), ['width', 'heigh', 'frame']) ? intval($v) : trim($v);
}
}
return $items;
}
/**
* 生成请求签名
* @param string $method 请求方法
* @param string $uri 请求URI
* @param string $date 请求时间
* @param integer $length 请求内容大小
* @return string 请求签名
*/
private function sign($method, $uri, $date, $length)
{
$sign = "{$method}&{$uri}&{$date}&{$length}&{$this->config['password']}";
return 'UpYun ' . $this->config['username'] . ':' . md5($sign);
}
/**
* 获取请求错误信息
* @param string $header 请求返回头信息
*/
private function error($header)
{
list($status, $stash) = explode("\r\n", $header, 2);
list($v, $code, $message) = explode(" ", $status, 3);
$message = is_null($message) ? 'File Not Found' : "[{$status}]:{$message}";
$this->error = $message;
}
}

View File

@@ -0,0 +1,366 @@
<?php
namespace org\upload\driver\qiniu;
class QiniuStorage
{
public $QINIU_RSF_HOST = 'http://rsf.qbox.me';
public $QINIU_RS_HOST = 'http://rs.qbox.me';
public $QINIU_UP_HOST = 'http://up.qiniu.com';
public $timeout = '';
public function __construct($config)
{
$this->sk = $config['secretKey'];
$this->ak = $config['accessKey'];
$this->domain = $config['domain'];
$this->bucket = $config['bucket'];
$this->timeout = isset($config['timeout']) ? $config['timeout'] : 3600;
}
public static function sign($sk, $ak, $data)
{
$sign = hash_hmac('sha1', $data, $sk, true);
return $ak . ':' . self::qiniuEncode($sign);
}
public static function signWithData($sk, $ak, $data)
{
$data = self::qiniuEncode($data);
return self::sign($sk, $ak, $data) . ':' . $data;
}
public function accessToken($url, $body = '')
{
$parsed_url = parse_url($url);
$path = $parsed_url['path'];
$access = $path;
if (isset($parsed_url['query'])) {
$access .= "?" . $parsed_url['query'];
}
$access .= "\n";
if ($body) {
$access .= $body;
}
return self::sign($this->sk, $this->ak, $access);
}
public function UploadToken($sk, $ak, $param)
{
$param['deadline'] = 0 == $param['Expires'] ? 3600 : $param['Expires'];
$param['deadline'] += time();
$data = ['scope' => $this->bucket, 'deadline' => $param['deadline']];
if (!empty($param['CallbackUrl'])) {
$data['callbackUrl'] = $param['CallbackUrl'];
}
if (!empty($param['CallbackBody'])) {
$data['callbackBody'] = $param['CallbackBody'];
}
if (!empty($param['ReturnUrl'])) {
$data['returnUrl'] = $param['ReturnUrl'];
}
if (!empty($param['ReturnBody'])) {
$data['returnBody'] = $param['ReturnBody'];
}
if (!empty($param['AsyncOps'])) {
$data['asyncOps'] = $param['AsyncOps'];
}
if (!empty($param['EndUser'])) {
$data['endUser'] = $param['EndUser'];
}
$data = json_encode($data);
return self::SignWithData($sk, $ak, $data);
}
public function upload($config, $file)
{
$uploadToken = $this->UploadToken($this->sk, $this->ak, $config);
$url = "{$this->QINIU_UP_HOST}";
$mimeBoundary = md5(microtime());
$header = ['Content-Type' => 'multipart/form-data;boundary=' . $mimeBoundary];
$data = [];
$fields = [
'token' => $uploadToken,
'key' => $config['saveName'] ?: $file['fileName'],
];
if (is_array($config['custom_fields']) && [] !== $config['custom_fields']) {
$fields = array_merge($fields, $config['custom_fields']);
}
foreach ($fields as $name => $val) {
array_push($data, '--' . $mimeBoundary);
array_push($data, "Content-Disposition: form-data; name=\"$name\"");
array_push($data, '');
array_push($data, $val);
}
//文件
array_push($data, '--' . $mimeBoundary);
$name = $file['name'];
$fileName = $file['fileName'];
$fileBody = $file['fileBody'];
$fileName = self::qiniuEscapequotes($fileName);
array_push($data, "Content-Disposition: form-data; name=\"$name\"; filename=\"$fileName\"");
array_push($data, 'Content-Type: application/octet-stream');
array_push($data, '');
array_push($data, $fileBody);
array_push($data, '--' . $mimeBoundary . '--');
array_push($data, '');
$body = implode("\r\n", $data);
$response = $this->request($url, 'POST', $header, $body);
return $response;
}
public function dealWithType($key, $type)
{
$param = $this->buildUrlParam();
$url = '';
switch ($type) {
case 'img':
$url = $this->downLink($key);
if ($param['imageInfo']) {
$url .= '?imageInfo';
} else if ($param['exif']) {
$url .= '?exif';
} else if ($param['imageView']) {
$url .= '?imageView/' . $param['mode'];
if ($param['w']) {
$url .= "/w/{$param['w']}";
}
if ($param['h']) {
$url .= "/h/{$param['h']}";
}
if ($param['q']) {
$url .= "/q/{$param['q']}";
}
if ($param['format']) {
$url .= "/format/{$param['format']}";
}
}
break;
case 'video'://TODO 视频处理
case 'doc':
$url = $this->downLink($key);
$url .= '?md2html';
if (isset($param['mode'])) {
$url .= '/' . (int) $param['mode'];
}
if ($param['cssurl']) {
$url .= '/' . self::qiniuEncode($param['cssurl']);
}
break;
}
return $url;
}
public function buildUrlParam()
{
return $_REQUEST;
}
//获取某个路径下的文件列表
public function getList($query = [], $path = '')
{
$query = array_merge(['bucket' => $this->bucket], $query);
$url = "{$this->QINIU_RSF_HOST}/list?" . http_build_query($query);
$accessToken = $this->accessToken($url);
$response = $this->request($url, 'POST', ['Authorization' => "QBox $accessToken"]);
return $response;
}
//获取某个文件的信息
public function info($key)
{
$key = trim($key);
$url = "{$this->QINIU_RS_HOST}/stat/" . self::qiniuEncode("{$this->bucket}:{$key}");
$accessToken = $this->accessToken($url);
$response = $this->request($url, 'POST', [
'Authorization' => "QBox $accessToken",
]);
return $response;
}
//获取文件下载资源链接
public function downLink($key)
{
$key = urlencode($key);
$key = self::qiniuEscapequotes($key);
$url = "http://{$this->domain}/{$key}";
return $url;
}
//重命名单个文件
public function rename($file, $new_file)
{
$key = trim($file);
$url = "{$this->QINIU_RS_HOST}/move/" . self::qiniuEncode("{$this->bucket}:{$key}") . '/' . self::qiniuEncode("{$this->bucket}:{$new_file}");
trace($url);
$accessToken = $this->accessToken($url);
$response = $this->request($url, 'POST', ['Authorization' => "QBox $accessToken"]);
return $response;
}
//删除单个文件
public function del($file)
{
$key = trim($file);
$url = "{$this->QINIU_RS_HOST}/delete/" . self::qiniuEncode("{$this->bucket}:{$key}");
$accessToken = $this->accessToken($url);
$response = $this->request($url, 'POST', ['Authorization' => "QBox $accessToken"]);
return $response;
}
//批量删除文件
public function delBatch($files)
{
$url = $this->QINIU_RS_HOST . '/batch';
$ops = [];
foreach ($files as $file) {
$ops[] = "/delete/" . self::qiniuEncode("{$this->bucket}:{$file}");
}
$params = 'op=' . implode('&op=', $ops);
$url .= '?' . $params;
trace($url);
$accessToken = $this->accessToken($url);
$response = $this->request($url, 'POST', ['Authorization' => "QBox $accessToken"]);
return $response;
}
public static function qiniuEncode($str)
{
// URLSafeBase64Encode
$find = ['+', '/'];
$replace = ['-', '_'];
return str_replace($find, $replace, base64_encode($str));
}
public static function qiniuEscapequotes($str)
{
$find = ["\\", "\""];
$replace = ["\\\\", "\\\""];
return str_replace($find, $replace, $str);
}
/**
* 请求云服务器
* @param string $path 请求的PATH
* @param string $method 请求方法
* @param array $headers 请求header
* @param resource $body 上传文件资源
* @return boolean
*/
private function request($path, $method, $headers = null, $body = null)
{
$ch = curl_init($path);
$_headers = ['Expect:'];
if (!is_null($headers) && is_array($headers)) {
foreach ($headers as $k => $v) {
array_push($_headers, "{$k}: {$v}");
}
}
$length = 0;
$date = gmdate('D, d M Y H:i:s \G\M\T');
if (!is_null($body)) {
if (is_resource($body)) {
fseek($body, 0, SEEK_END);
$length = ftell($body);
fseek($body, 0);
array_push($_headers, "Content-Length: {$length}");
curl_setopt($ch, CURLOPT_INFILE, $body);
curl_setopt($ch, CURLOPT_INFILESIZE, $length);
} else {
$length = @strlen($body);
array_push($_headers, "Content-Length: {$length}");
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
} else {
array_push($_headers, "Content-Length: {$length}");
}
// array_push($_headers, 'Authorization: ' . $this->sign($method, $uri, $date, $length));
array_push($_headers, "Date: {$date}");
curl_setopt($ch, CURLOPT_HTTPHEADER, $_headers);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
if ('PUT' == $method || 'POST' == $method) {
curl_setopt($ch, CURLOPT_POST, 1);
} else {
curl_setopt($ch, CURLOPT_POST, 0);
}
if ('HEAD' == $method) {
curl_setopt($ch, CURLOPT_NOBODY, true);
}
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
list($header, $body) = explode("\r\n\r\n", $response, 2);
if (200 == $status) {
if ('GET' == $method) {
return $body;
} else {
return $this->response($response);
}
} else {
$this->error($header, $body);
return false;
}
}
/**
* 获取响应数据
* @param string $text 响应头字符串
* @return array 响应数据列表
*/
private function response($text)
{
$headers = explode(PHP_EOL, $text);
$items = [];
foreach ($headers as $header) {
$header = trim($header);
if (strpos($header, '{') !== false) {
$items = json_decode($header, 1);
break;
}
}
return $items;
}
/**
* 获取请求错误信息
* @param string $header 请求返回头信息
*/
private function error($header, $body)
{
list($status, $stash) = explode("\r\n", $header, 2);
list($v, $code, $message) = explode(" ", $status, 3);
$message = is_null($message) ? 'File Not Found' : "[{$status}]:{$message}]";
$this->error = $message;
$this->errorStr = json_decode($body, 1);
$this->errorStr = $this->errorStr['error'];
}
}

BIN
extend/org/verify/bgs/1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
extend/org/verify/bgs/2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
extend/org/verify/bgs/3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
extend/org/verify/bgs/4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
extend/org/verify/bgs/5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
extend/org/verify/bgs/6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
extend/org/verify/bgs/7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
extend/org/verify/bgs/8.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.