diff --git a/extend/.gitignore b/extend/.gitignore deleted file mode 100755 index a3a0c8b5..00000000 --- a/extend/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore \ No newline at end of file diff --git a/extend/.htaccess b/extend/.htaccess new file mode 100644 index 00000000..3418e55a --- /dev/null +++ b/extend/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/extend/fast/Auth.php b/extend/fast/Auth.php new file mode 100644 index 00000000..2d7d677f --- /dev/null +++ b/extend/fast/Auth.php @@ -0,0 +1,288 @@ + +// +---------------------------------------------------------------------- +// | 修改者: 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]; + } + +} diff --git a/extend/fast/Date.php b/extend/fast/Date.php new file mode 100644 index 00000000..8e84d044 --- /dev/null +++ b/extend/fast/Date.php @@ -0,0 +1,287 @@ +. + * + * @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; + } + +} diff --git a/extend/fast/Form.php b/extend/fast/Form.php new file mode 100644 index 00000000..af877e2e --- /dev/null +++ b/extend/fast/Form.php @@ -0,0 +1,1201 @@ + '__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 '
'; + } + + /** + * 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 ''; + } + + /** + * 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 '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 ''; + } + + /** + * 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 ""; + } + + /** + * 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 ''; + } + + /** + * 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 ''; + } + + /** + * 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 ''; + } + + /** + * 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); + } + +} diff --git a/extend/fast/Http.php b/extend/fast/Http.php new file mode 100644 index 00000000..2206bb53 --- /dev/null +++ b/extend/fast/Http.php @@ -0,0 +1,190 @@ + 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; + } + } + } + +} diff --git a/extend/fast/Menu.php b/extend/fast/Menu.php new file mode 100644 index 00000000..62081718 --- /dev/null +++ b/extend/fast/Menu.php @@ -0,0 +1,166 @@ +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] = '' . $nums . ''; + } + } + + // 读取管理员当前拥有的权限节点 + $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, '', '', '' + ), preg_replace(array( + "/\[color=([#\w]+?)\]/i", + "/\[size=(\d+?)\]/i", + "/\[size=(\d+(\.\d+)?(px|pt|in|cm|mm|pc|em|ex|%)+?)\]/i", + "/\[font=([^\[\<]+?)\]/i", + "/\[align=(left|center|right)\]/i", + "/\[float=(left|right)\]/i" + ), array( + "", + "", + "", + "", + "
",
+ ""
+ ), $message));
+ if (strpos($message, '[/quote]') !== FALSE)
+ {
+ $message = preg_replace("/\s*\[quote\][\n\r]*(.+?)[\n\r]*\[\/quote\]\s*/is", $this->tpl_quote(), $message);
+ }
+ if (strpos($message, '[/img]') !== FALSE)
+ {
+ $message = preg_replace(array(
+ "/\[img\]\s*([^\[\<\r\n]+?)\s*\[\/img\]/ies",
+ "/\[img=(\d{1,4})[x|\,](\d{1,4})\]\s*([^\[\<\r\n]+?)\s*\[\/img\]/ies"
+ ), array(
+ "\$this->bbcodeurl('\\1', '')",
+ "\$this->bbcodeurl('\\3', '
')"
+ ), $message);
+ }
+ for ($i = 0; $i <= $this->uccode['pcodecount']; $i++)
+ {
+ $message = str_replace("[\tUCENTER_CODE_$i\t]", $this->uccode['codehtml'][$i], $message);
+ }
+ return nl2br(str_replace(array("\t", ' ', ' '), array(' ', ' ', ' '), $message));
+ }
+
+ function parseurl($url, $text)
+ {
+ if (!$url && preg_match("/((https?|ftp|gopher|news|telnet|rtsp|mms|callto|bctp|ed2k|thunder|synacast){1}:\/\/|www\.)[^\[\"']+/i", trim($text), $matches))
+ {
+ $url = $matches[0];
+ $length = 65;
+ if (strlen($url) > $length)
+ {
+ $text = substr($url, 0, intval($length * 0.5)) . ' ... ' . substr($url, - intval($length * 0.3));
+ }
+ return '' . $text . '';
+ }
+ else
+ {
+ $url = substr($url, 1);
+ if (substr(strtolower($url), 0, 4) == 'www.')
+ {
+ $url = 'http://' . $url;
+ }
+ return '' . $text . '';
+ }
+ }
+
+ function parseemail($email, $text)
+ {
+ $text = str_replace('\"', '"', $text);
+ if (!$email && preg_match("/\s*([a-z0-9\-_.+]+)@([a-z0-9\-_]+[.][a-z0-9\-_.]+)\s*/i", $text, $matches))
+ {
+ $email = trim($matches[0]);
+ return '' . $email . '';
+ }
+ else
+ {
+ return '' . $text . '';
+ }
+ }
+
+ function bbcodeurl($url, $tags)
+ {
+ if (!preg_match("/<.+?>/s", $url))
+ {
+ if (!in_array(strtolower(substr($url, 0, 6)), array('http:/', 'https:', 'ftp://', 'rtsp:/', 'mms://')))
+ {
+ $url = 'http://' . $url;
+ }
+ return str_replace(array('submit', 'logging.php'), array('', ''), sprintf($tags, $url, addslashes($url)));
+ }
+ else
+ {
+ return ' ' . $url;
+ }
+ }
+
+ function tpl_codedisp($code)
+ {
+ return '
' . $code . '\\1