php动态高亮web源代码
注:配置好不允许高亮的文件名,安全第一
#php实现动态展示目录树结构源代码
适用于开放源代码,结合html缓存使用效果更佳,因循环较多不适合放首页
能力有限没实现行号
演示:show source|开放源代码
效果截图
代码
4个文件放统一目录
1.php,1.html,net.js,showsource.php
1.php
<?php
header('Content-Type: text/html; charset=utf-8');
define('root',str_replace('\\','/',realpath(__DIR__.'/../..')));
$dir = root;
$res = [];
$root_dir = $dir;
$res = tree($res,$dir);
//$res = preg_replace('/^'.preg_quote($dir,'/').'/', 'root', $res);
$data = base64_encode(json_encode($res));
require '1.html';
function tree(&$res,$dir)
{
global $root_dir;
if(count($res)===0){
$res[]=[
'id'=>sha1($dir),
'pid'=>0,
'type'=>'dir',
'handle'=>$dir,
'name'=>substr($dir,strripos($dir,'/')+1,strlen($dir)),
];
}
$handles = array_merge(glob($dir . '/.*'), glob($dir . '/*'));
$handles = preg_replace('/^[\s\S]*\/\.$/','',$handles);
$handles = preg_replace('/^[\s\S]*\/\.\.$/','',$handles);
$handles = array_filter($handles);
sort($handles);
foreach ($handles as $item){
if(is_dir($item)){
$res[]=[
'id'=>sha1($item),
'pid'=>sha1($dir),
'type'=>'dir',
'handle'=>preg_replace('/^'.preg_quote($root_dir,'/').'/','root',$item),
'name'=>substr($item,strripos($item,'/')+1,strlen($item)),
];
tree($res,$item);
}else{
$res[]=[
'id'=>sha1($item),
'pid'=>sha1($dir),
'type'=>'file',
'handle'=>preg_replace('/^'.preg_quote($root_dir,'/').'/','root',$item),
'name'=>substr($item,strripos($item,'/')+1,strlen($item)),
];
}
}
return $res;
}
function dump($s=null,$return = false)
{
ob_start();
var_dump($s);
$res = ob_get_clean();
$res = preg_replace('/'.preg_quote(']=>','/').'\n[\s]+/m', '] => ', $res);
switch (php_sapi_name()){
case 'cgi-fcgi':
$res = preg_replace('/ /U', "\t", $res);
$res = '<pre><code>'.$res.'</code></pre>';
if($return)
return $res;
echo $res;
break;
case 'cli':
if($return)
return $res;
echo $res;
break;
}
}
1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>show source</title>
<style>
.tree {
--spacing: 1.5rem;
--radius: 10px;
padding-left: 0;
padding-right: 10px;
}
.tree li {
display: block;
position: relative;
padding-left: calc(2 * var(--spacing) - var(--radius) - 2px);
}
.tree ul {
margin-left: calc(var(--radius) - var(--spacing));
padding-left: 0;
}
.tree ul li {
border-left: 2px solid #ddd;
}
.tree ul li:last-child {
border-color: transparent;
}
.tree ul li::before {
content: '';
display: block;
position: absolute;
top: calc(var(--spacing) / -2);
left: -2px;
width: calc(var(--spacing) + 2px);
height: calc(var(--spacing) + 1px);
border: solid #ddd;
border-width: 0 0 2px 2px;
}
.tree summary {
display: block;
cursor: pointer;
}
.tree summary::marker,
.tree summary::-webkit-details-marker {
display: none;
}
.tree summary:focus {
outline: none;
}
.tree summary:focus-visible {
outline: 1px dotted #000;
}
.tree summary::before {
content: '';
display: block;
position: absolute;
top: calc(var(--spacing) / 2 - var(--radius));
left: calc(var(--spacing) - var(--radius) - 1px);
width: calc(2 * var(--radius));
height: calc(2 * var(--radius));
border-radius: 50%;
background: #ddd;
}
.tree li a::after {
content: '';
display: block;
position: absolute;
top: calc(var(--spacing) / 2 - var(--radius));
left: calc(var(--spacing) - var(--radius) - 1px);
width: calc(2 * var(--radius));
height: calc(2 * var(--radius));
border-radius: 50%;
background: #ddd;
}
.tree .active a::after {
content: 'z';
z-index: 1;
background: #696;
color: #fff;
line-height: calc(2 * var(--radius) - 2px);
text-align: center;
}
.tree .active a{
color: #0a67fb;
}
.tree summary::before {
content: '+';
z-index: 1;
background: #696;
color: #fff;
line-height: calc(2 * var(--radius) - 2px);
text-align: center;
}
.tree details[open] > summary::before {
content: '−';
}
.tree ul li a {
display: block;
text-decoration: none;
color: #222222;
}
.tree summary:hover{
color: royalblue;
}
.tree a:hover{
color: royalblue;
}
body{
background: #fff;
}
.file-menu{
background: #eee;
}
.source{
/*background: royalblue;*/
}
.main{
width: 100%;
display: inline-flex;
}
.main .left{
width: 30%;
}
.main .right{
width: 70%;
}
.source{
padding-left: 10px;
}
</style>
</head>
<body>
<div class="main">
<div class="left">
<div class="file-menu">
<ul id="file-list" class="tree">
</ul>
</div>
</div>
<div class="right">
<div id="source" class="source"></div>
</div>
</div>
<script src="/static/js/net.js"></script>
<script>
net = new Net();
var data = "<?php echo $data; ?>";
data = JSON.parse(atob(data));
var tree = treeFile(data);
initFileList(tree);
function showSource(obj) {
document.querySelectorAll('#file-list .active').forEach(function (active) {
active.classList.remove('active');
});
obj.parentElement.classList.add('active');
let post_data = {file:obj.dataset.file};
net.post('showsource.php','data='+JSON.stringify(post_data),function (data) {
data = JSON.parse(data);
if(data.code!==200){
document.getElementById("source").textContent=data.msg;
return;
}
document.getElementById("source").innerHTML='<pre>'+data.data+'</pre>';
});
}
function treeFile(data) {
for (let i = 0; i < data.length; i++) {
data[i].childnodes = [];
data[i].list = [];
for (let j = 0; j < data.length; j++) {
if (data[j].pid === data[i].id) {
switch (data[j].type) {
case 'dir':
data[i].childnodes.push(data[j]);
break;
case 'file':
data[i].list.push(data[j]);
break;
}
}
}
}
return data[0];
}
function initFileList(data) {
var ul = '';
ul = getFileList(data, ul);
document.getElementById("file-list").innerHTML = ul;
document.querySelectorAll('#file-list a').forEach(function (link) {
link.addEventListener('click', function (event) {
event.preventDefault();
showSource(link);
});
});
}
function getFileList(obj, ul) {
ul += '<li>';
ul += '<details>';
ul += '<summary>' + obj.name + '</summary>';
ul += '<ul>';
if (!(obj.childnodes.length === 0)) {
for (let key in obj.childnodes) {
ul = getFileList(obj.childnodes[key], ul);
}
}
if (!(obj.list.length === 0)) {
for (let key in obj.list) {
ul += '<li><a href="#" data-file="'+obj.list[key].handle+'">' + obj.list[key].name + '</a></li>';
}
}
ul += '</ul>';
ul += '</details>';
ul += '</li>';
return ul;
}
</script>
</body>
</html>
net.js
function Net() {
this.xhr = new XMLHttpRequest();
this.get = function (url, func, pram = {}) {
xhr = this.xhr;
xhr.onreadystatechange = function () {
if (this.readyState == 4) {
pram.code = this.status;
if (this.status == 200) {
func(this.responseText, pram);
} else {
func(this.responseText, pram);
}
}
};
xhr.open('get', url, false);
xhr.send()
};
this.post = function (url, data, func,pram = {}) {
xhr = this.xhr;
xhr.onreadystatechange = function () {
if (this.readyState == 4) {
pram.code = this.status;
if (this.status == 200) {
func(this.responseText,pram);
} else {
func(this.responseText,pram);
}
}
};
xhr.open("POST", url, false);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(data)
}
}
showsource.php
<?php
header('Content-Type: text/html; charset=utf-8');
if(empty($_POST['data'])){
die;
}
$data = $_POST['data'];
$data = json_decode($data,true);
if(empty($data['file'])){
die;
}
define('root',str_replace('\\','/',realpath(__DIR__.'/../..')));
$dir = root;
$root_dir = $dir;
$pass_files = [
'root/app/conf.php',
];
$allow_ext = [
'html','js','css','php','txt','xml','json',
];
$file = $data['file'];
if(in_array($file,$pass_files)){
outSource(500,'涉及配置不允许高亮','');
die;
}
if(strpos($file,'.')!==false){
$file_ext = substr($file,strripos($file,'.')+1,strlen($file));
if(!in_array($file_ext,$allow_ext)){
outSource(500,'只允许高亮'.implode('|',$allow_ext).'后缀文件','');
die;
}
}
$file = preg_replace('/^root/',$root_dir,$file);
if(!is_file($file)){
outSource(500,'文件不存在','');
die;
}
$code = highlight_file($file,true);
outSource(200,'',$code);
function outSource($code,$msg,$data)
{
echo json_encode([
'code'=>$code,
'msg'=>$msg,
'data'=>$data,
],JSON_UNESCAPED_UNICODE);
}
注:配置好不允许高亮的文件名,安全第一