作者 Karson

--no commit message

要显示太多修改。

为保证性能只显示 16 of 16+ 个文件。

*
!.gitignore
\ No newline at end of file
deny from all
\ No newline at end of file
... ...
<?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>
// +----------------------------------------------------------------------
// | 修改者: anuo (本权限类在原3.2.3的基础上修改过来的)
// +----------------------------------------------------------------------
namespace fast;
use think\Db;
use think\Config;
use think\Session;
use think\Request;
use think\Loader;
/**
* 权限认证类
* 功能特性:
* 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 表中定义一条规则,condition字段就可以定义规则表达式。 如定义{score}>5 and {score}<100
* 表示用户的分数在5-100之间时这条规则才会通过。
*/
class Auth
{
/**
* @var object 对象实例
*/
protected static $instance;
/**
* 当前请求实例
* @var Request
*/
protected $request;
//默认配置
protected $config = [
'auth_on' => 1, // 权限开关
'auth_type' => 1, // 认证方式,1为实时认证;2为登录认证。
'auth_group' => 'auth_group', // 用户组数据表名
'auth_group_access' => 'auth_group_access', // 用户-用户组关系表
'auth_rule' => 'auth_rule', // 权限规则表
'auth_user' => 'user', // 用户信息表
];
/**
* 类架构函数
* Auth constructor.
*/
public function __construct()
{
if ($auth = Config::get('auth'))
{
$this->config = array_merge($this->config, $auth);
}
// 初始化request
$this->request = Request::instance();
}
/**
* 初始化
* @access public
* @param array $options 参数
* @return Auth
*/
public static function instance($options = [])
{
if (is_null(self::$instance))
{
self::$instance = new static($options);
}
return self::$instance;
}
/**
* 检查权限
* @param $name string|array 需要验证的规则列表,支持逗号分隔的权限规则或索引数组
* @param $uid int 认证用户的id
* @param string $relation 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证
* @param string $mode 执行验证的模式,可分为url,normal
* @return bool 通过验证返回true;失败返回false
*/
public function check($name, $uid, $relation = 'or', $mode = 'url')
{
if (!$this->config['auth_on'])
{
return true;
}
// 获取用户需要验证的所有有效规则列表
$rulelist = $this->getRuleList($uid);
if (in_array('*', $rulelist))
return true;
if (is_string($name))
{
$name = strtolower($name);
if (strpos($name, ',') !== false)
{
$name = explode(',', $name);
}
else
{
$name = [$name];
}
}
$list = []; //保存验证通过的规则名
if ('url' == $mode)
{
$REQUEST = unserialize(strtolower(serialize($this->request->param())));
}
foreach ($rulelist as $rule)
{
$query = preg_replace('/^.+\?/U', '', $rule);
if ('url' == $mode && $query != $rule)
{
parse_str($query, $param); //解析规则中的param
$intersect = array_intersect_assoc($REQUEST, $param);
$rule = preg_replace('/\?.*$/U', '', $rule);
if (in_array($rule, $name) && $intersect == $param)
{
//如果节点相符且url参数满足
$list[] = $rule;
}
}
else
{
if (in_array($rule, $name))
{
$list[] = $rule;
}
}
}
if ('or' == $relation && !empty($list))
{
return true;
}
$diff = array_diff($name, $list);
if ('and' == $relation && empty($diff))
{
return true;
}
return false;
}
/**
* 根据用户id获取用户组,返回值为数组
* @param $uid int 用户id
* @return array 用户所属的用户组 array(
* array('uid'=>'用户id','group_id'=>'用户组id','name'=>'用户组名称','rules'=>'用户组拥有的规则id,多个,号隔开'),
* ...)
*/
public function getGroups($uid)
{
static $groups = [];
if (isset($groups[$uid]))
{
return $groups[$uid];
}
// 转换表名
$auth_group_access = Loader::parseName($this->config['auth_group_access'], 1);
$auth_group = Loader::parseName($this->config['auth_group'], 1);
// 执行查询
$user_groups = Db::view($auth_group_access, 'uid,group_id')
->view($auth_group, 'id,pid,name,rules', "{$auth_group_access}.group_id={$auth_group}.id", 'LEFT')
->where("{$auth_group_access}.uid='{$uid}' and {$auth_group}.status='normal'")
->select();
$groups[$uid] = $user_groups ? : [];
return $groups[$uid];
}
/**
* 获得权限规则列表
* @param integer $uid 用户id
* @return array
*/
public function getRuleList($uid)
{
static $_rulelist = []; //保存用户验证通过的权限列表
if (isset($_rulelist[$uid]))
{
return $_rulelist[$uid];
}
if (2 == $this->config['auth_type'] && Session::has('_rule_list_' . $uid))
{
return Session::get('_rule_list_' . $uid);
}
// 读取用户规则节点
$ids = $this->getRuleIds($uid);
if (empty($ids))
{
$_rulelist[$uid] = [];
return [];
}
// 筛选条件
$where = [
'status' => 'normal'
];
if (!in_array('*', $ids))
{
$where['id'] = ['in', $ids];
}
//读取用户组所有权限规则
$rules = Db::name($this->config['auth_rule'])->where($where)->field('id,condition,name,ismenu')->select();
//循环规则,判断结果。
$rulelist = []; //
foreach ($rules as $rule)
{
//超级管理员无需验证condition
if (!empty($rule['condition']) && !in_array('*', $ids))
{
//根据condition进行验证
$user = $this->getUserInfo($uid); //获取用户信息,一维数组
$command = preg_replace('/\{(\w*?)\}/', '$user[\'\\1\']', $rule['condition']);
@(eval('$condition=(' . $command . ');'));
if ($condition)
{
$rulelist[$rule['id']] = strtolower($rule['name']);
}
}
else
{
//只要存在就记录
$rulelist[$rule['id']] = strtolower($rule['name']);
}
}
$_rulelist[$uid] = $rulelist;
//登录验证则需要保存规则列表
if (2 == $this->config['auth_type'])
{
//规则列表结果保存到session
Session::set('_rule_list_' . $uid, $rulelist);
}
return array_unique($rulelist);
}
public function getRuleIds($uid)
{
//读取用户所属用户组
$groups = $this->getGroups($uid);
$ids = []; //保存用户所属用户组设置的所有权限规则id
foreach ($groups as $g)
{
$ids = array_merge($ids, explode(',', trim($g['rules'], ',')));
}
$ids = array_unique($ids);
return $ids;
}
/**
* 获得用户资料
* @param $uid
* @return mixed
*/
protected function getUserInfo($uid)
{
static $user_info = [];
$user = Db::name($this->config['auth_user']);
// 获取用户表主键
$_pk = is_string($user->getPk()) ? $user->getPk() : 'uid';
if (!isset($user_info[$uid]))
{
$user_info[$uid] = $user->where($_pk, $uid)->find();
}
return $user_info[$uid];
}
}
... ...
<?php
namespace fast;
/**
* 日期时间处理类
*/
class Date
{
const YEAR = 31536000;
const MONTH = 2592000;
const WEEK = 604800;
const DAY = 86400;
const HOUR = 3600;
const MINUTE = 60;
/**
* 计算两个时区间相差的时长,单位为秒
*
* $seconds = self::offset('America/Chicago', 'GMT');
*
* [!!] A list of time zones that PHP supports can be found at
* <http://php.net/timezones>.
*
* @param string $remote timezone that to find the offset of
* @param string $local timezone used as the baseline
* @param mixed $now UNIX timestamp or date string
* @return integer
*/
public static function offset($remote, $local = NULL, $now = NULL)
{
if ($local === NULL)
{
// Use the default timezone
$local = date_default_timezone_get();
}
if (is_int($now))
{
// Convert the timestamp into a string
$now = date(DateTime::RFC2822, $now);
}
// Create timezone objects
$zone_remote = new DateTimeZone($remote);
$zone_local = new DateTimeZone($local);
// Create date objects from timezones
$time_remote = new DateTime($now, $zone_remote);
$time_local = new DateTime($now, $zone_local);
// Find the offset
$offset = $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local);
return $offset;
}
/**
* 计算两个时间戳之间相差的时间
*
* $span = self::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2)
* $span = self::span(60, 182, 'minutes'); // 2
*
* @param int $remote timestamp to find the span of
* @param int $local timestamp to use as the baseline
* @param string $output formatting string
* @return string when only a single output is requested
* @return array associative list of all outputs requested
*/
public static function span($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
{
// Normalize output
$output = trim(strtolower((string) $output));
if (!$output)
{
// Invalid output
return FALSE;
}
// Array with the output formats
$output = preg_split('/[^a-z]+/', $output);
// Convert the list of outputs to an associative array
$output = array_combine($output, array_fill(0, count($output), 0));
// Make the output values into keys
extract(array_flip($output), EXTR_SKIP);
if ($local === NULL)
{
// Calculate the span from the current time
$local = time();
}
// Calculate timespan (seconds)
$timespan = abs($remote - $local);
if (isset($output['years']))
{
$timespan -= self::YEAR * ($output['years'] = (int) floor($timespan / self::YEAR));
}
if (isset($output['months']))
{
$timespan -= self::MONTH * ($output['months'] = (int) floor($timespan / self::MONTH));
}
if (isset($output['weeks']))
{
$timespan -= self::WEEK * ($output['weeks'] = (int) floor($timespan / self::WEEK));
}
if (isset($output['days']))
{
$timespan -= self::DAY * ($output['days'] = (int) floor($timespan / self::DAY));
}
if (isset($output['hours']))
{
$timespan -= self::HOUR * ($output['hours'] = (int) floor($timespan / self::HOUR));
}
if (isset($output['minutes']))
{
$timespan -= self::MINUTE * ($output['minutes'] = (int) floor($timespan / self::MINUTE));
}
// Seconds ago, 1
if (isset($output['seconds']))
{
$output['seconds'] = $timespan;
}
if (count($output) === 1)
{
// Only a single output was requested, return it
return array_pop($output);
}
// Return array
return $output;
}
/**
* 格式化 UNIX 时间戳为人易读的字符串
*
* @param int Unix 时间戳
* @param mixed $local 本地时间
*
* @return string 格式化的日期字符串
*/
public static function human($remote, $local = null)
{
$timediff = (is_null($local) || $local ? time() : $local) - $remote;
$chunks = array(
array(60 * 60 * 24 * 365, 'year'),
array(60 * 60 * 24 * 30, 'month'),
array(60 * 60 * 24 * 7, 'week'),
array(60 * 60 * 24, 'day'),
array(60 * 60, 'hour'),
array(60, 'minute'),
array(1, 'second')
);
for ($i = 0, $j = count($chunks); $i < $j; $i++)
{
$seconds = $chunks[$i][0];
$name = $chunks[$i][1];
if (($count = floor($timediff / $seconds)) != 0)
{
break;
}
}
return __("%d {$name}%s ago", $count, ($count > 1 ? 's' : ''));
}
/**
* 判断Unix时间是否满足Cron指定的执行条件
*
* @param string $cron Crontab格式
* @param string $time 时间,默认为当前时间
* @return boolean
*/
public static function cron($cron, $time = null)
{
$time = is_null($time) ? time() : $time;
$cron_parts = explode(' ', $cron);
if (count($cron_parts) != 5)
{
return false;
}
list($min, $hour, $day, $mon, $week) = explode(' ', $cron);
$to_check = array('min' => 'i', 'hour' => 'G', 'day' => 'j', 'mon' => 'n', 'week' => 'w');
$ranges = array(
'min' => '0-59',
'hour' => '0-23',
'day' => '1-31',
'mon' => '1-12',
'week' => '0-6',
);
foreach ($to_check as $part => $c)
{
$val = $$part;
$values = [];
if (strpos($val, '/') !== false)
{
//Get the range and step
list($range, $steps) = explode('/', $val);
//Now get the start and stop
if ($range == '*')
{
$range = $ranges[$part];
}
list($start, $stop) = explode('-', $range);
for ($i = $start; $i <= $stop; $i = $i + $steps)
{
$values[] = $i;
}
}
else
{
$k = explode(',', $val);
foreach ($k as $v)
{
if (strpos($v, '-') !== false)
{
list($start, $stop) = explode('-', $v);
for ($i = $start; $i <= $stop; $i++)
{
$values[] = $i;
}
}
else
{
$values[] = $v;
}
}
}
if (!in_array(date($c, $time), $values) and ( strval($val) != '*'))
{
return false;
}
}
return true;
}
/**
* 获取一个基于时间偏移的Unix时间戳
*
* @param string $type 时间类型,默认为day,可选minute,hour,day,week,month,quarter,year
* @param int $offset 时间偏移量 默认为0,正数表示当前type之后,负数表示当前type之前
* @param string $position 时间的开始或结束,默认为begin,可选前(begin,start,first,front),end
* @param int $year 基准年,默认为null,即以当前年为基准
* @param int $month 基准月,默认为null,即以当前月为基准
* @param int $day 基准天,默认为null,即以当前天为基准
* @param int $hour 基准小时,默认为null,即以当前年小时基准
* @param int $minute 基准分钟,默认为null,即以当前分钟为基准
* @return int 处理后的Unix时间戳
*/
public static function unixtime($type = 'day', $offset = 0, $position = 'begin', $year = null, $month = null, $day = null, $hour = null, $minute = null)
{
$year = is_null($year) ? date('Y') : $year;
$month = is_null($month) ? date('m') : $month;
$day = is_null($day) ? date('d') : $day;
$hour = is_null($hour) ? date('H') : $hour;
$minute = is_null($minute) ? date('i') : $minute;
$position = in_array($position, array('begin', 'start', 'first', 'front'));
switch ($type)
{
case 'minute':
$time = $position ? mktime($hour, $minute + $offset, 0, $month, $day, $year) : mktime($hour, $minute + $offset, 59, $month, $day, $year);
break;
case 'hour':
$time = $position ? mktime($hour + $offset, 0, 0, $month, $day, $year) : mktime($hour + $offset, 59, 59, $month, $day, $year);
break;
case 'day':
$time = $position ? mktime(0, 0, 0, $month, $day + $offset, $year) : mktime(23, 59, 59, $month, $day + $offset, $year);
break;
case 'week':
$time = $position ?
mktime(0, 0, 0, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 1 - 7 * (-$offset), $year) :
mktime(23, 59, 59, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 7 - 7 * (-$offset), $year);
break;
case 'month':
$time = $position ? mktime(0, 0, 0, $month + $offset, 1, $year) : mktime(23, 59, 59, $month + $offset, get_month_days($month + $offset, $year), $year);
break;
case 'quarter':
$time = $position ?
mktime(0, 0, 0, 1 + ((ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) - 1) * 3, 1, $year) :
mktime(23, 59, 59, (ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, get_month_days((ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, $year), $year);
break;
case 'year':
$time = $position ? mktime(0, 0, 0, 1, 1, $year + $offset) : mktime(23, 59, 59, 12, 31, $year + $offset);
break;
default:
$time = mktime($hour, $minute, 0, $month, $day, $year);
break;
}
return $time;
}
}
... ...
<?php
namespace fast;
use ArrayAccess;
/**
* @class Form
* @package fast
* @method mixed token() static token
* @method mixed close() static 关闭一个HTML表单
* @method mixed setModel(mixed $model) static 设置HTML表单模型
* @method mixed open(array $options) static 打开一个新的HTML表单
* @method mixed file(string $name, array $options = array()) static 表单file
* @method mixed reset(string $name, array $options = array()) static 表单reset
* @method mixed password(string $name, array $options = array()) static 表单password
* @method mixed button(string $value = null, array $options = array()) static 表单button
* @method mixed model(mixed $model ,array $options = array()) static 打开一个新的HTML表单模型
* @method mixed submit(string $value = null, array $options = array()) static 表单submit提交
* @method mixed text(string $name, string $value = null, array $options = []) static 表单text
* @method mixed label(string $name, string $value = null, array $options = []) static label标签
* @method mixed url(string $name, string $value = null, array $options = array()) static 表单url
* @method mixed email(string $name, string $value = null, array $options = array()) static 表单email
* @method mixed hidden(string $name, string $value = null, array $options = array()) static 表单hidden
* @method mixed image(string $url, string $name = null, array $attributes = array()) static 表单image提交
* @method mixed textarea(string $name, string $value = null, array $options = array()) static 表单textarea
* @method mixed checkbox(string $name, string $value = 1, string $checked = null, array $options = array()) static 表单checkbox
* @method mixed radio(string $name, string $value = null, string $checked = null, array $options = array())) static 表单radio
* @method mixed select(string $name, array $list = array(), string $selected = null, array $options = array()) static 表单textarea
* @method mixed selectYear(string $name ,string $begin,string $end, string $selected = null ,array $options = array()) static select年
* @method mixed selectMonth(string $name ,string $selected = null ,array $options = array() ,string $format = '%m') static select月
*/
class Form
{
public function __construct()
{
}
public static function __callStatic($name, $arguments)
{
return call_user_func_array([FormBuilder::instance(), $name], $arguments);
}
}
class FormBuilder
{
/**
* The CSRF token used by the form builder.
*
* @var string
*/
protected $csrfToken = array('name' => '__token__');
/**
* The current model instance for the form.
*
* @var mixed
*/
protected $model;
/**
* An array of label names we've created.
*
* @var array
*/
protected $labels = array();
/**
* The reserved form open attributes.
*
* @var array
*/
protected $reserved = array('method', 'url', 'route', 'action', 'files');
/**
* The form methods that should be spoofed, in uppercase.
*
* @var array
*/
protected $spoofedMethods = array('DELETE', 'PATCH', 'PUT');
/**
* The types of inputs to not fill values on by default.
*
* @var array
*/
protected $skipValueTypes = array('file', 'password', 'checkbox', 'radio');
protected static $instance;
/**
* Create a new form builder instance.
*
* @return void
*/
public function __construct()
{
}
public static function instance($options = [])
{
if (is_null(self::$instance))
{
self::$instance = new static($options);
}
return self::$instance;
}
/**
* Open up a new HTML form.
*
* @param array $options
* @return string
*/
public function open(array $options = array())
{
$method = array_get($options, 'method', 'post');
// We need to extract the proper method from the attributes. If the method is
// something other than GET or POST we'll use POST since we will spoof the
// actual method since forms don't support the reserved methods in HTML.
$attributes['method'] = $this->getMethod($method);
$attributes['action'] = array_get($options, 'action', '');
$attributes['accept-charset'] = 'UTF-8';
// If the method is PUT, PATCH or DELETE we will need to add a spoofer hidden
// field that will instruct the Symfony request to pretend the method is a
// different method than it actually is, for convenience from the forms.
$append = $this->getAppendage($method);
if (isset($options['files']) && $options['files'])
{
$options['enctype'] = 'multipart/form-data';
}
// Finally we're ready to create the final form HTML field. We will attribute
// format the array of attributes. We will also add on the appendage which
// is used to spoof requests for this PUT, PATCH, etc. methods on forms.
$attributes = array_merge(
$attributes, array_except($options, $this->reserved)
);
// Finally, we will concatenate all of the attributes into a single string so
// we can build out the final form open statement. We'll also append on an
// extra value for the hidden _method field if it's needed for the form.
$attributes = $this->attributes($attributes);
return '<form' . $attributes . '>' . $append;
}
/**
* Create a new model based form builder.
*
* @param mixed $model
* @param array $options
* @return string
*/
public function model($model, array $options = array())
{
$this->model = $model;
return $this->open($options);
}
/**
* Set the model instance on the form builder.
*
* @param mixed $model
* @return void
*/
public function setModel($model)
{
$this->model = $model;
}
/**
* Close the current form.
*
* @return string
*/
public function close()
{
$this->labels = array();
$this->model = null;
return '</form>';
}
/**
* Generate a hidden field with the current CSRF token.
*
* @return string
*/
public function token($name = '__token__', $type = 'md5')
{
if (function_exists('token'))
{
return token($name, $type);
}
return '';
}
/**
* Create a form label element.
*
* @param string $name
* @param string $value
* @param array $options
* @return string
*/
public function label($name, $value = null, $options = array())
{
$this->labels[] = $name;
$options = $this->attributes($options);
$value = e($this->formatLabel($name, $value));
return '<label for="' . $name . '"' . $options . '>' . $value . '</label>';
}
/**
* Format the label value.
*
* @param string $name
* @param string|null $value
* @return string
*/
protected function formatLabel($name, $value)
{
return $value ? : ucwords(str_replace('_', ' ', $name));
}
/**
* Create a form input field.
*
* @param string $type
* @param string $name
* @param string $value
* @param array $options
* @return string
*/
public function input($type, $name, $value = null, $options = array())
{
if (!isset($options['name']))
$options['name'] = $name;
// We will get the appropriate value for the given field. We will look for the
// value in the session for the value in the old input data then we'll look
// in the model instance if one is set. Otherwise we will just use empty.
$id = $this->getIdAttribute($name, $options);
if (!in_array($type, $this->skipValueTypes))
{
$value = $this->getValueAttribute($name, $value);
}
// Once we have the type, value, and ID we can merge them into the rest of the
// attributes array so we can convert them into their HTML attribute format
// when creating the HTML element. Then, we will return the entire input.
$merge = compact('type', 'value', 'id');
$options = array_merge($options, $merge);
return '<input' . $this->attributes($options) . '>';
}
/**
* Create a text input field.
*
* @param string $name
* @param string $value
* @param array $options
* @return string
*/
public function text($name, $value = null, $options = array())
{
return $this->input('text', $name, $value, $options);
}
/**
* Create a password input field.
*
* @param string $name
* @param array $options
* @return string
*/
public function password($name, $options = array())
{
return $this->input('password', $name, '', $options);
}
/**
* Create a hidden input field.
*
* @param string $name
* @param string $value
* @param array $options
* @return string
*/
public function hidden($name, $value = null, $options = array())
{
return $this->input('hidden', $name, $value, $options);
}
/**
* Create an e-mail input field.
*
* @param string $name
* @param string $value
* @param array $options
* @return string
*/
public function email($name, $value = null, $options = array())
{
return $this->input('email', $name, $value, $options);
}
/**
* Create a url input field.
*
* @param string $name
* @param string $value
* @param array $options
* @return string
*/
public function url($name, $value = null, $options = array())
{
return $this->input('url', $name, $value, $options);
}
/**
* Create a file input field.
*
* @param string $name
* @param array $options
* @return string
*/
public function file($name, $options = array())
{
return $this->input('file', $name, null, $options);
}
/**
* Create a textarea input field.
*
* @param string $name
* @param string $value
* @param array $options
* @return string
*/
public function textarea($name, $value = null, $options = array())
{
if (!isset($options['name']))
$options['name'] = $name;
// Next we will look for the rows and cols attributes, as each of these are put
// on the textarea element definition. If they are not present, we will just
// assume some sane default values for these attributes for the developer.
$options = $this->setTextAreaSize($options);
$options['id'] = $this->getIdAttribute($name, $options);
$value = (string) $this->getValueAttribute($name, $value);
unset($options['size']);
// Next we will convert the attributes into a string form. Also we have removed
// the size attribute, as it was merely a short-cut for the rows and cols on
// the element. Then we'll create the final textarea elements HTML for us.
$options = $this->attributes($options);
return '<textarea' . $options . '>' . e($value) . '</textarea>';
}
/**
* Set the text area size on the attributes.
*
* @param array $options
* @return array
*/
protected function setTextAreaSize($options)
{
if (isset($options['size']))
{
return $this->setQuickTextAreaSize($options);
}
// If the "size" attribute was not specified, we will just look for the regular
// columns and rows attributes, using sane defaults if these do not exist on
// the attributes array. We'll then return this entire options array back.
$cols = array_get($options, 'cols', 50);
$rows = array_get($options, 'rows', 10);
return array_merge($options, compact('cols', 'rows'));
}
/**
* Set the text area size using the quick "size" attribute.
*
* @param array $options
* @return array
*/
protected function setQuickTextAreaSize($options)
{
$segments = explode('x', $options['size']);
return array_merge($options, array('cols' => $segments[0], 'rows' => $segments[1]));
}
/**
* Create a select box field.
*
* @param string $name
* @param array $list
* @param string $selected
* @param array $options
* @return string
*/
public function select($name, $list = array(), $selected = null, $options = array())
{
// When building a select box the "value" attribute is really the selected one
// so we will use that when checking the model or session for a value which
// should provide a convenient method of re-populating the forms on post.
$selected = $this->getValueAttribute($name, $selected);
$options['id'] = $this->getIdAttribute($name, $options);
if (!isset($options['name']))
$options['name'] = $name;
// We will simply loop through the options and build an HTML value for each of
// them until we have an array of HTML declarations. Then we will join them
// all together into one single HTML element that can be put on the form.
$html = array();
foreach ($list as $value => $display)
{
$html[] = $this->getSelectOption($display, $value, $selected);
}
// Once we have all of this HTML, we can join this into a single element after
// formatting the attributes into an HTML "attributes" string, then we will
// build out a final select statement, which will contain all the values.
$options = $this->attributes($options);
$list = implode('', $html);
return "<select{$options}>{$list}</select>";
}
/**
* Create a select range field.
*
* @param string $name
* @param string $begin
* @param string $end
* @param string $selected
* @param array $options
* @return string
*/
public function selectRange($name, $begin, $end, $selected = null, $options = array())
{
$range = array_combine($range = range($begin, $end), $range);
return $this->select($name, $range, $selected, $options);
}
/**
* Create a select year field.
*
* @param string $name
* @param string $begin
* @param string $end
* @param string $selected
* @param array $options
* @return string
*/
public function selectYear()
{
return call_user_func_array(array($this, 'selectRange'), func_get_args());
}
/**
* Create a select month field.
*
* @param string $name
* @param string $selected
* @param array $options
* @param string $format
* @return string
*/
public function selectMonth($name, $selected = null, $options = array(), $format = '%m')
{
$months = array();
foreach (range(1, 12) as $month)
{
$months[$month] = strftime($format, mktime(0, 0, 0, $month, 1));
}
return $this->select($name, $months, $selected, $options);
}
/**
* Get the select option for the given value.
*
* @param string $display
* @param string $value
* @param string $selected
* @return string
*/
public function getSelectOption($display, $value, $selected)
{
if (is_array($display))
{
return $this->optionGroup($display, $value, $selected);
}
return $this->option($display, $value, $selected);
}
/**
* Create an option group form element.
*
* @param array $list
* @param string $label
* @param string $selected
* @return string
*/
protected function optionGroup($list, $label, $selected)
{
$html = array();
foreach ($list as $value => $display)
{
$html[] = $this->option($display, $value, $selected);
}
return '<optgroup label="' . e($label) . '">' . implode('', $html) . '</optgroup>';
}
/**
* Create a select element option.
*
* @param string $display
* @param string $value
* @param string $selected
* @return string
*/
protected function option($display, $value, $selected)
{
$selected = $this->getSelectedValue($value, $selected);
$options = array('value' => e($value), 'selected' => $selected);
return '<option' . $this->attributes($options) . '>' . e($display) . '</option>';
}
/**
* Determine if the value is selected.
*
* @param string $value
* @param string $selected
* @return string
*/
protected function getSelectedValue($value, $selected)
{
if (is_array($selected))
{
return in_array($value, $selected) ? 'selected' : null;
}
return ((string) $value == (string) $selected) ? 'selected' : null;
}
/**
* Create a checkbox input field.
*
* @param string $name
* @param mixed $value
* @param bool $checked
* @param array $options
* @return string
*/
public function checkbox($name, $value = 1, $checked = null, $options = array())
{
return $this->checkable('checkbox', $name, $value, $checked, $options);
}
/**
* Create a radio button input field.
*
* @param string $name
* @param mixed $value
* @param bool $checked
* @param array $options
* @return string
*/
public function radio($name, $value = null, $checked = null, $options = array())
{
if (is_null($value))
$value = $name;
return $this->checkable('radio', $name, $value, $checked, $options);
}
/**
* Create a checkable input field.
*
* @param string $type
* @param string $name
* @param mixed $value
* @param bool $checked
* @param array $options
* @return string
*/
protected function checkable($type, $name, $value, $checked, $options)
{
$checked = $this->getCheckedState($type, $name, $value, $checked);
if ($checked)
$options['checked'] = 'checked';
return $this->input($type, $name, $value, $options);
}
/**
* Get the check state for a checkable input.
*
* @param string $type
* @param string $name
* @param mixed $value
* @param bool $checked
* @return bool
*/
protected function getCheckedState($type, $name, $value, $checked)
{
switch ($type)
{
case 'checkbox':
return $this->getCheckboxCheckedState($name, $value, $checked);
case 'radio':
return $this->getRadioCheckedState($name, $value, $checked);
default:
return $this->getValueAttribute($name) == $value;
}
}
/**
* Get the check state for a checkbox input.
*
* @param string $name
* @param mixed $value
* @param bool $checked
* @return bool
*/
protected function getCheckboxCheckedState($name, $value, $checked)
{
if (isset($this->session) && !$this->oldInputIsEmpty() && is_null($this->old($name)))
return false;
if ($this->missingOldAndModel($name))
return $checked;
$posted = $this->getValueAttribute($name);
return is_array($posted) ? in_array($value, $posted) : (bool) $posted;
}
/**
* Get the check state for a radio input.
*
* @param string $name
* @param mixed $value
* @param bool $checked
* @return bool
*/
protected function getRadioCheckedState($name, $value, $checked)
{
if ($this->missingOldAndModel($name))
return $checked;
return $this->getValueAttribute($name) == $value;
}
/**
* Determine if old input or model input exists for a key.
*
* @param string $name
* @return bool
*/
protected function missingOldAndModel($name)
{
return (is_null($this->old($name)) && is_null($this->getModelValueAttribute($name)));
}
/**
* Create a HTML reset input element.
*
* @param string $value
* @param array $attributes
* @return string
*/
public function reset($value, $attributes = array())
{
return $this->input('reset', null, $value, $attributes);
}
/**
* Create a HTML image input element.
*
* @param string $url
* @param string $name
* @param array $attributes
* @return string
*/
public function image($url, $name = null, $attributes = array())
{
$attributes['src'] = $url;
return $this->input('image', $name, null, $attributes);
}
/**
* Create a submit button element.
*
* @param string $value
* @param array $options
* @return string
*/
public function submit($value = null, $options = array())
{
return $this->input('submit', null, $value, $options);
}
/**
* Create a button element.
*
* @param string $value
* @param array $options
* @return string
*/
public function button($value = null, $options = array())
{
if (!array_key_exists('type', $options))
{
$options['type'] = 'button';
}
return '<button' . $this->attributes($options) . '>' . $value . '</button>';
}
/**
* Parse the form action method.
*
* @param string $method
* @return string
*/
protected function getMethod($method)
{
$method = strtoupper($method);
return $method != 'GET' ? 'POST' : $method;
}
/**
* Get the form action from the options.
*
* @param array $options
* @return string
*/
/* protected function getAction(array $options)
{
// We will also check for a "route" or "action" parameter on the array so that
// developers can easily specify a route or controller action when creating
// a form providing a convenient interface for creating the form actions.
if (isset($options['url']))
{
return $this->getUrlAction($options['url']);
}
if (isset($options['route']))
{
return $this->getRouteAction($options['route']);
}
// If an action is available, we are attempting to open a form to a controller
// action route. So, we will use the URL generator to get the path to these
// actions and return them from the method. Otherwise, we'll use current.
elseif (isset($options['action']))
{
return $this->getControllerAction($options['action']);
}
return $this->url->current();
} */
/**
* Get the action for a "url" option.
*
* @param array|string $options
* @return string
*/
/* protected function getUrlAction($options)
{
if (is_array($options))
{
return $this->url->to($options[0], array_slice($options, 1));
}
return $this->url->to($options);
} */
/**
* Get the action for a "route" option.
*
* @param array|string $options
* @return string
*/
/* protected function getRouteAction($options)
{
if (is_array($options))
{
return $this->url->route($options[0], array_slice($options, 1));
}
return $this->url->route($options);
} */
/**
* Get the action for an "action" option.
*
* @param array|string $options
* @return string
*/
/* protected function getControllerAction($options)
{
if (is_array($options))
{
return $this->url->action($options[0], array_slice($options, 1));
}
return $this->url->action($options);
} */
/**
* Get the form appendage for the given method.
*
* @param string $method
* @return string
*/
protected function getAppendage($method)
{
list($method, $appendage) = array(strtoupper($method), '');
// If the HTTP method is in this list of spoofed methods, we will attach the
// method spoofer hidden input to the form. This allows us to use regular
// form to initiate PUT and DELETE requests in addition to the typical.
if (in_array($method, $this->spoofedMethods))
{
$appendage .= $this->hidden('_method', $method);
}
// If the method is something other than GET we will go ahead and attach the
// CSRF token to the form, as this can't hurt and is convenient to simply
// always have available on every form the developers creates for them.
if ($method != 'GET')
{
$appendage .= $this->token(array_get($this->csrfToken, 'name'), array_get($this->csrfToken, 'type'));
}
return $appendage;
}
/**
* Get the ID attribute for a field name.
*
* @param string $name
* @param array $attributes
* @return string
*/
public function getIdAttribute($name, $attributes)
{
if (array_key_exists('id', $attributes))
{
return $attributes['id'];
}
if (in_array($name, $this->labels))
{
return $name;
}
}
/**
* Get the value that should be assigned to the field.
*
* @param string $name
* @param string $value
* @return string
*/
public function getValueAttribute($name, $value = null)
{
if (is_null($name))
return $value;
if (!is_null($this->old($name)))
{
return $this->old($name);
}
if (!is_null($value))
return $value;
if (isset($this->model))
{
return $this->getModelValueAttribute($name);
}
}
/**
* Get the model value that should be assigned to the field.
*
* @param string $name
* @return string
*/
protected function getModelValueAttribute($name)
{
if (is_object($this->model))
{
return object_get($this->model, $this->transformKey($name));
}
elseif (is_array($this->model))
{
return array_get($this->model, $this->transformKey($name));
}
}
/**
* Get a value from the session's old input.
*
* @param string $name
* @return string
*/
public function old($name)
{
if (isset($this->session))
{
return $this->session->getOldInput($this->transformKey($name));
}
}
/**
* Determine if the old input is empty.
*
* @return bool
*/
public function oldInputIsEmpty()
{
return (isset($this->session) && count($this->session->getOldInput()) == 0);
}
/**
* Transform key from array to dot syntax.
*
* @param string $key
* @return string
*/
protected function transformKey($key)
{
return str_replace(array('.', '[]', '[', ']'), array('_', '', '.', ''), $key);
}
/**
* 数组转换成一个HTML属性字符串。
*
* @param array $attributes
* @return string
*/
public function attributes($attributes)
{
$html = array();
// 假设我们的keys 和 value 是相同的,
// 拿HTML“required”属性来说,假设是['required']数组,
// 会已 required="required" 拼接起来,而不是用数字keys去拼接
foreach ((array) $attributes as $key => $value)
{
$element = $this->attributeElement($key, $value);
if (!is_null($element))
$html[] = $element;
}
return count($html) > 0 ? ' ' . implode(' ', $html) : '';
}
/**
* 拼接成一个属性。
*
* @param string $key
* @param string $value
* @return string
*/
protected function attributeElement($key, $value)
{
if (is_numeric($key))
$key = $value;
if (!is_null($value))
return $key . '="' . $value . '"';
}
}
class Arr
{
/**
* Determine whether the given value is array accessible.
*
* @param mixed $value
* @return bool
*/
public static function accessible($value)
{
return is_array($value) || $value instanceof ArrayAccess;
}
/**
* Determine if the given key exists in the provided array.
*
* @param \ArrayAccess|array $array
* @param string|int $key
* @return bool
*/
public static function exists($array, $key)
{
if ($array instanceof ArrayAccess)
{
return $array->offsetExists($key);
}
return array_key_exists($key, $array);
}
/**
* Get an item from an array using "dot" notation.
*
* @param \ArrayAccess|array $array
* @param string $key
* @param mixed $default
* @return mixed
*/
public static function get($array, $key, $default = null)
{
if (!static::accessible($array))
{
return $default;
}
if (is_null($key))
{
return $array;
}
if (static::exists($array, $key))
{
return $array[$key];
}
foreach (explode('.', $key) as $segment)
{
if (static::accessible($array) && static::exists($array, $segment))
{
$array = $array[$segment];
}
else
{
return $default;
}
}
return $array;
}
/**
* Get all of the given array except for a specified array of items.
*
* @param array $array
* @param array|string $keys
* @return array
*/
public static function except($array, $keys)
{
static::forget($array, $keys);
return $array;
}
/**
* Remove one or many array items from a given array using "dot" notation.
*
* @param array $array
* @param array|string $keys
* @return void
*/
public static function forget(&$array, $keys)
{
$original = &$array;
$keys = (array) $keys;
if (count($keys) === 0)
{
return;
}
foreach ($keys as $key)
{
// if the exact key exists in the top-level, remove it
if (static::exists($array, $key))
{
unset($array[$key]);
continue;
}
$parts = explode('.', $key);
// clean up before each pass
$array = &$original;
while (count($parts) > 1)
{
$part = array_shift($parts);
if (isset($array[$part]) && is_array($array[$part]))
{
$array = &$array[$part];
}
else
{
continue 2;
}
}
unset($array[array_shift($parts)]);
}
}
}
if (!function_exists('array_get'))
{
/**
* Get an item from an array using "dot" notation.
*
* @param \ArrayAccess|array $array
* @param string $key
* @param mixed $default
* @return mixed
*/
function array_get($array, $key, $default = null)
{
return Arr::get($array, $key, $default);
}
}
if (!function_exists('e'))
{
/**
* Escape HTML special characters in a string.
*
*
* @return string
*/
function e($value)
{
if (is_array($value))
{
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
}
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
}
}
if (!function_exists('array_except'))
{
/**
* Get all of the given array except for a specified array of items.
*
* @param array $array
* @param array|string $keys
* @return array
*/
function array_except($array, $keys)
{
return Arr::except($array, $keys);
}
}
... ...
<?php
namespace fast;
/**
* 字符串类
*/
class Http
{
/**
* 发送一个POST请求
*/
public static function post($url, $params = [], $options = [])
{
$req = self::sendRequest($url, $params, 'POST', $options);
return $req['ret'] ? $req['msg'] : '';
}
/**
* 发送一个GET请求
*/
public static function get($url, $params = [], $options = [])
{
$req = self::sendRequest($url, $params, 'GET', $options);
return $req['ret'] ? $req['msg'] : '';
}
/**
* CURL发送Request请求,含POST和REQUEST
* @param string $url 请求的链接
* @param mixed $params 传递的参数
* @param string $method 请求的方法
* @param mixed $options CURL的参数
* @return array
*/
public static function sendRequest($url, $params = [], $method = 'POST', $options = [])
{
$method = strtoupper($method);
$protocol = substr($url, 0, 5);
$query_string = is_array($params) ? http_build_query($params) : $params;
$ch = curl_init();
$defaults = [];
if ('GET' == $method)
{
$geturl = $query_string ? $url . (stripos($url, "?") !== FALSE ? "&" : "?") . $query_string : $url;
$defaults[CURLOPT_URL] = $geturl;
}
else
{
$defaults[CURLOPT_URL] = $url;
if ($method == 'POST')
{
$defaults[CURLOPT_POST] = 1;
}
else
{
$defaults[CURLOPT_CUSTOMREQUEST] = $method;
}
$defaults[CURLOPT_POSTFIELDS] = $query_string;
}
$defaults[CURLOPT_HEADER] = FALSE;
$defaults[CURLOPT_USERAGENT] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36";
$defaults[CURLOPT_FOLLOWLOCATION] = TRUE;
$defaults[CURLOPT_RETURNTRANSFER] = TRUE;
$defaults[CURLOPT_CONNECTTIMEOUT] = 3;
$defaults[CURLOPT_TIMEOUT] = 3;
// disable 100-continue
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
if ('https' == $protocol)
{
$defaults[CURLOPT_SSL_VERIFYPEER] = FALSE;
$defaults[CURLOPT_SSL_VERIFYHOST] = FALSE;
}
curl_setopt_array($ch, (array) $options + $defaults);
$ret = curl_exec($ch);
$err = curl_error($ch);
if (FALSE === $ret || !empty($err))
{
$errno = curl_errno($ch);
$info = curl_getinfo($ch);
curl_close($ch);
return [
'ret' => FALSE,
'errno' => $errno,
'msg' => $err,
'info' => $info,
];
}
curl_close($ch);
return [
'ret' => TRUE,
'msg' => $ret,
];
}
/**
* 异步发送一个请求
* @param string $url 请求的链接
* @param mixed $params 请求的参数
* @param string $method 请求的方法
* @return boolean TRUE
*/
public static function sendAsyncRequest($url, $params = [], $method = 'POST')
{
$method = strtoupper($method);
$method = $method == 'POST' ? 'POST' : 'GET';
//构造传递的参数
if (is_array($params))
{
$post_params = [];
foreach ($params as $k => &$v)
{
if (is_array($v))
$v = implode(',', $v);
$post_params[] = $k . '=' . urlencode($v);
}
$post_string = implode('&', $post_params);
}else
{
$post_string = $params;
}
$parts = parse_url($url);
//构造查询的参数
if ($method == 'GET' && $post_string)
{
$parts['query'] = isset($parts['query']) ? $parts['query'] . '&' . $post_string : $post_string;
$post_string = '';
}
$parts['query'] = isset($parts['query']) && $parts['query'] ? '?' . $parts['query'] : '';
//发送socket请求,获得连接句柄
$fp = fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80, $errno, $errstr, 3);
if (!$fp)
return FALSE;
//设置超时时间
stream_set_timeout($fp, 3);
$out = "{$method} {$parts['path']}{$parts['query']} HTTP/1.1\r\n";
$out.= "Host: {$parts['host']}\r\n";
$out.= "Content-Type: application/x-www-form-urlencoded\r\n";
$out.= "Content-Length: " . strlen($post_string) . "\r\n";
$out.= "Connection: Close\r\n\r\n";
if ($post_string !== '')
$out .= $post_string;
fwrite($fp, $out);
//不用关心服务器返回结果
//echo fread($fp, 1024);
fclose($fp);
return TRUE;
}
/**
* 发送文件到客户端
* @param string $file
* @param bool $delaftersend
* @param bool $exitaftersend
*/
public static function sendToBrowser($file, $delaftersend = true, $exitaftersend = true)
{
if (file_exists($file) && is_readable($file))
{
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment;filename = ' . basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check = 0, pre-check = 0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
if ($delaftersend)
{
unlink($file);
}
if ($exitaftersend)
{
exit;
}
}
}
}
... ...
<?php
namespace fast;
use app\admin\library\Auth;
use fast\Tree;
use think\Config;
use think\Db;
/**
* 菜单
*/
class Menu
{
protected static $instance;
//默认配置
protected $config = [];
public function __construct($options = [])
{
if ($config = Config::get('menu'))
{
$this->config = array_merge($this->config, $config);
}
$this->config = array_merge($this->config, is_array($options) ? $options : []);
}
/**
* 单例
* @param array $options 参数
* @return Menu
*/
public static function instance($options = [])
{
if (is_null(self::$instance))
{
self::$instance = new static($options);
}
return self::$instance;
}
/**
* 导入节点数据
* @param array $treearr
* @param int $pid
* @param string $module
*/
public function import($treearr, $pid = 0, $module = NULL)
{
$module = is_null($module) ? Config::user('request.modulename') : $module;
foreach ($treearr as $key => & $v)
{
$controller = strtolower($v['controller']) . ($v['childlist'] ? "_folder" : '');
$icon = isset($v['icon']) ? $v['icon'] : (stripos($v['name'], '_') !== FALSE ? $v['childlist'] ? 'folder' : 'circle-o' : strtolower($v['name']));
$actionkeys = array_keys($v['actionlist']);
$action = '-' . ($v['actionlist'] ? reset($actionkeys) : '');
$values = array(
'name' => $v['name'],
'weigh' => $key,
'pid' => $pid,
'module' => $module,
'controller' => $controller,
'action' => $action,
'icon' => $icon,
'type' => 'menu',
'status' => 'normal'
);
$id = Db::table("node")->data($values)->insert();
$v['id'] = $id;
}
unset($v);
foreach ($treearr as $k => $v)
{
if ($v['childlist'])
{
$this->import($v['childlist'], $v['id'], $module);
}
else
{
$i = 0;
foreach ($v['actionlist'] as $m => $n)
{
$values = array(
'name' => $n ? $n : $m,
'weigh' => $i,
'pid' => $v['id'],
'module' => $module,
'controller' => strtolower($v['controller']),
'action' => $m,
'icon' => 'circle-o',
'type' => 'file',
'status' => 'normal'
);
Db::table("node")->data($values)->insert();
$i++;
}
}
}
}
/**
* 获取左侧菜单栏
*
* @param array $params URL对应的badge数据
* @return string
*/
public function sidebar($params = [])
{
$colorArr = ['red', 'green', 'yellow', 'blue', 'teal', 'orange', 'purple'];
$colorNums = count($colorArr);
$badgeList = [];
// 生成菜单的badge
foreach ($params as $k => $v)
{
if (stripos($k, '/') === false)
{
$url = '/admin/' . $k;
}
else
{
$url = url($k);
}
if (is_array($v))
{
$nums = isset($v[0]) ? $v[0] : 0;
$color = isset($v[1]) ? $v[1] : $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums];
$class = isset($v[2]) ? $v[2] : 'label';
}
else
{
$nums = $v;
$color = $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums];
$class = 'label';
}
//必须nums大于0才显示
if ($nums)
{
$badgeList[$url] = '<small class="' . $class . ' pull-right bg-' . $color . '">' . $nums . '</small>';
}
}
// 读取管理员当前拥有的权限节点
$userRule = Auth::instance()->getRuleList();
$select_id = 0;
$dashboard = rtrim(url('dashboard/'), '/');
// 必须将结果集转换为数组
$ruleList = collection(model('AuthRule')->where('ismenu', 1)->order('weigh', 'desc')->cache("__menu__")->select())->toArray();
foreach ($ruleList as $k => &$v)
{
if (!in_array($v['name'], $userRule))
continue;
$select_id = $v['name'] == $dashboard ? $v['id'] : $select_id;
$v['url'] = $v['name'];
$v['badge'] = isset($badgeList[$v['name']]) ? $badgeList[$v['name']] : '';
}
// 构造菜单数据
Tree::instance()->init($ruleList);
$menu = Tree::instance()->getTreeMenu(0, '<li class="@class"><a href="@url" addtabs="@id" url="@url"><i class="@icon"></i> <span>@title</span> <span class="pull-right-container">@caret @badge</span></a> @childlist</li>', $select_id, '', 'ul', 'class="treeview-menu"');
return $menu;
}
}
... ...
<?php
namespace fast;
/**
* 中文转拼音类
*/
class Pinyin
{
protected static $keys = "a|ai|an|ang|ao|ba|bai|ban|bang|bao|bei|ben|beng|bi|bian|biao|bie|bin|bing|bo|bu|ca|cai|can|cang|cao|ce|ceng|cha|chai|chan|chang|chao|che|chen|cheng|chi|chong|chou|chu|chuai|chuan|chuang|chui|chun|chuo|ci|cong|cou|cu|cuan|cui|cun|cuo|da|dai|dan|dang|dao|de|deng|di|dian|diao|die|ding|diu|dong|dou|du|duan|dui|dun|duo|e|en|er|fa|fan|fang|fei|fen|feng|fo|fou|fu|ga|gai|gan|gang|gao|ge|gei|gen|geng|gong|gou|gu|gua|guai|guan|guang|gui|gun|guo|ha|hai|han|hang|hao|he|hei|hen|heng|hong|hou|hu|hua|huai|huan|huang|hui|hun|huo|ji|jia|jian|jiang|jiao|jie|jin|jing|jiong|jiu|ju|juan|jue|jun|ka|kai|kan|kang|kao|ke|ken|keng|kong|kou|ku|kua|kuai|kuan|kuang|kui|kun|kuo|la|lai|lan|lang|lao|le|lei|leng|li|lia|lian|liang|liao|lie|lin|ling|liu|long|lou|lu|lv|luan|lue|lun|luo|ma|mai|man|mang|mao|me|mei|men|meng|mi|mian|miao|mie|min|ming|miu|mo|mou|mu|na|nai|nan|nang|nao|ne|nei|nen|neng|ni|nian|niang|niao|nie|nin|ning|niu|nong|nu|nv|nuan|nue|nuo|o|ou|pa|pai|pan|pang|pao|pei|pen|peng|pi|pian|piao|pie|pin|ping|po|pu|qi|qia|qian|qiang|qiao|qie|qin|qing|qiong|qiu|qu|quan|que|qun|ran|rang|rao|re|ren|reng|ri|rong|rou|ru|ruan|rui|run|ruo|sa|sai|san|sang|sao|se|sen|seng|sha|shai|shan|shang|shao|she|shen|sheng|shi|shou|shu|shua|shuai|shuan|shuang|shui|shun|shuo|si|song|sou|su|suan|sui|sun|suo|ta|tai|tan|tang|tao|te|teng|ti|tian|tiao|tie|ting|tong|tou|tu|tuan|tui|tun|tuo|wa|wai|wan|wang|wei|wen|weng|wo|wu|xi|xia|xian|xiang|xiao|xie|xin|xing|xiong|xiu|xu|xuan|xue|xun|ya|yan|yang|yao|ye|yi|yin|ying|yo|yong|you|yu|yuan|yue|yun|za|zai|zan|zang|zao|ze|zei|zen|zeng|zha|zhai|zhan|zhang|zhao|zhe|zhen|zheng|zhi|zhong|zhou|zhu|zhua|zhuai|zhuan|zhuang|zhui|zhun|zhuo|zi|zong|zou|zu|zuan|zui|zun|zuo";
protected static $values = "-20319|-20317|-20304|-20295|-20292|-20283|-20265|-20257|-20242|-20230|-20051|-20036|-20032|-20026|-20002|-19990|-19986|-19982|-19976|-19805|-19784|-19775|-19774|-19763|-19756|-19751|-19746|-19741|-19739|-19728|-19725|-19715|-19540|-19531|-19525|-19515|-19500|-19484|-19479|-19467|-19289|-19288|-19281|-19275|-19270|-19263|-19261|-19249|-19243|-19242|-19238|-19235|-19227|-19224|-19218|-19212|-19038|-19023|-19018|-19006|-19003|-18996|-18977|-18961|-18952|-18783|-18774|-18773|-18763|-18756|-18741|-18735|-18731|-18722|-18710|-18697|-18696|-18526|-18518|-18501|-18490|-18478|-18463|-18448|-18447|-18446|-18239|-18237|-18231|-18220|-18211|-18201|-18184|-18183|-18181|-18012|-17997|-17988|-17970|-17964|-17961|-17950|-17947|-17931|-17928|-17922|-17759|-17752|-17733|-17730|-17721|-17703|-17701|-17697|-17692|-17683|-17676|-17496|-17487|-17482|-17468|-17454|-17433|-17427|-17417|-17202|-17185|-16983|-16970|-16942|-16915|-16733|-16708|-16706|-16689|-16664|-16657|-16647|-16474|-16470|-16465|-16459|-16452|-16448|-16433|-16429|-16427|-16423|-16419|-16412|-16407|-16403|-16401|-16393|-16220|-16216|-16212|-16205|-16202|-16187|-16180|-16171|-16169|-16158|-16155|-15959|-15958|-15944|-15933|-15920|-15915|-15903|-15889|-15878|-15707|-15701|-15681|-15667|-15661|-15659|-15652|-15640|-15631|-15625|-15454|-15448|-15436|-15435|-15419|-15416|-15408|-15394|-15385|-15377|-15375|-15369|-15363|-15362|-15183|-15180|-15165|-15158|-15153|-15150|-15149|-15144|-15143|-15141|-15140|-15139|-15128|-15121|-15119|-15117|-15110|-15109|-14941|-14937|-14933|-14930|-14929|-14928|-14926|-14922|-14921|-14914|-14908|-14902|-14894|-14889|-14882|-14873|-14871|-14857|-14678|-14674|-14670|-14668|-14663|-14654|-14645|-14630|-14594|-14429|-14407|-14399|-14384|-14379|-14368|-14355|-14353|-14345|-14170|-14159|-14151|-14149|-14145|-14140|-14137|-14135|-14125|-14123|-14122|-14112|-14109|-14099|-14097|-14094|-14092|-14090|-14087|-14083|-13917|-13914|-13910|-13907|-13906|-13905|-13896|-13894|-13878|-13870|-13859|-13847|-13831|-13658|-13611|-13601|-13406|-13404|-13400|-13398|-13395|-13391|-13387|-13383|-13367|-13359|-13356|-13343|-13340|-13329|-13326|-13318|-13147|-13138|-13120|-13107|-13096|-13095|-13091|-13076|-13068|-13063|-13060|-12888|-12875|-12871|-12860|-12858|-12852|-12849|-12838|-12831|-12829|-12812|-12802|-12607|-12597|-12594|-12585|-12556|-12359|-12346|-12320|-12300|-12120|-12099|-12089|-12074|-12067|-12058|-12039|-11867|-11861|-11847|-11831|-11798|-11781|-11604|-11589|-11536|-11358|-11340|-11339|-11324|-11303|-11097|-11077|-11067|-11055|-11052|-11045|-11041|-11038|-11024|-11020|-11019|-11018|-11014|-10838|-10832|-10815|-10800|-10790|-10780|-10764|-10587|-10544|-10533|-10519|-10331|-10329|-10328|-10322|-10315|-10309|-10307|-10296|-10281|-10274|-10270|-10262|-10260|-10256|-10254";
/**
* 获取文字的拼音
* @param string $chinese 中文汉字
* @param boolean $ucfirst 是否首字母大写
* @param string $charset 文字编码
* @return string
*/
public static function get($chinese, $ucfirst = false, $charset = 'utf-8')
{
$keys_a = explode('|', self::$keys);
$values_a = explode('|', self::$values);
$data = array_combine($keys_a, $values_a);
arsort($data);
reset($data);
if ($charset != 'gb2312')
$chinese = self::_u2_utf8_gb($chinese);
$result = '';
for ($i = 0; $i < strlen($chinese); $i++)
{
$_P = ord(substr($chinese, $i, 1));
if ($_P > 160)
{
$_Q = ord(substr($chinese, ++$i, 1));
$_P = $_P * 256 + $_Q - 65536;
}
$result .= ($ucfirst ? ucfirst(self::_pinyin($_P, $data)) : self::_pinyin($_P, $data));
}
return preg_replace("/[^a-z0-9_\-]*/i", '', $result);
}
private static function _pinyin($num, $data)
{
if ($num > 0 && $num < 160)
return chr($num);
elseif ($num < -20319 || $num > -10247)
return '';
else
{
foreach ($data as $k => $v)
{
if ($v <= $num)
break;
}
return $k;
}
}
private static function _u2_utf8_gb($c)
{
$string = '';
if ($c < 0x80)
$string .= $c;
elseif ($c < 0x800)
{
$string .= chr(0xC0 | $c >> 6);
$string .= chr(0x80 | $c & 0x3F);
}
elseif ($c < 0x10000)
{
$string .= chr(0xE0 | $c >> 12);
$string .= chr(0x80 | $c >> 6 & 0x3F);
$string .= chr(0x80 | $c & 0x3F);
}
elseif ($c < 0x200000)
{
$string .= chr(0xF0 | $c >> 18);
$string .= chr(0x80 | $c >> 12 & 0x3F);
$string .= chr(0x80 | $c >> 6 & 0x3F);
$string .= chr(0x80 | $c & 0x3F);
}
return iconv('UTF-8', 'GB2312//IGNORE', $string);
}
}
... ...
<?php
namespace fast;
/**
* 随机生成类
*/
class Random
{
/**
* 生成数字和字母
*
* @param int $len 长度
* @return string
*/
public static function alnum($len = 6)
{
return self::build('alnum', $len);
}
/**
* 仅生成字符
*
* @param int $len 长度
* @return string
*/
public static function alpha($len = 6)
{
return self::build('alpha', $len);
}
/**
* 生成指定长度的随机数字
*
* @param int $len 长度
* @return string
*/
public static function numeric($len = 4)
{
return self::build('numeric', $len);
}
/**
* 数字和字母组合的随机字符串
*
* @param int $len 长度
* @return string
*/
public static function nozero($len = 4)
{
return self::build('nozero', $len);
}
/**
* 能用的随机数生成
* @param string $type 类型 alpha/alnum/numeric/nozero/unique/md5/encrypt/sha1
* @param int $len 长度
* @return string
*/
public static function build($type = 'alnum', $len = 8)
{
switch ($type)
{
case 'alpha':
case 'alnum':
case 'numeric':
case 'nozero':
switch ($type)
{
case 'alpha':
$pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
break;
case 'alnum':
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
break;
case 'numeric':
$pool = '0123456789';
break;
case 'nozero':
$pool = '123456789';
break;
}
return substr(str_shuffle(str_repeat($pool, ceil($len / strlen($pool)))), 0, $len);
case 'unique':
case 'md5':
return md5(uniqid(mt_rand()));
case 'encrypt':
case 'sha1':
return sha1(uniqid(mt_rand(), TRUE));
}
}
/**
* 根据数组元素的概率获得键名
*
* @param array $ps array('p1'=>20, 'p2'=>30, 'p3'=>50);
* @param array $num 默认为1,即随机出来的数量
* @param array $unique 默认为true,即当num>1时,随机出的数量是否唯一
* @return mixed 当num为1时返回键名,反之返回一维数组
*/
public static function lottery($ps, $num = 1, $unique = true)
{
if (!$ps)
{
return $num == 1 ? '' : [];
}
if ($num >= count($ps) && $unique)
{
$res = array_keys($ps);
return $num == 1 ? $res[0] : $res;
}
$max_exp = 0;
$res = [];
foreach ($ps as $key => $value)
{
$value = substr($value, 0, stripos($value, ".") + 6);
$exp = strlen(strchr($value, '.')) - 1;
if ($exp > $max_exp)
{
$max_exp = $exp;
}
}
$pow_exp = pow(10, $max_exp);
if ($pow_exp > 1)
{
reset($ps);
foreach ($ps as $key => $value)
{
$ps[$key] = $value * $pow_exp;
}
}
$pro_sum = array_sum($ps);
if ($pro_sum < 1)
{
return $num == 1 ? '' : [];
}
for ($i = 0; $i < $num; $i++)
{
$rand_num = mt_rand(1, $pro_sum);
reset($ps);
foreach ($ps as $key => $value)
{
if ($rand_num <= $value)
{
break;
}
else
{
$rand_num -= $value;
}
}
if ($num == 1)
{
$res = $key;
break;
}
else
{
$res[$i] = $key;
}
if ($unique)
{
$pro_sum -= $value;
unset($ps[$key]);
}
}
return $res;
}
/**
* 获取全球唯一标识
* @return string
*/
public static function uuid()
{
return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
}
}
... ...
<?php
namespace fast;
/**
* RSA签名类
*/
class Rsa
{
public $publicKey = '';
public $privateKey = '';
private $_privKey;
/**
* * private key
*/
private $_pubKey;
/**
* * public key
*/
private $_keyPath;
/**
* * the keys saving path
*/
/**
* * the construtor,the param $path is the keys saving path
*/
function __construct($publicKey = null, $privateKey = null)
{
$this->setKey($publicKey, $privateKey);
}
/**
* 设置公钥和私钥
* @param string $publicKey 公钥
* @param string $privateKey 私钥
*/
public function setKey($publicKey = null, $privateKey = null)
{
if (!is_null($publicKey))
$this->publicKey = $publicKey;
if (!is_null($privateKey))
$this->privateKey = $privateKey;
}
/**
* * setup the private key
*/
private function setupPrivKey()
{
if (is_resource($this->_privKey))
{
return true;
}
$pem = chunk_split($this->privateKey, 64, "\n");
$pem = "-----BEGIN PRIVATE KEY-----\n" . $pem . "-----END PRIVATE KEY-----\n";
$this->_privKey = openssl_pkey_get_private($pem);
return true;
}
/**
* * setup the public key
*/
private function setupPubKey()
{
if (is_resource($this->_pubKey))
{
return true;
}
$pem = chunk_split($this->publicKey, 64, "\n");
$pem = "-----BEGIN PUBLIC KEY-----\n" . $pem . "-----END PUBLIC KEY-----\n";
$this->_pubKey = openssl_pkey_get_public($pem);
return true;
}
/**
* * encrypt with the private key
*/
public function privEncrypt($data)
{
if (!is_string($data))
{
return null;
}
$this->setupPrivKey();
$r = openssl_private_encrypt($data, $encrypted, $this->_privKey);
if ($r)
{
return base64_encode($encrypted);
}
return null;
}
/**
* * decrypt with the private key
*/
public function privDecrypt($encrypted)
{
if (!is_string($encrypted))
{
return null;
}
$this->setupPrivKey();
$encrypted = base64_decode($encrypted);
$r = openssl_private_decrypt($encrypted, $decrypted, $this->_privKey);
if ($r)
{
return $decrypted;
}
return null;
}
/**
* * encrypt with public key
*/
public function pubEncrypt($data)
{
if (!is_string($data))
{
return null;
}
$this->setupPubKey();
$r = openssl_public_encrypt($data, $encrypted, $this->_pubKey);
if ($r)
{
return base64_encode($encrypted);
}
return null;
}
/**
* * decrypt with the public key
*/
public function pubDecrypt($crypted)
{
if (!is_string($crypted))
{
return null;
}
$this->setupPubKey();
$crypted = base64_decode($crypted);
$r = openssl_public_decrypt($crypted, $decrypted, $this->_pubKey);
if ($r)
{
return $decrypted;
}
return null;
}
/**
* 构造签名
* @param string $dataString 被签名数据
* @return string
*/
public function sign($dataString)
{
$this->setupPrivKey();
$signature = false;
openssl_sign($dataString, $signature, $this->_privKey);
return base64_encode($signature);
}
/**
* 验证签名
* @param string $dataString 被签名数据
* @param string $signString 已经签名的字符串
* @return number 1签名正确 0签名错误
*/
public function verify($dataString, $signString)
{
$this->setupPubKey();
$signature = base64_decode($signString);
$flg = openssl_verify($dataString, $signature, $this->_pubKey);
return $flg;
}
public function __destruct()
{
is_resource($this->_privKey) && @openssl_free_key($this->_privKey);
is_resource($this->_pubKey) && @openssl_free_key($this->_pubKey);
}
}
... ...
<?php
namespace fast;
use think\Config;
/**
* 通用的树型类
* @author XiaoYao <476552238li@gmail.com>
*/
class Tree
{
protected static $instance;
//默认配置
protected $config = [];
public $options = [];
/**
* 生成树型结构所需要的2维数组
* @var array
*/
public $arr = [];
/**
* 生成树型结构所需修饰符号,可以换成图片
* @var array
*/
public $icon = array('│', '├', '└');
public $nbsp = "&nbsp;";
public $pidname = 'pid';
public function __construct($options = [])
{
if ($config = Config::get('tree'))
{
$this->options = array_merge($this->config, $config);
}
$this->options = array_merge($this->config, $options);
}
/**
* 初始化
* @access public
* @param array $options 参数
* @return Tree
*/
public static function instance($options = [])
{
if (is_null(self::$instance))
{
self::$instance = new static($options);
}
return self::$instance;
}
/**
* 初始化方法
* @param array 2维数组,例如:
* array(
* 1 => array('id'=>'1','pid'=>0,'name'=>'一级栏目一'),
* 2 => array('id'=>'2','pid'=>0,'name'=>'一级栏目二'),
* 3 => array('id'=>'3','pid'=>1,'name'=>'二级栏目一'),
* 4 => array('id'=>'4','pid'=>1,'name'=>'二级栏目二'),
* 5 => array('id'=>'5','pid'=>2,'name'=>'二级栏目三'),
* 6 => array('id'=>'6','pid'=>3,'name'=>'三级栏目一'),
* 7 => array('id'=>'7','pid'=>3,'name'=>'三级栏目二')
* )
*/
public function init($arr = [], $pidname = NULL, $nbsp = NULL)
{
$this->arr = $arr;
if (!is_null($pidname))
$this->pidname = $pidname;
if (!is_null($nbsp))
$this->nbsp = $nbsp;
return $this;
}
/**
* 得到子级数组
* @param int
* @return array
*/
public function getChild($myid)
{
$newarr = [];
foreach ($this->arr as $value)
{
if (!isset($value['id']))
continue;
if ($value[$this->pidname] == $myid)
$newarr[$value['id']] = $value;
}
return $newarr;
}
/**
* 读取指定节点的所有孩子节点
* @param int $myid 节点ID
* @param boolean $withself 是否包含自身
* @return array
*/
public function getChildren($myid, $withself = FALSE)
{
$newarr = [];
foreach ($this->arr as $value)
{
if (!isset($value['id']))
continue;
if ($value[$this->pidname] == $myid)
{
$newarr[] = $value;
$newarr = array_merge($newarr, $this->getChildren($value['id']));
}
else if ($withself && $value['id'] == $myid)
{
$newarr[] = $value;
}
}
return $newarr;
}
/**
* 读取指定节点的所有孩子节点ID
* @param int $myid 节点ID
* @param boolean $withself 是否包含自身
* @return array
*/
public function getChildrenIds($myid, $withself = FALSE)
{
$childrenlist = $this->getChildren($myid, $withself);
$childrenids = [];
foreach ($childrenlist as $k => $v)
{
$childrenids[] = $v['id'];
}
return $childrenids;
}
/**
* 得到当前位置父辈数组
* @param int
* @return array
*/
public function getParent($myid)
{
$pid = 0;
$newarr = [];
foreach ($this->arr as $value)
{
if (!isset($value['id']))
continue;
if ($value['id'] == $myid)
{
$pid = $value[$this->pidname];
break;
}
}
if ($pid)
{
foreach ($this->arr as $value)
{
if ($value['id'] == $pid)
{
$newarr[] = $value;
break;
}
}
}
return $newarr;
}
/**
* 得到当前位置所有父辈数组
* @param int
* @return array
*/
public function getParents($myid, $withself = FALSE)
{
$pid = 0;
$newarr = [];
foreach ($this->arr as $value)
{
if (!isset($value['id']))
continue;
if ($value['id'] == $myid)
{
if ($withself)
{
$newarr[] = $value;
}
$pid = $value[$this->pidname];
break;
}
}
if ($pid)
{
$arr = $this->getParents($pid, TRUE);
$newarr = array_merge($arr, $newarr);
}
return $newarr;
}
/**
* 读取指定节点所有父类节点ID
* @param int $myid
* @param boolean $withself
* @return array
*/
public function getParentsIds($myid, $withself = FALSE)
{
$parentlist = $this->getParents($myid, $withself);
$parentsids = [];
foreach ($parentlist as $k => $v)
{
$parentsids[] = $v['id'];
}
return $parentsids;
}
/**
* 树型结构Option
* @param int $myid 表示获得这个ID下的所有子级
* @param string $itemtpl 条目模板 如:"<option value=@id @selected @disabled>@spacer@name</option>"
* @param mixed $selectedids 被选中的ID,比如在做树型下拉框的时候需要用到
* @param mixed $disabledids 被禁用的ID,比如在做树型下拉框的时候需要用到
* @param string $itemprefix 每一项前缀
* @param string $toptpl 顶级栏目的模板
* @return string
*/
public function getTree($myid, $itemtpl = "<option value=@id @selected @disabled>@spacer@name</option>", $selectedids = '', $disabledids = '', $itemprefix = '', $toptpl = '')
{
$ret = '';
$number = 1;
$childs = $this->getChild($myid);
if ($childs)
{
$total = count($childs);
foreach ($childs as $value)
{
$id = $value['id'];
$j = $k = '';
if ($number == $total)
{
$j .= $this->icon[2];
$k = $itemprefix ? $this->nbsp : '';
}
else
{
$j .= $this->icon[1];
$k = $itemprefix ? $this->icon[0] : '';
}
$spacer = $itemprefix ? $itemprefix . $j : '';
$selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
$disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
$value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled, 'spacer' => $spacer));
$value = array_combine(array_map(function($k)
{
return '@' . $k;
}, array_keys($value)), $value);
$nstr = strtr((($value["@{$this->pidname}"] == 0 || $this->getChild($id) ) && $toptpl ? $toptpl : $itemtpl), $value);
$ret .= $nstr;
$ret .= $this->getTree($id, $itemtpl, $selectedids, $disabledids, $itemprefix . $k . $this->nbsp, $toptpl);
$number++;
}
}
return $ret;
}
/**
* 树型结构UL
* @param int $myid 表示获得这个ID下的所有子级
* @param string $itemtpl 条目模板 如:"<li value=@id @selected @disabled>@name @childlist</li>"
* @param string $selectedids 选中的ID
* @param string $disabledids 禁用的ID
* @param string $wraptag 子列表包裹标签
* @return string
*/
public function getTreeUl($myid, $itemtpl, $selectedids = '', $disabledids = '', $wraptag = 'ul', $wrapattr = '')
{
$str = '';
$childs = $this->getChild($myid);
if ($childs)
{
foreach ($childs as $value)
{
$id = $value['id'];
unset($value['child']);
$selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
$disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
$value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled));
$value = array_combine(array_map(function($k)
{
return '@' . $k;
}, array_keys($value)), $value);
$nstr = strtr($itemtpl, $value);
$childdata = $this->getTreeUl($id, $itemtpl, $selectedids, $disabledids, $wraptag, $wrapattr);
$childlist = $childdata ? "<{$wraptag} {$wrapattr}>" . $childdata . "</{$wraptag}>" : "";
$str .= strtr($nstr, array('@childlist' => $childlist));
}
}
return $str;
}
/**
* 菜单数据
* @param int $myid
* @param string $itemtpl
* @param mixed $selectedids
* @param mixed $disabledids
* @param string $wraptag
* @param string $wrapattr
* @param int $deeplevel
* @return string
*/
public function getTreeMenu($myid, $itemtpl, $selectedids = '', $disabledids = '', $wraptag = 'ul', $wrapattr = '', $deeplevel = 0)
{
$str = '';
$childs = $this->getChild($myid);
if ($childs)
{
foreach ($childs as $value)
{
$id = $value['id'];
unset($value['child']);
$selected = in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
$disabled = in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
$value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled));
$value = array_combine(array_map(function($k)
{
return '@' . $k;
}, array_keys($value)), $value);
$bakvalue = array_intersect_key($value, array_flip(['@url', '@caret', '@class']));
$value = array_diff_key($value, $bakvalue);
$nstr = strtr($itemtpl, $value);
$value = array_merge($value, $bakvalue);
$childdata = $this->getTreeMenu($id, $itemtpl, $selectedids, $disabledids, $wraptag, $wrapattr, $deeplevel + 1);
$childlist = $childdata ? "<{$wraptag} {$wrapattr}>" . $childdata . "</{$wraptag}>" : "";
$childlist = strtr($childlist, array('@class' => $childdata ? 'last' : ''));
$value = array(
'@childlist' => $childlist,
'@url' => $childdata || !isset($value['@url']) ? "javascript:;" : url($value['@url']),
'@caret' => ($childdata && !$value['@badge'] ? '<i class="fa fa-angle-left"></i>' : ''),
'@badge' => $value['@badge'],
'@class' => ($selected ? ' active' : '') . ($disabled ? ' disabled' : '') . ($childdata ? ' treeview' : ''),
);
$str .= strtr($nstr, $value);
}
}
return $str;
}
/**
* 特殊
* @param integer $myid 要查询的ID
* @param string $itemtpl1 第一种HTML代码方式
* @param string $itemtpl2 第二种HTML代码方式
* @param mixed $selectedids 默认选中
* @param mixed $disabledids 禁用
* @param integer $itemprefix 前缀
*/
public function getTreeSpecial($myid, $itemtpl1, $itemtpl2, $selectedids = 0, $disabledids = 0, $itemprefix = '')
{
$ret = '';
$number = 1;
$childs = $this->getChild($myid);
if ($childs)
{
$total = count($childs);
foreach ($childs as $id => $value)
{
$j = $k = '';
if ($number == $total)
{
$j .= $this->icon[2];
$k = $itemprefix ? $this->nbsp : '';
}
else
{
$j .= $this->icon[1];
$k = $itemprefix ? $this->icon[0] : '';
}
$spacer = $itemprefix ? $itemprefix . $j : '';
$selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : '';
$disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : '';
$value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled, 'spacer' => $spacer));
$value = array_combine(array_map(function($k)
{
return '@' . $k;
}, array_keys($value)), $value);
$nstr = strtr(!isset($value['@disabled']) || !$value['@disabled'] ? $itemtpl1 : $itemtpl2, $value);
$ret .= $nstr;
$ret .= $this->getTreeSpecial($id, $itemtpl1, $itemtpl2, $selectedids, $disabledids, $itemprefix . $k . $this->nbsp);
$number++;
}
}
return $ret;
}
/**
*
* 获取树状数组
* @param string $myid 要查询的ID
* @param string $nametpl 名称条目模板
* @param string $itemprefix 前缀
* @return string
*/
public function getTreeArray($myid, $itemprefix = '')
{
$childs = $this->getChild($myid);
$n = 0;
$data = [];
$number = 1;
if ($childs)
{
$total = count($childs);
foreach ($childs as $id => $value)
{
$j = $k = '';
if ($number == $total)
{
$j .= $this->icon[2];
$k = $itemprefix ? $this->nbsp : '';
}
else
{
$j .= $this->icon[1];
$k = $itemprefix ? $this->icon[0] : '';
}
$spacer = $itemprefix ? $itemprefix . $j : '';
$value['spacer'] = $spacer;
$data[$n] = $value;
$data[$n]['childlist'] = $this->getTreeArray($id, $itemprefix . $k . $this->nbsp);
$n++;
$number++;
}
}
return $data;
}
/**
* 将getTreeArray的结果返回为二维数组
* @param array $data
* @return array
*/
public function getTreeList($data = [], $field = 'name')
{
$arr = [];
foreach ($data as $k => $v)
{
$childlist = isset($v['childlist']) ? $v['childlist'] : [];
unset($v['childlist']);
$v[$field] = $v['spacer'] . ' ' . $v[$field];
$v['haschild'] = $childlist ? 1 : 0;
if ($v['id'])
$arr[] = $v;
if ($childlist)
{
$arr = array_merge($arr, $this->getTreeList($childlist, $field));
}
}
return $arr;
}
}
... ...
<?php
namespace fast\payment;
use DOMDocument;
use Exception;
/**
* 支付宝
* @link https://github.com/mytharcher/alipay-php-sdk
*/
class Alipay
{
const SERVICE = 'create_direct_pay_by_user';
const SERVICE_WAP = 'alipay.wap.trade.create.direct';
const SERVICE_WAP_AUTH = 'alipay.wap.auth.authAndExecute';
const SERVICE_APP = 'mobile.securitypay.pay';
const GATEWAY = 'https://mapi.alipay.com/gateway.do?';
const GATEWAY_MOBILE = 'http://wappaygw.alipay.com/service/rest.htm?';
const VERIFY_URL = 'http://notify.alipay.com/trade/notify_query.do?';
const VERIFY_URL_HTTPS = 'https://mapi.alipay.com/gateway.do?service=notify_verify&';
// 配置信息在实例化时传入,以下为范例
private $config = array(
// 即时到账方式
'payment_type' => 1,
// 传输协议
'transport' => 'http',
// 编码方式
'input_charset' => 'utf-8',
// 签名方法
'sign_type' => 'MD5',
// 证书路径
'cacert' => './cacert.pem',
//验签公钥地址
'public_key_path' => './alipay_public_key.pem',
'private_key_path' => '',
// 支付完成异步通知调用地址
// 'notify_url' => 'http://'.$_SERVER['HTTP_HOST'].'/order/callback_alipay/notify',
// 支付完成同步返回地址
// 'return_url' => 'http://'.$_SERVER['HTTP_HOST'].'/order/callback_alipay/return',
// 支付宝商家 ID
'partner' => '2088xxxxxxxx',
// // 支付宝商家 KEY
'key' => 'xxxxxxxxxxxx',
// // 支付宝商家注册邮箱
'seller_email' => 'email@domain.com'
);
private $is_mobile = FALSE;
public $service = self::SERVICE;
public $gateway = self::GATEWAY;
/**
* 配置
* @param $options array 配置信息
* @param null $type string 类型 wap app
*/
public function __construct($options = [], $type = null)
{
if ($config = Config::get('payment.alipay'))
{
$this->config = array_merge($this->config, $config);
}
$this->config = array_merge($this->config, is_array($options) ? $options : []);
$this->is_mobile = (($type == 'wap' || $type === true) ? true : false);
if ($this->is_mobile)
{
$this->gateway = self::GATEWAY_MOBILE;
}
if ($type == 'wap' || $type === true)
{
$this->service = self::SERVICE_WAP;
}
elseif ($type == 'app')
{
$this->service = self::SERVICE_APP;
}
}
/**
* 生成请求参数的签名
*
* @param $params <Array>
* @return <String>
*
*/
function signParameters($params)
{
// 支付宝的签名串必须是未经过 urlencode 的字符串
// 不清楚为何 PHP 5.5 里没有 http_build_str() 方法
$paramStr = urldecode(http_build_query($params));
switch (strtoupper(trim($this->config['sign_type'])))
{
case "MD5" :
$result = md5($paramStr . $this->config['key']);
break;
case "RSA" :
case "0001" :
$priKey = file_get_contents($this->config['private_key_path']);
$res = openssl_get_privatekey($priKey);
openssl_sign($paramStr, $sign, $res);
openssl_free_key($res);
//base64编码
$result = base64_encode($sign);
break;
default :
$result = "";
}
return $result;
}
/**
* 准备签名参数
*
* @param $params <Array>
* $params['out_trade_no'] 唯一订单编号
* $params['subject']
* $params['total_fee']
* $params['body']
* $params['show_url']
* $params['anti_phishing_key']
* $params['exter_invoke_ip']
* $params['it_b_pay']
* $params['_input_charset']
* @return <Array>
*/
function prepareParameters($params)
{
$default = array(
'service' => $this->service,
'partner' => $this->config['partner'],
'_input_charset' => trim(strtolower($this->config['input_charset']))
);
if (!$this->is_mobile)
{
$default = array_merge($default, array(
'payment_type' => $this->config['payment_type'],
'seller_id' => $this->config['partner'],
'notify_url' => $this->config['notify_url'],
));
if (isset($this->config['return_url']))
{
$default['return_url'] = $this->config['return_url'];
}
}
$params = $this->filterSignParameter(array_merge($default, (array) $params));
ksort($params);
reset($params);
return $params;
}
/**
* 生成签名后的请求参数
*
*/
function buildSignedParameters($params)
{
$params = $this->prepareParameters($params);
$params['sign'] = $this->signParameters($params);
if ($params['service'] != self::SERVICE_WAP && $params['service'] != self::SERVICE_WAP_AUTH)
{
$params['sign_type'] = strtoupper(trim($this->config['sign_type']));
}
return $params;
}
/**
* https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.NgdeQA&treeId=59&articleId=103663&docType=1
* 服务端生成app支付使用的参数以及签名
* @param $params <Array>
* @return <Array>
*/
function buildSignedParametersForApp($params)
{
$params = $this->prepareParameters($params);
$params['sign'] = urlencode($this->signParameters($params));
$params['sign_type'] = 'RSA';
$paramStr = [];
foreach ($params as $k => &$param)
{
$param = '"' . $param . '"';
$paramStr[] = $k . '=' . $param;
}
return implode('&', $paramStr);
}
/**
* 生成请求参数的发送表单HTML
*
* 其实这个函数没有必要,更应该使用签名后的参数自己组装,只不过有时候方便就从官方 SDK 里留下了。
*
* @param $params <Array> 请求参数(未签名的)
* @param $method <String> 请求方法,默认:post,可选 get
* @param $target <String> 提交目标,默认:_self
* @return <String>
*
*/
function buildRequestFormHTML($params, $method = 'post', $target = '_self')
{
$params = $this->buildSignedParameters($params);
$html = '<meta charset="' . $this->config['input_charset'] . '" /><form id="alipaysubmit" name="alipaysubmit" action="' . $this->gateway . ' _input_charset="' . trim(strtolower($this->config['input_charset'])) . '" method="' . $method . ' target="$target">';
foreach ($params as $key => $value)
{
$html .= "<input type='hidden' name='$key' value='$value'/>";
}
$html .= "</form><script>document.forms['alipaysubmit'].submit();</script>";
return $html;
}
/**
* 准备移动网页支付的请求参数
*
* 移动网页支付接口不同,需要先服务器提交一次请求,拿到返回 token 再返回客户端发起真实支付请求。
* 该方法只完成第一次服务端请求,生成参数后需要客户端另行处理(可调用`buildRequestFormHTML`生成表单提交)。
*
* @param $params <Array>
* $params['out_trade_no'] 订单唯一编号
* $params['subject'] 商品标题
* $params['total_fee'] 支付总费用
* $params['merchant_url'] 商品链接地址
* $params['req_id'] 请求唯一 ID
* $params['it_b_pay'] 超期时间(秒)
* @return <Array>/<NULL>
*/
function prepareMobileTradeData($params)
{
// 不要用 SimpleXML 来构建 xml 结构,因为有第一行文档申明支付宝验证不通过
$xml_str = '<direct_trade_create_req>' .
'<notify_url>' . $this->config['notify_url'] . '</notify_url>' .
'<call_back_url>' . $this->config['return_url'] . '</call_back_url>' .
'<seller_account_name>' . $this->config['seller_email'] . '</seller_account_name>' .
'<out_trade_no>' . $params['out_trade_no'] . '</out_trade_no>' .
'<subject>' . htmlspecialchars($params['subject'], ENT_XML1, 'UTF-8') . '</subject>' .
'<total_fee>' . $params['total_fee'] . '</total_fee>' .
'<merchant_url>' . $params['merchant_url'] . '</merchant_url>' .
(isset($params['it_b_pay']) ? '<pay_expire>' . $params['it_b_pay'] . '</pay_expire>' : '') .
'</direct_trade_create_req>';
$request_data = $this->buildSignedParameters(array(
'service' => $this->service,
'partner' => $this->config['partner'],
'sec_id' => $this->config['sign_type'],
'format' => 'xml',
'v' => '2.0',
'req_id' => $params['req_id'],
'req_data' => $xml_str
));
$url = $this->gateway;
$input_charset = trim(strtolower($this->config['input_charset']));
if (trim($input_charset) != '')
{
$url = $url . "_input_charset=" . $input_charset;
}
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); //SSL证书认证
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); //严格认证
curl_setopt($curl, CURLOPT_CAINFO, $this->config['cacert']); //证书地址
curl_setopt($curl, CURLOPT_HEADER, 0); // 过滤HTTP头
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 显示输出结果
curl_setopt($curl, CURLOPT_POST, true); // post传输数据
curl_setopt($curl, CURLOPT_POSTFIELDS, $request_data); // post传输数据
$responseText = curl_exec($curl);
//var_dump( curl_error($curl) );//如果执行curl过程中出现异常,可打开此开关,以便查看异常内容
curl_close($curl);
if (empty($responseText))
{
return NULL;
}
parse_str($responseText, $responseData);
if (empty($responseData['res_data']))
{
return NULL;
}
if ($this->config['sign_type'] == '0001')
{
$responseData['res_data'] = $this->rsaDecrypt($responseData['res_data'], $this->config['private_key_path']);
}
//token从res_data中解析出来(也就是说res_data中已经包含token的内容)
$doc = new DOMDocument();
$doc->loadXML($responseData['res_data']);
$responseData['request_token'] = $doc->getElementsByTagName("request_token")->item(0)->nodeValue;
$xml_str = '<auth_and_execute_req>' .
'<request_token>' . $responseData['request_token'] . '</request_token>' .
'</auth_and_execute_req>';
return array(
'service' => self::SERVICE_WAP_AUTH,
'partner' => $this->config['partner'],
'sec_id' => $this->config['sign_type'],
'format' => 'xml',
'v' => '2.0',
'req_data' => $xml_str
);
}
/**
* 支付完成验证返回参数(包含同步和异步)
*
* @return <Boolean>
*/
function verifyCallback()
{
$async = empty($_GET);
$data = $async ? $_POST : $_GET;
if (empty($data))
{
return FALSE;
}
$signValid = $this->verifyParameters($data, $data["sign"]);
$notify_id = isset($data['notify_id']) ? $data['notify_id'] : NULL;
if ($async && $this->is_mobile)
{
//对notify_data解密
if ($this->config['sign_type'] == '0001')
{
$data['notify_data'] = $this->rsaDecrypt($data['notify_data'], $this->config['private_key_path']);
}
//notify_id从decrypt_post_para中解析出来(也就是说decrypt_post_para中已经包含notify_id的内容)
$doc = new DOMDocument();
$doc->loadXML($data['notify_data']);
$notify_id = $doc->getElementsByTagName('notify_id')->item(0)->nodeValue;
}
//获取支付宝远程服务器ATN结果(验证是否是支付宝发来的消息)
$responseTxt = 'true';
if (!empty($notify_id))
{
$responseTxt = $this->verifyFromServer($notify_id);
}
//验证
//$signValid的结果不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
//$responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
return $signValid && preg_match("/true$/i", $responseTxt);
}
function verifyParameters($params, $sign)
{
$params = $this->filterSignParameter($params);
if (isset($params['notify_data']))
{
$params = array(
'service' => $params['service'],
'v' => $params['v'],
'sec_id' => $params['sec_id'],
'notify_data' => $params['notify_data']
);
}
else
{
ksort($params);
reset($params);
}
$content = urldecode(http_build_query($params));
switch (strtoupper(trim($this->config['sign_type'])))
{
case "MD5" :
return md5($content . $this->config['key']) == $sign;
case "RSA" :
case "0001" :
return $this->rsaVerify($content, $this->config['public_key_path'], $sign);
default :
return FALSE;
}
}
/**
* 过滤参数,去除sign/sign_type参数
* @param $params
* @return <Array>
*/
function filterSignParameter($params)
{
$result = array();
foreach ($params as $key => $value)
{
if ($key != 'sign' && $key != 'sign_type' && $value)
{
$result[$key] = $value;
}
}
return $result;
}
function verifyFromServer($notify_id)
{
$transport = strtolower(trim($this->config['transport']));
$partner = trim($this->config['partner']);
$veryfy_url = ($transport == 'https' ? self::VERIFY_URL_HTTPS : self::VERIFY_URL) . "partner=$partner&notify_id=$notify_id";
$curl = curl_init($veryfy_url);
curl_setopt($curl, CURLOPT_HEADER, 0); // 过滤HTTP头
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); //SSL证书认证
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); //严格认证
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 显示输出结果
curl_setopt($curl, CURLOPT_CAINFO, $this->config['cacert']); //证书地址
$responseText = curl_exec($curl);
// var_dump( curl_error($curl) );//如果执行curl过程中出现异常,可打开此开关,以便查看异常内容
curl_close($curl);
return $responseText;
}
/**
* RSA验签,注意验签的公钥是支付宝的公钥,不是自己生成的rsa公钥,可以在淘宝的demo中获得
* @param $data string 待签名数据
* @param $ali_public_key_path string 支付宝的公钥文件路径
* @param $sign string 要校对的的签名结果
* @return <Boolean> 验证结果
* @throws Exception
*/
function rsaVerify($data, $ali_public_key_path, $sign)
{
$pubKey = file_get_contents($ali_public_key_path);
$res = openssl_get_publickey($pubKey);
if (!$res)
{
throw new Exception('公钥格式错误');
}
$result = (bool) openssl_verify($data, base64_decode($sign), $res);
openssl_free_key($res);
return $result;
}
/**
* RSA解密
* @param $content string 需要解密的内容,密文
* @param $private_key_path string 商户私钥文件路径
* @return string 解密后内容,明文
*/
function rsaDecrypt($content, $private_key_path)
{
$priKey = file_get_contents($private_key_path);
$res = openssl_get_privatekey($priKey);
//用base64将内容还原成二进制
$content = base64_decode($content);
//把需要解密的内容,按128位拆开解密
$result = '';
for ($i = 0; $i < strlen($content) / 128; $i++)
{
$data = substr($content, $i * 128, 128);
openssl_private_decrypt($data, $decrypt, $res);
$result .= $decrypt;
}
openssl_free_key($res);
return $result;
}
}
... ...
<?php
namespace fast\payment;
use Exception;
use think\Config;
/**
* @link https://github.com/zhangv/wechat-pay
*/
class Wechat
{
const TRADETYPE_JSAPI = 'JSAPI', TRADETYPE_NATIVE = 'NATIVE', TRADETYPE_APP = 'APP';
const URL_UNIFIEDORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
const URL_ORDERQUERY = "https://api.mch.weixin.qq.com/pay/orderquery";
const URL_CLOSEORDER = 'https://api.mch.weixin.qq.com/pay/closeorder';
const URL_REFUND = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
const URL_REFUNDQUERY = 'https://api.mch.weixin.qq.com/pay/refundquery';
const URL_DOWNLOADBILL = 'https://api.mch.weixin.qq.com/pay/downloadbill';
const URL_REPORT = 'https://api.mch.weixin.qq.com/payitil/report';
const URL_SHORTURL = 'https://api.mch.weixin.qq.com/tools/shorturl';
const URL_MICROPAY = 'https://api.mch.weixin.qq.com/pay/micropay';
/**
* 错误信息
*/
public $error = null;
/**
* 错误信息XML
*/
public $errorXML = null;
/**
* 微信支付配置数组
* appid 公众账号appid
* mch_id 商户号
* apikey 加密key
* appsecret 公众号appsecret
* sslcertPath 证书路径(apiclient_cert.pem)
* sslkeyPath 密钥路径(apiclient_key.pem)
*/
private $_config;
/**
* @param $options 微信支付配置数组
*/
public function __construct($options = [])
{
if ($config = Config::get('payment.wechat'))
{
$this->config = array_merge($this->config, $config);
}
$this->_config = array_merge($this->_config, is_array($options) ? $options : []);
}
/**
* JSAPI获取prepay_id
*
* @param string $body
* @param string $out_trade_no
* @param int $total_fee
* @param string $openid
* @param array $ext
* @return string
*/
public function getPrepayId($body, $out_trade_no, $total_fee, $openid, $ext = null)
{
$data = $ext? : [];
$data["nonce_str"] = $this->getNonceStr();
$data["body"] = $body;
$data["out_trade_no"] = $out_trade_no;
$data["total_fee"] = $total_fee;
$data["spbill_create_ip"] = $_SERVER["REMOTE_ADDR"];
$data["trade_type"] = self::TRADETYPE_JSAPI;
$data["openid"] = $openid;
$result = $this->unifiedOrder($data);
if ($result["return_code"] == "SUCCESS" && $result["result_code"] == "SUCCESS")
{
return $result["prepay_id"];
}
else
{
$this->error = $result["return_code"] == "SUCCESS" ? $result["err_code_des"] : $result["return_msg"];
$this->errorXML = $this->array2xml($result);
return null;
}
}
private function getNonceStr()
{
return substr(str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789"), 0, 32);
}
/**
* 统一下单接口
*/
public function unifiedOrder($params)
{
$data = array();
$data["appid"] = $this->_config["appid"];
$data["mch_id"] = $this->_config["mch_id"];
$data["device_info"] = (isset($params['device_info']) && trim($params['device_info']) != '') ? $params['device_info'] : null;
$data["nonce_str"] = $this->getNonceStr();
$data["body"] = $params['body'];
$data["detail"] = isset($params['detail']) ? $params['detail'] : null; //optional
$data["attach"] = isset($params['attach']) ? $params['attach'] : null; //optional
$data["out_trade_no"] = isset($params['out_trade_no']) ? $params['out_trade_no'] : null;
$data["fee_type"] = isset($params['fee_type']) ? $params['fee_type'] : 'CNY';
$data["total_fee"] = $params['total_fee'];
$data["spbill_create_ip"] = $params['spbill_create_ip'];
$data["time_start"] = isset($params['time_start']) ? $params['time_start'] : null; //optional
$data["time_expire"] = isset($params['time_expire']) ? $params['time_expire'] : null; //optional
$data["goods_tag"] = isset($params['goods_tag']) ? $params['goods_tag'] : null;
$data["notify_url"] = isset($params['notify_url']) ? $params['notify_url'] : $this->_config['notify_url'];
$data["trade_type"] = $params['trade_type'];
$data["product_id"] = isset($params['product_id']) ? $params['product_id'] : null; //required when trade_type = NATIVE
$data["openid"] = isset($params['openid']) ? $params['openid'] : null; //required when trade_type = JSAPI
$result = $this->post(self::URL_UNIFIEDORDER, $data);
return $result;
}
private function post($url, $data, $cert = false)
{
if (!isset($data['sign']))
$data["sign"] = $this->sign($data);
$xml = $this->array2xml($data);
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $url);
if ($cert == true)
{
//使用证书:cert 与 key 分别属于两个.pem文件
curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
curl_setopt($ch, CURLOPT_SSLCERT, $this->_config['sslcertPath']);
curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
curl_setopt($ch, CURLOPT_SSLKEY, $this->_config['sslkeyPath']);
}
$content = curl_exec($ch);
$array = $this->xml2array($content);
return $array;
}
/**
* 扫码支付(模式二)获取支付二维码
*
* @param string $body
* @param string $out_trade_no
* @param int $total_fee
* @param string $product_id
* @param array $ext
* @return string
*/
public function getCodeUrl($body, $out_trade_no, $total_fee, $product_id, $ext = null)
{
$data = $ext ? $ext : [];
$data["nonce_str"] = $this->getNonceStr();
$data["body"] = $body;
$data["out_trade_no"] = $out_trade_no;
$data["total_fee"] = $total_fee;
$data["spbill_create_ip"] = $_SERVER["SERVER_ADDR"];
$data["trade_type"] = self::TRADETYPE_NATIVE;
$data["product_id"] = $product_id;
$result = $this->unifiedOrder($data);
if ($result["return_code"] == "SUCCESS" && $result["result_code"] == "SUCCESS")
{
return $result["code_url"];
}
else
{
$this->error = $result["return_code"] == "SUCCESS" ? $result["err_code_des"] : $result["return_msg"];
return null;
}
}
/**
* 查询订单
* @param $transaction_id
* @param $out_trade_no
* @return array
*/
public function orderQuery($transaction_id, $out_trade_no)
{
$data = array();
$data["appid"] = $this->_config["appid"];
$data["mch_id"] = $this->_config["mch_id"];
$data["transaction_id"] = $transaction_id;
$data["out_trade_no"] = $out_trade_no;
$data["nonce_str"] = $this->getNonceStr();
$result = $this->post(self::URL_ORDERQUERY, $data);
return $result;
}
/**
* 关闭订单
* @param $out_trade_no
* @return array
*/
public function closeOrder($out_trade_no)
{
$data = array();
$data["appid"] = $this->_config["appid"];
$data["mch_id"] = $this->_config["mch_id"];
$data["out_trade_no"] = $out_trade_no;
$data["nonce_str"] = $this->getNonceStr();
$result = $this->post(self::URL_CLOSEORDER, $data);
return $result;
}
/**
* 申请退款 - 使用商户订单号
* @param $out_trade_no 商户订单号
* @param $out_refund_no 退款单号
* @param $total_fee 总金额(单位:分)
* @param $refund_fee 退款金额(单位:分)
* @param $op_user_id 操作员账号
* @return array
*/
public function refund($out_trade_no, $out_refund_no, $total_fee, $refund_fee, $op_user_id)
{
$data = array();
$data["appid"] = $this->_config["appid"];
$data["mch_id"] = $this->_config["mch_id"];
$data["nonce_str"] = $this->getNonceStr();
$data["out_trade_no"] = $out_trade_no;
$data["out_refund_no"] = $out_refund_no;
$data["total_fee"] = $total_fee;
$data["refund_fee"] = $refund_fee;
$data["op_user_id"] = $op_user_id;
$result = $this->post(self::URL_REFUND, $data, true);
return $result;
}
/**
* 申请退款 - 使用微信订单号
* @param $transaction_id 微信订单号
* @param $out_refund_no 退款单号
* @param $total_fee 总金额(单位:分)
* @param $refund_fee 退款金额(单位:分)
* @param $op_user_id 操作员账号
* @return array
*/
public function refundByTransId($transaction_id, $out_refund_no, $total_fee, $refund_fee, $op_user_id)
{
$data = array();
$data["appid"] = $this->_config["appid"];
$data["mch_id"] = $this->_config["mch_id"];
$data["nonce_str"] = $this->getNonceStr();
$data["transaction_id"] = $transaction_id;
$data["out_refund_no"] = $out_refund_no;
$data["total_fee"] = $total_fee;
$data["refund_fee"] = $refund_fee;
$data["op_user_id"] = $op_user_id;
$result = $this->post(self::URL_REFUND, $data, true);
return $result;
}
/**
* 下载对账单
* @param $bill_date 下载对账单的日期,格式:20140603
* @param string $bill_type 类型
* @return array
*/
public function downloadBill($bill_date, $bill_type = 'ALL')
{
$data = array();
$data["appid"] = $this->_config["appid"];
$data["mch_id"] = $this->_config["mch_id"];
$data["bill_date"] = $bill_date;
$data["bill_type"] = $bill_type;
$data["nonce_str"] = $this->getNonceStr();
$result = $this->post(self::URL_DOWNLOADBILL, $data);
return $result;
}
/**
* 扫码原生支付模式一中的二维码链接转成短链接
* @param $long_url 需要转换的URL,签名用原串,传输需URLencode
* @return array
*/
public function shortUrl($long_url)
{
$data = array();
$data["appid"] = $this->_config["appid"];
$data["mch_id"] = $this->_config["mch_id"];
$data["long_url"] = $long_url;
$data["nonce_str"] = $this->getNonceStr();
$data["sign"] = $this->sign($data);
$data["long_url"] = urlencode($long_url);
$result = $this->post(self::URL_SHORTURL, $data);
return $result;
}
/**
* 获取jsapi支付所需参数
*
* @param string $prepay_id
* @return array
*/
public function getPackageData($prepay_id)
{
$data = array();
$data["appId"] = $this->_config["appid"];
//解决微信支付调用JSAPI缺少参数:timeStamp
$data["timeStamp"] = time();
$data["nonceStr"] = $this->getNonceStr();
$data["package"] = "prepay_id=$prepay_id";
$data["signType"] = "MD5";
$data["paySign"] = $this->sign($data);
return $data;
}
/**
* 获取发送到通知地址的数据(在通知地址内使用)
* @return string 结果数组,如果不是微信服务器发送的数据返回null
* appid
* bank_type
* cash_fee
* fee_type
* is_subscribe
* mch_id
* nonce_str
* openid
* out_trade_no 商户订单号
* result_code
* return_code
* sign
* time_end
* total_fee 总金额
* trade_type
* transaction_id 微信支付订单号
*/
public function getNotifyData()
{
$xml = file_get_contents("php://input");
$data = $this->xml2array($xml);
if ($this->validate($data))
{
return $data;
}
else
{
return null;
}
}
/**
* 验证数据签名
* @param $data 数据数组
* @return 数据校验结果
*/
public function validate($data)
{
if (!isset($data["sign"]))
{
return false;
}
$sign = $data["sign"];
unset($data["sign"]);
return $this->sign($data) == $sign;
}
/**
* 响应微信支付后台通知
* @param string $return_code 返回状态码 SUCCESS/FAIL
* @param $return_msg 返回信息
*/
public function response_back($return_code = "SUCCESS", $return_msg = null)
{
$data = array();
$data["return_code"] = $return_code;
if ($return_msg)
{
$data["return_msg"] = $return_msg;
}
$xml = $this->array2xml($data);
print $xml;
}
/**
* 数据签名
* @param $data
* @return string
*/
private function sign($data)
{
ksort($data);
$string1 = "";
foreach ($data as $k => $v)
{
if ($v && trim($v) != '')
{
$string1 .= "$k=$v&";
}
}
$stringSignTemp = $string1 . "key=" . $this->_config["apikey"];
$sign = strtoupper(md5($stringSignTemp));
return $sign;
}
private function array2xml($array)
{
$xml = "<xml>" . PHP_EOL;
foreach ($array as $k => $v)
{
if ($v && trim($v) != '')
$xml .= "<$k><![CDATA[$v]]></$k>" . PHP_EOL;
}
$xml .= "</xml>";
return $xml;
}
private function xml2array($xml)
{
$array = array();
$tmp = null;
try
{
$tmp = (array) simplexml_load_string($xml);
}
catch (Exception $e)
{
}
if ($tmp && is_array($tmp))
{
foreach ($tmp as $k => $v)
{
$array[$k] = (string) $v;
}
}
return $array;
}
}
... ...
<?php
namespace fast\service;
use think\Config;
/**
* 阿里大于SMS短信发送
*/
class Alisms
{
private $_params = [];
public $error = [];
protected $config = [];
public function __construct($options = [])
{
if ($config = Config::get('service.alisms'))
{
$this->config = array_merge($this->config, $config);
}
$this->config = array_merge($this->config, is_array($options) ? $options : []);
}
/**
* 单例
* @param array $options 参数
* @return Alisms
*/
public static function instance($options = [])
{
if (is_null(self::$instance))
{
self::$instance = new static($options);
}
return self::$instance;
}
/**
* 设置签名
* @param string $sign
* @return Alisms
*/
public function sign($sign = '')
{
$this->_params['sms_free_sign_name'] = $sign;
return $this;
}
/**
* 设置参数
* @param array $param
* @return Alisms
*/
public function param(array $param = [])
{
foreach ($param as $k => &$v)
{
$v = (string) $v;
}
unset($v);
$this->_params['sms_param'] = json_encode($param);
return $this;
}
/**
* 设置模板
* @param string $code 短信模板
* @return Alisms
*/
public function template($code = '')
{
$this->_params['sms_template_code'] = $code;
return $this;
}
/**
* 接收手机
* @param string $mobile 手机号码
* @return Alisms
*/
public function mobile($mobile = '')
{
$this->_params['rec_num'] = $mobile;
return $this;
}
/**
* 立即发送
* @return boolean
*/
public function send()
{
$this->error = [];
$params = $this->_params();
$params['sign'] = $this->_signed($params);
$reponse = $this->_curl($params);
if ($reponse !== FALSE)
{
$res = json_decode($reponse, TRUE);
$res = array_pop($res);
if (isset($res['result']))
return TRUE;
$this->error = $res;
}
else
{
$this->error = array('code' => 0, 'msg' => 'HTTP_RESPONSE_NOT_WELL_FORMED');
}
return FALSE;
}
/**
* 获取错误信息
* @return array
*/
public function getError()
{
return $this->error;
}
private function _params()
{
return array_merge([
'app_key' => $this->config['key'],
'format' => 'json',
'method' => 'alibaba.aliqin.fc.sms.num.send',
'v' => '2.0',
'timestamp' => date('Y-m-d H:i:s'),
'sign_method' => 'md5',
'sms_type' => 'normal'
], $this->_params);
}
private function _signed($params)
{
ksort($params);
$sign = $this->config['secret'];
foreach ($params as $k => $v)
{
if (is_string($v) && '@' != substr($v, 0, 1))
$sign .= $k . $v;
}
$sign .= $this->config['secret'];
return strtoupper(md5($sign));
}
private function _curl($params)
{
$uri = 'https://eco.taobao.com/router/rest?' . http_build_query($params);
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_URL, $uri);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36");
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
$reponse = curl_exec($ch);
curl_close($ch);
return $reponse;
}
}
... ...
<?php
namespace fast\service;
use fast\Http;
use think\Cache;
use think\Config;
use think\Log;
/**
* 环信消息类
*/
class Easemob
{
const URL = 'https://a1.easemob.com';
static $_config = null;
/**
* 创建用户
*
* @param string $username 用户名
* @param string $password 密码
* @param string $nickname 昵称
* @param boolean $token 是否认证模式
* @return array
*/
public static function register($username, $password, $nickname = '', $token = TRUE)
{
$params = ['username' => $username, 'password' => $password];
if ($nickname)
{
$params['nickname'] = $nickname;
}
return self::api('users', $params, $token);
}
/**
* 创建聊天室
*
* @param string $name 名称
* @param string $description 描述
* @param string $owner 创建人
* @param int $maxusers 最多参与者
* @param array $members 成员
* @return array
*/
public static function createRoom($name, $description, $owner, $maxusers = 5000, $members = [])
{
$owner = (string) $owner;
$params = [
"name" => $name, //聊天室名称,此属性为必须的
"description" => $description, //聊天室描述,此属性为必须的
"maxusers" => $maxusers, //聊天室成员最大数(包括群主),值为数值类型,默认值200,最大值5000,此属性为可选的
"owner" => $owner, //聊天室的管理员,此属性为必须的
];
if ($members)
{
if (!in_array($owner, $members))
{
$members[] = $owner;
}
$params['members'] = $members;
}
return self::api('chatrooms', $params, TRUE);
}
/**
* 创建群组
*
* @param string $name 名称
* @param string $description 描述
* @param string $owner 管理员
* @param int $maxusers 最大成员数量
* @param array $members 成员列表
* @param boolean $public 是否公开
* @param boolean $approval 加入是否审核
* @return array
*/
public static function createGroup($name, $description, $owner, $maxusers = 2000, $members = [], $public = TRUE, $approval = FALSE)
{
$owner = (string) $owner;
$params = [
"groupname" => $name,
"desc" => $description,
"public" => (bool) $public,
"maxusers" => $maxusers,
"approval" => (bool) $approval,
"owner" => $owner,
];
if ($members)
{
if (!in_array($owner, $members))
{
$members[] = $owner;
}
$params['members'] = $members;
}
return self::api('chatgroups', $params, TRUE);
}
/**
* 修改群组信息
*
* @param int $group_id
* @param string $name
* @param string $description
* @param int $maxusers
* @return array
*/
public static function editGroup($group_id, $name, $description, $maxusers)
{
$params = [
"groupname" => $name,
"desc" => $description,
"maxusers" => $maxusers,
];
return self::api('chatgroups/' . $group_id, $params, TRUE, 'PUT');
}
/**
* 获取好友列表
*/
public static function getFiends($owner_username)
{
return self::api("users/{$owner_username}/contacts/users", [], TRUE, 'GET');
}
/**
* 删除群组
*
* @param int $group_id
* @return array
*/
public static function deleteGroup($group_id)
{
$params = [];
return self::api('chatgroups/' . $group_id, $params, TRUE, 'DELETE');
}
/**
* 发送消息
*
* @param string $from 发件人
* @param string $to 收件人
* @param mixed $msg 消息内容
* @param string $target_type 消息类型 users/chatgroups/chatrooms
* @return array
*/
public static function sendMessage($from, $to, $msg, $target_type = 'users')
{
if (!is_array($msg))
{
$msg = [
'type' => 'txt',
'msg' => $msg
];
}
$data = [
'target_type' => $target_type,
'target' => is_array($to) ? $to : [$to],
'from' => $from,
];
if (isset($msg['ext']))
{
$data['ext'] = $msg['ext'];
}
unset($msg['ext']);
$data['msg'] = $msg;
return self::api('messages', $data);
}
/**
* 获取离线消息记录条数
* @param string $owner_username
* @return array
*/
public static function getOfflineMsgCount($owner_username)
{
return self::api("users/{$owner_username}/offline_msg_count", [], TRUE, 'GET');
}
/**
* 群组添加成员
* @param int $chatroom_id
* @param array $usernames
* @param array
*/
public static function addChatRoomMembers($chatroom_id, $usernames)
{
return self::api("chatgroups/{$chatroom_id}/users", $usernames, TRUE);
}
/**
* 添加单个成员POST
*/
public static function addOneChatRoomMember($chatroom_id, $username)
{
//return $chatroom_id;
return self::api("chatgroups/{$chatroom_id}/users/{$username}", [], TRUE);
}
/**
* 群组删除成员
* @param int $chatroom_id
* @param string $usernames
* @return array
*/
public static function minusChatRoomMembers($chatroom_id, $usernames)
{
return self::api("chatgroups/{$chatroom_id}/users/{$usernames}", [], TRUE, 'DELETE');
}
/**
* 添加好友
*/
public static function addFriends($owner_username, $friend_username)
{
return self::api("users/{$owner_username}/contacts/users/{$friend_username}", [], TRUE);
}
/**
* 删除好友
*/
public static function minusFriends($owner_username, $friend_username)
{
return self::api("users/{$owner_username}/contacts/users/{$friend_username}", [], TRUE, 'DELETE');
}
/**
* 查看用户参与的所有群组
* @param type $owner_username
* @return type
*/
public static function joinedChatgroups($owner_username)
{
return self::api("users/{$owner_username}/joined_chatgroups", [], TRUE, 'GET');
}
/**
* 调用API接口
*
* @param string $api 接口
* @param array $params request head参数
* @param boolean $token 是否认证模式
* @param string $method 请求方法 POST/GET
* @param array $options 扩展配置
* @return array
*/
public static function api($api, $params = [], $token = TRUE, $method = 'POST', $options = [])
{
$header = ['Content-Type:application/json'];
if ($token)
{
$header[] = self::getNewToken();
}
$config = Config::get('service.easemob');
$options[CURLOPT_HTTPHEADER] = $header;
$url = self::URL . '/' . $config['org_name'] . '/' . $config['app_name'] . '/' . $api;
//return $url;
$ret = Http::sendRequest($url, json_encode($params), $method, $options);
if ($ret['ret'] && $ret['msg'])
{
$msg = json_decode($ret['msg'], TRUE);
if (isset($msg['error']))
{
Log::error($ret['msg']);
}
return isset($msg['error']) ? [] : $msg;
}
else
{
return [];
}
}
private static function getToken()
{
$tokendata = Cache::get('easemobtoken');
if ($tokendata && $tokendata['expiretime'] > time())
{
return $tokendata['access_token'];
}
else
{
$config = Config::get('service.easemob');
$data = self::api('token', [
'grant_type' => 'client_credentials',
'client_id' => $config['client_id'],
'client_secret' => $config['client_secret'],
], FALSE, 'POST');
if ($data)
{
$data['expiretime'] = time() + $data['expires_in'];
Cache::set('easemobtoken', $data, $data['expires_in'] - 10);
return $data['access_token'];
}
else
{
return '';
}
}
}
}
... ...
<?php
namespace fast\service;
use think\Config;
use think\Log;
/**
* 七牛上传文件管理
*/
class Qiniu
{
const QINIU_RS = 'http://rs.qbox.me';
static $_config = null;
/**
* 获取文件
*
* @param string $domain 域名
* @param string $name 文件名
* @param string $param 附加参数
* @return string
*/
public static function download($domain, $name, $param = [])
{
$url = $domain . $name . '?' . http_build_query($param);
$token = self::sign($url);
return $url . '&token=' . $token;
}
/**
* 重命名【移动】
*
* @param string $from 来源位置
* @param string $to 目标位置
* return bool
*/
public static function move($from, $to)
{
// $bucket = $this->_config['bucket'];
$op = '/move/' . self::qiniuEncode($from) . '/' . self::qiniuEncode($to);
return self::opration($op);
}
/**
* 复制文件
*
* @param string $from 来源文件
* @param string $saveas 目标文件
* @return bool
*/
public static function copy($from, $saveas)
{
// $bucket = $this->_config['bucket'];
$op = '/copy/' . self::qiniuEncode($from) . '/' . self::qiniuEncode($saveas);
return self::opration($op);
}
/**
* 获取token
*
* @param string $bucket 空间名
* @param string $key 密钥
* @param int $max 文件大小限制
* @param int $timeout 超时时间
* @return string
*/
public static function getToken($bucket, $key, $max = 10485760, $timeout = 600)
{
$setting = array(
'scope' => $bucket,
'saveKey' => $key,
'deadline' => $timeout + $_SERVER['REQUEST_TIME'],
'fsizeLimit' => intval($max),
);
$setting = self::qiniuEncode(json_encode($setting));
return self::sign($setting) . ':' . $setting;
}
/**
* 删除
*
* @param string $uri 文件路径
* @return boolean
*/
public static function delete($uri)
{
$file = self::qiniuEncode($uri);
return self::opration('/delete/' . $file);
}
/**
* 判断文件是否存在
*
* @param string $uri
* @return boolean
*/
public static function has($uri)
{
$op = '/stat/' . self::qiniuEncode($uri);
return self::opration($op);
}
/**
* 转pdf
*
* @param string $bucket
* @param string $key
* @param string $saveas
* @return boolean
*/
public static function toPdf($bucket, $key, $saveas)
{
$API = 'http://api.qiniu.com';
$op = '/pfop/';
$data = 'bucket=' . $bucket . '&key=' . $key . '&fops=yifangyun_preview|saveas/' . self::qiniuEncode($saveas);
return self::opration($op, $data, $API);
}
/**
* 七牛操作
*
* @param string $op 操作命令
* @param string $data 操作结果
* @param string $host
* @return boolean
*/
private static function opration($op, $data = null, $host = self::QINIU_RS)
{
$token = self::sign(is_string($data) ? $op . "\n" . $data : $op . "\n");
$url = $host . $op;
$header = array('Authorization: QBox ' . $token);
if ($ch = curl_init($url))
{
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
if ($data)
{
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($ch, CURLOPT_HEADER, 1);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status == 200)
{
return true;
}
// elseif (\Config::get('debug'))
// {
// /*操作出错*/
// \Log::debug($response, '七牛请求出错');
// }
}
Log::error('[QINIU]七牛错误' . $url . ':' . ($response ? : '请求失败'), 'ERROR');
return false;
}
/**
* 获取url签名
*
* @param string $url
* @return string
*/
private static function sign($url)
{
$config = self::$_config ? : (self::$_config = Config::get('service.qiniu'));
$sign = hash_hmac('sha1', $url, $config['secretkey'], true);
$ak = $config['accesskey'];
return $ak . ':' . self::qiniuEncode($sign);
}
/**
* 七牛安全编码
*/
private static function qiniuEncode($str)
{
return strtr(base64_encode($str), ['+' => '-', '/' => '_']);
}
}
... ...