http->getName(); if (strlen($path)) $path = '/' . ltrim($path, '/'); $prefix = rtrim(dirname(Library::$sapp->request->basefile()), '\\/'); $data = [ '__APP__' => rtrim(url('@')->build(), '\\/') . $path, '__ROOT__' => $prefix . $path, '__PLUG__' => "{$prefix}/static/extra/{$plugin}{$path}", '__FULL__' => Library::$sapp->request->domain() . $prefix . $path ]; return is_null($type) ? $data : ($data[$type] ?? $default); } /** * 生成全部静态路径 * @param string $path * @return string[] */ public static function uris(string $path = ''): array { return static::uri($path, null); } /** * 设置配置数据 * @param string $name 配置名称 * @param mixed $value 配置内容 * @return integer|string * @throws \think\admin\Exception */ public static function set(string $name, $value = '') { [$type, $field] = static::_parse($name); if (is_array($value)) { $count = 0; foreach ($value as $kk => $vv) { $count += static::set("{$field}.{$kk}", $vv); } return $count; } else try { $map = ['type' => $type, 'name' => $field]; SystemConfig::mk()->master()->where($map)->findOrEmpty()->save(array_merge($map, ['value' => $value])); sysvar('think.admin.config', []); Library::$sapp->cache->delete('SystemConfig'); return 1; } catch (\Exception $exception) { throw new Exception($exception->getMessage(), $exception->getCode()); } } /** * 读取配置数据 * @param string $name * @param string $default * @return array|mixed|string * @throws \think\admin\Exception */ public static function get(string $name = '', string $default = '') { try { if (empty($config = sysvar($keys = 'think.admin.config') ?: [])) { SystemConfig::mk()->cache('SystemConfig')->select()->map(function ($item) use (&$config) { $config[$item['type']][$item['name']] = $item['value']; }); sysvar($keys, $config); } [$type, $field, $outer] = static::_parse($name); if (empty($name)) { return $config; } elseif (isset($config[$type])) { $group = $config[$type]; if ($outer !== 'raw') foreach ($group as $kk => $vo) { $group[$kk] = htmlspecialchars(strval($vo)); } return $field ? ($group[$field] ?? $default) : $group; } else { return $default; } } catch (\Exception $exception) { throw new Exception($exception->getMessage(), $exception->getCode()); } } /** * 数据增量保存 * @param Model|Query|string $query 数据查询对象 * @param array $data 需要保存的数据,成功返回对应模型 * @param string $key 更新条件查询主键 * @param mixed $map 额外更新查询条件 * @return boolean|integer 失败返回 false, 成功返回主键值或 true * @throws \think\admin\Exception */ public static function save($query, array &$data, string $key = 'id', $map = []) { try { $query = Helper::buildQuery($query)->master()->strict(false); if (empty($map[$key])) $query->where([$key => $data[$key] ?? null]); $model = $query->where($map)->findOrEmpty(); // 当前操作方法描述 $action = $model->isExists() ? 'onAdminUpdate' : 'onAdminInsert'; // 写入或更新模型数据 if ($model->save($data) === false) return false; // 模型自定义事件回调 if ($model instanceof \think\admin\Model) { $model->$action(strval($model->getAttr($key))); } $data = $model->toArray(); return $model[$key] ?? true; } catch (\Exception $exception) { throw new Exception($exception->getMessage(), $exception->getCode()); } } /** * 批量更新保存数据 * @param Model|Query|string $query 数据查询对象 * @param array $data 需要保存的数据,成功返回对应模型 * @param string $key 更新条件查询主键 * @param mixed $map 额外更新查询条件 * @return boolean|integer 失败返回 false, 成功返回主键值或 true * @throws \think\admin\Exception */ public static function batchUpdate($query, array $data, string $key = 'id', $map = []) { try { $query = Helper::buildQuery($query)->master()->strict(false)->where($map); if (empty($map[$key])) $query->where([$key => $data[$key] ?? null]); return (clone $query)->count() > 0 ? (clone $query)->update($data) : (clone $query)->save($data); } catch (\Exception $exception) { throw new Exception($exception->getMessage(), $exception->getCode()); } } /** * 解析缓存名称 * @param string $rule 配置名称 * @return array */ private static function _parse(string $rule): array { $type = 'base'; if (stripos($rule, '.') !== false) { [$type, $rule] = explode('.', $rule, 2); } [$field, $outer] = explode('|', "{$rule}|"); return [$type, $field, strtolower($outer)]; } /** * 获取数据库所有数据表 * @return array [table, total, count] */ public static function getTables(): array { $tables = Library::$sapp->db->getTables(); return [$tables, count($tables), 0]; } /** * 复制并创建表结构 * @param string $from 来源表名 * @param string $create 创建表名 * @param array $tables 现有表集合 * @param boolean $copy 是否复制 * @param mixed $where 复制条件 * @throws \think\admin\Exception */ public static function copyTableStruct(string $from, string $create, array $tables = [], bool $copy = false, $where = []) { try { if (empty($tables)) [$tables] = static::getTables(); if (!in_array($from, $tables)) { throw new Exception("待复制的数据表 {$from} 不存在!"); } if (!in_array($create, $tables)) { Library::$sapp->db->connect()->query("CREATE TABLE IF NOT EXISTS {$create} (LIKE {$from})"); if ($copy) { $sql1 = Library::$sapp->db->name($from)->where($where)->buildSql(false); Library::$sapp->db->connect()->query("INSERT INTO {$create} {$sql1}"); } } } catch (\Exception $exception) { throw new Exception($exception->getMessage(), $exception->getCode()); } } /** * 保存数据内容 * @param string $name 数据名称 * @param mixed $value 数据内容 * @return boolean * @throws \think\admin\Exception */ public static function setData(string $name, $value): bool { try { $data = ['name' => $name, 'value' => json_encode([$value], 64 | 256)]; return SystemData::mk()->where(['name' => $name])->findOrEmpty()->save($data); } catch (\Exception $exception) { throw new Exception($exception->getMessage(), $exception->getCode()); } } /** * 读取数据内容 * @param string $name 数据名称 * @param mixed $default 默认内容 * @return mixed */ public static function getData(string $name, $default = []) { try { // 读取原始序列化或JSON数据 $value = SystemData::mk()->where(['name' => $name])->value('value'); if (is_null($value)) return $default; if (is_string($value) && strpos($value, '[') === 0) { return json_decode($value, true)[0]; } } catch (\Exception $exception) { trace_file($exception); return $default; } try { // 尝试正常反序列解析 return unserialize($value); } catch (\Exception $exception) { trace_file($exception); } try { // 尝试修复反序列解析 $unit = 'i:\d+;|b:[01];|s:\d+:".*?";|O:\d+:".*?":\d+:\{'; $preg = '/(?=^|' . $unit . ')s:(\d+):"(.*?)";(?=' . $unit . '|}+$)/'; return unserialize(preg_replace_callback($preg, static function ($attr) { return sprintf('s:%d:"%s";', strlen($attr[2]), $attr[2]); }, $value)); } catch (\Exception $exception) { trace_file($exception); return $default; } } /** * 写入系统日志内容 * @param string $action * @param string $content * @return boolean */ public static function setOplog(string $action, string $content): bool { return SystemOplog::mk()->save(static::getOplog($action, $content)) !== false; } /** * 获取系统日志内容 * @param string $action * @param string $content * @return array */ public static function getOplog(string $action, string $content): array { return [ 'node' => NodeService::getCurrent(), 'action' => lang($action), 'content' => lang($content), 'geoip' => Library::$sapp->request->ip() ?: '127.0.0.1', 'username' => AdminService::getUserName() ?: '-', 'create_at' => date('Y-m-d H:i:s'), ]; } /** * 打印输出数据到文件 * @param mixed $data 输出的数据 * @param boolean $new 强制替换文件 * @param string|null $file 文件名称 * @return false|int */ public static function putDebug($data, bool $new = false, ?string $file = null) { ob_start(); var_dump($data); $output = preg_replace('/]=>\n(\s+)/m', '] => ', ob_get_clean()); if (is_null($file)) $file = syspath('runtime/' . date('Ymd') . '.log'); else if (!preg_match('#[/\\\\]+#', $file)) $file = syspath("runtime/{$file}.log"); is_dir($dir = dirname($file)) or mkdir($dir, 0777, true); return $new ? file_put_contents($file, $output) : file_put_contents($file, $output, FILE_APPEND); } /** * 设置网页标签图标 * @param ?string $icon 网页标签图标 * @return boolean * @throws \think\admin\Exception */ public static function setFavicon(?string $icon = null): bool { try { $icon = $icon ?: sysconf('base.site_icon|raw'); if (!preg_match('#^https?://#i', $icon)) { throw new Exception(lang('无效的原文件地址!')); } if (preg_match('#/upload/(\w{2}/\w{30}.\w+)$#i', $icon, $vars)) { $info = LocalStorage::instance()->info($vars[1]); } if (empty($info) || empty($info['file'])) { $name = Storage::name($icon, 'tmp', 'icon'); $info = LocalStorage::instance()->set($name, Storage::curlGet($icon), true); } if (empty($info) || empty($info['file'])) return false; $favicon = new FaviconExtend($info['file'], [48, 48]); return $favicon->saveIco(syspath('public/favicon.ico')); } catch (Exception $exception) { throw $exception; } catch (\Exception $exception) { trace_file($exception); throw new Exception($exception->getMessage(), $exception->getCode()); } } /** * 魔术方法调用(临时) * @param string $method 方法名称 * @param array $arguments 调用参数 * @return mixed * @throws \think\admin\Exception */ public function __call(string $method, array $arguments) { return static::__callStatic($method, $arguments); } /** * 静态方法兼容(临时) * @param string $method 方法名称 * @param array $arguments 调用参数 * @return mixed * @throws \think\admin\Exception */ public static function __callStatic(string $method, array $arguments) { $map = [ 'setRuntime' => 'set', 'getRuntime' => 'get', 'bindRuntime' => 'apply', 'isDebug' => 'isDebug', 'isOnline' => 'isOnline', 'doInit' => 'doWebsiteInit', 'doConsoleInit' => 'doConsoleInit', 'pushRuntime' => 'push', 'clearRuntime' => 'clear', 'checkRunMode' => 'check', ]; switch (strtolower($method)) { case 'setconfig': return self::setData(...$arguments); case 'getconfig': return self::getData(...$arguments); } if (isset($map[$method])) { return RuntimeService::{$map[$method]}(...$arguments); } else { throw new Exception("method not exists: RuntimeService::{$method}()"); } } }