SoChishun <14507247@qq.com> Added.
* @since 2.0 <2015-7-24> SoChishun
* 1.重命名为SuExplorer.php
* 2.改进若干外观样式
* 3.新增登录验证模块
* 4.新增删除功能
* @since 3.0 <2015-10-7> SoChishun
* 1.新增在线压缩和解压功能
* 2.新增chomd权限设置功能
* 3.新增rename重命名功能
* 4.新增新建文件和目录功能
* 5.类SuFileEditor重构为SuExplorer,类方法改为静态方法
* 6.新增主配置文件功能
* 7.重构页面逻辑,改为脚本混合代码,便于阅读
* 8.基于绝对路径操作改为基于网站根目录的相对路径操作
* @since 3.1 <2016-9-13> SoChishun
* 1. 新增文件上传功能
* @since 3.2 <2017-8-23> SoChishun
* 1. 修正无法查看脚本文件的问题
* 2. 对html输出增加htmlspecialchars过滤功能
*/
// 程序版本号 [2015-10-7] Added.
$version = '3.2';
// session键名 [2015-10-7] Added.
$sess_id = 'sess_suexplorer';
// 权限规则 [2015-10-7] Added.
$prules = array('delfile', 'deldir', 'savefile', 'newfile', 'mkdir', 'renamefile', 'renamedir', 'chomdfile', 'chomddir', 'zip', 'unzip');
// 主配置 [2015-10-7] Added.
$config = array(
/* 用户配置 */
'users' => array(
'admin' => array('admin123', array('allow' => array(), 'forbit' => array()))
),
);
// 登录信息 [2015-10-7] Added.
$login_data = isset($_SESSION[$sess_id]) ? $_SESSION[$sess_id] : false;
$action = I('action');
$view = I('view');
$path = I('path', '/'); // urldecode($_GET['path']) $_SERVER['DOCUMENT_ROOT']
$parent_path = path_getdir($path);
switch ($action) {
case 'login': // 用户登录
if (!SuExplorer::user_login($config, $sess_id, $msg)) {
redirect('?r=fail', 1, $msg);
} else {
redirect('?r=ok');
}
break;
case 'logout': // 注销登录
SuExplorer::user_logout($sess_id);
redirect('?r=ok');
break;
case 'del': // 删除路径(文件或目录)
if (!SuExplorer::act_delete_path($path, $msg)) {
redirect('?path=' . $path, 1, $msg);
} else {
redirect('?path=' . path_getdir($path), 1, '恭喜,操作成功!');
}
break;
case 'savefile': // 保存文件
case 'save_newfile': // 新建文件
if (!SuExplorer::act_save_file($msg)) {
redirect('?path=' . $path, 1, $msg);
} else {
redirect('?path=' . $path, 1, '恭喜,操作成功!');
}
break;
case 'save_newdir': // 新建目录
if (!SuExplorer::act_save_newdir($msg)) {
redirect('?path=' . $path, 1, $msg);
} else {
redirect('?path=' . $path, 1, '恭喜,操作成功!');
}
break;
case 'upload_file': // 上传文件
if (!SuExplorer::act_upload_file($msg)) {
redirect('?path=' . $path, 1, $msg);
} else {
redirect('?path=' . $path, 1, '恭喜,文件上传成功!');
}
break;
case 'rename_path': // 重命名路径(文件或目录)
if (!SuExplorer::act_rename_path($msg)) {
redirect('?path=' . $path, 1, $msg);
} else {
redirect('?path=' . pathinfo($path, PATHINFO_DIRNAME), 1, '恭喜,操作成功!');
}
break;
case 'chmod_path': // 修改权限(文件或目录)
if (!SuExplorer::act_chmod_path($msg)) {
redirect('?path=' . $path, 1, $msg);
} else {
redirect('?path=' . $path, 1, '恭喜,操作成功!');
}
break;
case 'zip': // 压缩
if (!SuExplorer::act_zip($msg)) {
redirect('?path=' . $path, 1, $msg);
} else {
redirect('?path=' . $path, 1, '恭喜,操作成功!');
}
break;
case 'unzip': // 解压缩
if (!SuExplorer::act_unzip($msg)) {
redirect('?path=' . $path, 1, $msg);
} else {
redirect('?path=' . $path, 1, '恭喜,操作成功!');
}
break;
}
?>
SuExplorer-
SoChishun Added.
*/
function I($name, $defv = '') {
if (isset($_GET[$name])) {
return $_GET[$name];
}
return isset($_POST[$name]) ? $_POST[$name] : $defv;
}
/**
* URL重定向
* @param string $url 重定向的URL地址
* @param integer $time 重定向的等待时间(秒)
* @param string $msg 重定向前的提示信息
* @return void
* @since 1.0 <2015-10-7> from ThinkPHP
*/
function redirect($url, $time = 0, $msg = '') {
//多行URL地址支持
$url = str_replace(array("\n", "\r"), '', $url);
if (empty($msg))
$msg = "系统将在{$time}秒之后自动跳转到{$url}!";
if (!headers_sent()) {
// redirect
if (0 === $time) {
header('Location: ' . $url);
} else {
header("refresh:{$time};url={$url}");
echo($msg);
}
exit();
} else {
$str = "";
if ($time != 0)
$str .= $msg;
exit($str);
}
}
/**
* 获取文件扩展名类型
* @param string $exten 扩展名(不带.)
* @return string
* @since 1.0 <2015-10-9> SoChishun Added.
*/
function get_exten_catetory($exten) {
if ($exten) {
$filetypes = array('zip' => array('zip', 'rar', '7-zip', 'tar', 'gz', 'gzip'), 'doc' => array('txt', 'rtf', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'wps', 'et'), 'script' => array('php', 'js', 'css', 'c'), 'image' => array('jpg', 'jpeg', 'png', 'gif', 'tiff', 'psd', 'bmp', 'ico'));
foreach ($filetypes as $catetory => $extens) {
if (in_array($exten, $extens)) {
return $catetory;
}
}
}
return '';
}
/**
* 绝对路径转相对路径
* @param string $path
* @return string
* @since 1.0 <2015-10-9> SoChishun Added.
*/
function path_ator($path) {
$root = $_SERVER['DOCUMENT_ROOT'];
$path = substr($path, strlen($root));
if ('/' != DIRECTORY_SEPARATOR) {
$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
}
return $path;
}
/**
* 相对路径转绝对路径
* @param string $path
* @return string
* @since 1.0 <2015-10-9> SoChishun Added.
*/
function path_rtoa($path) {
$root = $_SERVER['DOCUMENT_ROOT'];
if ('/' != DIRECTORY_SEPARATOR) {
$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
}
return $root . $path;
}
function path_rt_oa($path) {
$root = $_SERVER['DOCUMENT_ROOT'];
if ('/' != DIRECTORY_SEPARATOR) {
$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
}
return '.' . $path;
}
/**
* 获取文件的目录地址
* @param string $path
* @param boolean $is_r 是否相对路径
* @return string
* @since 1.0 <2015-10-9> SoChishun Added.
*/
function path_getdir($path, $is_r = true) {
if (!$path || is_dir($is_r ? path_rtoa($path) : $path)) {
return $path;
}
return pathinfo($path, PATHINFO_DIRNAME);
}
/**
* 页面主类
* @since 1.0 <2015-5-11> SoChishun <14507247@qq.com> Added.
* @since 3.0 <2015-10-7> SoChishun 重构.
*/
class SuExplorer {
/**
* 版本号
* @var string
* @since 1.0 <2015-10-7> SoChishun Added.
*/
CONST VERSION = '3.0.0';
/**
* 显示网站目录的项目内容
* @since 1.0 <2015-5-11> SoChishun Added.
*/
static function index($view, $path) {
// 面包屑导航
self::location_to_breadcrumb($path);
// 视图显示
switch ($view) {
case 'newfile':
self::view_create_file($path);
break;
case 'newdir':
self::view_create_dir($path);
break;
case 'upload':
self::view_upload_file($path);
break;
case 'rename':
self::view_rename_path($path);
break;
case 'chmod':
self::view_chmod_path($path);
break;
case 'zip':
self::view_zip();
break;
case 'unzip':
self::view_unzip();
break;
default:
// 列出文件
$sapath = path_rtoa($path);
if (is_dir($sapath)) {
self::view_content_list($path);
} else if (is_file($sapath)) {
self::view_edit_file($path);
} else {
echo '文件或目录不存在或已删除!';
}
break;
}
}
/**
* 用户登录操作
* @param array $config
* @param string $sess_id
* @param string $msg 错误消息
* @return boolean
* @since 1.0 <2015-10-9> SoChishun Added.
*/
static function user_login($config, $sess_id, &$msg = '') {
if (!$_POST || !isset($_POST['uname'])) {
$msg = '表单数据无效!';
return false;
}
$uname = $_POST['uname'];
if (!array_key_exists($uname, $config['users'])) {
$msg = '用户不存在';
return false;
}
$login_data = $config['users'][$uname];
if ($login_data[0] != $_POST['upwd']) {
$msg = '密码错误!';
return false;
}
$_SESSION[$sess_id] = array('user_name' => $uname, 'rules' => isset($login_data[1]) ? $login_data[1] : false);
return true;
}
/**
* 用户登出操作
* @param string $sess_id
* @return boolean
* @since 1.0 <2015-10-9> SoChishun Added.
*/
static function user_logout($sess_id) {
if (isset($_SESSION[$sess_id])) {
unset($_SESSION[$sess_id]);
}
return true;
}
/**
* 删除路径(文件或目录)
* @param string $path 路径
* @param string $msg 错误消息
* @return boolean|string
* @since 1.0 <2015-10-7> SoChishun Added.
* @since 2.0 <2015-10-8> SoChishun 将delete_file和delete_dir合并到delete_path
*/
static function act_delete_path($path, &$msg = '') {
if ('yes' != I('do')) {
// 为防止黑客破坏,删除操作需要手动增加参数do=yes
$msg = '非法操作!';
return false;
}
if ('/' == $path) {
$msg = '根目录无法删除!';
return false;
}
$path = path_rtoa($path);
if (is_file($path)) {
if (!@unlink($path)) {
$msg = '文件删除失败!';
return false;
}
return true;
}
if (is_dir($path)) {
if (!@rmdir($path)) {
$msg = '目录删除失败!(非空目录或权限不足)';
return false;
}
return true;
}
$msg = '不是有效文件或目录!';
return false;
}
/**
* 保存(新)文件
* @param type $msg
* @return boolean
* @since 1.0 <2015-10-7> SoChishun Added.
*/
static function act_save_file(&$msg = '') {
$filename = I('filename');
if (!$filename || !strpos($filename, '.')) {
$msg = '文件扩展名无效!';
return false;
}
$content = I('content');
$path = I('path');
$path = path_rtoa($path);
if (is_file($path)) {
$path = path_getdir($path, false);
}
$newpath = $path . DIRECTORY_SEPARATOR . $filename;
try {
file_put_contents($newpath, $content);
return true;
} catch (Exception $ex) {
$msg = $ex->getMessage();
return false;
}
}
/**
* 保存新目录
* @param string $msg 错误消息
* @return boolean
* @since 1.0 <2015-10-8> SoChishun Added.
*/
static function act_save_newdir(&$msg = '') {
$filename = I('filename');
if (!$filename) {
$msg = '目录名称无效';
return false;
}
$path = path_rtoa(I('path'));
$newpath = $path . DIRECTORY_SEPARATOR . $filename;
try {
mkdir($newpath, I('mode'));
return true;
} catch (Exception $ex) {
$msg = $ex->getMessage();
return false;
}
}
/**
* 上传文件
* @param type $msg
* @return boolean
* @since 1.0 <2016-9-13> SoChishun Added.
*/
static function act_upload_file(&$msg = '') {
if ($_FILES["file"]["error"] > 0) {
$msg = $_FILES["file"]["error"];
return false;
}
$path = I('path');
$path = path_rtoa($path);
$filename = $_FILES["file"]["name"];
if (file_exists($path . $filename)) {
$msg = $filename . " 文件已存在!";
return false;
}
move_uploaded_file($_FILES["file"]["tmp_name"], $path . $filename);
return true;
}
/**
* 重命名路径操作
* @param string $msg 错误消息
* @return boolean
* @since 1.0 <2015-10-9> SoChishun Added.
*/
static function act_rename_path(&$msg = '') {
$filename2 = I('filename2');
if (!$filename2) {
$msg = '新名称未填写!';
return false;
}
$path = path_rtoa(I('path'));
if (is_file($path) && !strpos($filename2, '.')) {
$msg = '文件扩展名无效!';
return false;
}
try {
$newname = pathinfo($path, PATHINFO_DIRNAME) . DIRECTORY_SEPARATOR . $filename2;
@rename($path, $newname);
return true;
} catch (Exception $ex) {
$msg = $ex->getMessage();
return false;
}
}
/**
* 编辑权限操作
* @param string $msg 错误消息
* @return boolean
* @since 1.0 <2015-10-9> SoChishun Added.
*/
static function act_chmod_path(&$msg = '') {
$mode = I('mode');
if (!$mode) {
$msg = '权限模式无效!';
return false;
}
$path = path_rtoa(I('path'));
try {
chmod($path, $mode);
return true;
} catch (Exception $ex) {
$msg = $ex->getMessage();
return false;
}
}
/**
* 压缩操作
* @param string $msg
* @return boolean
* @since 1.0 <2015-10-9> SoChishun Added.
*/
static function act_zip(&$msg = '') {
$filename = I('filename');
$content = I('content');
if (!$filename || !strpos($filename, '.')) {
$msg = '压缩文件名无效!';
return false;
}
if (!$content) {
$msg = '压缩内容无效!';
return false;
}
$include = array();
$exclude = array();
$paths = explode(PHP_EOL, $content);
foreach ($paths as $path) {
if (0 === strpos($path, 'exclude ')) {
$exclude[] = trim(substr($path, 8));
} else {
$include[] = $path;
}
}
if (!$include) {
$msg = '压缩内容无效!';
return false;
}
$zip = new ZipHelper();
return $zip->zip($filename, $msg, $include, $exclude, I('rootpath'));
}
/**
* 解压缩文件
* @param type $msg
* @return type
*/
static function act_unzip(&$msg = '') {
$root = I('root');
$filename = I('filename');
if (!$filename) {
$msg = '压缩文件路径无效!';
return false;
}
if (!$root) {
$msg = '解压缩路径无效!';
return false;
}
$zip = new ZipHelper();
return $zip->unzip($filename, $root, $msg);
}
/**
* 目录内容视图
* @param type $path
* @since 1.0 <2015-5-11> SoChishun Added.
*/
static function view_content_list($path) {
$files = self::get_dir_contents($path, array('name' => true, 'path' => false, 'real_path' => false, 'relative_path' => true, 'exten' => false, 'ctime' => false, 'mtime' => false, 'size' => false, 'is_dir' => true, 'is_file' => false, 'is_link' => true, 'is_executable' => true, 'is_readable' => false, 'is_writable' => false, 'filetype' => true));
echo '';
foreach ($files as $file) {
if ($file['is_dir']) {
echo '
' . $file['name'] . '';
} else {
$class = '';
if ($file['is_link']) {
$class = 'lightblue';
} else if ($file['is_executable']) {
$class = 'green';
} else {
switch ($file['filetype']) {
case 'zip':
$class = 'red';
break;
case 'image':
$class = 'purple';
break;
default:
$class = 'grey';
break;
}
}
echo '
' . $file['name'] . '';
}
}
echo '
';
}
/**
* 文件内容编辑视图
* @param type $path
* @since 1.0 <2015-5-11> SoChishun Added.
*/
static function view_edit_file($path) {
$sapath = path_rtoa($path);
if (!is_file($sapath)) {
return;
}
$category = get_exten_catetory(pathinfo($path, PATHINFO_EXTENSION));
switch ($category) {
case 'doc':
case 'script':
$btns = '';
if (!is_writable($sapath)) {
echo '文件不可写
';
$btns = '';
}
echo '';
break;
case 'image':
echo '
';
echo basename($path);
echo ' [原图]';
break;
default:
echo basename($path);
echo ' [下载]';
break;
}
}
/**
* 新增文件视图
* @param string $path 路径
* @since 1.0 <2015-10-8> SoChishun Added.
*/
static function view_create_file($path) {
echo '';
}
/**
* 新增目录视图
* @param string $path 路径
* @since 1.0 <2015-10-8> SoChishun Added.
*/
static function view_create_dir($path) {
echo '
';
}
static function view_upload_file($path) {
echo '';
}
/**
* 重命名文件视图
* @param string $path 路径
* @since 1.0 <2015-10-8> SoChishun Added.
*/
static function view_rename_path($path) {
echo '
';
}
/**
* 编辑权限视图
* @param string $path 路径
* @since 1.0 <2015-10-9> SoChishun Added.
*/
static function view_chmod_path($path) {
echo '
';
}
/**
* 压缩文件视图
* @since 1.0 <2015-10-9> SoChishun Added.
*/
static function view_zip() {
echo '';
}
/**
* 解压缩文件视图
* @since 1.0 <2015-10-9> SoChishun Added.
*/
static function view_unzip() {
echo '';
}
/**
* 返回指定路径下的内容
* @param string $directory 路径
* @param array $config 选项
* @return array
* @throws Exception
* @since 1.0 <2015-5-11> SoChishun Added.
* @since 1.1 <2015-10-8> SoChishun 新增filetype文件类别属性
*/
static function get_dir_contents($directory, $options = array()) {
$config = array('name' => true, 'path' => true, 'real_path' => true, 'relative_path' => false, 'exten' => false, 'ctime' => false, 'mtime' => false, 'size' => false, 'is_dir' => true, 'is_file' => false, 'is_link' => false, 'is_executable' => false, 'is_readable' => false, 'is_writable' => false, 'filetype' => false);
if ($options) {
$config = array_merge($config, $options);
}
try {
$dir = new DirectoryIterator(path_rtoa($directory));
} catch (Exception $e) {
throw new Exception($directory . ' is not readable');
}
$files = array();
foreach ($dir as $file) {
if ($file->isDot()) {
continue;
}
if ($config['name']) {
$item['name'] = $file->getFileName();
}
if ($config['path']) {
$item['path'] = $file->getPath();
}
if ($config['real_path']) {
$item['real_path'] = $file->getRealPath();
}
if ($config['relative_path']) {
$item['relative_path'] = path_ator($file->getRealPath());
}
$exten = $file->getExtension();
if ($config['exten']) {
$item['exten'] = $exten;
}
if ($config['filetype']) {
$item['filetype'] = get_exten_catetory($exten);
}
if ($config['mtime']) {
$item['mtime'] = $file->getMTime();
}
if ($config['ctime']) {
$item['ctime'] = $file->getCTime();
}
if ($config['size']) {
$item['size'] = $file->getSize();
}
if ($config['is_dir']) {
$item['is_dir'] = $file->isDir();
}
if ($config['is_file']) {
$item['is_file'] = $file->isFile();
}
if ($config['is_link']) {
$item['is_link'] = $file->isLink();
}
if ($config['is_executable']) {
$item['is_executable'] = $file->isExecutable();
}
if ($config['is_readable']) {
$item['is_readable'] = $file->isReadable();
}
if ($config['is_writable']) {
$item['is_writable'] = $file->isWritable();
}
$files[] = $item;
}
return $files;
}
/**
* 路径转为导航
* @param string $path
* @since 1.0 <2015-5-11> SoChishun Added.
*/
static function location_to_breadcrumb($path) {
echo '/';
if ('/' != $path) {
$asubpath = explode('/', substr($path, 1));
if ($asubpath) {
$str = '';
foreach ($asubpath as $sub) {
$str .= '/' . $sub;
echo '
', $sub, '';
}
}
}
// echo '
', path_rtoa($path), '
';
// 不显示根目录, 只显示相对路径,
echo '
', path_rt_oa($path), '
';
echo '
';
}
}
/**
* 压缩类
* @since 1.0 <2015-10-9> SoChishun Added.
*/
class ZipHelper {
/**
* 解压缩之
* @param type $filename
* @param type $root
* @param type $msg
* @return boolean
* @since 1.0 <2015-10-9> SoChishun Added.
*/
function unzip($filename, $root, &$msg = '') {
if (!$filename) {
$msg = '压缩文件名无效!';
return false;
}
$zip = new ZipArchive();
$msg = $zip->open($filename);
if (true !== $msg) {
$msg = var_export($msg, true);
return false;
}
$zip->extractTo($root);
$zip->close();
return true;
}
/**
* 压缩之
* @param type $filename
* @param type $msg
* @param type $include
* @param type $exclude
* @param string $trimpath
* @param string $comment
* @return boolean
* @since 1.0 <2015-10-9> SoChishun Added.
*/
function zip($filename, &$msg = '', $include = array(), $exclude = array(), $trimpath = '', $comment = 'default') {
if (!$filename) {
$msg = '压缩文件名无效!';
return false;
}
if (!$include) {
$msg = '压缩内容无效!';
return false;
}
if ('default' == $comment) {
$comment = basename($filename) . PHP_EOL . 'Generate at ' . date('Y-m-d H:i:s') . PHP_EOL . 'Powerd by SuExplorer.'; // 注释内容
}
try {
$zip = new ZipArchive();
$msg = $zip->open($filename, ZIPARCHIVE::OVERWRITE);
if (true !== $msg) {
$msg = var_export($msg, true);
return false;
}
if ($comment) {
$zip->setArchiveComment($comment);
}
if ($trimpath) {
$trimpath = rtrim($trimpath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
}
$substart = strlen($trimpath);
foreach ($include as $source) {
$this->zip_dir($zip, $source, $exclude, $substart);
}
$zip->close();
return true;
} catch (Exception $ex) {
$msg = $ex->getMessage();
return false;
}
}
/**
* 递归压缩整个目录
* @param ZipArchive $zip
* @param string $source 包含的路径
* @param array $exclude 排除的路径
* @param int $substart 开始截取的路径字符串(用于去除路径中的根目录路径)
* @since 1.0 <2015-8-28> SoChishun Added.
*/
function zip_dir(&$zip, $source, $exclude, $substart = 0) {
if (is_dir($source)) {
$source = rtrim($source, DIRECTORY_SEPARATOR);
if ($handle = opendir($source)) {
while (false !== ( $f = readdir($handle) )) {
if ('.' == $f || '..' == $f) {
continue;
}
$filename = $source . DIRECTORY_SEPARATOR . $f;
if (is_dir($filename)) {
if ($exclude && in_array($filename, $exclude)) {
continue;
}
$this->zip_dir($zip, $filename, $exclude, $substart);
} else {
if ($exclude && in_array($filename, $exclude)) {
continue;
}
$zip->addFile($filename, substr($filename, $substart));
}
}
closedir($handle);
}
} else {
if ($exclude && in_array($source, $exclude)) {
return;
}
$zip->addFile($source);
}
}
}